@payloadcms/storage-s3 3.83.0-internal.06ac84e → 3.83.0-internal.ddc1147

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/adapter.d.ts +15 -0
  2. package/dist/adapter.d.ts.map +1 -0
  3. package/dist/adapter.js +56 -0
  4. package/dist/adapter.js.map +1 -0
  5. package/dist/client/S3ClientUploadHandler.d.ts.map +1 -1
  6. package/dist/client/S3ClientUploadHandler.js +4 -3
  7. package/dist/client/S3ClientUploadHandler.js.map +1 -1
  8. package/dist/deleteFile.d.ts +12 -0
  9. package/dist/deleteFile.d.ts.map +1 -0
  10. package/dist/deleteFile.js +15 -0
  11. package/dist/deleteFile.js.map +1 -0
  12. package/dist/generateSignedURL.d.ts +2 -1
  13. package/dist/generateSignedURL.d.ts.map +1 -1
  14. package/dist/generateSignedURL.js +11 -5
  15. package/dist/generateSignedURL.js.map +1 -1
  16. package/dist/generateURL.d.ts +7 -4
  17. package/dist/generateURL.d.ts.map +1 -1
  18. package/dist/generateURL.js +11 -5
  19. package/dist/generateURL.js.map +1 -1
  20. package/dist/getFile.d.ts +27 -0
  21. package/dist/getFile.d.ts.map +1 -0
  22. package/dist/getFile.js +174 -0
  23. package/dist/getFile.js.map +1 -0
  24. package/dist/index.d.ts +15 -1
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +25 -43
  27. package/dist/index.js.map +1 -1
  28. package/dist/uploadFile.d.ts +16 -0
  29. package/dist/uploadFile.d.ts.map +1 -0
  30. package/dist/uploadFile.js +39 -0
  31. package/dist/uploadFile.js.map +1 -0
  32. package/package.json +4 -4
  33. package/dist/handleDelete.d.ts +0 -9
  34. package/dist/handleDelete.d.ts.map +0 -1
  35. package/dist/handleDelete.js +0 -11
  36. package/dist/handleDelete.js.map +0 -1
  37. package/dist/handleUpload.d.ts +0 -13
  38. package/dist/handleUpload.d.ts.map +0 -1
  39. package/dist/handleUpload.js +0 -37
  40. package/dist/handleUpload.js.map +0 -1
  41. package/dist/staticHandler.d.ts +0 -21
  42. package/dist/staticHandler.d.ts.map +0 -1
  43. package/dist/staticHandler.js +0 -170
  44. package/dist/staticHandler.js.map +0 -1
@@ -0,0 +1,15 @@
1
+ import type * as AWS from '@aws-sdk/client-s3';
2
+ import type { Adapter, ClientUploadsConfig } from '@payloadcms/plugin-cloud-storage/types';
3
+ import type { SignedDownloadsConfig } from './getFile.js';
4
+ interface CreateS3AdapterArgs {
5
+ acl?: 'private' | 'public-read';
6
+ bucket: string;
7
+ clientUploads?: ClientUploadsConfig;
8
+ config: AWS.S3ClientConfig;
9
+ getStorageClient: () => AWS.S3;
10
+ signedDownloads: SignedDownloadsConfig;
11
+ useCompositePrefixes?: boolean;
12
+ }
13
+ export declare function createS3Adapter({ acl, bucket, clientUploads, config, getStorageClient, signedDownloads, useCompositePrefixes, }: CreateS3AdapterArgs): Adapter;
14
+ export {};
15
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,oBAAoB,CAAA;AAC9C,OAAO,KAAK,EACV,OAAO,EACP,mBAAmB,EAEpB,MAAM,wCAAwC,CAAA;AAE/C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AAOzD,UAAU,mBAAmB;IAC3B,GAAG,CAAC,EAAE,SAAS,GAAG,aAAa,CAAA;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,CAAC,EAAE,mBAAmB,CAAA;IACnC,MAAM,EAAE,GAAG,CAAC,cAAc,CAAA;IAC1B,gBAAgB,EAAE,MAAM,GAAG,CAAC,EAAE,CAAA;IAC9B,eAAe,EAAE,qBAAqB,CAAA;IACtC,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,wBAAgB,eAAe,CAAC,EAC9B,GAAG,EACH,MAAM,EACN,aAAa,EACb,MAAM,EACN,gBAAgB,EAChB,eAAe,EACf,oBAA4B,GAC7B,EAAE,mBAAmB,GAAG,OAAO,CA4D/B"}
@@ -0,0 +1,56 @@
1
+ import { deleteFile } from './deleteFile.js';
2
+ import { generateURL } from './generateURL.js';
3
+ import { getFile } from './getFile.js';
4
+ import { uploadFile } from './uploadFile.js';
5
+ export function createS3Adapter({ acl, bucket, clientUploads, config, getStorageClient, signedDownloads, useCompositePrefixes = false }) {
6
+ return ({ collection, prefix = '' })=>({
7
+ name: 's3',
8
+ clientUploads,
9
+ generateURL: ({ filename, prefix: urlPrefix = '' })=>generateURL({
10
+ bucket,
11
+ collectionPrefix: prefix,
12
+ endpoint: config.endpoint,
13
+ filename,
14
+ prefix: urlPrefix,
15
+ useCompositePrefixes
16
+ }),
17
+ handleDelete: ({ doc: { prefix: docPrefix = '' }, filename })=>deleteFile({
18
+ bucket,
19
+ client: getStorageClient(),
20
+ collectionPrefix: prefix,
21
+ docPrefix,
22
+ filename,
23
+ useCompositePrefixes
24
+ }),
25
+ handleUpload: async ({ data, file })=>{
26
+ await uploadFile({
27
+ acl,
28
+ bucket,
29
+ buffer: file.buffer,
30
+ client: getStorageClient(),
31
+ collectionPrefix: prefix,
32
+ docPrefix: data.prefix,
33
+ filename: file.filename,
34
+ mimeType: file.mimeType,
35
+ tempFilePath: file.tempFilePath,
36
+ useCompositePrefixes
37
+ });
38
+ return data;
39
+ },
40
+ staticHandler: (req, { headers, params: { clientUploadContext, filename, prefix: prefixQueryParam } })=>getFile({
41
+ bucket,
42
+ client: getStorageClient(),
43
+ clientUploadContext,
44
+ collection,
45
+ collectionPrefix: prefix,
46
+ filename,
47
+ incomingHeaders: headers,
48
+ prefixQueryParam,
49
+ req,
50
+ signedDownloads,
51
+ useCompositePrefixes
52
+ })
53
+ });
54
+ }
55
+
56
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapter.ts"],"sourcesContent":["import type * as AWS from '@aws-sdk/client-s3'\nimport type {\n Adapter,\n ClientUploadsConfig,\n GeneratedAdapter,\n} from '@payloadcms/plugin-cloud-storage/types'\n\nimport type { SignedDownloadsConfig } from './getFile.js'\n\nimport { deleteFile } from './deleteFile.js'\nimport { generateURL } from './generateURL.js'\nimport { getFile } from './getFile.js'\nimport { uploadFile } from './uploadFile.js'\n\ninterface CreateS3AdapterArgs {\n acl?: 'private' | 'public-read'\n bucket: string\n clientUploads?: ClientUploadsConfig\n config: AWS.S3ClientConfig\n getStorageClient: () => AWS.S3\n signedDownloads: SignedDownloadsConfig\n useCompositePrefixes?: boolean\n}\n\nexport function createS3Adapter({\n acl,\n bucket,\n clientUploads,\n config,\n getStorageClient,\n signedDownloads,\n useCompositePrefixes = false,\n}: CreateS3AdapterArgs): Adapter {\n return ({ collection, prefix = '' }): GeneratedAdapter => ({\n name: 's3',\n clientUploads,\n\n generateURL: ({ filename, prefix: urlPrefix = '' }) =>\n generateURL({\n bucket,\n collectionPrefix: prefix,\n endpoint: config.endpoint,\n filename,\n prefix: urlPrefix,\n useCompositePrefixes,\n }),\n\n handleDelete: ({ doc: { prefix: docPrefix = '' }, filename }) =>\n deleteFile({\n bucket,\n client: getStorageClient(),\n collectionPrefix: prefix,\n docPrefix,\n filename,\n useCompositePrefixes,\n }),\n\n handleUpload: async ({ data, file }) => {\n await uploadFile({\n acl,\n bucket,\n buffer: file.buffer,\n client: getStorageClient(),\n collectionPrefix: prefix,\n docPrefix: data.prefix,\n filename: file.filename,\n mimeType: file.mimeType,\n tempFilePath: file.tempFilePath,\n useCompositePrefixes,\n })\n\n return data\n },\n\n staticHandler: (\n req,\n { headers, params: { clientUploadContext, filename, prefix: prefixQueryParam } },\n ) =>\n getFile({\n bucket,\n client: getStorageClient(),\n clientUploadContext,\n collection,\n collectionPrefix: prefix,\n filename,\n incomingHeaders: headers,\n prefixQueryParam,\n req,\n signedDownloads,\n useCompositePrefixes,\n }),\n })\n}\n"],"names":["deleteFile","generateURL","getFile","uploadFile","createS3Adapter","acl","bucket","clientUploads","config","getStorageClient","signedDownloads","useCompositePrefixes","collection","prefix","name","filename","urlPrefix","collectionPrefix","endpoint","handleDelete","doc","docPrefix","client","handleUpload","data","file","buffer","mimeType","tempFilePath","staticHandler","req","headers","params","clientUploadContext","prefixQueryParam","incomingHeaders"],"mappings":"AASA,SAASA,UAAU,QAAQ,kBAAiB;AAC5C,SAASC,WAAW,QAAQ,mBAAkB;AAC9C,SAASC,OAAO,QAAQ,eAAc;AACtC,SAASC,UAAU,QAAQ,kBAAiB;AAY5C,OAAO,SAASC,gBAAgB,EAC9BC,GAAG,EACHC,MAAM,EACNC,aAAa,EACbC,MAAM,EACNC,gBAAgB,EAChBC,eAAe,EACfC,uBAAuB,KAAK,EACR;IACpB,OAAO,CAAC,EAAEC,UAAU,EAAEC,SAAS,EAAE,EAAE,GAAwB,CAAA;YACzDC,MAAM;YACNP;YAEAN,aAAa,CAAC,EAAEc,QAAQ,EAAEF,QAAQG,YAAY,EAAE,EAAE,GAChDf,YAAY;oBACVK;oBACAW,kBAAkBJ;oBAClBK,UAAUV,OAAOU,QAAQ;oBACzBH;oBACAF,QAAQG;oBACRL;gBACF;YAEFQ,cAAc,CAAC,EAAEC,KAAK,EAAEP,QAAQQ,YAAY,EAAE,EAAE,EAAEN,QAAQ,EAAE,GAC1Df,WAAW;oBACTM;oBACAgB,QAAQb;oBACRQ,kBAAkBJ;oBAClBQ;oBACAN;oBACAJ;gBACF;YAEFY,cAAc,OAAO,EAAEC,IAAI,EAAEC,IAAI,EAAE;gBACjC,MAAMtB,WAAW;oBACfE;oBACAC;oBACAoB,QAAQD,KAAKC,MAAM;oBACnBJ,QAAQb;oBACRQ,kBAAkBJ;oBAClBQ,WAAWG,KAAKX,MAAM;oBACtBE,UAAUU,KAAKV,QAAQ;oBACvBY,UAAUF,KAAKE,QAAQ;oBACvBC,cAAcH,KAAKG,YAAY;oBAC/BjB;gBACF;gBAEA,OAAOa;YACT;YAEAK,eAAe,CACbC,KACA,EAAEC,OAAO,EAAEC,QAAQ,EAAEC,mBAAmB,EAAElB,QAAQ,EAAEF,QAAQqB,gBAAgB,EAAE,EAAE,GAEhFhC,QAAQ;oBACNI;oBACAgB,QAAQb;oBACRwB;oBACArB;oBACAK,kBAAkBJ;oBAClBE;oBACAoB,iBAAiBJ;oBACjBG;oBACAJ;oBACApB;oBACAC;gBACF;QACJ,CAAA;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"S3ClientUploadHandler.d.ts","sourceRoot":"","sources":["../../src/client/S3ClientUploadHandler.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,qBAAqB;;;;;;;aAwB5B,OAAG,aAeP,CAAA"}
1
+ {"version":3,"file":"S3ClientUploadHandler.d.ts","sourceRoot":"","sources":["../../src/client/S3ClientUploadHandler.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,qBAAqB;;;;;;;aAyBa,OAAO,aAgBpD,CAAA"}
@@ -2,7 +2,7 @@
2
2
  import { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client';
3
3
  import { formatAdminURL } from 'payload/shared';
4
4
  export const S3ClientUploadHandler = createClientUploadHandler({
5
- handler: async ({ apiRoute, collectionSlug, file, prefix, serverHandlerPath, serverURL })=>{
5
+ handler: async ({ apiRoute, collectionSlug, docPrefix, file, serverHandlerPath, serverURL })=>{
6
6
  const endpointRoute = formatAdminURL({
7
7
  apiRoute,
8
8
  path: serverHandlerPath,
@@ -11,6 +11,7 @@ export const S3ClientUploadHandler = createClientUploadHandler({
11
11
  const response = await fetch(endpointRoute, {
12
12
  body: JSON.stringify({
13
13
  collectionSlug,
14
+ docPrefix,
14
15
  filename: file.name,
15
16
  filesize: file.size,
16
17
  mimeType: file.type
@@ -22,7 +23,7 @@ export const S3ClientUploadHandler = createClientUploadHandler({
22
23
  const { errors } = await response.json();
23
24
  throw new Error(errors.reduce((acc, err)=>`${acc ? `${acc}, ` : ''}${err.message}`, ''));
24
25
  }
25
- const { url } = await response.json();
26
+ const { docPrefix: returnedDocPrefix, url } = await response.json();
26
27
  await fetch(url, {
27
28
  body: file,
28
29
  headers: {
@@ -32,7 +33,7 @@ export const S3ClientUploadHandler = createClientUploadHandler({
32
33
  method: 'PUT'
33
34
  });
34
35
  return {
35
- prefix
36
+ prefix: returnedDocPrefix
36
37
  };
37
38
  }
38
39
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/client/S3ClientUploadHandler.ts"],"sourcesContent":["'use client'\nimport { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client'\nimport { toast } from '@payloadcms/ui'\nimport { formatAdminURL } from 'payload/shared'\n\nexport const S3ClientUploadHandler = createClientUploadHandler({\n handler: async ({ apiRoute, collectionSlug, file, prefix, serverHandlerPath, serverURL }) => {\n const endpointRoute = formatAdminURL({\n apiRoute,\n path: serverHandlerPath,\n serverURL,\n })\n\n const response = await fetch(endpointRoute, {\n body: JSON.stringify({\n collectionSlug,\n filename: file.name,\n filesize: file.size,\n mimeType: file.type,\n }),\n credentials: 'include',\n method: 'POST',\n })\n\n if (!response.ok) {\n const { errors } = (await response.json()) as {\n errors: { message: string }[]\n }\n\n throw new Error(errors.reduce((acc, err) => `${acc ? `${acc}, ` : ''}${err.message}`, ''))\n }\n\n const { url } = (await response.json()) as {\n url: string\n }\n\n await fetch(url, {\n body: file,\n headers: { 'Content-Length': file.size.toString(), 'Content-Type': file.type },\n method: 'PUT',\n })\n\n return { prefix }\n },\n})\n"],"names":["createClientUploadHandler","formatAdminURL","S3ClientUploadHandler","handler","apiRoute","collectionSlug","file","prefix","serverHandlerPath","serverURL","endpointRoute","path","response","fetch","body","JSON","stringify","filename","name","filesize","size","mimeType","type","credentials","method","ok","errors","json","Error","reduce","acc","err","message","url","headers","toString"],"mappings":"AAAA;AACA,SAASA,yBAAyB,QAAQ,0CAAyC;AAEnF,SAASC,cAAc,QAAQ,iBAAgB;AAE/C,OAAO,MAAMC,wBAAwBF,0BAA0B;IAC7DG,SAAS,OAAO,EAAEC,QAAQ,EAAEC,cAAc,EAAEC,IAAI,EAAEC,MAAM,EAAEC,iBAAiB,EAAEC,SAAS,EAAE;QACtF,MAAMC,gBAAgBT,eAAe;YACnCG;YACAO,MAAMH;YACNC;QACF;QAEA,MAAMG,WAAW,MAAMC,MAAMH,eAAe;YAC1CI,MAAMC,KAAKC,SAAS,CAAC;gBACnBX;gBACAY,UAAUX,KAAKY,IAAI;gBACnBC,UAAUb,KAAKc,IAAI;gBACnBC,UAAUf,KAAKgB,IAAI;YACrB;YACAC,aAAa;YACbC,QAAQ;QACV;QAEA,IAAI,CAACZ,SAASa,EAAE,EAAE;YAChB,MAAM,EAAEC,MAAM,EAAE,GAAI,MAAMd,SAASe,IAAI;YAIvC,MAAM,IAAIC,MAAMF,OAAOG,MAAM,CAAC,CAACC,KAAKC,MAAQ,GAAGD,MAAM,GAAGA,IAAI,EAAE,CAAC,GAAG,KAAKC,IAAIC,OAAO,EAAE,EAAE;QACxF;QAEA,MAAM,EAAEC,GAAG,EAAE,GAAI,MAAMrB,SAASe,IAAI;QAIpC,MAAMd,MAAMoB,KAAK;YACfnB,MAAMR;YACN4B,SAAS;gBAAE,kBAAkB5B,KAAKc,IAAI,CAACe,QAAQ;gBAAI,gBAAgB7B,KAAKgB,IAAI;YAAC;YAC7EE,QAAQ;QACV;QAEA,OAAO;YAAEjB;QAAO;IAClB;AACF,GAAE"}
1
+ {"version":3,"sources":["../../src/client/S3ClientUploadHandler.ts"],"sourcesContent":["'use client'\nimport { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client'\nimport { formatAdminURL } from 'payload/shared'\n\nexport const S3ClientUploadHandler = createClientUploadHandler({\n handler: async ({ apiRoute, collectionSlug, docPrefix, file, serverHandlerPath, serverURL }) => {\n const endpointRoute = formatAdminURL({\n apiRoute,\n path: serverHandlerPath,\n serverURL,\n })\n\n const response = await fetch(endpointRoute, {\n body: JSON.stringify({\n collectionSlug,\n docPrefix,\n filename: file.name,\n filesize: file.size,\n mimeType: file.type,\n }),\n credentials: 'include',\n method: 'POST',\n })\n\n if (!response.ok) {\n const { errors } = (await response.json()) as {\n errors: { message: string }[]\n }\n\n throw new Error(errors.reduce((acc, err) => `${acc ? `${acc}, ` : ''}${err.message}`, ''))\n }\n\n const { docPrefix: returnedDocPrefix, url } = (await response.json()) as {\n docPrefix: string\n url: string\n }\n\n await fetch(url, {\n body: file,\n headers: { 'Content-Length': file.size.toString(), 'Content-Type': file.type },\n method: 'PUT',\n })\n\n return { prefix: returnedDocPrefix }\n },\n})\n"],"names":["createClientUploadHandler","formatAdminURL","S3ClientUploadHandler","handler","apiRoute","collectionSlug","docPrefix","file","serverHandlerPath","serverURL","endpointRoute","path","response","fetch","body","JSON","stringify","filename","name","filesize","size","mimeType","type","credentials","method","ok","errors","json","Error","reduce","acc","err","message","returnedDocPrefix","url","headers","toString","prefix"],"mappings":"AAAA;AACA,SAASA,yBAAyB,QAAQ,0CAAyC;AACnF,SAASC,cAAc,QAAQ,iBAAgB;AAE/C,OAAO,MAAMC,wBAAwBF,0BAA0B;IAC7DG,SAAS,OAAO,EAAEC,QAAQ,EAAEC,cAAc,EAAEC,SAAS,EAAEC,IAAI,EAAEC,iBAAiB,EAAEC,SAAS,EAAE;QACzF,MAAMC,gBAAgBT,eAAe;YACnCG;YACAO,MAAMH;YACNC;QACF;QAEA,MAAMG,WAAW,MAAMC,MAAMH,eAAe;YAC1CI,MAAMC,KAAKC,SAAS,CAAC;gBACnBX;gBACAC;gBACAW,UAAUV,KAAKW,IAAI;gBACnBC,UAAUZ,KAAKa,IAAI;gBACnBC,UAAUd,KAAKe,IAAI;YACrB;YACAC,aAAa;YACbC,QAAQ;QACV;QAEA,IAAI,CAACZ,SAASa,EAAE,EAAE;YAChB,MAAM,EAAEC,MAAM,EAAE,GAAI,MAAMd,SAASe,IAAI;YAIvC,MAAM,IAAIC,MAAMF,OAAOG,MAAM,CAAC,CAACC,KAAKC,MAAQ,GAAGD,MAAM,GAAGA,IAAI,EAAE,CAAC,GAAG,KAAKC,IAAIC,OAAO,EAAE,EAAE;QACxF;QAEA,MAAM,EAAE1B,WAAW2B,iBAAiB,EAAEC,GAAG,EAAE,GAAI,MAAMtB,SAASe,IAAI;QAKlE,MAAMd,MAAMqB,KAAK;YACfpB,MAAMP;YACN4B,SAAS;gBAAE,kBAAkB5B,KAAKa,IAAI,CAACgB,QAAQ;gBAAI,gBAAgB7B,KAAKe,IAAI;YAAC;YAC7EE,QAAQ;QACV;QAEA,OAAO;YAAEa,QAAQJ;QAAkB;IACrC;AACF,GAAE"}
@@ -0,0 +1,12 @@
1
+ import type * as AWS from '@aws-sdk/client-s3';
2
+ interface DeleteArgs {
3
+ bucket: string;
4
+ client: AWS.S3;
5
+ collectionPrefix?: string;
6
+ docPrefix: string;
7
+ filename: string;
8
+ useCompositePrefixes?: boolean;
9
+ }
10
+ export declare function deleteFile({ bucket, client, collectionPrefix, docPrefix, filename, useCompositePrefixes, }: DeleteArgs): Promise<void>;
11
+ export {};
12
+ //# sourceMappingURL=deleteFile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deleteFile.d.ts","sourceRoot":"","sources":["../src/deleteFile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,oBAAoB,CAAA;AAI9C,UAAU,UAAU;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,GAAG,CAAC,EAAE,CAAA;IACd,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,MAAM,EACN,gBAAqB,EACrB,SAAS,EACT,QAAQ,EACR,oBAA4B,GAC7B,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAY5B"}
@@ -0,0 +1,15 @@
1
+ import { getFileKey } from '@payloadcms/plugin-cloud-storage/utilities';
2
+ export async function deleteFile({ bucket, client, collectionPrefix = '', docPrefix, filename, useCompositePrefixes = false }) {
3
+ const key = getFileKey({
4
+ collectionPrefix,
5
+ docPrefix,
6
+ filename,
7
+ useCompositePrefixes
8
+ });
9
+ await client.deleteObject({
10
+ Bucket: bucket,
11
+ Key: key
12
+ });
13
+ }
14
+
15
+ //# sourceMappingURL=deleteFile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/deleteFile.ts"],"sourcesContent":["import type * as AWS from '@aws-sdk/client-s3'\n\nimport { getFileKey } from '@payloadcms/plugin-cloud-storage/utilities'\n\ninterface DeleteArgs {\n bucket: string\n client: AWS.S3\n collectionPrefix?: string\n docPrefix: string\n filename: string\n useCompositePrefixes?: boolean\n}\n\nexport async function deleteFile({\n bucket,\n client,\n collectionPrefix = '',\n docPrefix,\n filename,\n useCompositePrefixes = false,\n}: DeleteArgs): Promise<void> {\n const key = getFileKey({\n collectionPrefix,\n docPrefix,\n filename,\n useCompositePrefixes,\n })\n\n await client.deleteObject({\n Bucket: bucket,\n Key: key,\n })\n}\n"],"names":["getFileKey","deleteFile","bucket","client","collectionPrefix","docPrefix","filename","useCompositePrefixes","key","deleteObject","Bucket","Key"],"mappings":"AAEA,SAASA,UAAU,QAAQ,6CAA4C;AAWvE,OAAO,eAAeC,WAAW,EAC/BC,MAAM,EACNC,MAAM,EACNC,mBAAmB,EAAE,EACrBC,SAAS,EACTC,QAAQ,EACRC,uBAAuB,KAAK,EACjB;IACX,MAAMC,MAAMR,WAAW;QACrBI;QACAC;QACAC;QACAC;IACF;IAEA,MAAMJ,OAAOM,YAAY,CAAC;QACxBC,QAAQR;QACRS,KAAKH;IACP;AACF"}
@@ -8,7 +8,8 @@ interface Args {
8
8
  bucket: string;
9
9
  collections: S3StorageOptions['collections'];
10
10
  getStorageClient: () => AWS.S3;
11
+ useCompositePrefixes?: boolean;
11
12
  }
12
- export declare const getGenerateSignedURLHandler: ({ access, acl, bucket, collections, getStorageClient, }: Args) => PayloadHandler;
13
+ export declare const getGenerateSignedURLHandler: ({ access, acl, bucket, collections, getStorageClient, useCompositePrefixes, }: Args) => PayloadHandler;
13
14
  export {};
14
15
  //# sourceMappingURL=generateSignedURL.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"generateSignedURL.d.ts","sourceRoot":"","sources":["../src/generateSignedURL.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAA;AACjF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C,OAAO,KAAK,GAAG,MAAM,oBAAoB,CAAA;AAMzC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAMlD,UAAU,IAAI;IACZ,MAAM,CAAC,EAAE,mBAAmB,CAAA;IAC5B,GAAG,CAAC,EAAE,SAAS,GAAG,aAAa,CAAA;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAA;IAC5C,gBAAgB,EAAE,MAAM,GAAG,CAAC,EAAE,CAAA;CAC/B;AAID,eAAO,MAAM,2BAA2B,4DAMrC,IAAI,KAAG,cAgET,CAAA"}
1
+ {"version":3,"file":"generateSignedURL.d.ts","sourceRoot":"","sources":["../src/generateSignedURL.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAA;AACjF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C,OAAO,KAAK,GAAG,MAAM,oBAAoB,CAAA;AAMzC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAMlD,UAAU,IAAI;IACZ,MAAM,CAAC,EAAE,mBAAmB,CAAA;IAC5B,GAAG,CAAC,EAAE,SAAS,GAAG,aAAa,CAAA;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAA;IAC5C,gBAAgB,EAAE,MAAM,GAAG,CAAC,EAAE,CAAA;IAC9B,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAID,eAAO,MAAM,2BAA2B,kFAOrC,IAAI,KAAG,cA0ET,CAAA"}
@@ -1,13 +1,13 @@
1
1
  import * as AWS from '@aws-sdk/client-s3';
2
2
  import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
3
- import path from 'path';
3
+ import { getFileKey } from '@payloadcms/plugin-cloud-storage/utilities';
4
4
  import { APIError, Forbidden } from 'payload';
5
5
  import { sanitizeFilename } from 'payload/shared';
6
6
  const bytesToMB = (bytes)=>{
7
7
  return bytes / 1024 / 1024;
8
8
  };
9
9
  const defaultAccess = ({ req })=>!!req.user;
10
- export const getGenerateSignedURLHandler = ({ access = defaultAccess, acl, bucket, collections, getStorageClient })=>{
10
+ export const getGenerateSignedURLHandler = ({ access = defaultAccess, acl, bucket, collections, getStorageClient, useCompositePrefixes = false })=>{
11
11
  return async (req)=>{
12
12
  if (!req.json) {
13
13
  throw new APIError('Content-Type expected to be application/json', 400);
@@ -16,12 +16,12 @@ export const getGenerateSignedURLHandler = ({ access = defaultAccess, acl, bucke
16
16
  if (filesizeLimit === Infinity) {
17
17
  filesizeLimit = undefined;
18
18
  }
19
- const { collectionSlug, filename, filesize, mimeType } = await req.json();
19
+ const { collectionSlug, docPrefix, filename, filesize, mimeType } = await req.json();
20
20
  const collectionS3Config = collections[collectionSlug];
21
21
  if (!collectionS3Config) {
22
22
  throw new APIError(`Collection ${collectionSlug} was not found in S3 options`);
23
23
  }
24
- const prefix = typeof collectionS3Config === 'object' && collectionS3Config.prefix || '';
24
+ const collectionPrefix = typeof collectionS3Config === 'object' && collectionS3Config.prefix || '';
25
25
  if (!await access({
26
26
  collectionSlug,
27
27
  req
@@ -29,7 +29,12 @@ export const getGenerateSignedURLHandler = ({ access = defaultAccess, acl, bucke
29
29
  throw new Forbidden();
30
30
  }
31
31
  const sanitizedFilename = sanitizeFilename(filename);
32
- const fileKey = path.posix.join(prefix, sanitizedFilename);
32
+ const fileKey = getFileKey({
33
+ collectionPrefix,
34
+ docPrefix: docPrefix || '',
35
+ filename: sanitizedFilename,
36
+ useCompositePrefixes
37
+ });
33
38
  const signableHeaders = new Set();
34
39
  if (filesizeLimit) {
35
40
  if (filesize > filesizeLimit) {
@@ -49,6 +54,7 @@ export const getGenerateSignedURLHandler = ({ access = defaultAccess, acl, bucke
49
54
  signableHeaders
50
55
  });
51
56
  return Response.json({
57
+ docPrefix: docPrefix || '',
52
58
  url
53
59
  });
54
60
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/generateSignedURL.ts"],"sourcesContent":["import type { ClientUploadsAccess } from '@payloadcms/plugin-cloud-storage/types'\nimport type { PayloadHandler } from 'payload'\n\nimport * as AWS from '@aws-sdk/client-s3'\nimport { getSignedUrl } from '@aws-sdk/s3-request-presigner'\nimport path from 'path'\nimport { APIError, Forbidden, ValidationError } from 'payload'\nimport { sanitizeFilename } from 'payload/shared'\n\nimport type { S3StorageOptions } from './index.js'\n\nconst bytesToMB = (bytes: number) => {\n return bytes / 1024 / 1024\n}\n\ninterface Args {\n access?: ClientUploadsAccess\n acl?: 'private' | 'public-read'\n bucket: string\n collections: S3StorageOptions['collections']\n getStorageClient: () => AWS.S3\n}\n\nconst defaultAccess: Args['access'] = ({ req }) => !!req.user\n\nexport const getGenerateSignedURLHandler = ({\n access = defaultAccess,\n acl,\n bucket,\n collections,\n getStorageClient,\n}: Args): PayloadHandler => {\n return async (req) => {\n if (!req.json) {\n throw new APIError('Content-Type expected to be application/json', 400)\n }\n\n let filesizeLimit = req.payload.config.upload.limits?.fileSize\n\n if (filesizeLimit === Infinity) {\n filesizeLimit = undefined\n }\n\n const { collectionSlug, filename, filesize, mimeType } = (await req.json()) as {\n collectionSlug: string\n filename: string\n filesize: number\n mimeType: string\n }\n\n const collectionS3Config = collections[collectionSlug]\n if (!collectionS3Config) {\n throw new APIError(`Collection ${collectionSlug} was not found in S3 options`)\n }\n\n const prefix = (typeof collectionS3Config === 'object' && collectionS3Config.prefix) || ''\n\n if (!(await access({ collectionSlug, req }))) {\n throw new Forbidden()\n }\n\n const sanitizedFilename = sanitizeFilename(filename)\n const fileKey = path.posix.join(prefix, sanitizedFilename)\n\n const signableHeaders = new Set<string>()\n\n if (filesizeLimit) {\n if (filesize > filesizeLimit) {\n throw new APIError(\n `Exceeded file size limit. Limit: ${bytesToMB(filesizeLimit).toFixed(2)}MB, got: ${bytesToMB(filesize).toFixed(2)}MB`,\n 400,\n )\n }\n\n // Still force S3 to validate\n signableHeaders.add('content-length')\n }\n\n const url = await getSignedUrl(\n getStorageClient(),\n new AWS.PutObjectCommand({\n ACL: acl,\n Bucket: bucket,\n ContentLength: filesizeLimit ? Math.min(filesize, filesizeLimit) : undefined,\n ContentType: mimeType,\n Key: fileKey,\n }),\n {\n expiresIn: 600,\n signableHeaders,\n },\n )\n\n return Response.json({ url })\n }\n}\n"],"names":["AWS","getSignedUrl","path","APIError","Forbidden","sanitizeFilename","bytesToMB","bytes","defaultAccess","req","user","getGenerateSignedURLHandler","access","acl","bucket","collections","getStorageClient","json","filesizeLimit","payload","config","upload","limits","fileSize","Infinity","undefined","collectionSlug","filename","filesize","mimeType","collectionS3Config","prefix","sanitizedFilename","fileKey","posix","join","signableHeaders","Set","toFixed","add","url","PutObjectCommand","ACL","Bucket","ContentLength","Math","min","ContentType","Key","expiresIn","Response"],"mappings":"AAGA,YAAYA,SAAS,qBAAoB;AACzC,SAASC,YAAY,QAAQ,gCAA+B;AAC5D,OAAOC,UAAU,OAAM;AACvB,SAASC,QAAQ,EAAEC,SAAS,QAAyB,UAAS;AAC9D,SAASC,gBAAgB,QAAQ,iBAAgB;AAIjD,MAAMC,YAAY,CAACC;IACjB,OAAOA,QAAQ,OAAO;AACxB;AAUA,MAAMC,gBAAgC,CAAC,EAAEC,GAAG,EAAE,GAAK,CAAC,CAACA,IAAIC,IAAI;AAE7D,OAAO,MAAMC,8BAA8B,CAAC,EAC1CC,SAASJ,aAAa,EACtBK,GAAG,EACHC,MAAM,EACNC,WAAW,EACXC,gBAAgB,EACX;IACL,OAAO,OAAOP;QACZ,IAAI,CAACA,IAAIQ,IAAI,EAAE;YACb,MAAM,IAAId,SAAS,gDAAgD;QACrE;QAEA,IAAIe,gBAAgBT,IAAIU,OAAO,CAACC,MAAM,CAACC,MAAM,CAACC,MAAM,EAAEC;QAEtD,IAAIL,kBAAkBM,UAAU;YAC9BN,gBAAgBO;QAClB;QAEA,MAAM,EAAEC,cAAc,EAAEC,QAAQ,EAAEC,QAAQ,EAAEC,QAAQ,EAAE,GAAI,MAAMpB,IAAIQ,IAAI;QAOxE,MAAMa,qBAAqBf,WAAW,CAACW,eAAe;QACtD,IAAI,CAACI,oBAAoB;YACvB,MAAM,IAAI3B,SAAS,CAAC,WAAW,EAAEuB,eAAe,4BAA4B,CAAC;QAC/E;QAEA,MAAMK,SAAS,AAAC,OAAOD,uBAAuB,YAAYA,mBAAmBC,MAAM,IAAK;QAExF,IAAI,CAAE,MAAMnB,OAAO;YAAEc;YAAgBjB;QAAI,IAAK;YAC5C,MAAM,IAAIL;QACZ;QAEA,MAAM4B,oBAAoB3B,iBAAiBsB;QAC3C,MAAMM,UAAU/B,KAAKgC,KAAK,CAACC,IAAI,CAACJ,QAAQC;QAExC,MAAMI,kBAAkB,IAAIC;QAE5B,IAAInB,eAAe;YACjB,IAAIU,WAAWV,eAAe;gBAC5B,MAAM,IAAIf,SACR,CAAC,iCAAiC,EAAEG,UAAUY,eAAeoB,OAAO,CAAC,GAAG,SAAS,EAAEhC,UAAUsB,UAAUU,OAAO,CAAC,GAAG,EAAE,CAAC,EACrH;YAEJ;YAEA,6BAA6B;YAC7BF,gBAAgBG,GAAG,CAAC;QACtB;QAEA,MAAMC,MAAM,MAAMvC,aAChBe,oBACA,IAAIhB,IAAIyC,gBAAgB,CAAC;YACvBC,KAAK7B;YACL8B,QAAQ7B;YACR8B,eAAe1B,gBAAgB2B,KAAKC,GAAG,CAAClB,UAAUV,iBAAiBO;YACnEsB,aAAalB;YACbmB,KAAKf;QACP,IACA;YACEgB,WAAW;YACXb;QACF;QAGF,OAAOc,SAASjC,IAAI,CAAC;YAAEuB;QAAI;IAC7B;AACF,EAAC"}
1
+ {"version":3,"sources":["../src/generateSignedURL.ts"],"sourcesContent":["import type { ClientUploadsAccess } from '@payloadcms/plugin-cloud-storage/types'\nimport type { PayloadHandler } from 'payload'\n\nimport * as AWS from '@aws-sdk/client-s3'\nimport { getSignedUrl } from '@aws-sdk/s3-request-presigner'\nimport { getFileKey } from '@payloadcms/plugin-cloud-storage/utilities'\nimport { APIError, Forbidden } from 'payload'\nimport { sanitizeFilename } from 'payload/shared'\n\nimport type { S3StorageOptions } from './index.js'\n\nconst bytesToMB = (bytes: number) => {\n return bytes / 1024 / 1024\n}\n\ninterface Args {\n access?: ClientUploadsAccess\n acl?: 'private' | 'public-read'\n bucket: string\n collections: S3StorageOptions['collections']\n getStorageClient: () => AWS.S3\n useCompositePrefixes?: boolean\n}\n\nconst defaultAccess: Args['access'] = ({ req }) => !!req.user\n\nexport const getGenerateSignedURLHandler = ({\n access = defaultAccess,\n acl,\n bucket,\n collections,\n getStorageClient,\n useCompositePrefixes = false,\n}: Args): PayloadHandler => {\n return async (req) => {\n if (!req.json) {\n throw new APIError('Content-Type expected to be application/json', 400)\n }\n\n let filesizeLimit = req.payload.config.upload.limits?.fileSize\n\n if (filesizeLimit === Infinity) {\n filesizeLimit = undefined\n }\n\n const { collectionSlug, docPrefix, filename, filesize, mimeType } = (await req.json()) as {\n collectionSlug: string\n docPrefix?: string\n filename: string\n filesize: number\n mimeType: string\n }\n\n const collectionS3Config = collections[collectionSlug]\n if (!collectionS3Config) {\n throw new APIError(`Collection ${collectionSlug} was not found in S3 options`)\n }\n\n const collectionPrefix =\n (typeof collectionS3Config === 'object' && collectionS3Config.prefix) || ''\n\n if (!(await access({ collectionSlug, req }))) {\n throw new Forbidden()\n }\n\n const sanitizedFilename = sanitizeFilename(filename)\n const fileKey = getFileKey({\n collectionPrefix,\n docPrefix: docPrefix || '',\n filename: sanitizedFilename,\n useCompositePrefixes,\n })\n\n const signableHeaders = new Set<string>()\n\n if (filesizeLimit) {\n if (filesize > filesizeLimit) {\n throw new APIError(\n `Exceeded file size limit. Limit: ${bytesToMB(filesizeLimit).toFixed(2)}MB, got: ${bytesToMB(filesize).toFixed(2)}MB`,\n 400,\n )\n }\n\n // Still force S3 to validate\n signableHeaders.add('content-length')\n }\n\n const url = await getSignedUrl(\n getStorageClient(),\n new AWS.PutObjectCommand({\n ACL: acl,\n Bucket: bucket,\n ContentLength: filesizeLimit ? Math.min(filesize, filesizeLimit) : undefined,\n ContentType: mimeType,\n Key: fileKey,\n }),\n {\n expiresIn: 600,\n signableHeaders,\n },\n )\n\n return Response.json({\n docPrefix: docPrefix || '',\n url,\n })\n }\n}\n"],"names":["AWS","getSignedUrl","getFileKey","APIError","Forbidden","sanitizeFilename","bytesToMB","bytes","defaultAccess","req","user","getGenerateSignedURLHandler","access","acl","bucket","collections","getStorageClient","useCompositePrefixes","json","filesizeLimit","payload","config","upload","limits","fileSize","Infinity","undefined","collectionSlug","docPrefix","filename","filesize","mimeType","collectionS3Config","collectionPrefix","prefix","sanitizedFilename","fileKey","signableHeaders","Set","toFixed","add","url","PutObjectCommand","ACL","Bucket","ContentLength","Math","min","ContentType","Key","expiresIn","Response"],"mappings":"AAGA,YAAYA,SAAS,qBAAoB;AACzC,SAASC,YAAY,QAAQ,gCAA+B;AAC5D,SAASC,UAAU,QAAQ,6CAA4C;AACvE,SAASC,QAAQ,EAAEC,SAAS,QAAQ,UAAS;AAC7C,SAASC,gBAAgB,QAAQ,iBAAgB;AAIjD,MAAMC,YAAY,CAACC;IACjB,OAAOA,QAAQ,OAAO;AACxB;AAWA,MAAMC,gBAAgC,CAAC,EAAEC,GAAG,EAAE,GAAK,CAAC,CAACA,IAAIC,IAAI;AAE7D,OAAO,MAAMC,8BAA8B,CAAC,EAC1CC,SAASJ,aAAa,EACtBK,GAAG,EACHC,MAAM,EACNC,WAAW,EACXC,gBAAgB,EAChBC,uBAAuB,KAAK,EACvB;IACL,OAAO,OAAOR;QACZ,IAAI,CAACA,IAAIS,IAAI,EAAE;YACb,MAAM,IAAIf,SAAS,gDAAgD;QACrE;QAEA,IAAIgB,gBAAgBV,IAAIW,OAAO,CAACC,MAAM,CAACC,MAAM,CAACC,MAAM,EAAEC;QAEtD,IAAIL,kBAAkBM,UAAU;YAC9BN,gBAAgBO;QAClB;QAEA,MAAM,EAAEC,cAAc,EAAEC,SAAS,EAAEC,QAAQ,EAAEC,QAAQ,EAAEC,QAAQ,EAAE,GAAI,MAAMtB,IAAIS,IAAI;QAQnF,MAAMc,qBAAqBjB,WAAW,CAACY,eAAe;QACtD,IAAI,CAACK,oBAAoB;YACvB,MAAM,IAAI7B,SAAS,CAAC,WAAW,EAAEwB,eAAe,4BAA4B,CAAC;QAC/E;QAEA,MAAMM,mBACJ,AAAC,OAAOD,uBAAuB,YAAYA,mBAAmBE,MAAM,IAAK;QAE3E,IAAI,CAAE,MAAMtB,OAAO;YAAEe;YAAgBlB;QAAI,IAAK;YAC5C,MAAM,IAAIL;QACZ;QAEA,MAAM+B,oBAAoB9B,iBAAiBwB;QAC3C,MAAMO,UAAUlC,WAAW;YACzB+B;YACAL,WAAWA,aAAa;YACxBC,UAAUM;YACVlB;QACF;QAEA,MAAMoB,kBAAkB,IAAIC;QAE5B,IAAInB,eAAe;YACjB,IAAIW,WAAWX,eAAe;gBAC5B,MAAM,IAAIhB,SACR,CAAC,iCAAiC,EAAEG,UAAUa,eAAeoB,OAAO,CAAC,GAAG,SAAS,EAAEjC,UAAUwB,UAAUS,OAAO,CAAC,GAAG,EAAE,CAAC,EACrH;YAEJ;YAEA,6BAA6B;YAC7BF,gBAAgBG,GAAG,CAAC;QACtB;QAEA,MAAMC,MAAM,MAAMxC,aAChBe,oBACA,IAAIhB,IAAI0C,gBAAgB,CAAC;YACvBC,KAAK9B;YACL+B,QAAQ9B;YACR+B,eAAe1B,gBAAgB2B,KAAKC,GAAG,CAACjB,UAAUX,iBAAiBO;YACnEsB,aAAajB;YACbkB,KAAKb;QACP,IACA;YACEc,WAAW;YACXb;QACF;QAGF,OAAOc,SAASjC,IAAI,CAAC;YACnBU,WAAWA,aAAa;YACxBa;QACF;IACF;AACF,EAAC"}
@@ -1,9 +1,12 @@
1
1
  import type * as AWS from '@aws-sdk/client-s3';
2
- import type { GenerateURL } from '@payloadcms/plugin-cloud-storage/types';
3
- interface Args {
2
+ interface GenerateURLArgs {
4
3
  bucket: string;
5
- config: AWS.S3ClientConfig;
4
+ collectionPrefix?: string;
5
+ endpoint?: AWS.S3ClientConfig['endpoint'];
6
+ filename: string;
7
+ prefix: string;
8
+ useCompositePrefixes?: boolean;
6
9
  }
7
- export declare const getGenerateURL: ({ bucket, config: { endpoint } }: Args) => GenerateURL;
10
+ export declare function generateURL({ bucket, collectionPrefix, endpoint, filename, prefix, useCompositePrefixes, }: GenerateURLArgs): string;
8
11
  export {};
9
12
  //# sourceMappingURL=generateURL.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"generateURL.d.ts","sourceRoot":"","sources":["../src/generateURL.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,oBAAoB,CAAA;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAA;AAIzE,UAAU,IAAI;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,GAAG,CAAC,cAAc,CAAA;CAC3B;AAED,eAAO,MAAM,cAAc,qCACU,IAAI,KAAG,WAIzC,CAAA"}
1
+ {"version":3,"file":"generateURL.d.ts","sourceRoot":"","sources":["../src/generateURL.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,oBAAoB,CAAA;AAI9C,UAAU,eAAe;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,EAAE,GAAG,CAAC,cAAc,CAAC,UAAU,CAAC,CAAA;IACzC,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,wBAAgB,WAAW,CAAC,EAC1B,MAAM,EACN,gBAAqB,EACrB,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,oBAA4B,GAC7B,EAAE,eAAe,GAAG,MAAM,CAW1B"}
@@ -1,7 +1,13 @@
1
- import path from 'path';
2
- export const getGenerateURL = ({ bucket, config: { endpoint } })=>({ filename, prefix = '' })=>{
3
- const stringifiedEndpoint = typeof endpoint === 'string' ? endpoint : endpoint?.toString();
4
- return `${stringifiedEndpoint}/${bucket}/${path.posix.join(prefix, encodeURIComponent(filename))}`;
5
- };
1
+ import { getFileKey } from '@payloadcms/plugin-cloud-storage/utilities';
2
+ export function generateURL({ bucket, collectionPrefix = '', endpoint, filename, prefix, useCompositePrefixes = false }) {
3
+ const fileKey = getFileKey({
4
+ collectionPrefix,
5
+ docPrefix: prefix,
6
+ filename: encodeURIComponent(filename),
7
+ useCompositePrefixes
8
+ });
9
+ const stringifiedEndpoint = typeof endpoint === 'string' ? endpoint : endpoint?.toString();
10
+ return `${stringifiedEndpoint}/${bucket}/${fileKey}`;
11
+ }
6
12
 
7
13
  //# sourceMappingURL=generateURL.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/generateURL.ts"],"sourcesContent":["import type * as AWS from '@aws-sdk/client-s3'\nimport type { GenerateURL } from '@payloadcms/plugin-cloud-storage/types'\n\nimport path from 'path'\n\ninterface Args {\n bucket: string\n config: AWS.S3ClientConfig\n}\n\nexport const getGenerateURL =\n ({ bucket, config: { endpoint } }: Args): GenerateURL =>\n ({ filename, prefix = '' }) => {\n const stringifiedEndpoint = typeof endpoint === 'string' ? endpoint : endpoint?.toString()\n return `${stringifiedEndpoint}/${bucket}/${path.posix.join(prefix, encodeURIComponent(filename))}`\n }\n"],"names":["path","getGenerateURL","bucket","config","endpoint","filename","prefix","stringifiedEndpoint","toString","posix","join","encodeURIComponent"],"mappings":"AAGA,OAAOA,UAAU,OAAM;AAOvB,OAAO,MAAMC,iBACX,CAAC,EAAEC,MAAM,EAAEC,QAAQ,EAAEC,QAAQ,EAAE,EAAQ,GACvC,CAAC,EAAEC,QAAQ,EAAEC,SAAS,EAAE,EAAE;QACxB,MAAMC,sBAAsB,OAAOH,aAAa,WAAWA,WAAWA,UAAUI;QAChF,OAAO,GAAGD,oBAAoB,CAAC,EAAEL,OAAO,CAAC,EAAEF,KAAKS,KAAK,CAACC,IAAI,CAACJ,QAAQK,mBAAmBN,YAAY;IACpG,EAAC"}
1
+ {"version":3,"sources":["../src/generateURL.ts"],"sourcesContent":["import type * as AWS from '@aws-sdk/client-s3'\n\nimport { getFileKey } from '@payloadcms/plugin-cloud-storage/utilities'\n\ninterface GenerateURLArgs {\n bucket: string\n collectionPrefix?: string\n endpoint?: AWS.S3ClientConfig['endpoint']\n filename: string\n prefix: string\n useCompositePrefixes?: boolean\n}\n\nexport function generateURL({\n bucket,\n collectionPrefix = '',\n endpoint,\n filename,\n prefix,\n useCompositePrefixes = false,\n}: GenerateURLArgs): string {\n const fileKey = getFileKey({\n collectionPrefix,\n docPrefix: prefix,\n filename: encodeURIComponent(filename),\n useCompositePrefixes,\n })\n\n const stringifiedEndpoint = typeof endpoint === 'string' ? endpoint : endpoint?.toString()\n\n return `${stringifiedEndpoint}/${bucket}/${fileKey}`\n}\n"],"names":["getFileKey","generateURL","bucket","collectionPrefix","endpoint","filename","prefix","useCompositePrefixes","fileKey","docPrefix","encodeURIComponent","stringifiedEndpoint","toString"],"mappings":"AAEA,SAASA,UAAU,QAAQ,6CAA4C;AAWvE,OAAO,SAASC,YAAY,EAC1BC,MAAM,EACNC,mBAAmB,EAAE,EACrBC,QAAQ,EACRC,QAAQ,EACRC,MAAM,EACNC,uBAAuB,KAAK,EACZ;IAChB,MAAMC,UAAUR,WAAW;QACzBG;QACAM,WAAWH;QACXD,UAAUK,mBAAmBL;QAC7BE;IACF;IAEA,MAAMI,sBAAsB,OAAOP,aAAa,WAAWA,WAAWA,UAAUQ;IAEhF,OAAO,GAAGD,oBAAoB,CAAC,EAAET,OAAO,CAAC,EAAEM,SAAS;AACtD"}
@@ -0,0 +1,27 @@
1
+ import type * as AWS from '@aws-sdk/client-s3';
2
+ import type { CollectionConfig, PayloadRequest } from 'payload';
3
+ export type SignedDownloadsConfig = {
4
+ /** @default 7200 */
5
+ expiresIn?: number;
6
+ shouldUseSignedURL?(args: {
7
+ collection: CollectionConfig;
8
+ filename: string;
9
+ req: PayloadRequest;
10
+ }): boolean | Promise<boolean>;
11
+ } | boolean;
12
+ interface GetFileArgs {
13
+ bucket: string;
14
+ client: AWS.S3;
15
+ clientUploadContext?: unknown;
16
+ collection: CollectionConfig;
17
+ collectionPrefix?: string;
18
+ filename: string;
19
+ incomingHeaders?: Headers;
20
+ prefixQueryParam?: string;
21
+ req: PayloadRequest;
22
+ signedDownloads: SignedDownloadsConfig;
23
+ useCompositePrefixes?: boolean;
24
+ }
25
+ export declare function getFile({ bucket, client, clientUploadContext, collection, collectionPrefix, filename, incomingHeaders, prefixQueryParam, req, signedDownloads, useCompositePrefixes, }: GetFileArgs): Promise<Response>;
26
+ export {};
27
+ //# sourceMappingURL=getFile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getFile.d.ts","sourceRoot":"","sources":["../src/getFile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,oBAAoB,CAAA;AAC9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAY/D,MAAM,MAAM,qBAAqB,GAC7B;IACE,oBAAoB;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,kBAAkB,CAAC,CAAC,IAAI,EAAE;QACxB,UAAU,EAAE,gBAAgB,CAAA;QAC5B,QAAQ,EAAE,MAAM,CAAA;QAChB,GAAG,EAAE,cAAc,CAAA;KACpB,GAAG,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CAC/B,GACD,OAAO,CAAA;AAEX,UAAU,WAAW;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,GAAG,CAAC,EAAE,CAAA;IACd,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B,UAAU,EAAE,gBAAgB,CAAA;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,GAAG,EAAE,cAAc,CAAA;IACnB,eAAe,EAAE,qBAAqB,CAAA;IACtC,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AA8BD,wBAAsB,OAAO,CAAC,EAC5B,MAAM,EACN,MAAM,EACN,mBAAmB,EACnB,UAAU,EACV,gBAAqB,EACrB,QAAQ,EACR,eAAe,EACf,gBAAgB,EAChB,GAAG,EACH,eAAe,EACf,oBAA4B,GAC7B,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CA8JjC"}
@@ -0,0 +1,174 @@
1
+ import { GetObjectCommand } from '@aws-sdk/client-s3';
2
+ import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
3
+ import { getFilePrefix as getDocPrefix, getFileKey } from '@payloadcms/plugin-cloud-storage/utilities';
4
+ import { getRangeRequestInfo } from 'payload/internal';
5
+ import { sanitizeFilename } from 'payload/shared';
6
+ const isNodeReadableStream = (body)=>{
7
+ return typeof body === 'object' && body !== null && 'pipe' in body && typeof body.pipe === 'function' && 'destroy' in body && typeof body.destroy === 'function';
8
+ };
9
+ const abortRequestAndDestroyStream = ({ abortController, object })=>{
10
+ try {
11
+ abortController.abort();
12
+ } catch {
13
+ /* noop */ }
14
+ if (object?.Body && isNodeReadableStream(object.Body)) {
15
+ object.Body.destroy();
16
+ }
17
+ };
18
+ export async function getFile({ bucket, client, clientUploadContext, collection, collectionPrefix = '', filename, incomingHeaders, prefixQueryParam, req, signedDownloads, useCompositePrefixes = false }) {
19
+ let object = undefined;
20
+ let streamed = false;
21
+ const abortController = new AbortController();
22
+ if (req.signal) {
23
+ req.signal.addEventListener('abort', ()=>{
24
+ abortRequestAndDestroyStream({
25
+ abortController,
26
+ object
27
+ });
28
+ });
29
+ }
30
+ try {
31
+ const docPrefix = await getDocPrefix({
32
+ clientUploadContext,
33
+ collection,
34
+ filename,
35
+ prefixQueryParam,
36
+ req
37
+ });
38
+ const key = getFileKey({
39
+ collectionPrefix,
40
+ docPrefix,
41
+ filename: sanitizeFilename(filename),
42
+ useCompositePrefixes
43
+ });
44
+ if (signedDownloads && !clientUploadContext) {
45
+ let useSignedURL = true;
46
+ if (typeof signedDownloads === 'object' && typeof signedDownloads.shouldUseSignedURL === 'function') {
47
+ useSignedURL = await signedDownloads.shouldUseSignedURL({
48
+ collection,
49
+ filename,
50
+ req
51
+ });
52
+ }
53
+ if (useSignedURL) {
54
+ const command = new GetObjectCommand({
55
+ Bucket: bucket,
56
+ Key: key
57
+ });
58
+ const signedUrl = await getSignedUrl(client, command, typeof signedDownloads === 'object' ? signedDownloads : {
59
+ expiresIn: 7200
60
+ });
61
+ return Response.redirect(signedUrl, 302);
62
+ }
63
+ }
64
+ // Get file size first for range validation and to set Content-Length header before streaming
65
+ const headObject = await client.headObject({
66
+ Bucket: bucket,
67
+ Key: key
68
+ });
69
+ const fileSize = headObject.ContentLength;
70
+ if (!fileSize) {
71
+ return new Response('Internal Server Error', {
72
+ status: 500
73
+ });
74
+ }
75
+ // Handle range request
76
+ const rangeHeader = req.headers.get('range');
77
+ const rangeResult = getRangeRequestInfo({
78
+ fileSize,
79
+ rangeHeader
80
+ });
81
+ if (rangeResult.type === 'invalid') {
82
+ return new Response(null, {
83
+ headers: new Headers(rangeResult.headers),
84
+ status: rangeResult.status
85
+ });
86
+ }
87
+ const rangeForS3 = rangeResult.type === 'partial' ? `bytes=${rangeResult.rangeStart}-${rangeResult.rangeEnd}` : undefined;
88
+ let headers = new Headers(incomingHeaders);
89
+ // Add range-related headers from the result
90
+ for (const [headerKey, value] of Object.entries(rangeResult.headers)){
91
+ headers.append(headerKey, value);
92
+ }
93
+ headers.append('Content-Type', String(headObject.ContentType));
94
+ if (headObject.ETag) {
95
+ headers.append('ETag', headObject.ETag);
96
+ }
97
+ // Add Content-Security-Policy header for SVG files to prevent executable code
98
+ if (headObject.ContentType === 'image/svg+xml') {
99
+ headers.append('Content-Security-Policy', "script-src 'none'");
100
+ }
101
+ const etagFromHeaders = req.headers.get('etag') || req.headers.get('if-none-match');
102
+ const objectEtag = headObject.ETag;
103
+ if (collection.upload && typeof collection.upload === 'object' && typeof collection.upload.modifyResponseHeaders === 'function') {
104
+ headers = collection.upload.modifyResponseHeaders({
105
+ headers
106
+ }) || headers;
107
+ }
108
+ if (etagFromHeaders && etagFromHeaders === objectEtag) {
109
+ return new Response(null, {
110
+ headers,
111
+ status: 304
112
+ });
113
+ }
114
+ object = await client.getObject({
115
+ Bucket: bucket,
116
+ Key: key,
117
+ Range: rangeForS3
118
+ }, {
119
+ abortSignal: abortController.signal
120
+ });
121
+ if (!object.Body) {
122
+ return new Response(null, {
123
+ status: 404,
124
+ statusText: 'Not Found'
125
+ });
126
+ }
127
+ if (!isNodeReadableStream(object.Body)) {
128
+ req.payload.logger.error({
129
+ key,
130
+ msg: 'S3 object body is not a readable stream'
131
+ });
132
+ return new Response('Internal Server Error', {
133
+ status: 500
134
+ });
135
+ }
136
+ const stream = object.Body;
137
+ stream.on('error', (err)=>{
138
+ req.payload.logger.error({
139
+ err,
140
+ key,
141
+ msg: 'Error while streaming S3 object (aborting)'
142
+ });
143
+ abortRequestAndDestroyStream({
144
+ abortController,
145
+ object
146
+ });
147
+ });
148
+ streamed = true;
149
+ return new Response(stream, {
150
+ headers,
151
+ status: rangeResult.status
152
+ });
153
+ } catch (err) {
154
+ if (err && typeof err === 'object' && ('name' in err && (err.name === 'NoSuchKey' || err.name === 'NotFound') || 'httpStatusCode' in err && err.httpStatusCode === 404)) {
155
+ return new Response(null, {
156
+ status: 404,
157
+ statusText: 'Not Found'
158
+ });
159
+ }
160
+ req.payload.logger.error(err);
161
+ return new Response('Internal Server Error', {
162
+ status: 500
163
+ });
164
+ } finally{
165
+ if (!streamed) {
166
+ abortRequestAndDestroyStream({
167
+ abortController,
168
+ object
169
+ });
170
+ }
171
+ }
172
+ }
173
+
174
+ //# sourceMappingURL=getFile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/getFile.ts"],"sourcesContent":["import type * as AWS from '@aws-sdk/client-s3'\nimport type { CollectionConfig, PayloadRequest } from 'payload'\nimport type { Readable } from 'stream'\n\nimport { GetObjectCommand } from '@aws-sdk/client-s3'\nimport { getSignedUrl } from '@aws-sdk/s3-request-presigner'\nimport {\n getFilePrefix as getDocPrefix,\n getFileKey,\n} from '@payloadcms/plugin-cloud-storage/utilities'\nimport { getRangeRequestInfo } from 'payload/internal'\nimport { sanitizeFilename } from 'payload/shared'\n\nexport type SignedDownloadsConfig =\n | {\n /** @default 7200 */\n expiresIn?: number\n shouldUseSignedURL?(args: {\n collection: CollectionConfig\n filename: string\n req: PayloadRequest\n }): boolean | Promise<boolean>\n }\n | boolean\n\ninterface GetFileArgs {\n bucket: string\n client: AWS.S3\n clientUploadContext?: unknown\n collection: CollectionConfig\n collectionPrefix?: string\n filename: string\n incomingHeaders?: Headers\n prefixQueryParam?: string\n req: PayloadRequest\n signedDownloads: SignedDownloadsConfig\n useCompositePrefixes?: boolean\n}\n\nconst isNodeReadableStream = (body: AWS.GetObjectOutput['Body']): body is Readable => {\n return (\n typeof body === 'object' &&\n body !== null &&\n 'pipe' in body &&\n typeof body.pipe === 'function' &&\n 'destroy' in body &&\n typeof body.destroy === 'function'\n )\n}\n\nconst abortRequestAndDestroyStream = ({\n abortController,\n object,\n}: {\n abortController: AbortController\n object?: AWS.GetObjectOutput\n}) => {\n try {\n abortController.abort()\n } catch {\n /* noop */\n }\n if (object?.Body && isNodeReadableStream(object.Body)) {\n object.Body.destroy()\n }\n}\n\nexport async function getFile({\n bucket,\n client,\n clientUploadContext,\n collection,\n collectionPrefix = '',\n filename,\n incomingHeaders,\n prefixQueryParam,\n req,\n signedDownloads,\n useCompositePrefixes = false,\n}: GetFileArgs): Promise<Response> {\n let object: AWS.GetObjectOutput | undefined = undefined\n let streamed = false\n\n const abortController = new AbortController()\n if (req.signal) {\n req.signal.addEventListener('abort', () => {\n abortRequestAndDestroyStream({ abortController, object })\n })\n }\n\n try {\n const docPrefix = await getDocPrefix({\n clientUploadContext,\n collection,\n filename,\n prefixQueryParam,\n req,\n })\n\n const key = getFileKey({\n collectionPrefix,\n docPrefix,\n filename: sanitizeFilename(filename),\n useCompositePrefixes,\n })\n\n if (signedDownloads && !clientUploadContext) {\n let useSignedURL = true\n if (\n typeof signedDownloads === 'object' &&\n typeof signedDownloads.shouldUseSignedURL === 'function'\n ) {\n useSignedURL = await signedDownloads.shouldUseSignedURL({ collection, filename, req })\n }\n\n if (useSignedURL) {\n const command = new GetObjectCommand({ Bucket: bucket, Key: key })\n const signedUrl = await getSignedUrl(\n client,\n command,\n typeof signedDownloads === 'object' ? signedDownloads : { expiresIn: 7200 },\n )\n return Response.redirect(signedUrl, 302)\n }\n }\n\n // Get file size first for range validation and to set Content-Length header before streaming\n const headObject = await client.headObject({\n Bucket: bucket,\n Key: key,\n })\n const fileSize = headObject.ContentLength\n\n if (!fileSize) {\n return new Response('Internal Server Error', { status: 500 })\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 const rangeForS3 =\n rangeResult.type === 'partial'\n ? `bytes=${rangeResult.rangeStart}-${rangeResult.rangeEnd}`\n : undefined\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 headers.append('Content-Type', String(headObject.ContentType))\n if (headObject.ETag) {\n headers.append('ETag', headObject.ETag)\n }\n\n // Add Content-Security-Policy header for SVG files to prevent executable code\n if (headObject.ContentType === 'image/svg+xml') {\n headers.append('Content-Security-Policy', \"script-src 'none'\")\n }\n\n const etagFromHeaders = req.headers.get('etag') || req.headers.get('if-none-match')\n const objectEtag = headObject.ETag\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 === objectEtag) {\n return new Response(null, {\n headers,\n status: 304,\n })\n }\n\n object = await client.getObject(\n {\n Bucket: bucket,\n Key: key,\n Range: rangeForS3,\n },\n { abortSignal: abortController.signal },\n )\n\n if (!object.Body) {\n return new Response(null, { status: 404, statusText: 'Not Found' })\n }\n\n if (!isNodeReadableStream(object.Body)) {\n req.payload.logger.error({\n key,\n msg: 'S3 object body is not a readable stream',\n })\n return new Response('Internal Server Error', { status: 500 })\n }\n\n const stream = object.Body\n stream.on('error', (err: Error) => {\n req.payload.logger.error({\n err,\n key,\n msg: 'Error while streaming S3 object (aborting)',\n })\n abortRequestAndDestroyStream({ abortController, object })\n })\n\n streamed = true\n return new Response(stream, { headers, status: rangeResult.status })\n } catch (err) {\n if (\n err &&\n typeof err === 'object' &&\n (('name' in err && (err.name === 'NoSuchKey' || err.name === 'NotFound')) ||\n ('httpStatusCode' in err && err.httpStatusCode === 404))\n ) {\n return new Response(null, { status: 404, statusText: 'Not Found' })\n }\n req.payload.logger.error(err)\n return new Response('Internal Server Error', { status: 500 })\n } finally {\n if (!streamed) {\n abortRequestAndDestroyStream({ abortController, object })\n }\n }\n}\n"],"names":["GetObjectCommand","getSignedUrl","getFilePrefix","getDocPrefix","getFileKey","getRangeRequestInfo","sanitizeFilename","isNodeReadableStream","body","pipe","destroy","abortRequestAndDestroyStream","abortController","object","abort","Body","getFile","bucket","client","clientUploadContext","collection","collectionPrefix","filename","incomingHeaders","prefixQueryParam","req","signedDownloads","useCompositePrefixes","undefined","streamed","AbortController","signal","addEventListener","docPrefix","key","useSignedURL","shouldUseSignedURL","command","Bucket","Key","signedUrl","expiresIn","Response","redirect","headObject","fileSize","ContentLength","status","rangeHeader","headers","get","rangeResult","type","Headers","rangeForS3","rangeStart","rangeEnd","headerKey","value","Object","entries","append","String","ContentType","ETag","etagFromHeaders","objectEtag","upload","modifyResponseHeaders","getObject","Range","abortSignal","statusText","payload","logger","error","msg","stream","on","err","name","httpStatusCode"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { ClientUploadsConfig, CollectionOptions } from '@payloadcms/plugin-cloud-storage/types';
2
2
  import type { Plugin, UploadCollectionSlug } from 'payload';
3
3
  import * as AWS from '@aws-sdk/client-s3';
4
- import type { SignedDownloadsConfig } from './staticHandler.js';
4
+ import type { SignedDownloadsConfig } from './getFile.js';
5
5
  export type S3StorageOptions = {
6
6
  /**
7
7
  * Access control list for uploaded files.
@@ -62,6 +62,20 @@ export type S3StorageOptions = {
62
62
  * Use pre-signed URLs for files downloading. Can be overriden per-collection.
63
63
  */
64
64
  signedDownloads?: SignedDownloadsConfig;
65
+ /**
66
+ * When true, the collection-level prefix and document-level prefix are combined
67
+ * (compositional). When false (default), document prefix overrides collection
68
+ * prefix entirely.
69
+ *
70
+ * Example:
71
+ * - collection prefix: `collection-prefix/`
72
+ * - document prefix: `document-prefix/`
73
+ * - resulting prefix with useCompositePrefixes=true: `collection-prefix/document-prefix/`
74
+ * - resulting prefix with useCompositePrefixes=false: `document-prefix/`
75
+ *
76
+ * @default false
77
+ */
78
+ useCompositePrefixes?: boolean;
65
79
  };
66
80
  type S3StoragePlugin = (storageS3Args: S3StorageOptions) => Plugin;
67
81
  export declare const s3Storage: S3StoragePlugin;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,mBAAmB,EAEnB,iBAAiB,EAElB,MAAM,wCAAwC,CAAA;AAE/C,OAAO,KAAK,EAAU,MAAM,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AAEnE,OAAO,KAAK,GAAG,MAAM,oBAAoB,CAAA;AAIzC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAQ/D,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;OAEG;IACH,GAAG,CAAC,EAAE,SAAS,GAAG,aAAa,CAAA;IAE/B;;;;;;;;OAQG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAE5B;;;;OAIG;IAEH,MAAM,EAAE,MAAM,CAAA;IAEd;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;IAEvB;;OAEG;IACH,aAAa,CAAC,EAAE,mBAAmB,CAAA;IACnC;;OAEG;IACH,WAAW,EAAE,OAAO,CAClB,MAAM,CACJ,oBAAoB,EAClB,CAAC;QACC,eAAe,CAAC,EAAE,qBAAqB,CAAA;KACxC,GAAG,IAAI,CAAC,iBAAiB,EAAE,SAAS,CAAC,CAAC,GACvC,IAAI,CACP,CACF,CAAA;IACD;;;;OAIG;IACH,MAAM,EAAE,GAAG,CAAC,cAAc,CAAA;IAE1B;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAE7B;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;OAEG;IACH,eAAe,CAAC,EAAE,qBAAqB,CAAA;CACxC,CAAA;AAED,KAAK,eAAe,GAAG,CAAC,aAAa,EAAE,gBAAgB,KAAK,MAAM,CAAA;AAelE,eAAO,MAAM,SAAS,EAAE,eA0GrB,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;AAE/C,OAAO,KAAK,EAAU,MAAM,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AAEnE,OAAO,KAAK,GAAG,MAAM,oBAAoB,CAAA;AAIzC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AAKzD,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;OAEG;IACH,GAAG,CAAC,EAAE,SAAS,GAAG,aAAa,CAAA;IAE/B;;;;;;;;OAQG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAE5B;;;;OAIG;IAEH,MAAM,EAAE,MAAM,CAAA;IAEd;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;IAEvB;;OAEG;IACH,aAAa,CAAC,EAAE,mBAAmB,CAAA;IACnC;;OAEG;IACH,WAAW,EAAE,OAAO,CAClB,MAAM,CACJ,oBAAoB,EAClB,CAAC;QACC,eAAe,CAAC,EAAE,qBAAqB,CAAA;KACxC,GAAG,IAAI,CAAC,iBAAiB,EAAE,SAAS,CAAC,CAAC,GACvC,IAAI,CACP,CACF,CAAA;IACD;;;;OAIG;IACH,MAAM,EAAE,GAAG,CAAC,cAAc,CAAA;IAE1B;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAE7B;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;OAEG;IACH,eAAe,CAAC,EAAE,qBAAqB,CAAA;IACvC;;;;;;;;;;;;OAYG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B,CAAA;AAED,KAAK,eAAe,GAAG,CAAC,aAAa,EAAE,gBAAgB,KAAK,MAAM,CAAA;AAelE,eAAO,MAAM,SAAS,EAAE,eAmIrB,CAAA"}