@payloadcms/storage-r2 4.0.0-internal.e7d4ebc → 4.0.0-internal.ec73d44

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.
package/dist/adapter.d.ts CHANGED
@@ -5,8 +5,9 @@ interface CreateR2AdapterArgs {
5
5
  bucket: R2Bucket;
6
6
  clientUploads?: ClientUploadsConfig;
7
7
  collections: R2StorageOptions['collections'];
8
+ copyCredentials?: R2StorageOptions['copyCredentials'];
8
9
  useCompositePrefixes?: boolean;
9
10
  }
10
- export declare function createR2Adapter({ bucket, clientUploads, collections, useCompositePrefixes, }: CreateR2AdapterArgs): Adapter;
11
+ export declare function createR2Adapter({ bucket, clientUploads, collections, copyCredentials, useCompositePrefixes, }: CreateR2AdapterArgs): Adapter;
11
12
  export {};
12
13
  //# 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;AAE/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAO1C,UAAU,mBAAmB;IAC3B,MAAM,EAAE,QAAQ,CAAA;IAChB,aAAa,CAAC,EAAE,mBAAmB,CAAA;IACnC,WAAW,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAA;IAC5C,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,wBAAgB,eAAe,CAAC,EAC9B,MAAM,EACN,aAAa,EACb,WAAW,EACX,oBAA4B,GAC7B,EAAE,mBAAmB,GAAG,OAAO,CAwE/B"}
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,mBAAmB,EAGpB,MAAM,wCAAwC,CAAA;AAE/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAO1C,UAAU,mBAAmB;IAC3B,MAAM,EAAE,QAAQ,CAAA;IAChB,aAAa,CAAC,EAAE,mBAAmB,CAAA;IACnC,WAAW,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAA;IAC5C,eAAe,CAAC,EAAE,gBAAgB,CAAC,iBAAiB,CAAC,CAAA;IACrD,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,wBAAgB,eAAe,CAAC,EAC9B,MAAM,EACN,aAAa,EACb,WAAW,EACX,eAAe,EACf,oBAA4B,GAC7B,EAAE,mBAAmB,GAAG,OAAO,CAuF/B"}
package/dist/adapter.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { deleteFile } from './deleteFile.js';
2
2
  import { getFile } from './getFile.js';
3
- import { defaultR2ClientUploadsAccess, getHandleMultiPartUpload } from './handleMultiPartUpload.js';
3
+ import { getHandleMultiPartUpload } from './handleMultiPartUpload.js';
4
4
  import { uploadFile } from './uploadFile.js';
5
- export function createR2Adapter({ bucket, clientUploads, collections, useCompositePrefixes = false }) {
6
- const access = typeof clientUploads === 'object' && clientUploads.access ? clientUploads.access : defaultR2ClientUploadsAccess;
5
+ export function createR2Adapter({ bucket, clientUploads, collections, copyCredentials, useCompositePrefixes = false }) {
6
+ const access = typeof clientUploads === 'object' ? clientUploads.access : undefined;
7
7
  const uploadInstructions = {
8
8
  adminHandler: {
9
9
  path: '@payloadcms/storage-r2/client#R2ClientUploadHandler'
@@ -28,34 +28,60 @@ export function createR2Adapter({ bucket, clientUploads, collections, useComposi
28
28
  uploadReference: {}
29
29
  }
30
30
  }),
31
+ requiresUploadReceipt: true,
31
32
  useInAdmin: true
32
33
  };
34
+ const deleteStoredFile = ({ storageFilePath })=>deleteFile({
35
+ bucket,
36
+ storageFilePath
37
+ });
33
38
  return ({ collection, prefix = '' })=>({
34
39
  name: 'r2',
35
40
  uploadInstructions,
36
- handleDelete: ({ doc: { prefix: docPrefix = '' }, filename })=>deleteFile({
37
- bucket,
38
- collectionPrefix: prefix,
39
- docPrefix,
40
- filename,
41
- useCompositePrefixes
42
- }),
43
- handleUpload: ({ data, file })=>uploadFile({
41
+ copyFile: async ({ from, req, to })=>{
42
+ if (!copyCredentials) {
43
+ throw new Error('R2 file copy requires S3 API credentials');
44
+ }
45
+ const [{ S3 }, { copyS3File }] = await Promise.all([
46
+ import('@aws-sdk/client-s3'),
47
+ import('@payloadcms/storage-s3/copy-file')
48
+ ]);
49
+ const client = new S3({
50
+ credentials: {
51
+ accessKeyId: copyCredentials.accessKeyId,
52
+ secretAccessKey: copyCredentials.secretAccessKey
53
+ },
54
+ endpoint: `https://${copyCredentials.accountId}.r2.cloudflarestorage.com`,
55
+ region: 'auto'
56
+ });
57
+ try {
58
+ await copyS3File({
59
+ bucket: copyCredentials.bucket,
60
+ client,
61
+ from,
62
+ req,
63
+ to
64
+ });
65
+ } finally{
66
+ client.destroy();
67
+ }
68
+ },
69
+ deleteFile: deleteStoredFile,
70
+ handleDelete: deleteStoredFile,
71
+ handleUpload: ({ file, storageFilePath })=>uploadFile({
44
72
  bucket,
45
73
  buffer: file.buffer,
46
- collectionPrefix: prefix,
47
- docPrefix: data.prefix,
48
- filename: file.filename,
49
74
  mimeType: file.mimeType,
50
- useCompositePrefixes
75
+ storageFilePath
51
76
  }),
52
- staticHandler: (req, { headers, params: { filename, prefix: prefixQueryParam, uploadReference } })=>getFile({
77
+ staticHandler: (req, { doc, headers, params: { filename, operation, uploadReference } })=>getFile({
53
78
  bucket,
54
79
  collection,
80
+ doc,
55
81
  filename,
56
82
  incomingHeaders: headers,
83
+ operation,
57
84
  prefix,
58
- prefixQueryParam,
59
85
  req,
60
86
  uploadReference,
61
87
  useCompositePrefixes
@@ -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 type { R2StorageOptions } from './index.js'\nimport type { R2Bucket } from './types.js'\n\nimport { deleteFile } from './deleteFile.js'\nimport { getFile } from './getFile.js'\nimport { defaultR2ClientUploadsAccess, getHandleMultiPartUpload } from './handleMultiPartUpload.js'\nimport { uploadFile } from './uploadFile.js'\n\ninterface CreateR2AdapterArgs {\n bucket: R2Bucket\n clientUploads?: ClientUploadsConfig\n collections: R2StorageOptions['collections']\n useCompositePrefixes?: boolean\n}\n\nexport function createR2Adapter({\n bucket,\n clientUploads,\n collections,\n useCompositePrefixes = false,\n}: CreateR2AdapterArgs): Adapter {\n const access =\n typeof clientUploads === 'object' && clientUploads.access\n ? clientUploads.access\n : defaultR2ClientUploadsAccess\n const uploadInstructions: GeneratedAdapter['uploadInstructions'] = {\n adminHandler: {\n path: '@payloadcms/storage-r2/client#R2ClientUploadHandler',\n },\n enabled: Boolean(clientUploads),\n endpoint: {\n handler: getHandleMultiPartUpload({\n access,\n bucket,\n collections,\n useCompositePrefixes,\n }),\n path: '/storage-r2-multi-part-upload',\n },\n generate: ({ filename, filesize, mimeType }) => ({\n name: 'uploadToR2',\n type: 'dispatch',\n file: {\n filename,\n mimeType,\n size: filesize,\n uploadReference: {},\n },\n }),\n useInAdmin: true,\n }\n\n return ({ collection, prefix = '' }): GeneratedAdapter => ({\n name: 'r2',\n uploadInstructions,\n\n handleDelete: ({ doc: { prefix: docPrefix = '' }, filename }) =>\n deleteFile({\n bucket,\n collectionPrefix: prefix,\n docPrefix,\n filename,\n useCompositePrefixes,\n }),\n\n handleUpload: ({ data, file }) =>\n uploadFile({\n bucket,\n buffer: file.buffer,\n collectionPrefix: prefix,\n docPrefix: data.prefix,\n filename: file.filename,\n mimeType: file.mimeType,\n useCompositePrefixes,\n }),\n\n staticHandler: (\n req,\n { headers, params: { filename, prefix: prefixQueryParam, uploadReference } },\n ) =>\n getFile({\n bucket,\n collection,\n filename,\n incomingHeaders: headers,\n prefix,\n prefixQueryParam,\n req,\n uploadReference,\n useCompositePrefixes,\n }),\n })\n}\n"],"names":["deleteFile","getFile","defaultR2ClientUploadsAccess","getHandleMultiPartUpload","uploadFile","createR2Adapter","bucket","clientUploads","collections","useCompositePrefixes","access","uploadInstructions","adminHandler","path","enabled","Boolean","endpoint","handler","generate","filename","filesize","mimeType","name","type","file","size","uploadReference","useInAdmin","collection","prefix","handleDelete","doc","docPrefix","collectionPrefix","handleUpload","data","buffer","staticHandler","req","headers","params","prefixQueryParam","incomingHeaders"],"mappings":"AASA,SAASA,UAAU,QAAQ,kBAAiB;AAC5C,SAASC,OAAO,QAAQ,eAAc;AACtC,SAASC,4BAA4B,EAAEC,wBAAwB,QAAQ,6BAA4B;AACnG,SAASC,UAAU,QAAQ,kBAAiB;AAS5C,OAAO,SAASC,gBAAgB,EAC9BC,MAAM,EACNC,aAAa,EACbC,WAAW,EACXC,uBAAuB,KAAK,EACR;IACpB,MAAMC,SACJ,OAAOH,kBAAkB,YAAYA,cAAcG,MAAM,GACrDH,cAAcG,MAAM,GACpBR;IACN,MAAMS,qBAA6D;QACjEC,cAAc;YACZC,MAAM;QACR;QACAC,SAASC,QAAQR;QACjBS,UAAU;YACRC,SAASd,yBAAyB;gBAChCO;gBACAJ;gBACAE;gBACAC;YACF;YACAI,MAAM;QACR;QACAK,UAAU,CAAC,EAAEC,QAAQ,EAAEC,QAAQ,EAAEC,QAAQ,EAAE,GAAM,CAAA;gBAC/CC,MAAM;gBACNC,MAAM;gBACNC,MAAM;oBACJL;oBACAE;oBACAI,MAAML;oBACNM,iBAAiB,CAAC;gBACpB;YACF,CAAA;QACAC,YAAY;IACd;IAEA,OAAO,CAAC,EAAEC,UAAU,EAAEC,SAAS,EAAE,EAAE,GAAwB,CAAA;YACzDP,MAAM;YACNX;YAEAmB,cAAc,CAAC,EAAEC,KAAK,EAAEF,QAAQG,YAAY,EAAE,EAAE,EAAEb,QAAQ,EAAE,GAC1DnB,WAAW;oBACTM;oBACA2B,kBAAkBJ;oBAClBG;oBACAb;oBACAV;gBACF;YAEFyB,cAAc,CAAC,EAAEC,IAAI,EAAEX,IAAI,EAAE,GAC3BpB,WAAW;oBACTE;oBACA8B,QAAQZ,KAAKY,MAAM;oBACnBH,kBAAkBJ;oBAClBG,WAAWG,KAAKN,MAAM;oBACtBV,UAAUK,KAAKL,QAAQ;oBACvBE,UAAUG,KAAKH,QAAQ;oBACvBZ;gBACF;YAEF4B,eAAe,CACbC,KACA,EAAEC,OAAO,EAAEC,QAAQ,EAAErB,QAAQ,EAAEU,QAAQY,gBAAgB,EAAEf,eAAe,EAAE,EAAE,GAE5EzB,QAAQ;oBACNK;oBACAsB;oBACAT;oBACAuB,iBAAiBH;oBACjBV;oBACAY;oBACAH;oBACAZ;oBACAjB;gBACF;QACJ,CAAA;AACF"}
1
+ {"version":3,"sources":["../src/adapter.ts"],"sourcesContent":["import type {\n Adapter,\n ClientUploadsConfig,\n DeleteFile,\n GeneratedAdapter,\n} from '@payloadcms/plugin-cloud-storage/types'\n\nimport type { R2StorageOptions } from './index.js'\nimport type { R2Bucket } from './types.js'\n\nimport { deleteFile } from './deleteFile.js'\nimport { getFile } from './getFile.js'\nimport { getHandleMultiPartUpload } from './handleMultiPartUpload.js'\nimport { uploadFile } from './uploadFile.js'\n\ninterface CreateR2AdapterArgs {\n bucket: R2Bucket\n clientUploads?: ClientUploadsConfig\n collections: R2StorageOptions['collections']\n copyCredentials?: R2StorageOptions['copyCredentials']\n useCompositePrefixes?: boolean\n}\n\nexport function createR2Adapter({\n bucket,\n clientUploads,\n collections,\n copyCredentials,\n useCompositePrefixes = false,\n}: CreateR2AdapterArgs): Adapter {\n const access = typeof clientUploads === 'object' ? clientUploads.access : undefined\n const uploadInstructions: GeneratedAdapter['uploadInstructions'] = {\n adminHandler: {\n path: '@payloadcms/storage-r2/client#R2ClientUploadHandler',\n },\n enabled: Boolean(clientUploads),\n endpoint: {\n handler: getHandleMultiPartUpload({\n access,\n bucket,\n collections,\n useCompositePrefixes,\n }),\n path: '/storage-r2-multi-part-upload',\n },\n generate: ({ filename, filesize, mimeType }) => ({\n name: 'uploadToR2',\n type: 'dispatch',\n file: {\n filename,\n mimeType,\n size: filesize,\n uploadReference: {},\n },\n }),\n requiresUploadReceipt: true,\n useInAdmin: true,\n }\n\n const deleteStoredFile: DeleteFile = ({ storageFilePath }) =>\n deleteFile({ bucket, storageFilePath })\n\n return ({ collection, prefix = '' }): GeneratedAdapter => ({\n name: 'r2',\n uploadInstructions,\n\n copyFile: async ({ from, req, to }) => {\n if (!copyCredentials) {\n throw new Error('R2 file copy requires S3 API credentials')\n }\n\n const [{ S3 }, { copyS3File }] = await Promise.all([\n import('@aws-sdk/client-s3'),\n import('@payloadcms/storage-s3/copy-file'),\n ])\n const client = new S3({\n credentials: {\n accessKeyId: copyCredentials.accessKeyId,\n secretAccessKey: copyCredentials.secretAccessKey,\n },\n endpoint: `https://${copyCredentials.accountId}.r2.cloudflarestorage.com`,\n region: 'auto',\n })\n\n try {\n await copyS3File({ bucket: copyCredentials.bucket, client, from, req, to })\n } finally {\n client.destroy()\n }\n },\n\n deleteFile: deleteStoredFile,\n handleDelete: deleteStoredFile,\n\n handleUpload: ({ file, storageFilePath }) =>\n uploadFile({\n bucket,\n buffer: file.buffer,\n mimeType: file.mimeType,\n storageFilePath,\n }),\n\n staticHandler: (req, { doc, headers, params: { filename, operation, uploadReference } }) =>\n getFile({\n bucket,\n collection,\n doc,\n filename,\n incomingHeaders: headers,\n operation,\n prefix,\n req,\n uploadReference,\n useCompositePrefixes,\n }),\n })\n}\n"],"names":["deleteFile","getFile","getHandleMultiPartUpload","uploadFile","createR2Adapter","bucket","clientUploads","collections","copyCredentials","useCompositePrefixes","access","undefined","uploadInstructions","adminHandler","path","enabled","Boolean","endpoint","handler","generate","filename","filesize","mimeType","name","type","file","size","uploadReference","requiresUploadReceipt","useInAdmin","deleteStoredFile","storageFilePath","collection","prefix","copyFile","from","req","to","Error","S3","copyS3File","Promise","all","client","credentials","accessKeyId","secretAccessKey","accountId","region","destroy","handleDelete","handleUpload","buffer","staticHandler","doc","headers","params","operation","incomingHeaders"],"mappings":"AAUA,SAASA,UAAU,QAAQ,kBAAiB;AAC5C,SAASC,OAAO,QAAQ,eAAc;AACtC,SAASC,wBAAwB,QAAQ,6BAA4B;AACrE,SAASC,UAAU,QAAQ,kBAAiB;AAU5C,OAAO,SAASC,gBAAgB,EAC9BC,MAAM,EACNC,aAAa,EACbC,WAAW,EACXC,eAAe,EACfC,uBAAuB,KAAK,EACR;IACpB,MAAMC,SAAS,OAAOJ,kBAAkB,WAAWA,cAAcI,MAAM,GAAGC;IAC1E,MAAMC,qBAA6D;QACjEC,cAAc;YACZC,MAAM;QACR;QACAC,SAASC,QAAQV;QACjBW,UAAU;YACRC,SAAShB,yBAAyB;gBAChCQ;gBACAL;gBACAE;gBACAE;YACF;YACAK,MAAM;QACR;QACAK,UAAU,CAAC,EAAEC,QAAQ,EAAEC,QAAQ,EAAEC,QAAQ,EAAE,GAAM,CAAA;gBAC/CC,MAAM;gBACNC,MAAM;gBACNC,MAAM;oBACJL;oBACAE;oBACAI,MAAML;oBACNM,iBAAiB,CAAC;gBACpB;YACF,CAAA;QACAC,uBAAuB;QACvBC,YAAY;IACd;IAEA,MAAMC,mBAA+B,CAAC,EAAEC,eAAe,EAAE,GACvD/B,WAAW;YAAEK;YAAQ0B;QAAgB;IAEvC,OAAO,CAAC,EAAEC,UAAU,EAAEC,SAAS,EAAE,EAAE,GAAwB,CAAA;YACzDV,MAAM;YACNX;YAEAsB,UAAU,OAAO,EAAEC,IAAI,EAAEC,GAAG,EAAEC,EAAE,EAAE;gBAChC,IAAI,CAAC7B,iBAAiB;oBACpB,MAAM,IAAI8B,MAAM;gBAClB;gBAEA,MAAM,CAAC,EAAEC,EAAE,EAAE,EAAE,EAAEC,UAAU,EAAE,CAAC,GAAG,MAAMC,QAAQC,GAAG,CAAC;oBACjD,MAAM,CAAC;oBACP,MAAM,CAAC;iBACR;gBACD,MAAMC,SAAS,IAAIJ,GAAG;oBACpBK,aAAa;wBACXC,aAAarC,gBAAgBqC,WAAW;wBACxCC,iBAAiBtC,gBAAgBsC,eAAe;oBAClD;oBACA7B,UAAU,CAAC,QAAQ,EAAET,gBAAgBuC,SAAS,CAAC,yBAAyB,CAAC;oBACzEC,QAAQ;gBACV;gBAEA,IAAI;oBACF,MAAMR,WAAW;wBAAEnC,QAAQG,gBAAgBH,MAAM;wBAAEsC;wBAAQR;wBAAMC;wBAAKC;oBAAG;gBAC3E,SAAU;oBACRM,OAAOM,OAAO;gBAChB;YACF;YAEAjD,YAAY8B;YACZoB,cAAcpB;YAEdqB,cAAc,CAAC,EAAE1B,IAAI,EAAEM,eAAe,EAAE,GACtC5B,WAAW;oBACTE;oBACA+C,QAAQ3B,KAAK2B,MAAM;oBACnB9B,UAAUG,KAAKH,QAAQ;oBACvBS;gBACF;YAEFsB,eAAe,CAACjB,KAAK,EAAEkB,GAAG,EAAEC,OAAO,EAAEC,QAAQ,EAAEpC,QAAQ,EAAEqC,SAAS,EAAE9B,eAAe,EAAE,EAAE,GACrF1B,QAAQ;oBACNI;oBACA2B;oBACAsB;oBACAlC;oBACAsC,iBAAiBH;oBACjBE;oBACAxB;oBACAG;oBACAT;oBACAlB;gBACF;QACJ,CAAA;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"R2ClientUploadHandler.d.ts","sourceRoot":"","sources":["../../src/client/R2ClientUploadHandler.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAEV,kCAAkC,EAInC,MAAM,aAAa,CAAA;AAEpB,eAAO,MAAM,qBAAqB;;;;;;iCA0FhC,CAAA"}
1
+ {"version":3,"file":"R2ClientUploadHandler.d.ts","sourceRoot":"","sources":["../../src/client/R2ClientUploadHandler.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAEV,kCAAkC,EAInC,MAAM,aAAa,CAAA;AAEpB,eAAO,MAAM,qBAAqB;;;;;;iCAqFhC,CAAA"}
@@ -1,17 +1,12 @@
1
1
  'use client';
2
- import { createClientUploadHandler, getFileKey } from '@payloadcms/plugin-cloud-storage/client';
2
+ import { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client';
3
3
  import { formatAdminURL } from 'payload/shared';
4
4
  export const R2ClientUploadHandler = createClientUploadHandler({
5
5
  name: 'uploadToR2',
6
- handler: async ({ apiRoute, collectionSlug, docPrefix, endpointPath, file, prefix, props: { chunkSize = 5 * 1024 * 1024 }, serverURL, updateFilename })=>{
7
- const { sanitizedDocPrefix } = getFileKey({
8
- collectionPrefix: prefix,
9
- docPrefix,
10
- filename: file.name
11
- });
6
+ handler: async ({ apiRoute, collectionSlug, docPrefix, endpointPath, file, props: { chunkSize = 5 * 1024 * 1024 }, serverURL, updateFilename })=>{
12
7
  const params = {
13
8
  collection: collectionSlug,
14
- docPrefix: sanitizedDocPrefix,
9
+ docPrefix,
15
10
  fileName: file.name,
16
11
  fileType: file.type
17
12
  };
@@ -28,13 +23,14 @@ export const R2ClientUploadHandler = createClientUploadHandler({
28
23
  if (!multipart.ok) {
29
24
  throw new Error('Failed to initialize multipart upload');
30
25
  }
31
- const { filename: sanitizedFilename, ...multipartUpload } = await multipart.json();
26
+ const { filename: sanitizedFilename, uploadReference, ...multipartUpload } = await multipart.json();
32
27
  if (sanitizedFilename && sanitizedFilename !== file.name) {
33
28
  updateFilename(sanitizedFilename);
34
29
  }
35
30
  const multipartUploadedParts = [];
36
31
  params.multipartId = multipartUpload.uploadId;
37
32
  params.multipartKey = multipartUpload.key;
33
+ params.signedReceipt = uploadReference.signedReceipt;
38
34
  const partTotal = Math.ceil(file.size / chunkSize);
39
35
  for(let part = 1; part <= partTotal; part++){
40
36
  const bytesEnd = Math.min(part * chunkSize, file.size);
@@ -68,10 +64,9 @@ export const R2ClientUploadHandler = createClientUploadHandler({
68
64
  if (!complete.ok) {
69
65
  throw new Error(`Failed to complete multipart upload`);
70
66
  }
71
- const key = await complete.text();
72
67
  return {
73
- key,
74
- prefix: sanitizedDocPrefix
68
+ prefix: uploadReference.prefix,
69
+ signedReceipt: uploadReference.signedReceipt
75
70
  };
76
71
  }
77
72
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/client/R2ClientUploadHandler.ts"],"sourcesContent":["'use client'\n\nimport { createClientUploadHandler, getFileKey } from '@payloadcms/plugin-cloud-storage/client'\nimport { formatAdminURL } from 'payload/shared'\n\nimport type {\n R2MultipartUpload,\n R2StorageClientUploadHandlerParams,\n R2StorageMultipartUploadHandlerParams,\n R2StorageUploadReference,\n R2UploadedPart,\n} from '../types.js'\n\nexport const R2ClientUploadHandler = createClientUploadHandler<R2StorageClientUploadHandlerParams>({\n name: 'uploadToR2',\n handler: async ({\n apiRoute,\n collectionSlug,\n docPrefix,\n endpointPath,\n file,\n prefix,\n props: { chunkSize = 5 * 1024 * 1024 },\n serverURL,\n updateFilename,\n }): Promise<R2StorageUploadReference | undefined> => {\n const { sanitizedDocPrefix } = getFileKey({\n collectionPrefix: prefix,\n docPrefix,\n filename: file.name,\n })\n\n const params: R2StorageMultipartUploadHandlerParams = {\n collection: collectionSlug,\n docPrefix: sanitizedDocPrefix,\n fileName: file.name,\n fileType: file.type,\n }\n const baseURL = formatAdminURL({\n apiRoute,\n path: endpointPath,\n serverURL,\n })\n\n const getEndpoint = () => `${baseURL}?${String(new URLSearchParams(params))}`\n\n // Initialize the multipart upload.\n const multipart = await fetch(getEndpoint(), { method: 'POST' })\n if (!multipart.ok) {\n throw new Error('Failed to initialize multipart upload')\n }\n\n const { filename: sanitizedFilename, ...multipartUpload } = (await multipart.json()) as {\n filename?: string\n } & Pick<R2MultipartUpload, 'key' | 'uploadId'>\n\n if (sanitizedFilename && sanitizedFilename !== file.name) {\n updateFilename(sanitizedFilename)\n }\n\n const multipartUploadedParts: R2UploadedPart[] = []\n\n params.multipartId = multipartUpload.uploadId\n params.multipartKey = multipartUpload.key\n\n const partTotal = Math.ceil(file.size / chunkSize)\n\n for (let part = 1; part <= partTotal; part++) {\n const bytesEnd = Math.min(part * chunkSize, file.size)\n const bytesStart = (part - 1) * chunkSize\n\n params.multipartNumber = String(part)\n\n const body = file.slice(bytesStart, bytesEnd)\n const headers = {\n 'Content-Length': String(body.size),\n 'Content-Type': 'application/octet-stream',\n }\n const uploaded = await fetch(getEndpoint(), { body, headers, method: 'POST' })\n if (!uploaded.ok) {\n throw new Error(`Failed to upload part ${part} / ${partTotal}`)\n }\n\n multipartUploadedParts.push((await uploaded.json()) as R2UploadedPart)\n\n if (part === partTotal) {\n delete params.multipartNumber\n\n const body = JSON.stringify(multipartUploadedParts)\n const headers = { 'Content-Type': 'application/json' }\n const complete = await fetch(getEndpoint(), { body, headers, method: 'POST' })\n if (!complete.ok) {\n throw new Error(`Failed to complete multipart upload`)\n }\n\n const key = await complete.text()\n return {\n key,\n prefix: sanitizedDocPrefix,\n }\n }\n }\n },\n})\n"],"names":["createClientUploadHandler","getFileKey","formatAdminURL","R2ClientUploadHandler","name","handler","apiRoute","collectionSlug","docPrefix","endpointPath","file","prefix","props","chunkSize","serverURL","updateFilename","sanitizedDocPrefix","collectionPrefix","filename","params","collection","fileName","fileType","type","baseURL","path","getEndpoint","String","URLSearchParams","multipart","fetch","method","ok","Error","sanitizedFilename","multipartUpload","json","multipartUploadedParts","multipartId","uploadId","multipartKey","key","partTotal","Math","ceil","size","part","bytesEnd","min","bytesStart","multipartNumber","body","slice","headers","uploaded","push","JSON","stringify","complete","text"],"mappings":"AAAA;AAEA,SAASA,yBAAyB,EAAEC,UAAU,QAAQ,0CAAyC;AAC/F,SAASC,cAAc,QAAQ,iBAAgB;AAU/C,OAAO,MAAMC,wBAAwBH,0BAA8D;IACjGI,MAAM;IACNC,SAAS,OAAO,EACdC,QAAQ,EACRC,cAAc,EACdC,SAAS,EACTC,YAAY,EACZC,IAAI,EACJC,MAAM,EACNC,OAAO,EAAEC,YAAY,IAAI,OAAO,IAAI,EAAE,EACtCC,SAAS,EACTC,cAAc,EACf;QACC,MAAM,EAAEC,kBAAkB,EAAE,GAAGf,WAAW;YACxCgB,kBAAkBN;YAClBH;YACAU,UAAUR,KAAKN,IAAI;QACrB;QAEA,MAAMe,SAAgD;YACpDC,YAAYb;YACZC,WAAWQ;YACXK,UAAUX,KAAKN,IAAI;YACnBkB,UAAUZ,KAAKa,IAAI;QACrB;QACA,MAAMC,UAAUtB,eAAe;YAC7BI;YACAmB,MAAMhB;YACNK;QACF;QAEA,MAAMY,cAAc,IAAM,GAAGF,QAAQ,CAAC,EAAEG,OAAO,IAAIC,gBAAgBT,UAAU;QAE7E,mCAAmC;QACnC,MAAMU,YAAY,MAAMC,MAAMJ,eAAe;YAAEK,QAAQ;QAAO;QAC9D,IAAI,CAACF,UAAUG,EAAE,EAAE;YACjB,MAAM,IAAIC,MAAM;QAClB;QAEA,MAAM,EAAEf,UAAUgB,iBAAiB,EAAE,GAAGC,iBAAiB,GAAI,MAAMN,UAAUO,IAAI;QAIjF,IAAIF,qBAAqBA,sBAAsBxB,KAAKN,IAAI,EAAE;YACxDW,eAAemB;QACjB;QAEA,MAAMG,yBAA2C,EAAE;QAEnDlB,OAAOmB,WAAW,GAAGH,gBAAgBI,QAAQ;QAC7CpB,OAAOqB,YAAY,GAAGL,gBAAgBM,GAAG;QAEzC,MAAMC,YAAYC,KAAKC,IAAI,CAAClC,KAAKmC,IAAI,GAAGhC;QAExC,IAAK,IAAIiC,OAAO,GAAGA,QAAQJ,WAAWI,OAAQ;YAC5C,MAAMC,WAAWJ,KAAKK,GAAG,CAACF,OAAOjC,WAAWH,KAAKmC,IAAI;YACrD,MAAMI,aAAa,AAACH,CAAAA,OAAO,CAAA,IAAKjC;YAEhCM,OAAO+B,eAAe,GAAGvB,OAAOmB;YAEhC,MAAMK,OAAOzC,KAAK0C,KAAK,CAACH,YAAYF;YACpC,MAAMM,UAAU;gBACd,kBAAkB1B,OAAOwB,KAAKN,IAAI;gBAClC,gBAAgB;YAClB;YACA,MAAMS,WAAW,MAAMxB,MAAMJ,eAAe;gBAAEyB;gBAAME;gBAAStB,QAAQ;YAAO;YAC5E,IAAI,CAACuB,SAAStB,EAAE,EAAE;gBAChB,MAAM,IAAIC,MAAM,CAAC,sBAAsB,EAAEa,KAAK,GAAG,EAAEJ,WAAW;YAChE;YAEAL,uBAAuBkB,IAAI,CAAE,MAAMD,SAASlB,IAAI;YAEhD,IAAIU,SAASJ,WAAW;gBACtB,OAAOvB,OAAO+B,eAAe;gBAE7B,MAAMC,OAAOK,KAAKC,SAAS,CAACpB;gBAC5B,MAAMgB,UAAU;oBAAE,gBAAgB;gBAAmB;gBACrD,MAAMK,WAAW,MAAM5B,MAAMJ,eAAe;oBAAEyB;oBAAME;oBAAStB,QAAQ;gBAAO;gBAC5E,IAAI,CAAC2B,SAAS1B,EAAE,EAAE;oBAChB,MAAM,IAAIC,MAAM,CAAC,mCAAmC,CAAC;gBACvD;gBAEA,MAAMQ,MAAM,MAAMiB,SAASC,IAAI;gBAC/B,OAAO;oBACLlB;oBACA9B,QAAQK;gBACV;YACF;QACF;IACF;AACF,GAAE"}
1
+ {"version":3,"sources":["../../src/client/R2ClientUploadHandler.ts"],"sourcesContent":["'use client'\n\nimport { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client'\nimport { formatAdminURL } from 'payload/shared'\n\nimport type {\n R2MultipartUpload,\n R2StorageClientUploadHandlerParams,\n R2StorageMultipartUploadHandlerParams,\n R2StorageUploadReference,\n R2UploadedPart,\n} from '../types.js'\n\nexport const R2ClientUploadHandler = createClientUploadHandler<R2StorageClientUploadHandlerParams>({\n name: 'uploadToR2',\n handler: async ({\n apiRoute,\n collectionSlug,\n docPrefix,\n endpointPath,\n file,\n props: { chunkSize = 5 * 1024 * 1024 },\n serverURL,\n updateFilename,\n }): Promise<R2StorageUploadReference | undefined> => {\n const params: R2StorageMultipartUploadHandlerParams = {\n collection: collectionSlug,\n docPrefix,\n fileName: file.name,\n fileType: file.type,\n }\n const baseURL = formatAdminURL({\n apiRoute,\n path: endpointPath,\n serverURL,\n })\n\n const getEndpoint = () => `${baseURL}?${String(new URLSearchParams(params))}`\n\n // Initialize the multipart upload.\n const multipart = await fetch(getEndpoint(), { method: 'POST' })\n if (!multipart.ok) {\n throw new Error('Failed to initialize multipart upload')\n }\n\n const {\n filename: sanitizedFilename,\n uploadReference,\n ...multipartUpload\n } = (await multipart.json()) as {\n filename?: string\n uploadReference: R2StorageUploadReference\n } & Pick<R2MultipartUpload, 'key' | 'uploadId'>\n\n if (sanitizedFilename && sanitizedFilename !== file.name) {\n updateFilename(sanitizedFilename)\n }\n\n const multipartUploadedParts: R2UploadedPart[] = []\n\n params.multipartId = multipartUpload.uploadId\n params.multipartKey = multipartUpload.key\n params.signedReceipt = uploadReference.signedReceipt\n\n const partTotal = Math.ceil(file.size / chunkSize)\n\n for (let part = 1; part <= partTotal; part++) {\n const bytesEnd = Math.min(part * chunkSize, file.size)\n const bytesStart = (part - 1) * chunkSize\n\n params.multipartNumber = String(part)\n\n const body = file.slice(bytesStart, bytesEnd)\n const headers = {\n 'Content-Length': String(body.size),\n 'Content-Type': 'application/octet-stream',\n }\n const uploaded = await fetch(getEndpoint(), { body, headers, method: 'POST' })\n if (!uploaded.ok) {\n throw new Error(`Failed to upload part ${part} / ${partTotal}`)\n }\n\n multipartUploadedParts.push((await uploaded.json()) as R2UploadedPart)\n\n if (part === partTotal) {\n delete params.multipartNumber\n\n const body = JSON.stringify(multipartUploadedParts)\n const headers = { 'Content-Type': 'application/json' }\n const complete = await fetch(getEndpoint(), { body, headers, method: 'POST' })\n if (!complete.ok) {\n throw new Error(`Failed to complete multipart upload`)\n }\n\n return { prefix: uploadReference.prefix, signedReceipt: uploadReference.signedReceipt }\n }\n }\n },\n})\n"],"names":["createClientUploadHandler","formatAdminURL","R2ClientUploadHandler","name","handler","apiRoute","collectionSlug","docPrefix","endpointPath","file","props","chunkSize","serverURL","updateFilename","params","collection","fileName","fileType","type","baseURL","path","getEndpoint","String","URLSearchParams","multipart","fetch","method","ok","Error","filename","sanitizedFilename","uploadReference","multipartUpload","json","multipartUploadedParts","multipartId","uploadId","multipartKey","key","signedReceipt","partTotal","Math","ceil","size","part","bytesEnd","min","bytesStart","multipartNumber","body","slice","headers","uploaded","push","JSON","stringify","complete","prefix"],"mappings":"AAAA;AAEA,SAASA,yBAAyB,QAAQ,0CAAyC;AACnF,SAASC,cAAc,QAAQ,iBAAgB;AAU/C,OAAO,MAAMC,wBAAwBF,0BAA8D;IACjGG,MAAM;IACNC,SAAS,OAAO,EACdC,QAAQ,EACRC,cAAc,EACdC,SAAS,EACTC,YAAY,EACZC,IAAI,EACJC,OAAO,EAAEC,YAAY,IAAI,OAAO,IAAI,EAAE,EACtCC,SAAS,EACTC,cAAc,EACf;QACC,MAAMC,SAAgD;YACpDC,YAAYT;YACZC;YACAS,UAAUP,KAAKN,IAAI;YACnBc,UAAUR,KAAKS,IAAI;QACrB;QACA,MAAMC,UAAUlB,eAAe;YAC7BI;YACAe,MAAMZ;YACNI;QACF;QAEA,MAAMS,cAAc,IAAM,GAAGF,QAAQ,CAAC,EAAEG,OAAO,IAAIC,gBAAgBT,UAAU;QAE7E,mCAAmC;QACnC,MAAMU,YAAY,MAAMC,MAAMJ,eAAe;YAAEK,QAAQ;QAAO;QAC9D,IAAI,CAACF,UAAUG,EAAE,EAAE;YACjB,MAAM,IAAIC,MAAM;QAClB;QAEA,MAAM,EACJC,UAAUC,iBAAiB,EAC3BC,eAAe,EACf,GAAGC,iBACJ,GAAI,MAAMR,UAAUS,IAAI;QAKzB,IAAIH,qBAAqBA,sBAAsBrB,KAAKN,IAAI,EAAE;YACxDU,eAAeiB;QACjB;QAEA,MAAMI,yBAA2C,EAAE;QAEnDpB,OAAOqB,WAAW,GAAGH,gBAAgBI,QAAQ;QAC7CtB,OAAOuB,YAAY,GAAGL,gBAAgBM,GAAG;QACzCxB,OAAOyB,aAAa,GAAGR,gBAAgBQ,aAAa;QAEpD,MAAMC,YAAYC,KAAKC,IAAI,CAACjC,KAAKkC,IAAI,GAAGhC;QAExC,IAAK,IAAIiC,OAAO,GAAGA,QAAQJ,WAAWI,OAAQ;YAC5C,MAAMC,WAAWJ,KAAKK,GAAG,CAACF,OAAOjC,WAAWF,KAAKkC,IAAI;YACrD,MAAMI,aAAa,AAACH,CAAAA,OAAO,CAAA,IAAKjC;YAEhCG,OAAOkC,eAAe,GAAG1B,OAAOsB;YAEhC,MAAMK,OAAOxC,KAAKyC,KAAK,CAACH,YAAYF;YACpC,MAAMM,UAAU;gBACd,kBAAkB7B,OAAO2B,KAAKN,IAAI;gBAClC,gBAAgB;YAClB;YACA,MAAMS,WAAW,MAAM3B,MAAMJ,eAAe;gBAAE4B;gBAAME;gBAASzB,QAAQ;YAAO;YAC5E,IAAI,CAAC0B,SAASzB,EAAE,EAAE;gBAChB,MAAM,IAAIC,MAAM,CAAC,sBAAsB,EAAEgB,KAAK,GAAG,EAAEJ,WAAW;YAChE;YAEAN,uBAAuBmB,IAAI,CAAE,MAAMD,SAASnB,IAAI;YAEhD,IAAIW,SAASJ,WAAW;gBACtB,OAAO1B,OAAOkC,eAAe;gBAE7B,MAAMC,OAAOK,KAAKC,SAAS,CAACrB;gBAC5B,MAAMiB,UAAU;oBAAE,gBAAgB;gBAAmB;gBACrD,MAAMK,WAAW,MAAM/B,MAAMJ,eAAe;oBAAE4B;oBAAME;oBAASzB,QAAQ;gBAAO;gBAC5E,IAAI,CAAC8B,SAAS7B,EAAE,EAAE;oBAChB,MAAM,IAAIC,MAAM,CAAC,mCAAmC,CAAC;gBACvD;gBAEA,OAAO;oBAAE6B,QAAQ1B,gBAAgB0B,MAAM;oBAAElB,eAAeR,gBAAgBQ,aAAa;gBAAC;YACxF;QACF;IACF;AACF,GAAE"}
@@ -1,11 +1,8 @@
1
1
  import type { R2Bucket } from './types.js';
2
2
  interface DeleteFileArgs {
3
3
  bucket: R2Bucket;
4
- collectionPrefix?: string;
5
- docPrefix: string;
6
- filename: string;
7
- useCompositePrefixes?: boolean;
4
+ storageFilePath: string;
8
5
  }
9
- export declare function deleteFile({ bucket, collectionPrefix, docPrefix, filename, useCompositePrefixes, }: DeleteFileArgs): Promise<void>;
6
+ export declare function deleteFile({ bucket, storageFilePath }: 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":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAE1C,UAAU,cAAc;IACtB,MAAM,EAAE,QAAQ,CAAA;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,wBAAsB,UAAU,CAAC,EAC/B,MAAM,EACN,gBAAqB,EACrB,SAAS,EACT,QAAQ,EACR,oBAA4B,GAC7B,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAShC"}
1
+ {"version":3,"file":"deleteFile.d.ts","sourceRoot":"","sources":["../src/deleteFile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAE1C,UAAU,cAAc;IACtB,MAAM,EAAE,QAAQ,CAAA;IAChB,eAAe,EAAE,MAAM,CAAA;CACxB;AAED,wBAAsB,UAAU,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAE3F"}
@@ -1,12 +1,5 @@
1
- import { getFileKey } from '@payloadcms/plugin-cloud-storage/utilities';
2
- export async function deleteFile({ bucket, collectionPrefix = '', docPrefix, filename, useCompositePrefixes = false }) {
3
- const { fileKey } = getFileKey({
4
- collectionPrefix,
5
- docPrefix,
6
- filename,
7
- useCompositePrefixes
8
- });
9
- await bucket.delete(fileKey);
1
+ export async function deleteFile({ bucket, storageFilePath }) {
2
+ await bucket.delete(storageFilePath);
10
3
  }
11
4
 
12
5
  //# sourceMappingURL=deleteFile.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/deleteFile.ts"],"sourcesContent":["import { getFileKey } from '@payloadcms/plugin-cloud-storage/utilities'\n\nimport type { R2Bucket } from './types.js'\n\ninterface DeleteFileArgs {\n bucket: R2Bucket\n collectionPrefix?: string\n docPrefix: string\n filename: string\n useCompositePrefixes?: boolean\n}\n\nexport async function deleteFile({\n bucket,\n collectionPrefix = '',\n docPrefix,\n filename,\n useCompositePrefixes = false,\n}: DeleteFileArgs): Promise<void> {\n const { fileKey } = getFileKey({\n collectionPrefix,\n docPrefix,\n filename,\n useCompositePrefixes,\n })\n\n await bucket.delete(fileKey)\n}\n"],"names":["getFileKey","deleteFile","bucket","collectionPrefix","docPrefix","filename","useCompositePrefixes","fileKey","delete"],"mappings":"AAAA,SAASA,UAAU,QAAQ,6CAA4C;AAYvE,OAAO,eAAeC,WAAW,EAC/BC,MAAM,EACNC,mBAAmB,EAAE,EACrBC,SAAS,EACTC,QAAQ,EACRC,uBAAuB,KAAK,EACb;IACf,MAAM,EAAEC,OAAO,EAAE,GAAGP,WAAW;QAC7BG;QACAC;QACAC;QACAC;IACF;IAEA,MAAMJ,OAAOM,MAAM,CAACD;AACtB"}
1
+ {"version":3,"sources":["../src/deleteFile.ts"],"sourcesContent":["import type { R2Bucket } from './types.js'\n\ninterface DeleteFileArgs {\n bucket: R2Bucket\n storageFilePath: string\n}\n\nexport async function deleteFile({ bucket, storageFilePath }: DeleteFileArgs): Promise<void> {\n await bucket.delete(storageFilePath)\n}\n"],"names":["deleteFile","bucket","storageFilePath","delete"],"mappings":"AAOA,OAAO,eAAeA,WAAW,EAAEC,MAAM,EAAEC,eAAe,EAAkB;IAC1E,MAAMD,OAAOE,MAAM,CAACD;AACtB"}
package/dist/getFile.d.ts CHANGED
@@ -1,16 +1,17 @@
1
- import type { CollectionConfig, PayloadRequest } from 'payload';
1
+ import type { CollectionConfig, FileHandlerOperation, PayloadRequest, TypeWithID } from 'payload';
2
2
  import type { R2Bucket } from './types.js';
3
3
  interface GetFileArgs {
4
4
  bucket: R2Bucket;
5
5
  collection: CollectionConfig;
6
+ doc?: TypeWithID;
6
7
  filename: string;
7
8
  incomingHeaders?: Headers;
9
+ operation?: FileHandlerOperation;
8
10
  prefix: string;
9
- prefixQueryParam?: string;
10
11
  req: PayloadRequest;
11
12
  uploadReference?: unknown;
12
13
  useCompositePrefixes?: boolean;
13
14
  }
14
- export declare function getFile({ bucket, collection, filename, incomingHeaders, prefix, prefixQueryParam, req, uploadReference, useCompositePrefixes, }: GetFileArgs): Promise<Response>;
15
+ export declare function getFile({ bucket, collection, doc, filename, incomingHeaders, operation, prefix, req, uploadReference, useCompositePrefixes, }: GetFileArgs): Promise<Response>;
15
16
  export {};
16
17
  //# sourceMappingURL=getFile.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"getFile.d.ts","sourceRoot":"","sources":["../src/getFile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAQ/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAE1C,UAAU,WAAW;IACnB,MAAM,EAAE,QAAQ,CAAA;IAChB,UAAU,EAAE,gBAAgB,CAAA;IAC5B,QAAQ,EAAE,MAAM,CAAA;IAChB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,MAAM,EAAE,MAAM,CAAA;IACd,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,GAAG,EAAE,cAAc,CAAA;IACnB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAID,wBAAsB,OAAO,CAAC,EAC5B,MAAM,EACN,UAAU,EACV,QAAQ,EACR,eAAe,EACf,MAAW,EACX,gBAAgB,EAChB,GAAG,EACH,eAAe,EACf,oBAA4B,GAC7B,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAsHjC"}
1
+ {"version":3,"file":"getFile.d.ts","sourceRoot":"","sources":["../src/getFile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAQjG,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAE1C,UAAU,WAAW;IACnB,MAAM,EAAE,QAAQ,CAAA;IAChB,UAAU,EAAE,gBAAgB,CAAA;IAC5B,GAAG,CAAC,EAAE,UAAU,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,SAAS,CAAC,EAAE,oBAAoB,CAAA;IAChC,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,EAAE,cAAc,CAAA;IACnB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAID,wBAAsB,OAAO,CAAC,EAC5B,MAAM,EACN,UAAU,EACV,GAAG,EACH,QAAQ,EACR,eAAe,EACf,SAAkB,EAClB,MAAW,EACX,GAAG,EACH,eAAe,EACf,oBAA4B,GAC7B,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAwHjC"}
package/dist/getFile.js CHANGED
@@ -1,23 +1,26 @@
1
- import { getFilePrefix as getDocPrefix, getFileKey } from '@payloadcms/plugin-cloud-storage/utilities';
2
- import { getRangeRequestInfo } from 'payload/internal';
1
+ import { buildStoragePathData, getFilePrefix as getDocPrefix } from '@payloadcms/plugin-cloud-storage/utilities';
2
+ import { getRangeRequestInfo, isXmlMimeType, uploadContentSecurityPolicy } from 'payload/internal';
3
3
  const isMiniflare = process.env.NODE_ENV === 'development';
4
- export async function getFile({ bucket, collection, filename, incomingHeaders, prefix = '', prefixQueryParam, req, uploadReference, useCompositePrefixes = false }) {
4
+ export async function getFile({ bucket, collection, doc, filename, incomingHeaders, operation = 'read', prefix = '', req, uploadReference, useCompositePrefixes = false }) {
5
+ const isTransformSource = operation === 'transform';
5
6
  try {
6
7
  const docPrefix = await getDocPrefix({
7
8
  collection,
9
+ collectionPrefix: prefix,
10
+ doc,
8
11
  filename,
9
- prefixQueryParam,
10
12
  req,
11
- uploadReference
13
+ uploadReference,
14
+ useCompositePrefixes
12
15
  });
13
- const { fileKey } = getFileKey({
16
+ const { storageFilePath } = buildStoragePathData({
14
17
  collectionPrefix: prefix,
15
18
  docPrefix,
16
19
  filename,
17
20
  useCompositePrefixes
18
21
  });
19
22
  // Get file size for range validation
20
- const headObj = await bucket?.head(fileKey);
23
+ const headObj = await bucket?.head(storageFilePath);
21
24
  if (!headObj) {
22
25
  return new Response(null, {
23
26
  status: 404,
@@ -25,14 +28,14 @@ export async function getFile({ bucket, collection, filename, incomingHeaders, p
25
28
  });
26
29
  }
27
30
  const fileSize = headObj.size;
28
- // Don't return large file uploads back to the client, or the Worker will run out of memory
29
- if (fileSize > 50 * 1024 * 1024 && uploadReference) {
31
+ // Don't return large file uploads back to the client, or the Worker will run out of memory.
32
+ // Skipped for `transform`, which needs the real bytes and would otherwise silently corrupt.
33
+ if (fileSize > 50 * 1024 * 1024 && uploadReference && !isTransformSource) {
30
34
  return new Response(null, {
31
35
  status: 200
32
36
  });
33
37
  }
34
- // Handle range request
35
- const rangeHeader = req.headers.get('range');
38
+ const rangeHeader = isTransformSource ? null : req.headers.get('range');
36
39
  const rangeResult = getRangeRequestInfo({
37
40
  fileSize,
38
41
  rangeHeader
@@ -43,15 +46,14 @@ export async function getFile({ bucket, collection, filename, incomingHeaders, p
43
46
  status: rangeResult.status
44
47
  });
45
48
  }
46
- // Get object with range if needed
47
49
  // Due to https://github.com/cloudflare/workers-sdk/issues/6047
48
50
  // We cannot send a Headers instance to Miniflare
49
- const obj = rangeResult.type === 'partial' && !isMiniflare ? await bucket?.get(fileKey, {
51
+ const obj = rangeResult.type === 'partial' && !isMiniflare ? await bucket?.get(storageFilePath, {
50
52
  range: {
51
53
  length: rangeResult.rangeEnd - rangeResult.rangeStart + 1,
52
54
  offset: rangeResult.rangeStart
53
55
  }
54
- }) : await bucket?.get(fileKey);
56
+ }) : await bucket?.get(storageFilePath);
55
57
  if (!obj || obj.body == undefined) {
56
58
  return new Response(null, {
57
59
  status: 404,
@@ -59,13 +61,10 @@ export async function getFile({ bucket, collection, filename, incomingHeaders, p
59
61
  });
60
62
  }
61
63
  let headers = new Headers(incomingHeaders);
62
- // Add range-related headers from the result
63
64
  for (const [headerKey, value] of Object.entries(rangeResult.headers)){
64
65
  headers.append(headerKey, value);
65
66
  }
66
- // Add R2-specific headers
67
67
  if (isMiniflare) {
68
- // In development with Miniflare, manually set headers from httpMetadata
69
68
  const metadata = obj.httpMetadata;
70
69
  if (metadata?.cacheControl) {
71
70
  headers.set('Cache-Control', metadata.cacheControl);
@@ -85,18 +84,18 @@ export async function getFile({ bucket, collection, filename, incomingHeaders, p
85
84
  } else {
86
85
  obj.writeHttpMetadata(headers);
87
86
  }
88
- // Add Content-Security-Policy header for SVG files to prevent executable code
89
87
  const contentType = headers.get('Content-Type');
90
- if (contentType === 'image/svg+xml') {
91
- headers.set('Content-Security-Policy', "script-src 'none'");
88
+ // Apply a restrictive policy to XML-family responses served through Payload.
89
+ if (isXmlMimeType(contentType)) {
90
+ headers.set('Content-Security-Policy', uploadContentSecurityPolicy);
92
91
  }
93
92
  const etagFromHeaders = req.headers.get('etag') || req.headers.get('if-none-match');
94
- if (collection.upload && typeof collection.upload === 'object' && typeof collection.upload.modifyResponseHeaders === 'function') {
93
+ if (!isTransformSource && collection.upload && typeof collection.upload === 'object' && typeof collection.upload.modifyResponseHeaders === 'function') {
95
94
  headers = collection.upload.modifyResponseHeaders({
96
95
  headers
97
96
  }) || headers;
98
97
  }
99
- if (etagFromHeaders && etagFromHeaders === obj.etag) {
98
+ if (!isTransformSource && etagFromHeaders && etagFromHeaders === obj.etag) {
100
99
  return new Response(null, {
101
100
  headers,
102
101
  status: 304
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/getFile.ts"],"sourcesContent":["import type { CollectionConfig, PayloadRequest } from 'payload'\n\nimport {\n getFilePrefix as getDocPrefix,\n getFileKey,\n} from '@payloadcms/plugin-cloud-storage/utilities'\nimport { getRangeRequestInfo } from 'payload/internal'\n\nimport type { R2Bucket } from './types.js'\n\ninterface GetFileArgs {\n bucket: R2Bucket\n collection: CollectionConfig\n filename: string\n incomingHeaders?: Headers\n prefix: string\n prefixQueryParam?: string\n req: PayloadRequest\n uploadReference?: unknown\n useCompositePrefixes?: boolean\n}\n\nconst isMiniflare = process.env.NODE_ENV === 'development'\n\nexport async function getFile({\n bucket,\n collection,\n filename,\n incomingHeaders,\n prefix = '',\n prefixQueryParam,\n req,\n uploadReference,\n useCompositePrefixes = false,\n}: GetFileArgs): Promise<Response> {\n try {\n const docPrefix = await getDocPrefix({\n collection,\n filename,\n prefixQueryParam,\n req,\n uploadReference,\n })\n\n const { fileKey } = getFileKey({\n collectionPrefix: prefix,\n docPrefix,\n filename,\n useCompositePrefixes,\n })\n\n // Get file size for range validation\n const headObj = await bucket?.head(fileKey)\n if (!headObj) {\n return new Response(null, { status: 404, statusText: 'Not Found' })\n }\n\n const fileSize = headObj.size\n\n // Don't return large file uploads back to the client, or the Worker will run out of memory\n if (fileSize > 50 * 1024 * 1024 && uploadReference) {\n return new Response(null, { status: 200 })\n }\n\n // Handle range request\n const rangeHeader = req.headers.get('range')\n const rangeResult = getRangeRequestInfo({ fileSize, rangeHeader })\n\n if (rangeResult.type === 'invalid') {\n return new Response(null, {\n headers: new Headers(rangeResult.headers),\n status: rangeResult.status,\n })\n }\n\n // Get object with range if needed\n // Due to https://github.com/cloudflare/workers-sdk/issues/6047\n // We cannot send a Headers instance to Miniflare\n const obj =\n rangeResult.type === 'partial' && !isMiniflare\n ? await bucket?.get(fileKey, {\n range: {\n length: rangeResult.rangeEnd - rangeResult.rangeStart + 1,\n offset: rangeResult.rangeStart,\n },\n })\n : await bucket?.get(fileKey)\n\n if (!obj || obj.body == undefined) {\n return new Response(null, { status: 404, statusText: 'Not Found' })\n }\n\n let headers = new Headers(incomingHeaders)\n\n // Add range-related headers from the result\n for (const [headerKey, value] of Object.entries(rangeResult.headers)) {\n headers.append(headerKey, value)\n }\n\n // Add R2-specific headers\n if (isMiniflare) {\n // In development with Miniflare, manually set headers from httpMetadata\n const metadata = obj.httpMetadata\n if (metadata?.cacheControl) {\n headers.set('Cache-Control', metadata.cacheControl)\n }\n if (metadata?.contentDisposition) {\n headers.set('Content-Disposition', metadata.contentDisposition)\n }\n if (metadata?.contentEncoding) {\n headers.set('Content-Encoding', metadata.contentEncoding)\n }\n if (metadata?.contentLanguage) {\n headers.set('Content-Language', metadata.contentLanguage)\n }\n if (metadata?.contentType) {\n headers.set('Content-Type', metadata.contentType)\n }\n } else {\n obj.writeHttpMetadata(headers)\n }\n\n // Add Content-Security-Policy header for SVG files to prevent executable code\n const contentType = headers.get('Content-Type')\n if (contentType === 'image/svg+xml') {\n headers.set('Content-Security-Policy', \"script-src 'none'\")\n }\n\n const etagFromHeaders = req.headers.get('etag') || req.headers.get('if-none-match')\n\n if (\n collection.upload &&\n typeof collection.upload === 'object' &&\n typeof collection.upload.modifyResponseHeaders === 'function'\n ) {\n headers = collection.upload.modifyResponseHeaders({ headers }) || headers\n }\n\n if (etagFromHeaders && etagFromHeaders === obj.etag) {\n return new Response(null, {\n headers,\n status: 304,\n })\n }\n\n return new Response(obj.body, {\n headers,\n status: rangeResult.status,\n })\n } catch (_err: unknown) {\n return new Response('Internal Server Error', { status: 500 })\n }\n}\n"],"names":["getFilePrefix","getDocPrefix","getFileKey","getRangeRequestInfo","isMiniflare","process","env","NODE_ENV","getFile","bucket","collection","filename","incomingHeaders","prefix","prefixQueryParam","req","uploadReference","useCompositePrefixes","docPrefix","fileKey","collectionPrefix","headObj","head","Response","status","statusText","fileSize","size","rangeHeader","headers","get","rangeResult","type","Headers","obj","range","length","rangeEnd","rangeStart","offset","body","undefined","headerKey","value","Object","entries","append","metadata","httpMetadata","cacheControl","set","contentDisposition","contentEncoding","contentLanguage","contentType","writeHttpMetadata","etagFromHeaders","upload","modifyResponseHeaders","etag","_err"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/getFile.ts"],"sourcesContent":["import type { CollectionConfig, FileHandlerOperation, PayloadRequest, TypeWithID } from 'payload'\n\nimport {\n buildStoragePathData,\n getFilePrefix as getDocPrefix,\n} from '@payloadcms/plugin-cloud-storage/utilities'\nimport { getRangeRequestInfo, isXmlMimeType, uploadContentSecurityPolicy } from 'payload/internal'\n\nimport type { R2Bucket } from './types.js'\n\ninterface GetFileArgs {\n bucket: R2Bucket\n collection: CollectionConfig\n doc?: TypeWithID\n filename: string\n incomingHeaders?: Headers\n operation?: FileHandlerOperation\n prefix: string\n req: PayloadRequest\n uploadReference?: unknown\n useCompositePrefixes?: boolean\n}\n\nconst isMiniflare = process.env.NODE_ENV === 'development'\n\nexport async function getFile({\n bucket,\n collection,\n doc,\n filename,\n incomingHeaders,\n operation = 'read',\n prefix = '',\n req,\n uploadReference,\n useCompositePrefixes = false,\n}: GetFileArgs): Promise<Response> {\n const isTransformSource = operation === 'transform'\n\n try {\n const docPrefix = await getDocPrefix({\n collection,\n collectionPrefix: prefix,\n doc,\n filename,\n req,\n uploadReference,\n useCompositePrefixes,\n })\n\n const { storageFilePath } = buildStoragePathData({\n collectionPrefix: prefix,\n docPrefix,\n filename,\n useCompositePrefixes,\n })\n\n // Get file size for range validation\n const headObj = await bucket?.head(storageFilePath)\n if (!headObj) {\n return new Response(null, { status: 404, statusText: 'Not Found' })\n }\n\n const fileSize = headObj.size\n\n // Don't return large file uploads back to the client, or the Worker will run out of memory.\n // Skipped for `transform`, which needs the real bytes and would otherwise silently corrupt.\n if (fileSize > 50 * 1024 * 1024 && uploadReference && !isTransformSource) {\n return new Response(null, { status: 200 })\n }\n\n const rangeHeader = isTransformSource ? null : req.headers.get('range')\n const rangeResult = getRangeRequestInfo({ fileSize, rangeHeader })\n\n if (rangeResult.type === 'invalid') {\n return new Response(null, {\n headers: new Headers(rangeResult.headers),\n status: rangeResult.status,\n })\n }\n\n // Due to https://github.com/cloudflare/workers-sdk/issues/6047\n // We cannot send a Headers instance to Miniflare\n const obj =\n rangeResult.type === 'partial' && !isMiniflare\n ? await bucket?.get(storageFilePath, {\n range: {\n length: rangeResult.rangeEnd - rangeResult.rangeStart + 1,\n offset: rangeResult.rangeStart,\n },\n })\n : await bucket?.get(storageFilePath)\n\n if (!obj || obj.body == undefined) {\n return new Response(null, { status: 404, statusText: 'Not Found' })\n }\n\n let headers = new Headers(incomingHeaders)\n\n for (const [headerKey, value] of Object.entries(rangeResult.headers)) {\n headers.append(headerKey, value)\n }\n\n if (isMiniflare) {\n const metadata = obj.httpMetadata\n if (metadata?.cacheControl) {\n headers.set('Cache-Control', metadata.cacheControl)\n }\n if (metadata?.contentDisposition) {\n headers.set('Content-Disposition', metadata.contentDisposition)\n }\n if (metadata?.contentEncoding) {\n headers.set('Content-Encoding', metadata.contentEncoding)\n }\n if (metadata?.contentLanguage) {\n headers.set('Content-Language', metadata.contentLanguage)\n }\n if (metadata?.contentType) {\n headers.set('Content-Type', metadata.contentType)\n }\n } else {\n obj.writeHttpMetadata(headers)\n }\n\n const contentType = headers.get('Content-Type')\n\n // Apply a restrictive policy to XML-family responses served through Payload.\n if (isXmlMimeType(contentType)) {\n headers.set('Content-Security-Policy', uploadContentSecurityPolicy)\n }\n\n const etagFromHeaders = req.headers.get('etag') || req.headers.get('if-none-match')\n\n if (\n !isTransformSource &&\n collection.upload &&\n typeof collection.upload === 'object' &&\n typeof collection.upload.modifyResponseHeaders === 'function'\n ) {\n headers = collection.upload.modifyResponseHeaders({ headers }) || headers\n }\n\n if (!isTransformSource && etagFromHeaders && etagFromHeaders === obj.etag) {\n return new Response(null, {\n headers,\n status: 304,\n })\n }\n\n return new Response(obj.body, {\n headers,\n status: rangeResult.status,\n })\n } catch (_err: unknown) {\n return new Response('Internal Server Error', { status: 500 })\n }\n}\n"],"names":["buildStoragePathData","getFilePrefix","getDocPrefix","getRangeRequestInfo","isXmlMimeType","uploadContentSecurityPolicy","isMiniflare","process","env","NODE_ENV","getFile","bucket","collection","doc","filename","incomingHeaders","operation","prefix","req","uploadReference","useCompositePrefixes","isTransformSource","docPrefix","collectionPrefix","storageFilePath","headObj","head","Response","status","statusText","fileSize","size","rangeHeader","headers","get","rangeResult","type","Headers","obj","range","length","rangeEnd","rangeStart","offset","body","undefined","headerKey","value","Object","entries","append","metadata","httpMetadata","cacheControl","set","contentDisposition","contentEncoding","contentLanguage","contentType","writeHttpMetadata","etagFromHeaders","upload","modifyResponseHeaders","etag","_err"],"mappings":"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"}
@@ -7,7 +7,6 @@ type Args = {
7
7
  collections: R2StorageOptions['collections'];
8
8
  useCompositePrefixes?: boolean;
9
9
  };
10
- export declare const defaultR2ClientUploadsAccess: UploadInstructionsAccess;
11
- export declare const getHandleMultiPartUpload: ({ access, bucket, collections, useCompositePrefixes, }: Args) => PayloadHandler;
10
+ export declare const getHandleMultiPartUpload: ({ access, bucket, collections, useCompositePrefixes }: Args) => PayloadHandler;
12
11
  export {};
13
12
  //# sourceMappingURL=handleMultiPartUpload.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"handleMultiPartUpload.d.ts","sourceRoot":"","sources":["../src/handleMultiPartUpload.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAA;AAKvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAyC,MAAM,YAAY,CAAA;AAEjF,KAAK,IAAI,GAAG;IACV,MAAM,CAAC,EAAE,wBAAwB,CAAA;IACjC,MAAM,EAAE,QAAQ,CAAA;IAChB,WAAW,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAA;IAC5C,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B,CAAA;AAED,eAAO,MAAM,4BAA4B,EAAE,wBAW1C,CAAA;AAGD,eAAO,MAAM,wBAAwB,2DAMhC,IAAI,KAAG,cA2DT,CAAA"}
1
+ {"version":3,"file":"handleMultiPartUpload.d.ts","sourceRoot":"","sources":["../src/handleMultiPartUpload.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAA;AAUvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAyD,MAAM,YAAY,CAAA;AAEjG,KAAK,IAAI,GAAG;IACV,MAAM,CAAC,EAAE,wBAAwB,CAAA;IACjC,MAAM,EAAE,QAAQ,CAAA;IAChB,WAAW,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAA;IAC5C,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B,CAAA;AAGD,eAAO,MAAM,wBAAwB,0DAC6B,IAAI,KAAG,cAqFtE,CAAA"}
@@ -1,48 +1,53 @@
1
1
  import { resolveSignedURLKey } from '@payloadcms/plugin-cloud-storage/utilities';
2
2
  import { APIError, Forbidden } from 'payload';
3
- export const defaultR2ClientUploadsAccess = async ({ collectionSlug, req })=>{
4
- const collection = req.payload.collections[collectionSlug];
5
- if (!collection?.config) {
6
- throw new APIError(`Collection ${collectionSlug} not found`);
7
- }
8
- const createAccess = collection.config.access?.create;
9
- return createAccess ? Boolean(await createAccess({
10
- req
11
- })) : Boolean(req.user);
12
- };
3
+ import { assertClientUploadAccess, assertClientUploadAllowed, verifyClientUploadReceipt } from 'payload/internal';
13
4
  // Adapted from https://developers.cloudflare.com/r2/api/workers/workers-multipart-usage/
14
- export const getHandleMultiPartUpload = ({ access = defaultR2ClientUploadsAccess, bucket, collections, useCompositePrefixes = false })=>async (req)=>{
5
+ export const getHandleMultiPartUpload = ({ access, bucket, collections, useCompositePrefixes = false })=>async (req)=>{
15
6
  const params = Object.fromEntries(req.searchParams);
16
7
  const collectionSlug = params.collection;
17
8
  const filetype = params.fileType;
9
+ await assertClientUploadAccess({
10
+ collectionSlug,
11
+ req
12
+ });
18
13
  const collectionConfig = collections[collectionSlug];
19
14
  if (!collectionConfig) {
20
15
  throw new APIError(`Collection ${collectionSlug} was not found in R2 Storage options`);
21
16
  }
22
- if (!await access({
17
+ if (access && !await access({
23
18
  collectionSlug,
24
19
  req
25
20
  })) {
26
21
  throw new Forbidden(req.t);
27
22
  }
28
- const collectionPrefix = typeof collectionConfig === 'object' && collectionConfig.prefix || '';
29
- const { fileKey, sanitizedFilename } = await resolveSignedURLKey({
30
- collectionPrefix,
31
- collectionSlug,
32
- docPrefix: params.docPrefix ?? undefined,
23
+ assertClientUploadAllowed({
24
+ collection: req.payload.collections[collectionSlug]?.config,
33
25
  filename: params.fileName,
34
- req,
35
- useCompositePrefixes
26
+ mimeType: filetype
36
27
  });
37
28
  const multipartId = params.multipartId;
38
29
  const multipartKey = params.multipartKey;
39
30
  const multipartNumber = parseInt(params.multipartNumber || '');
31
+ const collectionPrefix = typeof collectionConfig === 'object' && collectionConfig.prefix || '';
40
32
  if (multipartId && multipartKey) {
33
+ const receipt = verifyClientUploadReceipt({
34
+ collectionSlug,
35
+ req,
36
+ signedReceipt: params.signedReceipt
37
+ });
38
+ // The receipt binds the full storage key (including the per-upload _objectKey segment), so
39
+ // compare against it directly rather than recomputing from prefix + filename.
40
+ if (receipt.storageFilePath !== multipartKey) {
41
+ throw new APIError('Invalid upload reference.', 400);
42
+ }
41
43
  const multipartUpload = bucket.resumeMultipartUpload(multipartKey, multipartId);
42
44
  const request = req;
43
45
  if (isNaN(multipartNumber)) {
44
46
  // Upload complete
45
47
  const object = await multipartUpload.complete(await request.json());
48
+ if (object.key !== multipartKey) {
49
+ throw new APIError('Uploaded source was stored at an unexpected location.', 400);
50
+ }
46
51
  return new Response(object.key, {
47
52
  status: 200
48
53
  });
@@ -52,8 +57,22 @@ export const getHandleMultiPartUpload = ({ access = defaultR2ClientUploadsAccess
52
57
  return Response.json(uploadedPart);
53
58
  }
54
59
  } else {
60
+ const { sanitizedFilename, storageFilePath, uploadReference } = await resolveSignedURLKey({
61
+ collectionPrefix,
62
+ collectionSlug,
63
+ docPrefix: params.docPrefix ?? undefined,
64
+ filename: params.fileName,
65
+ req,
66
+ useCompositePrefixes
67
+ });
68
+ const existing = await bucket.head(storageFilePath);
69
+ if (existing) {
70
+ return new Response('Object already exists', {
71
+ status: 412
72
+ });
73
+ }
55
74
  // Create multipart upload
56
- const multipartUpload = await bucket.createMultipartUpload(fileKey, {
75
+ const multipartUpload = await bucket.createMultipartUpload(storageFilePath, {
57
76
  httpMetadata: {
58
77
  contentType: filetype
59
78
  }
@@ -61,7 +80,8 @@ export const getHandleMultiPartUpload = ({ access = defaultR2ClientUploadsAccess
61
80
  return Response.json({
62
81
  filename: sanitizedFilename,
63
82
  key: multipartUpload.key,
64
- uploadId: multipartUpload.uploadId
83
+ uploadId: multipartUpload.uploadId,
84
+ uploadReference
65
85
  });
66
86
  }
67
87
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/handleMultiPartUpload.ts"],"sourcesContent":["import type { PayloadHandler, UploadInstructionsAccess } from 'payload'\n\nimport { resolveSignedURLKey } from '@payloadcms/plugin-cloud-storage/utilities'\nimport { APIError, Forbidden } from 'payload'\n\nimport type { R2StorageOptions } from './index.js'\nimport type { R2Bucket, R2StorageMultipartUploadHandlerParams } from './types.js'\n\ntype Args = {\n access?: UploadInstructionsAccess\n bucket: R2Bucket\n collections: R2StorageOptions['collections']\n useCompositePrefixes?: boolean\n}\n\nexport const defaultR2ClientUploadsAccess: UploadInstructionsAccess = async ({\n collectionSlug,\n req,\n}) => {\n const collection = req.payload.collections[collectionSlug]\n if (!collection?.config) {\n throw new APIError(`Collection ${collectionSlug} not found`)\n }\n\n const createAccess = collection.config.access?.create\n return createAccess ? Boolean(await createAccess({ req })) : Boolean(req.user)\n}\n\n// Adapted from https://developers.cloudflare.com/r2/api/workers/workers-multipart-usage/\nexport const getHandleMultiPartUpload =\n ({\n access = defaultR2ClientUploadsAccess,\n bucket,\n collections,\n useCompositePrefixes = false,\n }: Args): PayloadHandler =>\n async (req) => {\n const params = Object.fromEntries(req.searchParams) as R2StorageMultipartUploadHandlerParams\n const collectionSlug = params.collection\n const filetype = params.fileType\n\n const collectionConfig = collections[collectionSlug]\n if (!collectionConfig) {\n throw new APIError(`Collection ${collectionSlug} was not found in R2 Storage options`)\n }\n\n if (!(await access({ collectionSlug, req }))) {\n throw new Forbidden(req.t)\n }\n\n const collectionPrefix = (typeof collectionConfig === 'object' && collectionConfig.prefix) || ''\n const { fileKey, sanitizedFilename } = await resolveSignedURLKey({\n collectionPrefix,\n collectionSlug,\n docPrefix: params.docPrefix ?? undefined,\n filename: params.fileName,\n req,\n useCompositePrefixes,\n })\n\n const multipartId = params.multipartId\n const multipartKey = params.multipartKey\n const multipartNumber = parseInt(params.multipartNumber || '')\n\n if (multipartId && multipartKey) {\n const multipartUpload = bucket.resumeMultipartUpload(multipartKey, multipartId)\n const request = req as Request\n\n if (isNaN(multipartNumber)) {\n // Upload complete\n const object = await multipartUpload.complete((await request.json()) as any)\n return new Response(object.key, { status: 200 })\n } else {\n // Upload part\n const uploadedPart = await multipartUpload.uploadPart(\n multipartNumber,\n await request.arrayBuffer(),\n )\n return Response.json(uploadedPart)\n }\n } else {\n // Create multipart upload\n const multipartUpload = await bucket.createMultipartUpload(fileKey, {\n httpMetadata: {\n contentType: filetype,\n },\n })\n\n return Response.json({\n filename: sanitizedFilename,\n key: multipartUpload.key,\n uploadId: multipartUpload.uploadId,\n })\n }\n }\n"],"names":["resolveSignedURLKey","APIError","Forbidden","defaultR2ClientUploadsAccess","collectionSlug","req","collection","payload","collections","config","createAccess","access","create","Boolean","user","getHandleMultiPartUpload","bucket","useCompositePrefixes","params","Object","fromEntries","searchParams","filetype","fileType","collectionConfig","t","collectionPrefix","prefix","fileKey","sanitizedFilename","docPrefix","undefined","filename","fileName","multipartId","multipartKey","multipartNumber","parseInt","multipartUpload","resumeMultipartUpload","request","isNaN","object","complete","json","Response","key","status","uploadedPart","uploadPart","arrayBuffer","createMultipartUpload","httpMetadata","contentType","uploadId"],"mappings":"AAEA,SAASA,mBAAmB,QAAQ,6CAA4C;AAChF,SAASC,QAAQ,EAAEC,SAAS,QAAQ,UAAS;AAY7C,OAAO,MAAMC,+BAAyD,OAAO,EAC3EC,cAAc,EACdC,GAAG,EACJ;IACC,MAAMC,aAAaD,IAAIE,OAAO,CAACC,WAAW,CAACJ,eAAe;IAC1D,IAAI,CAACE,YAAYG,QAAQ;QACvB,MAAM,IAAIR,SAAS,CAAC,WAAW,EAAEG,eAAe,UAAU,CAAC;IAC7D;IAEA,MAAMM,eAAeJ,WAAWG,MAAM,CAACE,MAAM,EAAEC;IAC/C,OAAOF,eAAeG,QAAQ,MAAMH,aAAa;QAAEL;IAAI,MAAMQ,QAAQR,IAAIS,IAAI;AAC/E,EAAC;AAED,yFAAyF;AACzF,OAAO,MAAMC,2BACX,CAAC,EACCJ,SAASR,4BAA4B,EACrCa,MAAM,EACNR,WAAW,EACXS,uBAAuB,KAAK,EACvB,GACP,OAAOZ;QACL,MAAMa,SAASC,OAAOC,WAAW,CAACf,IAAIgB,YAAY;QAClD,MAAMjB,iBAAiBc,OAAOZ,UAAU;QACxC,MAAMgB,WAAWJ,OAAOK,QAAQ;QAEhC,MAAMC,mBAAmBhB,WAAW,CAACJ,eAAe;QACpD,IAAI,CAACoB,kBAAkB;YACrB,MAAM,IAAIvB,SAAS,CAAC,WAAW,EAAEG,eAAe,oCAAoC,CAAC;QACvF;QAEA,IAAI,CAAE,MAAMO,OAAO;YAAEP;YAAgBC;QAAI,IAAK;YAC5C,MAAM,IAAIH,UAAUG,IAAIoB,CAAC;QAC3B;QAEA,MAAMC,mBAAmB,AAAC,OAAOF,qBAAqB,YAAYA,iBAAiBG,MAAM,IAAK;QAC9F,MAAM,EAAEC,OAAO,EAAEC,iBAAiB,EAAE,GAAG,MAAM7B,oBAAoB;YAC/D0B;YACAtB;YACA0B,WAAWZ,OAAOY,SAAS,IAAIC;YAC/BC,UAAUd,OAAOe,QAAQ;YACzB5B;YACAY;QACF;QAEA,MAAMiB,cAAchB,OAAOgB,WAAW;QACtC,MAAMC,eAAejB,OAAOiB,YAAY;QACxC,MAAMC,kBAAkBC,SAASnB,OAAOkB,eAAe,IAAI;QAE3D,IAAIF,eAAeC,cAAc;YAC/B,MAAMG,kBAAkBtB,OAAOuB,qBAAqB,CAACJ,cAAcD;YACnE,MAAMM,UAAUnC;YAEhB,IAAIoC,MAAML,kBAAkB;gBAC1B,kBAAkB;gBAClB,MAAMM,SAAS,MAAMJ,gBAAgBK,QAAQ,CAAE,MAAMH,QAAQI,IAAI;gBACjE,OAAO,IAAIC,SAASH,OAAOI,GAAG,EAAE;oBAAEC,QAAQ;gBAAI;YAChD,OAAO;gBACL,cAAc;gBACd,MAAMC,eAAe,MAAMV,gBAAgBW,UAAU,CACnDb,iBACA,MAAMI,QAAQU,WAAW;gBAE3B,OAAOL,SAASD,IAAI,CAACI;YACvB;QACF,OAAO;YACL,0BAA0B;YAC1B,MAAMV,kBAAkB,MAAMtB,OAAOmC,qBAAqB,CAACvB,SAAS;gBAClEwB,cAAc;oBACZC,aAAa/B;gBACf;YACF;YAEA,OAAOuB,SAASD,IAAI,CAAC;gBACnBZ,UAAUH;gBACViB,KAAKR,gBAAgBQ,GAAG;gBACxBQ,UAAUhB,gBAAgBgB,QAAQ;YACpC;QACF;IACF,EAAC"}
1
+ {"version":3,"sources":["../src/handleMultiPartUpload.ts"],"sourcesContent":["import type { PayloadHandler, UploadInstructionsAccess } from 'payload'\n\nimport { resolveSignedURLKey } from '@payloadcms/plugin-cloud-storage/utilities'\nimport { APIError, Forbidden } from 'payload'\nimport {\n assertClientUploadAccess,\n assertClientUploadAllowed,\n verifyClientUploadReceipt,\n} from 'payload/internal'\n\nimport type { R2StorageOptions } from './index.js'\nimport type { R2Bucket, R2StorageMultipartUploadHandlerParams, R2UploadedPart } from './types.js'\n\ntype Args = {\n access?: UploadInstructionsAccess\n bucket: R2Bucket\n collections: R2StorageOptions['collections']\n useCompositePrefixes?: boolean\n}\n\n// Adapted from https://developers.cloudflare.com/r2/api/workers/workers-multipart-usage/\nexport const getHandleMultiPartUpload =\n ({ access, bucket, collections, useCompositePrefixes = false }: Args): PayloadHandler =>\n async (req) => {\n const params = Object.fromEntries(req.searchParams) as R2StorageMultipartUploadHandlerParams\n const collectionSlug = params.collection\n const filetype = params.fileType\n\n await assertClientUploadAccess({ collectionSlug, req })\n\n const collectionConfig = collections[collectionSlug]\n if (!collectionConfig) {\n throw new APIError(`Collection ${collectionSlug} was not found in R2 Storage options`)\n }\n\n if (access && !(await access({ collectionSlug, req }))) {\n throw new Forbidden(req.t)\n }\n\n assertClientUploadAllowed({\n collection: req.payload.collections[collectionSlug]?.config,\n filename: params.fileName,\n mimeType: filetype,\n })\n\n const multipartId = params.multipartId\n const multipartKey = params.multipartKey\n const multipartNumber = parseInt(params.multipartNumber || '')\n const collectionPrefix = (typeof collectionConfig === 'object' && collectionConfig.prefix) || ''\n\n if (multipartId && multipartKey) {\n const receipt = verifyClientUploadReceipt({\n collectionSlug,\n req,\n signedReceipt: params.signedReceipt,\n })\n // The receipt binds the full storage key (including the per-upload _objectKey segment), so\n // compare against it directly rather than recomputing from prefix + filename.\n if (receipt.storageFilePath !== multipartKey) {\n throw new APIError('Invalid upload reference.', 400)\n }\n const multipartUpload = bucket.resumeMultipartUpload(multipartKey, multipartId)\n const request = req as Request\n\n if (isNaN(multipartNumber)) {\n // Upload complete\n const object = await multipartUpload.complete((await request.json()) as R2UploadedPart[])\n if (object.key !== multipartKey) {\n throw new APIError('Uploaded source was stored at an unexpected location.', 400)\n }\n return new Response(object.key, { status: 200 })\n } else {\n // Upload part\n const uploadedPart = await multipartUpload.uploadPart(\n multipartNumber,\n await request.arrayBuffer(),\n )\n return Response.json(uploadedPart)\n }\n } else {\n const { sanitizedFilename, storageFilePath, uploadReference } = await resolveSignedURLKey({\n collectionPrefix,\n collectionSlug,\n docPrefix: params.docPrefix ?? undefined,\n filename: params.fileName,\n req,\n useCompositePrefixes,\n })\n const existing = await bucket.head(storageFilePath)\n if (existing) {\n return new Response('Object already exists', { status: 412 })\n }\n\n // Create multipart upload\n const multipartUpload = await bucket.createMultipartUpload(storageFilePath, {\n httpMetadata: {\n contentType: filetype,\n },\n })\n\n return Response.json({\n filename: sanitizedFilename,\n key: multipartUpload.key,\n uploadId: multipartUpload.uploadId,\n uploadReference,\n })\n }\n }\n"],"names":["resolveSignedURLKey","APIError","Forbidden","assertClientUploadAccess","assertClientUploadAllowed","verifyClientUploadReceipt","getHandleMultiPartUpload","access","bucket","collections","useCompositePrefixes","req","params","Object","fromEntries","searchParams","collectionSlug","collection","filetype","fileType","collectionConfig","t","payload","config","filename","fileName","mimeType","multipartId","multipartKey","multipartNumber","parseInt","collectionPrefix","prefix","receipt","signedReceipt","storageFilePath","multipartUpload","resumeMultipartUpload","request","isNaN","object","complete","json","key","Response","status","uploadedPart","uploadPart","arrayBuffer","sanitizedFilename","uploadReference","docPrefix","undefined","existing","head","createMultipartUpload","httpMetadata","contentType","uploadId"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -2,16 +2,6 @@ import type { ClientUploadsConfig, CollectionOptions } from '@payloadcms/plugin-
2
2
  import type { StorageAdapter, UploadCollectionSlug } from 'payload';
3
3
  import type { R2Bucket } from './types.js';
4
4
  export interface R2StorageOptions {
5
- /**
6
- * When enabled, fields (like the prefix field) will always be inserted into
7
- * the collection schema regardless of whether the plugin is enabled. This
8
- * ensures a consistent schema across all environments.
9
- *
10
- * This will be enabled by default in Payload v4.
11
- *
12
- * @default false
13
- */
14
- alwaysInsertFields?: boolean;
15
5
  bucket: R2Bucket;
16
6
  /**
17
7
  * Upload files in chunks through Payload before document creation.
@@ -21,17 +11,25 @@ export interface R2StorageOptions {
21
11
  * Collection options to apply the R2 adapter to.
22
12
  */
23
13
  collections: Partial<Record<UploadCollectionSlug, Omit<CollectionOptions, 'adapter'> | true>>;
14
+ /** S3 API credentials required only for copy-dependent file versioning operations. */
15
+ copyCredentials?: {
16
+ accessKeyId: string;
17
+ accountId: string;
18
+ bucket: string;
19
+ secretAccessKey: string;
20
+ };
24
21
  enabled?: boolean;
25
22
  /**
26
23
  * When true, the collection-level prefix and document-level prefix are combined
27
- * (compositional). When false (default), document prefix overrides collection
28
- * prefix entirely.
24
+ * (compositional). When false (default), a document prefix already within the
25
+ * collection prefix is used as-is for new uploads; otherwise it is nested beneath it.
26
+ * Existing files retain their stored prefixes for reads, URLs, and cleanup.
29
27
  *
30
- * Example:
31
- * - collection prefix: `collection-prefix/`
32
- * - document prefix: `document-prefix/`
33
- * - resulting prefix with useCompositePrefixes=true: `collection-prefix/document-prefix/`
34
- * - resulting prefix with useCompositePrefixes=false: `document-prefix/`
28
+ * Example with a document prefix already contained by the collection prefix:
29
+ * - collection prefix: `uploads/`
30
+ * - document prefix: `uploads/documents/`
31
+ * - resulting prefix with useCompositePrefixes=true: `uploads/uploads/documents/`
32
+ * - resulting prefix with useCompositePrefixes=false: `uploads/documents/`
35
33
  *
36
34
  * @default false
37
35
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EAEnB,iBAAiB,EAClB,MAAM,wCAAwC,CAAA;AAC/C,OAAO,KAAK,EAAU,cAAc,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AAI3E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAI1C,MAAM,WAAW,gBAAgB;IAC/B;;;;;;;;OAQG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAE5B,MAAM,EAAE,QAAQ,CAAA;IAChB;;OAEG;IACH,aAAa,CAAC,EAAE,mBAAmB,CAAA;IACnC;;OAEG;IACH,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,oBAAoB,EAAE,IAAI,CAAC,iBAAiB,EAAE,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC,CAAA;IAC7F,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;;;;;;;;;;OAYG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,KAAK,gBAAgB,GAAG,CAAC,aAAa,EAAE,gBAAgB,KAAK,cAAc,CAAA;AAE3E,eAAO,MAAM,SAAS,EAAE,gBAyDtB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EAEnB,iBAAiB,EAClB,MAAM,wCAAwC,CAAA;AAC/C,OAAO,KAAK,EAAU,cAAc,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AAI3E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAI1C,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,QAAQ,CAAA;IAChB;;OAEG;IACH,aAAa,CAAC,EAAE,mBAAmB,CAAA;IACnC;;OAEG;IACH,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,oBAAoB,EAAE,IAAI,CAAC,iBAAiB,EAAE,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC,CAAA;IAC7F,sFAAsF;IACtF,eAAe,CAAC,EAAE;QAChB,WAAW,EAAE,MAAM,CAAA;QACnB,SAAS,EAAE,MAAM,CAAA;QACjB,MAAM,EAAE,MAAM,CAAA;QACd,eAAe,EAAE,MAAM,CAAA;KACxB,CAAA;IACD,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;;;;;;;;;;;OAaG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,KAAK,gBAAgB,GAAG,CAAC,aAAa,EAAE,gBAAgB,KAAK,cAAc,CAAA;AAE3E,eAAO,MAAM,SAAS,EAAE,gBAyEtB,CAAA"}
package/dist/index.js CHANGED
@@ -8,11 +8,25 @@ export const r2Storage = (r2StorageOptions)=>({
8
8
  bucket: r2StorageOptions.bucket,
9
9
  clientUploads: r2StorageOptions.clientUploads,
10
10
  collections: r2StorageOptions.collections,
11
+ copyCredentials: r2StorageOptions.copyCredentials,
11
12
  useCompositePrefixes: r2StorageOptions.useCompositePrefixes
12
13
  });
13
14
  const isPluginDisabled = r2StorageOptions.enabled === false;
14
15
  if (isPluginDisabled) {
15
- return incomingConfig;
16
+ // Still call cloudStoragePlugin with adapter: null so fields (like prefix) are
17
+ // inserted into the schema, keeping it consistent across environments.
18
+ const collectionsWithoutAdapter = Object.entries(r2StorageOptions.collections).reduce((acc, [slug, collOptions])=>({
19
+ ...acc,
20
+ [slug]: {
21
+ ...collOptions === true ? {} : collOptions,
22
+ adapter: null
23
+ }
24
+ }), {});
25
+ return cloudStoragePlugin({
26
+ collections: collectionsWithoutAdapter,
27
+ enabled: false,
28
+ useCompositePrefixes: r2StorageOptions.useCompositePrefixes
29
+ })(incomingConfig);
16
30
  }
17
31
  // Add adapter to each collection option object
18
32
  const collectionsWithAdapter = Object.entries(r2StorageOptions.collections).reduce((acc, [slug, collOptions])=>({
@@ -39,7 +53,6 @@ export const r2Storage = (r2StorageOptions)=>({
39
53
  })
40
54
  };
41
55
  return cloudStoragePlugin({
42
- alwaysInsertFields: r2StorageOptions.alwaysInsertFields,
43
56
  collections: collectionsWithAdapter,
44
57
  useCompositePrefixes: r2StorageOptions.useCompositePrefixes
45
58
  })(config);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type {\n ClientUploadsConfig,\n PluginOptions as CloudStoragePluginOptions,\n CollectionOptions,\n} from '@payloadcms/plugin-cloud-storage/types'\nimport type { Config, StorageAdapter, UploadCollectionSlug } from 'payload'\n\nimport { cloudStoragePlugin } from '@payloadcms/plugin-cloud-storage'\n\nimport type { R2Bucket } from './types.js'\n\nimport { createR2Adapter } from './adapter.js'\n\nexport interface R2StorageOptions {\n /**\n * When enabled, fields (like the prefix field) will always be inserted into\n * the collection schema regardless of whether the plugin is enabled. This\n * ensures a consistent schema across all environments.\n *\n * This will be enabled by default in Payload v4.\n *\n * @default false\n */\n alwaysInsertFields?: boolean\n\n bucket: R2Bucket\n /**\n * Upload files in chunks through Payload before document creation.\n */\n clientUploads?: ClientUploadsConfig\n /**\n * Collection options to apply the R2 adapter to.\n */\n collections: Partial<Record<UploadCollectionSlug, Omit<CollectionOptions, 'adapter'> | true>>\n enabled?: boolean\n /**\n * When true, the collection-level prefix and document-level prefix are combined\n * (compositional). When false (default), document prefix overrides collection\n * prefix entirely.\n *\n * Example:\n * - collection prefix: `collection-prefix/`\n * - document prefix: `document-prefix/`\n * - resulting prefix with useCompositePrefixes=true: `collection-prefix/document-prefix/`\n * - resulting prefix with useCompositePrefixes=false: `document-prefix/`\n *\n * @default false\n */\n useCompositePrefixes?: boolean\n}\n\ntype R2StorageFactory = (r2StorageArgs: R2StorageOptions) => StorageAdapter\n\nexport const r2Storage: R2StorageFactory = (\n r2StorageOptions: R2StorageOptions,\n): StorageAdapter => ({\n name: 'r2',\n collections: Object.keys(r2StorageOptions.collections),\n init: (incomingConfig: Config): Config => {\n const adapter = createR2Adapter({\n bucket: r2StorageOptions.bucket,\n clientUploads: r2StorageOptions.clientUploads,\n collections: r2StorageOptions.collections,\n useCompositePrefixes: r2StorageOptions.useCompositePrefixes,\n })\n\n const isPluginDisabled = r2StorageOptions.enabled === false\n\n if (isPluginDisabled) {\n return incomingConfig\n }\n\n // Add adapter to each collection option object\n const collectionsWithAdapter: CloudStoragePluginOptions['collections'] = Object.entries(\n r2StorageOptions.collections,\n ).reduce(\n (acc, [slug, collOptions]) => ({\n ...acc,\n [slug]: {\n ...(collOptions === true ? {} : collOptions),\n adapter,\n },\n }),\n {} as Record<string, CollectionOptions>,\n )\n\n // Set disableLocalStorage: true for collections specified in the plugin options\n const config = {\n ...incomingConfig,\n collections: (incomingConfig.collections || []).map((collection) => {\n if (!collectionsWithAdapter[collection.slug]) {\n return collection\n }\n\n return {\n ...collection,\n upload: {\n ...(typeof collection.upload === 'object' ? collection.upload : {}),\n disableLocalStorage: true,\n },\n }\n }),\n }\n\n return cloudStoragePlugin({\n alwaysInsertFields: r2StorageOptions.alwaysInsertFields,\n collections: collectionsWithAdapter,\n useCompositePrefixes: r2StorageOptions.useCompositePrefixes,\n })(config)\n },\n})\n"],"names":["cloudStoragePlugin","createR2Adapter","r2Storage","r2StorageOptions","name","collections","Object","keys","init","incomingConfig","adapter","bucket","clientUploads","useCompositePrefixes","isPluginDisabled","enabled","collectionsWithAdapter","entries","reduce","acc","slug","collOptions","config","map","collection","upload","disableLocalStorage","alwaysInsertFields"],"mappings":"AAOA,SAASA,kBAAkB,QAAQ,mCAAkC;AAIrE,SAASC,eAAe,QAAQ,eAAc;AA0C9C,OAAO,MAAMC,YAA8B,CACzCC,mBACoB,CAAA;QACpBC,MAAM;QACNC,aAAaC,OAAOC,IAAI,CAACJ,iBAAiBE,WAAW;QACrDG,MAAM,CAACC;YACL,MAAMC,UAAUT,gBAAgB;gBAC9BU,QAAQR,iBAAiBQ,MAAM;gBAC/BC,eAAeT,iBAAiBS,aAAa;gBAC7CP,aAAaF,iBAAiBE,WAAW;gBACzCQ,sBAAsBV,iBAAiBU,oBAAoB;YAC7D;YAEA,MAAMC,mBAAmBX,iBAAiBY,OAAO,KAAK;YAEtD,IAAID,kBAAkB;gBACpB,OAAOL;YACT;YAEA,+CAA+C;YAC/C,MAAMO,yBAAmEV,OAAOW,OAAO,CACrFd,iBAAiBE,WAAW,EAC5Ba,MAAM,CACN,CAACC,KAAK,CAACC,MAAMC,YAAY,GAAM,CAAA;oBAC7B,GAAGF,GAAG;oBACN,CAACC,KAAK,EAAE;wBACN,GAAIC,gBAAgB,OAAO,CAAC,IAAIA,WAAW;wBAC3CX;oBACF;gBACF,CAAA,GACA,CAAC;YAGH,gFAAgF;YAChF,MAAMY,SAAS;gBACb,GAAGb,cAAc;gBACjBJ,aAAa,AAACI,CAAAA,eAAeJ,WAAW,IAAI,EAAE,AAAD,EAAGkB,GAAG,CAAC,CAACC;oBACnD,IAAI,CAACR,sBAAsB,CAACQ,WAAWJ,IAAI,CAAC,EAAE;wBAC5C,OAAOI;oBACT;oBAEA,OAAO;wBACL,GAAGA,UAAU;wBACbC,QAAQ;4BACN,GAAI,OAAOD,WAAWC,MAAM,KAAK,WAAWD,WAAWC,MAAM,GAAG,CAAC,CAAC;4BAClEC,qBAAqB;wBACvB;oBACF;gBACF;YACF;YAEA,OAAO1B,mBAAmB;gBACxB2B,oBAAoBxB,iBAAiBwB,kBAAkB;gBACvDtB,aAAaW;gBACbH,sBAAsBV,iBAAiBU,oBAAoB;YAC7D,GAAGS;QACL;IACF,CAAA,EAAE"}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type {\n ClientUploadsConfig,\n PluginOptions as CloudStoragePluginOptions,\n CollectionOptions,\n} from '@payloadcms/plugin-cloud-storage/types'\nimport type { Config, StorageAdapter, UploadCollectionSlug } from 'payload'\n\nimport { cloudStoragePlugin } from '@payloadcms/plugin-cloud-storage'\n\nimport type { R2Bucket } from './types.js'\n\nimport { createR2Adapter } from './adapter.js'\n\nexport interface R2StorageOptions {\n bucket: R2Bucket\n /**\n * Upload files in chunks through Payload before document creation.\n */\n clientUploads?: ClientUploadsConfig\n /**\n * Collection options to apply the R2 adapter to.\n */\n collections: Partial<Record<UploadCollectionSlug, Omit<CollectionOptions, 'adapter'> | true>>\n /** S3 API credentials required only for copy-dependent file versioning operations. */\n copyCredentials?: {\n accessKeyId: string\n accountId: string\n bucket: string\n secretAccessKey: string\n }\n enabled?: boolean\n /**\n * When true, the collection-level prefix and document-level prefix are combined\n * (compositional). When false (default), a document prefix already within the\n * collection prefix is used as-is for new uploads; otherwise it is nested beneath it.\n * Existing files retain their stored prefixes for reads, URLs, and cleanup.\n *\n * Example with a document prefix already contained by the collection prefix:\n * - collection prefix: `uploads/`\n * - document prefix: `uploads/documents/`\n * - resulting prefix with useCompositePrefixes=true: `uploads/uploads/documents/`\n * - resulting prefix with useCompositePrefixes=false: `uploads/documents/`\n *\n * @default false\n */\n useCompositePrefixes?: boolean\n}\n\ntype R2StorageFactory = (r2StorageArgs: R2StorageOptions) => StorageAdapter\n\nexport const r2Storage: R2StorageFactory = (\n r2StorageOptions: R2StorageOptions,\n): StorageAdapter => ({\n name: 'r2',\n collections: Object.keys(r2StorageOptions.collections),\n init: (incomingConfig: Config): Config => {\n const adapter = createR2Adapter({\n bucket: r2StorageOptions.bucket,\n clientUploads: r2StorageOptions.clientUploads,\n collections: r2StorageOptions.collections,\n copyCredentials: r2StorageOptions.copyCredentials,\n useCompositePrefixes: r2StorageOptions.useCompositePrefixes,\n })\n\n const isPluginDisabled = r2StorageOptions.enabled === false\n\n if (isPluginDisabled) {\n // Still call cloudStoragePlugin with adapter: null so fields (like prefix) are\n // inserted into the schema, keeping it consistent across environments.\n const collectionsWithoutAdapter: CloudStoragePluginOptions['collections'] = Object.entries(\n r2StorageOptions.collections,\n ).reduce(\n (acc, [slug, collOptions]) => ({\n ...acc,\n [slug]: { ...(collOptions === true ? {} : collOptions), adapter: null },\n }),\n {} as Record<string, CollectionOptions>,\n )\n\n return cloudStoragePlugin({\n collections: collectionsWithoutAdapter,\n enabled: false,\n useCompositePrefixes: r2StorageOptions.useCompositePrefixes,\n })(incomingConfig)\n }\n\n // Add adapter to each collection option object\n const collectionsWithAdapter: CloudStoragePluginOptions['collections'] = Object.entries(\n r2StorageOptions.collections,\n ).reduce(\n (acc, [slug, collOptions]) => ({\n ...acc,\n [slug]: {\n ...(collOptions === true ? {} : collOptions),\n adapter,\n },\n }),\n {} as Record<string, CollectionOptions>,\n )\n\n // Set disableLocalStorage: true for collections specified in the plugin options\n const config = {\n ...incomingConfig,\n collections: (incomingConfig.collections || []).map((collection) => {\n if (!collectionsWithAdapter[collection.slug]) {\n return collection\n }\n\n return {\n ...collection,\n upload: {\n ...(typeof collection.upload === 'object' ? collection.upload : {}),\n disableLocalStorage: true,\n },\n }\n }),\n }\n\n return cloudStoragePlugin({\n collections: collectionsWithAdapter,\n useCompositePrefixes: r2StorageOptions.useCompositePrefixes,\n })(config)\n },\n})\n"],"names":["cloudStoragePlugin","createR2Adapter","r2Storage","r2StorageOptions","name","collections","Object","keys","init","incomingConfig","adapter","bucket","clientUploads","copyCredentials","useCompositePrefixes","isPluginDisabled","enabled","collectionsWithoutAdapter","entries","reduce","acc","slug","collOptions","collectionsWithAdapter","config","map","collection","upload","disableLocalStorage"],"mappings":"AAOA,SAASA,kBAAkB,QAAQ,mCAAkC;AAIrE,SAASC,eAAe,QAAQ,eAAc;AAuC9C,OAAO,MAAMC,YAA8B,CACzCC,mBACoB,CAAA;QACpBC,MAAM;QACNC,aAAaC,OAAOC,IAAI,CAACJ,iBAAiBE,WAAW;QACrDG,MAAM,CAACC;YACL,MAAMC,UAAUT,gBAAgB;gBAC9BU,QAAQR,iBAAiBQ,MAAM;gBAC/BC,eAAeT,iBAAiBS,aAAa;gBAC7CP,aAAaF,iBAAiBE,WAAW;gBACzCQ,iBAAiBV,iBAAiBU,eAAe;gBACjDC,sBAAsBX,iBAAiBW,oBAAoB;YAC7D;YAEA,MAAMC,mBAAmBZ,iBAAiBa,OAAO,KAAK;YAEtD,IAAID,kBAAkB;gBACpB,+EAA+E;gBAC/E,uEAAuE;gBACvE,MAAME,4BAAsEX,OAAOY,OAAO,CACxFf,iBAAiBE,WAAW,EAC5Bc,MAAM,CACN,CAACC,KAAK,CAACC,MAAMC,YAAY,GAAM,CAAA;wBAC7B,GAAGF,GAAG;wBACN,CAACC,KAAK,EAAE;4BAAE,GAAIC,gBAAgB,OAAO,CAAC,IAAIA,WAAW;4BAAGZ,SAAS;wBAAK;oBACxE,CAAA,GACA,CAAC;gBAGH,OAAOV,mBAAmB;oBACxBK,aAAaY;oBACbD,SAAS;oBACTF,sBAAsBX,iBAAiBW,oBAAoB;gBAC7D,GAAGL;YACL;YAEA,+CAA+C;YAC/C,MAAMc,yBAAmEjB,OAAOY,OAAO,CACrFf,iBAAiBE,WAAW,EAC5Bc,MAAM,CACN,CAACC,KAAK,CAACC,MAAMC,YAAY,GAAM,CAAA;oBAC7B,GAAGF,GAAG;oBACN,CAACC,KAAK,EAAE;wBACN,GAAIC,gBAAgB,OAAO,CAAC,IAAIA,WAAW;wBAC3CZ;oBACF;gBACF,CAAA,GACA,CAAC;YAGH,gFAAgF;YAChF,MAAMc,SAAS;gBACb,GAAGf,cAAc;gBACjBJ,aAAa,AAACI,CAAAA,eAAeJ,WAAW,IAAI,EAAE,AAAD,EAAGoB,GAAG,CAAC,CAACC;oBACnD,IAAI,CAACH,sBAAsB,CAACG,WAAWL,IAAI,CAAC,EAAE;wBAC5C,OAAOK;oBACT;oBAEA,OAAO;wBACL,GAAGA,UAAU;wBACbC,QAAQ;4BACN,GAAI,OAAOD,WAAWC,MAAM,KAAK,WAAWD,WAAWC,MAAM,GAAG,CAAC,CAAC;4BAClEC,qBAAqB;wBACvB;oBACF;gBACF;YACF;YAEA,OAAO5B,mBAAmB;gBACxBK,aAAakB;gBACbT,sBAAsBX,iBAAiBW,oBAAoB;YAC7D,GAAGU;QACL;IACF,CAAA,EAAE"}
package/dist/types.d.ts CHANGED
@@ -4,6 +4,7 @@
4
4
  * so Node's Blob/Buffer/Headers are accepted and we avoid strict Workers-only types.
5
5
  */
6
6
  import type { R2Range } from '@cloudflare/workers-types/2023-07-01/index.js';
7
+ import type { UploadReference } from '@payloadcms/plugin-cloud-storage/types';
7
8
  export type { R2Range };
8
9
  export interface R2GetOptions {
9
10
  [key: string]: any;
@@ -48,10 +49,7 @@ export interface R2UploadedPart {
48
49
  etag: string;
49
50
  partNumber: number;
50
51
  }
51
- export interface R2StorageUploadReference {
52
- key: string;
53
- prefix: string;
54
- }
52
+ export type R2StorageUploadReference = UploadReference;
55
53
  export type R2StorageClientUploadHandlerParams = {
56
54
  chunkSize?: number;
57
55
  };
@@ -63,5 +61,6 @@ export type R2StorageMultipartUploadHandlerParams = {
63
61
  multipartId?: string;
64
62
  multipartKey?: string;
65
63
  multipartNumber?: string;
64
+ signedReceipt?: string;
66
65
  };
67
66
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts"],"sourcesContent":["/**\n * R2 API types compatible with both Node (Miniflare) and Cloudflare Workers.\n * R2Range is sourced from Cloudflare so it cannot drift; other types are our own\n * so Node's Blob/Buffer/Headers are accepted and we avoid strict Workers-only types.\n */\nimport type { R2Range } from '@cloudflare/workers-types/2023-07-01/index.js'\n\nexport type { R2Range }\n\nexport interface R2GetOptions {\n [key: string]: any\n onlyIf?: any | Headers\n range?: R2Range\n}\n\nexport interface R2Bucket {\n createMultipartUpload(key: string, options?: any): Promise<R2MultipartUpload>\n delete(keys: string | string[]): Promise<void>\n get(key: string, options?: R2GetOptions): Promise<null | R2ObjectBody>\n head(key: string): Promise<null | R2Object>\n list(options?: any): Promise<any>\n put(\n key: string,\n value: ArrayBuffer | ArrayBufferView | Blob | null | ReadableStream | string,\n options?: any,\n ): Promise<any>\n resumeMultipartUpload(key: string, uploadId: string): R2MultipartUpload\n}\n\ninterface R2HTTPMetadata {\n cacheControl?: string\n cacheExpiry?: Date\n contentDisposition?: string\n contentEncoding?: string\n contentLanguage?: string\n contentType?: string\n}\n\nexport interface R2Object {\n readonly etag: string\n readonly httpMetadata?: R2HTTPMetadata\n readonly key: string\n readonly size: number\n writeHttpMetadata(headers: Headers): void\n}\n\nexport interface R2ObjectBody extends R2Object {\n get body(): ReadableStream\n}\n\nexport interface R2MultipartUpload {\n abort(): Promise<void>\n complete(uploadedParts: R2UploadedPart[]): Promise<R2Object>\n readonly key: string\n readonly uploadId: string\n uploadPart(\n partNumber: number,\n value: (ArrayBuffer | ArrayBufferView) | Blob | ReadableStream | string,\n options?: any,\n ): Promise<R2UploadedPart>\n}\n\nexport interface R2UploadedPart {\n etag: string\n partNumber: number\n}\n\nexport interface R2StorageUploadReference {\n key: string\n prefix: string\n}\nexport type R2StorageClientUploadHandlerParams = {\n chunkSize?: number\n}\n\nexport type R2StorageMultipartUploadHandlerParams = {\n collection: string\n docPrefix?: string\n fileName: string\n fileType: string\n multipartId?: string\n multipartKey?: string\n multipartNumber?: string\n}\n"],"names":[],"mappings":"AAAA;;;;CAIC,GAuED,WAQC"}
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["/**\n * R2 API types compatible with both Node (Miniflare) and Cloudflare Workers.\n * R2Range is sourced from Cloudflare so it cannot drift; other types are our own\n * so Node's Blob/Buffer/Headers are accepted and we avoid strict Workers-only types.\n */\nimport type { R2Range } from '@cloudflare/workers-types/2023-07-01/index.js'\nimport type { UploadReference } from '@payloadcms/plugin-cloud-storage/types'\n\nexport type { R2Range }\n\nexport interface R2GetOptions {\n [key: string]: any\n onlyIf?: any | Headers\n range?: R2Range\n}\n\nexport interface R2Bucket {\n createMultipartUpload(key: string, options?: any): Promise<R2MultipartUpload>\n delete(keys: string | string[]): Promise<void>\n get(key: string, options?: R2GetOptions): Promise<null | R2ObjectBody>\n head(key: string): Promise<null | R2Object>\n list(options?: any): Promise<any>\n put(\n key: string,\n value: ArrayBuffer | ArrayBufferView | Blob | null | ReadableStream | string,\n options?: any,\n ): Promise<any>\n resumeMultipartUpload(key: string, uploadId: string): R2MultipartUpload\n}\n\ninterface R2HTTPMetadata {\n cacheControl?: string\n cacheExpiry?: Date\n contentDisposition?: string\n contentEncoding?: string\n contentLanguage?: string\n contentType?: string\n}\n\nexport interface R2Object {\n readonly etag: string\n readonly httpMetadata?: R2HTTPMetadata\n readonly key: string\n readonly size: number\n writeHttpMetadata(headers: Headers): void\n}\n\nexport interface R2ObjectBody extends R2Object {\n get body(): ReadableStream\n}\n\nexport interface R2MultipartUpload {\n abort(): Promise<void>\n complete(uploadedParts: R2UploadedPart[]): Promise<R2Object>\n readonly key: string\n readonly uploadId: string\n uploadPart(\n partNumber: number,\n value: (ArrayBuffer | ArrayBufferView) | Blob | ReadableStream | string,\n options?: any,\n ): Promise<R2UploadedPart>\n}\n\nexport interface R2UploadedPart {\n etag: string\n partNumber: number\n}\n\nexport type R2StorageUploadReference = UploadReference\nexport type R2StorageClientUploadHandlerParams = {\n chunkSize?: number\n}\n\nexport type R2StorageMultipartUploadHandlerParams = {\n collection: string\n docPrefix?: string\n fileName: string\n fileType: string\n multipartId?: string\n multipartKey?: string\n multipartNumber?: string\n signedReceipt?: string\n}\n"],"names":[],"mappings":"AAAA;;;;CAIC,GAqED,WASC"}
@@ -2,12 +2,9 @@ import type { R2Bucket } from './types.js';
2
2
  interface UploadFileArgs {
3
3
  bucket: R2Bucket;
4
4
  buffer: Buffer;
5
- collectionPrefix?: string;
6
- docPrefix?: string;
7
- filename: string;
8
5
  mimeType: string;
9
- useCompositePrefixes?: boolean;
6
+ storageFilePath: string;
10
7
  }
11
- export declare function uploadFile({ bucket, buffer, collectionPrefix, docPrefix, filename, mimeType, useCompositePrefixes, }: UploadFileArgs): Promise<void>;
8
+ export declare function uploadFile({ bucket, buffer, mimeType, storageFilePath, }: UploadFileArgs): Promise<void>;
12
9
  export {};
13
10
  //# sourceMappingURL=uploadFile.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"uploadFile.d.ts","sourceRoot":"","sources":["../src/uploadFile.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAI1C,UAAU,cAAc;IACtB,MAAM,EAAE,QAAQ,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,wBAAsB,UAAU,CAAC,EAC/B,MAAM,EACN,MAAM,EACN,gBAAqB,EACrB,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,oBAA4B,GAC7B,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAchC"}
1
+ {"version":3,"file":"uploadFile.d.ts","sourceRoot":"","sources":["../src/uploadFile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAI1C,UAAU,cAAc;IACtB,MAAM,EAAE,QAAQ,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,eAAe,EAAE,MAAM,CAAA;CACxB;AAED,wBAAsB,UAAU,CAAC,EAC/B,MAAM,EACN,MAAM,EACN,QAAQ,EACR,eAAe,GAChB,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAOhC"}
@@ -1,17 +1,10 @@
1
- import { getFileKey } from '@payloadcms/plugin-cloud-storage/utilities';
2
1
  const isMiniflare = process.env.NODE_ENV === 'development';
3
- export async function uploadFile({ bucket, buffer, collectionPrefix = '', docPrefix, filename, mimeType, useCompositePrefixes = false }) {
2
+ export async function uploadFile({ bucket, buffer, mimeType, storageFilePath }) {
4
3
  // Read more: https://github.com/cloudflare/workers-sdk/issues/6047#issuecomment-2691217843
5
4
  const body = isMiniflare ? new Blob([
6
5
  buffer
7
6
  ]) : buffer;
8
- const { fileKey } = getFileKey({
9
- collectionPrefix,
10
- docPrefix,
11
- filename,
12
- useCompositePrefixes
13
- });
14
- await bucket.put(fileKey, body, {
7
+ await bucket.put(storageFilePath, body, {
15
8
  httpMetadata: {
16
9
  contentType: mimeType
17
10
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/uploadFile.ts"],"sourcesContent":["import { getFileKey } from '@payloadcms/plugin-cloud-storage/utilities'\n\nimport type { R2Bucket } from './types.js'\n\nconst isMiniflare = process.env.NODE_ENV === 'development'\n\ninterface UploadFileArgs {\n bucket: R2Bucket\n buffer: Buffer\n collectionPrefix?: string\n docPrefix?: string\n filename: string\n mimeType: string\n useCompositePrefixes?: boolean\n}\n\nexport async function uploadFile({\n bucket,\n buffer,\n collectionPrefix = '',\n docPrefix,\n filename,\n mimeType,\n useCompositePrefixes = false,\n}: UploadFileArgs): Promise<void> {\n // Read more: https://github.com/cloudflare/workers-sdk/issues/6047#issuecomment-2691217843\n const body = isMiniflare ? new Blob([buffer]) : buffer\n\n const { fileKey } = getFileKey({\n collectionPrefix,\n docPrefix,\n filename,\n useCompositePrefixes,\n })\n\n await bucket.put(fileKey, body, {\n httpMetadata: { contentType: mimeType },\n })\n}\n"],"names":["getFileKey","isMiniflare","process","env","NODE_ENV","uploadFile","bucket","buffer","collectionPrefix","docPrefix","filename","mimeType","useCompositePrefixes","body","Blob","fileKey","put","httpMetadata","contentType"],"mappings":"AAAA,SAASA,UAAU,QAAQ,6CAA4C;AAIvE,MAAMC,cAAcC,QAAQC,GAAG,CAACC,QAAQ,KAAK;AAY7C,OAAO,eAAeC,WAAW,EAC/BC,MAAM,EACNC,MAAM,EACNC,mBAAmB,EAAE,EACrBC,SAAS,EACTC,QAAQ,EACRC,QAAQ,EACRC,uBAAuB,KAAK,EACb;IACf,2FAA2F;IAC3F,MAAMC,OAAOZ,cAAc,IAAIa,KAAK;QAACP;KAAO,IAAIA;IAEhD,MAAM,EAAEQ,OAAO,EAAE,GAAGf,WAAW;QAC7BQ;QACAC;QACAC;QACAE;IACF;IAEA,MAAMN,OAAOU,GAAG,CAACD,SAASF,MAAM;QAC9BI,cAAc;YAAEC,aAAaP;QAAS;IACxC;AACF"}
1
+ {"version":3,"sources":["../src/uploadFile.ts"],"sourcesContent":["import type { R2Bucket } from './types.js'\n\nconst isMiniflare = process.env.NODE_ENV === 'development'\n\ninterface UploadFileArgs {\n bucket: R2Bucket\n buffer: Buffer\n mimeType: string\n storageFilePath: string\n}\n\nexport async function uploadFile({\n bucket,\n buffer,\n mimeType,\n storageFilePath,\n}: UploadFileArgs): Promise<void> {\n // Read more: https://github.com/cloudflare/workers-sdk/issues/6047#issuecomment-2691217843\n const body = isMiniflare ? new Blob([buffer]) : buffer\n\n await bucket.put(storageFilePath, body, {\n httpMetadata: { contentType: mimeType },\n })\n}\n"],"names":["isMiniflare","process","env","NODE_ENV","uploadFile","bucket","buffer","mimeType","storageFilePath","body","Blob","put","httpMetadata","contentType"],"mappings":"AAEA,MAAMA,cAAcC,QAAQC,GAAG,CAACC,QAAQ,KAAK;AAS7C,OAAO,eAAeC,WAAW,EAC/BC,MAAM,EACNC,MAAM,EACNC,QAAQ,EACRC,eAAe,EACA;IACf,2FAA2F;IAC3F,MAAMC,OAAOT,cAAc,IAAIU,KAAK;QAACJ;KAAO,IAAIA;IAEhD,MAAMD,OAAOM,GAAG,CAACH,iBAAiBC,MAAM;QACtCG,cAAc;YAAEC,aAAaN;QAAS;IACxC;AACF"}
package/package.json CHANGED
@@ -1,13 +1,8 @@
1
1
  {
2
2
  "name": "@payloadcms/storage-r2",
3
- "version": "4.0.0-internal.e7d4ebc",
3
+ "version": "4.0.0-internal.ec73d44",
4
4
  "description": "Payload storage adapter for Cloudflare R2",
5
5
  "homepage": "https://payloadcms.com",
6
- "repository": {
7
- "type": "git",
8
- "url": "https://github.com/payloadcms/payload.git",
9
- "directory": "packages/storage-r2"
10
- },
11
6
  "license": "MIT",
12
7
  "author": "Payload <dev@payloadcms.com> (https://payloadcms.com)",
13
8
  "maintainers": [
@@ -17,8 +12,18 @@
17
12
  "url": "https://payloadcms.com"
18
13
  }
19
14
  ],
20
- "sideEffects": false,
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/payloadcms/payload.git",
18
+ "directory": "packages/storage-r2"
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
21
23
  "type": "module",
24
+ "sideEffects": false,
25
+ "main": "./dist/index.js",
26
+ "types": "./dist/index.d.ts",
22
27
  "exports": {
23
28
  ".": {
24
29
  "import": "./dist/index.js",
@@ -31,23 +36,20 @@
31
36
  "default": "./dist/exports/client.js"
32
37
  }
33
38
  },
34
- "main": "./dist/index.js",
35
- "types": "./dist/index.d.ts",
36
- "files": [
37
- "dist"
38
- ],
39
39
  "dependencies": {
40
- "@payloadcms/plugin-cloud-storage": "4.0.0-internal.e7d4ebc"
40
+ "@aws-sdk/client-s3": "^3.614.0",
41
+ "@payloadcms/plugin-cloud-storage": "4.0.0-internal.ec73d44",
42
+ "@payloadcms/storage-s3": "4.0.0-internal.ec73d44"
41
43
  },
42
44
  "devDependencies": {
43
45
  "@cloudflare/workers-types": "4.20260218.0",
44
- "payload": "4.0.0-internal.e7d4ebc"
46
+ "payload": "4.0.0-internal.ec73d44"
45
47
  },
46
48
  "peerDependencies": {
47
- "payload": "4.0.0-internal.e7d4ebc"
49
+ "payload": "4.0.0-internal.ec73d44"
48
50
  },
49
51
  "engines": {
50
- "node": ">=24.15.0"
52
+ "node": ">=24.22.1"
51
53
  },
52
54
  "scripts": {
53
55
  "build": "pnpm build:types && pnpm build:swc",
@@ -55,8 +57,6 @@
55
57
  "build:debug": "pnpm build",
56
58
  "build:swc": "swc ./src -d ./dist --config-file .swcrc --strip-leading-paths",
57
59
  "build:types": "tsc --emitDeclarationOnly --outDir dist",
58
- "clean": "rimraf -g {dist,*.tsbuildinfo}",
59
- "lint": "eslint .",
60
- "lint:fix": "eslint . --fix"
60
+ "clean": "rimraf -g {dist,*.tsbuildinfo}"
61
61
  }
62
62
  }