payload 3.88.0 → 3.89.0-internal.95c9cba

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 (61) hide show
  1. package/dist/collections/operations/create.d.ts.map +1 -1
  2. package/dist/collections/operations/create.js +10 -0
  3. package/dist/collections/operations/create.js.map +1 -1
  4. package/dist/collections/operations/update.d.ts.map +1 -1
  5. package/dist/collections/operations/update.js +10 -0
  6. package/dist/collections/operations/update.js.map +1 -1
  7. package/dist/collections/operations/updateByID.d.ts.map +1 -1
  8. package/dist/collections/operations/updateByID.js +10 -0
  9. package/dist/collections/operations/updateByID.js.map +1 -1
  10. package/dist/index.bundled.d.ts +10 -0
  11. package/dist/uploads/cropImage.d.ts.map +1 -1
  12. package/dist/uploads/cropImage.js +2 -5
  13. package/dist/uploads/cropImage.js.map +1 -1
  14. package/dist/uploads/generateFileData.d.ts +8 -0
  15. package/dist/uploads/generateFileData.d.ts.map +1 -1
  16. package/dist/uploads/generateFileData.js +55 -29
  17. package/dist/uploads/generateFileData.js.map +1 -1
  18. package/dist/uploads/generateFileData.spec.js +144 -0
  19. package/dist/uploads/generateFileData.spec.js.map +1 -0
  20. package/dist/uploads/generateFileData.tempFileBuffering.spec.js +81 -0
  21. package/dist/uploads/generateFileData.tempFileBuffering.spec.js.map +1 -0
  22. package/dist/uploads/getFileContentRequirement.d.ts +9 -0
  23. package/dist/uploads/getFileContentRequirement.d.ts.map +1 -0
  24. package/dist/uploads/getFileContentRequirement.js +24 -0
  25. package/dist/uploads/getFileContentRequirement.js.map +1 -0
  26. package/dist/uploads/getFileContentRequirement.spec.js +95 -0
  27. package/dist/uploads/getFileContentRequirement.spec.js.map +1 -0
  28. package/dist/uploads/getFileFromClientUpload.d.ts +20 -0
  29. package/dist/uploads/getFileFromClientUpload.d.ts.map +1 -0
  30. package/dist/uploads/getFileFromClientUpload.js +199 -0
  31. package/dist/uploads/getFileFromClientUpload.js.map +1 -0
  32. package/dist/uploads/getFileFromClientUpload.spec.js +528 -0
  33. package/dist/uploads/getFileFromClientUpload.spec.js.map +1 -0
  34. package/dist/uploads/image-resizing/createImageSizes.d.ts.map +1 -1
  35. package/dist/uploads/image-resizing/createImageSizes.js +2 -5
  36. package/dist/uploads/image-resizing/createImageSizes.js.map +1 -1
  37. package/dist/uploads/isAnimatedImage.d.ts +6 -0
  38. package/dist/uploads/isAnimatedImage.d.ts.map +1 -0
  39. package/dist/uploads/isAnimatedImage.js +19 -0
  40. package/dist/uploads/isAnimatedImage.js.map +1 -0
  41. package/dist/uploads/isAnimatedImage.spec.js +19 -0
  42. package/dist/uploads/isAnimatedImage.spec.js.map +1 -0
  43. package/dist/uploads/types.d.ts +10 -0
  44. package/dist/uploads/types.d.ts.map +1 -1
  45. package/dist/uploads/types.js.map +1 -1
  46. package/dist/uploads/unlinkTempFiles.d.ts.map +1 -1
  47. package/dist/uploads/unlinkTempFiles.js +12 -2
  48. package/dist/uploads/unlinkTempFiles.js.map +1 -1
  49. package/dist/uploads/unlinkTempFiles.spec.js +157 -0
  50. package/dist/uploads/unlinkTempFiles.spec.js.map +1 -0
  51. package/dist/uploads/uploadFiles.d.ts.map +1 -1
  52. package/dist/uploads/uploadFiles.js +7 -2
  53. package/dist/uploads/uploadFiles.js.map +1 -1
  54. package/dist/uploads/uploadFiles.spec.js +44 -0
  55. package/dist/uploads/uploadFiles.spec.js.map +1 -0
  56. package/dist/utilities/addDataAndFileToRequest.d.ts.map +1 -1
  57. package/dist/utilities/addDataAndFileToRequest.js +7 -46
  58. package/dist/utilities/addDataAndFileToRequest.js.map +1 -1
  59. package/dist/utilities/addDataAndFileToRequest.spec.js +131 -1
  60. package/dist/utilities/addDataAndFileToRequest.spec.js.map +1 -1
  61. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/uploads/image-resizing/createImageSizes.ts"],"sourcesContent":["import type { Sharp, SharpOptions } from 'sharp'\n\nimport { fileTypeFromBuffer } from 'file-type'\nimport fs from 'fs/promises'\n\nimport type { SanitizedCollectionConfig } from '../../collections/config/types.js'\nimport type { SharpDependency } from '../../config/types.js'\nimport type { PayloadRequest } from '../../types/index.js'\nimport type { WithMetadata } from '../optionallyAppendMetadata.js'\nimport type { FileSize, FileSizes, FileToSave, FocalPoint, ProbedImageSize } from '../types.js'\n\nimport { fileExists } from '../fileExists.js'\nimport { optionallyAppendMetadata } from '../optionallyAppendMetadata.js'\nimport { createImageSize } from './createImageSize.js'\nimport { extractHeightFromImage } from './extractHeightFromImage.js'\nimport { generateImageSizeFilename } from './generateImageSizeFilename.js'\nimport { getImageResizeAction } from './getImageResizeAction.js'\nimport { parseFilename } from './parseFilename.js'\nimport { sanitizeResizeConfig } from './sanitizeResizeConfig.js'\n\n/**\n * For the provided image sizes, handle the resizing and the transforms\n * (format, trim, etc.) of each requested image size and return the result object.\n * This only handles the image sizes. The transforms of the original image\n * are handled in {@link ./generateFileData.ts}.\n *\n * The image will be resized according to the provided\n * resize config. If no image sizes are requested, the resolved data will be empty.\n * For every image that does not need to be resized, a result object with `null`\n * parameters will be returned.\n *\n * @param resizeConfig - the resize config\n * @returns the result of the resize operation(s)\n */\n\ntype ResizeArgs = {\n config: SanitizedCollectionConfig\n dimensions: ProbedImageSize\n file: PayloadRequest['file']\n focalPoint?: FocalPoint\n mimeType: string\n req: PayloadRequest\n savedFilename: string\n sharp?: SharpDependency\n staticPath: string\n withMetadata?: WithMetadata\n}\n\n/** Result from resizing and transforming the requested image sizes */\ntype ImageSizesResult = {\n sizeData: FileSizes\n sizesToSave: FileToSave[]\n}\n\nexport async function createImageSizes({\n config,\n dimensions,\n file,\n focalPoint,\n mimeType,\n req,\n savedFilename,\n sharp,\n staticPath,\n withMetadata,\n}: ResizeArgs): Promise<ImageSizesResult> {\n const { imageSizes } = config.upload\n\n if (!imageSizes || !sharp) {\n return {\n sizeData: {},\n sizesToSave: [],\n }\n }\n\n // Determine if the file is animated\n const fileIsAnimatedType = ['image/avif', 'image/gif', 'image/webp'].includes(file!.mimetype)\n const sharpOptions: SharpOptions = {}\n\n if (fileIsAnimatedType) {\n sharpOptions.animated = true\n }\n\n const sharpBase: Sharp | undefined = sharp(\n file!.tempFilePath || file!.data,\n sharpOptions,\n ).rotate() // pass rotate() to auto-rotate based on EXIF data. https://github.com/payloadcms/payload/pull/3081\n const originalImageMeta = await sharpBase.metadata()\n\n let adjustedDimensions = { ...dimensions }\n\n // Images with an exif orientation of 5, 6, 7, or 8 are auto-rotated by sharp\n // Need to adjust the dimensions to match the original image\n if ([5, 6, 7, 8].includes(originalImageMeta.orientation!)) {\n adjustedDimensions = {\n ...dimensions,\n height: dimensions.width,\n width: dimensions.height,\n }\n }\n\n const resizeImageMeta = {\n height: originalImageMeta.height,\n width: originalImageMeta.width,\n }\n\n const sizes = {} as Record<string, FileSize>\n const imageSizeFiles: FileToSave[] = []\n\n await Promise.all(\n imageSizes.map(async (imageResizeConfig): Promise<void> => {\n imageResizeConfig = sanitizeResizeConfig(imageResizeConfig)\n\n const resizeAction = getImageResizeAction({\n dimensions,\n hasFocalPoint: Boolean(focalPoint),\n imageResizeConfig,\n })\n\n if (resizeAction === 'omit') {\n sizes[imageResizeConfig.name] = createImageSize({})\n return\n }\n\n const imageToResize = sharpBase.clone()\n let resized = imageToResize\n\n if (resizeAction === 'resizeWithFocalPoint') {\n let { height: resizeHeight, width: resizeWidth } = imageResizeConfig\n\n const originalAspectRatio = adjustedDimensions.width / adjustedDimensions.height\n\n // Calculate resizeWidth based on original aspect ratio if it's undefined\n if (resizeHeight && !resizeWidth) {\n resizeWidth = Math.round(resizeHeight * originalAspectRatio)\n }\n\n // Calculate resizeHeight based on original aspect ratio if it's undefined\n if (resizeWidth && !resizeHeight) {\n resizeHeight = Math.round(resizeWidth / originalAspectRatio)\n }\n\n if (!resizeHeight) {\n resizeHeight = resizeImageMeta.height\n }\n if (!resizeWidth) {\n resizeWidth = resizeImageMeta.width\n }\n\n const resizeAspectRatio = resizeWidth! / resizeHeight!\n const prioritizeHeight = resizeAspectRatio < originalAspectRatio\n // Scales the image before extracting from it\n resized = imageToResize.resize({\n fastShrinkOnLoad: false,\n height: prioritizeHeight ? resizeHeight : undefined,\n width: prioritizeHeight ? undefined : resizeWidth,\n })\n\n const metadataAppendedFile = await optionallyAppendMetadata({\n req,\n sharpFile: resized,\n withMetadata: withMetadata!,\n })\n\n // Must read from buffer, resized.metadata will return the original image metadata\n const { info } = await metadataAppendedFile.toBuffer({ resolveWithObject: true })\n\n resizeImageMeta.height = extractHeightFromImage({\n ...originalImageMeta,\n height: info.height,\n })\n resizeImageMeta.width = info.width\n\n const halfResizeX = resizeWidth! / 2\n const xFocalCenter = resizeImageMeta.width * (focalPoint!.x / 100)\n const calculatedRightPixelBound = xFocalCenter + halfResizeX\n let leftBound = xFocalCenter - halfResizeX\n\n // if the right bound is greater than the image width, adjust the left bound\n // keeping focus on the right\n if (calculatedRightPixelBound > resizeImageMeta.width) {\n leftBound = resizeImageMeta.width - resizeWidth!\n }\n\n // if the left bound is less than 0, adjust the left bound to 0\n // keeping the focus on the left\n if (leftBound < 0) {\n leftBound = 0\n }\n\n const halfResizeY = resizeHeight! / 2\n const yFocalCenter = resizeImageMeta.height * (focalPoint!.y / 100)\n const calculatedBottomPixelBound = yFocalCenter + halfResizeY\n let topBound = yFocalCenter - halfResizeY\n\n // if the bottom bound is greater than the image height, adjust the top bound\n // keeping the image as far right as possible\n if (calculatedBottomPixelBound > resizeImageMeta.height) {\n topBound = resizeImageMeta.height - resizeHeight!\n }\n\n // if the top bound is less than 0, adjust the top bound to 0\n // keeping the image focus near the top\n if (topBound < 0) {\n topBound = 0\n }\n\n resized = resized.extract({\n height: resizeHeight!,\n left: Math.floor(leftBound),\n top: Math.floor(topBound),\n width: resizeWidth!,\n })\n } else {\n resized = imageToResize.resize(imageResizeConfig)\n }\n\n if (imageResizeConfig.formatOptions) {\n resized = resized.toFormat(\n imageResizeConfig.formatOptions.format,\n imageResizeConfig.formatOptions.options,\n )\n }\n\n if (imageResizeConfig.trimOptions) {\n resized = resized.trim(imageResizeConfig.trimOptions)\n }\n\n const metadataAppendedFile = await optionallyAppendMetadata({\n req,\n sharpFile: resized,\n withMetadata: withMetadata!,\n })\n\n const { data: bufferData, info: bufferInfo } = await metadataAppendedFile.toBuffer({\n resolveWithObject: true,\n })\n\n const { name, ext } = parseFilename(savedFilename)\n\n if (req.payloadUploadSizes) {\n req.payloadUploadSizes[imageResizeConfig.name] = bufferData\n }\n\n const mimeInfo = await fileTypeFromBuffer(bufferData)\n\n const imageNameWithDimensions = imageResizeConfig.generateImageName\n ? imageResizeConfig.generateImageName({\n extension: mimeInfo?.ext || ext,\n height: extractHeightFromImage({\n ...originalImageMeta,\n height: bufferInfo.height,\n }),\n originalName: name,\n sizeName: imageResizeConfig.name,\n width: bufferInfo.width,\n })\n : generateImageSizeFilename({\n extension: mimeInfo?.ext || ext,\n height: extractHeightFromImage({\n ...originalImageMeta,\n height: bufferInfo.height,\n }),\n outputImageName: name,\n width: bufferInfo.width,\n })\n\n const imagePath = `${staticPath}/${imageNameWithDimensions}`\n\n if (await fileExists(imagePath)) {\n try {\n await fs.unlink(imagePath)\n } catch {\n // Ignore unlink errors\n }\n }\n\n const { height, size, width } = bufferInfo\n sizes[imageResizeConfig.name] = createImageSize({\n filename: imageNameWithDimensions,\n filesize: size,\n height:\n fileIsAnimatedType && originalImageMeta.pages ? height / originalImageMeta.pages : height,\n mimeType: mimeInfo?.mime || mimeType,\n width,\n })\n\n imageSizeFiles.push({\n buffer: bufferData,\n path: imagePath,\n })\n }),\n )\n\n return {\n sizeData: sizes,\n sizesToSave: imageSizeFiles,\n }\n}\n"],"names":["fileTypeFromBuffer","fs","fileExists","optionallyAppendMetadata","createImageSize","extractHeightFromImage","generateImageSizeFilename","getImageResizeAction","parseFilename","sanitizeResizeConfig","createImageSizes","config","dimensions","file","focalPoint","mimeType","req","savedFilename","sharp","staticPath","withMetadata","imageSizes","upload","sizeData","sizesToSave","fileIsAnimatedType","includes","mimetype","sharpOptions","animated","sharpBase","tempFilePath","data","rotate","originalImageMeta","metadata","adjustedDimensions","orientation","height","width","resizeImageMeta","sizes","imageSizeFiles","Promise","all","map","imageResizeConfig","resizeAction","hasFocalPoint","Boolean","name","imageToResize","clone","resized","resizeHeight","resizeWidth","originalAspectRatio","Math","round","resizeAspectRatio","prioritizeHeight","resize","fastShrinkOnLoad","undefined","metadataAppendedFile","sharpFile","info","toBuffer","resolveWithObject","halfResizeX","xFocalCenter","x","calculatedRightPixelBound","leftBound","halfResizeY","yFocalCenter","y","calculatedBottomPixelBound","topBound","extract","left","floor","top","formatOptions","toFormat","format","options","trimOptions","trim","bufferData","bufferInfo","ext","payloadUploadSizes","mimeInfo","imageNameWithDimensions","generateImageName","extension","originalName","sizeName","outputImageName","imagePath","unlink","size","filename","filesize","pages","mime","push","buffer","path"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/uploads/image-resizing/createImageSizes.ts"],"sourcesContent":["import type { Sharp, SharpOptions } from 'sharp'\n\nimport { fileTypeFromBuffer } from 'file-type'\nimport fs from 'fs/promises'\n\nimport type { SanitizedCollectionConfig } from '../../collections/config/types.js'\nimport type { SharpDependency } from '../../config/types.js'\nimport type { PayloadRequest } from '../../types/index.js'\nimport type { WithMetadata } from '../optionallyAppendMetadata.js'\nimport type { FileSize, FileSizes, FileToSave, FocalPoint, ProbedImageSize } from '../types.js'\n\nimport { fileExists } from '../fileExists.js'\nimport { isAnimatedImage } from '../isAnimatedImage.js'\nimport { optionallyAppendMetadata } from '../optionallyAppendMetadata.js'\nimport { createImageSize } from './createImageSize.js'\nimport { extractHeightFromImage } from './extractHeightFromImage.js'\nimport { generateImageSizeFilename } from './generateImageSizeFilename.js'\nimport { getImageResizeAction } from './getImageResizeAction.js'\nimport { parseFilename } from './parseFilename.js'\nimport { sanitizeResizeConfig } from './sanitizeResizeConfig.js'\n\n/**\n * For the provided image sizes, handle the resizing and the transforms\n * (format, trim, etc.) of each requested image size and return the result object.\n * This only handles the image sizes. The transforms of the original image\n * are handled in {@link ./generateFileData.ts}.\n *\n * The image will be resized according to the provided\n * resize config. If no image sizes are requested, the resolved data will be empty.\n * For every image that does not need to be resized, a result object with `null`\n * parameters will be returned.\n *\n * @param resizeConfig - the resize config\n * @returns the result of the resize operation(s)\n */\n\ntype ResizeArgs = {\n config: SanitizedCollectionConfig\n dimensions: ProbedImageSize\n file: PayloadRequest['file']\n focalPoint?: FocalPoint\n mimeType: string\n req: PayloadRequest\n savedFilename: string\n sharp?: SharpDependency\n staticPath: string\n withMetadata?: WithMetadata\n}\n\n/** Result from resizing and transforming the requested image sizes */\ntype ImageSizesResult = {\n sizeData: FileSizes\n sizesToSave: FileToSave[]\n}\n\nexport async function createImageSizes({\n config,\n dimensions,\n file,\n focalPoint,\n mimeType,\n req,\n savedFilename,\n sharp,\n staticPath,\n withMetadata,\n}: ResizeArgs): Promise<ImageSizesResult> {\n const { imageSizes } = config.upload\n\n if (!imageSizes || !sharp) {\n return {\n sizeData: {},\n sizesToSave: [],\n }\n }\n\n // Determine if the file is animated\n const fileIsAnimatedType = isAnimatedImage(file!.mimetype)\n const sharpOptions: SharpOptions = {}\n\n if (fileIsAnimatedType) {\n sharpOptions.animated = true\n }\n\n const sharpBase: Sharp | undefined = sharp(\n file!.tempFilePath || file!.data,\n sharpOptions,\n ).rotate() // pass rotate() to auto-rotate based on EXIF data. https://github.com/payloadcms/payload/pull/3081\n const originalImageMeta = await sharpBase.metadata()\n\n let adjustedDimensions = { ...dimensions }\n\n // Images with an exif orientation of 5, 6, 7, or 8 are auto-rotated by sharp\n // Need to adjust the dimensions to match the original image\n if ([5, 6, 7, 8].includes(originalImageMeta.orientation!)) {\n adjustedDimensions = {\n ...dimensions,\n height: dimensions.width,\n width: dimensions.height,\n }\n }\n\n const resizeImageMeta = {\n height: originalImageMeta.height,\n width: originalImageMeta.width,\n }\n\n const sizes = {} as Record<string, FileSize>\n const imageSizeFiles: FileToSave[] = []\n\n await Promise.all(\n imageSizes.map(async (imageResizeConfig): Promise<void> => {\n imageResizeConfig = sanitizeResizeConfig(imageResizeConfig)\n\n const resizeAction = getImageResizeAction({\n dimensions,\n hasFocalPoint: Boolean(focalPoint),\n imageResizeConfig,\n })\n\n if (resizeAction === 'omit') {\n sizes[imageResizeConfig.name] = createImageSize({})\n return\n }\n\n const imageToResize = sharpBase.clone()\n let resized = imageToResize\n\n if (resizeAction === 'resizeWithFocalPoint') {\n let { height: resizeHeight, width: resizeWidth } = imageResizeConfig\n\n const originalAspectRatio = adjustedDimensions.width / adjustedDimensions.height\n\n // Calculate resizeWidth based on original aspect ratio if it's undefined\n if (resizeHeight && !resizeWidth) {\n resizeWidth = Math.round(resizeHeight * originalAspectRatio)\n }\n\n // Calculate resizeHeight based on original aspect ratio if it's undefined\n if (resizeWidth && !resizeHeight) {\n resizeHeight = Math.round(resizeWidth / originalAspectRatio)\n }\n\n if (!resizeHeight) {\n resizeHeight = resizeImageMeta.height\n }\n if (!resizeWidth) {\n resizeWidth = resizeImageMeta.width\n }\n\n const resizeAspectRatio = resizeWidth! / resizeHeight!\n const prioritizeHeight = resizeAspectRatio < originalAspectRatio\n // Scales the image before extracting from it\n resized = imageToResize.resize({\n fastShrinkOnLoad: false,\n height: prioritizeHeight ? resizeHeight : undefined,\n width: prioritizeHeight ? undefined : resizeWidth,\n })\n\n const metadataAppendedFile = await optionallyAppendMetadata({\n req,\n sharpFile: resized,\n withMetadata: withMetadata!,\n })\n\n // Must read from buffer, resized.metadata will return the original image metadata\n const { info } = await metadataAppendedFile.toBuffer({ resolveWithObject: true })\n\n resizeImageMeta.height = extractHeightFromImage({\n ...originalImageMeta,\n height: info.height,\n })\n resizeImageMeta.width = info.width\n\n const halfResizeX = resizeWidth! / 2\n const xFocalCenter = resizeImageMeta.width * (focalPoint!.x / 100)\n const calculatedRightPixelBound = xFocalCenter + halfResizeX\n let leftBound = xFocalCenter - halfResizeX\n\n // if the right bound is greater than the image width, adjust the left bound\n // keeping focus on the right\n if (calculatedRightPixelBound > resizeImageMeta.width) {\n leftBound = resizeImageMeta.width - resizeWidth!\n }\n\n // if the left bound is less than 0, adjust the left bound to 0\n // keeping the focus on the left\n if (leftBound < 0) {\n leftBound = 0\n }\n\n const halfResizeY = resizeHeight! / 2\n const yFocalCenter = resizeImageMeta.height * (focalPoint!.y / 100)\n const calculatedBottomPixelBound = yFocalCenter + halfResizeY\n let topBound = yFocalCenter - halfResizeY\n\n // if the bottom bound is greater than the image height, adjust the top bound\n // keeping the image as far right as possible\n if (calculatedBottomPixelBound > resizeImageMeta.height) {\n topBound = resizeImageMeta.height - resizeHeight!\n }\n\n // if the top bound is less than 0, adjust the top bound to 0\n // keeping the image focus near the top\n if (topBound < 0) {\n topBound = 0\n }\n\n resized = resized.extract({\n height: resizeHeight!,\n left: Math.floor(leftBound),\n top: Math.floor(topBound),\n width: resizeWidth!,\n })\n } else {\n resized = imageToResize.resize(imageResizeConfig)\n }\n\n if (imageResizeConfig.formatOptions) {\n resized = resized.toFormat(\n imageResizeConfig.formatOptions.format,\n imageResizeConfig.formatOptions.options,\n )\n }\n\n if (imageResizeConfig.trimOptions) {\n resized = resized.trim(imageResizeConfig.trimOptions)\n }\n\n const metadataAppendedFile = await optionallyAppendMetadata({\n req,\n sharpFile: resized,\n withMetadata: withMetadata!,\n })\n\n const { data: bufferData, info: bufferInfo } = await metadataAppendedFile.toBuffer({\n resolveWithObject: true,\n })\n\n const { name, ext } = parseFilename(savedFilename)\n\n if (req.payloadUploadSizes) {\n req.payloadUploadSizes[imageResizeConfig.name] = bufferData\n }\n\n const mimeInfo = await fileTypeFromBuffer(bufferData)\n\n const imageNameWithDimensions = imageResizeConfig.generateImageName\n ? imageResizeConfig.generateImageName({\n extension: mimeInfo?.ext || ext,\n height: extractHeightFromImage({\n ...originalImageMeta,\n height: bufferInfo.height,\n }),\n originalName: name,\n sizeName: imageResizeConfig.name,\n width: bufferInfo.width,\n })\n : generateImageSizeFilename({\n extension: mimeInfo?.ext || ext,\n height: extractHeightFromImage({\n ...originalImageMeta,\n height: bufferInfo.height,\n }),\n outputImageName: name,\n width: bufferInfo.width,\n })\n\n const imagePath = `${staticPath}/${imageNameWithDimensions}`\n\n if (await fileExists(imagePath)) {\n try {\n await fs.unlink(imagePath)\n } catch {\n // Ignore unlink errors\n }\n }\n\n const { height, size, width } = bufferInfo\n sizes[imageResizeConfig.name] = createImageSize({\n filename: imageNameWithDimensions,\n filesize: size,\n height:\n fileIsAnimatedType && originalImageMeta.pages ? height / originalImageMeta.pages : height,\n mimeType: mimeInfo?.mime || mimeType,\n width,\n })\n\n imageSizeFiles.push({\n buffer: bufferData,\n path: imagePath,\n })\n }),\n )\n\n return {\n sizeData: sizes,\n sizesToSave: imageSizeFiles,\n }\n}\n"],"names":["fileTypeFromBuffer","fs","fileExists","isAnimatedImage","optionallyAppendMetadata","createImageSize","extractHeightFromImage","generateImageSizeFilename","getImageResizeAction","parseFilename","sanitizeResizeConfig","createImageSizes","config","dimensions","file","focalPoint","mimeType","req","savedFilename","sharp","staticPath","withMetadata","imageSizes","upload","sizeData","sizesToSave","fileIsAnimatedType","mimetype","sharpOptions","animated","sharpBase","tempFilePath","data","rotate","originalImageMeta","metadata","adjustedDimensions","includes","orientation","height","width","resizeImageMeta","sizes","imageSizeFiles","Promise","all","map","imageResizeConfig","resizeAction","hasFocalPoint","Boolean","name","imageToResize","clone","resized","resizeHeight","resizeWidth","originalAspectRatio","Math","round","resizeAspectRatio","prioritizeHeight","resize","fastShrinkOnLoad","undefined","metadataAppendedFile","sharpFile","info","toBuffer","resolveWithObject","halfResizeX","xFocalCenter","x","calculatedRightPixelBound","leftBound","halfResizeY","yFocalCenter","y","calculatedBottomPixelBound","topBound","extract","left","floor","top","formatOptions","toFormat","format","options","trimOptions","trim","bufferData","bufferInfo","ext","payloadUploadSizes","mimeInfo","imageNameWithDimensions","generateImageName","extension","originalName","sizeName","outputImageName","imagePath","unlink","size","filename","filesize","pages","mime","push","buffer","path"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Whether sharp needs to read every frame/page of this mime type (rather than just the first) to
3
+ * process it correctly - used to decide whether sharp should be asked to read all frames.
4
+ */
5
+ export declare function isAnimatedImage(mimeType: string): boolean;
6
+ //# sourceMappingURL=isAnimatedImage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isAnimatedImage.d.ts","sourceRoot":"","sources":["../../src/uploads/isAnimatedImage.ts"],"names":[],"mappings":"AAQA;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEzD"}
@@ -0,0 +1,19 @@
1
+ // Matches the formats sharp's own `animated` input option documents as supported (reading every
2
+ // frame/page instead of just the first): https://sharp.pixelplumbing.com/api-constructor - "Set to
3
+ // true to read all frames/pages of an animated image (GIF, WebP, TIFF)". AVIF is deliberately
4
+ // excluded even though the format itself supports animation: this sharp version's AVIF encoder
5
+ // flattens `animated: true` frames into a single tall static image instead of a multi-page
6
+ // sequence, so there is no working animated-AVIF path to account for here.
7
+ const ANIMATED_IMAGE_MIME_TYPES = [
8
+ 'image/gif',
9
+ 'image/tiff',
10
+ 'image/webp'
11
+ ];
12
+ /**
13
+ * Whether sharp needs to read every frame/page of this mime type (rather than just the first) to
14
+ * process it correctly - used to decide whether sharp should be asked to read all frames.
15
+ */ export function isAnimatedImage(mimeType) {
16
+ return ANIMATED_IMAGE_MIME_TYPES.includes(mimeType);
17
+ }
18
+
19
+ //# sourceMappingURL=isAnimatedImage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/uploads/isAnimatedImage.ts"],"sourcesContent":["// Matches the formats sharp's own `animated` input option documents as supported (reading every\n// frame/page instead of just the first): https://sharp.pixelplumbing.com/api-constructor - \"Set to\n// true to read all frames/pages of an animated image (GIF, WebP, TIFF)\". AVIF is deliberately\n// excluded even though the format itself supports animation: this sharp version's AVIF encoder\n// flattens `animated: true` frames into a single tall static image instead of a multi-page\n// sequence, so there is no working animated-AVIF path to account for here.\nconst ANIMATED_IMAGE_MIME_TYPES = ['image/gif', 'image/tiff', 'image/webp']\n\n/**\n * Whether sharp needs to read every frame/page of this mime type (rather than just the first) to\n * process it correctly - used to decide whether sharp should be asked to read all frames.\n */\nexport function isAnimatedImage(mimeType: string): boolean {\n return ANIMATED_IMAGE_MIME_TYPES.includes(mimeType)\n}\n"],"names":["ANIMATED_IMAGE_MIME_TYPES","isAnimatedImage","mimeType","includes"],"mappings":"AAAA,gGAAgG;AAChG,mGAAmG;AACnG,8FAA8F;AAC9F,+FAA+F;AAC/F,2FAA2F;AAC3F,2EAA2E;AAC3E,MAAMA,4BAA4B;IAAC;IAAa;IAAc;CAAa;AAE3E;;;CAGC,GACD,OAAO,SAASC,gBAAgBC,QAAgB;IAC9C,OAAOF,0BAA0BG,QAAQ,CAACD;AAC5C"}
@@ -0,0 +1,19 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { isAnimatedImage } from './isAnimatedImage.js';
3
+ describe('isAnimatedImage', ()=>{
4
+ it('returns true for the formats sharp documents as supporting its animated option', ()=>{
5
+ expect(isAnimatedImage('image/gif')).toBe(true);
6
+ expect(isAnimatedImage('image/webp')).toBe(true);
7
+ expect(isAnimatedImage('image/tiff')).toBe(true);
8
+ });
9
+ it('returns false for avif, since sharp does not read or write multi-frame avif sequences', ()=>{
10
+ expect(isAnimatedImage('image/avif')).toBe(false);
11
+ });
12
+ it('returns false for static-only image formats', ()=>{
13
+ expect(isAnimatedImage('image/png')).toBe(false);
14
+ expect(isAnimatedImage('image/jpeg')).toBe(false);
15
+ expect(isAnimatedImage('image/svg+xml')).toBe(false);
16
+ });
17
+ });
18
+
19
+ //# sourceMappingURL=isAnimatedImage.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/uploads/isAnimatedImage.spec.ts"],"sourcesContent":["import { describe, expect, it } from 'vitest'\n\nimport { isAnimatedImage } from './isAnimatedImage.js'\n\ndescribe('isAnimatedImage', () => {\n it('returns true for the formats sharp documents as supporting its animated option', () => {\n expect(isAnimatedImage('image/gif')).toBe(true)\n expect(isAnimatedImage('image/webp')).toBe(true)\n expect(isAnimatedImage('image/tiff')).toBe(true)\n })\n\n it('returns false for avif, since sharp does not read or write multi-frame avif sequences', () => {\n expect(isAnimatedImage('image/avif')).toBe(false)\n })\n\n it('returns false for static-only image formats', () => {\n expect(isAnimatedImage('image/png')).toBe(false)\n expect(isAnimatedImage('image/jpeg')).toBe(false)\n expect(isAnimatedImage('image/svg+xml')).toBe(false)\n })\n})\n"],"names":["describe","expect","it","isAnimatedImage","toBe"],"mappings":"AAAA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,QAAQ,SAAQ;AAE7C,SAASC,eAAe,QAAQ,uBAAsB;AAEtDH,SAAS,mBAAmB;IAC1BE,GAAG,kFAAkF;QACnFD,OAAOE,gBAAgB,cAAcC,IAAI,CAAC;QAC1CH,OAAOE,gBAAgB,eAAeC,IAAI,CAAC;QAC3CH,OAAOE,gBAAgB,eAAeC,IAAI,CAAC;IAC7C;IAEAF,GAAG,yFAAyF;QAC1FD,OAAOE,gBAAgB,eAAeC,IAAI,CAAC;IAC7C;IAEAF,GAAG,+CAA+C;QAChDD,OAAOE,gBAAgB,cAAcC,IAAI,CAAC;QAC1CH,OAAOE,gBAAgB,eAAeC,IAAI,CAAC;QAC3CH,OAAOE,gBAAgB,kBAAkBC,IAAI,CAAC;IAChD;AACF"}
@@ -336,6 +336,16 @@ export type FileToSave = {
336
336
  * The path to save the file.
337
337
  */
338
338
  path: string;
339
+ } | {
340
+ /**
341
+ * The path to save the file.
342
+ */
343
+ path: string;
344
+ /**
345
+ * An existing file on disk to copy to `path`, instead of `buffer` - avoids loading a file
346
+ * that's already on disk (e.g. a temp file) fully into memory just to write it back out.
347
+ */
348
+ sourcePath: string;
339
349
  };
340
350
  type Crop = {
341
351
  height: number;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/uploads/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/uploads/types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/uploads/types.ts"],"sourcesContent":["import type { ResizeOptions, Sharp, SharpOptions } from 'sharp'\n\nimport type { CollectionConfig, TypeWithID } from '../collections/config/types.js'\nimport type { PayloadComponent } from '../config/types.js'\nimport type { PayloadRequest } from '../types/index.js'\nimport type { WithMetadata } from './optionallyAppendMetadata.js'\n\nexport type FileSize = {\n filename: null | string\n filesize: null | number\n height: null | number\n mimeType: null | string\n url?: null | string // TODO V4: make non-optional\n width: null | number\n}\n\n// TODO: deprecate in Payload v4.\n/**\n * FileSizeImproved is a more precise type, and will replace FileSize in Payload v4.\n * This type is for internal use only as it will be deprecated in the future.\n * @internal\n */\nexport type FileSizeImproved = {\n url: null | string\n} & FileSize\n\nexport type FileSizes = {\n [size: string]: FileSize\n}\n\nexport type FileData = {\n filename: string\n filesize: number\n focalX?: number\n focalY?: number\n height: number\n mimeType: string\n sizes: FileSizes\n tempFilePath?: string\n url?: string\n width: number\n}\n\nexport type ProbedImageSize = {\n height: number\n width: number\n}\n\n/**\n * Params sent to the sharp `toFormat()` function\n * @link https://sharp.pixelplumbing.com/api-output#toformat\n */\nexport type ImageUploadFormatOptions = {\n format: Parameters<Sharp['toFormat']>[0]\n options?: Parameters<Sharp['toFormat']>[1]\n}\n\n/**\n * Params sent to the sharp trim() function\n * @link https://sharp.pixelplumbing.com/api-resize#trim\n */\nexport type ImageUploadTrimOptions = Parameters<Sharp['trim']>[0]\n\nexport type GenerateImageName = (args: {\n extension: string\n height: number\n originalName: string\n sizeName: string\n width: number\n}) => string\n\nexport type ImageSize = {\n /**\n * Admin UI options that control how this image size appears in list views.\n *\n * NOTE: In Payload v4, these options (`disableGroupBy`, `disableListColumn` and `disableListFilter`)\n * should default to `true` so image size subfields are hidden from list columns\n * and filters by default, reducing noise in the admin UI.\n */\n admin?: {\n /**\n * If set to true, this image size will not be available\n * as a selectable groupBy option in the collection list view.\n * @default false\n */\n disableGroupBy?: boolean\n /**\n * If set to true, this image size will not be available\n * as a selectable column in the collection list view.\n * @default false\n */\n disableListColumn?: boolean\n /**\n * If set to true, this image size will not be available\n * as a filter option in the collection list view.\n * @default false\n */\n disableListFilter?: boolean\n }\n /**\n * @deprecated prefer position\n */\n crop?: string // comes from sharp package\n formatOptions?: ImageUploadFormatOptions\n /**\n * Generate a custom name for the file of this image size.\n */\n generateImageName?: GenerateImageName\n name: string\n trimOptions?: ImageUploadTrimOptions\n /**\n * When an uploaded image is smaller than the defined image size, we have 3 options:\n *\n * `undefined | false | true`\n *\n * 1. `undefined` [default]: uploading images with smaller width AND height than the image size will return null\n * 2. `false`: always enlarge images to the image size\n * 3. `true`: if the image is smaller than the image size, return the original image\n */\n withoutEnlargement?: ResizeOptions['withoutEnlargement']\n} & Omit<ResizeOptions, 'withoutEnlargement'>\n\nexport type GetAdminThumbnail = (args: { doc: Record<string, unknown> }) => false | null | string\n\nexport type AllowList = Array<{\n hostname: string\n pathname?: string\n port?: string\n protocol?: 'http' | 'https'\n search?: string\n}>\n\nexport type FileAllowList = Array<{\n extensions: string[]\n mimeType: string\n}>\n\ntype Admin = {\n components?: {\n /**\n * The Controls component to extend the upload controls in the admin panel.\n */\n controls?: PayloadComponent[]\n }\n}\n\nexport type UploadConfig = {\n /**\n * The adapter name to use for uploads. Used for storage adapter telemetry.\n * @default undefined\n */\n adapter?: string\n /**\n * The admin configuration for the upload field.\n */\n admin?: Admin\n /**\n * Represents an admin thumbnail, which can be either a React component or a string.\n * - If a string, it should be one of the image size names.\n * - A function that generates a fully qualified URL for the thumbnail, receives the doc as the only argument.\n **/\n adminThumbnail?: GetAdminThumbnail | string\n /**\n * Allow restricted file types known to be problematic.\n * - If set to `true`, it will allow all file types.\n * - If set to `false`, it will not allow file types and extensions known to be problematic.\n * - This setting is overriden by the `mimeTypes` option.\n * @default false\n */\n allowRestrictedFileTypes?: boolean\n /**\n * Enables bulk upload of files from the list view.\n * @default true\n */\n bulkUpload?: boolean\n /**\n * Appends a cache tag to the image URL when fetching the thumbnail in the admin panel. It may be desirable to disable this when hosting via CDNs with strict parameters.\n *\n * @default true\n */\n cacheTags?: boolean\n /**\n * Sharp constructor options to be passed to the uploaded file.\n * @link https://sharp.pixelplumbing.com/api-constructor/#sharp\n */\n constructorOptions?: SharpOptions\n /**\n * Enables cropping of images.\n * @default true\n */\n crop?: boolean\n /**\n * Disable the ability to save files to disk.\n * @default false\n */\n disableLocalStorage?: boolean\n /**\n * Enable displaying preview of the uploaded file in Upload fields related to this Collection.\n * Can be locally overridden by `displayPreview` option in Upload field.\n * @default false\n */\n displayPreview?: boolean\n /**\n *\n * Accepts existing headers and returns the headers after filtering or modifying.\n * If using this option, you should handle the removal of any sensitive cookies\n * (like payload-prefixed cookies) to prevent leaking session information to external\n * services. By default, Payload automatically filters out payload-prefixed cookies\n * when this option is NOT defined.\n *\n * Useful for adding custom headers to fetch from external providers.\n * @default undefined\n */\n externalFileHeaderFilter?: (headers: Record<string, string>) => Record<string, string>\n /**\n * Field slugs to use for a compound index instead of the default filename index.\n */\n filenameCompoundIndex?: string[]\n /**\n * Require files to be uploaded when creating a document.\n * @default true\n */\n filesRequiredOnCreate?: boolean\n /**\n * Enables focal point positioning for image manipulation.\n * @default true\n */\n focalPoint?: boolean\n /**\n * Format options for the uploaded file. Formatting image sizes needs to be done within each formatOptions individually.\n */\n formatOptions?: ImageUploadFormatOptions\n /**\n * Custom handlers to run when a file is fetched.\n *\n * - If a handler returns a Response, the response will be sent to the client and no further handlers will be run.\n * - If a handler returns null, the next handler will be run.\n * - If no handlers return a response the file will be returned by default.\n *\n * @link https://sharp.pixelplumbing.com/api-output/#toformat\n * @default undefined\n */\n handlers?: ((\n req: PayloadRequest,\n args: {\n doc: TypeWithID\n headers?: Headers\n params: {\n clientUploadContext?: unknown\n collection: string\n filename: string\n prefix?: string\n }\n },\n ) => Promise<Response> | Promise<void> | Response | void)[]\n /**\n * Set to `true` to prevent the admin UI from showing file inputs during document creation, useful for programmatic file generation.\n */\n hideFileInputOnCreate?: boolean\n /**\n * Set to `true` to prevent the admin UI having a way to remove an existing file while editing.\n */\n hideRemoveFile?: boolean\n imageSizes?: ImageSize[]\n /**\n * Restrict mimeTypes in the file picker. Array of valid mime types or mimetype wildcards\n * @example ['image/*', 'application/pdf']\n * @default undefined\n */\n mimeTypes?: string[]\n /**\n * Ability to modify the response headers fetching a file.\n * @default undefined\n */\n modifyResponseHeaders?: ({ headers }: { headers: Headers }) => Headers | void\n /**\n * Controls the behavior of pasting/uploading files from URLs.\n * If set to `false`, fetching from remote URLs is disabled.\n * If an `allowList` is provided, server-side fetching will be enabled for specified URLs.\n *\n * @default true (client-side fetching enabled)\n */\n pasteURL?:\n | {\n allowList: AllowList\n }\n | false\n /**\n * Sharp resize options for the original image.\n * @link https://sharp.pixelplumbing.com/api-resize#resize\n * @default undefined\n */\n resizeOptions?: ResizeOptions\n /**\n * Skip safe fetch when using server-side fetching for external files from these URLs.\n * @default false\n */\n skipSafeFetch?: AllowList | boolean\n /**\n * The directory to serve static files from. Defaults to collection slug.\n * @default undefined\n */\n staticDir?: string\n trimOptions?: ImageUploadTrimOptions\n /**\n * Optionally append metadata to the image during processing.\n *\n * Can be a boolean or a function.\n *\n * If true, metadata will be appended to the image.\n * If false, no metadata will be appended.\n * If a function, it will receive an object containing the metadata and should return a boolean indicating whether to append the metadata.\n * @default false\n */\n withMetadata?: WithMetadata\n}\nexport type checkFileRestrictionsParams = {\n collection: CollectionConfig\n file: File\n req: PayloadRequest\n}\n\nexport type SanitizedUploadConfig = {\n staticDir: UploadConfig['staticDir']\n} & UploadConfig\n\nexport type File = {\n /**\n * The buffer of the file.\n */\n data: Buffer\n /**\n * The mimetype of the file.\n */\n mimetype: string\n /**\n * The name of the file.\n */\n name: string\n /**\n * The size of the file in bytes.\n */\n size: number\n /**\n * Path to the temp file on disk when useTempFiles is enabled. In this case file.data will be an empty buffer.\n */\n tempFilePath?: string\n}\n\nexport type FileToSave = {\n /**\n * The buffer of the file.\n */\n buffer: Buffer\n /**\n * The path to save the file.\n */\n path: string\n}\n\ntype Crop = {\n height: number\n unit: '%' | 'px'\n width: number\n x: number\n y: number\n}\n\nexport type FocalPoint = {\n x: number\n y: number\n}\n\nexport type UploadEdits = {\n crop?: Crop\n focalPoint?: FocalPoint\n heightInPixels?: number\n widthInPixels?: number\n}\n"],"names":[],"mappings":"AAqXA,WAKC"}
1
+ {"version":3,"sources":["../../src/uploads/types.ts"],"sourcesContent":["import type { ResizeOptions, Sharp, SharpOptions } from 'sharp'\n\nimport type { CollectionConfig, TypeWithID } from '../collections/config/types.js'\nimport type { PayloadComponent } from '../config/types.js'\nimport type { PayloadRequest } from '../types/index.js'\nimport type { WithMetadata } from './optionallyAppendMetadata.js'\n\nexport type FileSize = {\n filename: null | string\n filesize: null | number\n height: null | number\n mimeType: null | string\n url?: null | string // TODO V4: make non-optional\n width: null | number\n}\n\n// TODO: deprecate in Payload v4.\n/**\n * FileSizeImproved is a more precise type, and will replace FileSize in Payload v4.\n * This type is for internal use only as it will be deprecated in the future.\n * @internal\n */\nexport type FileSizeImproved = {\n url: null | string\n} & FileSize\n\nexport type FileSizes = {\n [size: string]: FileSize\n}\n\nexport type FileData = {\n filename: string\n filesize: number\n focalX?: number\n focalY?: number\n height: number\n mimeType: string\n sizes: FileSizes\n tempFilePath?: string\n url?: string\n width: number\n}\n\nexport type ProbedImageSize = {\n height: number\n width: number\n}\n\n/**\n * Params sent to the sharp `toFormat()` function\n * @link https://sharp.pixelplumbing.com/api-output#toformat\n */\nexport type ImageUploadFormatOptions = {\n format: Parameters<Sharp['toFormat']>[0]\n options?: Parameters<Sharp['toFormat']>[1]\n}\n\n/**\n * Params sent to the sharp trim() function\n * @link https://sharp.pixelplumbing.com/api-resize#trim\n */\nexport type ImageUploadTrimOptions = Parameters<Sharp['trim']>[0]\n\nexport type GenerateImageName = (args: {\n extension: string\n height: number\n originalName: string\n sizeName: string\n width: number\n}) => string\n\nexport type ImageSize = {\n /**\n * Admin UI options that control how this image size appears in list views.\n *\n * NOTE: In Payload v4, these options (`disableGroupBy`, `disableListColumn` and `disableListFilter`)\n * should default to `true` so image size subfields are hidden from list columns\n * and filters by default, reducing noise in the admin UI.\n */\n admin?: {\n /**\n * If set to true, this image size will not be available\n * as a selectable groupBy option in the collection list view.\n * @default false\n */\n disableGroupBy?: boolean\n /**\n * If set to true, this image size will not be available\n * as a selectable column in the collection list view.\n * @default false\n */\n disableListColumn?: boolean\n /**\n * If set to true, this image size will not be available\n * as a filter option in the collection list view.\n * @default false\n */\n disableListFilter?: boolean\n }\n /**\n * @deprecated prefer position\n */\n crop?: string // comes from sharp package\n formatOptions?: ImageUploadFormatOptions\n /**\n * Generate a custom name for the file of this image size.\n */\n generateImageName?: GenerateImageName\n name: string\n trimOptions?: ImageUploadTrimOptions\n /**\n * When an uploaded image is smaller than the defined image size, we have 3 options:\n *\n * `undefined | false | true`\n *\n * 1. `undefined` [default]: uploading images with smaller width AND height than the image size will return null\n * 2. `false`: always enlarge images to the image size\n * 3. `true`: if the image is smaller than the image size, return the original image\n */\n withoutEnlargement?: ResizeOptions['withoutEnlargement']\n} & Omit<ResizeOptions, 'withoutEnlargement'>\n\nexport type GetAdminThumbnail = (args: { doc: Record<string, unknown> }) => false | null | string\n\nexport type AllowList = Array<{\n hostname: string\n pathname?: string\n port?: string\n protocol?: 'http' | 'https'\n search?: string\n}>\n\nexport type FileAllowList = Array<{\n extensions: string[]\n mimeType: string\n}>\n\ntype Admin = {\n components?: {\n /**\n * The Controls component to extend the upload controls in the admin panel.\n */\n controls?: PayloadComponent[]\n }\n}\n\nexport type UploadConfig = {\n /**\n * The adapter name to use for uploads. Used for storage adapter telemetry.\n * @default undefined\n */\n adapter?: string\n /**\n * The admin configuration for the upload field.\n */\n admin?: Admin\n /**\n * Represents an admin thumbnail, which can be either a React component or a string.\n * - If a string, it should be one of the image size names.\n * - A function that generates a fully qualified URL for the thumbnail, receives the doc as the only argument.\n **/\n adminThumbnail?: GetAdminThumbnail | string\n /**\n * Allow restricted file types known to be problematic.\n * - If set to `true`, it will allow all file types.\n * - If set to `false`, it will not allow file types and extensions known to be problematic.\n * - This setting is overriden by the `mimeTypes` option.\n * @default false\n */\n allowRestrictedFileTypes?: boolean\n /**\n * Enables bulk upload of files from the list view.\n * @default true\n */\n bulkUpload?: boolean\n /**\n * Appends a cache tag to the image URL when fetching the thumbnail in the admin panel. It may be desirable to disable this when hosting via CDNs with strict parameters.\n *\n * @default true\n */\n cacheTags?: boolean\n /**\n * Sharp constructor options to be passed to the uploaded file.\n * @link https://sharp.pixelplumbing.com/api-constructor/#sharp\n */\n constructorOptions?: SharpOptions\n /**\n * Enables cropping of images.\n * @default true\n */\n crop?: boolean\n /**\n * Disable the ability to save files to disk.\n * @default false\n */\n disableLocalStorage?: boolean\n /**\n * Enable displaying preview of the uploaded file in Upload fields related to this Collection.\n * Can be locally overridden by `displayPreview` option in Upload field.\n * @default false\n */\n displayPreview?: boolean\n /**\n *\n * Accepts existing headers and returns the headers after filtering or modifying.\n * If using this option, you should handle the removal of any sensitive cookies\n * (like payload-prefixed cookies) to prevent leaking session information to external\n * services. By default, Payload automatically filters out payload-prefixed cookies\n * when this option is NOT defined.\n *\n * Useful for adding custom headers to fetch from external providers.\n * @default undefined\n */\n externalFileHeaderFilter?: (headers: Record<string, string>) => Record<string, string>\n /**\n * Field slugs to use for a compound index instead of the default filename index.\n */\n filenameCompoundIndex?: string[]\n /**\n * Require files to be uploaded when creating a document.\n * @default true\n */\n filesRequiredOnCreate?: boolean\n /**\n * Enables focal point positioning for image manipulation.\n * @default true\n */\n focalPoint?: boolean\n /**\n * Format options for the uploaded file. Formatting image sizes needs to be done within each formatOptions individually.\n */\n formatOptions?: ImageUploadFormatOptions\n /**\n * Custom handlers to run when a file is fetched.\n *\n * - If a handler returns a Response, the response will be sent to the client and no further handlers will be run.\n * - If a handler returns null, the next handler will be run.\n * - If no handlers return a response the file will be returned by default.\n *\n * @link https://sharp.pixelplumbing.com/api-output/#toformat\n * @default undefined\n */\n handlers?: ((\n req: PayloadRequest,\n args: {\n doc: TypeWithID\n headers?: Headers\n params: {\n clientUploadContext?: unknown\n collection: string\n filename: string\n prefix?: string\n }\n },\n ) => Promise<Response> | Promise<void> | Response | void)[]\n /**\n * Set to `true` to prevent the admin UI from showing file inputs during document creation, useful for programmatic file generation.\n */\n hideFileInputOnCreate?: boolean\n /**\n * Set to `true` to prevent the admin UI having a way to remove an existing file while editing.\n */\n hideRemoveFile?: boolean\n imageSizes?: ImageSize[]\n /**\n * Restrict mimeTypes in the file picker. Array of valid mime types or mimetype wildcards\n * @example ['image/*', 'application/pdf']\n * @default undefined\n */\n mimeTypes?: string[]\n /**\n * Ability to modify the response headers fetching a file.\n * @default undefined\n */\n modifyResponseHeaders?: ({ headers }: { headers: Headers }) => Headers | void\n /**\n * Controls the behavior of pasting/uploading files from URLs.\n * If set to `false`, fetching from remote URLs is disabled.\n * If an `allowList` is provided, server-side fetching will be enabled for specified URLs.\n *\n * @default true (client-side fetching enabled)\n */\n pasteURL?:\n | {\n allowList: AllowList\n }\n | false\n /**\n * Sharp resize options for the original image.\n * @link https://sharp.pixelplumbing.com/api-resize#resize\n * @default undefined\n */\n resizeOptions?: ResizeOptions\n /**\n * Skip safe fetch when using server-side fetching for external files from these URLs.\n * @default false\n */\n skipSafeFetch?: AllowList | boolean\n /**\n * The directory to serve static files from. Defaults to collection slug.\n * @default undefined\n */\n staticDir?: string\n trimOptions?: ImageUploadTrimOptions\n /**\n * Optionally append metadata to the image during processing.\n *\n * Can be a boolean or a function.\n *\n * If true, metadata will be appended to the image.\n * If false, no metadata will be appended.\n * If a function, it will receive an object containing the metadata and should return a boolean indicating whether to append the metadata.\n * @default false\n */\n withMetadata?: WithMetadata\n}\nexport type checkFileRestrictionsParams = {\n collection: CollectionConfig\n file: File\n req: PayloadRequest\n}\n\nexport type SanitizedUploadConfig = {\n staticDir: UploadConfig['staticDir']\n} & UploadConfig\n\nexport type File = {\n /**\n * The buffer of the file.\n */\n data: Buffer\n /**\n * The mimetype of the file.\n */\n mimetype: string\n /**\n * The name of the file.\n */\n name: string\n /**\n * The size of the file in bytes.\n */\n size: number\n /**\n * Path to the temp file on disk when useTempFiles is enabled. In this case file.data will be an empty buffer.\n */\n tempFilePath?: string\n}\n\nexport type FileToSave =\n | {\n /**\n * The buffer of the file.\n */\n buffer: Buffer\n /**\n * The path to save the file.\n */\n path: string\n }\n | {\n /**\n * The path to save the file.\n */\n path: string\n /**\n * An existing file on disk to copy to `path`, instead of `buffer` - avoids loading a file\n * that's already on disk (e.g. a temp file) fully into memory just to write it back out.\n */\n sourcePath: string\n }\n\ntype Crop = {\n height: number\n unit: '%' | 'px'\n width: number\n x: number\n y: number\n}\n\nexport type FocalPoint = {\n x: number\n y: number\n}\n\nexport type UploadEdits = {\n crop?: Crop\n focalPoint?: FocalPoint\n heightInPixels?: number\n widthInPixels?: number\n}\n"],"names":[],"mappings":"AAiYA,WAKC"}
@@ -1 +1 @@
1
- {"version":3,"file":"unlinkTempFiles.d.ts","sourceRoot":"","sources":["../../src/uploads/unlinkTempFiles.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAA;AAC/E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAIvD,KAAK,IAAI,GAAG;IACV,gBAAgB,EAAE,yBAAyB,CAAA;IAC3C,MAAM,EAAE,eAAe,CAAA;IACvB,GAAG,EAAE,cAAc,CAAA;CACpB,CAAA;AACD;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,IAAI,CAezD,CAAA"}
1
+ {"version":3,"file":"unlinkTempFiles.d.ts","sourceRoot":"","sources":["../../src/uploads/unlinkTempFiles.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAA;AAC/E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAKvD,KAAK,IAAI,GAAG;IACV,gBAAgB,EAAE,yBAAyB,CAAA;IAC3C,MAAM,EAAE,eAAe,CAAA;IACvB,GAAG,EAAE,cAAc,CAAA;CACpB,CAAA;AACD;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,IAAI,CAgCzD,CAAA"}
@@ -1,10 +1,12 @@
1
1
  import fs from 'fs/promises';
2
2
  import { mapAsync } from '../utilities/mapAsync.js';
3
+ import { CLIENT_UPLOAD_TEMP_FILE_PATH_CONTEXT_KEY } from './getFileFromClientUpload.js';
3
4
  /**
4
5
  * Cleanup temp files after operation lifecycle
5
6
  */ export const unlinkTempFiles = async ({ collectionConfig, config, req })=>{
6
- if (config.upload?.useTempFiles && collectionConfig.upload) {
7
- const { file } = req;
7
+ const { file } = req;
8
+ const isClientUploadTempFile = Boolean(file?.tempFilePath && Object.prototype.hasOwnProperty.call(file, 'clientUploadContext'));
9
+ if (collectionConfig.upload && (config.upload?.useTempFiles || isClientUploadTempFile)) {
8
10
  const fileArray = [
9
11
  {
10
12
  file
@@ -17,6 +19,14 @@ import { mapAsync } from '../utilities/mapAsync.js';
17
19
  }
18
20
  });
19
21
  }
22
+ // plugin-cloud-storage's afterChange hook clears req.file after uploading generated image
23
+ // sizes, before this cleanup runs, so the materializer's temp file is tracked separately and
24
+ // may still need removing even though req.file no longer references it.
25
+ const clientUploadTempFilePath = req.context?.[CLIENT_UPLOAD_TEMP_FILE_PATH_CONTEXT_KEY];
26
+ if (typeof clientUploadTempFilePath === 'string' && clientUploadTempFilePath !== file?.tempFilePath) {
27
+ delete req.context[CLIENT_UPLOAD_TEMP_FILE_PATH_CONTEXT_KEY];
28
+ await fs.unlink(clientUploadTempFilePath);
29
+ }
20
30
  };
21
31
 
22
32
  //# sourceMappingURL=unlinkTempFiles.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/uploads/unlinkTempFiles.ts"],"sourcesContent":["import fs from 'fs/promises'\n\nimport type { SanitizedCollectionConfig } from '../collections/config/types.js'\nimport type { SanitizedConfig } from '../config/types.js'\nimport type { PayloadRequest } from '../types/index.js'\n\nimport { mapAsync } from '../utilities/mapAsync.js'\n\ntype Args = {\n collectionConfig: SanitizedCollectionConfig\n config: SanitizedConfig\n req: PayloadRequest\n}\n/**\n * Cleanup temp files after operation lifecycle\n */\nexport const unlinkTempFiles: (args: Args) => Promise<void> = async ({\n collectionConfig,\n config,\n req,\n}) => {\n if (config.upload?.useTempFiles && collectionConfig.upload) {\n const { file } = req\n const fileArray = [{ file }]\n await mapAsync(fileArray, async ({ file }) => {\n // Still need this check because this will not be populated if using local API\n if (file?.tempFilePath) {\n await fs.unlink(file.tempFilePath)\n }\n })\n }\n}\n"],"names":["fs","mapAsync","unlinkTempFiles","collectionConfig","config","req","upload","useTempFiles","file","fileArray","tempFilePath","unlink"],"mappings":"AAAA,OAAOA,QAAQ,cAAa;AAM5B,SAASC,QAAQ,QAAQ,2BAA0B;AAOnD;;CAEC,GACD,OAAO,MAAMC,kBAAiD,OAAO,EACnEC,gBAAgB,EAChBC,MAAM,EACNC,GAAG,EACJ;IACC,IAAID,OAAOE,MAAM,EAAEC,gBAAgBJ,iBAAiBG,MAAM,EAAE;QAC1D,MAAM,EAAEE,IAAI,EAAE,GAAGH;QACjB,MAAMI,YAAY;YAAC;gBAAED;YAAK;SAAE;QAC5B,MAAMP,SAASQ,WAAW,OAAO,EAAED,IAAI,EAAE;YACvC,8EAA8E;YAC9E,IAAIA,MAAME,cAAc;gBACtB,MAAMV,GAAGW,MAAM,CAACH,KAAKE,YAAY;YACnC;QACF;IACF;AACF,EAAC"}
1
+ {"version":3,"sources":["../../src/uploads/unlinkTempFiles.ts"],"sourcesContent":["import fs from 'fs/promises'\n\nimport type { SanitizedCollectionConfig } from '../collections/config/types.js'\nimport type { SanitizedConfig } from '../config/types.js'\nimport type { PayloadRequest } from '../types/index.js'\n\nimport { mapAsync } from '../utilities/mapAsync.js'\nimport { CLIENT_UPLOAD_TEMP_FILE_PATH_CONTEXT_KEY } from './getFileFromClientUpload.js'\n\ntype Args = {\n collectionConfig: SanitizedCollectionConfig\n config: SanitizedConfig\n req: PayloadRequest\n}\n/**\n * Cleanup temp files after operation lifecycle\n */\nexport const unlinkTempFiles: (args: Args) => Promise<void> = async ({\n collectionConfig,\n config,\n req,\n}) => {\n const { file } = req\n const isClientUploadTempFile = Boolean(\n file?.tempFilePath && Object.prototype.hasOwnProperty.call(file, 'clientUploadContext'),\n )\n\n if (collectionConfig.upload && (config.upload?.useTempFiles || isClientUploadTempFile)) {\n const fileArray = [{ file }]\n await mapAsync(fileArray, async ({ file }) => {\n // Still need this check because this will not be populated if using local API\n if (file?.tempFilePath) {\n await fs.unlink(file.tempFilePath)\n }\n })\n }\n\n // plugin-cloud-storage's afterChange hook clears req.file after uploading generated image\n // sizes, before this cleanup runs, so the materializer's temp file is tracked separately and\n // may still need removing even though req.file no longer references it.\n const clientUploadTempFilePath = req.context?.[CLIENT_UPLOAD_TEMP_FILE_PATH_CONTEXT_KEY]\n\n if (\n typeof clientUploadTempFilePath === 'string' &&\n clientUploadTempFilePath !== file?.tempFilePath\n ) {\n delete req.context[CLIENT_UPLOAD_TEMP_FILE_PATH_CONTEXT_KEY]\n await fs.unlink(clientUploadTempFilePath)\n }\n}\n"],"names":["fs","mapAsync","CLIENT_UPLOAD_TEMP_FILE_PATH_CONTEXT_KEY","unlinkTempFiles","collectionConfig","config","req","file","isClientUploadTempFile","Boolean","tempFilePath","Object","prototype","hasOwnProperty","call","upload","useTempFiles","fileArray","unlink","clientUploadTempFilePath","context"],"mappings":"AAAA,OAAOA,QAAQ,cAAa;AAM5B,SAASC,QAAQ,QAAQ,2BAA0B;AACnD,SAASC,wCAAwC,QAAQ,+BAA8B;AAOvF;;CAEC,GACD,OAAO,MAAMC,kBAAiD,OAAO,EACnEC,gBAAgB,EAChBC,MAAM,EACNC,GAAG,EACJ;IACC,MAAM,EAAEC,IAAI,EAAE,GAAGD;IACjB,MAAME,yBAAyBC,QAC7BF,MAAMG,gBAAgBC,OAAOC,SAAS,CAACC,cAAc,CAACC,IAAI,CAACP,MAAM;IAGnE,IAAIH,iBAAiBW,MAAM,IAAKV,CAAAA,OAAOU,MAAM,EAAEC,gBAAgBR,sBAAqB,GAAI;QACtF,MAAMS,YAAY;YAAC;gBAAEV;YAAK;SAAE;QAC5B,MAAMN,SAASgB,WAAW,OAAO,EAAEV,IAAI,EAAE;YACvC,8EAA8E;YAC9E,IAAIA,MAAMG,cAAc;gBACtB,MAAMV,GAAGkB,MAAM,CAACX,KAAKG,YAAY;YACnC;QACF;IACF;IAEA,0FAA0F;IAC1F,6FAA6F;IAC7F,wEAAwE;IACxE,MAAMS,2BAA2Bb,IAAIc,OAAO,EAAE,CAAClB,yCAAyC;IAExF,IACE,OAAOiB,6BAA6B,YACpCA,6BAA6BZ,MAAMG,cACnC;QACA,OAAOJ,IAAIc,OAAO,CAAClB,yCAAyC;QAC5D,MAAMF,GAAGkB,MAAM,CAACC;IAClB;AACF,EAAC"}
@@ -0,0 +1,157 @@
1
+ import fs from 'fs/promises';
2
+ import os from 'os';
3
+ import path from 'path';
4
+ import { afterEach, describe, expect, it } from 'vitest';
5
+ import { CLIENT_UPLOAD_TEMP_FILE_PATH_CONTEXT_KEY } from './getFileFromClientUpload.js';
6
+ import { unlinkTempFiles } from './unlinkTempFiles.js';
7
+ const createTempFile = async (contents = 'temp-file-contents')=>{
8
+ const tempFilePath = path.join(os.tmpdir(), `unlink-temp-files-spec-${Date.now()}-${Math.random()}`);
9
+ await fs.writeFile(tempFilePath, contents);
10
+ return tempFilePath;
11
+ };
12
+ const fileExists = async (filePath)=>{
13
+ try {
14
+ await fs.access(filePath);
15
+ return true;
16
+ } catch {
17
+ return false;
18
+ }
19
+ };
20
+ const collectionConfig = {
21
+ upload: {
22
+ disableLocalStorage: true
23
+ }
24
+ };
25
+ describe('unlinkTempFiles', ()=>{
26
+ const tempFilesToRemove = [];
27
+ afterEach(async ()=>{
28
+ for (const tempFilePath of tempFilesToRemove){
29
+ await fs.rm(tempFilePath, {
30
+ force: true
31
+ });
32
+ }
33
+ tempFilesToRemove.length = 0;
34
+ });
35
+ it('removes a client-upload materialized temp file even when useTempFiles is false', async ()=>{
36
+ const tempFilePath = await createTempFile();
37
+ tempFilesToRemove.push(tempFilePath);
38
+ const req = {
39
+ file: {
40
+ clientUploadContext: undefined,
41
+ data: Buffer.alloc(0),
42
+ mimetype: 'video/mp4',
43
+ name: 'clip.mp4',
44
+ size: 10,
45
+ tempFilePath
46
+ }
47
+ };
48
+ await unlinkTempFiles({
49
+ collectionConfig,
50
+ config: {
51
+ upload: {
52
+ useTempFiles: false
53
+ }
54
+ },
55
+ req
56
+ });
57
+ expect(await fileExists(tempFilePath)).toBe(false);
58
+ });
59
+ it('leaves a local-API temp file alone when useTempFiles is false', async ()=>{
60
+ const tempFilePath = await createTempFile();
61
+ tempFilesToRemove.push(tempFilePath);
62
+ const req = {
63
+ file: {
64
+ data: Buffer.alloc(0),
65
+ mimetype: 'video/mp4',
66
+ name: 'clip.mp4',
67
+ size: 10,
68
+ tempFilePath
69
+ }
70
+ };
71
+ await unlinkTempFiles({
72
+ collectionConfig,
73
+ config: {
74
+ upload: {
75
+ useTempFiles: false
76
+ }
77
+ },
78
+ req
79
+ });
80
+ expect(await fileExists(tempFilePath)).toBe(true);
81
+ });
82
+ it('removes a multipart temp file when useTempFiles is true', async ()=>{
83
+ const tempFilePath = await createTempFile();
84
+ tempFilesToRemove.push(tempFilePath);
85
+ const req = {
86
+ file: {
87
+ data: Buffer.alloc(0),
88
+ mimetype: 'video/mp4',
89
+ name: 'clip.mp4',
90
+ size: 10,
91
+ tempFilePath
92
+ }
93
+ };
94
+ await unlinkTempFiles({
95
+ collectionConfig,
96
+ config: {
97
+ upload: {
98
+ useTempFiles: true
99
+ }
100
+ },
101
+ req
102
+ });
103
+ expect(await fileExists(tempFilePath)).toBe(false);
104
+ });
105
+ it('removes a client-upload temp file tracked on req.context after req.file was cleared', async ()=>{
106
+ const tempFilePath = await createTempFile();
107
+ tempFilesToRemove.push(tempFilePath);
108
+ // Mirrors plugin-cloud-storage's afterChange hook, which clears req.file after uploading
109
+ // generated image sizes but leaves req.context untouched.
110
+ const req = {
111
+ context: {
112
+ [CLIENT_UPLOAD_TEMP_FILE_PATH_CONTEXT_KEY]: tempFilePath
113
+ },
114
+ file: undefined
115
+ };
116
+ await unlinkTempFiles({
117
+ collectionConfig,
118
+ config: {
119
+ upload: {
120
+ useTempFiles: false
121
+ }
122
+ },
123
+ req
124
+ });
125
+ expect(await fileExists(tempFilePath)).toBe(false);
126
+ expect(req.context[CLIENT_UPLOAD_TEMP_FILE_PATH_CONTEXT_KEY]).toBeUndefined();
127
+ });
128
+ it('does not attempt a second unlink when the context-tracked path matches req.file.tempFilePath', async ()=>{
129
+ const tempFilePath = await createTempFile();
130
+ tempFilesToRemove.push(tempFilePath);
131
+ const req = {
132
+ context: {
133
+ [CLIENT_UPLOAD_TEMP_FILE_PATH_CONTEXT_KEY]: tempFilePath
134
+ },
135
+ file: {
136
+ clientUploadContext: undefined,
137
+ data: Buffer.alloc(0),
138
+ mimetype: 'video/mp4',
139
+ name: 'clip.mp4',
140
+ size: 10,
141
+ tempFilePath
142
+ }
143
+ };
144
+ await expect(unlinkTempFiles({
145
+ collectionConfig,
146
+ config: {
147
+ upload: {
148
+ useTempFiles: false
149
+ }
150
+ },
151
+ req
152
+ })).resolves.not.toThrow();
153
+ expect(await fileExists(tempFilePath)).toBe(false);
154
+ });
155
+ });
156
+
157
+ //# sourceMappingURL=unlinkTempFiles.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/uploads/unlinkTempFiles.spec.ts"],"sourcesContent":["import type { SanitizedCollectionConfig } from '../collections/config/types.js'\nimport type { SanitizedConfig } from '../config/types.js'\nimport type { PayloadRequest } from '../types/index.js'\n\nimport fs from 'fs/promises'\nimport os from 'os'\nimport path from 'path'\nimport { afterEach, describe, expect, it } from 'vitest'\n\nimport { CLIENT_UPLOAD_TEMP_FILE_PATH_CONTEXT_KEY } from './getFileFromClientUpload.js'\nimport { unlinkTempFiles } from './unlinkTempFiles.js'\n\nconst createTempFile = async (contents = 'temp-file-contents'): Promise<string> => {\n const tempFilePath = path.join(\n os.tmpdir(),\n `unlink-temp-files-spec-${Date.now()}-${Math.random()}`,\n )\n await fs.writeFile(tempFilePath, contents)\n return tempFilePath\n}\n\nconst fileExists = async (filePath: string): Promise<boolean> => {\n try {\n await fs.access(filePath)\n return true\n } catch {\n return false\n }\n}\n\nconst collectionConfig = {\n upload: { disableLocalStorage: true },\n} as unknown as SanitizedCollectionConfig\n\ndescribe('unlinkTempFiles', () => {\n const tempFilesToRemove: string[] = []\n\n afterEach(async () => {\n for (const tempFilePath of tempFilesToRemove) {\n await fs.rm(tempFilePath, { force: true })\n }\n tempFilesToRemove.length = 0\n })\n\n it('removes a client-upload materialized temp file even when useTempFiles is false', async () => {\n const tempFilePath = await createTempFile()\n tempFilesToRemove.push(tempFilePath)\n\n const req = {\n file: {\n clientUploadContext: undefined,\n data: Buffer.alloc(0),\n mimetype: 'video/mp4',\n name: 'clip.mp4',\n size: 10,\n tempFilePath,\n },\n } as unknown as PayloadRequest\n\n await unlinkTempFiles({\n collectionConfig,\n config: { upload: { useTempFiles: false } } as unknown as SanitizedConfig,\n req,\n })\n\n expect(await fileExists(tempFilePath)).toBe(false)\n })\n\n it('leaves a local-API temp file alone when useTempFiles is false', async () => {\n const tempFilePath = await createTempFile()\n tempFilesToRemove.push(tempFilePath)\n\n const req = {\n file: {\n data: Buffer.alloc(0),\n mimetype: 'video/mp4',\n name: 'clip.mp4',\n size: 10,\n tempFilePath,\n },\n } as unknown as PayloadRequest\n\n await unlinkTempFiles({\n collectionConfig,\n config: { upload: { useTempFiles: false } } as unknown as SanitizedConfig,\n req,\n })\n\n expect(await fileExists(tempFilePath)).toBe(true)\n })\n\n it('removes a multipart temp file when useTempFiles is true', async () => {\n const tempFilePath = await createTempFile()\n tempFilesToRemove.push(tempFilePath)\n\n const req = {\n file: {\n data: Buffer.alloc(0),\n mimetype: 'video/mp4',\n name: 'clip.mp4',\n size: 10,\n tempFilePath,\n },\n } as unknown as PayloadRequest\n\n await unlinkTempFiles({\n collectionConfig,\n config: { upload: { useTempFiles: true } } as unknown as SanitizedConfig,\n req,\n })\n\n expect(await fileExists(tempFilePath)).toBe(false)\n })\n\n it('removes a client-upload temp file tracked on req.context after req.file was cleared', async () => {\n const tempFilePath = await createTempFile()\n tempFilesToRemove.push(tempFilePath)\n\n // Mirrors plugin-cloud-storage's afterChange hook, which clears req.file after uploading\n // generated image sizes but leaves req.context untouched.\n const req = {\n context: { [CLIENT_UPLOAD_TEMP_FILE_PATH_CONTEXT_KEY]: tempFilePath },\n file: undefined,\n } as unknown as PayloadRequest\n\n await unlinkTempFiles({\n collectionConfig,\n config: { upload: { useTempFiles: false } } as unknown as SanitizedConfig,\n req,\n })\n\n expect(await fileExists(tempFilePath)).toBe(false)\n expect(req.context[CLIENT_UPLOAD_TEMP_FILE_PATH_CONTEXT_KEY]).toBeUndefined()\n })\n\n it('does not attempt a second unlink when the context-tracked path matches req.file.tempFilePath', async () => {\n const tempFilePath = await createTempFile()\n tempFilesToRemove.push(tempFilePath)\n\n const req = {\n context: { [CLIENT_UPLOAD_TEMP_FILE_PATH_CONTEXT_KEY]: tempFilePath },\n file: {\n clientUploadContext: undefined,\n data: Buffer.alloc(0),\n mimetype: 'video/mp4',\n name: 'clip.mp4',\n size: 10,\n tempFilePath,\n },\n } as unknown as PayloadRequest\n\n await expect(\n unlinkTempFiles({\n collectionConfig,\n config: { upload: { useTempFiles: false } } as unknown as SanitizedConfig,\n req,\n }),\n ).resolves.not.toThrow()\n\n expect(await fileExists(tempFilePath)).toBe(false)\n })\n})\n"],"names":["fs","os","path","afterEach","describe","expect","it","CLIENT_UPLOAD_TEMP_FILE_PATH_CONTEXT_KEY","unlinkTempFiles","createTempFile","contents","tempFilePath","join","tmpdir","Date","now","Math","random","writeFile","fileExists","filePath","access","collectionConfig","upload","disableLocalStorage","tempFilesToRemove","rm","force","length","push","req","file","clientUploadContext","undefined","data","Buffer","alloc","mimetype","name","size","config","useTempFiles","toBe","context","toBeUndefined","resolves","not","toThrow"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"uploadFiles.d.ts","sourceRoot":"","sources":["../../src/uploads/uploadFiles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAK5C,eAAO,MAAM,WAAW,YACb,OAAO,SACT,UAAU,EAAE,OACd,cAAc,KAClB,OAAO,CAAC,IAAI,CAWd,CAAA"}
1
+ {"version":3,"file":"uploadFiles.d.ts","sourceRoot":"","sources":["../../src/uploads/uploadFiles.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAK5C,eAAO,MAAM,WAAW,YACb,OAAO,SACT,UAAU,EAAE,OACd,cAAc,KAClB,OAAO,CAAC,IAAI,CAed,CAAA"}
@@ -1,9 +1,14 @@
1
+ import fs from 'fs/promises';
1
2
  import { FileUploadError } from '../errors/index.js';
2
3
  import { saveBufferToFile } from './saveBufferToFile.js';
3
4
  export const uploadFiles = async (payload, files, req)=>{
4
5
  try {
5
- await Promise.all(files.map(async ({ buffer, path })=>{
6
- await saveBufferToFile(buffer, path);
6
+ await Promise.all(files.map(async (file)=>{
7
+ if ('sourcePath' in file) {
8
+ await fs.copyFile(file.sourcePath, file.path);
9
+ } else {
10
+ await saveBufferToFile(file.buffer, file.path);
11
+ }
7
12
  }));
8
13
  } catch (err) {
9
14
  payload.logger.error(err);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/uploads/uploadFiles.ts"],"sourcesContent":["import type { Payload } from '../index.js'\nimport type { PayloadRequest } from '../types/index.js'\nimport type { FileToSave } from './types.js'\n\nimport { FileUploadError } from '../errors/index.js'\nimport { saveBufferToFile } from './saveBufferToFile.js'\n\nexport const uploadFiles = async (\n payload: Payload,\n files: FileToSave[],\n req: PayloadRequest,\n): Promise<void> => {\n try {\n await Promise.all(\n files.map(async ({ buffer, path }) => {\n await saveBufferToFile(buffer, path)\n }),\n )\n } catch (err) {\n payload.logger.error(err)\n throw new FileUploadError(req.t)\n }\n}\n"],"names":["FileUploadError","saveBufferToFile","uploadFiles","payload","files","req","Promise","all","map","buffer","path","err","logger","error","t"],"mappings":"AAIA,SAASA,eAAe,QAAQ,qBAAoB;AACpD,SAASC,gBAAgB,QAAQ,wBAAuB;AAExD,OAAO,MAAMC,cAAc,OACzBC,SACAC,OACAC;IAEA,IAAI;QACF,MAAMC,QAAQC,GAAG,CACfH,MAAMI,GAAG,CAAC,OAAO,EAAEC,MAAM,EAAEC,IAAI,EAAE;YAC/B,MAAMT,iBAAiBQ,QAAQC;QACjC;IAEJ,EAAE,OAAOC,KAAK;QACZR,QAAQS,MAAM,CAACC,KAAK,CAACF;QACrB,MAAM,IAAIX,gBAAgBK,IAAIS,CAAC;IACjC;AACF,EAAC"}
1
+ {"version":3,"sources":["../../src/uploads/uploadFiles.ts"],"sourcesContent":["import fs from 'fs/promises'\n\nimport type { Payload } from '../index.js'\nimport type { PayloadRequest } from '../types/index.js'\nimport type { FileToSave } from './types.js'\n\nimport { FileUploadError } from '../errors/index.js'\nimport { saveBufferToFile } from './saveBufferToFile.js'\n\nexport const uploadFiles = async (\n payload: Payload,\n files: FileToSave[],\n req: PayloadRequest,\n): Promise<void> => {\n try {\n await Promise.all(\n files.map(async (file) => {\n if ('sourcePath' in file) {\n await fs.copyFile(file.sourcePath, file.path)\n } else {\n await saveBufferToFile(file.buffer, file.path)\n }\n }),\n )\n } catch (err) {\n payload.logger.error(err)\n throw new FileUploadError(req.t)\n }\n}\n"],"names":["fs","FileUploadError","saveBufferToFile","uploadFiles","payload","files","req","Promise","all","map","file","copyFile","sourcePath","path","buffer","err","logger","error","t"],"mappings":"AAAA,OAAOA,QAAQ,cAAa;AAM5B,SAASC,eAAe,QAAQ,qBAAoB;AACpD,SAASC,gBAAgB,QAAQ,wBAAuB;AAExD,OAAO,MAAMC,cAAc,OACzBC,SACAC,OACAC;IAEA,IAAI;QACF,MAAMC,QAAQC,GAAG,CACfH,MAAMI,GAAG,CAAC,OAAOC;YACf,IAAI,gBAAgBA,MAAM;gBACxB,MAAMV,GAAGW,QAAQ,CAACD,KAAKE,UAAU,EAAEF,KAAKG,IAAI;YAC9C,OAAO;gBACL,MAAMX,iBAAiBQ,KAAKI,MAAM,EAAEJ,KAAKG,IAAI;YAC/C;QACF;IAEJ,EAAE,OAAOE,KAAK;QACZX,QAAQY,MAAM,CAACC,KAAK,CAACF;QACrB,MAAM,IAAId,gBAAgBK,IAAIY,CAAC;IACjC;AACF,EAAC"}
@@ -0,0 +1,44 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ const copyFileMock = vi.fn().mockResolvedValue(undefined);
3
+ const writeFileMock = vi.fn().mockResolvedValue(undefined);
4
+ vi.mock('fs/promises', ()=>({
5
+ default: {
6
+ copyFile: copyFileMock,
7
+ writeFile: writeFileMock
8
+ }
9
+ }));
10
+ const { uploadFiles } = await import('./uploadFiles.js');
11
+ describe('uploadFiles', ()=>{
12
+ const payload = {
13
+ logger: {
14
+ error: vi.fn()
15
+ }
16
+ };
17
+ const req = {};
18
+ beforeEach(()=>{
19
+ vi.clearAllMocks();
20
+ });
21
+ it('writes a buffer entry to disk', async ()=>{
22
+ const buffer = Buffer.from('hello');
23
+ await uploadFiles(payload, [
24
+ {
25
+ buffer,
26
+ path: '/tmp/media/hello.txt'
27
+ }
28
+ ], req);
29
+ expect(writeFileMock).toHaveBeenCalledWith('/tmp/media/hello.txt', buffer);
30
+ expect(copyFileMock).not.toHaveBeenCalled();
31
+ });
32
+ it('copies a sourcePath entry directly, without reading it into memory', async ()=>{
33
+ await uploadFiles(payload, [
34
+ {
35
+ path: '/tmp/media/video.mp4',
36
+ sourcePath: '/tmp/payload-upload-abc'
37
+ }
38
+ ], req);
39
+ expect(copyFileMock).toHaveBeenCalledWith('/tmp/payload-upload-abc', '/tmp/media/video.mp4');
40
+ expect(writeFileMock).not.toHaveBeenCalled();
41
+ });
42
+ });
43
+
44
+ //# sourceMappingURL=uploadFiles.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/uploads/uploadFiles.spec.ts"],"sourcesContent":["import type { Payload } from '../index.js'\nimport type { PayloadRequest } from '../types/index.js'\n\nimport { beforeEach, describe, expect, it, vi } from 'vitest'\n\nconst copyFileMock = vi.fn().mockResolvedValue(undefined)\nconst writeFileMock = vi.fn().mockResolvedValue(undefined)\n\nvi.mock('fs/promises', () => ({\n default: {\n copyFile: copyFileMock,\n writeFile: writeFileMock,\n },\n}))\n\nconst { uploadFiles } = await import('./uploadFiles.js')\n\ndescribe('uploadFiles', () => {\n const payload = { logger: { error: vi.fn() } } as unknown as Payload\n const req = {} as unknown as PayloadRequest\n\n beforeEach(() => {\n vi.clearAllMocks()\n })\n\n it('writes a buffer entry to disk', async () => {\n const buffer = Buffer.from('hello')\n\n await uploadFiles(payload, [{ buffer, path: '/tmp/media/hello.txt' }], req)\n\n expect(writeFileMock).toHaveBeenCalledWith('/tmp/media/hello.txt', buffer)\n expect(copyFileMock).not.toHaveBeenCalled()\n })\n\n it('copies a sourcePath entry directly, without reading it into memory', async () => {\n await uploadFiles(\n payload,\n [{ path: '/tmp/media/video.mp4', sourcePath: '/tmp/payload-upload-abc' }],\n req,\n )\n\n expect(copyFileMock).toHaveBeenCalledWith('/tmp/payload-upload-abc', '/tmp/media/video.mp4')\n expect(writeFileMock).not.toHaveBeenCalled()\n })\n})\n"],"names":["beforeEach","describe","expect","it","vi","copyFileMock","fn","mockResolvedValue","undefined","writeFileMock","mock","default","copyFile","writeFile","uploadFiles","payload","logger","error","req","clearAllMocks","buffer","Buffer","from","path","toHaveBeenCalledWith","not","toHaveBeenCalled","sourcePath"],"mappings":"AAGA,SAASA,UAAU,EAAEC,QAAQ,EAAEC,MAAM,EAAEC,EAAE,EAAEC,EAAE,QAAQ,SAAQ;AAE7D,MAAMC,eAAeD,GAAGE,EAAE,GAAGC,iBAAiB,CAACC;AAC/C,MAAMC,gBAAgBL,GAAGE,EAAE,GAAGC,iBAAiB,CAACC;AAEhDJ,GAAGM,IAAI,CAAC,eAAe,IAAO,CAAA;QAC5BC,SAAS;YACPC,UAAUP;YACVQ,WAAWJ;QACb;IACF,CAAA;AAEA,MAAM,EAAEK,WAAW,EAAE,GAAG,MAAM,MAAM,CAAC;AAErCb,SAAS,eAAe;IACtB,MAAMc,UAAU;QAAEC,QAAQ;YAAEC,OAAOb,GAAGE,EAAE;QAAG;IAAE;IAC7C,MAAMY,MAAM,CAAC;IAEblB,WAAW;QACTI,GAAGe,aAAa;IAClB;IAEAhB,GAAG,iCAAiC;QAClC,MAAMiB,SAASC,OAAOC,IAAI,CAAC;QAE3B,MAAMR,YAAYC,SAAS;YAAC;gBAAEK;gBAAQG,MAAM;YAAuB;SAAE,EAAEL;QAEvEhB,OAAOO,eAAee,oBAAoB,CAAC,wBAAwBJ;QACnElB,OAAOG,cAAcoB,GAAG,CAACC,gBAAgB;IAC3C;IAEAvB,GAAG,sEAAsE;QACvE,MAAMW,YACJC,SACA;YAAC;gBAAEQ,MAAM;gBAAwBI,YAAY;YAA0B;SAAE,EACzET;QAGFhB,OAAOG,cAAcmB,oBAAoB,CAAC,2BAA2B;QACrEtB,OAAOO,eAAegB,GAAG,CAACC,gBAAgB;IAC5C;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"addDataAndFileToRequest.d.ts","sourceRoot":"","sources":["../../src/utilities/addDataAndFileToRequest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAKvD,KAAK,uBAAuB,GAAG,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AAErE;;GAEG;AACH,eAAO,MAAM,uBAAuB,EAAE,uBA+GrC,CAAA"}
1
+ {"version":3,"file":"addDataAndFileToRequest.d.ts","sourceRoot":"","sources":["../../src/utilities/addDataAndFileToRequest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAOvD,KAAK,uBAAuB,GAAG,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AAErE;;GAEG;AACH,eAAO,MAAM,uBAAuB,EAAE,uBAiErC,CAAA"}