@payloadcms/plugin-cloud-storage 4.0.0-internal.f715c6e → 4.0.0-internal.f980c7c

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 (90) hide show
  1. package/README.md +2 -0
  2. package/dist/exports/client.d.ts +1 -1
  3. package/dist/exports/client.d.ts.map +1 -1
  4. package/dist/exports/client.js +1 -1
  5. package/dist/exports/client.js.map +1 -1
  6. package/dist/exports/utilities.d.ts +2 -1
  7. package/dist/exports/utilities.d.ts.map +1 -1
  8. package/dist/exports/utilities.js +2 -1
  9. package/dist/exports/utilities.js.map +1 -1
  10. package/dist/fields/getFields.d.ts +1 -5
  11. package/dist/fields/getFields.d.ts.map +1 -1
  12. package/dist/fields/getFields.js +118 -24
  13. package/dist/fields/getFields.js.map +1 -1
  14. package/dist/hooks/afterChange.d.ts +5 -1
  15. package/dist/hooks/afterChange.d.ts.map +1 -1
  16. package/dist/hooks/afterChange.js +137 -23
  17. package/dist/hooks/afterChange.js.map +1 -1
  18. package/dist/hooks/afterDelete.d.ts +3 -1
  19. package/dist/hooks/afterDelete.d.ts.map +1 -1
  20. package/dist/hooks/afterDelete.js +27 -5
  21. package/dist/hooks/afterDelete.js.map +1 -1
  22. package/dist/hooks/afterRead.d.ts +6 -1
  23. package/dist/hooks/afterRead.d.ts.map +1 -1
  24. package/dist/hooks/afterRead.js +34 -7
  25. package/dist/hooks/afterRead.js.map +1 -1
  26. package/dist/hooks/beforeChange.d.ts +2 -1
  27. package/dist/hooks/beforeChange.d.ts.map +1 -1
  28. package/dist/hooks/beforeChange.js +18 -4
  29. package/dist/hooks/beforeChange.js.map +1 -1
  30. package/dist/hooks/normalizeUploadPrefix.d.ts +11 -0
  31. package/dist/hooks/normalizeUploadPrefix.d.ts.map +1 -0
  32. package/dist/hooks/normalizeUploadPrefix.js +42 -0
  33. package/dist/hooks/normalizeUploadPrefix.js.map +1 -0
  34. package/dist/hooks/publicOriginalURL.d.ts +8 -0
  35. package/dist/hooks/publicOriginalURL.d.ts.map +1 -0
  36. package/dist/hooks/publicOriginalURL.js +34 -0
  37. package/dist/hooks/publicOriginalURL.js.map +1 -0
  38. package/dist/plugin.d.ts.map +1 -1
  39. package/dist/plugin.js +72 -42
  40. package/dist/plugin.js.map +1 -1
  41. package/dist/types.d.ts +51 -12
  42. package/dist/types.d.ts.map +1 -1
  43. package/dist/types.js.map +1 -1
  44. package/dist/utilities/buildPrefixWithObjectKey.d.ts +8 -0
  45. package/dist/utilities/buildPrefixWithObjectKey.d.ts.map +1 -0
  46. package/dist/utilities/buildPrefixWithObjectKey.js +13 -0
  47. package/dist/utilities/buildPrefixWithObjectKey.js.map +1 -0
  48. package/dist/utilities/buildStoragePathData.d.ts +49 -0
  49. package/dist/utilities/buildStoragePathData.d.ts.map +1 -0
  50. package/dist/utilities/buildStoragePathData.js +67 -0
  51. package/dist/utilities/buildStoragePathData.js.map +1 -0
  52. package/dist/utilities/createFileOperations.d.ts +13 -0
  53. package/dist/utilities/createFileOperations.d.ts.map +1 -0
  54. package/dist/utilities/createFileOperations.js +293 -0
  55. package/dist/utilities/createFileOperations.js.map +1 -0
  56. package/dist/utilities/getFilePrefix.d.ts +17 -10
  57. package/dist/utilities/getFilePrefix.d.ts.map +1 -1
  58. package/dist/utilities/getFilePrefix.js +92 -30
  59. package/dist/utilities/getFilePrefix.js.map +1 -1
  60. package/dist/utilities/getIncomingFiles.js +2 -2
  61. package/dist/utilities/getIncomingFiles.js.map +1 -1
  62. package/dist/utilities/getStorageLocationSelect.d.ts +9 -0
  63. package/dist/utilities/getStorageLocationSelect.d.ts.map +1 -0
  64. package/dist/utilities/getStorageLocationSelect.js +60 -0
  65. package/dist/utilities/getStorageLocationSelect.js.map +1 -0
  66. package/dist/utilities/resolveSignedURLKey.d.ts +15 -2
  67. package/dist/utilities/resolveSignedURLKey.d.ts.map +1 -1
  68. package/dist/utilities/resolveSignedURLKey.js +44 -9
  69. package/dist/utilities/resolveSignedURLKey.js.map +1 -1
  70. package/dist/utilities/sanitizePrefix.d.ts +1 -9
  71. package/dist/utilities/sanitizePrefix.d.ts.map +1 -1
  72. package/dist/utilities/sanitizePrefix.js +1 -21
  73. package/dist/utilities/sanitizePrefix.js.map +1 -1
  74. package/package.json +24 -26
  75. package/dist/admin/fields/getFields.d.ts +0 -17
  76. package/dist/admin/fields/getFields.d.ts.map +0 -1
  77. package/dist/admin/fields/getFields.js +0 -98
  78. package/dist/admin/fields/getFields.js.map +0 -1
  79. package/dist/admin/index.d.ts +0 -4
  80. package/dist/admin/index.d.ts.map +0 -1
  81. package/dist/admin/index.js +0 -34
  82. package/dist/admin/index.js.map +0 -1
  83. package/dist/utilities/getFileKey.d.ts +0 -22
  84. package/dist/utilities/getFileKey.d.ts.map +0 -1
  85. package/dist/utilities/getFileKey.js +0 -23
  86. package/dist/utilities/getFileKey.js.map +0 -1
  87. package/dist/utilities/getFileKey.spec.js +0 -174
  88. package/dist/utilities/getFileKey.spec.js.map +0 -1
  89. package/dist/utilities/getFilePrefix.spec.js +0 -105
  90. package/dist/utilities/getFilePrefix.spec.js.map +0 -1
@@ -0,0 +1,293 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { isDeepStrictEqual } from 'node:util';
4
+ import { deepCopyObjectSimple, generatePayloadFileURL } from 'payload';
5
+ import { buildPrefixWithObjectKey } from './buildPrefixWithObjectKey.js';
6
+ import { buildStoragePathData, buildUploadPrefix, buildUploadStoragePathData } from './buildStoragePathData.js';
7
+ export const createFileOperations = ({ adapter, collection, collectionPrefix, disablePayloadAccessControl, generateFileURL, useCompositePrefixes })=>{
8
+ if (typeof adapter.deleteFile !== 'function') {
9
+ throw new Error(`Storage adapter "${adapter.name}" must implement deleteFile for collection "${collection.slug}".`);
10
+ }
11
+ return {
12
+ copy: async ({ from, req, to, trackStagedObject })=>{
13
+ await adapter.copyFile({
14
+ collection: req.payload.collections[collection.slug].config,
15
+ from,
16
+ req,
17
+ to
18
+ });
19
+ trackStagedObject({
20
+ key: to,
21
+ remove: async ()=>{
22
+ await adapter.deleteFile({
23
+ collection,
24
+ req,
25
+ storageFilePath: to
26
+ });
27
+ }
28
+ });
29
+ },
30
+ delete: async ({ key, req })=>{
31
+ await adapter.deleteFile({
32
+ collection,
33
+ req,
34
+ storageFilePath: key
35
+ });
36
+ },
37
+ hasLegacyFile: async ({ doc, req })=>{
38
+ if (typeof doc.filename !== 'string' || typeof doc.url !== 'string') {
39
+ return false;
40
+ }
41
+ const docPrefix = typeof doc.prefix === 'string' ? doc.prefix : undefined;
42
+ const objectFolder = buildPrefixWithObjectKey({
43
+ objectKey: typeof doc._objectKey === 'string' ? doc._objectKey : undefined,
44
+ prefix: docPrefix
45
+ });
46
+ const expectedURL = async ({ filename, size })=>{
47
+ if (generateFileURL) {
48
+ return generateFileURL({
49
+ collection,
50
+ filename,
51
+ prefix: objectFolder,
52
+ size
53
+ });
54
+ }
55
+ if (disablePayloadAccessControl && adapter.generateURL) {
56
+ return adapter.generateURL({
57
+ collection,
58
+ data: doc,
59
+ filename,
60
+ prefix: objectFolder
61
+ });
62
+ }
63
+ return generatePayloadFileURL({
64
+ collectionSlug: collection.slug,
65
+ config: req.payload.config,
66
+ filename,
67
+ relative: true
68
+ });
69
+ };
70
+ if (!isSameFileLocation({
71
+ actual: doc.url,
72
+ expected: await expectedURL({
73
+ filename: doc.filename
74
+ })
75
+ })) {
76
+ return false;
77
+ }
78
+ if (doc.variants && typeof doc.variants === 'object' && !Array.isArray(doc.variants)) {
79
+ for (const [sizeKey, size] of Object.entries(doc.variants)){
80
+ if (size && typeof size === 'object' && 'filename' in size && typeof size.filename === 'string') {
81
+ const imageSize = req.payload.collections[collection.slug]?.config.upload.variants?.find(({ name })=>name === sizeKey);
82
+ if (!('url' in size) || typeof size.url !== 'string' || !isSameFileLocation({
83
+ actual: size.url,
84
+ expected: await expectedURL({
85
+ filename: size.filename,
86
+ size: imageSize
87
+ })
88
+ })) {
89
+ return false;
90
+ }
91
+ }
92
+ }
93
+ }
94
+ return true;
95
+ },
96
+ resolveStorageKey: ({ _objectKey, filename, prefix })=>buildStoragePathData({
97
+ collectionPrefix,
98
+ docPrefix: buildPrefixWithObjectKey({
99
+ objectKey: _objectKey,
100
+ prefix
101
+ }),
102
+ filename,
103
+ useCompositePrefixes
104
+ }).storageFilePath,
105
+ ...adapter.moveFile && {
106
+ move: async ({ from, req, to, trackStagedObject })=>{
107
+ await adapter.moveFile({
108
+ collection: req.payload.collections[collection.slug].config,
109
+ from,
110
+ req,
111
+ to
112
+ });
113
+ trackStagedObject({
114
+ key: to,
115
+ remove: ()=>adapter.moveFile({
116
+ collection: req.payload.collections[collection.slug].config,
117
+ from: to,
118
+ req,
119
+ to: from
120
+ })
121
+ });
122
+ }
123
+ },
124
+ stage: async ({ data, files, req, trackStagedObject })=>{
125
+ const uploadedKeys = new Set();
126
+ const metadata = {};
127
+ const verifiedOriginal = req.context?._payloadVerifiedProviderOriginal;
128
+ for (const file of files){
129
+ const filename = path.basename(file.path);
130
+ const destinations = getStageDestinations({
131
+ data,
132
+ file,
133
+ filename
134
+ });
135
+ // Provider-direct uploads already placed the source object at its verified location.
136
+ // A bounded content probe can still appear in files, but it is not a representation
137
+ // Payload should upload again.
138
+ if (!destinations.length && verifiedOriginal) {
139
+ continue;
140
+ }
141
+ if (!destinations.length) {
142
+ throw new Error(`No stored representation describes ${filename}`);
143
+ }
144
+ const prefixLocation = destinations.find((destination)=>typeof destination.prefix === 'string');
145
+ const objectKeyLocation = destinations.find((destination)=>typeof destination._objectKey === 'string');
146
+ const prefix = typeof prefixLocation?.prefix === 'string' ? prefixLocation.prefix : undefined;
147
+ const objectKey = typeof objectKeyLocation?._objectKey === 'string' ? objectKeyLocation._objectKey : undefined;
148
+ const prefixData = buildUploadPrefix({
149
+ collectionPrefix,
150
+ docPrefix: typeof prefix === 'string' ? prefix : undefined,
151
+ useCompositePrefixes
152
+ });
153
+ const uploadPrefix = useCompositePrefixes ? prefixData.sanitizedDocPrefix : prefixData.uploadPrefix;
154
+ for (const destination of destinations){
155
+ destination.prefix = uploadPrefix;
156
+ destination._objectKey = objectKey;
157
+ }
158
+ const docPrefix = buildPrefixWithObjectKey({
159
+ objectKey,
160
+ prefix: uploadPrefix
161
+ });
162
+ const dataForUpload = {
163
+ ...data,
164
+ prefix: docPrefix
165
+ };
166
+ const storageFilePath = buildUploadStoragePathData({
167
+ collectionPrefix,
168
+ docPrefix,
169
+ filename,
170
+ useCompositePrefixes
171
+ }).storageFilePath;
172
+ if (uploadedKeys.has(storageFilePath)) {
173
+ throw new Error(`Duplicate managed cloud file: ${storageFilePath}`);
174
+ }
175
+ uploadedKeys.add(storageFilePath);
176
+ const buffer = 'buffer' in file ? file.buffer : adapter.supportsTempFiles ? Buffer.alloc(0) : await fs.readFile(file.sourcePath);
177
+ const uploadFile = {
178
+ buffer,
179
+ filename,
180
+ filesize: 'buffer' in file ? file.buffer.length : (await fs.stat(file.sourcePath)).size,
181
+ mimeType: getMimeType({
182
+ data,
183
+ destination: destinations[0],
184
+ filename
185
+ }),
186
+ ...'sourcePath' in file ? {
187
+ tempFilePath: file.sourcePath
188
+ } : {}
189
+ };
190
+ // A new object folder is allocated for every server upload, so an interrupted
191
+ // provider write can be removed without touching an earlier revision.
192
+ trackStagedObject({
193
+ key: storageFilePath,
194
+ remove: async ()=>{
195
+ await adapter.deleteFile({
196
+ collection,
197
+ req,
198
+ storageFilePath
199
+ });
200
+ }
201
+ });
202
+ const dataBeforeUpload = deepCopyObjectSimple(dataForUpload);
203
+ const result = await adapter.handleUpload({
204
+ collection,
205
+ data: dataForUpload,
206
+ file: uploadFile,
207
+ req,
208
+ storageFilePath
209
+ });
210
+ if (result && typeof result === 'object') {
211
+ // Adapters may return the entire input document, including fields made stale by this upload.
212
+ for (const [key, value] of Object.entries(result)){
213
+ if (!isDeepStrictEqual(value, dataBeforeUpload[key])) {
214
+ metadata[key] = value;
215
+ }
216
+ }
217
+ }
218
+ }
219
+ delete metadata.prefix;
220
+ delete metadata._objectKey;
221
+ return {
222
+ metadata
223
+ };
224
+ }
225
+ };
226
+ };
227
+ /**
228
+ * The document fields a staged file is stored for. The file's role names its field, because the
229
+ * main file and the original can share a filename in different folders. Fields with the same
230
+ * filename and `_objectKey` describe the same object, so they share its location.
231
+ */ const getStageDestinations = ({ data, file, filename })=>{
232
+ const representations = [
233
+ data
234
+ ];
235
+ if (data.original && typeof data.original === 'object') {
236
+ representations.push(data.original);
237
+ }
238
+ if (data.variants && typeof data.variants === 'object') {
239
+ representations.push(...Object.values(data.variants).filter((value)=>Boolean(value) && typeof value === 'object'));
240
+ }
241
+ const namedDestinations = representations.filter((value)=>value.filename === filename);
242
+ const roleDestination = file.role ? getRoleRepresentation({
243
+ data,
244
+ role: file.role
245
+ }) : undefined;
246
+ if (roleDestination?.filename !== filename) {
247
+ return namedDestinations;
248
+ }
249
+ return [
250
+ roleDestination,
251
+ ...namedDestinations.filter((value)=>value !== roleDestination && value._objectKey === roleDestination._objectKey)
252
+ ];
253
+ };
254
+ const getRoleRepresentation = ({ data, role })=>{
255
+ let representation = data;
256
+ if (role.type === 'original') {
257
+ representation = data.original;
258
+ } else if (role.type === 'size') {
259
+ representation = data.variants?.[role.sizeKey];
260
+ }
261
+ return representation && typeof representation === 'object' ? representation : undefined;
262
+ };
263
+ const getMimeType = ({ data, destination, filename })=>{
264
+ if (typeof destination.mimeType === 'string') {
265
+ return destination.mimeType;
266
+ }
267
+ if (data.original && typeof data.original === 'object') {
268
+ const original = data.original;
269
+ if (original.filename === filename && original.mimeType) {
270
+ return original.mimeType;
271
+ }
272
+ }
273
+ if (data.variants && typeof data.variants === 'object') {
274
+ for (const size of Object.values(data.variants)){
275
+ const candidate = size;
276
+ if (candidate?.filename === filename && candidate.mimeType) {
277
+ return candidate.mimeType;
278
+ }
279
+ }
280
+ }
281
+ return typeof data.mimeType === 'string' ? data.mimeType : 'application/octet-stream';
282
+ };
283
+ /** Signed and time-limited URLs change their query on every generation, so only the location is compared. */ const isSameFileLocation = ({ actual, expected })=>typeof expected === 'string' && getFileLocation({
284
+ url: actual
285
+ }) === getFileLocation({
286
+ url: expected
287
+ });
288
+ const getFileLocation = ({ url })=>{
289
+ const parsed = new URL(url, 'http://payload.local');
290
+ return url.startsWith('/') ? parsed.pathname : `${parsed.origin}${parsed.pathname}`;
291
+ };
292
+
293
+ //# sourceMappingURL=createFileOperations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/createFileOperations.ts"],"sourcesContent":["import type { CollectionConfig, FileToSave, ImageSize, JsonObject, UploadConfig } from 'payload'\n\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { isDeepStrictEqual } from 'node:util'\nimport { deepCopyObjectSimple, generatePayloadFileURL } from 'payload'\n\nimport type { GeneratedAdapter, GenerateFileURL } from '../types.js'\n\nimport { buildPrefixWithObjectKey } from './buildPrefixWithObjectKey.js'\nimport {\n buildStoragePathData,\n buildUploadPrefix,\n buildUploadStoragePathData,\n} from './buildStoragePathData.js'\n\ntype Args = {\n adapter: GeneratedAdapter\n collection: CollectionConfig\n collectionPrefix?: string\n disablePayloadAccessControl?: boolean\n generateFileURL?: GenerateFileURL\n useCompositePrefixes?: boolean\n}\n\nexport const createFileOperations = ({\n adapter,\n collection,\n collectionPrefix,\n disablePayloadAccessControl,\n generateFileURL,\n useCompositePrefixes,\n}: Args): NonNullable<UploadConfig['fileOperations']> => {\n if (typeof adapter.deleteFile !== 'function') {\n throw new Error(\n `Storage adapter \"${adapter.name}\" must implement deleteFile for collection \"${collection.slug}\".`,\n )\n }\n\n return {\n copy: async ({ from, req, to, trackStagedObject }) => {\n await adapter.copyFile({\n collection: req.payload.collections[collection.slug]!.config,\n from,\n req,\n to,\n })\n trackStagedObject({\n key: to,\n remove: async () => {\n await adapter.deleteFile({\n collection,\n req,\n storageFilePath: to,\n })\n },\n })\n },\n delete: async ({ key, req }) => {\n await adapter.deleteFile({\n collection,\n req,\n storageFilePath: key,\n })\n },\n hasLegacyFile: async ({ doc, req }) => {\n if (typeof doc.filename !== 'string' || typeof doc.url !== 'string') {\n return false\n }\n\n const docPrefix = typeof doc.prefix === 'string' ? doc.prefix : undefined\n const objectFolder = buildPrefixWithObjectKey({\n objectKey: typeof doc._objectKey === 'string' ? doc._objectKey : undefined,\n prefix: docPrefix,\n })\n const expectedURL = async ({ filename, size }: { filename: string; size?: ImageSize }) => {\n if (generateFileURL) {\n return generateFileURL({ collection, filename, prefix: objectFolder, size })\n }\n if (disablePayloadAccessControl && adapter.generateURL) {\n return adapter.generateURL({ collection, data: doc, filename, prefix: objectFolder })\n }\n return generatePayloadFileURL({\n collectionSlug: collection.slug,\n config: req.payload.config as unknown as Parameters<\n typeof generatePayloadFileURL\n >[0]['config'],\n filename,\n relative: true,\n })\n }\n if (\n !isSameFileLocation({\n actual: doc.url,\n expected: await expectedURL({ filename: doc.filename }),\n })\n ) {\n return false\n }\n if (doc.variants && typeof doc.variants === 'object' && !Array.isArray(doc.variants)) {\n for (const [sizeKey, size] of Object.entries(doc.variants)) {\n if (\n size &&\n typeof size === 'object' &&\n 'filename' in size &&\n typeof size.filename === 'string'\n ) {\n const imageSize = req.payload.collections[\n collection.slug\n ]?.config.upload.variants?.find(({ name }) => name === sizeKey)\n if (\n !('url' in size) ||\n typeof size.url !== 'string' ||\n !isSameFileLocation({\n actual: size.url,\n expected: await expectedURL({ filename: size.filename, size: imageSize }),\n })\n ) {\n return false\n }\n }\n }\n }\n return true\n },\n resolveStorageKey: ({ _objectKey, filename, prefix }) =>\n buildStoragePathData({\n collectionPrefix,\n docPrefix: buildPrefixWithObjectKey({ objectKey: _objectKey, prefix }),\n filename,\n useCompositePrefixes,\n }).storageFilePath,\n ...(adapter.moveFile && {\n move: async ({ from, req, to, trackStagedObject }) => {\n await adapter.moveFile!({\n collection: req.payload.collections[collection.slug]!.config,\n from,\n req,\n to,\n })\n trackStagedObject({\n key: to,\n remove: () =>\n adapter.moveFile!({\n collection: req.payload.collections[collection.slug]!.config,\n from: to,\n req,\n to: from,\n }),\n })\n },\n }),\n stage: async ({ data, files, req, trackStagedObject }) => {\n const uploadedKeys = new Set<string>()\n const metadata: Record<string, unknown> = {}\n const verifiedOriginal = req.context?._payloadVerifiedProviderOriginal as\n | { filename: string }\n | undefined\n for (const file of files) {\n const filename = path.basename(file.path)\n const destinations = getStageDestinations({ data, file, filename })\n // Provider-direct uploads already placed the source object at its verified location.\n // A bounded content probe can still appear in files, but it is not a representation\n // Payload should upload again.\n if (!destinations.length && verifiedOriginal) {\n continue\n }\n if (!destinations.length) {\n throw new Error(`No stored representation describes ${filename}`)\n }\n const prefixLocation = destinations.find(\n (destination) => typeof destination.prefix === 'string',\n )\n const objectKeyLocation = destinations.find(\n (destination) => typeof destination._objectKey === 'string',\n )\n const prefix =\n typeof prefixLocation?.prefix === 'string' ? prefixLocation.prefix : undefined\n const objectKey =\n typeof objectKeyLocation?._objectKey === 'string'\n ? objectKeyLocation._objectKey\n : undefined\n const prefixData = buildUploadPrefix({\n collectionPrefix,\n docPrefix: typeof prefix === 'string' ? prefix : undefined,\n useCompositePrefixes,\n })\n const uploadPrefix = useCompositePrefixes\n ? prefixData.sanitizedDocPrefix\n : prefixData.uploadPrefix\n for (const destination of destinations) {\n destination.prefix = uploadPrefix\n destination._objectKey = objectKey\n }\n const docPrefix = buildPrefixWithObjectKey({ objectKey, prefix: uploadPrefix })\n const dataForUpload = { ...data, prefix: docPrefix }\n const storageFilePath = buildUploadStoragePathData({\n collectionPrefix,\n docPrefix,\n filename,\n useCompositePrefixes,\n }).storageFilePath\n\n if (uploadedKeys.has(storageFilePath)) {\n throw new Error(`Duplicate managed cloud file: ${storageFilePath}`)\n }\n\n uploadedKeys.add(storageFilePath)\n const buffer =\n 'buffer' in file\n ? file.buffer\n : adapter.supportsTempFiles\n ? Buffer.alloc(0)\n : await fs.readFile(file.sourcePath)\n const uploadFile = {\n buffer,\n filename,\n filesize: 'buffer' in file ? file.buffer.length : (await fs.stat(file.sourcePath)).size,\n mimeType: getMimeType({ data, destination: destinations[0]!, filename }),\n ...('sourcePath' in file ? { tempFilePath: file.sourcePath } : {}),\n }\n\n // A new object folder is allocated for every server upload, so an interrupted\n // provider write can be removed without touching an earlier revision.\n trackStagedObject({\n key: storageFilePath,\n remove: async () => {\n await adapter.deleteFile({\n collection,\n req,\n storageFilePath,\n })\n },\n })\n\n const dataBeforeUpload = deepCopyObjectSimple(dataForUpload as JsonObject)\n const result = await adapter.handleUpload({\n collection,\n data: dataForUpload,\n file: uploadFile,\n req,\n storageFilePath,\n })\n\n if (result && typeof result === 'object') {\n // Adapters may return the entire input document, including fields made stale by this upload.\n for (const [key, value] of Object.entries(result)) {\n if (!isDeepStrictEqual(value, dataBeforeUpload[key])) {\n metadata[key] = value\n }\n }\n }\n }\n\n delete metadata.prefix\n delete metadata._objectKey\n return { metadata }\n },\n }\n}\n\n/**\n * The document fields a staged file is stored for. The file's role names its field, because the\n * main file and the original can share a filename in different folders. Fields with the same\n * filename and `_objectKey` describe the same object, so they share its location.\n */\nconst getStageDestinations = ({\n data,\n file,\n filename,\n}: {\n data: Record<string, unknown>\n file: FileToSave\n filename: string\n}): Record<string, unknown>[] => {\n const representations: Record<string, unknown>[] = [data]\n if (data.original && typeof data.original === 'object') {\n representations.push(data.original as Record<string, unknown>)\n }\n if (data.variants && typeof data.variants === 'object') {\n representations.push(\n ...Object.values(data.variants).filter(\n (value): value is Record<string, unknown> => Boolean(value) && typeof value === 'object',\n ),\n )\n }\n const namedDestinations = representations.filter((value) => value.filename === filename)\n const roleDestination = file.role ? getRoleRepresentation({ data, role: file.role }) : undefined\n\n if (roleDestination?.filename !== filename) {\n return namedDestinations\n }\n\n return [\n roleDestination,\n ...namedDestinations.filter(\n (value) => value !== roleDestination && value._objectKey === roleDestination._objectKey,\n ),\n ]\n}\n\nconst getRoleRepresentation = ({\n data,\n role,\n}: {\n data: Record<string, unknown>\n role: NonNullable<FileToSave['role']>\n}): Record<string, unknown> | undefined => {\n let representation: unknown = data\n\n if (role.type === 'original') {\n representation = data.original\n } else if (role.type === 'size') {\n representation = (data.variants as Record<string, unknown> | undefined)?.[role.sizeKey]\n }\n\n return representation && typeof representation === 'object'\n ? (representation as Record<string, unknown>)\n : undefined\n}\n\nconst getMimeType = ({\n data,\n destination,\n filename,\n}: {\n data: Record<string, unknown>\n destination: Record<string, unknown>\n filename: string\n}): string => {\n if (typeof destination.mimeType === 'string') {\n return destination.mimeType\n }\n if (data.original && typeof data.original === 'object') {\n const original = data.original as { filename?: string; mimeType?: string }\n if (original.filename === filename && original.mimeType) {\n return original.mimeType\n }\n }\n if (data.variants && typeof data.variants === 'object') {\n for (const size of Object.values(data.variants)) {\n const candidate = size as { filename?: string; mimeType?: string } | null\n if (candidate?.filename === filename && candidate.mimeType) {\n return candidate.mimeType\n }\n }\n }\n return typeof data.mimeType === 'string' ? data.mimeType : 'application/octet-stream'\n}\n\n/** Signed and time-limited URLs change their query on every generation, so only the location is compared. */\nconst isSameFileLocation = ({\n actual,\n expected,\n}: {\n actual: string\n expected: null | string | undefined\n}): boolean =>\n typeof expected === 'string' &&\n getFileLocation({ url: actual }) === getFileLocation({ url: expected })\n\nconst getFileLocation = ({ url }: { url: string }): string => {\n const parsed = new URL(url, 'http://payload.local')\n\n return url.startsWith('/') ? parsed.pathname : `${parsed.origin}${parsed.pathname}`\n}\n"],"names":["fs","path","isDeepStrictEqual","deepCopyObjectSimple","generatePayloadFileURL","buildPrefixWithObjectKey","buildStoragePathData","buildUploadPrefix","buildUploadStoragePathData","createFileOperations","adapter","collection","collectionPrefix","disablePayloadAccessControl","generateFileURL","useCompositePrefixes","deleteFile","Error","name","slug","copy","from","req","to","trackStagedObject","copyFile","payload","collections","config","key","remove","storageFilePath","delete","hasLegacyFile","doc","filename","url","docPrefix","prefix","undefined","objectFolder","objectKey","_objectKey","expectedURL","size","generateURL","data","collectionSlug","relative","isSameFileLocation","actual","expected","variants","Array","isArray","sizeKey","Object","entries","imageSize","upload","find","resolveStorageKey","moveFile","move","stage","files","uploadedKeys","Set","metadata","verifiedOriginal","context","_payloadVerifiedProviderOriginal","file","basename","destinations","getStageDestinations","length","prefixLocation","destination","objectKeyLocation","prefixData","uploadPrefix","sanitizedDocPrefix","dataForUpload","has","add","buffer","supportsTempFiles","Buffer","alloc","readFile","sourcePath","uploadFile","filesize","stat","mimeType","getMimeType","tempFilePath","dataBeforeUpload","result","handleUpload","value","representations","original","push","values","filter","Boolean","namedDestinations","roleDestination","role","getRoleRepresentation","representation","type","candidate","getFileLocation","parsed","URL","startsWith","pathname","origin"],"mappings":"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"}
@@ -1,20 +1,27 @@
1
- import type { CollectionConfig, PayloadRequest } from 'payload';
1
+ import type { CollectionConfig, FileData, PayloadRequest, TypeWithID } from 'payload';
2
2
  /**
3
- * Resolves the file prefix from the highest-priority available source and
4
- * always returns a sanitized value.
3
+ * Resolves the folder a stored object lives under (semantic `prefix` plus `_objectKey`).
5
4
  *
6
- * Resolution order:
7
- * 1. `prefixQueryParam`
8
- * 2. `uploadReference.prefix`
9
- * 3. Stored document `prefix` from the database
10
- *
11
- * Resolved values are passed through `sanitizePrefix`.
5
+ * An already-authorized `doc` wins outright. Otherwise an `uploadReference` is used (its
6
+ * `prefix` re-contained with {@link buildUploadStoragePathData}, since it hasn't been verified against
7
+ * a signed receipt by every adapter). Otherwise the document is resolved with
8
+ * `overrideAccess: false`, any query-param prefix only narrows that lookup, and `_objectKey`
9
+ * is read from the resolved document via `showHiddenFields`.
12
10
  */
13
- export declare function getFilePrefix({ collection, filename, prefixQueryParam, req, uploadReference, }: {
11
+ export declare function getFilePrefix({ collection, collectionPrefix, doc, filename, isOriginal, prefixQueryParam, req, uploadReference, useCompositePrefixes, }: {
14
12
  collection: CollectionConfig;
13
+ collectionPrefix?: string;
14
+ doc?: Partial<FileData> & TypeWithID;
15
15
  filename: string;
16
+ /** Serves the original when it shares the main file's name. */
17
+ isOriginal?: boolean;
18
+ /**
19
+ * Only narrows the access-controlled fallback lookup below; never trusted or
20
+ * returned directly. Prefer passing the access-checked document as `doc` instead.
21
+ */
16
22
  prefixQueryParam?: string;
17
23
  req: PayloadRequest;
18
24
  uploadReference?: unknown;
25
+ useCompositePrefixes?: boolean;
19
26
  }): Promise<string>;
20
27
  //# sourceMappingURL=getFilePrefix.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"getFilePrefix.d.ts","sourceRoot":"","sources":["../../src/utilities/getFilePrefix.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAgB,MAAM,SAAS,CAAA;AAI7E;;;;;;;;;;GAUG;AACH,wBAAsB,aAAa,CAAC,EAClC,UAAU,EACV,QAAQ,EACR,gBAAgB,EAChB,GAAG,EACH,eAAe,GAChB,EAAE;IACD,UAAU,EAAE,gBAAgB,CAAA;IAC5B,QAAQ,EAAE,MAAM,CAAA;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,GAAG,EAAE,cAAc,CAAA;IACnB,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B,GAAG,OAAO,CAAC,MAAM,CAAC,CAoClB"}
1
+ {"version":3,"file":"getFilePrefix.d.ts","sourceRoot":"","sources":["../../src/utilities/getFilePrefix.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,QAAQ,EACR,cAAc,EAEd,UAAU,EACX,MAAM,SAAS,CAAA;AAQhB;;;;;;;;GAQG;AACH,wBAAsB,aAAa,CAAC,EAClC,UAAU,EACV,gBAAgB,EAChB,GAAG,EACH,QAAQ,EACR,UAAU,EACV,gBAAgB,EAChB,GAAG,EACH,eAAe,EACf,oBAA4B,GAC7B,EAAE;IACD,UAAU,EAAE,gBAAgB,CAAA;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,GAAG,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,UAAU,CAAA;IACpC,QAAQ,EAAE,MAAM,CAAA;IAChB,+DAA+D;IAC/D,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,GAAG,EAAE,cAAc,CAAA;IACnB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B,GAAG,OAAO,CAAC,MAAM,CAAC,CA2ElB"}
@@ -1,46 +1,108 @@
1
+ import { isOriginalFileRequest } from 'payload/internal';
2
+ import { buildPrefixWithObjectKey } from './buildPrefixWithObjectKey.js';
3
+ import { buildUploadStoragePathData } from './buildStoragePathData.js';
1
4
  import { sanitizePrefix } from './sanitizePrefix.js';
2
5
  /**
3
- * Resolves the file prefix from the highest-priority available source and
4
- * always returns a sanitized value.
6
+ * Resolves the folder a stored object lives under (semantic `prefix` plus `_objectKey`).
5
7
  *
6
- * Resolution order:
7
- * 1. `prefixQueryParam`
8
- * 2. `uploadReference.prefix`
9
- * 3. Stored document `prefix` from the database
10
- *
11
- * Resolved values are passed through `sanitizePrefix`.
12
- */ export async function getFilePrefix({ collection, filename, prefixQueryParam, req, uploadReference }) {
13
- if (typeof prefixQueryParam === 'string') {
14
- return sanitizePrefix(prefixQueryParam);
8
+ * An already-authorized `doc` wins outright. Otherwise an `uploadReference` is used (its
9
+ * `prefix` re-contained with {@link buildUploadStoragePathData}, since it hasn't been verified against
10
+ * a signed receipt by every adapter). Otherwise the document is resolved with
11
+ * `overrideAccess: false`, any query-param prefix only narrows that lookup, and `_objectKey`
12
+ * is read from the resolved document via `showHiddenFields`.
13
+ */ export async function getFilePrefix({ collection, collectionPrefix, doc, filename, isOriginal, prefixQueryParam, req, uploadReference, useCompositePrefixes = false }) {
14
+ // The serve path already loaded and authorized this document — trust it over any
15
+ // client-supplied upload reference or query prefix.
16
+ if (doc) {
17
+ return getRepresentationPrefix({
18
+ doc,
19
+ filename,
20
+ isOriginal
21
+ });
15
22
  }
16
- // Prioritize the upload reference prefix if there is one.
23
+ // Upload instructions call handlers without a document yet. Re-contain the claimed
24
+ // prefix — it hasn't been verified against a signed receipt by every adapter.
17
25
  if (uploadReference && typeof uploadReference === 'object' && 'prefix' in uploadReference && typeof uploadReference.prefix === 'string') {
18
- return sanitizePrefix(uploadReference.prefix);
26
+ const referenceObjectKey = '_objectKey' in uploadReference ? uploadReference._objectKey : undefined;
27
+ const containedPrefix = buildUploadStoragePathData({
28
+ collectionPrefix,
29
+ docPrefix: uploadReference.prefix,
30
+ filename,
31
+ useCompositePrefixes
32
+ }).sanitizedDocPrefix;
33
+ return buildPrefixWithObjectKey({
34
+ objectKey: referenceObjectKey,
35
+ prefix: containedPrefix
36
+ });
19
37
  }
20
- const imageSizes = collection?.upload?.imageSizes || [];
38
+ // Reads without a query prefix or read-access constraints skip the endpoint's document lookup.
39
+ const variants = collection?.upload?.variants || [];
40
+ const filenameClause = {
41
+ or: [
42
+ {
43
+ filename: {
44
+ equals: filename
45
+ }
46
+ },
47
+ {
48
+ 'original.filename': {
49
+ equals: filename
50
+ }
51
+ },
52
+ ...variants.map((imageSize)=>({
53
+ [`variants.${imageSize.name}.filename`]: {
54
+ equals: filename
55
+ }
56
+ }))
57
+ ]
58
+ };
59
+ // Only filter by prefix when the collection persists a `prefix` field (querying a missing field throws).
60
+ const hasPrefixField = collection.fields?.some((field)=>'name' in field && field.name === 'prefix');
61
+ const where = typeof prefixQueryParam === 'string' && hasPrefixField ? {
62
+ and: [
63
+ filenameClause,
64
+ {
65
+ prefix: {
66
+ equals: sanitizePrefix(prefixQueryParam)
67
+ }
68
+ }
69
+ ]
70
+ } : filenameClause;
21
71
  const files = await req.payload.find({
22
72
  collection: collection.slug,
23
73
  depth: 0,
24
74
  draft: true,
25
75
  limit: 1,
76
+ overrideAccess: false,
26
77
  pagination: false,
27
- where: {
28
- or: [
29
- {
30
- filename: {
31
- equals: filename
32
- }
33
- },
34
- ...imageSizes.map((imageSize)=>({
35
- [`sizes.${imageSize.name}.filename`]: {
36
- equals: filename
37
- }
38
- }))
39
- ]
40
- }
78
+ req,
79
+ select: {
80
+ _objectKey: true,
81
+ filename: true,
82
+ original: true,
83
+ prefix: true,
84
+ variants: true
85
+ },
86
+ showHiddenFields: true,
87
+ where
41
88
  });
42
- const prefix = files?.docs?.[0]?.prefix;
43
- return prefix ? sanitizePrefix(prefix) : '';
89
+ const found = files?.docs?.[0];
90
+ return found ? getRepresentationPrefix({
91
+ doc: found,
92
+ filename,
93
+ isOriginal
94
+ }) : '';
44
95
  }
96
+ /** The main file wins a shared filename unless the original is asked for. */ const getRepresentationPrefix = ({ doc, filename, isOriginal })=>{
97
+ const representation = isOriginalFileRequest({
98
+ doc,
99
+ filename,
100
+ isOriginal
101
+ }) ? doc.original : Object.values(doc.variants ?? {}).find((variant)=>variant?.filename === filename) ?? doc;
102
+ return buildPrefixWithObjectKey({
103
+ objectKey: representation._objectKey ?? (!doc.original?.filename ? doc._objectKey : undefined),
104
+ prefix: representation.prefix ?? (!doc.original?.filename ? doc.prefix : undefined)
105
+ });
106
+ };
45
107
 
46
108
  //# sourceMappingURL=getFilePrefix.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utilities/getFilePrefix.ts"],"sourcesContent":["import type { CollectionConfig, PayloadRequest, UploadConfig } from 'payload'\n\nimport { sanitizePrefix } from './sanitizePrefix.js'\n\n/**\n * Resolves the file prefix from the highest-priority available source and\n * always returns a sanitized value.\n *\n * Resolution order:\n * 1. `prefixQueryParam`\n * 2. `uploadReference.prefix`\n * 3. Stored document `prefix` from the database\n *\n * Resolved values are passed through `sanitizePrefix`.\n */\nexport async function getFilePrefix({\n collection,\n filename,\n prefixQueryParam,\n req,\n uploadReference,\n}: {\n collection: CollectionConfig\n filename: string\n prefixQueryParam?: string\n req: PayloadRequest\n uploadReference?: unknown\n}): Promise<string> {\n if (typeof prefixQueryParam === 'string') {\n return sanitizePrefix(prefixQueryParam)\n }\n\n // Prioritize the upload reference prefix if there is one.\n if (\n uploadReference &&\n typeof uploadReference === 'object' &&\n 'prefix' in uploadReference &&\n typeof uploadReference.prefix === 'string'\n ) {\n return sanitizePrefix(uploadReference.prefix)\n }\n\n const imageSizes = (collection?.upload as UploadConfig)?.imageSizes || []\n\n const files = await req.payload.find({\n collection: collection.slug,\n depth: 0,\n draft: true,\n limit: 1,\n pagination: false,\n where: {\n or: [\n {\n filename: { equals: filename },\n },\n ...imageSizes.map((imageSize) => ({\n [`sizes.${imageSize.name}.filename`]: { equals: filename },\n })),\n ],\n },\n })\n const prefix = files?.docs?.[0]?.prefix\n return prefix ? sanitizePrefix(prefix as string) : ''\n}\n"],"names":["sanitizePrefix","getFilePrefix","collection","filename","prefixQueryParam","req","uploadReference","prefix","imageSizes","upload","files","payload","find","slug","depth","draft","limit","pagination","where","or","equals","map","imageSize","name","docs"],"mappings":"AAEA,SAASA,cAAc,QAAQ,sBAAqB;AAEpD;;;;;;;;;;CAUC,GACD,OAAO,eAAeC,cAAc,EAClCC,UAAU,EACVC,QAAQ,EACRC,gBAAgB,EAChBC,GAAG,EACHC,eAAe,EAOhB;IACC,IAAI,OAAOF,qBAAqB,UAAU;QACxC,OAAOJ,eAAeI;IACxB;IAEA,0DAA0D;IAC1D,IACEE,mBACA,OAAOA,oBAAoB,YAC3B,YAAYA,mBACZ,OAAOA,gBAAgBC,MAAM,KAAK,UAClC;QACA,OAAOP,eAAeM,gBAAgBC,MAAM;IAC9C;IAEA,MAAMC,aAAa,AAACN,YAAYO,QAAyBD,cAAc,EAAE;IAEzE,MAAME,QAAQ,MAAML,IAAIM,OAAO,CAACC,IAAI,CAAC;QACnCV,YAAYA,WAAWW,IAAI;QAC3BC,OAAO;QACPC,OAAO;QACPC,OAAO;QACPC,YAAY;QACZC,OAAO;YACLC,IAAI;gBACF;oBACEhB,UAAU;wBAAEiB,QAAQjB;oBAAS;gBAC/B;mBACGK,WAAWa,GAAG,CAAC,CAACC,YAAe,CAAA;wBAChC,CAAC,CAAC,MAAM,EAAEA,UAAUC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE;4BAAEH,QAAQjB;wBAAS;oBAC3D,CAAA;aACD;QACH;IACF;IACA,MAAMI,SAASG,OAAOc,MAAM,CAAC,EAAE,EAAEjB;IACjC,OAAOA,SAASP,eAAeO,UAAoB;AACrD"}
1
+ {"version":3,"sources":["../../src/utilities/getFilePrefix.ts"],"sourcesContent":["import type {\n CollectionConfig,\n FileData,\n PayloadRequest,\n SanitizedUploadConfig,\n TypeWithID,\n} from 'payload'\n\nimport { isOriginalFileRequest } from 'payload/internal'\n\nimport { buildPrefixWithObjectKey } from './buildPrefixWithObjectKey.js'\nimport { buildUploadStoragePathData } from './buildStoragePathData.js'\nimport { sanitizePrefix } from './sanitizePrefix.js'\n\n/**\n * Resolves the folder a stored object lives under (semantic `prefix` plus `_objectKey`).\n *\n * An already-authorized `doc` wins outright. Otherwise an `uploadReference` is used (its\n * `prefix` re-contained with {@link buildUploadStoragePathData}, since it hasn't been verified against\n * a signed receipt by every adapter). Otherwise the document is resolved with\n * `overrideAccess: false`, any query-param prefix only narrows that lookup, and `_objectKey`\n * is read from the resolved document via `showHiddenFields`.\n */\nexport async function getFilePrefix({\n collection,\n collectionPrefix,\n doc,\n filename,\n isOriginal,\n prefixQueryParam,\n req,\n uploadReference,\n useCompositePrefixes = false,\n}: {\n collection: CollectionConfig\n collectionPrefix?: string\n doc?: Partial<FileData> & TypeWithID\n filename: string\n /** Serves the original when it shares the main file's name. */\n isOriginal?: boolean\n /**\n * Only narrows the access-controlled fallback lookup below; never trusted or\n * returned directly. Prefer passing the access-checked document as `doc` instead.\n */\n prefixQueryParam?: string\n req: PayloadRequest\n uploadReference?: unknown\n useCompositePrefixes?: boolean\n}): Promise<string> {\n // The serve path already loaded and authorized this document — trust it over any\n // client-supplied upload reference or query prefix.\n if (doc) {\n return getRepresentationPrefix({ doc, filename, isOriginal })\n }\n\n // Upload instructions call handlers without a document yet. Re-contain the claimed\n // prefix — it hasn't been verified against a signed receipt by every adapter.\n if (\n uploadReference &&\n typeof uploadReference === 'object' &&\n 'prefix' in uploadReference &&\n typeof uploadReference.prefix === 'string'\n ) {\n const referenceObjectKey =\n '_objectKey' in uploadReference\n ? (uploadReference as { _objectKey?: string })._objectKey\n : undefined\n const containedPrefix = buildUploadStoragePathData({\n collectionPrefix,\n docPrefix: uploadReference.prefix,\n filename,\n useCompositePrefixes,\n }).sanitizedDocPrefix\n return buildPrefixWithObjectKey({ objectKey: referenceObjectKey, prefix: containedPrefix })\n }\n\n // Reads without a query prefix or read-access constraints skip the endpoint's document lookup.\n const variants = (collection?.upload as SanitizedUploadConfig)?.variants || []\n\n const filenameClause = {\n or: [\n {\n filename: { equals: filename },\n },\n { 'original.filename': { equals: filename } },\n ...variants.map((imageSize) => ({\n [`variants.${imageSize.name}.filename`]: { equals: filename },\n })),\n ],\n }\n\n // Only filter by prefix when the collection persists a `prefix` field (querying a missing field throws).\n const hasPrefixField = collection.fields?.some(\n (field) => 'name' in field && field.name === 'prefix',\n )\n const where =\n typeof prefixQueryParam === 'string' && hasPrefixField\n ? { and: [filenameClause, { prefix: { equals: sanitizePrefix(prefixQueryParam) } }] }\n : filenameClause\n\n const files = await req.payload.find({\n collection: collection.slug,\n depth: 0,\n draft: true,\n limit: 1,\n overrideAccess: false,\n pagination: false,\n req,\n select: {\n _objectKey: true,\n filename: true,\n original: true,\n prefix: true,\n variants: true,\n },\n showHiddenFields: true,\n where,\n })\n\n const found = files?.docs?.[0] as\n | ({ _objectKey?: string; prefix?: string } & Partial<FileData>)\n | undefined\n return found ? getRepresentationPrefix({ doc: found, filename, isOriginal }) : ''\n}\n\n/** The main file wins a shared filename unless the original is asked for. */\nconst getRepresentationPrefix = ({\n doc,\n filename,\n isOriginal,\n}: {\n doc: Partial<FileData>\n filename: string\n isOriginal?: boolean\n}): string => {\n const representation = isOriginalFileRequest({ doc, filename, isOriginal })\n ? doc.original!\n : (Object.values(doc.variants ?? {}).find((variant) => variant?.filename === filename) ?? doc)\n return buildPrefixWithObjectKey({\n objectKey: representation._objectKey ?? (!doc.original?.filename ? doc._objectKey : undefined),\n prefix: representation.prefix ?? (!doc.original?.filename ? doc.prefix : undefined),\n })\n}\n"],"names":["isOriginalFileRequest","buildPrefixWithObjectKey","buildUploadStoragePathData","sanitizePrefix","getFilePrefix","collection","collectionPrefix","doc","filename","isOriginal","prefixQueryParam","req","uploadReference","useCompositePrefixes","getRepresentationPrefix","prefix","referenceObjectKey","_objectKey","undefined","containedPrefix","docPrefix","sanitizedDocPrefix","objectKey","variants","upload","filenameClause","or","equals","map","imageSize","name","hasPrefixField","fields","some","field","where","and","files","payload","find","slug","depth","draft","limit","overrideAccess","pagination","select","original","showHiddenFields","found","docs","representation","Object","values","variant"],"mappings":"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"}
@@ -16,8 +16,8 @@ export function getIncomingFiles({ data, req }) {
16
16
  files = [
17
17
  mainFile
18
18
  ];
19
- if (data?.sizes) {
20
- Object.entries(data.sizes).forEach(([key, resizedFileData])=>{
19
+ if (data?.variants) {
20
+ Object.entries(data.variants).forEach(([key, resizedFileData])=>{
21
21
  if (payloadUploadSizes?.[key] && resizedFileData.mimeType) {
22
22
  files = files.concat([
23
23
  {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utilities/getIncomingFiles.ts"],"sourcesContent":["import type { FileData, PayloadRequest } from 'payload'\n\nimport type { File } from '../types.js'\n\ninterface CloudStorageContext {\n file: PayloadRequest['file']\n uploadSizes: PayloadRequest['payloadUploadSizes']\n}\n\nexport function getIncomingFiles({\n data,\n req,\n}: {\n data: Partial<FileData>\n req: PayloadRequest\n}): File[] {\n // Fall back to context if req.file was cleared\n const ctx = req.context?._payloadCloudStorage as CloudStorageContext | undefined\n const file = req.file ?? ctx?.file\n const payloadUploadSizes = req.payloadUploadSizes ?? ctx?.uploadSizes\n\n let files: File[] = []\n\n if (file && data.filename && data.mimeType) {\n const mainFile: File = {\n buffer: file.data,\n filename: data.filename,\n filesize: file.size,\n mimeType: data.mimeType,\n tempFilePath: file.tempFilePath,\n uploadReference: file.uploadReference,\n }\n\n files = [mainFile]\n\n if (data?.sizes) {\n Object.entries(data.sizes).forEach(([key, resizedFileData]) => {\n if (payloadUploadSizes?.[key] && resizedFileData.mimeType) {\n files = files.concat([\n {\n buffer: payloadUploadSizes[key],\n filename: `${resizedFileData.filename}`,\n filesize: payloadUploadSizes[key].length,\n mimeType: resizedFileData.mimeType,\n },\n ])\n }\n })\n }\n }\n\n return files\n}\n"],"names":["getIncomingFiles","data","req","ctx","context","_payloadCloudStorage","file","payloadUploadSizes","uploadSizes","files","filename","mimeType","mainFile","buffer","filesize","size","tempFilePath","uploadReference","sizes","Object","entries","forEach","key","resizedFileData","concat","length"],"mappings":"AASA,OAAO,SAASA,iBAAiB,EAC/BC,IAAI,EACJC,GAAG,EAIJ;IACC,+CAA+C;IAC/C,MAAMC,MAAMD,IAAIE,OAAO,EAAEC;IACzB,MAAMC,OAAOJ,IAAII,IAAI,IAAIH,KAAKG;IAC9B,MAAMC,qBAAqBL,IAAIK,kBAAkB,IAAIJ,KAAKK;IAE1D,IAAIC,QAAgB,EAAE;IAEtB,IAAIH,QAAQL,KAAKS,QAAQ,IAAIT,KAAKU,QAAQ,EAAE;QAC1C,MAAMC,WAAiB;YACrBC,QAAQP,KAAKL,IAAI;YACjBS,UAAUT,KAAKS,QAAQ;YACvBI,UAAUR,KAAKS,IAAI;YACnBJ,UAAUV,KAAKU,QAAQ;YACvBK,cAAcV,KAAKU,YAAY;YAC/BC,iBAAiBX,KAAKW,eAAe;QACvC;QAEAR,QAAQ;YAACG;SAAS;QAElB,IAAIX,MAAMiB,OAAO;YACfC,OAAOC,OAAO,CAACnB,KAAKiB,KAAK,EAAEG,OAAO,CAAC,CAAC,CAACC,KAAKC,gBAAgB;gBACxD,IAAIhB,oBAAoB,CAACe,IAAI,IAAIC,gBAAgBZ,QAAQ,EAAE;oBACzDF,QAAQA,MAAMe,MAAM,CAAC;wBACnB;4BACEX,QAAQN,kBAAkB,CAACe,IAAI;4BAC/BZ,UAAU,GAAGa,gBAAgBb,QAAQ,EAAE;4BACvCI,UAAUP,kBAAkB,CAACe,IAAI,CAACG,MAAM;4BACxCd,UAAUY,gBAAgBZ,QAAQ;wBACpC;qBACD;gBACH;YACF;QACF;IACF;IAEA,OAAOF;AACT"}
1
+ {"version":3,"sources":["../../src/utilities/getIncomingFiles.ts"],"sourcesContent":["import type { FileData, PayloadRequest } from 'payload'\n\nimport type { File } from '../types.js'\n\ninterface CloudStorageContext {\n file: PayloadRequest['file']\n uploadSizes: PayloadRequest['payloadUploadSizes']\n}\n\nexport function getIncomingFiles({\n data,\n req,\n}: {\n data: Partial<FileData>\n req: PayloadRequest\n}): File[] {\n // Fall back to context if req.file was cleared\n const ctx = req.context?._payloadCloudStorage as CloudStorageContext | undefined\n const file = req.file ?? ctx?.file\n const payloadUploadSizes = req.payloadUploadSizes ?? ctx?.uploadSizes\n\n let files: File[] = []\n\n if (file && data.filename && data.mimeType) {\n const mainFile: File = {\n buffer: file.data,\n filename: data.filename,\n filesize: file.size,\n mimeType: data.mimeType,\n tempFilePath: file.tempFilePath,\n uploadReference: file.uploadReference,\n }\n\n files = [mainFile]\n\n if (data?.variants) {\n Object.entries(data.variants).forEach(([key, resizedFileData]) => {\n if (payloadUploadSizes?.[key] && resizedFileData.mimeType) {\n files = files.concat([\n {\n buffer: payloadUploadSizes[key],\n filename: `${resizedFileData.filename}`,\n filesize: payloadUploadSizes[key].length,\n mimeType: resizedFileData.mimeType,\n },\n ])\n }\n })\n }\n }\n\n return files\n}\n"],"names":["getIncomingFiles","data","req","ctx","context","_payloadCloudStorage","file","payloadUploadSizes","uploadSizes","files","filename","mimeType","mainFile","buffer","filesize","size","tempFilePath","uploadReference","variants","Object","entries","forEach","key","resizedFileData","concat","length"],"mappings":"AASA,OAAO,SAASA,iBAAiB,EAC/BC,IAAI,EACJC,GAAG,EAIJ;IACC,+CAA+C;IAC/C,MAAMC,MAAMD,IAAIE,OAAO,EAAEC;IACzB,MAAMC,OAAOJ,IAAII,IAAI,IAAIH,KAAKG;IAC9B,MAAMC,qBAAqBL,IAAIK,kBAAkB,IAAIJ,KAAKK;IAE1D,IAAIC,QAAgB,EAAE;IAEtB,IAAIH,QAAQL,KAAKS,QAAQ,IAAIT,KAAKU,QAAQ,EAAE;QAC1C,MAAMC,WAAiB;YACrBC,QAAQP,KAAKL,IAAI;YACjBS,UAAUT,KAAKS,QAAQ;YACvBI,UAAUR,KAAKS,IAAI;YACnBJ,UAAUV,KAAKU,QAAQ;YACvBK,cAAcV,KAAKU,YAAY;YAC/BC,iBAAiBX,KAAKW,eAAe;QACvC;QAEAR,QAAQ;YAACG;SAAS;QAElB,IAAIX,MAAMiB,UAAU;YAClBC,OAAOC,OAAO,CAACnB,KAAKiB,QAAQ,EAAEG,OAAO,CAAC,CAAC,CAACC,KAAKC,gBAAgB;gBAC3D,IAAIhB,oBAAoB,CAACe,IAAI,IAAIC,gBAAgBZ,QAAQ,EAAE;oBACzDF,QAAQA,MAAMe,MAAM,CAAC;wBACnB;4BACEX,QAAQN,kBAAkB,CAACe,IAAI;4BAC/BZ,UAAU,GAAGa,gBAAgBb,QAAQ,EAAE;4BACvCI,UAAUP,kBAAkB,CAACe,IAAI,CAACG,MAAM;4BACxCd,UAAUY,gBAAgBZ,QAAQ;wBACpC;qBACD;gBACH;YACF;QACF;IACF;IAEA,OAAOF;AACT"}
@@ -0,0 +1,9 @@
1
+ import type { CollectionConfig } from 'payload';
2
+ /**
3
+ * URL hooks rebuild direct URLs from each representation's stored location, so a positive
4
+ * selection of a URL must also read the fields that location is built from.
5
+ */
6
+ export declare const getStorageLocationSelect: ({ select: existingSelect, }: {
7
+ select: CollectionConfig['select'];
8
+ }) => NonNullable<CollectionConfig['select']>;
9
+ //# sourceMappingURL=getStorageLocationSelect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getStorageLocationSelect.d.ts","sourceRoot":"","sources":["../../src/utilities/getStorageLocationSelect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAc,MAAM,SAAS,CAAA;AAQ3D;;;GAGG;AACH,eAAO,MAAM,wBAAwB,gCAGhC;IACD,MAAM,EAAE,gBAAgB,CAAC,QAAQ,CAAC,CAAA;CACnC,KAAG,WAAW,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CASzC,CAAA"}