@payloadcms/plugin-cloud-storage 4.0.0-internal.f11981a → 4.0.0-internal.f15737c

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 (76) 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 +1 -1
  7. package/dist/exports/utilities.d.ts.map +1 -1
  8. package/dist/exports/utilities.js +1 -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 +54 -22
  13. package/dist/fields/getFields.js.map +1 -1
  14. package/dist/hooks/afterChange.d.ts +3 -1
  15. package/dist/hooks/afterChange.d.ts.map +1 -1
  16. package/dist/hooks/afterChange.js +78 -22
  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 +17 -3
  21. package/dist/hooks/afterDelete.js.map +1 -1
  22. package/dist/hooks/afterRead.d.ts.map +1 -1
  23. package/dist/hooks/afterRead.js +16 -3
  24. package/dist/hooks/afterRead.js.map +1 -1
  25. package/dist/hooks/beforeChange.d.ts.map +1 -1
  26. package/dist/hooks/beforeChange.js +14 -3
  27. package/dist/hooks/beforeChange.js.map +1 -1
  28. package/dist/hooks/normalizeUploadPrefix.d.ts +11 -0
  29. package/dist/hooks/normalizeUploadPrefix.d.ts.map +1 -0
  30. package/dist/hooks/normalizeUploadPrefix.js +42 -0
  31. package/dist/hooks/normalizeUploadPrefix.js.map +1 -0
  32. package/dist/plugin.d.ts.map +1 -1
  33. package/dist/plugin.js +43 -38
  34. package/dist/plugin.js.map +1 -1
  35. package/dist/types.d.ts +22 -12
  36. package/dist/types.d.ts.map +1 -1
  37. package/dist/types.js.map +1 -1
  38. package/dist/utilities/buildPrefixWithObjectKey.d.ts +8 -0
  39. package/dist/utilities/buildPrefixWithObjectKey.d.ts.map +1 -0
  40. package/dist/utilities/buildPrefixWithObjectKey.js +13 -0
  41. package/dist/utilities/buildPrefixWithObjectKey.js.map +1 -0
  42. package/dist/utilities/buildStoragePathData.d.ts +49 -0
  43. package/dist/utilities/buildStoragePathData.d.ts.map +1 -0
  44. package/dist/utilities/buildStoragePathData.js +67 -0
  45. package/dist/utilities/buildStoragePathData.js.map +1 -0
  46. package/dist/utilities/getFilePrefix.d.ts +18 -10
  47. package/dist/utilities/getFilePrefix.d.ts.map +1 -1
  48. package/dist/utilities/getFilePrefix.js +70 -30
  49. package/dist/utilities/getFilePrefix.js.map +1 -1
  50. package/dist/utilities/getIncomingFiles.js +2 -2
  51. package/dist/utilities/getIncomingFiles.js.map +1 -1
  52. package/dist/utilities/resolveSignedURLKey.d.ts +12 -1
  53. package/dist/utilities/resolveSignedURLKey.d.ts.map +1 -1
  54. package/dist/utilities/resolveSignedURLKey.js +42 -8
  55. package/dist/utilities/resolveSignedURLKey.js.map +1 -1
  56. package/dist/utilities/sanitizePrefix.d.ts +1 -9
  57. package/dist/utilities/sanitizePrefix.d.ts.map +1 -1
  58. package/dist/utilities/sanitizePrefix.js +1 -21
  59. package/dist/utilities/sanitizePrefix.js.map +1 -1
  60. package/package.json +6 -6
  61. package/dist/admin/fields/getFields.d.ts +0 -17
  62. package/dist/admin/fields/getFields.d.ts.map +0 -1
  63. package/dist/admin/fields/getFields.js +0 -98
  64. package/dist/admin/fields/getFields.js.map +0 -1
  65. package/dist/admin/index.d.ts +0 -4
  66. package/dist/admin/index.d.ts.map +0 -1
  67. package/dist/admin/index.js +0 -34
  68. package/dist/admin/index.js.map +0 -1
  69. package/dist/utilities/getFileKey.d.ts +0 -22
  70. package/dist/utilities/getFileKey.d.ts.map +0 -1
  71. package/dist/utilities/getFileKey.js +0 -23
  72. package/dist/utilities/getFileKey.js.map +0 -1
  73. package/dist/utilities/getFileKey.spec.js +0 -174
  74. package/dist/utilities/getFileKey.spec.js.map +0 -1
  75. package/dist/utilities/getFilePrefix.spec.js +0 -105
  76. package/dist/utilities/getFilePrefix.spec.js.map +0 -1
package/README.md CHANGED
@@ -98,6 +98,8 @@ Instead, all uploads will still be reached from the default `/:collectionSlug/fi
98
98
 
99
99
  If this does not apply to you (your upload collection has `read: () => true` or similar) you can disable this functionality by setting `disablePayloadAccessControl` to `true`. When this setting is in place, this plugin will update your file URLs to point directly to your cloud host.
100
100
 
101
+ `disablePayloadAccessControl` does not disable safe outbound fetching. If Payload needs to fetch files from a private or internal storage endpoint, configure the upload collection's [`skipSafeFetch`](https://payloadcms.com/docs/upload/overview#skip-safe-fetch) option with an explicit allowlist for the trusted destination. Set `skipSafeFetch: true` only when every URL accepted by the collection is trusted.
102
+
101
103
  ## Credit
102
104
 
103
105
  This plugin was created with significant help, and code, from [Alex Bechmann](https://github.com/alexbechmann) and [Richard VanBergen](https://github.com/richardvanbergen). Thank you!!
@@ -1,4 +1,4 @@
1
1
  export { createClientUploadHandler } from '../client/createClientUploadHandler.js';
2
- export { getFileKey } from '../utilities/getFileKey.js';
2
+ export { buildStoragePathData, buildUploadStoragePathData, } from '../utilities/buildStoragePathData.js';
3
3
  export { sanitizePrefix } from '../utilities/sanitizePrefix.js';
4
4
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/exports/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,wCAAwC,CAAA;AAClF,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/exports/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,wCAAwC,CAAA;AAClF,OAAO,EACL,oBAAoB,EACpB,0BAA0B,GAC3B,MAAM,sCAAsC,CAAA;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA"}
@@ -1,5 +1,5 @@
1
1
  export { createClientUploadHandler } from '../client/createClientUploadHandler.js';
2
- export { getFileKey } from '../utilities/getFileKey.js';
2
+ export { buildStoragePathData, buildUploadStoragePathData } from '../utilities/buildStoragePathData.js';
3
3
  export { sanitizePrefix } from '../utilities/sanitizePrefix.js';
4
4
 
5
5
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/exports/client.ts"],"sourcesContent":["export { createClientUploadHandler } from '../client/createClientUploadHandler.js'\nexport { getFileKey } from '../utilities/getFileKey.js'\nexport { sanitizePrefix } from '../utilities/sanitizePrefix.js'\n"],"names":["createClientUploadHandler","getFileKey","sanitizePrefix"],"mappings":"AAAA,SAASA,yBAAyB,QAAQ,yCAAwC;AAClF,SAASC,UAAU,QAAQ,6BAA4B;AACvD,SAASC,cAAc,QAAQ,iCAAgC"}
1
+ {"version":3,"sources":["../../src/exports/client.ts"],"sourcesContent":["export { createClientUploadHandler } from '../client/createClientUploadHandler.js'\nexport {\n buildStoragePathData,\n buildUploadStoragePathData,\n} from '../utilities/buildStoragePathData.js'\nexport { sanitizePrefix } from '../utilities/sanitizePrefix.js'\n"],"names":["createClientUploadHandler","buildStoragePathData","buildUploadStoragePathData","sanitizePrefix"],"mappings":"AAAA,SAASA,yBAAyB,QAAQ,yCAAwC;AAClF,SACEC,oBAAoB,EACpBC,0BAA0B,QACrB,uCAAsC;AAC7C,SAASC,cAAc,QAAQ,iCAAgC"}
@@ -1,4 +1,4 @@
1
- export { getFileKey } from '../utilities/getFileKey.js';
1
+ export { buildStoragePathData, buildUploadStoragePathData, isStoragePathWithinCollectionPrefix, } from '../utilities/buildStoragePathData.js';
2
2
  export { getFilePrefix } from '../utilities/getFilePrefix.js';
3
3
  export { resolveSignedURLKey } from '../utilities/resolveSignedURLKey.js';
4
4
  export { sanitizePrefix } from '../utilities/sanitizePrefix.js';
@@ -1 +1 @@
1
- {"version":3,"file":"utilities.d.ts","sourceRoot":"","sources":["../../src/exports/utilities.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAA;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAA;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAA;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA"}
1
+ {"version":3,"file":"utilities.d.ts","sourceRoot":"","sources":["../../src/exports/utilities.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,0BAA0B,EAC1B,mCAAmC,GACpC,MAAM,sCAAsC,CAAA;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAA;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAA;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA"}
@@ -1,4 +1,4 @@
1
- export { getFileKey } from '../utilities/getFileKey.js';
1
+ export { buildStoragePathData, buildUploadStoragePathData, isStoragePathWithinCollectionPrefix } from '../utilities/buildStoragePathData.js';
2
2
  export { getFilePrefix } from '../utilities/getFilePrefix.js';
3
3
  export { resolveSignedURLKey } from '../utilities/resolveSignedURLKey.js';
4
4
  export { sanitizePrefix } from '../utilities/sanitizePrefix.js';
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/exports/utilities.ts"],"sourcesContent":["export { getFileKey } from '../utilities/getFileKey.js'\nexport { getFilePrefix } from '../utilities/getFilePrefix.js'\nexport { resolveSignedURLKey } from '../utilities/resolveSignedURLKey.js'\nexport { sanitizePrefix } from '../utilities/sanitizePrefix.js'\n"],"names":["getFileKey","getFilePrefix","resolveSignedURLKey","sanitizePrefix"],"mappings":"AAAA,SAASA,UAAU,QAAQ,6BAA4B;AACvD,SAASC,aAAa,QAAQ,gCAA+B;AAC7D,SAASC,mBAAmB,QAAQ,sCAAqC;AACzE,SAASC,cAAc,QAAQ,iCAAgC"}
1
+ {"version":3,"sources":["../../src/exports/utilities.ts"],"sourcesContent":["export {\n buildStoragePathData,\n buildUploadStoragePathData,\n isStoragePathWithinCollectionPrefix,\n} from '../utilities/buildStoragePathData.js'\nexport { getFilePrefix } from '../utilities/getFilePrefix.js'\nexport { resolveSignedURLKey } from '../utilities/resolveSignedURLKey.js'\nexport { sanitizePrefix } from '../utilities/sanitizePrefix.js'\n"],"names":["buildStoragePathData","buildUploadStoragePathData","isStoragePathWithinCollectionPrefix","getFilePrefix","resolveSignedURLKey","sanitizePrefix"],"mappings":"AAAA,SACEA,oBAAoB,EACpBC,0BAA0B,EAC1BC,mCAAmC,QAC9B,uCAAsC;AAC7C,SAASC,aAAa,QAAQ,gCAA+B;AAC7D,SAASC,mBAAmB,QAAQ,sCAAqC;AACzE,SAASC,cAAc,QAAQ,iCAAgC"}
@@ -2,10 +2,6 @@ import type { CollectionConfig, Field } from 'payload';
2
2
  import type { GeneratedAdapter, GenerateFileURL } from '../types.js';
3
3
  interface Args {
4
4
  adapter?: GeneratedAdapter;
5
- /**
6
- * When true, always insert the prefix field regardless of whether a prefix is configured.
7
- */
8
- alwaysInsertFields?: boolean;
9
5
  collection: CollectionConfig;
10
6
  disablePayloadAccessControl?: true;
11
7
  generateFileURL?: GenerateFileURL;
@@ -16,6 +12,6 @@ interface Args {
16
12
  */
17
13
  useCompositePrefixes?: boolean;
18
14
  }
19
- export declare const getFields: ({ adapter, alwaysInsertFields, collection, disablePayloadAccessControl, generateFileURL, prefix, useCompositePrefixes, }: Args) => Field[];
15
+ export declare const getFields: ({ adapter, collection, disablePayloadAccessControl, generateFileURL, prefix, useCompositePrefixes, }: Args) => Field[];
20
16
  export {};
21
17
  //# sourceMappingURL=getFields.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"getFields.d.ts","sourceRoot":"","sources":["../../src/fields/getFields.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAyB,MAAM,SAAS,CAAA;AAI7E,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAKpE,UAAU,IAAI;IACZ,OAAO,CAAC,EAAE,gBAAgB,CAAA;IAC1B;;OAEG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,UAAU,EAAE,gBAAgB,CAAA;IAC5B,2BAA2B,CAAC,EAAE,IAAI,CAAA;IAClC,eAAe,CAAC,EAAE,eAAe,CAAA;IACjC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,eAAO,MAAM,SAAS,6HAQnB,IAAI,KAAG,KAAK,EA+Kd,CAAA"}
1
+ {"version":3,"file":"getFields.d.ts","sourceRoot":"","sources":["../../src/fields/getFields.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAgD,MAAM,SAAS,CAAA;AAIpG,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAMpE,UAAU,IAAI;IACZ,OAAO,CAAC,EAAE,gBAAgB,CAAA;IAC1B,UAAU,EAAE,gBAAgB,CAAA;IAC5B,2BAA2B,CAAC,EAAE,IAAI,CAAA;IAClC,eAAe,CAAC,EAAE,eAAe,CAAA;IACjC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,eAAO,MAAM,SAAS,yGAOnB,IAAI,KAAG,KAAK,EAkNd,CAAA"}
@@ -1,7 +1,8 @@
1
1
  import path from 'path';
2
2
  import { getAfterReadHook } from '../hooks/afterRead.js';
3
3
  import { getBeforeChangeHook } from '../hooks/beforeChange.js';
4
- export const getFields = ({ adapter, alwaysInsertFields, collection, disablePayloadAccessControl, generateFileURL, prefix, useCompositePrefixes = false })=>{
4
+ import { getNormalizeUploadPrefixFieldHook } from '../hooks/normalizeUploadPrefix.js';
5
+ export const getFields = ({ adapter, collection, disablePayloadAccessControl, generateFileURL, prefix, useCompositePrefixes = false })=>{
5
6
  const baseURLField = {
6
7
  name: 'url',
7
8
  type: 'text',
@@ -15,10 +16,22 @@ export const getFields = ({ adapter, alwaysInsertFields, collection, disablePayl
15
16
  name: 'prefix',
16
17
  type: 'text',
17
18
  admin: {
19
+ disabled: {
20
+ bulkEdit: true,
21
+ column: true,
22
+ filter: true,
23
+ groupBy: true
24
+ },
18
25
  hidden: true,
19
26
  readOnly: true
20
27
  }
21
28
  };
29
+ // Server-owned key segment; hidden from the API and admin, read internally via showHiddenFields.
30
+ const baseObjectKeyField = {
31
+ name: '_objectKey',
32
+ type: 'text',
33
+ hidden: true
34
+ };
22
35
  const fields = [
23
36
  ...collection.fields,
24
37
  ...adapter?.fields || []
@@ -67,10 +80,13 @@ export const getFields = ({ adapter, alwaysInsertFields, collection, disablePayl
67
80
  ...existingURLField || {}
68
81
  });
69
82
  }
70
- if (typeof collection.upload === 'object' && collection.upload.imageSizes) {
83
+ // Storage adapters add these fields during their `init`, after transformers (e.g. Sharp) have
84
+ // written each collection's image sizes onto its upload config.
85
+ const variants = typeof collection.upload === 'object' ? collection.upload.variants : undefined;
86
+ if (variants) {
71
87
  let existingSizesFieldIndex = -1;
72
88
  const existingSizesField = fields.find((existingField, i)=>{
73
- if ('name' in existingField && existingField.name === 'sizes') {
89
+ if ('name' in existingField && existingField.name === 'variants') {
74
90
  existingSizesFieldIndex = i;
75
91
  return true;
76
92
  }
@@ -81,12 +97,12 @@ export const getFields = ({ adapter, alwaysInsertFields, collection, disablePayl
81
97
  }
82
98
  const sizesField = {
83
99
  ...existingSizesField || {},
84
- name: 'sizes',
100
+ name: 'variants',
85
101
  type: 'group',
86
102
  admin: {
87
103
  hidden: true
88
104
  },
89
- fields: collection.upload.imageSizes.map((size)=>{
105
+ fields: variants.map((size)=>{
90
106
  const existingSizeField = existingSizesField?.fields.find((existingField)=>'name' in existingField && existingField.name === size.name);
91
107
  const existingSizeURLField = existingSizeField?.fields.find((existingField)=>'name' in existingField && existingField.name === 'url');
92
108
  // Only add afterRead hook if adapter is provided
@@ -132,25 +148,41 @@ export const getFields = ({ adapter, alwaysInsertFields, collection, disablePayl
132
148
  };
133
149
  fields.push(sizesField);
134
150
  }
135
- // If prefix is enabled or alwaysInsertFields is true, save it to db
136
- if (typeof prefix !== 'undefined' || alwaysInsertFields) {
137
- let existingPrefixFieldIndex = -1;
138
- const existingPrefixField = fields.find((existingField, i)=>{
139
- if ('name' in existingField && existingField.name === 'prefix') {
140
- existingPrefixFieldIndex = i;
141
- return true;
142
- }
143
- return false;
144
- });
145
- if (existingPrefixFieldIndex > -1) {
146
- fields.splice(existingPrefixFieldIndex, 1);
151
+ // Always insert the prefix field so the schema stays consistent regardless of
152
+ // whether the plugin is enabled or a prefix is configured.
153
+ let existingPrefixFieldIndex = -1;
154
+ const existingPrefixField = fields.find((existingField, i)=>{
155
+ if ('name' in existingField && existingField.name === 'prefix') {
156
+ existingPrefixFieldIndex = i;
157
+ return true;
147
158
  }
148
- fields.push({
149
- ...basePrefixField,
150
- ...existingPrefixField || {},
151
- defaultValue: existingPrefixField?.defaultValue ?? (useCompositePrefixes ? '' : prefix ? path.posix.join(prefix) : '')
152
- });
159
+ return false;
160
+ });
161
+ if (existingPrefixFieldIndex > -1) {
162
+ fields.splice(existingPrefixFieldIndex, 1);
153
163
  }
164
+ fields.push({
165
+ ...basePrefixField,
166
+ ...existingPrefixField || {},
167
+ defaultValue: existingPrefixField?.defaultValue ?? (useCompositePrefixes ? '' : prefix ? path.posix.join(prefix) : ''),
168
+ hooks: {
169
+ ...existingPrefixField?.hooks,
170
+ beforeChange: [
171
+ ...existingPrefixField?.hooks?.beforeChange || [],
172
+ getNormalizeUploadPrefixFieldHook({
173
+ collectionPrefix: prefix,
174
+ useCompositePrefixes
175
+ })
176
+ ]
177
+ }
178
+ });
179
+ const existingObjectKeyFieldIndex = fields.findIndex((existingField)=>'name' in existingField && existingField.name === '_objectKey');
180
+ if (existingObjectKeyFieldIndex > -1) {
181
+ fields.splice(existingObjectKeyFieldIndex, 1);
182
+ }
183
+ fields.push({
184
+ ...baseObjectKeyField
185
+ });
154
186
  return fields;
155
187
  };
156
188
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/fields/getFields.ts"],"sourcesContent":["import type { CollectionConfig, Field, GroupField, TextField } from 'payload'\n\nimport path from 'path'\n\nimport type { GeneratedAdapter, GenerateFileURL } from '../types.js'\n\nimport { getAfterReadHook } from '../hooks/afterRead.js'\nimport { getBeforeChangeHook } from '../hooks/beforeChange.js'\n\ninterface Args {\n adapter?: GeneratedAdapter\n /**\n * When true, always insert the prefix field regardless of whether a prefix is configured.\n */\n alwaysInsertFields?: boolean\n collection: CollectionConfig\n disablePayloadAccessControl?: true\n generateFileURL?: GenerateFileURL\n prefix?: string\n /**\n * When true, do not default the `prefix` field to the collection prefix; the\n * document field holds only the document-level segment.\n */\n useCompositePrefixes?: boolean\n}\n\nexport const getFields = ({\n adapter,\n alwaysInsertFields,\n collection,\n disablePayloadAccessControl,\n generateFileURL,\n prefix,\n useCompositePrefixes = false,\n}: Args): Field[] => {\n const baseURLField: TextField = {\n name: 'url',\n type: 'text',\n admin: {\n hidden: true,\n readOnly: true,\n },\n label: 'URL',\n }\n\n const basePrefixField: TextField = {\n name: 'prefix',\n type: 'text',\n admin: {\n hidden: true,\n readOnly: true,\n },\n }\n\n const fields = [...collection.fields, ...(adapter?.fields || [])]\n\n // Inject a hook into all URL fields to generate URLs\n\n let existingURLFieldIndex = -1\n\n const existingURLField = fields.find((existingField, i) => {\n if ('name' in existingField && existingField.name === 'url') {\n existingURLFieldIndex = i\n return true\n }\n return false\n }) as TextField\n\n if (existingURLFieldIndex > -1) {\n fields.splice(existingURLFieldIndex, 1)\n }\n\n // Only add afterRead hook if adapter is provided\n if (adapter) {\n fields.push({\n ...baseURLField,\n ...(existingURLField || {}),\n hooks: {\n afterRead: [\n getAfterReadHook({ adapter, collection, disablePayloadAccessControl, generateFileURL }),\n ...(existingURLField?.hooks?.afterRead || []),\n ],\n beforeChange: [\n getBeforeChangeHook({\n adapter,\n collection,\n disablePayloadAccessControl,\n generateFileURL,\n }),\n ...(existingURLField?.hooks?.beforeChange || []),\n ],\n },\n } as TextField)\n } else {\n fields.push({\n ...baseURLField,\n ...(existingURLField || {}),\n } as TextField)\n }\n\n if (typeof collection.upload === 'object' && collection.upload.imageSizes) {\n let existingSizesFieldIndex = -1\n\n const existingSizesField = fields.find((existingField, i) => {\n if ('name' in existingField && existingField.name === 'sizes') {\n existingSizesFieldIndex = i\n return true\n }\n\n return false\n }) as GroupField\n\n if (existingSizesFieldIndex > -1) {\n fields.splice(existingSizesFieldIndex, 1)\n }\n\n const sizesField: Field = {\n ...(existingSizesField || {}),\n name: 'sizes',\n type: 'group',\n admin: {\n hidden: true,\n },\n fields: collection.upload.imageSizes.map((size) => {\n const existingSizeField = existingSizesField?.fields.find(\n (existingField) => 'name' in existingField && existingField.name === size.name,\n ) as GroupField\n\n const existingSizeURLField = existingSizeField?.fields.find(\n (existingField) => 'name' in existingField && existingField.name === 'url',\n ) as TextField\n\n // Only add afterRead hook if adapter is provided\n const sizeURLField: TextField = adapter\n ? ({\n ...(existingSizeURLField || {}),\n ...baseURLField,\n hooks: {\n afterRead: [\n getAfterReadHook({\n adapter,\n collection,\n disablePayloadAccessControl,\n generateFileURL,\n size,\n }),\n ...((typeof existingSizeURLField === 'object' &&\n 'hooks' in existingSizeURLField &&\n existingSizeURLField?.hooks?.afterRead) ||\n []),\n ],\n beforeChange: [\n getBeforeChangeHook({\n adapter,\n collection,\n disablePayloadAccessControl,\n generateFileURL,\n size,\n }),\n ...((typeof existingSizeURLField === 'object' &&\n 'hooks' in existingSizeURLField &&\n existingSizeURLField?.hooks?.beforeChange) ||\n []),\n ],\n },\n } as TextField)\n : ({\n ...(existingSizeURLField || {}),\n ...baseURLField,\n } as TextField)\n\n return {\n ...existingSizeField,\n name: size.name,\n type: 'group',\n fields: [...(adapter?.fields || []), sizeURLField],\n } as Field\n }),\n }\n\n fields.push(sizesField)\n }\n\n // If prefix is enabled or alwaysInsertFields is true, save it to db\n if (typeof prefix !== 'undefined' || alwaysInsertFields) {\n let existingPrefixFieldIndex = -1\n\n const existingPrefixField = fields.find((existingField, i) => {\n if ('name' in existingField && existingField.name === 'prefix') {\n existingPrefixFieldIndex = i\n return true\n }\n return false\n }) as TextField\n\n if (existingPrefixFieldIndex > -1) {\n fields.splice(existingPrefixFieldIndex, 1)\n }\n\n fields.push({\n ...basePrefixField,\n ...(existingPrefixField || {}),\n defaultValue:\n existingPrefixField?.defaultValue ??\n (useCompositePrefixes ? '' : prefix ? path.posix.join(prefix) : ''),\n } as TextField)\n }\n\n return fields\n}\n"],"names":["path","getAfterReadHook","getBeforeChangeHook","getFields","adapter","alwaysInsertFields","collection","disablePayloadAccessControl","generateFileURL","prefix","useCompositePrefixes","baseURLField","name","type","admin","hidden","readOnly","label","basePrefixField","fields","existingURLFieldIndex","existingURLField","find","existingField","i","splice","push","hooks","afterRead","beforeChange","upload","imageSizes","existingSizesFieldIndex","existingSizesField","sizesField","map","size","existingSizeField","existingSizeURLField","sizeURLField","existingPrefixFieldIndex","existingPrefixField","defaultValue","posix","join"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/fields/getFields.ts"],"sourcesContent":["import type { CollectionConfig, Field, GroupField, SanitizedUploadConfig, TextField } from 'payload'\n\nimport path from 'path'\n\nimport type { GeneratedAdapter, GenerateFileURL } from '../types.js'\n\nimport { getAfterReadHook } from '../hooks/afterRead.js'\nimport { getBeforeChangeHook } from '../hooks/beforeChange.js'\nimport { getNormalizeUploadPrefixFieldHook } from '../hooks/normalizeUploadPrefix.js'\n\ninterface Args {\n adapter?: GeneratedAdapter\n collection: CollectionConfig\n disablePayloadAccessControl?: true\n generateFileURL?: GenerateFileURL\n prefix?: string\n /**\n * When true, do not default the `prefix` field to the collection prefix; the\n * document field holds only the document-level segment.\n */\n useCompositePrefixes?: boolean\n}\n\nexport const getFields = ({\n adapter,\n collection,\n disablePayloadAccessControl,\n generateFileURL,\n prefix,\n useCompositePrefixes = false,\n}: Args): Field[] => {\n const baseURLField: TextField = {\n name: 'url',\n type: 'text',\n admin: {\n hidden: true,\n readOnly: true,\n },\n label: 'URL',\n }\n\n const basePrefixField: TextField = {\n name: 'prefix',\n type: 'text',\n admin: {\n disabled: {\n bulkEdit: true,\n column: true,\n filter: true,\n groupBy: true,\n },\n hidden: true,\n readOnly: true,\n },\n }\n\n // Server-owned key segment; hidden from the API and admin, read internally via showHiddenFields.\n const baseObjectKeyField: TextField = {\n name: '_objectKey',\n type: 'text',\n hidden: true,\n }\n\n const fields = [...collection.fields, ...(adapter?.fields || [])]\n\n // Inject a hook into all URL fields to generate URLs\n\n let existingURLFieldIndex = -1\n\n const existingURLField = fields.find((existingField, i) => {\n if ('name' in existingField && existingField.name === 'url') {\n existingURLFieldIndex = i\n return true\n }\n return false\n }) as TextField\n\n if (existingURLFieldIndex > -1) {\n fields.splice(existingURLFieldIndex, 1)\n }\n\n // Only add afterRead hook if adapter is provided\n if (adapter) {\n fields.push({\n ...baseURLField,\n ...(existingURLField || {}),\n hooks: {\n afterRead: [\n getAfterReadHook({ adapter, collection, disablePayloadAccessControl, generateFileURL }),\n ...(existingURLField?.hooks?.afterRead || []),\n ],\n beforeChange: [\n getBeforeChangeHook({\n adapter,\n collection,\n disablePayloadAccessControl,\n generateFileURL,\n }),\n ...(existingURLField?.hooks?.beforeChange || []),\n ],\n },\n } as TextField)\n } else {\n fields.push({\n ...baseURLField,\n ...(existingURLField || {}),\n } as TextField)\n }\n\n // Storage adapters add these fields during their `init`, after transformers (e.g. Sharp) have\n // written each collection's image sizes onto its upload config.\n const variants =\n typeof collection.upload === 'object'\n ? (collection.upload as SanitizedUploadConfig).variants\n : undefined\n\n if (variants) {\n let existingSizesFieldIndex = -1\n\n const existingSizesField = fields.find((existingField, i) => {\n if ('name' in existingField && existingField.name === 'variants') {\n existingSizesFieldIndex = i\n return true\n }\n\n return false\n }) as GroupField\n\n if (existingSizesFieldIndex > -1) {\n fields.splice(existingSizesFieldIndex, 1)\n }\n\n const sizesField: Field = {\n ...(existingSizesField || {}),\n name: 'variants',\n type: 'group',\n admin: {\n hidden: true,\n },\n fields: variants.map((size) => {\n const existingSizeField = existingSizesField?.fields.find(\n (existingField) => 'name' in existingField && existingField.name === size.name,\n ) as GroupField\n\n const existingSizeURLField = existingSizeField?.fields.find(\n (existingField) => 'name' in existingField && existingField.name === 'url',\n ) as TextField\n\n // Only add afterRead hook if adapter is provided\n const sizeURLField: TextField = adapter\n ? ({\n ...(existingSizeURLField || {}),\n ...baseURLField,\n hooks: {\n afterRead: [\n getAfterReadHook({\n adapter,\n collection,\n disablePayloadAccessControl,\n generateFileURL,\n size,\n }),\n ...((typeof existingSizeURLField === 'object' &&\n 'hooks' in existingSizeURLField &&\n existingSizeURLField?.hooks?.afterRead) ||\n []),\n ],\n beforeChange: [\n getBeforeChangeHook({\n adapter,\n collection,\n disablePayloadAccessControl,\n generateFileURL,\n size,\n }),\n ...((typeof existingSizeURLField === 'object' &&\n 'hooks' in existingSizeURLField &&\n existingSizeURLField?.hooks?.beforeChange) ||\n []),\n ],\n },\n } as TextField)\n : ({\n ...(existingSizeURLField || {}),\n ...baseURLField,\n } as TextField)\n\n return {\n ...existingSizeField,\n name: size.name,\n type: 'group',\n fields: [...(adapter?.fields || []), sizeURLField],\n } as Field\n }),\n }\n\n fields.push(sizesField)\n }\n\n // Always insert the prefix field so the schema stays consistent regardless of\n // whether the plugin is enabled or a prefix is configured.\n let existingPrefixFieldIndex = -1\n\n const existingPrefixField = fields.find((existingField, i) => {\n if ('name' in existingField && existingField.name === 'prefix') {\n existingPrefixFieldIndex = i\n return true\n }\n return false\n }) as TextField\n\n if (existingPrefixFieldIndex > -1) {\n fields.splice(existingPrefixFieldIndex, 1)\n }\n\n fields.push({\n ...basePrefixField,\n ...(existingPrefixField || {}),\n defaultValue:\n existingPrefixField?.defaultValue ??\n (useCompositePrefixes ? '' : prefix ? path.posix.join(prefix) : ''),\n hooks: {\n ...existingPrefixField?.hooks,\n beforeChange: [\n ...(existingPrefixField?.hooks?.beforeChange || []),\n getNormalizeUploadPrefixFieldHook({ collectionPrefix: prefix, useCompositePrefixes }),\n ],\n },\n } as TextField)\n\n const existingObjectKeyFieldIndex = fields.findIndex(\n (existingField) => 'name' in existingField && existingField.name === '_objectKey',\n )\n if (existingObjectKeyFieldIndex > -1) {\n fields.splice(existingObjectKeyFieldIndex, 1)\n }\n\n fields.push({ ...baseObjectKeyField } as TextField)\n\n return fields\n}\n"],"names":["path","getAfterReadHook","getBeforeChangeHook","getNormalizeUploadPrefixFieldHook","getFields","adapter","collection","disablePayloadAccessControl","generateFileURL","prefix","useCompositePrefixes","baseURLField","name","type","admin","hidden","readOnly","label","basePrefixField","disabled","bulkEdit","column","filter","groupBy","baseObjectKeyField","fields","existingURLFieldIndex","existingURLField","find","existingField","i","splice","push","hooks","afterRead","beforeChange","variants","upload","undefined","existingSizesFieldIndex","existingSizesField","sizesField","map","size","existingSizeField","existingSizeURLField","sizeURLField","existingPrefixFieldIndex","existingPrefixField","defaultValue","posix","join","collectionPrefix","existingObjectKeyFieldIndex","findIndex"],"mappings":"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"}
@@ -3,7 +3,9 @@ import type { GeneratedAdapter } from '../types.js';
3
3
  interface Args {
4
4
  adapter: GeneratedAdapter;
5
5
  collection: CollectionConfig;
6
+ collectionPrefix?: string;
7
+ useCompositePrefixes?: boolean;
6
8
  }
7
- export declare const getAfterChangeHook: ({ adapter, collection }: Args) => CollectionAfterChangeHook<FileData & TypeWithID>;
9
+ export declare const getAfterChangeHook: ({ adapter, collection, collectionPrefix, useCompositePrefixes, }: Args) => CollectionAfterChangeHook<FileData & TypeWithID>;
8
10
  export {};
9
11
  //# sourceMappingURL=afterChange.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"afterChange.d.ts","sourceRoot":"","sources":["../../src/hooks/afterChange.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,gBAAgB,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEhG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAInD,UAAU,IAAI;IACZ,OAAO,EAAE,gBAAgB,CAAA;IACzB,UAAU,EAAE,gBAAgB,CAAA;CAC7B;AAED,eAAO,MAAM,kBAAkB,4BACH,IAAI,KAAG,yBAAyB,CAAC,QAAQ,GAAG,UAAU,CAyH/E,CAAA"}
1
+ {"version":3,"file":"afterChange.d.ts","sourceRoot":"","sources":["../../src/hooks/afterChange.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,gBAAgB,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAIhG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AASnD,UAAU,IAAI;IACZ,OAAO,EAAE,gBAAgB,CAAA;IACzB,UAAU,EAAE,gBAAgB,CAAA;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAQD,eAAO,MAAM,kBAAkB,qEAM1B,IAAI,KAAG,yBAAyB,CAAC,QAAQ,GAAG,UAAU,CA8KxD,CAAA"}
@@ -1,27 +1,54 @@
1
+ import { deepMergeWithSourceArrays } from 'payload';
2
+ import { buildPrefixWithObjectKey } from '../utilities/buildPrefixWithObjectKey.js';
3
+ import { buildStoragePathData, buildUploadStoragePathData } from '../utilities/buildStoragePathData.js';
1
4
  import { getIncomingFiles } from '../utilities/getIncomingFiles.js';
2
- export const getAfterChangeHook = ({ adapter, collection })=>async ({ doc, operation, previousDoc, req })=>{
5
+ // The object's folder: semantic prefix + `_objectKey` segment.
6
+ const getObjectFolder = (data)=>{
7
+ const record = data ?? {};
8
+ return buildPrefixWithObjectKey({
9
+ objectKey: record._objectKey,
10
+ prefix: record.prefix
11
+ });
12
+ };
13
+ export const getAfterChangeHook = ({ adapter, collection, collectionPrefix, useCompositePrefixes })=>async ({ data, doc, operation, previousDoc, req, select })=>{
3
14
  // Skip if this is an internal update to prevent infinite loop
4
15
  if (req.context?.skipCloudStorage) {
5
16
  return doc;
6
17
  }
7
- const isDraftSave = doc._status === 'draft';
18
+ // Restore upload metadata removed by select, including partially selected image sizes.
19
+ const uploadData = select ? deepMergeWithSourceArrays(data, doc) : doc;
20
+ const isDraftSave = uploadData._status === 'draft';
8
21
  const isDraftOverPublished = isDraftSave && previousDoc?._status === 'published';
9
22
  try {
10
23
  const files = getIncomingFiles({
11
- data: doc,
24
+ data: uploadData,
12
25
  req
13
26
  });
14
27
  if (files.length > 0) {
28
+ // Fold `_objectKey` so generated sizes land in the same folder as the original.
29
+ const dataForUpload = {
30
+ ...uploadData,
31
+ prefix: getObjectFolder(uploadData)
32
+ };
15
33
  const uploadResults = await Promise.all(files.filter((file)=>!file.uploadReference).map((file)=>adapter.handleUpload({
16
34
  collection,
17
- data: doc,
35
+ data: dataForUpload,
18
36
  file,
19
- req
37
+ req,
38
+ storageFilePath: buildUploadStoragePathData({
39
+ collectionPrefix,
40
+ docPrefix: dataForUpload.prefix,
41
+ filename: file.filename,
42
+ useCompositePrefixes
43
+ }).storageFilePath
20
44
  })));
21
45
  const uploadMetadata = uploadResults.filter((result)=>result != null && typeof result === 'object').reduce((acc, metadata)=>({
22
46
  ...acc,
23
47
  ...metadata
24
48
  }), {});
49
+ // Adapters may echo `data` back as metadata; keep the document's own `prefix`/`_objectKey`.
50
+ delete uploadMetadata.prefix;
51
+ delete uploadMetadata._objectKey;
25
52
  let docWithMetadata = doc;
26
53
  if (Object.keys(uploadMetadata).length > 0) {
27
54
  if (!req.context) {
@@ -32,21 +59,27 @@ export const getAfterChangeHook = ({ adapter, collection })=>async ({ doc, opera
32
59
  req.file = undefined;
33
60
  req.payloadUploadSizes = undefined;
34
61
  try {
35
- await req.payload.update({
62
+ const updatedDoc = await req.payload.update({
36
63
  id: doc.id,
37
64
  collection: collection.slug,
38
65
  data: uploadMetadata,
39
66
  depth: 0,
40
67
  draft: isDraftSave,
41
- req
68
+ overrideAccess: true,
69
+ req,
70
+ select
42
71
  });
72
+ // Persist all adapter metadata, but do not add unselected fields to the response.
73
+ docWithMetadata = select ? {
74
+ ...doc,
75
+ ...updatedDoc
76
+ } : {
77
+ ...doc,
78
+ ...uploadMetadata
79
+ };
43
80
  } finally{
44
81
  delete req.context.skipCloudStorage;
45
82
  }
46
- docWithMetadata = {
47
- ...doc,
48
- ...uploadMetadata
49
- };
50
83
  }
51
84
  // Delete previous files only after the new upload and metadata
52
85
  // persistence have succeeded. Deleting earlier would orphan the
@@ -57,30 +90,53 @@ export const getAfterChangeHook = ({ adapter, collection })=>async ({ doc, opera
57
90
  if (typeof previousDoc?.filename === 'string') {
58
91
  filesToDelete.push(previousDoc.filename);
59
92
  }
60
- if (typeof previousDoc.sizes === 'object') {
61
- filesToDelete = filesToDelete.concat(Object.values(previousDoc?.sizes || []).map((resizedFileData)=>resizedFileData?.filename));
93
+ if (typeof previousDoc.variants === 'object') {
94
+ filesToDelete = filesToDelete.concat(Object.values(previousDoc?.variants || []).map((resizedFileData)=>resizedFileData?.filename));
62
95
  }
63
- // Collect new filenames (main + sizes) so we don't delete a
64
- // file that the new upload reused (e.g. same filename on reupload
65
- // where Payload overwrites in place).
96
+ // Compare full locations: a replacement can reuse a filename while moving
97
+ // a legacy object beneath the collection prefix.
98
+ const newFileData = {
99
+ ...uploadData,
100
+ ...uploadMetadata
101
+ };
66
102
  const newFilenames = new Set();
67
- if (typeof docWithMetadata.filename === 'string') {
68
- newFilenames.add(docWithMetadata.filename);
103
+ if (typeof newFileData.filename === 'string') {
104
+ newFilenames.add(newFileData.filename);
69
105
  }
70
- if (typeof docWithMetadata.sizes === 'object') {
71
- for (const size of Object.values(docWithMetadata.sizes || {})){
106
+ if (typeof newFileData.variants === 'object') {
107
+ for (const size of Object.values(newFileData.variants || {})){
72
108
  if (size?.filename && typeof size.filename === 'string') {
73
109
  newFilenames.add(size.filename);
74
110
  }
75
111
  }
76
112
  }
113
+ const resolveKey = ({ data, filename })=>buildStoragePathData({
114
+ collectionPrefix,
115
+ docPrefix: getObjectFolder(data),
116
+ filename,
117
+ useCompositePrefixes
118
+ }).storageFilePath;
119
+ const newKeys = new Set([
120
+ ...newFilenames
121
+ ].map((filename)=>resolveKey({
122
+ data: newFileData,
123
+ filename
124
+ })));
77
125
  const deletionPromises = filesToDelete.map(async (filename)=>{
78
- if (filename && !newFilenames.has(filename)) {
126
+ if (!filename) {
127
+ return;
128
+ }
129
+ const storageFilePath = resolveKey({
130
+ data: previousDoc,
131
+ filename
132
+ });
133
+ if (!newKeys.has(storageFilePath)) {
79
134
  await adapter.handleDelete({
80
135
  collection,
81
136
  doc: previousDoc,
82
137
  filename,
83
- req
138
+ req,
139
+ storageFilePath
84
140
  });
85
141
  }
86
142
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/hooks/afterChange.ts"],"sourcesContent":["import type { CollectionAfterChangeHook, CollectionConfig, FileData, TypeWithID } from 'payload'\n\nimport type { GeneratedAdapter } from '../types.js'\n\nimport { getIncomingFiles } from '../utilities/getIncomingFiles.js'\n\ninterface Args {\n adapter: GeneratedAdapter\n collection: CollectionConfig\n}\n\nexport const getAfterChangeHook =\n ({ adapter, collection }: Args): CollectionAfterChangeHook<FileData & TypeWithID> =>\n async ({ doc, operation, previousDoc, req }) => {\n // Skip if this is an internal update to prevent infinite loop\n if (req.context?.skipCloudStorage) {\n return doc\n }\n\n const isDraftSave = (doc as { _status?: string })._status === 'draft'\n const isDraftOverPublished =\n isDraftSave && (previousDoc as { _status?: string } | undefined)?._status === 'published'\n\n try {\n const files = getIncomingFiles({ data: doc, req })\n\n if (files.length > 0) {\n const uploadResults = await Promise.all(\n files\n .filter((file) => !file.uploadReference)\n .map((file) =>\n adapter.handleUpload({\n collection,\n data: doc,\n file,\n req,\n }),\n ),\n )\n\n const uploadMetadata = uploadResults\n .filter(\n (result): result is Partial<FileData & TypeWithID> =>\n result != null && typeof result === 'object',\n )\n .reduce(\n (acc, metadata) => ({ ...acc, ...metadata }),\n {} as Partial<FileData & TypeWithID>,\n )\n\n let docWithMetadata = doc\n\n if (Object.keys(uploadMetadata).length > 0) {\n if (!req.context) {\n req.context = {}\n }\n req.context.skipCloudStorage = true\n\n // Clear to prevent re-processing\n req.file = undefined\n req.payloadUploadSizes = undefined\n\n try {\n await req.payload.update({\n id: doc.id,\n collection: collection.slug,\n data: uploadMetadata,\n depth: 0,\n draft: isDraftSave,\n req,\n })\n } finally {\n delete req.context.skipCloudStorage\n }\n\n docWithMetadata = { ...doc, ...uploadMetadata }\n }\n\n // Delete previous files only after the new upload and metadata\n // persistence have succeeded. Deleting earlier would orphan the\n // record if a later step throws (e.g. a user-defined afterChange\n // hook on the same collection).\n if (previousDoc && operation === 'update' && !isDraftOverPublished) {\n let filesToDelete: string[] = []\n\n if (typeof previousDoc?.filename === 'string') {\n filesToDelete.push(previousDoc.filename)\n }\n\n if (typeof previousDoc.sizes === 'object') {\n filesToDelete = filesToDelete.concat(\n Object.values(previousDoc?.sizes || []).map(\n (resizedFileData) => resizedFileData?.filename as string,\n ),\n )\n }\n\n // Collect new filenames (main + sizes) so we don't delete a\n // file that the new upload reused (e.g. same filename on reupload\n // where Payload overwrites in place).\n const newFilenames = new Set<string>()\n if (typeof docWithMetadata.filename === 'string') {\n newFilenames.add(docWithMetadata.filename)\n }\n if (typeof docWithMetadata.sizes === 'object') {\n for (const size of Object.values(docWithMetadata.sizes || {})) {\n if (size?.filename && typeof size.filename === 'string') {\n newFilenames.add(size.filename)\n }\n }\n }\n\n const deletionPromises = filesToDelete.map(async (filename) => {\n if (filename && !newFilenames.has(filename)) {\n await adapter.handleDelete({ collection, doc: previousDoc, filename, req })\n }\n })\n\n await Promise.all(deletionPromises)\n }\n\n if (docWithMetadata !== doc) {\n return docWithMetadata\n }\n }\n } catch (err: unknown) {\n req.payload.logger.error(\n `There was an error while uploading files corresponding to the collection ${collection.slug} with filename ${doc.filename}:`,\n )\n req.payload.logger.error({ err })\n throw err\n }\n return doc\n }\n"],"names":["getIncomingFiles","getAfterChangeHook","adapter","collection","doc","operation","previousDoc","req","context","skipCloudStorage","isDraftSave","_status","isDraftOverPublished","files","data","length","uploadResults","Promise","all","filter","file","uploadReference","map","handleUpload","uploadMetadata","result","reduce","acc","metadata","docWithMetadata","Object","keys","undefined","payloadUploadSizes","payload","update","id","slug","depth","draft","filesToDelete","filename","push","sizes","concat","values","resizedFileData","newFilenames","Set","add","size","deletionPromises","has","handleDelete","err","logger","error"],"mappings":"AAIA,SAASA,gBAAgB,QAAQ,mCAAkC;AAOnE,OAAO,MAAMC,qBACX,CAAC,EAAEC,OAAO,EAAEC,UAAU,EAAQ,GAC9B,OAAO,EAAEC,GAAG,EAAEC,SAAS,EAAEC,WAAW,EAAEC,GAAG,EAAE;QACzC,8DAA8D;QAC9D,IAAIA,IAAIC,OAAO,EAAEC,kBAAkB;YACjC,OAAOL;QACT;QAEA,MAAMM,cAAc,AAACN,IAA6BO,OAAO,KAAK;QAC9D,MAAMC,uBACJF,eAAe,AAACJ,aAAkDK,YAAY;QAEhF,IAAI;YACF,MAAME,QAAQb,iBAAiB;gBAAEc,MAAMV;gBAAKG;YAAI;YAEhD,IAAIM,MAAME,MAAM,GAAG,GAAG;gBACpB,MAAMC,gBAAgB,MAAMC,QAAQC,GAAG,CACrCL,MACGM,MAAM,CAAC,CAACC,OAAS,CAACA,KAAKC,eAAe,EACtCC,GAAG,CAAC,CAACF,OACJlB,QAAQqB,YAAY,CAAC;wBACnBpB;wBACAW,MAAMV;wBACNgB;wBACAb;oBACF;gBAIN,MAAMiB,iBAAiBR,cACpBG,MAAM,CACL,CAACM,SACCA,UAAU,QAAQ,OAAOA,WAAW,UAEvCC,MAAM,CACL,CAACC,KAAKC,WAAc,CAAA;wBAAE,GAAGD,GAAG;wBAAE,GAAGC,QAAQ;oBAAC,CAAA,GAC1C,CAAC;gBAGL,IAAIC,kBAAkBzB;gBAEtB,IAAI0B,OAAOC,IAAI,CAACP,gBAAgBT,MAAM,GAAG,GAAG;oBAC1C,IAAI,CAACR,IAAIC,OAAO,EAAE;wBAChBD,IAAIC,OAAO,GAAG,CAAC;oBACjB;oBACAD,IAAIC,OAAO,CAACC,gBAAgB,GAAG;oBAE/B,iCAAiC;oBACjCF,IAAIa,IAAI,GAAGY;oBACXzB,IAAI0B,kBAAkB,GAAGD;oBAEzB,IAAI;wBACF,MAAMzB,IAAI2B,OAAO,CAACC,MAAM,CAAC;4BACvBC,IAAIhC,IAAIgC,EAAE;4BACVjC,YAAYA,WAAWkC,IAAI;4BAC3BvB,MAAMU;4BACNc,OAAO;4BACPC,OAAO7B;4BACPH;wBACF;oBACF,SAAU;wBACR,OAAOA,IAAIC,OAAO,CAACC,gBAAgB;oBACrC;oBAEAoB,kBAAkB;wBAAE,GAAGzB,GAAG;wBAAE,GAAGoB,cAAc;oBAAC;gBAChD;gBAEA,+DAA+D;gBAC/D,gEAAgE;gBAChE,iEAAiE;gBACjE,gCAAgC;gBAChC,IAAIlB,eAAeD,cAAc,YAAY,CAACO,sBAAsB;oBAClE,IAAI4B,gBAA0B,EAAE;oBAEhC,IAAI,OAAOlC,aAAamC,aAAa,UAAU;wBAC7CD,cAAcE,IAAI,CAACpC,YAAYmC,QAAQ;oBACzC;oBAEA,IAAI,OAAOnC,YAAYqC,KAAK,KAAK,UAAU;wBACzCH,gBAAgBA,cAAcI,MAAM,CAClCd,OAAOe,MAAM,CAACvC,aAAaqC,SAAS,EAAE,EAAErB,GAAG,CACzC,CAACwB,kBAAoBA,iBAAiBL;oBAG5C;oBAEA,4DAA4D;oBAC5D,kEAAkE;oBAClE,sCAAsC;oBACtC,MAAMM,eAAe,IAAIC;oBACzB,IAAI,OAAOnB,gBAAgBY,QAAQ,KAAK,UAAU;wBAChDM,aAAaE,GAAG,CAACpB,gBAAgBY,QAAQ;oBAC3C;oBACA,IAAI,OAAOZ,gBAAgBc,KAAK,KAAK,UAAU;wBAC7C,KAAK,MAAMO,QAAQpB,OAAOe,MAAM,CAAChB,gBAAgBc,KAAK,IAAI,CAAC,GAAI;4BAC7D,IAAIO,MAAMT,YAAY,OAAOS,KAAKT,QAAQ,KAAK,UAAU;gCACvDM,aAAaE,GAAG,CAACC,KAAKT,QAAQ;4BAChC;wBACF;oBACF;oBAEA,MAAMU,mBAAmBX,cAAclB,GAAG,CAAC,OAAOmB;wBAChD,IAAIA,YAAY,CAACM,aAAaK,GAAG,CAACX,WAAW;4BAC3C,MAAMvC,QAAQmD,YAAY,CAAC;gCAAElD;gCAAYC,KAAKE;gCAAamC;gCAAUlC;4BAAI;wBAC3E;oBACF;oBAEA,MAAMU,QAAQC,GAAG,CAACiC;gBACpB;gBAEA,IAAItB,oBAAoBzB,KAAK;oBAC3B,OAAOyB;gBACT;YACF;QACF,EAAE,OAAOyB,KAAc;YACrB/C,IAAI2B,OAAO,CAACqB,MAAM,CAACC,KAAK,CACtB,CAAC,yEAAyE,EAAErD,WAAWkC,IAAI,CAAC,eAAe,EAAEjC,IAAIqC,QAAQ,CAAC,CAAC,CAAC;YAE9HlC,IAAI2B,OAAO,CAACqB,MAAM,CAACC,KAAK,CAAC;gBAAEF;YAAI;YAC/B,MAAMA;QACR;QACA,OAAOlD;IACT,EAAC"}
1
+ {"version":3,"sources":["../../src/hooks/afterChange.ts"],"sourcesContent":["import type { CollectionAfterChangeHook, CollectionConfig, FileData, TypeWithID } from 'payload'\n\nimport { deepMergeWithSourceArrays } from 'payload'\n\nimport type { GeneratedAdapter } from '../types.js'\n\nimport { buildPrefixWithObjectKey } from '../utilities/buildPrefixWithObjectKey.js'\nimport {\n buildStoragePathData,\n buildUploadStoragePathData,\n} from '../utilities/buildStoragePathData.js'\nimport { getIncomingFiles } from '../utilities/getIncomingFiles.js'\n\ninterface Args {\n adapter: GeneratedAdapter\n collection: CollectionConfig\n collectionPrefix?: string\n useCompositePrefixes?: boolean\n}\n\n// The object's folder: semantic prefix + `_objectKey` segment.\nconst getObjectFolder = (data: unknown): string => {\n const record = (data ?? {}) as { _objectKey?: string; prefix?: string }\n return buildPrefixWithObjectKey({ objectKey: record._objectKey, prefix: record.prefix })\n}\n\nexport const getAfterChangeHook =\n ({\n adapter,\n collection,\n collectionPrefix,\n useCompositePrefixes,\n }: Args): CollectionAfterChangeHook<FileData & TypeWithID> =>\n async ({ data, doc, operation, previousDoc, req, select }) => {\n // Skip if this is an internal update to prevent infinite loop\n if (req.context?.skipCloudStorage) {\n return doc\n }\n\n // Restore upload metadata removed by select, including partially selected image sizes.\n const uploadData = select ? deepMergeWithSourceArrays<FileData & TypeWithID>(data, doc) : doc\n const isDraftSave = (uploadData as { _status?: string })._status === 'draft'\n const isDraftOverPublished =\n isDraftSave && (previousDoc as { _status?: string } | undefined)?._status === 'published'\n\n try {\n const files = getIncomingFiles({ data: uploadData, req })\n\n if (files.length > 0) {\n // Fold `_objectKey` so generated sizes land in the same folder as the original.\n const dataForUpload = { ...uploadData, prefix: getObjectFolder(uploadData) }\n\n const uploadResults = await Promise.all(\n files\n .filter((file) => !file.uploadReference)\n .map((file) =>\n adapter.handleUpload({\n collection,\n data: dataForUpload,\n file,\n req,\n storageFilePath: buildUploadStoragePathData({\n collectionPrefix,\n docPrefix: dataForUpload.prefix,\n filename: file.filename,\n useCompositePrefixes,\n }).storageFilePath,\n }),\n ),\n )\n\n const uploadMetadata = uploadResults\n .filter(\n (result): result is Partial<FileData & TypeWithID> =>\n result != null && typeof result === 'object',\n )\n .reduce(\n (acc, metadata) => ({ ...acc, ...metadata }),\n {} as Partial<FileData & TypeWithID>,\n )\n\n // Adapters may echo `data` back as metadata; keep the document's own `prefix`/`_objectKey`.\n delete (uploadMetadata as Record<string, unknown>).prefix\n delete (uploadMetadata as Record<string, unknown>)._objectKey\n\n let docWithMetadata = doc\n\n if (Object.keys(uploadMetadata).length > 0) {\n if (!req.context) {\n req.context = {}\n }\n req.context.skipCloudStorage = true\n\n // Clear to prevent re-processing\n req.file = undefined\n req.payloadUploadSizes = undefined\n\n try {\n const updatedDoc = await req.payload.update({\n id: doc.id,\n collection: collection.slug,\n data: uploadMetadata,\n depth: 0,\n draft: isDraftSave,\n overrideAccess: true,\n req,\n select,\n })\n\n // Persist all adapter metadata, but do not add unselected fields to the response.\n docWithMetadata = select ? { ...doc, ...updatedDoc } : { ...doc, ...uploadMetadata }\n } finally {\n delete req.context.skipCloudStorage\n }\n }\n\n // Delete previous files only after the new upload and metadata\n // persistence have succeeded. Deleting earlier would orphan the\n // record if a later step throws (e.g. a user-defined afterChange\n // hook on the same collection).\n if (previousDoc && operation === 'update' && !isDraftOverPublished) {\n let filesToDelete: string[] = []\n\n if (typeof previousDoc?.filename === 'string') {\n filesToDelete.push(previousDoc.filename)\n }\n\n if (typeof previousDoc.variants === 'object') {\n filesToDelete = filesToDelete.concat(\n Object.values(previousDoc?.variants || []).map(\n (resizedFileData) => resizedFileData?.filename as string,\n ),\n )\n }\n\n // Compare full locations: a replacement can reuse a filename while moving\n // a legacy object beneath the collection prefix.\n const newFileData = { ...uploadData, ...uploadMetadata }\n const newFilenames = new Set<string>()\n if (typeof newFileData.filename === 'string') {\n newFilenames.add(newFileData.filename)\n }\n if (typeof newFileData.variants === 'object') {\n for (const size of Object.values(newFileData.variants || {})) {\n if (size?.filename && typeof size.filename === 'string') {\n newFilenames.add(size.filename)\n }\n }\n }\n\n const resolveKey = ({\n data,\n filename,\n }: {\n data: { _objectKey?: string; prefix?: string }\n filename: string\n }) =>\n buildStoragePathData({\n collectionPrefix,\n docPrefix: getObjectFolder(data),\n filename,\n useCompositePrefixes,\n }).storageFilePath\n const newKeys = new Set(\n [...newFilenames].map((filename) =>\n resolveKey({\n data: newFileData as { _objectKey?: string; prefix?: string },\n filename,\n }),\n ),\n )\n\n const deletionPromises = filesToDelete.map(async (filename) => {\n if (!filename) {\n return\n }\n const storageFilePath = resolveKey({\n data: previousDoc as { _objectKey?: string; prefix?: string },\n filename,\n })\n if (!newKeys.has(storageFilePath)) {\n await adapter.handleDelete({\n collection,\n doc: previousDoc,\n filename,\n req,\n storageFilePath,\n })\n }\n })\n\n await Promise.all(deletionPromises)\n }\n\n if (docWithMetadata !== doc) {\n return docWithMetadata\n }\n }\n } catch (err: unknown) {\n req.payload.logger.error(\n `There was an error while uploading files corresponding to the collection ${collection.slug} with filename ${doc.filename}:`,\n )\n req.payload.logger.error({ err })\n throw err\n }\n return doc\n }\n"],"names":["deepMergeWithSourceArrays","buildPrefixWithObjectKey","buildStoragePathData","buildUploadStoragePathData","getIncomingFiles","getObjectFolder","data","record","objectKey","_objectKey","prefix","getAfterChangeHook","adapter","collection","collectionPrefix","useCompositePrefixes","doc","operation","previousDoc","req","select","context","skipCloudStorage","uploadData","isDraftSave","_status","isDraftOverPublished","files","length","dataForUpload","uploadResults","Promise","all","filter","file","uploadReference","map","handleUpload","storageFilePath","docPrefix","filename","uploadMetadata","result","reduce","acc","metadata","docWithMetadata","Object","keys","undefined","payloadUploadSizes","updatedDoc","payload","update","id","slug","depth","draft","overrideAccess","filesToDelete","push","variants","concat","values","resizedFileData","newFileData","newFilenames","Set","add","size","resolveKey","newKeys","deletionPromises","has","handleDelete","err","logger","error"],"mappings":"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"}
@@ -3,7 +3,9 @@ import type { GeneratedAdapter, TypeWithPrefix } from '../types.js';
3
3
  interface Args {
4
4
  adapter: GeneratedAdapter;
5
5
  collection: CollectionConfig;
6
+ collectionPrefix?: string;
7
+ useCompositePrefixes?: boolean;
6
8
  }
7
- export declare const getAfterDeleteHook: ({ adapter, collection, }: Args) => CollectionAfterDeleteHook<FileData & TypeWithID & TypeWithPrefix>;
9
+ export declare const getAfterDeleteHook: ({ adapter, collection, collectionPrefix, useCompositePrefixes, }: Args) => CollectionAfterDeleteHook<FileData & TypeWithID & TypeWithPrefix>;
8
10
  export {};
9
11
  //# sourceMappingURL=afterDelete.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"afterDelete.d.ts","sourceRoot":"","sources":["../../src/hooks/afterDelete.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,gBAAgB,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEhG,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEnE,UAAU,IAAI;IACZ,OAAO,EAAE,gBAAgB,CAAA;IACzB,UAAU,EAAE,gBAAgB,CAAA;CAC7B;AAED,eAAO,MAAM,kBAAkB,6BAG5B,IAAI,KAAG,yBAAyB,CAAC,QAAQ,GAAG,UAAU,GAAG,cAAc,CAyBzE,CAAA"}
1
+ {"version":3,"file":"afterDelete.d.ts","sourceRoot":"","sources":["../../src/hooks/afterDelete.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,gBAAgB,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEhG,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAKnE,UAAU,IAAI;IACZ,OAAO,EAAE,gBAAgB,CAAA;IACzB,UAAU,EAAE,gBAAgB,CAAA;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,eAAO,MAAM,kBAAkB,qEAK5B,IAAI,KAAG,yBAAyB,CAAC,QAAQ,GAAG,UAAU,GAAG,cAAc,CAqCzE,CAAA"}
@@ -1,17 +1,31 @@
1
- export const getAfterDeleteHook = ({ adapter, collection })=>{
1
+ import { buildPrefixWithObjectKey } from '../utilities/buildPrefixWithObjectKey.js';
2
+ import { buildStoragePathData } from '../utilities/buildStoragePathData.js';
3
+ export const getAfterDeleteHook = ({ adapter, collection, collectionPrefix, useCompositePrefixes })=>{
2
4
  return async ({ doc, req })=>{
3
5
  try {
6
+ // Fold `_objectKey` so deletes target the real object folder.
7
+ const docPrefix = buildPrefixWithObjectKey({
8
+ objectKey: doc._objectKey,
9
+ prefix: doc.prefix
10
+ });
4
11
  const filesToDelete = [
5
12
  doc.filename,
6
- ...Object.values(doc?.sizes || []).map((resizedFileData)=>resizedFileData?.filename)
13
+ ...Object.values(doc?.variants || []).map((resizedFileData)=>resizedFileData?.filename)
7
14
  ];
8
15
  const promises = filesToDelete.map(async (filename)=>{
9
16
  if (filename) {
17
+ const { storageFilePath } = buildStoragePathData({
18
+ collectionPrefix,
19
+ docPrefix,
20
+ filename,
21
+ useCompositePrefixes
22
+ });
10
23
  await adapter.handleDelete({
11
24
  collection,
12
25
  doc,
13
26
  filename,
14
- req
27
+ req,
28
+ storageFilePath
15
29
  });
16
30
  }
17
31
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/hooks/afterDelete.ts"],"sourcesContent":["import type { CollectionAfterDeleteHook, CollectionConfig, FileData, TypeWithID } from 'payload'\n\nimport type { GeneratedAdapter, TypeWithPrefix } from '../types.js'\n\ninterface Args {\n adapter: GeneratedAdapter\n collection: CollectionConfig\n}\n\nexport const getAfterDeleteHook = ({\n adapter,\n collection,\n}: Args): CollectionAfterDeleteHook<FileData & TypeWithID & TypeWithPrefix> => {\n return async ({ doc, req }) => {\n try {\n const filesToDelete: string[] = [\n doc.filename,\n ...Object.values(doc?.sizes || []).map(\n (resizedFileData) => resizedFileData?.filename as string,\n ),\n ]\n\n const promises = filesToDelete.map(async (filename) => {\n if (filename) {\n await adapter.handleDelete({ collection, doc, filename, req })\n }\n })\n\n await Promise.all(promises)\n } catch (err: unknown) {\n req.payload.logger.error({\n err,\n msg: `There was an error while deleting files corresponding to the ${collection.labels?.singular} with ID ${doc.id}.`,\n })\n }\n return doc\n }\n}\n"],"names":["getAfterDeleteHook","adapter","collection","doc","req","filesToDelete","filename","Object","values","sizes","map","resizedFileData","promises","handleDelete","Promise","all","err","payload","logger","error","msg","labels","singular","id"],"mappings":"AASA,OAAO,MAAMA,qBAAqB,CAAC,EACjCC,OAAO,EACPC,UAAU,EACL;IACL,OAAO,OAAO,EAAEC,GAAG,EAAEC,GAAG,EAAE;QACxB,IAAI;YACF,MAAMC,gBAA0B;gBAC9BF,IAAIG,QAAQ;mBACTC,OAAOC,MAAM,CAACL,KAAKM,SAAS,EAAE,EAAEC,GAAG,CACpC,CAACC,kBAAoBA,iBAAiBL;aAEzC;YAED,MAAMM,WAAWP,cAAcK,GAAG,CAAC,OAAOJ;gBACxC,IAAIA,UAAU;oBACZ,MAAML,QAAQY,YAAY,CAAC;wBAAEX;wBAAYC;wBAAKG;wBAAUF;oBAAI;gBAC9D;YACF;YAEA,MAAMU,QAAQC,GAAG,CAACH;QACpB,EAAE,OAAOI,KAAc;YACrBZ,IAAIa,OAAO,CAACC,MAAM,CAACC,KAAK,CAAC;gBACvBH;gBACAI,KAAK,CAAC,6DAA6D,EAAElB,WAAWmB,MAAM,EAAEC,SAAS,SAAS,EAAEnB,IAAIoB,EAAE,CAAC,CAAC,CAAC;YACvH;QACF;QACA,OAAOpB;IACT;AACF,EAAC"}
1
+ {"version":3,"sources":["../../src/hooks/afterDelete.ts"],"sourcesContent":["import type { CollectionAfterDeleteHook, CollectionConfig, FileData, TypeWithID } from 'payload'\n\nimport type { GeneratedAdapter, TypeWithPrefix } from '../types.js'\n\nimport { buildPrefixWithObjectKey } from '../utilities/buildPrefixWithObjectKey.js'\nimport { buildStoragePathData } from '../utilities/buildStoragePathData.js'\n\ninterface Args {\n adapter: GeneratedAdapter\n collection: CollectionConfig\n collectionPrefix?: string\n useCompositePrefixes?: boolean\n}\n\nexport const getAfterDeleteHook = ({\n adapter,\n collection,\n collectionPrefix,\n useCompositePrefixes,\n}: Args): CollectionAfterDeleteHook<FileData & TypeWithID & TypeWithPrefix> => {\n return async ({ doc, req }) => {\n try {\n // Fold `_objectKey` so deletes target the real object folder.\n const docPrefix = buildPrefixWithObjectKey({\n objectKey: (doc as { _objectKey?: string })._objectKey,\n prefix: doc.prefix,\n })\n\n const filesToDelete: string[] = [\n doc.filename,\n ...Object.values(doc?.variants || []).map(\n (resizedFileData) => resizedFileData?.filename as string,\n ),\n ]\n\n const promises = filesToDelete.map(async (filename) => {\n if (filename) {\n const { storageFilePath } = buildStoragePathData({\n collectionPrefix,\n docPrefix,\n filename,\n useCompositePrefixes,\n })\n await adapter.handleDelete({ collection, doc, filename, req, storageFilePath })\n }\n })\n\n await Promise.all(promises)\n } catch (err: unknown) {\n req.payload.logger.error({\n err,\n msg: `There was an error while deleting files corresponding to the ${collection.labels?.singular} with ID ${doc.id}.`,\n })\n }\n return doc\n }\n}\n"],"names":["buildPrefixWithObjectKey","buildStoragePathData","getAfterDeleteHook","adapter","collection","collectionPrefix","useCompositePrefixes","doc","req","docPrefix","objectKey","_objectKey","prefix","filesToDelete","filename","Object","values","variants","map","resizedFileData","promises","storageFilePath","handleDelete","Promise","all","err","payload","logger","error","msg","labels","singular","id"],"mappings":"AAIA,SAASA,wBAAwB,QAAQ,2CAA0C;AACnF,SAASC,oBAAoB,QAAQ,uCAAsC;AAS3E,OAAO,MAAMC,qBAAqB,CAAC,EACjCC,OAAO,EACPC,UAAU,EACVC,gBAAgB,EAChBC,oBAAoB,EACf;IACL,OAAO,OAAO,EAAEC,GAAG,EAAEC,GAAG,EAAE;QACxB,IAAI;YACF,8DAA8D;YAC9D,MAAMC,YAAYT,yBAAyB;gBACzCU,WAAW,AAACH,IAAgCI,UAAU;gBACtDC,QAAQL,IAAIK,MAAM;YACpB;YAEA,MAAMC,gBAA0B;gBAC9BN,IAAIO,QAAQ;mBACTC,OAAOC,MAAM,CAACT,KAAKU,YAAY,EAAE,EAAEC,GAAG,CACvC,CAACC,kBAAoBA,iBAAiBL;aAEzC;YAED,MAAMM,WAAWP,cAAcK,GAAG,CAAC,OAAOJ;gBACxC,IAAIA,UAAU;oBACZ,MAAM,EAAEO,eAAe,EAAE,GAAGpB,qBAAqB;wBAC/CI;wBACAI;wBACAK;wBACAR;oBACF;oBACA,MAAMH,QAAQmB,YAAY,CAAC;wBAAElB;wBAAYG;wBAAKO;wBAAUN;wBAAKa;oBAAgB;gBAC/E;YACF;YAEA,MAAME,QAAQC,GAAG,CAACJ;QACpB,EAAE,OAAOK,KAAc;YACrBjB,IAAIkB,OAAO,CAACC,MAAM,CAACC,KAAK,CAAC;gBACvBH;gBACAI,KAAK,CAAC,6DAA6D,EAAEzB,WAAW0B,MAAM,EAAEC,SAAS,SAAS,EAAExB,IAAIyB,EAAE,CAAC,CAAC,CAAC;YACvH;QACF;QACA,OAAOzB;IACT;AACF,EAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"afterRead.d.ts","sourceRoot":"","sources":["../../src/hooks/afterRead.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAErE,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAEpE,UAAU,IAAI;IACZ,OAAO,EAAE,gBAAgB,CAAA;IACzB,UAAU,EAAE,gBAAgB,CAAA;IAC5B,2BAA2B,CAAC,EAAE,OAAO,CAAA;IACrC,eAAe,CAAC,EAAE,eAAe,CAAA;IACjC,IAAI,CAAC,EAAE,SAAS,CAAA;CACjB;AAED,eAAO,MAAM,gBAAgB,gFACmD,IAAI,KAAG,SA4BpF,CAAA"}
1
+ {"version":3,"file":"afterRead.d.ts","sourceRoot":"","sources":["../../src/hooks/afterRead.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAErE,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAIpE,UAAU,IAAI;IACZ,OAAO,EAAE,gBAAgB,CAAA;IACzB,UAAU,EAAE,gBAAgB,CAAA;IAC5B,2BAA2B,CAAC,EAAE,OAAO,CAAA;IACrC,eAAe,CAAC,EAAE,eAAe,CAAA;IACjC,IAAI,CAAC,EAAE,SAAS,CAAA;CACjB;AAiBD,eAAO,MAAM,gBAAgB,gFACmD,IAAI,KAAG,SA8BpF,CAAA"}
@@ -1,13 +1,26 @@
1
+ import { sanitizePrefix } from '../utilities/sanitizePrefix.js';
2
+ // The object's folder: semantic prefix + `_objectKey` segment.
3
+ const getObjectFolder = (data)=>{
4
+ const record = data ?? {};
5
+ const safePrefix = sanitizePrefix(typeof record.prefix === 'string' ? record.prefix : '');
6
+ const safeObjectKey = sanitizePrefix(typeof record._objectKey === 'string' ? record._objectKey : '');
7
+ if (safePrefix && safeObjectKey) {
8
+ return `${safePrefix}/${safeObjectKey}`;
9
+ }
10
+ return safePrefix || safeObjectKey;
11
+ };
1
12
  export const getAfterReadHook = ({ adapter, collection, disablePayloadAccessControl, generateFileURL, size })=>async ({ data, value })=>{
2
- const filename = size ? data?.sizes?.[size.name]?.filename : data?.filename;
13
+ const filename = size ? data?.variants?.[size.name]?.filename : data?.filename;
3
14
  const prefix = data?.prefix;
15
+ // Direct-serve URLs encode the full location; the proxy resolves `_objectKey` server-side.
16
+ const objectFolder = getObjectFolder(data);
4
17
  let url = value;
5
18
  if (filename) {
6
19
  if (generateFileURL) {
7
20
  url = await generateFileURL({
8
21
  collection,
9
22
  filename,
10
- prefix,
23
+ prefix: objectFolder,
11
24
  size
12
25
  });
13
26
  } else if (disablePayloadAccessControl && adapter.generateURL) {
@@ -15,7 +28,7 @@ export const getAfterReadHook = ({ adapter, collection, disablePayloadAccessCont
15
28
  collection,
16
29
  data,
17
30
  filename,
18
- prefix
31
+ prefix: objectFolder
19
32
  });
20
33
  } else if (url && prefix) {
21
34
  const separator = url.includes('?') ? '&' : '?';