@payloadcms/storage-azure 3.91.0-internal.6e3dc85 → 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 (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
package/LICENSE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2018-2025 Payload CMS, Inc. <info@payloadcms.com>
3
+ Copyright (c) 2018-2026 Payload CMS, LLC <info@payloadcms.com>
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining
6
6
  a copy of this software and associated documentation files (the
package/README.md CHANGED
@@ -14,24 +14,13 @@ pnpm add @payloadcms/storage-azure
14
14
 
15
15
  - Configure the `collections` object to specify which collections should use the Azure Blob Storage adapter. The slug _must_ match one of your existing collection slugs.
16
16
  - When enabled, this package will automatically set `disableLocalStorage` to `true` for each collection.
17
- - When deploying to Vercel, server uploads are limited with 4.5MB. Set `clientUploads` to `true` to do uploads directly on the client.
17
+ - When deploying to Vercel, server uploads are limited to 4.5MB. Set `clientUploads` to `true` to use upload instructions and send files directly to Azure.
18
18
 
19
- ### Large client uploads (files over ~5GB)
19
+ ### Client uploads and CORS
20
20
 
21
- By default, client uploads (`clientUploads: true`) send each file in a single request, which Azure caps at ~5GB. To upload larger files, set `chunkLargeFiles: true`:
21
+ Client uploads (`clientUploads: true`) use the Azure Blob SDK, which splits large files into blocks and therefore avoids the ~5GB limit of a single upload request. Because the SDK sends `x-ms-*` headers, the browser issues a CORS preflight, so your storage account's CORS rules must allow the `OPTIONS` and `PUT` methods **and** the required headers.
22
22
 
23
- ```ts
24
- azureStorage({
25
- // ...
26
- clientUploads: {
27
- chunkLargeFiles: true,
28
- },
29
- })
30
- ```
31
-
32
- This uploads through the Azure Blob SDK, which splits the file into blocks (`Put Block` + `Put Block List`) and raises the limit to Azure's block-blob maximum (~190TiB).
33
-
34
- Because the SDK sends additional `x-ms-*` headers, the browser issues a CORS preflight, so your storage account's CORS rules must allow the `OPTIONS` and `PUT` methods **and** those headers. Configure a CORS rule on the Blob service (Storage account → **Resource sharing (CORS)**):
23
+ Configure a CORS rule on the Blob service (Storage account → **Resource sharing (CORS)**):
35
24
 
36
25
  | Field | Value |
37
26
  | --------------- | -------------------------------------------------------- |
@@ -41,6 +30,8 @@ Because the SDK sends additional `x-ms-*` headers, the browser issues a CORS pre
41
30
  | Exposed headers | `*` |
42
31
  | Max age | `3600` |
43
32
 
33
+ > If you previously configured CORS for the older single-`PUT` upload flow, broaden `Allowed headers` to include the `x-ms-*` headers when upgrading.
34
+
44
35
  ```ts
45
36
  import { azureStorage } from '@payloadcms/storage-azure'
46
37
  import { Media } from './collections/Media'
@@ -76,6 +67,6 @@ export default buildConfig({
76
67
  | `baseURL` | Base URL for the Azure Blob storage account | |
77
68
  | `connectionString` | Azure Blob storage connection string | |
78
69
  | `containerName` | Azure Blob storage container name | |
79
- | `clientUploads` | Do uploads directly on the client to bypass limits on Vercel. | |
70
+ | `clientUploads` | Upload directly to Azure instead of through Payload. | |
80
71
 
81
72
  > **Breaking change (private by default):** When `allowContainerCreate: true`, the plugin now creates containers with **private** access. Previously it created them with public `blob` access. If you relied on files being served directly from the anonymous Azure blob URL (typically alongside `disablePayloadAccessControl: true`), set `containerAccess: 'blob'` to restore the old behaviour. This only affects **newly created** containers — existing containers keep their current access level, so you must flip their ACL manually if needed.
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAC1D,OAAO,KAAK,EACV,OAAO,EACP,mBAAmB,EAEpB,MAAM,wCAAwC,CAAA;AAQ/C,UAAU,sBAAsB;IAC9B,oBAAoB,EAAE,OAAO,CAAA;IAC7B,OAAO,EAAE,MAAM,CAAA;IACf,aAAa,CAAC,EAAE,mBAAmB,CAAA;IACnC,aAAa,EAAE,MAAM,CAAA;IACrB,0BAA0B,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IACtD,gBAAgB,EAAE,MAAM,eAAe,CAAA;IACvC,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,wBAAgB,kBAAkB,CAAC,EACjC,oBAAoB,EACpB,OAAO,EACP,aAAa,EACb,aAAa,EACb,0BAA0B,EAC1B,gBAAgB,EAChB,oBAA4B,GAC7B,EAAE,sBAAsB,GAAG,OAAO,CAiDlC"}
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAC1D,OAAO,KAAK,EACV,OAAO,EACP,mBAAmB,EAGpB,MAAM,wCAAwC,CAAA;AAU/C,UAAU,sBAAsB;IAC9B,oBAAoB,EAAE,OAAO,CAAA;IAC7B,OAAO,EAAE,MAAM,CAAA;IACf,aAAa,CAAC,EAAE,mBAAmB,CAAA;IACnC,aAAa,EAAE,MAAM,CAAA;IACrB,0BAA0B,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IACtD,gBAAgB,EAAE,MAAM,eAAe,CAAA;IACvC,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,wBAAgB,kBAAkB,CAAC,EACjC,oBAAoB,EACpB,OAAO,EACP,aAAa,EACb,aAAa,EACb,0BAA0B,EAC1B,gBAAgB,EAChB,oBAA4B,GAC7B,EAAE,sBAAsB,GAAG,OAAO,CAmElC"}
package/dist/adapter.js CHANGED
@@ -1,13 +1,25 @@
1
+ import { copyAzureFile } from './copyFile.js';
1
2
  import { deleteFile } from './deleteFile.js';
3
+ import { generateUploadInstructions } from './generateUploadInstructions.js';
2
4
  import { generateURL } from './generateURL.js';
3
5
  import { getFile } from './getFile.js';
4
- import { isAzureClientUploadAllowed } from './isClientUploadAllowed.js';
6
+ import { isClientUploadAllowed } from './isClientUploadAllowed.js';
5
7
  import { uploadFile } from './uploadFile.js';
6
8
  export function createAzureAdapter({ allowContainerCreate, baseURL, clientUploads, containerName, createContainerIfNotExists, getStorageClient, useCompositePrefixes = false }) {
9
+ const deleteStoredFile = ({ storageFilePath })=>deleteFile({
10
+ client: getStorageClient(),
11
+ storageFilePath
12
+ });
7
13
  return ({ collection, prefix = '' })=>({
8
14
  name: 'azure',
9
- clientUploads: isAzureClientUploadAllowed(collection) ? clientUploads : false,
10
- requiresClientUploadReceipt: true,
15
+ supportsTempFiles: true,
16
+ copyFile: ({ from, req, to })=>copyAzureFile({
17
+ client: getStorageClient(),
18
+ from,
19
+ req,
20
+ to
21
+ }),
22
+ deleteFile: deleteStoredFile,
11
23
  generateURL: ({ filename, prefix: urlPrefix = '' })=>generateURL({
12
24
  baseURL,
13
25
  collectionPrefix: prefix,
@@ -16,10 +28,22 @@ export function createAzureAdapter({ allowContainerCreate, baseURL, clientUpload
16
28
  prefix: urlPrefix,
17
29
  useCompositePrefixes
18
30
  }),
19
- handleDelete: ({ storageFilePath })=>deleteFile({
20
- client: getStorageClient(),
21
- storageFilePath
31
+ uploadInstructions: {
32
+ adminHandler: {
33
+ path: '@payloadcms/storage-azure/client#AzureClientUploadHandler'
34
+ },
35
+ enabled: Boolean(clientUploads) && isClientUploadAllowed(collection),
36
+ generate: generateUploadInstructions({
37
+ access: typeof clientUploads === 'object' ? clientUploads.access : undefined,
38
+ collectionPrefix: prefix,
39
+ containerName,
40
+ getStorageClient,
41
+ useCompositePrefixes
22
42
  }),
43
+ requiresUploadReceipt: true,
44
+ useInAdmin: true
45
+ },
46
+ handleDelete: deleteStoredFile,
23
47
  handleUpload: async ({ data, file, storageFilePath })=>{
24
48
  await uploadFile({
25
49
  buffer: file.buffer,
@@ -30,15 +54,16 @@ export function createAzureAdapter({ allowContainerCreate, baseURL, clientUpload
30
54
  });
31
55
  return data;
32
56
  },
33
- staticHandler: (req, { doc, headers, params: { clientUploadContext, filename } })=>getFile({
57
+ staticHandler: (req, { doc, headers, params: { filename, operation, uploadReference } })=>getFile({
34
58
  client: getStorageClient(),
35
- clientUploadContext,
36
59
  collection,
37
60
  collectionPrefix: prefix,
38
61
  doc,
39
62
  filename,
40
63
  incomingHeaders: headers,
64
+ operation,
41
65
  req,
66
+ uploadReference,
42
67
  useCompositePrefixes
43
68
  }),
44
69
  ...allowContainerCreate && {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/adapter.ts"],"sourcesContent":["import type { ContainerClient } from '@azure/storage-blob'\nimport type {\n Adapter,\n ClientUploadsConfig,\n GeneratedAdapter,\n} from '@payloadcms/plugin-cloud-storage/types'\n\nimport { deleteFile } from './deleteFile.js'\nimport { generateURL } from './generateURL.js'\nimport { getFile } from './getFile.js'\nimport { isAzureClientUploadAllowed } from './isClientUploadAllowed.js'\nimport { uploadFile } from './uploadFile.js'\n\ninterface CreateAzureAdapterArgs {\n allowContainerCreate: boolean\n baseURL: string\n clientUploads?: ClientUploadsConfig\n containerName: string\n createContainerIfNotExists: () => Promise<void> | void\n getStorageClient: () => ContainerClient\n useCompositePrefixes?: boolean\n}\n\nexport function createAzureAdapter({\n allowContainerCreate,\n baseURL,\n clientUploads,\n containerName,\n createContainerIfNotExists,\n getStorageClient,\n useCompositePrefixes = false,\n}: CreateAzureAdapterArgs): Adapter {\n return ({ collection, prefix = '' }): GeneratedAdapter => ({\n name: 'azure',\n clientUploads: isAzureClientUploadAllowed(collection) ? clientUploads : false,\n requiresClientUploadReceipt: true,\n\n generateURL: ({ filename, prefix: urlPrefix = '' }) =>\n generateURL({\n baseURL,\n collectionPrefix: prefix,\n containerName,\n filename,\n prefix: urlPrefix,\n useCompositePrefixes,\n }),\n\n handleDelete: ({ storageFilePath }) =>\n deleteFile({\n client: getStorageClient(),\n storageFilePath,\n }),\n\n handleUpload: async ({ data, file, storageFilePath }) => {\n await uploadFile({\n buffer: file.buffer,\n client: getStorageClient(),\n mimeType: file.mimeType,\n storageFilePath,\n tempFilePath: file.tempFilePath,\n })\n\n return data\n },\n\n staticHandler: (req, { doc, headers, params: { clientUploadContext, filename } }) =>\n getFile({\n client: getStorageClient(),\n clientUploadContext,\n collection,\n collectionPrefix: prefix,\n doc,\n filename,\n incomingHeaders: headers,\n req,\n useCompositePrefixes,\n }),\n\n ...(allowContainerCreate && { onInit: createContainerIfNotExists }),\n })\n}\n"],"names":["deleteFile","generateURL","getFile","isAzureClientUploadAllowed","uploadFile","createAzureAdapter","allowContainerCreate","baseURL","clientUploads","containerName","createContainerIfNotExists","getStorageClient","useCompositePrefixes","collection","prefix","name","requiresClientUploadReceipt","filename","urlPrefix","collectionPrefix","handleDelete","storageFilePath","client","handleUpload","data","file","buffer","mimeType","tempFilePath","staticHandler","req","doc","headers","params","clientUploadContext","incomingHeaders","onInit"],"mappings":"AAOA,SAASA,UAAU,QAAQ,kBAAiB;AAC5C,SAASC,WAAW,QAAQ,mBAAkB;AAC9C,SAASC,OAAO,QAAQ,eAAc;AACtC,SAASC,0BAA0B,QAAQ,6BAA4B;AACvE,SAASC,UAAU,QAAQ,kBAAiB;AAY5C,OAAO,SAASC,mBAAmB,EACjCC,oBAAoB,EACpBC,OAAO,EACPC,aAAa,EACbC,aAAa,EACbC,0BAA0B,EAC1BC,gBAAgB,EAChBC,uBAAuB,KAAK,EACL;IACvB,OAAO,CAAC,EAAEC,UAAU,EAAEC,SAAS,EAAE,EAAE,GAAwB,CAAA;YACzDC,MAAM;YACNP,eAAeL,2BAA2BU,cAAcL,gBAAgB;YACxEQ,6BAA6B;YAE7Bf,aAAa,CAAC,EAAEgB,QAAQ,EAAEH,QAAQI,YAAY,EAAE,EAAE,GAChDjB,YAAY;oBACVM;oBACAY,kBAAkBL;oBAClBL;oBACAQ;oBACAH,QAAQI;oBACRN;gBACF;YAEFQ,cAAc,CAAC,EAAEC,eAAe,EAAE,GAChCrB,WAAW;oBACTsB,QAAQX;oBACRU;gBACF;YAEFE,cAAc,OAAO,EAAEC,IAAI,EAAEC,IAAI,EAAEJ,eAAe,EAAE;gBAClD,MAAMjB,WAAW;oBACfsB,QAAQD,KAAKC,MAAM;oBACnBJ,QAAQX;oBACRgB,UAAUF,KAAKE,QAAQ;oBACvBN;oBACAO,cAAcH,KAAKG,YAAY;gBACjC;gBAEA,OAAOJ;YACT;YAEAK,eAAe,CAACC,KAAK,EAAEC,GAAG,EAAEC,OAAO,EAAEC,QAAQ,EAAEC,mBAAmB,EAAEjB,QAAQ,EAAE,EAAE,GAC9Ef,QAAQ;oBACNoB,QAAQX;oBACRuB;oBACArB;oBACAM,kBAAkBL;oBAClBiB;oBACAd;oBACAkB,iBAAiBH;oBACjBF;oBACAlB;gBACF;YAEF,GAAIN,wBAAwB;gBAAE8B,QAAQ1B;YAA2B,CAAC;QACpE,CAAA;AACF"}
1
+ {"version":3,"sources":["../src/adapter.ts"],"sourcesContent":["import type { ContainerClient } from '@azure/storage-blob'\nimport type {\n Adapter,\n ClientUploadsConfig,\n DeleteFile,\n GeneratedAdapter,\n} from '@payloadcms/plugin-cloud-storage/types'\n\nimport { copyAzureFile } from './copyFile.js'\nimport { deleteFile } from './deleteFile.js'\nimport { generateUploadInstructions } from './generateUploadInstructions.js'\nimport { generateURL } from './generateURL.js'\nimport { getFile } from './getFile.js'\nimport { isClientUploadAllowed } from './isClientUploadAllowed.js'\nimport { uploadFile } from './uploadFile.js'\n\ninterface CreateAzureAdapterArgs {\n allowContainerCreate: boolean\n baseURL: string\n clientUploads?: ClientUploadsConfig\n containerName: string\n createContainerIfNotExists: () => Promise<void> | void\n getStorageClient: () => ContainerClient\n useCompositePrefixes?: boolean\n}\n\nexport function createAzureAdapter({\n allowContainerCreate,\n baseURL,\n clientUploads,\n containerName,\n createContainerIfNotExists,\n getStorageClient,\n useCompositePrefixes = false,\n}: CreateAzureAdapterArgs): Adapter {\n const deleteStoredFile: DeleteFile = ({ storageFilePath }) =>\n deleteFile({ client: getStorageClient(), storageFilePath })\n\n return ({ collection, prefix = '' }): GeneratedAdapter => ({\n name: 'azure',\n supportsTempFiles: true,\n\n copyFile: ({ from, req, to }) => copyAzureFile({ client: getStorageClient(), from, req, to }),\n deleteFile: deleteStoredFile,\n\n generateURL: ({ filename, prefix: urlPrefix = '' }) =>\n generateURL({\n baseURL,\n collectionPrefix: prefix,\n containerName,\n filename,\n prefix: urlPrefix,\n useCompositePrefixes,\n }),\n\n uploadInstructions: {\n adminHandler: {\n path: '@payloadcms/storage-azure/client#AzureClientUploadHandler',\n },\n enabled: Boolean(clientUploads) && isClientUploadAllowed(collection),\n generate: generateUploadInstructions({\n access: typeof clientUploads === 'object' ? clientUploads.access : undefined,\n collectionPrefix: prefix,\n containerName,\n getStorageClient,\n useCompositePrefixes,\n }),\n requiresUploadReceipt: true,\n useInAdmin: true,\n },\n\n handleDelete: deleteStoredFile,\n\n handleUpload: async ({ data, file, storageFilePath }) => {\n await uploadFile({\n buffer: file.buffer,\n client: getStorageClient(),\n mimeType: file.mimeType,\n storageFilePath,\n tempFilePath: file.tempFilePath,\n })\n\n return data\n },\n\n staticHandler: (req, { doc, headers, params: { filename, operation, uploadReference } }) =>\n getFile({\n client: getStorageClient(),\n collection,\n collectionPrefix: prefix,\n doc,\n filename,\n incomingHeaders: headers,\n operation,\n req,\n uploadReference,\n useCompositePrefixes,\n }),\n\n ...(allowContainerCreate && { onInit: createContainerIfNotExists }),\n })\n}\n"],"names":["copyAzureFile","deleteFile","generateUploadInstructions","generateURL","getFile","isClientUploadAllowed","uploadFile","createAzureAdapter","allowContainerCreate","baseURL","clientUploads","containerName","createContainerIfNotExists","getStorageClient","useCompositePrefixes","deleteStoredFile","storageFilePath","client","collection","prefix","name","supportsTempFiles","copyFile","from","req","to","filename","urlPrefix","collectionPrefix","uploadInstructions","adminHandler","path","enabled","Boolean","generate","access","undefined","requiresUploadReceipt","useInAdmin","handleDelete","handleUpload","data","file","buffer","mimeType","tempFilePath","staticHandler","doc","headers","params","operation","uploadReference","incomingHeaders","onInit"],"mappings":"AAQA,SAASA,aAAa,QAAQ,gBAAe;AAC7C,SAASC,UAAU,QAAQ,kBAAiB;AAC5C,SAASC,0BAA0B,QAAQ,kCAAiC;AAC5E,SAASC,WAAW,QAAQ,mBAAkB;AAC9C,SAASC,OAAO,QAAQ,eAAc;AACtC,SAASC,qBAAqB,QAAQ,6BAA4B;AAClE,SAASC,UAAU,QAAQ,kBAAiB;AAY5C,OAAO,SAASC,mBAAmB,EACjCC,oBAAoB,EACpBC,OAAO,EACPC,aAAa,EACbC,aAAa,EACbC,0BAA0B,EAC1BC,gBAAgB,EAChBC,uBAAuB,KAAK,EACL;IACvB,MAAMC,mBAA+B,CAAC,EAAEC,eAAe,EAAE,GACvDf,WAAW;YAAEgB,QAAQJ;YAAoBG;QAAgB;IAE3D,OAAO,CAAC,EAAEE,UAAU,EAAEC,SAAS,EAAE,EAAE,GAAwB,CAAA;YACzDC,MAAM;YACNC,mBAAmB;YAEnBC,UAAU,CAAC,EAAEC,IAAI,EAAEC,GAAG,EAAEC,EAAE,EAAE,GAAKzB,cAAc;oBAAEiB,QAAQJ;oBAAoBU;oBAAMC;oBAAKC;gBAAG;YAC3FxB,YAAYc;YAEZZ,aAAa,CAAC,EAAEuB,QAAQ,EAAEP,QAAQQ,YAAY,EAAE,EAAE,GAChDxB,YAAY;oBACVM;oBACAmB,kBAAkBT;oBAClBR;oBACAe;oBACAP,QAAQQ;oBACRb;gBACF;YAEFe,oBAAoB;gBAClBC,cAAc;oBACZC,MAAM;gBACR;gBACAC,SAASC,QAAQvB,kBAAkBL,sBAAsBa;gBACzDgB,UAAUhC,2BAA2B;oBACnCiC,QAAQ,OAAOzB,kBAAkB,WAAWA,cAAcyB,MAAM,GAAGC;oBACnER,kBAAkBT;oBAClBR;oBACAE;oBACAC;gBACF;gBACAuB,uBAAuB;gBACvBC,YAAY;YACd;YAEAC,cAAcxB;YAEdyB,cAAc,OAAO,EAAEC,IAAI,EAAEC,IAAI,EAAE1B,eAAe,EAAE;gBAClD,MAAMV,WAAW;oBACfqC,QAAQD,KAAKC,MAAM;oBACnB1B,QAAQJ;oBACR+B,UAAUF,KAAKE,QAAQ;oBACvB5B;oBACA6B,cAAcH,KAAKG,YAAY;gBACjC;gBAEA,OAAOJ;YACT;YAEAK,eAAe,CAACtB,KAAK,EAAEuB,GAAG,EAAEC,OAAO,EAAEC,QAAQ,EAAEvB,QAAQ,EAAEwB,SAAS,EAAEC,eAAe,EAAE,EAAE,GACrF/C,QAAQ;oBACNa,QAAQJ;oBACRK;oBACAU,kBAAkBT;oBAClB4B;oBACArB;oBACA0B,iBAAiBJ;oBACjBE;oBACA1B;oBACA2B;oBACArC;gBACF;YAEF,GAAIN,wBAAwB;gBAAE6C,QAAQzC;YAA2B,CAAC;QACpE,CAAA;AACF"}
@@ -1,12 +1,8 @@
1
- export type AzureClientUploadHandlerExtra = {
2
- chunkLargeFiles: boolean;
3
- };
4
- export declare const AzureClientUploadHandler: ({ children, collectionSlug, enabled, extra, prefix, serverHandlerPath, }: {
1
+ export declare const AzureClientUploadHandler: ({ children, collectionSlug, endpointPath, prefix, props, }: {
5
2
  children: import("react").ReactNode;
6
- collectionSlug: import("packages/payload/src/index.js").UploadCollectionSlug;
7
- enabled?: boolean;
8
- extra: AzureClientUploadHandlerExtra;
3
+ collectionSlug: import("payload").UploadCollectionSlug;
4
+ endpointPath?: `/${string}`;
9
5
  prefix?: string;
10
- serverHandlerPath: `/${string}`;
6
+ props: Record<string, unknown>;
11
7
  }) => import("react").JSX.Element;
12
8
  //# sourceMappingURL=AzureClientUploadHandler.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AzureClientUploadHandler.d.ts","sourceRoot":"","sources":["../../src/client/AzureClientUploadHandler.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,6BAA6B,GAAG;IAC1C,eAAe,EAAE,OAAO,CAAA;CACzB,CAAA;AAED,eAAO,MAAM,wBAAwB;;;;;;;aAuBgI,OAAO,aAD1K,CAAA"}
1
+ {"version":3,"file":"AzureClientUploadHandler.d.ts","sourceRoot":"","sources":["../../src/client/AzureClientUploadHandler.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,wBAAwB;;;;;;iCAGnC,CAAA"}
@@ -2,18 +2,8 @@
2
2
  import { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client';
3
3
  import { handleAzureUpload } from './handleAzureUpload.js';
4
4
  export const AzureClientUploadHandler = createClientUploadHandler({
5
- handler: async ({ apiRoute, collectionSlug, docPrefix, extra: { chunkLargeFiles }, file, serverHandlerPath, serverURL, updateFilename })=>{
6
- return handleAzureUpload({
7
- apiRoute,
8
- chunkLargeFiles,
9
- collectionSlug,
10
- docPrefix,
11
- file,
12
- serverHandlerPath,
13
- serverURL,
14
- updateFilename
15
- });
16
- }
5
+ name: 'uploadToAzure',
6
+ handler: handleAzureUpload
17
7
  });
18
8
 
19
9
  //# sourceMappingURL=AzureClientUploadHandler.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/client/AzureClientUploadHandler.ts"],"sourcesContent":["'use client'\nimport { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client'\n\nimport { handleAzureUpload } from './handleAzureUpload.js'\n\nexport type AzureClientUploadHandlerExtra = {\n chunkLargeFiles: boolean\n}\n\nexport const AzureClientUploadHandler = createClientUploadHandler<AzureClientUploadHandlerExtra>({\n handler: async ({\n apiRoute,\n collectionSlug,\n docPrefix,\n extra: { chunkLargeFiles },\n file,\n serverHandlerPath,\n serverURL,\n updateFilename,\n }) => {\n return handleAzureUpload({\n apiRoute,\n chunkLargeFiles,\n collectionSlug,\n docPrefix,\n file,\n serverHandlerPath,\n serverURL,\n updateFilename,\n })\n },\n})\n"],"names":["createClientUploadHandler","handleAzureUpload","AzureClientUploadHandler","handler","apiRoute","collectionSlug","docPrefix","extra","chunkLargeFiles","file","serverHandlerPath","serverURL","updateFilename"],"mappings":"AAAA;AACA,SAASA,yBAAyB,QAAQ,0CAAyC;AAEnF,SAASC,iBAAiB,QAAQ,yBAAwB;AAM1D,OAAO,MAAMC,2BAA2BF,0BAAyD;IAC/FG,SAAS,OAAO,EACdC,QAAQ,EACRC,cAAc,EACdC,SAAS,EACTC,OAAO,EAAEC,eAAe,EAAE,EAC1BC,IAAI,EACJC,iBAAiB,EACjBC,SAAS,EACTC,cAAc,EACf;QACC,OAAOX,kBAAkB;YACvBG;YACAI;YACAH;YACAC;YACAG;YACAC;YACAC;YACAC;QACF;IACF;AACF,GAAE"}
1
+ {"version":3,"sources":["../../src/client/AzureClientUploadHandler.ts"],"sourcesContent":["'use client'\nimport { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client'\n\nimport { handleAzureUpload } from './handleAzureUpload.js'\n\nexport const AzureClientUploadHandler = createClientUploadHandler({\n name: 'uploadToAzure',\n handler: handleAzureUpload,\n})\n"],"names":["createClientUploadHandler","handleAzureUpload","AzureClientUploadHandler","name","handler"],"mappings":"AAAA;AACA,SAASA,yBAAyB,QAAQ,0CAAyC;AAEnF,SAASC,iBAAiB,QAAQ,yBAAwB;AAE1D,OAAO,MAAMC,2BAA2BF,0BAA0B;IAChEG,MAAM;IACNC,SAASH;AACX,GAAE"}
@@ -1,19 +1,7 @@
1
- import type { ClientUploadContext } from '@payloadcms/plugin-cloud-storage/types';
2
1
  type HandleAzureUploadArgs = {
3
- apiRoute: string;
4
- /**
5
- * When true, upload through the Azure Blob SDK, which splits the file into
6
- * blocks (Put Block + Put Block List) and lifts the ~5 GB single-request
7
- * limit. When false, use the legacy single Put Blob request.
8
- */
9
- chunkLargeFiles: boolean;
10
- collectionSlug: string;
11
- docPrefix?: string;
2
+ data?: unknown;
12
3
  file: File;
13
- serverHandlerPath: `/${string}`;
14
- serverURL: string;
15
- updateFilename: (value: string) => void;
16
4
  };
17
- export declare const handleAzureUpload: ({ apiRoute, chunkLargeFiles, collectionSlug, docPrefix, file, serverHandlerPath, serverURL, updateFilename, }: HandleAzureUploadArgs) => Promise<ClientUploadContext>;
5
+ export declare const handleAzureUpload: ({ data, file }: HandleAzureUploadArgs) => Promise<void>;
18
6
  export {};
19
7
  //# sourceMappingURL=handleAzureUpload.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"handleAzureUpload.d.ts","sourceRoot":"","sources":["../../src/client/handleAzureUpload.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAA;AASjF,KAAK,qBAAqB,GAAG;IAC3B,QAAQ,EAAE,MAAM,CAAA;IAChB;;;;OAIG;IACH,eAAe,EAAE,OAAO,CAAA;IACxB,cAAc,EAAE,MAAM,CAAA;IACtB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,IAAI,CAAA;IACV,iBAAiB,EAAE,IAAI,MAAM,EAAE,CAAA;IAC/B,SAAS,EAAE,MAAM,CAAA;IACjB,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACxC,CAAA;AAED,eAAO,MAAM,iBAAiB,kHAS3B,qBAAqB,KAAG,OAAO,CAAC,mBAAmB,CAiFrD,CAAA"}
1
+ {"version":3,"file":"handleAzureUpload.d.ts","sourceRoot":"","sources":["../../src/client/handleAzureUpload.ts"],"names":[],"mappings":"AASA,KAAK,qBAAqB,GAAG;IAC3B,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,IAAI,EAAE,IAAI,CAAA;CACX,CAAA;AAED,eAAO,MAAM,iBAAiB,mBAA0B,qBAAqB,KAAG,OAAO,CAAC,IAAI,CAc3F,CAAA"}
@@ -1,67 +1,23 @@
1
1
  'use client';
2
- import { formatAdminURL } from 'payload/shared';
3
2
  // Balances upload parallelism against browser memory: `blockSize * concurrency`
4
- // bytes are buffered in flight (64 MiB * 4 = 256 MiB). Only used on the SDK path.
3
+ // bytes are buffered in flight. The SDK uses a single Put Blob for small files
4
+ // and switches to Put Block + Put Block List above its single-shot threshold,
5
+ // lifting the ~5 GB single-request ceiling to Azure's block-blob maximum.
5
6
  const BLOCK_SIZE = 64 * 1024 * 1024;
6
7
  const CONCURRENCY = 4;
7
- export const handleAzureUpload = async ({ apiRoute, chunkLargeFiles, collectionSlug, docPrefix, file, serverHandlerPath, serverURL, updateFilename })=>{
8
- const endpointRoute = formatAdminURL({
9
- apiRoute,
10
- path: serverHandlerPath,
11
- serverURL
8
+ export const handleAzureUpload = async ({ data, file })=>{
9
+ const { url } = data;
10
+ // Loaded on demand so @azure/storage-blob is only pulled into the client bundle
11
+ // when a client upload actually runs, rather than for every Azure adapter user.
12
+ const { BlockBlobClient } = await import('@azure/storage-blob');
13
+ const blockBlobClient = new BlockBlobClient(url);
14
+ await blockBlobClient.uploadData(file, {
15
+ blobHTTPHeaders: {
16
+ blobContentType: file.type
17
+ },
18
+ blockSize: BLOCK_SIZE,
19
+ concurrency: CONCURRENCY
12
20
  });
13
- const response = await fetch(endpointRoute, {
14
- body: JSON.stringify({
15
- collectionSlug,
16
- docPrefix,
17
- filename: file.name,
18
- mimeType: file.type
19
- }),
20
- credentials: 'include',
21
- method: 'POST'
22
- });
23
- if (!response.ok) {
24
- throw new Error('Failed to retrieve signed URL for Azure client upload');
25
- }
26
- const { clientUploadContext, filename: sanitizedFilename, url } = await response.json();
27
- if (typeof clientUploadContext !== 'object' || clientUploadContext === null || !('prefix' in clientUploadContext) || typeof clientUploadContext.prefix !== 'string' || !('signedReceipt' in clientUploadContext) || typeof clientUploadContext.signedReceipt !== 'string') {
28
- throw new Error('Invalid Azure client upload reference');
29
- }
30
- if (sanitizedFilename && sanitizedFilename !== file.name) {
31
- updateFilename(sanitizedFilename);
32
- }
33
- if (chunkLargeFiles) {
34
- // Loaded on demand so @azure/storage-blob is only pulled into the client bundle
35
- // when a chunked client upload actually runs, rather than for every Azure user.
36
- const { BlockBlobClient } = await import('@azure/storage-blob');
37
- const blockBlobClient = new BlockBlobClient(url);
38
- await blockBlobClient.uploadData(file, {
39
- blobHTTPHeaders: {
40
- blobContentType: file.type
41
- },
42
- blockSize: BLOCK_SIZE,
43
- concurrency: CONCURRENCY
44
- });
45
- } else {
46
- const upload = await fetch(url, {
47
- body: file,
48
- headers: {
49
- 'Content-Length': file.size.toString(),
50
- 'Content-Type': file.type,
51
- // Required for azure
52
- 'x-ms-blob-type': 'BlockBlob'
53
- },
54
- method: 'PUT'
55
- });
56
- if (!upload.ok) {
57
- throw new Error('Failed to upload file to Azure Blob Storage');
58
- }
59
- }
60
- return {
61
- prefix: clientUploadContext.prefix,
62
- // Validated as a string above; re-typed to its branded receipt form for the return contract.
63
- signedReceipt: clientUploadContext.signedReceipt
64
- };
65
21
  };
66
22
 
67
23
  //# sourceMappingURL=handleAzureUpload.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/client/handleAzureUpload.ts"],"sourcesContent":["'use client'\nimport type { ClientUploadContext } from '@payloadcms/plugin-cloud-storage/types'\n\nimport { formatAdminURL } from 'payload/shared'\n\n// Balances upload parallelism against browser memory: `blockSize * concurrency`\n// bytes are buffered in flight (64 MiB * 4 = 256 MiB). Only used on the SDK path.\nconst BLOCK_SIZE = 64 * 1024 * 1024\nconst CONCURRENCY = 4\n\ntype HandleAzureUploadArgs = {\n apiRoute: string\n /**\n * When true, upload through the Azure Blob SDK, which splits the file into\n * blocks (Put Block + Put Block List) and lifts the ~5 GB single-request\n * limit. When false, use the legacy single Put Blob request.\n */\n chunkLargeFiles: boolean\n collectionSlug: string\n docPrefix?: string\n file: File\n serverHandlerPath: `/${string}`\n serverURL: string\n updateFilename: (value: string) => void\n}\n\nexport const handleAzureUpload = async ({\n apiRoute,\n chunkLargeFiles,\n collectionSlug,\n docPrefix,\n file,\n serverHandlerPath,\n serverURL,\n updateFilename,\n}: HandleAzureUploadArgs): Promise<ClientUploadContext> => {\n const endpointRoute = formatAdminURL({\n apiRoute,\n path: serverHandlerPath,\n serverURL,\n })\n\n const response = await fetch(endpointRoute, {\n body: JSON.stringify({\n collectionSlug,\n docPrefix,\n filename: file.name,\n mimeType: file.type,\n }),\n credentials: 'include',\n method: 'POST',\n })\n\n if (!response.ok) {\n throw new Error('Failed to retrieve signed URL for Azure client upload')\n }\n\n const {\n clientUploadContext,\n filename: sanitizedFilename,\n url,\n } = (await response.json()) as {\n clientUploadContext?: unknown\n filename?: string\n url: string\n }\n\n if (\n typeof clientUploadContext !== 'object' ||\n clientUploadContext === null ||\n !('prefix' in clientUploadContext) ||\n typeof clientUploadContext.prefix !== 'string' ||\n !('signedReceipt' in clientUploadContext) ||\n typeof clientUploadContext.signedReceipt !== 'string'\n ) {\n throw new Error('Invalid Azure client upload reference')\n }\n\n if (sanitizedFilename && sanitizedFilename !== file.name) {\n updateFilename(sanitizedFilename)\n }\n\n if (chunkLargeFiles) {\n // Loaded on demand so @azure/storage-blob is only pulled into the client bundle\n // when a chunked client upload actually runs, rather than for every Azure user.\n const { BlockBlobClient } = await import('@azure/storage-blob')\n\n const blockBlobClient = new BlockBlobClient(url)\n\n await blockBlobClient.uploadData(file, {\n blobHTTPHeaders: { blobContentType: file.type },\n blockSize: BLOCK_SIZE,\n concurrency: CONCURRENCY,\n })\n } else {\n const upload = await fetch(url, {\n body: file,\n headers: {\n 'Content-Length': file.size.toString(),\n 'Content-Type': file.type,\n // Required for azure\n 'x-ms-blob-type': 'BlockBlob',\n },\n method: 'PUT',\n })\n\n if (!upload.ok) {\n throw new Error('Failed to upload file to Azure Blob Storage')\n }\n }\n\n return {\n prefix: clientUploadContext.prefix,\n // Validated as a string above; re-typed to its branded receipt form for the return contract.\n signedReceipt: clientUploadContext.signedReceipt as ClientUploadContext['signedReceipt'],\n }\n}\n"],"names":["formatAdminURL","BLOCK_SIZE","CONCURRENCY","handleAzureUpload","apiRoute","chunkLargeFiles","collectionSlug","docPrefix","file","serverHandlerPath","serverURL","updateFilename","endpointRoute","path","response","fetch","body","JSON","stringify","filename","name","mimeType","type","credentials","method","ok","Error","clientUploadContext","sanitizedFilename","url","json","prefix","signedReceipt","BlockBlobClient","blockBlobClient","uploadData","blobHTTPHeaders","blobContentType","blockSize","concurrency","upload","headers","size","toString"],"mappings":"AAAA;AAGA,SAASA,cAAc,QAAQ,iBAAgB;AAE/C,gFAAgF;AAChF,kFAAkF;AAClF,MAAMC,aAAa,KAAK,OAAO;AAC/B,MAAMC,cAAc;AAkBpB,OAAO,MAAMC,oBAAoB,OAAO,EACtCC,QAAQ,EACRC,eAAe,EACfC,cAAc,EACdC,SAAS,EACTC,IAAI,EACJC,iBAAiB,EACjBC,SAAS,EACTC,cAAc,EACQ;IACtB,MAAMC,gBAAgBZ,eAAe;QACnCI;QACAS,MAAMJ;QACNC;IACF;IAEA,MAAMI,WAAW,MAAMC,MAAMH,eAAe;QAC1CI,MAAMC,KAAKC,SAAS,CAAC;YACnBZ;YACAC;YACAY,UAAUX,KAAKY,IAAI;YACnBC,UAAUb,KAAKc,IAAI;QACrB;QACAC,aAAa;QACbC,QAAQ;IACV;IAEA,IAAI,CAACV,SAASW,EAAE,EAAE;QAChB,MAAM,IAAIC,MAAM;IAClB;IAEA,MAAM,EACJC,mBAAmB,EACnBR,UAAUS,iBAAiB,EAC3BC,GAAG,EACJ,GAAI,MAAMf,SAASgB,IAAI;IAMxB,IACE,OAAOH,wBAAwB,YAC/BA,wBAAwB,QACxB,CAAE,CAAA,YAAYA,mBAAkB,KAChC,OAAOA,oBAAoBI,MAAM,KAAK,YACtC,CAAE,CAAA,mBAAmBJ,mBAAkB,KACvC,OAAOA,oBAAoBK,aAAa,KAAK,UAC7C;QACA,MAAM,IAAIN,MAAM;IAClB;IAEA,IAAIE,qBAAqBA,sBAAsBpB,KAAKY,IAAI,EAAE;QACxDT,eAAeiB;IACjB;IAEA,IAAIvB,iBAAiB;QACnB,gFAAgF;QAChF,gFAAgF;QAChF,MAAM,EAAE4B,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC;QAEzC,MAAMC,kBAAkB,IAAID,gBAAgBJ;QAE5C,MAAMK,gBAAgBC,UAAU,CAAC3B,MAAM;YACrC4B,iBAAiB;gBAAEC,iBAAiB7B,KAAKc,IAAI;YAAC;YAC9CgB,WAAWrC;YACXsC,aAAarC;QACf;IACF,OAAO;QACL,MAAMsC,SAAS,MAAMzB,MAAMc,KAAK;YAC9Bb,MAAMR;YACNiC,SAAS;gBACP,kBAAkBjC,KAAKkC,IAAI,CAACC,QAAQ;gBACpC,gBAAgBnC,KAAKc,IAAI;gBACzB,qBAAqB;gBACrB,kBAAkB;YACpB;YACAE,QAAQ;QACV;QAEA,IAAI,CAACgB,OAAOf,EAAE,EAAE;YACd,MAAM,IAAIC,MAAM;QAClB;IACF;IAEA,OAAO;QACLK,QAAQJ,oBAAoBI,MAAM;QAClC,6FAA6F;QAC7FC,eAAeL,oBAAoBK,aAAa;IAClD;AACF,EAAC"}
1
+ {"version":3,"sources":["../../src/client/handleAzureUpload.ts"],"sourcesContent":["'use client'\n\n// Balances upload parallelism against browser memory: `blockSize * concurrency`\n// bytes are buffered in flight. The SDK uses a single Put Blob for small files\n// and switches to Put Block + Put Block List above its single-shot threshold,\n// lifting the ~5 GB single-request ceiling to Azure's block-blob maximum.\nconst BLOCK_SIZE = 64 * 1024 * 1024\nconst CONCURRENCY = 4\n\ntype HandleAzureUploadArgs = {\n data?: unknown\n file: File\n}\n\nexport const handleAzureUpload = async ({ data, file }: HandleAzureUploadArgs): Promise<void> => {\n const { url } = data as { url: string }\n\n // Loaded on demand so @azure/storage-blob is only pulled into the client bundle\n // when a client upload actually runs, rather than for every Azure adapter user.\n const { BlockBlobClient } = await import('@azure/storage-blob')\n\n const blockBlobClient = new BlockBlobClient(url)\n\n await blockBlobClient.uploadData(file, {\n blobHTTPHeaders: { blobContentType: file.type },\n blockSize: BLOCK_SIZE,\n concurrency: CONCURRENCY,\n })\n}\n"],"names":["BLOCK_SIZE","CONCURRENCY","handleAzureUpload","data","file","url","BlockBlobClient","blockBlobClient","uploadData","blobHTTPHeaders","blobContentType","type","blockSize","concurrency"],"mappings":"AAAA;AAEA,gFAAgF;AAChF,+EAA+E;AAC/E,8EAA8E;AAC9E,0EAA0E;AAC1E,MAAMA,aAAa,KAAK,OAAO;AAC/B,MAAMC,cAAc;AAOpB,OAAO,MAAMC,oBAAoB,OAAO,EAAEC,IAAI,EAAEC,IAAI,EAAyB;IAC3E,MAAM,EAAEC,GAAG,EAAE,GAAGF;IAEhB,gFAAgF;IAChF,gFAAgF;IAChF,MAAM,EAAEG,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC;IAEzC,MAAMC,kBAAkB,IAAID,gBAAgBD;IAE5C,MAAME,gBAAgBC,UAAU,CAACJ,MAAM;QACrCK,iBAAiB;YAAEC,iBAAiBN,KAAKO,IAAI;QAAC;QAC9CC,WAAWZ;QACXa,aAAaZ;IACf;AACF,EAAC"}
@@ -0,0 +1,11 @@
1
+ import type { ContainerClient } from '@azure/storage-blob';
2
+ import type { PayloadRequest } from 'payload';
3
+ type Args = {
4
+ client: ContainerClient;
5
+ from: string;
6
+ req?: PayloadRequest;
7
+ to: string;
8
+ };
9
+ export declare const copyAzureFile: ({ client, from, req, to }: Args) => Promise<void>;
10
+ export {};
11
+ //# sourceMappingURL=copyFile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copyFile.d.ts","sourceRoot":"","sources":["../src/copyFile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAE1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C,KAAK,IAAI,GAAG;IAAE,MAAM,EAAE,eAAe,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,cAAc,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAA;AAEvF,eAAO,MAAM,aAAa,8BAAqC,IAAI,KAAG,OAAO,CAAC,IAAI,CAqDjF,CAAA"}
@@ -0,0 +1,52 @@
1
+ export const copyAzureFile = async ({ client, from, req, to })=>{
2
+ if (from === to) {
3
+ throw new Error('Storage copy requires different source and destination keys');
4
+ }
5
+ const source = client.getBlockBlobClient(from);
6
+ const destination = client.getBlockBlobClient(to);
7
+ const properties = await source.getProperties();
8
+ const { tags } = await source.getTags();
9
+ const download = await source.download();
10
+ if (!download.readableStreamBody) {
11
+ throw new Error(`Azure storage source is not readable: ${from}`);
12
+ }
13
+ const uploaded = await destination.uploadStream(download.readableStreamBody, 4 * 1024 * 1024, 4, {
14
+ blobHTTPHeaders: {
15
+ blobCacheControl: properties.cacheControl,
16
+ blobContentDisposition: properties.contentDisposition,
17
+ blobContentEncoding: properties.contentEncoding,
18
+ blobContentLanguage: properties.contentLanguage,
19
+ blobContentType: properties.contentType
20
+ },
21
+ conditions: {
22
+ ifNoneMatch: '*'
23
+ },
24
+ metadata: properties.metadata,
25
+ tags
26
+ });
27
+ try {
28
+ const copied = await destination.getProperties();
29
+ if (copied.contentLength !== properties.contentLength) {
30
+ throw new Error(`Copied Azure object is not readable at its expected length: ${to}`);
31
+ }
32
+ } catch (err) {
33
+ try {
34
+ if (!uploaded.etag) {
35
+ throw new Error('Azure copy did not return an ETag for safe cleanup');
36
+ }
37
+ await destination.deleteIfExists({
38
+ conditions: {
39
+ ifMatch: uploaded.etag
40
+ }
41
+ });
42
+ } catch (cleanupError) {
43
+ req?.payload.logger.error({
44
+ err: cleanupError,
45
+ msg: `Failed to remove unsuccessful Azure copy at ${client.containerName}/${to}`
46
+ });
47
+ }
48
+ throw err;
49
+ }
50
+ };
51
+
52
+ //# sourceMappingURL=copyFile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/copyFile.ts"],"sourcesContent":["import type { ContainerClient } from '@azure/storage-blob'\nimport type { Readable } from 'node:stream'\nimport type { PayloadRequest } from 'payload'\n\ntype Args = { client: ContainerClient; from: string; req?: PayloadRequest; to: string }\n\nexport const copyAzureFile = async ({ client, from, req, to }: Args): Promise<void> => {\n if (from === to) {\n throw new Error('Storage copy requires different source and destination keys')\n }\n\n const source = client.getBlockBlobClient(from)\n const destination = client.getBlockBlobClient(to)\n const properties = await source.getProperties()\n const { tags } = await source.getTags()\n const download = await source.download()\n\n if (!download.readableStreamBody) {\n throw new Error(`Azure storage source is not readable: ${from}`)\n }\n\n const uploaded = await destination.uploadStream(\n download.readableStreamBody as Readable,\n 4 * 1024 * 1024,\n 4,\n {\n blobHTTPHeaders: {\n blobCacheControl: properties.cacheControl,\n blobContentDisposition: properties.contentDisposition,\n blobContentEncoding: properties.contentEncoding,\n blobContentLanguage: properties.contentLanguage,\n blobContentType: properties.contentType,\n },\n conditions: { ifNoneMatch: '*' },\n metadata: properties.metadata,\n tags,\n },\n )\n\n try {\n const copied = await destination.getProperties()\n\n if (copied.contentLength !== properties.contentLength) {\n throw new Error(`Copied Azure object is not readable at its expected length: ${to}`)\n }\n } catch (err) {\n try {\n if (!uploaded.etag) {\n throw new Error('Azure copy did not return an ETag for safe cleanup')\n }\n await destination.deleteIfExists({ conditions: { ifMatch: uploaded.etag } })\n } catch (cleanupError) {\n req?.payload.logger.error({\n err: cleanupError,\n msg: `Failed to remove unsuccessful Azure copy at ${client.containerName}/${to}`,\n })\n }\n throw err\n }\n}\n"],"names":["copyAzureFile","client","from","req","to","Error","source","getBlockBlobClient","destination","properties","getProperties","tags","getTags","download","readableStreamBody","uploaded","uploadStream","blobHTTPHeaders","blobCacheControl","cacheControl","blobContentDisposition","contentDisposition","blobContentEncoding","contentEncoding","blobContentLanguage","contentLanguage","blobContentType","contentType","conditions","ifNoneMatch","metadata","copied","contentLength","err","etag","deleteIfExists","ifMatch","cleanupError","payload","logger","error","msg","containerName"],"mappings":"AAMA,OAAO,MAAMA,gBAAgB,OAAO,EAAEC,MAAM,EAAEC,IAAI,EAAEC,GAAG,EAAEC,EAAE,EAAQ;IACjE,IAAIF,SAASE,IAAI;QACf,MAAM,IAAIC,MAAM;IAClB;IAEA,MAAMC,SAASL,OAAOM,kBAAkB,CAACL;IACzC,MAAMM,cAAcP,OAAOM,kBAAkB,CAACH;IAC9C,MAAMK,aAAa,MAAMH,OAAOI,aAAa;IAC7C,MAAM,EAAEC,IAAI,EAAE,GAAG,MAAML,OAAOM,OAAO;IACrC,MAAMC,WAAW,MAAMP,OAAOO,QAAQ;IAEtC,IAAI,CAACA,SAASC,kBAAkB,EAAE;QAChC,MAAM,IAAIT,MAAM,CAAC,sCAAsC,EAAEH,MAAM;IACjE;IAEA,MAAMa,WAAW,MAAMP,YAAYQ,YAAY,CAC7CH,SAASC,kBAAkB,EAC3B,IAAI,OAAO,MACX,GACA;QACEG,iBAAiB;YACfC,kBAAkBT,WAAWU,YAAY;YACzCC,wBAAwBX,WAAWY,kBAAkB;YACrDC,qBAAqBb,WAAWc,eAAe;YAC/CC,qBAAqBf,WAAWgB,eAAe;YAC/CC,iBAAiBjB,WAAWkB,WAAW;QACzC;QACAC,YAAY;YAAEC,aAAa;QAAI;QAC/BC,UAAUrB,WAAWqB,QAAQ;QAC7BnB;IACF;IAGF,IAAI;QACF,MAAMoB,SAAS,MAAMvB,YAAYE,aAAa;QAE9C,IAAIqB,OAAOC,aAAa,KAAKvB,WAAWuB,aAAa,EAAE;YACrD,MAAM,IAAI3B,MAAM,CAAC,4DAA4D,EAAED,IAAI;QACrF;IACF,EAAE,OAAO6B,KAAK;QACZ,IAAI;YACF,IAAI,CAAClB,SAASmB,IAAI,EAAE;gBAClB,MAAM,IAAI7B,MAAM;YAClB;YACA,MAAMG,YAAY2B,cAAc,CAAC;gBAAEP,YAAY;oBAAEQ,SAASrB,SAASmB,IAAI;gBAAC;YAAE;QAC5E,EAAE,OAAOG,cAAc;YACrBlC,KAAKmC,QAAQC,OAAOC,MAAM;gBACxBP,KAAKI;gBACLI,KAAK,CAAC,4CAA4C,EAAExC,OAAOyC,aAAa,CAAC,CAAC,EAAEtC,IAAI;YAClF;QACF;QACA,MAAM6B;IACR;AACF,EAAC"}
@@ -0,0 +1,12 @@
1
+ import type { ContainerClient } from '@azure/storage-blob';
2
+ import type { GenerateUploadInstructions, UploadInstructionsAccess } from 'payload';
3
+ interface Args {
4
+ access?: UploadInstructionsAccess;
5
+ collectionPrefix: string;
6
+ containerName: string;
7
+ getStorageClient: () => ContainerClient;
8
+ useCompositePrefixes?: boolean;
9
+ }
10
+ export declare const generateUploadInstructions: ({ access, collectionPrefix, containerName, getStorageClient, useCompositePrefixes, }: Args) => GenerateUploadInstructions;
11
+ export {};
12
+ //# sourceMappingURL=generateUploadInstructions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generateUploadInstructions.d.ts","sourceRoot":"","sources":["../src/generateUploadInstructions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAA8B,MAAM,qBAAqB,CAAA;AACtF,OAAO,KAAK,EAAE,0BAA0B,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAA;AASnF,UAAU,IAAI;IACZ,MAAM,CAAC,EAAE,wBAAwB,CAAA;IACjC,gBAAgB,EAAE,MAAM,CAAA;IACxB,aAAa,EAAE,MAAM,CAAA;IACrB,gBAAgB,EAAE,MAAM,eAAe,CAAA;IACvC,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,eAAO,MAAM,0BAA0B,yFAMpC,IAAI,KAAG,0BA8DT,CAAA"}
@@ -0,0 +1,57 @@
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 { assertClientUploadAllowed } from 'payload/internal';
5
+ import { isClientUploadAllowed } from './isClientUploadAllowed.js';
6
+ export const generateUploadInstructions = ({ access, collectionPrefix, containerName, getStorageClient, useCompositePrefixes = false })=>{
7
+ return async ({ collectionSlug, docPrefix, filename, filesize, mimeType, overrideAccess, req })=>{
8
+ if (!overrideAccess && (access ? !await access({
9
+ collectionSlug,
10
+ req
11
+ }) : !req.user)) {
12
+ throw new Forbidden(req.t);
13
+ }
14
+ const collection = req.payload.collections[collectionSlug]?.config;
15
+ if (!isClientUploadAllowed(collection)) {
16
+ throw new APIError('Azure client uploads require allowRestrictedFileTypes to be enabled.', 400);
17
+ }
18
+ assertClientUploadAllowed({
19
+ collection,
20
+ filename,
21
+ mimeType
22
+ });
23
+ const { sanitizedFilename, storageFilePath, uploadReference } = await resolveSignedURLKey({
24
+ collectionPrefix,
25
+ collectionSlug,
26
+ docPrefix,
27
+ filename,
28
+ req,
29
+ useCompositePrefixes
30
+ });
31
+ const blobClient = getStorageClient().getBlobClient(storageFilePath);
32
+ const sasToken = generateBlobSASQueryParameters({
33
+ blobName: storageFilePath,
34
+ containerName,
35
+ contentType: mimeType,
36
+ expiresOn: new Date(Date.now() + 3 * 60 * 60 * 1000),
37
+ permissions: BlobSASPermissions.parse('c'),
38
+ startsOn: new Date(),
39
+ version: '2026-04-06'
40
+ }, getStorageClient().credential);
41
+ return {
42
+ name: 'uploadToAzure',
43
+ type: 'dispatch',
44
+ data: {
45
+ url: `${blobClient.url}?${sasToken.toString()}`
46
+ },
47
+ file: {
48
+ filename: sanitizedFilename,
49
+ mimeType,
50
+ size: filesize,
51
+ uploadReference
52
+ }
53
+ };
54
+ };
55
+ };
56
+
57
+ //# sourceMappingURL=generateUploadInstructions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/generateUploadInstructions.ts"],"sourcesContent":["import type { ContainerClient, StorageSharedKeyCredential } from '@azure/storage-blob'\nimport type { GenerateUploadInstructions, UploadInstructionsAccess } from 'payload'\n\nimport { BlobSASPermissions, generateBlobSASQueryParameters } from '@azure/storage-blob'\nimport { resolveSignedURLKey } from '@payloadcms/plugin-cloud-storage/utilities'\nimport { APIError, Forbidden } from 'payload'\nimport { assertClientUploadAllowed } from 'payload/internal'\n\nimport { isClientUploadAllowed } from './isClientUploadAllowed.js'\n\ninterface Args {\n access?: UploadInstructionsAccess\n collectionPrefix: string\n containerName: string\n getStorageClient: () => ContainerClient\n useCompositePrefixes?: boolean\n}\n\nexport const generateUploadInstructions = ({\n access,\n collectionPrefix,\n containerName,\n getStorageClient,\n useCompositePrefixes = false,\n}: Args): GenerateUploadInstructions => {\n return async ({\n collectionSlug,\n docPrefix,\n filename,\n filesize,\n mimeType,\n overrideAccess,\n req,\n }) => {\n if (!overrideAccess && (access ? !(await access({ collectionSlug, req })) : !req.user)) {\n throw new Forbidden(req.t)\n }\n\n const collection = req.payload.collections[collectionSlug]?.config\n if (!isClientUploadAllowed(collection)) {\n throw new APIError(\n 'Azure client uploads require allowRestrictedFileTypes to be enabled.',\n 400,\n )\n }\n\n assertClientUploadAllowed({ collection, filename, mimeType })\n\n const { sanitizedFilename, storageFilePath, uploadReference } = await resolveSignedURLKey({\n collectionPrefix,\n collectionSlug,\n docPrefix,\n filename,\n req,\n useCompositePrefixes,\n })\n\n const blobClient = getStorageClient().getBlobClient(storageFilePath)\n\n const sasToken = generateBlobSASQueryParameters(\n {\n blobName: storageFilePath,\n containerName,\n contentType: mimeType,\n expiresOn: new Date(Date.now() + 3 * 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 {\n name: 'uploadToAzure',\n type: 'dispatch',\n data: {\n url: `${blobClient.url}?${sasToken.toString()}`,\n },\n file: {\n filename: sanitizedFilename,\n mimeType,\n size: filesize,\n uploadReference,\n },\n }\n }\n}\n"],"names":["BlobSASPermissions","generateBlobSASQueryParameters","resolveSignedURLKey","APIError","Forbidden","assertClientUploadAllowed","isClientUploadAllowed","generateUploadInstructions","access","collectionPrefix","containerName","getStorageClient","useCompositePrefixes","collectionSlug","docPrefix","filename","filesize","mimeType","overrideAccess","req","user","t","collection","payload","collections","config","sanitizedFilename","storageFilePath","uploadReference","blobClient","getBlobClient","sasToken","blobName","contentType","expiresOn","Date","now","permissions","parse","startsOn","version","credential","name","type","data","url","toString","file","size"],"mappings":"AAGA,SAASA,kBAAkB,EAAEC,8BAA8B,QAAQ,sBAAqB;AACxF,SAASC,mBAAmB,QAAQ,6CAA4C;AAChF,SAASC,QAAQ,EAAEC,SAAS,QAAQ,UAAS;AAC7C,SAASC,yBAAyB,QAAQ,mBAAkB;AAE5D,SAASC,qBAAqB,QAAQ,6BAA4B;AAUlE,OAAO,MAAMC,6BAA6B,CAAC,EACzCC,MAAM,EACNC,gBAAgB,EAChBC,aAAa,EACbC,gBAAgB,EAChBC,uBAAuB,KAAK,EACvB;IACL,OAAO,OAAO,EACZC,cAAc,EACdC,SAAS,EACTC,QAAQ,EACRC,QAAQ,EACRC,QAAQ,EACRC,cAAc,EACdC,GAAG,EACJ;QACC,IAAI,CAACD,kBAAmBV,CAAAA,SAAS,CAAE,MAAMA,OAAO;YAAEK;YAAgBM;QAAI,KAAM,CAACA,IAAIC,IAAI,AAAD,GAAI;YACtF,MAAM,IAAIhB,UAAUe,IAAIE,CAAC;QAC3B;QAEA,MAAMC,aAAaH,IAAII,OAAO,CAACC,WAAW,CAACX,eAAe,EAAEY;QAC5D,IAAI,CAACnB,sBAAsBgB,aAAa;YACtC,MAAM,IAAInB,SACR,wEACA;QAEJ;QAEAE,0BAA0B;YAAEiB;YAAYP;YAAUE;QAAS;QAE3D,MAAM,EAAES,iBAAiB,EAAEC,eAAe,EAAEC,eAAe,EAAE,GAAG,MAAM1B,oBAAoB;YACxFO;YACAI;YACAC;YACAC;YACAI;YACAP;QACF;QAEA,MAAMiB,aAAalB,mBAAmBmB,aAAa,CAACH;QAEpD,MAAMI,WAAW9B,+BACf;YACE+B,UAAUL;YACVjB;YACAuB,aAAahB;YACbiB,WAAW,IAAIC,KAAKA,KAAKC,GAAG,KAAK,IAAI,KAAK,KAAK;YAC/CC,aAAarC,mBAAmBsC,KAAK,CAAC;YACtCC,UAAU,IAAIJ;YACdK,SAAS;QACX,GACA7B,mBAAmB8B,UAAU;QAG/B,OAAO;YACLC,MAAM;YACNC,MAAM;YACNC,MAAM;gBACJC,KAAK,GAAGhB,WAAWgB,GAAG,CAAC,CAAC,EAAEd,SAASe,QAAQ,IAAI;YACjD;YACAC,MAAM;gBACJhC,UAAUW;gBACVT;gBACA+B,MAAMhC;gBACNY;YACF;QACF;IACF;AACF,EAAC"}
package/dist/getFile.d.ts CHANGED
@@ -1,16 +1,17 @@
1
1
  import type { ContainerClient } from '@azure/storage-blob';
2
- import type { CollectionConfig, PayloadRequest, TypeWithID } from 'payload';
2
+ import type { CollectionConfig, FileHandlerOperation, PayloadRequest, TypeWithID } from 'payload';
3
3
  interface GetFileArgs {
4
4
  client: ContainerClient;
5
- clientUploadContext?: unknown;
6
5
  collection: CollectionConfig;
7
6
  collectionPrefix?: string;
8
7
  doc?: TypeWithID;
9
8
  filename: string;
10
9
  incomingHeaders?: Headers;
10
+ operation?: FileHandlerOperation;
11
11
  req: PayloadRequest;
12
+ uploadReference?: unknown;
12
13
  useCompositePrefixes?: boolean;
13
14
  }
14
- export declare function getFile({ client, clientUploadContext, collection, collectionPrefix, doc, filename, incomingHeaders, req, useCompositePrefixes, }: GetFileArgs): Promise<Response>;
15
+ export declare function getFile({ client, collection, collectionPrefix, doc, filename, incomingHeaders, operation, req, uploadReference, useCompositePrefixes, }: GetFileArgs): Promise<Response>;
15
16
  export {};
16
17
  //# sourceMappingURL=getFile.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"getFile.d.ts","sourceRoot":"","sources":["../src/getFile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA8B,eAAe,EAAE,MAAM,qBAAqB,CAAA;AACtF,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAW3E,UAAU,WAAW;IACnB,MAAM,EAAE,eAAe,CAAA;IACvB,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B,UAAU,EAAE,gBAAgB,CAAA;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,GAAG,CAAC,EAAE,UAAU,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,GAAG,EAAE,cAAc,CAAA;IACnB,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAgCD,wBAAsB,OAAO,CAAC,EAC5B,MAAM,EACN,mBAAmB,EACnB,UAAU,EACV,gBAAqB,EACrB,GAAG,EACH,QAAQ,EACR,eAAe,EACf,GAAG,EACH,oBAA4B,GAC7B,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAkHjC"}
1
+ {"version":3,"file":"getFile.d.ts","sourceRoot":"","sources":["../src/getFile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA8B,eAAe,EAAE,MAAM,qBAAqB,CAAA;AACtF,OAAO,KAAK,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAUjG,UAAU,WAAW;IACnB,MAAM,EAAE,eAAe,CAAA;IACvB,UAAU,EAAE,gBAAgB,CAAA;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,GAAG,CAAC,EAAE,UAAU,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,SAAS,CAAC,EAAE,oBAAoB,CAAA;IAChC,GAAG,EAAE,cAAc,CAAA;IACnB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAgCD,wBAAsB,OAAO,CAAC,EAC5B,MAAM,EACN,UAAU,EACV,gBAAqB,EACrB,GAAG,EACH,QAAQ,EACR,eAAe,EACf,SAAkB,EAClB,GAAG,EACH,eAAe,EACf,oBAA4B,GAC7B,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAsHjC"}