@maas/payload-plugin-media-cloud 0.0.45 → 0.0.47

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.
@@ -3,16 +3,28 @@ import { useErrorHandler } from "../hooks/useErrorHandler.mjs";
3
3
  import { s3Store } from "../plugin.mjs";
4
4
 
5
5
  //#region src/collectionHooks/afterChange.ts
6
- const afterChangeHook = async ({ doc, previousDoc, req }) => {
6
+ const afterChangeHook = async ({ doc, previousDoc, collection, operation, req }) => {
7
7
  const { throwError } = useErrorHandler();
8
8
  if (req.context?._mediaCloudPluginInternal) return doc;
9
- if (!previousDoc?.path) return doc;
9
+ if (operation !== "update") return doc;
10
10
  if (doc.path !== previousDoc?.path) {
11
11
  if (doc.storage === "s3" && s3Store) try {
12
12
  const oldKey = previousDoc?.path;
13
13
  const newKey = doc.path;
14
14
  await s3Store.copy(oldKey, newKey);
15
15
  } catch (error) {
16
+ req.context = { ...req.context };
17
+ req.context._mediaCloudPluginInternal = true;
18
+ await req.payload.update({
19
+ collection: collection.slug,
20
+ id: doc.id,
21
+ data: {
22
+ path: previousDoc.path,
23
+ filename: previousDoc.filename
24
+ },
25
+ req
26
+ });
27
+ delete req.context._mediaCloudPluginInternal;
16
28
  throwError({
17
29
  ...MediaCloudErrors.S3_MOVE_ERROR,
18
30
  cause: error
@@ -1 +1 @@
1
- {"version":3,"file":"afterChange.mjs","names":["afterChangeHook: CollectionAfterChangeHook"],"sources":["../../src/collectionHooks/afterChange.ts"],"sourcesContent":["import { s3Store } from '../plugin'\nimport { MediaCloudErrors } from '../types/errors'\nimport { useErrorHandler } from '../hooks/useErrorHandler'\n\nimport type { CollectionAfterChangeHook } from 'payload'\n\nexport const afterChangeHook: CollectionAfterChangeHook = async ({\n doc,\n previousDoc,\n req,\n}) => {\n const { throwError } = useErrorHandler()\n\n // Skip if this is an internal update to prevent infinite loop\n if (req.context?._mediaCloudPluginInternal) {\n return doc\n }\n\n // Skip if this is a new document, only move files on updates\n if (!previousDoc?.path) {\n return doc\n }\n\n // Move asset in S3 if path has changed\n // Path is updated in `beforeChange` if filename or folder changes\n if (doc.path !== previousDoc?.path) {\n if (doc.storage === 's3' && s3Store) {\n try {\n const oldKey = previousDoc?.path\n const newKey = doc.path\n\n await s3Store.copy(oldKey, newKey)\n } catch (error) {\n throwError({ ...MediaCloudErrors.S3_MOVE_ERROR, cause: error })\n }\n }\n }\n\n return doc\n}\n"],"mappings":";;;;;AAMA,MAAaA,kBAA6C,OAAO,EAC/D,KACA,aACA,UACI;CACJ,MAAM,EAAE,eAAe,iBAAiB;AAGxC,KAAI,IAAI,SAAS,0BACf,QAAO;AAIT,KAAI,CAAC,aAAa,KAChB,QAAO;AAKT,KAAI,IAAI,SAAS,aAAa,MAC5B;MAAI,IAAI,YAAY,QAAQ,QAC1B,KAAI;GACF,MAAM,SAAS,aAAa;GAC5B,MAAM,SAAS,IAAI;AAEnB,SAAM,QAAQ,KAAK,QAAQ,OAAO;WAC3B,OAAO;AACd,cAAW;IAAE,GAAG,iBAAiB;IAAe,OAAO;IAAO,CAAC;;;AAKrE,QAAO"}
1
+ {"version":3,"file":"afterChange.mjs","names":["afterChangeHook: CollectionAfterChangeHook"],"sources":["../../src/collectionHooks/afterChange.ts"],"sourcesContent":["import { s3Store } from '../plugin'\nimport { MediaCloudErrors } from '../types/errors'\nimport { useErrorHandler } from '../hooks/useErrorHandler'\n\nimport type { CollectionAfterChangeHook } from 'payload'\n\nexport const afterChangeHook: CollectionAfterChangeHook = async ({\n doc,\n previousDoc,\n collection,\n operation,\n req,\n}) => {\n const { throwError } = useErrorHandler()\n\n // Skip if this is an internal update to prevent infinite loop\n if (req.context?._mediaCloudPluginInternal) {\n return doc\n }\n\n // Skip if this is a new document, only move files on update\n if (operation !== 'update') {\n return doc\n }\n\n // Move asset in S3 if path has changed\n // Path is updated in `beforeChange` if filename or folder changes\n if (doc.path !== previousDoc?.path) {\n if (doc.storage === 's3' && s3Store) {\n try {\n const oldKey = previousDoc?.path\n const newKey = doc.path\n\n await s3Store.copy(oldKey, newKey)\n } catch (error) {\n // Revert path and filename if the file was not moved successfully\n // Marked as internal to prevent infinite loop\n req.context = { ...req.context }\n req.context._mediaCloudPluginInternal = true\n\n await req.payload.update({\n collection: collection.slug,\n id: doc.id,\n data: { path: previousDoc.path, filename: previousDoc.filename },\n req,\n })\n\n delete req.context._mediaCloudPluginInternal\n\n throwError({ ...MediaCloudErrors.S3_MOVE_ERROR, cause: error })\n }\n }\n }\n\n return doc\n}\n"],"mappings":";;;;;AAMA,MAAaA,kBAA6C,OAAO,EAC/D,KACA,aACA,YACA,WACA,UACI;CACJ,MAAM,EAAE,eAAe,iBAAiB;AAGxC,KAAI,IAAI,SAAS,0BACf,QAAO;AAIT,KAAI,cAAc,SAChB,QAAO;AAKT,KAAI,IAAI,SAAS,aAAa,MAC5B;MAAI,IAAI,YAAY,QAAQ,QAC1B,KAAI;GACF,MAAM,SAAS,aAAa;GAC5B,MAAM,SAAS,IAAI;AAEnB,SAAM,QAAQ,KAAK,QAAQ,OAAO;WAC3B,OAAO;AAGd,OAAI,UAAU,EAAE,GAAG,IAAI,SAAS;AAChC,OAAI,QAAQ,4BAA4B;AAExC,SAAM,IAAI,QAAQ,OAAO;IACvB,YAAY,WAAW;IACvB,IAAI,IAAI;IACR,MAAM;KAAE,MAAM,YAAY;KAAM,UAAU,YAAY;KAAU;IAChE;IACD,CAAC;AAEF,UAAO,IAAI,QAAQ;AAEnB,cAAW;IAAE,GAAG,iBAAiB;IAAe,OAAO;IAAO,CAAC;;;AAKrE,QAAO"}
@@ -18,7 +18,7 @@ const beforeChangeHook = async ({ data, originalDoc, req }) => {
18
18
  name: true,
19
19
  folder: true
20
20
  },
21
- depth: 10
21
+ depth: 20
22
22
  });
23
23
  if (folder) prefix = buildS3Path(folder);
24
24
  } catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"beforeChange.mjs","names":["beforeChangeHook: CollectionBeforeChangeHook"],"sources":["../../src/collectionHooks/beforeChange.ts"],"sourcesContent":["import { useErrorHandler } from '../hooks/useErrorHandler'\nimport { MediaCloudErrors } from '../types/errors'\nimport { buildS3Path } from '../utils/buildS3Path'\nimport type { CollectionBeforeChangeHook } from 'payload'\n\nexport const beforeChangeHook: CollectionBeforeChangeHook = async ({\n data,\n originalDoc,\n req,\n}) => {\n const { throwError } = useErrorHandler()\n const { custom } = req?.payload?.config ?? {}\n\n // Check if folders are enabled in plugin options\n const folders = custom?.mediaCloud?.options?.folders\n\n // Update path if missing or filename or folder have changed\n if (\n (data.filename && data.filename !== originalDoc?.filename) ||\n (data.folder && data.folder !== originalDoc?.folder) ||\n !data.path\n ) {\n switch (folders) {\n case true: {\n let prefix = ''\n\n if (data.folder) {\n try {\n const folder = await req.payload.findByID({\n id: data.folder,\n collection: 'payload-folders',\n select: { name: true, folder: true },\n depth: 10,\n })\n\n if (folder) {\n prefix = buildS3Path(folder)\n }\n } catch (error) {\n throwError({\n ...MediaCloudErrors.FOLDER_FETCH_ERROR,\n cause: error,\n })\n }\n }\n\n // Save path\n data.path = prefix ? `${prefix}/${data.filename}` : data.filename\n break\n }\n case false: {\n // For flat structures, use filename as path\n data.path = data.filename\n break\n }\n }\n }\n\n return data\n}\n"],"mappings":";;;;;AAKA,MAAaA,mBAA+C,OAAO,EACjE,MACA,aACA,UACI;CACJ,MAAM,EAAE,eAAe,iBAAiB;CACxC,MAAM,EAAE,WAAW,KAAK,SAAS,UAAU,EAAE;CAG7C,MAAM,UAAU,QAAQ,YAAY,SAAS;AAG7C,KACG,KAAK,YAAY,KAAK,aAAa,aAAa,YAChD,KAAK,UAAU,KAAK,WAAW,aAAa,UAC7C,CAAC,KAAK,KAEN,SAAQ,SAAR;EACE,KAAK,MAAM;GACT,IAAI,SAAS;AAEb,OAAI,KAAK,OACP,KAAI;IACF,MAAM,SAAS,MAAM,IAAI,QAAQ,SAAS;KACxC,IAAI,KAAK;KACT,YAAY;KACZ,QAAQ;MAAE,MAAM;MAAM,QAAQ;MAAM;KACpC,OAAO;KACR,CAAC;AAEF,QAAI,OACF,UAAS,YAAY,OAAO;YAEvB,OAAO;AACd,eAAW;KACT,GAAG,iBAAiB;KACpB,OAAO;KACR,CAAC;;AAKN,QAAK,OAAO,SAAS,GAAG,OAAO,GAAG,KAAK,aAAa,KAAK;AACzD;;EAEF,KAAK;AAEH,QAAK,OAAO,KAAK;AACjB;;AAKN,QAAO"}
1
+ {"version":3,"file":"beforeChange.mjs","names":["beforeChangeHook: CollectionBeforeChangeHook"],"sources":["../../src/collectionHooks/beforeChange.ts"],"sourcesContent":["import { useErrorHandler } from '../hooks/useErrorHandler'\nimport { MediaCloudErrors } from '../types/errors'\nimport { buildS3Path } from '../utils/buildS3Path'\nimport type { CollectionBeforeChangeHook } from 'payload'\n\nexport const beforeChangeHook: CollectionBeforeChangeHook = async ({\n data,\n originalDoc,\n req,\n}) => {\n const { throwError } = useErrorHandler()\n const { custom } = req?.payload?.config ?? {}\n\n // Check if folders are enabled in plugin options\n const folders = custom?.mediaCloud?.options?.folders\n\n // Update path if missing or filename or folder have changed\n if (\n (data.filename && data.filename !== originalDoc?.filename) ||\n (data.folder && data.folder !== originalDoc?.folder) ||\n !data.path\n ) {\n switch (folders) {\n case true: {\n let prefix = ''\n\n if (data.folder) {\n try {\n const folder = await req.payload.findByID({\n id: data.folder,\n collection: 'payload-folders',\n select: { name: true, folder: true },\n depth: 20,\n })\n\n if (folder) {\n prefix = buildS3Path(folder)\n }\n } catch (error) {\n throwError({\n ...MediaCloudErrors.FOLDER_FETCH_ERROR,\n cause: error,\n })\n }\n }\n\n // Save path\n data.path = prefix ? `${prefix}/${data.filename}` : data.filename\n break\n }\n case false: {\n // For flat structures, use filename as path\n data.path = data.filename\n break\n }\n }\n }\n\n return data\n}\n"],"mappings":";;;;;AAKA,MAAaA,mBAA+C,OAAO,EACjE,MACA,aACA,UACI;CACJ,MAAM,EAAE,eAAe,iBAAiB;CACxC,MAAM,EAAE,WAAW,KAAK,SAAS,UAAU,EAAE;CAG7C,MAAM,UAAU,QAAQ,YAAY,SAAS;AAG7C,KACG,KAAK,YAAY,KAAK,aAAa,aAAa,YAChD,KAAK,UAAU,KAAK,WAAW,aAAa,UAC7C,CAAC,KAAK,KAEN,SAAQ,SAAR;EACE,KAAK,MAAM;GACT,IAAI,SAAS;AAEb,OAAI,KAAK,OACP,KAAI;IACF,MAAM,SAAS,MAAM,IAAI,QAAQ,SAAS;KACxC,IAAI,KAAK;KACT,YAAY;KACZ,QAAQ;MAAE,MAAM;MAAM,QAAQ;MAAM;KACpC,OAAO;KACR,CAAC;AAEF,QAAI,OACF,UAAS,YAAY,OAAO;YAEvB,OAAO;AACd,eAAW;KACT,GAAG,iBAAiB;KACpB,OAAO;KACR,CAAC;;AAKN,QAAK,OAAO,SAAS,GAAG,OAAO,GAAG,KAAK,aAAa,KAAK;AACzD;;EAEF,KAAK;AAEH,QAAK,OAAO,KAAK;AACjB;;AAKN,QAAO"}
@@ -0,0 +1,7 @@
1
+ import { CollectionAfterChangeHook } from "payload";
2
+
3
+ //#region src/collectionHooks/folderAfterChange.d.ts
4
+ declare const folderAfterChangeHook: CollectionAfterChangeHook;
5
+ //#endregion
6
+ export { folderAfterChangeHook };
7
+ //# sourceMappingURL=folderAfterChange.d.mts.map
@@ -0,0 +1,80 @@
1
+ import { MediaCloudErrors } from "../types/errors.mjs";
2
+ import { useErrorHandler } from "../hooks/useErrorHandler.mjs";
3
+ import { buildS3Path } from "../utils/buildS3Path.mjs";
4
+
5
+ //#region src/collectionHooks/folderAfterChange.ts
6
+ const folderAfterChangeHook = async ({ doc, previousDoc, req }) => {
7
+ const { throwError } = useErrorHandler();
8
+ if (doc.name === previousDoc.name) return doc;
9
+ const { custom } = req?.payload?.config ?? {};
10
+ const collectionSlug = custom?.mediaCloud?.options?.collection ?? "media";
11
+ const foldersSlug = typeof req.payload.config.folders === "object" ? req.payload.config.folders.slug : "payload-folders";
12
+ let populatedFolder;
13
+ try {
14
+ populatedFolder = await req.payload.findByID({
15
+ id: doc.id,
16
+ collection: foldersSlug,
17
+ select: {
18
+ name: true,
19
+ folder: true
20
+ },
21
+ depth: 20
22
+ });
23
+ } catch (error) {
24
+ throwError({
25
+ ...MediaCloudErrors.FOLDER_FETCH_ERROR,
26
+ cause: error
27
+ });
28
+ return doc;
29
+ }
30
+ if (!populatedFolder) return doc;
31
+ const newPrefix = buildS3Path(populatedFolder);
32
+ const oldPrefix = buildS3Path({
33
+ ...populatedFolder,
34
+ name: previousDoc.name
35
+ });
36
+ if (!oldPrefix || !newPrefix || oldPrefix === newPrefix) return doc;
37
+ let page = 1;
38
+ let hasNextPage = true;
39
+ while (hasNextPage) {
40
+ let mediaItems;
41
+ try {
42
+ mediaItems = await req.payload.find({
43
+ collection: collectionSlug,
44
+ where: { path: { contains: oldPrefix } },
45
+ limit: 100,
46
+ page,
47
+ depth: 0
48
+ });
49
+ } catch (error) {
50
+ throwError({
51
+ ...MediaCloudErrors.FOLDER_FETCH_ERROR,
52
+ cause: error
53
+ });
54
+ break;
55
+ }
56
+ const itemsToUpdate = mediaItems.docs.filter((item) => item.path?.startsWith(`${oldPrefix}/`));
57
+ await Promise.all(itemsToUpdate.map(async (item) => {
58
+ const newPath = item.path.replace(oldPrefix, newPrefix);
59
+ try {
60
+ await req.payload.update({
61
+ collection: collectionSlug,
62
+ id: item.id,
63
+ data: { path: newPath }
64
+ });
65
+ } catch (error) {
66
+ throwError({
67
+ ...MediaCloudErrors.PATH_UPDATE_ERROR,
68
+ cause: error
69
+ });
70
+ }
71
+ }));
72
+ hasNextPage = mediaItems.hasNextPage;
73
+ page++;
74
+ }
75
+ return doc;
76
+ };
77
+
78
+ //#endregion
79
+ export { folderAfterChangeHook };
80
+ //# sourceMappingURL=folderAfterChange.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"folderAfterChange.mjs","names":["folderAfterChangeHook: CollectionAfterChangeHook","collectionSlug: string"],"sources":["../../src/collectionHooks/folderAfterChange.ts"],"sourcesContent":["import { MediaCloudErrors } from '../types/errors'\nimport { useErrorHandler } from '../hooks/useErrorHandler'\nimport { buildS3Path } from '../utils/buildS3Path'\n\nimport type { CollectionAfterChangeHook } from 'payload'\n\nexport const folderAfterChangeHook: CollectionAfterChangeHook = async ({\n doc,\n previousDoc,\n req,\n}) => {\n const { throwError } = useErrorHandler()\n\n // Only proceed if the folder name has actually changed\n if (doc.name === previousDoc.name) {\n return doc\n }\n\n const { custom } = req?.payload?.config ?? {}\n const collectionSlug: string =\n custom?.mediaCloud?.options?.collection ?? 'media'\n\n const foldersSlug =\n typeof req.payload.config.folders === 'object'\n ? req.payload.config.folders.slug\n : 'payload-folders'\n\n // Fetch the renamed folder with fully populated parent folders so we can\n // reconstruct the full path prefix (e.g. \"grandparent/parent/thisFolder\")\n let populatedFolder\n try {\n populatedFolder = await req.payload.findByID({\n id: doc.id,\n collection: foldersSlug,\n select: { name: true, folder: true },\n depth: 20,\n })\n } catch (error) {\n throwError({ ...MediaCloudErrors.FOLDER_FETCH_ERROR, cause: error })\n return doc\n }\n\n if (!populatedFolder) {\n return doc\n }\n\n // Build the new path prefix using the current (already renamed) folder document\n const newPrefix = buildS3Path(populatedFolder)\n\n // Build the old path prefix by substituting the previous name into the same\n // folder structure – the parent chain is identical, only the leaf name changed\n const oldPrefix = buildS3Path({ ...populatedFolder, name: previousDoc.name })\n\n if (!oldPrefix || !newPrefix || oldPrefix === newPrefix) {\n return doc\n }\n\n // Find and update all media items whose path starts with the old folder prefix.\n // Pagination ensures every item is processed regardless of collection size.\n let page = 1\n let hasNextPage = true\n\n while (hasNextPage) {\n let mediaItems\n try {\n mediaItems = await req.payload.find({\n collection: collectionSlug,\n where: {\n path: {\n contains: oldPrefix,\n },\n },\n limit: 100,\n page,\n depth: 0,\n })\n } catch (error) {\n throwError({ ...MediaCloudErrors.FOLDER_FETCH_ERROR, cause: error })\n break\n }\n\n // Only update items whose path starts with `oldPrefix` exactly\n const itemsToUpdate = mediaItems.docs.filter((item) =>\n item.path?.startsWith(`${oldPrefix}/`)\n )\n\n await Promise.all(\n itemsToUpdate.map(async (item) => {\n const newPath = (item.path as string).replace(oldPrefix, newPrefix)\n\n try {\n await req.payload.update({\n collection: collectionSlug,\n id: item.id,\n data: { path: newPath },\n })\n } catch (error) {\n throwError({ ...MediaCloudErrors.PATH_UPDATE_ERROR, cause: error })\n }\n })\n )\n\n hasNextPage = mediaItems.hasNextPage\n page++\n }\n\n return doc\n}\n"],"mappings":";;;;;AAMA,MAAaA,wBAAmD,OAAO,EACrE,KACA,aACA,UACI;CACJ,MAAM,EAAE,eAAe,iBAAiB;AAGxC,KAAI,IAAI,SAAS,YAAY,KAC3B,QAAO;CAGT,MAAM,EAAE,WAAW,KAAK,SAAS,UAAU,EAAE;CAC7C,MAAMC,iBACJ,QAAQ,YAAY,SAAS,cAAc;CAE7C,MAAM,cACJ,OAAO,IAAI,QAAQ,OAAO,YAAY,WAClC,IAAI,QAAQ,OAAO,QAAQ,OAC3B;CAIN,IAAI;AACJ,KAAI;AACF,oBAAkB,MAAM,IAAI,QAAQ,SAAS;GAC3C,IAAI,IAAI;GACR,YAAY;GACZ,QAAQ;IAAE,MAAM;IAAM,QAAQ;IAAM;GACpC,OAAO;GACR,CAAC;UACK,OAAO;AACd,aAAW;GAAE,GAAG,iBAAiB;GAAoB,OAAO;GAAO,CAAC;AACpE,SAAO;;AAGT,KAAI,CAAC,gBACH,QAAO;CAIT,MAAM,YAAY,YAAY,gBAAgB;CAI9C,MAAM,YAAY,YAAY;EAAE,GAAG;EAAiB,MAAM,YAAY;EAAM,CAAC;AAE7E,KAAI,CAAC,aAAa,CAAC,aAAa,cAAc,UAC5C,QAAO;CAKT,IAAI,OAAO;CACX,IAAI,cAAc;AAElB,QAAO,aAAa;EAClB,IAAI;AACJ,MAAI;AACF,gBAAa,MAAM,IAAI,QAAQ,KAAK;IAClC,YAAY;IACZ,OAAO,EACL,MAAM,EACJ,UAAU,WACX,EACF;IACD,OAAO;IACP;IACA,OAAO;IACR,CAAC;WACK,OAAO;AACd,cAAW;IAAE,GAAG,iBAAiB;IAAoB,OAAO;IAAO,CAAC;AACpE;;EAIF,MAAM,gBAAgB,WAAW,KAAK,QAAQ,SAC5C,KAAK,MAAM,WAAW,GAAG,UAAU,GAAG,CACvC;AAED,QAAM,QAAQ,IACZ,cAAc,IAAI,OAAO,SAAS;GAChC,MAAM,UAAW,KAAK,KAAgB,QAAQ,WAAW,UAAU;AAEnE,OAAI;AACF,UAAM,IAAI,QAAQ,OAAO;KACvB,YAAY;KACZ,IAAI,KAAK;KACT,MAAM,EAAE,MAAM,SAAS;KACxB,CAAC;YACK,OAAO;AACd,eAAW;KAAE,GAAG,iBAAiB;KAAmB,OAAO;KAAO,CAAC;;IAErE,CACH;AAED,gBAAc,WAAW;AACzB;;AAGF,QAAO"}
@@ -0,0 +1,7 @@
1
+ import { CollectionAfterChangeHook } from "payload";
2
+
3
+ //#region src/collectionHooks/thumbnailAfterChange.d.ts
4
+ declare const thumbnailAfterChangeHook: CollectionAfterChangeHook;
5
+ //#endregion
6
+ export { thumbnailAfterChangeHook };
7
+ //# sourceMappingURL=thumbnailAfterChange.d.mts.map
@@ -3,8 +3,8 @@ import { useErrorHandler } from "../hooks/useErrorHandler.mjs";
3
3
  import { buildThumbnailURL } from "../utils/buildThumbnailURL.mjs";
4
4
  import { s3Store } from "../plugin.mjs";
5
5
 
6
- //#region src/collectionHooks/thumbnail.ts
7
- const thumbnailHook = async ({ collection, doc, previousDoc, req }) => {
6
+ //#region src/collectionHooks/thumbnailAfterChange.ts
7
+ const thumbnailAfterChangeHook = async ({ collection, doc, previousDoc, req }) => {
8
8
  const { throwError } = useErrorHandler();
9
9
  if (req.context?._mediaCloudPluginInternal) return doc;
10
10
  if (!doc.thumbnail || previousDoc?.path !== doc.path) try {
@@ -34,5 +34,5 @@ const thumbnailHook = async ({ collection, doc, previousDoc, req }) => {
34
34
  };
35
35
 
36
36
  //#endregion
37
- export { thumbnailHook };
38
- //# sourceMappingURL=thumbnail.mjs.map
37
+ export { thumbnailAfterChangeHook };
38
+ //# sourceMappingURL=thumbnailAfterChange.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thumbnailAfterChange.mjs","names":["thumbnailAfterChangeHook: CollectionAfterChangeHook"],"sources":["../../src/collectionHooks/thumbnailAfterChange.ts"],"sourcesContent":["import { buildThumbnailURL } from '../utils/buildThumbnailURL'\nimport { s3Store } from '../plugin'\nimport { MediaCloudErrors } from '../types/errors'\nimport { useErrorHandler } from '../hooks/useErrorHandler'\n\nimport type { CollectionAfterChangeHook } from 'payload'\n\nexport const thumbnailAfterChangeHook: CollectionAfterChangeHook = async ({\n collection,\n doc,\n previousDoc,\n req,\n}) => {\n const { throwError } = useErrorHandler()\n\n // Skip if this is an internal update to prevent infinite loop\n if (req.context?._mediaCloudPluginInternal) {\n return doc\n }\n\n // Handle thumbnail\n if (!doc.thumbnail || previousDoc?.path !== doc.path) {\n try {\n const thumbnail = buildThumbnailURL({\n storage: doc.storage,\n playbackId: doc.storage === 'mux' ? doc.mux.playbackId : undefined,\n s3Key: doc.storage === 's3' ? (doc.path ?? doc.filename) : undefined,\n s3Store,\n })\n\n req.context = { ...req.context }\n req.context._mediaCloudPluginInternal = true\n\n const update = await req.payload.update({\n collection: collection.slug,\n id: doc.id,\n data: { thumbnail },\n req,\n })\n\n delete req.context._mediaCloudPluginInternal\n\n return update\n } catch (error) {\n throwError({\n ...MediaCloudErrors.THUMBNAIL_GENERATION_ERROR,\n cause: error,\n })\n }\n }\n\n return doc\n}\n"],"mappings":";;;;;;AAOA,MAAaA,2BAAsD,OAAO,EACxE,YACA,KACA,aACA,UACI;CACJ,MAAM,EAAE,eAAe,iBAAiB;AAGxC,KAAI,IAAI,SAAS,0BACf,QAAO;AAIT,KAAI,CAAC,IAAI,aAAa,aAAa,SAAS,IAAI,KAC9C,KAAI;EACF,MAAM,YAAY,kBAAkB;GAClC,SAAS,IAAI;GACb,YAAY,IAAI,YAAY,QAAQ,IAAI,IAAI,aAAa;GACzD,OAAO,IAAI,YAAY,OAAQ,IAAI,QAAQ,IAAI,WAAY;GAC3D;GACD,CAAC;AAEF,MAAI,UAAU,EAAE,GAAG,IAAI,SAAS;AAChC,MAAI,QAAQ,4BAA4B;EAExC,MAAM,SAAS,MAAM,IAAI,QAAQ,OAAO;GACtC,YAAY,WAAW;GACvB,IAAI,IAAI;GACR,MAAM,EAAE,WAAW;GACnB;GACD,CAAC;AAEF,SAAO,IAAI,QAAQ;AAEnB,SAAO;UACA,OAAO;AACd,aAAW;GACT,GAAG,iBAAiB;GACpB,OAAO;GACR,CAAC;;AAIN,QAAO"}
@@ -8,7 +8,7 @@ import { storageField } from "../fields/storage.mjs";
8
8
  import { muxField } from "../fields/mux.mjs";
9
9
  import { beforeChangeHook } from "../collectionHooks/beforeChange.mjs";
10
10
  import { afterChangeHook } from "../collectionHooks/afterChange.mjs";
11
- import { thumbnailHook } from "../collectionHooks/thumbnail.mjs";
11
+ import { thumbnailAfterChangeHook } from "../collectionHooks/thumbnailAfterChange.mjs";
12
12
 
13
13
  //#region src/collections/mediaCollection.ts
14
14
  /**
@@ -50,7 +50,7 @@ function getMediaCollection(args) {
50
50
  ],
51
51
  hooks: {
52
52
  beforeChange: [beforeChangeHook],
53
- afterChange: [afterChangeHook, thumbnailHook]
53
+ afterChange: [afterChangeHook, thumbnailAfterChangeHook]
54
54
  }
55
55
  };
56
56
  if (!baseCollection) return config;
@@ -1 +1 @@
1
- {"version":3,"file":"mediaCollection.mjs","names":["config: CollectionConfig"],"sources":["../../src/collections/mediaCollection.ts"],"sourcesContent":["import { thumbnailField } from '../fields/thumbnail'\nimport { pathField } from '../fields/path'\nimport { filenameField } from '../fields/filename'\nimport { altField } from '../fields/alt'\nimport { widthField } from '../fields/width'\nimport { heightField } from '../fields/height'\nimport { storageField } from '../fields/storage'\nimport { muxField } from '../fields/mux'\n\nimport { beforeChangeHook } from '../collectionHooks/beforeChange'\nimport { afterChangeHook } from '../collectionHooks/afterChange'\nimport { thumbnailHook } from '../collectionHooks/thumbnail'\n\nimport type { CollectionConfig } from 'payload'\nimport type { Document } from 'payload'\nimport type { MediaCloudPluginOptions } from '../types/index'\n\ninterface GetMediaCollectionArgs {\n view: MediaCloudPluginOptions['view']\n baseCollection?: CollectionConfig\n}\n\n/**\n * Creates a media collection configuration for Payload CMS\n * @param args - Arguments including the S3Store instance\n * @returns A configured Payload collection for media files\n */\nexport function getMediaCollection(\n args: GetMediaCollectionArgs\n): CollectionConfig {\n const { baseCollection, view } = args\n\n const hooks: CollectionConfig['hooks'] = {\n beforeChange: [beforeChangeHook],\n afterChange: [afterChangeHook, thumbnailHook],\n }\n\n // Override list view to use grid view if specified\n const components =\n view === 'grid'\n ? {\n views: {\n list: {\n Component: '@maas/payload-plugin-media-cloud/components#GridView',\n },\n },\n }\n : undefined\n\n const config: CollectionConfig = {\n slug: 'media',\n access: {\n read: () => true,\n delete: () => true,\n },\n admin: {\n components,\n group: 'Media Cloud',\n pagination: {\n defaultLimit: 50,\n },\n },\n upload: {\n crop: false,\n displayPreview: true,\n hideRemoveFile: true,\n adminThumbnail({ doc }: { doc: Document }) {\n return doc.thumbnail ?? null\n },\n },\n fields: [\n thumbnailField,\n pathField,\n altField,\n {\n type: 'row',\n fields: [widthField, heightField],\n },\n storageField,\n muxField,\n ],\n hooks: hooks,\n }\n\n if (!baseCollection) {\n return config\n }\n\n return {\n ...baseCollection,\n slug: baseCollection.slug ?? config.slug,\n admin: {\n ...(config.admin ?? {}),\n ...(baseCollection.admin ?? {}),\n },\n access: {\n ...(config.access ?? {}),\n ...(baseCollection.access ?? {}),\n },\n upload: config.upload,\n fields: [...(baseCollection.fields ?? []), ...config.fields, filenameField],\n hooks: {\n ...baseCollection.hooks,\n beforeChange: [\n ...(baseCollection.hooks?.beforeChange ?? []),\n ...(config.hooks?.beforeChange ?? []),\n ],\n afterChange: [\n ...(baseCollection.hooks?.afterChange ?? []),\n ...(config.hooks?.afterChange ?? []),\n ],\n },\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AA2BA,SAAgB,mBACd,MACkB;CAClB,MAAM,EAAE,gBAAgB,SAAS;CAmBjC,MAAMA,SAA2B;EAC/B,MAAM;EACN,QAAQ;GACN,YAAY;GACZ,cAAc;GACf;EACD,OAAO;GACL,YAjBF,SAAS,SACL,EACE,OAAO,EACL,MAAM,EACJ,WAAW,wDACZ,EACF,EACF,GACD;GAUF,OAAO;GACP,YAAY,EACV,cAAc,IACf;GACF;EACD,QAAQ;GACN,MAAM;GACN,gBAAgB;GAChB,gBAAgB;GAChB,eAAe,EAAE,OAA0B;AACzC,WAAO,IAAI,aAAa;;GAE3B;EACD,QAAQ;GACN;GACA;GACA;GACA;IACE,MAAM;IACN,QAAQ,CAAC,YAAY,YAAY;IAClC;GACD;GACA;GACD;EACD,OAjDuC;GACvC,cAAc,CAAC,iBAAiB;GAChC,aAAa,CAAC,iBAAiB,cAAc;GAC9C;EA+CA;AAED,KAAI,CAAC,eACH,QAAO;AAGT,QAAO;EACL,GAAG;EACH,MAAM,eAAe,QAAQ,OAAO;EACpC,OAAO;GACL,GAAI,OAAO,SAAS,EAAE;GACtB,GAAI,eAAe,SAAS,EAAE;GAC/B;EACD,QAAQ;GACN,GAAI,OAAO,UAAU,EAAE;GACvB,GAAI,eAAe,UAAU,EAAE;GAChC;EACD,QAAQ,OAAO;EACf,QAAQ;GAAC,GAAI,eAAe,UAAU,EAAE;GAAG,GAAG,OAAO;GAAQ;GAAc;EAC3E,OAAO;GACL,GAAG,eAAe;GAClB,cAAc,CACZ,GAAI,eAAe,OAAO,gBAAgB,EAAE,EAC5C,GAAI,OAAO,OAAO,gBAAgB,EAAE,CACrC;GACD,aAAa,CACX,GAAI,eAAe,OAAO,eAAe,EAAE,EAC3C,GAAI,OAAO,OAAO,eAAe,EAAE,CACpC;GACF;EACF"}
1
+ {"version":3,"file":"mediaCollection.mjs","names":["config: CollectionConfig"],"sources":["../../src/collections/mediaCollection.ts"],"sourcesContent":["import { thumbnailField } from '../fields/thumbnail'\nimport { pathField } from '../fields/path'\nimport { filenameField } from '../fields/filename'\nimport { altField } from '../fields/alt'\nimport { widthField } from '../fields/width'\nimport { heightField } from '../fields/height'\nimport { storageField } from '../fields/storage'\nimport { muxField } from '../fields/mux'\n\nimport { beforeChangeHook } from '../collectionHooks/beforeChange'\nimport { afterChangeHook } from '../collectionHooks/afterChange'\nimport { thumbnailAfterChangeHook } from '../collectionHooks/thumbnailAfterChange'\n\nimport type { CollectionConfig } from 'payload'\nimport type { Document } from 'payload'\nimport type { MediaCloudPluginOptions } from '../types/index'\n\ninterface GetMediaCollectionArgs {\n view: MediaCloudPluginOptions['view']\n baseCollection?: CollectionConfig\n}\n\n/**\n * Creates a media collection configuration for Payload CMS\n * @param args - Arguments including the S3Store instance\n * @returns A configured Payload collection for media files\n */\nexport function getMediaCollection(\n args: GetMediaCollectionArgs\n): CollectionConfig {\n const { baseCollection, view } = args\n\n const hooks: CollectionConfig['hooks'] = {\n beforeChange: [beforeChangeHook],\n afterChange: [afterChangeHook, thumbnailAfterChangeHook],\n }\n\n // Override list view to use grid view if specified\n const components =\n view === 'grid'\n ? {\n views: {\n list: {\n Component: '@maas/payload-plugin-media-cloud/components#GridView',\n },\n },\n }\n : undefined\n\n const config: CollectionConfig = {\n slug: 'media',\n access: {\n read: () => true,\n delete: () => true,\n },\n admin: {\n components,\n group: 'Media Cloud',\n pagination: {\n defaultLimit: 50,\n },\n },\n upload: {\n crop: false,\n displayPreview: true,\n hideRemoveFile: true,\n adminThumbnail({ doc }: { doc: Document }) {\n return doc.thumbnail ?? null\n },\n },\n fields: [\n thumbnailField,\n pathField,\n altField,\n {\n type: 'row',\n fields: [widthField, heightField],\n },\n storageField,\n muxField,\n ],\n hooks: hooks,\n }\n\n if (!baseCollection) {\n return config\n }\n\n return {\n ...baseCollection,\n slug: baseCollection.slug ?? config.slug,\n admin: {\n ...(config.admin ?? {}),\n ...(baseCollection.admin ?? {}),\n },\n access: {\n ...(config.access ?? {}),\n ...(baseCollection.access ?? {}),\n },\n upload: config.upload,\n fields: [...(baseCollection.fields ?? []), ...config.fields, filenameField],\n hooks: {\n ...baseCollection.hooks,\n beforeChange: [\n ...(baseCollection.hooks?.beforeChange ?? []),\n ...(config.hooks?.beforeChange ?? []),\n ],\n afterChange: [\n ...(baseCollection.hooks?.afterChange ?? []),\n ...(config.hooks?.afterChange ?? []),\n ],\n },\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AA2BA,SAAgB,mBACd,MACkB;CAClB,MAAM,EAAE,gBAAgB,SAAS;CAmBjC,MAAMA,SAA2B;EAC/B,MAAM;EACN,QAAQ;GACN,YAAY;GACZ,cAAc;GACf;EACD,OAAO;GACL,YAjBF,SAAS,SACL,EACE,OAAO,EACL,MAAM,EACJ,WAAW,wDACZ,EACF,EACF,GACD;GAUF,OAAO;GACP,YAAY,EACV,cAAc,IACf;GACF;EACD,QAAQ;GACN,MAAM;GACN,gBAAgB;GAChB,gBAAgB;GAChB,eAAe,EAAE,OAA0B;AACzC,WAAO,IAAI,aAAa;;GAE3B;EACD,QAAQ;GACN;GACA;GACA;GACA;IACE,MAAM;IACN,QAAQ,CAAC,YAAY,YAAY;IAClC;GACD;GACA;GACD;EACD,OAjDuC;GACvC,cAAc,CAAC,iBAAiB;GAChC,aAAa,CAAC,iBAAiB,yBAAyB;GACzD;EA+CA;AAED,KAAI,CAAC,eACH,QAAO;AAGT,QAAO;EACL,GAAG;EACH,MAAM,eAAe,QAAQ,OAAO;EACpC,OAAO;GACL,GAAI,OAAO,SAAS,EAAE;GACtB,GAAI,eAAe,SAAS,EAAE;GAC/B;EACD,QAAQ;GACN,GAAI,OAAO,UAAU,EAAE;GACvB,GAAI,eAAe,UAAU,EAAE;GAChC;EACD,QAAQ,OAAO;EACf,QAAQ;GAAC,GAAI,eAAe,UAAU,EAAE;GAAG,GAAG,OAAO;GAAQ;GAAc;EAC3E,OAAO;GACL,GAAG,eAAe;GAClB,cAAc,CACZ,GAAI,eAAe,OAAO,gBAAgB,EAAE,EAC5C,GAAI,OAAO,OAAO,gBAAgB,EAAE,CACrC;GACD,aAAa,CACX,GAAI,eAAe,OAAO,eAAe,EAAE,EAC3C,GAAI,OAAO,OAAO,eAAe,EAAE,CACpC;GACF;EACF"}
@@ -171,16 +171,8 @@ function UploadHandler(props) {
171
171
  const mimeType = await getMimeType(file);
172
172
  const allowedMimeTypes = pluginOptions.limits?.mimeTypes ?? [];
173
173
  magicError.assert(mimeType, MediaCloudErrors.FILE_TYPE_UNKNOWN);
174
- if (!allowedMimeTypes.some((allowed) => allowed.endsWith("/*") ? mimeType?.startsWith(allowed.replace("/*", "/")) : mimeType === allowed)) {
175
- throwError(MediaCloudErrors.FILE_TYPE_NOT_ALLOWED);
176
- onResolve(null);
177
- return;
178
- }
179
- if (file.size > pluginOptions.limits.fileSize) {
180
- throwError(MediaCloudErrors.FILE_SIZE_EXCEEDED);
181
- onResolve(null);
182
- return;
183
- }
174
+ if (!allowedMimeTypes.some((allowed) => allowed.endsWith("/*") ? mimeType?.startsWith(allowed.replace("/*", "/")) : mimeType === allowed)) throwError(MediaCloudErrors.FILE_TYPE_NOT_ALLOWED);
175
+ if (file.size > pluginOptions.limits.fileSize) throwError(MediaCloudErrors.FILE_SIZE_EXCEEDED);
184
176
  let mappedFile = file;
185
177
  try {
186
178
  const sanitizedFilename = sanitizeFilename(file.name);
@@ -202,8 +194,6 @@ function UploadHandler(props) {
202
194
  ...MediaCloudErrors.NAMING_FUNCTION_ERROR,
203
195
  cause: error
204
196
  });
205
- onResolve(null);
206
- return;
207
197
  }
208
198
  try {
209
199
  const uploadArgs = {
@@ -226,12 +216,11 @@ function UploadHandler(props) {
226
216
  ...MediaCloudErrors.UPLOAD_HANDLER_ERROR,
227
217
  cause: error
228
218
  });
229
- onResolve(null);
230
219
  }
231
220
  }
232
- return new Promise((resolve) => {
221
+ return new Promise((resolve, reject) => {
233
222
  magicError.assert(queue, MediaCloudErrors.UPLOAD_QUEUE_NOT_INITIALIZED);
234
- queue.add(() => upload(resolve));
223
+ queue.add(() => upload(resolve)).catch(reject);
235
224
  });
236
225
  } })(props);
237
226
  }
@@ -1 +1 @@
1
- {"version":3,"file":"uploadHandler.mjs","names":["PQueue","upchunk","tus","createClientUploadHandler","MediaCloudErrors","MediaCloudLogs","useMediaCloudEmitter","isVideo","getMimeType","generateUniqueFilename","sanitizeFilename","useMagicError","queue","MUX_CHUNK_SIZE","TUS_CHUNK_SIZE","TUS_RETRY_DELAYS","magicError","prefix","throwError","log","emitter","muxUpload","args","file","serverURL","apiRoute","mimeType","onResolve","endpoint","filename","name","response","fetch","body","JSON","stringify","credentials","method","headers","url","uploadId","json","uploader","createUpload","chunkSize","emit","polling","pollingUrl","storage","Promise","resolve","reject","on","error","MUX_UPLOAD_ERROR","cause","progress","detail","MUX_DIRECT_UPLOAD_ERROR","tusUpload","filetype","type","filesize","size","toString","uploadUrlAvailable","upload","Upload","retryDelays","storeFingerprintForResuming","metadata","contentType","contentLength","contentDisposition","onError","navigator","sendBeacon","message","TUS_UPLOAD_ERROR","onProgress","bytesUploaded","bytesTotal","percentage","Math","round","onSuccess","console","onUploadUrlAvailable","start","UploadHandler","props","handler","updateFilename","extra","pluginOptions","concurrency","limits","allowedMimeTypes","mimeTypes","assert","FILE_TYPE_UNKNOWN","mimeTypeMatches","some","allowed","endsWith","startsWith","replace","FILE_TYPE_NOT_ALLOWED","fileSize","FILE_SIZE_EXCEEDED","mappedFile","sanitizedFilename","status","S3_STORE_FILE_FOUND","S3_STORE_FILE_NOT_FOUND","newFilename","File","NAMING_FUNCTION_ERROR","uploadArgs","storageForMimeType","Object","prototype","hasOwnProperty","call","undefined","isVideoFile","storeOnMux","UPLOAD_HANDLER_ERROR","UPLOAD_QUEUE_NOT_INITIALIZED","add"],"sources":["../../../src/components/uploadHandler/uploadHandler.tsx"],"sourcesContent":["'use client'\n\nimport PQueue from 'p-queue'\n\nimport * as upchunk from '@mux/upchunk'\nimport * as tus from 'tus-js-client'\n\nimport { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client'\n\nimport { MediaCloudErrors, MediaCloudLogs } from '../../types/errors'\nimport { useMediaCloudEmitter } from '../../hooks/useMediaCloudEmitter'\nimport {\n isVideo,\n getMimeType,\n generateUniqueFilename,\n sanitizeFilename,\n} from '../../utils/file'\n\nimport type {\n MediaCloudPluginOptions,\n Storage,\n MimeType,\n RequireAllNested,\n} from '../../types/index'\nimport type { ReactNode } from 'react'\nimport type { UploadCollectionSlug } from 'payload'\nimport { useMagicError, UseMagicErrorReturn } from '@maas/error-handler'\n\ninterface UploadResult {\n storage: Storage\n mimeType: MimeType\n filename: string\n uploadId?: string\n}\n\ninterface UploadArgs {\n serverURL: string\n apiRoute: string\n file: File\n mimeType: MimeType\n prefix?: string\n onResolve: (result: UploadResult | null) => void\n}\n\ninterface MuxCreateUploadResponse {\n url: string\n uploadId: string\n filename: string\n}\n\nlet queue: PQueue | undefined\n\nconst MUX_CHUNK_SIZE = 30720\nconst TUS_CHUNK_SIZE = 1024 * 1024\nconst TUS_RETRY_DELAYS = [0, 1000, 2000, 5000]\n\nconst magicError: UseMagicErrorReturn = useMagicError({\n prefix: 'PLUGIN-MEDIA-CLOUD',\n})\n\nconst { throwError, log } = magicError\nconst emitter = useMediaCloudEmitter()\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<void> {\n const { file, serverURL, apiRoute, mimeType, onResolve } = args\n\n const endpoint = `${serverURL}${apiRoute}`\n const filename = file.name\n\n try {\n // Request upload URL from Mux\n const response = await fetch(`${endpoint}/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: `${endpoint}/mux/asset`,\n })\n\n // Update collection entry\n await onResolve({\n storage: 'mux',\n filename,\n uploadId,\n mimeType,\n })\n\n return new Promise((resolve, reject) => {\n // Set up event handlers\n uploader.on('error', function (error) {\n emitter.emit('removeUpload', { uploadId })\n throwError({ ...MediaCloudErrors.MUX_UPLOAD_ERROR, cause: error })\n reject(error)\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 resolve()\n })\n })\n } catch {\n throwError(MediaCloudErrors.MUX_DIRECT_UPLOAD_ERROR)\n emitter.emit('uploadError', { filename, error: 'Video upload failed' })\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<void> {\n const { apiRoute, serverURL, file, prefix = '', mimeType, onResolve } = args\n\n const filename = file.name\n const filetype = file.type\n const filesize = file.size.toString()\n\n const endpoint = `${serverURL}${apiRoute}/uploads`\n\n // Flag to prevent multiple onUploadUrlAvailable calls\n let uploadUrlAvailable = false\n\n return new Promise((resolve, reject) => {\n const upload = new tus.Upload(file, {\n endpoint,\n retryDelays: TUS_RETRY_DELAYS,\n chunkSize: TUS_CHUNK_SIZE,\n storeFingerprintForResuming: false,\n metadata: {\n filetype,\n filesize,\n filename,\n prefix,\n contentType: filetype,\n contentLength: filesize,\n contentDisposition: 'inline',\n },\n onError(error) {\n // Clean up\n navigator.sendBeacon(\n `${endpoint}/cleanup`,\n JSON.stringify({ filename })\n )\n\n // Inform user\n emitter.emit('uploadError', { filename, error: error.message })\n emitter.emit('removeUpload', { filename })\n throwError({ ...MediaCloudErrors.TUS_UPLOAD_ERROR, cause: error })\n reject(error)\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 async onSuccess() {\n try {\n // Mark upload as complete in UI\n emitter.emit('uploadComplete', { filename })\n\n // Trigger post upload processing\n await fetch(`${endpoint}/${filename}/process`)\n\n // Move file to correct folder\n await fetch(`${endpoint}/${filename}/folder`)\n } catch (error) {\n console.error(error)\n } finally {\n resolve()\n }\n },\n onUploadUrlAvailable: async function () {\n try {\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 onResolve({\n filename,\n mimeType,\n storage: 's3',\n })\n } catch (error) {\n // Handled elsewhere\n console.error(error)\n }\n },\n })\n\n upload.start()\n })\n}\n\ninterface Extra {\n pluginOptions: RequireAllNested<MediaCloudPluginOptions>\n}\n\ntype ClientUploadHandlerProps<T extends Record<string, unknown>> = {\n children: ReactNode\n collectionSlug: UploadCollectionSlug\n enabled?: boolean\n extra: T\n prefix?: string\n serverHandlerPath: `/${string}`\n}\n\nexport function UploadHandler(\n props: ClientUploadHandlerProps<{ pluginOptions: MediaCloudPluginOptions }>\n) {\n return createClientUploadHandler({\n handler: async function (args) {\n const { serverURL, apiRoute, file, prefix, updateFilename, extra } = args\n const { pluginOptions } = extra as unknown as Extra\n\n // Initialize upload queue\n if (!queue) {\n queue = new PQueue({\n concurrency: pluginOptions.limits.concurrency,\n })\n }\n\n // Update concurrency\n if (queue && queue.concurrency !== pluginOptions.limits.concurrency) {\n queue.concurrency = pluginOptions.limits.concurrency\n }\n\n async function upload(onResolve: UploadArgs['onResolve']) {\n // Check mime type\n const mimeType = await getMimeType(file)\n const allowedMimeTypes = pluginOptions.limits?.mimeTypes ?? []\n\n magicError.assert(mimeType, MediaCloudErrors.FILE_TYPE_UNKNOWN)\n\n const mimeTypeMatches = allowedMimeTypes.some((allowed) =>\n allowed.endsWith('/*')\n ? mimeType?.startsWith(allowed.replace('/*', '/'))\n : mimeType === allowed\n )\n\n if (!mimeTypeMatches) {\n throwError(MediaCloudErrors.FILE_TYPE_NOT_ALLOWED)\n onResolve(null)\n return\n }\n\n // Check file size limit\n if (file.size > pluginOptions.limits.fileSize) {\n throwError(MediaCloudErrors.FILE_SIZE_EXCEEDED)\n onResolve(null)\n return\n }\n\n // Clone file\n let mappedFile = file\n\n try {\n // Check if file with same name exists\n const sanitizedFilename = sanitizeFilename(file.name)\n const endpoint = `${serverURL}${apiRoute}/uploads`\n const response = await fetch(\n `${endpoint}/${sanitizedFilename}/exists`\n )\n\n switch (response?.status) {\n case 200:\n log(MediaCloudLogs.S3_STORE_FILE_FOUND)\n break\n case 204:\n log(MediaCloudLogs.S3_STORE_FILE_NOT_FOUND)\n break\n }\n\n // If file exists, generate a cloned file with a unique filename\n // If not, generate a cloned file with a sanitized filename\n const newFilename =\n response?.status === 200\n ? generateUniqueFilename(sanitizedFilename)\n : sanitizedFilename\n\n mappedFile = new File([file], newFilename, {\n type: file.type,\n })\n\n await updateFilename(newFilename)\n } catch (error) {\n throwError({\n ...MediaCloudErrors.NAMING_FUNCTION_ERROR,\n cause: error,\n })\n onResolve(null)\n return\n }\n\n try {\n const uploadArgs: UploadArgs = {\n file: mappedFile,\n serverURL,\n apiRoute,\n mimeType,\n prefix,\n onResolve,\n }\n\n // Check storage mapping for mime type\n type StorageMap = NonNullable<typeof pluginOptions.storage>\n\n const storageForMimeType =\n pluginOptions.storage &&\n Object.prototype.hasOwnProperty.call(\n pluginOptions.storage,\n mimeType\n )\n ? pluginOptions.storage[mimeType as keyof StorageMap]\n : undefined\n\n const isVideoFile = await isVideo(file)\n const storeOnMux =\n pluginOptions.storage?.['video/*'] === 'mux' ||\n storageForMimeType === 'mux'\n\n switch (true) {\n case storeOnMux && isVideoFile:\n return await muxUpload(uploadArgs)\n default:\n return await tusUpload(uploadArgs)\n }\n } catch (error) {\n throwError({\n ...MediaCloudErrors.UPLOAD_HANDLER_ERROR,\n cause: error,\n })\n\n onResolve(null)\n }\n }\n\n return new Promise((resolve) => {\n magicError.assert(queue, MediaCloudErrors.UPLOAD_QUEUE_NOT_INITIALIZED)\n queue.add(() => upload(resolve))\n })\n },\n })(props)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"uploadHandler.mjs","names":["PQueue","upchunk","tus","createClientUploadHandler","MediaCloudErrors","MediaCloudLogs","useMediaCloudEmitter","isVideo","getMimeType","generateUniqueFilename","sanitizeFilename","useMagicError","queue","MUX_CHUNK_SIZE","TUS_CHUNK_SIZE","TUS_RETRY_DELAYS","magicError","prefix","throwError","log","emitter","muxUpload","args","file","serverURL","apiRoute","mimeType","onResolve","endpoint","filename","name","response","fetch","body","JSON","stringify","credentials","method","headers","url","uploadId","json","uploader","createUpload","chunkSize","emit","polling","pollingUrl","storage","Promise","resolve","reject","on","error","MUX_UPLOAD_ERROR","cause","progress","detail","MUX_DIRECT_UPLOAD_ERROR","tusUpload","filetype","type","filesize","size","toString","uploadUrlAvailable","upload","Upload","retryDelays","storeFingerprintForResuming","metadata","contentType","contentLength","contentDisposition","onError","navigator","sendBeacon","message","TUS_UPLOAD_ERROR","onProgress","bytesUploaded","bytesTotal","percentage","Math","round","onSuccess","console","onUploadUrlAvailable","start","UploadHandler","props","handler","updateFilename","extra","pluginOptions","concurrency","limits","allowedMimeTypes","mimeTypes","assert","FILE_TYPE_UNKNOWN","mimeTypeMatches","some","allowed","endsWith","startsWith","replace","FILE_TYPE_NOT_ALLOWED","fileSize","FILE_SIZE_EXCEEDED","mappedFile","sanitizedFilename","status","S3_STORE_FILE_FOUND","S3_STORE_FILE_NOT_FOUND","newFilename","File","NAMING_FUNCTION_ERROR","uploadArgs","storageForMimeType","Object","prototype","hasOwnProperty","call","undefined","isVideoFile","storeOnMux","UPLOAD_HANDLER_ERROR","UPLOAD_QUEUE_NOT_INITIALIZED","add","catch"],"sources":["../../../src/components/uploadHandler/uploadHandler.tsx"],"sourcesContent":["'use client'\n\nimport PQueue from 'p-queue'\n\nimport * as upchunk from '@mux/upchunk'\nimport * as tus from 'tus-js-client'\n\nimport { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client'\n\nimport { MediaCloudErrors, MediaCloudLogs } from '../../types/errors'\nimport { useMediaCloudEmitter } from '../../hooks/useMediaCloudEmitter'\nimport {\n isVideo,\n getMimeType,\n generateUniqueFilename,\n sanitizeFilename,\n} from '../../utils/file'\n\nimport type {\n MediaCloudPluginOptions,\n Storage,\n MimeType,\n RequireAllNested,\n} from '../../types/index'\nimport type { ReactNode } from 'react'\nimport type { UploadCollectionSlug } from 'payload'\nimport { useMagicError, UseMagicErrorReturn } from '@maas/error-handler'\n\ninterface UploadResult {\n storage: Storage\n mimeType: MimeType\n filename: string\n uploadId?: string\n}\n\ninterface UploadArgs {\n serverURL: string\n apiRoute: string\n file: File\n mimeType: MimeType\n prefix?: string\n onResolve: (result: UploadResult | null) => void\n}\n\ninterface MuxCreateUploadResponse {\n url: string\n uploadId: string\n filename: string\n}\n\nlet queue: PQueue | undefined\n\nconst MUX_CHUNK_SIZE = 30720\nconst TUS_CHUNK_SIZE = 1024 * 1024\nconst TUS_RETRY_DELAYS = [0, 1000, 2000, 5000]\n\nconst magicError: UseMagicErrorReturn = useMagicError({\n prefix: 'PLUGIN-MEDIA-CLOUD',\n})\n\nconst { throwError, log } = magicError\nconst emitter = useMediaCloudEmitter()\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<void> {\n const { file, serverURL, apiRoute, mimeType, onResolve } = args\n\n const endpoint = `${serverURL}${apiRoute}`\n const filename = file.name\n\n try {\n // Request upload URL from Mux\n const response = await fetch(`${endpoint}/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: `${endpoint}/mux/asset`,\n })\n\n // Update collection entry\n await onResolve({\n storage: 'mux',\n filename,\n uploadId,\n mimeType,\n })\n\n return new Promise((resolve, reject) => {\n // Set up event handlers\n uploader.on('error', function (error) {\n emitter.emit('removeUpload', { uploadId })\n throwError({ ...MediaCloudErrors.MUX_UPLOAD_ERROR, cause: error })\n reject(error)\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 resolve()\n })\n })\n } catch {\n throwError(MediaCloudErrors.MUX_DIRECT_UPLOAD_ERROR)\n emitter.emit('uploadError', { filename, error: 'Video upload failed' })\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<void> {\n const { apiRoute, serverURL, file, prefix = '', mimeType, onResolve } = args\n\n const filename = file.name\n const filetype = file.type\n const filesize = file.size.toString()\n\n const endpoint = `${serverURL}${apiRoute}/uploads`\n\n // Flag to prevent multiple onUploadUrlAvailable calls\n let uploadUrlAvailable = false\n\n return new Promise((resolve, reject) => {\n const upload = new tus.Upload(file, {\n endpoint,\n retryDelays: TUS_RETRY_DELAYS,\n chunkSize: TUS_CHUNK_SIZE,\n storeFingerprintForResuming: false,\n metadata: {\n filetype,\n filesize,\n filename,\n prefix,\n contentType: filetype,\n contentLength: filesize,\n contentDisposition: 'inline',\n },\n onError(error) {\n // Clean up\n navigator.sendBeacon(\n `${endpoint}/cleanup`,\n JSON.stringify({ filename })\n )\n\n // Inform user\n emitter.emit('uploadError', { filename, error: error.message })\n emitter.emit('removeUpload', { filename })\n throwError({ ...MediaCloudErrors.TUS_UPLOAD_ERROR, cause: error })\n reject(error)\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 async onSuccess() {\n try {\n // Mark upload as complete in UI\n emitter.emit('uploadComplete', { filename })\n\n // Trigger post upload processing\n await fetch(`${endpoint}/${filename}/process`)\n\n // Move file to correct folder\n await fetch(`${endpoint}/${filename}/folder`)\n } catch (error) {\n console.error(error)\n } finally {\n resolve()\n }\n },\n onUploadUrlAvailable: async function () {\n try {\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 onResolve({\n filename,\n mimeType,\n storage: 's3',\n })\n } catch (error) {\n // Handled elsewhere\n console.error(error)\n }\n },\n })\n\n upload.start()\n })\n}\n\ninterface Extra {\n pluginOptions: RequireAllNested<MediaCloudPluginOptions>\n}\n\ntype ClientUploadHandlerProps<T extends Record<string, unknown>> = {\n children: ReactNode\n collectionSlug: UploadCollectionSlug\n enabled?: boolean\n extra: T\n prefix?: string\n serverHandlerPath: `/${string}`\n}\n\nexport function UploadHandler(\n props: ClientUploadHandlerProps<{ pluginOptions: MediaCloudPluginOptions }>\n) {\n return createClientUploadHandler({\n handler: async function (args) {\n const { serverURL, apiRoute, file, prefix, updateFilename, extra } = args\n const { pluginOptions } = extra as unknown as Extra\n\n // Initialize upload queue\n if (!queue) {\n queue = new PQueue({\n concurrency: pluginOptions.limits.concurrency,\n })\n }\n\n // Update concurrency\n if (queue && queue.concurrency !== pluginOptions.limits.concurrency) {\n queue.concurrency = pluginOptions.limits.concurrency\n }\n\n async function upload(onResolve: UploadArgs['onResolve']) {\n // Check mime type\n const mimeType = await getMimeType(file)\n const allowedMimeTypes = pluginOptions.limits?.mimeTypes ?? []\n\n magicError.assert(mimeType, MediaCloudErrors.FILE_TYPE_UNKNOWN)\n\n const mimeTypeMatches = allowedMimeTypes.some((allowed) =>\n allowed.endsWith('/*')\n ? mimeType?.startsWith(allowed.replace('/*', '/'))\n : mimeType === allowed\n )\n\n if (!mimeTypeMatches) {\n throwError(MediaCloudErrors.FILE_TYPE_NOT_ALLOWED)\n }\n\n // Check file size limit\n if (file.size > pluginOptions.limits.fileSize) {\n throwError(MediaCloudErrors.FILE_SIZE_EXCEEDED)\n }\n\n // Clone file\n let mappedFile = file\n\n try {\n // Check if file with same name exists\n const sanitizedFilename = sanitizeFilename(file.name)\n const endpoint = `${serverURL}${apiRoute}/uploads`\n const response = await fetch(\n `${endpoint}/${sanitizedFilename}/exists`\n )\n\n switch (response?.status) {\n case 200:\n log(MediaCloudLogs.S3_STORE_FILE_FOUND)\n break\n case 204:\n log(MediaCloudLogs.S3_STORE_FILE_NOT_FOUND)\n break\n }\n\n // If file exists, generate a cloned file with a unique filename\n // If not, generate a cloned file with a sanitized filename\n const newFilename =\n response?.status === 200\n ? generateUniqueFilename(sanitizedFilename)\n : sanitizedFilename\n\n mappedFile = new File([file], newFilename, {\n type: file.type,\n })\n\n await updateFilename(newFilename)\n } catch (error) {\n throwError({\n ...MediaCloudErrors.NAMING_FUNCTION_ERROR,\n cause: error,\n })\n }\n\n try {\n const uploadArgs: UploadArgs = {\n file: mappedFile,\n serverURL,\n apiRoute,\n mimeType,\n prefix,\n onResolve,\n }\n\n // Check storage mapping for mime type\n type StorageMap = NonNullable<typeof pluginOptions.storage>\n\n const storageForMimeType =\n pluginOptions.storage &&\n Object.prototype.hasOwnProperty.call(\n pluginOptions.storage,\n mimeType\n )\n ? pluginOptions.storage[mimeType as keyof StorageMap]\n : undefined\n\n const isVideoFile = await isVideo(file)\n const storeOnMux =\n pluginOptions.storage?.['video/*'] === 'mux' ||\n storageForMimeType === 'mux'\n\n switch (true) {\n case storeOnMux && isVideoFile:\n return await muxUpload(uploadArgs)\n default:\n return await tusUpload(uploadArgs)\n }\n } catch (error) {\n throwError({\n ...MediaCloudErrors.UPLOAD_HANDLER_ERROR,\n cause: error,\n })\n }\n }\n\n return new Promise((resolve, reject) => {\n magicError.assert(queue, MediaCloudErrors.UPLOAD_QUEUE_NOT_INITIALIZED)\n queue.add(() => upload(resolve)).catch(reject)\n })\n },\n })(props)\n}\n"],"mappings":"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"}
package/dist/plugin.mjs CHANGED
@@ -2,17 +2,24 @@ import { MediaCloudErrors } from "./types/errors.mjs";
2
2
  import { useErrorHandler } from "./hooks/useErrorHandler.mjs";
3
3
  import { getStorageAdapter } from "./adapter/storageAdapter.mjs";
4
4
  import { getMediaCollection } from "./collections/mediaCollection.mjs";
5
+ import { folderAfterChangeHook } from "./collectionHooks/folderAfterChange.mjs";
5
6
  import { createS3Store } from "./tus/stores/s3/index.mjs";
6
7
  import { createMuxClient, createMuxEndpoints } from "./utils/mux.mjs";
7
8
  import { createFileEndpoints } from "./utils/file.mjs";
8
9
  import { createTusEndpoints, createTusServer } from "./utils/tus.mjs";
9
10
  import { defaultOptions } from "./utils/defaultOptions.mjs";
10
- import { defu } from "defu";
11
+ import { createDefu } from "defu";
11
12
  import { cloudStoragePlugin } from "@payloadcms/plugin-cloud-storage";
12
13
  import { initClientUploads } from "@payloadcms/plugin-cloud-storage/utilities";
13
14
 
14
15
  //#region src/plugin.ts
15
16
  const { logError } = useErrorHandler();
17
+ const defu = createDefu((obj, key, value) => {
18
+ if (Array.isArray(value)) {
19
+ obj[key] = value;
20
+ return true;
21
+ }
22
+ });
16
23
  let muxClient = null;
17
24
  let s3Store = null;
18
25
  let tusServer = null;
@@ -92,6 +99,22 @@ function mediaCloudPlugin(options) {
92
99
  clientUploads: true,
93
100
  disableLocalStorage: true
94
101
  } } };
102
+ if (pluginOptions.folders) {
103
+ const existingFoldersConfig = typeof config.folders === "object" && config.folders !== null ? config.folders : {};
104
+ config = {
105
+ ...config,
106
+ folders: {
107
+ ...existingFoldersConfig,
108
+ collectionOverrides: [...existingFoldersConfig.collectionOverrides ?? [], ({ collection }) => ({
109
+ ...collection,
110
+ hooks: {
111
+ ...collection.hooks,
112
+ afterChange: [...collection.hooks?.afterChange ?? [], folderAfterChangeHook]
113
+ }
114
+ })]
115
+ }
116
+ };
117
+ }
95
118
  const mergedConfig = {
96
119
  ...config,
97
120
  admin: {
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.mjs","names":["muxClient: Mux | null","s3Store: S3Store | null","tusServer: Server | null","mergedConfig: Config"],"sources":["../src/plugin.ts"],"sourcesContent":["import Mux from '@mux/mux-node'\nimport { defu } from 'defu'\nimport { cloudStoragePlugin } from '@payloadcms/plugin-cloud-storage'\nimport { initClientUploads } from '@payloadcms/plugin-cloud-storage/utilities'\n\nimport { MediaCloudErrors } from './types/errors'\nimport { getStorageAdapter } from './adapter/storageAdapter'\nimport { getMediaCollection } from './collections/mediaCollection'\nimport { useErrorHandler } from './hooks/useErrorHandler'\nimport { createS3Store } from './tus/stores/s3'\nimport { createMuxClient, createMuxEndpoints } from './utils/mux'\nimport { createTusEndpoints, createTusServer } from './utils/tus'\nimport { createFileEndpoints } from './utils/file'\nimport { defaultOptions } from './utils/defaultOptions'\n\nimport type { Config } from 'payload'\nimport type { Server } from '@tus/server'\nimport type { MediaCloudPluginOptions } from './types'\nimport type { S3Store } from './tus/stores/s3/s3Store'\n\nconst { logError } = useErrorHandler()\n\nlet muxClient: Mux | null = null\nlet s3Store: S3Store | null = null\nlet tusServer: Server | null = null\n\n/**\n * Media Cloud Plugin for Payload CMS\n * @param options Configuration options\n * @returns Payload config function\n */\nexport function mediaCloudPlugin(options: MediaCloudPluginOptions) {\n return function (config: Config): Config {\n // Check if config is invalid or disabled\n if (!options) {\n logError(MediaCloudErrors.PLUGIN_NOT_CONFIGURED.message)\n return config\n }\n\n // Merge user options with default options\n const pluginOptions = defu(options, defaultOptions)\n\n // Check if the plugin is disabled\n if (pluginOptions.enabled === false) {\n return config\n }\n\n /**\n * Helper function to get or create Mux client instance\n * @returns Mux client instance\n */\n function getMuxClient(): Mux {\n return muxClient ?? createMuxClient(pluginOptions.mux)\n }\n\n /**\n * Helper function to get or create S3 store instance\n * @returns S3 store instance\n */\n function getS3Store(): S3Store {\n return s3Store ?? createS3Store(pluginOptions.s3)\n }\n\n /**\n * Helper function to get or create tus server instance\n * @returns TUS server instance\n */\n function getTusServer(): Server {\n return tusServer ?? createTusServer({ getS3Store, pluginOptions })\n }\n\n // Initialize Mux client if configuration is provided\n if (pluginOptions.mux) {\n muxClient = getMuxClient()\n }\n\n // Initialize S3 store and TUS server if configuration is provided\n if (pluginOptions.s3) {\n s3Store = getS3Store()\n tusServer = getTusServer()\n }\n\n // Check if base collection exists\n const baseCollection = config.collections?.find(\n ({ slug }) => slug === pluginOptions.collection\n )\n\n const { view } = pluginOptions\n\n const mediaCollection = getMediaCollection({\n baseCollection,\n view,\n })\n\n // Remove base collection\n // It’ll be replaced with the merged media collection\n if (baseCollection) {\n config = {\n ...config,\n collections:\n config.collections?.filter(\n ({ slug }) => slug !== baseCollection?.slug\n ) ?? [],\n }\n }\n\n initClientUploads({\n config,\n enabled: true,\n clientHandler:\n '@maas/payload-plugin-media-cloud/components#UploadHandler',\n collections: {\n [mediaCollection.slug]: {\n clientUploads: true,\n disableLocalStorage: true,\n prefix: pluginOptions.s3?.prefix ?? '',\n },\n },\n extraClientHandlerProps: () => ({\n pluginOptions,\n }),\n serverHandler: () => {\n return Response.json(\n { message: 'Server handler is not implemented' },\n { status: 501 }\n )\n },\n serverHandlerPath: '/media-cloud/upload',\n })\n\n const cloudStorageConfig = {\n collections: {\n [mediaCollection.slug]: {\n adapter: getStorageAdapter({\n getMuxClient,\n pluginOptions,\n getS3Store,\n }),\n clientUploads: true,\n disableLocalStorage: true,\n },\n },\n }\n\n const mergedConfig: Config = {\n ...config,\n admin: {\n ...config.admin,\n components: {\n ...config.admin?.components,\n providers: [\n ...(config.admin?.components?.providers ?? []),\n '@maas/payload-plugin-media-cloud/components#UploadManagerProvider',\n ],\n },\n },\n collections: [...(config.collections ?? []), mediaCollection],\n endpoints: [\n ...(config.endpoints ?? []),\n ...createTusEndpoints({ getTusServer, getS3Store, pluginOptions }),\n ...createMuxEndpoints({ getMuxClient, pluginOptions }),\n ...createFileEndpoints({ getS3Store, pluginOptions }),\n ],\n custom: {\n ...config.custom,\n mediaCloud: {\n options: {\n enabled: pluginOptions.enabled,\n collection: pluginOptions.collection,\n view: pluginOptions.view,\n storage: pluginOptions.storage,\n folders: pluginOptions.folders,\n limits: pluginOptions.limits,\n },\n },\n },\n }\n\n return cloudStoragePlugin(cloudStorageConfig)(mergedConfig)\n }\n}\n\nexport { s3Store }\n"],"mappings":";;;;;;;;;;;;;;AAoBA,MAAM,EAAE,aAAa,iBAAiB;AAEtC,IAAIA,YAAwB;AAC5B,IAAIC,UAA0B;AAC9B,IAAIC,YAA2B;;;;;;AAO/B,SAAgB,iBAAiB,SAAkC;AACjE,QAAO,SAAU,QAAwB;AAEvC,MAAI,CAAC,SAAS;AACZ,YAAS,iBAAiB,sBAAsB,QAAQ;AACxD,UAAO;;EAIT,MAAM,gBAAgB,KAAK,SAAS,eAAe;AAGnD,MAAI,cAAc,YAAY,MAC5B,QAAO;;;;;EAOT,SAAS,eAAoB;AAC3B,UAAO,aAAa,gBAAgB,cAAc,IAAI;;;;;;EAOxD,SAAS,aAAsB;AAC7B,UAAO,WAAW,cAAc,cAAc,GAAG;;;;;;EAOnD,SAAS,eAAuB;AAC9B,UAAO,aAAa,gBAAgB;IAAE;IAAY;IAAe,CAAC;;AAIpE,MAAI,cAAc,IAChB,aAAY,cAAc;AAI5B,MAAI,cAAc,IAAI;AACpB,aAAU,YAAY;AACtB,eAAY,cAAc;;EAI5B,MAAM,iBAAiB,OAAO,aAAa,MACxC,EAAE,WAAW,SAAS,cAAc,WACtC;EAED,MAAM,EAAE,SAAS;EAEjB,MAAM,kBAAkB,mBAAmB;GACzC;GACA;GACD,CAAC;AAIF,MAAI,eACF,UAAS;GACP,GAAG;GACH,aACE,OAAO,aAAa,QACjB,EAAE,WAAW,SAAS,gBAAgB,KACxC,IAAI,EAAE;GACV;AAGH,oBAAkB;GAChB;GACA,SAAS;GACT,eACE;GACF,aAAa,GACV,gBAAgB,OAAO;IACtB,eAAe;IACf,qBAAqB;IACrB,QAAQ,cAAc,IAAI,UAAU;IACrC,EACF;GACD,gCAAgC,EAC9B,eACD;GACD,qBAAqB;AACnB,WAAO,SAAS,KACd,EAAE,SAAS,qCAAqC,EAChD,EAAE,QAAQ,KAAK,CAChB;;GAEH,mBAAmB;GACpB,CAAC;EAEF,MAAM,qBAAqB,EACzB,aAAa,GACV,gBAAgB,OAAO;GACtB,SAAS,kBAAkB;IACzB;IACA;IACA;IACD,CAAC;GACF,eAAe;GACf,qBAAqB;GACtB,EACF,EACF;EAED,MAAMC,eAAuB;GAC3B,GAAG;GACH,OAAO;IACL,GAAG,OAAO;IACV,YAAY;KACV,GAAG,OAAO,OAAO;KACjB,WAAW,CACT,GAAI,OAAO,OAAO,YAAY,aAAa,EAAE,EAC7C,oEACD;KACF;IACF;GACD,aAAa,CAAC,GAAI,OAAO,eAAe,EAAE,EAAG,gBAAgB;GAC7D,WAAW;IACT,GAAI,OAAO,aAAa,EAAE;IAC1B,GAAG,mBAAmB;KAAE;KAAc;KAAY;KAAe,CAAC;IAClE,GAAG,mBAAmB;KAAE;KAAc;KAAe,CAAC;IACtD,GAAG,oBAAoB;KAAE;KAAY;KAAe,CAAC;IACtD;GACD,QAAQ;IACN,GAAG,OAAO;IACV,YAAY,EACV,SAAS;KACP,SAAS,cAAc;KACvB,YAAY,cAAc;KAC1B,MAAM,cAAc;KACpB,SAAS,cAAc;KACvB,SAAS,cAAc;KACvB,QAAQ,cAAc;KACvB,EACF;IACF;GACF;AAED,SAAO,mBAAmB,mBAAmB,CAAC,aAAa"}
1
+ {"version":3,"file":"plugin.mjs","names":["muxClient: Mux | null","s3Store: S3Store | null","tusServer: Server | null","mergedConfig: Config"],"sources":["../src/plugin.ts"],"sourcesContent":["import Mux from '@mux/mux-node'\nimport { createDefu } from 'defu'\nimport { cloudStoragePlugin } from '@payloadcms/plugin-cloud-storage'\nimport { initClientUploads } from '@payloadcms/plugin-cloud-storage/utilities'\n\nimport { MediaCloudErrors } from './types/errors'\nimport { getStorageAdapter } from './adapter/storageAdapter'\nimport { getMediaCollection } from './collections/mediaCollection'\nimport { folderAfterChangeHook } from './collectionHooks/folderAfterChange'\nimport { useErrorHandler } from './hooks/useErrorHandler'\nimport { createS3Store } from './tus/stores/s3'\nimport { createMuxClient, createMuxEndpoints } from './utils/mux'\nimport { createTusEndpoints, createTusServer } from './utils/tus'\nimport { createFileEndpoints } from './utils/file'\nimport { defaultOptions } from './utils/defaultOptions'\n\nimport type { Config } from 'payload'\nimport type { Server } from '@tus/server'\nimport type { MediaCloudPluginOptions } from './types'\nimport type { S3Store } from './tus/stores/s3/s3Store'\n\nconst { logError } = useErrorHandler()\n\n// Prevent arrays from being merged\nconst defu = createDefu((obj, key, value) => {\n if (Array.isArray(value)) {\n obj[key] = value\n return true\n }\n})\n\nlet muxClient: Mux | null = null\nlet s3Store: S3Store | null = null\nlet tusServer: Server | null = null\n\n/**\n * Media Cloud Plugin for Payload CMS\n * @param options Configuration options\n * @returns Payload config function\n */\nexport function mediaCloudPlugin(options: MediaCloudPluginOptions) {\n return function (config: Config): Config {\n // Check if config is invalid or disabled\n if (!options) {\n logError(MediaCloudErrors.PLUGIN_NOT_CONFIGURED.message)\n return config\n }\n\n // Merge user options with default options\n const pluginOptions = defu(options, defaultOptions)\n\n // Check if the plugin is disabled\n if (pluginOptions.enabled === false) {\n return config\n }\n\n /**\n * Helper function to get or create Mux client instance\n * @returns Mux client instance\n */\n function getMuxClient(): Mux {\n return muxClient ?? createMuxClient(pluginOptions.mux)\n }\n\n /**\n * Helper function to get or create S3 store instance\n * @returns S3 store instance\n */\n function getS3Store(): S3Store {\n return s3Store ?? createS3Store(pluginOptions.s3)\n }\n\n /**\n * Helper function to get or create tus server instance\n * @returns TUS server instance\n */\n function getTusServer(): Server {\n return tusServer ?? createTusServer({ getS3Store, pluginOptions })\n }\n\n // Initialize Mux client if configuration is provided\n if (pluginOptions.mux) {\n muxClient = getMuxClient()\n }\n\n // Initialize S3 store and TUS server if configuration is provided\n if (pluginOptions.s3) {\n s3Store = getS3Store()\n tusServer = getTusServer()\n }\n\n // Check if base media collection exists\n const baseCollection = config.collections?.find(\n ({ slug }) => slug === pluginOptions.collection\n )\n\n const { view } = pluginOptions\n const mediaCollection = getMediaCollection({\n baseCollection: baseCollection,\n view,\n })\n\n // Remove base media collection\n // It’ll be replaced with the merged media collection\n if (baseCollection) {\n config = {\n ...config,\n collections:\n config.collections?.filter(\n ({ slug }) => slug !== baseCollection?.slug\n ) ?? [],\n }\n }\n\n initClientUploads({\n config,\n enabled: true,\n clientHandler:\n '@maas/payload-plugin-media-cloud/components#UploadHandler',\n collections: {\n [mediaCollection.slug]: {\n clientUploads: true,\n disableLocalStorage: true,\n prefix: pluginOptions.s3?.prefix ?? '',\n },\n },\n extraClientHandlerProps: () => ({\n pluginOptions,\n }),\n serverHandler: () => {\n return Response.json(\n { message: 'Server handler is not implemented' },\n { status: 501 }\n )\n },\n serverHandlerPath: '/media-cloud/upload',\n })\n\n const cloudStorageConfig = {\n collections: {\n [mediaCollection.slug]: {\n adapter: getStorageAdapter({\n getMuxClient,\n pluginOptions,\n getS3Store,\n }),\n clientUploads: true,\n disableLocalStorage: true,\n },\n },\n }\n\n // Inject folderAfterChangeHook into folder collection\n if (pluginOptions.folders) {\n const existingFoldersConfig =\n typeof config.folders === 'object' && config.folders !== null\n ? config.folders\n : {}\n\n config = {\n ...config,\n folders: {\n ...existingFoldersConfig,\n collectionOverrides: [\n ...(existingFoldersConfig.collectionOverrides ?? []),\n ({ collection }) => ({\n ...collection,\n hooks: {\n ...collection.hooks,\n afterChange: [\n ...(collection.hooks?.afterChange ?? []),\n folderAfterChangeHook,\n ],\n },\n }),\n ],\n },\n }\n }\n\n const mergedConfig: Config = {\n ...config,\n admin: {\n ...config.admin,\n components: {\n ...config.admin?.components,\n providers: [\n ...(config.admin?.components?.providers ?? []),\n '@maas/payload-plugin-media-cloud/components#UploadManagerProvider',\n ],\n },\n },\n collections: [...(config.collections ?? []), mediaCollection],\n endpoints: [\n ...(config.endpoints ?? []),\n ...createTusEndpoints({ getTusServer, getS3Store, pluginOptions }),\n ...createMuxEndpoints({ getMuxClient, pluginOptions }),\n ...createFileEndpoints({ getS3Store, pluginOptions }),\n ],\n custom: {\n ...config.custom,\n mediaCloud: {\n options: {\n enabled: pluginOptions.enabled,\n collection: pluginOptions.collection,\n view: pluginOptions.view,\n storage: pluginOptions.storage,\n folders: pluginOptions.folders,\n limits: pluginOptions.limits,\n },\n },\n },\n }\n\n return cloudStoragePlugin(cloudStorageConfig)(mergedConfig)\n }\n}\n\nexport { s3Store }\n"],"mappings":";;;;;;;;;;;;;;;AAqBA,MAAM,EAAE,aAAa,iBAAiB;AAGtC,MAAM,OAAO,YAAY,KAAK,KAAK,UAAU;AAC3C,KAAI,MAAM,QAAQ,MAAM,EAAE;AACxB,MAAI,OAAO;AACX,SAAO;;EAET;AAEF,IAAIA,YAAwB;AAC5B,IAAIC,UAA0B;AAC9B,IAAIC,YAA2B;;;;;;AAO/B,SAAgB,iBAAiB,SAAkC;AACjE,QAAO,SAAU,QAAwB;AAEvC,MAAI,CAAC,SAAS;AACZ,YAAS,iBAAiB,sBAAsB,QAAQ;AACxD,UAAO;;EAIT,MAAM,gBAAgB,KAAK,SAAS,eAAe;AAGnD,MAAI,cAAc,YAAY,MAC5B,QAAO;;;;;EAOT,SAAS,eAAoB;AAC3B,UAAO,aAAa,gBAAgB,cAAc,IAAI;;;;;;EAOxD,SAAS,aAAsB;AAC7B,UAAO,WAAW,cAAc,cAAc,GAAG;;;;;;EAOnD,SAAS,eAAuB;AAC9B,UAAO,aAAa,gBAAgB;IAAE;IAAY;IAAe,CAAC;;AAIpE,MAAI,cAAc,IAChB,aAAY,cAAc;AAI5B,MAAI,cAAc,IAAI;AACpB,aAAU,YAAY;AACtB,eAAY,cAAc;;EAI5B,MAAM,iBAAiB,OAAO,aAAa,MACxC,EAAE,WAAW,SAAS,cAAc,WACtC;EAED,MAAM,EAAE,SAAS;EACjB,MAAM,kBAAkB,mBAAmB;GACzB;GAChB;GACD,CAAC;AAIF,MAAI,eACF,UAAS;GACP,GAAG;GACH,aACE,OAAO,aAAa,QACjB,EAAE,WAAW,SAAS,gBAAgB,KACxC,IAAI,EAAE;GACV;AAGH,oBAAkB;GAChB;GACA,SAAS;GACT,eACE;GACF,aAAa,GACV,gBAAgB,OAAO;IACtB,eAAe;IACf,qBAAqB;IACrB,QAAQ,cAAc,IAAI,UAAU;IACrC,EACF;GACD,gCAAgC,EAC9B,eACD;GACD,qBAAqB;AACnB,WAAO,SAAS,KACd,EAAE,SAAS,qCAAqC,EAChD,EAAE,QAAQ,KAAK,CAChB;;GAEH,mBAAmB;GACpB,CAAC;EAEF,MAAM,qBAAqB,EACzB,aAAa,GACV,gBAAgB,OAAO;GACtB,SAAS,kBAAkB;IACzB;IACA;IACA;IACD,CAAC;GACF,eAAe;GACf,qBAAqB;GACtB,EACF,EACF;AAGD,MAAI,cAAc,SAAS;GACzB,MAAM,wBACJ,OAAO,OAAO,YAAY,YAAY,OAAO,YAAY,OACrD,OAAO,UACP,EAAE;AAER,YAAS;IACP,GAAG;IACH,SAAS;KACP,GAAG;KACH,qBAAqB,CACnB,GAAI,sBAAsB,uBAAuB,EAAE,GAClD,EAAE,kBAAkB;MACnB,GAAG;MACH,OAAO;OACL,GAAG,WAAW;OACd,aAAa,CACX,GAAI,WAAW,OAAO,eAAe,EAAE,EACvC,sBACD;OACF;MACF,EACF;KACF;IACF;;EAGH,MAAMC,eAAuB;GAC3B,GAAG;GACH,OAAO;IACL,GAAG,OAAO;IACV,YAAY;KACV,GAAG,OAAO,OAAO;KACjB,WAAW,CACT,GAAI,OAAO,OAAO,YAAY,aAAa,EAAE,EAC7C,oEACD;KACF;IACF;GACD,aAAa,CAAC,GAAI,OAAO,eAAe,EAAE,EAAG,gBAAgB;GAC7D,WAAW;IACT,GAAI,OAAO,aAAa,EAAE;IAC1B,GAAG,mBAAmB;KAAE;KAAc;KAAY;KAAe,CAAC;IAClE,GAAG,mBAAmB;KAAE;KAAc;KAAe,CAAC;IACtD,GAAG,oBAAoB;KAAE;KAAY;KAAe,CAAC;IACtD;GACD,QAAQ;IACN,GAAG,OAAO;IACV,YAAY,EACV,SAAS;KACP,SAAS,cAAc;KACvB,YAAY,cAAc;KAC1B,MAAM,cAAc;KACpB,SAAS,cAAc;KACvB,SAAS,cAAc;KACvB,QAAQ,cAAc;KACvB,EACF;IACF;GACF;AAED,SAAO,mBAAmB,mBAAmB,CAAC,aAAa"}
@@ -84,6 +84,10 @@ declare const MediaCloudErrors: {
84
84
  readonly message: "Error generating thumbnail URL";
85
85
  readonly errorCode: 500;
86
86
  };
87
+ readonly PATH_UPDATE_ERROR: {
88
+ readonly message: "Error updating media path";
89
+ readonly errorCode: 500;
90
+ };
87
91
  readonly FILE_TYPE_UNKNOWN: {
88
92
  readonly message: "Unable to determine file type";
89
93
  readonly errorCode: 400;
@@ -84,6 +84,10 @@ const MediaCloudErrors = {
84
84
  message: "Error generating thumbnail URL",
85
85
  errorCode: 500
86
86
  },
87
+ PATH_UPDATE_ERROR: {
88
+ message: "Error updating media path",
89
+ errorCode: 500
90
+ },
87
91
  FILE_TYPE_UNKNOWN: {
88
92
  message: "Unable to determine file type",
89
93
  errorCode: 400
@@ -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_RENAME_ERROR: {\n message: 'Error renaming file in S3',\n errorCode: 500,\n },\n S3_MOVE_ERROR: {\n message: 'Error moving file in S3',\n errorCode: 500,\n },\n S3_PREFIX_ERROR: {\n message: 'Error updating S3 prefix',\n errorCode: 500,\n },\n S3_GET_OBJECT_ERROR: {\n message: 'Error getting object from S3',\n errorCode: 500,\n },\n S3_UNIQUE_NAME_ERROR: {\n message: 'Could not find a unique file name after maximum tries',\n errorCode: 500,\n },\n\n // TUS Errors\n TUS_UPLOAD_ERROR: {\n message: 'TUS file upload error occurred',\n errorCode: 500,\n },\n TUS_CLEANUP_ERROR: {\n message: 'Error during cleanup',\n errorCode: 500,\n },\n\n // General Errors\n UNKNOWN_STORAGE_TYPE: {\n message: 'Unknown storage type for media',\n errorCode: 500,\n },\n COLLECTION_REQUIRED: {\n message: 'A collection must be specified in the plugin options',\n errorCode: 400,\n },\n THUMBNAIL_GENERATION_ERROR: {\n message: 'Error generating thumbnail URL',\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 FILE_TYPE_UNSUPPORTED_ERROR: {\n message: 'Unsupported file type',\n errorCode: 400,\n },\n FILE_TYPE_NOT_ALLOWED: {\n message: 'File type not allowed',\n errorCode: 400,\n },\n FILE_SIZE_EXCEEDED: {\n message: 'File size exceeds the maximum allowed limit',\n errorCode: 400,\n },\n FILENAME_SANITIZE_ERROR: {\n message: 'Error sanitizing filename',\n errorCode: 500,\n },\n FILE_MISSING_NAME: {\n message: 'Filename is missing, cannot parse file',\n errorCode: 500,\n },\n FILE_NOT_FOUND: {\n message: 'File not found',\n errorCode: 404,\n },\n FILE_DIMENSIONS_ERROR: {\n message: 'Error setting media dimensions',\n errorCode: 500,\n },\n\n // Upload Errors\n UPLOAD_NO_URL: {\n message: 'No upload URL provided, cannot parse upload ID',\n errorCode: 400,\n },\n UPLOAD_HANDLER_ERROR: {\n message: 'Upload handler error occurred',\n errorCode: 500,\n },\n UPLOAD_POLLING_ERROR: {\n message: 'Polling error for upload',\n errorCode: 500,\n },\n UPLOAD_QUEUE_NOT_INITIALIZED: {\n message: 'Upload queue not initialized',\n errorCode: 500,\n },\n\n // Plugin Errors\n PLUGIN_NOT_CONFIGURED: {\n message: 'Payload Media Cloud plugin is not configured',\n errorCode: 500,\n },\n NAMING_FUNCTION_ERROR: {\n message: 'Error in namingFunction',\n errorCode: 500,\n },\n\n // HOOK Errors\n FOLDER_FETCH_ERROR: {\n message: 'Error fetching folder data',\n errorCode: 500,\n },\n} as const\n\nexport enum MediaCloudLogs {\n S3_STORE_MULTIPART_INIT = 'Initializing multipart upload',\n S3_STORE_MULTIPART_CREATED = 'Multipart upload created',\n S3_STORE_UPLOAD_FAILED = 'Failed to finish upload',\n S3_STORE_READ_ATTEMPT = 'Attempting to read file from S3',\n S3_STORE_READ_SUCCESS = 'Successfully read file from S3',\n S3_STORE_READ_RETRY = 'Failed to read file, trying again',\n S3_STORE_READ_FAILED = 'Failed to read file',\n S3_STORE_FILE_NOT_FOUND = 'No file found',\n S3_STORE_FILE_FOUND = 'File found',\n S3_STORE_RETRIEVE_PARTS_ERROR = 'Error retrieving parts',\n S3_STORE_METADATA_SAVING = 'Saving metadata',\n S3_STORE_METADATA_SAVED = 'Metadata file saved',\n S3_STORE_INCOMPLETE_PART_UPLOADED = 'Finished uploading incomplete part',\n S3_STORE_CHUNK_REMOVAL_FAILED = 'Failed to remove chunk',\n}\n"],"mappings":";AAAA,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,iBAAiB;EACf,SAAS;EACT,WAAW;EACZ;CACD,eAAe;EACb,SAAS;EACT,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;CACD,qBAAqB;EACnB,SAAS;EACT,WAAW;EACZ;CACD,4BAA4B;EAC1B,SAAS;EACT,WAAW;EACZ;CAGD,mBAAmB;EACjB,SAAS;EACT,WAAW;EACZ;CACD,iBAAiB;EACf,SAAS;EACT,WAAW;EACZ;CACD,6BAA6B;EAC3B,SAAS;EACT,WAAW;EACZ;CACD,uBAAuB;EACrB,SAAS;EACT,WAAW;EACZ;CACD,oBAAoB;EAClB,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;CACD,8BAA8B;EAC5B,SAAS;EACT,WAAW;EACZ;CAGD,uBAAuB;EACrB,SAAS;EACT,WAAW;EACZ;CACD,uBAAuB;EACrB,SAAS;EACT,WAAW;EACZ;CAGD,oBAAoB;EAClB,SAAS;EACT,WAAW;EACZ;CACF;AAED,IAAY,4DAAL;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
1
+ {"version":3,"file":"errors.mjs","names":[],"sources":["../../src/types/errors.ts"],"sourcesContent":["export const MediaCloudErrors = {\n // Mux Errors\n MUX_CONFIG_MISSING: {\n message:\n 'Mux configuration (tokenId and tokenSecret) must be provided in pluginOptions to use Mux',\n errorCode: 400,\n },\n MUX_CONFIG_INCOMPLETE: {\n message: 'Mux configuration is missing. Mux features will not be available',\n errorCode: 400,\n },\n MUX_UPLOAD_ID_MISSING: {\n message: 'No upload-id found for upload',\n errorCode: 400,\n },\n MUX_ASSET_DELETE_ERROR: {\n message: 'Error deleting Mux asset',\n errorCode: 500,\n },\n MUX_UPLOAD_ERROR: {\n message: 'Mux video upload failed',\n errorCode: 500,\n },\n MUX_DIRECT_UPLOAD_ERROR: {\n message: 'Mux direct upload failed',\n errorCode: 500,\n },\n MUX_CREATE_UPLOAD_ERROR: {\n message: 'Error in Mux create upload handler',\n errorCode: 500,\n },\n MUX_REQUEST_NO_JSON: {\n message: 'Request does not support json() method',\n errorCode: 400,\n },\n MUX_WEBHOOK_BODY_INVALID: {\n message: 'Invalid Mux webhook body',\n errorCode: 400,\n },\n\n // S3 Errors\n S3_CONFIG_MISSING: {\n message:\n 'S3 configuration (bucket, region, accessKeyId, secretAccessKey) must be provided in pluginOptions',\n errorCode: 400,\n },\n S3_DELETE_ERROR: {\n message: 'Error deleting file from S3',\n errorCode: 500,\n },\n S3_RENAME_ERROR: {\n message: 'Error renaming file in S3',\n errorCode: 500,\n },\n S3_MOVE_ERROR: {\n message: 'Error moving file in S3',\n errorCode: 500,\n },\n S3_PREFIX_ERROR: {\n message: 'Error updating S3 prefix',\n errorCode: 500,\n },\n S3_GET_OBJECT_ERROR: {\n message: 'Error getting object from S3',\n errorCode: 500,\n },\n S3_UNIQUE_NAME_ERROR: {\n message: 'Could not find a unique file name after maximum tries',\n errorCode: 500,\n },\n\n // TUS Errors\n TUS_UPLOAD_ERROR: {\n message: 'TUS file upload error occurred',\n errorCode: 500,\n },\n TUS_CLEANUP_ERROR: {\n message: 'Error during cleanup',\n errorCode: 500,\n },\n\n // General Errors\n UNKNOWN_STORAGE_TYPE: {\n message: 'Unknown storage type for media',\n errorCode: 500,\n },\n COLLECTION_REQUIRED: {\n message: 'A collection must be specified in the plugin options',\n errorCode: 400,\n },\n THUMBNAIL_GENERATION_ERROR: {\n message: 'Error generating thumbnail URL',\n errorCode: 500,\n },\n PATH_UPDATE_ERROR: {\n message: 'Error updating media path',\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 FILE_TYPE_UNSUPPORTED_ERROR: {\n message: 'Unsupported file type',\n errorCode: 400,\n },\n FILE_TYPE_NOT_ALLOWED: {\n message: 'File type not allowed',\n errorCode: 400,\n },\n FILE_SIZE_EXCEEDED: {\n message: 'File size exceeds the maximum allowed limit',\n errorCode: 400,\n },\n FILENAME_SANITIZE_ERROR: {\n message: 'Error sanitizing filename',\n errorCode: 500,\n },\n FILE_MISSING_NAME: {\n message: 'Filename is missing, cannot parse file',\n errorCode: 500,\n },\n FILE_NOT_FOUND: {\n message: 'File not found',\n errorCode: 404,\n },\n FILE_DIMENSIONS_ERROR: {\n message: 'Error setting media dimensions',\n errorCode: 500,\n },\n\n // Upload Errors\n UPLOAD_NO_URL: {\n message: 'No upload URL provided, cannot parse upload ID',\n errorCode: 400,\n },\n UPLOAD_HANDLER_ERROR: {\n message: 'Upload handler error occurred',\n errorCode: 500,\n },\n UPLOAD_POLLING_ERROR: {\n message: 'Polling error for upload',\n errorCode: 500,\n },\n UPLOAD_QUEUE_NOT_INITIALIZED: {\n message: 'Upload queue not initialized',\n errorCode: 500,\n },\n\n // Plugin Errors\n PLUGIN_NOT_CONFIGURED: {\n message: 'Payload Media Cloud plugin is not configured',\n errorCode: 500,\n },\n NAMING_FUNCTION_ERROR: {\n message: 'Error in namingFunction',\n errorCode: 500,\n },\n\n // HOOK Errors\n FOLDER_FETCH_ERROR: {\n message: 'Error fetching folder data',\n errorCode: 500,\n },\n} as const\n\nexport enum MediaCloudLogs {\n S3_STORE_MULTIPART_INIT = 'Initializing multipart upload',\n S3_STORE_MULTIPART_CREATED = 'Multipart upload created',\n S3_STORE_UPLOAD_FAILED = 'Failed to finish upload',\n S3_STORE_READ_ATTEMPT = 'Attempting to read file from S3',\n S3_STORE_READ_SUCCESS = 'Successfully read file from S3',\n S3_STORE_READ_RETRY = 'Failed to read file, trying again',\n S3_STORE_READ_FAILED = 'Failed to read file',\n S3_STORE_FILE_NOT_FOUND = 'No file found',\n S3_STORE_FILE_FOUND = 'File found',\n S3_STORE_RETRIEVE_PARTS_ERROR = 'Error retrieving parts',\n S3_STORE_METADATA_SAVING = 'Saving metadata',\n S3_STORE_METADATA_SAVED = 'Metadata file saved',\n S3_STORE_INCOMPLETE_PART_UPLOADED = 'Finished uploading incomplete part',\n S3_STORE_CHUNK_REMOVAL_FAILED = 'Failed to remove chunk',\n}\n"],"mappings":";AAAA,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,iBAAiB;EACf,SAAS;EACT,WAAW;EACZ;CACD,eAAe;EACb,SAAS;EACT,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;CACD,qBAAqB;EACnB,SAAS;EACT,WAAW;EACZ;CACD,4BAA4B;EAC1B,SAAS;EACT,WAAW;EACZ;CACD,mBAAmB;EACjB,SAAS;EACT,WAAW;EACZ;CAGD,mBAAmB;EACjB,SAAS;EACT,WAAW;EACZ;CACD,iBAAiB;EACf,SAAS;EACT,WAAW;EACZ;CACD,6BAA6B;EAC3B,SAAS;EACT,WAAW;EACZ;CACD,uBAAuB;EACrB,SAAS;EACT,WAAW;EACZ;CACD,oBAAoB;EAClB,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;CACD,8BAA8B;EAC5B,SAAS;EACT,WAAW;EACZ;CAGD,uBAAuB;EACrB,SAAS;EACT,WAAW;EACZ;CACD,uBAAuB;EACrB,SAAS;EACT,WAAW;EACZ;CAGD,oBAAoB;EAClB,SAAS;EACT,WAAW;EACZ;CACF;AAED,IAAY,4DAAL;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
@@ -1,6 +1,6 @@
1
1
  import { MediaCloudPluginOptions, MimeType } from "../types/index.mjs";
2
2
  import { S3Store } from "../tus/stores/s3/s3Store.mjs";
3
- import * as payload0 from "payload";
3
+ import * as payload3 from "payload";
4
4
 
5
5
  //#region src/utils/file.d.ts
6
6
 
@@ -35,7 +35,7 @@ interface CreateFileEndpointsArgs {
35
35
  pluginOptions: MediaCloudPluginOptions;
36
36
  }
37
37
  declare function createFileEndpoints(args: CreateFileEndpointsArgs): {
38
- handler: payload0.PayloadHandler;
38
+ handler: payload3.PayloadHandler;
39
39
  method: "get";
40
40
  path: string;
41
41
  }[];
@@ -1,5 +1,5 @@
1
1
  import { MediaCloudPluginOptions } from "../types/index.mjs";
2
- import * as payload2 from "payload";
2
+ import * as payload1 from "payload";
3
3
  import Mux from "@mux/mux-node";
4
4
 
5
5
  //#region src/utils/mux.d.ts
@@ -14,11 +14,11 @@ interface CreateMuxEndpointsArgs {
14
14
  pluginOptions: MediaCloudPluginOptions;
15
15
  }
16
16
  declare function createMuxEndpoints(args: CreateMuxEndpointsArgs): ({
17
- handler: payload2.PayloadHandler;
17
+ handler: payload1.PayloadHandler;
18
18
  method: "post";
19
19
  path: string;
20
20
  } | {
21
- handler: payload2.PayloadHandler;
21
+ handler: payload1.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.45",
3
+ "version": "0.0.47",
4
4
  "type": "module",
5
5
  "contributors": [
6
6
  {
@@ -1,7 +0,0 @@
1
- import { CollectionAfterChangeHook } from "payload";
2
-
3
- //#region src/collectionHooks/thumbnail.d.ts
4
- declare const thumbnailHook: CollectionAfterChangeHook;
5
- //#endregion
6
- export { thumbnailHook };
7
- //# sourceMappingURL=thumbnail.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"thumbnail.mjs","names":["thumbnailHook: CollectionAfterChangeHook"],"sources":["../../src/collectionHooks/thumbnail.ts"],"sourcesContent":["import { buildThumbnailURL } from '../utils/buildThumbnailURL'\nimport { s3Store } from '../plugin'\nimport { MediaCloudErrors } from '../types/errors'\nimport { useErrorHandler } from '../hooks/useErrorHandler'\n\nimport type { CollectionAfterChangeHook } from 'payload'\n\nexport const thumbnailHook: CollectionAfterChangeHook = async ({\n collection,\n doc,\n previousDoc,\n req,\n}) => {\n const { throwError } = useErrorHandler()\n\n // Skip if this is an internal update to prevent infinite loop\n if (req.context?._mediaCloudPluginInternal) {\n return doc\n }\n\n // Handle thumbnail\n if (!doc.thumbnail || previousDoc?.path !== doc.path) {\n try {\n const thumbnail = buildThumbnailURL({\n storage: doc.storage,\n playbackId: doc.storage === 'mux' ? doc.mux.playbackId : undefined,\n s3Key: doc.storage === 's3' ? (doc.path ?? doc.filename) : undefined,\n s3Store,\n })\n\n req.context = { ...req.context }\n req.context._mediaCloudPluginInternal = true\n\n const update = await req.payload.update({\n collection: collection.slug,\n id: doc.id,\n data: { thumbnail },\n req,\n })\n\n delete req.context._mediaCloudPluginInternal\n\n return update\n } catch (error) {\n throwError({\n ...MediaCloudErrors.THUMBNAIL_GENERATION_ERROR,\n cause: error,\n })\n }\n }\n\n return doc\n}\n"],"mappings":";;;;;;AAOA,MAAaA,gBAA2C,OAAO,EAC7D,YACA,KACA,aACA,UACI;CACJ,MAAM,EAAE,eAAe,iBAAiB;AAGxC,KAAI,IAAI,SAAS,0BACf,QAAO;AAIT,KAAI,CAAC,IAAI,aAAa,aAAa,SAAS,IAAI,KAC9C,KAAI;EACF,MAAM,YAAY,kBAAkB;GAClC,SAAS,IAAI;GACb,YAAY,IAAI,YAAY,QAAQ,IAAI,IAAI,aAAa;GACzD,OAAO,IAAI,YAAY,OAAQ,IAAI,QAAQ,IAAI,WAAY;GAC3D;GACD,CAAC;AAEF,MAAI,UAAU,EAAE,GAAG,IAAI,SAAS;AAChC,MAAI,QAAQ,4BAA4B;EAExC,MAAM,SAAS,MAAM,IAAI,QAAQ,OAAO;GACtC,YAAY,WAAW;GACvB,IAAI,IAAI;GACR,MAAM,EAAE,WAAW;GACnB;GACD,CAAC;AAEF,SAAO,IAAI,QAAQ;AAEnB,SAAO;UACA,OAAO;AACd,aAAW;GACT,GAAG,iBAAiB;GACpB,OAAO;GACR,CAAC;;AAIN,QAAO"}