stream-chat 9.51.0 → 9.52.1

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 (39) hide show
  1. package/dist/cjs/index.browser.js +418 -215
  2. package/dist/cjs/index.browser.js.map +4 -4
  3. package/dist/cjs/index.node.js +427 -216
  4. package/dist/cjs/index.node.js.map +4 -4
  5. package/dist/esm/index.mjs +416 -213
  6. package/dist/esm/index.mjs.map +4 -4
  7. package/dist/types/channel.d.ts +5 -3
  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 +29 -6
  21. package/package.json +1 -1
  22. package/src/channel.ts +42 -26
  23. package/src/client.ts +27 -14
  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/messageDelivery/MessageDeliveryReporter.ts +5 -4
  36. package/src/middleware.ts +11 -0
  37. package/src/offline-support/offline_support_api.ts +4 -0
  38. package/src/uploadManager.ts +45 -0
  39. package/src/utils.ts +102 -7
@@ -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";
@@ -364,6 +364,22 @@ function logChatPromiseExecution(promise, name) {
364
364
  console.warn(`failed to do ${name}, ran into error: `, error);
365
365
  });
366
366
  }
367
+ var noopLogger = () => null;
368
+ var logError = (logger, error, event) => {
369
+ const message = "Unhandled error in event listener";
370
+ logger("error", message, { error, event });
371
+ if (logger === noopLogger) console.error(message, error, event);
372
+ };
373
+ var invokeEventListener = (listener, event, logger) => {
374
+ try {
375
+ const result = listener(event);
376
+ if (result && typeof result.then === "function") {
377
+ Promise.resolve(result).catch((error) => logError(logger, error, event));
378
+ }
379
+ } catch (error) {
380
+ logError(logger, error, event);
381
+ }
382
+ };
367
383
  var sleep = (m) => new Promise((r) => setTimeout(r, m));
368
384
  function isFunction(value) {
369
385
  return typeof value === "function" || value instanceof Function || Object.prototype.toString.call(value) === "[object Function]";
@@ -411,7 +427,6 @@ var channelHasReadEvents = (channel) => {
411
427
  return !(Array.isArray(ownCapabilities) && !ownCapabilities.includes("read-events"));
412
428
  };
413
429
  var channelTracksReadLocally = (channel) => !channelHasReadEvents(channel) && !!channel?.getClient().options.isLocalUnreadCountEnabled;
414
- var userHasReadReceipts = (client) => client.user?.privacy_settings?.read_receipts?.enabled ?? true;
415
430
  function addFileToFormData(uri, name, contentType) {
416
431
  const data = new FormData();
417
432
  if (isReadableStream(uri) || isBuffer(uri) || isFileWebAPI(uri) || isBlobWebAPI(uri)) {
@@ -575,6 +590,38 @@ function unformatMessage(message) {
575
590
  quoted_message: toMessageResponseBase(message.quoted_message)
576
591
  };
577
592
  }
593
+ var isRemoteUrl = (url) => !!url && /^https?:\/\//i.test(url.trim());
594
+ var sanitizeOutgoingAttachments = ({
595
+ client,
596
+ message
597
+ }) => {
598
+ const attachments = message.attachments;
599
+ if (!attachments?.length) return message;
600
+ const unresolved = [];
601
+ let changed = false;
602
+ const sanitized = attachments.reduce((acc, attachment) => {
603
+ const { localMetadata, ...rest } = attachment;
604
+ if (!localMetadata) {
605
+ acc.push(attachment);
606
+ return acc;
607
+ }
608
+ changed = true;
609
+ const hasRemoteSource = isRemoteUrl(rest.asset_url) || isRemoteUrl(rest.image_url) || isRemoteUrl(rest.og_scrape_url) || isRemoteUrl(rest.title_link);
610
+ if (hasRemoteSource) {
611
+ acc.push(rest);
612
+ } else {
613
+ unresolved.push(rest);
614
+ }
615
+ return acc;
616
+ }, []);
617
+ if (unresolved.length) {
618
+ const text = `Dropped ${unresolved.length} attachment(s) whose upload never completed. Composing with pending uploads requires awaiting them before sending.`;
619
+ const extraData = { attachments: unresolved, message, tags: ["attachments"] };
620
+ client.logger("warn", text, extraData);
621
+ console.warn(text, extraData);
622
+ }
623
+ return changed ? { ...message, attachments: sanitized } : message;
624
+ };
578
625
  var localMessageToNewMessagePayload = (localMessage) => {
579
626
  const {
580
627
  // Remove all timestamp fields and client-specific fields.
@@ -2347,6 +2394,12 @@ var CooldownTimer = class extends WithSubscriptions {
2347
2394
  var isScrapedContent = (attachment) => !!attachment?.og_scrape_url || !!attachment?.title_link;
2348
2395
  var isLocalAttachment = (attachment) => !!attachment?.localMetadata?.id;
2349
2396
  var isLocalUploadAttachment = (attachment) => !!attachment?.localMetadata?.uploadState;
2397
+ var PENDING_UPLOAD_STATES = [
2398
+ "pending",
2399
+ "uploading"
2400
+ ];
2401
+ var isPendingUpload = (attachment) => isLocalUploadAttachment(attachment) && PENDING_UPLOAD_STATES.includes(attachment.localMetadata.uploadState);
2402
+ var isFinishedUpload = (attachment) => isLocalUploadAttachment(attachment) && attachment.localMetadata.uploadState === "finished";
2350
2403
  var isFileAttachment = (attachment, supportedVideoFormat = []) => attachment.type === "file" || !!(attachment.mime_type && supportedVideoFormat.indexOf(attachment.mime_type) === -1 && attachment.type !== "video");
2351
2404
  var isLocalFileAttachment = (attachment) => isFileAttachment(attachment) && isLocalAttachment(attachment);
2352
2405
  var isImageAttachment = (attachment) => attachment.type === "image" && !isScrapedContent(attachment);
@@ -2422,7 +2475,7 @@ var ensureIsLocalAttachment = (attachment) => {
2422
2475
  };
2423
2476
 
2424
2477
  // src/messageComposer/middleware/attachmentManager/postUpload/attachmentEnrichment.ts
2425
- var createPostUploadAttachmentEnrichmentMiddleware = () => ({
2478
+ var createPostUploadAttachmentEnrichmentMiddleware = (composer) => ({
2426
2479
  id: "stream-io/attachment-manager-middleware/post-upload-enrichment",
2427
2480
  handlers: {
2428
2481
  postProcess: ({
@@ -2435,9 +2488,10 @@ var createPostUploadAttachmentEnrichmentMiddleware = () => ({
2435
2488
  if (error) return forward();
2436
2489
  if (!attachment || !response) return discard();
2437
2490
  const enrichedAttachment = { ...attachment };
2438
- const previewUri = attachment.localMetadata.previewUri;
2439
- if (previewUri) {
2440
- if (previewUri.startsWith("blob:")) URL.revokeObjectURL(previewUri);
2491
+ const { id, previewUri } = attachment.localMetadata;
2492
+ const attachmentIsStillInComposer = !composer || !!composer.attachmentManager.attachmentsById[id];
2493
+ if (previewUri && attachmentIsStillInComposer) {
2494
+ if (previewUri.startsWith("blob:")) URL.revokeObjectURL?.(previewUri);
2441
2495
  delete enrichedAttachment.localMetadata.previewUri;
2442
2496
  }
2443
2497
  if (isLocalImageAttachment(attachment)) {
@@ -2460,6 +2514,12 @@ var createPostUploadAttachmentEnrichmentMiddleware = () => ({
2460
2514
  var withoutConcurrency = createRunner(wrapWithContinuationTracking);
2461
2515
  var withCancellation = createRunner(wrapWithCancellation);
2462
2516
  var pendingPromises = /* @__PURE__ */ new Map();
2517
+ function hasPending(tag) {
2518
+ return pendingPromises.has(tag);
2519
+ }
2520
+ async function settled(tag) {
2521
+ await pendingPromises.get(tag)?.promise;
2522
+ }
2463
2523
  function createRunner(wrapper) {
2464
2524
  return function run(tag, cb) {
2465
2525
  const { cb: wrapped, onContinued } = wrapper(tag, cb);
@@ -2502,6 +2562,16 @@ var MiddlewareExecutor = class {
2502
2562
  this.middleware = [];
2503
2563
  this.id = generateUUIDv4();
2504
2564
  }
2565
+ /**
2566
+ * The middleware currently installed, in execution order.
2567
+ *
2568
+ * Read-only: use `use` / `insert` / `replace` / `setOrder` / `remove` to change the chain. It
2569
+ * exists so components can ask what the chain is capable of - a middleware may declare
2570
+ * behaviour its host has to account for, and the host has no other way to find out.
2571
+ */
2572
+ get installedMiddleware() {
2573
+ return this.middleware;
2574
+ }
2505
2575
  use(middleware) {
2506
2576
  this.middleware = this.middleware.concat(middleware);
2507
2577
  return this;
@@ -2605,6 +2675,142 @@ var MiddlewareExecutor = class {
2605
2675
  }
2606
2676
  };
2607
2677
 
2678
+ // src/uploadManager.ts
2679
+ import axios from "axios";
2680
+ var isUploadCancellation = (error) => axios.isCancel(error) || error instanceof Error && error.name === "AbortError";
2681
+ var initState = () => ({ uploads: {} });
2682
+ var upsertById = (uploads, record) => ({
2683
+ ...uploads,
2684
+ [record.id]: { ...uploads[record.id], ...record }
2685
+ });
2686
+ var updateById = (uploads, record) => {
2687
+ if (!(record.id in uploads)) return null;
2688
+ const current = uploads[record.id];
2689
+ return { ...uploads, [record.id]: { ...current, ...record } };
2690
+ };
2691
+ var UploadManager = class {
2692
+ constructor(client) {
2693
+ this.client = client;
2694
+ this.inFlightUploads = /* @__PURE__ */ new Map();
2695
+ this.getUpload = (id) => this.uploads[id];
2696
+ /**
2697
+ * Clears all upload records.
2698
+ * Invoked when the user disconnects so a later session does not inherit stale upload state.
2699
+ * Aborts every in-flight upload request via its `UploadRequestOptions.abortSignal`.
2700
+ */
2701
+ this.reset = () => {
2702
+ for (const { abortController } of this.inFlightUploads.values()) {
2703
+ abortController.abort();
2704
+ }
2705
+ this.inFlightUploads.clear();
2706
+ this.state.next(initState());
2707
+ };
2708
+ /**
2709
+ * Removes the upload record for `id` if present.
2710
+ * If an upload is still in progress, aborts its `UploadRequestOptions.abortSignal`.
2711
+ */
2712
+ this.deleteUploadRecord = (id) => {
2713
+ const flight = this.inFlightUploads.get(id);
2714
+ if (flight) {
2715
+ this.inFlightUploads.delete(id);
2716
+ flight.abortController.abort();
2717
+ }
2718
+ this.state.next((current) => {
2719
+ if (!(id in current.uploads)) return current;
2720
+ const uploads = { ...current.uploads };
2721
+ delete uploads[id];
2722
+ return { ...current, uploads };
2723
+ });
2724
+ };
2725
+ /**
2726
+ * Starts an upload for `id`, or returns the existing in-flight promise if one is already running.
2727
+ * Uses {@link StreamChat.channel}(`channelCid`) → `messageComposer.attachmentManager.doUploadRequest`.
2728
+ * Resolves with that result; rejects if the upload rejects (the record is removed from state either way).
2729
+ */
2730
+ this.upload = ({
2731
+ id,
2732
+ channelCid,
2733
+ file
2734
+ }) => {
2735
+ const existing = this.inFlightUploads.get(id);
2736
+ if (existing) return existing.promise;
2737
+ let resolvePromise;
2738
+ let rejectPromise;
2739
+ const promise = new Promise((resolve, reject) => {
2740
+ resolvePromise = resolve;
2741
+ rejectPromise = reject;
2742
+ });
2743
+ const abortController = new AbortController();
2744
+ this.inFlightUploads.set(id, { promise, abortController });
2745
+ void (async () => {
2746
+ const attachmentManager = this.resolveAttachmentManager(channelCid);
2747
+ const trackProgress = attachmentManager.config.trackUploadProgress;
2748
+ try {
2749
+ this.upsertUpload({
2750
+ uploadConfirmationPending: false,
2751
+ id,
2752
+ uploadProgress: trackProgress ? 0 : void 0
2753
+ });
2754
+ const onProgress = trackProgress ? (progress) => {
2755
+ this.updateUpload({
2756
+ id,
2757
+ uploadProgress: progress,
2758
+ // Only a number says anything about the flush, so a report without one leaves
2759
+ // the flag alone: `undefined` means the transport cannot measure this upload,
2760
+ // not that bytes were un-sent. Lowering it there would drop a UI that had
2761
+ // already gone indeterminate back to a determinate one with nothing to show.
2762
+ //
2763
+ // The record is removed in the `finally` below, so a record that still exists
2764
+ // while reporting 100% can only mean "flushed, awaiting the response".
2765
+ ...typeof progress === "number" ? { uploadConfirmationPending: progress >= 100 } : {}
2766
+ });
2767
+ } : void 0;
2768
+ const uploadRequestOptions = {
2769
+ abortSignal: abortController.signal,
2770
+ ...onProgress ? { onProgress } : {}
2771
+ };
2772
+ const response = await attachmentManager.doUploadRequest(
2773
+ file,
2774
+ uploadRequestOptions
2775
+ );
2776
+ resolvePromise(response);
2777
+ } catch (error) {
2778
+ rejectPromise(error);
2779
+ } finally {
2780
+ this.inFlightUploads.delete(id);
2781
+ this.deleteUploadRecord(id);
2782
+ }
2783
+ })();
2784
+ return promise;
2785
+ };
2786
+ this.upsertUpload = (record) => {
2787
+ this.state.partialNext({
2788
+ uploads: upsertById(this.uploads, record)
2789
+ });
2790
+ };
2791
+ this.updateUpload = (record) => {
2792
+ this.state.next((current) => {
2793
+ const nextUploads = updateById(current.uploads, record);
2794
+ if (!nextUploads) return current;
2795
+ return { ...current, uploads: nextUploads };
2796
+ });
2797
+ };
2798
+ this.state = new StateStore(initState());
2799
+ }
2800
+ resolveAttachmentManager(channelCid) {
2801
+ const colon = channelCid.indexOf(":");
2802
+ if (colon <= 0 || colon === channelCid.length - 1) {
2803
+ throw new Error(`Invalid channelCid: ${channelCid}`);
2804
+ }
2805
+ const channelType = channelCid.slice(0, colon);
2806
+ const channelId = channelCid.slice(colon + 1);
2807
+ return this.client.channel(channelType, channelId).messageComposer.attachmentManager;
2808
+ }
2809
+ get uploads() {
2810
+ return this.state.getLatestValue().uploads;
2811
+ }
2812
+ };
2813
+
2608
2814
  // src/messageComposer/middleware/attachmentManager/postUpload/uploadErrorHandler.ts
2609
2815
  var createUploadErrorHandlerMiddleware = (composer) => ({
2610
2816
  id: "stream-io/attachment-manager-middleware/upload-error",
@@ -2617,6 +2823,7 @@ var createUploadErrorHandlerMiddleware = (composer) => ({
2617
2823
  const { attachment, error } = state;
2618
2824
  if (!error) return forward();
2619
2825
  if (!attachment) return discard();
2826
+ if (isUploadCancellation(error)) return forward();
2620
2827
  const reason = error instanceof Error ? error.message : "unknown error";
2621
2828
  composer.client.notifications.addError({
2622
2829
  message: "Error uploading attachment",
@@ -2641,7 +2848,7 @@ var AttachmentPostUploadMiddlewareExecutor = class extends MiddlewareExecutor {
2641
2848
  super();
2642
2849
  this.use([
2643
2850
  createUploadErrorHandlerMiddleware(composer),
2644
- createPostUploadAttachmentEnrichmentMiddleware()
2851
+ createPostUploadAttachmentEnrichmentMiddleware(composer)
2645
2852
  ]);
2646
2853
  }
2647
2854
  };
@@ -3089,7 +3296,7 @@ function mergeWithDiff(target, source, customizer) {
3089
3296
  }
3090
3297
 
3091
3298
  // src/messageComposer/attachmentManager.ts
3092
- var initState = ({
3299
+ var initState2 = ({
3093
3300
  message
3094
3301
  }) => ({
3095
3302
  attachments: (message?.attachments ?? [])?.filter(({ og_scrape_url }) => !og_scrape_url).map((att) => {
@@ -3117,13 +3324,14 @@ var _AttachmentManager = class _AttachmentManager {
3117
3324
  ...attachment,
3118
3325
  localMetadata: {
3119
3326
  ...attachment.localMetadata,
3327
+ uploadConfirmationPending: false,
3120
3328
  uploadProgress: void 0,
3121
3329
  uploadState: "failed"
3122
3330
  }
3123
3331
  };
3124
3332
  };
3125
3333
  this.initState = ({ message } = {}) => {
3126
- this.state.next(initState({ message }));
3334
+ this.state.next(initState2({ message }));
3127
3335
  };
3128
3336
  this.getSnapshot = () => {
3129
3337
  const state = this.state.getLatestValue();
@@ -3200,8 +3408,22 @@ var _AttachmentManager = class _AttachmentManager {
3200
3408
  this.state.partialNext({ attachments });
3201
3409
  }
3202
3410
  };
3411
+ /**
3412
+ * Releases a local preview URL the SDK minted, if it minted one.
3413
+ *
3414
+ * Only `blob:` URLs are ours to revoke: `toLocalUploadAttachment` puts a
3415
+ * `URL.createObjectURL` result in `previewUri` for a `File`, but for a React Native
3416
+ * `FileReference` it stores the caller's own native file URI, which must be left alone.
3417
+ */
3418
+ this.revokePreviewUri = (attachment) => {
3419
+ const previewUri = attachment.localMetadata?.previewUri;
3420
+ if (previewUri?.startsWith("blob:")) URL.revokeObjectURL?.(previewUri);
3421
+ };
3203
3422
  this.removeAttachments = (localAttachmentIds) => {
3204
3423
  if (!localAttachmentIds.length) return;
3424
+ const removedAttachments = this.attachments.filter(
3425
+ (attachment) => localAttachmentIds.includes(attachment.localMetadata?.id)
3426
+ );
3205
3427
  this.state.partialNext({
3206
3428
  attachments: this.attachments.filter(
3207
3429
  (attachment) => !localAttachmentIds.includes(attachment.localMetadata?.id)
@@ -3210,6 +3432,7 @@ var _AttachmentManager = class _AttachmentManager {
3210
3432
  for (const id of localAttachmentIds) {
3211
3433
  this.client.uploadManager.deleteUploadRecord(id);
3212
3434
  }
3435
+ removedAttachments.forEach(this.revokePreviewUri);
3213
3436
  };
3214
3437
  this.getUploadConfigCheck = async (fileLike) => {
3215
3438
  const client = this.channel.getClient();
@@ -3361,21 +3584,27 @@ var _AttachmentManager = class _AttachmentManager {
3361
3584
  localMetadata: {
3362
3585
  ...attachment.localMetadata,
3363
3586
  uploadState: "failed",
3364
- uploadProgress: void 0
3587
+ uploadProgress: void 0,
3588
+ // `false`, not `undefined`: these updates go through `updateAttachment`, which merges
3589
+ // via `mergeWith` — and that skips `undefined` source values, so `undefined` would
3590
+ // leave a stale `true` in place.
3591
+ uploadConfirmationPending: false
3365
3592
  }
3366
3593
  };
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
- });
3594
+ if (!isUploadCancellation(error)) {
3595
+ this.client.notifications.addError({
3596
+ message: "Error uploading attachment",
3597
+ origin: {
3598
+ emitter: "AttachmentManager",
3599
+ context: { attachment, failedAttachment }
3600
+ },
3601
+ options: {
3602
+ type: "api:attachment:upload:failed",
3603
+ metadata: { reason },
3604
+ originalError: error instanceof Error ? error : void 0
3605
+ }
3606
+ });
3607
+ }
3379
3608
  this.updateAttachment(failedAttachment);
3380
3609
  return failedAttachment;
3381
3610
  }
@@ -3388,7 +3617,9 @@ var _AttachmentManager = class _AttachmentManager {
3388
3617
  localMetadata: {
3389
3618
  ...attachment.localMetadata,
3390
3619
  uploadState: "finished",
3391
- uploadProgress: void 0
3620
+ uploadProgress: void 0,
3621
+ // See the note in the failure branch: `undefined` cannot clear through `mergeWith`.
3622
+ uploadConfirmationPending: false
3392
3623
  }
3393
3624
  };
3394
3625
  const previewUri = uploadedAttachment.localMetadata.previewUri;
@@ -3437,7 +3668,9 @@ var _AttachmentManager = class _AttachmentManager {
3437
3668
  localMetadata: {
3438
3669
  ...attachment.localMetadata,
3439
3670
  uploadState: error ? "failed" : "finished",
3440
- uploadProgress: void 0
3671
+ uploadProgress: void 0,
3672
+ // See the note in `uploadAttachment`: `undefined` cannot clear through `mergeWith`.
3673
+ uploadConfirmationPending: false
3441
3674
  }
3442
3675
  },
3443
3676
  error,
@@ -3462,7 +3695,7 @@ var _AttachmentManager = class _AttachmentManager {
3462
3695
  );
3463
3696
  };
3464
3697
  this.composer = composer;
3465
- this.state = new StateStore(initState({ message }));
3698
+ this.state = new StateStore(initState2({ message }));
3466
3699
  this.attachmentsByIdGetterCache = { attachmentsById: {}, attachments: [] };
3467
3700
  this.preUploadMiddlewareExecutor = new AttachmentPreUploadMiddlewareExecutor({
3468
3701
  composer
@@ -3567,7 +3800,8 @@ var _AttachmentManager = class _AttachmentManager {
3567
3800
  localMetadata: {
3568
3801
  ...attachment.localMetadata,
3569
3802
  uploadState: "uploading",
3570
- uploadProgress: this.config.trackUploadProgress ? 0 : void 0
3803
+ uploadProgress: this.config.trackUploadProgress ? 0 : void 0,
3804
+ uploadConfirmationPending: false
3571
3805
  }
3572
3806
  }
3573
3807
  ]);
@@ -3580,7 +3814,8 @@ var _AttachmentManager = class _AttachmentManager {
3580
3814
  localMetadata: {
3581
3815
  ...attachment.localMetadata,
3582
3816
  uploadState: "uploading",
3583
- uploadProgress: nextUpload.uploadProgress
3817
+ uploadProgress: nextUpload.uploadProgress,
3818
+ uploadConfirmationPending: nextUpload.uploadConfirmationPending
3584
3819
  }
3585
3820
  });
3586
3821
  }
@@ -3634,6 +3869,50 @@ var localAttachmentToAttachment = (localAttachment) => {
3634
3869
  const { localMetadata, ...attachment } = localAttachment;
3635
3870
  return attachment;
3636
3871
  };
3872
+ var composeWithPendingUploads = ({
3873
+ composer,
3874
+ state
3875
+ }) => {
3876
+ const composerIsKeptAsDraft = !!composer.pollId;
3877
+ const relevantAttachments = composer.attachmentManager.attachments.filter(
3878
+ (attachment) => isFinishedUpload(attachment) || !composerIsKeptAsDraft && isPendingUpload(attachment)
3879
+ );
3880
+ const localAttachments = (state.localMessage.attachments ?? []).concat(
3881
+ relevantAttachments.map(
3882
+ (attachment) => isPendingUpload(attachment) ? attachment : localAttachmentToAttachment(attachment)
3883
+ )
3884
+ );
3885
+ const messageAttachments = (state.message.attachments ?? []).concat(
3886
+ relevantAttachments.filter(isFinishedUpload).map(localAttachmentToAttachment)
3887
+ );
3888
+ if (!localAttachments.length && !messageAttachments.length) return state;
3889
+ return {
3890
+ ...state,
3891
+ localMessage: localAttachments.length ? {
3892
+ ...state.localMessage,
3893
+ attachments: localAttachments
3894
+ } : state.localMessage,
3895
+ message: messageAttachments.length ? {
3896
+ ...state.message,
3897
+ attachments: messageAttachments
3898
+ } : state.message
3899
+ };
3900
+ };
3901
+ var createSendWithPendingUploadsAttachmentsMiddleware = (composer) => ({
3902
+ allowsPendingUploads: true,
3903
+ id: "stream-io/message-composer-middleware/attachments",
3904
+ handlers: {
3905
+ compose: ({
3906
+ state,
3907
+ next,
3908
+ forward
3909
+ }) => {
3910
+ const { attachmentManager } = composer;
3911
+ if (!attachmentManager) return forward();
3912
+ return next(composeWithPendingUploads({ composer, state }));
3913
+ }
3914
+ }
3915
+ });
3637
3916
  var createAttachmentsCompositionMiddleware = (composer) => ({
3638
3917
  id: "stream-io/message-composer-middleware/attachments",
3639
3918
  handlers: {
@@ -3850,7 +4129,7 @@ var getInitialMentions = (message) => {
3850
4129
  };
3851
4130
  var isSameMentionEntity = (first, second) => first.id === second.id && first.mentionType === second.mentionType;
3852
4131
  var getMentionsFromState = (state) => state.mentions ?? state.mentionedUsers.map(userResponseToMentionEntity);
3853
- var initState2 = ({
4132
+ var initState3 = ({
3854
4133
  composer,
3855
4134
  message
3856
4135
  }) => {
@@ -3879,7 +4158,7 @@ var initState2 = ({
3879
4158
  var TextComposer = class {
3880
4159
  constructor({ composer, message }) {
3881
4160
  this.initState = ({ message } = {}) => {
3882
- this.state.next(initState2({ composer: this.composer, message }));
4161
+ this.state.next(initState3({ composer: this.composer, message }));
3883
4162
  };
3884
4163
  this.getSnapshot = (state = this.state.getLatestValue()) => state;
3885
4164
  this.restoreSnapshot = (snapshot) => {
@@ -4072,7 +4351,7 @@ var TextComposer = class {
4072
4351
  this.commitState(output.state);
4073
4352
  };
4074
4353
  this.composer = composer;
4075
- this.state = new StateStore(initState2({ composer, message }));
4354
+ this.state = new StateStore(initState3({ composer, message }));
4076
4355
  this.middlewareExecutor = new TextComposerMiddlewareExecutor({ composer });
4077
4356
  }
4078
4357
  get channel() {
@@ -5774,6 +6053,13 @@ var MessageComposerMiddlewareExecutor = class extends MiddlewareExecutor {
5774
6053
  createCompositionDataCleanupMiddleware(composer)
5775
6054
  ]);
5776
6055
  }
6056
+ /**
6057
+ * Narrows the base accessor to the composition middleware type, so callers can read the
6058
+ * declarations it carries (`allowsPendingUploads`) without casting.
6059
+ */
6060
+ get installedMiddleware() {
6061
+ return super.installedMiddleware;
6062
+ }
5777
6063
  };
5778
6064
  var MessageDraftComposerMiddlewareExecutor = class extends MiddlewareExecutor {
5779
6065
  constructor({ composer }) {
@@ -6888,7 +7174,7 @@ var DEFAULT_COMPOSER_CONFIG = {
6888
7174
  };
6889
7175
 
6890
7176
  // src/messageComposer/CustomDataManager.ts
6891
- var initState3 = (options) => {
7177
+ var initState4 = (options) => {
6892
7178
  if (!options)
6893
7179
  return { message: {}, custom: {} };
6894
7180
  return { message: {}, custom: {} };
@@ -6897,14 +7183,14 @@ var CustomDataManager = class {
6897
7183
  constructor({ composer, message }) {
6898
7184
  this.isMessageDataEqual = (nextState, previousState) => JSON.stringify(nextState.message) === JSON.stringify(previousState?.message);
6899
7185
  this.initState = ({ message } = {}) => {
6900
- this.state.next(initState3({ composer: this.composer, message }));
7186
+ this.state.next(initState4({ composer: this.composer, message }));
6901
7187
  };
6902
7188
  this.getSnapshot = () => this.state.getLatestValue();
6903
7189
  this.restoreSnapshot = (snapshot) => {
6904
7190
  this.state.next(snapshot);
6905
7191
  };
6906
7192
  this.composer = composer;
6907
- this.state = new StateStore(initState3({ composer, message }));
7193
+ this.state = new StateStore(initState4({ composer, message }));
6908
7194
  }
6909
7195
  get customMessageData() {
6910
7196
  return this.state.getLatestValue().message;
@@ -6940,7 +7226,7 @@ var LinkPreviewStatus = /* @__PURE__ */ ((LinkPreviewStatus2) => {
6940
7226
  return LinkPreviewStatus2;
6941
7227
  })(LinkPreviewStatus || {});
6942
7228
  var linkPreviewArrayToMap = (linkPreviews) => new Map(linkPreviews.map((linkPreview) => [linkPreview.og_scrape_url, linkPreview]));
6943
- var initState4 = ({
7229
+ var initState5 = ({
6944
7230
  message
6945
7231
  }) => message ? {
6946
7232
  previews: message.attachments?.reduce((acc, attachment) => {
@@ -6959,7 +7245,7 @@ var _LinkPreviewsManager = class _LinkPreviewsManager {
6959
7245
  this.shouldDiscardEnrichQueries = false;
6960
7246
  this.initState = ({ message } = {}) => {
6961
7247
  this.cancelURLEnrichment();
6962
- this.state.next(initState4({ message: this.enabled ? message : void 0 }));
7248
+ this.state.next(initState5({ message: this.enabled ? message : void 0 }));
6963
7249
  };
6964
7250
  this.getSnapshot = () => this.state.getLatestValue();
6965
7251
  this.restoreSnapshot = (snapshot) => {
@@ -7062,7 +7348,7 @@ var _LinkPreviewsManager = class _LinkPreviewsManager {
7062
7348
  };
7063
7349
  this.composer = composer;
7064
7350
  this.state = new StateStore(
7065
- initState4({ message: this.enabled ? message : void 0 })
7351
+ initState5({ message: this.enabled ? message : void 0 })
7066
7352
  );
7067
7353
  this.findAndEnrichUrls = debounce(
7068
7354
  this._findAndEnrichUrls.bind(this),
@@ -7150,7 +7436,7 @@ var LinkPreviewsManager = _LinkPreviewsManager;
7150
7436
 
7151
7437
  // src/messageComposer/LocationComposer.ts
7152
7438
  var MIN_LIVE_LOCATION_SHARE_DURATION = 60 * 1e3;
7153
- var initState5 = ({
7439
+ var initState6 = ({
7154
7440
  message
7155
7441
  }) => ({
7156
7442
  location: message?.shared_location ?? null
@@ -7158,7 +7444,7 @@ var initState5 = ({
7158
7444
  var LocationComposer = class {
7159
7445
  constructor({ composer, message }) {
7160
7446
  this.initState = ({ message } = {}) => {
7161
- this.state.next(initState5({ message }));
7447
+ this.state.next(initState6({ message }));
7162
7448
  };
7163
7449
  this.getSnapshot = () => this.state.getLatestValue();
7164
7450
  this.restoreSnapshot = (snapshot) => {
@@ -7176,7 +7462,7 @@ var LocationComposer = class {
7176
7462
  });
7177
7463
  };
7178
7464
  this.composer = composer;
7179
- this.state = new StateStore(initState5({ message }));
7465
+ this.state = new StateStore(initState6({ message }));
7180
7466
  this._deviceId = this.config.getDeviceId();
7181
7467
  }
7182
7468
  get config() {
@@ -7897,7 +8183,7 @@ var initEditingAuditState = (composition) => {
7897
8183
  }
7898
8184
  };
7899
8185
  };
7900
- var initState6 = (composition) => {
8186
+ var initState7 = (composition) => {
7901
8187
  if (!composition) {
7902
8188
  return {
7903
8189
  draftId: null,
@@ -7984,7 +8270,7 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
7984
8270
  this.textComposer.initState({ message });
7985
8271
  this.pollComposer.initState();
7986
8272
  this.customDataManager.initState({ message });
7987
- this.state.next(initState6(composition));
8273
+ this.state.next(initState7(composition));
7988
8274
  };
7989
8275
  this.initStateFromChannelResponse = (channelApiResponse) => {
7990
8276
  if (this.channel.cid !== channelApiResponse.channel.cid) {
@@ -8450,7 +8736,7 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
8450
8736
  this.editingAuditState = new StateStore(
8451
8737
  this.initEditingAuditState(composition)
8452
8738
  );
8453
- this.state = new StateStore(initState6(composition));
8739
+ this.state = new StateStore(initState7(composition));
8454
8740
  this.compositionMiddlewareExecutor = new MessageComposerMiddlewareExecutor({
8455
8741
  composer: this
8456
8742
  });
@@ -8529,8 +8815,32 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
8529
8815
  const currentCommand = this.textComposer.command;
8530
8816
  return !currentCommand || this.validateCommandSendability(currentCommand).ready;
8531
8817
  }
8818
+ /**
8819
+ * Whether the installed composition middleware composes messages whose uploads are still in
8820
+ * flight — see `MessageCompositionMiddleware.allowsPendingUploads`.
8821
+ *
8822
+ * Derived from the chain rather than from config, so there is exactly one switch: the UI SDK
8823
+ * that implements the rest of the flow installs the middleware, and everything that has to
8824
+ * follow from that (starting with {@link hasSendableData}) follows on its own.
8825
+ *
8826
+ * **Temporary API.** In v10 sending with pending uploads becomes part of the composer
8827
+ * configuration (`MessageComposerConfig`, set through `updateConfig`), and this getter,
8828
+ * together with the middleware declaration it reads, is replaced by that config field.
8829
+ */
8830
+ get allowsPendingUploads() {
8831
+ return this.compositionMiddlewareExecutor.installedMiddleware.some(
8832
+ (middleware) => middleware.allowsPendingUploads
8833
+ );
8834
+ }
8532
8835
  get hasSendableData() {
8533
- return this.isCommandSendable && !!(!this.attachmentManager.uploadsInProgressCount && (!this.textComposer.textIsEmpty || this.attachmentManager.successfulUploadsCount > 0) || this.pollId || !!this.locationComposer.validLocation);
8836
+ if (!this.isCommandSendable) return false;
8837
+ if (this.allowsPendingUploads) {
8838
+ const hasSendableAttachment = this.attachmentManager.attachments.some(
8839
+ (attachment) => isFinishedUpload(attachment) || isPendingUpload(attachment)
8840
+ );
8841
+ return !!(!this.textComposer.textIsEmpty || hasSendableAttachment || this.pollId || this.locationComposer.validLocation);
8842
+ }
8843
+ return !!(!this.attachmentManager.uploadsInProgressCount && (!this.textComposer.textIsEmpty || this.attachmentManager.successfulUploadsCount > 0) || this.pollId || !!this.locationComposer.validLocation);
8534
8844
  }
8535
8845
  get compositionIsEmpty() {
8536
8846
  return !this.quotedMessage && this.contentIsEmpty;
@@ -8669,7 +8979,6 @@ var MessageDeliveryReporter = class _MessageDeliveryReporter {
8669
8979
  * @param options
8670
8980
  */
8671
8981
  this.markRead = async (collection, options) => {
8672
- if (!userHasReadReceipts(this.client)) return null;
8673
8982
  let result = null;
8674
8983
  if (isChannel(collection)) {
8675
8984
  result = await collection.markAsReadRequest(options);
@@ -8679,7 +8988,7 @@ var MessageDeliveryReporter = class _MessageDeliveryReporter {
8679
8988
  thread_id: collection.id
8680
8989
  });
8681
8990
  }
8682
- this.removeCandidateFor(collection);
8991
+ if (result) this.removeCandidateFor(collection);
8683
8992
  return result;
8684
8993
  };
8685
8994
  /**
@@ -9064,18 +9373,14 @@ var Channel = class {
9064
9373
  return await this.query(defaultOptions, "latest");
9065
9374
  };
9066
9375
  this._callChannelListeners = (event) => {
9067
- const channel = this;
9068
- const listeners = [];
9069
- if (channel.listeners.all) {
9070
- listeners.push(...channel.listeners.all);
9071
- }
9072
- if (channel.listeners[event.type]) {
9073
- listeners.push(...channel.listeners[event.type]);
9074
- }
9376
+ const listeners = [
9377
+ ...this.listeners.all ?? [],
9378
+ ...this.listeners[event.type] ?? []
9379
+ ];
9380
+ const { logger } = this._client;
9075
9381
  for (const listener of listeners) {
9076
- if (typeof listener !== "string") {
9077
- listener(event);
9078
- }
9382
+ if (typeof listener === "string") continue;
9383
+ invokeEventListener(listener, event, logger);
9079
9384
  }
9080
9385
  };
9081
9386
  /**
@@ -9157,14 +9462,16 @@ var Channel = class {
9157
9462
  *
9158
9463
  * @return {Promise<SendMessageAPIResponse>} The Server Response
9159
9464
  */
9160
- async _sendMessage(message, options) {
9161
- return await this.getClient().post(
9162
- this._channelURL() + "/message",
9163
- {
9164
- message,
9165
- ...options
9166
- }
9167
- );
9465
+ async _sendMessage(rawMessage, options) {
9466
+ const client = this.getClient();
9467
+ const message = sanitizeOutgoingAttachments({
9468
+ client,
9469
+ message: rawMessage
9470
+ });
9471
+ return await client.post(this._channelURL() + "/message", {
9472
+ message,
9473
+ ...options
9474
+ });
9168
9475
  }
9169
9476
  async sendMessage(message, options) {
9170
9477
  try {
@@ -9987,14 +10294,15 @@ var Channel = class {
9987
10294
  return await this.getClient().messageDeliveryReporter.markRead(this, data);
9988
10295
  }
9989
10296
  /**
9990
- * markReadRequest - Send the mark read event for this user, only works if the `read_events` setting is enabled
10297
+ * markReadRequest - Send the mark read event for this user, only works if the `read_events` setting is
10298
+ * enabled and the user holds the `read-events` capability on this channel
9991
10299
  *
9992
10300
  * @param {MarkReadOptions} data
9993
10301
  * @return {Promise<EventAPIResponse | null>} Description
9994
10302
  */
9995
10303
  async markAsReadRequest(data = {}) {
9996
10304
  this._checkInitialized();
9997
- if (!this.getConfig()?.read_events && !this.getClient()._isUsingServerAuth()) {
10305
+ if (!this.getClient()._isUsingServerAuth() && (!this.getConfig()?.read_events || !channelHasReadEvents(this))) {
9998
10306
  return null;
9999
10307
  }
10000
10308
  return await this.getClient().post(this._channelURL() + "/read", {
@@ -10002,14 +10310,15 @@ var Channel = class {
10002
10310
  });
10003
10311
  }
10004
10312
  /**
10005
- * markUnread - Mark the channel as unread from messageID, only works if the `read_events` setting is enabled
10313
+ * markUnread - Mark the channel as unread from messageID, only works if the `read_events` setting is
10314
+ * enabled and the user holds the `read-events` capability on this channel
10006
10315
  *
10007
10316
  * @param {MarkUnreadOptions} data
10008
10317
  * @return {APIResponse} An API response
10009
10318
  */
10010
10319
  async markUnread(data) {
10011
10320
  this._checkInitialized();
10012
- if (!this.getConfig()?.read_events && !this.getClient()._isUsingServerAuth()) {
10321
+ if (!this.getClient()._isUsingServerAuth() && (!this.getConfig()?.read_events || !channelHasReadEvents(this))) {
10013
10322
  return Promise.resolve(null);
10014
10323
  }
10015
10324
  return await this.getClient().post(this._channelURL() + "/unread", {
@@ -10615,6 +10924,7 @@ var Channel = class {
10615
10924
  // delivery-report network sync below is skipped for it.
10616
10925
  case "message.read_locally":
10617
10926
  case "message.read":
10927
+ if (event.thread) break;
10618
10928
  if (event.user?.id && event.created_at) {
10619
10929
  const previousReadState = channelState.read[event.user.id];
10620
10930
  channelState.read[event.user.id] = {
@@ -11064,7 +11374,7 @@ var ClientState = class {
11064
11374
  import WebSocket from "isomorphic-ws";
11065
11375
 
11066
11376
  // src/insights.ts
11067
- import axios from "axios";
11377
+ import axios2 from "axios";
11068
11378
  var InsightMetrics = class {
11069
11379
  constructor() {
11070
11380
  this.connectionStartTimestamp = null;
@@ -11077,7 +11387,7 @@ var postInsights = async (insightType, insights) => {
11077
11387
  const maxAttempts = 3;
11078
11388
  for (let i = 0; i < maxAttempts; i++) {
11079
11389
  try {
11080
- await axios.post(
11390
+ await axios2.post(
11081
11391
  `https://chat-insights.getstream.io/insights/${insightType}`,
11082
11392
  insights
11083
11393
  );
@@ -11585,131 +11895,6 @@ var StableWSConnection = class {
11585
11895
  }
11586
11896
  };
11587
11897
 
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
11898
  // src/signing.ts
11714
11899
  var import_jsonwebtoken = __toESM(require_jsonwebtoken());
11715
11900
  var import_crypto = __toESM(require_crypto());
@@ -11968,7 +12153,7 @@ var TokenManager = class {
11968
12153
  };
11969
12154
 
11970
12155
  // src/connection_fallback.ts
11971
- import axios2 from "axios";
12156
+ import axios3 from "axios";
11972
12157
  var WSConnectionFallback = class {
11973
12158
  constructor({ client }) {
11974
12159
  /** @private */
@@ -11987,7 +12172,7 @@ var WSConnectionFallback = class {
11987
12172
  /** @private */
11988
12173
  this._req = async (params, config, retry) => {
11989
12174
  if (!this.cancelToken && !params.close) {
11990
- this.cancelToken = axios2.CancelToken.source();
12175
+ this.cancelToken = axios3.CancelToken.source();
11991
12176
  }
11992
12177
  try {
11993
12178
  const res = await this.client.doAxiosRequest(
@@ -12023,7 +12208,7 @@ var WSConnectionFallback = class {
12023
12208
  }
12024
12209
  }
12025
12210
  } catch (error) {
12026
- if (axios2.isCancel(error)) {
12211
+ if (axios3.isCancel(error)) {
12027
12212
  this._log(`_poll() - axios canceled request`);
12028
12213
  return;
12029
12214
  }
@@ -14446,7 +14631,7 @@ var StreamChat = class _StreamChat {
14446
14631
  return this.handleResponse(response);
14447
14632
  } catch (e) {
14448
14633
  e.client_request_id = requestConfig.headers?.["x-client-request-id"];
14449
- if (axios3.isCancel(e)) throw e;
14634
+ if (axios4.isCancel(e)) throw e;
14450
14635
  this._logApiError(type, url, e);
14451
14636
  this.consecutiveFailures += 1;
14452
14637
  if (e.response) {
@@ -14595,16 +14780,12 @@ var StreamChat = class _StreamChat {
14595
14780
  }
14596
14781
  };
14597
14782
  this._callClientListeners = (event) => {
14598
- const client = this;
14599
- const listeners = [];
14600
- if (client.listeners.all) {
14601
- listeners.push(...client.listeners.all);
14602
- }
14603
- if (client.listeners[event.type]) {
14604
- listeners.push(...client.listeners[event.type]);
14605
- }
14783
+ const listeners = [
14784
+ ...this.listeners.all ?? [],
14785
+ ...this.listeners[event.type] ?? []
14786
+ ];
14606
14787
  for (const listener of listeners) {
14607
- listener(event);
14788
+ invokeEventListener(listener, event, this.logger);
14608
14789
  }
14609
14790
  };
14610
14791
  this.recoverState = async () => {
@@ -14758,7 +14939,7 @@ var StreamChat = class _StreamChat {
14758
14939
  return this.userAgent;
14759
14940
  }
14760
14941
  if (!this.cachedUserAgent) {
14761
- const version = "9.51.0";
14942
+ const version = "9.52.1";
14762
14943
  const { name: sdkName, version: sdkVersion } = this.sdkIdentifier ?? {};
14763
14944
  const { name: appName, version: appVersion } = this.appIdentifier ?? {};
14764
14945
  const { os, model: deviceModel } = this.deviceIdentifier ?? {};
@@ -14826,7 +15007,7 @@ var StreamChat = class _StreamChat {
14826
15007
  keepAliveMsecs: 3e3
14827
15008
  });
14828
15009
  }
14829
- this.axiosInstance = axios3.create(this.options);
15010
+ this.axiosInstance = axios4.create(this.options);
14830
15011
  this.setBaseURL(this.options.baseURL || "https://chat.stream-io-api.com");
14831
15012
  if (typeof process !== "undefined" && "env" in process && process.env.STREAM_LOCAL_TEST_RUN) {
14832
15013
  this.setBaseURL("http://localhost:3030");
@@ -14847,7 +15028,7 @@ var StreamChat = class _StreamChat {
14847
15028
  this.defaultWSTimeoutWithFallback = 6 * 1e3;
14848
15029
  this.defaultWSTimeout = 15 * 1e3;
14849
15030
  this.axiosInstance.defaults.paramsSerializer = axiosParamsSerializer;
14850
- this.logger = isFunction(inputOptions.logger) ? inputOptions.logger : () => null;
15031
+ this.logger = isFunction(inputOptions.logger) ? inputOptions.logger : noopLogger;
14851
15032
  this.recoverStateOnReconnect = this.options.recoverStateOnReconnect;
14852
15033
  this.threads = new ThreadManager({ client: this });
14853
15034
  this.polls = new PollManager({ client: this });
@@ -16178,6 +16359,11 @@ var StreamChat = class _StreamChat {
16178
16359
  }
16179
16360
  /**
16180
16361
  * unflagMessage - unflag a message
16362
+ *
16363
+ * @deprecated The `/moderation/unflag` endpoint is deprecated and is a no-op on
16364
+ * the backend - calling this resolves successfully but does not remove the flag.
16365
+ * Use `client.moderation.submitAction()` against the review queue item instead.
16366
+ *
16181
16367
  * @param {string} targetMessageID
16182
16368
  * @param {string} [options.user_id] currentUserID, only used with serverside auth
16183
16369
  * @returns {Promise<APIResponse>}
@@ -16190,6 +16376,11 @@ var StreamChat = class _StreamChat {
16190
16376
  }
16191
16377
  /**
16192
16378
  * unflagUser - unflag a user
16379
+ *
16380
+ * @deprecated The `/moderation/unflag` endpoint is deprecated and is a no-op on
16381
+ * the backend - calling this resolves successfully but does not remove the flag.
16382
+ * Use `client.moderation.submitAction()` against the review queue item instead.
16383
+ *
16193
16384
  * @param {string} targetID
16194
16385
  * @param {string} [options.user_id] currentUserID, only used with serverside auth
16195
16386
  * @returns {Promise<APIResponse>}
@@ -16474,7 +16665,10 @@ var StreamChat = class _StreamChat {
16474
16665
  if (!message.id) {
16475
16666
  throw Error("Please specify the message.id when calling updateMessage");
16476
16667
  }
16477
- const payload = toUpdatedMessagePayload(message);
16668
+ const payload = sanitizeOutgoingAttachments({
16669
+ client: this,
16670
+ message: toUpdatedMessagePayload(message)
16671
+ });
16478
16672
  if (typeof partialUserOrUserId === "string") {
16479
16673
  payload.user_id = partialUserOrUserId;
16480
16674
  } else if (typeof partialUserOrUserId?.id === "string") {
@@ -18947,6 +19141,7 @@ var AbstractOfflineDB = class {
18947
19141
  return this.handleMessageUpdatedEvent({ event, execute });
18948
19142
  }
18949
19143
  if (type === "message.read" || type === "notification.mark_read") {
19144
+ if (event.thread) return [];
18950
19145
  return this.handleRead({ event, unreadMessages: 0, execute });
18951
19146
  }
18952
19147
  if (type === "message.read_locally") {
@@ -19513,6 +19708,7 @@ export {
19513
19708
  createMessageComposerStateCompositionMiddleware,
19514
19709
  createPollComposerStateMiddleware,
19515
19710
  createPostUploadAttachmentEnrichmentMiddleware,
19711
+ createSendWithPendingUploadsAttachmentsMiddleware,
19516
19712
  createSharedLocationCompositionMiddleware,
19517
19713
  createTextComposerCompositionMiddleware,
19518
19714
  createTextComposerPreValidationMiddleware,
@@ -19541,6 +19737,7 @@ export {
19541
19737
  getTokenizedSuggestionDisplayName,
19542
19738
  getTriggerCharWithToken,
19543
19739
  gunzipPayload,
19740
+ hasPending,
19544
19741
  insertItemWithTrigger,
19545
19742
  isAudioAttachment,
19546
19743
  isBlobButNotFile,
@@ -19548,6 +19745,7 @@ export {
19548
19745
  isFileAttachment,
19549
19746
  isFileList,
19550
19747
  isFileReference,
19748
+ isFinishedUpload,
19551
19749
  isGiphyAttachment,
19552
19750
  isImageAttachment,
19553
19751
  isImageFile,
@@ -19560,9 +19758,11 @@ export {
19560
19758
  isLocalVoiceRecordingAttachment,
19561
19759
  isOwnUser,
19562
19760
  isPatch,
19761
+ isPendingUpload,
19563
19762
  isScrapedContent,
19564
19763
  isSharedLocationResponse,
19565
19764
  isTargetedOptionTextUpdate,
19765
+ isUploadCancellation,
19566
19766
  isUploadedAttachment,
19567
19767
  isVideoAttachment,
19568
19768
  isVoiceRecordingAttachment,
@@ -19582,11 +19782,14 @@ export {
19582
19782
  readFileAsArrayBuffer,
19583
19783
  removeDiacritics,
19584
19784
  replaceWordWithEntity,
19785
+ settled,
19585
19786
  stripCommandFromText,
19586
19787
  stripMentionTokens,
19587
19788
  textIsEmpty,
19588
19789
  timeLeftMs,
19589
19790
  verifyAndParseWebhook,
19590
- verifySignature
19791
+ verifySignature,
19792
+ withCancellation,
19793
+ withoutConcurrency
19591
19794
  };
19592
19795
  //# sourceMappingURL=index.mjs.map