alepha 0.13.0 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/dist/api-jobs/index.d.ts +26 -26
  2. package/dist/api-users/index.d.ts +1 -1
  3. package/dist/cli/{dist-Sz2EXvQX.cjs → dist-Dl9Vl7Ur.js} +17 -13
  4. package/dist/cli/{dist-BBPjuQ56.js.map → dist-Dl9Vl7Ur.js.map} +1 -1
  5. package/dist/cli/index.d.ts +3 -11
  6. package/dist/cli/index.js +106 -74
  7. package/dist/cli/index.js.map +1 -1
  8. package/dist/email/index.js +71 -73
  9. package/dist/email/index.js.map +1 -1
  10. package/dist/orm/index.d.ts +1 -1
  11. package/dist/orm/index.js.map +1 -1
  12. package/dist/queue/index.d.ts +4 -4
  13. package/dist/retry/index.d.ts +1 -1
  14. package/dist/retry/index.js +2 -2
  15. package/dist/retry/index.js.map +1 -1
  16. package/dist/scheduler/index.d.ts +6 -6
  17. package/dist/security/index.d.ts +28 -28
  18. package/dist/server/index.js +1 -1
  19. package/dist/server/index.js.map +1 -1
  20. package/dist/server-health/index.d.ts +17 -17
  21. package/dist/server-metrics/index.js +170 -174
  22. package/dist/server-metrics/index.js.map +1 -1
  23. package/dist/server-security/index.d.ts +9 -9
  24. package/dist/vite/index.js +4 -5
  25. package/dist/vite/index.js.map +1 -1
  26. package/dist/websocket/index.d.ts +7 -7
  27. package/package.json +52 -103
  28. package/src/cli/apps/AlephaPackageBuilderCli.ts +7 -2
  29. package/src/cli/assets/appRouterTs.ts +9 -0
  30. package/src/cli/assets/indexHtml.ts +2 -1
  31. package/src/cli/assets/mainBrowserTs.ts +10 -0
  32. package/src/cli/commands/CoreCommands.ts +6 -5
  33. package/src/cli/commands/DrizzleCommands.ts +65 -57
  34. package/src/cli/commands/VerifyCommands.ts +1 -1
  35. package/src/cli/services/ProjectUtils.ts +44 -38
  36. package/src/orm/providers/DrizzleKitProvider.ts +1 -1
  37. package/src/retry/descriptors/$retry.ts +5 -3
  38. package/src/server/providers/NodeHttpServerProvider.ts +1 -1
  39. package/src/vite/helpers/boot.ts +3 -3
  40. package/dist/api-files/index.cjs +0 -1293
  41. package/dist/api-files/index.cjs.map +0 -1
  42. package/dist/api-files/index.d.cts +0 -829
  43. package/dist/api-jobs/index.cjs +0 -274
  44. package/dist/api-jobs/index.cjs.map +0 -1
  45. package/dist/api-jobs/index.d.cts +0 -654
  46. package/dist/api-notifications/index.cjs +0 -380
  47. package/dist/api-notifications/index.cjs.map +0 -1
  48. package/dist/api-notifications/index.d.cts +0 -289
  49. package/dist/api-parameters/index.cjs +0 -66
  50. package/dist/api-parameters/index.cjs.map +0 -1
  51. package/dist/api-parameters/index.d.cts +0 -84
  52. package/dist/api-users/index.cjs +0 -6009
  53. package/dist/api-users/index.cjs.map +0 -1
  54. package/dist/api-users/index.d.cts +0 -4740
  55. package/dist/api-verifications/index.cjs +0 -407
  56. package/dist/api-verifications/index.cjs.map +0 -1
  57. package/dist/api-verifications/index.d.cts +0 -207
  58. package/dist/batch/index.cjs +0 -408
  59. package/dist/batch/index.cjs.map +0 -1
  60. package/dist/batch/index.d.cts +0 -330
  61. package/dist/bin/index.cjs +0 -17
  62. package/dist/bin/index.cjs.map +0 -1
  63. package/dist/bin/index.d.cts +0 -1
  64. package/dist/bucket/index.cjs +0 -303
  65. package/dist/bucket/index.cjs.map +0 -1
  66. package/dist/bucket/index.d.cts +0 -355
  67. package/dist/cache/index.cjs +0 -241
  68. package/dist/cache/index.cjs.map +0 -1
  69. package/dist/cache/index.d.cts +0 -202
  70. package/dist/cache-redis/index.cjs +0 -84
  71. package/dist/cache-redis/index.cjs.map +0 -1
  72. package/dist/cache-redis/index.d.cts +0 -40
  73. package/dist/cli/chunk-DSlc6foC.cjs +0 -43
  74. package/dist/cli/dist-BBPjuQ56.js +0 -2778
  75. package/dist/cli/dist-Sz2EXvQX.cjs.map +0 -1
  76. package/dist/cli/index.cjs +0 -1241
  77. package/dist/cli/index.cjs.map +0 -1
  78. package/dist/cli/index.d.cts +0 -422
  79. package/dist/command/index.cjs +0 -693
  80. package/dist/command/index.cjs.map +0 -1
  81. package/dist/command/index.d.cts +0 -340
  82. package/dist/core/index.cjs +0 -2264
  83. package/dist/core/index.cjs.map +0 -1
  84. package/dist/core/index.d.cts +0 -1927
  85. package/dist/datetime/index.cjs +0 -318
  86. package/dist/datetime/index.cjs.map +0 -1
  87. package/dist/datetime/index.d.cts +0 -145
  88. package/dist/email/index.cjs +0 -10874
  89. package/dist/email/index.cjs.map +0 -1
  90. package/dist/email/index.d.cts +0 -186
  91. package/dist/fake/index.cjs +0 -34641
  92. package/dist/fake/index.cjs.map +0 -1
  93. package/dist/fake/index.d.cts +0 -74
  94. package/dist/file/index.cjs +0 -1212
  95. package/dist/file/index.cjs.map +0 -1
  96. package/dist/file/index.d.cts +0 -698
  97. package/dist/lock/index.cjs +0 -226
  98. package/dist/lock/index.cjs.map +0 -1
  99. package/dist/lock/index.d.cts +0 -361
  100. package/dist/lock-redis/index.cjs +0 -113
  101. package/dist/lock-redis/index.cjs.map +0 -1
  102. package/dist/lock-redis/index.d.cts +0 -24
  103. package/dist/logger/index.cjs +0 -521
  104. package/dist/logger/index.cjs.map +0 -1
  105. package/dist/logger/index.d.cts +0 -281
  106. package/dist/orm/index.cjs +0 -2986
  107. package/dist/orm/index.cjs.map +0 -1
  108. package/dist/orm/index.d.cts +0 -2213
  109. package/dist/queue/index.cjs +0 -1044
  110. package/dist/queue/index.cjs.map +0 -1
  111. package/dist/queue/index.d.cts +0 -1265
  112. package/dist/queue-redis/index.cjs +0 -873
  113. package/dist/queue-redis/index.cjs.map +0 -1
  114. package/dist/queue-redis/index.d.cts +0 -82
  115. package/dist/redis/index.cjs +0 -153
  116. package/dist/redis/index.cjs.map +0 -1
  117. package/dist/redis/index.d.cts +0 -82
  118. package/dist/retry/index.cjs +0 -146
  119. package/dist/retry/index.cjs.map +0 -1
  120. package/dist/retry/index.d.cts +0 -172
  121. package/dist/router/index.cjs +0 -111
  122. package/dist/router/index.cjs.map +0 -1
  123. package/dist/router/index.d.cts +0 -46
  124. package/dist/scheduler/index.cjs +0 -576
  125. package/dist/scheduler/index.cjs.map +0 -1
  126. package/dist/scheduler/index.d.cts +0 -145
  127. package/dist/security/index.cjs +0 -2402
  128. package/dist/security/index.cjs.map +0 -1
  129. package/dist/security/index.d.cts +0 -598
  130. package/dist/server/index.cjs +0 -1680
  131. package/dist/server/index.cjs.map +0 -1
  132. package/dist/server/index.d.cts +0 -810
  133. package/dist/server-auth/index.cjs +0 -3146
  134. package/dist/server-auth/index.cjs.map +0 -1
  135. package/dist/server-auth/index.d.cts +0 -1164
  136. package/dist/server-cache/index.cjs +0 -252
  137. package/dist/server-cache/index.cjs.map +0 -1
  138. package/dist/server-cache/index.d.cts +0 -164
  139. package/dist/server-compress/index.cjs +0 -141
  140. package/dist/server-compress/index.cjs.map +0 -1
  141. package/dist/server-compress/index.d.cts +0 -38
  142. package/dist/server-cookies/index.cjs +0 -234
  143. package/dist/server-cookies/index.cjs.map +0 -1
  144. package/dist/server-cookies/index.d.cts +0 -144
  145. package/dist/server-cors/index.cjs +0 -201
  146. package/dist/server-cors/index.cjs.map +0 -1
  147. package/dist/server-cors/index.d.cts +0 -140
  148. package/dist/server-health/index.cjs +0 -62
  149. package/dist/server-health/index.cjs.map +0 -1
  150. package/dist/server-health/index.d.cts +0 -58
  151. package/dist/server-helmet/index.cjs +0 -131
  152. package/dist/server-helmet/index.cjs.map +0 -1
  153. package/dist/server-helmet/index.d.cts +0 -97
  154. package/dist/server-links/index.cjs +0 -992
  155. package/dist/server-links/index.cjs.map +0 -1
  156. package/dist/server-links/index.d.cts +0 -513
  157. package/dist/server-metrics/index.cjs +0 -4535
  158. package/dist/server-metrics/index.cjs.map +0 -1
  159. package/dist/server-metrics/index.d.cts +0 -35
  160. package/dist/server-multipart/index.cjs +0 -237
  161. package/dist/server-multipart/index.cjs.map +0 -1
  162. package/dist/server-multipart/index.d.cts +0 -50
  163. package/dist/server-proxy/index.cjs +0 -186
  164. package/dist/server-proxy/index.cjs.map +0 -1
  165. package/dist/server-proxy/index.d.cts +0 -234
  166. package/dist/server-rate-limit/index.cjs +0 -241
  167. package/dist/server-rate-limit/index.cjs.map +0 -1
  168. package/dist/server-rate-limit/index.d.cts +0 -183
  169. package/dist/server-security/index.cjs +0 -316
  170. package/dist/server-security/index.cjs.map +0 -1
  171. package/dist/server-security/index.d.cts +0 -173
  172. package/dist/server-static/index.cjs +0 -170
  173. package/dist/server-static/index.cjs.map +0 -1
  174. package/dist/server-static/index.d.cts +0 -121
  175. package/dist/server-swagger/index.cjs +0 -1021
  176. package/dist/server-swagger/index.cjs.map +0 -1
  177. package/dist/server-swagger/index.d.cts +0 -382
  178. package/dist/sms/index.cjs +0 -221
  179. package/dist/sms/index.cjs.map +0 -1
  180. package/dist/sms/index.d.cts +0 -130
  181. package/dist/thread/index.cjs +0 -350
  182. package/dist/thread/index.cjs.map +0 -1
  183. package/dist/thread/index.d.cts +0 -260
  184. package/dist/topic/index.cjs +0 -282
  185. package/dist/topic/index.cjs.map +0 -1
  186. package/dist/topic/index.d.cts +0 -523
  187. package/dist/topic-redis/index.cjs +0 -71
  188. package/dist/topic-redis/index.cjs.map +0 -1
  189. package/dist/topic-redis/index.d.cts +0 -42
  190. package/dist/vite/index.cjs +0 -1077
  191. package/dist/vite/index.cjs.map +0 -1
  192. package/dist/vite/index.d.cts +0 -542
  193. package/dist/websocket/index.cjs +0 -1117
  194. package/dist/websocket/index.cjs.map +0 -1
  195. package/dist/websocket/index.d.cts +0 -861
@@ -1,1680 +0,0 @@
1
- let alepha = require("alepha");
2
- let alepha_logger = require("alepha/logger");
3
- let node_stream = require("node:stream");
4
- let alepha_datetime = require("alepha/datetime");
5
- let node_stream_web = require("node:stream/web");
6
- let alepha_router = require("alepha/router");
7
- let alepha_cache = require("alepha/cache");
8
- let node_http = require("node:http");
9
- let node_zlib = require("node:zlib");
10
-
11
- //#region src/server/helpers/isMultipart.ts
12
- /**
13
- * Checks if the route has multipart/form-data request body.
14
- */
15
- const isMultipart = (options) => {
16
- if (options.requestBodyType === "multipart/form-data") return true;
17
- if (options.schema?.body && "properties" in options.schema.body) {
18
- const properties = options.schema.body.properties;
19
- for (const key in properties) if (properties[key].format === "binary") return true;
20
- }
21
- return false;
22
- };
23
-
24
- //#endregion
25
- //#region src/server/helpers/ServerReply.ts
26
- /**
27
- * Helper for building server replies.
28
- */
29
- var ServerReply = class {
30
- headers = {};
31
- status;
32
- body;
33
- /**
34
- * Redirect to a given URL with optional status code (default 302).
35
- */
36
- redirect(url, status = 302) {
37
- this.status = status;
38
- this.headers.location = url;
39
- }
40
- /**
41
- * Set the response status code.
42
- */
43
- setStatus(status) {
44
- this.status = status;
45
- return this;
46
- }
47
- /**
48
- * Set a response header.
49
- */
50
- setHeader(name, value) {
51
- this.headers[name.toLowerCase()] = value;
52
- return this;
53
- }
54
- /**
55
- * Set the response body.
56
- */
57
- setBody(body) {
58
- this.body = body;
59
- return this;
60
- }
61
- };
62
-
63
- //#endregion
64
- //#region src/server/errors/HttpError.ts
65
- const isHttpError = (error, status) => {
66
- if (!(!!error && typeof error === "object" && "message" in error && typeof error.message === "string" && "status" in error && typeof error.status === "number")) return false;
67
- if (status) return error.status === status;
68
- return true;
69
- };
70
- var HttpError = class extends alepha.AlephaError {
71
- name = "HttpError";
72
- static is = isHttpError;
73
- static toJSON(error) {
74
- const json = {
75
- error: error.error,
76
- status: error.status,
77
- message: error.message
78
- };
79
- if (error.details) json.details = error.details;
80
- if (error.requestId) json.requestId = error.requestId;
81
- if (error.reason) json.cause = error.reason;
82
- return json;
83
- }
84
- error;
85
- status;
86
- requestId;
87
- details;
88
- reason;
89
- constructor(options, cause) {
90
- super(options.message, { cause });
91
- this.status = options.status ?? 500;
92
- this.details = options.details;
93
- this.requestId = options.requestId;
94
- if (typeof options.cause === "object") this.reason = {
95
- name: options.cause.name,
96
- message: options.cause.message
97
- };
98
- else if (cause instanceof Error) this.reason = {
99
- name: cause.name,
100
- message: cause.message
101
- };
102
- if (this.constructor.name === "HttpError") this.error = options.error ?? errorNameByStatus[this.status] ?? "HttpError";
103
- else this.error = this.constructor.name;
104
- }
105
- };
106
- const errorNameByStatus = {
107
- 400: "BadRequestError",
108
- 401: "UnauthorizedError",
109
- 403: "ForbiddenError",
110
- 404: "NotFoundError",
111
- 405: "MethodNotAllowedError",
112
- 409: "ConflictError",
113
- 410: "GoneError",
114
- 413: "PayloadTooLargeError",
115
- 415: "UnsupportedMediaTypeError",
116
- 429: "TooManyRequestsError",
117
- 500: "InternalServerError",
118
- 501: "NotImplementedError",
119
- 502: "BadGatewayError",
120
- 503: "ServiceUnavailableError",
121
- 504: "GatewayTimeoutError"
122
- };
123
-
124
- //#endregion
125
- //#region src/server/errors/ValidationError.ts
126
- var ValidationError = class extends HttpError {
127
- constructor(message = "Validation has failed", cause) {
128
- super({
129
- message,
130
- status: 400
131
- }, cause);
132
- }
133
- };
134
-
135
- //#endregion
136
- //#region src/server/services/UserAgentParser.ts
137
- /**
138
- * Simple User-Agent parser to detect OS, browser, and device type.
139
- * This parser is not exhaustive and may not cover all edge cases.
140
- *
141
- * Use result for non
142
- */
143
- var UserAgentParser = class {
144
- parse(userAgent = "") {
145
- const ua = userAgent.toLowerCase();
146
- let os = "Windows";
147
- let browser = "Chrome";
148
- let device = "DESKTOP";
149
- if (ua.includes("windows phone")) os = "Windows Phone";
150
- else if (ua.includes("windows")) os = "Windows";
151
- else if (ua.includes("android")) os = "Android";
152
- else if (ua.includes("iphone") || ua.includes("ipad") || ua.includes("ipod") || ua.includes("ios") && !ua.includes("android")) os = "iOS";
153
- else if (ua.includes("mac os") || ua.includes("macos") || ua.includes("macintosh")) os = "MacOS";
154
- else if (ua.includes("cros") || ua.includes("chromeos")) os = "ChromeOS";
155
- else if (ua.includes("ubuntu")) os = "Ubuntu";
156
- else if (ua.includes("freebsd")) os = "FreeBSD";
157
- else if (ua.includes("openbsd")) os = "OpenBSD";
158
- else if (ua.includes("blackberry") || ua.includes("bb10")) os = "BlackBerry";
159
- else if (ua.includes("symbian") || ua.includes("symbos")) os = "Symbian";
160
- else if (ua.includes("linux") || ua.includes("x11")) os = "Linux";
161
- if (ua.includes("yabrowser") || ua.includes("yandex")) browser = "Yandex";
162
- else if (ua.includes("brave")) browser = "Brave";
163
- else if (ua.includes("vivaldi")) browser = "Vivaldi";
164
- else if (ua.includes("samsungbrowser") || ua.includes("samsung")) browser = "Samsung Browser";
165
- else if (ua.includes("ucbrowser") || ua.includes("uc browser")) browser = "UC Browser";
166
- else if (ua.includes("opera") || ua.includes("opr/") || ua.includes("opios")) browser = "Opera";
167
- else if (ua.includes("edg/") || ua.includes("edge") || ua.includes("edgios")) browser = "Edge";
168
- else if (ua.includes("firefox") && !ua.includes("seamonkey")) browser = "Firefox";
169
- else if (ua.includes("trident") || ua.includes("msie")) browser = "Internet Explorer";
170
- else if (ua.includes("safari") && !ua.includes("chrome") && !ua.includes("chromium")) browser = "Safari";
171
- else if (ua.includes("chrome") || ua.includes("chromium") || ua.includes("crios")) browser = "Chrome";
172
- const mobileKeywords = [
173
- "mobile",
174
- "android",
175
- "iphone",
176
- "ipod",
177
- "blackberry",
178
- "windows phone",
179
- "opera mini",
180
- "iemobile",
181
- "mobile safari",
182
- "nokia",
183
- "symbian"
184
- ];
185
- const isTablet = [
186
- "ipad",
187
- "tablet",
188
- "kindle",
189
- "silk",
190
- "gt-p",
191
- "sm-t",
192
- "nexus 7",
193
- "nexus 10"
194
- ].some((keyword) => ua.includes(keyword));
195
- const isMobile = mobileKeywords.some((keyword) => ua.includes(keyword));
196
- if (isTablet) device = "TABLET";
197
- else if (isMobile) device = "MOBILE";
198
- else device = "DESKTOP";
199
- return {
200
- os,
201
- browser,
202
- device
203
- };
204
- }
205
- };
206
-
207
- //#endregion
208
- //#region src/server/services/ServerRequestParser.ts
209
- var ServerRequestParser = class {
210
- alepha = (0, alepha.$inject)(alepha.Alepha);
211
- userAgentParser = (0, alepha.$inject)(UserAgentParser);
212
- createServerRequest(rawRequest) {
213
- const self = this;
214
- return {
215
- method: rawRequest.method,
216
- url: rawRequest.url,
217
- raw: rawRequest.raw,
218
- headers: rawRequest.headers,
219
- query: rawRequest.query,
220
- params: rawRequest.params,
221
- body: null,
222
- metadata: {},
223
- requestId: this.getRequestId(rawRequest) || crypto.randomUUID(),
224
- reply: this.alepha.inject(ServerReply, { lifetime: "transient" }),
225
- get ip() {
226
- return self.getRequestIp(rawRequest);
227
- },
228
- get userAgent() {
229
- return self.getRequestUserAgent(rawRequest);
230
- }
231
- };
232
- }
233
- getRequestId(request) {
234
- return request.headers["x-request-id"];
235
- }
236
- getRequestUserAgent(request) {
237
- return this.userAgentParser.parse(request.headers["user-agent"]);
238
- }
239
- getRequestIp(request) {
240
- const forwardedFor = request.headers["x-forwarded-for"];
241
- if (forwardedFor) return Array.isArray(forwardedFor) ? forwardedFor[0] : forwardedFor.split(",")[0].trim();
242
- const xRealIP = request.headers["x-real-ip"];
243
- if (xRealIP) return Array.isArray(xRealIP) ? xRealIP[0] : xRealIP;
244
- }
245
- };
246
-
247
- //#endregion
248
- //#region src/server/providers/ServerTimingProvider.ts
249
- var ServerTimingProvider = class {
250
- log = (0, alepha_logger.$logger)();
251
- alepha = (0, alepha.$inject)(alepha.Alepha);
252
- options = {
253
- prefix: this.alepha.env.APP_NAME ? `${this.alepha.env.APP_NAME.toLowerCase()}-` : "",
254
- disabled: this.alepha.isProduction()
255
- };
256
- onRequest = (0, alepha.$hook)({
257
- priority: "first",
258
- on: "server:onRequest",
259
- handler: async ({ request }) => {
260
- if (this.options.disabled) return;
261
- request.metadata.timing = {};
262
- request.metadata.timing[this.handlerName] = [Date.now()];
263
- }
264
- });
265
- onResponse = (0, alepha.$hook)({
266
- priority: "last",
267
- on: "server:onResponse",
268
- handler: async ({ request }) => {
269
- if (this.options.disabled) return;
270
- if (request.metadata.timing) {
271
- this.setDuration(this.handlerName, request.metadata.timing);
272
- let timingHeader = "";
273
- for (const [name, [start, duration]] of Object.entries(request.metadata.timing)) {
274
- if (typeof start !== "number" || typeof duration !== "number") {
275
- this.log.warn(`Invalid timing for '${name}': [${start}, ${duration}]`);
276
- continue;
277
- }
278
- const formattedName = this.options.prefix + name.replace(/[^a-zA-Z0-9-]/g, "-");
279
- timingHeader += `${formattedName};dur=${duration}, `;
280
- }
281
- if (request.reply.headers["Server-Timing"]) request.reply.headers["Server-Timing"] += `, ${timingHeader}`;
282
- else request.reply.headers["Server-Timing"] = timingHeader;
283
- }
284
- }
285
- });
286
- get handlerName() {
287
- return `request`;
288
- }
289
- beginTiming(name) {
290
- if (this.options.disabled) return;
291
- const request = this.alepha.context.get("request");
292
- if (!request) return;
293
- request.metadata ??= {};
294
- request.metadata.timing ??= {};
295
- request.metadata.timing[name] = [Date.now()];
296
- }
297
- endTiming(name) {
298
- if (this.options.disabled) return;
299
- const request = this.alepha.context.get("request");
300
- if (!request) return;
301
- if (!request.metadata?.timing || !(name in request.metadata.timing)) {
302
- this.log.warn(`No timing found for '${name}'.`);
303
- return;
304
- }
305
- const start = request.metadata.timing[name]?.[0];
306
- if (typeof start !== "number") {
307
- this.log.warn(`Invalid timing start for '${name}': ${start}`);
308
- return;
309
- }
310
- this.setDuration(name, request.metadata.timing);
311
- }
312
- setDuration(name, timing) {
313
- timing[name] = [timing[name][0], Date.now() - timing[name][0]];
314
- }
315
- };
316
-
317
- //#endregion
318
- //#region src/server/providers/ServerRouterProvider.ts
319
- /**
320
- * Main router for all routes on the server side.
321
- *
322
- * - $route => generic route
323
- * - $action => action route (for API calls)
324
- * - $page => React route (for SSR)
325
- */
326
- var ServerRouterProvider = class extends alepha_router.RouterProvider {
327
- log = (0, alepha_logger.$logger)();
328
- alepha = (0, alepha.$inject)(alepha.Alepha);
329
- routes = [];
330
- serverTimingProvider = (0, alepha.$inject)(ServerTimingProvider);
331
- serverRequestParser = (0, alepha.$inject)(ServerRequestParser);
332
- /**
333
- * Get all registered routes, optionally filtered by a pattern.
334
- *
335
- * Pattern accept simple wildcard '*' at the end.
336
- * Example: '/api/*' will match all routes starting with '/api/' but '/api/' will match only that exact route.
337
- */
338
- getRoutes(pattern) {
339
- if (pattern) if (pattern.endsWith("*")) {
340
- const basePattern = pattern.slice(0, -1);
341
- return this.routes.filter((route) => route.path.startsWith(basePattern));
342
- } else return this.routes.filter((route) => route.path === pattern);
343
- return this.routes;
344
- }
345
- createRoute(route) {
346
- route.method ??= "GET";
347
- route.method = route.method.toUpperCase();
348
- this.routes.push(route);
349
- const path = `/${route.method}/${route.path}`.replace(/\/+/g, "/");
350
- const responseKind = this.getResponseType(route.schema);
351
- this.log.trace(`Create route ${path}`);
352
- this.push({
353
- path,
354
- handler: (rawRequest) => {
355
- const request = this.serverRequestParser.createServerRequest(rawRequest);
356
- return this.alepha.context.run(() => this.processRequest(request, route, responseKind), { context: this.getContextId(rawRequest.headers) });
357
- }
358
- });
359
- }
360
- getContextId(headers) {
361
- const contextId = headers["x-request-id"] || headers["x-correlation-id"];
362
- if ((0, alepha.isUUID)(contextId)) return contextId;
363
- return crypto.randomUUID();
364
- }
365
- async processRequest(request, route, responseKind) {
366
- await this.runRouteHandler(route, request, responseKind).catch((error) => {
367
- return this.errorHandler(route, request, error);
368
- });
369
- await this.alepha.events.emit("server:onSend", {
370
- request,
371
- route
372
- }, { catch: true });
373
- const response = {
374
- status: request.reply.status ?? (request.reply.body ? 200 : 204),
375
- headers: request.reply.headers,
376
- body: request.reply.body
377
- };
378
- await this.alepha.events.emit("server:onResponse", {
379
- request,
380
- route,
381
- response
382
- }, { catch: true });
383
- return response;
384
- }
385
- async runRouteHandler(route, request, responseKind) {
386
- await this.alepha.events.emit("server:onRequest", {
387
- request,
388
- route
389
- }, { log: false });
390
- if (request.reply.body || request.reply.status && request.reply.status >= 200) return;
391
- this.alepha.context.set("request", request);
392
- this.serverTimingProvider.beginTiming("validateRequest");
393
- try {
394
- this.validateRequest(route, request);
395
- } finally {
396
- this.serverTimingProvider.endTiming("validateRequest");
397
- }
398
- this.serverTimingProvider.beginTiming("runHandler");
399
- try {
400
- const result = await route.handler(request);
401
- if (result) request.reply.body = result;
402
- } finally {
403
- this.serverTimingProvider.endTiming("runHandler");
404
- }
405
- this.serverTimingProvider.beginTiming("serializeResponse");
406
- try {
407
- this.serializeResponse(route, request.reply, responseKind);
408
- } finally {
409
- this.serverTimingProvider.endTiming("serializeResponse");
410
- }
411
- }
412
- serializeResponse(route, reply, responseKind) {
413
- if (responseKind === "json" && route.schema?.response) {
414
- reply.headers["content-type"] = "application/json";
415
- reply.body = this.alepha.codec.encode(route.schema.response, reply.body, { as: "string" });
416
- return;
417
- }
418
- if (responseKind === "file") {
419
- if (!(0, alepha.isFileLike)(reply.body)) throw new HttpError({
420
- status: 500,
421
- message: "Invalid response body - not a file"
422
- });
423
- reply.headers["content-type"] = reply.body.type;
424
- reply.headers["content-disposition"] = `attachment; filename="${reply.body.name.replaceAll("\"", "")}"`;
425
- reply.body = reply.body.stream();
426
- return;
427
- }
428
- if (responseKind === "text") {
429
- reply.body = String(reply.body);
430
- if (reply.body.startsWith("<!DOCTYPE html>")) reply.headers["content-type"] ??= "text/html; charset=UTF-8";
431
- else reply.headers["content-type"] ??= "text/plain";
432
- return;
433
- }
434
- if (reply.body == null || responseKind === "void") {
435
- delete reply.headers["content-type"];
436
- reply.body = void 0;
437
- return;
438
- }
439
- if (Buffer.isBuffer(reply.body)) {
440
- reply.headers["content-type"] ??= "application/octet-stream";
441
- return;
442
- }
443
- if (reply.body instanceof node_stream_web.ReadableStream || reply.body instanceof node_stream.Readable) {
444
- reply.headers["content-type"] ??= "application/octet-stream";
445
- return;
446
- }
447
- reply.headers["content-type"] ??= "text/plain";
448
- reply.body = String(reply.body);
449
- }
450
- getResponseType(schema) {
451
- if (schema?.response) {
452
- if (alepha.t.schema.isObject(schema.response) || alepha.t.schema.isRecord(schema.response) || alepha.t.schema.isArray(schema.response)) return "json";
453
- if (alepha.t.schema.isString(schema.response) || alepha.t.schema.isInteger(schema.response) || alepha.t.schema.isNumber(schema.response) || alepha.t.schema.isBoolean(schema.response)) return "text";
454
- if ((0, alepha.isTypeFile)(schema.response)) return "file";
455
- if (alepha.t.schema.isVoid(schema.response)) return "void";
456
- }
457
- return "any";
458
- }
459
- async errorHandler(route, request, error) {
460
- request.reply.body = null;
461
- await this.alepha.events.emit("server:onError", {
462
- request,
463
- route,
464
- error
465
- }, { log: false });
466
- if (!request.reply.body && !request.reply.status) if (error instanceof HttpError) {
467
- request.reply.status = error.status;
468
- request.reply.headers["content-type"] = "application/json";
469
- request.reply.body = JSON.stringify({
470
- ...HttpError.toJSON(error),
471
- requestId: request.requestId
472
- });
473
- } else {
474
- if ("status" in error && typeof error.status === "number" && !!errorNameByStatus[error.status]) {
475
- request.reply.status = error.status;
476
- request.reply.headers["content-type"] = "application/json";
477
- request.reply.body = JSON.stringify({
478
- status: error.status,
479
- error: errorNameByStatus[error.status],
480
- message: error.message,
481
- requestId: request.requestId
482
- });
483
- return;
484
- }
485
- request.reply.status = 500;
486
- request.reply.headers["content-type"] = "application/json";
487
- request.reply.body = JSON.stringify({
488
- status: 500,
489
- error: "InternalServerError",
490
- message: error.message,
491
- requestId: request.requestId
492
- });
493
- }
494
- }
495
- validateRequest(route, request) {
496
- if (route.schema?.params) try {
497
- request.params = this.alepha.codec.validate(route.schema.params, request.params);
498
- } catch (error) {
499
- throw new ValidationError("Invalid request params", error);
500
- }
501
- if (route.schema?.query) try {
502
- const query = {};
503
- for (const key in route.schema.query.properties) if (request.query[key] != null) query[key] = this.alepha.codec.decode(route.schema.query.properties[key], request.query[key]);
504
- request.query = query;
505
- } catch (error) {
506
- throw new ValidationError("Invalid request query", error);
507
- }
508
- if (route.schema?.headers) try {
509
- request.headers = this.alepha.codec.validate(route.schema.headers, request.headers);
510
- } catch (error) {
511
- throw new ValidationError("Invalid request header", error);
512
- }
513
- if (route.schema?.body) try {
514
- request.body = this.alepha.codec.decode(route.schema.body, request.body);
515
- } catch (error) {
516
- throw new ValidationError("Invalid request body", error);
517
- }
518
- }
519
- };
520
-
521
- //#endregion
522
- //#region src/server/providers/ServerProvider.ts
523
- /**
524
- * Base server provider to handle incoming requests and route them.
525
- *
526
- * This is the default implementation for serverless environments.
527
- *
528
- * ServerProvider supports both Node.js HTTP requests and Web (Fetch API) requests.
529
- */
530
- var ServerProvider = class {
531
- log = (0, alepha_logger.$logger)();
532
- alepha = (0, alepha.$inject)(alepha.Alepha);
533
- dateTimeProvider = (0, alepha.$inject)(alepha_datetime.DateTimeProvider);
534
- router = (0, alepha.$inject)(ServerRouterProvider);
535
- internalServerErrorMessage = "Internal Server Error";
536
- get hostname() {
537
- return "";
538
- }
539
- /**
540
- * When a Node.js HTTP request is received from outside. (Vercel, AWS Lambda, etc.)
541
- */
542
- onNodeRequest = (0, alepha.$hook)({
543
- on: "node:request",
544
- handler: (ev) => this.handleNodeRequest(ev)
545
- });
546
- /**
547
- * When a Web (Fetch API) request is received from outside. (Netlify, Cloudflare Workers, etc.)
548
- */
549
- onWebRequest = (0, alepha.$hook)({
550
- on: "web:request",
551
- handler: (ev) => {
552
- return this.handleWebRequest(ev);
553
- }
554
- });
555
- /**
556
- * Handle Node.js HTTP request event.
557
- *
558
- * Technically, we just convert Node.js request to Web Standard Request.
559
- */
560
- async handleNodeRequest(nodeRequestEvent) {
561
- const { req, res } = nodeRequestEvent;
562
- const { route, params } = this.router.match(`/${req.method}${req.url}`);
563
- if (this.isViteNotFound(req.url, route, params)) return;
564
- if (!route) {
565
- res.writeHead(404, { "content-type": "text/plain" });
566
- res.end("Not Found");
567
- return;
568
- }
569
- const headers = req.headers ?? {};
570
- const proto = headers["x-forwarded-proto"] === "https" ? "https" : "http";
571
- const url = new URL(`${proto}://${headers.host}${req.url}`);
572
- const query = Object.fromEntries(url.searchParams.entries());
573
- const request = {
574
- method: req.method?.toUpperCase() ?? "GET",
575
- url,
576
- headers,
577
- params: params ?? {},
578
- query,
579
- raw: { node: nodeRequestEvent }
580
- };
581
- const response = await route.handler(request).catch(() => {
582
- return {
583
- status: 500,
584
- headers: { "content-type": "text/plain" },
585
- body: this.internalServerErrorMessage
586
- };
587
- });
588
- if (!response.body) {
589
- res.writeHead(response.status, response.headers).end();
590
- return;
591
- }
592
- if (typeof response.body === "string" || Buffer.isBuffer(response.body)) {
593
- res.writeHead(response.status, response.headers).end(response.body);
594
- return;
595
- }
596
- if (response.body instanceof node_stream.Readable) {
597
- res.writeHead(response.status, response.headers);
598
- response.body.pipe(res);
599
- return;
600
- }
601
- if (response.body instanceof ReadableStream) {
602
- res.writeHead(response.status, response.headers);
603
- try {
604
- for await (const chunk of response.body) res.write(chunk);
605
- } catch (error) {
606
- this.log.error("Error piping proxy response stream", error);
607
- } finally {
608
- res.end();
609
- }
610
- return;
611
- }
612
- this.log.error("Unknown response body type:", typeof response.body);
613
- res.writeHead(500, { "content-type": "text/plain" });
614
- res.end(this.internalServerErrorMessage);
615
- }
616
- /**
617
- * Handle Web (Fetch API) request event.
618
- */
619
- async handleWebRequest(ev) {
620
- const req = ev.req;
621
- const url = new URL(req.url);
622
- const { route, params } = this.router.match(`/${req.method}${url.pathname}`);
623
- if (this.isViteNotFound(req.url, route, params)) return;
624
- if (!route) {
625
- ev.res = new Response("Not Found", {
626
- status: 404,
627
- headers: { "content-type": "text/plain" }
628
- });
629
- return;
630
- }
631
- const headers = {};
632
- req.headers.forEach((value, key) => {
633
- headers[key] = value;
634
- });
635
- const query = Object.fromEntries(url.searchParams.entries());
636
- const request = {
637
- method: req.method.toUpperCase() ?? "GET",
638
- url,
639
- headers,
640
- params: params || {},
641
- query,
642
- raw: { web: ev }
643
- };
644
- const response = await route.handler(request).catch(() => {
645
- return {
646
- status: 500,
647
- headers: { "content-type": "text/plain" },
648
- body: this.internalServerErrorMessage
649
- };
650
- });
651
- if (!response.body) {
652
- ev.res = new Response(null, {
653
- status: response.status,
654
- headers: response.headers
655
- });
656
- return;
657
- }
658
- if (typeof response.body === "string") {
659
- ev.res = new Response(response.body, {
660
- status: response.status,
661
- headers: response.headers
662
- });
663
- return;
664
- }
665
- if (Buffer.isBuffer(response.body)) {
666
- ev.res = new Response(response.body.buffer, {
667
- status: response.status,
668
- headers: response.headers
669
- });
670
- return;
671
- }
672
- if (response.body instanceof node_stream.Readable) {
673
- ev.res = new Response(node_stream.Readable.toWeb(response.body), {
674
- status: response.status,
675
- headers: response.headers
676
- });
677
- return;
678
- }
679
- if (response.body instanceof ReadableStream) {
680
- ev.res = new Response(response.body, {
681
- status: response.status,
682
- headers: response.headers
683
- });
684
- return;
685
- }
686
- this.log.error(`Unknown response body type: ${typeof response.body}`);
687
- ev.res = new Response(this.internalServerErrorMessage, {
688
- status: 500,
689
- headers: { "content-type": "text/plain" }
690
- });
691
- }
692
- /**
693
- * Helper for Vite development mode to let Vite handle (or not) 404.
694
- */
695
- isViteNotFound(url, route, params) {
696
- if (this.alepha.isViteDev()) {
697
- if (!route) return true;
698
- url = url?.split("?")[0];
699
- if (!!params?.["*"] && `/${params?.["*"]}` === url) return true;
700
- }
701
- return false;
702
- }
703
- };
704
-
705
- //#endregion
706
- //#region src/server/schemas/errorSchema.ts
707
- const errorSchema = alepha.t.object({
708
- error: alepha.t.text({ description: "HTTP error name" }),
709
- status: alepha.t.integer({ description: "HTTP status code" }),
710
- message: alepha.t.text({
711
- description: "Short text which describe the error",
712
- size: "rich"
713
- }),
714
- details: alepha.t.optional(alepha.t.text({
715
- description: "Detailed description of the error",
716
- size: "rich"
717
- })),
718
- requestId: alepha.t.optional(alepha.t.text()),
719
- cause: alepha.t.optional(alepha.t.object({
720
- name: alepha.t.text(),
721
- message: alepha.t.text({
722
- description: "Cause Error message",
723
- size: "rich"
724
- })
725
- }))
726
- }, {
727
- title: "HttpError",
728
- description: "Generic response after a failed operation"
729
- });
730
-
731
- //#endregion
732
- //#region src/server/services/HttpClient.ts
733
- var HttpClient = class {
734
- log = (0, alepha_logger.$logger)();
735
- alepha = (0, alepha.$inject)(alepha.Alepha);
736
- cache = (0, alepha_cache.$cache)();
737
- pendingRequests = {};
738
- async fetchAction(args) {
739
- const route = args.action;
740
- const options = args.options ?? {};
741
- const config = args.config ?? {};
742
- const host = args.host ?? "";
743
- const request = { ...options.request };
744
- const method = route.method;
745
- const headers = {};
746
- const url = this.url(host, route, config);
747
- await this.alepha.events.emit("client:onRequest", {
748
- route,
749
- config,
750
- options,
751
- headers,
752
- request
753
- });
754
- request.method ??= method;
755
- await this.body(request, headers, route, config);
756
- request.headers = {
757
- ...config.headers,
758
- ...Object.fromEntries(new Headers(request.headers).entries()),
759
- ...headers
760
- };
761
- return await this.fetch(url, {
762
- ...request,
763
- schema: route.schema,
764
- ...options
765
- });
766
- }
767
- async fetch(url, request = {}) {
768
- const options = {
769
- cache: request.localCache,
770
- schema: request.schema?.response,
771
- key: request.key
772
- };
773
- request.method ??= "GET";
774
- this.log.trace("Request", {
775
- url,
776
- method: request.method,
777
- body: request.body,
778
- headers: request.headers,
779
- options
780
- });
781
- const cached = await this.cache.get(url);
782
- if (cached && request.method === "GET") if (cached.etag) {
783
- request.headers = new Headers(request.headers);
784
- if (!request.headers.has("if-none-match")) request.headers.set("if-none-match", cached.etag);
785
- } else return {
786
- data: cached.data,
787
- status: 200,
788
- statusText: "OK",
789
- headers: new Headers()
790
- };
791
- await this.alepha.events.emit("client:beforeFetch", {
792
- url,
793
- options,
794
- request
795
- });
796
- const key = options.key ?? JSON.stringify({
797
- url,
798
- method: request.method,
799
- body: request.body
800
- });
801
- const existing = this.pendingRequests[key];
802
- if (existing) {
803
- this.log.info("Request already pending", key);
804
- return existing;
805
- }
806
- this.pendingRequests[key] = fetch(url, request).then(async (response) => {
807
- this.log.debug("Response", {
808
- url,
809
- status: response.status
810
- });
811
- const fetchResponse = {
812
- data: await this.responseData(response, options),
813
- status: response.status,
814
- statusText: response.statusText,
815
- headers: response.headers,
816
- raw: response
817
- };
818
- if (request.method === "GET") {
819
- if (options.cache) await this.cache.set(url, { data: fetchResponse.data }, typeof options.cache === "boolean" ? void 0 : options.cache);
820
- else if (!this.alepha.isBrowser()) {
821
- const etag = response.headers.get("etag") ?? void 0;
822
- if (etag) await this.cache.set(url, {
823
- data: fetchResponse.data,
824
- etag
825
- });
826
- }
827
- }
828
- return fetchResponse;
829
- }).finally(() => {
830
- delete this.pendingRequests[key];
831
- });
832
- return this.pendingRequests[key];
833
- }
834
- url(host, action, args) {
835
- let url = host;
836
- if (action.prefix) url += action.prefix;
837
- url += action.path;
838
- url = this.pathVariables(url, action, args);
839
- url = this.queryParams(url, action, args);
840
- return url;
841
- }
842
- async body(init, headers, action, args = {}) {
843
- if (typeof init.headers === "object" && "content-type" in init.headers && init.headers["content-type"] === "multipart/form-data" || isMultipart(action)) {
844
- if (typeof init.headers === "object" && "content-type" in init.headers) delete init.headers["content-type"];
845
- const formData = new FormData();
846
- for (const [key, value] of Object.entries(args.body ?? {})) {
847
- if (typeof value === "string") {
848
- formData.append(key, value);
849
- continue;
850
- }
851
- if (value instanceof Blob) {
852
- formData.append(key, value);
853
- continue;
854
- }
855
- if ((0, alepha.isFileLike)(value)) formData.append(key, new File([await value.arrayBuffer()], value.name, { type: value.type }));
856
- }
857
- init.body = formData;
858
- return;
859
- }
860
- if (!init.body && action.schema?.body) {
861
- headers["content-type"] = "application/json";
862
- init.body = this.alepha.codec.encode(action.schema?.body, args.body, { as: "string" });
863
- }
864
- }
865
- async responseData(response, options) {
866
- if (response.status === 304) {
867
- let cacheKey = response.url;
868
- if (typeof window !== "undefined") cacheKey = cacheKey.replace(window.location.origin, "");
869
- const cached = await this.cache.get(cacheKey);
870
- if (cached) return cached.data;
871
- return "";
872
- }
873
- if (response.status === 204) return;
874
- if (this.isMaybeFile(response)) return this.createFileLike(response);
875
- if (response.headers.get("Content-Type")?.startsWith("text/")) return await response.text();
876
- if (response.headers.get("Content-Type") === "application/json") {
877
- const json = await response.json();
878
- if (response.status >= 400) {
879
- const error = new HttpError(this.alepha.codec.decode(errorSchema, json));
880
- await this.alepha.events.emit("client:onError", { error });
881
- throw error;
882
- }
883
- if (options.schema) return this.alepha.codec.decode(options.schema, json);
884
- return json;
885
- }
886
- if (response.status >= 400) {
887
- const error = new HttpError({
888
- status: response.status,
889
- message: `An error occurred while fetching the resource. (${response.statusText})`
890
- });
891
- await this.alepha.events.emit("client:onError", { error });
892
- throw error;
893
- }
894
- return response;
895
- }
896
- isMaybeFile(response) {
897
- const contentType = response.headers.get("Content-Type");
898
- if (!contentType) return false;
899
- if (response.headers.get("Content-Disposition")?.includes("attachment")) return true;
900
- return contentType.startsWith("application/octet-stream") || contentType.startsWith("application/pdf") || contentType.startsWith("application/zip") || contentType.startsWith("image/") || contentType.startsWith("video/") || contentType.startsWith("audio/");
901
- }
902
- createFileLike(response, defaultFileName = "") {
903
- const match = (response.headers.get("Content-Disposition") ?? "").match(/filename="(.+)"/);
904
- return {
905
- name: match?.[1] ? match[1] : defaultFileName,
906
- type: response.headers.get("Content-Type") ?? "application/octet-stream",
907
- size: Number(response.headers.get("Content-Length") ?? 0),
908
- lastModified: Date.now(),
909
- stream: () => {
910
- throw new Error("Not implemented");
911
- },
912
- arrayBuffer: async () => {
913
- return await response.arrayBuffer();
914
- },
915
- text: async () => {
916
- return await response.text();
917
- }
918
- };
919
- }
920
- pathVariables(url, action, args = {}) {
921
- if (typeof args.params === "object") {
922
- const params = action.schema?.params ? this.alepha.codec.decode(action.schema.params, args.params) : args.params;
923
- for (const key of Object.keys(params)) {
924
- url = url.replace(`:${key}`, params[key]);
925
- url = url.replace(`{${key}}`, params[key]);
926
- }
927
- }
928
- return url;
929
- }
930
- queryParams(url, action, args = {}) {
931
- if (typeof args.query === "object") {
932
- const query = action.schema?.query ? this.alepha.codec.decode(action.schema.query, args.query ?? {}) : args.query;
933
- for (const key of Object.keys(query)) {
934
- if (query[key] === void 0) delete query[key];
935
- if (typeof query[key] === "object") query[key] = JSON.stringify(query[key]);
936
- }
937
- return `${url}?${new URLSearchParams(query).toString()}`;
938
- }
939
- return url;
940
- }
941
- };
942
-
943
- //#endregion
944
- //#region src/server/descriptors/$action.ts
945
- /**
946
- * Creates a server action descriptor for defining type-safe HTTP endpoints.
947
- *
948
- * Server actions are the core building blocks for REST APIs in Alepha, providing declarative
949
- * HTTP endpoints with full type safety, automatic validation, and OpenAPI documentation.
950
- *
951
- * **Key Features**
952
- * - Full TypeScript inference for request/response types
953
- * - Automatic schema validation using TypeBox
954
- * - Convention-based URL generation with customizable paths
955
- * - Direct invocation (`run()`) or HTTP requests (`fetch()`)
956
- * - Built-in authentication and authorization support
957
- * - Automatic content-type handling (JSON, form-data, plain text)
958
- *
959
- * **URL Generation**
960
- * Actions are prefixed with `/api` by default (configurable via `SERVER_API_PREFIX`).
961
- * HTTP method defaults to GET, or POST if body schema is provided.
962
- *
963
- * **Common Use Cases**
964
- * - CRUD operations with type safety
965
- * - File upload and download endpoints
966
- * - Microservice communication
967
- *
968
- * @example
969
- * ```ts
970
- * class UserController {
971
- * getUsers = $action({
972
- * path: "/users",
973
- * schema: {
974
- * query: t.object({
975
- * page: t.optional(t.number({ default: 1 })),
976
- * limit: t.optional(t.number({ default: 10 }))
977
- * }),
978
- * response: t.object({
979
- * users: t.array(t.object({
980
- * id: t.text(),
981
- * name: t.text(),
982
- * email: t.text()
983
- * })),
984
- * total: t.number()
985
- * })
986
- * },
987
- * handler: async ({ query }) => {
988
- * const users = await this.userService.findUsers(query);
989
- * return { users: users.items, total: users.total };
990
- * }
991
- * });
992
- *
993
- * createUser = $action({
994
- * method: "POST",
995
- * path: "/users",
996
- * schema: {
997
- * body: t.object({
998
- * name: t.text(),
999
- * email: t.text({ format: "email" })
1000
- * }),
1001
- * response: t.object({ id: t.text(), name: t.text() })
1002
- * },
1003
- * handler: async ({ body }) => {
1004
- * return await this.userService.create(body);
1005
- * }
1006
- * });
1007
- * }
1008
- * ```
1009
- */
1010
- const $action = (options) => {
1011
- const instance = (0, alepha.createDescriptor)(ActionDescriptor, options);
1012
- const fn = (config, options$1) => {
1013
- return instance.run(config, options$1);
1014
- };
1015
- Object.defineProperty(fn, "name", { get() {
1016
- return instance.options.name || instance.config.propertyKey;
1017
- } });
1018
- return Object.setPrototypeOf(fn, instance);
1019
- };
1020
- const envSchema$3 = alepha.t.object({ SERVER_API_PREFIX: alepha.t.text({
1021
- description: "Prefix for all API routes (e.g. $action).",
1022
- default: "/api"
1023
- }) });
1024
- var ActionDescriptor = class extends alepha.Descriptor {
1025
- log = (0, alepha_logger.$logger)();
1026
- env = (0, alepha.$env)(envSchema$3);
1027
- httpClient = (0, alepha.$inject)(HttpClient);
1028
- serverProvider = (0, alepha.$inject)(ServerProvider);
1029
- serverRouterProvider = (0, alepha.$inject)(ServerRouterProvider);
1030
- onInit() {
1031
- if (this.options.disabled) {
1032
- this.log.debug(`Action '${this.name}' is disabled. It won't be available in the API.`);
1033
- return;
1034
- }
1035
- this.serverRouterProvider.createRoute(this.route);
1036
- }
1037
- get prefix() {
1038
- return this.env.SERVER_API_PREFIX;
1039
- }
1040
- get route() {
1041
- return {
1042
- ...this.options,
1043
- method: this.method,
1044
- path: `${this.prefix}${this.path}`
1045
- };
1046
- }
1047
- /**
1048
- * Returns the name of the action.
1049
- */
1050
- get name() {
1051
- return this.options.name || this.config.propertyKey;
1052
- }
1053
- /**
1054
- * Returns the group of the action. (e.g. "orders", "admin", etc.)
1055
- */
1056
- get group() {
1057
- return this.options.group || this.config.service.name;
1058
- }
1059
- /**
1060
- * Returns the HTTP method of the action.
1061
- */
1062
- get method() {
1063
- return this.options.method || (this.options.schema?.body ? "POST" : "GET");
1064
- }
1065
- /**
1066
- * Returns the path of the action.
1067
- *
1068
- * Path is prefixed by `/api` by default.
1069
- */
1070
- get path() {
1071
- if (this.options.path) return this.options.path;
1072
- let path = `/${this.name}`;
1073
- if (this.options.schema?.params) for (const [key] of Object.entries(this.options.schema.params.properties)) path += `/:${key}`;
1074
- return path;
1075
- }
1076
- get schema() {
1077
- return this.options.schema;
1078
- }
1079
- getBodyContentType() {
1080
- if (this.options.schema?.body) {
1081
- if (isMultipart(this.options)) return "multipart/form-data";
1082
- if (alepha.t.schema.isString(this.options.schema.body)) return "text/plain";
1083
- if (alepha.t.schema.isObject(this.options.schema.body) || alepha.t.schema.isArray(this.options.schema.body) || alepha.t.schema.isRecord(this.options.schema.body)) return "application/json";
1084
- }
1085
- }
1086
- /**
1087
- * Call the action handler directly.
1088
- * There is no HTTP layer involved.
1089
- */
1090
- async run(config, options = {}) {
1091
- const handler = this.options.handler;
1092
- const { body, params = {}, query = {}, headers = {} } = config ?? {};
1093
- const reply = new ServerReply();
1094
- const serverActionRequest = {
1095
- method: this.method,
1096
- url: new URL(`http://localhost${this.path ?? ""}`),
1097
- body,
1098
- params,
1099
- query,
1100
- headers,
1101
- reply,
1102
- metadata: {}
1103
- };
1104
- await this.alepha.events.emit("action:onRequest", {
1105
- action: this,
1106
- request: serverActionRequest,
1107
- options
1108
- });
1109
- if (serverActionRequest.reply?.body) return serverActionRequest.reply.body;
1110
- if (serverActionRequest.query && this.options.schema?.query) serverActionRequest.query = this.alepha.codec.encode(this.options.schema.query, serverActionRequest.query);
1111
- if (serverActionRequest.headers && this.options.schema?.headers) serverActionRequest.headers = this.alepha.codec.encode(this.options.schema.headers, serverActionRequest.headers);
1112
- if (serverActionRequest.body && this.options.schema?.body) serverActionRequest.body = this.alepha.codec.encode(this.options.schema.body, serverActionRequest.body);
1113
- if (serverActionRequest.params && this.options.schema?.params) serverActionRequest.params = this.alepha.codec.encode(this.options.schema.params, serverActionRequest.params);
1114
- this.serverRouterProvider.validateRequest(this.options, serverActionRequest);
1115
- let response = await handler(serverActionRequest);
1116
- if (this.options.schema?.response && !(0, alepha.isTypeFile)(this.options.schema.response)) response = this.alepha.codec.validate(this.options.schema.response, response);
1117
- await this.alepha.events.emit("action:onResponse", {
1118
- action: this,
1119
- request: serverActionRequest,
1120
- options,
1121
- response
1122
- });
1123
- return response;
1124
- }
1125
- /**
1126
- * Works like `run`, but always fetches (http request) the route.
1127
- */
1128
- fetch(config, options) {
1129
- return this.httpClient.fetchAction({
1130
- host: this.serverProvider.hostname,
1131
- action: this,
1132
- config,
1133
- options
1134
- });
1135
- }
1136
- };
1137
- $action[alepha.KIND] = ActionDescriptor;
1138
-
1139
- //#endregion
1140
- //#region src/server/descriptors/$route.ts
1141
- /**
1142
- * Create a basic endpoint.
1143
- *
1144
- * It's a low level descriptor. You probably want to use `$action` instead.
1145
- *
1146
- * @see {@link $action}
1147
- * @see {@link $page}
1148
- */
1149
- const $route = (options) => {
1150
- return (0, alepha.createDescriptor)(RouteDescriptor, options);
1151
- };
1152
- var RouteDescriptor = class extends alepha.Descriptor {
1153
- serverRouterProvider = (0, alepha.$inject)(ServerRouterProvider);
1154
- onInit() {
1155
- this.serverRouterProvider.createRoute(this.options);
1156
- }
1157
- };
1158
- $route[alepha.KIND] = RouteDescriptor;
1159
-
1160
- //#endregion
1161
- //#region src/server/providers/BunHttpServerProvider.ts
1162
- const envSchema$2 = alepha.t.object({
1163
- SERVER_PORT: alepha.t.integer({
1164
- default: 3e3,
1165
- min: 0,
1166
- max: 65535,
1167
- description: "Set 0 to listen on a random port."
1168
- }),
1169
- SERVER_HOST: alepha.t.text({
1170
- default: "localhost",
1171
- description: "Set 0.0.0.0 to listen on all interfaces."
1172
- })
1173
- });
1174
- var BunHttpServerProvider = class extends ServerProvider {
1175
- alepha = (0, alepha.$inject)(alepha.Alepha);
1176
- dateTimeProvider = (0, alepha.$inject)(alepha_datetime.DateTimeProvider);
1177
- log = (0, alepha_logger.$logger)();
1178
- env = (0, alepha.$env)(envSchema$2);
1179
- router = (0, alepha.$inject)(ServerRouterProvider);
1180
- bunServer;
1181
- get hostname() {
1182
- if (this.bunServer) return `http://${this.bunServer.hostname}:${this.bunServer.port}`;
1183
- return `http://${this.env.SERVER_HOST}:${this.env.SERVER_PORT}`;
1184
- }
1185
- start = (0, alepha.$hook)({
1186
- on: "start",
1187
- handler: async () => {
1188
- await this.listen();
1189
- }
1190
- });
1191
- stop = (0, alepha.$hook)({
1192
- on: "stop",
1193
- handler: async () => {
1194
- if (this.alepha.isProduction()) {
1195
- await this.close();
1196
- return;
1197
- }
1198
- this.close().catch(() => {});
1199
- }
1200
- });
1201
- async listen() {
1202
- let port = this.env.SERVER_PORT;
1203
- if (this.alepha.isTest() && port === 3e3) port = 0;
1204
- try {
1205
- this.bunServer = Bun.serve({
1206
- port,
1207
- hostname: this.env.SERVER_HOST,
1208
- fetch: async (request) => {
1209
- this.log.trace(`Incoming Bun request -> ${request.url}`);
1210
- const webRequestEvent = {
1211
- req: request,
1212
- res: void 0
1213
- };
1214
- try {
1215
- await this.handleWebRequest(webRequestEvent);
1216
- if (!webRequestEvent.res) return new Response("Internal Server Error", {
1217
- status: 500,
1218
- headers: { "content-type": "text/plain" }
1219
- });
1220
- return webRequestEvent.res;
1221
- } catch (err) {
1222
- this.log.error("Error handling request", err);
1223
- return new Response("Internal Server Error", {
1224
- status: 500,
1225
- headers: { "content-type": "text/plain" }
1226
- });
1227
- }
1228
- },
1229
- error: (error) => {
1230
- this.log.error("Bun server error", error);
1231
- return new Response("Internal Server Error", {
1232
- status: 500,
1233
- headers: { "content-type": "text/plain" }
1234
- });
1235
- }
1236
- });
1237
- this.log.info(`Server listening on ${this.hostname}`);
1238
- } catch (err) {
1239
- this.log.error("Failed to start Bun server", err);
1240
- throw err;
1241
- }
1242
- }
1243
- async close() {
1244
- if (!this.bunServer) return;
1245
- try {
1246
- const stopPromise = this.bunServer.stop();
1247
- await Promise.race([this.dateTimeProvider.wait(1e4), stopPromise]);
1248
- this.bunServer = void 0;
1249
- this.log.info("Server closed");
1250
- } catch (err) {
1251
- this.log.error("Error closing Bun server", err);
1252
- throw err;
1253
- }
1254
- }
1255
- };
1256
-
1257
- //#endregion
1258
- //#region src/server/providers/NodeHttpServerProvider.ts
1259
- const envSchema$1 = alepha.t.object({
1260
- SERVER_PORT: alepha.t.integer({
1261
- default: 3e3,
1262
- min: 0,
1263
- max: 65535,
1264
- description: "Set 0 to listen on a random port."
1265
- }),
1266
- SERVER_HOST: alepha.t.text({
1267
- default: "localhost",
1268
- description: "Set 0.0.0.0 to listen on all interfaces."
1269
- })
1270
- });
1271
- var NodeHttpServerProvider = class extends ServerProvider {
1272
- alepha = (0, alepha.$inject)(alepha.Alepha);
1273
- dateTimeProvider = (0, alepha.$inject)(alepha_datetime.DateTimeProvider);
1274
- log = (0, alepha_logger.$logger)();
1275
- env = (0, alepha.$env)(envSchema$1);
1276
- router = (0, alepha.$inject)(ServerRouterProvider);
1277
- get hostname() {
1278
- if (this.server.listening) {
1279
- const address = this.server.address();
1280
- if (typeof address === "object" && address !== null) return `http://${this.env.SERVER_HOST}:${address.port}`;
1281
- }
1282
- return `http://${this.env.SERVER_HOST}:${this.env.SERVER_PORT}`;
1283
- }
1284
- server = this.createHttpServer((req, res) => {
1285
- this.log.trace(`Incoming Node.js message -> ${req.url}`);
1286
- this.handleNodeRequest({
1287
- req,
1288
- res
1289
- }).catch((err) => {
1290
- this.log.error("Error handling request", err);
1291
- res.statusCode = 500;
1292
- res.end("Internal Server Error");
1293
- });
1294
- });
1295
- start = (0, alepha.$hook)({
1296
- on: "start",
1297
- handler: async () => {
1298
- await this.listen();
1299
- this.alepha.state.set("alepha.node.server", this.server);
1300
- }
1301
- });
1302
- createHttpServer(func) {
1303
- return (0, node_http.createServer)({ keepAlive: true }, func);
1304
- }
1305
- stop = (0, alepha.$hook)({
1306
- on: "stop",
1307
- handler: async () => {
1308
- if (this.alepha.isProduction()) {
1309
- await this.close();
1310
- return;
1311
- }
1312
- this.close().catch(() => {});
1313
- }
1314
- });
1315
- async listen() {
1316
- let port = this.env.SERVER_PORT;
1317
- if (this.alepha.isTest() && port === 3e3) port = 0;
1318
- await new Promise((resolve, reject) => {
1319
- this.server?.listen(port, this.env.SERVER_HOST, () => {
1320
- this.log.info(`Server listening on ${this.hostname}`);
1321
- resolve();
1322
- });
1323
- this.server?.on("error", (err) => {
1324
- reject(err);
1325
- });
1326
- });
1327
- }
1328
- async close() {
1329
- const promise = new Promise((resolve, reject) => {
1330
- this.server?.close((err) => {
1331
- if (err) reject(err);
1332
- else resolve();
1333
- });
1334
- });
1335
- await Promise.race([this.dateTimeProvider.wait(2e3), promise]);
1336
- this.log.info("Server closed");
1337
- }
1338
- };
1339
-
1340
- //#endregion
1341
- //#region src/server/providers/ServerBodyParserProvider.ts
1342
- const envSchema = alepha.t.object({
1343
- SERVER_BODY_PARSER_INFLATE: alepha.t.boolean({
1344
- default: true,
1345
- description: "Enable decompression of request body."
1346
- }),
1347
- SERVER_BODY_PARSER_LIMIT: alepha.t.integer({
1348
- default: 1e5,
1349
- min: 0,
1350
- description: "Maximum size of request body in bytes."
1351
- })
1352
- });
1353
- var ServerBodyParserProvider = class {
1354
- env = (0, alepha.$env)(envSchema);
1355
- alepha = (0, alepha.$inject)(alepha.Alepha);
1356
- log = (0, alepha_logger.$logger)();
1357
- onRequest = (0, alepha.$hook)({
1358
- on: "server:onRequest",
1359
- handler: async ({ route, request }) => {
1360
- if (request.body) return;
1361
- let stream;
1362
- if (request.raw.web?.req.body) stream = request.raw.web.req.body;
1363
- else if (request.raw.node?.req) stream = node_stream_web.ReadableStream.from(request.raw.node.req);
1364
- if (!stream) return;
1365
- if (route.schema?.body) try {
1366
- const body = await this.parse(stream, request.headers);
1367
- if (body) request.body = body;
1368
- } catch (error) {
1369
- if (error instanceof HttpError) throw error;
1370
- throw new HttpError({
1371
- status: 400,
1372
- message: "Failed to parse request body"
1373
- }, error);
1374
- }
1375
- }
1376
- });
1377
- async parse(stream, headers) {
1378
- const contentType = headers["content-type"];
1379
- const contentEncoding = headers["content-encoding"];
1380
- if (!contentType) return void 0;
1381
- if (contentType.startsWith("application/json")) return this.parseJson(stream, contentEncoding);
1382
- if (contentType.startsWith("text/plain")) return this.parseText(stream, contentEncoding);
1383
- if (contentType.startsWith("application/x-www-form-urlencoded")) return this.parseUrlEncoded(stream, contentEncoding);
1384
- }
1385
- async parseText(stream, contentEncoding) {
1386
- const buffer = await this.streamToBuffer(stream);
1387
- return (await this.maybeDecompress(buffer, contentEncoding)).toString("utf-8");
1388
- }
1389
- async parseUrlEncoded(stream, contentEncoding) {
1390
- const text = await this.parseText(stream, contentEncoding);
1391
- const params = new URLSearchParams(text);
1392
- const result = {};
1393
- for (const [key, value] of params.entries()) result[key] = value;
1394
- return result;
1395
- }
1396
- async parseJson(stream, contentEncoding) {
1397
- const text = await this.parseText(stream, contentEncoding);
1398
- return JSON.parse(text);
1399
- }
1400
- async maybeDecompress(buffer, encoding) {
1401
- if (!this.env.SERVER_BODY_PARSER_INFLATE && encoding) throw new HttpError({
1402
- status: 415,
1403
- message: `Content-Encoding ${encoding} not allowed`
1404
- });
1405
- switch (encoding) {
1406
- case "gzip": return new Promise((res, rej) => (0, node_zlib.createGunzip)().end(buffer, () => {}).on("data", res).on("error", rej));
1407
- case "deflate": return new Promise((res, rej) => (0, node_zlib.createInflate)().end(buffer, () => {}).on("data", res).on("error", rej));
1408
- case "br": return new Promise((res, rej) => (0, node_zlib.createBrotliDecompress)().end(buffer, () => {}).on("data", res).on("error", rej));
1409
- case void 0:
1410
- case "identity": return buffer;
1411
- default: throw new Error(`Unsupported Content-Encoding: ${encoding}`);
1412
- }
1413
- }
1414
- /**
1415
- * Convert Web ReadableStream to Buffer, with a size limit.
1416
- *
1417
- * TODO: move to alepha/file FileUtils
1418
- */
1419
- async streamToBuffer(stream) {
1420
- const chunks = [];
1421
- let totalLength = 0;
1422
- const reader = stream.getReader();
1423
- try {
1424
- while (true) {
1425
- const { done, value } = await reader.read();
1426
- if (done) break;
1427
- if (value) {
1428
- totalLength += value.length;
1429
- if (totalLength > this.env.SERVER_BODY_PARSER_LIMIT) {
1430
- this.log.error(`Body size limit exceeded: ${totalLength} > ${this.env.SERVER_BODY_PARSER_LIMIT}`);
1431
- await reader.cancel();
1432
- throw new HttpError({
1433
- status: 413,
1434
- message: `Request body size limit exceeded`
1435
- });
1436
- }
1437
- chunks.push(value);
1438
- }
1439
- }
1440
- const combinedLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
1441
- const combined = new Uint8Array(combinedLength);
1442
- let offset = 0;
1443
- for (const chunk of chunks) {
1444
- combined.set(chunk, offset);
1445
- offset += chunk.length;
1446
- }
1447
- return Buffer.from(combined);
1448
- } catch (error) {
1449
- reader.releaseLock();
1450
- throw error;
1451
- }
1452
- }
1453
- };
1454
-
1455
- //#endregion
1456
- //#region src/server/providers/ServerLoggerProvider.ts
1457
- var ServerLoggerProvider = class {
1458
- log = (0, alepha_logger.$logger)();
1459
- alepha = (0, alepha.$inject)(alepha.Alepha);
1460
- onRequest = (0, alepha.$hook)({
1461
- on: "server:onRequest",
1462
- priority: "first",
1463
- handler: ({ route, request }) => {
1464
- if (!route.silent) {
1465
- request.metadata.now = Date.now();
1466
- const data = {
1467
- method: request.method,
1468
- path: request.url.pathname
1469
- };
1470
- if (this.alepha.isProduction()) {
1471
- data.agent = request.headers["user-agent"];
1472
- const ip = request.ip;
1473
- if (ip) data.ip = ip;
1474
- }
1475
- this.log.info("Incoming request", data);
1476
- }
1477
- }
1478
- });
1479
- onError = (0, alepha.$hook)({
1480
- on: "server:onError",
1481
- priority: "last",
1482
- handler: ({ error }) => {
1483
- this.log.error("Request has failed", error);
1484
- }
1485
- });
1486
- onResponse = (0, alepha.$hook)({
1487
- on: "server:onResponse",
1488
- priority: "last",
1489
- handler: ({ route, request, response }) => {
1490
- if (!route.silent) {
1491
- const ms = Date.now() - request.metadata.now;
1492
- this.log.info("Request completed", {
1493
- status: response.status,
1494
- ms
1495
- });
1496
- }
1497
- }
1498
- });
1499
- };
1500
-
1501
- //#endregion
1502
- //#region src/server/providers/ServerNotReadyProvider.ts
1503
- /**
1504
- * On every request, this provider checks if the server is ready.
1505
- *
1506
- * If the server is not ready, it responds with a 503 status code and a message indicating that the server is not ready yet.
1507
- *
1508
- * The response also includes a `Retry-After` header indicating that the client should retry after 5 seconds.
1509
- */
1510
- var ServerNotReadyProvider = class {
1511
- alepha = (0, alepha.$inject)(alepha.Alepha);
1512
- onRequest = (0, alepha.$hook)({
1513
- on: "server:onRequest",
1514
- priority: "first",
1515
- handler: ({ request: { reply } }) => {
1516
- if (this.alepha.isReady()) return;
1517
- reply.headers["Retry-After"] = "5";
1518
- throw new HttpError({
1519
- status: 503,
1520
- message: "Server is not ready yet. Please try again later."
1521
- });
1522
- }
1523
- });
1524
- };
1525
-
1526
- //#endregion
1527
- //#region src/server/constants/routeMethods.ts
1528
- const routeMethods = [
1529
- "GET",
1530
- "POST",
1531
- "PUT",
1532
- "PATCH",
1533
- "DELETE",
1534
- "HEAD",
1535
- "OPTIONS",
1536
- "CONNECT",
1537
- "TRACE"
1538
- ];
1539
-
1540
- //#endregion
1541
- //#region src/server/errors/BadRequestError.ts
1542
- var BadRequestError = class extends HttpError {
1543
- constructor(message = "Invalid request body", cause) {
1544
- super({
1545
- message,
1546
- status: 400
1547
- }, cause);
1548
- }
1549
- };
1550
-
1551
- //#endregion
1552
- //#region src/server/errors/ConflictError.ts
1553
- var ConflictError = class extends HttpError {
1554
- constructor(message = "Entity already exists", cause) {
1555
- super({
1556
- message,
1557
- status: 409
1558
- }, cause);
1559
- }
1560
- };
1561
-
1562
- //#endregion
1563
- //#region src/server/errors/ForbiddenError.ts
1564
- var ForbiddenError = class extends HttpError {
1565
- constructor(message = "No permission to access this resource", cause) {
1566
- super({
1567
- message,
1568
- status: 403
1569
- }, cause);
1570
- }
1571
- };
1572
-
1573
- //#endregion
1574
- //#region src/server/errors/NotFoundError.ts
1575
- var NotFoundError = class extends HttpError {
1576
- constructor(message = "Resource not found", cause) {
1577
- super({
1578
- message,
1579
- status: 404
1580
- }, cause);
1581
- }
1582
- };
1583
-
1584
- //#endregion
1585
- //#region src/server/errors/UnauthorizedError.ts
1586
- var UnauthorizedError = class extends HttpError {
1587
- name = "UnauthorizedError";
1588
- constructor(message = "Not allowed to access this resource", cause) {
1589
- super({
1590
- message,
1591
- status: 401
1592
- }, cause);
1593
- }
1594
- };
1595
-
1596
- //#endregion
1597
- //#region src/server/schemas/okSchema.ts
1598
- const okSchema = alepha.t.object({
1599
- ok: alepha.t.boolean({ description: "True when operation succeed" }),
1600
- id: alepha.t.optional(alepha.t.union([alepha.t.text(), alepha.t.integer()])),
1601
- count: alepha.t.optional(alepha.t.number({ description: "Number of resources affected" }))
1602
- }, {
1603
- title: "Ok",
1604
- description: "Generic response after a successful operation on a resource"
1605
- });
1606
-
1607
- //#endregion
1608
- //#region src/server/index.ts
1609
- /**
1610
- * Provides high-performance HTTP server capabilities with declarative routing and action descriptors.
1611
- *
1612
- * The server module enables building REST APIs and web applications using `$route` and `$action` descriptors
1613
- * on class properties. It provides automatic request/response handling, schema validation, middleware support,
1614
- * and seamless integration with other Alepha modules for a complete backend solution.
1615
- *
1616
- * @see {@link $route}
1617
- * @see {@link $action}
1618
- * @module alepha.server
1619
- */
1620
- const AlephaServer = (0, alepha.$module)({
1621
- name: "alepha.server",
1622
- descriptors: [$route, $action],
1623
- services: [
1624
- ServerProvider,
1625
- BunHttpServerProvider,
1626
- NodeHttpServerProvider,
1627
- ServerBodyParserProvider,
1628
- ServerLoggerProvider,
1629
- ServerNotReadyProvider,
1630
- ServerTimingProvider,
1631
- HttpClient
1632
- ],
1633
- register: (alepha$1) => {
1634
- if (!alepha$1.isServerless() && !alepha$1.isViteDev()) if (alepha$1.isBun()) alepha$1.with({
1635
- optional: true,
1636
- provide: ServerProvider,
1637
- use: BunHttpServerProvider
1638
- });
1639
- else alepha$1.with({
1640
- optional: true,
1641
- provide: ServerProvider,
1642
- use: NodeHttpServerProvider
1643
- });
1644
- else alepha$1.with(ServerProvider);
1645
- alepha$1.with(ServerBodyParserProvider);
1646
- alepha$1.with(ServerLoggerProvider);
1647
- alepha$1.with(ServerNotReadyProvider);
1648
- if (!alepha$1.isProduction()) alepha$1.with(ServerTimingProvider);
1649
- }
1650
- });
1651
-
1652
- //#endregion
1653
- exports.$action = $action;
1654
- exports.$route = $route;
1655
- exports.ActionDescriptor = ActionDescriptor;
1656
- exports.AlephaServer = AlephaServer;
1657
- exports.BadRequestError = BadRequestError;
1658
- exports.BunHttpServerProvider = BunHttpServerProvider;
1659
- exports.ConflictError = ConflictError;
1660
- exports.ForbiddenError = ForbiddenError;
1661
- exports.HttpClient = HttpClient;
1662
- exports.HttpError = HttpError;
1663
- exports.NodeHttpServerProvider = NodeHttpServerProvider;
1664
- exports.NotFoundError = NotFoundError;
1665
- exports.RouteDescriptor = RouteDescriptor;
1666
- exports.ServerLoggerProvider = ServerLoggerProvider;
1667
- exports.ServerNotReadyProvider = ServerNotReadyProvider;
1668
- exports.ServerProvider = ServerProvider;
1669
- exports.ServerReply = ServerReply;
1670
- exports.ServerRouterProvider = ServerRouterProvider;
1671
- exports.ServerTimingProvider = ServerTimingProvider;
1672
- exports.UnauthorizedError = UnauthorizedError;
1673
- exports.ValidationError = ValidationError;
1674
- exports.errorNameByStatus = errorNameByStatus;
1675
- exports.errorSchema = errorSchema;
1676
- exports.isHttpError = isHttpError;
1677
- exports.isMultipart = isMultipart;
1678
- exports.okSchema = okSchema;
1679
- exports.routeMethods = routeMethods;
1680
- //# sourceMappingURL=index.cjs.map