stream-chat 9.13.0 → 9.15.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 (39) hide show
  1. package/dist/cjs/index.browser.cjs +427 -189
  2. package/dist/cjs/index.browser.cjs.map +4 -4
  3. package/dist/cjs/index.node.cjs +1647 -232
  4. package/dist/cjs/index.node.cjs.map +4 -4
  5. package/dist/esm/index.js +427 -189
  6. package/dist/esm/index.js.map +4 -4
  7. package/dist/types/messageComposer/attachmentManager.d.ts +11 -3
  8. package/dist/types/messageComposer/configuration/types.d.ts +1 -1
  9. package/dist/types/messageComposer/middleware/attachmentManager/index.d.ts +3 -0
  10. package/dist/types/messageComposer/middleware/attachmentManager/postUpload/AttachmentPostUploadMiddlewareExecutor.d.ts +5 -0
  11. package/dist/types/messageComposer/middleware/attachmentManager/postUpload/attachmentEnrichment.d.ts +2 -0
  12. package/dist/types/messageComposer/middleware/attachmentManager/postUpload/index.d.ts +3 -0
  13. package/dist/types/messageComposer/middleware/attachmentManager/postUpload/uploadErrorHandler.d.ts +3 -0
  14. package/dist/types/messageComposer/middleware/attachmentManager/preUpload/AttachmentPreUploadMiddlewareExecutor.d.ts +5 -0
  15. package/dist/types/messageComposer/middleware/attachmentManager/preUpload/blockedUploadNotification.d.ts +3 -0
  16. package/dist/types/messageComposer/middleware/attachmentManager/preUpload/index.d.ts +3 -0
  17. package/dist/types/messageComposer/middleware/attachmentManager/preUpload/serverUploadConfigCheck.d.ts +3 -0
  18. package/dist/types/messageComposer/middleware/attachmentManager/types.d.ts +20 -0
  19. package/dist/types/messageComposer/types.d.ts +1 -0
  20. package/dist/types/middleware.d.ts +4 -2
  21. package/dist/types/poll.d.ts +1 -1
  22. package/package.json +2 -2
  23. package/src/messageComposer/attachmentManager.ts +116 -25
  24. package/src/messageComposer/configuration/types.ts +3 -2
  25. package/src/messageComposer/messageComposer.ts +1 -1
  26. package/src/messageComposer/middleware/attachmentManager/index.ts +3 -0
  27. package/src/messageComposer/middleware/attachmentManager/postUpload/AttachmentPostUploadMiddlewareExecutor.ts +20 -0
  28. package/src/messageComposer/middleware/attachmentManager/postUpload/attachmentEnrichment.ts +43 -0
  29. package/src/messageComposer/middleware/attachmentManager/postUpload/index.ts +3 -0
  30. package/src/messageComposer/middleware/attachmentManager/postUpload/uploadErrorHandler.ts +39 -0
  31. package/src/messageComposer/middleware/attachmentManager/preUpload/AttachmentPreUploadMiddlewareExecutor.ts +20 -0
  32. package/src/messageComposer/middleware/attachmentManager/preUpload/blockedUploadNotification.ts +38 -0
  33. package/src/messageComposer/middleware/attachmentManager/preUpload/index.ts +3 -0
  34. package/src/messageComposer/middleware/attachmentManager/preUpload/serverUploadConfigCheck.ts +40 -0
  35. package/src/messageComposer/middleware/attachmentManager/types.ts +32 -0
  36. package/src/messageComposer/types.ts +6 -0
  37. package/src/middleware.ts +31 -10
  38. package/src/poll.ts +11 -14
  39. package/src/utils.ts +5 -1
@@ -140,7 +140,8 @@ var require_https = __commonJS({
140
140
  // node_modules/form-data/lib/browser.js
141
141
  var require_browser = __commonJS({
142
142
  "node_modules/form-data/lib/browser.js"(exports, module2) {
143
- module2.exports = typeof self == "object" ? self.FormData : window.FormData;
143
+ "use strict";
144
+ module2.exports = typeof self === "object" ? self.FormData : window.FormData;
144
145
  }
145
146
  });
146
147
 
@@ -3151,7 +3152,8 @@ var messagePaginationLinear = ({
3151
3152
  return newPagination;
3152
3153
  };
3153
3154
  var messageSetPagination = (params) => {
3154
- if (params.parentSet.messages.length < params.returnedPage.length) {
3155
+ const messagesFilteredLocally = params.returnedPage.filter(({ shadowed }) => shadowed);
3156
+ if (params.parentSet.messages.length + messagesFilteredLocally.length < params.returnedPage.length) {
3155
3157
  params.logger?.(
3156
3158
  "error",
3157
3159
  "Corrupted message set state: parent set size < returned page size"
@@ -4088,6 +4090,300 @@ var ensureIsLocalAttachment = (attachment) => {
4088
4090
  };
4089
4091
  };
4090
4092
 
4093
+ // src/messageComposer/middleware/attachmentManager/postUpload/attachmentEnrichment.ts
4094
+ var createPostUploadAttachmentEnrichmentMiddleware = () => ({
4095
+ id: "stream-io/attachment-manager-middleware/post-upload-enrichment",
4096
+ handlers: {
4097
+ postProcess: ({
4098
+ state,
4099
+ discard,
4100
+ forward,
4101
+ next
4102
+ }) => {
4103
+ const { attachment, error, response } = state;
4104
+ if (error) return forward();
4105
+ if (!attachment || !response) return discard();
4106
+ const enrichedAttachment = { ...attachment };
4107
+ if (isLocalImageAttachment(attachment)) {
4108
+ if (attachment.localMetadata.previewUri) {
4109
+ URL.revokeObjectURL(attachment.localMetadata.previewUri);
4110
+ delete enrichedAttachment.localMetadata.previewUri;
4111
+ }
4112
+ enrichedAttachment.image_url = response.file;
4113
+ } else {
4114
+ enrichedAttachment.asset_url = response.file;
4115
+ }
4116
+ if (response.thumb_url) {
4117
+ enrichedAttachment.thumb_url = response.thumb_url;
4118
+ }
4119
+ return next({
4120
+ ...state,
4121
+ attachment: enrichedAttachment
4122
+ });
4123
+ }
4124
+ }
4125
+ });
4126
+
4127
+ // src/utils/concurrency.ts
4128
+ var withoutConcurrency = createRunner(wrapWithContinuationTracking);
4129
+ var withCancellation = createRunner(wrapWithCancellation);
4130
+ var pendingPromises = /* @__PURE__ */ new Map();
4131
+ function createRunner(wrapper) {
4132
+ return function run(tag, cb) {
4133
+ const { cb: wrapped, onContinued } = wrapper(tag, cb);
4134
+ const pending = pendingPromises.get(tag);
4135
+ pending?.onContinued();
4136
+ const promise = pending ? pending.promise.then(wrapped, wrapped) : wrapped();
4137
+ pendingPromises.set(tag, { promise, onContinued });
4138
+ return promise;
4139
+ };
4140
+ }
4141
+ function wrapWithContinuationTracking(tag, cb) {
4142
+ let hasContinuation = false;
4143
+ const wrapped = () => cb().finally(() => {
4144
+ if (!hasContinuation) {
4145
+ pendingPromises.delete(tag);
4146
+ }
4147
+ });
4148
+ const onContinued = () => hasContinuation = true;
4149
+ return { cb: wrapped, onContinued };
4150
+ }
4151
+ function wrapWithCancellation(tag, cb) {
4152
+ const ac = new AbortController();
4153
+ const wrapped = () => {
4154
+ if (ac.signal.aborted) {
4155
+ return Promise.resolve("canceled");
4156
+ }
4157
+ return cb(ac.signal).finally(() => {
4158
+ if (!ac.signal.aborted) {
4159
+ pendingPromises.delete(tag);
4160
+ }
4161
+ });
4162
+ };
4163
+ const onContinued = () => ac.abort();
4164
+ return { cb: wrapped, onContinued };
4165
+ }
4166
+
4167
+ // src/middleware.ts
4168
+ var MiddlewareExecutor = class {
4169
+ constructor() {
4170
+ this.middleware = [];
4171
+ this.id = generateUUIDv4();
4172
+ }
4173
+ use(middleware) {
4174
+ this.middleware = this.middleware.concat(middleware);
4175
+ return this;
4176
+ }
4177
+ // todo: document how to re-arrange the order of middleware using replace
4178
+ replace(middleware) {
4179
+ const newMiddleware = [...this.middleware];
4180
+ middleware.forEach((upserted) => {
4181
+ const existingIndex = this.middleware.findIndex(
4182
+ (existing) => existing.id === upserted.id
4183
+ );
4184
+ if (existingIndex >= 0) {
4185
+ newMiddleware.splice(existingIndex, 1, upserted);
4186
+ } else {
4187
+ newMiddleware.push(upserted);
4188
+ }
4189
+ });
4190
+ this.middleware = newMiddleware;
4191
+ return this;
4192
+ }
4193
+ insert({
4194
+ middleware,
4195
+ position,
4196
+ unique
4197
+ }) {
4198
+ if (unique) {
4199
+ middleware.forEach((md) => {
4200
+ const existingMiddlewareIndex = this.middleware.findIndex((m) => m.id === md.id);
4201
+ if (existingMiddlewareIndex >= 0) {
4202
+ this.middleware.splice(existingMiddlewareIndex, 1);
4203
+ }
4204
+ });
4205
+ }
4206
+ const targetId = position.after || position.before;
4207
+ const targetIndex = this.middleware.findIndex((m) => m.id === targetId);
4208
+ const insertionIndex = position.after ? targetIndex + 1 : targetIndex;
4209
+ this.middleware.splice(insertionIndex, 0, ...middleware);
4210
+ return this;
4211
+ }
4212
+ setOrder(order) {
4213
+ this.middleware = order.map((id) => this.middleware.find((middleware) => middleware.id === id)).filter(Boolean);
4214
+ }
4215
+ remove(middlewareIds) {
4216
+ if (!middlewareIds && !middlewareIds.length) return;
4217
+ this.middleware = this.middleware.filter(
4218
+ (md) => typeof middlewareIds === "string" ? middlewareIds !== md.id : !middlewareIds.includes(md.id)
4219
+ );
4220
+ }
4221
+ async executeMiddlewareChain({
4222
+ eventName,
4223
+ initialValue,
4224
+ mode = "cancelable"
4225
+ }) {
4226
+ let index = -1;
4227
+ const execute = async (i, state, status) => {
4228
+ if (i <= index) {
4229
+ throw new Error("next() called multiple times");
4230
+ }
4231
+ index = i;
4232
+ const returnFromChain = i === this.middleware.length || status && ["complete", "discard"].includes(status);
4233
+ if (returnFromChain) return { state, status };
4234
+ const middleware = this.middleware[i];
4235
+ const handler = middleware.handlers[eventName];
4236
+ if (!handler) {
4237
+ return execute(i + 1, state, status);
4238
+ }
4239
+ const next = (adjustedState) => execute(i + 1, adjustedState);
4240
+ const complete = (adjustedState) => execute(i + 1, adjustedState, "complete");
4241
+ const discard = () => execute(i + 1, state, "discard");
4242
+ const forward = () => execute(i + 1, state);
4243
+ return await handler({
4244
+ state,
4245
+ next,
4246
+ complete,
4247
+ discard,
4248
+ forward
4249
+ });
4250
+ };
4251
+ const result = mode === "cancelable" ? await withCancellation(
4252
+ `middleware-execution-${this.id}-${eventName}`,
4253
+ async (abortSignal) => {
4254
+ const result2 = await execute(0, initialValue);
4255
+ if (abortSignal.aborted) {
4256
+ return "canceled";
4257
+ }
4258
+ return result2;
4259
+ }
4260
+ ) : await execute(0, initialValue);
4261
+ return result === "canceled" ? { state: initialValue, status: "discard" } : result;
4262
+ }
4263
+ async execute({
4264
+ eventName,
4265
+ initialValue: initialState,
4266
+ mode
4267
+ }) {
4268
+ return await this.executeMiddlewareChain({
4269
+ eventName,
4270
+ initialValue: initialState,
4271
+ mode
4272
+ });
4273
+ }
4274
+ };
4275
+
4276
+ // src/messageComposer/middleware/attachmentManager/postUpload/uploadErrorHandler.ts
4277
+ var createUploadErrorHandlerMiddleware = (composer) => ({
4278
+ id: "stream-io/attachment-manager-middleware/upload-error",
4279
+ handlers: {
4280
+ postProcess: ({
4281
+ state,
4282
+ discard,
4283
+ forward
4284
+ }) => {
4285
+ const { attachment, error } = state;
4286
+ if (!error) return forward();
4287
+ if (!attachment) return discard();
4288
+ const reason = error instanceof Error ? error.message : "unknown error";
4289
+ composer.client.notifications.addError({
4290
+ message: "Error uploading attachment",
4291
+ origin: {
4292
+ emitter: "AttachmentManager",
4293
+ context: { attachment }
4294
+ },
4295
+ options: {
4296
+ type: "api:attachment:upload:failed",
4297
+ metadata: { reason },
4298
+ originalError: error
4299
+ }
4300
+ });
4301
+ return forward();
4302
+ }
4303
+ }
4304
+ });
4305
+
4306
+ // src/messageComposer/middleware/attachmentManager/postUpload/AttachmentPostUploadMiddlewareExecutor.ts
4307
+ var AttachmentPostUploadMiddlewareExecutor = class extends MiddlewareExecutor {
4308
+ constructor({ composer }) {
4309
+ super();
4310
+ this.use([
4311
+ createUploadErrorHandlerMiddleware(composer),
4312
+ createPostUploadAttachmentEnrichmentMiddleware()
4313
+ ]);
4314
+ }
4315
+ };
4316
+
4317
+ // src/messageComposer/middleware/attachmentManager/preUpload/serverUploadConfigCheck.ts
4318
+ var createUploadConfigCheckMiddleware = (composer) => ({
4319
+ id: "stream-io/attachment-manager-middleware/file-upload-config-check",
4320
+ handlers: {
4321
+ prepare: async ({
4322
+ state,
4323
+ next,
4324
+ discard
4325
+ }) => {
4326
+ const { attachmentManager } = composer;
4327
+ if (!attachmentManager || !state.attachment) return discard();
4328
+ const uploadPermissionCheck = await attachmentManager.getUploadConfigCheck(
4329
+ state.attachment.localMetadata.file
4330
+ );
4331
+ const attachment = {
4332
+ ...state.attachment,
4333
+ localMetadata: {
4334
+ ...state.attachment.localMetadata,
4335
+ uploadPermissionCheck,
4336
+ uploadState: uploadPermissionCheck.uploadBlocked ? "blocked" : "pending"
4337
+ }
4338
+ };
4339
+ return next({
4340
+ ...state,
4341
+ attachment
4342
+ });
4343
+ }
4344
+ }
4345
+ });
4346
+
4347
+ // src/messageComposer/middleware/attachmentManager/preUpload/blockedUploadNotification.ts
4348
+ var createBlockedAttachmentUploadNotificationMiddleware = (composer) => ({
4349
+ id: "stream-io/attachment-manager-middleware/blocked-upload-notification",
4350
+ handlers: {
4351
+ prepare: ({
4352
+ state: { attachment },
4353
+ forward
4354
+ }) => {
4355
+ if (!attachment) return forward();
4356
+ if (attachment.localMetadata.uploadPermissionCheck?.uploadBlocked) {
4357
+ composer.client.notifications.addError({
4358
+ message: `The attachment upload was blocked`,
4359
+ origin: {
4360
+ emitter: "AttachmentManager",
4361
+ context: { blockedAttachment: attachment }
4362
+ },
4363
+ options: {
4364
+ type: "validation:attachment:upload:blocked",
4365
+ metadata: {
4366
+ reason: attachment.localMetadata.uploadPermissionCheck?.reason
4367
+ }
4368
+ }
4369
+ });
4370
+ }
4371
+ return forward();
4372
+ }
4373
+ }
4374
+ });
4375
+
4376
+ // src/messageComposer/middleware/attachmentManager/preUpload/AttachmentPreUploadMiddlewareExecutor.ts
4377
+ var AttachmentPreUploadMiddlewareExecutor = class extends MiddlewareExecutor {
4378
+ constructor({ composer }) {
4379
+ super();
4380
+ this.use([
4381
+ createUploadConfigCheckMiddleware(composer),
4382
+ createBlockedAttachmentUploadNotificationMiddleware(composer)
4383
+ ]);
4384
+ }
4385
+ };
4386
+
4091
4387
  // src/store.ts
4092
4388
  var isPatch = (value) => typeof value === "function";
4093
4389
  var noop2 = () => {
@@ -4679,7 +4975,7 @@ var initState = ({
4679
4975
  };
4680
4976
  })
4681
4977
  });
4682
- var AttachmentManager = class {
4978
+ var _AttachmentManager = class _AttachmentManager {
4683
4979
  constructor({ composer, message }) {
4684
4980
  this.setCustomUploadFn = (doUploadRequest) => {
4685
4981
  this.composer.updateConfig({ attachments: { doUploadRequest } });
@@ -4786,42 +5082,18 @@ var AttachmentManager = class {
4786
5082
  }
4787
5083
  return { uploadBlocked: false };
4788
5084
  };
5085
+ // @deprecated use AttachmentManager.toLocalUploadAttachment(file)
4789
5086
  this.fileToLocalUploadAttachment = async (fileLike) => {
4790
- const file = isFileReference(fileLike) || isFile2(fileLike) ? fileLike : createFileFromBlobs({
4791
- blobsArray: [fileLike],
4792
- fileName: generateFileName(fileLike.type),
4793
- mimeType: fileLike.type
4794
- });
4795
- const uploadPermissionCheck = await this.getUploadConfigCheck(file);
4796
- const localAttachment = {
4797
- file_size: file.size,
4798
- mime_type: file.type,
4799
- localMetadata: {
4800
- file,
4801
- id: generateUUIDv4(),
4802
- uploadPermissionCheck,
4803
- uploadState: uploadPermissionCheck.uploadBlocked ? "blocked" : "pending"
4804
- },
4805
- type: getAttachmentTypeFromMimeType(file.type)
4806
- };
4807
- localAttachment[isImageFile(file) ? "fallback" : "title"] = file.name;
4808
- if (isImageFile(file)) {
4809
- localAttachment.localMetadata.previewUri = isFileReference(fileLike) ? fileLike.uri : URL.createObjectURL?.(fileLike);
4810
- if (isFileReference(fileLike) && fileLike.height && fileLike.width) {
4811
- localAttachment.original_height = fileLike.height;
4812
- localAttachment.original_width = fileLike.width;
4813
- }
4814
- }
4815
- if (isFileReference(fileLike) && fileLike.thumb_url) {
4816
- localAttachment.thumb_url = fileLike.thumb_url;
4817
- }
4818
- if (isFileReference(fileLike) && fileLike.duration) {
4819
- localAttachment.duration = fileLike.duration;
4820
- }
5087
+ const localAttachment = _AttachmentManager.toLocalUploadAttachment(fileLike);
5088
+ const uploadPermissionCheck = await this.getUploadConfigCheck(
5089
+ localAttachment.localMetadata.file
5090
+ );
5091
+ localAttachment.localMetadata.uploadPermissionCheck = uploadPermissionCheck;
5092
+ localAttachment.localMetadata.uploadState = uploadPermissionCheck.uploadBlocked ? "blocked" : "pending";
4821
5093
  return localAttachment;
4822
5094
  };
4823
5095
  this.ensureLocalUploadAttachment = async (attachment) => {
4824
- if (!attachment.localMetadata?.file || !attachment.localMetadata.id) {
5096
+ if (!attachment.localMetadata?.file) {
4825
5097
  this.client.notifications.addError({
4826
5098
  message: "File is required for upload attachment",
4827
5099
  origin: { emitter: "AttachmentManager", context: { attachment } },
@@ -4829,6 +5101,14 @@ var AttachmentManager = class {
4829
5101
  });
4830
5102
  return;
4831
5103
  }
5104
+ if (!attachment.localMetadata.id) {
5105
+ this.client.notifications.addError({
5106
+ message: "Local upload attachment missing local id",
5107
+ origin: { emitter: "AttachmentManager", context: { attachment } },
5108
+ options: { type: "validation:attachment:id:missing" }
5109
+ });
5110
+ return;
5111
+ }
4832
5112
  if (!this.fileUploadFilter(attachment)) return;
4833
5113
  const newAttachment = await this.fileToLocalUploadAttachment(
4834
5114
  attachment.localMetadata.file
@@ -4868,6 +5148,7 @@ var AttachmentManager = class {
4868
5148
  }
4869
5149
  return this.doDefaultUploadRequest(fileLike);
4870
5150
  };
5151
+ // @deprecated use attachmentManager.uploadFile(file)
4871
5152
  this.uploadAttachment = async (attachment) => {
4872
5153
  if (!this.isUploadEnabled) return;
4873
5154
  const localAttachment = await this.ensureLocalUploadAttachment(attachment);
@@ -4951,19 +5232,75 @@ var AttachmentManager = class {
4951
5232
  this.updateAttachment(uploadedAttachment);
4952
5233
  return uploadedAttachment;
4953
5234
  };
5235
+ this.uploadFile = async (file) => {
5236
+ const preUpload = await this.preUploadMiddlewareExecutor.execute({
5237
+ eventName: "prepare",
5238
+ initialValue: {
5239
+ attachment: _AttachmentManager.toLocalUploadAttachment(file)
5240
+ },
5241
+ mode: "concurrent"
5242
+ });
5243
+ let attachment = preUpload.state.attachment;
5244
+ if (preUpload.status === "discard") return attachment;
5245
+ if (!this.fileUploadFilter(attachment)) return attachment;
5246
+ if (attachment.localMetadata.uploadState === "blocked") {
5247
+ this.upsertAttachments([attachment]);
5248
+ return preUpload.state.attachment;
5249
+ }
5250
+ attachment = {
5251
+ ...attachment,
5252
+ localMetadata: {
5253
+ ...attachment.localMetadata,
5254
+ uploadState: "uploading"
5255
+ }
5256
+ };
5257
+ this.upsertAttachments([attachment]);
5258
+ let response;
5259
+ let error;
5260
+ try {
5261
+ response = await this.doUploadRequest(file);
5262
+ } catch (err) {
5263
+ error = err instanceof Error ? err : void 0;
5264
+ }
5265
+ const postUpload = await this.postUploadMiddlewareExecutor.execute({
5266
+ eventName: "postProcess",
5267
+ initialValue: {
5268
+ attachment: {
5269
+ ...attachment,
5270
+ localMetadata: {
5271
+ ...attachment.localMetadata,
5272
+ uploadState: error ? "failed" : "finished"
5273
+ }
5274
+ },
5275
+ error,
5276
+ response
5277
+ },
5278
+ mode: "concurrent"
5279
+ });
5280
+ attachment = postUpload.state.attachment;
5281
+ if (postUpload.status === "discard") {
5282
+ this.removeAttachments([attachment.localMetadata.id]);
5283
+ return attachment;
5284
+ }
5285
+ this.updateAttachment(attachment);
5286
+ return attachment;
5287
+ };
4954
5288
  this.uploadFiles = async (files) => {
4955
5289
  if (!this.isUploadEnabled) return;
4956
5290
  const iterableFiles = isFileList2(files) ? Array.from(files) : files;
4957
- const attachments = await Promise.all(
4958
- iterableFiles.map(this.fileToLocalUploadAttachment)
4959
- );
4960
- return Promise.all(
4961
- attachments.filter(this.fileUploadFilter).slice(0, this.availableUploadSlots).map(this.uploadAttachment)
5291
+ return await Promise.all(
5292
+ iterableFiles.slice(0, this.availableUploadSlots).map(this.uploadFile)
4962
5293
  );
4963
5294
  };
4964
5295
  this.composer = composer;
4965
5296
  this.state = new StateStore(initState({ message }));
4966
5297
  this.attachmentsByIdGetterCache = { attachmentsById: {}, attachments: [] };
5298
+ this.preUploadMiddlewareExecutor = new AttachmentPreUploadMiddlewareExecutor({
5299
+ composer
5300
+ });
5301
+ this.postUploadMiddlewareExecutor = new AttachmentPostUploadMiddlewareExecutor({
5302
+ composer
5303
+ });
4967
5304
  }
4968
5305
  get attachmentsById() {
4969
5306
  const { attachments } = this.state.getLatestValue();
@@ -4993,9 +5330,15 @@ var AttachmentManager = class {
4993
5330
  set acceptedFiles(acceptedFiles) {
4994
5331
  this.composer.updateConfig({ attachments: { acceptedFiles } });
4995
5332
  }
5333
+ /*
5334
+ @deprecated attachments can be filtered using injecting pre-upload middleware
5335
+ */
4996
5336
  get fileUploadFilter() {
4997
5337
  return this.config.fileUploadFilter;
4998
5338
  }
5339
+ /*
5340
+ @deprecated attachments can be filtered using injecting pre-upload middleware
5341
+ */
4999
5342
  set fileUploadFilter(fileUploadFilter) {
5000
5343
  this.composer.updateConfig({ attachments: { fileUploadFilter } });
5001
5344
  }
@@ -5042,6 +5385,39 @@ var AttachmentManager = class {
5042
5385
  );
5043
5386
  }
5044
5387
  };
5388
+ _AttachmentManager.toLocalUploadAttachment = (fileLike) => {
5389
+ const file = isFileReference(fileLike) || isFile2(fileLike) ? fileLike : createFileFromBlobs({
5390
+ blobsArray: [fileLike],
5391
+ fileName: generateFileName(fileLike.type),
5392
+ mimeType: fileLike.type
5393
+ });
5394
+ const localAttachment = {
5395
+ file_size: file.size,
5396
+ mime_type: file.type,
5397
+ localMetadata: {
5398
+ file,
5399
+ id: generateUUIDv4(),
5400
+ uploadState: "pending"
5401
+ },
5402
+ type: getAttachmentTypeFromMimeType(file.type)
5403
+ };
5404
+ localAttachment[isImageFile(file) ? "fallback" : "title"] = file.name;
5405
+ if (isImageFile(file)) {
5406
+ localAttachment.localMetadata.previewUri = isFileReference(fileLike) ? fileLike.uri : URL.createObjectURL?.(fileLike);
5407
+ if (isFileReference(fileLike) && fileLike.height && fileLike.width) {
5408
+ localAttachment.original_height = fileLike.height;
5409
+ localAttachment.original_width = fileLike.width;
5410
+ }
5411
+ }
5412
+ if (isFileReference(fileLike) && fileLike.thumb_url) {
5413
+ localAttachment.thumb_url = fileLike.thumb_url;
5414
+ }
5415
+ if (isFileReference(fileLike) && fileLike.duration) {
5416
+ localAttachment.duration = fileLike.duration;
5417
+ }
5418
+ return localAttachment;
5419
+ };
5420
+ var AttachmentManager = _AttachmentManager;
5045
5421
 
5046
5422
  // src/messageComposer/configuration/configuration.ts
5047
5423
  var import_linkifyjs = require("linkifyjs");
@@ -5381,146 +5757,6 @@ var LocationComposer = class {
5381
5757
  }
5382
5758
  };
5383
5759
 
5384
- // src/utils/concurrency.ts
5385
- var withoutConcurrency = createRunner(wrapWithContinuationTracking);
5386
- var withCancellation = createRunner(wrapWithCancellation);
5387
- var pendingPromises = /* @__PURE__ */ new Map();
5388
- function createRunner(wrapper) {
5389
- return function run(tag, cb) {
5390
- const { cb: wrapped, onContinued } = wrapper(tag, cb);
5391
- const pending = pendingPromises.get(tag);
5392
- pending?.onContinued();
5393
- const promise = pending ? pending.promise.then(wrapped, wrapped) : wrapped();
5394
- pendingPromises.set(tag, { promise, onContinued });
5395
- return promise;
5396
- };
5397
- }
5398
- function wrapWithContinuationTracking(tag, cb) {
5399
- let hasContinuation = false;
5400
- const wrapped = () => cb().finally(() => {
5401
- if (!hasContinuation) {
5402
- pendingPromises.delete(tag);
5403
- }
5404
- });
5405
- const onContinued = () => hasContinuation = true;
5406
- return { cb: wrapped, onContinued };
5407
- }
5408
- function wrapWithCancellation(tag, cb) {
5409
- const ac = new AbortController();
5410
- const wrapped = () => {
5411
- if (ac.signal.aborted) {
5412
- return Promise.resolve("canceled");
5413
- }
5414
- return cb(ac.signal).finally(() => {
5415
- if (!ac.signal.aborted) {
5416
- pendingPromises.delete(tag);
5417
- }
5418
- });
5419
- };
5420
- const onContinued = () => ac.abort();
5421
- return { cb: wrapped, onContinued };
5422
- }
5423
-
5424
- // src/middleware.ts
5425
- var MiddlewareExecutor = class {
5426
- constructor() {
5427
- this.middleware = [];
5428
- this.id = generateUUIDv4();
5429
- }
5430
- use(middleware) {
5431
- this.middleware = this.middleware.concat(middleware);
5432
- return this;
5433
- }
5434
- // todo: document how to re-arrange the order of middleware using replace
5435
- replace(middleware) {
5436
- const newMiddleware = [...this.middleware];
5437
- middleware.forEach((upserted) => {
5438
- const existingIndex = this.middleware.findIndex(
5439
- (existing) => existing.id === upserted.id
5440
- );
5441
- if (existingIndex >= 0) {
5442
- newMiddleware.splice(existingIndex, 1, upserted);
5443
- } else {
5444
- newMiddleware.push(upserted);
5445
- }
5446
- });
5447
- this.middleware = newMiddleware;
5448
- return this;
5449
- }
5450
- insert({
5451
- middleware,
5452
- position,
5453
- unique
5454
- }) {
5455
- if (unique) {
5456
- middleware.forEach((md) => {
5457
- const existingMiddlewareIndex = this.middleware.findIndex((m) => m.id === md.id);
5458
- if (existingMiddlewareIndex >= 0) {
5459
- this.middleware.splice(existingMiddlewareIndex, 1);
5460
- }
5461
- });
5462
- }
5463
- const targetId = position.after || position.before;
5464
- const targetIndex = this.middleware.findIndex((m) => m.id === targetId);
5465
- const insertionIndex = position.after ? targetIndex + 1 : targetIndex;
5466
- this.middleware.splice(insertionIndex, 0, ...middleware);
5467
- return this;
5468
- }
5469
- setOrder(order) {
5470
- this.middleware = order.map((id) => this.middleware.find((middleware) => middleware.id === id)).filter(Boolean);
5471
- }
5472
- async executeMiddlewareChain({
5473
- eventName,
5474
- initialValue
5475
- }) {
5476
- let index = -1;
5477
- const execute = async (i, state, status) => {
5478
- if (i <= index) {
5479
- throw new Error("next() called multiple times");
5480
- }
5481
- index = i;
5482
- const returnFromChain = i === this.middleware.length || status && ["complete", "discard"].includes(status);
5483
- if (returnFromChain) return { state, status };
5484
- const middleware = this.middleware[i];
5485
- const handler = middleware.handlers[eventName];
5486
- if (!handler) {
5487
- return execute(i + 1, state, status);
5488
- }
5489
- const next = (adjustedState) => execute(i + 1, adjustedState);
5490
- const complete = (adjustedState) => execute(i + 1, adjustedState, "complete");
5491
- const discard = () => execute(i + 1, state, "discard");
5492
- const forward = () => execute(i + 1, state);
5493
- return await handler({
5494
- state,
5495
- next,
5496
- complete,
5497
- discard,
5498
- forward
5499
- });
5500
- };
5501
- const result = await withCancellation(
5502
- `middleware-execution-${this.id}-${eventName}`,
5503
- async (abortSignal) => {
5504
- const result2 = await execute(0, initialValue);
5505
- if (abortSignal.aborted) {
5506
- return "canceled";
5507
- }
5508
- return result2;
5509
- }
5510
- );
5511
- return result === "canceled" ? { state: initialValue, status: "discard" } : result;
5512
- }
5513
- async execute({
5514
- eventName,
5515
- initialValue: initialState
5516
- }) {
5517
- return await this.executeMiddlewareChain({
5518
- eventName,
5519
- initialValue: initialState
5520
- });
5521
- }
5522
- };
5523
-
5524
5760
  // src/messageComposer/middleware/pollComposer/state.ts
5525
5761
  var VALID_MAX_VOTES_VALUE_REGEX = /^([2-9]|10)$/;
5526
5762
  var MAX_POLL_OPTIONS = 100;
@@ -8455,7 +8691,7 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
8455
8691
  id: this.id,
8456
8692
  mentioned_users: [],
8457
8693
  parent_id: this.threadId ?? void 0,
8458
- pinned_at: null,
8694
+ pinned_at: this.editedMessage?.pinned_at || null,
8459
8695
  reaction_groups: null,
8460
8696
  status: this.editedMessage ? this.editedMessage.status : "sending",
8461
8697
  text,
@@ -12283,15 +12519,17 @@ var Poll = class {
12283
12519
  const { max_votes_allowed, ownVotesByOptionId } = this.data;
12284
12520
  const reachedVoteLimit = max_votes_allowed && max_votes_allowed === Object.keys(ownVotesByOptionId).length;
12285
12521
  if (reachedVoteLimit) {
12286
- let oldestVote = Object.values(ownVotesByOptionId)[0];
12287
- Object.values(ownVotesByOptionId).slice(1).forEach((vote) => {
12288
- if (!oldestVote?.created_at || new Date(vote.created_at) < new Date(oldestVote.created_at)) {
12289
- oldestVote = vote;
12522
+ this.client.notifications.addInfo({
12523
+ message: "Reached the vote limit. Remove an existing vote first.",
12524
+ origin: {
12525
+ emitter: "Poll",
12526
+ context: { messageId, optionId }
12527
+ },
12528
+ options: {
12529
+ type: "validation:poll:castVote:limit"
12290
12530
  }
12291
12531
  });
12292
- if (oldestVote?.id) {
12293
- await this.removeVote(oldestVote.id, messageId);
12294
- }
12532
+ return;
12295
12533
  }
12296
12534
  return await this.client.castPollVote(messageId, this.id, {
12297
12535
  option_id: optionId
@@ -15774,7 +16012,7 @@ var StreamChat = class _StreamChat {
15774
16012
  if (this.userAgent) {
15775
16013
  return this.userAgent;
15776
16014
  }
15777
- const version = "9.13.0";
16015
+ const version = "9.15.0";
15778
16016
  const clientBundle = "browser-cjs";
15779
16017
  let userAgentString = "";
15780
16018
  if (this.sdkIdentifier) {