@visulima/storage 1.0.0-alpha.18 → 1.0.0-alpha.19

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 (185) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/LICENSE.md +30 -0
  3. package/dist/adapter/nuxt/module.d.ts +3 -3
  4. package/dist/adapter/nuxt/module.js +1 -93
  5. package/dist/handler/http/fetch/index.d.ts +6 -6
  6. package/dist/handler/http/fetch/index.js +1 -4
  7. package/dist/handler/http/hono/index.d.ts +3 -3
  8. package/dist/handler/http/hono/index.js +1 -65
  9. package/dist/handler/http/nextjs/index.d.ts +4 -4
  10. package/dist/handler/http/nextjs/index.js +1 -37
  11. package/dist/handler/http/node/index.d.ts +6 -6
  12. package/dist/handler/http/node/index.js +1 -8
  13. package/dist/handler/http/solid-start/index.d.ts +4 -4
  14. package/dist/handler/http/solid-start/index.js +1 -44
  15. package/dist/index.js +1 -13
  16. package/dist/openapi/index.js +1 -5
  17. package/dist/packem_shared/AwsLightFile-BTLiRxXj.js +1 -0
  18. package/dist/packem_shared/AwsLightMetaStorage-DHz1nf_H.js +1 -0
  19. package/dist/packem_shared/AwsLightStorage-CA9cdfJ6.js +1 -0
  20. package/dist/packem_shared/AzureFile-BKHru1zB.js +1 -0
  21. package/dist/packem_shared/AzureSMetaStorage-BqA4jUDJ.js +1 -0
  22. package/dist/packem_shared/AzureStorage-BKUjCymW.js +1 -0
  23. package/dist/packem_shared/BaseTransformer-D68eLyz1.js +1 -0
  24. package/dist/packem_shared/DiskStorage-DNjpDMX9.js +1 -0
  25. package/dist/packem_shared/DiskStorageWithChecksum-Bb6JX_tC.js +1 -0
  26. package/dist/packem_shared/ERRORS-B9jkvKwx.js +1 -0
  27. package/dist/packem_shared/File-alcSNpLq.js +1 -0
  28. package/dist/packem_shared/GCSConfig-BQ0OpVhe.js +1 -0
  29. package/dist/packem_shared/GCSFile-CHjX_fJk.js +1 -0
  30. package/dist/packem_shared/GCSMetaStorage-CpxQ8wM9.js +1 -0
  31. package/dist/packem_shared/GCStorage-DAg1V55T.js +1 -0
  32. package/dist/packem_shared/LocalMetaStorage-BWuFvWn4.js +1 -0
  33. package/dist/packem_shared/MediaTransformer-2C8MJeAg.js +1 -0
  34. package/dist/packem_shared/MetaStorage-Y4m1dWgd.js +1 -0
  35. package/dist/packem_shared/Metadata-B6Ir0KO7.js +1 -0
  36. package/dist/packem_shared/Multipart-Bd9YFFn0.js +1 -0
  37. package/dist/packem_shared/Multipart-D5bcEvga.js +1 -0
  38. package/dist/packem_shared/NetlifyBlobFile-CpzkMKng.js +1 -0
  39. package/dist/packem_shared/NetlifyBlobMetaStorage-dD0oBLom.js +1 -0
  40. package/dist/packem_shared/NetlifyBlobStorage-ByTwCN-h.js +1 -0
  41. package/dist/packem_shared/NoOpMetrics-Dky06sf1.js +1 -0
  42. package/dist/packem_shared/OpenTelemetryMetrics-9uxQMSlg.js +1 -0
  43. package/dist/packem_shared/Rest-CYEBbtCD.js +1 -0
  44. package/dist/packem_shared/Rest-DoonVU7B.js +1 -0
  45. package/dist/packem_shared/S3File-Du6wP0Sz.js +1 -0
  46. package/dist/packem_shared/S3MetaStorage-vcriiosn.js +1 -0
  47. package/dist/packem_shared/S3Storage-Fx6-OSc-.js +1 -0
  48. package/dist/packem_shared/TUS_RESUMABLE-B7KtPVeC.js +1 -0
  49. package/dist/packem_shared/Tus-Bd_2Ki2G.js +1 -0
  50. package/dist/packem_shared/Tus-CUBCkuPE.js +1 -0
  51. package/dist/packem_shared/ValidationError-D8KKJiKA.js +1 -0
  52. package/dist/packem_shared/VercelBlobFile-CL9MjKcY.js +1 -0
  53. package/dist/packem_shared/VercelBlobMetaStorage-BsOTx-Su.js +1 -0
  54. package/dist/packem_shared/VercelBlobStorage-kNkk5Cn4.js +1 -0
  55. package/dist/packem_shared/_commonjsHelpers-D6W6KoPK.js +1 -0
  56. package/dist/packem_shared/aws-light-meta-storage-TPyQwLvk.js +4 -0
  57. package/dist/packem_shared/backblaze-DEflv87m.js +1 -0
  58. package/dist/packem_shared/base-handler-core-DaggCVsq.js +1 -0
  59. package/dist/packem_shared/base-handler-fetch-DnarP64X.js +1 -0
  60. package/dist/packem_shared/base-handler-node-BTes6sql.js +1 -0
  61. package/dist/packem_shared/cache-qV_5Umeq.js +1 -0
  62. package/dist/packem_shared/cloudflare-DLAeQvgD.js +1 -0
  63. package/dist/packem_shared/defaultCloudStorageFileNameValidation-yiM9GxHr.js +1 -0
  64. package/dist/packem_shared/digitalOcean-nhZUds87.js +1 -0
  65. package/dist/packem_shared/disk-storage-BvdK4bqF.js +5 -0
  66. package/dist/packem_shared/gcs-meta-storage-CvGkpzVE.js +1 -0
  67. package/dist/packem_shared/has-content-Bx2p8Okx.js +1 -0
  68. package/dist/packem_shared/headers-DC2MEOrK.js +27 -0
  69. package/dist/packem_shared/is-expired-1JuZjDtb.js +1 -0
  70. package/dist/packem_shared/isRetryableError-DQNSZiBc.js +1 -0
  71. package/dist/packem_shared/isValidMediaType-B-2UKjZx.js +1 -0
  72. package/dist/packem_shared/local-meta-storage-Bkwqi_gi.js +1 -0
  73. package/dist/packem_shared/{media-transformer.d-DeHZkBVx.d.ts → media-transformer.d-CY9CsEOe.d.ts} +1 -1
  74. package/dist/packem_shared/minio-BWwgGXeE.js +1 -0
  75. package/dist/packem_shared/multipart-base-C6OjBwMS.js +1 -0
  76. package/dist/packem_shared/part-match-DqD5U7An.js +1 -0
  77. package/dist/packem_shared/path-D7rMc9nq-B718mbm6.js +1 -0
  78. package/dist/packem_shared/response-builder-Wdo_fvcF.js +1 -0
  79. package/dist/packem_shared/rest-base-B2Dv7esY.js +1 -0
  80. package/dist/packem_shared/restOpenApiSpec-PaIVbESh.js +1 -0
  81. package/dist/packem_shared/s3-base-storage-xHZNtsGe.js +1 -0
  82. package/dist/packem_shared/sharedGet-Ce7WGAsc.js +1 -0
  83. package/dist/packem_shared/storage-m_Cxgiih.js +1 -0
  84. package/dist/packem_shared/tigris-DZoyrMNd.js +1 -0
  85. package/dist/packem_shared/transformOpenApiSpec-C0vEbDTO.js +1 -0
  86. package/dist/packem_shared/{tus-base.d-BftIixIA.d.ts → tus-base.d-DNZRXR9X.d.ts} +2 -2
  87. package/dist/packem_shared/tusOpenApiSpec-DF0-hlWV.js +1 -0
  88. package/dist/packem_shared/{types.d-BPd_JkW1.d.ts → types.d-BCN94sB_.d.ts} +1 -1
  89. package/dist/packem_shared/{types.d-D-mlfmef.d.ts → types.d-D4caL2Fp.d.ts} +1 -1
  90. package/dist/packem_shared/update-size-DANV7aa2.js +1 -0
  91. package/dist/packem_shared/validator-DK41xp8N.js +1 -0
  92. package/dist/packem_shared/waitForStorage-ChSBLhLc.js +1 -0
  93. package/dist/packem_shared/wasabi-Dmce4x9s.js +1 -0
  94. package/dist/packem_shared/xhrOpenApiSpec-nyoboz83.js +1 -0
  95. package/dist/storage/aws/clients/index.js +1 -6
  96. package/dist/storage/aws/index.js +1 -3
  97. package/dist/storage/aws-light/index.js +1 -3
  98. package/dist/storage/azure/index.js +1 -3
  99. package/dist/storage/gcs/index.js +1 -4
  100. package/dist/storage/local/index.js +1 -3
  101. package/dist/storage/netlify-blob/index.js +1 -3
  102. package/dist/storage/vercel-blob/index.js +1 -3
  103. package/dist/transformer/audio-transformer.d.ts +1 -1
  104. package/dist/transformer/audio-transformer.js +1 -278
  105. package/dist/transformer/image-transformer.d.ts +1 -1
  106. package/dist/transformer/image-transformer.js +1 -1100
  107. package/dist/transformer/index.d.ts +2 -2
  108. package/dist/transformer/index.js +1 -4
  109. package/dist/transformer/video-transformer.d.ts +1 -1
  110. package/dist/transformer/video-transformer.js +1 -310
  111. package/package.json +17 -17
  112. package/dist/packem_shared/AwsLightFile-C-Nd5rtl.js +0 -11
  113. package/dist/packem_shared/AwsLightMetaStorage-BIDb5EPg.js +0 -4
  114. package/dist/packem_shared/AwsLightStorage-CMuKw4bI.js +0 -133
  115. package/dist/packem_shared/AzureFile-C0kug4w4.js +0 -8
  116. package/dist/packem_shared/AzureSMetaStorage-BEbXX9Uk.js +0 -89
  117. package/dist/packem_shared/AzureStorage-98-LRZ1e.js +0 -359
  118. package/dist/packem_shared/BaseTransformer-9965D4pb.js +0 -83
  119. package/dist/packem_shared/DiskStorage-COMe_G42.js +0 -12
  120. package/dist/packem_shared/DiskStorageWithChecksum-BVaaZiyS.js +0 -249
  121. package/dist/packem_shared/ERRORS-D0apMqnc.js +0 -97
  122. package/dist/packem_shared/File-CPUhj3DI.js +0 -69
  123. package/dist/packem_shared/GCSConfig-vPP22kN6.js +0 -11
  124. package/dist/packem_shared/GCSFile-DaDiCKE2.js +0 -8
  125. package/dist/packem_shared/GCSMetaStorage-w3c8c5ak.js +0 -7
  126. package/dist/packem_shared/GCStorage-DSktYrrp.js +0 -410
  127. package/dist/packem_shared/LocalMetaStorage-DvnZqf61.js +0 -7
  128. package/dist/packem_shared/MediaTransformer-BRRqYA0E.js +0 -1202
  129. package/dist/packem_shared/MetaStorage-pECeFOad.js +0 -46
  130. package/dist/packem_shared/Metadata-DRLXeJ0F.js +0 -89
  131. package/dist/packem_shared/Multipart-BzzvNxt4.js +0 -128
  132. package/dist/packem_shared/Multipart-DLtFLQ8s.js +0 -163
  133. package/dist/packem_shared/NetlifyBlobFile-CfyJtGWN.js +0 -14
  134. package/dist/packem_shared/NetlifyBlobMetaStorage-eJT6RFy9.js +0 -9
  135. package/dist/packem_shared/NetlifyBlobStorage-nPv7iIAG.js +0 -376
  136. package/dist/packem_shared/NoOpMetrics-DhAk5rXc.js +0 -10
  137. package/dist/packem_shared/OpenTelemetryMetrics-BnxhqIaH.js +0 -67
  138. package/dist/packem_shared/Rest-DEa7IA9R.js +0 -267
  139. package/dist/packem_shared/Rest-qhMbCn5a.js +0 -228
  140. package/dist/packem_shared/S3File-BXCuJaQZ.js +0 -11
  141. package/dist/packem_shared/S3MetaStorage-yT4CznlV.js +0 -77
  142. package/dist/packem_shared/S3Storage-wi5XT-yu.js +0 -317
  143. package/dist/packem_shared/TUS_RESUMABLE-Dtfsffnj.js +0 -434
  144. package/dist/packem_shared/Tus-Ck0Hdle5.js +0 -195
  145. package/dist/packem_shared/Tus-_0vCdHyB.js +0 -200
  146. package/dist/packem_shared/ValidationError-BfF1aE4h.js +0 -26
  147. package/dist/packem_shared/VercelBlobFile-DkFIIb9Q.js +0 -18
  148. package/dist/packem_shared/VercelBlobMetaStorage-DjFmi4vp.js +0 -9
  149. package/dist/packem_shared/VercelBlobStorage-CmMgiDgr.js +0 -261
  150. package/dist/packem_shared/_commonjsHelpers-BqLXS_qQ.js +0 -5
  151. package/dist/packem_shared/aws-light-meta-storage-BGZEiY3y.js +0 -447
  152. package/dist/packem_shared/backblaze-BlMnIcBC.js +0 -20
  153. package/dist/packem_shared/base-handler-core-lVB-D59L.js +0 -306
  154. package/dist/packem_shared/base-handler-fetch-CitgN-Aw.js +0 -291
  155. package/dist/packem_shared/base-handler-node-BiO2DF87.js +0 -731
  156. package/dist/packem_shared/cache-B88MXQ_2.js +0 -18
  157. package/dist/packem_shared/cloudflare-Bi1q8wXE.js +0 -21
  158. package/dist/packem_shared/defaultCloudStorageFileNameValidation-Brw40ZEP.js +0 -11
  159. package/dist/packem_shared/digitalOcean-CWQRJM3L.js +0 -21
  160. package/dist/packem_shared/disk-storage-D9RfoKjW.js +0 -832
  161. package/dist/packem_shared/gcs-meta-storage-CPSVL_a7.js +0 -159
  162. package/dist/packem_shared/has-content-CY66ehMK.js +0 -3
  163. package/dist/packem_shared/headers-YLRp1dLo.js +0 -1318
  164. package/dist/packem_shared/is-expired-CTThU1q5.js +0 -8
  165. package/dist/packem_shared/isRetryableError-Dycp7127.js +0 -82
  166. package/dist/packem_shared/isValidMediaType-BeDgiObq.js +0 -35
  167. package/dist/packem_shared/local-meta-storage-D-pPc3FI.js +0 -658
  168. package/dist/packem_shared/minio-C2YBZQOw.js +0 -22
  169. package/dist/packem_shared/multipart-base-C994naBi.js +0 -102
  170. package/dist/packem_shared/part-match-Dt738Ddb.js +0 -96
  171. package/dist/packem_shared/path-aV9lMdcn-DMU5H1-h.js +0 -162
  172. package/dist/packem_shared/response-builder-BtnRiBUI.js +0 -45
  173. package/dist/packem_shared/rest-base-k1ory8uB.js +0 -457
  174. package/dist/packem_shared/restOpenApiSpec-BBJ_SiPE.js +0 -800
  175. package/dist/packem_shared/s3-base-storage-zUO8u8Cp.js +0 -510
  176. package/dist/packem_shared/sharedGet-CkSLSnMd.js +0 -1022
  177. package/dist/packem_shared/storage-5XW1E7XU.js +0 -1188
  178. package/dist/packem_shared/tigris-Hac8TKnX.js +0 -21
  179. package/dist/packem_shared/transformOpenApiSpec-BodFhFls.js +0 -975
  180. package/dist/packem_shared/tusOpenApiSpec-DzppyJTW.js +0 -740
  181. package/dist/packem_shared/update-size-CCGm6i1J.js +0 -8
  182. package/dist/packem_shared/validator-DuO17wFF.js +0 -79
  183. package/dist/packem_shared/waitForStorage-Cscw85sx.js +0 -18
  184. package/dist/packem_shared/wasabi-DIyflHSd.js +0 -21
  185. package/dist/packem_shared/xhrOpenApiSpec-CXz7BC5J.js +0 -264
@@ -1,731 +0,0 @@
1
- import { paginate } from '@visulima/pagination';
2
- import { H as HeaderUtilities, h as httpErrorsExports, c as createHttpError } from './headers-YLRp1dLo.js';
3
- import mime from 'mime';
4
- import { ERRORS, isUploadError } from './ERRORS-D0apMqnc.js';
5
- import { match } from 'path-to-regexp';
6
- import { p as pick, B as BaseHandlerCore, s as setHeaders, d as getRealPath, u as uuidRegex } from './base-handler-core-lVB-D59L.js';
7
- import { i as isValidationError } from './validator-DuO17wFF.js';
8
- import { waitForStorage } from './waitForStorage-Cscw85sx.js';
9
- import { PassThrough } from 'node:stream';
10
- import { e as convertHeadersToString } from './response-builder-BtnRiBUI.js';
11
-
12
- const internalMatcher = match(["/:uuid", "/*path/:uuid.:ext/:metadata", "/*path/:uuid/:metadata", "/*path/:uuid.:ext", "/*path/:uuid"], {
13
- decode: decodeURIComponent
14
- });
15
- const filePathUrlMatcher = (path) => {
16
- const result = internalMatcher(path);
17
- if (!result) {
18
- return void 0;
19
- }
20
- const { params } = result;
21
- if (!params.uuid) {
22
- return void 0;
23
- }
24
- const orderedParams = {
25
- uuid: params.uuid
26
- };
27
- if (params.metadata !== void 0) {
28
- orderedParams.metadata = params.metadata;
29
- }
30
- if (params.path !== void 0) {
31
- orderedParams.path = params.path;
32
- }
33
- if (params.ext !== void 0) {
34
- orderedParams.ext = params.ext;
35
- }
36
- return {
37
- params: orderedParams,
38
- path: result.path
39
- };
40
- };
41
-
42
- const createRangeLimitedStream = (sourceStream, start, end) => {
43
- let bytesRead = 0;
44
- let bytesSent = 0;
45
- const contentLength = end - start + 1;
46
- return new PassThrough({
47
- // Use appropriate high water mark for better backpressure handling
48
- highWaterMark: Math.min(64 * 1024, contentLength),
49
- // 64KB or content length, whichever is smaller
50
- transform(chunk, _, callback) {
51
- const chunkSize = chunk.length;
52
- const currentPos = bytesRead;
53
- const endPos = currentPos + chunkSize - 1;
54
- bytesRead += chunkSize;
55
- if (endPos < start) {
56
- callback();
57
- return;
58
- }
59
- if (currentPos > end) {
60
- this.end();
61
- callback();
62
- return;
63
- }
64
- const chunkStart = Math.max(0, start - currentPos);
65
- const chunkEnd = Math.min(chunkSize, end - currentPos + 1);
66
- if (chunkStart < chunkEnd) {
67
- const dataToSend = chunk.subarray(chunkStart, chunkEnd);
68
- bytesSent += dataToSend.length;
69
- const canContinue = this.push(dataToSend);
70
- if (!canContinue) {
71
- sourceStream.pause();
72
- }
73
- }
74
- if (bytesSent >= contentLength) {
75
- this.end();
76
- sourceStream.destroy();
77
- }
78
- callback();
79
- }
80
- });
81
- };
82
- const pipeWithBackpressure = (source, destination, sendError) => {
83
- let isDestroyed = false;
84
- const cleanup = () => {
85
- if (isDestroyed) {
86
- return;
87
- }
88
- isDestroyed = true;
89
- source.destroy();
90
- };
91
- destination.on("drain", () => {
92
- source.resume();
93
- });
94
- destination.on("close", cleanup);
95
- destination.on("finish", cleanup);
96
- destination.on("error", cleanup);
97
- source.on("end", () => {
98
- destination.end();
99
- });
100
- source.on("error", async (error) => {
101
- if (!isDestroyed) {
102
- await sendError(destination, error);
103
- cleanup();
104
- }
105
- });
106
- source.on("data", (chunk) => {
107
- const canContinue = destination.write(chunk);
108
- if (!canContinue) {
109
- source.pause();
110
- }
111
- });
112
- if (typeof destination.listeners === "function" && destination.listeners("close")?.length === 0) {
113
- destination.on("close", cleanup);
114
- }
115
- };
116
-
117
- const handleHeadOptionsRequest = (file, send, response) => {
118
- const { headers, statusCode } = file;
119
- send(response, { headers, statusCode });
120
- };
121
- const handleGetRequest = (file, request, response, next, send, sendStream, parseRangeHeader) => {
122
- request.body = file?.data === void 0 ? file : file.data;
123
- const { headers, statusCode } = file;
124
- const streamingFile = file;
125
- if (streamingFile.stream) {
126
- if (typeof next === "function") {
127
- next();
128
- } else {
129
- const range = parseRangeHeader(request.headers.range, streamingFile.size || 0);
130
- sendStream(response, streamingFile.stream, {
131
- headers,
132
- range: range || void 0,
133
- size: streamingFile.size,
134
- statusCode
135
- });
136
- }
137
- } else {
138
- let body = "";
139
- if (file.content !== void 0) {
140
- body = file.content;
141
- } else if (typeof file === "object" && "data" in file) {
142
- body = file.data;
143
- }
144
- if (typeof next === "function") {
145
- next();
146
- } else {
147
- send(response, { body, headers, statusCode });
148
- }
149
- }
150
- };
151
- const handleCompletedUpload = async (file, request, response, next, storage, logger, finish) => {
152
- const { headers: fileHeaders, statusCode, ...basicFile } = file;
153
- if (typeof next === "function") {
154
- request._body = true;
155
- request.body = basicFile;
156
- next();
157
- } else {
158
- const responseFile = file;
159
- const originalHeaders = responseFile.headers ? { ...responseFile.headers } : {};
160
- if (responseFile.headers === void 0) {
161
- responseFile.headers = {};
162
- }
163
- if (responseFile.statusCode === void 0) {
164
- responseFile.statusCode = 200;
165
- }
166
- try {
167
- const result = storage.onComplete(basicFile, responseFile, request);
168
- if (result instanceof Promise) {
169
- await result;
170
- }
171
- } catch (error) {
172
- if (logger?.error) {
173
- logger.error("[onComplete error]: %O", error);
174
- }
175
- throw error;
176
- }
177
- if (Object.keys(responseFile.headers || {}).length === 0 && Object.keys(originalHeaders).length > 0) {
178
- responseFile.headers = originalHeaders;
179
- } else if (responseFile.headers && Object.keys(originalHeaders).length > 0) {
180
- responseFile.headers = {
181
- ...originalHeaders,
182
- ...responseFile.headers
183
- };
184
- }
185
- finish(request, response, responseFile);
186
- }
187
- };
188
- const handlePartialUpload = (file, request, response, send) => {
189
- const { headers: fileHeaders, statusCode, ...basicFile } = file;
190
- const headers = fileHeaders || {};
191
- const isChunkedUploadInit = headers["X-Chunked-Upload"] === "true" || headers["x-chunked-upload"] === "true";
192
- let body;
193
- const responseHeaders = {
194
- ...convertHeadersToString(headers),
195
- ...fileHeaders ? convertHeadersToString(fileHeaders) : {},
196
- ...basicFile.hash === void 0 ? {} : { [`X-Range-${basicFile.hash?.algorithm.toUpperCase()}`]: String(basicFile.hash?.value) }
197
- };
198
- if (isChunkedUploadInit) {
199
- body = JSON.stringify(basicFile);
200
- if (!responseHeaders["Content-Type"] && !responseHeaders["content-type"]) {
201
- responseHeaders["Content-Type"] = "application/json; charset=utf-8";
202
- }
203
- }
204
- if (request.method === "HEAD") {
205
- body = "";
206
- }
207
- send(response, {
208
- body,
209
- headers: responseHeaders,
210
- statusCode: statusCode || 200
211
- });
212
- };
213
- const handleUploadError = async (error, request, emit, listenerCount, logger, sendError, response) => {
214
- const errorObject = error instanceof Error ? error : new Error(String(error));
215
- const uError = pick(errorObject, ["name", ...Object.getOwnPropertyNames(errorObject)]);
216
- const errorEvent = { ...uError, request: pick(request, ["headers", "method", "url"]) };
217
- if (listenerCount("error") > 0) {
218
- emit("error", errorEvent);
219
- }
220
- if (logger?.error) {
221
- logger.error("[error]: %O", errorEvent);
222
- }
223
- await sendError(response, errorObject);
224
- };
225
-
226
- const CONTENT_TYPE = "Content-Type";
227
- class BaseHandlerNode extends BaseHandlerCore {
228
- /**
229
- * Limiting enabled HTTP method handler.
230
- */
231
- static methods = ["delete", "get", "head", "options", "patch", "post", "put"];
232
- /**
233
- * Map of registered HTTP method handlers.
234
- */
235
- registeredHandlers = /* @__PURE__ */ new Map();
236
- constructor(options) {
237
- super(options);
238
- this.compose();
239
- }
240
- /**
241
- * Gets the registered handlers map.
242
- * @returns Map of registered handlers.
243
- */
244
- get handlers() {
245
- return this.registeredHandlers;
246
- }
247
- /**
248
- * Handles HTTP request (alias for upload method).
249
- * @param request Node.js IncomingMessage.
250
- * @param response Node.js ServerResponse.
251
- */
252
- handle = async (request, response) => this.upload(request, response);
253
- /**
254
- * Main upload handler that processes HTTP requests and routes them to appropriate method handlers.
255
- * @param request Node.js IncomingMessage.
256
- * @param response Node.js ServerResponse.
257
- * @param next Optional Express-style next function for middleware compatibility.
258
- * @throws {UploadError} When storage is not ready or method is not allowed.
259
- */
260
- upload = async (request, response, next) => {
261
- request.on("error", (error) => this.logger?.error("[request error]: %O", error));
262
- this.logger?.debug("[request]: %s %s", request.method, request.url);
263
- const handler = this.registeredHandlers.get(request.method);
264
- if (!handler) {
265
- await this.sendError(response, { UploadErrorCode: ERRORS.METHOD_NOT_ALLOWED });
266
- return;
267
- }
268
- try {
269
- await waitForStorage(this.storage);
270
- } catch {
271
- await this.sendError(response, { UploadErrorCode: ERRORS.STORAGE_ERROR });
272
- return;
273
- }
274
- try {
275
- const file = await handler.call(this, request, response);
276
- if (["HEAD", "OPTIONS"].includes(request.method)) {
277
- handleHeadOptionsRequest(file, this.send.bind(this), response);
278
- } else if (request.method === "GET") {
279
- const fileResponse = file;
280
- if (fileResponse) {
281
- handleGetRequest(
282
- fileResponse,
283
- request,
284
- response,
285
- next,
286
- this.send.bind(this),
287
- this.sendStream.bind(this),
288
- this.parseRangeHeader.bind(this)
289
- );
290
- }
291
- } else {
292
- const { headers: fileHeaders, statusCode, ...basicFile } = file;
293
- this.logger?.debug("[%s]: %s: %d/%d", basicFile.status, basicFile.name, basicFile.bytesWritten, basicFile.size);
294
- if (basicFile.status !== void 0 && this.listenerCount(basicFile.status) > 0) {
295
- this.emit(basicFile.status, {
296
- ...basicFile,
297
- request: pick(request, ["headers", "method", "url"])
298
- });
299
- }
300
- if (basicFile.status === "completed") {
301
- await handleCompletedUpload(
302
- file,
303
- request,
304
- response,
305
- next,
306
- this.storage,
307
- this.logger,
308
- (_request, resp, uploadResp) => {
309
- this.finish(_request, resp, uploadResp);
310
- }
311
- );
312
- } else {
313
- handlePartialUpload(file, request, response, (resp, data) => {
314
- this.send(resp, {
315
- body: data.body,
316
- headers: data.headers,
317
- statusCode: data.statusCode
318
- });
319
- });
320
- }
321
- }
322
- } catch (error) {
323
- await handleUploadError(error, request, this.emit.bind(this), this.listenerCount.bind(this), this.logger, this.sendError.bind(this), response);
324
- }
325
- };
326
- /**
327
- * Sends an HTTP response with the provided body, headers, and status code.
328
- * @param response Node.js ServerResponse to send data to.
329
- * @param body Response body, headers, and status code.
330
- */
331
- // eslint-disable-next-line class-methods-use-this
332
- send(response, { body = "", headers = {}, statusCode = 200 }) {
333
- let data;
334
- const finalHeaders = { ...headers };
335
- if (typeof body === "string") {
336
- data = body;
337
- if (finalHeaders[CONTENT_TYPE] === void 0) {
338
- finalHeaders[CONTENT_TYPE] = HeaderUtilities.createContentType({ mediaType: "text/plain" });
339
- } else {
340
- const contentTypeValue = Array.isArray(finalHeaders[CONTENT_TYPE]) ? finalHeaders[CONTENT_TYPE].join(", ") : String(finalHeaders[CONTENT_TYPE]);
341
- finalHeaders[CONTENT_TYPE] = HeaderUtilities.ensureCharset(contentTypeValue);
342
- }
343
- if (finalHeaders["Content-Length"] === void 0) {
344
- finalHeaders["Content-Length"] = Buffer.byteLength(body);
345
- }
346
- } else if (body instanceof Buffer) {
347
- data = body;
348
- } else {
349
- data = JSON.stringify(body);
350
- if (!finalHeaders[CONTENT_TYPE]) {
351
- finalHeaders[CONTENT_TYPE] = HeaderUtilities.createContentType({
352
- charset: "utf8",
353
- mediaType: "application/json"
354
- });
355
- }
356
- }
357
- setHeaders(response, finalHeaders);
358
- response.statusCode = statusCode;
359
- response.end(data);
360
- }
361
- /**
362
- * Sends an error response to the client with appropriate status code and message.
363
- * @param response Node.js ServerResponse to send error to.
364
- * @param error Error object to convert to HTTP error response.
365
- */
366
- async sendError(response, error) {
367
- let httpError;
368
- if (isUploadError(error)) {
369
- httpError = this.internalErrorResponses[error.UploadErrorCode];
370
- } else if (!isValidationError(error) && !httpErrorsExports.isHttpError(error)) {
371
- httpError = this.storage.normalizeError(error);
372
- } else {
373
- httpError = {
374
- ...error,
375
- code: error.code || error.name,
376
- headers: error.headers || {},
377
- message: error.message,
378
- name: error.name,
379
- statusCode: error.statusCode || 500
380
- };
381
- }
382
- await this.storage.onError(httpError);
383
- let errorResponse;
384
- if (httpError.body) {
385
- if (typeof httpError.body === "object" && httpError.body !== null) {
386
- errorResponse = { body: httpError.body, headers: httpError.headers, statusCode: httpError.statusCode };
387
- } else {
388
- errorResponse = {
389
- body: {
390
- error: {
391
- code: httpError.code || httpError.name || "Error",
392
- message: httpError.body || httpError.message || "Unknown error",
393
- name: httpError.name || "Error"
394
- }
395
- },
396
- headers: httpError.headers,
397
- statusCode: httpError.statusCode || 500
398
- };
399
- }
400
- } else {
401
- errorResponse = {
402
- body: {
403
- error: {
404
- code: httpError.code || httpError.name || "Error",
405
- message: httpError.message || "Unknown error",
406
- name: httpError.name || "Error"
407
- }
408
- },
409
- headers: httpError.headers,
410
- statusCode: httpError.statusCode || 500
411
- };
412
- }
413
- this.send(response, errorResponse);
414
- }
415
- /**
416
- * Sends streaming response to client with proper backpressure handling and range request support.
417
- * @param response Node.js ServerResponse to stream data to.
418
- * @param stream Readable stream containing the file data.
419
- * @param options Streaming options including headers, range, size, and status code.
420
- */
421
- sendStream(response, stream, {
422
- headers = {},
423
- range,
424
- size,
425
- statusCode = 200
426
- }) {
427
- setHeaders(response, headers);
428
- response.statusCode = statusCode;
429
- let finalStream = stream;
430
- if (range && size) {
431
- response.statusCode = 206;
432
- response.setHeader("Content-Range", `bytes ${range.start}-${range.end}/${size}`);
433
- response.setHeader("Content-Length", range.end - range.start + 1);
434
- response.setHeader("Accept-Ranges", "bytes");
435
- finalStream = createRangeLimitedStream(stream, range.start, range.end);
436
- } else {
437
- if (size) {
438
- response.setHeader("Content-Length", size);
439
- }
440
- response.setHeader("Accept-Ranges", "bytes");
441
- }
442
- pipeWithBackpressure(finalStream, response, (resp, error) => this.sendError(resp, error));
443
- }
444
- /**
445
- * Finish upload by sending final response to client with completed file data.
446
- * @param _request HTTP request (unused parameter)
447
- * @param response HTTP response object to send final response to
448
- * @param uploadResponse Final upload response data containing body, headers, and status code
449
- */
450
- finish(_request, response, uploadResponse) {
451
- const isResponseFile = "id" in uploadResponse && "headers" in uploadResponse && !("body" in uploadResponse);
452
- let body;
453
- let headers;
454
- let statusCode;
455
- if (isResponseFile) {
456
- const responseFile = uploadResponse;
457
- const fileHeaders = responseFile.headers || {};
458
- const fileStatusCode = responseFile.statusCode || 200;
459
- const { headers: _, statusCode: __, ...fileData } = responseFile;
460
- body = fileData;
461
- headers = { ...fileHeaders };
462
- statusCode = fileStatusCode;
463
- } else {
464
- const uploadResp = uploadResponse;
465
- body = uploadResp.body;
466
- headers = uploadResp.headers || {};
467
- statusCode = uploadResp.statusCode || 200;
468
- }
469
- if (body && typeof body === "object" && body.content !== void 0) {
470
- const { content, contentType } = body;
471
- body = content;
472
- headers = {
473
- ...headers,
474
- "Content-Type": contentType
475
- };
476
- }
477
- this.send(response, {
478
- body: body || uploadResponse,
479
- headers,
480
- statusCode
481
- });
482
- }
483
- /**
484
- * Build file URL from request and file data.
485
- * @param request HTTP request with optional originalUrl property
486
- * @param file File object containing ID and content type
487
- * @returns Constructed file URL with extension based on content type
488
- */
489
- buildFileUrl(request, file) {
490
- return this.buildFileUrlFromString(request.originalUrl || request.url, file);
491
- }
492
- /**
493
- * Negotiates content type based on Accept header and supported formats.
494
- * @param request HTTP request object containing Accept header.
495
- * @param supportedTypes Array of supported MIME types to match against.
496
- * @returns Best matching content type or undefined if no match found.
497
- */
498
- negotiateContentType(request, supportedTypes) {
499
- return super.negotiateContentTypeFromHeader(request.headers.accept, supportedTypes);
500
- }
501
- /**
502
- * Default OPTIONS handler.
503
- * @param _request HTTP request (unused)
504
- * @param _response HTTP response (unused)
505
- * @returns Promise resolving to ResponseFile with CORS headers
506
- */
507
- async options(_request, _response) {
508
- const child = this.constructor;
509
- return {
510
- headers: {
511
- "Access-Control-Allow-Methods": (child.methods || BaseHandlerNode.methods).map((method) => method.toUpperCase()).join(", ")
512
- },
513
- statusCode: 204
514
- };
515
- }
516
- /**
517
- * Retrieves a file or list of files based on the request path.
518
- * @param request Node.js IncomingMessage with optional originalUrl.
519
- * @param response Node.js ServerResponse.
520
- * @returns Promise resolving to a single file, paginated list, or array of files.
521
- * @throws {UploadError} When file is not found or storage error occurs.
522
- */
523
- // eslint-disable-next-line sonarjs/cognitive-complexity
524
- async get(request, _response) {
525
- const pathMatch = filePathUrlMatcher(getRealPath(request));
526
- if (pathMatch?.params.uuid) {
527
- const { ext, metadata, uuid: rawUuid } = pathMatch.params;
528
- const uuid = ext ? rawUuid.replace(new RegExp(String.raw`\.${ext}$`), "") : rawUuid;
529
- if (metadata === "metadata" && getRealPath(request).endsWith("/metadata")) {
530
- try {
531
- const file = await this.storage.getMeta(uuid);
532
- return {
533
- ...file,
534
- content: JSON.stringify(file),
535
- headers: {
536
- "Content-Type": HeaderUtilities.createContentType({
537
- charset: "utf8",
538
- mediaType: "application/json"
539
- }),
540
- ...file.expiredAt === void 0 ? {} : { "X-Upload-Expires": file.expiredAt.toString() },
541
- ...file.modifiedAt === void 0 ? {} : { "Last-Modified": file.modifiedAt.toString() }
542
- },
543
- statusCode: 200
544
- };
545
- } catch (error) {
546
- const errorWithCode = error;
547
- if (errorWithCode.UploadErrorCode === ERRORS.FILE_NOT_FOUND || errorWithCode.UploadErrorCode === ERRORS.GONE) {
548
- throw createHttpError(404, "File metadata not found");
549
- }
550
- throw error;
551
- }
552
- }
553
- if (!uuidRegex.test(uuid)) {
554
- return this.list(request, _response);
555
- }
556
- try {
557
- const url = new URL(request.url || "", "http://localhost");
558
- const queryParameters = Object.fromEntries(url.searchParams.entries());
559
- const hasTransformationParameters = Object.keys(queryParameters).length > 0 && this.mediaTransformer;
560
- if (hasTransformationParameters && this.mediaTransformer) {
561
- try {
562
- const transformedResult = await this.mediaTransformer.handle(uuid, queryParameters);
563
- return {
564
- content: transformedResult.buffer,
565
- headers: {
566
- "Content-Length": String(transformedResult.size),
567
- "Content-Type": `${transformedResult.mediaType}/${transformedResult.format}`,
568
- "X-Media-Type": transformedResult.mediaType,
569
- "X-Original-Format": transformedResult.originalFile?.contentType?.split("/")[1] || "",
570
- "X-Transformed-Format": transformedResult.format,
571
- ...transformedResult.originalFile?.expiredAt === void 0 ? {} : { "X-Upload-Expires": transformedResult.originalFile.expiredAt.toString() },
572
- ...transformedResult.originalFile?.modifiedAt === void 0 ? {} : { "Last-Modified": transformedResult.originalFile.modifiedAt.toString() },
573
- ...transformedResult.originalFile?.ETag === void 0 ? {} : { ETag: transformedResult.originalFile.ETag }
574
- },
575
- statusCode: 200
576
- };
577
- } catch (transformError) {
578
- if (transformError.name === "ValidationError") {
579
- throw createHttpError(400, transformError.message);
580
- }
581
- this.logger?.warn(`Media transformation failed: ${transformError.message}`);
582
- }
583
- }
584
- const fileMeta = await this.storage.getMeta(uuid);
585
- const useStreaming = request.headers.range || fileMeta.size && fileMeta.size > 1024 * 1024;
586
- if (useStreaming && this.storage.getStream) {
587
- try {
588
- const streamResult = await this.storage.getStream({ id: uuid });
589
- let contentType2 = streamResult.headers?.["Content-Type"] || fileMeta.contentType;
590
- if (contentType2.includes("image") && typeof ext === "string") {
591
- contentType2 = mime.getType(ext) || contentType2;
592
- }
593
- return {
594
- headers: {
595
- ...streamResult.headers,
596
- "Accept-Ranges": "bytes",
597
- // Indicate we support range requests
598
- "Content-Type": contentType2
599
- },
600
- size: streamResult.size,
601
- statusCode: 200,
602
- stream: streamResult.stream,
603
- ...fileMeta,
604
- contentType: contentType2
605
- };
606
- } catch (streamError) {
607
- this.logger?.warn(`Streaming failed, falling back to buffer: ${streamError}`);
608
- }
609
- }
610
- const file = await this.storage.get({ id: uuid });
611
- let { contentType } = file;
612
- if (contentType.includes("image") && typeof ext === "string") {
613
- contentType = mime.getType(ext) || contentType;
614
- }
615
- const { ETag, expiredAt, modifiedAt, size } = file;
616
- return {
617
- headers: {
618
- "Accept-Ranges": "bytes",
619
- // Indicate we support range requests
620
- "Content-Length": String(size),
621
- "Content-Type": contentType,
622
- ...expiredAt === void 0 ? {} : { "X-Upload-Expires": expiredAt.toString() },
623
- ...modifiedAt === void 0 ? {} : { "Last-Modified": modifiedAt.toString() },
624
- ...ETag === void 0 ? {} : { ETag }
625
- },
626
- statusCode: 200,
627
- ...file,
628
- contentType
629
- };
630
- } catch (error) {
631
- const errorWithCode = error;
632
- if (errorWithCode.UploadErrorCode === ERRORS.FILE_NOT_FOUND || errorWithCode.UploadErrorCode === ERRORS.GONE) {
633
- throw createHttpError(404, "File not found");
634
- }
635
- throw error;
636
- }
637
- }
638
- return this.list(request, _response);
639
- }
640
- /**
641
- * Returns a list of uploaded files with optional pagination support.
642
- * @param request Node.js IncomingMessage containing query parameters for pagination.
643
- * @param _response Node.js ServerResponse (unused).
644
- * @returns Promise resolving to a paginated or complete list of uploaded files.
645
- */
646
- async list(request, _response) {
647
- const url = new URL(request.url || "", "http://localhost");
648
- const limit = url.searchParams.get("limit");
649
- const page = url.searchParams.get("page");
650
- const list = await this.storage.list(Number(limit || 1e3));
651
- if (list.length === 0) {
652
- return {
653
- data: [],
654
- headers: {},
655
- statusCode: 200
656
- };
657
- }
658
- if (page !== void 0 && limit !== void 0) {
659
- return {
660
- data: paginate(Number(page), Number(limit), list.length, list),
661
- headers: {},
662
- statusCode: 200
663
- };
664
- }
665
- return {
666
- data: list,
667
- headers: {},
668
- statusCode: 200
669
- };
670
- }
671
- /**
672
- * Streams download of a file with resumable support using HTTP range requests.
673
- * @param request Node.js IncomingMessage with optional originalUrl and range header.
674
- * @param response Node.js ServerResponse to stream the file to.
675
- * @throws {HttpError} When file is not found or streaming is not supported.
676
- */
677
- async download(request, response) {
678
- const pathMatch = filePathUrlMatcher(getRealPath(request));
679
- if (!pathMatch?.params.uuid || !uuidRegex.test(pathMatch.params.uuid)) {
680
- throw createHttpError(404, "File not found");
681
- }
682
- const { ext, uuid } = pathMatch.params;
683
- try {
684
- const fileMeta = await this.storage.getMeta(uuid);
685
- if (!this.storage.getStream) {
686
- await this.sendError(response, createHttpError(501, "Streaming download not supported"));
687
- return;
688
- }
689
- const streamResult = await this.storage.getStream({ id: uuid });
690
- let contentType = streamResult.headers?.["Content-Type"] || fileMeta.contentType;
691
- if (contentType.includes("image") && typeof ext === "string") {
692
- contentType = mime.getType(ext) || contentType;
693
- }
694
- const range = this.parseRangeHeader(request.headers.range, streamResult.size || 0);
695
- const headers = {
696
- ...streamResult.headers,
697
- "Accept-Ranges": "bytes",
698
- "Content-Disposition": HeaderUtilities.createContentDisposition({
699
- filename: fileMeta.originalName || uuid,
700
- type: "attachment"
701
- }),
702
- "Content-Type": contentType
703
- };
704
- this.sendStream(response, streamResult.stream, {
705
- headers,
706
- range: range || void 0,
707
- size: streamResult.size,
708
- statusCode: range ? 206 : 200
709
- });
710
- } catch (error) {
711
- const errorWithCode = error;
712
- if (errorWithCode.UploadErrorCode === ERRORS.FILE_NOT_FOUND || errorWithCode.UploadErrorCode === ERRORS.GONE) {
713
- await this.sendError(response, createHttpError(404, "File not found"));
714
- return;
715
- }
716
- await this.sendError(response, error);
717
- }
718
- }
719
- /**
720
- * Check if error is related to undefined ID or path and throw appropriate HTTP error.
721
- * @param error Error object to check
722
- */
723
- // eslint-disable-next-line class-methods-use-this
724
- checkForUndefinedIdOrPath(error) {
725
- if (error instanceof Error && ["Id is undefined", "Invalid request URL", "Path is undefined"].includes(error.message)) {
726
- throw createHttpError(404, "File not found");
727
- }
728
- }
729
- }
730
-
731
- export { BaseHandlerNode as B };