@maas/payload-plugin-media-cloud 0.0.31 → 0.0.33

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 (163) hide show
  1. package/dist/adapter/handleDelete.d.mts +3 -3
  2. package/dist/adapter/handleDelete.mjs +6 -6
  3. package/dist/adapter/handleDelete.mjs.map +1 -1
  4. package/dist/adapter/handleUpload.mjs.map +1 -1
  5. package/dist/adapter/staticHandler.d.mts +5 -3
  6. package/dist/adapter/staticHandler.mjs +8 -7
  7. package/dist/adapter/staticHandler.mjs.map +1 -1
  8. package/dist/adapter/storageAdapter.d.mts +3 -3
  9. package/dist/adapter/storageAdapter.mjs +4 -4
  10. package/dist/adapter/storageAdapter.mjs.map +1 -1
  11. package/dist/collectionHooks/afterChange.d.mts +7 -0
  12. package/dist/collectionHooks/afterChange.mjs +39 -0
  13. package/dist/collectionHooks/afterChange.mjs.map +1 -0
  14. package/dist/collectionHooks/beforeChange.d.mts +7 -0
  15. package/dist/collectionHooks/beforeChange.mjs +33 -0
  16. package/dist/collectionHooks/beforeChange.mjs.map +1 -0
  17. package/dist/collections/mediaCollection.d.mts +3 -2
  18. package/dist/collections/mediaCollection.mjs +36 -187
  19. package/dist/collections/mediaCollection.mjs.map +1 -1
  20. package/dist/components/folderFileCard/folderFileCard.d.mts +13 -0
  21. package/dist/components/folderFileCard/folderFileCard.mjs +75 -0
  22. package/dist/components/folderFileCard/folderFileCard.mjs.map +1 -0
  23. package/dist/components/gridContext/gridContext.d.mts +51 -0
  24. package/dist/components/gridContext/gridContext.mjs +227 -0
  25. package/dist/components/gridContext/gridContext.mjs.map +1 -0
  26. package/dist/components/gridView/gridView.css +50 -0
  27. package/dist/components/gridView/gridView.d.mts +16 -0
  28. package/dist/components/gridView/gridView.mjs +283 -0
  29. package/dist/components/gridView/gridView.mjs.map +1 -0
  30. package/dist/components/gridView/index.d.mts +2 -0
  31. package/dist/components/gridView/index.mjs +3 -0
  32. package/dist/components/index.d.mts +9 -7
  33. package/dist/components/index.mjs +5 -4
  34. package/dist/components/itemCardGrid/itemCardGrid.css +12 -0
  35. package/dist/components/itemCardGrid/itemCardGrid.d.mts +18 -0
  36. package/dist/components/itemCardGrid/itemCardGrid.mjs +42 -0
  37. package/dist/components/itemCardGrid/itemCardGrid.mjs.map +1 -0
  38. package/dist/components/muxPreview/index.d.mts +2 -0
  39. package/dist/components/muxPreview/index.mjs +3 -0
  40. package/dist/components/{mux-preview/mux-preview.d.mts → muxPreview/muxPreview.d.mts} +2 -2
  41. package/dist/components/muxPreview/muxPreview.mjs +65 -0
  42. package/dist/components/muxPreview/muxPreview.mjs.map +1 -0
  43. package/dist/components/uploadHandler/index.d.mts +2 -0
  44. package/dist/components/uploadHandler/index.mjs +3 -0
  45. package/dist/components/uploadHandler/uploadHandler.d.mts +20 -0
  46. package/dist/components/{upload-handler/upload-handler.mjs → uploadHandler/uploadHandler.mjs} +82 -52
  47. package/dist/components/uploadHandler/uploadHandler.mjs.map +1 -0
  48. package/dist/components/uploadManager/index.d.mts +2 -0
  49. package/dist/components/uploadManager/index.mjs +3 -0
  50. package/dist/components/{upload-manager/upload-manager.d.mts → uploadManager/uploadManager.d.mts} +3 -3
  51. package/dist/components/uploadManager/uploadManager.mjs +363 -0
  52. package/dist/components/uploadManager/uploadManager.mjs.map +1 -0
  53. package/dist/endpoints/fileExistsHandler.d.mts +12 -0
  54. package/dist/endpoints/{tusFileExistsHandler.mjs → fileExistsHandler.mjs} +7 -7
  55. package/dist/endpoints/fileExistsHandler.mjs.map +1 -0
  56. package/dist/endpoints/muxAssetHandler.d.mts +1 -0
  57. package/dist/endpoints/muxAssetHandler.mjs +3 -3
  58. package/dist/endpoints/muxAssetHandler.mjs.map +1 -1
  59. package/dist/endpoints/muxCreateUploadHandler.mjs.map +1 -1
  60. package/dist/endpoints/muxWebhookHandler.d.mts +1 -0
  61. package/dist/endpoints/muxWebhookHandler.mjs +6 -5
  62. package/dist/endpoints/muxWebhookHandler.mjs.map +1 -1
  63. package/dist/endpoints/tusCleanupHandler.d.mts +3 -2
  64. package/dist/endpoints/tusCleanupHandler.mjs +4 -4
  65. package/dist/endpoints/tusCleanupHandler.mjs.map +1 -1
  66. package/dist/endpoints/tusFolderHandler.d.mts +12 -0
  67. package/dist/endpoints/tusFolderHandler.mjs +39 -0
  68. package/dist/endpoints/tusFolderHandler.mjs.map +1 -0
  69. package/dist/endpoints/tusPostProcessorHandler.d.mts +3 -2
  70. package/dist/endpoints/tusPostProcessorHandler.mjs +6 -5
  71. package/dist/endpoints/tusPostProcessorHandler.mjs.map +1 -1
  72. package/dist/fields/alt.d.mts +7 -0
  73. package/dist/fields/alt.mjs +10 -0
  74. package/dist/fields/alt.mjs.map +1 -0
  75. package/dist/fields/filename.d.mts +7 -0
  76. package/dist/fields/filename.mjs +14 -0
  77. package/dist/fields/filename.mjs.map +1 -0
  78. package/dist/fields/height.d.mts +7 -0
  79. package/dist/fields/height.mjs +15 -0
  80. package/dist/fields/height.mjs.map +1 -0
  81. package/dist/fields/mux.d.mts +7 -0
  82. package/dist/fields/mux.mjs +149 -0
  83. package/dist/fields/mux.mjs.map +1 -0
  84. package/dist/fields/path.d.mts +7 -0
  85. package/dist/fields/path.mjs +14 -0
  86. package/dist/fields/path.mjs.map +1 -0
  87. package/dist/fields/storage.d.mts +7 -0
  88. package/dist/fields/storage.mjs +18 -0
  89. package/dist/fields/storage.mjs.map +1 -0
  90. package/dist/fields/thumbnail.d.mts +7 -0
  91. package/dist/fields/thumbnail.mjs +17 -0
  92. package/dist/fields/thumbnail.mjs.map +1 -0
  93. package/dist/fields/width.d.mts +7 -0
  94. package/dist/fields/width.mjs +15 -0
  95. package/dist/fields/width.mjs.map +1 -0
  96. package/dist/hooks/useErrorHandler.d.mts +1 -1
  97. package/dist/hooks/useErrorHandler.mjs +26 -7
  98. package/dist/hooks/useErrorHandler.mjs.map +1 -1
  99. package/dist/hooks/useMediaCloudEmitter.mjs.map +1 -1
  100. package/dist/plugin.d.mts +5 -4
  101. package/dist/plugin.mjs +53 -29
  102. package/dist/plugin.mjs.map +1 -1
  103. package/dist/tus/stores/s3/{expiration-manager.d.mts → expirationManager.d.mts} +4 -3
  104. package/dist/tus/stores/s3/{expiration-manager.mjs → expirationManager.mjs} +6 -3
  105. package/dist/tus/stores/s3/expirationManager.mjs.map +1 -0
  106. package/dist/tus/stores/s3/{file-operations.d.mts → fileOperations.d.mts} +2 -2
  107. package/dist/tus/stores/s3/{file-operations.mjs → fileOperations.mjs} +2 -2
  108. package/dist/tus/stores/s3/fileOperations.mjs.map +1 -0
  109. package/dist/tus/stores/s3/index.d.mts +1 -1
  110. package/dist/tus/stores/s3/index.mjs +20 -9
  111. package/dist/tus/stores/s3/index.mjs.map +1 -1
  112. package/dist/tus/stores/s3/{metadata-manager.d.mts → metadataManager.d.mts} +4 -2
  113. package/dist/tus/stores/s3/{metadata-manager.mjs → metadataManager.mjs} +6 -5
  114. package/dist/tus/stores/s3/metadataManager.mjs.map +1 -0
  115. package/dist/tus/stores/s3/{parts-manager.d.mts → partsManager.d.mts} +4 -4
  116. package/dist/tus/stores/s3/{parts-manager.mjs → partsManager.mjs} +67 -29
  117. package/dist/tus/stores/s3/partsManager.mjs.map +1 -0
  118. package/dist/tus/stores/s3/{s3-store.d.mts → s3Store.d.mts} +38 -32
  119. package/dist/tus/stores/s3/{s3-store.mjs → s3Store.mjs} +104 -59
  120. package/dist/tus/stores/s3/s3Store.mjs.map +1 -0
  121. package/dist/tus/stores/s3/semaphore.mjs.map +1 -1
  122. package/dist/types/errors.d.mts +32 -0
  123. package/dist/types/errors.mjs +32 -0
  124. package/dist/types/errors.mjs.map +1 -1
  125. package/dist/types/index.d.mts +42 -4
  126. package/dist/utils/buildS3Path.d.mts +10 -0
  127. package/dist/utils/buildS3Path.mjs +16 -0
  128. package/dist/utils/buildS3Path.mjs.map +1 -0
  129. package/dist/utils/buildThumbnailURL.d.mts +14 -0
  130. package/dist/utils/buildThumbnailURL.mjs +10 -0
  131. package/dist/utils/buildThumbnailURL.mjs.map +1 -0
  132. package/dist/utils/defaultOptions.d.mts +7 -0
  133. package/dist/utils/defaultOptions.mjs +12 -0
  134. package/dist/utils/defaultOptions.mjs.map +1 -0
  135. package/dist/utils/file.d.mts +16 -2
  136. package/dist/utils/file.mjs +58 -6
  137. package/dist/utils/file.mjs.map +1 -1
  138. package/dist/utils/mux.mjs +19 -8
  139. package/dist/utils/mux.mjs.map +1 -1
  140. package/dist/utils/tus.d.mts +9 -6
  141. package/dist/utils/tus.mjs +31 -11
  142. package/dist/utils/tus.mjs.map +1 -1
  143. package/package.json +11 -4
  144. package/dist/components/mux-preview/index.d.mts +0 -2
  145. package/dist/components/mux-preview/index.mjs +0 -3
  146. package/dist/components/mux-preview/mux-preview.mjs +0 -29
  147. package/dist/components/mux-preview/mux-preview.mjs.map +0 -1
  148. package/dist/components/upload-handler/index.d.mts +0 -2
  149. package/dist/components/upload-handler/index.mjs +0 -3
  150. package/dist/components/upload-handler/upload-handler.d.mts +0 -22
  151. package/dist/components/upload-handler/upload-handler.mjs.map +0 -1
  152. package/dist/components/upload-manager/index.d.mts +0 -2
  153. package/dist/components/upload-manager/index.mjs +0 -3
  154. package/dist/components/upload-manager/upload-manager.mjs +0 -273
  155. package/dist/components/upload-manager/upload-manager.mjs.map +0 -1
  156. package/dist/endpoints/tusFileExistsHandler.d.mts +0 -11
  157. package/dist/endpoints/tusFileExistsHandler.mjs.map +0 -1
  158. package/dist/tus/stores/s3/expiration-manager.mjs.map +0 -1
  159. package/dist/tus/stores/s3/file-operations.mjs.map +0 -1
  160. package/dist/tus/stores/s3/metadata-manager.mjs.map +0 -1
  161. package/dist/tus/stores/s3/parts-manager.mjs.map +0 -1
  162. package/dist/tus/stores/s3/s3-store.mjs.map +0 -1
  163. /package/dist/components/{upload-manager/upload-manager.css → uploadManager/uploadManager.css} +0 -0
@@ -1,15 +1,15 @@
1
1
  import { MediaCloudLogs } from "../../../types/errors.mjs";
2
2
  import { useErrorHandler } from "../../../hooks/useErrorHandler.mjs";
3
3
  import { Semaphore } from "./semaphore.mjs";
4
- import { S3ExpirationManager } from "./expiration-manager.mjs";
5
- import { S3FileOperations } from "./file-operations.mjs";
6
- import { S3MetadataManager } from "./metadata-manager.mjs";
7
- import { S3PartsManager } from "./parts-manager.mjs";
4
+ import { S3ExpirationManager } from "./expirationManager.mjs";
5
+ import { S3FileOperations } from "./fileOperations.mjs";
6
+ import { S3MetadataManager } from "./metadataManager.mjs";
7
+ import { S3PartsManager } from "./partsManager.mjs";
8
8
  import stream from "node:stream";
9
9
  import { NoSuchKey, NotFound, S3 } from "@aws-sdk/client-s3";
10
10
  import { DataStore, ERRORS, TUS_RESUMABLE, Upload } from "@tus/utils";
11
11
 
12
- //#region src/tus/stores/s3/s3-store.ts
12
+ //#region src/tus/stores/s3/s3Store.ts
13
13
  const { log } = useErrorHandler();
14
14
  var S3Store = class extends DataStore {
15
15
  constructor(options) {
@@ -42,27 +42,10 @@ var S3Store = class extends DataStore {
42
42
  this.metadataManager = new S3MetadataManager(this.client, this.bucket, this.shouldUseExpirationTags.bind(this), this.generateCompleteTag.bind(this));
43
43
  this.fileOperations = new S3FileOperations(this.maxMultipartParts, this.maxUploadSize, this.minPartSize, this.partSize);
44
44
  this.partsManager = new S3PartsManager(this.client, this.bucket, this.minPartSize, this.partUploadSemaphore, this.metadataManager, this.fileOperations, this.generateCompleteTag.bind(this));
45
- this.expirationManager = new S3ExpirationManager(this.client, this.bucket, this.expirationPeriodInMilliseconds, this.generateInfoKey.bind(this), this.generatePartKey.bind(this));
45
+ this.expirationManager = new S3ExpirationManager(this.client, this.bucket, this.expirationPeriodInMilliseconds, this.metadataManager.generateInfoKey.bind(this.metadataManager), this.metadataManager.generatePartKey.bind(this.metadataManager));
46
46
  this.deleteExpired();
47
47
  }
48
48
  /**
49
- * Generate the key name for the info file
50
- * @param id - The upload ID
51
- * @returns The info file key
52
- */
53
- generateInfoKey(id) {
54
- return `${id}.info`;
55
- }
56
- /**
57
- * Generate the key name for a part file
58
- * @param id - The upload ID
59
- * @param isIncompletePart - Whether this is an incomplete part (default: false)
60
- * @returns The part file key
61
- */
62
- generatePartKey(id, isIncompletePart = false) {
63
- return isIncompletePart ? `${id}.part` : id;
64
- }
65
- /**
66
49
  * Helper method to check if expiration tags should be used
67
50
  * @returns True if expiration tags should be used
68
51
  */
@@ -72,7 +55,7 @@ var S3Store = class extends DataStore {
72
55
  /**
73
56
  * Generates a tag for marking complete/incomplete uploads
74
57
  * @param value - Either 'false' or 'true' to mark completion status
75
- * @returns The tag string or undefined if tags shouldn't be used
58
+ * @returns The tag string or undefined if tags shouldnt be used
76
59
  */
77
60
  generateCompleteTag(value) {
78
61
  if (!this.shouldUseExpirationTags()) return;
@@ -83,13 +66,16 @@ var S3Store = class extends DataStore {
83
66
  * Also, a `${file_id}.info` file is created which holds some information
84
67
  * about the upload itself like: `upload-id`, `upload-length`, etc.
85
68
  * @param upload - The upload object to create
86
- * @returns Promise that resolves to the created upload
69
+ * @return Promise that resolves to the created upload object with storage information
87
70
  */
88
71
  async create(upload) {
89
72
  log(MediaCloudLogs.S3_STORE_MULTIPART_INIT);
90
73
  const request = {
91
74
  Bucket: this.bucket,
92
- Key: upload.id,
75
+ Key: this.metadataManager.generatePartKey({
76
+ id: upload.id,
77
+ prefix: upload.metadata?.prefix ?? void 0
78
+ }),
93
79
  Metadata: { "tus-version": TUS_RESUMABLE }
94
80
  };
95
81
  if (upload.metadata?.contentType) request.ContentType = upload.metadata.contentType;
@@ -110,26 +96,11 @@ var S3Store = class extends DataStore {
110
96
  return upload;
111
97
  }
112
98
  /**
113
- * Declares the length of the upload
114
- * @param file_id - The file ID
115
- * @param upload_length - The length of the upload
116
- * @returns Promise that resolves when length is declared
117
- */
118
- async declareUploadLength(file_id, upload_length) {
119
- const { file, "upload-id": uploadId } = await this.metadataManager.getMetadata({ id: file_id });
120
- if (!file) throw ERRORS.FILE_NOT_FOUND;
121
- file.size = upload_length;
122
- await this.metadataManager.saveMetadata({
123
- upload: file,
124
- uploadId
125
- });
126
- }
127
- /**
128
- * Writes `buffer` to the file specified by the upload's `id` at `offset`
99
+ * Writes `buffer` to the file specified by the upload’s `id` at `offset`
129
100
  * @param readable - The readable stream to write
130
101
  * @param id - The upload ID
131
102
  * @param offset - The byte offset to write at
132
- * @returns Promise that resolves to the number of bytes written
103
+ * @return Promise that resolves to the new offset after writing the data
133
104
  */
134
105
  async write(readable, id, offset) {
135
106
  const metadata = await this.metadataManager.getMetadata({ id });
@@ -144,10 +115,10 @@ var S3Store = class extends DataStore {
144
115
  if (incompletePart.size !== offsetDiff) throw ERRORS.FILE_WRITE_ERROR;
145
116
  await this.partsManager.deleteIncompletePart({ id });
146
117
  offset = requestedOffset - incompletePart.size;
147
- finalReadable = stream.Readable.from((async function* () {
118
+ finalReadable = stream.Readable.from(async function* () {
148
119
  yield* incompletePart.createReader({ cleanUpOnEnd: true });
149
120
  yield* readable;
150
- })());
121
+ }());
151
122
  }
152
123
  const partNumber = this.fileOperations.calculatePartNumber({ parts: await this.partsManager.retrieveParts({ id }) });
153
124
  const newOffset = requestedOffset + await this.partsManager.uploadParts({
@@ -178,6 +149,8 @@ var S3Store = class extends DataStore {
178
149
  /**
179
150
  * Returns the current state of the upload, i.e how much data has been
180
151
  * uploaded and if the upload is complete.
152
+ * @param id - The upload ID to retrieve
153
+ * @returns Promise that resolves to the upload object with current offset and storage information
181
154
  */
182
155
  async getUpload(id) {
183
156
  let metadata;
@@ -211,7 +184,9 @@ var S3Store = class extends DataStore {
211
184
  });
212
185
  }
213
186
  /**
214
- * Reads the file specified by the upload's `id` and returns a readable stream
187
+ * Reads the file specified by the uploads `id` and returns a readable stream
188
+ * @param id - The upload ID to read
189
+ * @returns Promise that resolves to a readable stream of the file's contents
215
190
  */
216
191
  async read(id) {
217
192
  log(MediaCloudLogs.S3_STORE_READ_ATTEMPT);
@@ -231,19 +206,66 @@ var S3Store = class extends DataStore {
231
206
  if (retries > 0) await new Promise((resolve) => setTimeout(resolve, 100));
232
207
  }
233
208
  log(MediaCloudLogs.S3_STORE_READ_FAILED);
234
- throw lastError || /* @__PURE__ */ new Error(`Failed to read file ${id}`);
209
+ throw lastError ?? /* @__PURE__ */ new Error(`Failed to read file ${id}`);
235
210
  }
236
211
  /**
237
- * Removes the file specified by the upload's `id`
212
+ *
213
+ * Moves the file specified by its `oldKey` to `newKey`.
214
+ * @param oldKey - The current S3 key of the file to be moved
215
+ * @param newKey - The new S3 key to move the file to
216
+ * @return Promise that resolves when the file has been successfully moved, or rejects with an error if the move operation fails
217
+ */
218
+ async copy(oldKey, newKey) {
219
+ try {
220
+ await this.client.copyObject({
221
+ Bucket: this.bucket,
222
+ CopySource: encodeURI(`${this.bucket}/${oldKey}`),
223
+ Key: newKey
224
+ });
225
+ await this.client.deleteObject({
226
+ Bucket: this.bucket,
227
+ Key: oldKey
228
+ });
229
+ } catch (error) {
230
+ if (error?.code && [
231
+ "NoSuchKey",
232
+ "NoSuchUpload",
233
+ "NotFound"
234
+ ].includes(error.Code || "")) {
235
+ log(MediaCloudLogs.S3_STORE_FILE_NOT_FOUND);
236
+ throw ERRORS.FILE_NOT_FOUND;
237
+ }
238
+ throw error;
239
+ }
240
+ }
241
+ /**
242
+ * Removes files specified by the upload’s `id`
243
+ * @param id - The upload ID to remove
244
+ * @returns Promise that resolves when the file and its metadata have been removed
238
245
  */
239
246
  async remove(id) {
240
247
  try {
241
- const { "upload-id": uploadId } = await this.metadataManager.getMetadata({ id });
248
+ const { "upload-id": uploadId, file } = await this.metadataManager.getMetadata({ id });
242
249
  if (uploadId) await this.client.abortMultipartUpload({
243
250
  Bucket: this.bucket,
244
251
  Key: id,
245
252
  UploadId: uploadId
246
253
  });
254
+ await this.client.deleteObjects({
255
+ Bucket: this.bucket,
256
+ Delete: { Objects: [
257
+ { Key: this.metadataManager.generatePartKey({
258
+ id,
259
+ prefix: file?.metadata?.prefix ?? void 0
260
+ }) },
261
+ { Key: this.metadataManager.generatePartKey({
262
+ id,
263
+ prefix: file?.metadata?.prefix ?? void 0,
264
+ isIncomplete: true
265
+ }) },
266
+ { Key: this.metadataManager.generateInfoKey({ id }) }
267
+ ] }
268
+ });
247
269
  } catch (error) {
248
270
  if (error?.code && [
249
271
  "NoSuchKey",
@@ -255,20 +277,39 @@ var S3Store = class extends DataStore {
255
277
  }
256
278
  throw error;
257
279
  }
258
- await this.client.deleteObjects({
259
- Bucket: this.bucket,
260
- Delete: { Objects: [
261
- { Key: id },
262
- { Key: this.metadataManager.generateInfoKey({ id }) },
263
- { Key: this.metadataManager.generatePartKey({
280
+ }
281
+ /**
282
+ * Removes the .info file specified by the upload’s `id`
283
+ * @param id - The upload ID to clean up
284
+ * @returns Promise that resolves when the metadata file has been removed
285
+ */
286
+ async cleanup(id) {
287
+ try {
288
+ const { file } = await this.metadataManager.getMetadata({ id });
289
+ await this.client.deleteObjects({
290
+ Bucket: this.bucket,
291
+ Delete: { Objects: [{ Key: this.metadataManager.generatePartKey({
264
292
  id,
293
+ prefix: file?.metadata?.prefix ?? void 0,
265
294
  isIncomplete: true
266
- }) }
267
- ] }
268
- });
295
+ }) }, { Key: this.metadataManager.generateInfoKey({ id }) }] }
296
+ });
297
+ } catch (error) {
298
+ if (error?.code && [
299
+ "NoSuchKey",
300
+ "NoSuchUpload",
301
+ "NotFound"
302
+ ].includes(error.Code || "")) {
303
+ log(MediaCloudLogs.S3_STORE_FILE_NOT_FOUND);
304
+ throw ERRORS.FILE_NOT_FOUND;
305
+ }
306
+ throw error;
307
+ }
269
308
  }
270
309
  /**
271
310
  * Combine all multipart uploads into a single object
311
+ * @param id - The upload ID to complete
312
+ * @returns Promise that resolves to the completed upload object with storage information
272
313
  */
273
314
  async completeMultipartUpload(id) {
274
315
  const metadata = await this.metadataManager.getMetadata({ id });
@@ -301,6 +342,8 @@ var S3Store = class extends DataStore {
301
342
  }
302
343
  /**
303
344
  * Get the full S3 URL for an uploaded file
345
+ * @param id - The upload ID to get the URL for
346
+ * @returns The full URL to access the uploaded file on S3
304
347
  */
305
348
  getUrl(id) {
306
349
  if (this.customEndpoint) return `${this.customEndpoint}/${this.bucket}/${id}`;
@@ -314,12 +357,14 @@ var S3Store = class extends DataStore {
314
357
  /**
315
358
  * Deletes expired incomplete uploads.
316
359
  * Returns the number of deleted uploads.
360
+ * @returns Promise that resolves to the number of deleted uploads
317
361
  */
318
362
  async deleteExpired() {
319
363
  return this.expirationManager.deleteExpired();
320
364
  }
321
365
  /**
322
366
  * Returns the expiration period in milliseconds
367
+ * @return The expiration period in milliseconds
323
368
  */
324
369
  getExpiration() {
325
370
  return this.expirationManager.getExpiration();
@@ -328,4 +373,4 @@ var S3Store = class extends DataStore {
328
373
 
329
374
  //#endregion
330
375
  export { S3Store };
331
- //# sourceMappingURL=s3-store.mjs.map
376
+ //# sourceMappingURL=s3Store.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"s3Store.mjs","names":["NoSuchKey","NotFound","S3","DataStore","ERRORS","TUS_RESUMABLE","Upload","stream","Readable","useErrorHandler","Semaphore","S3ExpirationManager","S3FileOperations","S3MetadataManager","S3PartsManager","MediaCloudLogs","AWS","AWSError","S3StoreConfig","TusUploadMetadata","log","S3Store","client","bucket","partSize","minPartSize","maxMultipartParts","maxUploadSize","const","useTags","expirationPeriodInMilliseconds","acl","partUploadSemaphore","metadataManager","fileOperations","partsManager","expirationManager","customEndpoint","constructor","options","s3ClientConfig","maxConcurrentPartUploads","restS3ClientConfig","extensions","String","endpoint","shouldUseExpirationTags","bind","generateCompleteTag","generateInfoKey","generatePartKey","deleteExpired","value","undefined","create","upload","Promise","S3_STORE_MULTIPART_INIT","request","CreateMultipartUploadCommandInput","Bucket","Key","id","prefix","metadata","Metadata","contentType","ContentType","cacheControl","CacheControl","ACL","ObjectCannedACL","creation_date","Date","toISOString","response","createMultipartUpload","storage","type","path","saveMetadata","uploadId","UploadId","S3_STORE_MULTIPART_CREATED","write","readable","offset","getMetadata","calculatedOffset","calculateOffsetFromParts","parts","retrieveParts","offsetDiff","requestedOffset","finalReadable","FILE_WRITE_ERROR","incompletePart","downloadIncompletePart","size","deleteIncompletePart","from","createReader","cleanUpOnEnd","partNumber","calculatePartNumber","bytesUploaded","uploadParts","readStream","currentPartNumber","newOffset","file","finishMultipartUpload","completedUpload","completeMetadata","error","S3_STORE_UPLOAD_FAILED","getUpload","Code","FILE_NOT_FOUND","S3_STORE_RETRIEVE_PARTS_ERROR","incompletePartSize","getIncompletePartSize","read","S3_STORE_READ_ATTEMPT","retries","lastError","Error","data","getObject","S3_STORE_READ_SUCCESS","Body","S3_STORE_READ_RETRY","resolve","setTimeout","S3_STORE_READ_FAILED","copy","oldKey","newKey","copyObject","CopySource","encodeURI","deleteObject","code","includes","S3_STORE_FILE_NOT_FOUND","remove","abortMultipartUpload","deleteObjects","Delete","Objects","isIncomplete","cleanup","completeMultipartUpload","incompletePartInfo","uploadPart","length","updatedParts","getUrl","regionConfig","config","region","getExpiration"],"sources":["../../../../src/tus/stores/s3/s3Store.ts"],"sourcesContent":["import { NoSuchKey, NotFound, S3 } from '@aws-sdk/client-s3'\nimport { DataStore, ERRORS, TUS_RESUMABLE, Upload } from '@tus/utils'\nimport stream, { type Readable } from 'node:stream'\n\nimport { useErrorHandler } from '../../../hooks/useErrorHandler'\nimport { Semaphore } from './semaphore'\nimport { S3ExpirationManager } from './expirationManager'\nimport { S3FileOperations } from './fileOperations'\nimport { S3MetadataManager } from './metadataManager'\nimport { S3PartsManager } from './partsManager'\n\nimport { MediaCloudLogs } from '../../../types/errors'\n\nimport type AWS from '@aws-sdk/client-s3'\nimport type { AWSError, S3StoreConfig, TusUploadMetadata } from '../../../types'\n\nconst { log } = useErrorHandler()\n\nexport class S3Store extends DataStore {\n public client: S3\n public bucket: string\n public partSize = 8 * 1024 * 1024 // 8MB preferred part size\n public minPartSize = 5 * 1024 * 1024 // 5MB minimum part size\n public maxMultipartParts = 10_000\n public maxUploadSize = 5_497_558_138_880 as const // 5TiB\n public useTags = false\n public expirationPeriodInMilliseconds = 0\n protected acl?: string\n\n protected partUploadSemaphore: Semaphore\n protected metadataManager: S3MetadataManager\n protected fileOperations: S3FileOperations\n protected partsManager: S3PartsManager\n protected expirationManager: S3ExpirationManager\n protected customEndpoint: string\n\n constructor(options: S3StoreConfig) {\n super()\n const {\n maxMultipartParts,\n minPartSize,\n partSize,\n s3ClientConfig,\n maxConcurrentPartUploads,\n useTags,\n expirationPeriodInMilliseconds,\n } = options\n const { acl, bucket, ...restS3ClientConfig } = s3ClientConfig\n\n this.extensions = [\n 'creation',\n 'creation-with-upload',\n 'creation-defer-length',\n 'termination',\n 'expiration',\n ]\n\n this.bucket = bucket\n this.acl = acl\n this.client = new S3(restS3ClientConfig)\n this.customEndpoint = String(restS3ClientConfig.endpoint)\n\n this.partSize = partSize ?? this.partSize\n this.minPartSize = minPartSize ?? this.minPartSize\n this.maxMultipartParts = maxMultipartParts ?? this.maxMultipartParts\n\n this.useTags = useTags ?? this.useTags\n this.expirationPeriodInMilliseconds =\n expirationPeriodInMilliseconds ?? this.expirationPeriodInMilliseconds\n this.partUploadSemaphore = new Semaphore(maxConcurrentPartUploads ?? 60)\n\n // Initialize component managers\n this.metadataManager = new S3MetadataManager(\n this.client,\n this.bucket,\n this.shouldUseExpirationTags.bind(this),\n this.generateCompleteTag.bind(this)\n )\n\n this.fileOperations = new S3FileOperations(\n this.maxMultipartParts,\n this.maxUploadSize,\n this.minPartSize,\n this.partSize\n )\n\n this.partsManager = new S3PartsManager(\n this.client,\n this.bucket,\n this.minPartSize,\n this.partUploadSemaphore,\n this.metadataManager,\n this.fileOperations,\n this.generateCompleteTag.bind(this)\n )\n\n this.expirationManager = new S3ExpirationManager(\n this.client,\n this.bucket,\n this.expirationPeriodInMilliseconds,\n this.metadataManager.generateInfoKey.bind(this.metadataManager),\n this.metadataManager.generatePartKey.bind(this.metadataManager)\n )\n\n // Cleanup expired uploads when the store is initialized\n this.deleteExpired()\n }\n\n /**\n * Helper method to check if expiration tags should be used\n * @returns True if expiration tags should be used\n */\n protected shouldUseExpirationTags(): boolean {\n return this.expirationPeriodInMilliseconds !== 0 && this.useTags\n }\n\n /**\n * Generates a tag for marking complete/incomplete uploads\n * @param value - Either 'false' or 'true' to mark completion status\n * @returns The tag string or undefined if tags shouldn’t be used\n */\n protected generateCompleteTag(value: 'false' | 'true'): string | undefined {\n if (!this.shouldUseExpirationTags()) {\n return undefined\n }\n return `Tus-Completed=${value}`\n }\n\n /**\n * Creates a multipart upload on S3 attaching any metadata to it.\n * Also, a `${file_id}.info` file is created which holds some information\n * about the upload itself like: `upload-id`, `upload-length`, etc.\n * @param upload - The upload object to create\n * @return Promise that resolves to the created upload object with storage information\n */\n public async create(upload: Upload): Promise<Upload> {\n log(MediaCloudLogs.S3_STORE_MULTIPART_INIT)\n\n const request: AWS.CreateMultipartUploadCommandInput = {\n Bucket: this.bucket,\n Key: this.metadataManager.generatePartKey({\n id: upload.id,\n prefix: upload.metadata?.prefix ?? undefined,\n }),\n Metadata: { 'tus-version': TUS_RESUMABLE },\n }\n\n if (upload.metadata?.contentType) {\n request.ContentType = upload.metadata.contentType as string\n }\n\n if (upload.metadata?.cacheControl) {\n request.CacheControl = upload.metadata.cacheControl as string\n }\n\n if (this.acl) {\n request.ACL = this.acl as AWS.ObjectCannedACL\n }\n\n upload.creation_date = new Date().toISOString()\n\n const response = await this.client.createMultipartUpload(request)\n\n upload.storage = {\n type: 's3',\n bucket: this.bucket,\n path: response.Key as string,\n }\n await this.metadataManager.saveMetadata({\n upload,\n uploadId: response.UploadId as string,\n })\n log(MediaCloudLogs.S3_STORE_MULTIPART_CREATED)\n\n return upload\n }\n\n /**\n * Writes `buffer` to the file specified by the upload’s `id` at `offset`\n * @param readable - The readable stream to write\n * @param id - The upload ID\n * @param offset - The byte offset to write at\n * @return Promise that resolves to the new offset after writing the data\n */\n public async write(\n readable: stream.Readable,\n id: string,\n offset: number\n ): Promise<number> {\n const metadata = await this.metadataManager.getMetadata({ id })\n\n // TUS sends PATCH requests with an `upload-offset` header.\n // Offset the write by the offset in the PATCH request.\n const calculatedOffset = this.fileOperations.calculateOffsetFromParts({\n parts: await this.partsManager.retrieveParts({ id }),\n })\n const offsetDiff = offset - calculatedOffset\n const requestedOffset = offset\n\n let finalReadable = readable\n\n if (offsetDiff < 0) {\n throw ERRORS.FILE_WRITE_ERROR\n }\n\n // If the offset given in the PATCH request is higher than\n // the expected offset, we need to prepend an incomplete\n // part to the readable stream, if one exists.\n if (offsetDiff > 0) {\n const incompletePart = await this.partsManager.downloadIncompletePart({\n id,\n })\n\n if (!incompletePart) {\n throw ERRORS.FILE_WRITE_ERROR\n }\n\n if (incompletePart.size !== offsetDiff) {\n throw ERRORS.FILE_WRITE_ERROR\n }\n\n // Clear the incomplete part from S3 since it's going to be combined with the current request\n await this.partsManager.deleteIncompletePart({ id })\n\n // Adjust offset to account for the incomplete part\n offset = requestedOffset - incompletePart.size\n\n finalReadable = stream.Readable.from(\n (async function* () {\n yield* incompletePart.createReader({ cleanUpOnEnd: true })\n yield* readable\n })()\n )\n }\n\n const partNumber = this.fileOperations.calculatePartNumber({\n parts: await this.partsManager.retrieveParts({ id }),\n })\n\n const bytesUploaded = await this.partsManager.uploadParts({\n metadata,\n readStream: finalReadable,\n currentPartNumber: partNumber,\n offset,\n })\n\n // The size of the incomplete part should not be counted, because the\n // process of the incomplete part should be fully transparent to the user.\n const newOffset =\n requestedOffset + bytesUploaded - (offsetDiff > 0 ? offsetDiff : 0)\n\n // Check if the upload is complete\n if (metadata.file.size === newOffset) {\n try {\n const parts = await this.partsManager.retrieveParts({ id })\n await this.partsManager.finishMultipartUpload({ metadata, parts })\n\n // Update the metadata with completed state\n const completedUpload = new Upload({\n ...metadata.file,\n offset: newOffset,\n size: metadata.file.size,\n storage: metadata.file.storage,\n })\n\n await this.metadataManager.completeMetadata({ upload: completedUpload })\n } catch (error) {\n log(MediaCloudLogs.S3_STORE_UPLOAD_FAILED)\n throw error\n }\n }\n\n return newOffset\n }\n\n /**\n * Returns the current state of the upload, i.e how much data has been\n * uploaded and if the upload is complete.\n * @param id - The upload ID to retrieve\n * @returns Promise that resolves to the upload object with current offset and storage information\n */\n public async getUpload(id: string): Promise<Upload> {\n let metadata: TusUploadMetadata\n\n try {\n metadata = await this.metadataManager.getMetadata({ id })\n } catch (error) {\n if (\n error instanceof NoSuchKey ||\n error instanceof NotFound ||\n (error as AWSError)?.Code === 'NotFound' ||\n (error as AWSError)?.Code === 'NoSuchKey'\n ) {\n throw ERRORS.FILE_NOT_FOUND\n }\n throw error\n }\n\n let offset: number\n\n try {\n const parts = await this.partsManager.retrieveParts({ id })\n offset = this.fileOperations.calculateOffsetFromParts({ parts })\n } catch (error) {\n // Check if the error is caused by the upload not being found. This happens\n // when the multipart upload has already been completed or aborted.\n if (\n (error as AWSError)?.Code === 'NoSuchUpload' ||\n (error as AWSError)?.Code === 'NoSuchKey'\n ) {\n return new Upload({\n ...metadata.file,\n metadata: metadata.file.metadata,\n offset: metadata.file.size as number,\n size: metadata.file.size,\n storage: metadata.file.storage,\n })\n }\n\n log(MediaCloudLogs.S3_STORE_RETRIEVE_PARTS_ERROR)\n throw error\n }\n\n const incompletePartSize = await this.partsManager.getIncompletePartSize({\n id,\n })\n\n return new Upload({\n ...metadata.file,\n offset: offset + (incompletePartSize ?? 0),\n size: metadata.file.size,\n storage: metadata.file.storage,\n })\n }\n\n /**\n * Reads the file specified by the upload’s `id` and returns a readable stream\n * @param id - The upload ID to read\n * @returns Promise that resolves to a readable stream of the file's contents\n */\n async read(id: string): Promise<Readable> {\n log(MediaCloudLogs.S3_STORE_READ_ATTEMPT)\n let retries = 3\n let lastError: Error | null = null\n\n while (retries > 0) {\n try {\n const data = await this.client.getObject({\n Bucket: this.bucket,\n Key: id,\n })\n log(MediaCloudLogs.S3_STORE_READ_SUCCESS)\n return data.Body as Readable\n } catch (error) {\n log(MediaCloudLogs.S3_STORE_READ_RETRY)\n lastError = error as Error\n retries--\n\n if (retries > 0) {\n // Wait a bit before retrying, in case S3 needs time for consistency\n await new Promise((resolve) => setTimeout(resolve, 100))\n }\n }\n }\n\n log(MediaCloudLogs.S3_STORE_READ_FAILED)\n throw lastError ?? new Error(`Failed to read file ${id}`)\n }\n\n /**\n *\n * Moves the file specified by its `oldKey` to `newKey`.\n * @param oldKey - The current S3 key of the file to be moved\n * @param newKey - The new S3 key to move the file to\n * @return Promise that resolves when the file has been successfully moved, or rejects with an error if the move operation fails\n */\n public async copy(oldKey: string, newKey: string): Promise<void> {\n try {\n await this.client.copyObject({\n Bucket: this.bucket,\n CopySource: encodeURI(`${this.bucket}/${oldKey}`),\n Key: newKey,\n })\n\n await this.client.deleteObject({\n Bucket: this.bucket,\n Key: oldKey,\n })\n } catch (error) {\n if (\n (error as AWSError)?.code &&\n ['NoSuchKey', 'NoSuchUpload', 'NotFound'].includes(\n (error as AWSError).Code || ''\n )\n ) {\n log(MediaCloudLogs.S3_STORE_FILE_NOT_FOUND)\n throw ERRORS.FILE_NOT_FOUND\n }\n throw error as Error\n }\n }\n\n /**\n * Removes files specified by the upload’s `id`\n * @param id - The upload ID to remove\n * @returns Promise that resolves when the file and its metadata have been removed\n */\n public async remove(id: string): Promise<void> {\n try {\n const { 'upload-id': uploadId, file } =\n await this.metadataManager.getMetadata({ id })\n if (uploadId) {\n await this.client.abortMultipartUpload({\n Bucket: this.bucket,\n Key: id,\n UploadId: uploadId,\n })\n }\n\n await this.client.deleteObjects({\n Bucket: this.bucket,\n Delete: {\n Objects: [\n {\n Key: this.metadataManager.generatePartKey({\n id,\n prefix: file?.metadata?.prefix ?? undefined,\n }),\n },\n {\n Key: this.metadataManager.generatePartKey({\n id,\n prefix: file?.metadata?.prefix ?? undefined,\n isIncomplete: true,\n }),\n },\n {\n Key: this.metadataManager.generateInfoKey({ id }),\n },\n ],\n },\n })\n } catch (error) {\n if (\n (error as AWSError)?.code &&\n ['NoSuchKey', 'NoSuchUpload', 'NotFound'].includes(\n (error as AWSError).Code || ''\n )\n ) {\n log(MediaCloudLogs.S3_STORE_FILE_NOT_FOUND)\n throw ERRORS.FILE_NOT_FOUND\n }\n throw error\n }\n }\n\n /**\n * Removes the .info file specified by the upload’s `id`\n * @param id - The upload ID to clean up\n * @returns Promise that resolves when the metadata file has been removed\n */\n public async cleanup(id: string): Promise<void> {\n try {\n const { file } = await this.metadataManager.getMetadata({ id })\n\n await this.client.deleteObjects({\n Bucket: this.bucket,\n Delete: {\n Objects: [\n {\n Key: this.metadataManager.generatePartKey({\n id,\n prefix: file?.metadata?.prefix ?? undefined,\n isIncomplete: true,\n }),\n },\n {\n Key: this.metadataManager.generateInfoKey({ id }),\n },\n ],\n },\n })\n } catch (error) {\n if (\n (error as AWSError)?.code &&\n ['NoSuchKey', 'NoSuchUpload', 'NotFound'].includes(\n (error as AWSError).Code || ''\n )\n ) {\n log(MediaCloudLogs.S3_STORE_FILE_NOT_FOUND)\n throw ERRORS.FILE_NOT_FOUND\n }\n throw error\n }\n }\n\n /**\n * Combine all multipart uploads into a single object\n * @param id - The upload ID to complete\n * @returns Promise that resolves to the completed upload object with storage information\n */\n public async completeMultipartUpload(id: string): Promise<Upload> {\n const metadata = await this.metadataManager.getMetadata({ id })\n const parts = await this.partsManager.retrieveParts({ id })\n\n const incompletePartInfo = await this.partsManager.downloadIncompletePart({\n id,\n })\n\n if (incompletePartInfo) {\n // Upload the incomplete part as a regular part\n await this.partsManager.uploadPart({\n metadata,\n readStream: incompletePartInfo.createReader({ cleanUpOnEnd: true }),\n partNumber: parts.length + 1,\n })\n\n // Remove the incomplete part\n await this.partsManager.deleteIncompletePart({ id })\n\n // Re-fetch parts to include the newly uploaded part\n const updatedParts = await this.partsManager.retrieveParts({ id })\n await this.partsManager.finishMultipartUpload({\n metadata,\n parts: updatedParts,\n })\n } else {\n await this.partsManager.finishMultipartUpload({ metadata, parts })\n }\n\n const completedUpload = new Upload({\n ...metadata.file,\n offset: metadata.file.size ?? 0,\n size: metadata.file.size ?? 0,\n storage: metadata.file.storage,\n })\n\n await this.metadataManager.completeMetadata({ upload: completedUpload })\n\n return completedUpload\n }\n\n /**\n * Get the full S3 URL for an uploaded file\n * @param id - The upload ID to get the URL for\n * @returns The full URL to access the uploaded file on S3\n */\n public getUrl(id: string): string {\n // Use the custom endpoint if available\n if (this.customEndpoint) {\n return `${this.customEndpoint}/${this.bucket}/${id}`\n }\n\n // Fallback to standard AWS S3 URL format\n const regionConfig = this.client.config.region\n let region: string\n\n // If region is a function, we can't resolve it synchronously, so use a fallback\n if (typeof regionConfig === 'function') {\n region = 'us-east-1' // fallback for sync calls\n } else {\n region = regionConfig || 'us-east-1'\n }\n\n // Standard AWS S3 URL format\n if (region === 'us-east-1') {\n return `https://${this.bucket}.s3.amazonaws.com/${id}`\n } else {\n return `https://${this.bucket}.s3.${region}.amazonaws.com/${id}`\n }\n }\n\n /**\n * Deletes expired incomplete uploads.\n * Returns the number of deleted uploads.\n * @returns Promise that resolves to the number of deleted uploads\n */\n async deleteExpired(): Promise<number> {\n return this.expirationManager.deleteExpired()\n }\n\n /**\n * Returns the expiration period in milliseconds\n * @return The expiration period in milliseconds\n */\n getExpiration(): number {\n return this.expirationManager.getExpiration()\n }\n}\n"],"mappings":";;;;;;;;;;;;AAgBA,MAAM,EAAEoB,QAAQX,iBAAiB;AAEjC,IAAaY,UAAb,cAA6BlB,UAAU;CAkBrCmC,YAAYC,SAAwB;AAClC,SAAO;kBAhBS,IAAI,OAAO;qBACR,IAAI,OAAO;2BACL;uBACJ;iBACN;wCACuB;EAYtC,MAAM,EACJb,mBACAD,aACAD,UACAgB,gBACAC,0BACAZ,SACAC,mCACES;EACJ,MAAM,EAAER,KAAKR,QAAQ,GAAGmB,uBAAuBF;AAE/C,OAAKG,aAAa;GAChB;GACA;GACA;GACA;GACA;GACD;AAED,OAAKpB,SAASA;AACd,OAAKQ,MAAMA;AACX,OAAKT,SAAS,IAAIpB,GAAGwC,mBAAmB;AACxC,OAAKL,iBAAiBO,OAAOF,mBAAmBG,SAAS;AAEzD,OAAKrB,WAAWA,YAAY,KAAKA;AACjC,OAAKC,cAAcA,eAAe,KAAKA;AACvC,OAAKC,oBAAoBA,qBAAqB,KAAKA;AAEnD,OAAKG,UAAUA,WAAW,KAAKA;AAC/B,OAAKC,iCACHA,kCAAkC,KAAKA;AACzC,OAAKE,sBAAsB,IAAItB,UAAU+B,4BAA4B,GAAG;AAGxE,OAAKR,kBAAkB,IAAIpB,kBACzB,KAAKS,QACL,KAAKC,QACL,KAAKuB,wBAAwBC,KAAK,KAAK,EACvC,KAAKC,oBAAoBD,KAAK,KAChC,CAAC;AAED,OAAKb,iBAAiB,IAAItB,iBACxB,KAAKc,mBACL,KAAKC,eACL,KAAKF,aACL,KAAKD,SACN;AAED,OAAKW,eAAe,IAAIrB,eACtB,KAAKQ,QACL,KAAKC,QACL,KAAKE,aACL,KAAKO,qBACL,KAAKC,iBACL,KAAKC,gBACL,KAAKc,oBAAoBD,KAAK,KAChC,CAAC;AAED,OAAKX,oBAAoB,IAAIzB,oBAC3B,KAAKW,QACL,KAAKC,QACL,KAAKO,gCACL,KAAKG,gBAAgBgB,gBAAgBF,KAAK,KAAKd,gBAAgB,EAC/D,KAAKA,gBAAgBiB,gBAAgBH,KAAK,KAAKd,gBACjD,CAAC;AAGD,OAAKkB,eAAe;;;;;;CAOtB,AAAUL,0BAAmC;AAC3C,SAAO,KAAKhB,mCAAmC,KAAK,KAAKD;;;;;;;CAQ3D,AAAUmB,oBAAoBI,OAA6C;AACzE,MAAI,CAAC,KAAKN,yBAAyB,CACjC;AAEF,SAAO,iBAAiBM;;;;;;;;;CAU1B,MAAaE,OAAOC,QAAiC;AACnDnC,MAAIL,eAAe0C,wBAAwB;EAE3C,MAAMC,UAAiD;GACrDE,QAAQ,KAAKrC;GACbsC,KAAK,KAAK5B,gBAAgBiB,gBAAgB;IACxCY,IAAIP,OAAOO;IACXC,QAAQR,OAAOS,UAAUD,UAAUV;IACpC,CAAC;GACFY,UAAU,EAAE,eAAe5D,eAAc;GAC1C;AAED,MAAIkD,OAAOS,UAAUE,YACnBR,SAAQS,cAAcZ,OAAOS,SAASE;AAGxC,MAAIX,OAAOS,UAAUI,aACnBV,SAAQW,eAAed,OAAOS,SAASI;AAGzC,MAAI,KAAKrC,IACP2B,SAAQY,MAAM,KAAKvC;AAGrBwB,SAAOiB,iCAAgB,IAAIC,MAAM,EAACC,aAAa;EAE/C,MAAMC,WAAW,MAAM,KAAKrD,OAAOsD,sBAAsBlB,QAAQ;AAEjEH,SAAOsB,UAAU;GACfC,MAAM;GACNvD,QAAQ,KAAKA;GACbwD,MAAMJ,SAASd;GAChB;AACD,QAAM,KAAK5B,gBAAgB+C,aAAa;GACtCzB;GACA0B,UAAUN,SAASO;GACpB,CAAC;AACF9D,MAAIL,eAAeoE,2BAA2B;AAE9C,SAAO5B;;;;;;;;;CAUT,MAAa6B,MACXC,UACAvB,IACAwB,QACiB;EACjB,MAAMtB,WAAW,MAAM,KAAK/B,gBAAgBsD,YAAY,EAAEzB,IAAI,CAAC;EAI/D,MAAM0B,mBAAmB,KAAKtD,eAAeuD,yBAAyB,EACpEC,OAAO,MAAM,KAAKvD,aAAawD,cAAc,EAAE7B,IAAI,CAAA,EACpD,CAAC;EACF,MAAM8B,aAAaN,SAASE;EAC5B,MAAMK,kBAAkBP;EAExB,IAAIQ,gBAAgBT;AAEpB,MAAIO,aAAa,EACf,OAAMxF,OAAO2F;AAMf,MAAIH,aAAa,GAAG;GAClB,MAAMI,iBAAiB,MAAM,KAAK7D,aAAa8D,uBAAuB,EACpEnC,IACD,CAAC;AAEF,OAAI,CAACkC,eACH,OAAM5F,OAAO2F;AAGf,OAAIC,eAAeE,SAASN,WAC1B,OAAMxF,OAAO2F;AAIf,SAAM,KAAK5D,aAAagE,qBAAqB,EAAErC,IAAI,CAAC;AAGpDwB,YAASO,kBAAkBG,eAAeE;AAE1CJ,mBAAgBvF,OAAOC,SAAS4F,KAC7B,mBAAmB;AAClB,WAAOJ,eAAeK,aAAa,EAAEC,cAAc,MAAM,CAAC;AAC1D,WAAOjB;MAEX,CAAC;;EAGH,MAAMkB,aAAa,KAAKrE,eAAesE,oBAAoB,EACzDd,OAAO,MAAM,KAAKvD,aAAawD,cAAc,EAAE7B,IAAI,CAAA,EACpD,CAAC;EAWF,MAAM+C,YACJhB,kBAVoB,MAAM,KAAK1D,aAAauE,YAAY;GACxD1C;GACA2C,YAAYb;GACZc,mBAAmBL;GACnBjB;GACD,CAAC,IAKmCM,aAAa,IAAIA,aAAa;AAGnE,MAAI5B,SAAS8C,KAAKZ,SAASW,UACzB,KAAI;GACF,MAAMnB,QAAQ,MAAM,KAAKvD,aAAawD,cAAc,EAAE7B,IAAI,CAAC;AAC3D,SAAM,KAAK3B,aAAa4E,sBAAsB;IAAE/C;IAAU0B;IAAO,CAAC;GAGlE,MAAMsB,kBAAkB,IAAI1G,OAAO;IACjC,GAAG0D,SAAS8C;IACZxB,QAAQuB;IACRX,MAAMlC,SAAS8C,KAAKZ;IACpBrB,SAASb,SAAS8C,KAAKjC;IACxB,CAAC;AAEF,SAAM,KAAK5C,gBAAgBgF,iBAAiB,EAAE1D,QAAQyD,iBAAiB,CAAC;WACjEE,OAAO;AACd9F,OAAIL,eAAeoG,uBAAuB;AAC1C,SAAMD;;AAIV,SAAOL;;;;;;;;CAST,MAAaO,UAAUtD,IAA6B;EAClD,IAAIE;AAEJ,MAAI;AACFA,cAAW,MAAM,KAAK/B,gBAAgBsD,YAAY,EAAEzB,IAAI,CAAC;WAClDoD,OAAO;AACd,OACEA,iBAAiBlH,aACjBkH,iBAAiBjH,YAChBiH,OAAoBG,SAAS,cAC7BH,OAAoBG,SAAS,YAE9B,OAAMjH,OAAOkH;AAEf,SAAMJ;;EAGR,IAAI5B;AAEJ,MAAI;GACF,MAAMI,QAAQ,MAAM,KAAKvD,aAAawD,cAAc,EAAE7B,IAAI,CAAC;AAC3DwB,YAAS,KAAKpD,eAAeuD,yBAAyB,EAAEC,OAAO,CAAC;WACzDwB,OAAO;AAGd,OACGA,OAAoBG,SAAS,kBAC7BH,OAAoBG,SAAS,YAE9B,QAAO,IAAI/G,OAAO;IAChB,GAAG0D,SAAS8C;IACZ9C,UAAUA,SAAS8C,KAAK9C;IACxBsB,QAAQtB,SAAS8C,KAAKZ;IACtBA,MAAMlC,SAAS8C,KAAKZ;IACpBrB,SAASb,SAAS8C,KAAKjC;IACxB,CAAC;AAGJzD,OAAIL,eAAewG,8BAA8B;AACjD,SAAML;;EAGR,MAAMM,qBAAqB,MAAM,KAAKrF,aAAasF,sBAAsB,EACvE3D,IACD,CAAC;AAEF,SAAO,IAAIxD,OAAO;GAChB,GAAG0D,SAAS8C;GACZxB,QAAQA,UAAUkC,sBAAsB;GACxCtB,MAAMlC,SAAS8C,KAAKZ;GACpBrB,SAASb,SAAS8C,KAAKjC;GACxB,CAAC;;;;;;;CAQJ,MAAM6C,KAAK5D,IAA+B;AACxC1C,MAAIL,eAAe4G,sBAAsB;EACzC,IAAIC,UAAU;EACd,IAAIC,YAA0B;AAE9B,SAAOD,UAAU,EACf,KAAI;GACF,MAAMG,OAAO,MAAM,KAAKzG,OAAO0G,UAAU;IACvCpE,QAAQ,KAAKrC;IACbsC,KAAKC;IACN,CAAC;AACF1C,OAAIL,eAAekH,sBAAsB;AACzC,UAAOF,KAAKG;WACLhB,OAAO;AACd9F,OAAIL,eAAeoH,oBAAoB;AACvCN,eAAYX;AACZU;AAEA,OAAIA,UAAU,EAEZ,OAAM,IAAIpE,SAAS4E,YAAYC,WAAWD,SAAS,IAAI,CAAC;;AAK9DhH,MAAIL,eAAeuH,qBAAqB;AACxC,QAAMT,6BAAa,IAAIC,MAAM,uBAAuBhE,KAAK;;;;;;;;;CAU3D,MAAayE,KAAKC,QAAgBC,QAA+B;AAC/D,MAAI;AACF,SAAM,KAAKnH,OAAOoH,WAAW;IAC3B9E,QAAQ,KAAKrC;IACboH,YAAYC,UAAU,GAAG,KAAKrH,OAAM,GAAIiH,SAAS;IACjD3E,KAAK4E;IACN,CAAC;AAEF,SAAM,KAAKnH,OAAOuH,aAAa;IAC7BjF,QAAQ,KAAKrC;IACbsC,KAAK2E;IACN,CAAC;WACKtB,OAAO;AACd,OACGA,OAAoB4B,QACrB;IAAC;IAAa;IAAgB;IAAW,CAACC,SACvC7B,MAAmBG,QAAQ,GAC7B,EACD;AACAjG,QAAIL,eAAeiI,wBAAwB;AAC3C,UAAM5I,OAAOkH;;AAEf,SAAMJ;;;;;;;;CASV,MAAa+B,OAAOnF,IAA2B;AAC7C,MAAI;GACF,MAAM,EAAE,aAAamB,UAAU6B,SAC7B,MAAM,KAAK7E,gBAAgBsD,YAAY,EAAEzB,IAAI,CAAC;AAChD,OAAImB,SACF,OAAM,KAAK3D,OAAO4H,qBAAqB;IACrCtF,QAAQ,KAAKrC;IACbsC,KAAKC;IACLoB,UAAUD;IACX,CAAC;AAGJ,SAAM,KAAK3D,OAAO6H,cAAc;IAC9BvF,QAAQ,KAAKrC;IACb6H,QAAQ,EACNC,SAAS;KACP,EACExF,KAAK,KAAK5B,gBAAgBiB,gBAAgB;MACxCY;MACAC,QAAQ+C,MAAM9C,UAAUD,UAAUV;MACnC,CAAA,EACF;KACD,EACEQ,KAAK,KAAK5B,gBAAgBiB,gBAAgB;MACxCY;MACAC,QAAQ+C,MAAM9C,UAAUD,UAAUV;MAClCiG,cAAc;MACf,CAAA,EACF;KACD,EACEzF,KAAK,KAAK5B,gBAAgBgB,gBAAgB,EAAEa,IAAI,CAAA,EACjD;KAAA,EAEL;IACD,CAAC;WACKoD,OAAO;AACd,OACGA,OAAoB4B,QACrB;IAAC;IAAa;IAAgB;IAAW,CAACC,SACvC7B,MAAmBG,QAAQ,GAC7B,EACD;AACAjG,QAAIL,eAAeiI,wBAAwB;AAC3C,UAAM5I,OAAOkH;;AAEf,SAAMJ;;;;;;;;CASV,MAAaqC,QAAQzF,IAA2B;AAC9C,MAAI;GACF,MAAM,EAAEgD,SAAS,MAAM,KAAK7E,gBAAgBsD,YAAY,EAAEzB,IAAI,CAAC;AAE/D,SAAM,KAAKxC,OAAO6H,cAAc;IAC9BvF,QAAQ,KAAKrC;IACb6H,QAAQ,EACNC,SAAS,CACP,EACExF,KAAK,KAAK5B,gBAAgBiB,gBAAgB;KACxCY;KACAC,QAAQ+C,MAAM9C,UAAUD,UAAUV;KAClCiG,cAAc;KACf,CAAA,EACF,EACD,EACEzF,KAAK,KAAK5B,gBAAgBgB,gBAAgB,EAAEa,IAAI,CAAA,EACjD,CAAA,EAEL;IACD,CAAC;WACKoD,OAAO;AACd,OACGA,OAAoB4B,QACrB;IAAC;IAAa;IAAgB;IAAW,CAACC,SACvC7B,MAAmBG,QAAQ,GAC7B,EACD;AACAjG,QAAIL,eAAeiI,wBAAwB;AAC3C,UAAM5I,OAAOkH;;AAEf,SAAMJ;;;;;;;;CASV,MAAasC,wBAAwB1F,IAA6B;EAChE,MAAME,WAAW,MAAM,KAAK/B,gBAAgBsD,YAAY,EAAEzB,IAAI,CAAC;EAC/D,MAAM4B,QAAQ,MAAM,KAAKvD,aAAawD,cAAc,EAAE7B,IAAI,CAAC;EAE3D,MAAM2F,qBAAqB,MAAM,KAAKtH,aAAa8D,uBAAuB,EACxEnC,IACD,CAAC;AAEF,MAAI2F,oBAAoB;AAEtB,SAAM,KAAKtH,aAAauH,WAAW;IACjC1F;IACA2C,YAAY8C,mBAAmBpD,aAAa,EAAEC,cAAc,MAAM,CAAC;IACnEC,YAAYb,MAAMiE,SAAS;IAC5B,CAAC;AAGF,SAAM,KAAKxH,aAAagE,qBAAqB,EAAErC,IAAI,CAAC;GAGpD,MAAM8F,eAAe,MAAM,KAAKzH,aAAawD,cAAc,EAAE7B,IAAI,CAAC;AAClE,SAAM,KAAK3B,aAAa4E,sBAAsB;IAC5C/C;IACA0B,OAAOkE;IACR,CAAC;QAEF,OAAM,KAAKzH,aAAa4E,sBAAsB;GAAE/C;GAAU0B;GAAO,CAAC;EAGpE,MAAMsB,kBAAkB,IAAI1G,OAAO;GACjC,GAAG0D,SAAS8C;GACZxB,QAAQtB,SAAS8C,KAAKZ,QAAQ;GAC9BA,MAAMlC,SAAS8C,KAAKZ,QAAQ;GAC5BrB,SAASb,SAAS8C,KAAKjC;GACxB,CAAC;AAEF,QAAM,KAAK5C,gBAAgBgF,iBAAiB,EAAE1D,QAAQyD,iBAAiB,CAAC;AAExE,SAAOA;;;;;;;CAQT,AAAO6C,OAAO/F,IAAoB;AAEhC,MAAI,KAAKzB,eACP,QAAO,GAAG,KAAKA,eAAc,GAAI,KAAKd,OAAM,GAAIuC;EAIlD,MAAMgG,eAAe,KAAKxI,OAAOyI,OAAOC;EACxC,IAAIA;AAGJ,MAAI,OAAOF,iBAAiB,WAC1BE,UAAS;MAETA,UAASF,gBAAgB;AAI3B,MAAIE,WAAW,YACb,QAAO,WAAW,KAAKzI,OAAM,oBAAqBuC;MAElD,QAAO,WAAW,KAAKvC,OAAM,MAAOyI,OAAM,iBAAkBlG;;;;;;;CAShE,MAAMX,gBAAiC;AACrC,SAAO,KAAKf,kBAAkBe,eAAe;;;;;;CAO/C8G,gBAAwB;AACtB,SAAO,KAAK7H,kBAAkB6H,eAAe"}
@@ -1 +1 @@
1
- {"version":3,"file":"semaphore.mjs","names":[],"sources":["../../../../src/tus/stores/s3/semaphore.ts"],"sourcesContent":["/**\n * A semaphore implementation for controlling concurrent operations.\n * Used to limit the number of simultaneous part uploads to S3.\n */\nexport class Semaphore {\n private permits: number\n private queue: (() => void)[] = []\n\n constructor(permits: number) {\n this.permits = permits\n }\n\n private release(): void {\n this.permits++\n const next = this.queue.shift()\n if (next) {\n next()\n }\n }\n\n async acquire(): Promise<() => void> {\n return new Promise((resolve) => {\n if (this.permits > 0) {\n this.permits--\n resolve(() => this.release())\n } else {\n this.queue.push(() => {\n this.permits--\n resolve(() => this.release())\n })\n }\n })\n }\n}\n\nexport type SemaphorePermit = () => void\n"],"mappings":";;;;;AAIA,IAAa,YAAb,MAAuB;CAIrB,YAAY,SAAiB;eAFG,EAAE;AAGhC,OAAK,UAAU;;CAGjB,AAAQ,UAAgB;AACtB,OAAK;EACL,MAAM,OAAO,KAAK,MAAM,OAAO;AAC/B,MAAI,KACF,OAAM;;CAIV,MAAM,UAA+B;AACnC,SAAO,IAAI,SAAS,YAAY;AAC9B,OAAI,KAAK,UAAU,GAAG;AACpB,SAAK;AACL,kBAAc,KAAK,SAAS,CAAC;SAE7B,MAAK,MAAM,WAAW;AACpB,SAAK;AACL,kBAAc,KAAK,SAAS,CAAC;KAC7B;IAEJ"}
1
+ {"version":3,"file":"semaphore.mjs","names":["Semaphore","permits","queue","constructor","release","next","shift","acquire","Promise","resolve","push","SemaphorePermit"],"sources":["../../../../src/tus/stores/s3/semaphore.ts"],"sourcesContent":["/**\n * A semaphore implementation for controlling concurrent operations.\n * Used to limit the number of simultaneous part uploads to S3.\n */\nexport class Semaphore {\n private permits: number\n private queue: (() => void)[] = []\n\n constructor(permits: number) {\n this.permits = permits\n }\n\n private release(): void {\n this.permits++\n const next = this.queue.shift()\n if (next) {\n next()\n }\n }\n\n async acquire(): Promise<() => void> {\n return new Promise((resolve) => {\n if (this.permits > 0) {\n this.permits--\n resolve(() => this.release())\n } else {\n this.queue.push(() => {\n this.permits--\n resolve(() => this.release())\n })\n }\n })\n }\n}\n\nexport type SemaphorePermit = () => void\n"],"mappings":";;;;;AAIA,IAAaA,YAAb,MAAuB;CAIrBG,YAAYF,SAAiB;eAFG,EAAE;AAGhC,OAAKA,UAAUA;;CAGjB,AAAQG,UAAgB;AACtB,OAAKH;EACL,MAAMI,OAAO,KAAKH,MAAMI,OAAO;AAC/B,MAAID,KACFA,OAAM;;CAIV,MAAME,UAA+B;AACnC,SAAO,IAAIC,SAASC,YAAY;AAC9B,OAAI,KAAKR,UAAU,GAAG;AACpB,SAAKA;AACLQ,kBAAc,KAAKL,SAAS,CAAC;SAE7B,MAAKF,MAAMQ,WAAW;AACpB,SAAKT;AACLQ,kBAAc,KAAKL,SAAS,CAAC;KAC7B;IAEJ"}
@@ -44,6 +44,18 @@ declare const MediaCloudErrors: {
44
44
  readonly message: "Error deleting file from S3";
45
45
  readonly errorCode: 500;
46
46
  };
47
+ readonly S3_RENAME_ERROR: {
48
+ readonly message: "Error renaming file in S3";
49
+ readonly errorCode: 500;
50
+ };
51
+ readonly S3_MOVE_ERROR: {
52
+ readonly message: "Error moving file in S3";
53
+ readonly errorCode: 500;
54
+ };
55
+ readonly S3_PREFIX_ERROR: {
56
+ readonly message: "Error updating S3 prefix";
57
+ readonly errorCode: 500;
58
+ };
47
59
  readonly S3_GET_OBJECT_ERROR: {
48
60
  readonly message: "Error getting object from S3";
49
61
  readonly errorCode: 500;
@@ -64,6 +76,10 @@ declare const MediaCloudErrors: {
64
76
  readonly message: "Unknown storage type for media";
65
77
  readonly errorCode: 500;
66
78
  };
79
+ readonly COLLECTION_REQUIRED: {
80
+ readonly message: "A collection must be specified in the plugin options";
81
+ readonly errorCode: 400;
82
+ };
67
83
  readonly FILE_TYPE_UNKNOWN: {
68
84
  readonly message: "Unable to determine file type";
69
85
  readonly errorCode: 400;
@@ -72,6 +88,18 @@ declare const MediaCloudErrors: {
72
88
  readonly message: "Error determining file type";
73
89
  readonly errorCode: 500;
74
90
  };
91
+ readonly FILE_TYPE_UNSUPPORTED_ERROR: {
92
+ readonly message: "Unsupported file type";
93
+ readonly errorCode: 400;
94
+ };
95
+ readonly FILE_TYPE_NOT_ALLOWED: {
96
+ readonly message: "File type not allowed";
97
+ readonly errorCode: 400;
98
+ };
99
+ readonly FILE_SIZE_EXCEEDED: {
100
+ readonly message: "File size exceeds the maximum allowed limit";
101
+ readonly errorCode: 400;
102
+ };
75
103
  readonly FILENAME_SANITIZE_ERROR: {
76
104
  readonly message: "Error sanitizing filename";
77
105
  readonly errorCode: 500;
@@ -108,6 +136,10 @@ declare const MediaCloudErrors: {
108
136
  readonly message: "Error in namingFunction";
109
137
  readonly errorCode: 500;
110
138
  };
139
+ readonly FOLDER_FETCH_ERROR: {
140
+ readonly message: "Error fetching folder data";
141
+ readonly errorCode: 500;
142
+ };
111
143
  };
112
144
  declare enum MediaCloudLogs {
113
145
  S3_STORE_MULTIPART_INIT = "Initializing multipart upload",
@@ -44,6 +44,18 @@ const MediaCloudErrors = {
44
44
  message: "Error deleting file from S3",
45
45
  errorCode: 500
46
46
  },
47
+ S3_RENAME_ERROR: {
48
+ message: "Error renaming file in S3",
49
+ errorCode: 500
50
+ },
51
+ S3_MOVE_ERROR: {
52
+ message: "Error moving file in S3",
53
+ errorCode: 500
54
+ },
55
+ S3_PREFIX_ERROR: {
56
+ message: "Error updating S3 prefix",
57
+ errorCode: 500
58
+ },
47
59
  S3_GET_OBJECT_ERROR: {
48
60
  message: "Error getting object from S3",
49
61
  errorCode: 500
@@ -64,6 +76,10 @@ const MediaCloudErrors = {
64
76
  message: "Unknown storage type for media",
65
77
  errorCode: 500
66
78
  },
79
+ COLLECTION_REQUIRED: {
80
+ message: "A collection must be specified in the plugin options",
81
+ errorCode: 400
82
+ },
67
83
  FILE_TYPE_UNKNOWN: {
68
84
  message: "Unable to determine file type",
69
85
  errorCode: 400
@@ -72,6 +88,18 @@ const MediaCloudErrors = {
72
88
  message: "Error determining file type",
73
89
  errorCode: 500
74
90
  },
91
+ FILE_TYPE_UNSUPPORTED_ERROR: {
92
+ message: "Unsupported file type",
93
+ errorCode: 400
94
+ },
95
+ FILE_TYPE_NOT_ALLOWED: {
96
+ message: "File type not allowed",
97
+ errorCode: 400
98
+ },
99
+ FILE_SIZE_EXCEEDED: {
100
+ message: "File size exceeds the maximum allowed limit",
101
+ errorCode: 400
102
+ },
75
103
  FILENAME_SANITIZE_ERROR: {
76
104
  message: "Error sanitizing filename",
77
105
  errorCode: 500
@@ -107,6 +135,10 @@ const MediaCloudErrors = {
107
135
  NAMING_FUNCTION_ERROR: {
108
136
  message: "Error in namingFunction",
109
137
  errorCode: 500
138
+ },
139
+ FOLDER_FETCH_ERROR: {
140
+ message: "Error fetching folder data",
141
+ errorCode: 500
110
142
  }
111
143
  };
112
144
  let MediaCloudLogs = /* @__PURE__ */ function(MediaCloudLogs$1) {
@@ -1 +1 @@
1
- {"version":3,"file":"errors.mjs","names":[],"sources":["../../src/types/errors.ts"],"sourcesContent":["export const MediaCloudErrors = {\n // Mux Errors\n MUX_CONFIG_MISSING: {\n message:\n 'Mux configuration (tokenId and tokenSecret) must be provided in pluginOptions to use Mux',\n errorCode: 400,\n },\n MUX_CONFIG_INCOMPLETE: {\n message: 'Mux configuration is missing. Mux features will not be available',\n errorCode: 400,\n },\n MUX_UPLOAD_ID_MISSING: {\n message: 'No upload-id found for upload',\n errorCode: 400,\n },\n MUX_ASSET_DELETE_ERROR: {\n message: 'Error deleting Mux asset',\n errorCode: 500,\n },\n MUX_UPLOAD_ERROR: {\n message: 'Mux video upload failed',\n errorCode: 500,\n },\n MUX_DIRECT_UPLOAD_ERROR: {\n message: 'Mux direct upload failed',\n errorCode: 500,\n },\n MUX_CREATE_UPLOAD_ERROR: {\n message: 'Error in Mux create upload handler',\n errorCode: 500,\n },\n MUX_REQUEST_NO_JSON: {\n message: 'Request does not support json() method',\n errorCode: 400,\n },\n MUX_WEBHOOK_BODY_INVALID: {\n message: 'Invalid Mux webhook body',\n errorCode: 400,\n },\n\n // S3 Errors\n S3_CONFIG_MISSING: {\n message:\n 'S3 configuration (bucket, region, accessKeyId, secretAccessKey) must be provided in pluginOptions',\n errorCode: 400,\n },\n S3_DELETE_ERROR: {\n message: 'Error deleting file from S3',\n errorCode: 500,\n },\n S3_GET_OBJECT_ERROR: {\n message: 'Error getting object from S3',\n errorCode: 500,\n },\n S3_UNIQUE_NAME_ERROR: {\n message: 'Could not find a unique file name after maximum tries',\n errorCode: 500,\n },\n\n // TUS Errors\n TUS_UPLOAD_ERROR: {\n message: 'TUS file upload error occurred',\n errorCode: 500,\n },\n TUS_CLEANUP_ERROR: {\n message: 'Error during cleanup',\n errorCode: 500,\n },\n\n // General Errors\n UNKNOWN_STORAGE_TYPE: {\n message: 'Unknown storage type for media',\n errorCode: 500,\n },\n\n // File Errors\n FILE_TYPE_UNKNOWN: {\n message: 'Unable to determine file type',\n errorCode: 400,\n },\n FILE_TYPE_ERROR: {\n message: 'Error determining file type',\n errorCode: 500,\n },\n FILENAME_SANITIZE_ERROR: {\n message: 'Error sanitizing filename',\n errorCode: 500,\n },\n FILE_MISSING_NAME: {\n message: 'Filename is missing, cannot parse file',\n errorCode: 500,\n },\n FILE_NOT_FOUND: {\n message: 'File not found',\n errorCode: 404,\n },\n FILE_DIMENSIONS_ERROR: {\n message: 'Error setting media dimensions',\n errorCode: 500,\n },\n\n // Upload Errors\n UPLOAD_NO_URL: {\n message: 'No upload URL provided, cannot parse upload ID',\n errorCode: 400,\n },\n UPLOAD_HANDLER_ERROR: {\n message: 'Upload handler error occurred',\n errorCode: 500,\n },\n UPLOAD_POLLING_ERROR: {\n message: 'Polling error for upload',\n errorCode: 500,\n },\n\n // Plugin Errors\n PLUGIN_NOT_CONFIGURED: {\n message: 'Payload Media Cloud plugin is not configured',\n errorCode: 500,\n },\n NAMING_FUNCTION_ERROR: {\n message: 'Error in namingFunction',\n errorCode: 500,\n },\n} as const\n\nexport enum MediaCloudLogs {\n S3_STORE_MULTIPART_INIT = 'Initializing multipart upload',\n S3_STORE_MULTIPART_CREATED = 'Multipart upload created',\n S3_STORE_UPLOAD_FAILED = 'Failed to finish upload',\n S3_STORE_READ_ATTEMPT = 'Attempting to read file from S3',\n S3_STORE_READ_SUCCESS = 'Successfully read file from S3',\n S3_STORE_READ_RETRY = 'Failed to read file, trying again',\n S3_STORE_READ_FAILED = 'Failed to read file',\n S3_STORE_FILE_NOT_FOUND = 'No file found',\n S3_STORE_FILE_FOUND = 'File found',\n S3_STORE_RETRIEVE_PARTS_ERROR = 'Error retrieving parts',\n S3_STORE_METADATA_SAVING = 'Saving metadata',\n S3_STORE_METADATA_SAVED = 'Metadata file saved',\n S3_STORE_INCOMPLETE_PART_UPLOADED = 'Finished uploading incomplete part',\n S3_STORE_CHUNK_REMOVAL_FAILED = 'Failed to remove chunk',\n}\n"],"mappings":";AAAA,MAAa,mBAAmB;CAE9B,oBAAoB;EAClB,SACE;EACF,WAAW;EACZ;CACD,uBAAuB;EACrB,SAAS;EACT,WAAW;EACZ;CACD,uBAAuB;EACrB,SAAS;EACT,WAAW;EACZ;CACD,wBAAwB;EACtB,SAAS;EACT,WAAW;EACZ;CACD,kBAAkB;EAChB,SAAS;EACT,WAAW;EACZ;CACD,yBAAyB;EACvB,SAAS;EACT,WAAW;EACZ;CACD,yBAAyB;EACvB,SAAS;EACT,WAAW;EACZ;CACD,qBAAqB;EACnB,SAAS;EACT,WAAW;EACZ;CACD,0BAA0B;EACxB,SAAS;EACT,WAAW;EACZ;CAGD,mBAAmB;EACjB,SACE;EACF,WAAW;EACZ;CACD,iBAAiB;EACf,SAAS;EACT,WAAW;EACZ;CACD,qBAAqB;EACnB,SAAS;EACT,WAAW;EACZ;CACD,sBAAsB;EACpB,SAAS;EACT,WAAW;EACZ;CAGD,kBAAkB;EAChB,SAAS;EACT,WAAW;EACZ;CACD,mBAAmB;EACjB,SAAS;EACT,WAAW;EACZ;CAGD,sBAAsB;EACpB,SAAS;EACT,WAAW;EACZ;CAGD,mBAAmB;EACjB,SAAS;EACT,WAAW;EACZ;CACD,iBAAiB;EACf,SAAS;EACT,WAAW;EACZ;CACD,yBAAyB;EACvB,SAAS;EACT,WAAW;EACZ;CACD,mBAAmB;EACjB,SAAS;EACT,WAAW;EACZ;CACD,gBAAgB;EACd,SAAS;EACT,WAAW;EACZ;CACD,uBAAuB;EACrB,SAAS;EACT,WAAW;EACZ;CAGD,eAAe;EACb,SAAS;EACT,WAAW;EACZ;CACD,sBAAsB;EACpB,SAAS;EACT,WAAW;EACZ;CACD,sBAAsB;EACpB,SAAS;EACT,WAAW;EACZ;CAGD,uBAAuB;EACrB,SAAS;EACT,WAAW;EACZ;CACD,uBAAuB;EACrB,SAAS;EACT,WAAW;EACZ;CACF;AAED,IAAY,4DAAL;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
1
+ {"version":3,"file":"errors.mjs","names":["MediaCloudErrors","MUX_CONFIG_MISSING","message","errorCode","MUX_CONFIG_INCOMPLETE","MUX_UPLOAD_ID_MISSING","MUX_ASSET_DELETE_ERROR","MUX_UPLOAD_ERROR","MUX_DIRECT_UPLOAD_ERROR","MUX_CREATE_UPLOAD_ERROR","MUX_REQUEST_NO_JSON","MUX_WEBHOOK_BODY_INVALID","S3_CONFIG_MISSING","S3_DELETE_ERROR","S3_RENAME_ERROR","S3_MOVE_ERROR","S3_PREFIX_ERROR","S3_GET_OBJECT_ERROR","S3_UNIQUE_NAME_ERROR","TUS_UPLOAD_ERROR","TUS_CLEANUP_ERROR","UNKNOWN_STORAGE_TYPE","COLLECTION_REQUIRED","FILE_TYPE_UNKNOWN","FILE_TYPE_ERROR","FILE_TYPE_UNSUPPORTED_ERROR","FILE_TYPE_NOT_ALLOWED","FILE_SIZE_EXCEEDED","FILENAME_SANITIZE_ERROR","FILE_MISSING_NAME","FILE_NOT_FOUND","FILE_DIMENSIONS_ERROR","UPLOAD_NO_URL","UPLOAD_HANDLER_ERROR","UPLOAD_POLLING_ERROR","PLUGIN_NOT_CONFIGURED","NAMING_FUNCTION_ERROR","FOLDER_FETCH_ERROR","const","MediaCloudLogs","S3_STORE_MULTIPART_INIT","S3_STORE_MULTIPART_CREATED","S3_STORE_UPLOAD_FAILED","S3_STORE_READ_ATTEMPT","S3_STORE_READ_SUCCESS","S3_STORE_READ_RETRY","S3_STORE_READ_FAILED","S3_STORE_FILE_NOT_FOUND","S3_STORE_FILE_FOUND","S3_STORE_RETRIEVE_PARTS_ERROR","S3_STORE_METADATA_SAVING","S3_STORE_METADATA_SAVED","S3_STORE_INCOMPLETE_PART_UPLOADED","S3_STORE_CHUNK_REMOVAL_FAILED"],"sources":["../../src/types/errors.ts"],"sourcesContent":["export const MediaCloudErrors = {\n // Mux Errors\n MUX_CONFIG_MISSING: {\n message:\n 'Mux configuration (tokenId and tokenSecret) must be provided in pluginOptions to use Mux',\n errorCode: 400,\n },\n MUX_CONFIG_INCOMPLETE: {\n message: 'Mux configuration is missing. Mux features will not be available',\n errorCode: 400,\n },\n MUX_UPLOAD_ID_MISSING: {\n message: 'No upload-id found for upload',\n errorCode: 400,\n },\n MUX_ASSET_DELETE_ERROR: {\n message: 'Error deleting Mux asset',\n errorCode: 500,\n },\n MUX_UPLOAD_ERROR: {\n message: 'Mux video upload failed',\n errorCode: 500,\n },\n MUX_DIRECT_UPLOAD_ERROR: {\n message: 'Mux direct upload failed',\n errorCode: 500,\n },\n MUX_CREATE_UPLOAD_ERROR: {\n message: 'Error in Mux create upload handler',\n errorCode: 500,\n },\n MUX_REQUEST_NO_JSON: {\n message: 'Request does not support json() method',\n errorCode: 400,\n },\n MUX_WEBHOOK_BODY_INVALID: {\n message: 'Invalid Mux webhook body',\n errorCode: 400,\n },\n\n // S3 Errors\n S3_CONFIG_MISSING: {\n message:\n 'S3 configuration (bucket, region, accessKeyId, secretAccessKey) must be provided in pluginOptions',\n errorCode: 400,\n },\n S3_DELETE_ERROR: {\n message: 'Error deleting file from S3',\n errorCode: 500,\n },\n S3_RENAME_ERROR: {\n message: 'Error renaming file in S3',\n errorCode: 500,\n },\n S3_MOVE_ERROR: {\n message: 'Error moving file in S3',\n errorCode: 500,\n },\n S3_PREFIX_ERROR: {\n message: 'Error updating S3 prefix',\n errorCode: 500,\n },\n S3_GET_OBJECT_ERROR: {\n message: 'Error getting object from S3',\n errorCode: 500,\n },\n S3_UNIQUE_NAME_ERROR: {\n message: 'Could not find a unique file name after maximum tries',\n errorCode: 500,\n },\n\n // TUS Errors\n TUS_UPLOAD_ERROR: {\n message: 'TUS file upload error occurred',\n errorCode: 500,\n },\n TUS_CLEANUP_ERROR: {\n message: 'Error during cleanup',\n errorCode: 500,\n },\n\n // General Errors\n UNKNOWN_STORAGE_TYPE: {\n message: 'Unknown storage type for media',\n errorCode: 500,\n },\n COLLECTION_REQUIRED: {\n message: 'A collection must be specified in the plugin options',\n errorCode: 400,\n },\n\n // File Errors\n FILE_TYPE_UNKNOWN: {\n message: 'Unable to determine file type',\n errorCode: 400,\n },\n FILE_TYPE_ERROR: {\n message: 'Error determining file type',\n errorCode: 500,\n },\n FILE_TYPE_UNSUPPORTED_ERROR: {\n message: 'Unsupported file type',\n errorCode: 400,\n },\n FILE_TYPE_NOT_ALLOWED: {\n message: 'File type not allowed',\n errorCode: 400,\n },\n FILE_SIZE_EXCEEDED: {\n message: 'File size exceeds the maximum allowed limit',\n errorCode: 400,\n },\n FILENAME_SANITIZE_ERROR: {\n message: 'Error sanitizing filename',\n errorCode: 500,\n },\n FILE_MISSING_NAME: {\n message: 'Filename is missing, cannot parse file',\n errorCode: 500,\n },\n FILE_NOT_FOUND: {\n message: 'File not found',\n errorCode: 404,\n },\n FILE_DIMENSIONS_ERROR: {\n message: 'Error setting media dimensions',\n errorCode: 500,\n },\n\n // Upload Errors\n UPLOAD_NO_URL: {\n message: 'No upload URL provided, cannot parse upload ID',\n errorCode: 400,\n },\n UPLOAD_HANDLER_ERROR: {\n message: 'Upload handler error occurred',\n errorCode: 500,\n },\n UPLOAD_POLLING_ERROR: {\n message: 'Polling error for upload',\n errorCode: 500,\n },\n\n // Plugin Errors\n PLUGIN_NOT_CONFIGURED: {\n message: 'Payload Media Cloud plugin is not configured',\n errorCode: 500,\n },\n NAMING_FUNCTION_ERROR: {\n message: 'Error in namingFunction',\n errorCode: 500,\n },\n\n // HOOK Errors\n FOLDER_FETCH_ERROR: {\n message: 'Error fetching folder data',\n errorCode: 500,\n },\n} as const\n\nexport enum MediaCloudLogs {\n S3_STORE_MULTIPART_INIT = 'Initializing multipart upload',\n S3_STORE_MULTIPART_CREATED = 'Multipart upload created',\n S3_STORE_UPLOAD_FAILED = 'Failed to finish upload',\n S3_STORE_READ_ATTEMPT = 'Attempting to read file from S3',\n S3_STORE_READ_SUCCESS = 'Successfully read file from S3',\n S3_STORE_READ_RETRY = 'Failed to read file, trying again',\n S3_STORE_READ_FAILED = 'Failed to read file',\n S3_STORE_FILE_NOT_FOUND = 'No file found',\n S3_STORE_FILE_FOUND = 'File found',\n S3_STORE_RETRIEVE_PARTS_ERROR = 'Error retrieving parts',\n S3_STORE_METADATA_SAVING = 'Saving metadata',\n S3_STORE_METADATA_SAVED = 'Metadata file saved',\n S3_STORE_INCOMPLETE_PART_UPLOADED = 'Finished uploading incomplete part',\n S3_STORE_CHUNK_REMOVAL_FAILED = 'Failed to remove chunk',\n}\n"],"mappings":";AAAA,MAAaA,mBAAmB;CAE9BC,oBAAoB;EAClBC,SACE;EACFC,WAAW;EACZ;CACDC,uBAAuB;EACrBF,SAAS;EACTC,WAAW;EACZ;CACDE,uBAAuB;EACrBH,SAAS;EACTC,WAAW;EACZ;CACDG,wBAAwB;EACtBJ,SAAS;EACTC,WAAW;EACZ;CACDI,kBAAkB;EAChBL,SAAS;EACTC,WAAW;EACZ;CACDK,yBAAyB;EACvBN,SAAS;EACTC,WAAW;EACZ;CACDM,yBAAyB;EACvBP,SAAS;EACTC,WAAW;EACZ;CACDO,qBAAqB;EACnBR,SAAS;EACTC,WAAW;EACZ;CACDQ,0BAA0B;EACxBT,SAAS;EACTC,WAAW;EACZ;CAGDS,mBAAmB;EACjBV,SACE;EACFC,WAAW;EACZ;CACDU,iBAAiB;EACfX,SAAS;EACTC,WAAW;EACZ;CACDW,iBAAiB;EACfZ,SAAS;EACTC,WAAW;EACZ;CACDY,eAAe;EACbb,SAAS;EACTC,WAAW;EACZ;CACDa,iBAAiB;EACfd,SAAS;EACTC,WAAW;EACZ;CACDc,qBAAqB;EACnBf,SAAS;EACTC,WAAW;EACZ;CACDe,sBAAsB;EACpBhB,SAAS;EACTC,WAAW;EACZ;CAGDgB,kBAAkB;EAChBjB,SAAS;EACTC,WAAW;EACZ;CACDiB,mBAAmB;EACjBlB,SAAS;EACTC,WAAW;EACZ;CAGDkB,sBAAsB;EACpBnB,SAAS;EACTC,WAAW;EACZ;CACDmB,qBAAqB;EACnBpB,SAAS;EACTC,WAAW;EACZ;CAGDoB,mBAAmB;EACjBrB,SAAS;EACTC,WAAW;EACZ;CACDqB,iBAAiB;EACftB,SAAS;EACTC,WAAW;EACZ;CACDsB,6BAA6B;EAC3BvB,SAAS;EACTC,WAAW;EACZ;CACDuB,uBAAuB;EACrBxB,SAAS;EACTC,WAAW;EACZ;CACDwB,oBAAoB;EAClBzB,SAAS;EACTC,WAAW;EACZ;CACDyB,yBAAyB;EACvB1B,SAAS;EACTC,WAAW;EACZ;CACD0B,mBAAmB;EACjB3B,SAAS;EACTC,WAAW;EACZ;CACD2B,gBAAgB;EACd5B,SAAS;EACTC,WAAW;EACZ;CACD4B,uBAAuB;EACrB7B,SAAS;EACTC,WAAW;EACZ;CAGD6B,eAAe;EACb9B,SAAS;EACTC,WAAW;EACZ;CACD8B,sBAAsB;EACpB/B,SAAS;EACTC,WAAW;EACZ;CACD+B,sBAAsB;EACpBhC,SAAS;EACTC,WAAW;EACZ;CAGDgC,uBAAuB;EACrBjC,SAAS;EACTC,WAAW;EACZ;CACDiC,uBAAuB;EACrBlC,SAAS;EACTC,WAAW;EACZ;CAGDkC,oBAAoB;EAClBnC,SAAS;EACTC,WAAW;EACb;CACD;AAED,IAAYoC,4DAAL;AACLC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC;AACAC"}
@@ -5,26 +5,64 @@ import Mux from "@mux/mux-node";
5
5
  import { EventType } from "mitt";
6
6
 
7
7
  //#region src/types/index.d.ts
8
+ type ImageMimeType = 'image/apng' | 'image/avif' | 'image/gif' | 'image/jpeg' | 'image/png' | 'image/svg+xml' | 'image/webp';
9
+ type VideoMimeType = 'application/mxf' | 'video/3gpp' | 'video/3gpp2' | 'video/mp2t' | 'video/mp4' | 'video/mpeg' | 'video/quicktime' | 'video/webm' | 'video/x-f4v' | 'video/x-flv' | 'video/x-matroska' | 'video/x-ms-asf' | 'video/x-ms-wmv' | 'video/x-msvideo' | 'video/x-mxf';
10
+ type MimeType = ImageMimeType | VideoMimeType;
8
11
  interface MediaCloudPluginOptions {
9
12
  enabled?: boolean;
10
13
  collection?: string;
11
- mux: {
14
+ view?: 'grid' | 'list';
15
+ storage?: {
16
+ 'video/*': 'mux' | 's3';
17
+ 'application/mxf'?: 'mux' | 's3';
18
+ 'video/3gpp'?: 'mux' | 's3';
19
+ 'video/3gpp2'?: 'mux' | 's3';
20
+ 'video/mp2t'?: 'mux' | 's3';
21
+ 'video/mp4'?: 'mux' | 's3';
22
+ 'video/mpeg'?: 'mux' | 's3';
23
+ 'video/quicktime'?: 'mux' | 's3';
24
+ 'video/webm'?: 'mux' | 's3';
25
+ 'video/x-f4v'?: 'mux' | 's3';
26
+ 'video/x-flv'?: 'mux' | 's3';
27
+ 'video/x-matroska'?: 'mux' | 's3';
28
+ 'video/x-ms-asf'?: 'mux' | 's3';
29
+ 'video/x-ms-wmv'?: 'mux' | 's3';
30
+ 'video/x-msvideo'?: 'mux' | 's3';
31
+ 'video/x-mxf'?: 'mux' | 's3';
32
+ };
33
+ limits?: {
34
+ mimeTypes?: (MimeType | 'image/*' | 'video/*')[];
35
+ fileSize?: number;
36
+ };
37
+ folders?: boolean;
38
+ mux?: {
12
39
  assetOptions: Partial<Mux.Video.Assets.AssetOptions>;
13
40
  tokenId: string;
14
41
  tokenSecret: string;
15
42
  webhookSecret: string;
16
43
  testMode?: boolean;
17
44
  };
18
- s3: {
45
+ s3?: {
19
46
  accessKeyId: string;
20
47
  bucket: string;
21
48
  region: string;
22
49
  secretAccessKey: string;
23
- acl?: string;
50
+ prefix?: string;
51
+ acl?: ObjectCannedACL;
24
52
  endpoint?: string;
25
53
  respectForwardedHeaders?: boolean;
26
54
  };
27
55
  }
56
+ interface MediaCloudDefaultPluginOptions {
57
+ enabled: boolean;
58
+ collection: string;
59
+ view: 'grid';
60
+ folders: boolean;
61
+ storage: {
62
+ 'video/*': 'mux';
63
+ };
64
+ }
65
+ type Storage = 'mux' | 's3';
28
66
  interface MediaCloudEmitterEvents {
29
67
  [key: EventType]: Record<string, unknown>;
30
68
  addUpload: {
@@ -97,5 +135,5 @@ interface NodeFSError extends Error {
97
135
  }
98
136
  type StaticRenditions = Record<string, unknown> | null | undefined;
99
137
  //#endregion
100
- export { AWSError, IncompletePartInfo, MediaCloudEmitterEvents, MediaCloudPluginOptions, NodeFSError, S3StoreConfig, StaticRenditions, TusUploadMetadata };
138
+ export { AWSError, ImageMimeType, IncompletePartInfo, MediaCloudDefaultPluginOptions, MediaCloudEmitterEvents, MediaCloudPluginOptions, MimeType, NodeFSError, S3StoreConfig, StaticRenditions, Storage, TusUploadMetadata, VideoMimeType };
101
139
  //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1,10 @@
1
+ //#region src/utils/buildS3Path.d.ts
2
+ interface BuildS3PathData {
3
+ id: string | number;
4
+ name: unknown;
5
+ folder?: unknown;
6
+ }
7
+ declare function buildS3Path(data: BuildS3PathData): string;
8
+ //#endregion
9
+ export { buildS3Path };
10
+ //# sourceMappingURL=buildS3Path.d.mts.map
@@ -0,0 +1,16 @@
1
+ //#region src/utils/buildS3Path.ts
2
+ function buildS3Path(data) {
3
+ const parts = [];
4
+ function traverseFolder(current) {
5
+ if (!current || typeof current !== "object") return;
6
+ if (current.folder) traverseFolder(current.folder);
7
+ if (current.name && typeof current.name === "string") parts.push(current.name);
8
+ }
9
+ traverseFolder(data.folder);
10
+ if (data.name && typeof data.name === "string") parts.push(data.name);
11
+ return parts.join("/");
12
+ }
13
+
14
+ //#endregion
15
+ export { buildS3Path };
16
+ //# sourceMappingURL=buildS3Path.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildS3Path.mjs","names":["Folder","id","name","folder","BuildS3PathData","buildS3Path","data","parts","Array","traverseFolder","current","push","join"],"sources":["../../src/utils/buildS3Path.ts"],"sourcesContent":["interface Folder {\n id: string | number\n name: unknown\n folder?: unknown\n}\n\ninterface BuildS3PathData {\n id: string | number\n name: unknown\n folder?: unknown\n}\n\nexport function buildS3Path(data: BuildS3PathData): string {\n const parts: Array<string> = []\n\n function traverseFolder(current: Folder): void {\n if (!current || typeof current !== 'object') {\n return\n }\n\n if (current.folder) {\n traverseFolder(current.folder as Folder)\n }\n\n if (current.name && typeof current.name === 'string') {\n parts.push(current.name)\n }\n }\n\n traverseFolder(data.folder as Folder)\n\n if (data.name && typeof data.name === 'string') {\n parts.push(data.name)\n }\n\n return parts.join('/')\n}\n"],"mappings":";AAYA,SAAgBK,YAAYC,MAA+B;CACzD,MAAMC,QAAuB,EAAE;CAE/B,SAASE,eAAeC,SAAuB;AAC7C,MAAI,CAACA,WAAW,OAAOA,YAAY,SACjC;AAGF,MAAIA,QAAQP,OACVM,gBAAeC,QAAQP,OAAiB;AAG1C,MAAIO,QAAQR,QAAQ,OAAOQ,QAAQR,SAAS,SAC1CK,OAAMI,KAAKD,QAAQR,KAAK;;AAI5BO,gBAAeH,KAAKH,OAAiB;AAErC,KAAIG,KAAKJ,QAAQ,OAAOI,KAAKJ,SAAS,SACpCK,OAAMI,KAAKL,KAAKJ,KAAK;AAGvB,QAAOK,MAAMK,KAAK,IAAI"}
@@ -0,0 +1,14 @@
1
+ import { Storage } from "../types/index.mjs";
2
+ import { S3Store } from "../tus/stores/s3/s3Store.mjs";
3
+
4
+ //#region src/utils/buildThumbnailURL.d.ts
5
+ interface BuildThumbnailURLArgs {
6
+ storage: Storage;
7
+ playbackId?: string;
8
+ s3Key?: string;
9
+ s3Store?: S3Store | null;
10
+ }
11
+ declare function buildThumbnailURL(args: BuildThumbnailURLArgs): string | undefined;
12
+ //#endregion
13
+ export { buildThumbnailURL };
14
+ //# sourceMappingURL=buildThumbnailURL.d.mts.map