@payloadcms/storage-vercel-blob 4.0.0-internal.5d5a2b2 → 4.0.0-internal.5e065b3

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 (43) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +2 -2
  3. package/dist/adapter.d.ts +3 -1
  4. package/dist/adapter.d.ts.map +1 -1
  5. package/dist/adapter.js +100 -17
  6. package/dist/adapter.js.map +1 -1
  7. package/dist/authorizeFileOverwrite.d.ts +16 -0
  8. package/dist/authorizeFileOverwrite.d.ts.map +1 -0
  9. package/dist/authorizeFileOverwrite.js +117 -0
  10. package/dist/authorizeFileOverwrite.js.map +1 -0
  11. package/dist/client/VercelBlobClientUploadHandler.d.ts +3 -8
  12. package/dist/client/VercelBlobClientUploadHandler.d.ts.map +1 -1
  13. package/dist/client/VercelBlobClientUploadHandler.js +9 -26
  14. package/dist/client/VercelBlobClientUploadHandler.js.map +1 -1
  15. package/dist/deleteFile.d.ts +2 -5
  16. package/dist/deleteFile.d.ts.map +1 -1
  17. package/dist/deleteFile.js +3 -10
  18. package/dist/deleteFile.js.map +1 -1
  19. package/dist/generateURL.d.ts +1 -0
  20. package/dist/generateURL.d.ts.map +1 -1
  21. package/dist/generateURL.js +13 -9
  22. package/dist/generateURL.js.map +1 -1
  23. package/dist/getCollectionSources.d.ts +20 -0
  24. package/dist/getCollectionSources.d.ts.map +1 -0
  25. package/dist/getCollectionSources.js +27 -0
  26. package/dist/getCollectionSources.js.map +1 -0
  27. package/dist/getFile.d.ts +5 -4
  28. package/dist/getFile.d.ts.map +1 -1
  29. package/dist/getFile.js +21 -13
  30. package/dist/getFile.js.map +1 -1
  31. package/dist/index.d.ts +9 -18
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +30 -41
  34. package/dist/index.js.map +1 -1
  35. package/dist/uploadFile.d.ts +2 -5
  36. package/dist/uploadFile.d.ts.map +1 -1
  37. package/dist/uploadFile.js +3 -9
  38. package/dist/uploadFile.js.map +1 -1
  39. package/package.json +4 -4
  40. package/dist/getClientUploadRoute.d.ts +0 -13
  41. package/dist/getClientUploadRoute.d.ts.map +0 -1
  42. package/dist/getClientUploadRoute.js +0 -35
  43. package/dist/getClientUploadRoute.js.map +0 -1
package/LICENSE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2018-2025 Payload CMS, Inc. <info@payloadcms.com>
3
+ Copyright (c) 2018-2026 Payload CMS, LLC <info@payloadcms.com>
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining
6
6
  a copy of this software and associated documentation files (the
package/README.md CHANGED
@@ -15,7 +15,7 @@ pnpm add @payloadcms/storage-vercel-blob
15
15
  - Configure the `collections` object to specify which collections should use the Vercel Blob adapter. The slug _must_ match one of your existing collection slugs.
16
16
  - Ensure you have `BLOB_READ_WRITE_TOKEN` set in your Vercel environment variables. This is usually set by Vercel automatically after adding blob storage to your project.
17
17
  - When enabled, this package will automatically set `disableLocalStorage` to `true` for each collection.
18
- - When deploying to Vercel, server uploads are limited with 4.5MB. Set `clientUploads` to `true` to do uploads directly on the client.
18
+ - When deploying to Vercel, server uploads are limited to 4.5MB. Set `clientUploads` to `true` to use upload instructions and send files directly to Vercel Blob.
19
19
 
20
20
  ```ts
21
21
  import { vercelBlobStorage } from '@payloadcms/storage-vercel-blob'
@@ -48,4 +48,4 @@ export default buildConfig({
48
48
  | `addRandomSuffix` | Add a random suffix to the uploaded file name in Vercel Blob storage | `false` |
49
49
  | `cacheControlMaxAge` | Cache-Control max-age in seconds | `365 * 24 * 60 * 60` (1 Year) |
50
50
  | `token` | Vercel Blob storage read/write token | `''` |
51
- | `clientUploads` | Do uploads directly on the client to bypass limits on Vercel | |
51
+ | `clientUploads` | Upload directly to Vercel Blob instead of through Payload. | |
package/dist/adapter.d.ts CHANGED
@@ -1,13 +1,15 @@
1
1
  import type { Adapter, ClientUploadsConfig } from '@payloadcms/plugin-cloud-storage/types';
2
+ import type { VercelBlobCollectionSource } from './authorizeFileOverwrite.js';
2
3
  interface CreateVercelBlobAdapterArgs {
3
4
  access: 'public';
4
5
  addRandomSuffix?: boolean;
5
6
  baseUrl: string;
6
7
  cacheControlMaxAge: number;
7
8
  clientUploads?: ClientUploadsConfig;
9
+ collectionSources: VercelBlobCollectionSource[];
8
10
  token: string;
9
11
  useCompositePrefixes?: boolean;
10
12
  }
11
- export declare function createVercelBlobAdapter({ access, addRandomSuffix, baseUrl, cacheControlMaxAge, clientUploads, token, useCompositePrefixes, }: CreateVercelBlobAdapterArgs): Adapter;
13
+ export declare function createVercelBlobAdapter({ access, addRandomSuffix, baseUrl, cacheControlMaxAge, clientUploads, collectionSources, token, useCompositePrefixes, }: CreateVercelBlobAdapterArgs): Adapter;
12
14
  export {};
13
15
  //# sourceMappingURL=adapter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,mBAAmB,EAEpB,MAAM,wCAAwC,CAAA;AAO/C,UAAU,2BAA2B;IACnC,MAAM,EAAE,QAAQ,CAAA;IAChB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,kBAAkB,EAAE,MAAM,CAAA;IAC1B,aAAa,CAAC,EAAE,mBAAmB,CAAA;IACnC,KAAK,EAAE,MAAM,CAAA;IACb,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,wBAAgB,uBAAuB,CAAC,EACtC,MAAM,EACN,eAAe,EACf,OAAO,EACP,kBAAkB,EAClB,aAAa,EACb,KAAK,EACL,oBAA4B,GAC7B,EAAE,2BAA2B,GAAG,OAAO,CA+DvC"}
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,mBAAmB,EAEpB,MAAM,wCAAwC,CAAA;AAU/C,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAA;AAQ7E,UAAU,2BAA2B;IACnC,MAAM,EAAE,QAAQ,CAAA;IAChB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,kBAAkB,EAAE,MAAM,CAAA;IAC1B,aAAa,CAAC,EAAE,mBAAmB,CAAA;IACnC,iBAAiB,EAAE,0BAA0B,EAAE,CAAA;IAC/C,KAAK,EAAE,MAAM,CAAA;IACb,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,wBAAgB,uBAAuB,CAAC,EACtC,MAAM,EACN,eAAe,EACf,OAAO,EACP,kBAAkB,EAClB,aAAa,EACb,iBAAiB,EACjB,KAAK,EACL,oBAA4B,GAC7B,EAAE,2BAA2B,GAAG,OAAO,CAiJvC"}
package/dist/adapter.js CHANGED
@@ -1,11 +1,99 @@
1
+ import { buildUploadStoragePathData, resolveSignedURLKey } from '@payloadcms/plugin-cloud-storage/utilities';
2
+ import { generateClientTokenFromReadWriteToken } from '@vercel/blob/client';
3
+ import { Forbidden } from 'payload';
4
+ import { assertClientUploadAllowed, createClientUploadReceipt } from 'payload/internal';
5
+ import { authorizeClientOverwrite } from './authorizeFileOverwrite.js';
1
6
  import { deleteFile } from './deleteFile.js';
2
7
  import { generateURL } from './generateURL.js';
3
8
  import { getFile } from './getFile.js';
4
9
  import { uploadFile } from './uploadFile.js';
5
- export function createVercelBlobAdapter({ access, addRandomSuffix, baseUrl, cacheControlMaxAge, clientUploads, token, useCompositePrefixes = false }) {
10
+ export function createVercelBlobAdapter({ access, addRandomSuffix, baseUrl, cacheControlMaxAge, clientUploads, collectionSources, token, useCompositePrefixes = false }) {
11
+ const clientUploadsAccess = typeof clientUploads === 'object' ? clientUploads.access : undefined;
6
12
  return ({ collection, prefix = '' })=>({
7
13
  name: 'vercel-blob',
8
- clientUploads,
14
+ uploadInstructions: {
15
+ adminHandler: {
16
+ path: '@payloadcms/storage-vercel-blob/client#VercelBlobClientUploadHandler'
17
+ },
18
+ enabled: Boolean(clientUploads),
19
+ generate: async ({ collectionSlug, docPrefix, filename, filesize, mimeType, overrideAccess, req })=>{
20
+ if (!overrideAccess && (clientUploadsAccess ? !await clientUploadsAccess({
21
+ collectionSlug,
22
+ req
23
+ }) : !req.user)) {
24
+ throw new Forbidden(req.t);
25
+ }
26
+ assertClientUploadAllowed({
27
+ collection,
28
+ filename,
29
+ mimeType
30
+ });
31
+ const requested = buildUploadStoragePathData({
32
+ collectionPrefix: prefix,
33
+ docPrefix,
34
+ filename,
35
+ useCompositePrefixes
36
+ });
37
+ const allowOverwrite = await authorizeClientOverwrite({
38
+ collectionPrefix: prefix,
39
+ collectionSources,
40
+ overrideAccess,
41
+ req,
42
+ requestedCollectionSlug: collectionSlug,
43
+ requestedFilename: requested.sanitizedFilename,
44
+ requestedStorageFilePath: requested.storageFilePath
45
+ });
46
+ const resolved = allowOverwrite ? {
47
+ ...requested,
48
+ uploadReference: {
49
+ prefix: requested.sanitizedDocPrefix,
50
+ signedReceipt: createClientUploadReceipt({
51
+ allowOverwrite: true,
52
+ collectionSlug,
53
+ filename: requested.sanitizedFilename,
54
+ filePrefix: requested.sanitizedDocPrefix,
55
+ req,
56
+ storageFilePath: requested.storageFilePath
57
+ })
58
+ }
59
+ } : await resolveSignedURLKey({
60
+ collectionPrefix: prefix,
61
+ collectionSlug,
62
+ docPrefix,
63
+ filename,
64
+ req,
65
+ useCompositePrefixes
66
+ });
67
+ return {
68
+ name: 'uploadToVercelBlob',
69
+ type: 'dispatch',
70
+ data: {
71
+ pathname: resolved.storageFilePath,
72
+ token: await generateClientTokenFromReadWriteToken({
73
+ addRandomSuffix: false,
74
+ allowedContentTypes: mimeType ? [
75
+ mimeType
76
+ ] : undefined,
77
+ ...allowOverwrite && {
78
+ allowOverwrite: true
79
+ },
80
+ cacheControlMaxAge,
81
+ maximumSizeInBytes: filesize,
82
+ pathname: resolved.storageFilePath,
83
+ token
84
+ })
85
+ },
86
+ file: {
87
+ filename: resolved.sanitizedFilename,
88
+ mimeType,
89
+ size: filesize,
90
+ uploadReference: resolved.uploadReference
91
+ }
92
+ };
93
+ },
94
+ requiresUploadReceipt: true,
95
+ useInAdmin: true
96
+ },
9
97
  generateURL: ({ filename, prefix: urlPrefix = '' })=>generateURL({
10
98
  baseUrl,
11
99
  collectionPrefix: prefix,
@@ -13,43 +101,38 @@ export function createVercelBlobAdapter({ access, addRandomSuffix, baseUrl, cach
13
101
  prefix: urlPrefix,
14
102
  useCompositePrefixes
15
103
  }),
16
- handleDelete: ({ doc: { prefix: docPrefix = '' }, filename })=>deleteFile({
104
+ handleDelete: ({ storageFilePath })=>deleteFile({
17
105
  baseUrl,
18
- collectionPrefix: prefix,
19
- docPrefix,
20
- filename,
21
- token,
22
- useCompositePrefixes
106
+ storageFilePath,
107
+ token
23
108
  }),
24
- handleUpload: async ({ data, file: { buffer, filename, mimeType } })=>{
109
+ handleUpload: async ({ data, file: { buffer, mimeType }, storageFilePath })=>{
25
110
  const result = await uploadFile({
26
111
  access,
27
112
  addRandomSuffix,
28
113
  buffer,
29
114
  cacheControlMaxAge,
30
- collectionPrefix: prefix,
31
- docPrefix: data.prefix,
32
- filename,
33
115
  mimeType,
34
- token,
35
- useCompositePrefixes
116
+ storageFilePath,
117
+ token
36
118
  });
37
119
  if (result.filename) {
38
120
  data.filename = result.filename;
39
121
  }
40
122
  return data;
41
123
  },
42
- staticHandler: (req, { headers, params: { clientUploadContext, filename, prefix: prefixQueryParam } })=>getFile({
124
+ staticHandler: (req, { doc, headers, params: { filename, operation, uploadReference } })=>getFile({
43
125
  baseUrl,
44
126
  cacheControlMaxAge,
45
- clientUploadContext,
46
127
  collection,
47
128
  collectionPrefix: prefix,
129
+ doc,
48
130
  filename,
49
131
  incomingHeaders: headers,
50
- prefixQueryParam,
132
+ operation,
51
133
  req,
52
134
  token,
135
+ uploadReference,
53
136
  useCompositePrefixes
54
137
  })
55
138
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/adapter.ts"],"sourcesContent":["import type {\n Adapter,\n ClientUploadsConfig,\n GeneratedAdapter,\n} from '@payloadcms/plugin-cloud-storage/types'\n\nimport { deleteFile } from './deleteFile.js'\nimport { generateURL } from './generateURL.js'\nimport { getFile } from './getFile.js'\nimport { uploadFile } from './uploadFile.js'\n\ninterface CreateVercelBlobAdapterArgs {\n access: 'public'\n addRandomSuffix?: boolean\n baseUrl: string\n cacheControlMaxAge: number\n clientUploads?: ClientUploadsConfig\n token: string\n useCompositePrefixes?: boolean\n}\n\nexport function createVercelBlobAdapter({\n access,\n addRandomSuffix,\n baseUrl,\n cacheControlMaxAge,\n clientUploads,\n token,\n useCompositePrefixes = false,\n}: CreateVercelBlobAdapterArgs): Adapter {\n return ({ collection, prefix = '' }): GeneratedAdapter => ({\n name: 'vercel-blob',\n clientUploads,\n\n generateURL: ({ filename, prefix: urlPrefix = '' }) =>\n generateURL({\n baseUrl,\n collectionPrefix: prefix,\n filename,\n prefix: urlPrefix,\n useCompositePrefixes,\n }),\n\n handleDelete: ({ doc: { prefix: docPrefix = '' }, filename }) =>\n deleteFile({\n baseUrl,\n collectionPrefix: prefix,\n docPrefix,\n filename,\n token,\n useCompositePrefixes,\n }),\n\n handleUpload: async ({ data, file: { buffer, filename, mimeType } }) => {\n const result = await uploadFile({\n access,\n addRandomSuffix,\n buffer,\n cacheControlMaxAge,\n collectionPrefix: prefix,\n docPrefix: data.prefix,\n filename,\n mimeType,\n token,\n useCompositePrefixes,\n })\n\n if (result.filename) {\n data.filename = result.filename\n }\n\n return data\n },\n\n staticHandler: (\n req,\n { headers, params: { clientUploadContext, filename, prefix: prefixQueryParam } },\n ) =>\n getFile({\n baseUrl,\n cacheControlMaxAge,\n clientUploadContext,\n collection,\n collectionPrefix: prefix,\n filename,\n incomingHeaders: headers,\n prefixQueryParam,\n req,\n token,\n useCompositePrefixes,\n }),\n })\n}\n"],"names":["deleteFile","generateURL","getFile","uploadFile","createVercelBlobAdapter","access","addRandomSuffix","baseUrl","cacheControlMaxAge","clientUploads","token","useCompositePrefixes","collection","prefix","name","filename","urlPrefix","collectionPrefix","handleDelete","doc","docPrefix","handleUpload","data","file","buffer","mimeType","result","staticHandler","req","headers","params","clientUploadContext","prefixQueryParam","incomingHeaders"],"mappings":"AAMA,SAASA,UAAU,QAAQ,kBAAiB;AAC5C,SAASC,WAAW,QAAQ,mBAAkB;AAC9C,SAASC,OAAO,QAAQ,eAAc;AACtC,SAASC,UAAU,QAAQ,kBAAiB;AAY5C,OAAO,SAASC,wBAAwB,EACtCC,MAAM,EACNC,eAAe,EACfC,OAAO,EACPC,kBAAkB,EAClBC,aAAa,EACbC,KAAK,EACLC,uBAAuB,KAAK,EACA;IAC5B,OAAO,CAAC,EAAEC,UAAU,EAAEC,SAAS,EAAE,EAAE,GAAwB,CAAA;YACzDC,MAAM;YACNL;YAEAR,aAAa,CAAC,EAAEc,QAAQ,EAAEF,QAAQG,YAAY,EAAE,EAAE,GAChDf,YAAY;oBACVM;oBACAU,kBAAkBJ;oBAClBE;oBACAF,QAAQG;oBACRL;gBACF;YAEFO,cAAc,CAAC,EAAEC,KAAK,EAAEN,QAAQO,YAAY,EAAE,EAAE,EAAEL,QAAQ,EAAE,GAC1Df,WAAW;oBACTO;oBACAU,kBAAkBJ;oBAClBO;oBACAL;oBACAL;oBACAC;gBACF;YAEFU,cAAc,OAAO,EAAEC,IAAI,EAAEC,MAAM,EAAEC,MAAM,EAAET,QAAQ,EAAEU,QAAQ,EAAE,EAAE;gBACjE,MAAMC,SAAS,MAAMvB,WAAW;oBAC9BE;oBACAC;oBACAkB;oBACAhB;oBACAS,kBAAkBJ;oBAClBO,WAAWE,KAAKT,MAAM;oBACtBE;oBACAU;oBACAf;oBACAC;gBACF;gBAEA,IAAIe,OAAOX,QAAQ,EAAE;oBACnBO,KAAKP,QAAQ,GAAGW,OAAOX,QAAQ;gBACjC;gBAEA,OAAOO;YACT;YAEAK,eAAe,CACbC,KACA,EAAEC,OAAO,EAAEC,QAAQ,EAAEC,mBAAmB,EAAEhB,QAAQ,EAAEF,QAAQmB,gBAAgB,EAAE,EAAE,GAEhF9B,QAAQ;oBACNK;oBACAC;oBACAuB;oBACAnB;oBACAK,kBAAkBJ;oBAClBE;oBACAkB,iBAAiBJ;oBACjBG;oBACAJ;oBACAlB;oBACAC;gBACF;QACJ,CAAA;AACF"}
1
+ {"version":3,"sources":["../src/adapter.ts"],"sourcesContent":["import type {\n Adapter,\n ClientUploadsConfig,\n GeneratedAdapter,\n} from '@payloadcms/plugin-cloud-storage/types'\n\nimport {\n buildUploadStoragePathData,\n resolveSignedURLKey,\n} from '@payloadcms/plugin-cloud-storage/utilities'\nimport { generateClientTokenFromReadWriteToken } from '@vercel/blob/client'\nimport { Forbidden } from 'payload'\nimport { assertClientUploadAllowed, createClientUploadReceipt } from 'payload/internal'\n\nimport type { VercelBlobCollectionSource } from './authorizeFileOverwrite.js'\n\nimport { authorizeClientOverwrite } from './authorizeFileOverwrite.js'\nimport { deleteFile } from './deleteFile.js'\nimport { generateURL } from './generateURL.js'\nimport { getFile } from './getFile.js'\nimport { uploadFile } from './uploadFile.js'\n\ninterface CreateVercelBlobAdapterArgs {\n access: 'public'\n addRandomSuffix?: boolean\n baseUrl: string\n cacheControlMaxAge: number\n clientUploads?: ClientUploadsConfig\n collectionSources: VercelBlobCollectionSource[]\n token: string\n useCompositePrefixes?: boolean\n}\n\nexport function createVercelBlobAdapter({\n access,\n addRandomSuffix,\n baseUrl,\n cacheControlMaxAge,\n clientUploads,\n collectionSources,\n token,\n useCompositePrefixes = false,\n}: CreateVercelBlobAdapterArgs): Adapter {\n const clientUploadsAccess = typeof clientUploads === 'object' ? clientUploads.access : undefined\n\n return ({ collection, prefix = '' }): GeneratedAdapter => ({\n name: 'vercel-blob',\n\n uploadInstructions: {\n adminHandler: {\n path: '@payloadcms/storage-vercel-blob/client#VercelBlobClientUploadHandler',\n },\n enabled: Boolean(clientUploads),\n generate: async ({\n collectionSlug,\n docPrefix,\n filename,\n filesize,\n mimeType,\n overrideAccess,\n req,\n }) => {\n if (\n !overrideAccess &&\n (clientUploadsAccess ? !(await clientUploadsAccess({ collectionSlug, req })) : !req.user)\n ) {\n throw new Forbidden(req.t)\n }\n\n assertClientUploadAllowed({ collection, filename, mimeType })\n\n const requested = buildUploadStoragePathData({\n collectionPrefix: prefix,\n docPrefix,\n filename,\n useCompositePrefixes,\n })\n const allowOverwrite = await authorizeClientOverwrite({\n collectionPrefix: prefix,\n collectionSources,\n overrideAccess,\n req,\n requestedCollectionSlug: collectionSlug,\n requestedFilename: requested.sanitizedFilename,\n requestedStorageFilePath: requested.storageFilePath,\n })\n const resolved = allowOverwrite\n ? {\n ...requested,\n uploadReference: {\n prefix: requested.sanitizedDocPrefix,\n signedReceipt: createClientUploadReceipt({\n allowOverwrite: true,\n collectionSlug,\n filename: requested.sanitizedFilename,\n filePrefix: requested.sanitizedDocPrefix,\n req,\n storageFilePath: requested.storageFilePath,\n }),\n },\n }\n : await resolveSignedURLKey({\n collectionPrefix: prefix,\n collectionSlug,\n docPrefix,\n filename,\n req,\n useCompositePrefixes,\n })\n\n return {\n name: 'uploadToVercelBlob',\n type: 'dispatch',\n data: {\n pathname: resolved.storageFilePath,\n token: await generateClientTokenFromReadWriteToken({\n addRandomSuffix: false,\n allowedContentTypes: mimeType ? [mimeType] : undefined,\n ...(allowOverwrite && { allowOverwrite: true }),\n cacheControlMaxAge,\n maximumSizeInBytes: filesize,\n pathname: resolved.storageFilePath,\n token,\n }),\n },\n file: {\n filename: resolved.sanitizedFilename,\n mimeType,\n size: filesize,\n uploadReference: resolved.uploadReference,\n },\n }\n },\n requiresUploadReceipt: true,\n useInAdmin: true,\n },\n\n generateURL: ({ filename, prefix: urlPrefix = '' }) =>\n generateURL({\n baseUrl,\n collectionPrefix: prefix,\n filename,\n prefix: urlPrefix,\n useCompositePrefixes,\n }),\n\n handleDelete: ({ storageFilePath }) =>\n deleteFile({\n baseUrl,\n storageFilePath,\n token,\n }),\n\n handleUpload: async ({ data, file: { buffer, mimeType }, storageFilePath }) => {\n const result = await uploadFile({\n access,\n addRandomSuffix,\n buffer,\n cacheControlMaxAge,\n mimeType,\n storageFilePath,\n token,\n })\n\n if (result.filename) {\n data.filename = result.filename\n }\n\n return data\n },\n\n staticHandler: (req, { doc, headers, params: { filename, operation, uploadReference } }) =>\n getFile({\n baseUrl,\n cacheControlMaxAge,\n collection,\n collectionPrefix: prefix,\n doc,\n filename,\n incomingHeaders: headers,\n operation,\n req,\n token,\n uploadReference,\n useCompositePrefixes,\n }),\n })\n}\n"],"names":["buildUploadStoragePathData","resolveSignedURLKey","generateClientTokenFromReadWriteToken","Forbidden","assertClientUploadAllowed","createClientUploadReceipt","authorizeClientOverwrite","deleteFile","generateURL","getFile","uploadFile","createVercelBlobAdapter","access","addRandomSuffix","baseUrl","cacheControlMaxAge","clientUploads","collectionSources","token","useCompositePrefixes","clientUploadsAccess","undefined","collection","prefix","name","uploadInstructions","adminHandler","path","enabled","Boolean","generate","collectionSlug","docPrefix","filename","filesize","mimeType","overrideAccess","req","user","t","requested","collectionPrefix","allowOverwrite","requestedCollectionSlug","requestedFilename","sanitizedFilename","requestedStorageFilePath","storageFilePath","resolved","uploadReference","sanitizedDocPrefix","signedReceipt","filePrefix","type","data","pathname","allowedContentTypes","maximumSizeInBytes","file","size","requiresUploadReceipt","useInAdmin","urlPrefix","handleDelete","handleUpload","buffer","result","staticHandler","doc","headers","params","operation","incomingHeaders"],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { PayloadRequest } from 'payload';
2
+ export type VercelBlobCollectionSource = {
3
+ collectionPrefix: string;
4
+ collectionSlug: string;
5
+ useCompositePrefixes: boolean;
6
+ };
7
+ export declare function authorizeClientOverwrite({ collectionPrefix: collectionPrefixArg, collectionSources, overrideAccess, req, requestedCollectionSlug, requestedFilename, requestedStorageFilePath, }: {
8
+ collectionPrefix: string;
9
+ collectionSources: VercelBlobCollectionSource[];
10
+ overrideAccess?: boolean;
11
+ req: PayloadRequest;
12
+ requestedCollectionSlug: string;
13
+ requestedFilename: string;
14
+ requestedStorageFilePath: string;
15
+ }): Promise<boolean>;
16
+ //# sourceMappingURL=authorizeFileOverwrite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authorizeFileOverwrite.d.ts","sourceRoot":"","sources":["../src/authorizeFileOverwrite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAY,cAAc,EAAqB,MAAM,SAAS,CAAA;AAU1E,MAAM,MAAM,0BAA0B,GAAG;IACvC,gBAAgB,EAAE,MAAM,CAAA;IACxB,cAAc,EAAE,MAAM,CAAA;IACtB,oBAAoB,EAAE,OAAO,CAAA;CAC9B,CAAA;AAmHD,wBAAsB,wBAAwB,CAAC,EAC7C,gBAAgB,EAAE,mBAAmB,EACrC,iBAAiB,EACjB,cAAsB,EACtB,GAAG,EACH,uBAAuB,EACvB,iBAAiB,EACjB,wBAAwB,GACzB,EAAE;IACD,gBAAgB,EAAE,MAAM,CAAA;IACxB,iBAAiB,EAAE,0BAA0B,EAAE,CAAA;IAC/C,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,GAAG,EAAE,cAAc,CAAA;IACnB,uBAAuB,EAAE,MAAM,CAAA;IAC/B,iBAAiB,EAAE,MAAM,CAAA;IACzB,wBAAwB,EAAE,MAAM,CAAA;CACjC,GAAG,OAAO,CAAC,OAAO,CAAC,CA8CnB"}
@@ -0,0 +1,117 @@
1
+ import { buildStoragePathData, isStoragePathWithinCollectionPrefix } from '@payloadcms/plugin-cloud-storage/utilities';
2
+ import { combineQueries, executeAccess, Forbidden } from 'payload';
3
+ function getDocumentFileKeys({ collectionPrefix, doc, useCompositePrefixes }) {
4
+ return [
5
+ doc.filename,
6
+ ...Object.values(doc.variants || {}).map((size)=>size?.filename)
7
+ ].filter((filename)=>typeof filename === 'string').map((filename)=>buildStoragePathData({
8
+ collectionPrefix,
9
+ docPrefix: doc.prefix,
10
+ filename,
11
+ useCompositePrefixes
12
+ }).storageFilePath);
13
+ }
14
+ async function findMatchingDocs({ collectionPrefix, collectionSlug, req, requestedFilename, requestedStorageFilePath, useCompositePrefixes }) {
15
+ const collection = req.payload.collections[collectionSlug];
16
+ if (!collection) {
17
+ return [];
18
+ }
19
+ const variants = collection.config.upload && typeof collection.config.upload === 'object' ? collection.config.upload.variants || [] : [];
20
+ const filenameQueries = [
21
+ {
22
+ filename: {
23
+ equals: requestedFilename
24
+ }
25
+ },
26
+ ...variants.map(({ name })=>({
27
+ [`variants.${name}.filename`]: {
28
+ equals: requestedFilename
29
+ }
30
+ }))
31
+ ];
32
+ const result = await req.payload.find({
33
+ collection: collectionSlug,
34
+ depth: 0,
35
+ draft: true,
36
+ overrideAccess: true,
37
+ pagination: false,
38
+ req,
39
+ where: {
40
+ or: filenameQueries
41
+ }
42
+ });
43
+ return result.docs.filter((doc)=>getDocumentFileKeys({
44
+ collectionPrefix,
45
+ doc,
46
+ useCompositePrefixes
47
+ }).includes(requestedStorageFilePath));
48
+ }
49
+ async function canUpdateDoc({ collectionSlug, doc, req }) {
50
+ const collection = req.payload.collections[collectionSlug];
51
+ if (!collection) {
52
+ return false;
53
+ }
54
+ const accessResult = await executeAccess({
55
+ id: doc.id,
56
+ slug: collectionSlug,
57
+ disableErrors: true,
58
+ req
59
+ }, collection.config.access.update);
60
+ if (typeof accessResult === 'boolean') {
61
+ return accessResult;
62
+ }
63
+ const result = await req.payload.find({
64
+ collection: collectionSlug,
65
+ depth: 0,
66
+ draft: true,
67
+ limit: 1,
68
+ overrideAccess: true,
69
+ pagination: false,
70
+ req,
71
+ where: combineQueries({
72
+ id: {
73
+ equals: doc.id
74
+ }
75
+ }, accessResult)
76
+ });
77
+ return result.docs.length === 1;
78
+ }
79
+ export async function authorizeClientOverwrite({ collectionPrefix: collectionPrefixArg, collectionSources, overrideAccess = false, req, requestedCollectionSlug, requestedFilename, requestedStorageFilePath }) {
80
+ if (!isStoragePathWithinCollectionPrefix({
81
+ collectionPrefix: collectionPrefixArg,
82
+ docPrefix: requestedStorageFilePath
83
+ })) {
84
+ throw new Forbidden(req.t);
85
+ }
86
+ const matchedDocs = (await Promise.all(collectionSources.map(async ({ collectionPrefix, collectionSlug, useCompositePrefixes })=>{
87
+ const docs = await findMatchingDocs({
88
+ collectionPrefix,
89
+ collectionSlug,
90
+ req,
91
+ requestedFilename,
92
+ requestedStorageFilePath,
93
+ useCompositePrefixes
94
+ });
95
+ return docs.map((doc)=>({
96
+ collectionSlug,
97
+ doc
98
+ }));
99
+ }))).flat();
100
+ if (matchedDocs.length === 0) {
101
+ return false;
102
+ }
103
+ const matchedDoc = matchedDocs[0];
104
+ if (matchedDocs.length !== 1 || matchedDoc.collectionSlug !== requestedCollectionSlug) {
105
+ throw new Forbidden(req.t);
106
+ }
107
+ if (!overrideAccess && !await canUpdateDoc({
108
+ collectionSlug: matchedDoc.collectionSlug,
109
+ doc: matchedDoc.doc,
110
+ req
111
+ })) {
112
+ throw new Forbidden(req.t);
113
+ }
114
+ return true;
115
+ }
116
+
117
+ //# sourceMappingURL=authorizeFileOverwrite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/authorizeFileOverwrite.ts"],"sourcesContent":["import type { FileData, PayloadRequest, TypeWithID, Where } from 'payload'\n\nimport {\n buildStoragePathData,\n isStoragePathWithinCollectionPrefix,\n} from '@payloadcms/plugin-cloud-storage/utilities'\nimport { combineQueries, executeAccess, Forbidden } from 'payload'\n\ntype OwnerDocument = { prefix?: string } & FileData & TypeWithID\n\nexport type VercelBlobCollectionSource = {\n collectionPrefix: string\n collectionSlug: string\n useCompositePrefixes: boolean\n}\n\ntype Owner = {\n collectionSlug: string\n doc: OwnerDocument\n}\n\nfunction getDocumentFileKeys({\n collectionPrefix,\n doc,\n useCompositePrefixes,\n}: {\n collectionPrefix: string\n doc: OwnerDocument\n useCompositePrefixes: boolean\n}): string[] {\n return [doc.filename, ...Object.values(doc.variants || {}).map((size) => size?.filename)]\n .filter((filename): filename is string => typeof filename === 'string')\n .map(\n (filename) =>\n buildStoragePathData({\n collectionPrefix,\n docPrefix: doc.prefix,\n filename,\n useCompositePrefixes,\n }).storageFilePath,\n )\n}\n\nasync function findMatchingDocs({\n collectionPrefix,\n collectionSlug,\n req,\n requestedFilename,\n requestedStorageFilePath,\n useCompositePrefixes,\n}: {\n collectionPrefix: string\n collectionSlug: string\n req: PayloadRequest\n requestedFilename: string\n requestedStorageFilePath: string\n useCompositePrefixes: boolean\n}): Promise<OwnerDocument[]> {\n const collection = req.payload.collections[collectionSlug]\n\n if (!collection) {\n return []\n }\n\n const variants =\n collection.config.upload && typeof collection.config.upload === 'object'\n ? collection.config.upload.variants || []\n : []\n const filenameQueries: Where[] = [\n { filename: { equals: requestedFilename } },\n ...variants.map(({ name }) => ({\n [`variants.${name}.filename`]: { equals: requestedFilename },\n })),\n ]\n const result = await req.payload.find({\n collection: collectionSlug,\n depth: 0,\n draft: true,\n overrideAccess: true,\n pagination: false,\n req,\n where: { or: filenameQueries },\n })\n\n return (result.docs as OwnerDocument[]).filter((doc) =>\n getDocumentFileKeys({ collectionPrefix, doc, useCompositePrefixes }).includes(\n requestedStorageFilePath,\n ),\n )\n}\n\nasync function canUpdateDoc({\n collectionSlug,\n doc,\n req,\n}: {\n collectionSlug: string\n doc: OwnerDocument\n req: PayloadRequest\n}): Promise<boolean> {\n const collection = req.payload.collections[collectionSlug]\n\n if (!collection) {\n return false\n }\n\n const accessResult = await executeAccess(\n { id: doc.id, slug: collectionSlug, disableErrors: true, req },\n collection.config.access.update,\n )\n\n if (typeof accessResult === 'boolean') {\n return accessResult\n }\n\n const result = await req.payload.find({\n collection: collectionSlug,\n depth: 0,\n draft: true,\n limit: 1,\n overrideAccess: true,\n pagination: false,\n req,\n where: combineQueries({ id: { equals: doc.id } }, accessResult),\n })\n\n return result.docs.length === 1\n}\n\nexport async function authorizeClientOverwrite({\n collectionPrefix: collectionPrefixArg,\n collectionSources,\n overrideAccess = false,\n req,\n requestedCollectionSlug,\n requestedFilename,\n requestedStorageFilePath,\n}: {\n collectionPrefix: string\n collectionSources: VercelBlobCollectionSource[]\n overrideAccess?: boolean\n req: PayloadRequest\n requestedCollectionSlug: string\n requestedFilename: string\n requestedStorageFilePath: string\n}): Promise<boolean> {\n if (\n !isStoragePathWithinCollectionPrefix({\n collectionPrefix: collectionPrefixArg,\n docPrefix: requestedStorageFilePath,\n })\n ) {\n throw new Forbidden(req.t)\n }\n\n const matchedDocs = (\n await Promise.all(\n collectionSources.map(\n async ({ collectionPrefix, collectionSlug, useCompositePrefixes }): Promise<Owner[]> => {\n const docs = await findMatchingDocs({\n collectionPrefix,\n collectionSlug,\n req,\n requestedFilename,\n requestedStorageFilePath,\n useCompositePrefixes,\n })\n\n return docs.map((doc) => ({ collectionSlug, doc }))\n },\n ),\n )\n ).flat()\n\n if (matchedDocs.length === 0) {\n return false\n }\n\n const matchedDoc = matchedDocs[0]!\n if (matchedDocs.length !== 1 || matchedDoc.collectionSlug !== requestedCollectionSlug) {\n throw new Forbidden(req.t)\n }\n\n if (\n !overrideAccess &&\n !(await canUpdateDoc({ collectionSlug: matchedDoc.collectionSlug, doc: matchedDoc.doc, req }))\n ) {\n throw new Forbidden(req.t)\n }\n\n return true\n}\n"],"names":["buildStoragePathData","isStoragePathWithinCollectionPrefix","combineQueries","executeAccess","Forbidden","getDocumentFileKeys","collectionPrefix","doc","useCompositePrefixes","filename","Object","values","variants","map","size","filter","docPrefix","prefix","storageFilePath","findMatchingDocs","collectionSlug","req","requestedFilename","requestedStorageFilePath","collection","payload","collections","config","upload","filenameQueries","equals","name","result","find","depth","draft","overrideAccess","pagination","where","or","docs","includes","canUpdateDoc","accessResult","id","slug","disableErrors","access","update","limit","length","authorizeClientOverwrite","collectionPrefixArg","collectionSources","requestedCollectionSlug","t","matchedDocs","Promise","all","flat","matchedDoc"],"mappings":"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"}
@@ -1,13 +1,8 @@
1
- export type VercelBlobClientUploadHandlerExtra = {
2
- addRandomSuffix: boolean;
3
- useCompositePrefixes: boolean;
4
- };
5
- export declare const VercelBlobClientUploadHandler: ({ children, collectionSlug, enabled, extra, prefix, serverHandlerPath, }: {
1
+ export declare const VercelBlobClientUploadHandler: ({ children, collectionSlug, endpointPath, prefix, props, }: {
6
2
  children: import("react").ReactNode;
7
3
  collectionSlug: import("payload").UploadCollectionSlug;
8
- enabled?: boolean;
9
- extra: VercelBlobClientUploadHandlerExtra;
4
+ endpointPath?: `/${string}`;
10
5
  prefix?: string;
11
- serverHandlerPath: `/${string}`;
6
+ props: Record<string, unknown>;
12
7
  }) => import("react").JSX.Element;
13
8
  //# sourceMappingURL=VercelBlobClientUploadHandler.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"VercelBlobClientUploadHandler.d.ts","sourceRoot":"","sources":["../../src/client/VercelBlobClientUploadHandler.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,kCAAkC,GAAG;IAC/C,eAAe,EAAE,OAAO,CAAA;IACxB,oBAAoB,EAAE,OAAO,CAAA;CAC9B,CAAA;AASD,eAAO,MAAM,6BAA6B;;;;;;;iCAyCtC,CAAA"}
1
+ {"version":3,"file":"VercelBlobClientUploadHandler.d.ts","sourceRoot":"","sources":["../../src/client/VercelBlobClientUploadHandler.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,6BAA6B;;;;;;iCAgBxC,CAAA"}
@@ -1,41 +1,24 @@
1
1
  'use client';
2
2
  import { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client';
3
- import { getFileKey } from '@payloadcms/plugin-cloud-storage/utilities';
4
- import { upload } from '@vercel/blob/client';
5
- import { formatAdminURL } from 'payload/shared';
3
+ import { put } from '@vercel/blob/client';
6
4
  /** Last path segment only (POSIX), for keys like `folder/sub/file.png`. */ function posixBasename(key) {
7
5
  const normalized = key.replace(/^\/+/, '');
8
6
  const lastSlash = normalized.lastIndexOf('/');
9
7
  return lastSlash === -1 ? normalized : normalized.slice(lastSlash + 1);
10
8
  }
11
9
  export const VercelBlobClientUploadHandler = createClientUploadHandler({
12
- handler: async ({ apiRoute, collectionSlug, docPrefix, extra: { addRandomSuffix, useCompositePrefixes = false }, file, prefix, serverHandlerPath, serverURL, updateFilename })=>{
13
- const endpointRoute = formatAdminURL({
14
- apiRoute,
15
- path: serverHandlerPath,
16
- serverURL
17
- });
18
- const { fileKey: pathname, sanitizedDocPrefix } = getFileKey({
19
- collectionPrefix: prefix,
20
- docPrefix,
21
- filename: file.name,
22
- useCompositePrefixes
23
- });
24
- // upload the file directly to Vercel Blob using the signed URL
25
- const result = await upload(pathname, file, {
10
+ name: 'uploadToVercelBlob',
11
+ handler: async ({ data, file, updateFilename })=>{
12
+ const { pathname, token } = data;
13
+ const result = await put(pathname, file, {
26
14
  access: 'public',
27
- clientPayload: collectionSlug,
28
15
  contentType: file.type,
29
- handleUploadUrl: endpointRoute
16
+ token
30
17
  });
31
- // Match server uploadFile: document `filename` is basename only; prefixes are separate.
32
- if (addRandomSuffix) {
33
- const pathname = result.pathname.replace(/^\/+/, '');
34
- updateFilename(decodeURIComponent(posixBasename(pathname)));
18
+ const filename = decodeURIComponent(posixBasename(result.pathname.replace(/^\/+/, '')));
19
+ if (filename !== file.name) {
20
+ updateFilename(filename);
35
21
  }
36
- return {
37
- prefix: sanitizedDocPrefix
38
- };
39
22
  }
40
23
  });
41
24
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/client/VercelBlobClientUploadHandler.ts"],"sourcesContent":["'use client'\nimport { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client'\nimport { getFileKey } from '@payloadcms/plugin-cloud-storage/utilities'\nimport { upload } from '@vercel/blob/client'\nimport { formatAdminURL } from 'payload/shared'\n\nexport type VercelBlobClientUploadHandlerExtra = {\n addRandomSuffix: boolean\n useCompositePrefixes: boolean\n}\n\n/** Last path segment only (POSIX), for keys like `folder/sub/file.png`. */\nfunction posixBasename(key: string): string {\n const normalized = key.replace(/^\\/+/, '')\n const lastSlash = normalized.lastIndexOf('/')\n return lastSlash === -1 ? normalized : normalized.slice(lastSlash + 1)\n}\n\nexport const VercelBlobClientUploadHandler =\n createClientUploadHandler<VercelBlobClientUploadHandlerExtra>({\n handler: async ({\n apiRoute,\n collectionSlug,\n docPrefix,\n extra: { addRandomSuffix, useCompositePrefixes = false },\n file,\n prefix,\n serverHandlerPath,\n serverURL,\n updateFilename,\n }) => {\n const endpointRoute = formatAdminURL({\n apiRoute,\n path: serverHandlerPath,\n serverURL,\n })\n const { fileKey: pathname, sanitizedDocPrefix } = getFileKey({\n collectionPrefix: prefix,\n docPrefix,\n filename: file.name,\n useCompositePrefixes,\n })\n\n // upload the file directly to Vercel Blob using the signed URL\n const result = await upload(pathname, file, {\n access: 'public',\n clientPayload: collectionSlug,\n contentType: file.type,\n handleUploadUrl: endpointRoute,\n })\n\n // Match server uploadFile: document `filename` is basename only; prefixes are separate.\n if (addRandomSuffix) {\n const pathname = result.pathname.replace(/^\\/+/, '')\n updateFilename(decodeURIComponent(posixBasename(pathname)))\n }\n\n return { prefix: sanitizedDocPrefix }\n },\n })\n"],"names":["createClientUploadHandler","getFileKey","upload","formatAdminURL","posixBasename","key","normalized","replace","lastSlash","lastIndexOf","slice","VercelBlobClientUploadHandler","handler","apiRoute","collectionSlug","docPrefix","extra","addRandomSuffix","useCompositePrefixes","file","prefix","serverHandlerPath","serverURL","updateFilename","endpointRoute","path","fileKey","pathname","sanitizedDocPrefix","collectionPrefix","filename","name","result","access","clientPayload","contentType","type","handleUploadUrl","decodeURIComponent"],"mappings":"AAAA;AACA,SAASA,yBAAyB,QAAQ,0CAAyC;AACnF,SAASC,UAAU,QAAQ,6CAA4C;AACvE,SAASC,MAAM,QAAQ,sBAAqB;AAC5C,SAASC,cAAc,QAAQ,iBAAgB;AAO/C,yEAAyE,GACzE,SAASC,cAAcC,GAAW;IAChC,MAAMC,aAAaD,IAAIE,OAAO,CAAC,QAAQ;IACvC,MAAMC,YAAYF,WAAWG,WAAW,CAAC;IACzC,OAAOD,cAAc,CAAC,IAAIF,aAAaA,WAAWI,KAAK,CAACF,YAAY;AACtE;AAEA,OAAO,MAAMG,gCACXX,0BAA8D;IAC5DY,SAAS,OAAO,EACdC,QAAQ,EACRC,cAAc,EACdC,SAAS,EACTC,OAAO,EAAEC,eAAe,EAAEC,uBAAuB,KAAK,EAAE,EACxDC,IAAI,EACJC,MAAM,EACNC,iBAAiB,EACjBC,SAAS,EACTC,cAAc,EACf;QACC,MAAMC,gBAAgBrB,eAAe;YACnCU;YACAY,MAAMJ;YACNC;QACF;QACA,MAAM,EAAEI,SAASC,QAAQ,EAAEC,kBAAkB,EAAE,GAAG3B,WAAW;YAC3D4B,kBAAkBT;YAClBL;YACAe,UAAUX,KAAKY,IAAI;YACnBb;QACF;QAEA,+DAA+D;QAC/D,MAAMc,SAAS,MAAM9B,OAAOyB,UAAUR,MAAM;YAC1Cc,QAAQ;YACRC,eAAepB;YACfqB,aAAahB,KAAKiB,IAAI;YACtBC,iBAAiBb;QACnB;QAEA,wFAAwF;QACxF,IAAIP,iBAAiB;YACnB,MAAMU,WAAWK,OAAOL,QAAQ,CAACpB,OAAO,CAAC,QAAQ;YACjDgB,eAAee,mBAAmBlC,cAAcuB;QAClD;QAEA,OAAO;YAAEP,QAAQQ;QAAmB;IACtC;AACF,GAAE"}
1
+ {"version":3,"sources":["../../src/client/VercelBlobClientUploadHandler.ts"],"sourcesContent":["'use client'\nimport { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client'\nimport { put } from '@vercel/blob/client'\n\n/** Last path segment only (POSIX), for keys like `folder/sub/file.png`. */\nfunction posixBasename(key: string): string {\n const normalized = key.replace(/^\\/+/, '')\n const lastSlash = normalized.lastIndexOf('/')\n return lastSlash === -1 ? normalized : normalized.slice(lastSlash + 1)\n}\n\nexport const VercelBlobClientUploadHandler = createClientUploadHandler({\n name: 'uploadToVercelBlob',\n handler: async ({ data, file, updateFilename }) => {\n const { pathname, token } = data as { pathname: string; token: string }\n\n const result = await put(pathname, file, {\n access: 'public',\n contentType: file.type,\n token,\n })\n\n const filename = decodeURIComponent(posixBasename(result.pathname.replace(/^\\/+/, '')))\n if (filename !== file.name) {\n updateFilename(filename)\n }\n },\n})\n"],"names":["createClientUploadHandler","put","posixBasename","key","normalized","replace","lastSlash","lastIndexOf","slice","VercelBlobClientUploadHandler","name","handler","data","file","updateFilename","pathname","token","result","access","contentType","type","filename","decodeURIComponent"],"mappings":"AAAA;AACA,SAASA,yBAAyB,QAAQ,0CAAyC;AACnF,SAASC,GAAG,QAAQ,sBAAqB;AAEzC,yEAAyE,GACzE,SAASC,cAAcC,GAAW;IAChC,MAAMC,aAAaD,IAAIE,OAAO,CAAC,QAAQ;IACvC,MAAMC,YAAYF,WAAWG,WAAW,CAAC;IACzC,OAAOD,cAAc,CAAC,IAAIF,aAAaA,WAAWI,KAAK,CAACF,YAAY;AACtE;AAEA,OAAO,MAAMG,gCAAgCT,0BAA0B;IACrEU,MAAM;IACNC,SAAS,OAAO,EAAEC,IAAI,EAAEC,IAAI,EAAEC,cAAc,EAAE;QAC5C,MAAM,EAAEC,QAAQ,EAAEC,KAAK,EAAE,GAAGJ;QAE5B,MAAMK,SAAS,MAAMhB,IAAIc,UAAUF,MAAM;YACvCK,QAAQ;YACRC,aAAaN,KAAKO,IAAI;YACtBJ;QACF;QAEA,MAAMK,WAAWC,mBAAmBpB,cAAce,OAAOF,QAAQ,CAACV,OAAO,CAAC,QAAQ;QAClF,IAAIgB,aAAaR,KAAKH,IAAI,EAAE;YAC1BI,eAAeO;QACjB;IACF;AACF,GAAE"}
@@ -1,11 +1,8 @@
1
1
  interface DeleteFileArgs {
2
2
  baseUrl: string;
3
- collectionPrefix?: string;
4
- docPrefix: string;
5
- filename: string;
3
+ storageFilePath: string;
6
4
  token: string;
7
- useCompositePrefixes?: boolean;
8
5
  }
9
- export declare function deleteFile({ baseUrl, collectionPrefix, docPrefix, filename, token, useCompositePrefixes, }: DeleteFileArgs): Promise<void>;
6
+ export declare function deleteFile({ baseUrl, storageFilePath, token, }: DeleteFileArgs): Promise<void>;
10
7
  export {};
11
8
  //# sourceMappingURL=deleteFile.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"deleteFile.d.ts","sourceRoot":"","sources":["../src/deleteFile.ts"],"names":[],"mappings":"AAIA,UAAU,cAAc;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,wBAAsB,UAAU,CAAC,EAC/B,OAAO,EACP,gBAAqB,EACrB,SAAS,EACT,QAAQ,EACR,KAAK,EACL,oBAA4B,GAC7B,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAUhC"}
1
+ {"version":3,"file":"deleteFile.d.ts","sourceRoot":"","sources":["../src/deleteFile.ts"],"names":[],"mappings":"AAIA,UAAU,cAAc;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,eAAe,EAAE,MAAM,CAAA;IACvB,KAAK,EAAE,MAAM,CAAA;CACd;AAED,wBAAsB,UAAU,CAAC,EAC/B,OAAO,EACP,eAAe,EACf,KAAK,GACN,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAEhC"}
@@ -1,14 +1,7 @@
1
1
  import { del } from '@vercel/blob';
2
- import { generateURL } from './generateURL.js';
3
- export async function deleteFile({ baseUrl, collectionPrefix = '', docPrefix, filename, token, useCompositePrefixes = false }) {
4
- const fileUrl = generateURL({
5
- baseUrl,
6
- collectionPrefix,
7
- filename,
8
- prefix: docPrefix,
9
- useCompositePrefixes
10
- });
11
- await del(fileUrl, {
2
+ import { buildBlobUrl } from './generateURL.js';
3
+ export async function deleteFile({ baseUrl, storageFilePath, token }) {
4
+ await del(buildBlobUrl(baseUrl, storageFilePath), {
12
5
  token
13
6
  });
14
7
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/deleteFile.ts"],"sourcesContent":["import { del } from '@vercel/blob'\n\nimport { generateURL } from './generateURL.js'\n\ninterface DeleteFileArgs {\n baseUrl: string\n collectionPrefix?: string\n docPrefix: string\n filename: string\n token: string\n useCompositePrefixes?: boolean\n}\n\nexport async function deleteFile({\n baseUrl,\n collectionPrefix = '',\n docPrefix,\n filename,\n token,\n useCompositePrefixes = false,\n}: DeleteFileArgs): Promise<void> {\n const fileUrl = generateURL({\n baseUrl,\n collectionPrefix,\n filename,\n prefix: docPrefix,\n useCompositePrefixes,\n })\n\n await del(fileUrl, { token })\n}\n"],"names":["del","generateURL","deleteFile","baseUrl","collectionPrefix","docPrefix","filename","token","useCompositePrefixes","fileUrl","prefix"],"mappings":"AAAA,SAASA,GAAG,QAAQ,eAAc;AAElC,SAASC,WAAW,QAAQ,mBAAkB;AAW9C,OAAO,eAAeC,WAAW,EAC/BC,OAAO,EACPC,mBAAmB,EAAE,EACrBC,SAAS,EACTC,QAAQ,EACRC,KAAK,EACLC,uBAAuB,KAAK,EACb;IACf,MAAMC,UAAUR,YAAY;QAC1BE;QACAC;QACAE;QACAI,QAAQL;QACRG;IACF;IAEA,MAAMR,IAAIS,SAAS;QAAEF;IAAM;AAC7B"}
1
+ {"version":3,"sources":["../src/deleteFile.ts"],"sourcesContent":["import { del } from '@vercel/blob'\n\nimport { buildBlobUrl } from './generateURL.js'\n\ninterface DeleteFileArgs {\n baseUrl: string\n storageFilePath: string\n token: string\n}\n\nexport async function deleteFile({\n baseUrl,\n storageFilePath,\n token,\n}: DeleteFileArgs): Promise<void> {\n await del(buildBlobUrl(baseUrl, storageFilePath), { token })\n}\n"],"names":["del","buildBlobUrl","deleteFile","baseUrl","storageFilePath","token"],"mappings":"AAAA,SAASA,GAAG,QAAQ,eAAc;AAElC,SAASC,YAAY,QAAQ,mBAAkB;AAQ/C,OAAO,eAAeC,WAAW,EAC/BC,OAAO,EACPC,eAAe,EACfC,KAAK,EACU;IACf,MAAML,IAAIC,aAAaE,SAASC,kBAAkB;QAAEC;IAAM;AAC5D"}
@@ -5,6 +5,7 @@ interface GenerateURLArgs {
5
5
  prefix: string;
6
6
  useCompositePrefixes?: boolean;
7
7
  }
8
+ export declare function buildBlobUrl(baseUrl: string, storageFilePath: string): string;
8
9
  export declare function generateURL({ baseUrl, collectionPrefix, filename, prefix, useCompositePrefixes, }: GenerateURLArgs): string;
9
10
  export {};
10
11
  //# sourceMappingURL=generateURL.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"generateURL.d.ts","sourceRoot":"","sources":["../src/generateURL.ts"],"names":[],"mappings":"AAGA,UAAU,eAAe;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,gBAAqB,EACrB,QAAQ,EACR,MAAM,EACN,oBAA4B,GAC7B,EAAE,eAAe,GAAG,MAAM,CAgB1B"}
1
+ {"version":3,"file":"generateURL.d.ts","sourceRoot":"","sources":["../src/generateURL.ts"],"names":[],"mappings":"AAGA,UAAU,eAAe;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAGD,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,MAAM,CAU7E;AAED,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,gBAAqB,EACrB,QAAQ,EACR,MAAM,EACN,oBAA4B,GAC7B,EAAE,eAAe,GAAG,MAAM,CAS1B"}
@@ -1,19 +1,23 @@
1
- import { getFileKey } from '@payloadcms/plugin-cloud-storage/utilities';
1
+ import { buildStoragePathData } from '@payloadcms/plugin-cloud-storage/utilities';
2
2
  import path from 'path';
3
+ // Builds the public blob URL for a storage path, URL-encoding only the filename segment.
4
+ export function buildBlobUrl(baseUrl, storageFilePath) {
5
+ // example: "my-collection/my-doc/my file.jpg" -> "my-collection/my-doc"
6
+ const dir = path.posix.dirname(storageFilePath);
7
+ // example: "my file.jpg" -> "my%20file.jpg"
8
+ const encodedFilename = encodeURIComponent(path.posix.basename(storageFilePath));
9
+ // example: "my-collection/my-doc/my%20file.jpg"
10
+ const storageFilePathWithEncodedFilename = dir === '.' ? encodedFilename : path.posix.join(dir, encodedFilename);
11
+ return `${baseUrl}/${storageFilePathWithEncodedFilename}`;
12
+ }
3
13
  export function generateURL({ baseUrl, collectionPrefix = '', filename, prefix, useCompositePrefixes = false }) {
4
- const { fileKey: fileKeyWithPrefix } = getFileKey({
14
+ const { storageFilePath } = buildStoragePathData({
5
15
  collectionPrefix,
6
16
  docPrefix: prefix,
7
17
  filename,
8
18
  useCompositePrefixes
9
19
  });
10
- // example: "my-collection/my-doc/my file.jpg" -> "my-collection/my-doc"
11
- const dir = path.posix.dirname(fileKeyWithPrefix);
12
- // example: "my file.jpg" -> "my%20file.jpg"
13
- const encodedFilename = encodeURIComponent(path.posix.basename(fileKeyWithPrefix));
14
- // example: "my-collection/my-doc/my%20file.jpg"
15
- const fileKeyWithEncodedFilename = dir === '.' ? encodedFilename : path.posix.join(dir, encodedFilename);
16
- return `${baseUrl}/${fileKeyWithEncodedFilename}`;
20
+ return buildBlobUrl(baseUrl, storageFilePath);
17
21
  }
18
22
 
19
23
  //# sourceMappingURL=generateURL.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/generateURL.ts"],"sourcesContent":["import { getFileKey } from '@payloadcms/plugin-cloud-storage/utilities'\nimport path from 'path'\n\ninterface GenerateURLArgs {\n baseUrl: string\n collectionPrefix?: string\n filename: string\n prefix: string\n useCompositePrefixes?: boolean\n}\n\nexport function generateURL({\n baseUrl,\n collectionPrefix = '',\n filename,\n prefix,\n useCompositePrefixes = false,\n}: GenerateURLArgs): string {\n const { fileKey: fileKeyWithPrefix } = getFileKey({\n collectionPrefix,\n docPrefix: prefix,\n filename,\n useCompositePrefixes,\n })\n // example: \"my-collection/my-doc/my file.jpg\" -> \"my-collection/my-doc\"\n const dir = path.posix.dirname(fileKeyWithPrefix)\n // example: \"my file.jpg\" -> \"my%20file.jpg\"\n const encodedFilename = encodeURIComponent(path.posix.basename(fileKeyWithPrefix))\n // example: \"my-collection/my-doc/my%20file.jpg\"\n const fileKeyWithEncodedFilename =\n dir === '.' ? encodedFilename : path.posix.join(dir, encodedFilename)\n\n return `${baseUrl}/${fileKeyWithEncodedFilename}`\n}\n"],"names":["getFileKey","path","generateURL","baseUrl","collectionPrefix","filename","prefix","useCompositePrefixes","fileKey","fileKeyWithPrefix","docPrefix","dir","posix","dirname","encodedFilename","encodeURIComponent","basename","fileKeyWithEncodedFilename","join"],"mappings":"AAAA,SAASA,UAAU,QAAQ,6CAA4C;AACvE,OAAOC,UAAU,OAAM;AAUvB,OAAO,SAASC,YAAY,EAC1BC,OAAO,EACPC,mBAAmB,EAAE,EACrBC,QAAQ,EACRC,MAAM,EACNC,uBAAuB,KAAK,EACZ;IAChB,MAAM,EAAEC,SAASC,iBAAiB,EAAE,GAAGT,WAAW;QAChDI;QACAM,WAAWJ;QACXD;QACAE;IACF;IACA,wEAAwE;IACxE,MAAMI,MAAMV,KAAKW,KAAK,CAACC,OAAO,CAACJ;IAC/B,4CAA4C;IAC5C,MAAMK,kBAAkBC,mBAAmBd,KAAKW,KAAK,CAACI,QAAQ,CAACP;IAC/D,gDAAgD;IAChD,MAAMQ,6BACJN,QAAQ,MAAMG,kBAAkBb,KAAKW,KAAK,CAACM,IAAI,CAACP,KAAKG;IAEvD,OAAO,GAAGX,QAAQ,CAAC,EAAEc,4BAA4B;AACnD"}
1
+ {"version":3,"sources":["../src/generateURL.ts"],"sourcesContent":["import { buildStoragePathData } from '@payloadcms/plugin-cloud-storage/utilities'\nimport path from 'path'\n\ninterface GenerateURLArgs {\n baseUrl: string\n collectionPrefix?: string\n filename: string\n prefix: string\n useCompositePrefixes?: boolean\n}\n\n// Builds the public blob URL for a storage path, URL-encoding only the filename segment.\nexport function buildBlobUrl(baseUrl: string, storageFilePath: string): string {\n // example: \"my-collection/my-doc/my file.jpg\" -> \"my-collection/my-doc\"\n const dir = path.posix.dirname(storageFilePath)\n // example: \"my file.jpg\" -> \"my%20file.jpg\"\n const encodedFilename = encodeURIComponent(path.posix.basename(storageFilePath))\n // example: \"my-collection/my-doc/my%20file.jpg\"\n const storageFilePathWithEncodedFilename =\n dir === '.' ? encodedFilename : path.posix.join(dir, encodedFilename)\n\n return `${baseUrl}/${storageFilePathWithEncodedFilename}`\n}\n\nexport function generateURL({\n baseUrl,\n collectionPrefix = '',\n filename,\n prefix,\n useCompositePrefixes = false,\n}: GenerateURLArgs): string {\n const { storageFilePath } = buildStoragePathData({\n collectionPrefix,\n docPrefix: prefix,\n filename,\n useCompositePrefixes,\n })\n\n return buildBlobUrl(baseUrl, storageFilePath)\n}\n"],"names":["buildStoragePathData","path","buildBlobUrl","baseUrl","storageFilePath","dir","posix","dirname","encodedFilename","encodeURIComponent","basename","storageFilePathWithEncodedFilename","join","generateURL","collectionPrefix","filename","prefix","useCompositePrefixes","docPrefix"],"mappings":"AAAA,SAASA,oBAAoB,QAAQ,6CAA4C;AACjF,OAAOC,UAAU,OAAM;AAUvB,yFAAyF;AACzF,OAAO,SAASC,aAAaC,OAAe,EAAEC,eAAuB;IACnE,wEAAwE;IACxE,MAAMC,MAAMJ,KAAKK,KAAK,CAACC,OAAO,CAACH;IAC/B,4CAA4C;IAC5C,MAAMI,kBAAkBC,mBAAmBR,KAAKK,KAAK,CAACI,QAAQ,CAACN;IAC/D,gDAAgD;IAChD,MAAMO,qCACJN,QAAQ,MAAMG,kBAAkBP,KAAKK,KAAK,CAACM,IAAI,CAACP,KAAKG;IAEvD,OAAO,GAAGL,QAAQ,CAAC,EAAEQ,oCAAoC;AAC3D;AAEA,OAAO,SAASE,YAAY,EAC1BV,OAAO,EACPW,mBAAmB,EAAE,EACrBC,QAAQ,EACRC,MAAM,EACNC,uBAAuB,KAAK,EACZ;IAChB,MAAM,EAAEb,eAAe,EAAE,GAAGJ,qBAAqB;QAC/Cc;QACAI,WAAWF;QACXD;QACAE;IACF;IAEA,OAAOf,aAAaC,SAASC;AAC/B"}