@maas/payload-plugin-media-cloud 0.0.24 → 0.0.26

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.
@@ -1,5 +1,5 @@
1
1
  import * as react0 from "react";
2
- import * as payload3 from "payload";
2
+ import * as payload1 from "payload";
3
3
 
4
4
  //#region src/components/upload-handler/upload-handler.d.ts
5
5
  declare const UploadHandler: ({
@@ -11,7 +11,7 @@ declare const UploadHandler: ({
11
11
  serverHandlerPath
12
12
  }: {
13
13
  children: react0.ReactNode;
14
- collectionSlug: payload3.UploadCollectionSlug;
14
+ collectionSlug: payload1.UploadCollectionSlug;
15
15
  enabled?: boolean;
16
16
  extra: Record<string, unknown>;
17
17
  prefix?: string;
@@ -81,10 +81,11 @@ async function muxUpload(args) {
81
81
  * @returns Promise that resolves to upload result or null if upload fails
82
82
  */
83
83
  async function tusUpload(args) {
84
- const { apiRoute, serverURL, file, mimeType, originalFilename } = args;
84
+ const { apiRoute, serverURL, file, mimeType } = args;
85
85
  const filename = file.name;
86
86
  const filetype = file.type;
87
87
  const filesize = file.size.toString();
88
+ Object.keys(localStorage).filter((key) => key.startsWith("tus::")).forEach((key) => localStorage.removeItem(key));
88
89
  let uploadUrlAvailable = false;
89
90
  function onBeforeUnload(e) {
90
91
  window.removeEventListener("beforeunload", onBeforeUnload);
@@ -99,11 +100,7 @@ async function tusUpload(args) {
99
100
  endpoint: `${serverURL}${apiRoute}/uploads`,
100
101
  retryDelays: TUS_RETRY_DELAYS,
101
102
  chunkSize: TUS_CHUNK_SIZE,
102
- fingerprint: function(file$1) {
103
- const hash = `${originalFilename}-${file$1.size}-${file$1.type}`;
104
- return Promise.resolve(hash);
105
- },
106
- removeFingerprintOnSuccess: true,
103
+ storeFingerprintForResuming: false,
107
104
  metadata: {
108
105
  filetype,
109
106
  filesize,
@@ -116,6 +113,7 @@ async function tusUpload(args) {
116
113
  logError(MediaCloudErrors.TUS_UPLOAD_ERROR.message);
117
114
  logError(error.message);
118
115
  navigator.sendBeacon(`${serverURL}${apiRoute}/uploads/cleanup`, JSON.stringify({ filename }));
116
+ emitter.emit("removeUpload", { filename });
119
117
  toast.error("File upload failed");
120
118
  resolve(null);
121
119
  },
@@ -161,9 +159,8 @@ const UploadHandler = createClientUploadHandler({ handler: async function(args)
161
159
  }
162
160
  const isVideoFile = await isVideo(file);
163
161
  let mappedFile = file;
164
- const originalFilename = file.name;
165
162
  if (!isVideoFile) try {
166
- if ((await fetch(`${serverURL}${apiRoute}/uploads/${file.name}/exists`)).status === 200) {
163
+ if ((await fetch(`${serverURL}${apiRoute}/uploads/${file.name}/exists`).catch(() => {}))?.status === 200) {
167
164
  log(MediaCloudLogs.S3_STORE_FILE_FOUND);
168
165
  const newFilename = generateUniqueFilename(file.name);
169
166
  mappedFile = new File([file], newFilename, { type: file.type });
@@ -176,8 +173,7 @@ const UploadHandler = createClientUploadHandler({ handler: async function(args)
176
173
  file: mappedFile,
177
174
  serverURL,
178
175
  apiRoute,
179
- mimeType,
180
- originalFilename
176
+ mimeType
181
177
  };
182
178
  if (isVideoFile) return await muxUpload(uploadArgs);
183
179
  else return await tusUpload(uploadArgs);
@@ -1 +1 @@
1
- {"version":3,"file":"upload-handler.mjs","names":["file","uploadArgs: UploadArgs"],"sources":["../../../src/components/upload-handler/upload-handler.tsx"],"sourcesContent":["'use client'\n\nimport * as upchunk from '@mux/upchunk'\nimport * as tus from 'tus-js-client'\n\nimport { toast } from '@payloadcms/ui'\nimport { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client'\n\nimport { MediaCloudErrors, MediaCloudLogs } from '../../types/errors'\nimport { useMediaCloudEmitter } from '../../hooks/useMediaCloudEmitter'\nimport { useErrorHandler } from '../../hooks/useErrorHandler'\nimport { isVideo, getFileType, generateUniqueFilename } from '../../utils/file'\n\ninterface UploadArgs {\n serverURL: string\n apiRoute: string\n file: File\n mimeType: string\n originalFilename: string\n}\n\ninterface UploadResult {\n filename: string\n uploadId?: string\n mimeType: string\n storage: 'mux' | 's3'\n}\n\ninterface MuxCreateUploadResponse {\n url: string\n uploadId: string\n filename: string\n}\n\nconst { logError, throwError } = useErrorHandler()\nconst emitter = useMediaCloudEmitter()\n\nconst MUX_CHUNK_SIZE = 30720\nconst TUS_CHUNK_SIZE = 1024 * 1024\nconst TUS_RETRY_DELAYS = [0, 1000, 2000, 5000]\n\n/**\n * Handles Mux video upload with progress tracking\n * @param args - The upload arguments including file, server URL, and callbacks\n * @returns Promise that resolves to upload result or null if upload fails\n */\nasync function muxUpload(args: UploadArgs): Promise<UploadResult | null> {\n const { file, serverURL, apiRoute, mimeType } = args\n\n const filename = file.name\n try {\n // Request upload URL from Mux\n const response = await fetch(`${serverURL}${apiRoute}/mux/upload`, {\n body: JSON.stringify({ filename, mimeType }),\n credentials: 'include',\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n })\n\n const { url, uploadId } = (await response.json()) as MuxCreateUploadResponse\n\n // Create upchunk uploader\n const uploader = await upchunk.createUpload({\n endpoint: url,\n file,\n chunkSize: MUX_CHUNK_SIZE,\n })\n\n // Add upload to tracker\n emitter.emit('addUpload', {\n filename,\n uploadId,\n polling: false,\n pollingUrl: `${serverURL}${apiRoute}/mux/asset`,\n })\n\n // Set up event handlers\n uploader.on('error', function () {\n logError(MediaCloudErrors.MUX_UPLOAD_ERROR.message)\n toast.error('Video upload failed')\n emitter.emit('removeUpload', { uploadId })\n })\n\n uploader.on('progress', function (progress) {\n emitter.emit('updateUpload', {\n filename,\n progress: progress.detail,\n })\n })\n\n uploader.on('success', function () {\n emitter.emit('uploadComplete', { filename })\n })\n\n // Update collection entry\n // with filename, uploadId, mimeType, and storage\n return {\n filename,\n uploadId,\n mimeType,\n storage: 'mux',\n }\n } catch (_error) {\n logError(MediaCloudErrors.MUX_DIRECT_UPLOAD_ERROR.message)\n toast.error('Video upload failed')\n return null\n }\n}\n\n/**\n * Handles TUS file upload with resumable capabilities\n * @param args - The upload arguments including file, server URL, and callbacks\n * @returns Promise that resolves to upload result or null if upload fails\n */\nasync function tusUpload(args: UploadArgs): Promise<UploadResult | null> {\n const { apiRoute, serverURL, file, mimeType, originalFilename } = args\n\n const filename = file.name\n const filetype = file.type\n const filesize = file.size.toString()\n\n // Flag to prevent multiple onUploadUrlAvailable calls\n let uploadUrlAvailable = false\n let eventListenerAdded = false\n\n function onBeforeUnload(e: BeforeUnloadEvent) {\n window.removeEventListener('beforeunload', onBeforeUnload)\n e.preventDefault()\n }\n\n function onPageUnload() {\n window.removeEventListener('unload', onPageUnload)\n navigator.sendBeacon(\n `${serverURL}${apiRoute}/uploads/cleanup`,\n JSON.stringify({ filename })\n )\n }\n\n return new Promise((resolve) => {\n const upload = new tus.Upload(file, {\n endpoint: `${serverURL}${apiRoute}/uploads`,\n retryDelays: TUS_RETRY_DELAYS,\n chunkSize: TUS_CHUNK_SIZE,\n // A stable fingerprint is required for the upload to be resumable\n // This fingerprint is based on the file’s content and original name\n fingerprint: function (file) {\n const hash = `${originalFilename}-${file.size}-${file.type}`\n return Promise.resolve(hash)\n },\n removeFingerprintOnSuccess: true,\n metadata: {\n filetype,\n filesize,\n filename,\n contentType: filetype,\n contentLength: filesize,\n contentDisposition: 'inline',\n },\n onError(error) {\n logError(MediaCloudErrors.TUS_UPLOAD_ERROR.message)\n logError(error.message)\n\n // Clean up\n navigator.sendBeacon(\n `${serverURL}${apiRoute}/uploads/cleanup`,\n JSON.stringify({ filename })\n )\n\n // Inform user\n toast.error('File upload failed')\n resolve(null)\n },\n onBeforeRequest() {\n if (!eventListenerAdded) {\n window.addEventListener('beforeunload', onBeforeUnload)\n window.addEventListener('unload', onPageUnload)\n }\n },\n onProgress: function (bytesUploaded, bytesTotal) {\n const percentage = Math.round((bytesUploaded / bytesTotal) * 100)\n emitter.emit('updateUpload', {\n filename,\n progress: percentage,\n })\n },\n onSuccess() {\n // Clean up beforeunload listener\n window.removeEventListener('beforeunload', onBeforeUnload)\n window.removeEventListener('unload', onPageUnload)\n\n // Mark upload as complete in UI\n emitter.emit('uploadComplete', { filename })\n\n // Trigger post upload processing\n fetch(`${serverURL}${apiRoute}/uploads/${filename}/process`)\n },\n onUploadUrlAvailable: async function () {\n // Prevent multiple callbacks\n if (uploadUrlAvailable) {\n return\n }\n\n // Update flag\n uploadUrlAvailable = true\n\n // Add upload to UI\n emitter.emit('addUpload', { filename })\n\n // Update collection entry\n // with filename, mimeType, and storage\n resolve({\n filename,\n mimeType,\n storage: 's3',\n })\n },\n })\n\n upload.start()\n })\n}\n\nconst { log } = useErrorHandler()\n\nexport const UploadHandler = createClientUploadHandler({\n handler: async function (args) {\n const { serverURL, apiRoute, file, updateFilename } = args\n console.log('upload handler called', file)\n try {\n const mimeType =\n (await getFileType(file)) || file.type || 'application/octet-stream'\n if (!mimeType) {\n throwError(MediaCloudErrors.FILE_TYPE_UNKNOWN)\n return null\n }\n\n const isVideoFile = await isVideo(file)\n\n // Clone file\n let mappedFile = file\n const originalFilename = file.name\n\n if (!isVideoFile) {\n try {\n // Check if file with same name exists\n const response = await fetch(\n `${serverURL}${apiRoute}/uploads/${file.name}/exists`\n )\n\n // If file exists, generate a cloned file with a unique filename\n if (response.status === 200) {\n log(MediaCloudLogs.S3_STORE_FILE_FOUND)\n\n const newFilename = generateUniqueFilename(file.name)\n mappedFile = new File([file], newFilename, { type: file.type })\n await updateFilename(newFilename)\n }\n } catch (_error) {\n logError(MediaCloudErrors.NAMING_FUNCTION_ERROR.message)\n }\n }\n\n const uploadArgs: UploadArgs = {\n file: mappedFile,\n serverURL,\n apiRoute,\n mimeType,\n originalFilename,\n }\n\n if (isVideoFile) {\n return await muxUpload(uploadArgs)\n } else {\n return await tusUpload(uploadArgs)\n }\n } catch (error) {\n console.error(\n '[PLUGIN-MEDIA-CLOUD] Upload handler detailed error:',\n error\n )\n logError(MediaCloudErrors.UPLOAD_HANDLER_ERROR.message)\n toast.error(\n `Upload failed: ${error instanceof Error ? error.message : 'Unknown error'}`\n )\n return null\n }\n },\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"upload-handler.mjs","names":["uploadArgs: UploadArgs"],"sources":["../../../src/components/upload-handler/upload-handler.tsx"],"sourcesContent":["'use client'\n\nimport * as upchunk from '@mux/upchunk'\nimport * as tus from 'tus-js-client'\n\nimport { toast } from '@payloadcms/ui'\nimport { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client'\n\nimport { MediaCloudErrors, MediaCloudLogs } from '../../types/errors'\nimport { useMediaCloudEmitter } from '../../hooks/useMediaCloudEmitter'\nimport { useErrorHandler } from '../../hooks/useErrorHandler'\nimport { isVideo, getFileType, generateUniqueFilename } from '../../utils/file'\n\ninterface UploadArgs {\n serverURL: string\n apiRoute: string\n file: File\n mimeType: string\n}\n\ninterface UploadResult {\n filename: string\n uploadId?: string\n mimeType: string\n storage: 'mux' | 's3'\n}\n\ninterface MuxCreateUploadResponse {\n url: string\n uploadId: string\n filename: string\n}\n\nconst { logError, throwError } = useErrorHandler()\nconst emitter = useMediaCloudEmitter()\n\nconst MUX_CHUNK_SIZE = 30720\nconst TUS_CHUNK_SIZE = 1024 * 1024\nconst TUS_RETRY_DELAYS = [0, 1000, 2000, 5000]\n\n/**\n * Handles Mux video upload with progress tracking\n * @param args - The upload arguments including file, server URL, and callbacks\n * @returns Promise that resolves to upload result or null if upload fails\n */\nasync function muxUpload(args: UploadArgs): Promise<UploadResult | null> {\n const { file, serverURL, apiRoute, mimeType } = args\n\n const filename = file.name\n try {\n // Request upload URL from Mux\n const response = await fetch(`${serverURL}${apiRoute}/mux/upload`, {\n body: JSON.stringify({ filename, mimeType }),\n credentials: 'include',\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n })\n\n const { url, uploadId } = (await response.json()) as MuxCreateUploadResponse\n\n // Create upchunk uploader\n const uploader = await upchunk.createUpload({\n endpoint: url,\n file,\n chunkSize: MUX_CHUNK_SIZE,\n })\n\n // Add upload to tracker\n emitter.emit('addUpload', {\n filename,\n uploadId,\n polling: false,\n pollingUrl: `${serverURL}${apiRoute}/mux/asset`,\n })\n\n // Set up event handlers\n uploader.on('error', function () {\n logError(MediaCloudErrors.MUX_UPLOAD_ERROR.message)\n toast.error('Video upload failed')\n emitter.emit('removeUpload', { uploadId })\n })\n\n uploader.on('progress', function (progress) {\n emitter.emit('updateUpload', {\n filename,\n progress: progress.detail,\n })\n })\n\n uploader.on('success', function () {\n emitter.emit('uploadComplete', { filename })\n })\n\n // Update collection entry\n // with filename, uploadId, mimeType, and storage\n return {\n filename,\n uploadId,\n mimeType,\n storage: 'mux',\n }\n } catch (_error) {\n logError(MediaCloudErrors.MUX_DIRECT_UPLOAD_ERROR.message)\n toast.error('Video upload failed')\n return null\n }\n}\n\n/**\n * Handles TUS file upload with resumable capabilities\n * @param args - The upload arguments including file, server URL, and callbacks\n * @returns Promise that resolves to upload result or null if upload fails\n */\nasync function tusUpload(args: UploadArgs): Promise<UploadResult | null> {\n const { apiRoute, serverURL, file, mimeType } = args\n\n const filename = file.name\n const filetype = file.type\n const filesize = file.size.toString()\n\n // Clear any stale TUS uploads from localStorage\n Object.keys(localStorage)\n .filter((key) => key.startsWith('tus::'))\n .forEach((key) => localStorage.removeItem(key))\n\n // Flag to prevent multiple onUploadUrlAvailable calls\n let uploadUrlAvailable = false\n let eventListenerAdded = false\n\n function onBeforeUnload(e: BeforeUnloadEvent) {\n window.removeEventListener('beforeunload', onBeforeUnload)\n e.preventDefault()\n }\n\n function onPageUnload() {\n window.removeEventListener('unload', onPageUnload)\n navigator.sendBeacon(\n `${serverURL}${apiRoute}/uploads/cleanup`,\n JSON.stringify({ filename })\n )\n }\n\n return new Promise((resolve) => {\n const upload = new tus.Upload(file, {\n endpoint: `${serverURL}${apiRoute}/uploads`,\n retryDelays: TUS_RETRY_DELAYS,\n chunkSize: TUS_CHUNK_SIZE,\n storeFingerprintForResuming: false,\n metadata: {\n filetype,\n filesize,\n filename,\n contentType: filetype,\n contentLength: filesize,\n contentDisposition: 'inline',\n },\n onError(error) {\n logError(MediaCloudErrors.TUS_UPLOAD_ERROR.message)\n logError(error.message)\n\n // Clean up\n navigator.sendBeacon(\n `${serverURL}${apiRoute}/uploads/cleanup`,\n JSON.stringify({ filename })\n )\n\n // Inform user\n emitter.emit('removeUpload', { filename })\n toast.error('File upload failed')\n resolve(null)\n },\n onBeforeRequest() {\n if (!eventListenerAdded) {\n window.addEventListener('beforeunload', onBeforeUnload)\n window.addEventListener('unload', onPageUnload)\n }\n },\n onProgress: function (bytesUploaded, bytesTotal) {\n const percentage = Math.round((bytesUploaded / bytesTotal) * 100)\n emitter.emit('updateUpload', {\n filename,\n progress: percentage,\n })\n },\n onSuccess() {\n // Clean up beforeunload listener\n window.removeEventListener('beforeunload', onBeforeUnload)\n window.removeEventListener('unload', onPageUnload)\n\n // Mark upload as complete in UI\n emitter.emit('uploadComplete', { filename })\n\n // Trigger post upload processing\n fetch(`${serverURL}${apiRoute}/uploads/${filename}/process`)\n },\n onUploadUrlAvailable: async function () {\n // Prevent multiple callbacks\n if (uploadUrlAvailable) {\n return\n }\n\n // Update flag\n uploadUrlAvailable = true\n\n // Add upload to UI\n emitter.emit('addUpload', { filename })\n\n // Update collection entry\n // with filename, mimeType, and storage\n resolve({\n filename,\n mimeType,\n storage: 's3',\n })\n },\n })\n\n upload.start()\n })\n}\n\nconst { log } = useErrorHandler()\n\nexport const UploadHandler = createClientUploadHandler({\n handler: async function (args) {\n const { serverURL, apiRoute, file, updateFilename } = args\n console.log('upload handler called', file)\n try {\n const mimeType =\n (await getFileType(file)) || file.type || 'application/octet-stream'\n if (!mimeType) {\n throwError(MediaCloudErrors.FILE_TYPE_UNKNOWN)\n return null\n }\n\n const isVideoFile = await isVideo(file)\n\n // Clone file\n let mappedFile = file\n\n if (!isVideoFile) {\n try {\n // Check if file with same name exists\n const response = await fetch(\n `${serverURL}${apiRoute}/uploads/${file.name}/exists`\n ).catch(() => {})\n\n // If file exists, generate a cloned file with a unique filename\n if (response?.status === 200) {\n log(MediaCloudLogs.S3_STORE_FILE_FOUND)\n\n const newFilename = generateUniqueFilename(file.name)\n mappedFile = new File([file], newFilename, { type: file.type })\n await updateFilename(newFilename)\n }\n } catch (_error) {\n logError(MediaCloudErrors.NAMING_FUNCTION_ERROR.message)\n }\n }\n\n const uploadArgs: UploadArgs = {\n file: mappedFile,\n serverURL,\n apiRoute,\n mimeType,\n }\n\n if (isVideoFile) {\n return await muxUpload(uploadArgs)\n } else {\n return await tusUpload(uploadArgs)\n }\n } catch (error) {\n console.error(\n '[PLUGIN-MEDIA-CLOUD] Upload handler detailed error:',\n error\n )\n logError(MediaCloudErrors.UPLOAD_HANDLER_ERROR.message)\n toast.error(\n `Upload failed: ${error instanceof Error ? error.message : 'Unknown error'}`\n )\n return null\n }\n },\n})\n"],"mappings":";;;;;;;;;;;;AAiCA,MAAM,EAAE,UAAU,eAAe,iBAAiB;AAClD,MAAM,UAAU,sBAAsB;AAEtC,MAAM,iBAAiB;AACvB,MAAM,iBAAiB,OAAO;AAC9B,MAAM,mBAAmB;CAAC;CAAG;CAAM;CAAM;CAAK;;;;;;AAO9C,eAAe,UAAU,MAAgD;CACvE,MAAM,EAAE,MAAM,WAAW,UAAU,aAAa;CAEhD,MAAM,WAAW,KAAK;AACtB,KAAI;EAWF,MAAM,EAAE,KAAK,aAAc,OATV,MAAM,MAAM,GAAG,YAAY,SAAS,cAAc;GACjE,MAAM,KAAK,UAAU;IAAE;IAAU;IAAU,CAAC;GAC5C,aAAa;GACb,QAAQ;GACR,SAAS,EACP,gBAAgB,oBACjB;GACF,CAAC,EAEwC,MAAM;EAGhD,MAAM,WAAW,MAAM,QAAQ,aAAa;GAC1C,UAAU;GACV;GACA,WAAW;GACZ,CAAC;AAGF,UAAQ,KAAK,aAAa;GACxB;GACA;GACA,SAAS;GACT,YAAY,GAAG,YAAY,SAAS;GACrC,CAAC;AAGF,WAAS,GAAG,SAAS,WAAY;AAC/B,YAAS,iBAAiB,iBAAiB,QAAQ;AACnD,SAAM,MAAM,sBAAsB;AAClC,WAAQ,KAAK,gBAAgB,EAAE,UAAU,CAAC;IAC1C;AAEF,WAAS,GAAG,YAAY,SAAU,UAAU;AAC1C,WAAQ,KAAK,gBAAgB;IAC3B;IACA,UAAU,SAAS;IACpB,CAAC;IACF;AAEF,WAAS,GAAG,WAAW,WAAY;AACjC,WAAQ,KAAK,kBAAkB,EAAE,UAAU,CAAC;IAC5C;AAIF,SAAO;GACL;GACA;GACA;GACA,SAAS;GACV;UACM,QAAQ;AACf,WAAS,iBAAiB,wBAAwB,QAAQ;AAC1D,QAAM,MAAM,sBAAsB;AAClC,SAAO;;;;;;;;AASX,eAAe,UAAU,MAAgD;CACvE,MAAM,EAAE,UAAU,WAAW,MAAM,aAAa;CAEhD,MAAM,WAAW,KAAK;CACtB,MAAM,WAAW,KAAK;CACtB,MAAM,WAAW,KAAK,KAAK,UAAU;AAGrC,QAAO,KAAK,aAAa,CACtB,QAAQ,QAAQ,IAAI,WAAW,QAAQ,CAAC,CACxC,SAAS,QAAQ,aAAa,WAAW,IAAI,CAAC;CAGjD,IAAI,qBAAqB;CAGzB,SAAS,eAAe,GAAsB;AAC5C,SAAO,oBAAoB,gBAAgB,eAAe;AAC1D,IAAE,gBAAgB;;CAGpB,SAAS,eAAe;AACtB,SAAO,oBAAoB,UAAU,aAAa;AAClD,YAAU,WACR,GAAG,YAAY,SAAS,mBACxB,KAAK,UAAU,EAAE,UAAU,CAAC,CAC7B;;AAGH,QAAO,IAAI,SAAS,YAAY;AA2E9B,EA1Ee,IAAI,IAAI,OAAO,MAAM;GAClC,UAAU,GAAG,YAAY,SAAS;GAClC,aAAa;GACb,WAAW;GACX,6BAA6B;GAC7B,UAAU;IACR;IACA;IACA;IACA,aAAa;IACb,eAAe;IACf,oBAAoB;IACrB;GACD,QAAQ,OAAO;AACb,aAAS,iBAAiB,iBAAiB,QAAQ;AACnD,aAAS,MAAM,QAAQ;AAGvB,cAAU,WACR,GAAG,YAAY,SAAS,mBACxB,KAAK,UAAU,EAAE,UAAU,CAAC,CAC7B;AAGD,YAAQ,KAAK,gBAAgB,EAAE,UAAU,CAAC;AAC1C,UAAM,MAAM,qBAAqB;AACjC,YAAQ,KAAK;;GAEf,kBAAkB;AAEd,WAAO,iBAAiB,gBAAgB,eAAe;AACvD,WAAO,iBAAiB,UAAU,aAAa;;GAGnD,YAAY,SAAU,eAAe,YAAY;IAC/C,MAAM,aAAa,KAAK,MAAO,gBAAgB,aAAc,IAAI;AACjE,YAAQ,KAAK,gBAAgB;KAC3B;KACA,UAAU;KACX,CAAC;;GAEJ,YAAY;AAEV,WAAO,oBAAoB,gBAAgB,eAAe;AAC1D,WAAO,oBAAoB,UAAU,aAAa;AAGlD,YAAQ,KAAK,kBAAkB,EAAE,UAAU,CAAC;AAG5C,UAAM,GAAG,YAAY,SAAS,WAAW,SAAS,UAAU;;GAE9D,sBAAsB,iBAAkB;AAEtC,QAAI,mBACF;AAIF,yBAAqB;AAGrB,YAAQ,KAAK,aAAa,EAAE,UAAU,CAAC;AAIvC,YAAQ;KACN;KACA;KACA,SAAS;KACV,CAAC;;GAEL,CAAC,CAEK,OAAO;GACd;;AAGJ,MAAM,EAAE,QAAQ,iBAAiB;AAEjC,MAAa,gBAAgB,0BAA0B,EACrD,SAAS,eAAgB,MAAM;CAC7B,MAAM,EAAE,WAAW,UAAU,MAAM,mBAAmB;AACtD,SAAQ,IAAI,yBAAyB,KAAK;AAC1C,KAAI;EACF,MAAM,WACH,MAAM,YAAY,KAAK,IAAK,KAAK,QAAQ;AAC5C,MAAI,CAAC,UAAU;AACb,cAAW,iBAAiB,kBAAkB;AAC9C,UAAO;;EAGT,MAAM,cAAc,MAAM,QAAQ,KAAK;EAGvC,IAAI,aAAa;AAEjB,MAAI,CAAC,YACH,KAAI;AAOF,QALiB,MAAM,MACrB,GAAG,YAAY,SAAS,WAAW,KAAK,KAAK,SAC9C,CAAC,YAAY,GAAG,GAGH,WAAW,KAAK;AAC5B,QAAI,eAAe,oBAAoB;IAEvC,MAAM,cAAc,uBAAuB,KAAK,KAAK;AACrD,iBAAa,IAAI,KAAK,CAAC,KAAK,EAAE,aAAa,EAAE,MAAM,KAAK,MAAM,CAAC;AAC/D,UAAM,eAAe,YAAY;;WAE5B,QAAQ;AACf,YAAS,iBAAiB,sBAAsB,QAAQ;;EAI5D,MAAMA,aAAyB;GAC7B,MAAM;GACN;GACA;GACA;GACD;AAED,MAAI,YACF,QAAO,MAAM,UAAU,WAAW;MAElC,QAAO,MAAM,UAAU,WAAW;UAE7B,OAAO;AACd,UAAQ,MACN,uDACA,MACD;AACD,WAAS,iBAAiB,qBAAqB,QAAQ;AACvD,QAAM,MACJ,kBAAkB,iBAAiB,QAAQ,MAAM,UAAU,kBAC5D;AACD,SAAO;;GAGZ,CAAC"}
@@ -10,8 +10,6 @@ function getTusFileExistsHandler(args) {
10
10
  const { routeParams, payload } = req;
11
11
  const filename = routeParams?.filename;
12
12
  if (!filename) throwError(MediaCloudErrors.FILE_MISSING_NAME);
13
- const url = s3Store.getUrl(filename);
14
- const s3Response = await fetch(url);
15
13
  const { docs } = await payload.find({
16
14
  collection: "media",
17
15
  where: { filename: { equals: filename } },
@@ -19,7 +17,8 @@ function getTusFileExistsHandler(args) {
19
17
  pagination: false
20
18
  });
21
19
  if (docs.length > 0) return Response.json({ message: "File found [payload]" }, { status: 200 });
22
- if (s3Response.status === 200) return Response.json({ message: "File found [S3]" }, { status: 200 });
20
+ const url = s3Store.getUrl(filename);
21
+ if ((await fetch(url, { method: "HEAD" })).status === 200) return Response.json({ message: "File found [S3]" }, { status: 200 });
23
22
  return Response.json({ message: "File not found" }, { status: 404 });
24
23
  } catch (_error) {
25
24
  logError(MediaCloudErrors.FILE_DIMENSIONS_ERROR.message);
@@ -1 +1 @@
1
- {"version":3,"file":"tusFileExistsHandler.mjs","names":[],"sources":["../../src/endpoints/tusFileExistsHandler.ts"],"sourcesContent":["import type { PayloadHandler } from 'payload'\nimport { useErrorHandler } from '../hooks/useErrorHandler'\nimport { MediaCloudErrors } from '../types/errors'\n\nimport { S3Store } from '../tus/stores/s3/s3-store'\n\ninterface GetTusPostProcessorHandlerArgs {\n s3Store: S3Store\n}\n\nexport function getTusFileExistsHandler(\n args: GetTusPostProcessorHandlerArgs\n): PayloadHandler {\n const { s3Store } = args\n const { throwError, logError } = useErrorHandler()\n\n return async function (req) {\n try {\n const { routeParams, payload } = req\n const filename = routeParams?.filename as string\n\n if (!filename) {\n throwError(MediaCloudErrors.FILE_MISSING_NAME)\n }\n\n const url = s3Store.getUrl(filename)\n const s3Response = await fetch(url)\n\n const { docs } = await payload.find({\n collection: 'media',\n where: {\n filename: {\n equals: filename,\n },\n },\n limit: 1,\n pagination: false,\n })\n\n // Return 200 if file exists in payload\n if (docs.length > 0) {\n return Response.json(\n { message: 'File found [payload]' },\n { status: 200 }\n )\n }\n\n // Return 200 if file exists in storage\n if (s3Response.status === 200) {\n return Response.json({ message: 'File found [S3]' }, { status: 200 })\n }\n\n // Return 404 if file does not exist in payload or storage\n return Response.json({ message: 'File not found' }, { status: 404 })\n } catch (_error) {\n logError(MediaCloudErrors.FILE_DIMENSIONS_ERROR.message)\n return Response.json(\n { message: 'Failed to process asset' },\n { status: 500 }\n )\n }\n }\n}\n"],"mappings":";;;;AAUA,SAAgB,wBACd,MACgB;CAChB,MAAM,EAAE,YAAY;CACpB,MAAM,EAAE,YAAY,aAAa,iBAAiB;AAElD,QAAO,eAAgB,KAAK;AAC1B,MAAI;GACF,MAAM,EAAE,aAAa,YAAY;GACjC,MAAM,WAAW,aAAa;AAE9B,OAAI,CAAC,SACH,YAAW,iBAAiB,kBAAkB;GAGhD,MAAM,MAAM,QAAQ,OAAO,SAAS;GACpC,MAAM,aAAa,MAAM,MAAM,IAAI;GAEnC,MAAM,EAAE,SAAS,MAAM,QAAQ,KAAK;IAClC,YAAY;IACZ,OAAO,EACL,UAAU,EACR,QAAQ,UACT,EACF;IACD,OAAO;IACP,YAAY;IACb,CAAC;AAGF,OAAI,KAAK,SAAS,EAChB,QAAO,SAAS,KACd,EAAE,SAAS,wBAAwB,EACnC,EAAE,QAAQ,KAAK,CAChB;AAIH,OAAI,WAAW,WAAW,IACxB,QAAO,SAAS,KAAK,EAAE,SAAS,mBAAmB,EAAE,EAAE,QAAQ,KAAK,CAAC;AAIvE,UAAO,SAAS,KAAK,EAAE,SAAS,kBAAkB,EAAE,EAAE,QAAQ,KAAK,CAAC;WAC7D,QAAQ;AACf,YAAS,iBAAiB,sBAAsB,QAAQ;AACxD,UAAO,SAAS,KACd,EAAE,SAAS,2BAA2B,EACtC,EAAE,QAAQ,KAAK,CAChB"}
1
+ {"version":3,"file":"tusFileExistsHandler.mjs","names":[],"sources":["../../src/endpoints/tusFileExistsHandler.ts"],"sourcesContent":["import type { PayloadHandler } from 'payload'\nimport { useErrorHandler } from '../hooks/useErrorHandler'\nimport { MediaCloudErrors } from '../types/errors'\n\nimport { S3Store } from '../tus/stores/s3/s3-store'\n\ninterface GetTusPostProcessorHandlerArgs {\n s3Store: S3Store\n}\n\nexport function getTusFileExistsHandler(\n args: GetTusPostProcessorHandlerArgs\n): PayloadHandler {\n const { s3Store } = args\n const { throwError, logError } = useErrorHandler()\n\n return async function (req) {\n try {\n const { routeParams, payload } = req\n const filename = routeParams?.filename as string\n\n if (!filename) {\n throwError(MediaCloudErrors.FILE_MISSING_NAME)\n }\n\n // Check if file exists in Payload database\n const { docs } = await payload.find({\n collection: 'media',\n where: {\n filename: {\n equals: filename,\n },\n },\n limit: 1,\n pagination: false,\n })\n\n if (docs.length > 0) {\n return Response.json(\n { message: 'File found [payload]' },\n { status: 200 }\n )\n }\n\n // Check if completed file exists in S3\n const url = s3Store.getUrl(filename)\n const s3Response = await fetch(url, { method: 'HEAD' })\n\n if (s3Response.status === 200) {\n return Response.json({ message: 'File found [S3]' }, { status: 200 })\n }\n\n return Response.json({ message: 'File not found' }, { status: 404 })\n } catch (_error) {\n logError(MediaCloudErrors.FILE_DIMENSIONS_ERROR.message)\n return Response.json(\n { message: 'Failed to process asset' },\n { status: 500 }\n )\n }\n }\n}\n"],"mappings":";;;;AAUA,SAAgB,wBACd,MACgB;CAChB,MAAM,EAAE,YAAY;CACpB,MAAM,EAAE,YAAY,aAAa,iBAAiB;AAElD,QAAO,eAAgB,KAAK;AAC1B,MAAI;GACF,MAAM,EAAE,aAAa,YAAY;GACjC,MAAM,WAAW,aAAa;AAE9B,OAAI,CAAC,SACH,YAAW,iBAAiB,kBAAkB;GAIhD,MAAM,EAAE,SAAS,MAAM,QAAQ,KAAK;IAClC,YAAY;IACZ,OAAO,EACL,UAAU,EACR,QAAQ,UACT,EACF;IACD,OAAO;IACP,YAAY;IACb,CAAC;AAEF,OAAI,KAAK,SAAS,EAChB,QAAO,SAAS,KACd,EAAE,SAAS,wBAAwB,EACnC,EAAE,QAAQ,KAAK,CAChB;GAIH,MAAM,MAAM,QAAQ,OAAO,SAAS;AAGpC,QAFmB,MAAM,MAAM,KAAK,EAAE,QAAQ,QAAQ,CAAC,EAExC,WAAW,IACxB,QAAO,SAAS,KAAK,EAAE,SAAS,mBAAmB,EAAE,EAAE,QAAQ,KAAK,CAAC;AAGvE,UAAO,SAAS,KAAK,EAAE,SAAS,kBAAkB,EAAE,EAAE,QAAQ,KAAK,CAAC;WAC7D,QAAQ;AACf,YAAS,iBAAiB,sBAAsB,QAAQ;AACxD,UAAO,SAAS,KACd,EAAE,SAAS,2BAA2B,EACtC,EAAE,QAAQ,KAAK,CAChB"}
@@ -1,6 +1,6 @@
1
1
  import { TusUploadMetadata } from "../../../types/index.mjs";
2
2
  import { S3 } from "@aws-sdk/client-s3";
3
- import { KvStore, Upload } from "@tus/utils";
3
+ import { Upload } from "@tus/utils";
4
4
 
5
5
  //#region src/tus/stores/s3/metadata-manager.d.ts
6
6
  type GenerateInfoKeyArgs = {
@@ -20,16 +20,12 @@ type SaveMetadataArgs = {
20
20
  type CompleteMetadataArgs = {
21
21
  upload: Upload;
22
22
  };
23
- type ClearCacheArgs = {
24
- id: string;
25
- };
26
23
  declare class S3MetadataManager {
27
24
  private client;
28
25
  private bucket;
29
- private cache;
30
26
  private shouldUseExpirationTags;
31
27
  private generateCompleteTag;
32
- constructor(client: S3, bucket: string, cache: KvStore<TusUploadMetadata>, shouldUseExpirationTags: () => boolean, generateCompleteTag: (value: 'false' | 'true') => string | undefined);
28
+ constructor(client: S3, bucket: string, shouldUseExpirationTags: () => boolean, generateCompleteTag: (value: 'false' | 'true') => string | undefined);
33
29
  /**
34
30
  * Generates the S3 key for metadata info files
35
31
  * @param args - The function arguments
@@ -46,9 +42,8 @@ declare class S3MetadataManager {
46
42
  */
47
43
  generatePartKey(args: GeneratePartKeyArgs): string;
48
44
  /**
49
- * Retrieves upload metadata previously saved in `${file_id}.info`
50
- * There's a small and simple caching mechanism to avoid multiple
51
- * HTTP calls to S3
45
+ * Retrieves upload metadata previously saved in `${file_id}.info`.
46
+ * Always fetches fresh data from S3 to ensure correctness.
52
47
  * @param args - The function arguments
53
48
  * @param args.id - The file ID to retrieve metadata for
54
49
  * @returns Promise that resolves to the upload metadata
@@ -56,9 +51,7 @@ declare class S3MetadataManager {
56
51
  getMetadata(args: GetMetadataArgs): Promise<TusUploadMetadata>;
57
52
  /**
58
53
  * Saves upload metadata to a `${file_id}.info` file on S3.
59
- * Please note that the file is empty and the metadata is saved
60
- * on the S3 object's `Metadata` field, so that only a `headObject`
61
- * is necessary to retrieve the data.
54
+ * The upload-id is stored in the S3 object metadata.
62
55
  * @param args - The function arguments
63
56
  * @param args.upload - The upload object containing metadata
64
57
  * @param args.uploadId - The upload ID for the multipart upload
@@ -72,13 +65,6 @@ declare class S3MetadataManager {
72
65
  * @returns Promise that resolves when metadata is updated
73
66
  */
74
67
  completeMetadata(args: CompleteMetadataArgs): Promise<void>;
75
- /**
76
- * Removes cached data for a given file
77
- * @param args - The function arguments
78
- * @param args.id - The file ID to remove from cache
79
- * @returns Promise that resolves when cache is cleared
80
- */
81
- clearCache(args: ClearCacheArgs): Promise<void>;
82
68
  }
83
69
  //#endregion
84
70
  export { S3MetadataManager };
@@ -5,10 +5,9 @@ import { TUS_RESUMABLE, Upload } from "@tus/utils";
5
5
  //#region src/tus/stores/s3/metadata-manager.ts
6
6
  const { log } = useErrorHandler();
7
7
  var S3MetadataManager = class {
8
- constructor(client, bucket, cache, shouldUseExpirationTags, generateCompleteTag) {
8
+ constructor(client, bucket, shouldUseExpirationTags, generateCompleteTag) {
9
9
  this.client = client;
10
10
  this.bucket = bucket;
11
- this.cache = cache;
12
11
  this.shouldUseExpirationTags = shouldUseExpirationTags;
13
12
  this.generateCompleteTag = generateCompleteTag;
14
13
  }
@@ -36,24 +35,21 @@ var S3MetadataManager = class {
36
35
  return key;
37
36
  }
38
37
  /**
39
- * Retrieves upload metadata previously saved in `${file_id}.info`
40
- * There's a small and simple caching mechanism to avoid multiple
41
- * HTTP calls to S3
38
+ * Retrieves upload metadata previously saved in `${file_id}.info`.
39
+ * Always fetches fresh data from S3 to ensure correctness.
42
40
  * @param args - The function arguments
43
41
  * @param args.id - The file ID to retrieve metadata for
44
42
  * @returns Promise that resolves to the upload metadata
45
43
  */
46
44
  async getMetadata(args) {
47
45
  const { id } = args;
48
- const cached = await this.cache.get(id);
49
- if (cached) return cached;
50
46
  const { Body, Metadata } = await this.client.getObject({
51
47
  Bucket: this.bucket,
52
48
  Key: this.generateInfoKey({ id })
53
49
  });
54
50
  const file = JSON.parse(await Body?.transformToString());
55
51
  const parsedSize = file.size === void 0 || file.size === null ? void 0 : Number(file.size);
56
- const metadata = {
52
+ return {
57
53
  file: new Upload({
58
54
  id,
59
55
  creation_date: file.creation_date,
@@ -63,16 +59,12 @@ var S3MetadataManager = class {
63
59
  storage: file.storage
64
60
  }),
65
61
  "tus-version": Metadata?.["tus-version"],
66
- "upload-id": Metadata?.["upload-id"]
62
+ "upload-id": Metadata?.["upload-id"] || file["upload-id"]
67
63
  };
68
- await this.cache.set(id, metadata);
69
- return metadata;
70
64
  }
71
65
  /**
72
66
  * Saves upload metadata to a `${file_id}.info` file on S3.
73
- * Please note that the file is empty and the metadata is saved
74
- * on the S3 object's `Metadata` field, so that only a `headObject`
75
- * is necessary to retrieve the data.
67
+ * The upload-id is stored in the S3 object metadata.
76
68
  * @param args - The function arguments
77
69
  * @param args.upload - The upload object containing metadata
78
70
  * @param args.uploadId - The upload ID for the multipart upload
@@ -101,14 +93,8 @@ var S3MetadataManager = class {
101
93
  */
102
94
  async completeMetadata(args) {
103
95
  const { upload } = args;
104
- const metadata = await this.getMetadata({ id: upload.id });
105
- const completedMetadata = {
106
- ...metadata,
107
- file: upload
108
- };
109
- await this.cache.set(upload.id, completedMetadata);
110
96
  if (!this.shouldUseExpirationTags()) return;
111
- const { "upload-id": uploadId } = metadata;
97
+ const { "upload-id": uploadId } = await this.getMetadata({ id: upload.id });
112
98
  await this.client.putObject({
113
99
  Body: JSON.stringify(upload),
114
100
  Bucket: this.bucket,
@@ -120,17 +106,6 @@ var S3MetadataManager = class {
120
106
  Tagging: this.generateCompleteTag("true")
121
107
  });
122
108
  }
123
- /**
124
- * Removes cached data for a given file
125
- * @param args - The function arguments
126
- * @param args.id - The file ID to remove from cache
127
- * @returns Promise that resolves when cache is cleared
128
- */
129
- async clearCache(args) {
130
- const { id } = args;
131
- log(MediaCloudLogs.S3_STORE_METADATA_CACHE_CLEARED);
132
- await this.cache.delete(id);
133
- }
134
109
  };
135
110
 
136
111
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"metadata-manager.mjs","names":["client: S3","bucket: string","cache: KvStore<TusUploadMetadata>","shouldUseExpirationTags: () => boolean","generateCompleteTag: (value: 'false' | 'true') => string | undefined","metadata: TusUploadMetadata","completedMetadata: TusUploadMetadata"],"sources":["../../../../src/tus/stores/s3/metadata-manager.ts"],"sourcesContent":["import { TUS_RESUMABLE, Upload } from '@tus/utils'\n\nimport { useErrorHandler } from '../../../hooks/useErrorHandler'\nimport { MediaCloudLogs } from '../../../types/errors'\n\nimport type { S3 } from '@aws-sdk/client-s3'\nimport type { KvStore } from '@tus/utils'\nimport type { TusUploadMetadata } from '../../../types'\n\ntype GenerateInfoKeyArgs = {\n id: string\n}\n\ntype GeneratePartKeyArgs = {\n id: string\n isIncomplete?: boolean\n}\n\ntype GetMetadataArgs = {\n id: string\n}\n\ntype SaveMetadataArgs = {\n upload: Upload\n uploadId: string\n}\n\ntype CompleteMetadataArgs = {\n upload: Upload\n}\n\ntype ClearCacheArgs = {\n id: string\n}\n\nconst { log } = useErrorHandler()\n\nexport class S3MetadataManager {\n constructor(\n private client: S3,\n private bucket: string,\n private cache: KvStore<TusUploadMetadata>,\n private shouldUseExpirationTags: () => boolean,\n private generateCompleteTag: (value: 'false' | 'true') => string | undefined\n ) {}\n /**\n * Generates the S3 key for metadata info files\n * @param args - The function arguments\n * @param args.id - The file ID\n * @returns The S3 key for the metadata file\n */\n generateInfoKey(args: GenerateInfoKeyArgs): string {\n const { id } = args\n return `${id}.info`\n }\n\n /**\n * Generates the S3 key for part files\n * @param args - The function arguments\n * @param args.id - The file ID\n * @param args.isIncomplete - Whether this is an incomplete part\n * @returns The S3 key for the part file\n */\n generatePartKey(args: GeneratePartKeyArgs): string {\n const { id, isIncomplete = false } = args\n let key = id\n if (isIncomplete) {\n key += '.part'\n }\n\n // TODO: introduce ObjectPrefixing for parts and incomplete parts\n // ObjectPrefix is prepended to the name of each S3 object that is created\n // to store uploaded files. It can be used to create a pseudo-directory\n // structure in the bucket, e.g. \"path/to/my/uploads\".\n return key\n }\n\n /**\n * Retrieves upload metadata previously saved in `${file_id}.info`\n * There's a small and simple caching mechanism to avoid multiple\n * HTTP calls to S3\n * @param args - The function arguments\n * @param args.id - The file ID to retrieve metadata for\n * @returns Promise that resolves to the upload metadata\n */\n async getMetadata(args: GetMetadataArgs): Promise<TusUploadMetadata> {\n const { id } = args\n const cached = await this.cache.get(id)\n\n if (cached) {\n return cached\n }\n\n const { Body, Metadata } = await this.client.getObject({\n Bucket: this.bucket,\n Key: this.generateInfoKey({ id }),\n })\n const file = JSON.parse((await Body?.transformToString()) as string)\n\n const parsedSize =\n file.size === undefined || file.size === null\n ? undefined\n : Number(file.size)\n const metadata: TusUploadMetadata = {\n file: new Upload({\n id,\n creation_date: file.creation_date,\n metadata: file.metadata,\n offset: Number.parseInt(file.offset, 10),\n size: Number.isFinite(parsedSize) ? parsedSize : undefined,\n storage: file.storage,\n }),\n 'tus-version': Metadata?.['tus-version'] as string,\n 'upload-id': Metadata?.['upload-id'] as string,\n }\n await this.cache.set(id, metadata)\n return metadata\n }\n\n /**\n * Saves upload metadata to a `${file_id}.info` file on S3.\n * Please note that the file is empty and the metadata is saved\n * on the S3 object's `Metadata` field, so that only a `headObject`\n * is necessary to retrieve the data.\n * @param args - The function arguments\n * @param args.upload - The upload object containing metadata\n * @param args.uploadId - The upload ID for the multipart upload\n * @returns Promise that resolves when metadata is saved\n */\n async saveMetadata(args: SaveMetadataArgs): Promise<void> {\n const { upload, uploadId } = args\n log(MediaCloudLogs.S3_STORE_METADATA_SAVING)\n await this.client.putObject({\n Body: JSON.stringify(upload),\n Bucket: this.bucket,\n Key: this.generateInfoKey({ id: upload.id }),\n Metadata: {\n 'tus-version': TUS_RESUMABLE,\n 'upload-id': uploadId,\n },\n Tagging: this.generateCompleteTag('false'),\n })\n log(MediaCloudLogs.S3_STORE_METADATA_SAVED)\n }\n\n /**\n * Completes metadata by updating it with completion tags\n * @param args - The function arguments\n * @param args.upload - The completed upload object\n * @returns Promise that resolves when metadata is updated\n */\n async completeMetadata(args: CompleteMetadataArgs): Promise<void> {\n const { upload } = args\n // Always update the cache with the completed upload\n const metadata = await this.getMetadata({ id: upload.id })\n const completedMetadata: TusUploadMetadata = {\n ...metadata,\n file: upload,\n }\n await this.cache.set(upload.id, completedMetadata)\n\n if (!this.shouldUseExpirationTags()) {\n return\n }\n\n const { 'upload-id': uploadId } = metadata\n await this.client.putObject({\n Body: JSON.stringify(upload),\n Bucket: this.bucket,\n Key: this.generateInfoKey({ id: upload.id }),\n Metadata: {\n 'tus-version': TUS_RESUMABLE,\n 'upload-id': uploadId,\n },\n Tagging: this.generateCompleteTag('true'),\n })\n }\n\n /**\n * Removes cached data for a given file\n * @param args - The function arguments\n * @param args.id - The file ID to remove from cache\n * @returns Promise that resolves when cache is cleared\n */\n async clearCache(args: ClearCacheArgs): Promise<void> {\n const { id } = args\n log(MediaCloudLogs.S3_STORE_METADATA_CACHE_CLEARED)\n await this.cache.delete(id)\n }\n}\n"],"mappings":";;;;;AAmCA,MAAM,EAAE,QAAQ,iBAAiB;AAEjC,IAAa,oBAAb,MAA+B;CAC7B,YACE,AAAQA,QACR,AAAQC,QACR,AAAQC,OACR,AAAQC,yBACR,AAAQC,qBACR;EALQ;EACA;EACA;EACA;EACA;;;;;;;;CAQV,gBAAgB,MAAmC;EACjD,MAAM,EAAE,OAAO;AACf,SAAO,GAAG,GAAG;;;;;;;;;CAUf,gBAAgB,MAAmC;EACjD,MAAM,EAAE,IAAI,eAAe,UAAU;EACrC,IAAI,MAAM;AACV,MAAI,aACF,QAAO;AAOT,SAAO;;;;;;;;;;CAWT,MAAM,YAAY,MAAmD;EACnE,MAAM,EAAE,OAAO;EACf,MAAM,SAAS,MAAM,KAAK,MAAM,IAAI,GAAG;AAEvC,MAAI,OACF,QAAO;EAGT,MAAM,EAAE,MAAM,aAAa,MAAM,KAAK,OAAO,UAAU;GACrD,QAAQ,KAAK;GACb,KAAK,KAAK,gBAAgB,EAAE,IAAI,CAAC;GAClC,CAAC;EACF,MAAM,OAAO,KAAK,MAAO,MAAM,MAAM,mBAAmB,CAAY;EAEpE,MAAM,aACJ,KAAK,SAAS,UAAa,KAAK,SAAS,OACrC,SACA,OAAO,KAAK,KAAK;EACvB,MAAMC,WAA8B;GAClC,MAAM,IAAI,OAAO;IACf;IACA,eAAe,KAAK;IACpB,UAAU,KAAK;IACf,QAAQ,OAAO,SAAS,KAAK,QAAQ,GAAG;IACxC,MAAM,OAAO,SAAS,WAAW,GAAG,aAAa;IACjD,SAAS,KAAK;IACf,CAAC;GACF,eAAe,WAAW;GAC1B,aAAa,WAAW;GACzB;AACD,QAAM,KAAK,MAAM,IAAI,IAAI,SAAS;AAClC,SAAO;;;;;;;;;;;;CAaT,MAAM,aAAa,MAAuC;EACxD,MAAM,EAAE,QAAQ,aAAa;AAC7B,MAAI,eAAe,yBAAyB;AAC5C,QAAM,KAAK,OAAO,UAAU;GAC1B,MAAM,KAAK,UAAU,OAAO;GAC5B,QAAQ,KAAK;GACb,KAAK,KAAK,gBAAgB,EAAE,IAAI,OAAO,IAAI,CAAC;GAC5C,UAAU;IACR,eAAe;IACf,aAAa;IACd;GACD,SAAS,KAAK,oBAAoB,QAAQ;GAC3C,CAAC;AACF,MAAI,eAAe,wBAAwB;;;;;;;;CAS7C,MAAM,iBAAiB,MAA2C;EAChE,MAAM,EAAE,WAAW;EAEnB,MAAM,WAAW,MAAM,KAAK,YAAY,EAAE,IAAI,OAAO,IAAI,CAAC;EAC1D,MAAMC,oBAAuC;GAC3C,GAAG;GACH,MAAM;GACP;AACD,QAAM,KAAK,MAAM,IAAI,OAAO,IAAI,kBAAkB;AAElD,MAAI,CAAC,KAAK,yBAAyB,CACjC;EAGF,MAAM,EAAE,aAAa,aAAa;AAClC,QAAM,KAAK,OAAO,UAAU;GAC1B,MAAM,KAAK,UAAU,OAAO;GAC5B,QAAQ,KAAK;GACb,KAAK,KAAK,gBAAgB,EAAE,IAAI,OAAO,IAAI,CAAC;GAC5C,UAAU;IACR,eAAe;IACf,aAAa;IACd;GACD,SAAS,KAAK,oBAAoB,OAAO;GAC1C,CAAC;;;;;;;;CASJ,MAAM,WAAW,MAAqC;EACpD,MAAM,EAAE,OAAO;AACf,MAAI,eAAe,gCAAgC;AACnD,QAAM,KAAK,MAAM,OAAO,GAAG"}
1
+ {"version":3,"file":"metadata-manager.mjs","names":["client: S3","bucket: string","shouldUseExpirationTags: () => boolean","generateCompleteTag: (value: 'false' | 'true') => string | undefined"],"sources":["../../../../src/tus/stores/s3/metadata-manager.ts"],"sourcesContent":["import { TUS_RESUMABLE, Upload } from '@tus/utils'\n\nimport { useErrorHandler } from '../../../hooks/useErrorHandler'\nimport { MediaCloudLogs } from '../../../types/errors'\n\nimport type { S3 } from '@aws-sdk/client-s3'\nimport type { TusUploadMetadata } from '../../../types'\n\ntype GenerateInfoKeyArgs = {\n id: string\n}\n\ntype GeneratePartKeyArgs = {\n id: string\n isIncomplete?: boolean\n}\n\ntype GetMetadataArgs = {\n id: string\n}\n\ntype SaveMetadataArgs = {\n upload: Upload\n uploadId: string\n}\n\ntype CompleteMetadataArgs = {\n upload: Upload\n}\n\nconst { log } = useErrorHandler()\n\nexport class S3MetadataManager {\n constructor(\n private client: S3,\n private bucket: string,\n private shouldUseExpirationTags: () => boolean,\n private generateCompleteTag: (value: 'false' | 'true') => string | undefined\n ) {}\n /**\n * Generates the S3 key for metadata info files\n * @param args - The function arguments\n * @param args.id - The file ID\n * @returns The S3 key for the metadata file\n */\n generateInfoKey(args: GenerateInfoKeyArgs): string {\n const { id } = args\n return `${id}.info`\n }\n\n /**\n * Generates the S3 key for part files\n * @param args - The function arguments\n * @param args.id - The file ID\n * @param args.isIncomplete - Whether this is an incomplete part\n * @returns The S3 key for the part file\n */\n generatePartKey(args: GeneratePartKeyArgs): string {\n const { id, isIncomplete = false } = args\n let key = id\n if (isIncomplete) {\n key += '.part'\n }\n\n // TODO: introduce ObjectPrefixing for parts and incomplete parts\n // ObjectPrefix is prepended to the name of each S3 object that is created\n // to store uploaded files. It can be used to create a pseudo-directory\n // structure in the bucket, e.g. \"path/to/my/uploads\".\n return key\n }\n\n /**\n * Retrieves upload metadata previously saved in `${file_id}.info`.\n * Always fetches fresh data from S3 to ensure correctness.\n * @param args - The function arguments\n * @param args.id - The file ID to retrieve metadata for\n * @returns Promise that resolves to the upload metadata\n */\n async getMetadata(args: GetMetadataArgs): Promise<TusUploadMetadata> {\n const { id } = args\n\n const { Body, Metadata } = await this.client.getObject({\n Bucket: this.bucket,\n Key: this.generateInfoKey({ id }),\n })\n\n const file = JSON.parse((await Body?.transformToString()) as string)\n\n const parsedSize =\n file.size === undefined || file.size === null\n ? undefined\n : Number(file.size)\n\n const metadata: TusUploadMetadata = {\n file: new Upload({\n id,\n creation_date: file.creation_date,\n metadata: file.metadata,\n offset: Number.parseInt(file.offset, 10),\n size: Number.isFinite(parsedSize) ? parsedSize : undefined,\n storage: file.storage,\n }),\n 'tus-version': Metadata?.['tus-version'] as string,\n 'upload-id': (Metadata?.['upload-id'] || file['upload-id']) as string,\n }\n\n return metadata\n }\n\n /**\n * Saves upload metadata to a `${file_id}.info` file on S3.\n * The upload-id is stored in the S3 object metadata.\n * @param args - The function arguments\n * @param args.upload - The upload object containing metadata\n * @param args.uploadId - The upload ID for the multipart upload\n * @returns Promise that resolves when metadata is saved\n */\n async saveMetadata(args: SaveMetadataArgs): Promise<void> {\n const { upload, uploadId } = args\n log(MediaCloudLogs.S3_STORE_METADATA_SAVING)\n\n await this.client.putObject({\n Body: JSON.stringify(upload),\n Bucket: this.bucket,\n Key: this.generateInfoKey({ id: upload.id }),\n Metadata: {\n 'tus-version': TUS_RESUMABLE,\n 'upload-id': uploadId,\n },\n Tagging: this.generateCompleteTag('false'),\n })\n log(MediaCloudLogs.S3_STORE_METADATA_SAVED)\n }\n\n /**\n * Completes metadata by updating it with completion tags\n * @param args - The function arguments\n * @param args.upload - The completed upload object\n * @returns Promise that resolves when metadata is updated\n */\n async completeMetadata(args: CompleteMetadataArgs): Promise<void> {\n const { upload } = args\n\n if (!this.shouldUseExpirationTags()) {\n return\n }\n\n const metadata = await this.getMetadata({ id: upload.id })\n const { 'upload-id': uploadId } = metadata\n\n await this.client.putObject({\n Body: JSON.stringify(upload),\n Bucket: this.bucket,\n Key: this.generateInfoKey({ id: upload.id }),\n Metadata: {\n 'tus-version': TUS_RESUMABLE,\n 'upload-id': uploadId,\n },\n Tagging: this.generateCompleteTag('true'),\n })\n }\n}\n"],"mappings":";;;;;AA8BA,MAAM,EAAE,QAAQ,iBAAiB;AAEjC,IAAa,oBAAb,MAA+B;CAC7B,YACE,AAAQA,QACR,AAAQC,QACR,AAAQC,yBACR,AAAQC,qBACR;EAJQ;EACA;EACA;EACA;;;;;;;;CAQV,gBAAgB,MAAmC;EACjD,MAAM,EAAE,OAAO;AACf,SAAO,GAAG,GAAG;;;;;;;;;CAUf,gBAAgB,MAAmC;EACjD,MAAM,EAAE,IAAI,eAAe,UAAU;EACrC,IAAI,MAAM;AACV,MAAI,aACF,QAAO;AAOT,SAAO;;;;;;;;;CAUT,MAAM,YAAY,MAAmD;EACnE,MAAM,EAAE,OAAO;EAEf,MAAM,EAAE,MAAM,aAAa,MAAM,KAAK,OAAO,UAAU;GACrD,QAAQ,KAAK;GACb,KAAK,KAAK,gBAAgB,EAAE,IAAI,CAAC;GAClC,CAAC;EAEF,MAAM,OAAO,KAAK,MAAO,MAAM,MAAM,mBAAmB,CAAY;EAEpE,MAAM,aACJ,KAAK,SAAS,UAAa,KAAK,SAAS,OACrC,SACA,OAAO,KAAK,KAAK;AAevB,SAboC;GAClC,MAAM,IAAI,OAAO;IACf;IACA,eAAe,KAAK;IACpB,UAAU,KAAK;IACf,QAAQ,OAAO,SAAS,KAAK,QAAQ,GAAG;IACxC,MAAM,OAAO,SAAS,WAAW,GAAG,aAAa;IACjD,SAAS,KAAK;IACf,CAAC;GACF,eAAe,WAAW;GAC1B,aAAc,WAAW,gBAAgB,KAAK;GAC/C;;;;;;;;;;CAaH,MAAM,aAAa,MAAuC;EACxD,MAAM,EAAE,QAAQ,aAAa;AAC7B,MAAI,eAAe,yBAAyB;AAE5C,QAAM,KAAK,OAAO,UAAU;GAC1B,MAAM,KAAK,UAAU,OAAO;GAC5B,QAAQ,KAAK;GACb,KAAK,KAAK,gBAAgB,EAAE,IAAI,OAAO,IAAI,CAAC;GAC5C,UAAU;IACR,eAAe;IACf,aAAa;IACd;GACD,SAAS,KAAK,oBAAoB,QAAQ;GAC3C,CAAC;AACF,MAAI,eAAe,wBAAwB;;;;;;;;CAS7C,MAAM,iBAAiB,MAA2C;EAChE,MAAM,EAAE,WAAW;AAEnB,MAAI,CAAC,KAAK,yBAAyB,CACjC;EAIF,MAAM,EAAE,aAAa,aADJ,MAAM,KAAK,YAAY,EAAE,IAAI,OAAO,IAAI,CAAC;AAG1D,QAAM,KAAK,OAAO,UAAU;GAC1B,MAAM,KAAK,UAAU,OAAO;GAC5B,QAAQ,KAAK;GACb,KAAK,KAAK,gBAAgB,EAAE,IAAI,OAAO,IAAI,CAAC;GAC5C,UAAU;IACR,eAAe;IACf,aAAa;IACd;GACD,SAAS,KAAK,oBAAoB,OAAO;GAC1C,CAAC"}
@@ -1,12 +1,12 @@
1
1
  import { Semaphore } from "./semaphore.mjs";
2
2
  import { S3ExpirationManager } from "./expiration-manager.mjs";
3
3
  import { S3FileOperations } from "./file-operations.mjs";
4
- import { S3StoreConfig, TusUploadMetadata } from "../../../types/index.mjs";
4
+ import { S3StoreConfig } from "../../../types/index.mjs";
5
5
  import { S3MetadataManager } from "./metadata-manager.mjs";
6
6
  import { S3PartsManager } from "./parts-manager.mjs";
7
7
  import stream, { Readable } from "node:stream";
8
8
  import { S3 } from "@aws-sdk/client-s3";
9
- import { DataStore, KvStore, Upload } from "@tus/utils";
9
+ import { DataStore, Upload } from "@tus/utils";
10
10
 
11
11
  //#region src/tus/stores/s3/s3-store.d.ts
12
12
  declare class S3Store extends DataStore {
@@ -19,7 +19,6 @@ declare class S3Store extends DataStore {
19
19
  useTags: boolean;
20
20
  expirationPeriodInMilliseconds: number;
21
21
  protected acl?: string;
22
- protected cache: KvStore<TusUploadMetadata>;
23
22
  protected partUploadSemaphore: Semaphore;
24
23
  protected metadataManager: S3MetadataManager;
25
24
  protected fileOperations: S3FileOperations;
@@ -7,7 +7,7 @@ import { S3MetadataManager } from "./metadata-manager.mjs";
7
7
  import { S3PartsManager } from "./parts-manager.mjs";
8
8
  import stream from "node:stream";
9
9
  import { NoSuchKey, NotFound, S3 } from "@aws-sdk/client-s3";
10
- import { DataStore, ERRORS, MemoryKvStore, TUS_RESUMABLE, Upload } from "@tus/utils";
10
+ import { DataStore, ERRORS, TUS_RESUMABLE, Upload } from "@tus/utils";
11
11
 
12
12
  //#region src/tus/stores/s3/s3-store.ts
13
13
  const { log } = useErrorHandler();
@@ -20,7 +20,7 @@ var S3Store = class extends DataStore {
20
20
  this.maxUploadSize = 5497558138880;
21
21
  this.useTags = false;
22
22
  this.expirationPeriodInMilliseconds = 0;
23
- const { maxMultipartParts, minPartSize, partSize, s3ClientConfig, maxConcurrentPartUploads, useTags, expirationPeriodInMilliseconds, cache } = options;
23
+ const { maxMultipartParts, minPartSize, partSize, s3ClientConfig, maxConcurrentPartUploads, useTags, expirationPeriodInMilliseconds } = options;
24
24
  const { acl, bucket, ...restS3ClientConfig } = s3ClientConfig;
25
25
  this.extensions = [
26
26
  "creation",
@@ -38,9 +38,8 @@ var S3Store = class extends DataStore {
38
38
  this.maxMultipartParts = maxMultipartParts ?? this.maxMultipartParts;
39
39
  this.useTags = useTags ?? this.useTags;
40
40
  this.expirationPeriodInMilliseconds = expirationPeriodInMilliseconds ?? this.expirationPeriodInMilliseconds;
41
- this.cache = cache ?? new MemoryKvStore();
42
41
  this.partUploadSemaphore = new Semaphore(maxConcurrentPartUploads ?? 60);
43
- this.metadataManager = new S3MetadataManager(this.client, this.bucket, this.cache, this.shouldUseExpirationTags.bind(this), this.generateCompleteTag.bind(this));
42
+ this.metadataManager = new S3MetadataManager(this.client, this.bucket, this.shouldUseExpirationTags.bind(this), this.generateCompleteTag.bind(this));
44
43
  this.fileOperations = new S3FileOperations(this.maxMultipartParts, this.maxUploadSize, this.minPartSize, this.partSize);
45
44
  this.partsManager = new S3PartsManager(this.client, this.bucket, this.minPartSize, this.partUploadSemaphore, this.metadataManager, this.fileOperations, this.generateCompleteTag.bind(this));
46
45
  this.expirationManager = new S3ExpirationManager(this.client, this.bucket, this.expirationPeriodInMilliseconds, this.generateInfoKey.bind(this), this.generatePartKey.bind(this));
@@ -267,7 +266,6 @@ var S3Store = class extends DataStore {
267
266
  }) }
268
267
  ] }
269
268
  });
270
- await this.metadataManager.clearCache({ id });
271
269
  }
272
270
  /**
273
271
  * Combine all multipart uploads into a single object
@@ -1 +1 @@
1
- {"version":3,"file":"s3-store.mjs","names":["request: AWS.CreateMultipartUploadCommandInput","metadata: TusUploadMetadata","offset: number","lastError: Error | null","region: string"],"sources":["../../../../src/tus/stores/s3/s3-store.ts"],"sourcesContent":["import { NoSuchKey, NotFound, S3 } from '@aws-sdk/client-s3'\nimport {\n DataStore,\n ERRORS,\n MemoryKvStore,\n TUS_RESUMABLE,\n Upload,\n type KvStore,\n} from '@tus/utils'\nimport stream, { type Readable } from 'node:stream'\n\nimport { useErrorHandler } from '../../../hooks/useErrorHandler'\nimport { Semaphore } from './semaphore'\nimport { S3ExpirationManager } from './expiration-manager'\nimport { S3FileOperations } from './file-operations'\nimport { S3MetadataManager } from './metadata-manager'\nimport { S3PartsManager } from './parts-manager'\n\nimport type AWS from '@aws-sdk/client-s3'\nimport type { AWSError, S3StoreConfig, TusUploadMetadata } from '../../../types'\nimport { MediaCloudLogs } from '../../../types/errors'\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 cache: KvStore<TusUploadMetadata>\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 cache,\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.cache = cache ?? new MemoryKvStore<TusUploadMetadata>()\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.cache,\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.generateInfoKey.bind(this),\n this.generatePartKey.bind(this)\n )\n\n // Cleanup expired uploads when the store is initialized\n this.deleteExpired()\n }\n\n /**\n * Generate the key name for the info file\n * @param id - The upload ID\n * @returns The info file key\n */\n protected generateInfoKey(id: string): string {\n return `${id}.info`\n }\n\n /**\n * Generate the key name for a part file\n * @param id - The upload ID\n * @param isIncompletePart - Whether this is an incomplete part (default: false)\n * @returns The part file key\n */\n protected generatePartKey(\n id: string,\n isIncompletePart: boolean = false\n ): string {\n return isIncompletePart ? `${id}.part` : id\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 * @returns Promise that resolves to the created upload\n */\n public async create(upload: Upload): Promise<Upload> {\n log(MediaCloudLogs.S3_STORE_MULTIPART_INIT)\n const request: AWS.CreateMultipartUploadCommandInput = {\n Bucket: this.bucket,\n Key: upload.id,\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 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 * Declares the length of the upload\n * @param file_id - The file ID\n * @param upload_length - The length of the upload\n * @returns Promise that resolves when length is declared\n */\n public async declareUploadLength(\n file_id: string,\n upload_length: number\n ): Promise<void> {\n const { file, 'upload-id': uploadId } =\n await this.metadataManager.getMetadata({ id: file_id })\n if (!file) {\n throw ERRORS.FILE_NOT_FOUND\n }\n\n file.size = upload_length\n\n await this.metadataManager.saveMetadata({ upload: file, uploadId })\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 * @returns Promise that resolves to the number of bytes written\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 // Don't clear cache immediately - Payload might still need the metadata\n // await this.metadataManager.clearCache(id)\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 */\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 */\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 * Removes the file specified by the upload's `id`\n */\n public async remove(id: string): Promise<void> {\n try {\n const { 'upload-id': uploadId } = await this.metadataManager.getMetadata({\n id,\n })\n if (uploadId) {\n await this.client.abortMultipartUpload({\n Bucket: this.bucket,\n Key: id,\n UploadId: uploadId,\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 await this.client.deleteObjects({\n Bucket: this.bucket,\n Delete: {\n Objects: [\n { Key: id },\n { Key: this.metadataManager.generateInfoKey({ id }) },\n {\n Key: this.metadataManager.generatePartKey({\n id,\n isIncomplete: true,\n }),\n },\n ],\n },\n })\n\n await this.metadataManager.clearCache({ id })\n }\n\n /**\n * Combine all multipart uploads into a single object\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 */\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 */\n async deleteExpired(): Promise<number> {\n return this.expirationManager.deleteExpired()\n }\n\n /**\n * Returns the expiration period in milliseconds\n */\n getExpiration(): number {\n return this.expirationManager.getExpiration()\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"s3-store.mjs","names":["request: AWS.CreateMultipartUploadCommandInput","metadata: TusUploadMetadata","offset: number","lastError: Error | null","region: string"],"sources":["../../../../src/tus/stores/s3/s3-store.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 './expiration-manager'\nimport { S3FileOperations } from './file-operations'\nimport { S3MetadataManager } from './metadata-manager'\nimport { S3PartsManager } from './parts-manager'\n\nimport type AWS from '@aws-sdk/client-s3'\nimport type { AWSError, S3StoreConfig, TusUploadMetadata } from '../../../types'\nimport { MediaCloudLogs } from '../../../types/errors'\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.generateInfoKey.bind(this),\n this.generatePartKey.bind(this)\n )\n\n // Cleanup expired uploads when the store is initialized\n this.deleteExpired()\n }\n\n /**\n * Generate the key name for the info file\n * @param id - The upload ID\n * @returns The info file key\n */\n protected generateInfoKey(id: string): string {\n return `${id}.info`\n }\n\n /**\n * Generate the key name for a part file\n * @param id - The upload ID\n * @param isIncompletePart - Whether this is an incomplete part (default: false)\n * @returns The part file key\n */\n protected generatePartKey(\n id: string,\n isIncompletePart: boolean = false\n ): string {\n return isIncompletePart ? `${id}.part` : id\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 * @returns Promise that resolves to the created upload\n */\n public async create(upload: Upload): Promise<Upload> {\n log(MediaCloudLogs.S3_STORE_MULTIPART_INIT)\n const request: AWS.CreateMultipartUploadCommandInput = {\n Bucket: this.bucket,\n Key: upload.id,\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 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 * Declares the length of the upload\n * @param file_id - The file ID\n * @param upload_length - The length of the upload\n * @returns Promise that resolves when length is declared\n */\n public async declareUploadLength(\n file_id: string,\n upload_length: number\n ): Promise<void> {\n const { file, 'upload-id': uploadId } =\n await this.metadataManager.getMetadata({ id: file_id })\n if (!file) {\n throw ERRORS.FILE_NOT_FOUND\n }\n\n file.size = upload_length\n\n await this.metadataManager.saveMetadata({ upload: file, uploadId })\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 * @returns Promise that resolves to the number of bytes written\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 */\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 */\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 * Removes the file specified by the upload's `id`\n */\n public async remove(id: string): Promise<void> {\n try {\n const { 'upload-id': uploadId } = await this.metadataManager.getMetadata({\n id,\n })\n if (uploadId) {\n await this.client.abortMultipartUpload({\n Bucket: this.bucket,\n Key: id,\n UploadId: uploadId,\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 await this.client.deleteObjects({\n Bucket: this.bucket,\n Delete: {\n Objects: [\n { Key: id },\n { Key: this.metadataManager.generateInfoKey({ id }) },\n {\n Key: this.metadataManager.generatePartKey({\n id,\n isIncomplete: true,\n }),\n },\n ],\n },\n })\n }\n\n /**\n * Combine all multipart uploads into a single object\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 */\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 */\n async deleteExpired(): Promise<number> {\n return this.expirationManager.deleteExpired()\n }\n\n /**\n * Returns the expiration period in milliseconds\n */\n getExpiration(): number {\n return this.expirationManager.getExpiration()\n }\n}\n"],"mappings":"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"}
@@ -122,7 +122,6 @@ declare enum MediaCloudLogs {
122
122
  S3_STORE_RETRIEVE_PARTS_ERROR = "Error retrieving parts",
123
123
  S3_STORE_METADATA_SAVING = "Saving metadata",
124
124
  S3_STORE_METADATA_SAVED = "Metadata file saved",
125
- S3_STORE_METADATA_CACHE_CLEARED = "Removing cached data",
126
125
  S3_STORE_INCOMPLETE_PART_UPLOADED = "Finished uploading incomplete part",
127
126
  S3_STORE_CHUNK_REMOVAL_FAILED = "Failed to remove chunk",
128
127
  }
@@ -122,7 +122,6 @@ let MediaCloudLogs = /* @__PURE__ */ function(MediaCloudLogs$1) {
122
122
  MediaCloudLogs$1["S3_STORE_RETRIEVE_PARTS_ERROR"] = "Error retrieving parts";
123
123
  MediaCloudLogs$1["S3_STORE_METADATA_SAVING"] = "Saving metadata";
124
124
  MediaCloudLogs$1["S3_STORE_METADATA_SAVED"] = "Metadata file saved";
125
- MediaCloudLogs$1["S3_STORE_METADATA_CACHE_CLEARED"] = "Removing cached data";
126
125
  MediaCloudLogs$1["S3_STORE_INCOMPLETE_PART_UPLOADED"] = "Finished uploading incomplete part";
127
126
  MediaCloudLogs$1["S3_STORE_CHUNK_REMOVAL_FAILED"] = "Failed to remove chunk";
128
127
  return 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_METADATA_CACHE_CLEARED = 'Removing cached data',\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;AACA"}
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,5 +1,5 @@
1
1
  import { ObjectCannedACL, S3ClientConfig } from "@aws-sdk/client-s3";
2
- import { KvStore, Upload } from "@tus/utils";
2
+ import { Upload } from "@tus/utils";
3
3
  import fs from "node:fs";
4
4
  import Mux from "@mux/mux-node";
5
5
  import { EventType } from "mitt";
@@ -52,7 +52,6 @@ interface MediaCloudEmitterEvents {
52
52
  };
53
53
  }
54
54
  interface S3StoreConfig {
55
- cache?: KvStore<TusUploadMetadata>;
56
55
  expirationPeriodInMilliseconds?: number;
57
56
  maxConcurrentPartUploads?: number;
58
57
  /**
@@ -1,6 +1,6 @@
1
1
  import { MediaCloudPluginOptions } from "../types/index.mjs";
2
2
  import Mux from "@mux/mux-node";
3
- import * as payload1 from "payload";
3
+ import * as payload2 from "payload";
4
4
 
5
5
  //#region src/utils/mux.d.ts
6
6
 
@@ -14,11 +14,11 @@ interface CreateMuxEndpointsArgs {
14
14
  pluginOptions: MediaCloudPluginOptions;
15
15
  }
16
16
  declare function createMuxEndpoints(args: CreateMuxEndpointsArgs): ({
17
- handler: payload1.PayloadHandler;
17
+ handler: payload2.PayloadHandler;
18
18
  method: "post";
19
19
  path: string;
20
20
  } | {
21
- handler: payload1.PayloadHandler;
21
+ handler: payload2.PayloadHandler;
22
22
  method: "get";
23
23
  path: string;
24
24
  })[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maas/payload-plugin-media-cloud",
3
- "version": "0.0.24",
3
+ "version": "0.0.26",
4
4
  "type": "module",
5
5
  "contributors": [
6
6
  {