@payloadcms/storage-vercel-blob 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.
Files changed (43) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +11 -9
  3. package/dist/adapter.d.ts +3 -1
  4. package/dist/adapter.d.ts.map +1 -1
  5. package/dist/adapter.js +109 -10
  6. package/dist/adapter.js.map +1 -1
  7. package/dist/authorizeFileOverwrite.d.ts +8 -6
  8. package/dist/authorizeFileOverwrite.d.ts.map +1 -1
  9. package/dist/authorizeFileOverwrite.js +9 -8
  10. package/dist/authorizeFileOverwrite.js.map +1 -1
  11. package/dist/client/VercelBlobClientUploadHandler.d.ts +3 -8
  12. package/dist/client/VercelBlobClientUploadHandler.d.ts.map +1 -1
  13. package/dist/client/VercelBlobClientUploadHandler.js +9 -37
  14. package/dist/client/VercelBlobClientUploadHandler.js.map +1 -1
  15. package/dist/copyFile.d.ts +12 -0
  16. package/dist/copyFile.d.ts.map +1 -0
  17. package/dist/copyFile.js +86 -0
  18. package/dist/copyFile.js.map +1 -0
  19. package/dist/generateURL.d.ts +1 -1
  20. package/dist/generateURL.d.ts.map +1 -1
  21. package/dist/generateURL.js +6 -6
  22. package/dist/generateURL.js.map +1 -1
  23. package/dist/getCollectionSources.d.ts +20 -0
  24. package/dist/getCollectionSources.d.ts.map +1 -0
  25. package/dist/getCollectionSources.js +27 -0
  26. package/dist/getCollectionSources.js.map +1 -0
  27. package/dist/getFile.d.ts +4 -3
  28. package/dist/getFile.d.ts.map +1 -1
  29. package/dist/getFile.js +16 -10
  30. package/dist/getFile.js.map +1 -1
  31. package/dist/index.d.ts +6 -15
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +72 -78
  34. package/dist/index.js.map +1 -1
  35. package/package.json +5 -5
  36. package/dist/authorizeFileOverwrite.spec.js +0 -87
  37. package/dist/authorizeFileOverwrite.spec.js.map +0 -1
  38. package/dist/getClientUploadRoute.d.ts +0 -16
  39. package/dist/getClientUploadRoute.d.ts.map +0 -1
  40. package/dist/getClientUploadRoute.js +0 -149
  41. package/dist/getClientUploadRoute.js.map +0 -1
  42. package/dist/staticHandler.spec.js +0 -71
  43. package/dist/staticHandler.spec.js.map +0 -1
@@ -1,87 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
- import { authorizeClientOverwrite } from './authorizeFileOverwrite.js';
3
- const owner = (id)=>({
4
- filename: 'image.png',
5
- id,
6
- prefix: 'shared'
7
- });
8
- const createRequest = ({ protectedOwner = false } = {})=>({
9
- payload: {
10
- collections: {
11
- media: {
12
- config: {
13
- access: {
14
- update: async ()=>true
15
- },
16
- upload: true
17
- }
18
- },
19
- protected: {
20
- config: {
21
- access: {
22
- update: async ()=>false
23
- },
24
- upload: true
25
- }
26
- }
27
- },
28
- find: vi.fn(async ({ collection })=>({
29
- docs: collection === 'media' ? [
30
- owner(1)
31
- ] : protectedOwner ? [
32
- owner(2)
33
- ] : []
34
- }))
35
- },
36
- t: vi.fn()
37
- });
38
- describe('authorizeClientOverwrite', ()=>{
39
- it('should reject a new key outside the requested collection prefix', async ()=>{
40
- await expect(authorizeClientOverwrite({
41
- collections: {
42
- media: {
43
- prefix: 'media'
44
- }
45
- },
46
- requestedFilename: 'image.png',
47
- requestedStorageFilePath: 'media-archive/image.png',
48
- req: createRequest(),
49
- requestedCollectionSlug: 'media',
50
- collectionPrefix: 'media'
51
- })).rejects.toMatchObject({
52
- status: 403
53
- });
54
- });
55
- it('allows an update when the requested collection is the only owner', async ()=>{
56
- await expect(authorizeClientOverwrite({
57
- collections: {
58
- media: true,
59
- protected: true
60
- },
61
- requestedFilename: 'image.png',
62
- requestedStorageFilePath: 'shared/image.png',
63
- req: createRequest(),
64
- requestedCollectionSlug: 'media',
65
- collectionPrefix: ''
66
- })).resolves.toBe(true);
67
- });
68
- it('rejects an update when another collection owns the same key', async ()=>{
69
- await expect(authorizeClientOverwrite({
70
- collections: {
71
- media: true,
72
- protected: true
73
- },
74
- requestedFilename: 'image.png',
75
- requestedStorageFilePath: 'shared/image.png',
76
- req: createRequest({
77
- protectedOwner: true
78
- }),
79
- requestedCollectionSlug: 'media',
80
- collectionPrefix: ''
81
- })).rejects.toMatchObject({
82
- status: 403
83
- });
84
- });
85
- });
86
-
87
- //# sourceMappingURL=authorizeFileOverwrite.spec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/authorizeFileOverwrite.spec.ts"],"sourcesContent":["import type { PayloadRequest } from 'payload'\n\nimport { describe, expect, it, vi } from 'vitest'\n\nimport { authorizeClientOverwrite } from './authorizeFileOverwrite.js'\n\nconst owner = (id: number) => ({ filename: 'image.png', id, prefix: 'shared' })\n\nconst createRequest = ({ protectedOwner = false }: { protectedOwner?: boolean } = {}) =>\n ({\n payload: {\n collections: {\n media: {\n config: { access: { update: async () => true }, upload: true },\n },\n protected: {\n config: { access: { update: async () => false }, upload: true },\n },\n },\n find: vi.fn(async ({ collection }: { collection: string }) => ({\n docs: collection === 'media' ? [owner(1)] : protectedOwner ? [owner(2)] : [],\n })),\n },\n t: vi.fn(),\n }) as unknown as PayloadRequest\n\ndescribe('authorizeClientOverwrite', () => {\n it('should reject a new key outside the requested collection prefix', async () => {\n await expect(\n authorizeClientOverwrite({\n collections: { media: { prefix: 'media' } },\n requestedFilename: 'image.png',\n requestedStorageFilePath: 'media-archive/image.png',\n req: createRequest(),\n requestedCollectionSlug: 'media',\n collectionPrefix: 'media',\n }),\n ).rejects.toMatchObject({ status: 403 })\n })\n\n it('allows an update when the requested collection is the only owner', async () => {\n await expect(\n authorizeClientOverwrite({\n collections: { media: true, protected: true },\n requestedFilename: 'image.png',\n requestedStorageFilePath: 'shared/image.png',\n req: createRequest(),\n requestedCollectionSlug: 'media',\n collectionPrefix: '',\n }),\n ).resolves.toBe(true)\n })\n\n it('rejects an update when another collection owns the same key', async () => {\n await expect(\n authorizeClientOverwrite({\n collections: { media: true, protected: true },\n requestedFilename: 'image.png',\n requestedStorageFilePath: 'shared/image.png',\n req: createRequest({ protectedOwner: true }),\n requestedCollectionSlug: 'media',\n collectionPrefix: '',\n }),\n ).rejects.toMatchObject({ status: 403 })\n })\n})\n"],"names":["describe","expect","it","vi","authorizeClientOverwrite","owner","id","filename","prefix","createRequest","protectedOwner","payload","collections","media","config","access","update","upload","protected","find","fn","collection","docs","t","requestedFilename","requestedStorageFilePath","req","requestedCollectionSlug","collectionPrefix","rejects","toMatchObject","status","resolves","toBe"],"mappings":"AAEA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,EAAEC,EAAE,QAAQ,SAAQ;AAEjD,SAASC,wBAAwB,QAAQ,8BAA6B;AAEtE,MAAMC,QAAQ,CAACC,KAAgB,CAAA;QAAEC,UAAU;QAAaD;QAAIE,QAAQ;IAAS,CAAA;AAE7E,MAAMC,gBAAgB,CAAC,EAAEC,iBAAiB,KAAK,EAAgC,GAAG,CAAC,CAAC,GACjF,CAAA;QACCC,SAAS;YACPC,aAAa;gBACXC,OAAO;oBACLC,QAAQ;wBAAEC,QAAQ;4BAAEC,QAAQ,UAAY;wBAAK;wBAAGC,QAAQ;oBAAK;gBAC/D;gBACAC,WAAW;oBACTJ,QAAQ;wBAAEC,QAAQ;4BAAEC,QAAQ,UAAY;wBAAM;wBAAGC,QAAQ;oBAAK;gBAChE;YACF;YACAE,MAAMhB,GAAGiB,EAAE,CAAC,OAAO,EAAEC,UAAU,EAA0B,GAAM,CAAA;oBAC7DC,MAAMD,eAAe,UAAU;wBAAChB,MAAM;qBAAG,GAAGK,iBAAiB;wBAACL,MAAM;qBAAG,GAAG,EAAE;gBAC9E,CAAA;QACF;QACAkB,GAAGpB,GAAGiB,EAAE;IACV,CAAA;AAEFpB,SAAS,4BAA4B;IACnCE,GAAG,mEAAmE;QACpE,MAAMD,OACJG,yBAAyB;YACvBQ,aAAa;gBAAEC,OAAO;oBAAEL,QAAQ;gBAAQ;YAAE;YAC1CgB,mBAAmB;YACnBC,0BAA0B;YAC1BC,KAAKjB;YACLkB,yBAAyB;YACzBC,kBAAkB;QACpB,IACAC,OAAO,CAACC,aAAa,CAAC;YAAEC,QAAQ;QAAI;IACxC;IAEA7B,GAAG,oEAAoE;QACrE,MAAMD,OACJG,yBAAyB;YACvBQ,aAAa;gBAAEC,OAAO;gBAAMK,WAAW;YAAK;YAC5CM,mBAAmB;YACnBC,0BAA0B;YAC1BC,KAAKjB;YACLkB,yBAAyB;YACzBC,kBAAkB;QACpB,IACAI,QAAQ,CAACC,IAAI,CAAC;IAClB;IAEA/B,GAAG,+DAA+D;QAChE,MAAMD,OACJG,yBAAyB;YACvBQ,aAAa;gBAAEC,OAAO;gBAAMK,WAAW;YAAK;YAC5CM,mBAAmB;YACnBC,0BAA0B;YAC1BC,KAAKjB,cAAc;gBAAEC,gBAAgB;YAAK;YAC1CiB,yBAAyB;YACzBC,kBAAkB;QACpB,IACAC,OAAO,CAACC,aAAa,CAAC;YAAEC,QAAQ;QAAI;IACxC;AACF"}
@@ -1,16 +0,0 @@
1
- import type { PayloadHandler, PayloadRequest, UploadCollectionSlug } from 'payload';
2
- import type { VercelBlobCollectionOptions } from './authorizeFileOverwrite.js';
3
- type Args = {
4
- access?: (args: {
5
- collectionSlug: UploadCollectionSlug;
6
- req: PayloadRequest;
7
- }) => boolean | Promise<boolean>;
8
- addRandomSuffix?: boolean;
9
- cacheControlMaxAge?: number;
10
- collections: VercelBlobCollectionOptions;
11
- token: string;
12
- useCompositePrefixes?: boolean;
13
- };
14
- export declare const getClientUploadRoute: ({ access, cacheControlMaxAge, collections, token, useCompositePrefixes, }: Args) => PayloadHandler;
15
- export {};
16
- //# sourceMappingURL=getClientUploadRoute.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"getClientUploadRoute.d.ts","sourceRoot":"","sources":["../src/getClientUploadRoute.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AAenF,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,6BAA6B,CAAA;AAI9E,KAAK,IAAI,GAAG;IACV,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE;QACd,cAAc,EAAE,oBAAoB,CAAA;QACpC,GAAG,EAAE,cAAc,CAAA;KACpB,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAChC,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,WAAW,EAAE,2BAA2B,CAAA;IACxC,KAAK,EAAE,MAAM,CAAA;IACb,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B,CAAA;AAID,eAAO,MAAM,oBAAoB,8EAO5B,IAAI,KAAG,cA8JT,CAAA"}
@@ -1,149 +0,0 @@
1
- import { buildStoragePathData, resolveSignedURLKey, verifyClientUploadReceiptForFileKey } from '@payloadcms/plugin-cloud-storage/utilities';
2
- import { handleUpload } from '@vercel/blob/client';
3
- import { APIError, Forbidden } from 'payload';
4
- import { assertClientUploadAccess, assertClientUploadAllowed, createClientUploadReceipt } from 'payload/internal';
5
- import { authorizeClientOverwrite } from './authorizeFileOverwrite.js';
6
- const defaultAccess = ({ req })=>!!req.user;
7
- export const getClientUploadRoute = ({ access = defaultAccess, cacheControlMaxAge, collections, token, useCompositePrefixes })=>async (req)=>{
8
- try {
9
- if (req.searchParams.get('issue-client-upload') === '1') {
10
- const { collectionSlug, docPrefix, filename, mimeType } = await req.json();
11
- if (typeof collectionSlug !== 'string' || typeof filename !== 'string' || typeof mimeType !== 'string' || docPrefix !== undefined && typeof docPrefix !== 'string' || !Object.hasOwn(collections, collectionSlug)) {
12
- throw new APIError('Invalid upload payload', 400);
13
- }
14
- await assertClientUploadAccess({
15
- collectionSlug,
16
- req
17
- });
18
- if (!await access({
19
- collectionSlug,
20
- req
21
- })) {
22
- throw new Forbidden();
23
- }
24
- assertClientUploadAllowed({
25
- collection: req.payload.collections[collectionSlug]?.config,
26
- filename,
27
- mimeType
28
- });
29
- const collectionConfig = collections[collectionSlug];
30
- const collectionPrefix = typeof collectionConfig === 'object' && collectionConfig.prefix || '';
31
- const requested = buildStoragePathData({
32
- collectionPrefix,
33
- docPrefix,
34
- filename,
35
- useCompositePrefixes
36
- });
37
- const allowOverwrite = await authorizeClientOverwrite({
38
- collectionPrefix,
39
- collections,
40
- req,
41
- requestedCollectionSlug: collectionSlug,
42
- requestedFilename: requested.sanitizedFilename,
43
- requestedStorageFilePath: requested.storageFilePath,
44
- useCompositePrefixes
45
- });
46
- const resolved = allowOverwrite ? {
47
- ...requested,
48
- clientUploadContext: {
49
- allowOverwrite: true,
50
- prefix: requested.sanitizedDocPrefix,
51
- signedReceipt: createClientUploadReceipt({
52
- collectionSlug,
53
- context: {
54
- allowOverwrite: true,
55
- prefix: requested.sanitizedDocPrefix
56
- },
57
- filename: requested.sanitizedFilename,
58
- req
59
- })
60
- }
61
- } : await resolveSignedURLKey({
62
- collectionPrefix,
63
- collectionSlug,
64
- docPrefix,
65
- filename,
66
- req,
67
- useCompositePrefixes
68
- });
69
- return Response.json({
70
- clientUploadContext: resolved.clientUploadContext,
71
- filename: resolved.sanitizedFilename,
72
- pathname: resolved.storageFilePath
73
- });
74
- }
75
- const body = await req.json();
76
- const jsonResponse = await handleUpload({
77
- body,
78
- onBeforeGenerateToken: async (pathname, clientPayload)=>{
79
- if (!clientPayload) {
80
- throw new APIError('No payload was provided');
81
- }
82
- let parsed;
83
- try {
84
- parsed = JSON.parse(clientPayload);
85
- } catch {
86
- parsed = {
87
- collectionSlug: clientPayload
88
- };
89
- }
90
- if (typeof parsed.collectionSlug !== 'string' || !parsed.collectionSlug || parsed.mimeType !== undefined && typeof parsed.mimeType !== 'string' || typeof parsed.signedReceipt !== 'string') {
91
- throw new APIError('Invalid upload payload', 400);
92
- }
93
- const { collectionSlug, mimeType, signedReceipt } = parsed;
94
- await assertClientUploadAccess({
95
- collectionSlug,
96
- req
97
- });
98
- if (!Object.hasOwn(collections, collectionSlug)) {
99
- throw new APIError('Invalid upload payload', 400);
100
- }
101
- if (!await access({
102
- collectionSlug,
103
- req
104
- })) {
105
- throw new Forbidden();
106
- }
107
- const collectionConfig = collections[collectionSlug];
108
- const collectionPrefix = typeof collectionConfig === 'object' && collectionConfig.prefix || '';
109
- const receipt = verifyClientUploadReceiptForFileKey({
110
- collectionPrefix,
111
- collectionSlug,
112
- expectedFileKey: pathname,
113
- req,
114
- signedReceipt,
115
- useCompositePrefixes
116
- });
117
- assertClientUploadAllowed({
118
- collection: req.payload.collections[collectionSlug]?.config,
119
- filename: pathname,
120
- mimeType
121
- });
122
- return {
123
- addRandomSuffix: false,
124
- ...mimeType ? {
125
- allowedContentTypes: [
126
- mimeType
127
- ]
128
- } : {},
129
- ...receipt.context.allowOverwrite === true && {
130
- allowOverwrite: true
131
- },
132
- cacheControlMaxAge
133
- };
134
- },
135
- onUploadCompleted: async ()=>{},
136
- request: req,
137
- token
138
- });
139
- return Response.json(jsonResponse);
140
- } catch (error) {
141
- if (error instanceof APIError) {
142
- throw error;
143
- }
144
- req.payload.logger.error(error);
145
- throw new APIError('storage-vercel-blob client upload route error');
146
- }
147
- };
148
-
149
- //# sourceMappingURL=getClientUploadRoute.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/getClientUploadRoute.ts"],"sourcesContent":["import type { PayloadHandler, PayloadRequest, UploadCollectionSlug } from 'payload'\n\nimport {\n buildStoragePathData,\n resolveSignedURLKey,\n verifyClientUploadReceiptForFileKey,\n} from '@payloadcms/plugin-cloud-storage/utilities'\nimport { handleUpload, type HandleUploadBody } from '@vercel/blob/client'\nimport { APIError, Forbidden } from 'payload'\nimport {\n assertClientUploadAccess,\n assertClientUploadAllowed,\n createClientUploadReceipt,\n} from 'payload/internal'\n\nimport type { VercelBlobCollectionOptions } from './authorizeFileOverwrite.js'\n\nimport { authorizeClientOverwrite } from './authorizeFileOverwrite.js'\n\ntype Args = {\n access?: (args: {\n collectionSlug: UploadCollectionSlug\n req: PayloadRequest\n }) => boolean | Promise<boolean>\n addRandomSuffix?: boolean\n cacheControlMaxAge?: number\n collections: VercelBlobCollectionOptions\n token: string\n useCompositePrefixes?: boolean\n}\n\nconst defaultAccess: Args['access'] = ({ req }) => !!req.user\n\nexport const getClientUploadRoute =\n ({\n access = defaultAccess,\n cacheControlMaxAge,\n collections,\n token,\n useCompositePrefixes,\n }: Args): PayloadHandler =>\n async (req) => {\n try {\n if (req.searchParams.get('issue-client-upload') === '1') {\n const { collectionSlug, docPrefix, filename, mimeType } = (await req.json!()) as {\n collectionSlug?: unknown\n docPrefix?: unknown\n filename?: unknown\n mimeType?: unknown\n }\n if (\n typeof collectionSlug !== 'string' ||\n typeof filename !== 'string' ||\n typeof mimeType !== 'string' ||\n (docPrefix !== undefined && typeof docPrefix !== 'string') ||\n !Object.hasOwn(collections, collectionSlug)\n ) {\n throw new APIError('Invalid upload payload', 400)\n }\n\n await assertClientUploadAccess({ collectionSlug, req })\n\n if (!(await access({ collectionSlug, req }))) {\n throw new Forbidden()\n }\n assertClientUploadAllowed({\n collection: req.payload.collections[collectionSlug]?.config,\n filename,\n mimeType,\n })\n const collectionConfig = collections[collectionSlug]\n const collectionPrefix =\n (typeof collectionConfig === 'object' && collectionConfig.prefix) || ''\n const requested = buildStoragePathData({\n collectionPrefix,\n docPrefix,\n filename,\n useCompositePrefixes,\n })\n const allowOverwrite = await authorizeClientOverwrite({\n collectionPrefix,\n collections,\n req,\n requestedCollectionSlug: collectionSlug,\n requestedFilename: requested.sanitizedFilename,\n requestedStorageFilePath: requested.storageFilePath,\n useCompositePrefixes,\n })\n const resolved = allowOverwrite\n ? {\n ...requested,\n clientUploadContext: {\n allowOverwrite: true,\n prefix: requested.sanitizedDocPrefix,\n signedReceipt: createClientUploadReceipt({\n collectionSlug,\n context: {\n allowOverwrite: true,\n prefix: requested.sanitizedDocPrefix,\n },\n filename: requested.sanitizedFilename,\n req,\n }),\n },\n }\n : await resolveSignedURLKey({\n collectionPrefix,\n collectionSlug,\n docPrefix,\n filename,\n req,\n useCompositePrefixes,\n })\n return Response.json({\n clientUploadContext: resolved.clientUploadContext,\n filename: resolved.sanitizedFilename,\n pathname: resolved.storageFilePath,\n })\n }\n\n const body = (await req.json!()) as HandleUploadBody\n const jsonResponse = await handleUpload({\n body,\n onBeforeGenerateToken: async (pathname: string, clientPayload: null | string) => {\n if (!clientPayload) {\n throw new APIError('No payload was provided')\n }\n\n let parsed: { collectionSlug?: unknown; mimeType?: unknown; signedReceipt?: unknown }\n try {\n parsed = JSON.parse(clientPayload) as {\n collectionSlug?: unknown\n mimeType?: unknown\n signedReceipt?: unknown\n }\n } catch {\n parsed = { collectionSlug: clientPayload }\n }\n\n if (\n typeof parsed.collectionSlug !== 'string' ||\n !parsed.collectionSlug ||\n (parsed.mimeType !== undefined && typeof parsed.mimeType !== 'string') ||\n typeof parsed.signedReceipt !== 'string'\n ) {\n throw new APIError('Invalid upload payload', 400)\n }\n\n const { collectionSlug, mimeType, signedReceipt } = parsed\n\n await assertClientUploadAccess({ collectionSlug, req })\n\n if (!Object.hasOwn(collections, collectionSlug)) {\n throw new APIError('Invalid upload payload', 400)\n }\n\n if (!(await access({ collectionSlug, req }))) {\n throw new Forbidden()\n }\n const collectionConfig = collections[collectionSlug]\n const collectionPrefix =\n (typeof collectionConfig === 'object' && collectionConfig.prefix) || ''\n const receipt = verifyClientUploadReceiptForFileKey({\n collectionPrefix,\n collectionSlug,\n expectedFileKey: pathname,\n req,\n signedReceipt,\n useCompositePrefixes,\n })\n\n assertClientUploadAllowed({\n collection: req.payload.collections[collectionSlug]?.config,\n filename: pathname,\n mimeType,\n })\n\n return {\n addRandomSuffix: false,\n ...(mimeType ? { allowedContentTypes: [mimeType] } : {}),\n ...(receipt.context.allowOverwrite === true && { allowOverwrite: true }),\n cacheControlMaxAge,\n }\n },\n onUploadCompleted: async () => {},\n request: req as Request,\n token,\n })\n\n return Response.json(jsonResponse)\n } catch (error) {\n if (error instanceof APIError) {\n throw error\n }\n\n req.payload.logger.error(error)\n throw new APIError('storage-vercel-blob client upload route error')\n }\n }\n"],"names":["buildStoragePathData","resolveSignedURLKey","verifyClientUploadReceiptForFileKey","handleUpload","APIError","Forbidden","assertClientUploadAccess","assertClientUploadAllowed","createClientUploadReceipt","authorizeClientOverwrite","defaultAccess","req","user","getClientUploadRoute","access","cacheControlMaxAge","collections","token","useCompositePrefixes","searchParams","get","collectionSlug","docPrefix","filename","mimeType","json","undefined","Object","hasOwn","collection","payload","config","collectionConfig","collectionPrefix","prefix","requested","allowOverwrite","requestedCollectionSlug","requestedFilename","sanitizedFilename","requestedStorageFilePath","storageFilePath","resolved","clientUploadContext","sanitizedDocPrefix","signedReceipt","context","Response","pathname","body","jsonResponse","onBeforeGenerateToken","clientPayload","parsed","JSON","parse","receipt","expectedFileKey","addRandomSuffix","allowedContentTypes","onUploadCompleted","request","error","logger"],"mappings":"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"}
@@ -1,71 +0,0 @@
1
- import { afterEach, describe, expect, it, vi } from 'vitest';
2
- import { head } from '@vercel/blob';
3
- vi.mock('@vercel/blob', async (importOriginal)=>({
4
- ...await importOriginal(),
5
- head: vi.fn(async ()=>({
6
- contentDisposition: 'inline',
7
- contentType: 'image/png',
8
- size: 4,
9
- uploadedAt: new Date('2026-01-01')
10
- }))
11
- }));
12
- afterEach(()=>{
13
- vi.unstubAllGlobals();
14
- vi.clearAllMocks();
15
- });
16
- import { createVercelBlobAdapter } from './adapter.js';
17
- describe('static handler document locations', ()=>{
18
- it('should read the checked document without another lookup', async ()=>{
19
- const fetchMock = vi.fn(async ()=>new Response('file'));
20
- vi.stubGlobal('fetch', fetchMock);
21
- const adapter = createVercelBlobAdapter({
22
- access: 'public',
23
- baseUrl: 'https://example.com',
24
- cacheControlMaxAge: 60,
25
- token: 'token'
26
- });
27
- const find = vi.fn(async ()=>({
28
- docs: [
29
- {
30
- prefix: 'database'
31
- }
32
- ]
33
- }));
34
- const generatedAdapter = adapter({
35
- collection: {
36
- fields: [],
37
- slug: 'media',
38
- upload: {}
39
- },
40
- prefix: 'media'
41
- });
42
- const doc = {
43
- id: 'authorized',
44
- prefix: 'legacy'
45
- };
46
- const response = await generatedAdapter.staticHandler({
47
- headers: new Headers(),
48
- payload: {
49
- find,
50
- logger: {
51
- error: vi.fn()
52
- }
53
- }
54
- }, {
55
- doc,
56
- params: {
57
- collection: 'media',
58
- filename: 'original.png'
59
- }
60
- });
61
- expect(response.status).toBe(200);
62
- expect(await response.text()).toBe('file');
63
- expect(head).toHaveBeenCalledWith(`https://example.com/legacy/original.png`, {
64
- token: 'token'
65
- });
66
- expect(fetchMock).toHaveBeenCalledWith(`https://example.com/legacy/original.png?2026-01-01T00:00:00.000Z`, expect.any(Object));
67
- expect(find).not.toHaveBeenCalled();
68
- });
69
- });
70
-
71
- //# sourceMappingURL=staticHandler.spec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/staticHandler.spec.ts"],"sourcesContent":["import { afterEach, describe, expect, it, vi } from 'vitest'\n\nimport { head } from '@vercel/blob'\n\nvi.mock('@vercel/blob', async (importOriginal) => ({\n ...(await importOriginal<typeof import('@vercel/blob')>()),\n head: vi.fn(async () => ({\n contentDisposition: 'inline',\n contentType: 'image/png',\n size: 4,\n uploadedAt: new Date('2026-01-01'),\n })),\n}))\n\nafterEach(() => {\n vi.unstubAllGlobals()\n vi.clearAllMocks()\n})\n\nimport { createVercelBlobAdapter } from './adapter.js'\n\ndescribe('static handler document locations', () => {\n it('should read the checked document without another lookup', async () => {\n const fetchMock = vi.fn(async () => new Response('file'))\n\n vi.stubGlobal('fetch', fetchMock)\n const adapter = createVercelBlobAdapter({\n access: 'public',\n baseUrl: 'https://example.com',\n cacheControlMaxAge: 60,\n token: 'token',\n })\n const find = vi.fn(async () => ({ docs: [{ prefix: 'database' }] }))\n const generatedAdapter = adapter({\n collection: {\n fields: [],\n slug: 'media',\n upload: {},\n },\n prefix: 'media',\n })\n const doc = {\n id: 'authorized',\n prefix: 'legacy',\n }\n const response = await generatedAdapter.staticHandler(\n {\n headers: new Headers(),\n payload: { find, logger: { error: vi.fn() } },\n } as never,\n {\n doc,\n params: { collection: 'media', filename: 'original.png' },\n },\n )\n\n expect(response.status).toBe(200)\n expect(await response.text()).toBe('file')\n expect(head).toHaveBeenCalledWith(`https://example.com/legacy/original.png`, { token: 'token' })\n expect(fetchMock).toHaveBeenCalledWith(\n `https://example.com/legacy/original.png?2026-01-01T00:00:00.000Z`,\n expect.any(Object),\n )\n expect(find).not.toHaveBeenCalled()\n })\n})\n"],"names":["afterEach","describe","expect","it","vi","head","mock","importOriginal","fn","contentDisposition","contentType","size","uploadedAt","Date","unstubAllGlobals","clearAllMocks","createVercelBlobAdapter","fetchMock","Response","stubGlobal","adapter","access","baseUrl","cacheControlMaxAge","token","find","docs","prefix","generatedAdapter","collection","fields","slug","upload","doc","id","response","staticHandler","headers","Headers","payload","logger","error","params","filename","status","toBe","text","toHaveBeenCalledWith","any","Object","not","toHaveBeenCalled"],"mappings":"AAAA,SAASA,SAAS,EAAEC,QAAQ,EAAEC,MAAM,EAAEC,EAAE,EAAEC,EAAE,QAAQ,SAAQ;AAE5D,SAASC,IAAI,QAAQ,eAAc;AAEnCD,GAAGE,IAAI,CAAC,gBAAgB,OAAOC,iBAAoB,CAAA;QACjD,GAAI,MAAMA,gBAA+C;QACzDF,MAAMD,GAAGI,EAAE,CAAC,UAAa,CAAA;gBACvBC,oBAAoB;gBACpBC,aAAa;gBACbC,MAAM;gBACNC,YAAY,IAAIC,KAAK;YACvB,CAAA;IACF,CAAA;AAEAb,UAAU;IACRI,GAAGU,gBAAgB;IACnBV,GAAGW,aAAa;AAClB;AAEA,SAASC,uBAAuB,QAAQ,eAAc;AAEtDf,SAAS,qCAAqC;IAC5CE,GAAG,2DAA2D;QAC5D,MAAMc,YAAYb,GAAGI,EAAE,CAAC,UAAY,IAAIU,SAAS;QAEjDd,GAAGe,UAAU,CAAC,SAASF;QACvB,MAAMG,UAAUJ,wBAAwB;YACtCK,QAAQ;YACRC,SAAS;YACTC,oBAAoB;YACpBC,OAAO;QACT;QACA,MAAMC,OAAOrB,GAAGI,EAAE,CAAC,UAAa,CAAA;gBAAEkB,MAAM;oBAAC;wBAAEC,QAAQ;oBAAW;iBAAE;YAAC,CAAA;QACjE,MAAMC,mBAAmBR,QAAQ;YAC/BS,YAAY;gBACVC,QAAQ,EAAE;gBACVC,MAAM;gBACNC,QAAQ,CAAC;YACX;YACAL,QAAQ;QACV;QACA,MAAMM,MAAM;YACVC,IAAI;YACJP,QAAQ;QACV;QACA,MAAMQ,WAAW,MAAMP,iBAAiBQ,aAAa,CACnD;YACEC,SAAS,IAAIC;YACbC,SAAS;gBAAEd;gBAAMe,QAAQ;oBAAEC,OAAOrC,GAAGI,EAAE;gBAAG;YAAE;QAC9C,GACA;YACEyB;YACAS,QAAQ;gBAAEb,YAAY;gBAASc,UAAU;YAAe;QAC1D;QAGFzC,OAAOiC,SAASS,MAAM,EAAEC,IAAI,CAAC;QAC7B3C,OAAO,MAAMiC,SAASW,IAAI,IAAID,IAAI,CAAC;QACnC3C,OAAOG,MAAM0C,oBAAoB,CAAC,CAAC,uCAAuC,CAAC,EAAE;YAAEvB,OAAO;QAAQ;QAC9FtB,OAAOe,WAAW8B,oBAAoB,CACpC,CAAC,gEAAgE,CAAC,EAClE7C,OAAO8C,GAAG,CAACC;QAEb/C,OAAOuB,MAAMyB,GAAG,CAACC,gBAAgB;IACnC;AACF"}