@payloadcms/storage-azure 3.91.0-internal.c1569dc → 4.0.0-beta.1

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 (58) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +7 -16
  3. package/dist/adapter.d.ts.map +1 -1
  4. package/dist/adapter.js +33 -8
  5. package/dist/adapter.js.map +1 -1
  6. package/dist/client/AzureClientUploadHandler.d.ts +4 -8
  7. package/dist/client/AzureClientUploadHandler.d.ts.map +1 -1
  8. package/dist/client/AzureClientUploadHandler.js +2 -12
  9. package/dist/client/AzureClientUploadHandler.js.map +1 -1
  10. package/dist/client/handleAzureUpload.d.ts +2 -14
  11. package/dist/client/handleAzureUpload.d.ts.map +1 -1
  12. package/dist/client/handleAzureUpload.js +15 -59
  13. package/dist/client/handleAzureUpload.js.map +1 -1
  14. package/dist/copyFile.d.ts +11 -0
  15. package/dist/copyFile.d.ts.map +1 -0
  16. package/dist/copyFile.js +52 -0
  17. package/dist/copyFile.js.map +1 -0
  18. package/dist/generateUploadInstructions.d.ts +12 -0
  19. package/dist/generateUploadInstructions.d.ts.map +1 -0
  20. package/dist/generateUploadInstructions.js +57 -0
  21. package/dist/generateUploadInstructions.js.map +1 -0
  22. package/dist/getFile.d.ts +4 -3
  23. package/dist/getFile.d.ts.map +1 -1
  24. package/dist/getFile.js +18 -16
  25. package/dist/getFile.js.map +1 -1
  26. package/dist/index.d.ts +11 -33
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +74 -79
  29. package/dist/index.js.map +1 -1
  30. package/dist/isClientUploadAllowed.d.ts +5 -2
  31. package/dist/isClientUploadAllowed.d.ts.map +1 -1
  32. package/dist/isClientUploadAllowed.js +1 -1
  33. package/dist/isClientUploadAllowed.js.map +1 -1
  34. package/package.json +6 -6
  35. package/dist/adapter.spec.d.ts +0 -2
  36. package/dist/adapter.spec.d.ts.map +0 -1
  37. package/dist/adapter.spec.js +0 -15
  38. package/dist/adapter.spec.js.map +0 -1
  39. package/dist/client/handleAzureUpload.spec.d.ts +0 -2
  40. package/dist/client/handleAzureUpload.spec.d.ts.map +0 -1
  41. package/dist/client/handleAzureUpload.spec.js +0 -214
  42. package/dist/client/handleAzureUpload.spec.js.map +0 -1
  43. package/dist/generateSignedURL.d.ts +0 -14
  44. package/dist/generateSignedURL.d.ts.map +0 -1
  45. package/dist/generateSignedURL.js +0 -63
  46. package/dist/generateSignedURL.js.map +0 -1
  47. package/dist/generateSignedURL.spec.d.ts +0 -2
  48. package/dist/generateSignedURL.spec.d.ts.map +0 -1
  49. package/dist/generateSignedURL.spec.js +0 -65
  50. package/dist/generateSignedURL.spec.js.map +0 -1
  51. package/dist/index.containerAccess.spec.d.ts +0 -2
  52. package/dist/index.containerAccess.spec.d.ts.map +0 -1
  53. package/dist/index.containerAccess.spec.js +0 -78
  54. package/dist/index.containerAccess.spec.js.map +0 -1
  55. package/dist/index.spec.d.ts +0 -2
  56. package/dist/index.spec.d.ts.map +0 -1
  57. package/dist/index.spec.js +0 -44
  58. package/dist/index.spec.js.map +0 -1
@@ -1,214 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from 'vitest';
2
- const uploadDataMock = vi.fn();
3
- const blockBlobClientMock = vi.fn(function() {
4
- return {
5
- uploadData: uploadDataMock
6
- };
7
- });
8
- vi.mock('@azure/storage-blob', ()=>({
9
- BlockBlobClient: blockBlobClientMock
10
- }));
11
- import { handleAzureUpload } from './handleAzureUpload.js';
12
- const serverURL = 'https://example.com';
13
- const apiRoute = '/api';
14
- const serverHandlerPath = '/storage-azure-generate-signed-url';
15
- const signedURL = 'https://account.blob.core.windows.net/container/file.png?sig=abc';
16
- const clientUploadContext = {
17
- prefix: 'docs',
18
- signedReceipt: 'receipt'
19
- };
20
- const createFile = ()=>new File([
21
- new Uint8Array([
22
- 1,
23
- 2,
24
- 3
25
- ])
26
- ], 'file.png', {
27
- type: 'image/png'
28
- });
29
- const mockSignedURLResponse = (body, ok = true)=>{
30
- const fetchMock = vi.fn().mockResolvedValue({
31
- json: ()=>Promise.resolve({
32
- clientUploadContext,
33
- ...body
34
- }),
35
- ok
36
- });
37
- vi.stubGlobal('fetch', fetchMock);
38
- return fetchMock;
39
- };
40
- const invoke = (overrides = {})=>handleAzureUpload({
41
- apiRoute,
42
- chunkLargeFiles: true,
43
- collectionSlug: 'media',
44
- file: createFile(),
45
- serverHandlerPath,
46
- serverURL,
47
- updateFilename: vi.fn(),
48
- ...overrides
49
- });
50
- describe('handleAzureUpload', ()=>{
51
- beforeEach(()=>{
52
- vi.clearAllMocks();
53
- vi.unstubAllGlobals();
54
- uploadDataMock.mockResolvedValue(undefined);
55
- });
56
- describe('shared behavior', ()=>{
57
- it('should request a signed URL from the server handler', async ()=>{
58
- const fetchMock = mockSignedURLResponse({
59
- docPrefix: 'docs',
60
- url: signedURL
61
- });
62
- await invoke({
63
- docPrefix: 'docs'
64
- });
65
- const [calledURL, init] = fetchMock.mock.calls[0];
66
- expect(calledURL).toBe(`${serverURL}${apiRoute}${serverHandlerPath}`);
67
- expect(init.method).toBe('POST');
68
- expect(JSON.parse(init.body)).toEqual({
69
- collectionSlug: 'media',
70
- docPrefix: 'docs',
71
- filename: 'file.png',
72
- mimeType: 'image/png'
73
- });
74
- });
75
- it('should call updateFilename when the server returns a sanitized filename', async ()=>{
76
- mockSignedURLResponse({
77
- docPrefix: 'docs',
78
- filename: 'file-1.png',
79
- url: signedURL
80
- });
81
- const updateFilename = vi.fn();
82
- await invoke({
83
- updateFilename
84
- });
85
- expect(updateFilename).toHaveBeenCalledWith('file-1.png');
86
- });
87
- it('should not call updateFilename when the filename is unchanged', async ()=>{
88
- mockSignedURLResponse({
89
- docPrefix: 'docs',
90
- filename: 'file.png',
91
- url: signedURL
92
- });
93
- const updateFilename = vi.fn();
94
- await invoke({
95
- updateFilename
96
- });
97
- expect(updateFilename).not.toHaveBeenCalled();
98
- });
99
- it('should reject a missing upload reference before uploading', async ()=>{
100
- mockSignedURLResponse({
101
- clientUploadContext: undefined,
102
- docPrefix: 'docs',
103
- url: signedURL
104
- });
105
- await expect(invoke()).rejects.toThrow('Invalid Azure client upload reference');
106
- expect(uploadDataMock).not.toHaveBeenCalled();
107
- expect(blockBlobClientMock).not.toHaveBeenCalled();
108
- });
109
- it('should throw when the signed URL request fails, before any upload', async ()=>{
110
- mockSignedURLResponse({}, false);
111
- await expect(invoke()).rejects.toThrow();
112
- expect(uploadDataMock).not.toHaveBeenCalled();
113
- expect(blockBlobClientMock).not.toHaveBeenCalled();
114
- });
115
- });
116
- describe('chunkLargeFiles: true (SDK block upload)', ()=>{
117
- it('should upload via BlockBlobClient.uploadData rather than a raw PUT', async ()=>{
118
- const fetchMock = mockSignedURLResponse({
119
- docPrefix: 'docs',
120
- url: signedURL
121
- });
122
- const file = createFile();
123
- await invoke({
124
- chunkLargeFiles: true,
125
- file
126
- });
127
- expect(blockBlobClientMock).toHaveBeenCalledWith(signedURL);
128
- expect(uploadDataMock).toHaveBeenCalledTimes(1);
129
- const [uploadedFile, options] = uploadDataMock.mock.calls[0];
130
- expect(uploadedFile).toBe(file);
131
- expect(options.blockSize).toBeGreaterThan(0);
132
- expect(options.concurrency).toBeGreaterThan(0);
133
- // Only the signed-URL request should hit fetch; no raw PUT to the blob URL.
134
- expect(fetchMock).toHaveBeenCalledTimes(1);
135
- });
136
- it('should pass the file content type to the blob headers', async ()=>{
137
- mockSignedURLResponse({
138
- docPrefix: 'docs',
139
- url: signedURL
140
- });
141
- await invoke({
142
- chunkLargeFiles: true
143
- });
144
- const [, options] = uploadDataMock.mock.calls[0];
145
- expect(options.blobHTTPHeaders).toEqual({
146
- blobContentType: 'image/png'
147
- });
148
- });
149
- it('should propagate errors from uploadData', async ()=>{
150
- mockSignedURLResponse({
151
- docPrefix: 'docs',
152
- url: signedURL
153
- });
154
- uploadDataMock.mockRejectedValue(new Error('block upload failed'));
155
- await expect(invoke({
156
- chunkLargeFiles: true
157
- })).rejects.toThrow('block upload failed');
158
- });
159
- });
160
- describe('chunkLargeFiles: false (single PUT, default/legacy)', ()=>{
161
- it('should upload via a single raw PUT with the BlockBlob header', async ()=>{
162
- const fetchMock = mockSignedURLResponse({
163
- docPrefix: 'docs',
164
- url: signedURL
165
- });
166
- const file = createFile();
167
- await invoke({
168
- chunkLargeFiles: false,
169
- file
170
- });
171
- // 1st fetch = signed-URL POST, 2nd = the raw PUT to the blob URL
172
- expect(fetchMock).toHaveBeenCalledTimes(2);
173
- const [putURL, putInit] = fetchMock.mock.calls[1];
174
- expect(putURL).toBe(signedURL);
175
- expect(putInit.method).toBe('PUT');
176
- expect(putInit.body).toBe(file);
177
- expect(putInit.headers['x-ms-blob-type']).toBe('BlockBlob');
178
- expect(putInit.headers['Content-Type']).toBe('image/png');
179
- });
180
- it('should not use the Azure SDK', async ()=>{
181
- mockSignedURLResponse({
182
- docPrefix: 'docs',
183
- url: signedURL
184
- });
185
- await invoke({
186
- chunkLargeFiles: false
187
- });
188
- expect(blockBlobClientMock).not.toHaveBeenCalled();
189
- expect(uploadDataMock).not.toHaveBeenCalled();
190
- });
191
- it('should throw when the raw PUT is rejected', async ()=>{
192
- const fetchMock = mockSignedURLResponse({
193
- docPrefix: 'docs',
194
- url: signedURL
195
- });
196
- fetchMock.mockResolvedValueOnce({
197
- json: ()=>Promise.resolve({
198
- clientUploadContext,
199
- docPrefix: 'docs',
200
- url: signedURL
201
- }),
202
- ok: true
203
- });
204
- fetchMock.mockResolvedValueOnce({
205
- ok: false
206
- });
207
- await expect(invoke({
208
- chunkLargeFiles: false
209
- })).rejects.toThrow('Failed to upload file to Azure Blob Storage');
210
- });
211
- });
212
- });
213
-
214
- //# sourceMappingURL=handleAzureUpload.spec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/client/handleAzureUpload.spec.ts"],"sourcesContent":["import { beforeEach, describe, expect, it, vi } from 'vitest'\n\nconst uploadDataMock = vi.fn()\nconst blockBlobClientMock = vi.fn(function () {\n return { uploadData: uploadDataMock }\n})\n\nvi.mock('@azure/storage-blob', () => ({\n BlockBlobClient: blockBlobClientMock,\n}))\n\nimport { handleAzureUpload } from './handleAzureUpload.js'\n\nconst serverURL = 'https://example.com'\nconst apiRoute = '/api'\nconst serverHandlerPath = '/storage-azure-generate-signed-url' as const\nconst signedURL = 'https://account.blob.core.windows.net/container/file.png?sig=abc'\nconst clientUploadContext = { prefix: 'docs', signedReceipt: 'receipt' }\n\nconst createFile = () => new File([new Uint8Array([1, 2, 3])], 'file.png', { type: 'image/png' })\n\nconst mockSignedURLResponse = (body: Record<string, unknown>, ok = true) => {\n const fetchMock = vi.fn().mockResolvedValue({\n json: () => Promise.resolve({ clientUploadContext, ...body }),\n ok,\n })\n vi.stubGlobal('fetch', fetchMock)\n return fetchMock\n}\n\nconst invoke = (overrides: Partial<Parameters<typeof handleAzureUpload>[0]> = {}) =>\n handleAzureUpload({\n apiRoute,\n chunkLargeFiles: true,\n collectionSlug: 'media',\n file: createFile(),\n serverHandlerPath,\n serverURL,\n updateFilename: vi.fn(),\n ...overrides,\n })\n\ndescribe('handleAzureUpload', () => {\n beforeEach(() => {\n vi.clearAllMocks()\n vi.unstubAllGlobals()\n uploadDataMock.mockResolvedValue(undefined)\n })\n\n describe('shared behavior', () => {\n it('should request a signed URL from the server handler', async () => {\n const fetchMock = mockSignedURLResponse({ docPrefix: 'docs', url: signedURL })\n\n await invoke({ docPrefix: 'docs' })\n\n const [calledURL, init] = fetchMock.mock.calls[0]!\n expect(calledURL).toBe(`${serverURL}${apiRoute}${serverHandlerPath}`)\n expect(init.method).toBe('POST')\n expect(JSON.parse(init.body)).toEqual({\n collectionSlug: 'media',\n docPrefix: 'docs',\n filename: 'file.png',\n mimeType: 'image/png',\n })\n })\n\n it('should call updateFilename when the server returns a sanitized filename', async () => {\n mockSignedURLResponse({ docPrefix: 'docs', filename: 'file-1.png', url: signedURL })\n const updateFilename = vi.fn()\n\n await invoke({ updateFilename })\n\n expect(updateFilename).toHaveBeenCalledWith('file-1.png')\n })\n\n it('should not call updateFilename when the filename is unchanged', async () => {\n mockSignedURLResponse({ docPrefix: 'docs', filename: 'file.png', url: signedURL })\n const updateFilename = vi.fn()\n\n await invoke({ updateFilename })\n\n expect(updateFilename).not.toHaveBeenCalled()\n })\n\n it('should reject a missing upload reference before uploading', async () => {\n mockSignedURLResponse({ clientUploadContext: undefined, docPrefix: 'docs', url: signedURL })\n\n await expect(invoke()).rejects.toThrow('Invalid Azure client upload reference')\n\n expect(uploadDataMock).not.toHaveBeenCalled()\n expect(blockBlobClientMock).not.toHaveBeenCalled()\n })\n\n it('should throw when the signed URL request fails, before any upload', async () => {\n mockSignedURLResponse({}, false)\n\n await expect(invoke()).rejects.toThrow()\n\n expect(uploadDataMock).not.toHaveBeenCalled()\n expect(blockBlobClientMock).not.toHaveBeenCalled()\n })\n })\n\n describe('chunkLargeFiles: true (SDK block upload)', () => {\n it('should upload via BlockBlobClient.uploadData rather than a raw PUT', async () => {\n const fetchMock = mockSignedURLResponse({ docPrefix: 'docs', url: signedURL })\n const file = createFile()\n\n await invoke({ chunkLargeFiles: true, file })\n\n expect(blockBlobClientMock).toHaveBeenCalledWith(signedURL)\n expect(uploadDataMock).toHaveBeenCalledTimes(1)\n\n const [uploadedFile, options] = uploadDataMock.mock.calls[0]!\n expect(uploadedFile).toBe(file)\n expect(options.blockSize).toBeGreaterThan(0)\n expect(options.concurrency).toBeGreaterThan(0)\n\n // Only the signed-URL request should hit fetch; no raw PUT to the blob URL.\n expect(fetchMock).toHaveBeenCalledTimes(1)\n })\n\n it('should pass the file content type to the blob headers', async () => {\n mockSignedURLResponse({ docPrefix: 'docs', url: signedURL })\n\n await invoke({ chunkLargeFiles: true })\n\n const [, options] = uploadDataMock.mock.calls[0]!\n expect(options.blobHTTPHeaders).toEqual({ blobContentType: 'image/png' })\n })\n\n it('should propagate errors from uploadData', async () => {\n mockSignedURLResponse({ docPrefix: 'docs', url: signedURL })\n uploadDataMock.mockRejectedValue(new Error('block upload failed'))\n\n await expect(invoke({ chunkLargeFiles: true })).rejects.toThrow('block upload failed')\n })\n })\n\n describe('chunkLargeFiles: false (single PUT, default/legacy)', () => {\n it('should upload via a single raw PUT with the BlockBlob header', async () => {\n const fetchMock = mockSignedURLResponse({ docPrefix: 'docs', url: signedURL })\n const file = createFile()\n\n await invoke({ chunkLargeFiles: false, file })\n\n // 1st fetch = signed-URL POST, 2nd = the raw PUT to the blob URL\n expect(fetchMock).toHaveBeenCalledTimes(2)\n const [putURL, putInit] = fetchMock.mock.calls[1]!\n expect(putURL).toBe(signedURL)\n expect(putInit.method).toBe('PUT')\n expect(putInit.body).toBe(file)\n expect(putInit.headers['x-ms-blob-type']).toBe('BlockBlob')\n expect(putInit.headers['Content-Type']).toBe('image/png')\n })\n\n it('should not use the Azure SDK', async () => {\n mockSignedURLResponse({ docPrefix: 'docs', url: signedURL })\n\n await invoke({ chunkLargeFiles: false })\n\n expect(blockBlobClientMock).not.toHaveBeenCalled()\n expect(uploadDataMock).not.toHaveBeenCalled()\n })\n\n it('should throw when the raw PUT is rejected', async () => {\n const fetchMock = mockSignedURLResponse({ docPrefix: 'docs', url: signedURL })\n fetchMock.mockResolvedValueOnce({\n json: () => Promise.resolve({ clientUploadContext, docPrefix: 'docs', url: signedURL }),\n ok: true,\n })\n fetchMock.mockResolvedValueOnce({ ok: false })\n\n await expect(invoke({ chunkLargeFiles: false })).rejects.toThrow(\n 'Failed to upload file to Azure Blob Storage',\n )\n })\n })\n})\n"],"names":["beforeEach","describe","expect","it","vi","uploadDataMock","fn","blockBlobClientMock","uploadData","mock","BlockBlobClient","handleAzureUpload","serverURL","apiRoute","serverHandlerPath","signedURL","clientUploadContext","prefix","signedReceipt","createFile","File","Uint8Array","type","mockSignedURLResponse","body","ok","fetchMock","mockResolvedValue","json","Promise","resolve","stubGlobal","invoke","overrides","chunkLargeFiles","collectionSlug","file","updateFilename","clearAllMocks","unstubAllGlobals","undefined","docPrefix","url","calledURL","init","calls","toBe","method","JSON","parse","toEqual","filename","mimeType","toHaveBeenCalledWith","not","toHaveBeenCalled","rejects","toThrow","toHaveBeenCalledTimes","uploadedFile","options","blockSize","toBeGreaterThan","concurrency","blobHTTPHeaders","blobContentType","mockRejectedValue","Error","putURL","putInit","headers","mockResolvedValueOnce"],"mappings":"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"}
@@ -1,14 +0,0 @@
1
- import type { ContainerClient } from '@azure/storage-blob';
2
- import type { ClientUploadsAccess } from '@payloadcms/plugin-cloud-storage/types';
3
- import type { PayloadHandler } from 'payload';
4
- import type { AzureStorageOptions } from './index.js';
5
- interface Args {
6
- access?: ClientUploadsAccess;
7
- collections: AzureStorageOptions['collections'];
8
- containerName: string;
9
- getStorageClient: () => ContainerClient;
10
- useCompositePrefixes?: boolean;
11
- }
12
- export declare const getGenerateSignedURLHandler: ({ access, collections, containerName, getStorageClient, useCompositePrefixes, }: Args) => PayloadHandler;
13
- export {};
14
- //# sourceMappingURL=generateSignedURL.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"generateSignedURL.d.ts","sourceRoot":"","sources":["../src/generateSignedURL.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAA8B,MAAM,qBAAqB,CAAA;AACtF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAA;AACjF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAO7C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAIrD,UAAU,IAAI;IACZ,MAAM,CAAC,EAAE,mBAAmB,CAAA;IAC5B,WAAW,EAAE,mBAAmB,CAAC,aAAa,CAAC,CAAA;IAC/C,aAAa,EAAE,MAAM,CAAA;IACrB,gBAAgB,EAAE,MAAM,eAAe,CAAA;IACvC,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAID,eAAO,MAAM,2BAA2B,oFAMrC,IAAI,KAAG,cAqET,CAAA"}
@@ -1,63 +0,0 @@
1
- import { BlobSASPermissions, generateBlobSASQueryParameters } from '@azure/storage-blob';
2
- import { resolveSignedURLKey } from '@payloadcms/plugin-cloud-storage/utilities';
3
- import { APIError, Forbidden } from 'payload';
4
- import { assertClientUploadAccess, assertClientUploadAllowed } from 'payload/internal';
5
- import { isAzureClientUploadAllowed } from './isClientUploadAllowed.js';
6
- const defaultAccess = ({ req })=>!!req.user;
7
- export const getGenerateSignedURLHandler = ({ access = defaultAccess, collections, containerName, getStorageClient, useCompositePrefixes = false })=>{
8
- return async (req)=>{
9
- if (!req.json) {
10
- throw new APIError('Unreachable');
11
- }
12
- const { collectionSlug, docPrefix, filename, mimeType } = await req.json();
13
- await assertClientUploadAccess({
14
- collectionSlug,
15
- req
16
- });
17
- const collectionStorageConfig = collections[collectionSlug];
18
- if (!collectionStorageConfig) {
19
- throw new APIError(`Collection ${collectionSlug} was not found in Azure storage options`);
20
- }
21
- if (!isAzureClientUploadAllowed(req.payload.collections[collectionSlug]?.config)) {
22
- throw new APIError('Azure client uploads require allowRestrictedFileTypes.', 400);
23
- }
24
- const collectionPrefix = typeof collectionStorageConfig === 'object' && collectionStorageConfig.prefix || '';
25
- if (!await access({
26
- collectionSlug,
27
- req
28
- })) {
29
- throw new Forbidden();
30
- }
31
- assertClientUploadAllowed({
32
- collection: req.payload.collections[collectionSlug]?.config,
33
- filename,
34
- mimeType
35
- });
36
- const { clientUploadContext, fileKey, sanitizedDocPrefix, sanitizedFilename } = await resolveSignedURLKey({
37
- collectionPrefix,
38
- collectionSlug,
39
- docPrefix,
40
- filename,
41
- req,
42
- useCompositePrefixes
43
- });
44
- const blobClient = getStorageClient().getBlobClient(fileKey);
45
- const sasToken = generateBlobSASQueryParameters({
46
- blobName: fileKey,
47
- containerName,
48
- contentType: mimeType,
49
- expiresOn: new Date(Date.now() + 60 * 60 * 1000),
50
- permissions: BlobSASPermissions.parse('c'),
51
- startsOn: new Date(),
52
- version: '2026-04-06'
53
- }, getStorageClient().credential);
54
- return Response.json({
55
- clientUploadContext,
56
- docPrefix: sanitizedDocPrefix,
57
- filename: sanitizedFilename,
58
- url: `${blobClient.url}?${sasToken.toString()}`
59
- });
60
- };
61
- };
62
-
63
- //# sourceMappingURL=generateSignedURL.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/generateSignedURL.ts"],"sourcesContent":["import type { ContainerClient, StorageSharedKeyCredential } from '@azure/storage-blob'\nimport type { ClientUploadsAccess } from '@payloadcms/plugin-cloud-storage/types'\nimport type { PayloadHandler } from 'payload'\n\nimport { BlobSASPermissions, generateBlobSASQueryParameters } from '@azure/storage-blob'\nimport { resolveSignedURLKey } from '@payloadcms/plugin-cloud-storage/utilities'\nimport { APIError, Forbidden } from 'payload'\nimport { assertClientUploadAccess, assertClientUploadAllowed } from 'payload/internal'\n\nimport type { AzureStorageOptions } from './index.js'\n\nimport { isAzureClientUploadAllowed } from './isClientUploadAllowed.js'\n\ninterface Args {\n access?: ClientUploadsAccess\n collections: AzureStorageOptions['collections']\n containerName: string\n getStorageClient: () => ContainerClient\n useCompositePrefixes?: boolean\n}\n\nconst defaultAccess: Args['access'] = ({ req }) => !!req.user\n\nexport const getGenerateSignedURLHandler = ({\n access = defaultAccess,\n collections,\n containerName,\n getStorageClient,\n useCompositePrefixes = false,\n}: Args): PayloadHandler => {\n return async (req) => {\n if (!req.json) {\n throw new APIError('Unreachable')\n }\n\n const { collectionSlug, docPrefix, filename, mimeType } = (await req.json()) as {\n collectionSlug: string\n docPrefix?: string\n filename: string\n mimeType: string\n }\n\n await assertClientUploadAccess({ collectionSlug, req })\n\n const collectionStorageConfig = collections[collectionSlug]\n if (!collectionStorageConfig) {\n throw new APIError(`Collection ${collectionSlug} was not found in Azure storage options`)\n }\n\n if (!isAzureClientUploadAllowed(req.payload.collections[collectionSlug]?.config)) {\n throw new APIError('Azure client uploads require allowRestrictedFileTypes.', 400)\n }\n\n const collectionPrefix =\n (typeof collectionStorageConfig === 'object' && collectionStorageConfig.prefix) || ''\n\n if (!(await access({ collectionSlug, req }))) {\n throw new Forbidden()\n }\n\n assertClientUploadAllowed({\n collection: req.payload.collections[collectionSlug]?.config,\n filename,\n mimeType,\n })\n\n const { clientUploadContext, fileKey, sanitizedDocPrefix, sanitizedFilename } =\n await resolveSignedURLKey({\n collectionPrefix,\n collectionSlug,\n docPrefix,\n filename,\n req,\n useCompositePrefixes,\n })\n\n const blobClient = getStorageClient().getBlobClient(fileKey)\n\n const sasToken = generateBlobSASQueryParameters(\n {\n blobName: fileKey,\n containerName,\n contentType: mimeType,\n expiresOn: new Date(Date.now() + 60 * 60 * 1000),\n permissions: BlobSASPermissions.parse('c'),\n startsOn: new Date(),\n version: '2026-04-06',\n },\n getStorageClient().credential as StorageSharedKeyCredential,\n )\n\n return Response.json({\n clientUploadContext,\n docPrefix: sanitizedDocPrefix,\n filename: sanitizedFilename,\n url: `${blobClient.url}?${sasToken.toString()}`,\n })\n }\n}\n"],"names":["BlobSASPermissions","generateBlobSASQueryParameters","resolveSignedURLKey","APIError","Forbidden","assertClientUploadAccess","assertClientUploadAllowed","isAzureClientUploadAllowed","defaultAccess","req","user","getGenerateSignedURLHandler","access","collections","containerName","getStorageClient","useCompositePrefixes","json","collectionSlug","docPrefix","filename","mimeType","collectionStorageConfig","payload","config","collectionPrefix","prefix","collection","clientUploadContext","fileKey","sanitizedDocPrefix","sanitizedFilename","blobClient","getBlobClient","sasToken","blobName","contentType","expiresOn","Date","now","permissions","parse","startsOn","version","credential","Response","url","toString"],"mappings":"AAIA,SAASA,kBAAkB,EAAEC,8BAA8B,QAAQ,sBAAqB;AACxF,SAASC,mBAAmB,QAAQ,6CAA4C;AAChF,SAASC,QAAQ,EAAEC,SAAS,QAAQ,UAAS;AAC7C,SAASC,wBAAwB,EAAEC,yBAAyB,QAAQ,mBAAkB;AAItF,SAASC,0BAA0B,QAAQ,6BAA4B;AAUvE,MAAMC,gBAAgC,CAAC,EAAEC,GAAG,EAAE,GAAK,CAAC,CAACA,IAAIC,IAAI;AAE7D,OAAO,MAAMC,8BAA8B,CAAC,EAC1CC,SAASJ,aAAa,EACtBK,WAAW,EACXC,aAAa,EACbC,gBAAgB,EAChBC,uBAAuB,KAAK,EACvB;IACL,OAAO,OAAOP;QACZ,IAAI,CAACA,IAAIQ,IAAI,EAAE;YACb,MAAM,IAAId,SAAS;QACrB;QAEA,MAAM,EAAEe,cAAc,EAAEC,SAAS,EAAEC,QAAQ,EAAEC,QAAQ,EAAE,GAAI,MAAMZ,IAAIQ,IAAI;QAOzE,MAAMZ,yBAAyB;YAAEa;YAAgBT;QAAI;QAErD,MAAMa,0BAA0BT,WAAW,CAACK,eAAe;QAC3D,IAAI,CAACI,yBAAyB;YAC5B,MAAM,IAAInB,SAAS,CAAC,WAAW,EAAEe,eAAe,uCAAuC,CAAC;QAC1F;QAEA,IAAI,CAACX,2BAA2BE,IAAIc,OAAO,CAACV,WAAW,CAACK,eAAe,EAAEM,SAAS;YAChF,MAAM,IAAIrB,SAAS,0DAA0D;QAC/E;QAEA,MAAMsB,mBACJ,AAAC,OAAOH,4BAA4B,YAAYA,wBAAwBI,MAAM,IAAK;QAErF,IAAI,CAAE,MAAMd,OAAO;YAAEM;YAAgBT;QAAI,IAAK;YAC5C,MAAM,IAAIL;QACZ;QAEAE,0BAA0B;YACxBqB,YAAYlB,IAAIc,OAAO,CAACV,WAAW,CAACK,eAAe,EAAEM;YACrDJ;YACAC;QACF;QAEA,MAAM,EAAEO,mBAAmB,EAAEC,OAAO,EAAEC,kBAAkB,EAAEC,iBAAiB,EAAE,GAC3E,MAAM7B,oBAAoB;YACxBuB;YACAP;YACAC;YACAC;YACAX;YACAO;QACF;QAEF,MAAMgB,aAAajB,mBAAmBkB,aAAa,CAACJ;QAEpD,MAAMK,WAAWjC,+BACf;YACEkC,UAAUN;YACVf;YACAsB,aAAaf;YACbgB,WAAW,IAAIC,KAAKA,KAAKC,GAAG,KAAK,KAAK,KAAK;YAC3CC,aAAaxC,mBAAmByC,KAAK,CAAC;YACtCC,UAAU,IAAIJ;YACdK,SAAS;QACX,GACA5B,mBAAmB6B,UAAU;QAG/B,OAAOC,SAAS5B,IAAI,CAAC;YACnBW;YACAT,WAAWW;YACXV,UAAUW;YACVe,KAAK,GAAGd,WAAWc,GAAG,CAAC,CAAC,EAAEZ,SAASa,QAAQ,IAAI;QACjD;IACF;AACF,EAAC"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=generateSignedURL.spec.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"generateSignedURL.spec.d.ts","sourceRoot":"","sources":["../src/generateSignedURL.spec.ts"],"names":[],"mappings":""}
@@ -1,65 +0,0 @@
1
- import { StorageSharedKeyCredential } from '@azure/storage-blob';
2
- import { describe, expect, it, vi } from 'vitest';
3
- import { getGenerateSignedURLHandler } from './generateSignedURL.js';
4
- const createRequest = (allowRestrictedFileTypes, mimeType, filename = 'reference.png')=>({
5
- json: ()=>Promise.resolve({
6
- collectionSlug: 'media',
7
- filename,
8
- mimeType
9
- }),
10
- payload: {
11
- collections: {
12
- media: {
13
- config: {
14
- slug: 'media',
15
- access: {},
16
- upload: {
17
- allowRestrictedFileTypes
18
- }
19
- }
20
- }
21
- },
22
- db: {
23
- findOne: ()=>Promise.resolve(null)
24
- }
25
- },
26
- user: {
27
- id: 'user-id'
28
- }
29
- });
30
- describe('Azure signed upload URLs', ()=>{
31
- it('should use Payload uploads under secure defaults', async ()=>{
32
- const getStorageClient = vi.fn();
33
- const handler = getGenerateSignedURLHandler({
34
- collections: {
35
- media: true
36
- },
37
- containerName: 'media',
38
- getStorageClient
39
- });
40
- await expect(handler(createRequest(false, 'image/png'))).rejects.toThrow('Azure client uploads require allowRestrictedFileTypes.');
41
- expect(getStorageClient).not.toHaveBeenCalled();
42
- });
43
- it('should generate direct instructions for the explicit opt-out', async ()=>{
44
- const credential = new StorageSharedKeyCredential('account', Buffer.alloc(32, 'a').toString('base64'));
45
- const getStorageClient = ()=>({
46
- credential,
47
- getBlobClient: (key)=>({
48
- url: `https://account.blob.core.windows.net/media/${key}`
49
- })
50
- });
51
- const handler = getGenerateSignedURLHandler({
52
- collections: {
53
- media: true
54
- },
55
- containerName: 'media',
56
- getStorageClient
57
- });
58
- const response = await handler(createRequest(true, 'image/png', 'folder\\nested/reference.png'));
59
- const result = await response.json();
60
- expect(result.filename).toBe('reference.png');
61
- expect(result.url).toMatch(/https:\/\/account\.blob\.core\.windows\.net\/media\/[0-9a-f-]+\/reference\.png\?/);
62
- });
63
- });
64
-
65
- //# sourceMappingURL=generateSignedURL.spec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/generateSignedURL.spec.ts"],"sourcesContent":["import type { ContainerClient } from '@azure/storage-blob'\nimport type { PayloadRequest } from 'payload'\n\nimport { StorageSharedKeyCredential } from '@azure/storage-blob'\nimport { describe, expect, it, vi } from 'vitest'\n\nimport { getGenerateSignedURLHandler } from './generateSignedURL.js'\n\nconst createRequest = (\n allowRestrictedFileTypes: boolean,\n mimeType: unknown,\n filename = 'reference.png',\n): PayloadRequest =>\n ({\n json: () =>\n Promise.resolve({\n collectionSlug: 'media',\n filename,\n mimeType,\n }),\n payload: {\n collections: {\n media: {\n config: {\n slug: 'media',\n access: {},\n upload: { allowRestrictedFileTypes },\n },\n },\n },\n db: { findOne: () => Promise.resolve(null) },\n },\n user: { id: 'user-id' },\n }) as unknown as PayloadRequest\n\ndescribe('Azure signed upload URLs', () => {\n it('should use Payload uploads under secure defaults', async () => {\n const getStorageClient = vi.fn()\n const handler = getGenerateSignedURLHandler({\n collections: { media: true },\n containerName: 'media',\n getStorageClient,\n })\n\n await expect(handler(createRequest(false, 'image/png'))).rejects.toThrow(\n 'Azure client uploads require allowRestrictedFileTypes.',\n )\n expect(getStorageClient).not.toHaveBeenCalled()\n })\n\n it('should generate direct instructions for the explicit opt-out', async () => {\n const credential = new StorageSharedKeyCredential(\n 'account',\n Buffer.alloc(32, 'a').toString('base64'),\n )\n const getStorageClient = () =>\n ({\n credential,\n getBlobClient: (key: string) => ({\n url: `https://account.blob.core.windows.net/media/${key}`,\n }),\n }) as unknown as ContainerClient\n const handler = getGenerateSignedURLHandler({\n collections: { media: true },\n containerName: 'media',\n getStorageClient,\n })\n\n const response = await handler(createRequest(true, 'image/png', 'folder\\\\nested/reference.png'))\n const result = (await response.json()) as { filename: string; url: string }\n\n expect(result.filename).toBe('reference.png')\n expect(result.url).toMatch(\n /https:\\/\\/account\\.blob\\.core\\.windows\\.net\\/media\\/[0-9a-f-]+\\/reference\\.png\\?/,\n )\n })\n})\n"],"names":["StorageSharedKeyCredential","describe","expect","it","vi","getGenerateSignedURLHandler","createRequest","allowRestrictedFileTypes","mimeType","filename","json","Promise","resolve","collectionSlug","payload","collections","media","config","slug","access","upload","db","findOne","user","id","getStorageClient","fn","handler","containerName","rejects","toThrow","not","toHaveBeenCalled","credential","Buffer","alloc","toString","getBlobClient","key","url","response","result","toBe","toMatch"],"mappings":"AAGA,SAASA,0BAA0B,QAAQ,sBAAqB;AAChE,SAASC,QAAQ,EAAEC,MAAM,EAAEC,EAAE,EAAEC,EAAE,QAAQ,SAAQ;AAEjD,SAASC,2BAA2B,QAAQ,yBAAwB;AAEpE,MAAMC,gBAAgB,CACpBC,0BACAC,UACAC,WAAW,eAAe,GAEzB,CAAA;QACCC,MAAM,IACJC,QAAQC,OAAO,CAAC;gBACdC,gBAAgB;gBAChBJ;gBACAD;YACF;QACFM,SAAS;YACPC,aAAa;gBACXC,OAAO;oBACLC,QAAQ;wBACNC,MAAM;wBACNC,QAAQ,CAAC;wBACTC,QAAQ;4BAAEb;wBAAyB;oBACrC;gBACF;YACF;YACAc,IAAI;gBAAEC,SAAS,IAAMX,QAAQC,OAAO,CAAC;YAAM;QAC7C;QACAW,MAAM;YAAEC,IAAI;QAAU;IACxB,CAAA;AAEFvB,SAAS,4BAA4B;IACnCE,GAAG,oDAAoD;QACrD,MAAMsB,mBAAmBrB,GAAGsB,EAAE;QAC9B,MAAMC,UAAUtB,4BAA4B;YAC1CU,aAAa;gBAAEC,OAAO;YAAK;YAC3BY,eAAe;YACfH;QACF;QAEA,MAAMvB,OAAOyB,QAAQrB,cAAc,OAAO,eAAeuB,OAAO,CAACC,OAAO,CACtE;QAEF5B,OAAOuB,kBAAkBM,GAAG,CAACC,gBAAgB;IAC/C;IAEA7B,GAAG,gEAAgE;QACjE,MAAM8B,aAAa,IAAIjC,2BACrB,WACAkC,OAAOC,KAAK,CAAC,IAAI,KAAKC,QAAQ,CAAC;QAEjC,MAAMX,mBAAmB,IACtB,CAAA;gBACCQ;gBACAI,eAAe,CAACC,MAAiB,CAAA;wBAC/BC,KAAK,CAAC,4CAA4C,EAAED,KAAK;oBAC3D,CAAA;YACF,CAAA;QACF,MAAMX,UAAUtB,4BAA4B;YAC1CU,aAAa;gBAAEC,OAAO;YAAK;YAC3BY,eAAe;YACfH;QACF;QAEA,MAAMe,WAAW,MAAMb,QAAQrB,cAAc,MAAM,aAAa;QAChE,MAAMmC,SAAU,MAAMD,SAAS9B,IAAI;QAEnCR,OAAOuC,OAAOhC,QAAQ,EAAEiC,IAAI,CAAC;QAC7BxC,OAAOuC,OAAOF,GAAG,EAAEI,OAAO,CACxB;IAEJ;AACF"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=index.containerAccess.spec.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.containerAccess.spec.d.ts","sourceRoot":"","sources":["../src/index.containerAccess.spec.ts"],"names":[],"mappings":""}
@@ -1,78 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
- // Inspect the access level the adapter passes when it provisions a container.
3
- const { createIfNotExists } = vi.hoisted(()=>({
4
- createIfNotExists: vi.fn()
5
- }));
6
- // Capture the closure index.ts hands to the adapter (normally run via onInit).
7
- const captured = vi.hoisted(()=>({
8
- createContainerIfNotExists: undefined
9
- }));
10
- vi.mock('./utils/getStorageClient.js', ()=>({
11
- getStorageClient: ()=>({
12
- createIfNotExists
13
- })
14
- }));
15
- vi.mock('./adapter.js', ()=>({
16
- createAzureAdapter: (args)=>{
17
- captured.createContainerIfNotExists = args.createContainerIfNotExists;
18
- return ()=>({
19
- name: 'azure'
20
- });
21
- }
22
- }));
23
- vi.mock('@payloadcms/plugin-cloud-storage', ()=>({
24
- cloudStoragePlugin: ()=>(config)=>config
25
- }));
26
- const { azureStorage } = await import('./index.js');
27
- const runContainerCreate = (containerAccess)=>{
28
- createIfNotExists.mockClear();
29
- azureStorage({
30
- allowContainerCreate: true,
31
- baseURL: 'https://account.blob.core.windows.net',
32
- collections: {
33
- media: true
34
- },
35
- connectionString: 'UseDevelopmentStorage=true',
36
- containerAccess,
37
- containerName: 'media'
38
- })({
39
- collections: []
40
- });
41
- captured.createContainerIfNotExists?.();
42
- return createIfNotExists.mock.calls[0]?.[0];
43
- };
44
- describe('azureStorage container creation', ()=>{
45
- it('should not create the container with anonymous public blob access by default', ()=>{
46
- const options = runContainerCreate();
47
- expect(createIfNotExists).toHaveBeenCalledTimes(1);
48
- // No access level == private. Any public value would bypass Payload's
49
- // access-controlled route, so the default must stay private.
50
- expect(options?.access).toBeUndefined();
51
- });
52
- it('should create a private container when containerAccess is private', ()=>{
53
- const options = runContainerCreate('private');
54
- expect(options?.access).toBeUndefined();
55
- });
56
- it('should apply public access only when explicitly opted in', ()=>{
57
- expect(runContainerCreate('blob')?.access).toBe('blob');
58
- expect(runContainerCreate('container')?.access).toBe('container');
59
- });
60
- it('should surface a creation failure as an awaitable rejection, not a swallowed promise', async ()=>{
61
- createIfNotExists.mockClear();
62
- createIfNotExists.mockRejectedValueOnce(new Error('AuthorizationFailure: public access is not permitted on this storage account'));
63
- azureStorage({
64
- allowContainerCreate: true,
65
- baseURL: 'https://account.blob.core.windows.net',
66
- collections: {
67
- media: true
68
- },
69
- connectionString: 'UseDevelopmentStorage=true',
70
- containerName: 'media'
71
- })({
72
- collections: []
73
- });
74
- await expect(captured.createContainerIfNotExists?.()).rejects.toThrow('AuthorizationFailure');
75
- });
76
- });
77
-
78
- //# sourceMappingURL=index.containerAccess.spec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/index.containerAccess.spec.ts"],"sourcesContent":["import type { Config } from 'payload'\n\nimport { describe, expect, it, vi } from 'vitest'\n\n// Inspect the access level the adapter passes when it provisions a container.\nconst { createIfNotExists } = vi.hoisted(() => ({ createIfNotExists: vi.fn() }))\n\n// Capture the closure index.ts hands to the adapter (normally run via onInit).\nconst captured = vi.hoisted(\n () => ({ createContainerIfNotExists: undefined }) as { createContainerIfNotExists?: () => void },\n)\n\nvi.mock('./utils/getStorageClient.js', () => ({\n getStorageClient: () => ({ createIfNotExists }),\n}))\n\nvi.mock('./adapter.js', () => ({\n createAzureAdapter: (args: { createContainerIfNotExists: () => void }) => {\n captured.createContainerIfNotExists = args.createContainerIfNotExists\n return () => ({ name: 'azure' })\n },\n}))\n\nvi.mock('@payloadcms/plugin-cloud-storage', () => ({\n cloudStoragePlugin: () => (config: Config) => config,\n}))\n\nconst { azureStorage } = await import('./index.js')\n\nconst runContainerCreate = (\n containerAccess?: 'blob' | 'container' | 'private',\n): { access?: string } | undefined => {\n createIfNotExists.mockClear()\n\n azureStorage({\n allowContainerCreate: true,\n baseURL: 'https://account.blob.core.windows.net',\n collections: { media: true },\n connectionString: 'UseDevelopmentStorage=true',\n containerAccess,\n containerName: 'media',\n })({ collections: [] } as unknown as Config)\n\n captured.createContainerIfNotExists?.()\n\n return createIfNotExists.mock.calls[0]?.[0] as { access?: string } | undefined\n}\n\ndescribe('azureStorage container creation', () => {\n it('should not create the container with anonymous public blob access by default', () => {\n const options = runContainerCreate()\n\n expect(createIfNotExists).toHaveBeenCalledTimes(1)\n // No access level == private. Any public value would bypass Payload's\n // access-controlled route, so the default must stay private.\n expect(options?.access).toBeUndefined()\n })\n\n it('should create a private container when containerAccess is private', () => {\n const options = runContainerCreate('private')\n\n expect(options?.access).toBeUndefined()\n })\n\n it('should apply public access only when explicitly opted in', () => {\n expect(runContainerCreate('blob')?.access).toBe('blob')\n expect(runContainerCreate('container')?.access).toBe('container')\n })\n\n it('should surface a creation failure as an awaitable rejection, not a swallowed promise', async () => {\n createIfNotExists.mockClear()\n createIfNotExists.mockRejectedValueOnce(\n new Error('AuthorizationFailure: public access is not permitted on this storage account'),\n )\n\n azureStorage({\n allowContainerCreate: true,\n baseURL: 'https://account.blob.core.windows.net',\n collections: { media: true },\n connectionString: 'UseDevelopmentStorage=true',\n containerName: 'media',\n })({ collections: [] } as unknown as Config)\n\n await expect(captured.createContainerIfNotExists?.()).rejects.toThrow('AuthorizationFailure')\n })\n})\n"],"names":["describe","expect","it","vi","createIfNotExists","hoisted","fn","captured","createContainerIfNotExists","undefined","mock","getStorageClient","createAzureAdapter","args","name","cloudStoragePlugin","config","azureStorage","runContainerCreate","containerAccess","mockClear","allowContainerCreate","baseURL","collections","media","connectionString","containerName","calls","options","toHaveBeenCalledTimes","access","toBeUndefined","toBe","mockRejectedValueOnce","Error","rejects","toThrow"],"mappings":"AAEA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,EAAEC,EAAE,QAAQ,SAAQ;AAEjD,8EAA8E;AAC9E,MAAM,EAAEC,iBAAiB,EAAE,GAAGD,GAAGE,OAAO,CAAC,IAAO,CAAA;QAAED,mBAAmBD,GAAGG,EAAE;IAAG,CAAA;AAE7E,+EAA+E;AAC/E,MAAMC,WAAWJ,GAAGE,OAAO,CACzB,IAAO,CAAA;QAAEG,4BAA4BC;IAAU,CAAA;AAGjDN,GAAGO,IAAI,CAAC,+BAA+B,IAAO,CAAA;QAC5CC,kBAAkB,IAAO,CAAA;gBAAEP;YAAkB,CAAA;IAC/C,CAAA;AAEAD,GAAGO,IAAI,CAAC,gBAAgB,IAAO,CAAA;QAC7BE,oBAAoB,CAACC;YACnBN,SAASC,0BAA0B,GAAGK,KAAKL,0BAA0B;YACrE,OAAO,IAAO,CAAA;oBAAEM,MAAM;gBAAQ,CAAA;QAChC;IACF,CAAA;AAEAX,GAAGO,IAAI,CAAC,oCAAoC,IAAO,CAAA;QACjDK,oBAAoB,IAAM,CAACC,SAAmBA;IAChD,CAAA;AAEA,MAAM,EAAEC,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC;AAEtC,MAAMC,qBAAqB,CACzBC;IAEAf,kBAAkBgB,SAAS;IAE3BH,aAAa;QACXI,sBAAsB;QACtBC,SAAS;QACTC,aAAa;YAAEC,OAAO;QAAK;QAC3BC,kBAAkB;QAClBN;QACAO,eAAe;IACjB,GAAG;QAAEH,aAAa,EAAE;IAAC;IAErBhB,SAASC,0BAA0B;IAEnC,OAAOJ,kBAAkBM,IAAI,CAACiB,KAAK,CAAC,EAAE,EAAE,CAAC,EAAE;AAC7C;AAEA3B,SAAS,mCAAmC;IAC1CE,GAAG,gFAAgF;QACjF,MAAM0B,UAAUV;QAEhBjB,OAAOG,mBAAmByB,qBAAqB,CAAC;QAChD,sEAAsE;QACtE,6DAA6D;QAC7D5B,OAAO2B,SAASE,QAAQC,aAAa;IACvC;IAEA7B,GAAG,qEAAqE;QACtE,MAAM0B,UAAUV,mBAAmB;QAEnCjB,OAAO2B,SAASE,QAAQC,aAAa;IACvC;IAEA7B,GAAG,4DAA4D;QAC7DD,OAAOiB,mBAAmB,SAASY,QAAQE,IAAI,CAAC;QAChD/B,OAAOiB,mBAAmB,cAAcY,QAAQE,IAAI,CAAC;IACvD;IAEA9B,GAAG,wFAAwF;QACzFE,kBAAkBgB,SAAS;QAC3BhB,kBAAkB6B,qBAAqB,CACrC,IAAIC,MAAM;QAGZjB,aAAa;YACXI,sBAAsB;YACtBC,SAAS;YACTC,aAAa;gBAAEC,OAAO;YAAK;YAC3BC,kBAAkB;YAClBC,eAAe;QACjB,GAAG;YAAEH,aAAa,EAAE;QAAC;QAErB,MAAMtB,OAAOM,SAASC,0BAA0B,MAAM2B,OAAO,CAACC,OAAO,CAAC;IACxE;AACF"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=index.spec.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.spec.d.ts","sourceRoot":"","sources":["../src/index.spec.ts"],"names":[],"mappings":""}
@@ -1,44 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { azureStorage } from './index.js';
3
- const createConfig = ()=>({
4
- collections: [
5
- {
6
- slug: 'media',
7
- fields: [],
8
- upload: true
9
- },
10
- {
11
- slug: 'direct-media',
12
- fields: [],
13
- upload: {
14
- allowRestrictedFileTypes: true
15
- }
16
- }
17
- ],
18
- secret: 'test-secret'
19
- });
20
- describe('azureStorage client uploads', ()=>{
21
- it('should register direct uploads only for explicit opt-out collections', ()=>{
22
- const config = azureStorage({
23
- allowContainerCreate: false,
24
- baseURL: 'https://account.blob.core.windows.net',
25
- clientUploads: true,
26
- collections: {
27
- 'direct-media': true,
28
- media: true
29
- },
30
- connectionString: 'UseDevelopmentStorage=true',
31
- containerName: 'media'
32
- })(createConfig());
33
- const providers = config.admin?.components?.providers ?? [];
34
- const providerCollections = providers.map((provider)=>{
35
- const clientProps = provider.clientProps;
36
- return typeof clientProps?.collectionSlug === 'string' ? clientProps.collectionSlug : undefined;
37
- }).filter(Boolean);
38
- expect(providerCollections).toEqual([
39
- 'direct-media'
40
- ]);
41
- });
42
- });
43
-
44
- //# sourceMappingURL=index.spec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/index.spec.ts"],"sourcesContent":["import type { Config } from 'payload'\n\nimport { describe, expect, it } from 'vitest'\n\nimport { azureStorage } from './index.js'\n\nconst createConfig = (): Config =>\n ({\n collections: [\n {\n slug: 'media',\n fields: [],\n upload: true,\n },\n {\n slug: 'direct-media',\n fields: [],\n upload: { allowRestrictedFileTypes: true },\n },\n ],\n secret: 'test-secret',\n }) as unknown as Config\n\ndescribe('azureStorage client uploads', () => {\n it('should register direct uploads only for explicit opt-out collections', () => {\n const config = azureStorage({\n allowContainerCreate: false,\n baseURL: 'https://account.blob.core.windows.net',\n clientUploads: true,\n collections: {\n 'direct-media': true,\n media: true,\n },\n connectionString: 'UseDevelopmentStorage=true',\n containerName: 'media',\n })(createConfig()) as Config\n\n const providers = config.admin?.components?.providers ?? []\n const providerCollections = providers\n .map((provider) => {\n const clientProps = (provider as { clientProps?: { collectionSlug?: unknown } }).clientProps\n return typeof clientProps?.collectionSlug === 'string'\n ? clientProps.collectionSlug\n : undefined\n })\n .filter(Boolean)\n\n expect(providerCollections).toEqual(['direct-media'])\n })\n})\n"],"names":["describe","expect","it","azureStorage","createConfig","collections","slug","fields","upload","allowRestrictedFileTypes","secret","config","allowContainerCreate","baseURL","clientUploads","media","connectionString","containerName","providers","admin","components","providerCollections","map","provider","clientProps","collectionSlug","undefined","filter","Boolean","toEqual"],"mappings":"AAEA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,QAAQ,SAAQ;AAE7C,SAASC,YAAY,QAAQ,aAAY;AAEzC,MAAMC,eAAe,IAClB,CAAA;QACCC,aAAa;YACX;gBACEC,MAAM;gBACNC,QAAQ,EAAE;gBACVC,QAAQ;YACV;YACA;gBACEF,MAAM;gBACNC,QAAQ,EAAE;gBACVC,QAAQ;oBAAEC,0BAA0B;gBAAK;YAC3C;SACD;QACDC,QAAQ;IACV,CAAA;AAEFV,SAAS,+BAA+B;IACtCE,GAAG,wEAAwE;QACzE,MAAMS,SAASR,aAAa;YAC1BS,sBAAsB;YACtBC,SAAS;YACTC,eAAe;YACfT,aAAa;gBACX,gBAAgB;gBAChBU,OAAO;YACT;YACAC,kBAAkB;YAClBC,eAAe;QACjB,GAAGb;QAEH,MAAMc,YAAYP,OAAOQ,KAAK,EAAEC,YAAYF,aAAa,EAAE;QAC3D,MAAMG,sBAAsBH,UACzBI,GAAG,CAAC,CAACC;YACJ,MAAMC,cAAc,AAACD,SAA4DC,WAAW;YAC5F,OAAO,OAAOA,aAAaC,mBAAmB,WAC1CD,YAAYC,cAAc,GAC1BC;QACN,GACCC,MAAM,CAACC;QAEV3B,OAAOoB,qBAAqBQ,OAAO,CAAC;YAAC;SAAe;IACtD;AACF"}