@payloadcms/storage-r2 3.91.0-internal.c1569dc → 4.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +1 -1
- package/dist/adapter.d.ts +4 -1
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +68 -9
- package/dist/adapter.js.map +1 -1
- package/dist/client/R2ClientUploadHandler.d.ts +4 -5
- package/dist/client/R2ClientUploadHandler.d.ts.map +1 -1
- package/dist/client/R2ClientUploadHandler.js +10 -17
- package/dist/client/R2ClientUploadHandler.js.map +1 -1
- package/dist/getFile.d.ts +4 -3
- package/dist/getFile.d.ts.map +1 -1
- package/dist/getFile.js +23 -20
- package/dist/getFile.js.map +1 -1
- package/dist/handleMultiPartUpload.d.ts +2 -3
- package/dist/handleMultiPartUpload.d.ts.map +1 -1
- package/dist/handleMultiPartUpload.js +18 -16
- package/dist/handleMultiPartUpload.js.map +1 -1
- package/dist/index.d.ts +11 -14
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +54 -54
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +3 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +5 -1
- package/dist/types.js.map +1 -1
- package/package.json +7 -5
- package/dist/client/R2ClientUploadHandler.spec.d.ts +0 -2
- package/dist/client/R2ClientUploadHandler.spec.d.ts.map +0 -1
- package/dist/client/R2ClientUploadHandler.spec.js +0 -76
- package/dist/client/R2ClientUploadHandler.spec.js.map +0 -1
- package/dist/handleMultiPartUpload.spec.d.ts +0 -2
- package/dist/handleMultiPartUpload.spec.d.ts.map +0 -1
- package/dist/handleMultiPartUpload.spec.js +0 -154
- package/dist/handleMultiPartUpload.spec.js.map +0 -1
- package/dist/staticHandler.spec.d.ts +0 -2
- package/dist/staticHandler.spec.d.ts.map +0 -1
- package/dist/staticHandler.spec.js +0 -60
- package/dist/staticHandler.spec.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,26 +1,23 @@
|
|
|
1
1
|
import type { ClientUploadsConfig, CollectionOptions } from '@payloadcms/plugin-cloud-storage/types';
|
|
2
|
-
import type {
|
|
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.
|
|
18
8
|
*/
|
|
19
9
|
clientUploads?: ClientUploadsConfig;
|
|
20
10
|
/**
|
|
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
|
|
@@ -38,7 +35,7 @@ export interface R2StorageOptions {
|
|
|
38
35
|
*/
|
|
39
36
|
useCompositePrefixes?: boolean;
|
|
40
37
|
}
|
|
41
|
-
type
|
|
42
|
-
export declare const r2Storage:
|
|
38
|
+
type R2StorageFactory = (r2StorageArgs: R2StorageOptions) => StorageAdapter;
|
|
39
|
+
export declare const r2Storage: R2StorageFactory;
|
|
43
40
|
export {};
|
|
44
41
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,
|
|
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
|
@@ -1,62 +1,62 @@
|
|
|
1
1
|
import { cloudStoragePlugin } from '@payloadcms/plugin-cloud-storage';
|
|
2
|
-
import { initClientUploads } from '@payloadcms/plugin-cloud-storage/utilities';
|
|
3
2
|
import { createR2Adapter } from './adapter.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
useCompositePrefixes: r2StorageOptions.useCompositePrefixes
|
|
10
|
-
});
|
|
11
|
-
const isPluginDisabled = r2StorageOptions.enabled === false;
|
|
12
|
-
initClientUploads({
|
|
13
|
-
clientHandler: '@payloadcms/storage-r2/client#R2ClientUploadHandler',
|
|
14
|
-
collections: r2StorageOptions.collections,
|
|
15
|
-
config: incomingConfig,
|
|
16
|
-
enabled: !isPluginDisabled && Boolean(r2StorageOptions.clientUploads),
|
|
17
|
-
extraClientHandlerProps: ()=>({
|
|
18
|
-
useCompositePrefixes: Boolean(r2StorageOptions.useCompositePrefixes)
|
|
19
|
-
}),
|
|
20
|
-
serverHandler: getHandleMultiPartUpload({
|
|
21
|
-
access: typeof r2StorageOptions.clientUploads === 'object' ? r2StorageOptions.clientUploads.access : undefined,
|
|
3
|
+
export const r2Storage = (r2StorageOptions)=>({
|
|
4
|
+
name: 'r2',
|
|
5
|
+
collections: Object.keys(r2StorageOptions.collections),
|
|
6
|
+
init: (incomingConfig)=>{
|
|
7
|
+
const adapter = createR2Adapter({
|
|
22
8
|
bucket: r2StorageOptions.bucket,
|
|
9
|
+
clientUploads: r2StorageOptions.clientUploads,
|
|
23
10
|
collections: r2StorageOptions.collections,
|
|
11
|
+
copyCredentials: r2StorageOptions.copyCredentials,
|
|
24
12
|
useCompositePrefixes: r2StorageOptions.useCompositePrefixes
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
13
|
+
});
|
|
14
|
+
const isPluginDisabled = r2StorageOptions.enabled === false;
|
|
15
|
+
if (isPluginDisabled) {
|
|
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);
|
|
30
|
+
}
|
|
31
|
+
// Add adapter to each collection option object
|
|
32
|
+
const collectionsWithAdapter = Object.entries(r2StorageOptions.collections).reduce((acc, [slug, collOptions])=>({
|
|
33
|
+
...acc,
|
|
34
|
+
[slug]: {
|
|
35
|
+
...collOptions === true ? {} : collOptions,
|
|
36
|
+
adapter
|
|
37
|
+
}
|
|
38
|
+
}), {});
|
|
39
|
+
// Set disableLocalStorage: true for collections specified in the plugin options
|
|
40
|
+
const config = {
|
|
41
|
+
...incomingConfig,
|
|
42
|
+
collections: (incomingConfig.collections || []).map((collection)=>{
|
|
43
|
+
if (!collectionsWithAdapter[collection.slug]) {
|
|
44
|
+
return collection;
|
|
51
45
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
46
|
+
return {
|
|
47
|
+
...collection,
|
|
48
|
+
upload: {
|
|
49
|
+
...typeof collection.upload === 'object' ? collection.upload : {},
|
|
50
|
+
disableLocalStorage: true
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
})
|
|
54
|
+
};
|
|
55
|
+
return cloudStoragePlugin({
|
|
56
|
+
collections: collectionsWithAdapter,
|
|
57
|
+
useCompositePrefixes: r2StorageOptions.useCompositePrefixes
|
|
58
|
+
})(config);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
61
|
|
|
62
62
|
//# sourceMappingURL=index.js.map
|
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,
|
|
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
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type { R2Range } from '@cloudflare/workers-types/2023-07-01/index.js';
|
|
2
1
|
/**
|
|
3
2
|
* R2 API types compatible with both Node (Miniflare) and Cloudflare Workers.
|
|
4
3
|
* R2Range is sourced from Cloudflare so it cannot drift; other types are our own
|
|
5
4
|
* so Node's Blob/Buffer/Headers are accepted and we avoid strict Workers-only types.
|
|
6
5
|
*/
|
|
7
|
-
import type {
|
|
6
|
+
import type { R2Range } from '@cloudflare/workers-types/2023-07-01/index.js';
|
|
7
|
+
import type { UploadReference } from '@payloadcms/plugin-cloud-storage/types';
|
|
8
8
|
export type { R2Range };
|
|
9
9
|
export interface R2GetOptions {
|
|
10
10
|
[key: string]: any;
|
|
@@ -49,12 +49,9 @@ export interface R2UploadedPart {
|
|
|
49
49
|
etag: string;
|
|
50
50
|
partNumber: number;
|
|
51
51
|
}
|
|
52
|
-
export type
|
|
53
|
-
key: string;
|
|
54
|
-
} & ClientUploadContext;
|
|
52
|
+
export type R2StorageUploadReference = UploadReference;
|
|
55
53
|
export type R2StorageClientUploadHandlerParams = {
|
|
56
54
|
chunkSize?: number;
|
|
57
|
-
useCompositePrefixes?: boolean;
|
|
58
55
|
};
|
|
59
56
|
export type R2StorageMultipartUploadHandlerParams = {
|
|
60
57
|
collection: string;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+CAA+C,CAAA;AAC5E
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+CAA+C,CAAA;AAC5E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wCAAwC,CAAA;AAE7E,YAAY,EAAE,OAAO,EAAE,CAAA;AAEvB,MAAM,WAAW,YAAY;IAC3B,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;IAClB,MAAM,CAAC,EAAE,GAAG,GAAG,OAAO,CAAA;IACtB,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,MAAM,WAAW,QAAQ;IACvB,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAA;IAC7E,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,GAAG,YAAY,CAAC,CAAA;IACtE,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,QAAQ,CAAC,CAAA;IAC3C,IAAI,CAAC,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAA;IACjC,GAAG,CACD,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,WAAW,GAAG,eAAe,GAAG,IAAI,GAAG,IAAI,GAAG,cAAc,GAAG,MAAM,EAC5E,OAAO,CAAC,EAAE,GAAG,GACZ,OAAO,CAAC,GAAG,CAAC,CAAA;IACf,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CAAA;CACxE;AAED,UAAU,cAAc;IACtB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,IAAI,CAAA;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,YAAY,CAAC,EAAE,cAAc,CAAA;IACtC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;CAC1C;AAED,MAAM,WAAW,YAAa,SAAQ,QAAQ;IAC5C,IAAI,IAAI,IAAI,cAAc,CAAA;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACtB,QAAQ,CAAC,aAAa,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC5D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,UAAU,CACR,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,CAAC,WAAW,GAAG,eAAe,CAAC,GAAG,IAAI,GAAG,cAAc,GAAG,MAAM,EACvE,OAAO,CAAC,EAAE,GAAG,GACZ,OAAO,CAAC,cAAc,CAAC,CAAA;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,MAAM,wBAAwB,GAAG,eAAe,CAAA;AACtD,MAAM,MAAM,kCAAkC,GAAG;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,qCAAqC,GAAG;IAClD,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA"}
|
package/dist/types.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* R2 API types compatible with both Node (Miniflare) and Cloudflare Workers.
|
|
3
|
+
* R2Range is sourced from Cloudflare so it cannot drift; other types are our own
|
|
4
|
+
* so Node's Blob/Buffer/Headers are accepted and we avoid strict Workers-only types.
|
|
5
|
+
*/ export { };
|
|
2
6
|
|
|
3
7
|
//# sourceMappingURL=types.js.map
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["
|
|
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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@payloadcms/storage-r2",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0-beta.0",
|
|
4
4
|
"description": "Payload storage adapter for Cloudflare R2",
|
|
5
5
|
"homepage": "https://payloadcms.com",
|
|
6
6
|
"repository": {
|
|
@@ -37,17 +37,19 @@
|
|
|
37
37
|
"dist"
|
|
38
38
|
],
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@
|
|
40
|
+
"@aws-sdk/client-s3": "^3.614.0",
|
|
41
|
+
"@payloadcms/plugin-cloud-storage": "4.0.0-beta.0",
|
|
42
|
+
"@payloadcms/storage-s3": "4.0.0-beta.0"
|
|
41
43
|
},
|
|
42
44
|
"devDependencies": {
|
|
43
45
|
"@cloudflare/workers-types": "4.20260218.0",
|
|
44
|
-
"payload": "
|
|
46
|
+
"payload": "4.0.0-beta.0"
|
|
45
47
|
},
|
|
46
48
|
"peerDependencies": {
|
|
47
|
-
"payload": "
|
|
49
|
+
"payload": "4.0.0-beta.0"
|
|
48
50
|
},
|
|
49
51
|
"engines": {
|
|
50
|
-
"node": "
|
|
52
|
+
"node": ">=24.15.0"
|
|
51
53
|
},
|
|
52
54
|
"scripts": {
|
|
53
55
|
"build": "pnpm build:types && pnpm build:swc",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"R2ClientUploadHandler.spec.d.ts","sourceRoot":"","sources":["../../src/client/R2ClientUploadHandler.spec.ts"],"names":[],"mappings":""}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
vi.mock('@payloadcms/plugin-cloud-storage/client', async ()=>{
|
|
3
|
-
const { buildUploadStoragePathData } = await import('@payloadcms/plugin-cloud-storage/utilities');
|
|
4
|
-
return {
|
|
5
|
-
createClientUploadHandler: ({ handler })=>handler,
|
|
6
|
-
buildUploadStoragePathData
|
|
7
|
-
};
|
|
8
|
-
});
|
|
9
|
-
import { r2Storage } from '../index.js';
|
|
10
|
-
import { R2ClientUploadHandler } from './R2ClientUploadHandler.js';
|
|
11
|
-
const invoke = R2ClientUploadHandler;
|
|
12
|
-
describe('R2ClientUploadHandler', ()=>{
|
|
13
|
-
afterEach(()=>{
|
|
14
|
-
vi.unstubAllGlobals();
|
|
15
|
-
});
|
|
16
|
-
it('should receive the configured composite prefix mode', ()=>{
|
|
17
|
-
const config = r2Storage({
|
|
18
|
-
bucket: {},
|
|
19
|
-
clientUploads: true,
|
|
20
|
-
collections: {
|
|
21
|
-
media: {
|
|
22
|
-
prefix: 'collection'
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
useCompositePrefixes: true
|
|
26
|
-
})({
|
|
27
|
-
collections: [
|
|
28
|
-
{
|
|
29
|
-
fields: [],
|
|
30
|
-
slug: 'media',
|
|
31
|
-
upload: true
|
|
32
|
-
}
|
|
33
|
-
]
|
|
34
|
-
});
|
|
35
|
-
const provider = config.admin?.components?.providers?.[0];
|
|
36
|
-
expect(provider?.clientProps?.extra).toEqual({
|
|
37
|
-
useCompositePrefixes: true
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
it('should preserve the document prefix for composite uploads', async ()=>{
|
|
41
|
-
const fetchMock = vi.fn().mockResolvedValueOnce(Response.json({
|
|
42
|
-
clientUploadContext: {
|
|
43
|
-
prefix: 'collection/document',
|
|
44
|
-
signedReceipt: 'receipt'
|
|
45
|
-
},
|
|
46
|
-
key: 'collection/document/file.txt',
|
|
47
|
-
uploadId: 'upload-id'
|
|
48
|
-
})).mockResolvedValueOnce(Response.json({
|
|
49
|
-
etag: 'etag',
|
|
50
|
-
partNumber: 1
|
|
51
|
-
})).mockResolvedValueOnce(new Response('collection/document/file.txt'));
|
|
52
|
-
vi.stubGlobal('fetch', fetchMock);
|
|
53
|
-
await invoke({
|
|
54
|
-
apiRoute: '/api',
|
|
55
|
-
collectionSlug: 'media',
|
|
56
|
-
docPrefix: 'document',
|
|
57
|
-
extra: {
|
|
58
|
-
chunkSize: 10,
|
|
59
|
-
useCompositePrefixes: true
|
|
60
|
-
},
|
|
61
|
-
file: new File([
|
|
62
|
-
'content'
|
|
63
|
-
], 'file.txt', {
|
|
64
|
-
type: 'text/plain'
|
|
65
|
-
}),
|
|
66
|
-
prefix: 'collection',
|
|
67
|
-
serverHandlerPath: '/storage-r2-multi-part-upload',
|
|
68
|
-
serverURL: 'https://example.com',
|
|
69
|
-
updateFilename: vi.fn()
|
|
70
|
-
});
|
|
71
|
-
const requestURL = new URL(fetchMock.mock.calls[0][0]);
|
|
72
|
-
expect(requestURL.searchParams.get('docPrefix')).toBe('document');
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
//# sourceMappingURL=R2ClientUploadHandler.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/client/R2ClientUploadHandler.spec.ts"],"sourcesContent":["import { afterEach, describe, expect, it, vi } from 'vitest'\n\nimport type { Config } from 'payload'\n\nvi.mock('@payloadcms/plugin-cloud-storage/client', async () => {\n const { buildUploadStoragePathData } = await import('@payloadcms/plugin-cloud-storage/utilities')\n\n return {\n createClientUploadHandler: ({ handler }: { handler: unknown }) => handler,\n buildUploadStoragePathData,\n }\n})\n\nimport type { R2Bucket, R2StorageClientUploadContext } from '../types.js'\n\nimport { r2Storage } from '../index.js'\nimport { R2ClientUploadHandler } from './R2ClientUploadHandler.js'\n\nconst invoke = R2ClientUploadHandler as unknown as (args: {\n apiRoute: string\n collectionSlug: string\n docPrefix?: string\n extra: { chunkSize?: number; useCompositePrefixes?: boolean }\n file: File\n prefix?: string\n serverHandlerPath: `/${string}`\n serverURL: string\n updateFilename: (value: string) => void\n}) => Promise<R2StorageClientUploadContext | undefined>\n\ndescribe('R2ClientUploadHandler', () => {\n afterEach(() => {\n vi.unstubAllGlobals()\n })\n\n it('should receive the configured composite prefix mode', () => {\n const config = r2Storage({\n bucket: {} as R2Bucket,\n clientUploads: true,\n collections: { media: { prefix: 'collection' } },\n useCompositePrefixes: true,\n })({\n collections: [{ fields: [], slug: 'media', upload: true }],\n } as unknown as Config) as Config\n const provider = config.admin?.components?.providers?.[0] as\n | { clientProps?: { extra?: { useCompositePrefixes?: boolean } } }\n | undefined\n\n expect(provider?.clientProps?.extra).toEqual({ useCompositePrefixes: true })\n })\n\n it('should preserve the document prefix for composite uploads', async () => {\n const fetchMock = vi\n .fn()\n .mockResolvedValueOnce(\n Response.json({\n clientUploadContext: { prefix: 'collection/document', signedReceipt: 'receipt' },\n key: 'collection/document/file.txt',\n uploadId: 'upload-id',\n }),\n )\n .mockResolvedValueOnce(Response.json({ etag: 'etag', partNumber: 1 }))\n .mockResolvedValueOnce(new Response('collection/document/file.txt'))\n vi.stubGlobal('fetch', fetchMock)\n\n await invoke({\n apiRoute: '/api',\n collectionSlug: 'media',\n docPrefix: 'document',\n extra: { chunkSize: 10, useCompositePrefixes: true },\n file: new File(['content'], 'file.txt', { type: 'text/plain' }),\n prefix: 'collection',\n serverHandlerPath: '/storage-r2-multi-part-upload',\n serverURL: 'https://example.com',\n updateFilename: vi.fn(),\n })\n\n const requestURL = new URL(fetchMock.mock.calls[0]![0])\n expect(requestURL.searchParams.get('docPrefix')).toBe('document')\n })\n})\n"],"names":["afterEach","describe","expect","it","vi","mock","buildUploadStoragePathData","createClientUploadHandler","handler","r2Storage","R2ClientUploadHandler","invoke","unstubAllGlobals","config","bucket","clientUploads","collections","media","prefix","useCompositePrefixes","fields","slug","upload","provider","admin","components","providers","clientProps","extra","toEqual","fetchMock","fn","mockResolvedValueOnce","Response","json","clientUploadContext","signedReceipt","key","uploadId","etag","partNumber","stubGlobal","apiRoute","collectionSlug","docPrefix","chunkSize","file","File","type","serverHandlerPath","serverURL","updateFilename","requestURL","URL","calls","searchParams","get","toBe"],"mappings":"AAAA,SAASA,SAAS,EAAEC,QAAQ,EAAEC,MAAM,EAAEC,EAAE,EAAEC,EAAE,QAAQ,SAAQ;AAI5DA,GAAGC,IAAI,CAAC,2CAA2C;IACjD,MAAM,EAAEC,0BAA0B,EAAE,GAAG,MAAM,MAAM,CAAC;IAEpD,OAAO;QACLC,2BAA2B,CAAC,EAAEC,OAAO,EAAwB,GAAKA;QAClEF;IACF;AACF;AAIA,SAASG,SAAS,QAAQ,cAAa;AACvC,SAASC,qBAAqB,QAAQ,6BAA4B;AAElE,MAAMC,SAASD;AAYfT,SAAS,yBAAyB;IAChCD,UAAU;QACRI,GAAGQ,gBAAgB;IACrB;IAEAT,GAAG,uDAAuD;QACxD,MAAMU,SAASJ,UAAU;YACvBK,QAAQ,CAAC;YACTC,eAAe;YACfC,aAAa;gBAAEC,OAAO;oBAAEC,QAAQ;gBAAa;YAAE;YAC/CC,sBAAsB;QACxB,GAAG;YACDH,aAAa;gBAAC;oBAAEI,QAAQ,EAAE;oBAAEC,MAAM;oBAASC,QAAQ;gBAAK;aAAE;QAC5D;QACA,MAAMC,WAAWV,OAAOW,KAAK,EAAEC,YAAYC,WAAW,CAAC,EAAE;QAIzDxB,OAAOqB,UAAUI,aAAaC,OAAOC,OAAO,CAAC;YAAEV,sBAAsB;QAAK;IAC5E;IAEAhB,GAAG,6DAA6D;QAC9D,MAAM2B,YAAY1B,GACf2B,EAAE,GACFC,qBAAqB,CACpBC,SAASC,IAAI,CAAC;YACZC,qBAAqB;gBAAEjB,QAAQ;gBAAuBkB,eAAe;YAAU;YAC/EC,KAAK;YACLC,UAAU;QACZ,IAEDN,qBAAqB,CAACC,SAASC,IAAI,CAAC;YAAEK,MAAM;YAAQC,YAAY;QAAE,IAClER,qBAAqB,CAAC,IAAIC,SAAS;QACtC7B,GAAGqC,UAAU,CAAC,SAASX;QAEvB,MAAMnB,OAAO;YACX+B,UAAU;YACVC,gBAAgB;YAChBC,WAAW;YACXhB,OAAO;gBAAEiB,WAAW;gBAAI1B,sBAAsB;YAAK;YACnD2B,MAAM,IAAIC,KAAK;gBAAC;aAAU,EAAE,YAAY;gBAAEC,MAAM;YAAa;YAC7D9B,QAAQ;YACR+B,mBAAmB;YACnBC,WAAW;YACXC,gBAAgB/C,GAAG2B,EAAE;QACvB;QAEA,MAAMqB,aAAa,IAAIC,IAAIvB,UAAUzB,IAAI,CAACiD,KAAK,CAAC,EAAE,AAAC,CAAC,EAAE;QACtDpD,OAAOkD,WAAWG,YAAY,CAACC,GAAG,CAAC,cAAcC,IAAI,CAAC;IACxD;AACF"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"handleMultiPartUpload.spec.d.ts","sourceRoot":"","sources":["../src/handleMultiPartUpload.spec.ts"],"names":[],"mappings":""}
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
import { createClientUploadReceipt } from 'payload/internal';
|
|
2
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
-
import { getHandleMultiPartUpload } from './handleMultiPartUpload.js';
|
|
4
|
-
describe('getHandleMultiPartUpload', ()=>{
|
|
5
|
-
it('rejects multipart uploads when the object already exists', async ()=>{
|
|
6
|
-
const createMultipartUpload = vi.fn();
|
|
7
|
-
const bucket = {
|
|
8
|
-
createMultipartUpload,
|
|
9
|
-
head: vi.fn().mockResolvedValue({
|
|
10
|
-
key: 'protected.png'
|
|
11
|
-
})
|
|
12
|
-
};
|
|
13
|
-
const req = {
|
|
14
|
-
payload: {
|
|
15
|
-
collections: {
|
|
16
|
-
media: {
|
|
17
|
-
config: {
|
|
18
|
-
access: {},
|
|
19
|
-
upload: true
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
|
-
db: {
|
|
24
|
-
findOne: vi.fn().mockResolvedValue(null)
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
searchParams: new URLSearchParams({
|
|
28
|
-
collection: 'media',
|
|
29
|
-
fileName: 'protected.png',
|
|
30
|
-
fileType: 'image/png'
|
|
31
|
-
}),
|
|
32
|
-
user: {
|
|
33
|
-
id: 'user-id'
|
|
34
|
-
}
|
|
35
|
-
};
|
|
36
|
-
const handler = getHandleMultiPartUpload({
|
|
37
|
-
access: async ()=>true,
|
|
38
|
-
bucket,
|
|
39
|
-
collections: {
|
|
40
|
-
media: true
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
const response = await handler(req);
|
|
44
|
-
expect(response.status).toBe(412);
|
|
45
|
-
expect(createMultipartUpload).not.toHaveBeenCalled();
|
|
46
|
-
});
|
|
47
|
-
describe('continuation', ()=>{
|
|
48
|
-
const createContinuationRequest = ({ multipartKey, multipartNumber, receiptPrefix, withReceipt = true })=>{
|
|
49
|
-
const req = {
|
|
50
|
-
arrayBuffer: vi.fn().mockResolvedValue(new ArrayBuffer(0)),
|
|
51
|
-
user: {
|
|
52
|
-
id: 'user'
|
|
53
|
-
},
|
|
54
|
-
json: vi.fn().mockResolvedValue([]),
|
|
55
|
-
payload: {
|
|
56
|
-
collections: {
|
|
57
|
-
media: {
|
|
58
|
-
config: {
|
|
59
|
-
access: {},
|
|
60
|
-
upload: true
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
},
|
|
64
|
-
db: {
|
|
65
|
-
findOne: vi.fn().mockResolvedValue(null)
|
|
66
|
-
},
|
|
67
|
-
secret: 'test-secret'
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
const signedReceipt = createClientUploadReceipt({
|
|
71
|
-
collectionSlug: 'media',
|
|
72
|
-
context: {
|
|
73
|
-
prefix: receiptPrefix
|
|
74
|
-
},
|
|
75
|
-
filename: 'photo.png',
|
|
76
|
-
req
|
|
77
|
-
});
|
|
78
|
-
req.searchParams = new URLSearchParams({
|
|
79
|
-
collection: 'media',
|
|
80
|
-
fileName: 'photo.png',
|
|
81
|
-
fileType: 'image/png',
|
|
82
|
-
multipartId: 'upload-id',
|
|
83
|
-
multipartKey,
|
|
84
|
-
...multipartNumber === undefined ? {} : {
|
|
85
|
-
multipartNumber
|
|
86
|
-
},
|
|
87
|
-
...withReceipt ? {
|
|
88
|
-
signedReceipt
|
|
89
|
-
} : {}
|
|
90
|
-
});
|
|
91
|
-
return req;
|
|
92
|
-
};
|
|
93
|
-
const createHandler = ()=>{
|
|
94
|
-
const resumeMultipartUpload = vi.fn(()=>({
|
|
95
|
-
complete: vi.fn().mockResolvedValue({
|
|
96
|
-
key: 'media/documents/photo.png'
|
|
97
|
-
}),
|
|
98
|
-
uploadPart: vi.fn().mockResolvedValue({
|
|
99
|
-
etag: 'etag',
|
|
100
|
-
partNumber: 1
|
|
101
|
-
})
|
|
102
|
-
}));
|
|
103
|
-
return {
|
|
104
|
-
handler: getHandleMultiPartUpload({
|
|
105
|
-
access: async ()=>true,
|
|
106
|
-
bucket: {
|
|
107
|
-
resumeMultipartUpload
|
|
108
|
-
},
|
|
109
|
-
collections: {
|
|
110
|
-
media: {
|
|
111
|
-
prefix: 'media'
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}),
|
|
115
|
-
resumeMultipartUpload
|
|
116
|
-
};
|
|
117
|
-
};
|
|
118
|
-
it('should reject a key outside the receipt', async ()=>{
|
|
119
|
-
const { handler, resumeMultipartUpload } = createHandler();
|
|
120
|
-
await expect(handler(createContinuationRequest({
|
|
121
|
-
multipartKey: 'media-archive/photo.png',
|
|
122
|
-
multipartNumber: '1',
|
|
123
|
-
receiptPrefix: 'media/documents'
|
|
124
|
-
}))).rejects.toMatchObject({
|
|
125
|
-
status: 400
|
|
126
|
-
});
|
|
127
|
-
expect(resumeMultipartUpload).not.toHaveBeenCalled();
|
|
128
|
-
});
|
|
129
|
-
it('should reject a continuation with no receipt', async ()=>{
|
|
130
|
-
const { handler, resumeMultipartUpload } = createHandler();
|
|
131
|
-
await expect(handler(createContinuationRequest({
|
|
132
|
-
multipartKey: 'media/documents/photo.png',
|
|
133
|
-
multipartNumber: '1',
|
|
134
|
-
receiptPrefix: 'media/documents',
|
|
135
|
-
withReceipt: false
|
|
136
|
-
}))).rejects.toMatchObject({
|
|
137
|
-
status: 400
|
|
138
|
-
});
|
|
139
|
-
expect(resumeMultipartUpload).not.toHaveBeenCalled();
|
|
140
|
-
});
|
|
141
|
-
it('should resume the upload issued by the receipt', async ()=>{
|
|
142
|
-
const { handler, resumeMultipartUpload } = createHandler();
|
|
143
|
-
const response = await handler(createContinuationRequest({
|
|
144
|
-
multipartKey: 'media/documents/photo.png',
|
|
145
|
-
multipartNumber: '1',
|
|
146
|
-
receiptPrefix: 'media/documents'
|
|
147
|
-
}));
|
|
148
|
-
expect(response.status).toBe(200);
|
|
149
|
-
expect(resumeMultipartUpload).toHaveBeenCalledWith('media/documents/photo.png', 'upload-id');
|
|
150
|
-
});
|
|
151
|
-
});
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
//# sourceMappingURL=handleMultiPartUpload.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/handleMultiPartUpload.spec.ts"],"sourcesContent":["import type { PayloadRequest } from 'payload'\n\nimport { createClientUploadReceipt } from 'payload/internal'\nimport { describe, expect, it, vi } from 'vitest'\n\nimport type { R2Bucket } from './types.js'\n\nimport { getHandleMultiPartUpload } from './handleMultiPartUpload.js'\n\ndescribe('getHandleMultiPartUpload', () => {\n it('rejects multipart uploads when the object already exists', async () => {\n const createMultipartUpload = vi.fn()\n const bucket = {\n createMultipartUpload,\n head: vi.fn().mockResolvedValue({ key: 'protected.png' }),\n } as unknown as R2Bucket\n const req = {\n payload: {\n collections: {\n media: { config: { access: {}, upload: true } },\n },\n db: { findOne: vi.fn().mockResolvedValue(null) },\n },\n searchParams: new URLSearchParams({\n collection: 'media',\n fileName: 'protected.png',\n fileType: 'image/png',\n }),\n user: { id: 'user-id' },\n } as unknown as PayloadRequest\n const handler = getHandleMultiPartUpload({\n access: async () => true,\n bucket,\n collections: { media: true },\n })\n\n const response = await handler(req)\n\n expect(response.status).toBe(412)\n expect(createMultipartUpload).not.toHaveBeenCalled()\n })\n\n describe('continuation', () => {\n const createContinuationRequest = ({\n multipartKey,\n multipartNumber,\n receiptPrefix,\n withReceipt = true,\n }: {\n multipartKey: string\n multipartNumber?: string\n receiptPrefix: string\n withReceipt?: boolean\n }) => {\n const req = {\n arrayBuffer: vi.fn().mockResolvedValue(new ArrayBuffer(0)),\n user: { id: 'user' },\n json: vi.fn().mockResolvedValue([]),\n payload: {\n collections: { media: { config: { access: {}, upload: true } } },\n db: { findOne: vi.fn().mockResolvedValue(null) },\n secret: 'test-secret',\n },\n } as unknown as PayloadRequest\n\n const signedReceipt = createClientUploadReceipt({\n collectionSlug: 'media',\n context: { prefix: receiptPrefix },\n filename: 'photo.png',\n req,\n })\n\n ;(req as { searchParams?: URLSearchParams }).searchParams = new URLSearchParams({\n collection: 'media',\n fileName: 'photo.png',\n fileType: 'image/png',\n multipartId: 'upload-id',\n multipartKey,\n ...(multipartNumber === undefined ? {} : { multipartNumber }),\n ...(withReceipt ? { signedReceipt } : {}),\n })\n\n return req\n }\n\n const createHandler = () => {\n const resumeMultipartUpload = vi.fn(() => ({\n complete: vi.fn().mockResolvedValue({ key: 'media/documents/photo.png' }),\n uploadPart: vi.fn().mockResolvedValue({ etag: 'etag', partNumber: 1 }),\n }))\n\n return {\n handler: getHandleMultiPartUpload({\n access: async () => true,\n bucket: { resumeMultipartUpload } as unknown as R2Bucket,\n collections: { media: { prefix: 'media' } },\n }),\n resumeMultipartUpload,\n }\n }\n\n it('should reject a key outside the receipt', async () => {\n const { handler, resumeMultipartUpload } = createHandler()\n\n await expect(\n handler(\n createContinuationRequest({\n multipartKey: 'media-archive/photo.png',\n multipartNumber: '1',\n receiptPrefix: 'media/documents',\n }),\n ),\n ).rejects.toMatchObject({ status: 400 })\n expect(resumeMultipartUpload).not.toHaveBeenCalled()\n })\n\n it('should reject a continuation with no receipt', async () => {\n const { handler, resumeMultipartUpload } = createHandler()\n\n await expect(\n handler(\n createContinuationRequest({\n multipartKey: 'media/documents/photo.png',\n multipartNumber: '1',\n receiptPrefix: 'media/documents',\n withReceipt: false,\n }),\n ),\n ).rejects.toMatchObject({ status: 400 })\n expect(resumeMultipartUpload).not.toHaveBeenCalled()\n })\n\n it('should resume the upload issued by the receipt', async () => {\n const { handler, resumeMultipartUpload } = createHandler()\n\n const response = await handler(\n createContinuationRequest({\n multipartKey: 'media/documents/photo.png',\n multipartNumber: '1',\n receiptPrefix: 'media/documents',\n }),\n )\n\n expect(response.status).toBe(200)\n expect(resumeMultipartUpload).toHaveBeenCalledWith('media/documents/photo.png', 'upload-id')\n })\n })\n})\n"],"names":["createClientUploadReceipt","describe","expect","it","vi","getHandleMultiPartUpload","createMultipartUpload","fn","bucket","head","mockResolvedValue","key","req","payload","collections","media","config","access","upload","db","findOne","searchParams","URLSearchParams","collection","fileName","fileType","user","id","handler","response","status","toBe","not","toHaveBeenCalled","createContinuationRequest","multipartKey","multipartNumber","receiptPrefix","withReceipt","arrayBuffer","ArrayBuffer","json","secret","signedReceipt","collectionSlug","context","prefix","filename","multipartId","undefined","createHandler","resumeMultipartUpload","complete","uploadPart","etag","partNumber","rejects","toMatchObject","toHaveBeenCalledWith"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"staticHandler.spec.d.ts","sourceRoot":"","sources":["../src/staticHandler.spec.ts"],"names":[],"mappings":""}
|