@visulima/storage 1.0.0-alpha.1 → 1.0.0-alpha.3

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 (224) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +33 -14
  3. package/dist/adapter/nuxt/module.d.ts +39 -0
  4. package/dist/adapter/nuxt/module.js +93 -0
  5. package/dist/adapter/nuxt/package.json +20 -0
  6. package/dist/handler/base/base-handler-core.d.ts +91 -0
  7. package/dist/handler/base/base-handler-fetch.d.ts +76 -0
  8. package/dist/handler/base/base-handler-node.d.ts +137 -0
  9. package/dist/handler/http/fetch/index.d.ts +4 -0
  10. package/dist/handler/http/fetch/index.js +4 -0
  11. package/dist/handler/http/hono/index.d.ts +67 -0
  12. package/dist/handler/http/hono/index.js +65 -0
  13. package/dist/handler/http/nextjs/index.d.ts +61 -0
  14. package/dist/handler/http/nextjs/index.js +37 -0
  15. package/dist/handler/http/node/index.d.ts +4 -0
  16. package/dist/handler/http/node/index.js +4 -0
  17. package/dist/handler/http/solid-start/index.d.ts +60 -0
  18. package/dist/handler/http/solid-start/index.js +44 -0
  19. package/dist/handler/multipart/multipart-base.d.ts +84 -0
  20. package/dist/handler/multipart/multipart-fetch.d.ts +64 -0
  21. package/dist/handler/multipart/multipart.d.ts +49 -0
  22. package/dist/handler/rest/rest-base.d.ts +106 -0
  23. package/dist/handler/rest/rest-fetch.d.ts +88 -0
  24. package/dist/handler/rest/rest.d.ts +93 -0
  25. package/dist/handler/tus/tus-base.d.ts +152 -0
  26. package/dist/handler/tus/tus-fetch.d.ts +72 -0
  27. package/dist/handler/tus/tus.d.ts +78 -0
  28. package/dist/handler/types.d.ts +53 -0
  29. package/dist/handler/utils/request-parser.d.ts +72 -0
  30. package/dist/handler/utils/response-builder.d.ts +83 -0
  31. package/dist/handler/utils/storage-utils.d.ts +10 -0
  32. package/dist/handler/utils/stream-utils.d.ts +29 -0
  33. package/dist/handler/utils/upload-handlers.d.ts +76 -0
  34. package/dist/index.d.ts +17 -0
  35. package/dist/index.js +13 -0
  36. package/dist/metrics/index.d.ts +2 -0
  37. package/dist/metrics/no-op-metrics.d.ts +15 -0
  38. package/dist/metrics/opentelemetry-metrics.d.ts +55 -0
  39. package/dist/openapi/index.d.ts +5 -0
  40. package/dist/openapi/index.js +5 -0
  41. package/dist/openapi/rest.d.ts +7 -0
  42. package/dist/openapi/shared.d.ts +13 -0
  43. package/dist/openapi/transform.d.ts +3 -0
  44. package/dist/openapi/tus.d.ts +7 -0
  45. package/dist/openapi/xhr.d.ts +7 -0
  46. package/dist/packem_shared/AwsLightFile-tTneXZgG.js +11 -0
  47. package/dist/packem_shared/AwsLightMetaStorage-BWSOtVaN.js +4 -0
  48. package/dist/packem_shared/AwsLightStorage-Blv-GP4v.js +133 -0
  49. package/dist/packem_shared/AzureFile-CesgFzps.js +8 -0
  50. package/dist/packem_shared/AzureSMetaStorage-CFs-OYJT.js +88 -0
  51. package/dist/packem_shared/AzureStorage-B0v048hG.js +357 -0
  52. package/dist/packem_shared/BaseTransformer-C2gLib6v.js +82 -0
  53. package/dist/packem_shared/DiskStorage-BlltBst6.js +12 -0
  54. package/dist/packem_shared/DiskStorageWithChecksum-BJTmMklc.js +238 -0
  55. package/dist/packem_shared/ERRORS-D0apMqnc.js +97 -0
  56. package/dist/packem_shared/File-Bb3P23dr.js +69 -0
  57. package/dist/packem_shared/GCSConfig-vPP22kN6.js +11 -0
  58. package/dist/packem_shared/GCSFile-BIEunhAN.js +8 -0
  59. package/dist/packem_shared/GCSMetaStorage-BMFEdzJ7.js +7 -0
  60. package/dist/packem_shared/GCStorage-DnhiwaS9.js +406 -0
  61. package/dist/packem_shared/LocalMetaStorage-CZHhKkMd.js +7 -0
  62. package/dist/packem_shared/MediaTransformer-D6658DL1.js +1197 -0
  63. package/dist/packem_shared/MetaStorage-pECeFOad.js +46 -0
  64. package/dist/packem_shared/Metadata-DRLXeJ0F.js +89 -0
  65. package/dist/packem_shared/Multipart-CNFK_Okz.js +163 -0
  66. package/dist/packem_shared/Multipart-CtUL7BQw.js +128 -0
  67. package/dist/packem_shared/NetlifyBlobFile-CXzyjqrD.js +14 -0
  68. package/dist/packem_shared/NetlifyBlobMetaStorage-DxZ5aDvD.js +9 -0
  69. package/dist/packem_shared/NetlifyBlobStorage-CLPtjHqd.js +376 -0
  70. package/dist/packem_shared/NoOpMetrics-DhAk5rXc.js +10 -0
  71. package/dist/packem_shared/OpenTelemetryMetrics-BnxhqIaH.js +67 -0
  72. package/dist/packem_shared/Rest-CAOAEkOj.js +267 -0
  73. package/dist/packem_shared/Rest-DNuLwBrK.js +228 -0
  74. package/dist/packem_shared/S3File-DZiyk9Qt.js +11 -0
  75. package/dist/packem_shared/S3MetaStorage-Dz9aDabA.js +76 -0
  76. package/dist/packem_shared/S3Storage-DHmTk-hU.js +316 -0
  77. package/dist/packem_shared/TUS_RESUMABLE-GJzZ9R-f.js +434 -0
  78. package/dist/packem_shared/Tus-B8PmlMgR.js +200 -0
  79. package/dist/packem_shared/Tus-C4F1aYOl.js +195 -0
  80. package/dist/packem_shared/ValidationError-BfF1aE4h.js +26 -0
  81. package/dist/packem_shared/VercelBlobFile-BCg4aTEq.js +18 -0
  82. package/dist/packem_shared/VercelBlobMetaStorage-Bd9F-VFm.js +9 -0
  83. package/dist/packem_shared/VercelBlobStorage-DD9LMOS6.js +261 -0
  84. package/dist/packem_shared/_commonjsHelpers-B85MJLTf.js +5 -0
  85. package/dist/packem_shared/aws-light-meta-storage-DKJBgbR6.js +446 -0
  86. package/dist/packem_shared/backblaze-BlMnIcBC.js +20 -0
  87. package/dist/packem_shared/base-handler-core-BKuf4YLT.js +303 -0
  88. package/dist/packem_shared/base-handler-fetch-Cr0hLiqg.js +291 -0
  89. package/dist/packem_shared/base-handler-node-gk5aN9Cx.js +730 -0
  90. package/dist/packem_shared/cache-B88MXQ_2.js +18 -0
  91. package/dist/packem_shared/cloudflare-Bi1q8wXE.js +21 -0
  92. package/dist/packem_shared/defaultCloudStorageFileNameValidation-DUuacjWv.js +11 -0
  93. package/dist/packem_shared/digitalOcean-CWQRJM3L.js +21 -0
  94. package/dist/packem_shared/disk-storage-Bh2wy3YW.js +810 -0
  95. package/dist/packem_shared/gcs-meta-storage-0tgMNnPO.js +158 -0
  96. package/dist/packem_shared/has-content-CY66ehMK.js +3 -0
  97. package/dist/packem_shared/headers-DoS5nwM-.js +3220 -0
  98. package/dist/packem_shared/is-expired-CTThU1q5.js +8 -0
  99. package/dist/packem_shared/isRetryableError-Dycp7127.js +82 -0
  100. package/dist/packem_shared/isValidMediaType-BeDgiObq.js +35 -0
  101. package/dist/packem_shared/local-meta-storage-By-3SNBI.js +649 -0
  102. package/dist/packem_shared/minio-C2YBZQOw.js +22 -0
  103. package/dist/packem_shared/multipart-base-B3i67SSX.js +102 -0
  104. package/dist/packem_shared/part-match-BMNqHDYD.js +96 -0
  105. package/dist/packem_shared/path-CR6YkPXX-7R1-9CMk.js +161 -0
  106. package/dist/packem_shared/response-builder-BtnRiBUI.js +45 -0
  107. package/dist/packem_shared/rest-base-GRCcnan7.js +457 -0
  108. package/dist/packem_shared/restOpenApiSpec-CwcHHY2C.js +800 -0
  109. package/dist/packem_shared/s3-base-storage-C1DIHTYY.js +510 -0
  110. package/dist/packem_shared/sharedGet-Cpo7QUyu.js +1019 -0
  111. package/dist/packem_shared/storage-C6NSXb8h.js +1175 -0
  112. package/dist/packem_shared/tigris-Hac8TKnX.js +21 -0
  113. package/dist/packem_shared/transformOpenApiSpec-DWM5WtnN.js +975 -0
  114. package/dist/packem_shared/tusOpenApiSpec-6ohiToBy.js +740 -0
  115. package/dist/packem_shared/update-size-CCGm6i1J.js +8 -0
  116. package/dist/packem_shared/validator-BeX_lJet.js +78 -0
  117. package/dist/packem_shared/waitForStorage-Cscw85sx.js +18 -0
  118. package/dist/packem_shared/wasabi-DIyflHSd.js +21 -0
  119. package/dist/packem_shared/xhrOpenApiSpec-DS17brnx.js +264 -0
  120. package/dist/storage/aws/clients/backblaze.d.ts +12 -0
  121. package/dist/storage/aws/clients/cloudflare.d.ts +13 -0
  122. package/dist/storage/aws/clients/digital-ocean.d.ts +12 -0
  123. package/dist/storage/aws/clients/index.d.ts +6 -0
  124. package/dist/storage/aws/clients/index.js +6 -0
  125. package/dist/storage/aws/clients/minio.d.ts +13 -0
  126. package/dist/storage/aws/clients/tigris.d.ts +12 -0
  127. package/dist/storage/aws/clients/types.d.ts +95 -0
  128. package/dist/storage/aws/clients/wasabi.d.ts +12 -0
  129. package/dist/storage/aws/index.d.ts +4 -0
  130. package/dist/storage/aws/index.js +3 -0
  131. package/dist/storage/aws/s3-base-storage.d.ts +248 -0
  132. package/dist/storage/aws/s3-client-adapter.d.ts +110 -0
  133. package/dist/storage/aws/s3-file.d.ts +10 -0
  134. package/dist/storage/aws/s3-meta-storage.d.ts +15 -0
  135. package/dist/storage/aws/s3-storage.d.ts +70 -0
  136. package/dist/storage/aws/types.d.ts +119 -0
  137. package/dist/storage/aws-light/aws-light-api-adapter.d.ts +130 -0
  138. package/dist/storage/aws-light/aws-light-file.d.ts +10 -0
  139. package/dist/storage/aws-light/aws-light-meta-storage.d.ts +19 -0
  140. package/dist/storage/aws-light/aws-light-storage.d.ts +65 -0
  141. package/dist/storage/aws-light/index.d.ts +4 -0
  142. package/dist/storage/aws-light/index.js +3 -0
  143. package/dist/storage/aws-light/types.d.ts +37 -0
  144. package/dist/storage/azure/azure-file.d.ts +6 -0
  145. package/dist/storage/azure/azure-meta-storage.d.ts +15 -0
  146. package/dist/storage/azure/azure-storage.d.ts +69 -0
  147. package/dist/storage/azure/index.d.ts +4 -0
  148. package/dist/storage/azure/index.js +3 -0
  149. package/dist/storage/azure/types.d.ts +62 -0
  150. package/dist/storage/gcs/fetch-error.d.ts +11 -0
  151. package/dist/storage/gcs/gcs-config.d.ts +2 -0
  152. package/dist/storage/gcs/gcs-file.d.ts +6 -0
  153. package/dist/storage/gcs/gcs-meta-storage.d.ts +27 -0
  154. package/dist/storage/gcs/gcs-storage.d.ts +91 -0
  155. package/dist/storage/gcs/index.d.ts +5 -0
  156. package/dist/storage/gcs/index.js +4 -0
  157. package/dist/storage/gcs/types.d.ts +55 -0
  158. package/dist/storage/gcs/utils.d.ts +7 -0
  159. package/dist/storage/local/disk-storage-with-checksum.d.ts +15 -0
  160. package/dist/storage/local/disk-storage.d.ts +135 -0
  161. package/dist/storage/local/index.d.ts +4 -0
  162. package/dist/storage/local/index.js +3 -0
  163. package/dist/storage/local/local-meta-storage.d.ts +32 -0
  164. package/dist/storage/meta-storage.d.ts +30 -0
  165. package/dist/storage/netlify-blob/index.d.ts +4 -0
  166. package/dist/storage/netlify-blob/index.js +3 -0
  167. package/dist/storage/netlify-blob/netlify-blob-file.d.ts +12 -0
  168. package/dist/storage/netlify-blob/netlify-blob-meta-storage.d.ts +7 -0
  169. package/dist/storage/netlify-blob/netlify-blob-storage.d.ts +93 -0
  170. package/dist/storage/netlify-blob/types.d.ts +30 -0
  171. package/dist/storage/storage.d.ts +353 -0
  172. package/dist/storage/types.d.ts +221 -0
  173. package/dist/storage/utils/file/file.d.ts +25 -0
  174. package/dist/storage/utils/file/get-file-status.d.ts +9 -0
  175. package/dist/storage/utils/file/has-content.d.ts +9 -0
  176. package/dist/storage/utils/file/index.d.ts +10 -0
  177. package/dist/storage/utils/file/is-expired.d.ts +8 -0
  178. package/dist/storage/utils/file/metadata.d.ts +18 -0
  179. package/dist/storage/utils/file/part-match.d.ts +11 -0
  180. package/dist/storage/utils/file/types.d.ts +37 -0
  181. package/dist/storage/utils/file/update-metadata.d.ts +10 -0
  182. package/dist/storage/utils/file/update-size.d.ts +10 -0
  183. package/dist/storage/vercel-blob/index.d.ts +4 -0
  184. package/dist/storage/vercel-blob/index.js +3 -0
  185. package/dist/storage/vercel-blob/types.d.ts +32 -0
  186. package/dist/storage/vercel-blob/vercel-blob-file.d.ts +16 -0
  187. package/dist/storage/vercel-blob/vercel-blob-meta-storage.d.ts +7 -0
  188. package/dist/storage/vercel-blob/vercel-blob-storage.d.ts +76 -0
  189. package/dist/transformer/audio-transformer.d.ts +128 -0
  190. package/dist/transformer/audio-transformer.js +278 -0
  191. package/dist/transformer/base-transformer.d.ts +63 -0
  192. package/dist/transformer/image-transformer.d.ts +571 -0
  193. package/dist/transformer/image-transformer.js +1100 -0
  194. package/dist/transformer/index.d.ts +5 -0
  195. package/dist/transformer/index.js +4 -0
  196. package/dist/transformer/media-transformer.d.ts +332 -0
  197. package/dist/transformer/types.d.ts +652 -0
  198. package/dist/transformer/utils.d.ts +33 -0
  199. package/dist/transformer/validation-error.d.ts +23 -0
  200. package/dist/transformer/video-transformer.d.ts +145 -0
  201. package/dist/transformer/video-transformer.js +310 -0
  202. package/dist/utils/cache.d.ts +32 -0
  203. package/dist/utils/chunked-upload.d.ts +65 -0
  204. package/dist/utils/detect-file-type.d.ts +28 -0
  205. package/dist/utils/errors.d.ts +74 -0
  206. package/dist/utils/file-path-url-matcher.d.ts +18 -0
  207. package/dist/utils/headers.d.ts +111 -0
  208. package/dist/utils/http.d.ts +92 -0
  209. package/dist/utils/locker.d.ts +26 -0
  210. package/dist/utils/pipes/stream-checksum.d.ts +47 -0
  211. package/dist/utils/pipes/stream-length.d.ts +22 -0
  212. package/dist/utils/primitives/get-last-one.d.ts +3 -0
  213. package/dist/utils/primitives/is-record.d.ts +2 -0
  214. package/dist/utils/primitives/map-values.d.ts +9 -0
  215. package/dist/utils/primitives/pick.d.ts +2 -0
  216. package/dist/utils/primitives/to-milliseconds.d.ts +12 -0
  217. package/dist/utils/primitives/to-seconds.d.ts +12 -0
  218. package/dist/utils/range-checksum.d.ts +33 -0
  219. package/dist/utils/range-hasher.d.ts +46 -0
  220. package/dist/utils/retry.d.ts +64 -0
  221. package/dist/utils/types.d.ts +108 -0
  222. package/dist/utils/validation-error.d.ts +22 -0
  223. package/dist/utils/validator.d.ts +36 -0
  224. package/package.json +5 -5
@@ -0,0 +1,1100 @@
1
+ import { Readable } from 'node:stream';
2
+ import sharp from 'sharp';
3
+ import BaseTransformer from '../packem_shared/BaseTransformer-C2gLib6v.js';
4
+ import { isValidMediaType, getFormatFromContentType } from '../packem_shared/isValidMediaType-BeDgiObq.js';
5
+
6
+ class ImageTransformer extends BaseTransformer {
7
+ /**
8
+ * Creates a new ImageTransformer instance.
9
+ * @param storage The storage backend for retrieving and storing image files.
10
+ * @param config Configuration options for image transformation including cache settings and size limits.
11
+ */
12
+ constructor(storage, config = {}) {
13
+ const logger = config.logger || storage.logger;
14
+ const transformerConfig = {
15
+ cacheTtl: 3600,
16
+ // 1 hour
17
+ maxCacheSize: 100,
18
+ // Max 100 transformed images in cache
19
+ maxImageSize: 50 * 1024 * 1024,
20
+ // 50MB
21
+ supportedFormats: ["jpeg", "png", "webp", "avif", "tiff", "gif", "svg"],
22
+ ...config
23
+ };
24
+ super(storage, transformerConfig, logger);
25
+ }
26
+ /**
27
+ * Resizes an image to specified dimensions with optional fit mode.
28
+ * @param fileId Unique identifier of the image file to resize.
29
+ * @param options Resize options including width, height, and fit mode.
30
+ * @returns Promise resolving to transformed image result.
31
+ */
32
+ async resize(fileId, options) {
33
+ return this.transform(fileId, [{ options, type: "resize" }]);
34
+ }
35
+ /**
36
+ * Crops an image to the specified region.
37
+ * @param fileId Unique identifier of the image file to crop.
38
+ * @param options Crop options including region coordinates.
39
+ * @returns Promise resolving to transformed image result.
40
+ */
41
+ async crop(fileId, options) {
42
+ return this.transform(fileId, [{ options, type: "crop" }]);
43
+ }
44
+ /**
45
+ * Rotates an image by the specified angle.
46
+ * @param fileId Unique identifier of the image file to rotate.
47
+ * @param options Rotate options including angle.
48
+ * @returns Promise resolving to transformed image result.
49
+ */
50
+ async rotate(fileId, options) {
51
+ return this.transform(fileId, [{ options, type: "rotate" }]);
52
+ }
53
+ /**
54
+ * Converts an image to the specified format.
55
+ * @param fileId Unique identifier of the image file to convert.
56
+ * @param format Target image format (e.g., "jpeg", "png", "webp").
57
+ * @param options Additional transform options.
58
+ * @returns Promise resolving to transformed image result.
59
+ */
60
+ async convertFormat(fileId, format, options = {}) {
61
+ return this.transform(fileId, [{ options: { ...options, format }, type: "format" }]);
62
+ }
63
+ /**
64
+ * Applies sharpening filter to an image.
65
+ * @param fileId Unique identifier of the image file to sharpen.
66
+ * @param options Sharpen options including sigma and flat/jagged threshold.
67
+ * @returns Promise resolving to transformed image result.
68
+ */
69
+ async sharpen(fileId, options = {}) {
70
+ return this.transform(fileId, [{ options, type: "sharpen" }]);
71
+ }
72
+ /**
73
+ * Applies blurring filter to an image.
74
+ * @param fileId Unique identifier of the image file to blur.
75
+ * @param options Blur options including sigma.
76
+ * @returns Promise resolving to transformed image result.
77
+ */
78
+ async blur(fileId, options = {}) {
79
+ return this.transform(fileId, [{ options, type: "blur" }]);
80
+ }
81
+ /**
82
+ * Applies median filter to an image for noise reduction.
83
+ * @param fileId Unique identifier of the image file to process.
84
+ * @param options Median filter options including radius.
85
+ * @returns Promise resolving to transformed image result.
86
+ */
87
+ async median(fileId, options = {}) {
88
+ return this.transform(fileId, [{ options, type: "median" }]);
89
+ }
90
+ /**
91
+ * Applies CLAHE (Contrast Limited Adaptive Histogram Equalization) to an image.
92
+ * @param fileId Unique identifier of the image file to process.
93
+ * @param options CLAHE options including width, height, and maxSlope.
94
+ * @returns Promise resolving to transformed image result.
95
+ */
96
+ async clahe(fileId, options = {}) {
97
+ return this.transform(fileId, [{ options, type: "clahe" }]);
98
+ }
99
+ /**
100
+ * Applies convolution kernel to an image.
101
+ * @param fileId Unique identifier of the image file to process.
102
+ * @param options Convolve options including kernel matrix.
103
+ * @returns Promise resolving to transformed image result.
104
+ */
105
+ async convolve(fileId, options) {
106
+ return this.transform(fileId, [{ options, type: "convolve" }]);
107
+ }
108
+ /**
109
+ * Applies thresholding to an image to create a binary image.
110
+ * @param fileId Unique identifier of the image file to process.
111
+ * @param options Threshold options including threshold value.
112
+ * @returns Promise resolving to transformed image result.
113
+ */
114
+ async threshold(fileId, options = {}) {
115
+ return this.transform(fileId, [{ options, type: "threshold" }]);
116
+ }
117
+ /**
118
+ * Applies boolean operation to an image.
119
+ * @param fileId Unique identifier of the image file to process.
120
+ * @param options Boolean operation options including operator and operand.
121
+ * @returns Promise resolving to transformed image result.
122
+ */
123
+ async boolean(fileId, options) {
124
+ return this.transform(fileId, [{ options, type: "boolean" }]);
125
+ }
126
+ /**
127
+ * Applies linear transformation to an image.
128
+ * @param fileId Unique identifier of the image file to process.
129
+ * @param options Linear transformation options including a and b coefficients.
130
+ * @returns Promise resolving to transformed image result.
131
+ */
132
+ async linear(fileId, options) {
133
+ return this.transform(fileId, [{ options, type: "linear" }]);
134
+ }
135
+ /**
136
+ * Applies recombine transformation to an image using a matrix.
137
+ * @param fileId Unique identifier of the image file to process.
138
+ * @param options Recombine options including matrix coefficients.
139
+ * @returns Promise resolving to transformed image result.
140
+ */
141
+ async recombine(fileId, options) {
142
+ return this.transform(fileId, [{ options, type: "recombine" }]);
143
+ }
144
+ /**
145
+ * Applies modulation (brightness, saturation, hue) to an image.
146
+ * @param fileId Unique identifier of the image file to process.
147
+ * @param options Modulate options including brightness, saturation, and hue.
148
+ * @returns Promise resolving to transformed image result.
149
+ */
150
+ async modulate(fileId, options) {
151
+ return this.transform(fileId, [{ options, type: "modulate" }]);
152
+ }
153
+ /**
154
+ * Applies tinting to an image.
155
+ * @param fileId Unique identifier of the image file to process.
156
+ * @param options Tint options including RGB color values.
157
+ * @returns Promise resolving to transformed image result.
158
+ */
159
+ async tint(fileId, options) {
160
+ return this.transform(fileId, [{ options, type: "tint" }]);
161
+ }
162
+ /**
163
+ * Converts an image to greyscale.
164
+ * @param fileId Unique identifier of the image file to convert.
165
+ * @param options Greyscale conversion options.
166
+ * @returns Promise resolving to transformed image result.
167
+ */
168
+ async greyscale(fileId, options = {}) {
169
+ return this.transform(fileId, [{ options, type: "greyscale" }]);
170
+ }
171
+ /**
172
+ * Converts an image to a different colourspace.
173
+ * @param fileId Unique identifier of the image file to convert.
174
+ * @param options Colourspace conversion options including target colourspace.
175
+ * @returns Promise resolving to transformed image result.
176
+ */
177
+ async colourspace(fileId, options) {
178
+ return this.transform(fileId, [{ options, type: "colourspace" }]);
179
+ }
180
+ /**
181
+ * Extracts a specific channel from an image.
182
+ * @param fileId Unique identifier of the image file to process.
183
+ * @param options Extract channel options including channel index.
184
+ * @returns Promise resolving to transformed image result.
185
+ */
186
+ async extractChannel(fileId, options) {
187
+ return this.transform(fileId, [{ options, type: "extractChannel" }]);
188
+ }
189
+ /**
190
+ * Joins channels to an image.
191
+ * @param fileId Unique identifier of the image file to process.
192
+ * @param options Join channel options including channel sources.
193
+ * @returns Promise resolving to transformed image result.
194
+ */
195
+ async joinChannel(fileId, options) {
196
+ return this.transform(fileId, [{ options, type: "joinChannel" }]);
197
+ }
198
+ /**
199
+ * Applies band boolean operation to an image.
200
+ * @param fileId Unique identifier of the image file to process.
201
+ * @param options Band boolean options including operator.
202
+ * @returns Promise resolving to transformed image result.
203
+ */
204
+ async bandbool(fileId, options) {
205
+ return this.transform(fileId, [{ options, type: "bandbool" }]);
206
+ }
207
+ /**
208
+ * Auto-orients an image based on EXIF orientation data.
209
+ * @param fileId Unique identifier of the image file to orient.
210
+ * @param options Additional transform options.
211
+ * @returns Promise resolving to transformed image result.
212
+ */
213
+ async autoOrient(fileId, options = {}) {
214
+ return this.transform(fileId, [{ options, type: "autoOrient" }]);
215
+ }
216
+ /**
217
+ * Flips an image vertically.
218
+ * @param fileId Unique identifier of the image file to flip.
219
+ * @param options Additional transform options.
220
+ * @returns Promise resolving to transformed image result.
221
+ */
222
+ async flip(fileId, options = {}) {
223
+ return this.transform(fileId, [{ options, type: "flip" }]);
224
+ }
225
+ /**
226
+ * Flops an image horizontally.
227
+ * @param fileId Unique identifier of the image file to flop.
228
+ * @param options Additional transform options.
229
+ * @returns Promise resolving to transformed image result.
230
+ */
231
+ async flop(fileId, options = {}) {
232
+ return this.transform(fileId, [{ options, type: "flop" }]);
233
+ }
234
+ /**
235
+ * Flattens an image alpha channel onto a background color.
236
+ * @param fileId Unique identifier of the image file to flatten.
237
+ * @param options Additional transform options.
238
+ * @returns Promise resolving to transformed image result.
239
+ */
240
+ async flatten(fileId, options = {}) {
241
+ return this.transform(fileId, [{ options, type: "flatten" }]);
242
+ }
243
+ /**
244
+ * Unflattens an image alpha channel.
245
+ * @param fileId Unique identifier of the image file to unflatten.
246
+ * @param options Additional transform options.
247
+ * @returns Promise resolving to transformed image result.
248
+ */
249
+ async unflatten(fileId, options = {}) {
250
+ return this.transform(fileId, [{ options, type: "unflatten" }]);
251
+ }
252
+ /**
253
+ * Applies gamma correction to an image.
254
+ * @param fileId Unique identifier of the image file to process.
255
+ * @param options Additional transform options.
256
+ * @returns Promise resolving to transformed image result.
257
+ */
258
+ async gamma(fileId, options = {}) {
259
+ return this.transform(fileId, [{ options, type: "gamma" }]);
260
+ }
261
+ /**
262
+ * Negates (inverts) an image.
263
+ * @param fileId Unique identifier of the image file to negate.
264
+ * @param options Additional transform options.
265
+ * @returns Promise resolving to transformed image result.
266
+ */
267
+ async negate(fileId, options = {}) {
268
+ return this.transform(fileId, [{ options, type: "negate" }]);
269
+ }
270
+ /**
271
+ * Normalises an image by enhancing contrast.
272
+ * @param fileId Unique identifier of the image file to normalise.
273
+ * @param options Additional transform options.
274
+ * @returns Promise resolving to transformed image result.
275
+ */
276
+ async normalise(fileId, options = {}) {
277
+ return this.transform(fileId, [{ options, type: "normalise" }]);
278
+ }
279
+ /**
280
+ * Applies affine transformation to an image.
281
+ * @param fileId Unique identifier of the image file to transform.
282
+ * @param options Affine transformation options including matrix.
283
+ * @returns Promise resolving to transformed image result.
284
+ */
285
+ async affine(fileId, options) {
286
+ return this.transform(fileId, [{ options, type: "affine" }]);
287
+ }
288
+ /**
289
+ * Applies dilation (morphological operation) to an image.
290
+ * @param fileId Unique identifier of the image file to process.
291
+ * @param options Dilate options including iterations.
292
+ * @returns Promise resolving to transformed image result.
293
+ */
294
+ async dilate(fileId, options = {}) {
295
+ return this.transform(fileId, [{ options, type: "dilate" }]);
296
+ }
297
+ /**
298
+ * Applies erosion (morphological operation) to an image.
299
+ * @param fileId Unique identifier of the image file to process.
300
+ * @param options Erode options including iterations.
301
+ * @returns Promise resolving to transformed image result.
302
+ */
303
+ async erode(fileId, options = {}) {
304
+ return this.transform(fileId, [{ options, type: "erode" }]);
305
+ }
306
+ /**
307
+ * Sets pipeline colourspace for an image.
308
+ * @param fileId Unique identifier of the image file to process.
309
+ * @param options Pipeline colourspace options including colourspace.
310
+ * @returns Promise resolving to transformed image result.
311
+ */
312
+ async pipelineColourspace(fileId, options) {
313
+ return this.transform(fileId, [{ options, type: "pipelineColourspace" }]);
314
+ }
315
+ /**
316
+ * Converts colourspace of an image.
317
+ * @param fileId Unique identifier of the image file to convert.
318
+ * @param options ToColourspace options including target colourspace.
319
+ * @returns Promise resolving to transformed image result.
320
+ */
321
+ async toColourspace(fileId, options) {
322
+ return this.transform(fileId, [{ options, type: "toColourspace" }]);
323
+ }
324
+ /**
325
+ * Removes alpha channel from an image.
326
+ * @param fileId Unique identifier of the image file to process.
327
+ * @param options RemoveAlpha transformation options.
328
+ * @returns Promise resolving to transformed image result.
329
+ */
330
+ async removeAlpha(fileId, options = {}) {
331
+ return this.transform(fileId, [{ options, type: "removeAlpha" }]);
332
+ }
333
+ /**
334
+ * Ensures alpha channel exists in an image.
335
+ * @param fileId Unique identifier of the image file to process.
336
+ * @param options EnsureAlpha transformation options.
337
+ * @returns Promise resolving to transformed image result.
338
+ */
339
+ async ensureAlpha(fileId, options = {}) {
340
+ return this.transform(fileId, [{ options, type: "ensureAlpha" }]);
341
+ }
342
+ /**
343
+ * Applies a custom transformation pipeline to an image.
344
+ * @param fileId Unique identifier of the image file to transform.
345
+ * @param steps Array of transformation steps to apply in sequence.
346
+ * @returns Promise resolving to transformed image result.
347
+ */
348
+ async transform(fileId, steps) {
349
+ const fileQuery = { id: fileId };
350
+ const cacheKey = this.generateCacheKey(fileId, steps);
351
+ const cached = this.cache ? await Promise.resolve(this.cache.get(cacheKey)) : void 0;
352
+ if (cached) {
353
+ this.logger?.debug("Returning cached transformed image for %s", fileId);
354
+ return cached;
355
+ }
356
+ const originalFile = await this.storage.get(fileQuery);
357
+ await this.validateImage(originalFile);
358
+ const transformedBuffer = await this.applyTransformations(originalFile.content, steps);
359
+ const result = await this.createTransformResult(transformedBuffer, originalFile);
360
+ if (this.cache) {
361
+ this.cache.set(cacheKey, result);
362
+ }
363
+ return result;
364
+ }
365
+ /**
366
+ * Streams transform of a file (for large files, falls back to regular transform).
367
+ * @param fileId Unique identifier of the image file to transform.
368
+ * @param steps Array of transformation steps to apply in sequence.
369
+ * @returns Promise resolving to stream with headers and size information.
370
+ */
371
+ async transformStream(fileId, steps) {
372
+ const result = await this.transform(fileId, steps);
373
+ return {
374
+ headers: {
375
+ "Content-Length": result.buffer.length.toString(),
376
+ "Content-Type": this.getContentTypeFromResult(result),
377
+ "X-Image-Height": result.height?.toString(),
378
+ "X-Image-Width": result.width?.toString()
379
+ },
380
+ size: result.buffer.length,
381
+ stream: Readable.from(result.buffer)
382
+ };
383
+ }
384
+ /**
385
+ * Applies multiple transformations in sequence using Sharp.
386
+ * @param buffer The original image buffer.
387
+ * @param steps Array of transformation steps to apply.
388
+ * @returns Promise resolving to transformed image buffer.
389
+ * @private
390
+ */
391
+ async applyTransformations(buffer, steps) {
392
+ let sharpInstance = sharp(buffer);
393
+ for (const step of steps) {
394
+ switch (step.type) {
395
+ case "affine": {
396
+ sharpInstance = this.applyAffine(sharpInstance, step.options);
397
+ break;
398
+ }
399
+ case "autoOrient": {
400
+ sharpInstance = sharpInstance.rotate();
401
+ break;
402
+ }
403
+ case "bandbool": {
404
+ sharpInstance = this.applyBandbool(sharpInstance, step.options);
405
+ break;
406
+ }
407
+ case "blur": {
408
+ sharpInstance = this.applyBlur(sharpInstance, step.options);
409
+ break;
410
+ }
411
+ case "boolean": {
412
+ sharpInstance = this.applyBoolean(sharpInstance, step.options);
413
+ break;
414
+ }
415
+ case "clahe": {
416
+ sharpInstance = this.applyCLAHE(sharpInstance, step.options);
417
+ break;
418
+ }
419
+ case "colourspace": {
420
+ sharpInstance = this.applyColourspace(sharpInstance, step.options);
421
+ break;
422
+ }
423
+ case "convolve": {
424
+ sharpInstance = this.applyConvolve(sharpInstance, step.options);
425
+ break;
426
+ }
427
+ case "crop": {
428
+ sharpInstance = this.applyCrop(sharpInstance, step.options);
429
+ break;
430
+ }
431
+ case "dilate": {
432
+ sharpInstance = this.applyDilate(sharpInstance, step.options);
433
+ break;
434
+ }
435
+ case "ensureAlpha": {
436
+ sharpInstance = this.applyEnsureAlpha(sharpInstance, step.options);
437
+ break;
438
+ }
439
+ case "erode": {
440
+ sharpInstance = this.applyErode(sharpInstance, step.options);
441
+ break;
442
+ }
443
+ case "extractChannel": {
444
+ sharpInstance = this.applyExtractChannel(sharpInstance, step.options);
445
+ break;
446
+ }
447
+ case "flatten": {
448
+ sharpInstance = sharpInstance.flatten();
449
+ break;
450
+ }
451
+ case "flip": {
452
+ sharpInstance = sharpInstance.flip();
453
+ break;
454
+ }
455
+ case "flop": {
456
+ sharpInstance = sharpInstance.flop();
457
+ break;
458
+ }
459
+ case "format":
460
+ case "quality": {
461
+ sharpInstance = this.applyFormatAndQuality(sharpInstance, step.options);
462
+ break;
463
+ }
464
+ case "gamma": {
465
+ sharpInstance = sharpInstance.gamma();
466
+ break;
467
+ }
468
+ case "greyscale": {
469
+ sharpInstance = this.applyGreyscale(sharpInstance, step.options);
470
+ break;
471
+ }
472
+ case "joinChannel": {
473
+ sharpInstance = this.applyJoinChannel(sharpInstance, step.options);
474
+ break;
475
+ }
476
+ case "linear": {
477
+ sharpInstance = this.applyLinear(sharpInstance, step.options);
478
+ break;
479
+ }
480
+ case "median": {
481
+ sharpInstance = this.applyMedian(sharpInstance, step.options);
482
+ break;
483
+ }
484
+ case "modulate": {
485
+ sharpInstance = this.applyModulate(sharpInstance, step.options);
486
+ break;
487
+ }
488
+ case "negate": {
489
+ sharpInstance = sharpInstance.negate();
490
+ break;
491
+ }
492
+ case "normalise":
493
+ case "normalize": {
494
+ sharpInstance = sharpInstance.normalise();
495
+ break;
496
+ }
497
+ case "pipelineColourspace": {
498
+ sharpInstance = this.applyPipelineColourspace(sharpInstance, step.options);
499
+ break;
500
+ }
501
+ case "recombine": {
502
+ sharpInstance = this.applyRecombine(sharpInstance, step.options);
503
+ break;
504
+ }
505
+ case "removeAlpha": {
506
+ sharpInstance = this.applyRemoveAlpha(sharpInstance, step.options);
507
+ break;
508
+ }
509
+ case "resize": {
510
+ sharpInstance = this.applyResize(sharpInstance, step.options);
511
+ break;
512
+ }
513
+ case "rotate": {
514
+ sharpInstance = this.applyRotate(sharpInstance, step.options);
515
+ break;
516
+ }
517
+ case "sharpen": {
518
+ sharpInstance = this.applySharpen(sharpInstance, step.options);
519
+ break;
520
+ }
521
+ case "threshold": {
522
+ sharpInstance = this.applyThreshold(sharpInstance, step.options);
523
+ break;
524
+ }
525
+ case "tint": {
526
+ sharpInstance = this.applyTint(sharpInstance, step.options);
527
+ break;
528
+ }
529
+ case "toColourspace": {
530
+ sharpInstance = this.applyToColourspace(sharpInstance, step.options);
531
+ break;
532
+ }
533
+ case "unflatten": {
534
+ sharpInstance = sharpInstance.unflatten();
535
+ break;
536
+ }
537
+ default: {
538
+ throw new Error(`Unknown transformation type: ${step.type}`);
539
+ }
540
+ }
541
+ }
542
+ return sharpInstance.toBuffer();
543
+ }
544
+ /**
545
+ * Applies resize transformation using Sharp.
546
+ * @param sharpInstance The Sharp instance to modify.
547
+ * @param options Resize transformation options.
548
+ * @returns Modified Sharp instance with resize applied.
549
+ * @private
550
+ */
551
+ applyResize(sharpInstance, options) {
552
+ const {
553
+ background,
554
+ fastShrinkOnLoad,
555
+ fit = "cover",
556
+ height,
557
+ kernel,
558
+ position,
559
+ width,
560
+ withoutEnlargement,
561
+ withoutReduction,
562
+ ...formatOptions
563
+ } = options;
564
+ let resizeOptions = {
565
+ background: background || "transparent",
566
+ fastShrinkOnLoad,
567
+ fit,
568
+ height,
569
+ kernel,
570
+ position,
571
+ width,
572
+ withoutEnlargement,
573
+ withoutReduction
574
+ };
575
+ if (Object.keys(formatOptions).length > 0) {
576
+ resizeOptions = { ...resizeOptions, ...this.getFormatOptions(formatOptions) };
577
+ }
578
+ return sharpInstance.resize(resizeOptions);
579
+ }
580
+ /**
581
+ * Applies crop transformation using Sharp.
582
+ * @param sharpInstance The Sharp instance to modify.
583
+ * @param options Crop transformation options.
584
+ * @returns Modified Sharp instance with crop applied.
585
+ * @private
586
+ */
587
+ applyCrop(sharpInstance, options) {
588
+ const { height, left, top, width, ...formatOptions } = options;
589
+ let cropInstance = sharpInstance.extract({
590
+ height,
591
+ left,
592
+ top,
593
+ width
594
+ });
595
+ if (Object.keys(formatOptions).length > 0) {
596
+ cropInstance = this.applyFormatAndQuality(cropInstance, formatOptions);
597
+ }
598
+ return cropInstance;
599
+ }
600
+ /**
601
+ * Applies rotate transformation using Sharp.
602
+ * @param sharpInstance The Sharp instance to modify.
603
+ * @param options Rotate transformation options.
604
+ * @returns Modified Sharp instance with rotation applied.
605
+ * @private
606
+ */
607
+ applyRotate(sharpInstance, options) {
608
+ const { angle, background, ...formatOptions } = options;
609
+ let rotateInstance = sharpInstance.rotate(angle, {
610
+ background: background || "transparent"
611
+ });
612
+ if (Object.keys(formatOptions).length > 0) {
613
+ rotateInstance = this.applyFormatAndQuality(rotateInstance, formatOptions);
614
+ }
615
+ return rotateInstance;
616
+ }
617
+ /**
618
+ * Applies sharpen transformation using Sharp.
619
+ * @param sharpInstance The Sharp instance to modify.
620
+ * @param options Sharpen transformation options.
621
+ * @returns Modified Sharp instance with sharpening applied.
622
+ * @private
623
+ */
624
+ applySharpen(sharpInstance, options) {
625
+ const { sigma, ...formatOptions } = options;
626
+ const sharpenOptions = {};
627
+ if (sigma !== void 0) {
628
+ sharpenOptions.sigma = sigma;
629
+ }
630
+ let sharpenInstance = sharpInstance.sharpen(sharpenOptions);
631
+ if (Object.keys(formatOptions).length > 0) {
632
+ sharpenInstance = this.applyFormatAndQuality(sharpenInstance, formatOptions);
633
+ }
634
+ return sharpenInstance;
635
+ }
636
+ /**
637
+ * Applies blur transformation using Sharp.
638
+ * @param sharpInstance The Sharp instance to modify.
639
+ * @param options Blur transformation options.
640
+ * @returns Modified Sharp instance with blurring applied.
641
+ * @private
642
+ */
643
+ applyBlur(sharpInstance, options) {
644
+ const { sigma, ...formatOptions } = options;
645
+ let blurInstance = sharpInstance.blur(sigma === void 0 ? true : sigma);
646
+ if (Object.keys(formatOptions).length > 0) {
647
+ blurInstance = this.applyFormatAndQuality(blurInstance, formatOptions);
648
+ }
649
+ return blurInstance;
650
+ }
651
+ /**
652
+ * Applies median filter using Sharp.
653
+ * @param sharpInstance The Sharp instance to modify.
654
+ * @param options Median transformation options.
655
+ * @returns Modified Sharp instance with median filter applied.
656
+ * @private
657
+ */
658
+ applyMedian(sharpInstance, options) {
659
+ const { size, ...formatOptions } = options;
660
+ let medianInstance = sharpInstance.median(size);
661
+ if (Object.keys(formatOptions).length > 0) {
662
+ medianInstance = this.applyFormatAndQuality(medianInstance, formatOptions);
663
+ }
664
+ return medianInstance;
665
+ }
666
+ /**
667
+ * Applies CLAHE (Contrast Limited Adaptive Histogram Equalization) using Sharp.
668
+ * @param sharpInstance The Sharp instance to modify.
669
+ * @param options CLAHE transformation options.
670
+ * @returns Modified Sharp instance with CLAHE applied.
671
+ * @private
672
+ */
673
+ applyCLAHE(sharpInstance, options) {
674
+ const { height, maxSlope, width, ...formatOptions } = options;
675
+ let claheInstance = sharpInstance.clahe({
676
+ height,
677
+ maxSlope,
678
+ width
679
+ });
680
+ if (Object.keys(formatOptions).length > 0) {
681
+ claheInstance = this.applyFormatAndQuality(claheInstance, formatOptions);
682
+ }
683
+ return claheInstance;
684
+ }
685
+ /**
686
+ * Applies convolution using Sharp.
687
+ * @param sharpInstance The Sharp instance to modify.
688
+ * @param options Convolution transformation options.
689
+ * @returns Modified Sharp instance with convolution applied.
690
+ * @private
691
+ */
692
+ applyConvolve(sharpInstance, options) {
693
+ const { height, kernel, offset, scale, width, ...formatOptions } = options;
694
+ let convolveInstance = sharpInstance.convolve({
695
+ height,
696
+ kernel,
697
+ offset,
698
+ scale,
699
+ width
700
+ });
701
+ if (Object.keys(formatOptions).length > 0) {
702
+ convolveInstance = this.applyFormatAndQuality(convolveInstance, formatOptions);
703
+ }
704
+ return convolveInstance;
705
+ }
706
+ /**
707
+ * Applies threshold using Sharp.
708
+ * @param sharpInstance The Sharp instance to modify.
709
+ * @param options Threshold transformation options.
710
+ * @returns Modified Sharp instance with thresholding applied.
711
+ * @private
712
+ */
713
+ applyThreshold(sharpInstance, options) {
714
+ const { grayscale, greyscale, threshold = 128, ...formatOptions } = options;
715
+ let thresholdInstance = sharpInstance.threshold(threshold, {
716
+ greyscale: greyscale || grayscale
717
+ });
718
+ if (Object.keys(formatOptions).length > 0) {
719
+ thresholdInstance = this.applyFormatAndQuality(thresholdInstance, formatOptions);
720
+ }
721
+ return thresholdInstance;
722
+ }
723
+ /**
724
+ * Applies boolean operation using Sharp.
725
+ * @param sharpInstance The Sharp instance to modify.
726
+ * @param options Boolean operation options.
727
+ * @returns Modified Sharp instance with boolean operation applied.
728
+ * @private
729
+ */
730
+ applyBoolean(sharpInstance, options) {
731
+ const { operand, operator, raw, ...formatOptions } = options;
732
+ let booleanInstance = sharpInstance.boolean(operand, operator, raw);
733
+ if (Object.keys(formatOptions).length > 0) {
734
+ booleanInstance = this.applyFormatAndQuality(booleanInstance, formatOptions);
735
+ }
736
+ return booleanInstance;
737
+ }
738
+ /**
739
+ * Applies linear transformation using Sharp.
740
+ * @param sharpInstance The Sharp instance to modify.
741
+ * @param options Linear transformation options.
742
+ * @returns Modified Sharp instance with linear transformation applied.
743
+ * @private
744
+ */
745
+ applyLinear(sharpInstance, options) {
746
+ const { a, b, ...formatOptions } = options;
747
+ let linearInstance = sharpInstance.linear(a, b);
748
+ if (Object.keys(formatOptions).length > 0) {
749
+ linearInstance = this.applyFormatAndQuality(linearInstance, formatOptions);
750
+ }
751
+ return linearInstance;
752
+ }
753
+ /**
754
+ * Applies recombine transformation using Sharp.
755
+ * @param sharpInstance The Sharp instance to modify.
756
+ * @param options Recombine transformation options.
757
+ * @returns Modified Sharp instance with recombine applied.
758
+ * @private
759
+ */
760
+ applyRecombine(sharpInstance, options) {
761
+ const { matrix, ...formatOptions } = options;
762
+ let recombineInstance = sharpInstance.recomb(matrix.flat());
763
+ if (Object.keys(formatOptions).length > 0) {
764
+ recombineInstance = this.applyFormatAndQuality(recombineInstance, formatOptions);
765
+ }
766
+ return recombineInstance;
767
+ }
768
+ /**
769
+ * Applies modulate transformation using Sharp.
770
+ * @param sharpInstance The Sharp instance to modify.
771
+ * @param options Modulate transformation options.
772
+ * @returns Modified Sharp instance with modulation applied.
773
+ * @private
774
+ */
775
+ applyModulate(sharpInstance, options) {
776
+ const { brightness, hue, lightness, saturation, ...formatOptions } = options;
777
+ let modulateInstance = sharpInstance.modulate({
778
+ brightness,
779
+ hue,
780
+ lightness,
781
+ saturation
782
+ });
783
+ if (Object.keys(formatOptions).length > 0) {
784
+ modulateInstance = this.applyFormatAndQuality(modulateInstance, formatOptions);
785
+ }
786
+ return modulateInstance;
787
+ }
788
+ /**
789
+ * Applies tint using Sharp.
790
+ * @param sharpInstance The Sharp instance to modify.
791
+ * @param options Tint transformation options.
792
+ * @returns Modified Sharp instance with tinting applied.
793
+ * @private
794
+ */
795
+ applyTint(sharpInstance, options) {
796
+ const { rgb, ...formatOptions } = options;
797
+ let tintInstance = sharpInstance.tint(Array.isArray(rgb) ? rgb : rgb);
798
+ if (Object.keys(formatOptions).length > 0) {
799
+ tintInstance = this.applyFormatAndQuality(tintInstance, formatOptions);
800
+ }
801
+ return tintInstance;
802
+ }
803
+ /**
804
+ * Applies greyscale conversion using Sharp.
805
+ * @param sharpInstance The Sharp instance to modify.
806
+ * @param options Greyscale transformation options.
807
+ * @returns Modified Sharp instance converted to greyscale.
808
+ * @private
809
+ */
810
+ applyGreyscale(sharpInstance, options) {
811
+ const { grayscale, greyscale, ...formatOptions } = options;
812
+ let greyscaleInstance = sharpInstance.greyscale(greyscale || grayscale);
813
+ if (Object.keys(formatOptions).length > 0) {
814
+ greyscaleInstance = this.applyFormatAndQuality(greyscaleInstance, formatOptions);
815
+ }
816
+ return greyscaleInstance;
817
+ }
818
+ /**
819
+ * Applies colourspace conversion using Sharp.
820
+ * @param sharpInstance The Sharp instance to modify.
821
+ * @param options Colourspace transformation options.
822
+ * @returns Modified Sharp instance with colourspace conversion.
823
+ * @private
824
+ */
825
+ applyColourspace(sharpInstance, options) {
826
+ const { colourspace, ...formatOptions } = options;
827
+ let colourspaceInstance = sharpInstance.toColourspace(colourspace);
828
+ if (Object.keys(formatOptions).length > 0) {
829
+ colourspaceInstance = this.applyFormatAndQuality(colourspaceInstance, formatOptions);
830
+ }
831
+ return colourspaceInstance;
832
+ }
833
+ /**
834
+ * Extracts channel using Sharp.
835
+ * @param sharpInstance The Sharp instance to modify.
836
+ * @param options Channel extraction options including channel index.
837
+ * @returns Modified Sharp instance with channel extracted.
838
+ * @private
839
+ */
840
+ applyExtractChannel(sharpInstance, options) {
841
+ const { channel, ...formatOptions } = options;
842
+ let extractInstance = sharpInstance.extractChannel(channel);
843
+ if (Object.keys(formatOptions).length > 0) {
844
+ extractInstance = this.applyFormatAndQuality(extractInstance, formatOptions);
845
+ }
846
+ return extractInstance;
847
+ }
848
+ /**
849
+ * Joins channels using Sharp.
850
+ * @param sharpInstance The Sharp instance to modify.
851
+ * @param options Channel joining options including image sources.
852
+ * @returns Modified Sharp instance with channels joined.
853
+ * @private
854
+ */
855
+ applyJoinChannel(sharpInstance, options) {
856
+ const { images, ...formatOptions } = options;
857
+ let joinInstance = sharpInstance.joinChannel(images);
858
+ if (Object.keys(formatOptions).length > 0) {
859
+ joinInstance = this.applyFormatAndQuality(joinInstance, formatOptions);
860
+ }
861
+ return joinInstance;
862
+ }
863
+ /**
864
+ * Applies band boolean operation using Sharp.
865
+ * @param sharpInstance The Sharp instance to modify.
866
+ * @param options Band boolean options including operator.
867
+ * @returns Modified Sharp instance with band boolean operation applied.
868
+ * @private
869
+ */
870
+ applyBandbool(sharpInstance, options) {
871
+ const { operator, ...formatOptions } = options;
872
+ let bandboolInstance = sharpInstance.bandbool(operator);
873
+ if (Object.keys(formatOptions).length > 0) {
874
+ bandboolInstance = this.applyFormatAndQuality(bandboolInstance, formatOptions);
875
+ }
876
+ return bandboolInstance;
877
+ }
878
+ /**
879
+ * Applies affine transformation using Sharp.
880
+ * @param sharpInstance The Sharp instance to modify.
881
+ * @param options Affine transformation options including matrix.
882
+ * @returns Modified Sharp instance with affine transformation applied.
883
+ * @private
884
+ */
885
+ applyAffine(sharpInstance, options) {
886
+ const { background, interpolation, matrix, ...formatOptions } = options;
887
+ let affineInstance = sharpInstance.affine(matrix, {
888
+ background: background || "transparent",
889
+ interpolator: interpolation || "bicubic"
890
+ });
891
+ if (Object.keys(formatOptions).length > 0) {
892
+ affineInstance = this.applyFormatAndQuality(affineInstance, formatOptions);
893
+ }
894
+ return affineInstance;
895
+ }
896
+ /**
897
+ * Applies dilation using Sharp.
898
+ * @param sharpInstance The Sharp instance to modify.
899
+ * @param options Dilate options including kernel size.
900
+ * @returns Modified Sharp instance with dilation applied.
901
+ * @private
902
+ */
903
+ applyDilate(sharpInstance, options) {
904
+ const { kernelSize, ...formatOptions } = options;
905
+ let dilateInstance = kernelSize ? sharpInstance.dilate(kernelSize) : sharpInstance.dilate();
906
+ if (Object.keys(formatOptions).length > 0) {
907
+ dilateInstance = this.applyFormatAndQuality(dilateInstance, formatOptions);
908
+ }
909
+ return dilateInstance;
910
+ }
911
+ /**
912
+ * Applies erosion using Sharp.
913
+ * @param sharpInstance The Sharp instance to modify.
914
+ * @param options Erode options including kernel size.
915
+ * @returns Modified Sharp instance with erosion applied.
916
+ * @private
917
+ */
918
+ applyErode(sharpInstance, options) {
919
+ const { kernelSize, ...formatOptions } = options;
920
+ let erodeInstance = kernelSize ? sharpInstance.erode(kernelSize) : sharpInstance.erode();
921
+ if (Object.keys(formatOptions).length > 0) {
922
+ erodeInstance = this.applyFormatAndQuality(erodeInstance, formatOptions);
923
+ }
924
+ return erodeInstance;
925
+ }
926
+ /**
927
+ * Applies pipeline colourspace using Sharp.
928
+ * @param sharpInstance The Sharp instance to modify.
929
+ * @param options Pipeline colourspace options including target colourspace.
930
+ * @returns Modified Sharp instance with pipeline colourspace applied.
931
+ * @private
932
+ */
933
+ applyPipelineColourspace(sharpInstance, options) {
934
+ const { colourspace, ...formatOptions } = options;
935
+ let colourspaceInstance = sharpInstance.pipelineColourspace(colourspace);
936
+ if (Object.keys(formatOptions).length > 0) {
937
+ colourspaceInstance = this.applyFormatAndQuality(colourspaceInstance, formatOptions);
938
+ }
939
+ return colourspaceInstance;
940
+ }
941
+ /**
942
+ * Applies to colourspace conversion using Sharp.
943
+ * @param sharpInstance The Sharp instance to modify.
944
+ * @param options To colourspace options including target colourspace.
945
+ * @returns Modified Sharp instance with colourspace conversion applied.
946
+ * @private
947
+ */
948
+ applyToColourspace(sharpInstance, options) {
949
+ const { colourspace, ...formatOptions } = options;
950
+ let colourspaceInstance = sharpInstance.toColourspace(colourspace);
951
+ if (Object.keys(formatOptions).length > 0) {
952
+ colourspaceInstance = this.applyFormatAndQuality(colourspaceInstance, formatOptions);
953
+ }
954
+ return colourspaceInstance;
955
+ }
956
+ /**
957
+ * Applies remove alpha using Sharp.
958
+ * @param sharpInstance The Sharp instance to modify.
959
+ * @param options Remove alpha transformation options.
960
+ * @returns Modified Sharp instance with alpha channel removed.
961
+ * @private
962
+ */
963
+ applyRemoveAlpha(sharpInstance, options) {
964
+ const { background, ...formatOptions } = options;
965
+ let alphaInstance = sharpInstance.removeAlpha();
966
+ if (Object.keys(formatOptions).length > 0) {
967
+ alphaInstance = this.applyFormatAndQuality(alphaInstance, formatOptions);
968
+ }
969
+ return alphaInstance;
970
+ }
971
+ /**
972
+ * Applies ensure alpha using Sharp.
973
+ * @param sharpInstance The Sharp instance to modify.
974
+ * @param options Ensure alpha transformation options.
975
+ * @returns Modified Sharp instance with alpha channel ensured.
976
+ * @private
977
+ */
978
+ applyEnsureAlpha(sharpInstance, options) {
979
+ const { alpha, ...formatOptions } = options;
980
+ let alphaInstance = sharpInstance.ensureAlpha();
981
+ if (Object.keys(formatOptions).length > 0) {
982
+ alphaInstance = this.applyFormatAndQuality(alphaInstance, formatOptions);
983
+ }
984
+ return alphaInstance;
985
+ }
986
+ /**
987
+ * Applies format and quality transformation using Sharp.
988
+ * @param sharpInstance The Sharp instance to modify.
989
+ * @param options Format and quality transformation options.
990
+ * @returns Modified Sharp instance with format and quality settings applied.
991
+ * @private
992
+ */
993
+ applyFormatAndQuality(sharpInstance, options) {
994
+ const formatOptions = this.getFormatOptions(options);
995
+ if (options.format) {
996
+ return sharpInstance.toFormat(options.format, formatOptions);
997
+ }
998
+ if (Object.keys(formatOptions).length > 0) {
999
+ return sharpInstance.jpeg(formatOptions).png(formatOptions).webp(formatOptions);
1000
+ }
1001
+ return sharpInstance;
1002
+ }
1003
+ /**
1004
+ * Gets format-specific options for Sharp.
1005
+ * @param options Transform options containing format-specific settings.
1006
+ * @returns Record of format options for Sharp processing.
1007
+ * @private
1008
+ */
1009
+ // eslint-disable-next-line class-methods-use-this
1010
+ getFormatOptions(options) {
1011
+ const { alphaQuality, compressionLevel, delay, effort, loop, lossless, progressive, quality } = options;
1012
+ const formatOptions = {};
1013
+ if (quality !== void 0) {
1014
+ formatOptions.quality = quality;
1015
+ }
1016
+ if (progressive !== void 0) {
1017
+ formatOptions.progressive = progressive;
1018
+ }
1019
+ if (lossless !== void 0) {
1020
+ formatOptions.lossless = lossless;
1021
+ }
1022
+ if (compressionLevel !== void 0) {
1023
+ formatOptions.compressionLevel = compressionLevel;
1024
+ }
1025
+ if (effort !== void 0) {
1026
+ formatOptions.effort = effort;
1027
+ }
1028
+ if (alphaQuality !== void 0) {
1029
+ formatOptions.alphaQuality = alphaQuality;
1030
+ }
1031
+ if (loop !== void 0) {
1032
+ formatOptions.loop = loop;
1033
+ }
1034
+ if (delay !== void 0) {
1035
+ formatOptions.delay = delay;
1036
+ }
1037
+ return formatOptions;
1038
+ }
1039
+ /**
1040
+ * Validates that the file is a supported image.
1041
+ * @param file The file to validate.
1042
+ * @returns Promise that resolves if validation passes.
1043
+ * @throws Error if file size exceeds limits, wrong content type, unsupported format, or invalid image.
1044
+ * @private
1045
+ */
1046
+ async validateImage(file) {
1047
+ const fileSize = typeof file.size === "string" ? Number.parseInt(file.size, 10) : file.size;
1048
+ if (this.config?.maxImageSize && fileSize > this.config.maxImageSize) {
1049
+ throw new Error(`Image size ${fileSize} exceeds maximum allowed size ${this.config.maxImageSize}`);
1050
+ }
1051
+ if (!isValidMediaType(file.contentType, "image")) {
1052
+ throw new Error(`File is not an image: ${file.contentType}`);
1053
+ }
1054
+ const format = getFormatFromContentType(file.contentType);
1055
+ if (this.config?.supportedFormats && format && !this.config.supportedFormats.includes(format)) {
1056
+ throw new Error(`Unsupported image format: ${format}`);
1057
+ }
1058
+ try {
1059
+ const metadata = await sharp(file.content).metadata();
1060
+ if (!metadata.width || !metadata.height) {
1061
+ throw new Error("Invalid image: missing dimensions");
1062
+ }
1063
+ } catch (error) {
1064
+ throw new Error(`Invalid image file: ${error}`);
1065
+ }
1066
+ }
1067
+ /**
1068
+ * Creates transformation result with metadata.
1069
+ * @param buffer The transformed image buffer.
1070
+ * @param originalFile The original file information.
1071
+ * @returns Image transformation result with metadata.
1072
+ * @private
1073
+ */
1074
+ // eslint-disable-next-line class-methods-use-this
1075
+ async createTransformResult(buffer, originalFile) {
1076
+ const metadata = await sharp(buffer).metadata();
1077
+ return {
1078
+ buffer,
1079
+ format: metadata.format || "unknown",
1080
+ height: metadata.height || 0,
1081
+ originalFile,
1082
+ size: buffer.length,
1083
+ width: metadata.width || 0
1084
+ };
1085
+ }
1086
+ /**
1087
+ * Generates cache key for transformation.
1088
+ * @param fileId The file identifier.
1089
+ * @param steps Array of transformation steps.
1090
+ * @returns Unique cache key string.
1091
+ * @private
1092
+ */
1093
+ // eslint-disable-next-line class-methods-use-this
1094
+ generateCacheKey(fileId, steps) {
1095
+ const stepsKey = steps.map((step) => `${step.type}:${JSON.stringify(step.options)}`).join("|");
1096
+ return `${fileId}:${stepsKey}`;
1097
+ }
1098
+ }
1099
+
1100
+ export { ImageTransformer as default };