@payloadcms/plugin-cloud-storage 3.89.0 → 3.90.0

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 (78) 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 +3 -2
  7. package/dist/exports/utilities.d.ts.map +1 -1
  8. package/dist/exports/utilities.js +3 -2
  9. package/dist/exports/utilities.js.map +1 -1
  10. package/dist/fields/getFields.d.ts.map +1 -1
  11. package/dist/fields/getFields.js +28 -1
  12. package/dist/fields/getFields.js.map +1 -1
  13. package/dist/hooks/afterChange.d.ts +3 -1
  14. package/dist/hooks/afterChange.d.ts.map +1 -1
  15. package/dist/hooks/afterChange.js +56 -9
  16. package/dist/hooks/afterChange.js.map +1 -1
  17. package/dist/hooks/afterChange.spec.js +150 -0
  18. package/dist/hooks/afterChange.spec.js.map +1 -0
  19. package/dist/hooks/afterDelete.d.ts +3 -1
  20. package/dist/hooks/afterDelete.d.ts.map +1 -1
  21. package/dist/hooks/afterDelete.js +16 -2
  22. package/dist/hooks/afterDelete.js.map +1 -1
  23. package/dist/hooks/afterRead.d.ts.map +1 -1
  24. package/dist/hooks/afterRead.js +13 -2
  25. package/dist/hooks/afterRead.js.map +1 -1
  26. package/dist/hooks/beforeChange.d.ts.map +1 -1
  27. package/dist/hooks/beforeChange.js +10 -1
  28. package/dist/hooks/beforeChange.js.map +1 -1
  29. package/dist/hooks/normalizeUploadPrefix.d.ts +11 -0
  30. package/dist/hooks/normalizeUploadPrefix.d.ts.map +1 -0
  31. package/dist/hooks/normalizeUploadPrefix.js +42 -0
  32. package/dist/hooks/normalizeUploadPrefix.js.map +1 -0
  33. package/dist/hooks/normalizeUploadPrefix.spec.js +154 -0
  34. package/dist/hooks/normalizeUploadPrefix.spec.js.map +1 -0
  35. package/dist/plugin.d.ts.map +1 -1
  36. package/dist/plugin.js +15 -6
  37. package/dist/plugin.js.map +1 -1
  38. package/dist/plugin.spec.js +166 -0
  39. package/dist/plugin.spec.js.map +1 -0
  40. package/dist/types.d.ts +23 -1
  41. package/dist/types.d.ts.map +1 -1
  42. package/dist/types.js.map +1 -1
  43. package/dist/utilities/assertReceiptMatchesFileKey.d.ts +15 -0
  44. package/dist/utilities/assertReceiptMatchesFileKey.d.ts.map +1 -0
  45. package/dist/utilities/assertReceiptMatchesFileKey.js +31 -0
  46. package/dist/utilities/assertReceiptMatchesFileKey.js.map +1 -0
  47. package/dist/utilities/buildPrefixWithObjectKey.d.ts +8 -0
  48. package/dist/utilities/buildPrefixWithObjectKey.d.ts.map +1 -0
  49. package/dist/utilities/buildPrefixWithObjectKey.js +13 -0
  50. package/dist/utilities/buildPrefixWithObjectKey.js.map +1 -0
  51. package/dist/utilities/buildStoragePathData.d.ts +55 -0
  52. package/dist/utilities/buildStoragePathData.d.ts.map +1 -0
  53. package/dist/utilities/buildStoragePathData.js +69 -0
  54. package/dist/utilities/buildStoragePathData.js.map +1 -0
  55. package/dist/utilities/buildStoragePathData.spec.js +254 -0
  56. package/dist/utilities/buildStoragePathData.spec.js.map +1 -0
  57. package/dist/utilities/getFilePrefix.d.ts +27 -11
  58. package/dist/utilities/getFilePrefix.d.ts.map +1 -1
  59. package/dist/utilities/getFilePrefix.js +75 -31
  60. package/dist/utilities/getFilePrefix.js.map +1 -1
  61. package/dist/utilities/getFilePrefix.spec.js +221 -35
  62. package/dist/utilities/getFilePrefix.spec.js.map +1 -1
  63. package/dist/utilities/resolveSignedURLKey.d.ts +11 -0
  64. package/dist/utilities/resolveSignedURLKey.d.ts.map +1 -1
  65. package/dist/utilities/resolveSignedURLKey.js +43 -8
  66. package/dist/utilities/resolveSignedURLKey.js.map +1 -1
  67. package/dist/utilities/resolveSignedURLKey.spec.js +56 -0
  68. package/dist/utilities/resolveSignedURLKey.spec.js.map +1 -0
  69. package/dist/utilities/sanitizePrefix.d.ts +2 -2
  70. package/dist/utilities/sanitizePrefix.js +4 -4
  71. package/dist/utilities/sanitizePrefix.js.map +1 -1
  72. package/dist/utilities/verifyClientUploadReceiptForFileKey.d.ts +20 -0
  73. package/dist/utilities/verifyClientUploadReceiptForFileKey.d.ts.map +1 -0
  74. package/dist/utilities/verifyClientUploadReceiptForFileKey.js +24 -0
  75. package/dist/utilities/verifyClientUploadReceiptForFileKey.js.map +1 -0
  76. package/dist/utilities/verifyClientUploadReceiptForFileKey.spec.js +108 -0
  77. package/dist/utilities/verifyClientUploadReceiptForFileKey.spec.js.map +1 -0
  78. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"resolveSignedURLKey.d.ts","sourceRoot":"","sources":["../../src/utilities/resolveSignedURLKey.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAM7C,KAAK,IAAI,GAAG;IACV,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,cAAc,EAAE,MAAM,CAAA;IACtB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,GAAG,EAAE,cAAc,CAAA;IACnB,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B,CAAA;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CAAC,EACxC,gBAAqB,EACrB,cAAc,EACd,SAAS,EACT,QAAQ,EACR,GAAG,EACH,oBAA4B,GAC7B,EAAE,IAAI;;;;GAgBN"}
1
+ {"version":3,"file":"resolveSignedURLKey.d.ts","sourceRoot":"","sources":["../../src/utilities/resolveSignedURLKey.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAM7C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAItD,KAAK,IAAI,GAAG;IACV,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,cAAc,EAAE,MAAM,CAAA;IACtB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,GAAG,EAAE,cAAc,CAAA;IACnB,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B,CAAA;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,mBAAmB,CAAC,EACxC,gBAAqB,EACrB,cAAc,EACd,SAAS,EACT,QAAQ,EACR,GAAG,EACH,oBAA4B,GAC7B,EAAE,IAAI;;;;;;;GAiDN"}
@@ -1,29 +1,64 @@
1
- import { getSafeFileName } from 'payload/internal';
2
- import { getFileKey } from './getFileKey.js';
1
+ import { randomUUID } from 'crypto';
2
+ import { createClientUploadReceipt, getSafeFileName } from 'payload/internal';
3
+ import { getSanitizedUploadFilename } from 'payload/shared';
4
+ import { buildUploadPrefix, buildUploadStoragePathData } from './buildStoragePathData.js';
3
5
  /**
4
6
  * Resolves the storage key for a clientUploads signed-URL request, deduping
5
7
  * the filename via {@link getSafeFileName} so a duplicate upload does not
6
8
  * overwrite an existing blob.
7
9
  *
10
+ * A per-upload segment is persisted in `_objectKey` (not `prefix`), so the stored `prefix`
11
+ * and `filename` stay clean.
12
+ *
8
13
  * The resolved `sanitizedFilename` is returned so the browser-side handler
9
14
  * can update the form via `updateFilename`.
15
+ *
16
+ * The semantic prefix is resolved with {@link buildUploadPrefix} and the final key with
17
+ * {@link buildUploadStoragePathData}, so the receipt only ever signs a key beneath the configured
18
+ * collection prefix.
10
19
  */ export async function resolveSignedURLKey({ collectionPrefix = '', collectionSlug, docPrefix, filename, req, useCompositePrefixes = false }) {
20
+ // Sanitize with the same helper generateFileData uses for the DB filename so the storage key
21
+ // and doc.filename stay in sync (#16694).
11
22
  const sanitizedFilename = await getSafeFileName({
12
23
  collectionSlug,
13
- desiredFilename: filename,
14
- prefix: docPrefix,
24
+ desiredFilename: getSanitizedUploadFilename(filename),
15
25
  req
16
26
  });
17
- const { fileKey, sanitizedDocPrefix } = getFileKey({
27
+ const rawBaseDocPrefix = useCompositePrefixes ? docPrefix : docPrefix || collectionPrefix;
28
+ const { sanitizedDocPrefix } = buildUploadPrefix({
18
29
  collectionPrefix,
19
- docPrefix,
30
+ docPrefix: rawBaseDocPrefix,
31
+ useCompositePrefixes
32
+ });
33
+ const _objectKey = randomUUID();
34
+ // Persisted in `_objectKey`, keeping `prefix` semantic.
35
+ const keyedDocPrefix = sanitizedDocPrefix ? `${sanitizedDocPrefix}/${_objectKey}` : _objectKey;
36
+ const { storageFilePath } = buildUploadStoragePathData({
37
+ collectionPrefix,
38
+ docPrefix: keyedDocPrefix,
20
39
  filename: sanitizedFilename,
21
40
  useCompositePrefixes
22
41
  });
42
+ const context = {
43
+ _objectKey,
44
+ prefix: sanitizedDocPrefix
45
+ };
46
+ const clientUploadContext = {
47
+ ...context,
48
+ signedReceipt: createClientUploadReceipt({
49
+ collectionSlug,
50
+ context,
51
+ filename: sanitizedFilename,
52
+ req
53
+ })
54
+ };
23
55
  return {
24
- fileKey,
56
+ _objectKey,
57
+ clientUploadContext,
58
+ fileKey: storageFilePath,
25
59
  sanitizedDocPrefix,
26
- sanitizedFilename
60
+ sanitizedFilename,
61
+ storageFilePath
27
62
  };
28
63
  }
29
64
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utilities/resolveSignedURLKey.ts"],"sourcesContent":["import type { PayloadRequest } from 'payload'\n\nimport { getSafeFileName } from 'payload/internal'\n\nimport { getFileKey } from './getFileKey.js'\n\ntype Args = {\n collectionPrefix?: string\n collectionSlug: string\n docPrefix?: string\n filename: string\n req: PayloadRequest\n useCompositePrefixes?: boolean\n}\n\n/**\n * Resolves the storage key for a clientUploads signed-URL request, deduping\n * the filename via {@link getSafeFileName} so a duplicate upload does not\n * overwrite an existing blob.\n *\n * The resolved `sanitizedFilename` is returned so the browser-side handler\n * can update the form via `updateFilename`.\n */\nexport async function resolveSignedURLKey({\n collectionPrefix = '',\n collectionSlug,\n docPrefix,\n filename,\n req,\n useCompositePrefixes = false,\n}: Args) {\n const sanitizedFilename = await getSafeFileName({\n collectionSlug,\n desiredFilename: filename,\n prefix: docPrefix,\n req,\n })\n\n const { fileKey, sanitizedDocPrefix } = getFileKey({\n collectionPrefix,\n docPrefix,\n filename: sanitizedFilename,\n useCompositePrefixes,\n })\n\n return { fileKey, sanitizedDocPrefix, sanitizedFilename }\n}\n"],"names":["getSafeFileName","getFileKey","resolveSignedURLKey","collectionPrefix","collectionSlug","docPrefix","filename","req","useCompositePrefixes","sanitizedFilename","desiredFilename","prefix","fileKey","sanitizedDocPrefix"],"mappings":"AAEA,SAASA,eAAe,QAAQ,mBAAkB;AAElD,SAASC,UAAU,QAAQ,kBAAiB;AAW5C;;;;;;;CAOC,GACD,OAAO,eAAeC,oBAAoB,EACxCC,mBAAmB,EAAE,EACrBC,cAAc,EACdC,SAAS,EACTC,QAAQ,EACRC,GAAG,EACHC,uBAAuB,KAAK,EACvB;IACL,MAAMC,oBAAoB,MAAMT,gBAAgB;QAC9CI;QACAM,iBAAiBJ;QACjBK,QAAQN;QACRE;IACF;IAEA,MAAM,EAAEK,OAAO,EAAEC,kBAAkB,EAAE,GAAGZ,WAAW;QACjDE;QACAE;QACAC,UAAUG;QACVD;IACF;IAEA,OAAO;QAAEI;QAASC;QAAoBJ;IAAkB;AAC1D"}
1
+ {"version":3,"sources":["../../src/utilities/resolveSignedURLKey.ts"],"sourcesContent":["import type { PayloadRequest } from 'payload'\n\nimport { randomUUID } from 'crypto'\nimport { createClientUploadReceipt, getSafeFileName } from 'payload/internal'\nimport { getSanitizedUploadFilename } from 'payload/shared'\n\nimport type { ClientUploadContext } from '../types.js'\n\nimport { buildUploadPrefix, buildUploadStoragePathData } from './buildStoragePathData.js'\n\ntype Args = {\n collectionPrefix?: string\n collectionSlug: string\n docPrefix?: string\n filename: string\n req: PayloadRequest\n useCompositePrefixes?: boolean\n}\n\n/**\n * Resolves the storage key for a clientUploads signed-URL request, deduping\n * the filename via {@link getSafeFileName} so a duplicate upload does not\n * overwrite an existing blob.\n *\n * A per-upload segment is persisted in `_objectKey` (not `prefix`), so the stored `prefix`\n * and `filename` stay clean.\n *\n * The resolved `sanitizedFilename` is returned so the browser-side handler\n * can update the form via `updateFilename`.\n *\n * The semantic prefix is resolved with {@link buildUploadPrefix} and the final key with\n * {@link buildUploadStoragePathData}, so the receipt only ever signs a key beneath the configured\n * collection prefix.\n */\nexport async function resolveSignedURLKey({\n collectionPrefix = '',\n collectionSlug,\n docPrefix,\n filename,\n req,\n useCompositePrefixes = false,\n}: Args) {\n // Sanitize with the same helper generateFileData uses for the DB filename so the storage key\n // and doc.filename stay in sync (#16694).\n const sanitizedFilename = await getSafeFileName({\n collectionSlug,\n desiredFilename: getSanitizedUploadFilename(filename),\n req,\n })\n\n const rawBaseDocPrefix = useCompositePrefixes ? docPrefix : docPrefix || collectionPrefix\n const { sanitizedDocPrefix } = buildUploadPrefix({\n collectionPrefix,\n docPrefix: rawBaseDocPrefix,\n useCompositePrefixes,\n })\n\n const _objectKey = randomUUID()\n // Persisted in `_objectKey`, keeping `prefix` semantic.\n const keyedDocPrefix = sanitizedDocPrefix ? `${sanitizedDocPrefix}/${_objectKey}` : _objectKey\n\n const { storageFilePath } = buildUploadStoragePathData({\n collectionPrefix,\n docPrefix: keyedDocPrefix,\n filename: sanitizedFilename,\n useCompositePrefixes,\n })\n\n const context = {\n _objectKey,\n prefix: sanitizedDocPrefix,\n }\n const clientUploadContext: ClientUploadContext = {\n ...context,\n signedReceipt: createClientUploadReceipt({\n collectionSlug,\n context,\n filename: sanitizedFilename,\n req,\n }),\n }\n\n return {\n _objectKey,\n clientUploadContext,\n fileKey: storageFilePath,\n sanitizedDocPrefix,\n sanitizedFilename,\n storageFilePath,\n }\n}\n"],"names":["randomUUID","createClientUploadReceipt","getSafeFileName","getSanitizedUploadFilename","buildUploadPrefix","buildUploadStoragePathData","resolveSignedURLKey","collectionPrefix","collectionSlug","docPrefix","filename","req","useCompositePrefixes","sanitizedFilename","desiredFilename","rawBaseDocPrefix","sanitizedDocPrefix","_objectKey","keyedDocPrefix","storageFilePath","context","prefix","clientUploadContext","signedReceipt","fileKey"],"mappings":"AAEA,SAASA,UAAU,QAAQ,SAAQ;AACnC,SAASC,yBAAyB,EAAEC,eAAe,QAAQ,mBAAkB;AAC7E,SAASC,0BAA0B,QAAQ,iBAAgB;AAI3D,SAASC,iBAAiB,EAAEC,0BAA0B,QAAQ,4BAA2B;AAWzF;;;;;;;;;;;;;;CAcC,GACD,OAAO,eAAeC,oBAAoB,EACxCC,mBAAmB,EAAE,EACrBC,cAAc,EACdC,SAAS,EACTC,QAAQ,EACRC,GAAG,EACHC,uBAAuB,KAAK,EACvB;IACL,6FAA6F;IAC7F,0CAA0C;IAC1C,MAAMC,oBAAoB,MAAMX,gBAAgB;QAC9CM;QACAM,iBAAiBX,2BAA2BO;QAC5CC;IACF;IAEA,MAAMI,mBAAmBH,uBAAuBH,YAAYA,aAAaF;IACzE,MAAM,EAAES,kBAAkB,EAAE,GAAGZ,kBAAkB;QAC/CG;QACAE,WAAWM;QACXH;IACF;IAEA,MAAMK,aAAajB;IACnB,wDAAwD;IACxD,MAAMkB,iBAAiBF,qBAAqB,GAAGA,mBAAmB,CAAC,EAAEC,YAAY,GAAGA;IAEpF,MAAM,EAAEE,eAAe,EAAE,GAAGd,2BAA2B;QACrDE;QACAE,WAAWS;QACXR,UAAUG;QACVD;IACF;IAEA,MAAMQ,UAAU;QACdH;QACAI,QAAQL;IACV;IACA,MAAMM,sBAA2C;QAC/C,GAAGF,OAAO;QACVG,eAAetB,0BAA0B;YACvCO;YACAY;YACAV,UAAUG;YACVF;QACF;IACF;IAEA,OAAO;QACLM;QACAK;QACAE,SAASL;QACTH;QACAH;QACAM;IACF;AACF"}
@@ -0,0 +1,56 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { createClientUploadReceipt, getSafeFileName } from 'payload/internal';
3
+ import { resolveSignedURLKey } from './resolveSignedURLKey.js';
4
+ vi.mock('payload/internal', ()=>({
5
+ createClientUploadReceipt: vi.fn(()=>'receipt'),
6
+ getSafeFileName: vi.fn()
7
+ }));
8
+ describe('resolveSignedURLKey', ()=>{
9
+ beforeEach(()=>{
10
+ vi.clearAllMocks();
11
+ });
12
+ it('should deduplicate within the resolved document prefix', async ()=>{
13
+ vi.mocked(getSafeFileName).mockImplementation(async ({ desiredFilename })=>{
14
+ expect(desiredFilename).toBe('photo.jpg');
15
+ return 'photo-1.jpg';
16
+ });
17
+ const result = await resolveSignedURLKey({
18
+ collectionPrefix: 'media',
19
+ collectionSlug: 'media',
20
+ docPrefix: 'media-archive',
21
+ filename: 'photo.jpg',
22
+ req: {}
23
+ });
24
+ expect(result.sanitizedDocPrefix).toBe('media/media-archive');
25
+ expect(result.fileKey).toMatch(/^media\/media-archive\/[0-9a-f-]+\/photo-1\.jpg$/);
26
+ expect(result.sanitizedFilename).toBe('photo-1.jpg');
27
+ expect(result._objectKey).toMatch(/^[0-9a-f-]+$/);
28
+ expect(result.clientUploadContext).toMatchObject({
29
+ _objectKey: result._objectKey,
30
+ prefix: 'media/media-archive',
31
+ signedReceipt: 'receipt'
32
+ });
33
+ });
34
+ it('should mint the receipt for a prefix contained by the collection prefix', async ()=>{
35
+ vi.mocked(getSafeFileName).mockResolvedValue('photo-1.jpg');
36
+ const { _objectKey, sanitizedDocPrefix } = await resolveSignedURLKey({
37
+ collectionPrefix: 'media',
38
+ collectionSlug: 'media',
39
+ docPrefix: 'media-archive',
40
+ filename: 'photo.jpg',
41
+ req: {}
42
+ });
43
+ expect(sanitizedDocPrefix).toBe('media/media-archive');
44
+ expect(createClientUploadReceipt).toHaveBeenCalledWith({
45
+ collectionSlug: 'media',
46
+ context: {
47
+ _objectKey,
48
+ prefix: sanitizedDocPrefix
49
+ },
50
+ filename: 'photo-1.jpg',
51
+ req: {}
52
+ });
53
+ });
54
+ });
55
+
56
+ //# sourceMappingURL=resolveSignedURLKey.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/resolveSignedURLKey.spec.ts"],"sourcesContent":["import type { PayloadRequest } from 'payload'\n\nimport { beforeEach, describe, expect, it, vi } from 'vitest'\n\nimport { createClientUploadReceipt, getSafeFileName } from 'payload/internal'\n\nimport { resolveSignedURLKey } from './resolveSignedURLKey.js'\n\nvi.mock('payload/internal', () => ({\n createClientUploadReceipt: vi.fn(() => 'receipt'),\n getSafeFileName: vi.fn(),\n}))\n\ndescribe('resolveSignedURLKey', () => {\n beforeEach(() => {\n vi.clearAllMocks()\n })\n\n it('should deduplicate within the resolved document prefix', async () => {\n vi.mocked(getSafeFileName).mockImplementation(async ({ desiredFilename }) => {\n expect(desiredFilename).toBe('photo.jpg')\n return 'photo-1.jpg'\n })\n\n const result = await resolveSignedURLKey({\n collectionPrefix: 'media',\n collectionSlug: 'media',\n docPrefix: 'media-archive',\n filename: 'photo.jpg',\n req: {} as PayloadRequest,\n })\n\n expect(result.sanitizedDocPrefix).toBe('media/media-archive')\n expect(result.fileKey).toMatch(/^media\\/media-archive\\/[0-9a-f-]+\\/photo-1\\.jpg$/)\n expect(result.sanitizedFilename).toBe('photo-1.jpg')\n expect(result._objectKey).toMatch(/^[0-9a-f-]+$/)\n expect(result.clientUploadContext).toMatchObject({\n _objectKey: result._objectKey,\n prefix: 'media/media-archive',\n signedReceipt: 'receipt',\n })\n })\n\n it('should mint the receipt for a prefix contained by the collection prefix', async () => {\n vi.mocked(getSafeFileName).mockResolvedValue('photo-1.jpg')\n\n const { _objectKey, sanitizedDocPrefix } = await resolveSignedURLKey({\n collectionPrefix: 'media',\n collectionSlug: 'media',\n docPrefix: 'media-archive',\n filename: 'photo.jpg',\n req: {} as PayloadRequest,\n })\n\n expect(sanitizedDocPrefix).toBe('media/media-archive')\n expect(createClientUploadReceipt).toHaveBeenCalledWith({\n collectionSlug: 'media',\n context: { _objectKey, prefix: sanitizedDocPrefix },\n filename: 'photo-1.jpg',\n req: {},\n })\n })\n})\n"],"names":["beforeEach","describe","expect","it","vi","createClientUploadReceipt","getSafeFileName","resolveSignedURLKey","mock","fn","clearAllMocks","mocked","mockImplementation","desiredFilename","toBe","result","collectionPrefix","collectionSlug","docPrefix","filename","req","sanitizedDocPrefix","fileKey","toMatch","sanitizedFilename","_objectKey","clientUploadContext","toMatchObject","prefix","signedReceipt","mockResolvedValue","toHaveBeenCalledWith","context"],"mappings":"AAEA,SAASA,UAAU,EAAEC,QAAQ,EAAEC,MAAM,EAAEC,EAAE,EAAEC,EAAE,QAAQ,SAAQ;AAE7D,SAASC,yBAAyB,EAAEC,eAAe,QAAQ,mBAAkB;AAE7E,SAASC,mBAAmB,QAAQ,2BAA0B;AAE9DH,GAAGI,IAAI,CAAC,oBAAoB,IAAO,CAAA;QACjCH,2BAA2BD,GAAGK,EAAE,CAAC,IAAM;QACvCH,iBAAiBF,GAAGK,EAAE;IACxB,CAAA;AAEAR,SAAS,uBAAuB;IAC9BD,WAAW;QACTI,GAAGM,aAAa;IAClB;IAEAP,GAAG,0DAA0D;QAC3DC,GAAGO,MAAM,CAACL,iBAAiBM,kBAAkB,CAAC,OAAO,EAAEC,eAAe,EAAE;YACtEX,OAAOW,iBAAiBC,IAAI,CAAC;YAC7B,OAAO;QACT;QAEA,MAAMC,SAAS,MAAMR,oBAAoB;YACvCS,kBAAkB;YAClBC,gBAAgB;YAChBC,WAAW;YACXC,UAAU;YACVC,KAAK,CAAC;QACR;QAEAlB,OAAOa,OAAOM,kBAAkB,EAAEP,IAAI,CAAC;QACvCZ,OAAOa,OAAOO,OAAO,EAAEC,OAAO,CAAC;QAC/BrB,OAAOa,OAAOS,iBAAiB,EAAEV,IAAI,CAAC;QACtCZ,OAAOa,OAAOU,UAAU,EAAEF,OAAO,CAAC;QAClCrB,OAAOa,OAAOW,mBAAmB,EAAEC,aAAa,CAAC;YAC/CF,YAAYV,OAAOU,UAAU;YAC7BG,QAAQ;YACRC,eAAe;QACjB;IACF;IAEA1B,GAAG,2EAA2E;QAC5EC,GAAGO,MAAM,CAACL,iBAAiBwB,iBAAiB,CAAC;QAE7C,MAAM,EAAEL,UAAU,EAAEJ,kBAAkB,EAAE,GAAG,MAAMd,oBAAoB;YACnES,kBAAkB;YAClBC,gBAAgB;YAChBC,WAAW;YACXC,UAAU;YACVC,KAAK,CAAC;QACR;QAEAlB,OAAOmB,oBAAoBP,IAAI,CAAC;QAChCZ,OAAOG,2BAA2B0B,oBAAoB,CAAC;YACrDd,gBAAgB;YAChBe,SAAS;gBAAEP;gBAAYG,QAAQP;YAAmB;YAClDF,UAAU;YACVC,KAAK,CAAC;QACR;IACF;AACF"}
@@ -3,8 +3,8 @@
3
3
  *
4
4
  * Decodes URI components once (so query-style `%2F` becomes `/`), rejects
5
5
  * values that still contain percent-encodings after decoding (e.g. `%252f`),
6
- * then normalizes slashes, drops `.` / `..` segments, strips leading slashes,
7
- * and removes control characters.
6
+ * then removes control characters, normalizes slashes, drops `.` / `..` segments,
7
+ * and strips leading slashes.
8
8
  */
9
9
  export declare function sanitizePrefix(prefix: string): string;
10
10
  //# sourceMappingURL=sanitizePrefix.d.ts.map
@@ -3,8 +3,8 @@
3
3
  *
4
4
  * Decodes URI components once (so query-style `%2F` becomes `/`), rejects
5
5
  * values that still contain percent-encodings after decoding (e.g. `%252f`),
6
- * then normalizes slashes, drops `.` / `..` segments, strips leading slashes,
7
- * and removes control characters.
6
+ * then removes control characters, normalizes slashes, drops `.` / `..` segments,
7
+ * and strips leading slashes.
8
8
  */ export function sanitizePrefix(prefix) {
9
9
  let decodedPrefix;
10
10
  try {
@@ -16,8 +16,8 @@
16
16
  if (/%[0-9a-f]{2}/i.test(decodedPrefix)) {
17
17
  return '';
18
18
  }
19
- return decodedPrefix.replace(/\\/g, '/').split('/').filter((segment)=>segment !== '..' && segment !== '.').join('/').replace(/^\/+/, '')// eslint-disable-next-line no-control-regex
20
- .replace(/[\x00-\x1f\x80-\x9f]/g, '');
19
+ return decodedPrefix// eslint-disable-next-line no-control-regex
20
+ .replace(/[\x00-\x1f\x80-\x9f]/g, '').replace(/\\/g, '/').split('/').filter((segment)=>segment && segment !== '..' && segment !== '.').join('/').replace(/^\/+/, '');
21
21
  }
22
22
 
23
23
  //# sourceMappingURL=sanitizePrefix.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utilities/sanitizePrefix.ts"],"sourcesContent":["/**\n * Normalizes a storage prefix for use in object keys and URLs.\n *\n * Decodes URI components once (so query-style `%2F` becomes `/`), rejects\n * values that still contain percent-encodings after decoding (e.g. `%252f`),\n * then normalizes slashes, drops `.` / `..` segments, strips leading slashes,\n * and removes control characters.\n */\nexport function sanitizePrefix(prefix: string): string {\n let decodedPrefix: string\n\n try {\n decodedPrefix = decodeURIComponent(prefix)\n } catch {\n return ''\n }\n\n // Reject multi-encoded values (e.g. `%252f`) by allowing only a single decode pass.\n if (/%[0-9a-f]{2}/i.test(decodedPrefix)) {\n return ''\n }\n\n return (\n decodedPrefix\n .replace(/\\\\/g, '/')\n .split('/')\n .filter((segment) => segment !== '..' && segment !== '.')\n .join('/')\n .replace(/^\\/+/, '')\n // eslint-disable-next-line no-control-regex\n .replace(/[\\x00-\\x1f\\x80-\\x9f]/g, '')\n )\n}\n"],"names":["sanitizePrefix","prefix","decodedPrefix","decodeURIComponent","test","replace","split","filter","segment","join"],"mappings":"AAAA;;;;;;;CAOC,GACD,OAAO,SAASA,eAAeC,MAAc;IAC3C,IAAIC;IAEJ,IAAI;QACFA,gBAAgBC,mBAAmBF;IACrC,EAAE,OAAM;QACN,OAAO;IACT;IAEA,oFAAoF;IACpF,IAAI,gBAAgBG,IAAI,CAACF,gBAAgB;QACvC,OAAO;IACT;IAEA,OACEA,cACGG,OAAO,CAAC,OAAO,KACfC,KAAK,CAAC,KACNC,MAAM,CAAC,CAACC,UAAYA,YAAY,QAAQA,YAAY,KACpDC,IAAI,CAAC,KACLJ,OAAO,CAAC,QAAQ,GACjB,4CAA4C;KAC3CA,OAAO,CAAC,yBAAyB;AAExC"}
1
+ {"version":3,"sources":["../../src/utilities/sanitizePrefix.ts"],"sourcesContent":["/**\n * Normalizes a storage prefix for use in object keys and URLs.\n *\n * Decodes URI components once (so query-style `%2F` becomes `/`), rejects\n * values that still contain percent-encodings after decoding (e.g. `%252f`),\n * then removes control characters, normalizes slashes, drops `.` / `..` segments,\n * and strips leading slashes.\n */\nexport function sanitizePrefix(prefix: string): string {\n let decodedPrefix: string\n\n try {\n decodedPrefix = decodeURIComponent(prefix)\n } catch {\n return ''\n }\n\n // Reject multi-encoded values (e.g. `%252f`) by allowing only a single decode pass.\n if (/%[0-9a-f]{2}/i.test(decodedPrefix)) {\n return ''\n }\n\n return (\n decodedPrefix\n // eslint-disable-next-line no-control-regex\n .replace(/[\\x00-\\x1f\\x80-\\x9f]/g, '')\n .replace(/\\\\/g, '/')\n .split('/')\n .filter((segment) => segment && segment !== '..' && segment !== '.')\n .join('/')\n .replace(/^\\/+/, '')\n )\n}\n"],"names":["sanitizePrefix","prefix","decodedPrefix","decodeURIComponent","test","replace","split","filter","segment","join"],"mappings":"AAAA;;;;;;;CAOC,GACD,OAAO,SAASA,eAAeC,MAAc;IAC3C,IAAIC;IAEJ,IAAI;QACFA,gBAAgBC,mBAAmBF;IACrC,EAAE,OAAM;QACN,OAAO;IACT;IAEA,oFAAoF;IACpF,IAAI,gBAAgBG,IAAI,CAACF,gBAAgB;QACvC,OAAO;IACT;IAEA,OACEA,aACE,4CAA4C;KAC3CG,OAAO,CAAC,yBAAyB,IACjCA,OAAO,CAAC,OAAO,KACfC,KAAK,CAAC,KACNC,MAAM,CAAC,CAACC,UAAYA,WAAWA,YAAY,QAAQA,YAAY,KAC/DC,IAAI,CAAC,KACLJ,OAAO,CAAC,QAAQ;AAEvB"}
@@ -0,0 +1,20 @@
1
+ import type { PayloadRequest } from 'payload';
2
+ import type { ClientUploadReceipt } from 'payload/internal';
3
+ type Args = {
4
+ collectionPrefix?: string;
5
+ collectionSlug: string;
6
+ expectedFileKey: string;
7
+ req: PayloadRequest;
8
+ signedReceipt: string;
9
+ useCompositePrefixes?: boolean;
10
+ };
11
+ /**
12
+ * Authenticates a client-upload receipt and asserts it authorizes writing to
13
+ * `expectedFileKey`, returning the verified receipt. Combines
14
+ * {@link verifyClientUploadReceipt} (authentication + collection binding) with
15
+ * {@link assertReceiptMatchesFileKey} (storage-key binding) so an adapter binds
16
+ * the receipt to its request in a single call.
17
+ */
18
+ export declare function verifyClientUploadReceiptForFileKey({ collectionPrefix, collectionSlug, expectedFileKey, req, signedReceipt, useCompositePrefixes, }: Args): ClientUploadReceipt;
19
+ export {};
20
+ //# sourceMappingURL=verifyClientUploadReceiptForFileKey.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verifyClientUploadReceiptForFileKey.d.ts","sourceRoot":"","sources":["../../src/utilities/verifyClientUploadReceiptForFileKey.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAM3D,KAAK,IAAI,GAAG;IACV,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,cAAc,EAAE,MAAM,CAAA;IACtB,eAAe,EAAE,MAAM,CAAA;IACvB,GAAG,EAAE,cAAc,CAAA;IACnB,aAAa,EAAE,MAAM,CAAA;IACrB,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,mCAAmC,CAAC,EAClD,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,GAAG,EACH,aAAa,EACb,oBAAoB,GACrB,EAAE,IAAI,GAAG,mBAAmB,CAM5B"}
@@ -0,0 +1,24 @@
1
+ import { verifyClientUploadReceipt } from 'payload/internal';
2
+ import { assertReceiptMatchesFileKey } from './assertReceiptMatchesFileKey.js';
3
+ /**
4
+ * Authenticates a client-upload receipt and asserts it authorizes writing to
5
+ * `expectedFileKey`, returning the verified receipt. Combines
6
+ * {@link verifyClientUploadReceipt} (authentication + collection binding) with
7
+ * {@link assertReceiptMatchesFileKey} (storage-key binding) so an adapter binds
8
+ * the receipt to its request in a single call.
9
+ */ export function verifyClientUploadReceiptForFileKey({ collectionPrefix, collectionSlug, expectedFileKey, req, signedReceipt, useCompositePrefixes }) {
10
+ const receipt = verifyClientUploadReceipt({
11
+ collectionSlug,
12
+ req,
13
+ signedReceipt
14
+ });
15
+ assertReceiptMatchesFileKey({
16
+ collectionPrefix,
17
+ expectedFileKey,
18
+ receipt,
19
+ useCompositePrefixes
20
+ });
21
+ return receipt;
22
+ }
23
+
24
+ //# sourceMappingURL=verifyClientUploadReceiptForFileKey.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/verifyClientUploadReceiptForFileKey.ts"],"sourcesContent":["import type { PayloadRequest } from 'payload'\nimport type { ClientUploadReceipt } from 'payload/internal'\n\nimport { verifyClientUploadReceipt } from 'payload/internal'\n\nimport { assertReceiptMatchesFileKey } from './assertReceiptMatchesFileKey.js'\n\ntype Args = {\n collectionPrefix?: string\n collectionSlug: string\n expectedFileKey: string\n req: PayloadRequest\n signedReceipt: string\n useCompositePrefixes?: boolean\n}\n\n/**\n * Authenticates a client-upload receipt and asserts it authorizes writing to\n * `expectedFileKey`, returning the verified receipt. Combines\n * {@link verifyClientUploadReceipt} (authentication + collection binding) with\n * {@link assertReceiptMatchesFileKey} (storage-key binding) so an adapter binds\n * the receipt to its request in a single call.\n */\nexport function verifyClientUploadReceiptForFileKey({\n collectionPrefix,\n collectionSlug,\n expectedFileKey,\n req,\n signedReceipt,\n useCompositePrefixes,\n}: Args): ClientUploadReceipt {\n const receipt = verifyClientUploadReceipt({ collectionSlug, req, signedReceipt })\n\n assertReceiptMatchesFileKey({ collectionPrefix, expectedFileKey, receipt, useCompositePrefixes })\n\n return receipt\n}\n"],"names":["verifyClientUploadReceipt","assertReceiptMatchesFileKey","verifyClientUploadReceiptForFileKey","collectionPrefix","collectionSlug","expectedFileKey","req","signedReceipt","useCompositePrefixes","receipt"],"mappings":"AAGA,SAASA,yBAAyB,QAAQ,mBAAkB;AAE5D,SAASC,2BAA2B,QAAQ,mCAAkC;AAW9E;;;;;;CAMC,GACD,OAAO,SAASC,oCAAoC,EAClDC,gBAAgB,EAChBC,cAAc,EACdC,eAAe,EACfC,GAAG,EACHC,aAAa,EACbC,oBAAoB,EACf;IACL,MAAMC,UAAUT,0BAA0B;QAAEI;QAAgBE;QAAKC;IAAc;IAE/EN,4BAA4B;QAAEE;QAAkBE;QAAiBI;QAASD;IAAqB;IAE/F,OAAOC;AACT"}
@@ -0,0 +1,108 @@
1
+ import { createClientUploadReceipt } from 'payload/internal';
2
+ import { describe, expect, it } from 'vitest';
3
+ import { verifyClientUploadReceiptForFileKey } from './verifyClientUploadReceiptForFileKey.js';
4
+ const req = {
5
+ payload: {
6
+ secret: 'test-secret'
7
+ }
8
+ };
9
+ const issueReceipt = ({ collectionSlug = 'media', filename = 'photo.png', prefix })=>createClientUploadReceipt({
10
+ collectionSlug,
11
+ context: {
12
+ prefix
13
+ },
14
+ filename,
15
+ req
16
+ });
17
+ const verify = ({ expectedFileKey, signedReceipt, useCompositePrefixes })=>verifyClientUploadReceiptForFileKey({
18
+ collectionPrefix: 'media',
19
+ collectionSlug: 'media',
20
+ expectedFileKey,
21
+ req,
22
+ signedReceipt,
23
+ useCompositePrefixes
24
+ });
25
+ /**
26
+ * This guard is what lets adapters accept a caller-supplied storage key: the key is
27
+ * recomputed from the signed prefix and filename, so a receipt issued for one location
28
+ * cannot be replayed against another.
29
+ */ describe('verifyClientUploadReceiptForFileKey', ()=>{
30
+ it('should accept the key the receipt was issued for', ()=>{
31
+ const signedReceipt = issueReceipt({
32
+ prefix: 'media/documents'
33
+ });
34
+ expect(verify({
35
+ expectedFileKey: 'media/documents/photo.png',
36
+ signedReceipt
37
+ }).filename).toBe('photo.png');
38
+ });
39
+ it.each([
40
+ [
41
+ 'a sibling of the collection prefix',
42
+ 'media-archive/photo.png'
43
+ ],
44
+ [
45
+ 'another collection',
46
+ 'invoices/photo.png'
47
+ ],
48
+ [
49
+ 'another document prefix in the same collection',
50
+ 'media/other-doc/photo.png'
51
+ ],
52
+ [
53
+ 'another filename under the issued prefix',
54
+ 'media/documents/payroll.pdf'
55
+ ]
56
+ ])('should reject %s', (_case, expectedFileKey)=>{
57
+ const signedReceipt = issueReceipt({
58
+ prefix: 'media/documents'
59
+ });
60
+ expect(()=>verify({
61
+ expectedFileKey,
62
+ signedReceipt
63
+ })).toThrow('Client upload reference does not match this request.');
64
+ });
65
+ it('should reject a receipt issued for another collection', ()=>{
66
+ const signedReceipt = issueReceipt({
67
+ collectionSlug: 'invoices',
68
+ prefix: 'media/documents'
69
+ });
70
+ expect(()=>verify({
71
+ expectedFileKey: 'media/documents/photo.png',
72
+ signedReceipt
73
+ })).toThrow('Invalid or expired client upload reference.');
74
+ });
75
+ it('should reject a receipt whose prefix is not a string', ()=>{
76
+ const signedReceipt = issueReceipt({
77
+ prefix: {
78
+ nested: true
79
+ }
80
+ });
81
+ expect(()=>verify({
82
+ expectedFileKey: 'media/photo.png',
83
+ signedReceipt
84
+ })).toThrow('Client upload reference does not match this request.');
85
+ });
86
+ it('should reject a tampered signature', ()=>{
87
+ const signedReceipt = issueReceipt({
88
+ prefix: 'media/documents'
89
+ });
90
+ const [encodedReceipt] = signedReceipt.split('.');
91
+ expect(()=>verify({
92
+ expectedFileKey: 'media/documents/photo.png',
93
+ signedReceipt: `${encodedReceipt}.not-the-signature`
94
+ })).toThrow();
95
+ });
96
+ it('should recompute the key in composite mode', ()=>{
97
+ const signedReceipt = issueReceipt({
98
+ prefix: 'documents'
99
+ });
100
+ expect(verify({
101
+ expectedFileKey: 'media/documents/photo.png',
102
+ signedReceipt,
103
+ useCompositePrefixes: true
104
+ }).filename).toBe('photo.png');
105
+ });
106
+ });
107
+
108
+ //# sourceMappingURL=verifyClientUploadReceiptForFileKey.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/verifyClientUploadReceiptForFileKey.spec.ts"],"sourcesContent":["import type { PayloadRequest } from 'payload'\n\nimport { createClientUploadReceipt } from 'payload/internal'\nimport { describe, expect, it } from 'vitest'\n\nimport { verifyClientUploadReceiptForFileKey } from './verifyClientUploadReceiptForFileKey.js'\n\nconst req = { payload: { secret: 'test-secret' } } as unknown as PayloadRequest\n\nconst issueReceipt = ({\n collectionSlug = 'media',\n filename = 'photo.png',\n prefix,\n}: {\n collectionSlug?: string\n filename?: string\n prefix: unknown\n}) =>\n createClientUploadReceipt({\n collectionSlug,\n context: { prefix },\n filename,\n req,\n })\n\nconst verify = ({\n expectedFileKey,\n signedReceipt,\n useCompositePrefixes,\n}: {\n expectedFileKey: string\n signedReceipt: string\n useCompositePrefixes?: boolean\n}) =>\n verifyClientUploadReceiptForFileKey({\n collectionPrefix: 'media',\n collectionSlug: 'media',\n expectedFileKey,\n req,\n signedReceipt,\n useCompositePrefixes,\n })\n\n/**\n * This guard is what lets adapters accept a caller-supplied storage key: the key is\n * recomputed from the signed prefix and filename, so a receipt issued for one location\n * cannot be replayed against another.\n */\ndescribe('verifyClientUploadReceiptForFileKey', () => {\n it('should accept the key the receipt was issued for', () => {\n const signedReceipt = issueReceipt({ prefix: 'media/documents' })\n\n expect(verify({ expectedFileKey: 'media/documents/photo.png', signedReceipt }).filename).toBe(\n 'photo.png',\n )\n })\n\n it.each([\n ['a sibling of the collection prefix', 'media-archive/photo.png'],\n ['another collection', 'invoices/photo.png'],\n ['another document prefix in the same collection', 'media/other-doc/photo.png'],\n ['another filename under the issued prefix', 'media/documents/payroll.pdf'],\n ])('should reject %s', (_case, expectedFileKey) => {\n const signedReceipt = issueReceipt({ prefix: 'media/documents' })\n\n expect(() => verify({ expectedFileKey, signedReceipt })).toThrow(\n 'Client upload reference does not match this request.',\n )\n })\n\n it('should reject a receipt issued for another collection', () => {\n const signedReceipt = issueReceipt({ collectionSlug: 'invoices', prefix: 'media/documents' })\n\n expect(() => verify({ expectedFileKey: 'media/documents/photo.png', signedReceipt })).toThrow(\n 'Invalid or expired client upload reference.',\n )\n })\n\n it('should reject a receipt whose prefix is not a string', () => {\n const signedReceipt = issueReceipt({ prefix: { nested: true } })\n\n expect(() => verify({ expectedFileKey: 'media/photo.png', signedReceipt })).toThrow(\n 'Client upload reference does not match this request.',\n )\n })\n\n it('should reject a tampered signature', () => {\n const signedReceipt = issueReceipt({ prefix: 'media/documents' })\n const [encodedReceipt] = signedReceipt.split('.')\n\n expect(() =>\n verify({\n expectedFileKey: 'media/documents/photo.png',\n signedReceipt: `${encodedReceipt}.not-the-signature`,\n }),\n ).toThrow()\n })\n\n it('should recompute the key in composite mode', () => {\n const signedReceipt = issueReceipt({ prefix: 'documents' })\n\n expect(\n verify({\n expectedFileKey: 'media/documents/photo.png',\n signedReceipt,\n useCompositePrefixes: true,\n }).filename,\n ).toBe('photo.png')\n })\n})\n"],"names":["createClientUploadReceipt","describe","expect","it","verifyClientUploadReceiptForFileKey","req","payload","secret","issueReceipt","collectionSlug","filename","prefix","context","verify","expectedFileKey","signedReceipt","useCompositePrefixes","collectionPrefix","toBe","each","_case","toThrow","nested","encodedReceipt","split"],"mappings":"AAEA,SAASA,yBAAyB,QAAQ,mBAAkB;AAC5D,SAASC,QAAQ,EAAEC,MAAM,EAAEC,EAAE,QAAQ,SAAQ;AAE7C,SAASC,mCAAmC,QAAQ,2CAA0C;AAE9F,MAAMC,MAAM;IAAEC,SAAS;QAAEC,QAAQ;IAAc;AAAE;AAEjD,MAAMC,eAAe,CAAC,EACpBC,iBAAiB,OAAO,EACxBC,WAAW,WAAW,EACtBC,MAAM,EAKP,GACCX,0BAA0B;QACxBS;QACAG,SAAS;YAAED;QAAO;QAClBD;QACAL;IACF;AAEF,MAAMQ,SAAS,CAAC,EACdC,eAAe,EACfC,aAAa,EACbC,oBAAoB,EAKrB,GACCZ,oCAAoC;QAClCa,kBAAkB;QAClBR,gBAAgB;QAChBK;QACAT;QACAU;QACAC;IACF;AAEF;;;;CAIC,GACDf,SAAS,uCAAuC;IAC9CE,GAAG,oDAAoD;QACrD,MAAMY,gBAAgBP,aAAa;YAAEG,QAAQ;QAAkB;QAE/DT,OAAOW,OAAO;YAAEC,iBAAiB;YAA6BC;QAAc,GAAGL,QAAQ,EAAEQ,IAAI,CAC3F;IAEJ;IAEAf,GAAGgB,IAAI,CAAC;QACN;YAAC;YAAsC;SAA0B;QACjE;YAAC;YAAsB;SAAqB;QAC5C;YAAC;YAAkD;SAA4B;QAC/E;YAAC;YAA4C;SAA8B;KAC5E,EAAE,oBAAoB,CAACC,OAAON;QAC7B,MAAMC,gBAAgBP,aAAa;YAAEG,QAAQ;QAAkB;QAE/DT,OAAO,IAAMW,OAAO;gBAAEC;gBAAiBC;YAAc,IAAIM,OAAO,CAC9D;IAEJ;IAEAlB,GAAG,yDAAyD;QAC1D,MAAMY,gBAAgBP,aAAa;YAAEC,gBAAgB;YAAYE,QAAQ;QAAkB;QAE3FT,OAAO,IAAMW,OAAO;gBAAEC,iBAAiB;gBAA6BC;YAAc,IAAIM,OAAO,CAC3F;IAEJ;IAEAlB,GAAG,wDAAwD;QACzD,MAAMY,gBAAgBP,aAAa;YAAEG,QAAQ;gBAAEW,QAAQ;YAAK;QAAE;QAE9DpB,OAAO,IAAMW,OAAO;gBAAEC,iBAAiB;gBAAmBC;YAAc,IAAIM,OAAO,CACjF;IAEJ;IAEAlB,GAAG,sCAAsC;QACvC,MAAMY,gBAAgBP,aAAa;YAAEG,QAAQ;QAAkB;QAC/D,MAAM,CAACY,eAAe,GAAGR,cAAcS,KAAK,CAAC;QAE7CtB,OAAO,IACLW,OAAO;gBACLC,iBAAiB;gBACjBC,eAAe,GAAGQ,eAAe,kBAAkB,CAAC;YACtD,IACAF,OAAO;IACX;IAEAlB,GAAG,8CAA8C;QAC/C,MAAMY,gBAAgBP,aAAa;YAAEG,QAAQ;QAAY;QAEzDT,OACEW,OAAO;YACLC,iBAAiB;YACjBC;YACAC,sBAAsB;QACxB,GAAGN,QAAQ,EACXQ,IAAI,CAAC;IACT;AACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@payloadcms/plugin-cloud-storage",
3
- "version": "3.89.0",
3
+ "version": "3.90.0",
4
4
  "description": "The official cloud storage plugin for Payload CMS",
5
5
  "homepage": "https://payloadcms.com",
6
6
  "repository": {
@@ -51,18 +51,18 @@
51
51
  "dependencies": {
52
52
  "find-node-modules": "^2.1.3",
53
53
  "range-parser": "^1.2.1",
54
- "@payloadcms/ui": "3.89.0"
54
+ "@payloadcms/ui": "3.90.0"
55
55
  },
56
56
  "devDependencies": {
57
57
  "@types/find-node-modules": "^2.1.2",
58
58
  "@types/react": "19.2.14",
59
59
  "@types/react-dom": "19.2.3",
60
- "payload": "3.89.0"
60
+ "payload": "3.90.0"
61
61
  },
62
62
  "peerDependencies": {
63
63
  "react": "^19.0.1 || ^19.1.2 || ^19.2.1",
64
64
  "react-dom": "^19.0.1 || ^19.1.2 || ^19.2.1",
65
- "payload": "3.89.0"
65
+ "payload": "3.90.0"
66
66
  },
67
67
  "publishConfig": {
68
68
  "registry": "https://registry.npmjs.org/"