@maas/payload-plugin-media-cloud 0.0.30 → 0.0.32

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 (157) 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/staticHandler.d.mts +5 -3
  5. package/dist/adapter/staticHandler.mjs +8 -7
  6. package/dist/adapter/staticHandler.mjs.map +1 -1
  7. package/dist/adapter/storageAdapter.d.mts +3 -3
  8. package/dist/adapter/storageAdapter.mjs +4 -4
  9. package/dist/adapter/storageAdapter.mjs.map +1 -1
  10. package/dist/collectionHooks/afterChange.d.mts +7 -0
  11. package/dist/collectionHooks/afterChange.mjs +39 -0
  12. package/dist/collectionHooks/afterChange.mjs.map +1 -0
  13. package/dist/collectionHooks/beforeChange.d.mts +7 -0
  14. package/dist/collectionHooks/beforeChange.mjs +33 -0
  15. package/dist/collectionHooks/beforeChange.mjs.map +1 -0
  16. package/dist/collections/mediaCollection.d.mts +3 -2
  17. package/dist/collections/mediaCollection.mjs +46 -198
  18. package/dist/collections/mediaCollection.mjs.map +1 -1
  19. package/dist/components/folderFileCard/folderFileCard.d.mts +13 -0
  20. package/dist/components/folderFileCard/folderFileCard.mjs +30 -0
  21. package/dist/components/folderFileCard/folderFileCard.mjs.map +1 -0
  22. package/dist/components/gridContext/gridContext.d.mts +51 -0
  23. package/dist/components/gridContext/gridContext.mjs +227 -0
  24. package/dist/components/gridContext/gridContext.mjs.map +1 -0
  25. package/dist/components/gridView/gridView.css +50 -0
  26. package/dist/components/gridView/gridView.d.mts +16 -0
  27. package/dist/components/gridView/gridView.mjs +124 -0
  28. package/dist/components/gridView/gridView.mjs.map +1 -0
  29. package/dist/components/gridView/index.d.mts +2 -0
  30. package/dist/components/gridView/index.mjs +3 -0
  31. package/dist/components/index.d.mts +9 -7
  32. package/dist/components/index.mjs +5 -4
  33. package/dist/components/itemCardGrid/itemCardGrid.css +12 -0
  34. package/dist/components/itemCardGrid/itemCardGrid.d.mts +18 -0
  35. package/dist/components/itemCardGrid/itemCardGrid.mjs +22 -0
  36. package/dist/components/itemCardGrid/itemCardGrid.mjs.map +1 -0
  37. package/dist/components/muxPreview/index.d.mts +2 -0
  38. package/dist/components/muxPreview/index.mjs +3 -0
  39. package/dist/components/{mux-preview/mux-preview.d.mts → muxPreview/muxPreview.d.mts} +2 -2
  40. package/dist/components/{mux-preview/mux-preview.mjs → muxPreview/muxPreview.mjs} +2 -2
  41. package/dist/components/muxPreview/muxPreview.mjs.map +1 -0
  42. package/dist/components/uploadHandler/index.d.mts +2 -0
  43. package/dist/components/uploadHandler/index.mjs +3 -0
  44. package/dist/components/uploadHandler/uploadHandler.d.mts +20 -0
  45. package/dist/components/{upload-handler/upload-handler.mjs → uploadHandler/uploadHandler.mjs} +82 -52
  46. package/dist/components/uploadHandler/uploadHandler.mjs.map +1 -0
  47. package/dist/components/uploadManager/index.d.mts +2 -0
  48. package/dist/components/uploadManager/index.mjs +3 -0
  49. package/dist/components/{upload-manager/upload-manager.d.mts → uploadManager/uploadManager.d.mts} +3 -3
  50. package/dist/components/{upload-manager/upload-manager.mjs → uploadManager/uploadManager.mjs} +3 -3
  51. package/dist/components/uploadManager/uploadManager.mjs.map +1 -0
  52. package/dist/endpoints/fileExistsHandler.d.mts +12 -0
  53. package/dist/endpoints/{tusFileExistsHandler.mjs → fileExistsHandler.mjs} +7 -7
  54. package/dist/endpoints/fileExistsHandler.mjs.map +1 -0
  55. package/dist/endpoints/muxAssetHandler.d.mts +1 -0
  56. package/dist/endpoints/muxAssetHandler.mjs +3 -3
  57. package/dist/endpoints/muxAssetHandler.mjs.map +1 -1
  58. package/dist/endpoints/muxWebhookHandler.d.mts +1 -0
  59. package/dist/endpoints/muxWebhookHandler.mjs +6 -5
  60. package/dist/endpoints/muxWebhookHandler.mjs.map +1 -1
  61. package/dist/endpoints/tusCleanupHandler.d.mts +3 -2
  62. package/dist/endpoints/tusCleanupHandler.mjs +4 -4
  63. package/dist/endpoints/tusCleanupHandler.mjs.map +1 -1
  64. package/dist/endpoints/tusFolderHandler.d.mts +12 -0
  65. package/dist/endpoints/tusFolderHandler.mjs +39 -0
  66. package/dist/endpoints/tusFolderHandler.mjs.map +1 -0
  67. package/dist/endpoints/tusPostProcessorHandler.d.mts +3 -2
  68. package/dist/endpoints/tusPostProcessorHandler.mjs +6 -5
  69. package/dist/endpoints/tusPostProcessorHandler.mjs.map +1 -1
  70. package/dist/fields/alt.d.mts +7 -0
  71. package/dist/fields/alt.mjs +10 -0
  72. package/dist/fields/alt.mjs.map +1 -0
  73. package/dist/fields/filename.d.mts +7 -0
  74. package/dist/fields/filename.mjs +14 -0
  75. package/dist/fields/filename.mjs.map +1 -0
  76. package/dist/fields/height.d.mts +7 -0
  77. package/dist/fields/height.mjs +15 -0
  78. package/dist/fields/height.mjs.map +1 -0
  79. package/dist/fields/mux.d.mts +7 -0
  80. package/dist/fields/mux.mjs +149 -0
  81. package/dist/fields/mux.mjs.map +1 -0
  82. package/dist/fields/path.d.mts +7 -0
  83. package/dist/fields/path.mjs +14 -0
  84. package/dist/fields/path.mjs.map +1 -0
  85. package/dist/fields/storage.d.mts +7 -0
  86. package/dist/fields/storage.mjs +18 -0
  87. package/dist/fields/storage.mjs.map +1 -0
  88. package/dist/fields/thumbnail.d.mts +7 -0
  89. package/dist/fields/thumbnail.mjs +17 -0
  90. package/dist/fields/thumbnail.mjs.map +1 -0
  91. package/dist/fields/width.d.mts +7 -0
  92. package/dist/fields/width.mjs +15 -0
  93. package/dist/fields/width.mjs.map +1 -0
  94. package/dist/hooks/useErrorHandler.d.mts +1 -1
  95. package/dist/hooks/useErrorHandler.mjs +4 -2
  96. package/dist/hooks/useErrorHandler.mjs.map +1 -1
  97. package/dist/plugin.d.mts +5 -4
  98. package/dist/plugin.mjs +53 -29
  99. package/dist/plugin.mjs.map +1 -1
  100. package/dist/tus/stores/s3/{expiration-manager.d.mts → expirationManager.d.mts} +4 -3
  101. package/dist/tus/stores/s3/{expiration-manager.mjs → expirationManager.mjs} +6 -3
  102. package/dist/tus/stores/s3/expirationManager.mjs.map +1 -0
  103. package/dist/tus/stores/s3/{file-operations.d.mts → fileOperations.d.mts} +2 -2
  104. package/dist/tus/stores/s3/{file-operations.mjs → fileOperations.mjs} +2 -2
  105. package/dist/tus/stores/s3/fileOperations.mjs.map +1 -0
  106. package/dist/tus/stores/s3/index.d.mts +1 -1
  107. package/dist/tus/stores/s3/index.mjs +20 -9
  108. package/dist/tus/stores/s3/index.mjs.map +1 -1
  109. package/dist/tus/stores/s3/{metadata-manager.d.mts → metadataManager.d.mts} +4 -2
  110. package/dist/tus/stores/s3/{metadata-manager.mjs → metadataManager.mjs} +6 -5
  111. package/dist/tus/stores/s3/metadataManager.mjs.map +1 -0
  112. package/dist/tus/stores/s3/{parts-manager.d.mts → partsManager.d.mts} +4 -4
  113. package/dist/tus/stores/s3/{parts-manager.mjs → partsManager.mjs} +67 -29
  114. package/dist/tus/stores/s3/partsManager.mjs.map +1 -0
  115. package/dist/tus/stores/s3/{s3-store.d.mts → s3Store.d.mts} +38 -32
  116. package/dist/tus/stores/s3/{s3-store.mjs → s3Store.mjs} +102 -57
  117. package/dist/tus/stores/s3/s3Store.mjs.map +1 -0
  118. package/dist/types/errors.d.mts +32 -0
  119. package/dist/types/errors.mjs +32 -0
  120. package/dist/types/errors.mjs.map +1 -1
  121. package/dist/types/index.d.mts +42 -4
  122. package/dist/utils/buildS3Path.d.mts +10 -0
  123. package/dist/utils/buildS3Path.mjs +16 -0
  124. package/dist/utils/buildS3Path.mjs.map +1 -0
  125. package/dist/utils/buildThumbnailURL.d.mts +14 -0
  126. package/dist/utils/buildThumbnailURL.mjs +10 -0
  127. package/dist/utils/buildThumbnailURL.mjs.map +1 -0
  128. package/dist/utils/defaultOptions.d.mts +7 -0
  129. package/dist/utils/defaultOptions.mjs +12 -0
  130. package/dist/utils/defaultOptions.mjs.map +1 -0
  131. package/dist/utils/file.d.mts +16 -2
  132. package/dist/utils/file.mjs +58 -6
  133. package/dist/utils/file.mjs.map +1 -1
  134. package/dist/utils/mux.mjs +19 -8
  135. package/dist/utils/mux.mjs.map +1 -1
  136. package/dist/utils/tus.d.mts +9 -6
  137. package/dist/utils/tus.mjs +31 -11
  138. package/dist/utils/tus.mjs.map +1 -1
  139. package/package.json +9 -4
  140. package/dist/components/mux-preview/index.d.mts +0 -2
  141. package/dist/components/mux-preview/index.mjs +0 -3
  142. package/dist/components/mux-preview/mux-preview.mjs.map +0 -1
  143. package/dist/components/upload-handler/index.d.mts +0 -2
  144. package/dist/components/upload-handler/index.mjs +0 -3
  145. package/dist/components/upload-handler/upload-handler.d.mts +0 -22
  146. package/dist/components/upload-handler/upload-handler.mjs.map +0 -1
  147. package/dist/components/upload-manager/index.d.mts +0 -2
  148. package/dist/components/upload-manager/index.mjs +0 -3
  149. package/dist/components/upload-manager/upload-manager.mjs.map +0 -1
  150. package/dist/endpoints/tusFileExistsHandler.d.mts +0 -11
  151. package/dist/endpoints/tusFileExistsHandler.mjs.map +0 -1
  152. package/dist/tus/stores/s3/expiration-manager.mjs.map +0 -1
  153. package/dist/tus/stores/s3/file-operations.mjs.map +0 -1
  154. package/dist/tus/stores/s3/metadata-manager.mjs.map +0 -1
  155. package/dist/tus/stores/s3/parts-manager.mjs.map +0 -1
  156. package/dist/tus/stores/s3/s3-store.mjs.map +0 -1
  157. /package/dist/components/{upload-manager/upload-manager.css → uploadManager/uploadManager.css} +0 -0
@@ -6,7 +6,7 @@ import { StreamSplitter } from "@tus/utils";
6
6
  import fs from "node:fs";
7
7
  import os from "node:os";
8
8
 
9
- //#region src/tus/stores/s3/parts-manager.ts
9
+ //#region src/tus/stores/s3/partsManager.ts
10
10
  const { log, throwError } = useErrorHandler();
11
11
  var S3PartsManager = class {
12
12
  constructor(client, bucket, minPartSize, partUploadSemaphore, metadataManager, fileOperations, generateCompleteTag) {
@@ -35,7 +35,10 @@ var S3PartsManager = class {
35
35
  }
36
36
  const params = {
37
37
  Bucket: this.bucket,
38
- Key: id,
38
+ Key: this.metadataManager.generatePartKey({
39
+ id,
40
+ prefix: metadata.file.metadata?.prefix ?? void 0
41
+ }),
39
42
  PartNumberMarker: partNumberMarker,
40
43
  UploadId: metadata["upload-id"]
41
44
  };
@@ -62,8 +65,11 @@ var S3PartsManager = class {
62
65
  async finishMultipartUpload(args) {
63
66
  const { metadata, parts } = args;
64
67
  const params = {
68
+ Key: this.metadataManager.generatePartKey({
69
+ id: metadata.file.id,
70
+ prefix: metadata.file.metadata?.prefix ?? void 0
71
+ }),
65
72
  Bucket: this.bucket,
66
- Key: metadata.file.id,
67
73
  MultipartUpload: { Parts: parts.map((part) => {
68
74
  return {
69
75
  ETag: part.ETag,
@@ -74,8 +80,11 @@ var S3PartsManager = class {
74
80
  };
75
81
  try {
76
82
  return (await this.client.completeMultipartUpload(params)).Location;
77
- } catch (_error) {
78
- throwError(MediaCloudErrors.TUS_UPLOAD_ERROR);
83
+ } catch (error) {
84
+ throwError({
85
+ ...MediaCloudErrors.TUS_UPLOAD_ERROR,
86
+ cause: error
87
+ });
79
88
  throw new Error();
80
89
  }
81
90
  }
@@ -88,10 +97,12 @@ var S3PartsManager = class {
88
97
  async getIncompletePart(args) {
89
98
  const { id } = args;
90
99
  try {
100
+ const { file } = await this.metadataManager.getMetadata({ id });
91
101
  return (await this.client.getObject({
92
102
  Bucket: this.bucket,
93
103
  Key: this.metadataManager.generatePartKey({
94
104
  id,
105
+ prefix: file?.metadata?.prefix ?? void 0,
95
106
  isIncomplete: true
96
107
  })
97
108
  })).Body;
@@ -109,11 +120,13 @@ var S3PartsManager = class {
109
120
  async getIncompletePartSize(args) {
110
121
  const { id } = args;
111
122
  try {
123
+ const { file } = await this.metadataManager.getMetadata({ id });
112
124
  return (await this.client.headObject({
113
125
  Bucket: this.bucket,
114
126
  Key: this.metadataManager.generatePartKey({
115
127
  id,
116
- isIncomplete: true
128
+ isIncomplete: true,
129
+ prefix: file?.metadata?.prefix ?? void 0
117
130
  })
118
131
  })).ContentLength;
119
132
  } catch (error) {
@@ -129,13 +142,22 @@ var S3PartsManager = class {
129
142
  */
130
143
  async deleteIncompletePart(args) {
131
144
  const { id } = args;
132
- await this.client.deleteObject({
133
- Bucket: this.bucket,
134
- Key: this.metadataManager.generatePartKey({
135
- id,
136
- isIncomplete: true
137
- })
138
- });
145
+ try {
146
+ const { file } = await this.metadataManager.getMetadata({ id });
147
+ await this.client.deleteObject({
148
+ Bucket: this.bucket,
149
+ Key: this.metadataManager.generatePartKey({
150
+ id,
151
+ isIncomplete: true,
152
+ prefix: file?.metadata?.prefix ?? void 0
153
+ })
154
+ });
155
+ } catch (error) {
156
+ throwError({
157
+ ...MediaCloudErrors.S3_DELETE_ERROR,
158
+ cause: error
159
+ });
160
+ }
139
161
  }
140
162
  /**
141
163
  * Downloads incomplete part to temporary file
@@ -187,17 +209,27 @@ var S3PartsManager = class {
187
209
  */
188
210
  async uploadIncompletePart(args) {
189
211
  const { id, readStream } = args;
190
- const data = await this.client.putObject({
191
- Body: readStream,
192
- Bucket: this.bucket,
193
- Key: this.metadataManager.generatePartKey({
194
- id,
195
- isIncomplete: true
196
- }),
197
- Tagging: this.generateCompleteTag("false")
198
- });
199
- log(MediaCloudLogs.S3_STORE_INCOMPLETE_PART_UPLOADED);
200
- return data.ETag;
212
+ try {
213
+ const { file } = await this.metadataManager.getMetadata({ id });
214
+ const data = await this.client.putObject({
215
+ Body: readStream,
216
+ Bucket: this.bucket,
217
+ Key: this.metadataManager.generatePartKey({
218
+ id,
219
+ isIncomplete: true,
220
+ prefix: file?.metadata?.prefix ?? void 0
221
+ }),
222
+ Tagging: this.generateCompleteTag("false")
223
+ });
224
+ log(MediaCloudLogs.S3_STORE_INCOMPLETE_PART_UPLOADED);
225
+ return data.ETag;
226
+ } catch (error) {
227
+ throwError({
228
+ ...MediaCloudErrors.TUS_UPLOAD_ERROR,
229
+ cause: error
230
+ });
231
+ throw error;
232
+ }
201
233
  }
202
234
  /**
203
235
  * Uploads a single part
@@ -217,7 +249,10 @@ var S3PartsManager = class {
217
249
  const params = {
218
250
  Body: readStream,
219
251
  Bucket: this.bucket,
220
- Key: metadata.file.id,
252
+ Key: this.metadataManager.generatePartKey({
253
+ id: metadata.file.id,
254
+ prefix: metadata.file.metadata?.prefix ?? void 0
255
+ }),
221
256
  PartNumber: partNumber,
222
257
  UploadId: metadata["upload-id"]
223
258
  };
@@ -226,9 +261,12 @@ var S3PartsManager = class {
226
261
  ETag: (await this.client.uploadPart(params)).ETag,
227
262
  PartNumber: partNumber
228
263
  };
229
- } catch (_error) {
230
- throwError(MediaCloudErrors.TUS_UPLOAD_ERROR);
231
- throw new Error();
264
+ } catch (error) {
265
+ throwError({
266
+ ...MediaCloudErrors.TUS_UPLOAD_ERROR,
267
+ cause: error
268
+ });
269
+ throw error;
232
270
  } finally {
233
271
  permit();
234
272
  }
@@ -320,4 +358,4 @@ var S3PartsManager = class {
320
358
 
321
359
  //#endregion
322
360
  export { S3PartsManager };
323
- //# sourceMappingURL=parts-manager.mjs.map
361
+ //# sourceMappingURL=partsManager.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"partsManager.mjs","names":["client: S3","bucket: string","minPartSize: number","partUploadSemaphore: Semaphore","metadataManager: S3MetadataManager","fileOperations: S3FileOperations","generateCompleteTag: (value: 'false' | 'true') => string | undefined","params: AWS.ListPartsCommandInput","params: AWS.UploadPartCommandInput","promises: Promise<void>[]","pendingChunkFilepath: null | string","permit: SemaphorePermit | undefined"],"sources":["../../../../src/tus/stores/s3/partsManager.ts"],"sourcesContent":["import fs from 'node:fs'\nimport os from 'node:os'\nimport stream from 'node:stream'\n\nimport { NoSuchKey, NotFound, type S3 } from '@aws-sdk/client-s3'\nimport { StreamSplitter } from '@tus/utils'\n\nimport { useErrorHandler } from '../../../hooks/useErrorHandler'\nimport { MediaCloudErrors, MediaCloudLogs } from '../../../types/errors'\n\nimport type AWS from '@aws-sdk/client-s3'\nimport type { Readable } from 'node:stream'\nimport type { IncompletePartInfo, TusUploadMetadata } from '../../../types'\nimport type { S3FileOperations } from './fileOperations'\nimport type { S3MetadataManager } from './metadataManager'\nimport type { Semaphore, SemaphorePermit } from './semaphore'\n\ntype RetrievePartsArgs = {\n id: string\n partNumberMarker?: string\n}\n\ntype FinishMultipartUploadArgs = {\n metadata: TusUploadMetadata\n parts: Array<AWS.Part>\n}\n\ntype GetIncompletePartArgs = {\n id: string\n}\n\ntype GetIncompletePartSizeArgs = {\n id: string\n}\n\ntype DeleteIncompletePartArgs = {\n id: string\n}\n\ntype DownloadIncompletePartArgs = {\n id: string\n}\n\ntype UploadIncompletePartArgs = {\n id: string\n readStream: fs.ReadStream | Readable\n}\n\ntype UploadPartArgs = {\n metadata: TusUploadMetadata\n readStream: fs.ReadStream | Readable\n partNumber: number\n}\n\ntype UploadPartsArgs = {\n metadata: TusUploadMetadata\n readStream: stream.Readable\n currentPartNumber: number\n offset: number\n}\n\nconst { log, throwError } = useErrorHandler()\n\nexport class S3PartsManager {\n constructor(\n private client: S3,\n private bucket: string,\n private minPartSize: number,\n private partUploadSemaphore: Semaphore,\n private metadataManager: S3MetadataManager,\n private fileOperations: S3FileOperations,\n private generateCompleteTag: (value: 'false' | 'true') => string | undefined\n ) {}\n\n /**\n * Gets the number of complete parts/chunks already uploaded to S3.\n * Retrieves only consecutive parts.\n * @param args - The function arguments\n * @param args.id - The upload ID\n * @param args.partNumberMarker - Marker for pagination (optional)\n * @returns Promise that resolves to array of uploaded parts\n */\n async retrieveParts(args: RetrievePartsArgs): Promise<Array<AWS.Part>> {\n const { id, partNumberMarker } = args\n const metadata = await this.metadataManager.getMetadata({ id })\n\n if (!metadata['upload-id']) {\n throwError(MediaCloudErrors.MUX_UPLOAD_ID_MISSING)\n throw new Error() // This will never execute but satisfies TypeScript\n }\n\n const params: AWS.ListPartsCommandInput = {\n Bucket: this.bucket,\n Key: this.metadataManager.generatePartKey({\n id,\n prefix: metadata.file.metadata?.prefix ?? undefined,\n }),\n PartNumberMarker: partNumberMarker,\n UploadId: metadata['upload-id'],\n }\n\n const data = await this.client.listParts(params)\n\n let parts = data.Parts ?? []\n\n if (data.IsTruncated) {\n const rest = await this.retrieveParts({\n id,\n partNumberMarker: data.NextPartNumberMarker,\n })\n parts = [...parts, ...rest]\n }\n\n if (!partNumberMarker) {\n parts.sort((a, b) => (a.PartNumber || 0) - (b.PartNumber || 0))\n }\n\n return parts\n }\n\n /**\n * Completes a multipart upload on S3.\n * This is where S3 concatenates all the uploaded parts.\n * @param args - The function arguments\n * @param args.metadata - The upload metadata\n * @param args.parts - Array of uploaded parts to complete\n * @returns Promise that resolves to the location URL (optional)\n */\n async finishMultipartUpload(\n args: FinishMultipartUploadArgs\n ): Promise<string | undefined> {\n const { metadata, parts } = args\n\n const params = {\n Key: this.metadataManager.generatePartKey({\n id: metadata.file.id,\n prefix: metadata.file.metadata?.prefix ?? undefined,\n }),\n Bucket: this.bucket,\n MultipartUpload: {\n Parts: parts.map((part) => {\n return {\n ETag: part.ETag,\n PartNumber: part.PartNumber,\n }\n }),\n },\n UploadId: metadata['upload-id'],\n }\n\n try {\n const result = await this.client.completeMultipartUpload(params)\n return result.Location\n } catch (error) {\n throwError({ ...MediaCloudErrors.TUS_UPLOAD_ERROR, cause: error })\n throw new Error() // This will never execute but satisfies TypeScript\n }\n }\n\n /**\n * Gets incomplete part from S3\n * @param args - The function arguments\n * @param args.id - The upload ID\n * @returns Promise that resolves to readable stream or undefined if not found\n */\n async getIncompletePart(\n args: GetIncompletePartArgs\n ): Promise<Readable | undefined> {\n const { id } = args\n\n try {\n const { file } = await this.metadataManager.getMetadata({ id })\n\n const data = await this.client.getObject({\n Bucket: this.bucket,\n Key: this.metadataManager.generatePartKey({\n id,\n prefix: file?.metadata?.prefix ?? undefined,\n isIncomplete: true,\n }),\n })\n return data.Body as Readable\n } catch (error) {\n if (error instanceof NoSuchKey) {\n return undefined\n }\n throw error\n }\n }\n\n /**\n * Gets the size of an incomplete part\n * @param args - The function arguments\n * @param args.id - The upload ID\n * @returns Promise that resolves to part size or undefined if not found\n */\n async getIncompletePartSize(\n args: GetIncompletePartSizeArgs\n ): Promise<number | undefined> {\n const { id } = args\n\n try {\n const { file } = await this.metadataManager.getMetadata({ id })\n\n const data = await this.client.headObject({\n Bucket: this.bucket,\n Key: this.metadataManager.generatePartKey({\n id,\n isIncomplete: true,\n prefix: file?.metadata?.prefix ?? undefined,\n }),\n })\n return data.ContentLength\n } catch (error) {\n if (error instanceof NotFound) {\n return undefined\n }\n throw error\n }\n }\n\n /**\n * Deletes an incomplete part\n * @param args - The function arguments\n * @param args.id - The upload ID\n * @returns Promise that resolves when deletion is complete\n */\n async deleteIncompletePart(args: DeleteIncompletePartArgs): Promise<void> {\n const { id } = args\n\n try {\n const { file } = await this.metadataManager.getMetadata({ id })\n\n await this.client.deleteObject({\n Bucket: this.bucket,\n Key: this.metadataManager.generatePartKey({\n id,\n isIncomplete: true,\n prefix: file?.metadata?.prefix ?? undefined,\n }),\n })\n } catch (error) {\n throwError({\n ...MediaCloudErrors.S3_DELETE_ERROR,\n cause: error,\n })\n }\n }\n\n /**\n * Downloads incomplete part to temporary file\n * @param args - The function arguments\n * @param args.id - The upload ID\n * @returns Promise that resolves to incomplete part info or undefined if not found\n */\n async downloadIncompletePart(\n args: DownloadIncompletePartArgs\n ): Promise<IncompletePartInfo | undefined> {\n const { id } = args\n const incompletePart = await this.getIncompletePart({ id })\n\n if (!incompletePart) {\n return\n }\n const filePath = await this.fileOperations.generateUniqueTmpFileName({\n template: 'tus-s3-incomplete-part-',\n })\n\n try {\n let incompletePartSize = 0\n\n const byteCounterTransform = new stream.Transform({\n transform(chunk, _, callback) {\n incompletePartSize += chunk.length\n callback(null, chunk)\n },\n })\n\n // Write to temporary file\n await stream.promises.pipeline(\n incompletePart,\n byteCounterTransform,\n fs.createWriteStream(filePath)\n )\n\n const createReadStream = (options: { cleanUpOnEnd: boolean }) => {\n const fileReader = fs.createReadStream(filePath)\n\n if (options.cleanUpOnEnd) {\n fileReader.on('end', () => {\n fs.unlink(filePath, () => {})\n })\n\n fileReader.on('error', (err) => {\n fileReader.destroy(err)\n fs.unlink(filePath, () => {})\n })\n }\n\n return fileReader\n }\n\n return {\n createReader: createReadStream,\n path: filePath,\n size: incompletePartSize,\n }\n } catch (err) {\n fs.promises.rm(filePath).catch(() => {})\n throw err\n }\n }\n\n /**\n * Uploads an incomplete part\n * @param args - The function arguments\n * @param args.id - The upload ID\n * @param args.readStream - The stream to read data from\n * @returns Promise that resolves to the ETag of the uploaded part\n */\n async uploadIncompletePart(args: UploadIncompletePartArgs): Promise<string> {\n const { id, readStream } = args\n try {\n const { file } = await this.metadataManager.getMetadata({ id })\n\n const data = await this.client.putObject({\n Body: readStream,\n Bucket: this.bucket,\n Key: this.metadataManager.generatePartKey({\n id,\n isIncomplete: true,\n prefix: file?.metadata?.prefix ?? undefined,\n }),\n Tagging: this.generateCompleteTag('false'),\n })\n log(MediaCloudLogs.S3_STORE_INCOMPLETE_PART_UPLOADED)\n return data.ETag as string\n } catch (error) {\n throwError({ ...MediaCloudErrors.TUS_UPLOAD_ERROR, cause: error })\n throw error\n }\n }\n\n /**\n * Uploads a single part\n * @param args - The function arguments\n * @param args.metadata - The upload metadata\n * @param args.readStream - The stream to read data from\n * @param args.partNumber - The part number to upload\n * @returns Promise that resolves to the ETag of the uploaded part\n */\n async uploadPart(args: UploadPartArgs): Promise<AWS.Part> {\n const { metadata, readStream, partNumber } = args\n const permit = await this.partUploadSemaphore.acquire()\n\n if (!metadata['upload-id']) {\n throwError(MediaCloudErrors.MUX_UPLOAD_ID_MISSING)\n throw new Error() // This will never execute but satisfies TypeScript\n }\n\n const params: AWS.UploadPartCommandInput = {\n Body: readStream,\n Bucket: this.bucket,\n Key: this.metadataManager.generatePartKey({\n id: metadata.file.id,\n prefix: metadata.file.metadata?.prefix ?? undefined,\n }),\n PartNumber: partNumber,\n UploadId: metadata['upload-id'],\n }\n\n try {\n const data = await this.client.uploadPart(params)\n return { ETag: data.ETag, PartNumber: partNumber }\n } catch (error) {\n throwError({ ...MediaCloudErrors.TUS_UPLOAD_ERROR, cause: error })\n throw error\n } finally {\n permit()\n }\n }\n\n /**\n * Uploads a stream to s3 using multiple parts\n * @param args - The function arguments\n * @param args.metadata - The upload metadata\n * @param args.readStream - The stream to read data from\n * @param args.currentPartNumber - The current part number to start from\n * @param args.offset - The byte offset to start from\n * @returns Promise that resolves to the number of bytes uploaded\n */\n async uploadParts(args: UploadPartsArgs): Promise<number> {\n const { metadata, readStream, offset: initialOffset } = args\n let { currentPartNumber } = args\n let offset = initialOffset\n const size = metadata.file.size\n const promises: Promise<void>[] = []\n let pendingChunkFilepath: null | string = null\n let bytesUploaded = 0\n let permit: SemaphorePermit | undefined = undefined\n\n const splitterStream = new StreamSplitter({\n chunkSize: this.fileOperations.calculateOptimalPartSize({ size }),\n directory: os.tmpdir(),\n })\n .on('beforeChunkStarted', async () => {\n permit = await this.partUploadSemaphore.acquire()\n })\n .on('chunkStarted', (filepath) => {\n pendingChunkFilepath = filepath\n })\n .on('chunkFinished', ({ path, size: partSize }) => {\n pendingChunkFilepath = null\n\n const acquiredPermit = permit\n const partNumber = currentPartNumber++\n\n offset += partSize\n\n const isFinalPart = size === offset\n\n const uploadChunk = async () => {\n try {\n // Only the first chunk of each PATCH request can prepend\n // an incomplete part (last chunk) from the previous request.\n const readable = fs.createReadStream(path)\n readable.on('error', function (error) {\n throw error\n })\n\n switch (true) {\n case partSize >= this.minPartSize || isFinalPart:\n await this.uploadPart({\n metadata,\n readStream: readable,\n partNumber,\n })\n break\n default:\n await this.uploadIncompletePart({\n id: metadata.file.id,\n readStream: readable,\n })\n break\n }\n\n bytesUploaded += partSize\n } catch (error) {\n // Destroy the splitter to stop processing more chunks\n const mappedError =\n error instanceof Error ? error : new Error(String(error))\n splitterStream.destroy(mappedError)\n throw mappedError\n } finally {\n fs.promises.rm(path).catch(function () {})\n acquiredPermit?.()\n }\n }\n\n const deferred = uploadChunk()\n\n promises.push(deferred)\n })\n .on('chunkError', () => {\n permit?.()\n })\n\n try {\n await stream.promises.pipeline(readStream, splitterStream)\n } catch (error) {\n if (pendingChunkFilepath !== null) {\n try {\n await fs.promises.rm(pendingChunkFilepath)\n } catch {\n log(MediaCloudLogs.S3_STORE_CHUNK_REMOVAL_FAILED)\n }\n }\n const mappedError =\n error instanceof Error ? error : new Error(String(error))\n promises.push(Promise.reject(mappedError))\n } finally {\n // Wait for all promises\n await Promise.allSettled(promises)\n // Reject the promise if any of the promises reject\n await Promise.all(promises)\n }\n\n return bytesUploaded\n }\n}\n"],"mappings":"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"}
@@ -1,14 +1,14 @@
1
1
  import { Semaphore } from "./semaphore.mjs";
2
- import { S3ExpirationManager } from "./expiration-manager.mjs";
3
- import { S3FileOperations } from "./file-operations.mjs";
4
2
  import { S3StoreConfig } from "../../../types/index.mjs";
5
- import { S3MetadataManager } from "./metadata-manager.mjs";
6
- import { S3PartsManager } from "./parts-manager.mjs";
3
+ import { S3MetadataManager } from "./metadataManager.mjs";
4
+ import { S3ExpirationManager } from "./expirationManager.mjs";
5
+ import { S3FileOperations } from "./fileOperations.mjs";
6
+ import { S3PartsManager } from "./partsManager.mjs";
7
7
  import stream, { Readable } from "node:stream";
8
8
  import { S3 } from "@aws-sdk/client-s3";
9
9
  import { DataStore, Upload } from "@tus/utils";
10
10
 
11
- //#region src/tus/stores/s3/s3-store.d.ts
11
+ //#region src/tus/stores/s3/s3Store.d.ts
12
12
  declare class S3Store extends DataStore {
13
13
  client: S3;
14
14
  bucket: string;
@@ -26,19 +26,6 @@ declare class S3Store extends DataStore {
26
26
  protected expirationManager: S3ExpirationManager;
27
27
  protected customEndpoint: string;
28
28
  constructor(options: S3StoreConfig);
29
- /**
30
- * Generate the key name for the info file
31
- * @param id - The upload ID
32
- * @returns The info file key
33
- */
34
- protected generateInfoKey(id: string): string;
35
- /**
36
- * Generate the key name for a part file
37
- * @param id - The upload ID
38
- * @param isIncompletePart - Whether this is an incomplete part (default: false)
39
- * @returns The part file key
40
- */
41
- protected generatePartKey(id: string, isIncompletePart?: boolean): string;
42
29
  /**
43
30
  * Helper method to check if expiration tags should be used
44
31
  * @returns True if expiration tags should be used
@@ -47,7 +34,7 @@ declare class S3Store extends DataStore {
47
34
  /**
48
35
  * Generates a tag for marking complete/incomplete uploads
49
36
  * @param value - Either 'false' or 'true' to mark completion status
50
- * @returns The tag string or undefined if tags shouldn't be used
37
+ * @returns The tag string or undefined if tags shouldnt be used
51
38
  */
52
39
  protected generateCompleteTag(value: 'false' | 'true'): string | undefined;
53
40
  /**
@@ -55,55 +42,74 @@ declare class S3Store extends DataStore {
55
42
  * Also, a `${file_id}.info` file is created which holds some information
56
43
  * about the upload itself like: `upload-id`, `upload-length`, etc.
57
44
  * @param upload - The upload object to create
58
- * @returns Promise that resolves to the created upload
45
+ * @return Promise that resolves to the created upload object with storage information
59
46
  */
60
47
  create(upload: Upload): Promise<Upload>;
61
48
  /**
62
- * Declares the length of the upload
63
- * @param file_id - The file ID
64
- * @param upload_length - The length of the upload
65
- * @returns Promise that resolves when length is declared
66
- */
67
- declareUploadLength(file_id: string, upload_length: number): Promise<void>;
68
- /**
69
- * Writes `buffer` to the file specified by the upload's `id` at `offset`
49
+ * Writes `buffer` to the file specified by the upload’s `id` at `offset`
70
50
  * @param readable - The readable stream to write
71
51
  * @param id - The upload ID
72
52
  * @param offset - The byte offset to write at
73
- * @returns Promise that resolves to the number of bytes written
53
+ * @return Promise that resolves to the new offset after writing the data
74
54
  */
75
55
  write(readable: stream.Readable, id: string, offset: number): Promise<number>;
76
56
  /**
77
57
  * Returns the current state of the upload, i.e how much data has been
78
58
  * uploaded and if the upload is complete.
59
+ * @param id - The upload ID to retrieve
60
+ * @returns Promise that resolves to the upload object with current offset and storage information
79
61
  */
80
62
  getUpload(id: string): Promise<Upload>;
81
63
  /**
82
- * Reads the file specified by the upload's `id` and returns a readable stream
64
+ * Reads the file specified by the uploads `id` and returns a readable stream
65
+ * @param id - The upload ID to read
66
+ * @returns Promise that resolves to a readable stream of the file's contents
83
67
  */
84
68
  read(id: string): Promise<Readable>;
85
69
  /**
86
- * Removes the file specified by the upload's `id`
70
+ *
71
+ * Moves the file specified by its `oldKey` to `newKey`.
72
+ * @param oldKey - The current S3 key of the file to be moved
73
+ * @param newKey - The new S3 key to move the file to
74
+ * @return Promise that resolves when the file has been successfully moved, or rejects with an error if the move operation fails
75
+ */
76
+ copy(oldKey: string, newKey: string): Promise<void>;
77
+ /**
78
+ * Removes files specified by the upload’s `id`
79
+ * @param id - The upload ID to remove
80
+ * @returns Promise that resolves when the file and its metadata have been removed
87
81
  */
88
82
  remove(id: string): Promise<void>;
83
+ /**
84
+ * Removes the .info file specified by the upload’s `id`
85
+ * @param id - The upload ID to clean up
86
+ * @returns Promise that resolves when the metadata file has been removed
87
+ */
88
+ cleanup(id: string): Promise<void>;
89
89
  /**
90
90
  * Combine all multipart uploads into a single object
91
+ * @param id - The upload ID to complete
92
+ * @returns Promise that resolves to the completed upload object with storage information
91
93
  */
92
94
  completeMultipartUpload(id: string): Promise<Upload>;
93
95
  /**
94
96
  * Get the full S3 URL for an uploaded file
97
+ * @param id - The upload ID to get the URL for
98
+ * @returns The full URL to access the uploaded file on S3
95
99
  */
96
100
  getUrl(id: string): string;
97
101
  /**
98
102
  * Deletes expired incomplete uploads.
99
103
  * Returns the number of deleted uploads.
104
+ * @returns Promise that resolves to the number of deleted uploads
100
105
  */
101
106
  deleteExpired(): Promise<number>;
102
107
  /**
103
108
  * Returns the expiration period in milliseconds
109
+ * @return The expiration period in milliseconds
104
110
  */
105
111
  getExpiration(): number;
106
112
  }
107
113
  //#endregion
108
114
  export { S3Store };
109
- //# sourceMappingURL=s3-store.d.mts.map
115
+ //# sourceMappingURL=s3Store.d.mts.map
@@ -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 });
@@ -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