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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/dist/adapter/handleDelete.d.mts +3 -3
  2. package/dist/adapter/handleDelete.mjs +6 -6
  3. package/dist/adapter/handleDelete.mjs.map +1 -1
  4. package/dist/adapter/handleUpload.mjs.map +1 -1
  5. package/dist/adapter/staticHandler.d.mts +5 -3
  6. package/dist/adapter/staticHandler.mjs +8 -7
  7. package/dist/adapter/staticHandler.mjs.map +1 -1
  8. package/dist/adapter/storageAdapter.d.mts +3 -3
  9. package/dist/adapter/storageAdapter.mjs +4 -4
  10. package/dist/adapter/storageAdapter.mjs.map +1 -1
  11. package/dist/collectionHooks/afterChange.d.mts +7 -0
  12. package/dist/collectionHooks/afterChange.mjs +39 -0
  13. package/dist/collectionHooks/afterChange.mjs.map +1 -0
  14. package/dist/collectionHooks/beforeChange.d.mts +7 -0
  15. package/dist/collectionHooks/beforeChange.mjs +33 -0
  16. package/dist/collectionHooks/beforeChange.mjs.map +1 -0
  17. package/dist/collections/mediaCollection.d.mts +3 -2
  18. package/dist/collections/mediaCollection.mjs +36 -187
  19. package/dist/collections/mediaCollection.mjs.map +1 -1
  20. package/dist/components/folderFileCard/folderFileCard.d.mts +13 -0
  21. package/dist/components/folderFileCard/folderFileCard.mjs +75 -0
  22. package/dist/components/folderFileCard/folderFileCard.mjs.map +1 -0
  23. package/dist/components/gridContext/gridContext.d.mts +51 -0
  24. package/dist/components/gridContext/gridContext.mjs +227 -0
  25. package/dist/components/gridContext/gridContext.mjs.map +1 -0
  26. package/dist/components/gridView/gridView.css +50 -0
  27. package/dist/components/gridView/gridView.d.mts +16 -0
  28. package/dist/components/gridView/gridView.mjs +283 -0
  29. package/dist/components/gridView/gridView.mjs.map +1 -0
  30. package/dist/components/gridView/index.d.mts +2 -0
  31. package/dist/components/gridView/index.mjs +3 -0
  32. package/dist/components/index.d.mts +9 -7
  33. package/dist/components/index.mjs +5 -4
  34. package/dist/components/itemCardGrid/itemCardGrid.css +12 -0
  35. package/dist/components/itemCardGrid/itemCardGrid.d.mts +18 -0
  36. package/dist/components/itemCardGrid/itemCardGrid.mjs +42 -0
  37. package/dist/components/itemCardGrid/itemCardGrid.mjs.map +1 -0
  38. package/dist/components/muxPreview/index.d.mts +2 -0
  39. package/dist/components/muxPreview/index.mjs +3 -0
  40. package/dist/components/{mux-preview/mux-preview.d.mts → muxPreview/muxPreview.d.mts} +2 -2
  41. package/dist/components/muxPreview/muxPreview.mjs +65 -0
  42. package/dist/components/muxPreview/muxPreview.mjs.map +1 -0
  43. package/dist/components/uploadHandler/index.d.mts +2 -0
  44. package/dist/components/uploadHandler/index.mjs +3 -0
  45. package/dist/components/uploadHandler/uploadHandler.d.mts +20 -0
  46. package/dist/components/{upload-handler/upload-handler.mjs → uploadHandler/uploadHandler.mjs} +82 -52
  47. package/dist/components/uploadHandler/uploadHandler.mjs.map +1 -0
  48. package/dist/components/uploadManager/index.d.mts +2 -0
  49. package/dist/components/uploadManager/index.mjs +3 -0
  50. package/dist/components/{upload-manager/upload-manager.d.mts → uploadManager/uploadManager.d.mts} +3 -3
  51. package/dist/components/uploadManager/uploadManager.mjs +363 -0
  52. package/dist/components/uploadManager/uploadManager.mjs.map +1 -0
  53. package/dist/endpoints/fileExistsHandler.d.mts +12 -0
  54. package/dist/endpoints/{tusFileExistsHandler.mjs → fileExistsHandler.mjs} +7 -7
  55. package/dist/endpoints/fileExistsHandler.mjs.map +1 -0
  56. package/dist/endpoints/muxAssetHandler.d.mts +1 -0
  57. package/dist/endpoints/muxAssetHandler.mjs +3 -3
  58. package/dist/endpoints/muxAssetHandler.mjs.map +1 -1
  59. package/dist/endpoints/muxCreateUploadHandler.mjs.map +1 -1
  60. package/dist/endpoints/muxWebhookHandler.d.mts +1 -0
  61. package/dist/endpoints/muxWebhookHandler.mjs +6 -5
  62. package/dist/endpoints/muxWebhookHandler.mjs.map +1 -1
  63. package/dist/endpoints/tusCleanupHandler.d.mts +3 -2
  64. package/dist/endpoints/tusCleanupHandler.mjs +4 -4
  65. package/dist/endpoints/tusCleanupHandler.mjs.map +1 -1
  66. package/dist/endpoints/tusFolderHandler.d.mts +12 -0
  67. package/dist/endpoints/tusFolderHandler.mjs +39 -0
  68. package/dist/endpoints/tusFolderHandler.mjs.map +1 -0
  69. package/dist/endpoints/tusPostProcessorHandler.d.mts +3 -2
  70. package/dist/endpoints/tusPostProcessorHandler.mjs +6 -5
  71. package/dist/endpoints/tusPostProcessorHandler.mjs.map +1 -1
  72. package/dist/fields/alt.d.mts +7 -0
  73. package/dist/fields/alt.mjs +10 -0
  74. package/dist/fields/alt.mjs.map +1 -0
  75. package/dist/fields/filename.d.mts +7 -0
  76. package/dist/fields/filename.mjs +14 -0
  77. package/dist/fields/filename.mjs.map +1 -0
  78. package/dist/fields/height.d.mts +7 -0
  79. package/dist/fields/height.mjs +15 -0
  80. package/dist/fields/height.mjs.map +1 -0
  81. package/dist/fields/mux.d.mts +7 -0
  82. package/dist/fields/mux.mjs +149 -0
  83. package/dist/fields/mux.mjs.map +1 -0
  84. package/dist/fields/path.d.mts +7 -0
  85. package/dist/fields/path.mjs +14 -0
  86. package/dist/fields/path.mjs.map +1 -0
  87. package/dist/fields/storage.d.mts +7 -0
  88. package/dist/fields/storage.mjs +18 -0
  89. package/dist/fields/storage.mjs.map +1 -0
  90. package/dist/fields/thumbnail.d.mts +7 -0
  91. package/dist/fields/thumbnail.mjs +17 -0
  92. package/dist/fields/thumbnail.mjs.map +1 -0
  93. package/dist/fields/width.d.mts +7 -0
  94. package/dist/fields/width.mjs +15 -0
  95. package/dist/fields/width.mjs.map +1 -0
  96. package/dist/hooks/useErrorHandler.d.mts +1 -1
  97. package/dist/hooks/useErrorHandler.mjs +26 -7
  98. package/dist/hooks/useErrorHandler.mjs.map +1 -1
  99. package/dist/hooks/useMediaCloudEmitter.mjs.map +1 -1
  100. package/dist/plugin.d.mts +5 -4
  101. package/dist/plugin.mjs +53 -29
  102. package/dist/plugin.mjs.map +1 -1
  103. package/dist/tus/stores/s3/{expiration-manager.d.mts → expirationManager.d.mts} +4 -3
  104. package/dist/tus/stores/s3/{expiration-manager.mjs → expirationManager.mjs} +6 -3
  105. package/dist/tus/stores/s3/expirationManager.mjs.map +1 -0
  106. package/dist/tus/stores/s3/{file-operations.d.mts → fileOperations.d.mts} +2 -2
  107. package/dist/tus/stores/s3/{file-operations.mjs → fileOperations.mjs} +2 -2
  108. package/dist/tus/stores/s3/fileOperations.mjs.map +1 -0
  109. package/dist/tus/stores/s3/index.d.mts +1 -1
  110. package/dist/tus/stores/s3/index.mjs +20 -9
  111. package/dist/tus/stores/s3/index.mjs.map +1 -1
  112. package/dist/tus/stores/s3/{metadata-manager.d.mts → metadataManager.d.mts} +4 -2
  113. package/dist/tus/stores/s3/{metadata-manager.mjs → metadataManager.mjs} +6 -5
  114. package/dist/tus/stores/s3/metadataManager.mjs.map +1 -0
  115. package/dist/tus/stores/s3/{parts-manager.d.mts → partsManager.d.mts} +4 -4
  116. package/dist/tus/stores/s3/{parts-manager.mjs → partsManager.mjs} +67 -29
  117. package/dist/tus/stores/s3/partsManager.mjs.map +1 -0
  118. package/dist/tus/stores/s3/{s3-store.d.mts → s3Store.d.mts} +38 -32
  119. package/dist/tus/stores/s3/{s3-store.mjs → s3Store.mjs} +104 -59
  120. package/dist/tus/stores/s3/s3Store.mjs.map +1 -0
  121. package/dist/tus/stores/s3/semaphore.mjs.map +1 -1
  122. package/dist/types/errors.d.mts +32 -0
  123. package/dist/types/errors.mjs +32 -0
  124. package/dist/types/errors.mjs.map +1 -1
  125. package/dist/types/index.d.mts +42 -4
  126. package/dist/utils/buildS3Path.d.mts +10 -0
  127. package/dist/utils/buildS3Path.mjs +16 -0
  128. package/dist/utils/buildS3Path.mjs.map +1 -0
  129. package/dist/utils/buildThumbnailURL.d.mts +14 -0
  130. package/dist/utils/buildThumbnailURL.mjs +10 -0
  131. package/dist/utils/buildThumbnailURL.mjs.map +1 -0
  132. package/dist/utils/defaultOptions.d.mts +7 -0
  133. package/dist/utils/defaultOptions.mjs +12 -0
  134. package/dist/utils/defaultOptions.mjs.map +1 -0
  135. package/dist/utils/file.d.mts +16 -2
  136. package/dist/utils/file.mjs +58 -6
  137. package/dist/utils/file.mjs.map +1 -1
  138. package/dist/utils/mux.mjs +19 -8
  139. package/dist/utils/mux.mjs.map +1 -1
  140. package/dist/utils/tus.d.mts +9 -6
  141. package/dist/utils/tus.mjs +31 -11
  142. package/dist/utils/tus.mjs.map +1 -1
  143. package/package.json +11 -4
  144. package/dist/components/mux-preview/index.d.mts +0 -2
  145. package/dist/components/mux-preview/index.mjs +0 -3
  146. package/dist/components/mux-preview/mux-preview.mjs +0 -29
  147. package/dist/components/mux-preview/mux-preview.mjs.map +0 -1
  148. package/dist/components/upload-handler/index.d.mts +0 -2
  149. package/dist/components/upload-handler/index.mjs +0 -3
  150. package/dist/components/upload-handler/upload-handler.d.mts +0 -22
  151. package/dist/components/upload-handler/upload-handler.mjs.map +0 -1
  152. package/dist/components/upload-manager/index.d.mts +0 -2
  153. package/dist/components/upload-manager/index.mjs +0 -3
  154. package/dist/components/upload-manager/upload-manager.mjs +0 -273
  155. package/dist/components/upload-manager/upload-manager.mjs.map +0 -1
  156. package/dist/endpoints/tusFileExistsHandler.d.mts +0 -11
  157. package/dist/endpoints/tusFileExistsHandler.mjs.map +0 -1
  158. package/dist/tus/stores/s3/expiration-manager.mjs.map +0 -1
  159. package/dist/tus/stores/s3/file-operations.mjs.map +0 -1
  160. package/dist/tus/stores/s3/metadata-manager.mjs.map +0 -1
  161. package/dist/tus/stores/s3/parts-manager.mjs.map +0 -1
  162. package/dist/tus/stores/s3/s3-store.mjs.map +0 -1
  163. /package/dist/components/{upload-manager/upload-manager.css → uploadManager/uploadManager.css} +0 -0
@@ -0,0 +1,10 @@
1
+ //#region src/utils/buildThumbnailURL.ts
2
+ function buildThumbnailURL(args) {
3
+ const { storage, s3Store, playbackId, s3Key } = args;
4
+ if (storage === "mux" && playbackId) return `https://image.mux.com/${playbackId}/thumbnail.jpg?width=200&height=200&fit_mode=pad`;
5
+ if (storage === "s3" && s3Key) return `https://wsrv.nl/?url=${s3Store?.getUrl(s3Key)}?width=512&height=512&default=1&q=80`;
6
+ }
7
+
8
+ //#endregion
9
+ export { buildThumbnailURL };
10
+ //# sourceMappingURL=buildThumbnailURL.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildThumbnailURL.mjs","names":["S3Store","Storage","BuildThumbnailURLArgs","storage","playbackId","s3Key","s3Store","buildThumbnailURL","args","url","getUrl"],"sources":["../../src/utils/buildThumbnailURL.ts"],"sourcesContent":["import type { S3Store } from '../tus/stores/s3/s3Store'\nimport type { Storage } from '../types'\n\ninterface BuildThumbnailURLArgs {\n storage: Storage\n playbackId?: string\n s3Key?: string\n s3Store?: S3Store | null\n}\n\nexport function buildThumbnailURL(args: BuildThumbnailURLArgs) {\n const { storage, s3Store, playbackId, s3Key } = args\n\n if (storage === 'mux' && playbackId) {\n return `https://image.mux.com/${playbackId}/thumbnail.jpg?width=200&height=200&fit_mode=pad`\n }\n\n if (storage === 's3' && s3Key) {\n const url = s3Store?.getUrl(s3Key)\n return `https://wsrv.nl/?url=${url}?width=512&height=512&default=1&q=80`\n }\n}\n"],"mappings":";AAUA,SAAgBO,kBAAkBC,MAA6B;CAC7D,MAAM,EAAEL,SAASG,SAASF,YAAYC,UAAUG;AAEhD,KAAIL,YAAY,SAASC,WACvB,QAAO,yBAAyBA,WAAU;AAG5C,KAAID,YAAY,QAAQE,MAEtB,QAAO,wBADKC,SAASI,OAAOL,MAAM,CACA"}
@@ -0,0 +1,7 @@
1
+ import { MediaCloudDefaultPluginOptions } from "../types/index.mjs";
2
+
3
+ //#region src/utils/defaultOptions.d.ts
4
+ declare const defaultOptions: MediaCloudDefaultPluginOptions;
5
+ //#endregion
6
+ export { defaultOptions };
7
+ //# sourceMappingURL=defaultOptions.d.mts.map
@@ -0,0 +1,12 @@
1
+ //#region src/utils/defaultOptions.ts
2
+ const defaultOptions = {
3
+ enabled: true,
4
+ collection: "media",
5
+ view: "grid",
6
+ folders: true,
7
+ storage: { "video/*": "mux" }
8
+ };
9
+
10
+ //#endregion
11
+ export { defaultOptions };
12
+ //# sourceMappingURL=defaultOptions.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaultOptions.mjs","names":["MediaCloudDefaultPluginOptions","defaultOptions","enabled","collection","view","folders","storage"],"sources":["../../src/utils/defaultOptions.ts"],"sourcesContent":["import { MediaCloudDefaultPluginOptions } from '../types'\n\nexport const defaultOptions: MediaCloudDefaultPluginOptions = {\n enabled: true,\n collection: 'media',\n view: 'grid',\n folders: true,\n storage: {\n 'video/*': 'mux',\n },\n}\n"],"mappings":";AAEA,MAAaC,iBAAiD;CAC5DC,SAAS;CACTC,YAAY;CACZC,MAAM;CACNC,SAAS;CACTC,SAAS,EACP,WAAW,OACb;CACD"}
@@ -1,4 +1,9 @@
1
+ import { MediaCloudPluginOptions, MimeType } from "../types/index.mjs";
2
+ import { S3Store } from "../tus/stores/s3/s3Store.mjs";
3
+ import * as payload0 from "payload";
4
+
1
5
  //#region src/utils/file.d.ts
6
+
2
7
  /**
3
8
  * Checks if a file is a video file supported by Mux
4
9
  * @param file - The file to check
@@ -10,7 +15,7 @@ declare function isVideo(file: File): Promise<boolean>;
10
15
  * @param file - The file to check
11
16
  * @returns Promise that resolves to the MIME type of the file or undefined if it cannot be determined
12
17
  */
13
- declare function getFileType(file: File): Promise<string | undefined>;
18
+ declare function getMimeType(file: File): Promise<MimeType | undefined>;
14
19
  /**
15
20
  * Generates a unique filename by appending a random suffix if needed
16
21
  * @param originalFilename - The original filename
@@ -25,6 +30,15 @@ declare function generateUniqueFilename(originalFilename: string): string;
25
30
  * @throws {TypeError} When filename is not a string
26
31
  */
27
32
  declare function sanitizeFilename(filename: string): string;
33
+ interface CreateFileEndpointsArgs {
34
+ getS3Store: () => S3Store;
35
+ pluginOptions: MediaCloudPluginOptions;
36
+ }
37
+ declare function createFileEndpoints(args: CreateFileEndpointsArgs): {
38
+ handler: payload0.PayloadHandler;
39
+ method: "get";
40
+ path: string;
41
+ }[];
28
42
  //#endregion
29
- export { generateUniqueFilename, getFileType, isVideo, sanitizeFilename };
43
+ export { createFileEndpoints, generateUniqueFilename, getMimeType, isVideo, sanitizeFilename };
30
44
  //# sourceMappingURL=file.d.mts.map
@@ -1,10 +1,12 @@
1
1
  import { MediaCloudErrors } from "../types/errors.mjs";
2
- import { useErrorHandler } from "../hooks/useErrorHandler.mjs";
2
+ import { useMagicError } from "../error-handler/dist/index.mjs";
3
+ import { getFileExistsHandler } from "../endpoints/fileExistsHandler.mjs";
3
4
  import { fileTypeFromBuffer } from "file-type";
4
5
  import { parse } from "pathe";
5
6
 
6
7
  //#region src/utils/file.ts
7
- const { logError } = useErrorHandler();
8
+ const magicError = useMagicError({ prefix: "PLUGIN-MEDIA-CLOUD" });
9
+ const { logError, throwError } = magicError;
8
10
  const MUX_SUPPORTED_VIDEO_FORMATS = new Set([
9
11
  "application/mxf",
10
12
  "video/3gpp",
@@ -35,14 +37,51 @@ async function isVideo(file) {
35
37
  return false;
36
38
  }
37
39
  }
40
+ const allowedMimeTypes = new Set([
41
+ "application/mxf",
42
+ "video/3gpp",
43
+ "video/3gpp2",
44
+ "video/mp2t",
45
+ "video/mp4",
46
+ "video/mpeg",
47
+ "video/quicktime",
48
+ "video/webm",
49
+ "video/x-f4v",
50
+ "video/x-flv",
51
+ "video/x-matroska",
52
+ "video/x-ms-asf",
53
+ "video/x-ms-wmv",
54
+ "video/x-msvideo",
55
+ "video/x-mxf",
56
+ "image/apng",
57
+ "image/avif",
58
+ "image/gif",
59
+ "image/jpeg",
60
+ "image/png",
61
+ "image/svg+xml",
62
+ "image/webp"
63
+ ]);
64
+ /**
65
+ * Check the mime type against allowed mime types
66
+ * @param mimeType - The file to check
67
+ */
68
+ function validateMimeType(mimeType) {
69
+ if (!allowedMimeTypes.has(mimeType)) throwError(MediaCloudErrors.FILE_TYPE_UNSUPPORTED_ERROR);
70
+ }
38
71
  /**
39
72
  * Gets the MIME type of a file using file-type library
40
73
  * @param file - The file to check
41
74
  * @returns Promise that resolves to the MIME type of the file or undefined if it cannot be determined
42
75
  */
43
- async function getFileType(file) {
76
+ async function getMimeType(file) {
44
77
  try {
45
- return (await fileTypeFromBuffer(new Uint8Array(await file.arrayBuffer())))?.mime;
78
+ const fileType = await fileTypeFromBuffer(new Uint8Array(await file.arrayBuffer()));
79
+ if (!fileType?.mime) {
80
+ logError(MediaCloudErrors.FILE_TYPE_ERROR.message);
81
+ return;
82
+ }
83
+ validateMimeType(fileType.mime);
84
+ return fileType.mime;
46
85
  } catch (_error) {
47
86
  logError(MediaCloudErrors.FILE_TYPE_ERROR.message);
48
87
  return;
@@ -56,7 +95,7 @@ async function getFileType(file) {
56
95
  function generateUniqueFilename(originalFilename) {
57
96
  const timestamp = Date.now().toString(36);
58
97
  const { name, ext } = parse(originalFilename);
59
- return `${name}-${timestamp}${ext}`;
98
+ return `${sanitizeFilename(name)}-${timestamp}${ext}`;
60
99
  }
61
100
  /**
62
101
  * Sanitizes a filename by removing/replacing invalid characters and handling edge cases
@@ -74,7 +113,20 @@ function sanitizeFilename(filename) {
74
113
  return filename;
75
114
  }
76
115
  }
116
+ function createFileEndpoints(args) {
117
+ const { getS3Store, pluginOptions } = args;
118
+ const collection = pluginOptions.collection;
119
+ magicError.assert(collection, MediaCloudErrors.COLLECTION_REQUIRED);
120
+ return [{
121
+ handler: getFileExistsHandler({
122
+ getS3Store,
123
+ collection
124
+ }),
125
+ method: "get",
126
+ path: "/uploads/:filename/exists"
127
+ }];
128
+ }
77
129
 
78
130
  //#endregion
79
- export { generateUniqueFilename, getFileType, isVideo, sanitizeFilename };
131
+ export { createFileEndpoints, generateUniqueFilename, getMimeType, isVideo, sanitizeFilename };
80
132
  //# sourceMappingURL=file.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"file.mjs","names":[],"sources":["../../src/utils/file.ts"],"sourcesContent":["import { fileTypeFromBuffer } from 'file-type'\nimport { parse } from 'pathe'\n\nimport { useErrorHandler } from '../hooks/useErrorHandler'\nimport { MediaCloudErrors } from '../types/errors'\n\nconst { logError } = useErrorHandler()\n\nconst MUX_SUPPORTED_VIDEO_FORMATS = new Set([\n 'application/mxf',\n 'video/3gpp',\n 'video/3gpp2',\n 'video/mp2t',\n 'video/mp4',\n 'video/mpeg',\n 'video/quicktime',\n 'video/webm',\n 'video/x-f4v',\n 'video/x-flv',\n 'video/x-matroska',\n 'video/x-ms-asf',\n 'video/x-ms-wmv',\n 'video/x-msvideo',\n 'video/x-mxf',\n])\n\n/**\n * Checks if a file is a video file supported by Mux\n * @param file - The file to check\n * @returns Promise that resolves to true if the file is a supported video format, false otherwise\n */\nexport async function isVideo(file: File): Promise<boolean> {\n try {\n const buffer = new Uint8Array(await file.arrayBuffer())\n const fileType = await fileTypeFromBuffer(buffer)\n\n return fileType?.mime\n ? MUX_SUPPORTED_VIDEO_FORMATS.has(fileType.mime)\n : false\n } catch {\n return false\n }\n}\n\n/**\n * Gets the MIME type of a file using file-type library\n * @param file - The file to check\n * @returns Promise that resolves to the MIME type of the file or undefined if it cannot be determined\n */\nexport async function getFileType(file: File): Promise<string | undefined> {\n try {\n const buffer = new Uint8Array(await file.arrayBuffer())\n const fileType = await fileTypeFromBuffer(buffer)\n return fileType?.mime\n } catch (_error) {\n logError(MediaCloudErrors.FILE_TYPE_ERROR.message)\n return undefined\n }\n}\n\n/**\n * Generates a unique filename by appending a random suffix if needed\n * @param originalFilename - The original filename\n * @returns A unique filename with a random suffix appended before the extension\n */\nexport function generateUniqueFilename(originalFilename: string): string {\n const timestamp = Date.now().toString(36)\n const { name, ext } = parse(originalFilename)\n return `${name}-${timestamp}${ext}`\n}\n\n/**\n * Sanitizes a filename by removing/replacing invalid characters and handling edge cases\n * Converts all non-alphanumeric characters (except dots for extensions) to underscores\n * @param filename - The filename to sanitize\n * @returns A sanitized filename safe for all operating systems\n * @throws {TypeError} When filename is not a string\n */\nexport function sanitizeFilename(filename: string): string {\n try {\n const parsed = parse(filename)\n const sanitized = parsed.name\n .replace(/[^a-zA-Z0-9_.-]/g, '_')\n .replace(/_{2,}/g, '_')\n .replace(/^_|_$/g, '')\n return `${sanitized}${parsed.ext}`\n } catch (_error) {\n logError(MediaCloudErrors.FILENAME_SANITIZE_ERROR.message)\n return filename\n }\n}\n"],"mappings":";;;;;;AAMA,MAAM,EAAE,aAAa,iBAAiB;AAEtC,MAAM,8BAA8B,IAAI,IAAI;CAC1C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;;;;;AAOF,eAAsB,QAAQ,MAA8B;AAC1D,KAAI;EAEF,MAAM,WAAW,MAAM,mBADR,IAAI,WAAW,MAAM,KAAK,aAAa,CAAC,CACN;AAEjD,SAAO,UAAU,OACb,4BAA4B,IAAI,SAAS,KAAK,GAC9C;SACE;AACN,SAAO;;;;;;;;AASX,eAAsB,YAAY,MAAyC;AACzE,KAAI;AAGF,UADiB,MAAM,mBADR,IAAI,WAAW,MAAM,KAAK,aAAa,CAAC,CACN,GAChC;UACV,QAAQ;AACf,WAAS,iBAAiB,gBAAgB,QAAQ;AAClD;;;;;;;;AASJ,SAAgB,uBAAuB,kBAAkC;CACvE,MAAM,YAAY,KAAK,KAAK,CAAC,SAAS,GAAG;CACzC,MAAM,EAAE,MAAM,QAAQ,MAAM,iBAAiB;AAC7C,QAAO,GAAG,KAAK,GAAG,YAAY;;;;;;;;;AAUhC,SAAgB,iBAAiB,UAA0B;AACzD,KAAI;EACF,MAAM,SAAS,MAAM,SAAS;AAK9B,SAAO,GAJW,OAAO,KACtB,QAAQ,oBAAoB,IAAI,CAChC,QAAQ,UAAU,IAAI,CACtB,QAAQ,UAAU,GAAG,GACF,OAAO;UACtB,QAAQ;AACf,WAAS,iBAAiB,wBAAwB,QAAQ;AAC1D,SAAO"}
1
+ {"version":3,"file":"file.mjs","names":["fileTypeFromBuffer","parse","getFileExistsHandler","MediaCloudErrors","MediaCloudPluginOptions","MimeType","useMagicError","UseMagicErrorReturn","S3Store","magicError","prefix","logError","throwError","MUX_SUPPORTED_VIDEO_FORMATS","Set","isVideo","file","File","Promise","buffer","Uint8Array","arrayBuffer","fileType","mime","has","allowedMimeTypes","validateMimeType","mimeType","FILE_TYPE_UNSUPPORTED_ERROR","getMimeType","FILE_TYPE_ERROR","message","undefined","_error","generateUniqueFilename","originalFilename","timestamp","Date","now","toString","name","ext","sanitizedName","sanitizeFilename","filename","parsed","sanitized","replace","FILENAME_SANITIZE_ERROR","CreateFileEndpointsArgs","getS3Store","pluginOptions","createFileEndpoints","args","collection","assert","COLLECTION_REQUIRED","handler","method","const","path"],"sources":["../../src/utils/file.ts"],"sourcesContent":["import { fileTypeFromBuffer } from 'file-type'\nimport { parse } from 'pathe'\n\nimport { getFileExistsHandler } from '../endpoints/fileExistsHandler'\nimport { MediaCloudErrors } from '../types/errors'\n\nimport { MediaCloudPluginOptions, MimeType } from '../types'\nimport { useMagicError, type UseMagicErrorReturn } from '@maas/error-handler'\n\nimport type { S3Store } from '../tus/stores/s3/s3Store'\n\nconst magicError: UseMagicErrorReturn = useMagicError({\n prefix: 'PLUGIN-MEDIA-CLOUD',\n})\n\nconst { logError, throwError } = magicError\n\nconst MUX_SUPPORTED_VIDEO_FORMATS = new Set([\n 'application/mxf',\n 'video/3gpp',\n 'video/3gpp2',\n 'video/mp2t',\n 'video/mp4',\n 'video/mpeg',\n 'video/quicktime',\n 'video/webm',\n 'video/x-f4v',\n 'video/x-flv',\n 'video/x-matroska',\n 'video/x-ms-asf',\n 'video/x-ms-wmv',\n 'video/x-msvideo',\n 'video/x-mxf',\n])\n\n/**\n * Checks if a file is a video file supported by Mux\n * @param file - The file to check\n * @returns Promise that resolves to true if the file is a supported video format, false otherwise\n */\nexport async function isVideo(file: File): Promise<boolean> {\n try {\n const buffer = new Uint8Array(await file.arrayBuffer())\n const fileType = await fileTypeFromBuffer(buffer)\n\n return fileType?.mime\n ? MUX_SUPPORTED_VIDEO_FORMATS.has(fileType.mime)\n : false\n } catch {\n return false\n }\n}\n\nconst allowedMimeTypes = new Set<MimeType>([\n 'application/mxf',\n 'video/3gpp',\n 'video/3gpp2',\n 'video/mp2t',\n 'video/mp4',\n 'video/mpeg',\n 'video/quicktime',\n 'video/webm',\n 'video/x-f4v',\n 'video/x-flv',\n 'video/x-matroska',\n 'video/x-ms-asf',\n 'video/x-ms-wmv',\n 'video/x-msvideo',\n 'video/x-mxf',\n 'image/apng',\n 'image/avif',\n 'image/gif',\n 'image/jpeg',\n 'image/png',\n 'image/svg+xml',\n 'image/webp',\n])\n\n/**\n * Check the mime type against allowed mime types\n * @param mimeType - The file to check\n */\nfunction validateMimeType(mimeType: string) {\n if (!allowedMimeTypes.has(mimeType as MimeType)) {\n throwError(MediaCloudErrors.FILE_TYPE_UNSUPPORTED_ERROR)\n }\n}\n\n/**\n * Gets the MIME type of a file using file-type library\n * @param file - The file to check\n * @returns Promise that resolves to the MIME type of the file or undefined if it cannot be determined\n */\nexport async function getMimeType(file: File): Promise<MimeType | undefined> {\n try {\n const buffer = new Uint8Array(await file.arrayBuffer())\n const fileType = await fileTypeFromBuffer(buffer)\n\n if (!fileType?.mime) {\n logError(MediaCloudErrors.FILE_TYPE_ERROR.message)\n return undefined\n }\n\n validateMimeType(fileType.mime)\n return fileType.mime as MimeType\n } catch (_error) {\n logError(MediaCloudErrors.FILE_TYPE_ERROR.message)\n return undefined\n }\n}\n\n/**\n * Generates a unique filename by appending a random suffix if needed\n * @param originalFilename - The original filename\n * @returns A unique filename with a random suffix appended before the extension\n */\nexport function generateUniqueFilename(originalFilename: string): string {\n const timestamp = Date.now().toString(36)\n // Parse name and extension\n const { name, ext } = parse(originalFilename)\n\n // Sanitize name\n const sanitizedName = sanitizeFilename(name)\n\n return `${sanitizedName}-${timestamp}${ext}`\n}\n\n/**\n * Sanitizes a filename by removing/replacing invalid characters and handling edge cases\n * Converts all non-alphanumeric characters (except dots for extensions) to underscores\n * @param filename - The filename to sanitize\n * @returns A sanitized filename safe for all operating systems\n * @throws {TypeError} When filename is not a string\n */\nexport function sanitizeFilename(filename: string): string {\n try {\n const parsed = parse(filename)\n const sanitized = parsed.name\n .replace(/[^a-zA-Z0-9_.-]/g, '_')\n .replace(/_{2,}/g, '_')\n .replace(/^_|_$/g, '')\n return `${sanitized}${parsed.ext}`\n } catch (_error) {\n logError(MediaCloudErrors.FILENAME_SANITIZE_ERROR.message)\n return filename\n }\n}\n\ninterface CreateFileEndpointsArgs {\n getS3Store: () => S3Store\n pluginOptions: MediaCloudPluginOptions\n}\n\nexport function createFileEndpoints(args: CreateFileEndpointsArgs) {\n const { getS3Store, pluginOptions } = args\n const collection = pluginOptions.collection\n\n magicError.assert(collection, MediaCloudErrors.COLLECTION_REQUIRED)\n\n return [\n {\n handler: getFileExistsHandler({ getS3Store, collection }),\n method: 'get' as const,\n path: '/uploads/:filename/exists',\n },\n ]\n}\n"],"mappings":";;;;;;;AAWA,MAAMS,aAAkCH,cAAc,EACpDI,QAAQ,sBACT,CAAC;AAEF,MAAM,EAAEC,UAAUC,eAAeH;AAEjC,MAAMI,8BAA8B,IAAIC,IAAI;CAC1C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;;;;;AAOF,eAAsBC,QAAQC,MAA8B;AAC1D,KAAI;EAEF,MAAMM,WAAW,MAAMtB,mBADR,IAAIoB,WAAW,MAAMJ,KAAKK,aAAa,CAAC,CACN;AAEjD,SAAOC,UAAUC,OACbV,4BAA4BW,IAAIF,SAASC,KAAK,GAC9C;SACE;AACN,SAAO;;;AAIX,MAAME,mBAAmB,IAAIX,IAAc;CACzC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;;;;AAMF,SAASY,iBAAiBC,UAAkB;AAC1C,KAAI,CAACF,iBAAiBD,IAAIG,SAAqB,CAC7Cf,YAAWT,iBAAiByB,4BAA4B;;;;;;;AAS5D,eAAsBC,YAAYb,MAA2C;AAC3E,KAAI;EAEF,MAAMM,WAAW,MAAMtB,mBADR,IAAIoB,WAAW,MAAMJ,KAAKK,aAAa,CAAC,CACN;AAEjD,MAAI,CAACC,UAAUC,MAAM;AACnBZ,YAASR,iBAAiB2B,gBAAgBC,QAAQ;AAClD;;AAGFL,mBAAiBJ,SAASC,KAAK;AAC/B,SAAOD,SAASC;UACTU,QAAQ;AACftB,WAASR,iBAAiB2B,gBAAgBC,QAAQ;AAClD;;;;;;;;AASJ,SAAgBG,uBAAuBC,kBAAkC;CACvE,MAAMC,YAAYC,KAAKC,KAAK,CAACC,SAAS,GAAG;CAEzC,MAAM,EAAEC,MAAMC,QAAQxC,MAAMkC,iBAAiB;AAK7C,QAAO,GAFeQ,iBAAiBH,KAAK,CAErB,GAAIJ,YAAYK;;;;;;;;;AAUzC,SAAgBE,iBAAiBC,UAA0B;AACzD,KAAI;EACF,MAAMC,SAAS5C,MAAM2C,SAAS;AAK9B,SAAO,GAJWC,OAAOL,KACtBO,QAAQ,oBAAoB,IAAI,CAChCA,QAAQ,UAAU,IAAI,CACtBA,QAAQ,UAAU,GAAG,GACFF,OAAOJ;UACtBR,QAAQ;AACftB,WAASR,iBAAiB6C,wBAAwBjB,QAAQ;AAC1D,SAAOa;;;AASX,SAAgBQ,oBAAoBC,MAA+B;CACjE,MAAM,EAAEH,YAAYC,kBAAkBE;CACtC,MAAMC,aAAaH,cAAcG;AAEjC7C,YAAW8C,OAAOD,YAAYnD,iBAAiBqD,oBAAoB;AAEnE,QAAO,CACL;EACEC,SAASvD,qBAAqB;GAAEgD;GAAYI;GAAY,CAAC;EACzDI,QAAQ;EACRE,MAAM;EACP,CACF"}
@@ -1,17 +1,22 @@
1
1
  import { MediaCloudErrors } from "../types/errors.mjs";
2
- import { useErrorHandler } from "../hooks/useErrorHandler.mjs";
2
+ import { useMagicError } from "../error-handler/dist/index.mjs";
3
3
  import { getMuxAssetHandler } from "../endpoints/muxAssetHandler.mjs";
4
4
  import { getMuxCreateUploadHandler } from "../endpoints/muxCreateUploadHandler.mjs";
5
5
  import { getMuxWebhookHandler } from "../endpoints/muxWebhookHandler.mjs";
6
6
  import Mux from "@mux/mux-node";
7
7
 
8
8
  //#region src/utils/mux.ts
9
- const { throwError } = useErrorHandler();
9
+ const magicError = useMagicError({ prefix: "PLUGIN-MEDIA-CLOUD" });
10
10
  function createMuxEndpoints(args) {
11
11
  const { getMuxClient, pluginOptions } = args;
12
+ const collection = pluginOptions.collection;
13
+ magicError.assert(collection, MediaCloudErrors.COLLECTION_REQUIRED);
12
14
  return [
13
15
  {
14
- handler: getMuxWebhookHandler({ getMuxClient }),
16
+ handler: getMuxWebhookHandler({
17
+ getMuxClient,
18
+ collection
19
+ }),
15
20
  method: "post",
16
21
  path: "/mux/webhook"
17
22
  },
@@ -24,7 +29,10 @@ function createMuxEndpoints(args) {
24
29
  path: "/mux/upload"
25
30
  },
26
31
  {
27
- handler: getMuxAssetHandler({ getMuxClient }),
32
+ handler: getMuxAssetHandler({
33
+ getMuxClient,
34
+ collection
35
+ }),
28
36
  method: "get",
29
37
  path: "/mux/asset"
30
38
  }
@@ -36,7 +44,9 @@ function createMuxEndpoints(args) {
36
44
  * @throws {Error} When required Mux configuration is missing
37
45
  */
38
46
  function validateMuxConfig(muxConfig) {
39
- if (!muxConfig?.tokenId || !muxConfig?.tokenSecret) throwError(MediaCloudErrors.MUX_CONFIG_MISSING);
47
+ magicError.assert(muxConfig, MediaCloudErrors.MUX_CONFIG_INCOMPLETE);
48
+ magicError.assert(muxConfig?.tokenId, MediaCloudErrors.MUX_CONFIG_MISSING);
49
+ magicError.assert(muxConfig?.tokenSecret, MediaCloudErrors.MUX_CONFIG_MISSING);
40
50
  }
41
51
  /**
42
52
  * Creates a new Mux client instance
@@ -44,10 +54,11 @@ function validateMuxConfig(muxConfig) {
44
54
  * @returns A configured Mux client
45
55
  */
46
56
  function createMuxClient(muxConfig) {
57
+ validateMuxConfig(muxConfig);
47
58
  return new Mux({
48
- tokenId: muxConfig.tokenId,
49
- tokenSecret: muxConfig.tokenSecret,
50
- webhookSecret: muxConfig.webhookSecret
59
+ tokenId: muxConfig?.tokenId,
60
+ tokenSecret: muxConfig?.tokenSecret,
61
+ webhookSecret: muxConfig?.webhookSecret
51
62
  });
52
63
  }
53
64
 
@@ -1 +1 @@
1
- {"version":3,"file":"mux.mjs","names":[],"sources":["../../src/utils/mux.ts"],"sourcesContent":["import Mux from '@mux/mux-node'\n\nimport { MediaCloudErrors } from '../types/errors'\nimport { useErrorHandler } from '../hooks/useErrorHandler'\nimport { getMuxAssetHandler } from '../endpoints/muxAssetHandler'\nimport { getMuxCreateUploadHandler } from '../endpoints/muxCreateUploadHandler'\nimport { getMuxWebhookHandler } from '../endpoints/muxWebhookHandler'\n\nimport type { MediaCloudPluginOptions } from '../types'\n\nconst { throwError } = useErrorHandler()\n\n/**\n * Creates Mux-related endpoints for asset handling\n * @param args - The arguments for creating Mux endpoints\n * @returns An array of Mux endpoint configurations\n */\ninterface CreateMuxEndpointsArgs {\n getMuxClient: () => Mux\n pluginOptions: MediaCloudPluginOptions\n}\n\nexport function createMuxEndpoints(args: CreateMuxEndpointsArgs) {\n const { getMuxClient, pluginOptions } = args\n return [\n {\n handler: getMuxWebhookHandler({ getMuxClient }),\n method: 'post' as const,\n path: '/mux/webhook',\n },\n {\n handler: getMuxCreateUploadHandler({ getMuxClient, pluginOptions }),\n method: 'post' as const,\n path: '/mux/upload',\n },\n {\n handler: getMuxAssetHandler({ getMuxClient }),\n method: 'get' as const,\n path: '/mux/asset',\n },\n ]\n}\n\n/**\n * Validates Mux configuration options\n * @param muxConfig - The Mux configuration to validate\n * @throws {Error} When required Mux configuration is missing\n */\nexport function validateMuxConfig(\n muxConfig: MediaCloudPluginOptions['mux']\n): void {\n if (!muxConfig?.tokenId || !muxConfig?.tokenSecret) {\n throwError(MediaCloudErrors.MUX_CONFIG_MISSING)\n }\n}\n\n/**\n * Creates a new Mux client instance\n * @param muxConfig - The Mux configuration options\n * @returns A configured Mux client\n */\nexport function createMuxClient(\n muxConfig: MediaCloudPluginOptions['mux']\n): Mux {\n // Create and return Mux client instance\n return new Mux({\n tokenId: muxConfig.tokenId,\n tokenSecret: muxConfig.tokenSecret,\n webhookSecret: muxConfig.webhookSecret,\n })\n}\n"],"mappings":";;;;;;;;AAUA,MAAM,EAAE,eAAe,iBAAiB;AAYxC,SAAgB,mBAAmB,MAA8B;CAC/D,MAAM,EAAE,cAAc,kBAAkB;AACxC,QAAO;EACL;GACE,SAAS,qBAAqB,EAAE,cAAc,CAAC;GAC/C,QAAQ;GACR,MAAM;GACP;EACD;GACE,SAAS,0BAA0B;IAAE;IAAc;IAAe,CAAC;GACnE,QAAQ;GACR,MAAM;GACP;EACD;GACE,SAAS,mBAAmB,EAAE,cAAc,CAAC;GAC7C,QAAQ;GACR,MAAM;GACP;EACF;;;;;;;AAQH,SAAgB,kBACd,WACM;AACN,KAAI,CAAC,WAAW,WAAW,CAAC,WAAW,YACrC,YAAW,iBAAiB,mBAAmB;;;;;;;AASnD,SAAgB,gBACd,WACK;AAEL,QAAO,IAAI,IAAI;EACb,SAAS,UAAU;EACnB,aAAa,UAAU;EACvB,eAAe,UAAU;EAC1B,CAAC"}
1
+ {"version":3,"file":"mux.mjs","names":["Mux","MediaCloudErrors","getMuxAssetHandler","getMuxCreateUploadHandler","getMuxWebhookHandler","useMagicError","MediaCloudPluginOptions","UseMagicErrorReturn","magicError","prefix","CreateMuxEndpointsArgs","getMuxClient","pluginOptions","createMuxEndpoints","args","collection","assert","COLLECTION_REQUIRED","handler","method","const","path","validateMuxConfig","muxConfig","MUX_CONFIG_INCOMPLETE","tokenId","MUX_CONFIG_MISSING","tokenSecret","createMuxClient","webhookSecret"],"sources":["../../src/utils/mux.ts"],"sourcesContent":["import Mux from '@mux/mux-node'\n\nimport { MediaCloudErrors } from '../types/errors'\nimport { getMuxAssetHandler } from '../endpoints/muxAssetHandler'\nimport { getMuxCreateUploadHandler } from '../endpoints/muxCreateUploadHandler'\nimport { getMuxWebhookHandler } from '../endpoints/muxWebhookHandler'\nimport { useMagicError } from '@maas/error-handler'\n\nimport type { MediaCloudPluginOptions } from '../types'\nimport type { UseMagicErrorReturn } from '@maas/error-handler'\n\nconst magicError: UseMagicErrorReturn = useMagicError({\n prefix: 'PLUGIN-MEDIA-CLOUD',\n})\n\n/**\n * Creates Mux-related endpoints for asset handling\n * @param args - The arguments for creating Mux endpoints\n * @returns An array of Mux endpoint configurations\n */\ninterface CreateMuxEndpointsArgs {\n getMuxClient: () => Mux\n pluginOptions: MediaCloudPluginOptions\n}\n\nexport function createMuxEndpoints(args: CreateMuxEndpointsArgs) {\n const { getMuxClient, pluginOptions } = args\n\n const collection = pluginOptions.collection\n\n magicError.assert(collection, MediaCloudErrors.COLLECTION_REQUIRED)\n\n return [\n {\n handler: getMuxWebhookHandler({ getMuxClient, collection }),\n method: 'post' as const,\n path: '/mux/webhook',\n },\n {\n handler: getMuxCreateUploadHandler({ getMuxClient, pluginOptions }),\n method: 'post' as const,\n path: '/mux/upload',\n },\n {\n handler: getMuxAssetHandler({ getMuxClient, collection }),\n method: 'get' as const,\n path: '/mux/asset',\n },\n ]\n}\n\n/**\n * Validates Mux configuration options\n * @param muxConfig - The Mux configuration to validate\n * @throws {Error} When required Mux configuration is missing\n */\nexport function validateMuxConfig(\n muxConfig: MediaCloudPluginOptions['mux']\n): void {\n magicError.assert(muxConfig, MediaCloudErrors.MUX_CONFIG_INCOMPLETE)\n magicError.assert(muxConfig?.tokenId, MediaCloudErrors.MUX_CONFIG_MISSING)\n magicError.assert(muxConfig?.tokenSecret, MediaCloudErrors.MUX_CONFIG_MISSING)\n}\n\n/**\n * Creates a new Mux client instance\n * @param muxConfig - The Mux configuration options\n * @returns A configured Mux client\n */\nexport function createMuxClient(\n muxConfig: MediaCloudPluginOptions['mux']\n): Mux {\n validateMuxConfig(muxConfig)\n\n return new Mux({\n tokenId: muxConfig?.tokenId,\n tokenSecret: muxConfig?.tokenSecret,\n webhookSecret: muxConfig?.webhookSecret,\n })\n}\n"],"mappings":";;;;;;;;AAWA,MAAMQ,aAAkCH,cAAc,EACpDI,QAAQ,sBACT,CAAC;AAYF,SAAgBI,mBAAmBC,MAA8B;CAC/D,MAAM,EAAEH,cAAcC,kBAAkBE;CAExC,MAAMC,aAAaH,cAAcG;AAEjCP,YAAWQ,OAAOD,YAAYd,iBAAiBgB,oBAAoB;AAEnE,QAAO;EACL;GACEC,SAASd,qBAAqB;IAAEO;IAAcI;IAAY,CAAC;GAC3DI,QAAQ;GACRE,MAAM;GACP;EACD;GACEH,SAASf,0BAA0B;IAAEQ;IAAcC;IAAe,CAAC;GACnEO,QAAQ;GACRE,MAAM;GACP;EACD;GACEH,SAAShB,mBAAmB;IAAES;IAAcI;IAAY,CAAC;GACzDI,QAAQ;GACRE,MAAM;GACP;EACF;;;;;;;AAQH,SAAgBC,kBACdC,WACM;AACNf,YAAWQ,OAAOO,WAAWtB,iBAAiBuB,sBAAsB;AACpEhB,YAAWQ,OAAOO,WAAWE,SAASxB,iBAAiByB,mBAAmB;AAC1ElB,YAAWQ,OAAOO,WAAWI,aAAa1B,iBAAiByB,mBAAmB;;;;;;;AAQhF,SAAgBE,gBACdL,WACK;AACLD,mBAAkBC,UAAU;AAE5B,QAAO,IAAIvB,IAAI;EACbyB,SAASF,WAAWE;EACpBE,aAAaJ,WAAWI;EACxBE,eAAeN,WAAWM;EAC3B,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { MediaCloudPluginOptions } from "../types/index.mjs";
2
- import { S3Store } from "../tus/stores/s3/s3-store.mjs";
2
+ import { S3Store } from "../tus/stores/s3/s3Store.mjs";
3
3
  import { Server } from "@tus/server";
4
4
  import * as payload0 from "payload";
5
5
  import { PayloadRequest } from "payload";
@@ -8,22 +8,25 @@ import { PayloadRequest } from "payload";
8
8
 
9
9
  /**
10
10
  * Creates a TUS server instance with S3 storage
11
- * @param args - The arguments for creating the TUS server
11
+ * @param args.getS3Store - Function that returns an S3 client instance
12
+ * @param args.pluginOptions - Media cloud plugin options
12
13
  * @returns A configured TusServer instance
13
14
  */
14
15
  interface CreateTusServerArgs {
15
- s3Store: S3Store;
16
+ getS3Store: () => S3Store;
16
17
  pluginOptions: MediaCloudPluginOptions;
17
18
  }
18
19
  declare function createTusServer(args: CreateTusServerArgs): Server;
19
20
  /**
20
21
  * Creates TUS upload endpoints for file handling
21
- * @param tusServer - The TUS server instance
22
+ * @param args.getTusServer - Function that returns a TUS server instance
23
+ * @param args.getS3Store - Function that returns an S3 client instance
22
24
  * @returns An array of endpoint configurations
23
25
  */
24
26
  interface CreateTusEndpointsArgs {
25
- tusServer: Server;
26
- s3Store: S3Store;
27
+ getTusServer: () => Server;
28
+ getS3Store: () => S3Store;
29
+ pluginOptions: MediaCloudPluginOptions;
27
30
  }
28
31
  declare function createTusEndpoints(args: CreateTusEndpointsArgs): ({
29
32
  handler: (req: PayloadRequest) => Promise<Response>;
@@ -1,30 +1,41 @@
1
+ import { MediaCloudErrors } from "../types/errors.mjs";
2
+ import { useMagicError } from "../error-handler/dist/index.mjs";
1
3
  import { generateUniqueFilename } from "./file.mjs";
2
4
  import { getTusPostProcessorHandler } from "../endpoints/tusPostProcessorHandler.mjs";
3
- import { getTusFileExistsHandler } from "../endpoints/tusFileExistsHandler.mjs";
5
+ import { getTusFolderHandler } from "../endpoints/tusFolderHandler.mjs";
4
6
  import { getTusCleanupHandler } from "../endpoints/tusCleanupHandler.mjs";
5
7
  import { Server } from "@tus/server";
6
8
 
7
9
  //#region src/utils/tus.ts
10
+ const magicError = useMagicError({ prefix: "PLUGIN-MEDIA-CLOUD" });
8
11
  function createTusServer(args) {
9
- const { s3Store, pluginOptions } = args;
12
+ const { getS3Store, pluginOptions } = args;
13
+ const s3Store = getS3Store();
10
14
  return new Server({
11
15
  datastore: s3Store,
16
+ path: "/api/uploads",
17
+ respectForwardedHeaders: pluginOptions.s3?.respectForwardedHeaders ?? true,
12
18
  namingFunction: async (_req, metadata) => {
13
19
  return metadata?.filename ?? generateUniqueFilename("");
14
20
  },
15
- path: "/api/uploads",
16
- respectForwardedHeaders: pluginOptions.s3.respectForwardedHeaders ?? true
21
+ onUploadFinish: async (_req, upload) => {
22
+ const filename = upload.metadata?.filename ?? "";
23
+ await s3Store.cleanup(filename);
24
+ return Promise.resolve({});
25
+ }
17
26
  });
18
27
  }
19
28
  function createTusEndpoints(args) {
20
- const { tusServer, s3Store } = args;
29
+ const { getTusServer, getS3Store, pluginOptions } = args;
30
+ const collection = pluginOptions.collection;
31
+ magicError.assert(collection, MediaCloudErrors.COLLECTION_REQUIRED);
21
32
  /**
22
33
  * Handles TUS requests through the server
23
34
  * @param req - The payload request object
24
35
  * @returns The server response
25
36
  */
26
37
  function tusHandler(req) {
27
- return tusServer.handleWeb(req);
38
+ return getTusServer().handleWeb(req);
28
39
  }
29
40
  return [
30
41
  {
@@ -58,17 +69,26 @@ function createTusEndpoints(args) {
58
69
  path: "/uploads/:id"
59
70
  },
60
71
  {
61
- handler: getTusFileExistsHandler({ s3Store }),
72
+ handler: getTusPostProcessorHandler({
73
+ getS3Store,
74
+ collection
75
+ }),
62
76
  method: "get",
63
- path: "/uploads/:filename/exists"
77
+ path: "/uploads/:filename/process"
64
78
  },
65
79
  {
66
- handler: getTusPostProcessorHandler({ s3Store }),
80
+ handler: getTusFolderHandler({
81
+ getS3Store,
82
+ collection
83
+ }),
67
84
  method: "get",
68
- path: "/uploads/:filename/process"
85
+ path: "/uploads/:filename/folder"
69
86
  },
70
87
  {
71
- handler: getTusCleanupHandler({ s3Store }),
88
+ handler: getTusCleanupHandler({
89
+ getS3Store,
90
+ collection
91
+ }),
72
92
  method: "post",
73
93
  path: "/uploads/cleanup"
74
94
  }
@@ -1 +1 @@
1
- {"version":3,"file":"tus.mjs","names":["TusServer"],"sources":["../../src/utils/tus.ts"],"sourcesContent":["import { Server as TusServer } from '@tus/server'\n\nimport { getTusPostProcessorHandler } from '../endpoints/tusPostProcessorHandler'\nimport { getTusFileExistsHandler } from '../endpoints/tusFileExistsHandler'\n\nimport { generateUniqueFilename } from './file'\n\nimport type { S3Store } from '../tus/stores/s3/s3-store'\nimport type { PayloadRequest } from 'payload'\nimport type { MediaCloudPluginOptions } from '../types'\nimport { getTusCleanupHandler } from '../endpoints/tusCleanupHandler'\n\n/**\n * Creates a TUS server instance with S3 storage\n * @param args - The arguments for creating the TUS server\n * @returns A configured TusServer instance\n */\ninterface CreateTusServerArgs {\n s3Store: S3Store\n pluginOptions: MediaCloudPluginOptions\n}\n\nexport function createTusServer(args: CreateTusServerArgs): TusServer {\n const { s3Store, pluginOptions } = args\n\n return new TusServer({\n datastore: s3Store,\n namingFunction: async (_req, metadata) => {\n return metadata?.filename ?? generateUniqueFilename('')\n },\n path: '/api/uploads',\n respectForwardedHeaders: pluginOptions.s3.respectForwardedHeaders ?? true,\n })\n}\n\n/**\n * Creates TUS upload endpoints for file handling\n * @param tusServer - The TUS server instance\n * @returns An array of endpoint configurations\n */\ninterface CreateTusEndpointsArgs {\n tusServer: TusServer\n s3Store: S3Store\n}\n\nexport function createTusEndpoints(args: CreateTusEndpointsArgs) {\n const { tusServer, s3Store } = args\n\n /**\n * Handles TUS requests through the server\n * @param req - The payload request object\n * @returns The server response\n */\n function tusHandler(req: PayloadRequest) {\n return tusServer.handleWeb(req as Request)\n }\n\n return [\n { handler: tusHandler, method: 'options' as const, path: '/uploads' },\n { handler: tusHandler, method: 'post' as const, path: '/uploads' },\n { handler: tusHandler, method: 'get' as const, path: '/uploads/:id' },\n { handler: tusHandler, method: 'put' as const, path: '/uploads/:id' },\n { handler: tusHandler, method: 'patch' as const, path: '/uploads/:id' },\n { handler: tusHandler, method: 'delete' as const, path: '/uploads/:id' },\n {\n handler: getTusFileExistsHandler({ s3Store }),\n method: 'get' as const,\n path: '/uploads/:filename/exists',\n },\n {\n handler: getTusPostProcessorHandler({ s3Store }),\n method: 'get' as const,\n path: '/uploads/:filename/process',\n },\n {\n handler: getTusCleanupHandler({ s3Store }),\n method: 'post' as const,\n path: '/uploads/cleanup',\n },\n ]\n}\n"],"mappings":";;;;;;;AAsBA,SAAgB,gBAAgB,MAAsC;CACpE,MAAM,EAAE,SAAS,kBAAkB;AAEnC,QAAO,IAAIA,OAAU;EACnB,WAAW;EACX,gBAAgB,OAAO,MAAM,aAAa;AACxC,UAAO,UAAU,YAAY,uBAAuB,GAAG;;EAEzD,MAAM;EACN,yBAAyB,cAAc,GAAG,2BAA2B;EACtE,CAAC;;AAaJ,SAAgB,mBAAmB,MAA8B;CAC/D,MAAM,EAAE,WAAW,YAAY;;;;;;CAO/B,SAAS,WAAW,KAAqB;AACvC,SAAO,UAAU,UAAU,IAAe;;AAG5C,QAAO;EACL;GAAE,SAAS;GAAY,QAAQ;GAAoB,MAAM;GAAY;EACrE;GAAE,SAAS;GAAY,QAAQ;GAAiB,MAAM;GAAY;EAClE;GAAE,SAAS;GAAY,QAAQ;GAAgB,MAAM;GAAgB;EACrE;GAAE,SAAS;GAAY,QAAQ;GAAgB,MAAM;GAAgB;EACrE;GAAE,SAAS;GAAY,QAAQ;GAAkB,MAAM;GAAgB;EACvE;GAAE,SAAS;GAAY,QAAQ;GAAmB,MAAM;GAAgB;EACxE;GACE,SAAS,wBAAwB,EAAE,SAAS,CAAC;GAC7C,QAAQ;GACR,MAAM;GACP;EACD;GACE,SAAS,2BAA2B,EAAE,SAAS,CAAC;GAChD,QAAQ;GACR,MAAM;GACP;EACD;GACE,SAAS,qBAAqB,EAAE,SAAS,CAAC;GAC1C,QAAQ;GACR,MAAM;GACP;EACF"}
1
+ {"version":3,"file":"tus.mjs","names":["Server","TusServer","getTusPostProcessorHandler","getTusFolderHandler","getTusCleanupHandler","generateUniqueFilename","useMagicError","UseMagicErrorReturn","S3Store","PayloadRequest","MediaCloudPluginOptions","MediaCloudErrors","magicError","prefix","CreateTusServerArgs","getS3Store","pluginOptions","createTusServer","args","s3Store","datastore","path","respectForwardedHeaders","s3","namingFunction","_req","metadata","filename","onUploadFinish","upload","cleanup","Promise","resolve","CreateTusEndpointsArgs","getTusServer","createTusEndpoints","collection","assert","COLLECTION_REQUIRED","tusHandler","req","handleWeb","Request","handler","method","const"],"sources":["../../src/utils/tus.ts"],"sourcesContent":["import { Server as TusServer } from '@tus/server'\n\nimport { getTusPostProcessorHandler } from '../endpoints/tusPostProcessorHandler'\nimport { getTusFolderHandler } from '../endpoints/tusFolderHandler'\nimport { getTusCleanupHandler } from '../endpoints/tusCleanupHandler'\n\nimport { generateUniqueFilename } from './file'\nimport { useMagicError, type UseMagicErrorReturn } from '@maas/error-handler'\n\nimport type { S3Store } from '../tus/stores/s3/s3Store'\nimport type { PayloadRequest } from 'payload'\nimport type { MediaCloudPluginOptions } from '../types'\nimport { MediaCloudErrors } from '../types/errors'\n\nconst magicError: UseMagicErrorReturn = useMagicError({\n prefix: 'PLUGIN-MEDIA-CLOUD',\n})\n\n/**\n * Creates a TUS server instance with S3 storage\n * @param args.getS3Store - Function that returns an S3 client instance\n * @param args.pluginOptions - Media cloud plugin options\n * @returns A configured TusServer instance\n */\ninterface CreateTusServerArgs {\n getS3Store: () => S3Store\n pluginOptions: MediaCloudPluginOptions\n}\n\nexport function createTusServer(args: CreateTusServerArgs): TusServer {\n const { getS3Store, pluginOptions } = args\n\n const s3Store = getS3Store()\n\n return new TusServer({\n datastore: s3Store,\n path: '/api/uploads',\n respectForwardedHeaders: pluginOptions.s3?.respectForwardedHeaders ?? true,\n namingFunction: async (_req, metadata) => {\n return metadata?.filename ?? generateUniqueFilename('')\n },\n onUploadFinish: async (_req, upload) => {\n // Clean up .part and .info files\n const filename = upload.metadata?.filename ?? ''\n await s3Store.cleanup(filename)\n\n // Prevent type error\n return Promise.resolve({})\n },\n })\n}\n\n/**\n * Creates TUS upload endpoints for file handling\n * @param args.getTusServer - Function that returns a TUS server instance\n * @param args.getS3Store - Function that returns an S3 client instance\n * @returns An array of endpoint configurations\n */\ninterface CreateTusEndpointsArgs {\n getTusServer: () => TusServer\n getS3Store: () => S3Store\n pluginOptions: MediaCloudPluginOptions\n}\n\nexport function createTusEndpoints(args: CreateTusEndpointsArgs) {\n const { getTusServer, getS3Store, pluginOptions } = args\n const collection = pluginOptions.collection\n\n magicError.assert(collection, MediaCloudErrors.COLLECTION_REQUIRED)\n\n /**\n * Handles TUS requests through the server\n * @param req - The payload request object\n * @returns The server response\n */\n function tusHandler(req: PayloadRequest) {\n return getTusServer().handleWeb(req as Request)\n }\n\n return [\n { handler: tusHandler, method: 'options' as const, path: '/uploads' },\n { handler: tusHandler, method: 'post' as const, path: '/uploads' },\n { handler: tusHandler, method: 'get' as const, path: '/uploads/:id' },\n { handler: tusHandler, method: 'put' as const, path: '/uploads/:id' },\n { handler: tusHandler, method: 'patch' as const, path: '/uploads/:id' },\n { handler: tusHandler, method: 'delete' as const, path: '/uploads/:id' },\n {\n handler: getTusPostProcessorHandler({ getS3Store, collection }),\n method: 'get' as const,\n path: '/uploads/:filename/process',\n },\n {\n handler: getTusFolderHandler({ getS3Store, collection }),\n method: 'get' as const,\n path: '/uploads/:filename/folder',\n },\n {\n handler: getTusCleanupHandler({ getS3Store, collection }),\n method: 'post' as const,\n path: '/uploads/cleanup',\n },\n ]\n}\n"],"mappings":";;;;;;;;;AAcA,MAAMY,aAAkCN,cAAc,EACpDO,QAAQ,sBACT,CAAC;AAaF,SAAgBI,gBAAgBC,MAAsC;CACpE,MAAM,EAAEH,YAAYC,kBAAkBE;CAEtC,MAAMC,UAAUJ,YAAY;AAE5B,QAAO,IAAId,OAAU;EACnBmB,WAAWD;EACXE,MAAM;EACNC,yBAAyBN,cAAcO,IAAID,2BAA2B;EACtEE,gBAAgB,OAAOC,MAAMC,aAAa;AACxC,UAAOA,UAAUC,YAAYtB,uBAAuB,GAAG;;EAEzDuB,gBAAgB,OAAOH,MAAMI,WAAW;GAEtC,MAAMF,WAAWE,OAAOH,UAAUC,YAAY;AAC9C,SAAMR,QAAQW,QAAQH,SAAS;AAG/B,UAAOI,QAAQC,QAAQ,EAAE,CAAC;;EAE7B,CAAC;;AAeJ,SAAgBG,mBAAmBjB,MAA8B;CAC/D,MAAM,EAAEgB,cAAcnB,YAAYC,kBAAkBE;CACpD,MAAMkB,aAAapB,cAAcoB;AAEjCxB,YAAWyB,OAAOD,YAAYzB,iBAAiB2B,oBAAoB;;;;;;CAOnE,SAASC,WAAWC,KAAqB;AACvC,SAAON,cAAc,CAACO,UAAUD,IAAe;;AAGjD,QAAO;EACL;GAAEG,SAASJ;GAAYK,QAAQ;GAAoBvB,MAAM;GAAY;EACrE;GAAEsB,SAASJ;GAAYK,QAAQ;GAAiBvB,MAAM;GAAY;EAClE;GAAEsB,SAASJ;GAAYK,QAAQ;GAAgBvB,MAAM;GAAgB;EACrE;GAAEsB,SAASJ;GAAYK,QAAQ;GAAgBvB,MAAM;GAAgB;EACrE;GAAEsB,SAASJ;GAAYK,QAAQ;GAAkBvB,MAAM;GAAgB;EACvE;GAAEsB,SAASJ;GAAYK,QAAQ;GAAmBvB,MAAM;GAAgB;EACxE;GACEsB,SAASzC,2BAA2B;IAAEa;IAAYqB;IAAY,CAAC;GAC/DQ,QAAQ;GACRvB,MAAM;GACP;EACD;GACEsB,SAASxC,oBAAoB;IAAEY;IAAYqB;IAAY,CAAC;GACxDQ,QAAQ;GACRvB,MAAM;GACP;EACD;GACEsB,SAASvC,qBAAqB;IAAEW;IAAYqB;IAAY,CAAC;GACzDQ,QAAQ;GACRvB,MAAM;GACP;EACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maas/payload-plugin-media-cloud",
3
- "version": "0.0.31",
3
+ "version": "0.0.33",
4
4
  "type": "module",
5
5
  "contributors": [
6
6
  {
@@ -40,6 +40,7 @@
40
40
  "@mux/upchunk": "^3.5.0",
41
41
  "@tus/server": "^2.3.0",
42
42
  "@tus/utils": "^0.6.0",
43
+ "defu": "^6.1.4",
43
44
  "file-type": "^21.1.1",
44
45
  "image-size": "^2.0.2",
45
46
  "mitt": "^3.0.1",
@@ -50,7 +51,9 @@
50
51
  "peerDependencies": {
51
52
  "@mux/mux-player-react": "^3",
52
53
  "@payloadcms/plugin-cloud-storage": "^3.61",
54
+ "@payloadcms/translations": "^3.61",
53
55
  "@payloadcms/ui": "^3.61",
56
+ "next": "^15.4.10",
54
57
  "payload": "^3.61",
55
58
  "react": "^19.1",
56
59
  "react-dom": "^19.1"
@@ -65,14 +68,18 @@
65
68
  },
66
69
  "devDependencies": {
67
70
  "@mux/mux-player-react": "^3.9.2",
68
- "@payloadcms/plugin-cloud-storage": "3.71.0",
69
- "@payloadcms/ui": "3.71.0",
71
+ "@payloadcms/plugin-cloud-storage": "3.75.0",
72
+ "@payloadcms/translations": "3.75.0",
73
+ "@payloadcms/ui": "3.75.0",
74
+ "@rollup/plugin-babel": "^6.1.0",
70
75
  "@types/multistream": "^4.1.3",
71
76
  "@types/node": "^22.19.1",
72
77
  "@types/react": "19.2.1",
73
78
  "@types/react-dom": "19.2.1",
79
+ "babel-plugin-react-compiler": "^1.0.0",
74
80
  "glob": "^13.0.0",
75
- "payload": "3.71.0",
81
+ "next": "15.4.8",
82
+ "payload": "3.75.0",
76
83
  "react": "19.2.1",
77
84
  "react-dom": "19.2.1",
78
85
  "tsdown": "^0.16.8",
@@ -1,2 +0,0 @@
1
- import { MuxPreview } from "./mux-preview.mjs";
2
- export { MuxPreview };
@@ -1,3 +0,0 @@
1
- import { MuxPreview } from "./mux-preview.mjs";
2
-
3
- export { MuxPreview };
@@ -1,29 +0,0 @@
1
- 'use client';
2
-
3
- import { useEffect, useState } from "react";
4
- import MuxPlayer from "@mux/mux-player-react";
5
-
6
- //#region src/components/mux-preview/mux-preview.tsx
7
- /**
8
- * React component for previewing Mux video assets
9
- * @param props - The UI field client props containing field data
10
- * @returns JSX element for Mux video preview
11
- */
12
- function MuxPreview(props) {
13
- const [playbackId, setPlaybackId] = useState(null);
14
- const [isClient, setIsClient] = useState(false);
15
- useEffect(() => {
16
- setIsClient(true);
17
- }, []);
18
- useEffect(() => {
19
- if (!isClient) return;
20
- const collection = props.schemaPath?.split(".")[0];
21
- const docId = window.location.pathname.split("/").pop();
22
- if (collection && docId && docId !== "create" && docId !== "admin") fetch(`/api/${collection}/${docId}`).then((res) => res.json()).then((data) => setPlaybackId(data.mux?.playbackId || null)).catch(() => setPlaybackId(null));
23
- }, [props.schemaPath, isClient]);
24
- return isClient && playbackId ? <MuxPlayer playbackId={playbackId} streamType="on-demand" disableTracking={true} style={{ height: "60vh" }} nohotkeys={true} preload="metadata" /> : null;
25
- }
26
-
27
- //#endregion
28
- export { MuxPreview };
29
- //# sourceMappingURL=mux-preview.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mux-preview.mjs","names":[],"sources":["../../../src/components/mux-preview/mux-preview.tsx"],"sourcesContent":["'use client'\n\nimport { useEffect, useState } from 'react'\nimport MuxPlayer from '@mux/mux-player-react'\n\nimport type React from 'react'\nimport type { UIFieldClientProps } from 'payload'\n\n/**\n * React component for previewing Mux video assets\n * @param props - The UI field client props containing field data\n * @returns JSX element for Mux video preview\n */\nexport function MuxPreview(props: UIFieldClientProps) {\n const [playbackId, setPlaybackId] = useState<string | null>(null)\n const [isClient, setIsClient] = useState(false)\n\n // Ensure we’re on the client side\n useEffect(() => {\n setIsClient(true)\n }, [])\n\n useEffect(() => {\n if (!isClient) {\n return\n }\n\n // Get collection from schemaPath and ID from URL\n const collection = props.schemaPath?.split('.')[0] // \"media.muxPlayer\" -> \"media\"\n const docId = window.location.pathname.split('/').pop() // Get last part of URL\n\n if (collection && docId && docId !== 'create' && docId !== 'admin') {\n fetch(`/api/${collection}/${docId}`)\n .then((res) => res.json())\n .then((data) => setPlaybackId(data.mux?.playbackId || null))\n .catch(() => setPlaybackId(null))\n }\n }, [props.schemaPath, isClient])\n\n // Only render on client with playbackId\n return isClient && playbackId ? (\n <MuxPlayer\n playbackId={playbackId}\n streamType=\"on-demand\"\n disableTracking={true}\n style={{ height: '60vh' }}\n nohotkeys={true}\n preload=\"metadata\"\n />\n ) : null\n}\n"],"mappings":";;;;;;;;;;;AAaA,SAAgB,WAAW,OAA2B;CACpD,MAAM,CAAC,YAAY,iBAAiB,SAAwB,KAAK;CACjE,MAAM,CAAC,UAAU,eAAe,SAAS,MAAM;AAG/C,iBAAgB;AACd,cAAY,KAAK;IAChB,EAAE,CAAC;AAEN,iBAAgB;AACd,MAAI,CAAC,SACH;EAIF,MAAM,aAAa,MAAM,YAAY,MAAM,IAAI,CAAC;EAChD,MAAM,QAAQ,OAAO,SAAS,SAAS,MAAM,IAAI,CAAC,KAAK;AAEvD,MAAI,cAAc,SAAS,UAAU,YAAY,UAAU,QACzD,OAAM,QAAQ,WAAW,GAAG,QAAQ,CACjC,MAAM,QAAQ,IAAI,MAAM,CAAC,CACzB,MAAM,SAAS,cAAc,KAAK,KAAK,cAAc,KAAK,CAAC,CAC3D,YAAY,cAAc,KAAK,CAAC;IAEpC,CAAC,MAAM,YAAY,SAAS,CAAC;AAGhC,QAAO,YAAY,aACjB,CAAC,UACC,YAAY,YACZ,uBACA,iBAAiB,MACjB,OAAO,EAAE,QAAQ,QAAQ,EACzB,WAAW,MACX,wBAEA"}
@@ -1,2 +0,0 @@
1
- import { UploadHandler } from "./upload-handler.mjs";
2
- export { UploadHandler };
@@ -1,3 +0,0 @@
1
- import { UploadHandler } from "./upload-handler.mjs";
2
-
3
- export { UploadHandler };
@@ -1,22 +0,0 @@
1
- import * as react0 from "react";
2
- import * as payload1 from "payload";
3
-
4
- //#region src/components/upload-handler/upload-handler.d.ts
5
- declare const UploadHandler: ({
6
- children,
7
- collectionSlug,
8
- enabled,
9
- extra,
10
- prefix,
11
- serverHandlerPath
12
- }: {
13
- children: react0.ReactNode;
14
- collectionSlug: payload1.UploadCollectionSlug;
15
- enabled?: boolean;
16
- extra: Record<string, unknown>;
17
- prefix?: string;
18
- serverHandlerPath: `/${string}`;
19
- }) => react0.JSX.Element;
20
- //#endregion
21
- export { UploadHandler };
22
- //# sourceMappingURL=upload-handler.d.mts.map
@@ -1 +0,0 @@
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 eventListenerAdded = true\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 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":"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"}
@@ -1,2 +0,0 @@
1
- import { UploadManagerProvider, useUploadManagerContext } from "./upload-manager.mjs";
2
- export { UploadManagerProvider, useUploadManagerContext };
@@ -1,3 +0,0 @@
1
- import { UploadManagerProvider, useUploadManagerContext } from "./upload-manager.mjs";
2
-
3
- export { UploadManagerProvider, useUploadManagerContext };