payload 4.0.0-beta.3 → 4.0.0-beta.4

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 (79) hide show
  1. package/dist/admin/adapters/router.d.ts +1 -1
  2. package/dist/admin/adapters/router.d.ts.map +1 -1
  3. package/dist/admin/adapters/router.js.map +1 -1
  4. package/dist/admin/initAdminContext.js.map +1 -1
  5. package/dist/auth/getFieldsToSign.js.map +1 -1
  6. package/dist/auth/loginWithCookie.js.map +1 -1
  7. package/dist/auth/serverFunctions/cookies.js.map +1 -1
  8. package/dist/auth/serverFunctions/login.js.map +1 -1
  9. package/dist/cli/defineCLICommand.js +1 -1
  10. package/dist/cli/defineCLICommand.js.map +1 -1
  11. package/dist/collections/config/client.js.map +1 -1
  12. package/dist/collections/operations/findByID.js.map +1 -1
  13. package/dist/collections/operations/findVersionByID.js.map +1 -1
  14. package/dist/collections/operations/findVersions.js.map +1 -1
  15. package/dist/collections/operations/local/findDistinct.js.map +1 -1
  16. package/dist/collections/operations/renameFile.js +1 -1
  17. package/dist/collections/operations/renameFile.js.map +1 -1
  18. package/dist/collections/operations/utilities/update.js +1 -1
  19. package/dist/collections/operations/utilities/update.js.map +1 -1
  20. package/dist/collections/operations/validate.js +4 -4
  21. package/dist/collections/operations/validate.js.map +1 -1
  22. package/dist/config/definePlugin.js.map +1 -1
  23. package/dist/config/sanitize.js +2 -2
  24. package/dist/config/sanitize.js.map +1 -1
  25. package/dist/exports/build-config.d.ts +9 -0
  26. package/dist/exports/build-config.d.ts.map +1 -0
  27. package/dist/exports/build-config.js +38 -0
  28. package/dist/exports/build-config.js.map +1 -0
  29. package/dist/fields/config/client.js.map +1 -1
  30. package/dist/fields/hooks/afterChange/promise.js.map +1 -1
  31. package/dist/fields/validations.d.ts.map +1 -1
  32. package/dist/fields/validations.js +4 -1
  33. package/dist/fields/validations.js.map +1 -1
  34. package/dist/globals/operations/local/findOne.js.map +1 -1
  35. package/dist/globals/operations/local/update.js.map +1 -1
  36. package/dist/globals/operations/restoreVersion.d.ts +2 -3
  37. package/dist/globals/operations/restoreVersion.d.ts.map +1 -1
  38. package/dist/globals/operations/restoreVersion.js +22 -15
  39. package/dist/globals/operations/restoreVersion.js.map +1 -1
  40. package/dist/globals/operations/validate.js +1 -1
  41. package/dist/globals/operations/validate.js.map +1 -1
  42. package/dist/hierarchy/createFolderField.js.map +1 -1
  43. package/dist/hierarchy/resolveHierarchyCollections.js +2 -2
  44. package/dist/hierarchy/resolveHierarchyCollections.js.map +1 -1
  45. package/dist/hierarchy/utils/computePaths.d.ts.map +1 -1
  46. package/dist/hierarchy/utils/computePaths.js.map +1 -1
  47. package/dist/index.bundled.d.ts +12 -3
  48. package/dist/queues/operations/runJobs/runJob/getRunTaskFunction.js +1 -1
  49. package/dist/queues/operations/runJobs/runJob/getRunTaskFunction.js.map +1 -1
  50. package/dist/uploads/fileVersioning/archive.js.map +1 -1
  51. package/dist/uploads/fileVersioning/resolveHistoricalFile.js.map +1 -1
  52. package/dist/uploads/generateFileData.d.ts.map +1 -1
  53. package/dist/uploads/generateFileData.js.map +1 -1
  54. package/dist/uploads/getFileFromUploadInstructions.js.map +1 -1
  55. package/dist/uploads/safeFetch.js.map +1 -1
  56. package/dist/uploads/stagedUpload.js.map +1 -1
  57. package/dist/uploads/transformers/handleDynamicFileRequest.js +11 -2
  58. package/dist/uploads/transformers/handleDynamicFileRequest.js.map +1 -1
  59. package/dist/utilities/addSelectGenericsToGeneretedTypes.d.ts.map +1 -1
  60. package/dist/utilities/addSelectGenericsToGeneretedTypes.js +0 -1
  61. package/dist/utilities/addSelectGenericsToGeneretedTypes.js.map +1 -1
  62. package/dist/utilities/appendDateTimezoneSelectFields.js.map +1 -1
  63. package/dist/utilities/cloneValidationRequest.js.map +1 -1
  64. package/dist/utilities/createPayloadRequest.js.map +1 -1
  65. package/dist/utilities/deepCopyObject.js.map +1 -1
  66. package/dist/utilities/documentMatchingWhereExists.js +1 -1
  67. package/dist/utilities/documentMatchingWhereExists.js.map +1 -1
  68. package/dist/utilities/filterDataToSelectedLocales.js.map +1 -1
  69. package/dist/utilities/getEntityPermissions/getEntityPermissions.d.ts.map +1 -1
  70. package/dist/utilities/getEntityPermissions/getEntityPermissions.js +1 -0
  71. package/dist/utilities/getEntityPermissions/getEntityPermissions.js.map +1 -1
  72. package/dist/utilities/getEntityPermissions/populateFieldPermissions.js.map +1 -1
  73. package/dist/utilities/parseParams/index.js.map +1 -1
  74. package/dist/utilities/parseValidationLocale.js.map +1 -1
  75. package/dist/utilities/sanitizePopulateParam.js.map +1 -1
  76. package/dist/utilities/telemetry/featureInfo/getFeatureInfo.js.map +1 -1
  77. package/dist/versions/allLocalesPublicationStatus.js.map +1 -1
  78. package/package.json +11 -9
  79. package/skills/payload/reference/PLUGIN-DEVELOPMENT.md +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/uploads/fileVersioning/archive.ts"],"sourcesContent":["import fs from 'node:fs/promises'\nimport path from 'node:path'\n\nimport type { SanitizedCollectionConfig } from '../../collections/config/types.js'\nimport type { Config } from '../../config/types.js'\nimport type { JsonObject, PayloadRequest } from '../../types/index.js'\nimport type { FileToSave } from '../types.js'\nimport type { StagedObject } from './fileOperationManager.js'\nimport type { StoredFileList } from './types.js'\n\nimport { saveVersion } from '../../versions/saveVersion.js'\nimport { removeUnreferencedStagedObjects, scheduleUnreferencedFileCleanup } from './cleanup.js'\nimport { runFileOperationPlan, stageLocalUploadFiles } from './fileOperationManager.js'\nimport { copyLocalFile } from './localStorage.js'\nimport { getArchivedFilename } from './naming.js'\nimport {\n collectStoredFiles,\n getStoredFileIdentity,\n withLegacyUploadFileData,\n} from './storedFiles.js'\n\ntype VersionRow = {\n createdAt: string\n id: number | string\n latest?: boolean\n parent: number | string\n publishedLocale?: string\n updatedAt: string\n version: JsonObject\n}\n\nexport const runLocalFileUpdate = async <T>({\n id,\n collection,\n current,\n files,\n next,\n req,\n write,\n}: {\n collection: SanitizedCollectionConfig\n current: JsonObject\n files: FileToSave[]\n id: number | string\n next: JsonObject\n req: PayloadRequest\n write: () => Promise<T>\n}): Promise<T> => {\n const storedCurrent = withLegacyUploadFileData({\n collection,\n config: req.payload.config,\n doc: current,\n })\n const nextDoc = { ...storedCurrent, ...next }\n\n if (\n !hasLocalFileChange({\n collection,\n config: req.payload.config,\n current,\n hasNewFiles: files.length > 0,\n next: nextDoc,\n req,\n })\n ) {\n return write()\n }\n\n return runFileOperationPlan({\n cleanupStagedAfterWriteFailure: (objects) =>\n removeUnreferencedStagedObjects({ collection, objects, req }),\n req,\n stage: ({ trackStagedObject }) =>\n stageLocalUploadFiles({\n files,\n staticDir: collection.upload.staticDir!,\n trackStagedObject,\n }),\n write: async ({ trackStagedObject }) => {\n await archiveOutgoingLocalFiles({\n id,\n collection,\n current,\n next: nextDoc,\n req,\n trackStagedObject,\n })\n\n const result = await write()\n\n await scheduleUnreferencedFileCleanup({\n candidates: getOutgoingLocalFiles({\n collection,\n config: req.payload.config,\n current,\n next: nextDoc,\n req,\n }),\n collection,\n req,\n })\n\n return result\n },\n })\n}\n\n/** Also detects removals, which have no new upload to stage. */\nconst hasLocalFileChange = ({\n collection,\n config,\n current,\n hasNewFiles,\n next,\n req,\n}: {\n collection: SanitizedCollectionConfig\n config: Pick<Config, 'routes' | 'serverURL'>\n current: JsonObject\n hasNewFiles: boolean\n next: JsonObject\n req: PayloadRequest\n}): boolean =>\n !collection.upload.disableLocalStorage &&\n (hasNewFiles ||\n getOutgoingLocalFiles({\n collection,\n config,\n current,\n next,\n req,\n }).length > 0)\n\n/** Preserves outgoing local objects and repairs every retained version that referenced them. */\nexport const archiveOutgoingLocalFiles = async ({\n id,\n collection,\n current,\n next,\n req,\n trackStagedObject,\n}: {\n collection: SanitizedCollectionConfig\n current: JsonObject\n id: number | string\n next: JsonObject\n req: PayloadRequest\n trackStagedObject: (object: StagedObject) => void\n}): Promise<void> => {\n const outgoing = getOutgoingLocalFiles({\n collection,\n config: req.payload.config,\n current,\n next,\n req,\n })\n\n if (!outgoing.length || !collection.versions) {\n return\n }\n\n const currentWithState = withLegacyUploadFileData({\n collection,\n config: req.payload.config,\n doc: current,\n })\n let versions = await getVersions({ id, collection, req })\n\n if (!versions.length) {\n await saveVersion({\n id,\n collection,\n docWithLocales: currentWithState,\n draft: false,\n operation: 'update',\n payload: req.payload,\n req,\n })\n versions = await getVersions({ id, collection, req })\n }\n\n const replacements = new Map<string, string>()\n for (const file of outgoing) {\n const identity = getStoredFileIdentity(file)\n const newestVersion = versions.find(({ version }) => {\n const stored = withLegacyUploadFileData({\n collection,\n config: req.payload.config,\n doc: version,\n })\n\n return collectStoredFiles({ collection, doc: stored, req }).some(\n (candidate) => getStoredFileIdentity(candidate) === identity,\n )\n })\n\n if (!newestVersion) {\n continue\n }\n\n const directory = path.posix.dirname(file.key)\n let archivedKey: string\n let suffix = 0\n\n while (true) {\n const archivedFilename = getArchivedFilename({\n filename: path.posix.basename(file.key),\n versionID: suffix ? `${newestVersion.id}-${suffix}` : newestVersion.id,\n })\n archivedKey = directory === '.' ? archivedFilename : `${directory}/${archivedFilename}`\n\n try {\n await copyLocalFile({\n from: file.key,\n staticDir: collection.upload.staticDir!,\n to: archivedKey,\n })\n break\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'EEXIST') {\n throw err\n }\n suffix += 1\n }\n }\n trackStagedObject({\n key: archivedKey,\n remove: () => fs.rm(path.join(collection.upload.staticDir!, archivedKey), { force: true }),\n })\n replacements.set(identity, archivedKey)\n }\n\n for (const row of versions) {\n const stored = withLegacyUploadFileData({\n collection,\n config: req.payload.config,\n doc: row.version,\n })\n const version = replaceStoredFileReferences({\n files: collectStoredFiles({ collection, doc: stored, req }),\n replacements,\n version: stored,\n })\n\n if (!version) {\n continue\n }\n\n await req.payload.db.updateVersion({\n id: row.id,\n collection: collection.slug,\n req,\n versionData: {\n createdAt: row.createdAt,\n latest: row.latest,\n parent: row.parent,\n publishedLocale: row.publishedLocale,\n updatedAt: row.updatedAt,\n version,\n },\n })\n }\n}\n\nconst getOutgoingLocalFiles = ({\n collection,\n config,\n current,\n next,\n req,\n}: {\n collection: SanitizedCollectionConfig\n config: Pick<Config, 'routes' | 'serverURL'>\n current: JsonObject\n next: JsonObject\n req: PayloadRequest\n}): StoredFileList => {\n const currentWithState = withLegacyUploadFileData({ collection, config, doc: current })\n const currentFiles = collectStoredFiles({ collection, doc: currentWithState, req })\n const nextFiles = collectStoredFiles({ collection, doc: next, req, trustGenerated: true })\n const nextIdentities = new Set(nextFiles.map(getStoredFileIdentity))\n return currentFiles.filter((file) => !nextIdentities.has(getStoredFileIdentity(file)))\n}\n\nconst getVersions = async ({\n id,\n collection,\n req,\n}: {\n collection: SanitizedCollectionConfig\n id: number | string\n req: PayloadRequest\n}): Promise<VersionRow[]> => {\n const { docs } = await req.payload.db.findVersions<JsonObject>({\n collection: collection.slug,\n limit: 0,\n pagination: false,\n req,\n sort: '-updatedAt',\n where: { parent: { equals: id } },\n })\n\n return docs as VersionRow[]\n}\n\nexport const replaceStoredFileReferences = ({\n files,\n replacements,\n version,\n}: {\n files: StoredFileList\n replacements: Map<string, string>\n version: JsonObject\n}): JsonObject | undefined => {\n if (!files.length) {\n return\n }\n\n const archived = structuredClone(version)\n let hasChanged = false\n\n for (const file of files) {\n const archivedKey = replacements.get(getStoredFileIdentity(file))\n\n if (!archivedKey) {\n continue\n }\n\n const archivedFilename = path.posix.basename(archivedKey)\n\n for (const role of file.roles) {\n let stored: JsonObject | undefined\n switch (role.type) {\n case 'default':\n stored = archived\n break\n case 'original':\n stored = archived.original as JsonObject | undefined\n break\n case 'size':\n stored = (archived.variants as Record<string, JsonObject> | undefined)?.[role.sizeKey]\n break\n }\n\n if (typeof stored?.filename === 'string') {\n const previousFilename = stored.filename\n const directory = path.posix.dirname(previousFilename)\n const filename = directory === '.' ? archivedFilename : `${directory}/${archivedFilename}`\n stored.filename = filename\n stored.url = replaceURLFilename({\n filename,\n previousFilename,\n url: stored.url,\n })\n hasChanged = true\n }\n }\n }\n\n return hasChanged ? archived : undefined\n}\n\nconst replaceURLFilename = ({\n filename,\n previousFilename,\n url,\n}: {\n filename: string\n previousFilename: string\n url: unknown\n}): unknown => {\n if (typeof url !== 'string') {\n return url\n }\n\n const previous = encodeURIComponent(previousFilename)\n const index = url.lastIndexOf(`/${previous}`)\n\n return index < 0\n ? url\n : `${url.slice(0, index + 1)}${encodeURIComponent(filename)}${url.slice(index + 1 + previous.length)}`\n}\n"],"names":["fs","path","saveVersion","removeUnreferencedStagedObjects","scheduleUnreferencedFileCleanup","runFileOperationPlan","stageLocalUploadFiles","copyLocalFile","getArchivedFilename","collectStoredFiles","getStoredFileIdentity","withLegacyUploadFileData","runLocalFileUpdate","id","collection","current","files","next","req","write","storedCurrent","config","payload","doc","nextDoc","hasLocalFileChange","hasNewFiles","length","cleanupStagedAfterWriteFailure","objects","stage","trackStagedObject","staticDir","upload","archiveOutgoingLocalFiles","result","candidates","getOutgoingLocalFiles","disableLocalStorage","outgoing","versions","currentWithState","getVersions","docWithLocales","draft","operation","replacements","Map","file","identity","newestVersion","find","version","stored","some","candidate","directory","posix","dirname","key","archivedKey","suffix","archivedFilename","filename","basename","versionID","from","to","err","code","remove","rm","join","force","set","row","replaceStoredFileReferences","db","updateVersion","slug","versionData","createdAt","latest","parent","publishedLocale","updatedAt","currentFiles","nextFiles","trustGenerated","nextIdentities","Set","map","filter","has","docs","findVersions","limit","pagination","sort","where","equals","archived","structuredClone","hasChanged","get","role","roles","type","original","variants","sizeKey","previousFilename","url","replaceURLFilename","undefined","previous","encodeURIComponent","index","lastIndexOf","slice"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/uploads/fileVersioning/archive.ts"],"sourcesContent":["import fs from 'node:fs/promises'\nimport path from 'node:path'\n\nimport type { SanitizedCollectionConfig } from '../../collections/config/types.js'\nimport type { Config } from '../../config/types.js'\nimport type { JsonObject, PayloadRequest } from '../../types/index.js'\nimport type { FileToSave } from '../types.js'\nimport type { StagedObject } from './fileOperationManager.js'\nimport type { StoredFileList } from './types.js'\n\nimport { saveVersion } from '../../versions/saveVersion.js'\nimport { removeUnreferencedStagedObjects, scheduleUnreferencedFileCleanup } from './cleanup.js'\nimport { runFileOperationPlan, stageLocalUploadFiles } from './fileOperationManager.js'\nimport { copyLocalFile } from './localStorage.js'\nimport { getArchivedFilename } from './naming.js'\nimport {\n collectStoredFiles,\n getStoredFileIdentity,\n withLegacyUploadFileData,\n} from './storedFiles.js'\n\ntype VersionRow = {\n createdAt: string\n id: number | string\n latest?: boolean\n parent: number | string\n publishedLocale?: string\n updatedAt: string\n version: JsonObject\n}\n\nexport const runLocalFileUpdate = async <T>({\n id,\n collection,\n current,\n files,\n next,\n req,\n write,\n}: {\n collection: SanitizedCollectionConfig\n current: JsonObject\n files: FileToSave[]\n id: number | string\n next: JsonObject\n req: PayloadRequest\n write: () => Promise<T>\n}): Promise<T> => {\n const storedCurrent = withLegacyUploadFileData({\n collection,\n config: req.payload.config,\n doc: current,\n })\n const nextDoc = { ...storedCurrent, ...next }\n\n if (\n !hasLocalFileChange({\n collection,\n config: req.payload.config,\n current,\n hasNewFiles: files.length > 0,\n next: nextDoc,\n req,\n })\n ) {\n return write()\n }\n\n return runFileOperationPlan({\n cleanupStagedAfterWriteFailure: (objects) =>\n removeUnreferencedStagedObjects({ collection, objects, req }),\n req,\n stage: ({ trackStagedObject }) =>\n stageLocalUploadFiles({\n files,\n staticDir: collection.upload.staticDir!,\n trackStagedObject,\n }),\n write: async ({ trackStagedObject }) => {\n await archiveOutgoingLocalFiles({\n id,\n collection,\n current,\n next: nextDoc,\n req,\n trackStagedObject,\n })\n\n const result = await write()\n\n await scheduleUnreferencedFileCleanup({\n candidates: getOutgoingLocalFiles({\n collection,\n config: req.payload.config,\n current,\n next: nextDoc,\n req,\n }),\n collection,\n req,\n })\n\n return result\n },\n })\n}\n\n/** Also detects removals, which have no new upload to stage. */\nconst hasLocalFileChange = ({\n collection,\n config,\n current,\n hasNewFiles,\n next,\n req,\n}: {\n collection: SanitizedCollectionConfig\n config: Pick<Config, 'routes' | 'serverURL'>\n current: JsonObject\n hasNewFiles: boolean\n next: JsonObject\n req: PayloadRequest\n}): boolean =>\n !collection.upload.disableLocalStorage &&\n (hasNewFiles ||\n getOutgoingLocalFiles({\n collection,\n config,\n current,\n next,\n req,\n }).length > 0)\n\n/** Preserves outgoing local objects and repairs every retained version that referenced them. */\nexport const archiveOutgoingLocalFiles = async ({\n id,\n collection,\n current,\n next,\n req,\n trackStagedObject,\n}: {\n collection: SanitizedCollectionConfig\n current: JsonObject\n id: number | string\n next: JsonObject\n req: PayloadRequest\n trackStagedObject: (object: StagedObject) => void\n}): Promise<void> => {\n const outgoing = getOutgoingLocalFiles({\n collection,\n config: req.payload.config,\n current,\n next,\n req,\n })\n\n if (!outgoing.length || !collection.versions) {\n return\n }\n\n const currentWithState = withLegacyUploadFileData({\n collection,\n config: req.payload.config,\n doc: current,\n })\n let versions = await getVersions({ id, collection, req })\n\n if (!versions.length) {\n await saveVersion({\n id,\n collection,\n docWithLocales: currentWithState,\n draft: false,\n operation: 'update',\n payload: req.payload,\n req,\n })\n versions = await getVersions({ id, collection, req })\n }\n\n const replacements = new Map<string, string>()\n for (const file of outgoing) {\n const identity = getStoredFileIdentity(file)\n const newestVersion = versions.find(({ version }) => {\n const stored = withLegacyUploadFileData({\n collection,\n config: req.payload.config,\n doc: version,\n })\n\n return collectStoredFiles({ collection, doc: stored, req }).some(\n (candidate) => getStoredFileIdentity(candidate) === identity,\n )\n })\n\n if (!newestVersion) {\n continue\n }\n\n const directory = path.posix.dirname(file.key)\n let archivedKey: string\n let suffix = 0\n\n while (true) {\n const archivedFilename = getArchivedFilename({\n filename: path.posix.basename(file.key),\n versionID: suffix ? `${newestVersion.id}-${suffix}` : newestVersion.id,\n })\n archivedKey = directory === '.' ? archivedFilename : `${directory}/${archivedFilename}`\n\n try {\n await copyLocalFile({\n from: file.key,\n staticDir: collection.upload.staticDir!,\n to: archivedKey,\n })\n break\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'EEXIST') {\n throw err\n }\n suffix += 1\n }\n }\n trackStagedObject({\n key: archivedKey,\n remove: () => fs.rm(path.join(collection.upload.staticDir!, archivedKey), { force: true }),\n })\n replacements.set(identity, archivedKey)\n }\n\n for (const row of versions) {\n const stored = withLegacyUploadFileData({\n collection,\n config: req.payload.config,\n doc: row.version,\n })\n const version = replaceStoredFileReferences({\n files: collectStoredFiles({ collection, doc: stored, req }),\n replacements,\n version: stored,\n })\n\n if (!version) {\n continue\n }\n\n await req.payload.db.updateVersion({\n id: row.id,\n collection: collection.slug,\n req,\n versionData: {\n createdAt: row.createdAt,\n latest: row.latest,\n parent: row.parent,\n publishedLocale: row.publishedLocale,\n updatedAt: row.updatedAt,\n version,\n },\n })\n }\n}\n\nconst getOutgoingLocalFiles = ({\n collection,\n config,\n current,\n next,\n req,\n}: {\n collection: SanitizedCollectionConfig\n config: Pick<Config, 'routes' | 'serverURL'>\n current: JsonObject\n next: JsonObject\n req: PayloadRequest\n}): StoredFileList => {\n const currentWithState = withLegacyUploadFileData({ collection, config, doc: current })\n const currentFiles = collectStoredFiles({ collection, doc: currentWithState, req })\n const nextFiles = collectStoredFiles({ collection, doc: next, req, trustGenerated: true })\n const nextIdentities = new Set(nextFiles.map(getStoredFileIdentity))\n return currentFiles.filter((file) => !nextIdentities.has(getStoredFileIdentity(file)))\n}\n\nconst getVersions = async ({\n id,\n collection,\n req,\n}: {\n collection: SanitizedCollectionConfig\n id: number | string\n req: PayloadRequest\n}): Promise<VersionRow[]> => {\n const { docs } = await req.payload.db.findVersions<JsonObject>({\n collection: collection.slug,\n limit: 0,\n pagination: false,\n req,\n sort: '-updatedAt',\n where: { parent: { equals: id } },\n })\n\n return docs\n}\n\nexport const replaceStoredFileReferences = ({\n files,\n replacements,\n version,\n}: {\n files: StoredFileList\n replacements: Map<string, string>\n version: JsonObject\n}): JsonObject | undefined => {\n if (!files.length) {\n return\n }\n\n const archived = structuredClone(version)\n let hasChanged = false\n\n for (const file of files) {\n const archivedKey = replacements.get(getStoredFileIdentity(file))\n\n if (!archivedKey) {\n continue\n }\n\n const archivedFilename = path.posix.basename(archivedKey)\n\n for (const role of file.roles) {\n let stored: JsonObject | undefined\n switch (role.type) {\n case 'default':\n stored = archived\n break\n case 'original':\n stored = archived.original as JsonObject | undefined\n break\n case 'size':\n stored = (archived.variants as Record<string, JsonObject> | undefined)?.[role.sizeKey]\n break\n }\n\n if (typeof stored?.filename === 'string') {\n const previousFilename = stored.filename\n const directory = path.posix.dirname(previousFilename)\n const filename = directory === '.' ? archivedFilename : `${directory}/${archivedFilename}`\n stored.filename = filename\n stored.url = replaceURLFilename({\n filename,\n previousFilename,\n url: stored.url,\n })\n hasChanged = true\n }\n }\n }\n\n return hasChanged ? archived : undefined\n}\n\nconst replaceURLFilename = ({\n filename,\n previousFilename,\n url,\n}: {\n filename: string\n previousFilename: string\n url: unknown\n}): unknown => {\n if (typeof url !== 'string') {\n return url\n }\n\n const previous = encodeURIComponent(previousFilename)\n const index = url.lastIndexOf(`/${previous}`)\n\n return index < 0\n ? url\n : `${url.slice(0, index + 1)}${encodeURIComponent(filename)}${url.slice(index + 1 + previous.length)}`\n}\n"],"names":["fs","path","saveVersion","removeUnreferencedStagedObjects","scheduleUnreferencedFileCleanup","runFileOperationPlan","stageLocalUploadFiles","copyLocalFile","getArchivedFilename","collectStoredFiles","getStoredFileIdentity","withLegacyUploadFileData","runLocalFileUpdate","id","collection","current","files","next","req","write","storedCurrent","config","payload","doc","nextDoc","hasLocalFileChange","hasNewFiles","length","cleanupStagedAfterWriteFailure","objects","stage","trackStagedObject","staticDir","upload","archiveOutgoingLocalFiles","result","candidates","getOutgoingLocalFiles","disableLocalStorage","outgoing","versions","currentWithState","getVersions","docWithLocales","draft","operation","replacements","Map","file","identity","newestVersion","find","version","stored","some","candidate","directory","posix","dirname","key","archivedKey","suffix","archivedFilename","filename","basename","versionID","from","to","err","code","remove","rm","join","force","set","row","replaceStoredFileReferences","db","updateVersion","slug","versionData","createdAt","latest","parent","publishedLocale","updatedAt","currentFiles","nextFiles","trustGenerated","nextIdentities","Set","map","filter","has","docs","findVersions","limit","pagination","sort","where","equals","archived","structuredClone","hasChanged","get","role","roles","type","original","variants","sizeKey","previousFilename","url","replaceURLFilename","undefined","previous","encodeURIComponent","index","lastIndexOf","slice"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/uploads/fileVersioning/resolveHistoricalFile.ts"],"sourcesContent":["import type { Collection, TypeWithID } from '../../collections/config/types.js'\nimport type { JsonObject, PayloadRequest } from '../../types/index.js'\n\nimport { Forbidden } from '../../errors/Forbidden.js'\nimport { NotFound } from '../../errors/NotFound.js'\nimport { checkFileAccess } from '../checkFileAccess.js'\nimport { collectStoredFiles, withLegacyCloudUploadFileData } from './storedFiles.js'\n\ntype StoredVersion = {\n id: number | string\n parent: number | string\n version: JsonObject\n}\n\n/** Finds an exact saved object reference, including sizes removed from the current config. */\nexport const resolveHistoricalFile = async ({\n collection,\n filename,\n prefix,\n req,\n versionID,\n}: {\n collection: Collection\n filename: string\n prefix?: string\n req: PayloadRequest\n versionID: string\n}): Promise<TypeWithID> => {\n const { docs } = await req.payload.db.findVersions<JsonObject>({\n collection: collection.config.slug,\n limit: 1,\n req,\n where: { id: { equals: versionID } },\n })\n const matched = docs[0]\n\n // Check both policies before exposing a missing version or a filename mismatch.\n await checkFileAccess({ collection, documentID: matched?.parent, filename, req })\n const authorized = await req.payload.findVersionByID({\n id: versionID,\n collection: collection.config.slug,\n depth: 0,\n overrideAccess: false,\n req,\n showHiddenFields: true,\n })\n\n if (!authorized) {\n throw new Forbidden(req.t)\n }\n\n if (!matched) {\n throw new NotFound(req.t)\n }\n\n const historical = await resolveVersionFile({ collection, filename, prefix, req, row: matched })\n\n if (!historical) {\n throw new NotFound(req.t)\n }\n\n return historical\n}\n\nconst resolveVersionFile = async ({\n collection,\n filename,\n prefix,\n req,\n row,\n}: {\n collection: Collection\n filename: string\n prefix?: string\n req: PayloadRequest\n row: StoredVersion\n}): Promise<TypeWithID | undefined> => {\n const saved = await withLegacyCloudUploadFileData({\n collection: collection.config,\n doc: row.version,\n req,\n })\n const hasFile = collectStoredFiles({ collection: collection.config, doc: saved, req }).some(\n (file) =>\n file.roles.some((role) => {\n const representation =\n role.type === 'size'\n ? (saved.variants as Record<string, JsonObject> | undefined)?.[role.sizeKey]\n : role.type === 'original'\n ? (saved.original as JsonObject | undefined)\n : saved\n return (\n representation?.filename === filename && (!prefix || representation.prefix === prefix)\n )\n }),\n )\n\n if (!hasFile) {\n return\n }\n\n return { ...saved, id: row.parent } as TypeWithID\n}\n"],"names":["Forbidden","NotFound","checkFileAccess","collectStoredFiles","withLegacyCloudUploadFileData","resolveHistoricalFile","collection","filename","prefix","req","versionID","docs","payload","db","findVersions","config","slug","limit","where","id","equals","matched","documentID","parent","authorized","findVersionByID","depth","overrideAccess","showHiddenFields","t","historical","resolveVersionFile","row","saved","doc","version","hasFile","some","file","roles","role","representation","type","variants","sizeKey","original"],"mappings":"AAGA,SAASA,SAAS,QAAQ,4BAA2B;AACrD,SAASC,QAAQ,QAAQ,2BAA0B;AACnD,SAASC,eAAe,QAAQ,wBAAuB;AACvD,SAASC,kBAAkB,EAAEC,6BAA6B,QAAQ,mBAAkB;AAQpF,4FAA4F,GAC5F,OAAO,MAAMC,wBAAwB,OAAO,EAC1CC,UAAU,EACVC,QAAQ,EACRC,MAAM,EACNC,GAAG,EACHC,SAAS,EAOV;IACC,MAAM,EAAEC,IAAI,EAAE,GAAG,MAAMF,IAAIG,OAAO,CAACC,EAAE,CAACC,YAAY,CAAa;QAC7DR,YAAYA,WAAWS,MAAM,CAACC,IAAI;QAClCC,OAAO;QACPR;QACAS,OAAO;YAAEC,IAAI;gBAAEC,QAAQV;YAAU;QAAE;IACrC;IACA,MAAMW,UAAUV,IAAI,CAAC,EAAE;IAEvB,gFAAgF;IAChF,MAAMT,gBAAgB;QAAEI;QAAYgB,YAAYD,SAASE;QAAQhB;QAAUE;IAAI;IAC/E,MAAMe,aAAa,MAAMf,IAAIG,OAAO,CAACa,eAAe,CAAC;QACnDN,IAAIT;QACJJ,YAAYA,WAAWS,MAAM,CAACC,IAAI;QAClCU,OAAO;QACPC,gBAAgB;QAChBlB;QACAmB,kBAAkB;IACpB;IAEA,IAAI,CAACJ,YAAY;QACf,MAAM,IAAIxB,UAAUS,IAAIoB,CAAC;IAC3B;IAEA,IAAI,CAACR,SAAS;QACZ,MAAM,IAAIpB,SAASQ,IAAIoB,CAAC;IAC1B;IAEA,MAAMC,aAAa,MAAMC,mBAAmB;QAAEzB;QAAYC;QAAUC;QAAQC;QAAKuB,KAAKX;IAAQ;IAE9F,IAAI,CAACS,YAAY;QACf,MAAM,IAAI7B,SAASQ,IAAIoB,CAAC;IAC1B;IAEA,OAAOC;AACT,EAAC;AAED,MAAMC,qBAAqB,OAAO,EAChCzB,UAAU,EACVC,QAAQ,EACRC,MAAM,EACNC,GAAG,EACHuB,GAAG,EAOJ;IACC,MAAMC,QAAQ,MAAM7B,8BAA8B;QAChDE,YAAYA,WAAWS,MAAM;QAC7BmB,KAAKF,IAAIG,OAAO;QAChB1B;IACF;IACA,MAAM2B,UAAUjC,mBAAmB;QAAEG,YAAYA,WAAWS,MAAM;QAAEmB,KAAKD;QAAOxB;IAAI,GAAG4B,IAAI,CACzF,CAACC,OACCA,KAAKC,KAAK,CAACF,IAAI,CAAC,CAACG;YACf,MAAMC,iBACJD,KAAKE,IAAI,KAAK,SACTT,MAAMU,QAAQ,EAA6C,CAACH,KAAKI,OAAO,CAAC,GAC1EJ,KAAKE,IAAI,KAAK,aACXT,MAAMY,QAAQ,GACfZ;YACR,OACEQ,gBAAgBlC,aAAaA,YAAa,CAAA,CAACC,UAAUiC,eAAejC,MAAM,KAAKA,MAAK;QAExF;IAGJ,IAAI,CAAC4B,SAAS;QACZ;IACF;IAEA,OAAO;QAAE,GAAGH,KAAK;QAAEd,IAAIa,IAAIT,MAAM;IAAC;AACpC"}
1
+ {"version":3,"sources":["../../../src/uploads/fileVersioning/resolveHistoricalFile.ts"],"sourcesContent":["import type { Collection, TypeWithID } from '../../collections/config/types.js'\nimport type { JsonObject, PayloadRequest } from '../../types/index.js'\n\nimport { Forbidden } from '../../errors/Forbidden.js'\nimport { NotFound } from '../../errors/NotFound.js'\nimport { checkFileAccess } from '../checkFileAccess.js'\nimport { collectStoredFiles, withLegacyCloudUploadFileData } from './storedFiles.js'\n\ntype StoredVersion = {\n id: number | string\n parent: number | string\n version: JsonObject\n}\n\n/** Finds an exact saved object reference, including sizes removed from the current config. */\nexport const resolveHistoricalFile = async ({\n collection,\n filename,\n prefix,\n req,\n versionID,\n}: {\n collection: Collection\n filename: string\n prefix?: string\n req: PayloadRequest\n versionID: string\n}): Promise<TypeWithID> => {\n const { docs } = await req.payload.db.findVersions<JsonObject>({\n collection: collection.config.slug,\n limit: 1,\n req,\n where: { id: { equals: versionID } },\n })\n const matched = docs[0]\n\n // Check both policies before exposing a missing version or a filename mismatch.\n await checkFileAccess({ collection, documentID: matched?.parent, filename, req })\n const authorized = await req.payload.findVersionByID({\n id: versionID,\n collection: collection.config.slug,\n depth: 0,\n overrideAccess: false,\n req,\n showHiddenFields: true,\n })\n\n if (!authorized) {\n throw new Forbidden(req.t)\n }\n\n if (!matched) {\n throw new NotFound(req.t)\n }\n\n const historical = await resolveVersionFile({ collection, filename, prefix, req, row: matched })\n\n if (!historical) {\n throw new NotFound(req.t)\n }\n\n return historical\n}\n\nconst resolveVersionFile = async ({\n collection,\n filename,\n prefix,\n req,\n row,\n}: {\n collection: Collection\n filename: string\n prefix?: string\n req: PayloadRequest\n row: StoredVersion\n}): Promise<TypeWithID | undefined> => {\n const saved = await withLegacyCloudUploadFileData({\n collection: collection.config,\n doc: row.version,\n req,\n })\n const hasFile = collectStoredFiles({ collection: collection.config, doc: saved, req }).some(\n (file) =>\n file.roles.some((role) => {\n const representation =\n role.type === 'size'\n ? (saved.variants as Record<string, JsonObject> | undefined)?.[role.sizeKey]\n : role.type === 'original'\n ? (saved.original as JsonObject | undefined)\n : saved\n return (\n representation?.filename === filename && (!prefix || representation.prefix === prefix)\n )\n }),\n )\n\n if (!hasFile) {\n return\n }\n\n return { ...saved, id: row.parent }\n}\n"],"names":["Forbidden","NotFound","checkFileAccess","collectStoredFiles","withLegacyCloudUploadFileData","resolveHistoricalFile","collection","filename","prefix","req","versionID","docs","payload","db","findVersions","config","slug","limit","where","id","equals","matched","documentID","parent","authorized","findVersionByID","depth","overrideAccess","showHiddenFields","t","historical","resolveVersionFile","row","saved","doc","version","hasFile","some","file","roles","role","representation","type","variants","sizeKey","original"],"mappings":"AAGA,SAASA,SAAS,QAAQ,4BAA2B;AACrD,SAASC,QAAQ,QAAQ,2BAA0B;AACnD,SAASC,eAAe,QAAQ,wBAAuB;AACvD,SAASC,kBAAkB,EAAEC,6BAA6B,QAAQ,mBAAkB;AAQpF,4FAA4F,GAC5F,OAAO,MAAMC,wBAAwB,OAAO,EAC1CC,UAAU,EACVC,QAAQ,EACRC,MAAM,EACNC,GAAG,EACHC,SAAS,EAOV;IACC,MAAM,EAAEC,IAAI,EAAE,GAAG,MAAMF,IAAIG,OAAO,CAACC,EAAE,CAACC,YAAY,CAAa;QAC7DR,YAAYA,WAAWS,MAAM,CAACC,IAAI;QAClCC,OAAO;QACPR;QACAS,OAAO;YAAEC,IAAI;gBAAEC,QAAQV;YAAU;QAAE;IACrC;IACA,MAAMW,UAAUV,IAAI,CAAC,EAAE;IAEvB,gFAAgF;IAChF,MAAMT,gBAAgB;QAAEI;QAAYgB,YAAYD,SAASE;QAAQhB;QAAUE;IAAI;IAC/E,MAAMe,aAAa,MAAMf,IAAIG,OAAO,CAACa,eAAe,CAAC;QACnDN,IAAIT;QACJJ,YAAYA,WAAWS,MAAM,CAACC,IAAI;QAClCU,OAAO;QACPC,gBAAgB;QAChBlB;QACAmB,kBAAkB;IACpB;IAEA,IAAI,CAACJ,YAAY;QACf,MAAM,IAAIxB,UAAUS,IAAIoB,CAAC;IAC3B;IAEA,IAAI,CAACR,SAAS;QACZ,MAAM,IAAIpB,SAASQ,IAAIoB,CAAC;IAC1B;IAEA,MAAMC,aAAa,MAAMC,mBAAmB;QAAEzB;QAAYC;QAAUC;QAAQC;QAAKuB,KAAKX;IAAQ;IAE9F,IAAI,CAACS,YAAY;QACf,MAAM,IAAI7B,SAASQ,IAAIoB,CAAC;IAC1B;IAEA,OAAOC;AACT,EAAC;AAED,MAAMC,qBAAqB,OAAO,EAChCzB,UAAU,EACVC,QAAQ,EACRC,MAAM,EACNC,GAAG,EACHuB,GAAG,EAOJ;IACC,MAAMC,QAAQ,MAAM7B,8BAA8B;QAChDE,YAAYA,WAAWS,MAAM;QAC7BmB,KAAKF,IAAIG,OAAO;QAChB1B;IACF;IACA,MAAM2B,UAAUjC,mBAAmB;QAAEG,YAAYA,WAAWS,MAAM;QAAEmB,KAAKD;QAAOxB;IAAI,GAAG4B,IAAI,CACzF,CAACC,OACCA,KAAKC,KAAK,CAACF,IAAI,CAAC,CAACG;YACf,MAAMC,iBACJD,KAAKE,IAAI,KAAK,SACTT,MAAMU,QAAQ,EAA6C,CAACH,KAAKI,OAAO,CAAC,GAC1EJ,KAAKE,IAAI,KAAK,aACXT,MAAMY,QAAQ,GACfZ;YACR,OACEQ,gBAAgBlC,aAAaA,YAAa,CAAA,CAACC,UAAUiC,eAAejC,MAAM,KAAKA,MAAK;QAExF;IAGJ,IAAI,CAAC4B,SAAS;QACZ;IACF;IAEA,OAAO;QAAE,GAAGH,KAAK;QAAEd,IAAIa,IAAIT,MAAM;IAAC;AACpC"}
@@ -1 +1 @@
1
- {"version":3,"file":"generateFileData.d.ts","sourceRoot":"","sources":["../../src/uploads/generateFileData.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAc,MAAM,gCAAgC,CAAA;AAC5E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,KAAK,EAAY,cAAc,EAAE,MAAM,mBAAmB,CAAA;AACjE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAA;AAEnE,OAAO,KAAK,EAAuB,UAAU,EAAe,MAAM,YAAY,CAAA;AAwB9E,KAAK,IAAI,CAAC,CAAC,IAAI;IACb,UAAU,EAAE,UAAU,CAAA;IACtB,MAAM,EAAE,eAAe,CAAA;IACvB,IAAI,EAAE,CAAC,CAAA;IACP,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,oBAAoB,CAAC,EAAE,oBAAoB,CAAA;IAC3C,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,SAAS,EAAE,QAAQ,GAAG,QAAQ,CAAA;IAC9B,WAAW,CAAC,EAAE,CAAC,CAAA;IACf,sBAAsB,CAAC,EAAE,OAAO,CAAA;IAChC,GAAG,EAAE,cAAc,CAAA;IACnB,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAC7B,CAAA;AAED,KAAK,MAAM,CAAC,CAAC,IAAI,OAAO,CAAC;IACvB,IAAI,EAAE,CAAC,CAAA;IACP,KAAK,EAAE,UAAU,EAAE,CAAA;CACpB,CAAC,CAAA;AA6BF,MAAM,MAAM,gBAAgB,GACxB;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,kBAAkB,CAAA;CAAE,GAChD;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,CAAA;AAEzB;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,+DAIjC;IACD,mBAAmB,EAAE,OAAO,CAAA;IAC5B,kBAAkB,EAAE,OAAO,CAAA;IAC3B,YAAY,EAAE,MAAM,GAAG,SAAS,CAAA;CACjC,KAAG,gBAQH,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,GAAU,CAAC,4KAWrC,IAAI,CAAC,CAAC,CAAC,KAAG,MAAM,CAAC,CAAC,CAsoBpB,CAAA"}
1
+ {"version":3,"file":"generateFileData.d.ts","sourceRoot":"","sources":["../../src/uploads/generateFileData.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAc,MAAM,gCAAgC,CAAA;AAC5E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,KAAK,EAAY,cAAc,EAAE,MAAM,mBAAmB,CAAA;AACjE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAA;AAEnE,OAAO,KAAK,EAAuB,UAAU,EAAe,MAAM,YAAY,CAAA;AAwB9E,KAAK,IAAI,CAAC,CAAC,IAAI;IACb,UAAU,EAAE,UAAU,CAAA;IACtB,MAAM,EAAE,eAAe,CAAA;IACvB,IAAI,EAAE,CAAC,CAAA;IACP,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,oBAAoB,CAAC,EAAE,oBAAoB,CAAA;IAC3C,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,SAAS,EAAE,QAAQ,GAAG,QAAQ,CAAA;IAC9B,WAAW,CAAC,EAAE,CAAC,CAAA;IACf,sBAAsB,CAAC,EAAE,OAAO,CAAA;IAChC,GAAG,EAAE,cAAc,CAAA;IACnB,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAC7B,CAAA;AAED,KAAK,MAAM,CAAC,CAAC,IAAI,OAAO,CAAC;IACvB,IAAI,EAAE,CAAC,CAAA;IACP,KAAK,EAAE,UAAU,EAAE,CAAA;CACpB,CAAC,CAAA;AA6BF,MAAM,MAAM,gBAAgB,GACxB;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,kBAAkB,CAAA;CAAE,GAChD;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,CAAA;AAEzB;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,+DAIjC;IACD,mBAAmB,EAAE,OAAO,CAAA;IAC5B,kBAAkB,EAAE,OAAO,CAAA;IAC3B,YAAY,EAAE,MAAM,GAAG,SAAS,CAAA;CACjC,KAAG,gBAQH,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,GAAU,CAAC,4KAWrC,IAAI,CAAC,CAAC,CAAC,KAAG,MAAM,CAAC,CAAC,CAooBpB,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/uploads/generateFileData.ts"],"sourcesContent":["import { fileTypeFromBuffer } from 'file-type'\nimport fs from 'fs/promises'\nimport { randomUUID } from 'node:crypto'\nimport { openAsBlob } from 'node:fs'\n\nimport type { Collection, TypeWithID } from '../collections/config/types.js'\nimport type { SanitizedConfig } from '../config/types.js'\nimport type { Document, PayloadRequest } from '../types/index.js'\nimport type { ExternalUploadSource } from './sanitizeUploadData.js'\nimport type { PreparedUploadTransformation } from './transformers/uploadTransformerBridge.js'\nimport type { FileData, FileSizes, FileToSave, UploadEdits } from './types.js'\n\nimport { FileRetrievalError, FileUploadError, Forbidden, MissingFile } from '../errors/index.js'\nimport { formatAdminURL } from '../utilities/formatAdminURL.js'\nimport { isNumber } from '../utilities/isNumber.js'\nimport { canResizeImage } from './canResizeImage.js'\nimport { checkFileRestrictions } from './checkFileRestrictions.js'\nimport { downloadFileToBuffer } from './downloadFileToBuffer.js'\nimport { getOriginalFilename } from './fileVersioning/naming.js'\nimport { generateImageSizeFilename } from './generateImageSizeFilename.js'\nimport { getFileByPath } from './getFileByPath.js'\nimport { getFileExtension, getSanitizedUploadFilename } from './getFileTypeIdentity.js'\nimport { getImageSize } from './getImageSize.js'\nimport { getSafeFileName, incrementName } from './getSafeFilename.js'\nimport { hasCropOrResizeEdit } from './hasCropOrResizeEdit.js'\nimport { hasFullFileContents } from './hasFullFileContents.js'\nimport { isProcessableImage } from './isProcessableImage.js'\nimport { parseFilename } from './parseFilename.js'\nimport { planTransformerPipeline } from './transformers/planTransformerPipeline.js'\nimport { transformUploadFile } from './transformers/transformUploadFile.js'\nimport {\n getUploadTransformerInternal,\n setUploadFilePath,\n} from './transformers/uploadTransformerBridge.js'\ntype Args<T> = {\n collection: Collection\n config: SanitizedConfig\n data: T\n draft?: boolean\n externalUploadSource?: ExternalUploadSource\n isDuplicating?: boolean\n operation: 'create' | 'update'\n originalDoc?: T\n overwriteExistingFiles?: boolean\n req: PayloadRequest\n throwOnMissingFile?: boolean\n}\n\ntype Result<T> = Promise<{\n data: T\n files: FileToSave[]\n}>\n\nconst shouldReupload = (\n uploadEdits: undefined | UploadEdits,\n fileData: Record<string, unknown> | undefined,\n) => {\n if (!fileData || !uploadEdits) {\n return false\n }\n\n if (hasCropOrResizeEdit(uploadEdits)) {\n return true\n }\n\n // Since uploadEdits always has focalPoint, compare to the value in the data if it was changed\n if (uploadEdits.focalPoint) {\n const incomingFocalX = uploadEdits.focalPoint.x\n const incomingFocalY = uploadEdits.focalPoint.y\n\n const currentFocalX = 'focalX' in fileData && fileData.focalX\n const currentFocalY = 'focalY' in fileData && fileData.focalY\n\n const isEqual = incomingFocalX === currentFocalX && incomingFocalY === currentFocalY\n return !isEqual\n }\n\n return false\n}\n\nexport type TempFileHandling =\n | { sourcePath: string; type: 'copyFromTempFile' }\n | { type: 'skip' }\n | { type: 'useBuffer' }\n\n/**\n * Decides how to get a file's bytes onto disk when no transformer rewrote it (a transformed\n * file is already an in-memory buffer, so it always takes the `useBuffer` path). Copies straight\n * from the temp file when possible, rather than reading a potentially large temp file into memory\n * just to write it back out - see the `generateFileData` function doc for why.\n */\nexport const resolveTempFileHandling = ({\n disableLocalStorage,\n hasProcessedBuffer,\n tempFilePath,\n}: {\n disableLocalStorage: boolean\n hasProcessedBuffer: boolean\n tempFilePath: string | undefined\n}): TempFileHandling => {\n if (hasProcessedBuffer || !tempFilePath) {\n return { type: 'useBuffer' }\n }\n\n return disableLocalStorage\n ? { type: 'skip' }\n : { type: 'copyFromTempFile', sourcePath: tempFilePath }\n}\n\n/**\n * Builds the document's file metadata and the list of files to write to disk.\n *\n * A large client upload may arrive as a temp file instead of an in-memory buffer (see\n * getFileFromUploadInstructions.ts), and `file.data` can hold only a partial probe rather than\n * the full file. To avoid loading such files into memory unnecessarily, no transformer runs\n * unless the full bytes are available, this skips reading a temp file entirely when local\n * storage is disabled, and copies it straight to its destination when local storage is enabled.\n */\nexport const generateFileData = async <T>({\n collection: { config: collectionConfig },\n data,\n draft,\n externalUploadSource,\n isDuplicating,\n operation,\n originalDoc,\n overwriteExistingFiles,\n req,\n throwOnMissingFile,\n}: Args<T>): Result<T> => {\n if (!collectionConfig.upload) {\n return {\n data,\n files: [],\n }\n }\n\n const { serverURL } = req.payload.config\n\n let file = isDuplicating ? undefined : req.file\n\n const uploadEdits = parseUploadEditsFromReqOrIncomingData({\n data,\n isDuplicating,\n operation,\n // Only a duplication source informs edit parsing. Updates now also pass `originalDoc` so the\n // stored file can be reprocessed, and that must not change which edits are applied.\n originalDoc: isDuplicating ? originalDoc : undefined,\n req,\n })\n\n const {\n disableLocalStorage,\n focalPoint: focalPointEnabled = true,\n staticDir,\n } = collectionConfig.upload\n const hasManagedCloudStorage = Boolean(collectionConfig.upload.fileOperations)\n const uploadReference = file?.uploadReference\n const hasProviderDirectReference =\n uploadReference && typeof uploadReference === 'object' && !('uploadId' in uploadReference)\n const verifiedOriginal = req.context?._payloadVerifiedProviderOriginal as\n | {\n _objectKey?: string\n filename: string\n key: string\n prefix?: string\n signedReceipt: string\n }\n | undefined\n const providerOriginal =\n hasProviderDirectReference &&\n 'signedReceipt' in uploadReference &&\n uploadReference.signedReceipt === verifiedOriginal?.signedReceipt &&\n verifiedOriginal &&\n typeof collectionConfig.upload.adapter === 'string'\n ? {\n _objectKey: verifiedOriginal._objectKey,\n filename: verifiedOriginal.filename,\n key: verifiedOriginal.key,\n prefix: verifiedOriginal.prefix,\n }\n : undefined\n const shouldStageCloudFiles = hasManagedCloudStorage && !hasProviderDirectReference\n\n const staticPath = staticDir\n\n const incomingFileData: Document = isDuplicating ? originalDoc : data\n const currentFileData = (operation === 'update' ? originalDoc : incomingFileData) as\n | FileData\n | undefined\n const retainedOriginal =\n operation === 'update' &&\n !file &&\n !externalUploadSource &&\n typeof currentFileData?.original?.filename === 'string' &&\n typeof currentFileData.original.url === 'string'\n ? currentFileData.original\n : undefined\n const fileSourceData = externalUploadSource ?? retainedOriginal ?? currentFileData\n let isLocalFile = false\n\n if (\n !file &&\n fileSourceData &&\n (externalUploadSource ||\n isDuplicating ||\n shouldReupload(uploadEdits, incomingFileData as Record<string, unknown>))\n ) {\n const { filename, url } = fileSourceData\n if (filename && (filename.includes('../') || filename.includes('..\\\\'))) {\n throw new Forbidden(req.t)\n }\n\n if ((serverURL && url?.startsWith(serverURL)) || url?.startsWith('/')) {\n isLocalFile = true\n }\n\n try {\n if (!externalUploadSource && !disableLocalStorage && isLocalFile) {\n // File is stored locally\n const filePath = `${staticPath}/${filename}`\n const response = await getFileByPath(filePath)\n file = response\n overwriteExistingFiles = true\n } else if (filename && retainedOriginal && hasManagedCloudStorage) {\n const { retrieveFileResponse } = await import('./endpoints/getFile.js')\n const response = await retrieveFileResponse({\n collection: { config: collectionConfig },\n doc: originalDoc as TypeWithID,\n filename,\n operation: 'transform',\n req,\n })\n if (!response.ok) {\n throw new Error(`Unable to read retained original ${filename}`)\n }\n const buffer = Buffer.from(await response.arrayBuffer())\n file = {\n name: filename,\n data: buffer,\n mimetype: retainedOriginal.mimeType,\n size: buffer.length,\n }\n overwriteExistingFiles = true\n } else if (filename && url) {\n // File is remote\n file = await downloadFileToBuffer({\n data: fileSourceData,\n req,\n uploadConfig: collectionConfig.upload,\n })\n overwriteExistingFiles = !externalUploadSource\n }\n } catch (err: unknown) {\n throw new FileRetrievalError(req.t, err instanceof Error ? err.message : undefined)\n }\n\n if (file && retainedOriginal && currentFileData?.filename) {\n file = { ...file, name: currentFileData.filename }\n }\n }\n\n if (isDuplicating) {\n overwriteExistingFiles = false\n }\n\n if (!file) {\n if (throwOnMissingFile) {\n throw new MissingFile(req.t)\n }\n\n return {\n data: incomingFileData!,\n files: [],\n }\n }\n\n const detectedFileType = await checkFileRestrictions({\n collection: collectionConfig,\n file,\n req,\n })\n\n const shouldUseDetectedFileType =\n detectedFileType &&\n (isProcessableImage(file.mimetype) || isProcessableImage(detectedFileType.mime))\n\n if (shouldUseDetectedFileType && detectedFileType.mime !== file.mimetype) {\n file = { ...file, mimetype: detectedFileType.mime }\n }\n\n if (!disableLocalStorage) {\n await fs.mkdir(staticPath!, { recursive: true })\n }\n\n let newData = incomingFileData as T\n const filesToSave: FileToSave[] = []\n const fileData: Partial<FileData> = {}\n const crop = uploadEdits?.crop\n const isResettingCrop =\n retainedOriginal &&\n crop?.unit === '%' &&\n Number(crop.width) === 100 &&\n Number(crop.height) === 100 &&\n Number(crop.x) === 0 &&\n Number(crop.y) === 0 &&\n (uploadEdits.widthInPixels === undefined ||\n Number(uploadEdits.widthInPixels) === retainedOriginal.width) &&\n (uploadEdits.heightInPixels === undefined ||\n Number(uploadEdits.heightInPixels) === retainedOriginal.height)\n const hasFocalPointChange =\n uploadEdits?.focalPoint &&\n (Number(uploadEdits.focalPoint.x) !== (currentFileData?.focalX ?? 50) ||\n Number(uploadEdits.focalPoint.y) !== (currentFileData?.focalY ?? 50))\n const editsForTransformer = isResettingCrop\n ? { ...uploadEdits, crop: undefined, heightInPixels: undefined, widthInPixels: undefined }\n : uploadEdits\n\n try {\n const pipeline = await planTransformerPipeline({\n args: {\n collectionSlug: collectionConfig.slug,\n mimeType: file.mimetype,\n operation: 'upload',\n req,\n },\n capability: 'transformFile',\n transformers: req.payload.config.upload?.transformers ?? [],\n })\n\n // A large client upload can arrive as a bounded probe alongside a temp file. Transformers\n // need the whole file, so leave such an upload untouched rather than buffering it.\n const canRunTransformers = pipeline.length > 0 && hasFullFileContents(file)\n\n const bridgeTransformers = canRunTransformers\n ? pipeline.filter((transformer) =>\n Boolean(getUploadTransformerInternal(transformer)?.prepareUpload),\n )\n : []\n\n const bridgeTransformer =\n bridgeTransformers.find((transformer) =>\n getUploadTransformerInternal(transformer)!.handlesCollection?.({\n collectionSlug: collectionConfig.slug,\n }),\n ) ?? bridgeTransformers[0]\n\n // The chosen bridge's task options are private to it, so other bridges must not see them.\n const bridgeTaskPipeline = pipeline.filter(\n (transformer) =>\n transformer === bridgeTransformer || !bridgeTransformers.includes(transformer),\n )\n\n let originalWebFile: File | undefined\n let mainWebFile: File | undefined\n let hasDimensionsFromBridge = false\n let sizeResults: PreparedUploadTransformation[] = []\n\n if (canRunTransformers) {\n const fileContents = file.tempFilePath ? await openAsBlob(file.tempFilePath) : file.data\n originalWebFile = new File([fileContents], file.name, { type: file.mimetype })\n if (file.tempFilePath) {\n setUploadFilePath(originalWebFile, file.tempFilePath)\n }\n\n if (bridgeTransformer) {\n const bridge = getUploadTransformerInternal(bridgeTransformer)!\n\n const results = await bridge.prepareUpload!({\n collectionSlug: collectionConfig.slug,\n file: originalWebFile,\n req,\n transform: (task) =>\n transformUploadFile({\n collectionSlug: collectionConfig.slug,\n file: task.file ?? originalWebFile!,\n options: task.options,\n pipeline: bridgeTaskPipeline,\n req,\n }),\n uploadEdits: editsForTransformer,\n })\n\n const mainResult = results.find((result) => result.fieldPath === 'filename')\n\n mainWebFile = mainResult?.file ?? originalWebFile\n fileData.width = mainResult?.width\n fileData.height = mainResult?.height\n hasDimensionsFromBridge = true\n sizeResults = results.filter((result) => result.fieldPath !== 'filename')\n } else {\n mainWebFile = await transformUploadFile({\n collectionSlug: collectionConfig.slug,\n file: originalWebFile,\n options: undefined,\n pipeline,\n req,\n })\n }\n }\n\n // Saved for any resizable image, not just one a transformer processed, so it's kept with no\n // transformer registered and on a header-only client upload.\n if (\n focalPointEnabled &&\n uploadEdits?.focalPoint &&\n (hasDimensionsFromBridge || canResizeImage(file.mimetype))\n ) {\n fileData.focalX = isNumber(uploadEdits.focalPoint.x)\n ? Math.round(uploadEdits.focalPoint.x)\n : 50\n fileData.focalY = isNumber(uploadEdits.focalPoint.y)\n ? Math.round(uploadEdits.focalPoint.y)\n : 50\n }\n\n const fileWasTransformed = Boolean(mainWebFile && mainWebFile !== originalWebFile)\n const hasReusableOriginalMain = Boolean(\n isResettingCrop && !fileWasTransformed && retainedOriginal,\n )\n if (hasReusableOriginalMain && !hasFocalPointChange && retainedOriginal) {\n return {\n data: {\n ...incomingFileData,\n _objectKey: retainedOriginal._objectKey,\n filename: retainedOriginal.filename,\n filesize: retainedOriginal.filesize,\n height: retainedOriginal.height,\n mimeType: retainedOriginal.mimeType,\n original: retainedOriginal,\n prefix: retainedOriginal.prefix,\n url: retainedOriginal.url,\n variants: currentFileData?.variants,\n width: retainedOriginal.width,\n ...(draft ? { _status: 'draft' } : {}),\n } as T,\n files: [],\n }\n }\n const mainBuffer = fileWasTransformed\n ? Buffer.from(await mainWebFile!.arrayBuffer())\n : undefined\n\n // A transformed file is named after what the transformer returned, not the upload.\n const outputName = hasReusableOriginalMain\n ? retainedOriginal!.filename\n : (fileWasTransformed && mainWebFile!.name) || file.name\n\n let mimeType: string\n let ext: string | undefined\n\n if (mainBuffer) {\n // Detected bytes win for binary formats (Sharp keeps the input's name and type on a\n // converted output), but text-like output such as CSV has no signature to detect, so\n // fall back to the type and extension the transformer declared on the returned File.\n const typeResult = await fileTypeFromBuffer(mainBuffer)\n ext =\n typeResult?.ext ?? (getFileExtension(getSanitizedUploadFilename(outputName)) || undefined)\n mimeType = typeResult?.mime ?? (mainWebFile!.type || file.mimetype)\n } else {\n mimeType = file.mimetype\n ext = getFileExtension(getSanitizedUploadFilename(file.name))\n }\n\n // Adjust SVG mime type. fromBuffer modifies it.\n if (mimeType === 'application/xml' && ext === 'svg') {\n mimeType = 'image/svg+xml'\n }\n fileData.mimeType = mimeType\n fileData.filesize = mainBuffer ? mainBuffer.length : file.size\n\n // Only probe formats that could carry dimensions - probing reads the file, and a large\n // non-image upload may only exist as a temp file we deliberately never buffer.\n if (!hasDimensionsFromBridge && isProcessableImage(mimeType)) {\n try {\n const probed = await getImageSize({\n file: mainBuffer ? { ...file, data: mainBuffer, tempFilePath: undefined } : file,\n })\n fileData.width = probed.width\n fileData.height = probed.height\n } catch {\n // Not a recognized image format — leave width/height unset.\n }\n }\n\n let fsSafeName = getSanitizedUploadFilename(outputName, ext)\n const isDuplicatingAnOriginal =\n isDuplicating && file.name === (originalDoc as FileData | undefined)?.original?.filename\n\n // A provider reference without a receipt names an object the client already stored; renaming\n // it would point the document at a key that was never uploaded.\n const isUnverifiedProviderReference = Boolean(hasProviderDirectReference && !providerOriginal)\n\n if (\n !fileWasTransformed &&\n !retainedOriginal &&\n !providerOriginal &&\n !isDuplicatingAnOriginal &&\n !isUnverifiedProviderReference\n ) {\n fsSafeName = getOriginalFilename({ filename: fsSafeName })\n }\n\n if (hasReusableOriginalMain) {\n fsSafeName = retainedOriginal!.filename\n } else if (\n !overwriteExistingFiles ||\n !disableLocalStorage ||\n (shouldStageCloudFiles && retainedOriginal)\n ) {\n // Extract prefix if present (added by plugin-cloud-storage)\n const prefix = (data as Record<string, unknown>)?.prefix as string | undefined\n fsSafeName = await getSafeFileName({\n collectionSlug: collectionConfig.slug,\n desiredFilename: fsSafeName,\n prefix,\n req,\n staticPath: staticPath!,\n })\n }\n\n fileData.filename = fsSafeName\n if (hasReusableOriginalMain) {\n fileData.url = retainedOriginal!.url\n }\n\n const hasGeneratedProviderRepresentations = Boolean(\n providerOriginal &&\n (fileWasTransformed || sizeResults.some((result) => Boolean(result.file))),\n )\n const newObjectKey =\n hasManagedCloudStorage && (shouldStageCloudFiles || hasGeneratedProviderRepresentations)\n ? randomUUID()\n : undefined\n if (newObjectKey) {\n fileData._objectKey = newObjectKey\n }\n if (providerOriginal && !fileWasTransformed) {\n fileData._objectKey = providerOriginal._objectKey\n fileData.prefix = providerOriginal.prefix\n }\n if (hasReusableOriginalMain && retainedOriginal) {\n fileData._objectKey = retainedOriginal._objectKey\n fileData.prefix = retainedOriginal.prefix\n }\n\n if (!disableLocalStorage || shouldStageCloudFiles || providerOriginal) {\n const originalFilename =\n retainedOriginal?.filename ??\n providerOriginal?.filename ??\n (fileWasTransformed\n ? await getSafeFileName({\n collectionSlug: collectionConfig.slug,\n desiredFilename: getOriginalFilename({\n filename: getSanitizedUploadFilename(file.name),\n }),\n req,\n staticPath: staticPath!,\n })\n : fsSafeName)\n const original =\n retainedOriginal ??\n ({\n _objectKey: providerOriginal ? providerOriginal._objectKey : newObjectKey,\n filename: originalFilename,\n filesize: file.size,\n mimeType: file.mimetype,\n prefix: providerOriginal?.prefix ?? currentFileData?.prefix,\n url: formatAdminURL({\n apiRoute: req.payload.config.routes.api,\n path: `/${collectionConfig.slug}/file/${encodeURIComponent(originalFilename)}`,\n relative: true,\n serverURL: req.payload.config.serverURL,\n }),\n } as NonNullable<FileData['original']>)\n\n if (!retainedOriginal && isProcessableImage(file.mimetype)) {\n try {\n const dimensions = await getImageSize({ file })\n original.width = dimensions.width\n original.height = dimensions.height\n } catch {\n // Files with an image MIME type may not have readable dimensions.\n }\n }\n\n fileData.original = original\n\n if (providerOriginal) {\n if (!fileWasTransformed) {\n fileData.filename = originalFilename\n }\n }\n\n if (fileWasTransformed && !retainedOriginal && !providerOriginal) {\n filesToSave.push({\n buffer: Buffer.from(await originalWebFile!.arrayBuffer()),\n path: `${staticPath}/${originalFilename}`,\n })\n }\n }\n\n if (mainBuffer) {\n // The stored bytes are no longer the ones the client uploaded, so the client's upload\n // reference must not be reused for them.\n delete file.uploadReference\n\n filesToSave.push({\n buffer: mainBuffer,\n path: `${staticPath}/${fsSafeName}`,\n })\n\n if (file.tempFilePath) {\n await fs.writeFile(file.tempFilePath, mainBuffer)\n } else {\n req.file = {\n ...file,\n data: mainBuffer,\n size: mainBuffer.length,\n }\n }\n } else if (!hasReusableOriginalMain) {\n // file.data is empty when useTempFiles is on, so the real content lives at\n // file.tempFilePath instead (see the function doc for why we avoid buffering it).\n const tempFileHandling = resolveTempFileHandling({\n disableLocalStorage: Boolean(disableLocalStorage) && !shouldStageCloudFiles,\n hasProcessedBuffer: false,\n tempFilePath: file.tempFilePath,\n })\n\n if (tempFileHandling.type === 'copyFromTempFile') {\n filesToSave.push({\n path: `${staticPath}/${fsSafeName}`,\n sourcePath: tempFileHandling.sourcePath,\n })\n } else if (tempFileHandling.type === 'useBuffer') {\n const bufferToSave = file.tempFilePath ? await fs.readFile(file.tempFilePath) : file.data\n\n // A 'header'/'none' content requirement (see getFileContentRequirement.ts) means\n // file.data is only a partial probe, not the real content - never save it as-is.\n const fileDataIsPartialView = !file.tempFilePath && bufferToSave.length !== file.size\n\n if (!fileDataIsPartialView) {\n filesToSave.push({\n buffer: bufferToSave,\n path: `${staticPath}/${fsSafeName}`,\n })\n\n if (bufferToSave.length > 0) {\n if (file.tempFilePath) {\n await fs.writeFile(file.tempFilePath, bufferToSave)\n } else {\n // Keep req.file in sync, since downstream hooks/plugins may read it.\n req.file = {\n ...file,\n data: bufferToSave,\n size: bufferToSave.length,\n }\n }\n }\n }\n }\n }\n\n if (sizeResults.length > 0) {\n req.payloadUploadSizes = {}\n const sizes: FileSizes = {}\n const { name: baseName, ext: baseExt } = parseFilename(fsSafeName)\n const plannedNames = new Set([fileData.original?.filename, fsSafeName])\n const plannedSizeBuffers = new Map<string, Buffer>()\n\n for (const result of sizeResults) {\n const sizeName = result.fieldPath.slice('variants.'.length)\n\n if (!result.file) {\n sizes[sizeName] = {\n filename: null,\n filesize: null,\n height: null,\n mimeType: null,\n url: null,\n width: null,\n }\n continue\n }\n\n const sizeBuffer = Buffer.from(await result.file.arrayBuffer())\n const sizeTypeResult = await fileTypeFromBuffer(sizeBuffer)\n const sizeExt = sizeTypeResult?.ext || baseExt\n const sizeMimeType = sizeTypeResult?.mime || result.mimeType || fileData.mimeType\n\n req.payloadUploadSizes[sizeName] = sizeBuffer\n\n const imageSizeConfig = collectionConfig.upload.variants?.find(\n (imageSize) => imageSize.name === sizeName,\n )\n\n const imageNameWithDimensions = imageSizeConfig?.generateImageName\n ? imageSizeConfig.generateImageName({\n extension: sizeExt,\n height: result.height!,\n originalName: baseName,\n sizeName,\n width: result.width!,\n })\n : generateImageSizeFilename({\n extension: sizeExt,\n height: result.height!,\n outputImageName: baseName,\n width: result.width!,\n })\n\n let imageName = imageNameWithDimensions\n\n if (!disableLocalStorage || shouldStageCloudFiles) {\n const prefix = (data as Record<string, unknown>)?.prefix as string | undefined\n\n while (true) {\n imageName = await getSafeFileName({\n collectionSlug: collectionConfig.slug,\n desiredFilename: imageName,\n prefix,\n req,\n staticPath: disableLocalStorage ? undefined : staticPath!,\n })\n if (plannedSizeBuffers.get(imageName)?.equals(sizeBuffer)) {\n break\n }\n if (!plannedNames.has(imageName)) {\n break\n }\n imageName = incrementName(imageName)\n }\n plannedNames.add(imageName)\n }\n\n const imagePath = `${staticPath}/${imageName}`\n\n sizes[sizeName] = {\n _objectKey: newObjectKey,\n filename: imageName,\n filesize: sizeBuffer.length,\n height: result.height!,\n mimeType: sizeMimeType,\n prefix: providerOriginal?.prefix ?? currentFileData?.prefix,\n url: null,\n width: result.width!,\n }\n\n if (!plannedSizeBuffers.has(imageName)) {\n plannedSizeBuffers.set(imageName, sizeBuffer)\n filesToSave.push({\n buffer: sizeBuffer,\n path: imagePath,\n })\n }\n }\n\n fileData.variants = sizes\n }\n } catch (err) {\n req.payload.logger.error(err)\n throw new FileUploadError(req.t)\n }\n\n newData = {\n ...newData,\n ...fileData,\n ...(draft ? { _status: 'draft' } : {}),\n }\n\n return {\n data: newData,\n files: filesToSave,\n }\n}\n\n/**\n * Parse upload edits from req or incoming data\n */\nfunction parseUploadEditsFromReqOrIncomingData(args: {\n data: unknown\n isDuplicating?: boolean\n operation: 'create' | 'update'\n originalDoc: unknown\n req: PayloadRequest\n}): UploadEdits {\n const { data, isDuplicating, operation, originalDoc, req } = args\n\n // Get intended focal point change from query string or incoming data\n const uploadEdits =\n req.query?.uploadEdits && typeof req.query.uploadEdits === 'object'\n ? (req.query.uploadEdits as UploadEdits)\n : {}\n\n if (uploadEdits.focalPoint) {\n return uploadEdits\n }\n\n const incomingData = data as FileData\n const origDoc = originalDoc as FileData\n\n if (origDoc && 'focalX' in origDoc && 'focalY' in origDoc) {\n // Admin always resends the current focal point, so treat an unchanged value as no edit.\n if (incomingData?.focalX === origDoc.focalX && incomingData?.focalY === origDoc.focalY) {\n return undefined!\n }\n\n if (isDuplicating) {\n uploadEdits.focalPoint = {\n x: incomingData?.focalX || origDoc.focalX!,\n y: incomingData?.focalY || origDoc.focalY!,\n }\n return uploadEdits\n }\n }\n\n if (incomingData?.focalX && incomingData?.focalY) {\n uploadEdits.focalPoint = {\n x: incomingData.focalX,\n y: incomingData.focalY,\n }\n return uploadEdits\n }\n\n // If no focal point is set, default to center\n if (operation === 'create') {\n uploadEdits.focalPoint = {\n x: 50,\n y: 50,\n }\n }\n\n return uploadEdits\n}\n"],"names":["fileTypeFromBuffer","fs","randomUUID","openAsBlob","FileRetrievalError","FileUploadError","Forbidden","MissingFile","formatAdminURL","isNumber","canResizeImage","checkFileRestrictions","downloadFileToBuffer","getOriginalFilename","generateImageSizeFilename","getFileByPath","getFileExtension","getSanitizedUploadFilename","getImageSize","getSafeFileName","incrementName","hasCropOrResizeEdit","hasFullFileContents","isProcessableImage","parseFilename","planTransformerPipeline","transformUploadFile","getUploadTransformerInternal","setUploadFilePath","shouldReupload","uploadEdits","fileData","focalPoint","incomingFocalX","x","incomingFocalY","y","currentFocalX","focalX","currentFocalY","focalY","isEqual","resolveTempFileHandling","disableLocalStorage","hasProcessedBuffer","tempFilePath","type","sourcePath","generateFileData","collection","config","collectionConfig","data","draft","externalUploadSource","isDuplicating","operation","originalDoc","overwriteExistingFiles","req","throwOnMissingFile","upload","files","serverURL","payload","file","undefined","parseUploadEditsFromReqOrIncomingData","focalPointEnabled","staticDir","hasManagedCloudStorage","Boolean","fileOperations","uploadReference","hasProviderDirectReference","verifiedOriginal","context","_payloadVerifiedProviderOriginal","providerOriginal","signedReceipt","adapter","_objectKey","filename","key","prefix","shouldStageCloudFiles","staticPath","incomingFileData","currentFileData","retainedOriginal","original","url","fileSourceData","isLocalFile","includes","t","startsWith","filePath","response","retrieveFileResponse","doc","ok","Error","buffer","Buffer","from","arrayBuffer","name","mimetype","mimeType","size","length","uploadConfig","err","message","detectedFileType","shouldUseDetectedFileType","mime","mkdir","recursive","newData","filesToSave","crop","isResettingCrop","unit","Number","width","height","widthInPixels","heightInPixels","hasFocalPointChange","editsForTransformer","pipeline","args","collectionSlug","slug","capability","transformers","canRunTransformers","bridgeTransformers","filter","transformer","prepareUpload","bridgeTransformer","find","handlesCollection","bridgeTaskPipeline","originalWebFile","mainWebFile","hasDimensionsFromBridge","sizeResults","fileContents","File","bridge","results","transform","task","options","mainResult","result","fieldPath","Math","round","fileWasTransformed","hasReusableOriginalMain","filesize","variants","_status","mainBuffer","outputName","ext","typeResult","probed","fsSafeName","isDuplicatingAnOriginal","isUnverifiedProviderReference","desiredFilename","hasGeneratedProviderRepresentations","some","newObjectKey","originalFilename","apiRoute","routes","api","path","encodeURIComponent","relative","dimensions","push","writeFile","tempFileHandling","bufferToSave","readFile","fileDataIsPartialView","payloadUploadSizes","sizes","baseName","baseExt","plannedNames","Set","plannedSizeBuffers","Map","sizeName","slice","sizeBuffer","sizeTypeResult","sizeExt","sizeMimeType","imageSizeConfig","imageSize","imageNameWithDimensions","generateImageName","extension","originalName","outputImageName","imageName","get","equals","has","add","imagePath","set","logger","error","query","incomingData","origDoc"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/uploads/generateFileData.ts"],"sourcesContent":["import { fileTypeFromBuffer } from 'file-type'\nimport fs from 'fs/promises'\nimport { randomUUID } from 'node:crypto'\nimport { openAsBlob } from 'node:fs'\n\nimport type { Collection, TypeWithID } from '../collections/config/types.js'\nimport type { SanitizedConfig } from '../config/types.js'\nimport type { Document, PayloadRequest } from '../types/index.js'\nimport type { ExternalUploadSource } from './sanitizeUploadData.js'\nimport type { PreparedUploadTransformation } from './transformers/uploadTransformerBridge.js'\nimport type { FileData, FileSizes, FileToSave, UploadEdits } from './types.js'\n\nimport { FileRetrievalError, FileUploadError, Forbidden, MissingFile } from '../errors/index.js'\nimport { formatAdminURL } from '../utilities/formatAdminURL.js'\nimport { isNumber } from '../utilities/isNumber.js'\nimport { canResizeImage } from './canResizeImage.js'\nimport { checkFileRestrictions } from './checkFileRestrictions.js'\nimport { downloadFileToBuffer } from './downloadFileToBuffer.js'\nimport { getOriginalFilename } from './fileVersioning/naming.js'\nimport { generateImageSizeFilename } from './generateImageSizeFilename.js'\nimport { getFileByPath } from './getFileByPath.js'\nimport { getFileExtension, getSanitizedUploadFilename } from './getFileTypeIdentity.js'\nimport { getImageSize } from './getImageSize.js'\nimport { getSafeFileName, incrementName } from './getSafeFilename.js'\nimport { hasCropOrResizeEdit } from './hasCropOrResizeEdit.js'\nimport { hasFullFileContents } from './hasFullFileContents.js'\nimport { isProcessableImage } from './isProcessableImage.js'\nimport { parseFilename } from './parseFilename.js'\nimport { planTransformerPipeline } from './transformers/planTransformerPipeline.js'\nimport { transformUploadFile } from './transformers/transformUploadFile.js'\nimport {\n getUploadTransformerInternal,\n setUploadFilePath,\n} from './transformers/uploadTransformerBridge.js'\ntype Args<T> = {\n collection: Collection\n config: SanitizedConfig\n data: T\n draft?: boolean\n externalUploadSource?: ExternalUploadSource\n isDuplicating?: boolean\n operation: 'create' | 'update'\n originalDoc?: T\n overwriteExistingFiles?: boolean\n req: PayloadRequest\n throwOnMissingFile?: boolean\n}\n\ntype Result<T> = Promise<{\n data: T\n files: FileToSave[]\n}>\n\nconst shouldReupload = (\n uploadEdits: undefined | UploadEdits,\n fileData: Record<string, unknown> | undefined,\n) => {\n if (!fileData || !uploadEdits) {\n return false\n }\n\n if (hasCropOrResizeEdit(uploadEdits)) {\n return true\n }\n\n // Since uploadEdits always has focalPoint, compare to the value in the data if it was changed\n if (uploadEdits.focalPoint) {\n const incomingFocalX = uploadEdits.focalPoint.x\n const incomingFocalY = uploadEdits.focalPoint.y\n\n const currentFocalX = 'focalX' in fileData && fileData.focalX\n const currentFocalY = 'focalY' in fileData && fileData.focalY\n\n const isEqual = incomingFocalX === currentFocalX && incomingFocalY === currentFocalY\n return !isEqual\n }\n\n return false\n}\n\nexport type TempFileHandling =\n | { sourcePath: string; type: 'copyFromTempFile' }\n | { type: 'skip' }\n | { type: 'useBuffer' }\n\n/**\n * Decides how to get a file's bytes onto disk when no transformer rewrote it (a transformed\n * file is already an in-memory buffer, so it always takes the `useBuffer` path). Copies straight\n * from the temp file when possible, rather than reading a potentially large temp file into memory\n * just to write it back out - see the `generateFileData` function doc for why.\n */\nexport const resolveTempFileHandling = ({\n disableLocalStorage,\n hasProcessedBuffer,\n tempFilePath,\n}: {\n disableLocalStorage: boolean\n hasProcessedBuffer: boolean\n tempFilePath: string | undefined\n}): TempFileHandling => {\n if (hasProcessedBuffer || !tempFilePath) {\n return { type: 'useBuffer' }\n }\n\n return disableLocalStorage\n ? { type: 'skip' }\n : { type: 'copyFromTempFile', sourcePath: tempFilePath }\n}\n\n/**\n * Builds the document's file metadata and the list of files to write to disk.\n *\n * A large client upload may arrive as a temp file instead of an in-memory buffer (see\n * getFileFromUploadInstructions.ts), and `file.data` can hold only a partial probe rather than\n * the full file. To avoid loading such files into memory unnecessarily, no transformer runs\n * unless the full bytes are available, this skips reading a temp file entirely when local\n * storage is disabled, and copies it straight to its destination when local storage is enabled.\n */\nexport const generateFileData = async <T>({\n collection: { config: collectionConfig },\n data,\n draft,\n externalUploadSource,\n isDuplicating,\n operation,\n originalDoc,\n overwriteExistingFiles,\n req,\n throwOnMissingFile,\n}: Args<T>): Result<T> => {\n if (!collectionConfig.upload) {\n return {\n data,\n files: [],\n }\n }\n\n const { serverURL } = req.payload.config\n\n let file = isDuplicating ? undefined : req.file\n\n const uploadEdits = parseUploadEditsFromReqOrIncomingData({\n data,\n isDuplicating,\n operation,\n // Only a duplication source informs edit parsing. Updates now also pass `originalDoc` so the\n // stored file can be reprocessed, and that must not change which edits are applied.\n originalDoc: isDuplicating ? originalDoc : undefined,\n req,\n })\n\n const {\n disableLocalStorage,\n focalPoint: focalPointEnabled = true,\n staticDir,\n } = collectionConfig.upload\n const hasManagedCloudStorage = Boolean(collectionConfig.upload.fileOperations)\n const uploadReference = file?.uploadReference\n const hasProviderDirectReference =\n uploadReference && typeof uploadReference === 'object' && !('uploadId' in uploadReference)\n const verifiedOriginal = req.context?._payloadVerifiedProviderOriginal as\n | {\n _objectKey?: string\n filename: string\n key: string\n prefix?: string\n signedReceipt: string\n }\n | undefined\n const providerOriginal =\n hasProviderDirectReference &&\n 'signedReceipt' in uploadReference &&\n uploadReference.signedReceipt === verifiedOriginal?.signedReceipt &&\n verifiedOriginal &&\n typeof collectionConfig.upload.adapter === 'string'\n ? {\n _objectKey: verifiedOriginal._objectKey,\n filename: verifiedOriginal.filename,\n key: verifiedOriginal.key,\n prefix: verifiedOriginal.prefix,\n }\n : undefined\n const shouldStageCloudFiles = hasManagedCloudStorage && !hasProviderDirectReference\n\n const staticPath = staticDir\n\n const incomingFileData: Document = isDuplicating ? originalDoc : data\n const currentFileData = (operation === 'update' ? originalDoc : incomingFileData) as\n | FileData\n | undefined\n const retainedOriginal =\n operation === 'update' &&\n !file &&\n !externalUploadSource &&\n typeof currentFileData?.original?.filename === 'string' &&\n typeof currentFileData.original.url === 'string'\n ? currentFileData.original\n : undefined\n const fileSourceData = externalUploadSource ?? retainedOriginal ?? currentFileData\n let isLocalFile = false\n\n if (\n !file &&\n fileSourceData &&\n (externalUploadSource ||\n isDuplicating ||\n shouldReupload(uploadEdits, incomingFileData as Record<string, unknown>))\n ) {\n const { filename, url } = fileSourceData\n if (filename && (filename.includes('../') || filename.includes('..\\\\'))) {\n throw new Forbidden(req.t)\n }\n\n if ((serverURL && url?.startsWith(serverURL)) || url?.startsWith('/')) {\n isLocalFile = true\n }\n\n try {\n if (!externalUploadSource && !disableLocalStorage && isLocalFile) {\n // File is stored locally\n const filePath = `${staticPath}/${filename}`\n const response = await getFileByPath(filePath)\n file = response\n overwriteExistingFiles = true\n } else if (filename && retainedOriginal && hasManagedCloudStorage) {\n const { retrieveFileResponse } = await import('./endpoints/getFile.js')\n const response = await retrieveFileResponse({\n collection: { config: collectionConfig },\n doc: originalDoc as TypeWithID,\n filename,\n operation: 'transform',\n req,\n })\n if (!response.ok) {\n throw new Error(`Unable to read retained original ${filename}`)\n }\n const buffer = Buffer.from(await response.arrayBuffer())\n file = {\n name: filename,\n data: buffer,\n mimetype: retainedOriginal.mimeType,\n size: buffer.length,\n }\n overwriteExistingFiles = true\n } else if (filename && url) {\n // File is remote\n file = await downloadFileToBuffer({\n data: fileSourceData,\n req,\n uploadConfig: collectionConfig.upload,\n })\n overwriteExistingFiles = !externalUploadSource\n }\n } catch (err: unknown) {\n throw new FileRetrievalError(req.t, err instanceof Error ? err.message : undefined)\n }\n\n if (file && retainedOriginal && currentFileData?.filename) {\n file = { ...file, name: currentFileData.filename }\n }\n }\n\n if (isDuplicating) {\n overwriteExistingFiles = false\n }\n\n if (!file) {\n if (throwOnMissingFile) {\n throw new MissingFile(req.t)\n }\n\n return {\n data: incomingFileData!,\n files: [],\n }\n }\n\n const detectedFileType = await checkFileRestrictions({\n collection: collectionConfig,\n file,\n req,\n })\n\n const shouldUseDetectedFileType =\n detectedFileType &&\n (isProcessableImage(file.mimetype) || isProcessableImage(detectedFileType.mime))\n\n if (shouldUseDetectedFileType && detectedFileType.mime !== file.mimetype) {\n file = { ...file, mimetype: detectedFileType.mime }\n }\n\n if (!disableLocalStorage) {\n await fs.mkdir(staticPath!, { recursive: true })\n }\n\n let newData = incomingFileData as T\n const filesToSave: FileToSave[] = []\n const fileData: Partial<FileData> = {}\n const crop = uploadEdits?.crop\n const isResettingCrop =\n retainedOriginal &&\n crop?.unit === '%' &&\n Number(crop.width) === 100 &&\n Number(crop.height) === 100 &&\n Number(crop.x) === 0 &&\n Number(crop.y) === 0 &&\n (uploadEdits.widthInPixels === undefined ||\n Number(uploadEdits.widthInPixels) === retainedOriginal.width) &&\n (uploadEdits.heightInPixels === undefined ||\n Number(uploadEdits.heightInPixels) === retainedOriginal.height)\n const hasFocalPointChange =\n uploadEdits?.focalPoint &&\n (Number(uploadEdits.focalPoint.x) !== (currentFileData?.focalX ?? 50) ||\n Number(uploadEdits.focalPoint.y) !== (currentFileData?.focalY ?? 50))\n const editsForTransformer = isResettingCrop\n ? { ...uploadEdits, crop: undefined, heightInPixels: undefined, widthInPixels: undefined }\n : uploadEdits\n\n try {\n const pipeline = await planTransformerPipeline({\n args: {\n collectionSlug: collectionConfig.slug,\n mimeType: file.mimetype,\n operation: 'upload',\n req,\n },\n capability: 'transformFile',\n transformers: req.payload.config.upload?.transformers ?? [],\n })\n\n // A large client upload can arrive as a bounded probe alongside a temp file. Transformers\n // need the whole file, so leave such an upload untouched rather than buffering it.\n const canRunTransformers = pipeline.length > 0 && hasFullFileContents(file)\n\n const bridgeTransformers = canRunTransformers\n ? pipeline.filter((transformer) =>\n Boolean(getUploadTransformerInternal(transformer)?.prepareUpload),\n )\n : []\n\n const bridgeTransformer =\n bridgeTransformers.find((transformer) =>\n getUploadTransformerInternal(transformer)!.handlesCollection?.({\n collectionSlug: collectionConfig.slug,\n }),\n ) ?? bridgeTransformers[0]\n\n // The chosen bridge's task options are private to it, so other bridges must not see them.\n const bridgeTaskPipeline = pipeline.filter(\n (transformer) =>\n transformer === bridgeTransformer || !bridgeTransformers.includes(transformer),\n )\n\n let originalWebFile: File | undefined\n let mainWebFile: File | undefined\n let hasDimensionsFromBridge = false\n let sizeResults: PreparedUploadTransformation[] = []\n\n if (canRunTransformers) {\n const fileContents = file.tempFilePath ? await openAsBlob(file.tempFilePath) : file.data\n originalWebFile = new File([fileContents], file.name, { type: file.mimetype })\n if (file.tempFilePath) {\n setUploadFilePath(originalWebFile, file.tempFilePath)\n }\n\n if (bridgeTransformer) {\n const bridge = getUploadTransformerInternal(bridgeTransformer)!\n\n const results = await bridge.prepareUpload!({\n collectionSlug: collectionConfig.slug,\n file: originalWebFile,\n req,\n transform: (task) =>\n transformUploadFile({\n collectionSlug: collectionConfig.slug,\n file: task.file ?? originalWebFile!,\n options: task.options,\n pipeline: bridgeTaskPipeline,\n req,\n }),\n uploadEdits: editsForTransformer,\n })\n\n const mainResult = results.find((result) => result.fieldPath === 'filename')\n\n mainWebFile = mainResult?.file ?? originalWebFile\n fileData.width = mainResult?.width\n fileData.height = mainResult?.height\n hasDimensionsFromBridge = true\n sizeResults = results.filter((result) => result.fieldPath !== 'filename')\n } else {\n mainWebFile = await transformUploadFile({\n collectionSlug: collectionConfig.slug,\n file: originalWebFile,\n options: undefined,\n pipeline,\n req,\n })\n }\n }\n\n // Saved for any resizable image, not just one a transformer processed, so it's kept with no\n // transformer registered and on a header-only client upload.\n if (\n focalPointEnabled &&\n uploadEdits?.focalPoint &&\n (hasDimensionsFromBridge || canResizeImage(file.mimetype))\n ) {\n fileData.focalX = isNumber(uploadEdits.focalPoint.x)\n ? Math.round(uploadEdits.focalPoint.x)\n : 50\n fileData.focalY = isNumber(uploadEdits.focalPoint.y)\n ? Math.round(uploadEdits.focalPoint.y)\n : 50\n }\n\n const fileWasTransformed = Boolean(mainWebFile && mainWebFile !== originalWebFile)\n const hasReusableOriginalMain = Boolean(\n isResettingCrop && !fileWasTransformed && retainedOriginal,\n )\n if (hasReusableOriginalMain && !hasFocalPointChange && retainedOriginal) {\n return {\n data: {\n ...incomingFileData,\n _objectKey: retainedOriginal._objectKey,\n filename: retainedOriginal.filename,\n filesize: retainedOriginal.filesize,\n height: retainedOriginal.height,\n mimeType: retainedOriginal.mimeType,\n original: retainedOriginal,\n prefix: retainedOriginal.prefix,\n url: retainedOriginal.url,\n variants: currentFileData?.variants,\n width: retainedOriginal.width,\n ...(draft ? { _status: 'draft' } : {}),\n } as T,\n files: [],\n }\n }\n const mainBuffer = fileWasTransformed\n ? Buffer.from(await mainWebFile!.arrayBuffer())\n : undefined\n\n // A transformed file is named after what the transformer returned, not the upload.\n const outputName = hasReusableOriginalMain\n ? retainedOriginal!.filename\n : (fileWasTransformed && mainWebFile!.name) || file.name\n\n let mimeType: string\n let ext: string | undefined\n\n if (mainBuffer) {\n // Detected bytes win for binary formats (Sharp keeps the input's name and type on a\n // converted output), but text-like output such as CSV has no signature to detect, so\n // fall back to the type and extension the transformer declared on the returned File.\n const typeResult = await fileTypeFromBuffer(mainBuffer)\n ext =\n typeResult?.ext ?? (getFileExtension(getSanitizedUploadFilename(outputName)) || undefined)\n mimeType = typeResult?.mime ?? (mainWebFile!.type || file.mimetype)\n } else {\n mimeType = file.mimetype\n ext = getFileExtension(getSanitizedUploadFilename(file.name))\n }\n\n // Adjust SVG mime type. fromBuffer modifies it.\n if (mimeType === 'application/xml' && ext === 'svg') {\n mimeType = 'image/svg+xml'\n }\n fileData.mimeType = mimeType\n fileData.filesize = mainBuffer ? mainBuffer.length : file.size\n\n // Only probe formats that could carry dimensions - probing reads the file, and a large\n // non-image upload may only exist as a temp file we deliberately never buffer.\n if (!hasDimensionsFromBridge && isProcessableImage(mimeType)) {\n try {\n const probed = await getImageSize({\n file: mainBuffer ? { ...file, data: mainBuffer, tempFilePath: undefined } : file,\n })\n fileData.width = probed.width\n fileData.height = probed.height\n } catch {\n // Not a recognized image format — leave width/height unset.\n }\n }\n\n let fsSafeName = getSanitizedUploadFilename(outputName, ext)\n const isDuplicatingAnOriginal =\n isDuplicating && file.name === (originalDoc as FileData | undefined)?.original?.filename\n\n // A provider reference without a receipt names an object the client already stored; renaming\n // it would point the document at a key that was never uploaded.\n const isUnverifiedProviderReference = Boolean(hasProviderDirectReference && !providerOriginal)\n\n if (\n !fileWasTransformed &&\n !retainedOriginal &&\n !providerOriginal &&\n !isDuplicatingAnOriginal &&\n !isUnverifiedProviderReference\n ) {\n fsSafeName = getOriginalFilename({ filename: fsSafeName })\n }\n\n if (hasReusableOriginalMain) {\n fsSafeName = retainedOriginal!.filename\n } else if (\n !overwriteExistingFiles ||\n !disableLocalStorage ||\n (shouldStageCloudFiles && retainedOriginal)\n ) {\n // Extract prefix if present (added by plugin-cloud-storage)\n const prefix = (data as Record<string, unknown>)?.prefix as string | undefined\n fsSafeName = await getSafeFileName({\n collectionSlug: collectionConfig.slug,\n desiredFilename: fsSafeName,\n prefix,\n req,\n staticPath: staticPath!,\n })\n }\n\n fileData.filename = fsSafeName\n if (hasReusableOriginalMain) {\n fileData.url = retainedOriginal!.url\n }\n\n const hasGeneratedProviderRepresentations = Boolean(\n providerOriginal &&\n (fileWasTransformed || sizeResults.some((result) => Boolean(result.file))),\n )\n const newObjectKey =\n hasManagedCloudStorage && (shouldStageCloudFiles || hasGeneratedProviderRepresentations)\n ? randomUUID()\n : undefined\n if (newObjectKey) {\n fileData._objectKey = newObjectKey\n }\n if (providerOriginal && !fileWasTransformed) {\n fileData._objectKey = providerOriginal._objectKey\n fileData.prefix = providerOriginal.prefix\n }\n if (hasReusableOriginalMain && retainedOriginal) {\n fileData._objectKey = retainedOriginal._objectKey\n fileData.prefix = retainedOriginal.prefix\n }\n\n if (!disableLocalStorage || shouldStageCloudFiles || providerOriginal) {\n const originalFilename =\n retainedOriginal?.filename ??\n providerOriginal?.filename ??\n (fileWasTransformed\n ? await getSafeFileName({\n collectionSlug: collectionConfig.slug,\n desiredFilename: getOriginalFilename({\n filename: getSanitizedUploadFilename(file.name),\n }),\n req,\n staticPath: staticPath!,\n })\n : fsSafeName)\n const original = retainedOriginal ?? {\n _objectKey: providerOriginal ? providerOriginal._objectKey : newObjectKey,\n filename: originalFilename,\n filesize: file.size,\n mimeType: file.mimetype,\n prefix: providerOriginal?.prefix ?? currentFileData?.prefix,\n url: formatAdminURL({\n apiRoute: req.payload.config.routes.api,\n path: `/${collectionConfig.slug}/file/${encodeURIComponent(originalFilename)}`,\n relative: true,\n serverURL: req.payload.config.serverURL,\n }),\n }\n\n if (!retainedOriginal && isProcessableImage(file.mimetype)) {\n try {\n const dimensions = await getImageSize({ file })\n original.width = dimensions.width\n original.height = dimensions.height\n } catch {\n // Files with an image MIME type may not have readable dimensions.\n }\n }\n\n fileData.original = original\n\n if (providerOriginal) {\n if (!fileWasTransformed) {\n fileData.filename = originalFilename\n }\n }\n\n if (fileWasTransformed && !retainedOriginal && !providerOriginal) {\n filesToSave.push({\n buffer: Buffer.from(await originalWebFile!.arrayBuffer()),\n path: `${staticPath}/${originalFilename}`,\n })\n }\n }\n\n if (mainBuffer) {\n // The stored bytes are no longer the ones the client uploaded, so the client's upload\n // reference must not be reused for them.\n delete file.uploadReference\n\n filesToSave.push({\n buffer: mainBuffer,\n path: `${staticPath}/${fsSafeName}`,\n })\n\n if (file.tempFilePath) {\n await fs.writeFile(file.tempFilePath, mainBuffer)\n } else {\n req.file = {\n ...file,\n data: mainBuffer,\n size: mainBuffer.length,\n }\n }\n } else if (!hasReusableOriginalMain) {\n // file.data is empty when useTempFiles is on, so the real content lives at\n // file.tempFilePath instead (see the function doc for why we avoid buffering it).\n const tempFileHandling = resolveTempFileHandling({\n disableLocalStorage: Boolean(disableLocalStorage) && !shouldStageCloudFiles,\n hasProcessedBuffer: false,\n tempFilePath: file.tempFilePath,\n })\n\n if (tempFileHandling.type === 'copyFromTempFile') {\n filesToSave.push({\n path: `${staticPath}/${fsSafeName}`,\n sourcePath: tempFileHandling.sourcePath,\n })\n } else if (tempFileHandling.type === 'useBuffer') {\n const bufferToSave = file.tempFilePath ? await fs.readFile(file.tempFilePath) : file.data\n\n // A 'header'/'none' content requirement (see getFileContentRequirement.ts) means\n // file.data is only a partial probe, not the real content - never save it as-is.\n const fileDataIsPartialView = !file.tempFilePath && bufferToSave.length !== file.size\n\n if (!fileDataIsPartialView) {\n filesToSave.push({\n buffer: bufferToSave,\n path: `${staticPath}/${fsSafeName}`,\n })\n\n if (bufferToSave.length > 0) {\n if (file.tempFilePath) {\n await fs.writeFile(file.tempFilePath, bufferToSave)\n } else {\n // Keep req.file in sync, since downstream hooks/plugins may read it.\n req.file = {\n ...file,\n data: bufferToSave,\n size: bufferToSave.length,\n }\n }\n }\n }\n }\n }\n\n if (sizeResults.length > 0) {\n req.payloadUploadSizes = {}\n const sizes: FileSizes = {}\n const { name: baseName, ext: baseExt } = parseFilename(fsSafeName)\n const plannedNames = new Set([fileData.original?.filename, fsSafeName])\n const plannedSizeBuffers = new Map<string, Buffer>()\n\n for (const result of sizeResults) {\n const sizeName = result.fieldPath.slice('variants.'.length)\n\n if (!result.file) {\n sizes[sizeName] = {\n filename: null,\n filesize: null,\n height: null,\n mimeType: null,\n url: null,\n width: null,\n }\n continue\n }\n\n const sizeBuffer = Buffer.from(await result.file.arrayBuffer())\n const sizeTypeResult = await fileTypeFromBuffer(sizeBuffer)\n const sizeExt = sizeTypeResult?.ext || baseExt\n const sizeMimeType = sizeTypeResult?.mime || result.mimeType || fileData.mimeType\n\n req.payloadUploadSizes[sizeName] = sizeBuffer\n\n const imageSizeConfig = collectionConfig.upload.variants?.find(\n (imageSize) => imageSize.name === sizeName,\n )\n\n const imageNameWithDimensions = imageSizeConfig?.generateImageName\n ? imageSizeConfig.generateImageName({\n extension: sizeExt,\n height: result.height!,\n originalName: baseName,\n sizeName,\n width: result.width!,\n })\n : generateImageSizeFilename({\n extension: sizeExt,\n height: result.height!,\n outputImageName: baseName,\n width: result.width!,\n })\n\n let imageName = imageNameWithDimensions\n\n if (!disableLocalStorage || shouldStageCloudFiles) {\n const prefix = (data as Record<string, unknown>)?.prefix as string | undefined\n\n while (true) {\n imageName = await getSafeFileName({\n collectionSlug: collectionConfig.slug,\n desiredFilename: imageName,\n prefix,\n req,\n staticPath: disableLocalStorage ? undefined : staticPath!,\n })\n if (plannedSizeBuffers.get(imageName)?.equals(sizeBuffer)) {\n break\n }\n if (!plannedNames.has(imageName)) {\n break\n }\n imageName = incrementName(imageName)\n }\n plannedNames.add(imageName)\n }\n\n const imagePath = `${staticPath}/${imageName}`\n\n sizes[sizeName] = {\n _objectKey: newObjectKey,\n filename: imageName,\n filesize: sizeBuffer.length,\n height: result.height!,\n mimeType: sizeMimeType,\n prefix: providerOriginal?.prefix ?? currentFileData?.prefix,\n url: null,\n width: result.width!,\n }\n\n if (!plannedSizeBuffers.has(imageName)) {\n plannedSizeBuffers.set(imageName, sizeBuffer)\n filesToSave.push({\n buffer: sizeBuffer,\n path: imagePath,\n })\n }\n }\n\n fileData.variants = sizes\n }\n } catch (err) {\n req.payload.logger.error(err)\n throw new FileUploadError(req.t)\n }\n\n newData = {\n ...newData,\n ...fileData,\n ...(draft ? { _status: 'draft' } : {}),\n }\n\n return {\n data: newData,\n files: filesToSave,\n }\n}\n\n/**\n * Parse upload edits from req or incoming data\n */\nfunction parseUploadEditsFromReqOrIncomingData(args: {\n data: unknown\n isDuplicating?: boolean\n operation: 'create' | 'update'\n originalDoc: unknown\n req: PayloadRequest\n}): UploadEdits {\n const { data, isDuplicating, operation, originalDoc, req } = args\n\n // Get intended focal point change from query string or incoming data\n const uploadEdits =\n req.query?.uploadEdits && typeof req.query.uploadEdits === 'object'\n ? (req.query.uploadEdits as UploadEdits)\n : {}\n\n if (uploadEdits.focalPoint) {\n return uploadEdits\n }\n\n const incomingData = data as FileData\n const origDoc = originalDoc as FileData\n\n if (origDoc && 'focalX' in origDoc && 'focalY' in origDoc) {\n // Admin always resends the current focal point, so treat an unchanged value as no edit.\n if (incomingData?.focalX === origDoc.focalX && incomingData?.focalY === origDoc.focalY) {\n return undefined!\n }\n\n if (isDuplicating) {\n uploadEdits.focalPoint = {\n x: incomingData?.focalX || origDoc.focalX!,\n y: incomingData?.focalY || origDoc.focalY!,\n }\n return uploadEdits\n }\n }\n\n if (incomingData?.focalX && incomingData?.focalY) {\n uploadEdits.focalPoint = {\n x: incomingData.focalX,\n y: incomingData.focalY,\n }\n return uploadEdits\n }\n\n // If no focal point is set, default to center\n if (operation === 'create') {\n uploadEdits.focalPoint = {\n x: 50,\n y: 50,\n }\n }\n\n return uploadEdits\n}\n"],"names":["fileTypeFromBuffer","fs","randomUUID","openAsBlob","FileRetrievalError","FileUploadError","Forbidden","MissingFile","formatAdminURL","isNumber","canResizeImage","checkFileRestrictions","downloadFileToBuffer","getOriginalFilename","generateImageSizeFilename","getFileByPath","getFileExtension","getSanitizedUploadFilename","getImageSize","getSafeFileName","incrementName","hasCropOrResizeEdit","hasFullFileContents","isProcessableImage","parseFilename","planTransformerPipeline","transformUploadFile","getUploadTransformerInternal","setUploadFilePath","shouldReupload","uploadEdits","fileData","focalPoint","incomingFocalX","x","incomingFocalY","y","currentFocalX","focalX","currentFocalY","focalY","isEqual","resolveTempFileHandling","disableLocalStorage","hasProcessedBuffer","tempFilePath","type","sourcePath","generateFileData","collection","config","collectionConfig","data","draft","externalUploadSource","isDuplicating","operation","originalDoc","overwriteExistingFiles","req","throwOnMissingFile","upload","files","serverURL","payload","file","undefined","parseUploadEditsFromReqOrIncomingData","focalPointEnabled","staticDir","hasManagedCloudStorage","Boolean","fileOperations","uploadReference","hasProviderDirectReference","verifiedOriginal","context","_payloadVerifiedProviderOriginal","providerOriginal","signedReceipt","adapter","_objectKey","filename","key","prefix","shouldStageCloudFiles","staticPath","incomingFileData","currentFileData","retainedOriginal","original","url","fileSourceData","isLocalFile","includes","t","startsWith","filePath","response","retrieveFileResponse","doc","ok","Error","buffer","Buffer","from","arrayBuffer","name","mimetype","mimeType","size","length","uploadConfig","err","message","detectedFileType","shouldUseDetectedFileType","mime","mkdir","recursive","newData","filesToSave","crop","isResettingCrop","unit","Number","width","height","widthInPixels","heightInPixels","hasFocalPointChange","editsForTransformer","pipeline","args","collectionSlug","slug","capability","transformers","canRunTransformers","bridgeTransformers","filter","transformer","prepareUpload","bridgeTransformer","find","handlesCollection","bridgeTaskPipeline","originalWebFile","mainWebFile","hasDimensionsFromBridge","sizeResults","fileContents","File","bridge","results","transform","task","options","mainResult","result","fieldPath","Math","round","fileWasTransformed","hasReusableOriginalMain","filesize","variants","_status","mainBuffer","outputName","ext","typeResult","probed","fsSafeName","isDuplicatingAnOriginal","isUnverifiedProviderReference","desiredFilename","hasGeneratedProviderRepresentations","some","newObjectKey","originalFilename","apiRoute","routes","api","path","encodeURIComponent","relative","dimensions","push","writeFile","tempFileHandling","bufferToSave","readFile","fileDataIsPartialView","payloadUploadSizes","sizes","baseName","baseExt","plannedNames","Set","plannedSizeBuffers","Map","sizeName","slice","sizeBuffer","sizeTypeResult","sizeExt","sizeMimeType","imageSizeConfig","imageSize","imageNameWithDimensions","generateImageName","extension","originalName","outputImageName","imageName","get","equals","has","add","imagePath","set","logger","error","query","incomingData","origDoc"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/uploads/getFileFromUploadInstructions.ts"],"sourcesContent":["import type { ReadableStream } from 'node:stream/web'\n\nimport { randomUUID } from 'node:crypto'\nimport fs from 'node:fs'\nimport { mkdir, rm, stat } from 'node:fs/promises'\nimport os from 'node:os'\nimport path from 'node:path'\nimport { Readable } from 'node:stream'\nimport { pipeline } from 'node:stream/promises'\n\nimport type { PayloadRequest } from '../types/index.js'\nimport type { SanitizedUploadConfig, UploadEdits, UploadInstructions } from './types.js'\n\nimport { APIError } from '../errors/APIError.js'\nimport { sanitizeFilename } from '../utilities/sanitizeFilename.js'\nimport { sanitizeUploadPrefix } from '../utilities/sanitizeUploadPrefix.js'\nimport { verifyClientUploadReceipt } from './clientUploadReceipt.js'\nimport { docWithFilenameExists } from './docWithFilenameExists.js'\nimport { getOriginalFilename, normalizeStorageKey } from './fileVersioning/naming.js'\nimport { getFileContentRequirement, HEADER_PROBE_BYTE_LENGTH } from './getFileContentRequirement.js'\nimport { getImageSize } from './getImageSize.js'\nimport { hasCropOrResizeEdit } from './hasCropOrResizeEdit.js'\nimport { getStagedFile } from './stagedUpload.js'\nimport { planTransformerPipeline } from './transformers/planTransformerPipeline.js'\nimport { getUploadTransformerInternal } from './transformers/uploadTransformerBridge.js'\n\nexport const getFileFromUploadInstructions = async ({\n collectionSlug,\n file,\n req,\n}: {\n collectionSlug: string\n file: UploadInstructions['file']\n req: PayloadRequest\n}): Promise<NonNullable<PayloadRequest['file']>> => {\n if (req.context) {\n delete req.context._payloadVerifiedProviderOriginal\n }\n\n if (\n !file ||\n typeof file !== 'object' ||\n typeof file.filename !== 'string' ||\n typeof file.mimeType !== 'string' ||\n !Number.isSafeInteger(file.size) ||\n file.size < 0 ||\n !file.uploadReference ||\n typeof file.uploadReference !== 'object' ||\n Array.isArray(file.uploadReference)\n ) {\n throw new APIError('Invalid upload reference.', 400)\n }\n\n /**\n * Handlers fetch files uploaded to a storage provider. An uploadId points to a temporary file\n * already stored by Payload, so no handler is needed.\n */\n if ('uploadId' in file.uploadReference) {\n return getStagedFile({ collectionSlug, req, uploadReference: file.uploadReference })\n }\n\n const uploadConfig = req.payload.collections[collectionSlug]!.config.upload\n let allowOverwrite = false\n let providerFilename = file.filename\n let verifiedOriginal:\n | {\n _objectKey?: string\n filename: string\n key: string\n prefix?: string\n signedReceipt: string\n }\n | undefined\n\n if (uploadConfig?.uploadInstructions?.requiresUploadReceipt) {\n const signedReceipt =\n 'signedReceipt' in file.uploadReference ? file.uploadReference.signedReceipt : undefined\n const receipt = verifyClientUploadReceipt({\n collectionSlug,\n filename: file.filename,\n req,\n signedReceipt,\n })\n allowOverwrite = receipt.allowOverwrite === true\n let originalFilename: string\n let storageFilePath: string\n\n try {\n originalFilename = getOriginalFilename({ filename: file.filename })\n storageFilePath = normalizeStorageKey({ key: receipt.storageFilePath })\n } catch {\n throw new APIError('Invalid upload reference.', 400)\n }\n\n const expectedKeySuffix = [receipt.filePrefix, receipt._objectKey, originalFilename]\n .filter(Boolean)\n .join('/')\n\n if (\n (!allowOverwrite &&\n (!receipt._objectKey ||\n path.posix.basename(storageFilePath) !== originalFilename ||\n !`/${storageFilePath}`.endsWith(`/${expectedKeySuffix}`))) ||\n ('prefix' in file.uploadReference && file.uploadReference.prefix !== receipt.filePrefix) ||\n ('_objectKey' in file.uploadReference &&\n file.uploadReference._objectKey !== receipt._objectKey)\n ) {\n throw new APIError('Invalid upload reference.', 400)\n }\n\n providerFilename = allowOverwrite ? file.filename : originalFilename\n if (!allowOverwrite) {\n verifiedOriginal = {\n _objectKey: receipt._objectKey,\n filename: originalFilename,\n key: storageFilePath,\n prefix: receipt.filePrefix,\n signedReceipt: signedReceipt as string,\n }\n }\n file = {\n ...file,\n uploadReference: {\n _objectKey: receipt._objectKey,\n prefix: receipt.filePrefix,\n signedReceipt,\n },\n }\n }\n\n const prefix =\n 'prefix' in file.uploadReference && typeof file.uploadReference.prefix === 'string'\n ? file.uploadReference.prefix\n : undefined\n\n let sanitizedFilename: string\n\n try {\n sanitizedFilename = sanitizeFilename(file.filename)\n } catch {\n throw new APIError('Invalid upload reference.', 400)\n }\n\n if (\n sanitizedFilename !== file.filename ||\n (typeof prefix === 'string' && sanitizeUploadPrefix(prefix) !== prefix)\n ) {\n throw new APIError('Invalid upload reference.', 400)\n }\n\n // Provider-backed references are submitted with the document request. They may only claim a\n // new object identity; existing top-level and generated filenames already belong to another\n // document and must continue through that document's read access checks.\n if (\n !allowOverwrite &&\n (await docWithFilenameExists({\n collectionSlug,\n filename: file.filename,\n matchAnyPrefix: true,\n path: '',\n prefix,\n req,\n }))\n ) {\n throw new APIError('Invalid upload reference.', 400)\n }\n\n if (!uploadConfig || !uploadConfig.handlers) {\n throw new APIError('uploadConfig.handlers is not present for ' + collectionSlug)\n }\n\n const contentRequirement = getFileContentRequirement({\n hasSizeEdits: requestHasSizeEdits(req),\n hasTransformFileStages: await hasTransformFileStages({\n collectionSlug,\n mimeType: file.mimeType,\n req,\n }),\n mimeType: file.mimeType,\n uploadConfig,\n })\n const providerFile = { ...file, filename: providerFilename }\n const rememberVerifiedOriginal = () => {\n if (verifiedOriginal) {\n req.context ??= {}\n req.context._payloadVerifiedProviderOriginal = verifiedOriginal\n }\n }\n\n // Nothing downstream reads this file's content - use the client-reported metadata directly\n // instead of re-downloading a file that, for a chunked upload, can be far larger than\n // the server's available memory or disk.\n if (contentRequirement === 'none') {\n const response = await fetchUploadResponse({\n collectionSlug,\n file: providerFile,\n rangeHeader: file.size > 0 ? 'bytes=0-0' : undefined,\n req,\n uploadConfig,\n })\n assertProviderFileSize({ expectedSize: file.size, response })\n const prefix = await readBoundedPrefix(response, 1)\n if (file.size > 0 && prefix.length === 0) {\n throw new APIError('Uploaded source is not readable.', 400)\n }\n rememberVerifiedOriginal()\n\n return {\n name: file.filename,\n data: Buffer.alloc(0),\n mimetype: file.mimeType,\n size: file.size,\n uploadReference: file.uploadReference,\n }\n }\n\n if (contentRequirement === 'header') {\n const headerFile = await fetchHeaderOnly({\n collectionSlug,\n file: providerFile,\n req,\n uploadConfig,\n })\n if (headerFile) {\n rememberVerifiedOriginal()\n return { ...headerFile, name: file.filename }\n }\n // The header wasn't enough to determine the image's dimensions - fall through to a full fetch.\n }\n\n const response = await fetchUploadResponse({\n collectionSlug,\n file: providerFile,\n req,\n uploadConfig,\n })\n\n const tempFilePath = await streamResponseToTempFile({ req, response })\n if ((await stat(tempFilePath)).size !== file.size) {\n await rm(tempFilePath, { force: true })\n throw new APIError('Uploaded source size does not match the declared size.', 400)\n }\n req.context ??= {}\n req.context._payloadClientUploadTempFile = true\n rememberVerifiedOriginal()\n\n return {\n name: file.filename,\n data: Buffer.alloc(0),\n mimetype: response.headers.get('Content-Type') || file.mimeType,\n size: file.size,\n tempFilePath,\n uploadReference: file.uploadReference,\n }\n}\n\n/**\n * Whether the request's `uploadEdits` query param carries a crop or resize edit - reads the raw\n * `req.query.uploadEdits`, since a full parse (with its `data`/`originalDoc` focal-point\n * fallback, done in generateFileData.ts) isn't available yet at this point in the request\n * lifecycle.\n */\nconst requestHasSizeEdits = (req: PayloadRequest): boolean => {\n const uploadEdits = req.query?.uploadEdits\n if (typeof uploadEdits !== 'object' || uploadEdits === null) {\n return false\n }\n\n return hasCropOrResizeEdit(uploadEdits as UploadEdits)\n}\n\n/**\n * Whether a transformer that reads the whole file will run `transformFile` on this upload. A\n * bridge transformer (e.g. `sharpTransformer`) is excluded: it projects what it needs onto the\n * sanitized upload config (`hasImageAdjustments`, `variants`) at startup instead.\n */\nconst hasTransformFileStages = async ({\n collectionSlug,\n mimeType,\n req,\n}: {\n collectionSlug: string\n mimeType: string\n req: PayloadRequest\n}): Promise<boolean> => {\n const transformers = req.payload.config.upload?.transformers ?? []\n\n if (transformers.length === 0) {\n return false\n }\n\n const pipeline = await planTransformerPipeline({\n args: { collectionSlug, mimeType, operation: 'upload', req },\n capability: 'transformFile',\n transformers,\n })\n\n return pipeline.some((transformer) => !getUploadTransformerInternal(transformer)?.prepareUpload)\n}\n\n/**\n * Fetches only the first `HEADER_PROBE_BYTE_LENGTH` bytes of the upload (via a best-effort byte\n * range request) and uses them to probe an image's dimensions, without downloading the rest of\n * the file. Returns null if that isn't enough to determine the dimensions, so the caller can\n * fall back to a full fetch.\n */\nconst fetchHeaderOnly = async ({\n collectionSlug,\n file,\n req,\n uploadConfig,\n}: {\n collectionSlug: string\n file: UploadInstructions['file']\n req: PayloadRequest\n uploadConfig: SanitizedUploadConfig\n}): Promise<NonNullable<PayloadRequest['file']> | null> => {\n const response = await fetchUploadResponse({\n collectionSlug,\n file,\n rangeHeader: `bytes=0-${HEADER_PROBE_BYTE_LENGTH - 1}`,\n req,\n uploadConfig,\n })\n assertProviderFileSize({ expectedSize: file.size, response })\n\n const headerBuffer = await readBoundedPrefix(response, HEADER_PROBE_BYTE_LENGTH)\n\n try {\n await getImageSize({\n file: {\n name: file.filename,\n data: headerBuffer,\n mimetype: file.mimeType,\n size: file.size,\n },\n })\n } catch {\n return null\n }\n\n return {\n name: file.filename,\n data: headerBuffer,\n mimetype: response.headers.get('Content-Type') || file.mimeType,\n size: file.size,\n uploadReference: file.uploadReference,\n }\n}\n\nconst assertProviderFileSize = ({\n expectedSize,\n response,\n}: {\n expectedSize: number\n response: Response\n}): void => {\n const contentRange = response.headers.get('Content-Range')\n const reportedSize =\n response.status === 206\n ? contentRange?.match(/^bytes \\d+-\\d+\\/(\\d+)$/)?.[1]\n : response.headers.get('Content-Length')\n\n if (\n reportedSize === undefined ||\n reportedSize === null ||\n Number(reportedSize) !== expectedSize\n ) {\n throw new APIError('Uploaded source size does not match the declared size.', 400)\n }\n}\n\n/**\n * Runs the collection's upload handlers, following a single redirect if one is returned.\n * `rangeHeader`, when passed, is a best-effort hint - handlers that ignore it simply return the\n * full file, which callers must still bound their own reads against.\n */\nconst fetchUploadResponse = async ({\n collectionSlug,\n file,\n rangeHeader,\n req,\n uploadConfig,\n}: {\n collectionSlug: string\n file: UploadInstructions['file']\n rangeHeader?: string\n req: PayloadRequest\n uploadConfig: SanitizedUploadConfig\n}): Promise<Response> => {\n const scopedReq = rangeHeader ? withRangeHeader(req, rangeHeader) : req\n\n let response: null | Response = null\n let error: unknown\n\n for (const handler of uploadConfig.handlers!) {\n try {\n const result = await handler(scopedReq, {\n doc: null!,\n params: {\n collection: collectionSlug,\n filename: file.filename,\n uploadReference: file.uploadReference,\n },\n })\n if (result) {\n response = result\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 *\n * @see packages/payload/src/uploads/types.ts\n */\n break\n }\n } catch (err) {\n error = err\n }\n }\n\n if (!response) {\n if (error) {\n req.payload.logger.error(error)\n }\n\n throw new APIError('Expected response from the upload handler.')\n }\n\n if (response.status >= 300 && response.status < 400) {\n const redirectUrl = response.headers.get('Location')\n if (redirectUrl) {\n response = await fetch(redirectUrl, {\n ...(rangeHeader && { headers: { Range: rangeHeader } }),\n })\n }\n }\n\n if (!response.ok) {\n throw new APIError('Uploaded source is not readable.', 400)\n }\n\n return response\n}\n\n/**\n * Overrides `req.headers` with a `Range` header while leaving the real incoming request\n * untouched. Uses a Proxy, rather than cloning `req`, because `req` is a native `Request`\n * instance at runtime - accessors like `.signal` are brand-checked against `req`'s internal\n * slots, so a plain clone (e.g. `Object.create(req)`) throws once a handler reads one of them.\n * Forwarding reads through `Reflect.get(req, prop, req)` keeps `this` bound to the real `req`\n * so those accessors keep working.\n */\nconst withRangeHeader = (req: PayloadRequest, rangeHeader: string): PayloadRequest => {\n const headers = new Headers(req.headers)\n headers.set('range', rangeHeader)\n\n return new Proxy(req, {\n get(target, prop) {\n return prop === 'headers' ? headers : Reflect.get(target, prop, target)\n },\n })\n}\n\n/**\n * Reads at most `maxBytes` from the response body, then cancels the reader - so a handler that\n * ignores the range hint and starts streaming the whole file is still cut short on our end.\n */\nconst readBoundedPrefix = async (response: Response, maxBytes: number): Promise<Buffer> => {\n if (!response.body) {\n return Buffer.alloc(0)\n }\n\n const reader = response.body.getReader()\n const chunks: Buffer[] = []\n let total = 0\n\n try {\n while (total < maxBytes) {\n const { done, value } = await reader.read()\n if (done || !value) {\n break\n }\n\n const remaining = maxBytes - total\n const chunk = value.byteLength > remaining ? value.subarray(0, remaining) : value\n chunks.push(Buffer.from(chunk))\n total += chunk.byteLength\n }\n } finally {\n await reader.cancel().catch(() => {})\n }\n\n return Buffer.concat(chunks, total)\n}\n\n/**\n * Streams the fetched upload straight to disk instead of buffering it in memory. A client upload\n * (e.g. Azure's chunkLargeFiles) can be far larger than the server's available memory, so this\n * avoids re-downloading the whole file into a single in-memory buffer just to read it back out.\n */\nconst streamResponseToTempFile = async ({\n req,\n response,\n}: {\n req: PayloadRequest\n response: Response\n}): Promise<string> => {\n if (!response.body) {\n throw new APIError('Expected a response body from the upload handler.')\n }\n\n const tempFileDir = req.payload.config.upload?.tempFileDir || os.tmpdir()\n await mkdir(tempFileDir, { recursive: true })\n const tempFilePath = path.join(tempFileDir, `payload-client-upload-${randomUUID()}`)\n\n try {\n await pipeline(\n Readable.fromWeb(response.body as ReadableStream<Uint8Array>),\n fs.createWriteStream(tempFilePath),\n )\n } catch (error) {\n await rm(tempFilePath, { force: true })\n throw error\n }\n\n return tempFilePath\n}\n"],"names":["randomUUID","fs","mkdir","rm","stat","os","path","Readable","pipeline","APIError","sanitizeFilename","sanitizeUploadPrefix","verifyClientUploadReceipt","docWithFilenameExists","getOriginalFilename","normalizeStorageKey","getFileContentRequirement","HEADER_PROBE_BYTE_LENGTH","getImageSize","hasCropOrResizeEdit","getStagedFile","planTransformerPipeline","getUploadTransformerInternal","getFileFromUploadInstructions","collectionSlug","file","req","context","_payloadVerifiedProviderOriginal","filename","mimeType","Number","isSafeInteger","size","uploadReference","Array","isArray","uploadConfig","payload","collections","config","upload","allowOverwrite","providerFilename","verifiedOriginal","uploadInstructions","requiresUploadReceipt","signedReceipt","undefined","receipt","originalFilename","storageFilePath","key","expectedKeySuffix","filePrefix","_objectKey","filter","Boolean","join","posix","basename","endsWith","prefix","sanitizedFilename","matchAnyPrefix","handlers","contentRequirement","hasSizeEdits","requestHasSizeEdits","hasTransformFileStages","providerFile","rememberVerifiedOriginal","response","fetchUploadResponse","rangeHeader","assertProviderFileSize","expectedSize","readBoundedPrefix","length","name","data","Buffer","alloc","mimetype","headerFile","fetchHeaderOnly","tempFilePath","streamResponseToTempFile","force","_payloadClientUploadTempFile","headers","get","uploadEdits","query","transformers","args","operation","capability","some","transformer","prepareUpload","headerBuffer","contentRange","reportedSize","status","match","scopedReq","withRangeHeader","error","handler","result","doc","params","collection","err","logger","redirectUrl","fetch","Range","ok","Headers","set","Proxy","target","prop","Reflect","maxBytes","body","reader","getReader","chunks","total","done","value","read","remaining","chunk","byteLength","subarray","push","from","cancel","catch","concat","tempFileDir","tmpdir","recursive","fromWeb","createWriteStream"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/uploads/getFileFromUploadInstructions.ts"],"sourcesContent":["import type { ReadableStream } from 'node:stream/web'\n\nimport { randomUUID } from 'node:crypto'\nimport fs from 'node:fs'\nimport { mkdir, rm, stat } from 'node:fs/promises'\nimport os from 'node:os'\nimport path from 'node:path'\nimport { Readable } from 'node:stream'\nimport { pipeline } from 'node:stream/promises'\n\nimport type { PayloadRequest } from '../types/index.js'\nimport type { SanitizedUploadConfig, UploadEdits, UploadInstructions } from './types.js'\n\nimport { APIError } from '../errors/APIError.js'\nimport { sanitizeFilename } from '../utilities/sanitizeFilename.js'\nimport { sanitizeUploadPrefix } from '../utilities/sanitizeUploadPrefix.js'\nimport { verifyClientUploadReceipt } from './clientUploadReceipt.js'\nimport { docWithFilenameExists } from './docWithFilenameExists.js'\nimport { getOriginalFilename, normalizeStorageKey } from './fileVersioning/naming.js'\nimport { getFileContentRequirement, HEADER_PROBE_BYTE_LENGTH } from './getFileContentRequirement.js'\nimport { getImageSize } from './getImageSize.js'\nimport { hasCropOrResizeEdit } from './hasCropOrResizeEdit.js'\nimport { getStagedFile } from './stagedUpload.js'\nimport { planTransformerPipeline } from './transformers/planTransformerPipeline.js'\nimport { getUploadTransformerInternal } from './transformers/uploadTransformerBridge.js'\n\nexport const getFileFromUploadInstructions = async ({\n collectionSlug,\n file,\n req,\n}: {\n collectionSlug: string\n file: UploadInstructions['file']\n req: PayloadRequest\n}): Promise<NonNullable<PayloadRequest['file']>> => {\n if (req.context) {\n delete req.context._payloadVerifiedProviderOriginal\n }\n\n if (\n !file ||\n typeof file !== 'object' ||\n typeof file.filename !== 'string' ||\n typeof file.mimeType !== 'string' ||\n !Number.isSafeInteger(file.size) ||\n file.size < 0 ||\n !file.uploadReference ||\n typeof file.uploadReference !== 'object' ||\n Array.isArray(file.uploadReference)\n ) {\n throw new APIError('Invalid upload reference.', 400)\n }\n\n /**\n * Handlers fetch files uploaded to a storage provider. An uploadId points to a temporary file\n * already stored by Payload, so no handler is needed.\n */\n if ('uploadId' in file.uploadReference) {\n return getStagedFile({ collectionSlug, req, uploadReference: file.uploadReference })\n }\n\n const uploadConfig = req.payload.collections[collectionSlug]!.config.upload\n let allowOverwrite = false\n let providerFilename = file.filename\n let verifiedOriginal:\n | {\n _objectKey?: string\n filename: string\n key: string\n prefix?: string\n signedReceipt: string\n }\n | undefined\n\n if (uploadConfig?.uploadInstructions?.requiresUploadReceipt) {\n const signedReceipt =\n 'signedReceipt' in file.uploadReference ? file.uploadReference.signedReceipt : undefined\n const receipt = verifyClientUploadReceipt({\n collectionSlug,\n filename: file.filename,\n req,\n signedReceipt,\n })\n allowOverwrite = receipt.allowOverwrite === true\n let originalFilename: string\n let storageFilePath: string\n\n try {\n originalFilename = getOriginalFilename({ filename: file.filename })\n storageFilePath = normalizeStorageKey({ key: receipt.storageFilePath })\n } catch {\n throw new APIError('Invalid upload reference.', 400)\n }\n\n const expectedKeySuffix = [receipt.filePrefix, receipt._objectKey, originalFilename]\n .filter(Boolean)\n .join('/')\n\n if (\n (!allowOverwrite &&\n (!receipt._objectKey ||\n path.posix.basename(storageFilePath) !== originalFilename ||\n !`/${storageFilePath}`.endsWith(`/${expectedKeySuffix}`))) ||\n ('prefix' in file.uploadReference && file.uploadReference.prefix !== receipt.filePrefix) ||\n ('_objectKey' in file.uploadReference &&\n file.uploadReference._objectKey !== receipt._objectKey)\n ) {\n throw new APIError('Invalid upload reference.', 400)\n }\n\n providerFilename = allowOverwrite ? file.filename : originalFilename\n if (!allowOverwrite) {\n verifiedOriginal = {\n _objectKey: receipt._objectKey,\n filename: originalFilename,\n key: storageFilePath,\n prefix: receipt.filePrefix,\n signedReceipt: signedReceipt as string,\n }\n }\n file = {\n ...file,\n uploadReference: {\n _objectKey: receipt._objectKey,\n prefix: receipt.filePrefix,\n signedReceipt,\n },\n }\n }\n\n const prefix =\n 'prefix' in file.uploadReference && typeof file.uploadReference.prefix === 'string'\n ? file.uploadReference.prefix\n : undefined\n\n let sanitizedFilename: string\n\n try {\n sanitizedFilename = sanitizeFilename(file.filename)\n } catch {\n throw new APIError('Invalid upload reference.', 400)\n }\n\n if (\n sanitizedFilename !== file.filename ||\n (typeof prefix === 'string' && sanitizeUploadPrefix(prefix) !== prefix)\n ) {\n throw new APIError('Invalid upload reference.', 400)\n }\n\n // Provider-backed references are submitted with the document request. They may only claim a\n // new object identity; existing top-level and generated filenames already belong to another\n // document and must continue through that document's read access checks.\n if (\n !allowOverwrite &&\n (await docWithFilenameExists({\n collectionSlug,\n filename: file.filename,\n matchAnyPrefix: true,\n path: '',\n prefix,\n req,\n }))\n ) {\n throw new APIError('Invalid upload reference.', 400)\n }\n\n if (!uploadConfig || !uploadConfig.handlers) {\n throw new APIError('uploadConfig.handlers is not present for ' + collectionSlug)\n }\n\n const contentRequirement = getFileContentRequirement({\n hasSizeEdits: requestHasSizeEdits(req),\n hasTransformFileStages: await hasTransformFileStages({\n collectionSlug,\n mimeType: file.mimeType,\n req,\n }),\n mimeType: file.mimeType,\n uploadConfig,\n })\n const providerFile = { ...file, filename: providerFilename }\n const rememberVerifiedOriginal = () => {\n if (verifiedOriginal) {\n req.context ??= {}\n req.context._payloadVerifiedProviderOriginal = verifiedOriginal\n }\n }\n\n // Nothing downstream reads this file's content - use the client-reported metadata directly\n // instead of re-downloading a file that, for a chunked upload, can be far larger than\n // the server's available memory or disk.\n if (contentRequirement === 'none') {\n const response = await fetchUploadResponse({\n collectionSlug,\n file: providerFile,\n rangeHeader: file.size > 0 ? 'bytes=0-0' : undefined,\n req,\n uploadConfig,\n })\n assertProviderFileSize({ expectedSize: file.size, response })\n const prefix = await readBoundedPrefix(response, 1)\n if (file.size > 0 && prefix.length === 0) {\n throw new APIError('Uploaded source is not readable.', 400)\n }\n rememberVerifiedOriginal()\n\n return {\n name: file.filename,\n data: Buffer.alloc(0),\n mimetype: file.mimeType,\n size: file.size,\n uploadReference: file.uploadReference,\n }\n }\n\n if (contentRequirement === 'header') {\n const headerFile = await fetchHeaderOnly({\n collectionSlug,\n file: providerFile,\n req,\n uploadConfig,\n })\n if (headerFile) {\n rememberVerifiedOriginal()\n return { ...headerFile, name: file.filename }\n }\n // The header wasn't enough to determine the image's dimensions - fall through to a full fetch.\n }\n\n const response = await fetchUploadResponse({\n collectionSlug,\n file: providerFile,\n req,\n uploadConfig,\n })\n\n const tempFilePath = await streamResponseToTempFile({ req, response })\n if ((await stat(tempFilePath)).size !== file.size) {\n await rm(tempFilePath, { force: true })\n throw new APIError('Uploaded source size does not match the declared size.', 400)\n }\n req.context ??= {}\n req.context._payloadClientUploadTempFile = true\n rememberVerifiedOriginal()\n\n return {\n name: file.filename,\n data: Buffer.alloc(0),\n mimetype: response.headers.get('Content-Type') || file.mimeType,\n size: file.size,\n tempFilePath,\n uploadReference: file.uploadReference,\n }\n}\n\n/**\n * Whether the request's `uploadEdits` query param carries a crop or resize edit - reads the raw\n * `req.query.uploadEdits`, since a full parse (with its `data`/`originalDoc` focal-point\n * fallback, done in generateFileData.ts) isn't available yet at this point in the request\n * lifecycle.\n */\nconst requestHasSizeEdits = (req: PayloadRequest): boolean => {\n const uploadEdits = req.query?.uploadEdits\n if (typeof uploadEdits !== 'object' || uploadEdits === null) {\n return false\n }\n\n return hasCropOrResizeEdit(uploadEdits)\n}\n\n/**\n * Whether a transformer that reads the whole file will run `transformFile` on this upload. A\n * bridge transformer (e.g. `sharpTransformer`) is excluded: it projects what it needs onto the\n * sanitized upload config (`hasImageAdjustments`, `variants`) at startup instead.\n */\nconst hasTransformFileStages = async ({\n collectionSlug,\n mimeType,\n req,\n}: {\n collectionSlug: string\n mimeType: string\n req: PayloadRequest\n}): Promise<boolean> => {\n const transformers = req.payload.config.upload?.transformers ?? []\n\n if (transformers.length === 0) {\n return false\n }\n\n const pipeline = await planTransformerPipeline({\n args: { collectionSlug, mimeType, operation: 'upload', req },\n capability: 'transformFile',\n transformers,\n })\n\n return pipeline.some((transformer) => !getUploadTransformerInternal(transformer)?.prepareUpload)\n}\n\n/**\n * Fetches only the first `HEADER_PROBE_BYTE_LENGTH` bytes of the upload (via a best-effort byte\n * range request) and uses them to probe an image's dimensions, without downloading the rest of\n * the file. Returns null if that isn't enough to determine the dimensions, so the caller can\n * fall back to a full fetch.\n */\nconst fetchHeaderOnly = async ({\n collectionSlug,\n file,\n req,\n uploadConfig,\n}: {\n collectionSlug: string\n file: UploadInstructions['file']\n req: PayloadRequest\n uploadConfig: SanitizedUploadConfig\n}): Promise<NonNullable<PayloadRequest['file']> | null> => {\n const response = await fetchUploadResponse({\n collectionSlug,\n file,\n rangeHeader: `bytes=0-${HEADER_PROBE_BYTE_LENGTH - 1}`,\n req,\n uploadConfig,\n })\n assertProviderFileSize({ expectedSize: file.size, response })\n\n const headerBuffer = await readBoundedPrefix(response, HEADER_PROBE_BYTE_LENGTH)\n\n try {\n await getImageSize({\n file: {\n name: file.filename,\n data: headerBuffer,\n mimetype: file.mimeType,\n size: file.size,\n },\n })\n } catch {\n return null\n }\n\n return {\n name: file.filename,\n data: headerBuffer,\n mimetype: response.headers.get('Content-Type') || file.mimeType,\n size: file.size,\n uploadReference: file.uploadReference,\n }\n}\n\nconst assertProviderFileSize = ({\n expectedSize,\n response,\n}: {\n expectedSize: number\n response: Response\n}): void => {\n const contentRange = response.headers.get('Content-Range')\n const reportedSize =\n response.status === 206\n ? contentRange?.match(/^bytes \\d+-\\d+\\/(\\d+)$/)?.[1]\n : response.headers.get('Content-Length')\n\n if (\n reportedSize === undefined ||\n reportedSize === null ||\n Number(reportedSize) !== expectedSize\n ) {\n throw new APIError('Uploaded source size does not match the declared size.', 400)\n }\n}\n\n/**\n * Runs the collection's upload handlers, following a single redirect if one is returned.\n * `rangeHeader`, when passed, is a best-effort hint - handlers that ignore it simply return the\n * full file, which callers must still bound their own reads against.\n */\nconst fetchUploadResponse = async ({\n collectionSlug,\n file,\n rangeHeader,\n req,\n uploadConfig,\n}: {\n collectionSlug: string\n file: UploadInstructions['file']\n rangeHeader?: string\n req: PayloadRequest\n uploadConfig: SanitizedUploadConfig\n}): Promise<Response> => {\n const scopedReq = rangeHeader ? withRangeHeader(req, rangeHeader) : req\n\n let response: null | Response = null\n let error: unknown\n\n for (const handler of uploadConfig.handlers!) {\n try {\n const result = await handler(scopedReq, {\n doc: null!,\n params: {\n collection: collectionSlug,\n filename: file.filename,\n uploadReference: file.uploadReference,\n },\n })\n if (result) {\n response = result\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 *\n * @see packages/payload/src/uploads/types.ts\n */\n break\n }\n } catch (err) {\n error = err\n }\n }\n\n if (!response) {\n if (error) {\n req.payload.logger.error(error)\n }\n\n throw new APIError('Expected response from the upload handler.')\n }\n\n if (response.status >= 300 && response.status < 400) {\n const redirectUrl = response.headers.get('Location')\n if (redirectUrl) {\n response = await fetch(redirectUrl, {\n ...(rangeHeader && { headers: { Range: rangeHeader } }),\n })\n }\n }\n\n if (!response.ok) {\n throw new APIError('Uploaded source is not readable.', 400)\n }\n\n return response\n}\n\n/**\n * Overrides `req.headers` with a `Range` header while leaving the real incoming request\n * untouched. Uses a Proxy, rather than cloning `req`, because `req` is a native `Request`\n * instance at runtime - accessors like `.signal` are brand-checked against `req`'s internal\n * slots, so a plain clone (e.g. `Object.create(req)`) throws once a handler reads one of them.\n * Forwarding reads through `Reflect.get(req, prop, req)` keeps `this` bound to the real `req`\n * so those accessors keep working.\n */\nconst withRangeHeader = (req: PayloadRequest, rangeHeader: string): PayloadRequest => {\n const headers = new Headers(req.headers)\n headers.set('range', rangeHeader)\n\n return new Proxy(req, {\n get(target, prop) {\n return prop === 'headers' ? headers : Reflect.get(target, prop, target)\n },\n })\n}\n\n/**\n * Reads at most `maxBytes` from the response body, then cancels the reader - so a handler that\n * ignores the range hint and starts streaming the whole file is still cut short on our end.\n */\nconst readBoundedPrefix = async (response: Response, maxBytes: number): Promise<Buffer> => {\n if (!response.body) {\n return Buffer.alloc(0)\n }\n\n const reader = response.body.getReader()\n const chunks: Buffer[] = []\n let total = 0\n\n try {\n while (total < maxBytes) {\n const { done, value } = await reader.read()\n if (done || !value) {\n break\n }\n\n const remaining = maxBytes - total\n const chunk = value.byteLength > remaining ? value.subarray(0, remaining) : value\n chunks.push(Buffer.from(chunk))\n total += chunk.byteLength\n }\n } finally {\n await reader.cancel().catch(() => {})\n }\n\n return Buffer.concat(chunks, total)\n}\n\n/**\n * Streams the fetched upload straight to disk instead of buffering it in memory. A client upload\n * (e.g. Azure's chunkLargeFiles) can be far larger than the server's available memory, so this\n * avoids re-downloading the whole file into a single in-memory buffer just to read it back out.\n */\nconst streamResponseToTempFile = async ({\n req,\n response,\n}: {\n req: PayloadRequest\n response: Response\n}): Promise<string> => {\n if (!response.body) {\n throw new APIError('Expected a response body from the upload handler.')\n }\n\n const tempFileDir = req.payload.config.upload?.tempFileDir || os.tmpdir()\n await mkdir(tempFileDir, { recursive: true })\n const tempFilePath = path.join(tempFileDir, `payload-client-upload-${randomUUID()}`)\n\n try {\n await pipeline(\n Readable.fromWeb(response.body as ReadableStream<Uint8Array>),\n fs.createWriteStream(tempFilePath),\n )\n } catch (error) {\n await rm(tempFilePath, { force: true })\n throw error\n }\n\n return tempFilePath\n}\n"],"names":["randomUUID","fs","mkdir","rm","stat","os","path","Readable","pipeline","APIError","sanitizeFilename","sanitizeUploadPrefix","verifyClientUploadReceipt","docWithFilenameExists","getOriginalFilename","normalizeStorageKey","getFileContentRequirement","HEADER_PROBE_BYTE_LENGTH","getImageSize","hasCropOrResizeEdit","getStagedFile","planTransformerPipeline","getUploadTransformerInternal","getFileFromUploadInstructions","collectionSlug","file","req","context","_payloadVerifiedProviderOriginal","filename","mimeType","Number","isSafeInteger","size","uploadReference","Array","isArray","uploadConfig","payload","collections","config","upload","allowOverwrite","providerFilename","verifiedOriginal","uploadInstructions","requiresUploadReceipt","signedReceipt","undefined","receipt","originalFilename","storageFilePath","key","expectedKeySuffix","filePrefix","_objectKey","filter","Boolean","join","posix","basename","endsWith","prefix","sanitizedFilename","matchAnyPrefix","handlers","contentRequirement","hasSizeEdits","requestHasSizeEdits","hasTransformFileStages","providerFile","rememberVerifiedOriginal","response","fetchUploadResponse","rangeHeader","assertProviderFileSize","expectedSize","readBoundedPrefix","length","name","data","Buffer","alloc","mimetype","headerFile","fetchHeaderOnly","tempFilePath","streamResponseToTempFile","force","_payloadClientUploadTempFile","headers","get","uploadEdits","query","transformers","args","operation","capability","some","transformer","prepareUpload","headerBuffer","contentRange","reportedSize","status","match","scopedReq","withRangeHeader","error","handler","result","doc","params","collection","err","logger","redirectUrl","fetch","Range","ok","Headers","set","Proxy","target","prop","Reflect","maxBytes","body","reader","getReader","chunks","total","done","value","read","remaining","chunk","byteLength","subarray","push","from","cancel","catch","concat","tempFileDir","tmpdir","recursive","fromWeb","createWriteStream"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/uploads/safeFetch.ts"],"sourcesContent":["import type { LookupFunction } from 'net'\n\nimport { lookup } from 'dns'\nimport ipaddr from 'ipaddr.js'\nimport { Agent, fetch as undiciFetch } from 'undici'\n\n/**\n * @internal this is used to mock the IP `lookup` function in integration tests\n */\nexport const _internal_safeFetchGlobal = {\n lookup,\n}\n\nconst buildBlockedFetchError = (hostname: string): Error =>\n new Error(\n `Blocked unsafe attempt to ${hostname}. This host resolves to a private or internal address. ` +\n `If this is a trusted destination, allowlist it via the upload collection's \"skipSafeFetch\" option ` +\n `(https://payloadcms.com/docs/upload/overview#skip-safe-fetch).`,\n )\n\nconst isSafeIp = (ip: string) => {\n try {\n if (!ip) {\n return false\n }\n\n if (!ipaddr.isValid(ip)) {\n return false\n }\n\n const parsedIpAddress = ipaddr.parse(ip)\n const range = parsedIpAddress.range()\n if (range !== 'unicast') {\n return false // Private IP Range\n }\n } catch (ignore) {\n return false\n }\n return true\n}\n\nconst ssrfFilterInterceptor: LookupFunction = (hostname, options, callback) => {\n _internal_safeFetchGlobal.lookup(hostname, options, (err, address, family) => {\n if (err) {\n callback(err, address, family)\n } else {\n let ips = [] as string[]\n if (Array.isArray(address)) {\n ips = address.map((a) => a.address)\n } else {\n ips = [address]\n }\n\n if (ips.some((ip) => !isSafeIp(ip))) {\n callback(buildBlockedFetchError(hostname), address, family)\n return\n }\n\n callback(null, address, family)\n }\n })\n}\n\nlet safeDispatcher: Agent | undefined\n\nconst getSafeDispatcher = (): Agent => {\n if (!safeDispatcher) {\n safeDispatcher = new Agent({ connect: { lookup: ssrfFilterInterceptor } })\n }\n return safeDispatcher\n}\n\n/**\n * A \"safe\" version of undici's fetch that prevents SSRF attacks.\n *\n * - Utilizes a custom dispatcher that filters out requests to unsafe IP addresses.\n * - Validates domain names by resolving them to IP addresses and checking if they're safe.\n * - Undici was used because it supported interceptors as well as \"credentials: include\". Native fetch\n */\nexport const safeFetch = async (...args: Parameters<typeof undiciFetch>): Promise<Response> => {\n const [unverifiedUrl, options] = args\n\n try {\n const url = new URL(unverifiedUrl)\n\n let hostname = url.hostname\n\n // Strip brackets from IPv6 addresses (e.g., \"[::1]\" => \"::1\")\n if (hostname.startsWith('[') && hostname.endsWith(']')) {\n hostname = hostname.slice(1, -1)\n }\n\n if (ipaddr.isValid(hostname)) {\n if (!isSafeIp(hostname)) {\n throw buildBlockedFetchError(hostname)\n }\n }\n return (await undiciFetch(url, {\n ...options,\n dispatcher: getSafeDispatcher(),\n redirect: 'manual', // Prevent automatic redirects\n })) as unknown as Response\n } catch (error) {\n if (error instanceof Error) {\n if (error.cause instanceof Error && error.cause.message.includes('unsafe')) {\n // Errors thrown from within interceptors always have 'fetch error' as the message\n // The desired message we want to bubble up is in the cause\n throw new Error(error.cause.message)\n } else {\n let stringifiedUrl: string | undefined = undefined\n if (typeof unverifiedUrl === 'string') {\n stringifiedUrl = unverifiedUrl\n } else if (unverifiedUrl instanceof URL) {\n stringifiedUrl = unverifiedUrl.toString()\n } else if (unverifiedUrl instanceof Request) {\n stringifiedUrl = unverifiedUrl.url\n }\n\n throw new Error(`Failed to fetch from ${stringifiedUrl}, ${error.message}`)\n }\n }\n throw error\n }\n}\n"],"names":["lookup","ipaddr","Agent","fetch","undiciFetch","_internal_safeFetchGlobal","buildBlockedFetchError","hostname","Error","isSafeIp","ip","isValid","parsedIpAddress","parse","range","ignore","ssrfFilterInterceptor","options","callback","err","address","family","ips","Array","isArray","map","a","some","safeDispatcher","getSafeDispatcher","connect","safeFetch","args","unverifiedUrl","url","URL","startsWith","endsWith","slice","dispatcher","redirect","error","cause","message","includes","stringifiedUrl","undefined","toString","Request"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/uploads/safeFetch.ts"],"sourcesContent":["import type { LookupFunction } from 'net'\n\nimport { lookup } from 'dns'\nimport ipaddr from 'ipaddr.js'\nimport { Agent, fetch as undiciFetch } from 'undici'\n\n/**\n * @internal this is used to mock the IP `lookup` function in integration tests\n */\nexport const _internal_safeFetchGlobal = {\n lookup,\n}\n\nconst buildBlockedFetchError = (hostname: string): Error =>\n new Error(\n `Blocked unsafe attempt to ${hostname}. This host resolves to a private or internal address. ` +\n `If this is a trusted destination, allowlist it via the upload collection's \"skipSafeFetch\" option ` +\n `(https://payloadcms.com/docs/upload/overview#skip-safe-fetch).`,\n )\n\nconst isSafeIp = (ip: string) => {\n try {\n if (!ip) {\n return false\n }\n\n if (!ipaddr.isValid(ip)) {\n return false\n }\n\n const parsedIpAddress = ipaddr.parse(ip)\n const range = parsedIpAddress.range()\n if (range !== 'unicast') {\n return false // Private IP Range\n }\n } catch (ignore) {\n return false\n }\n return true\n}\n\nconst ssrfFilterInterceptor: LookupFunction = (hostname, options, callback) => {\n _internal_safeFetchGlobal.lookup(hostname, options, (err, address, family) => {\n if (err) {\n callback(err, address, family)\n } else {\n let ips = [] as string[]\n if (Array.isArray(address)) {\n ips = address.map((a) => a.address)\n } else {\n ips = [address]\n }\n\n if (ips.some((ip) => !isSafeIp(ip))) {\n callback(buildBlockedFetchError(hostname), address, family)\n return\n }\n\n callback(null, address, family)\n }\n })\n}\n\nlet safeDispatcher: Agent | undefined\n\nconst getSafeDispatcher = (): Agent => {\n if (!safeDispatcher) {\n safeDispatcher = new Agent({ connect: { lookup: ssrfFilterInterceptor } })\n }\n return safeDispatcher\n}\n\n/**\n * A \"safe\" version of undici's fetch that prevents SSRF attacks.\n *\n * - Utilizes a custom dispatcher that filters out requests to unsafe IP addresses.\n * - Validates domain names by resolving them to IP addresses and checking if they're safe.\n * - Undici was used because it supported interceptors as well as \"credentials: include\". Native fetch\n */\nexport const safeFetch = async (...args: Parameters<typeof undiciFetch>): Promise<Response> => {\n const [unverifiedUrl, options] = args\n\n try {\n const url = new URL(unverifiedUrl)\n\n let hostname = url.hostname\n\n // Strip brackets from IPv6 addresses (e.g., \"[::1]\" => \"::1\")\n if (hostname.startsWith('[') && hostname.endsWith(']')) {\n hostname = hostname.slice(1, -1)\n }\n\n if (ipaddr.isValid(hostname)) {\n if (!isSafeIp(hostname)) {\n throw buildBlockedFetchError(hostname)\n }\n }\n return await undiciFetch(url, {\n ...options,\n dispatcher: getSafeDispatcher(),\n redirect: 'manual', // Prevent automatic redirects\n })\n } catch (error) {\n if (error instanceof Error) {\n if (error.cause instanceof Error && error.cause.message.includes('unsafe')) {\n // Errors thrown from within interceptors always have 'fetch error' as the message\n // The desired message we want to bubble up is in the cause\n throw new Error(error.cause.message)\n } else {\n let stringifiedUrl: string | undefined = undefined\n if (typeof unverifiedUrl === 'string') {\n stringifiedUrl = unverifiedUrl\n } else if (unverifiedUrl instanceof URL) {\n stringifiedUrl = unverifiedUrl.toString()\n } else if (unverifiedUrl instanceof Request) {\n stringifiedUrl = unverifiedUrl.url\n }\n\n throw new Error(`Failed to fetch from ${stringifiedUrl}, ${error.message}`)\n }\n }\n throw error\n }\n}\n"],"names":["lookup","ipaddr","Agent","fetch","undiciFetch","_internal_safeFetchGlobal","buildBlockedFetchError","hostname","Error","isSafeIp","ip","isValid","parsedIpAddress","parse","range","ignore","ssrfFilterInterceptor","options","callback","err","address","family","ips","Array","isArray","map","a","some","safeDispatcher","getSafeDispatcher","connect","safeFetch","args","unverifiedUrl","url","URL","startsWith","endsWith","slice","dispatcher","redirect","error","cause","message","includes","stringifiedUrl","undefined","toString","Request"],"mappings":"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"}