alepha 0.12.1 → 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 (198) hide show
  1. package/dist/api-notifications/index.d.ts +111 -111
  2. package/dist/api-users/index.d.ts +1240 -1240
  3. package/dist/api-verifications/index.d.ts +94 -94
  4. package/dist/cli/{dist-Sz2EXvQX.cjs → dist-Dl9Vl7Ur.js} +17 -13
  5. package/dist/cli/{dist-BBPjuQ56.js.map → dist-Dl9Vl7Ur.js.map} +1 -1
  6. package/dist/cli/index.d.ts +3 -11
  7. package/dist/cli/index.js +106 -74
  8. package/dist/cli/index.js.map +1 -1
  9. package/dist/email/index.js +71 -73
  10. package/dist/email/index.js.map +1 -1
  11. package/dist/orm/index.d.ts +1 -1
  12. package/dist/orm/index.js.map +1 -1
  13. package/dist/queue/index.d.ts +4 -4
  14. package/dist/redis/index.d.ts +10 -10
  15. package/dist/retry/index.d.ts +1 -1
  16. package/dist/retry/index.js +2 -2
  17. package/dist/retry/index.js.map +1 -1
  18. package/dist/scheduler/index.d.ts +6 -6
  19. package/dist/server/index.js +1 -1
  20. package/dist/server/index.js.map +1 -1
  21. package/dist/server-auth/index.d.ts +193 -193
  22. package/dist/server-health/index.d.ts +17 -17
  23. package/dist/server-links/index.d.ts +34 -34
  24. package/dist/server-metrics/index.js +170 -174
  25. package/dist/server-metrics/index.js.map +1 -1
  26. package/dist/server-security/index.d.ts +9 -9
  27. package/dist/vite/index.js +4 -5
  28. package/dist/vite/index.js.map +1 -1
  29. package/dist/websocket/index.d.ts +7 -7
  30. package/package.json +52 -103
  31. package/src/cli/apps/AlephaPackageBuilderCli.ts +7 -2
  32. package/src/cli/assets/appRouterTs.ts +9 -0
  33. package/src/cli/assets/indexHtml.ts +2 -1
  34. package/src/cli/assets/mainBrowserTs.ts +10 -0
  35. package/src/cli/commands/CoreCommands.ts +6 -5
  36. package/src/cli/commands/DrizzleCommands.ts +65 -57
  37. package/src/cli/commands/VerifyCommands.ts +1 -1
  38. package/src/cli/services/ProjectUtils.ts +44 -38
  39. package/src/orm/providers/DrizzleKitProvider.ts +1 -1
  40. package/src/retry/descriptors/$retry.ts +5 -3
  41. package/src/server/providers/NodeHttpServerProvider.ts +1 -1
  42. package/src/vite/helpers/boot.ts +3 -3
  43. package/dist/api-files/index.cjs +0 -1293
  44. package/dist/api-files/index.cjs.map +0 -1
  45. package/dist/api-files/index.d.cts +0 -829
  46. package/dist/api-jobs/index.cjs +0 -274
  47. package/dist/api-jobs/index.cjs.map +0 -1
  48. package/dist/api-jobs/index.d.cts +0 -654
  49. package/dist/api-notifications/index.cjs +0 -380
  50. package/dist/api-notifications/index.cjs.map +0 -1
  51. package/dist/api-notifications/index.d.cts +0 -289
  52. package/dist/api-parameters/index.cjs +0 -66
  53. package/dist/api-parameters/index.cjs.map +0 -1
  54. package/dist/api-parameters/index.d.cts +0 -84
  55. package/dist/api-users/index.cjs +0 -6009
  56. package/dist/api-users/index.cjs.map +0 -1
  57. package/dist/api-users/index.d.cts +0 -4740
  58. package/dist/api-verifications/index.cjs +0 -407
  59. package/dist/api-verifications/index.cjs.map +0 -1
  60. package/dist/api-verifications/index.d.cts +0 -207
  61. package/dist/batch/index.cjs +0 -408
  62. package/dist/batch/index.cjs.map +0 -1
  63. package/dist/batch/index.d.cts +0 -330
  64. package/dist/bin/index.cjs +0 -17
  65. package/dist/bin/index.cjs.map +0 -1
  66. package/dist/bin/index.d.cts +0 -1
  67. package/dist/bucket/index.cjs +0 -303
  68. package/dist/bucket/index.cjs.map +0 -1
  69. package/dist/bucket/index.d.cts +0 -355
  70. package/dist/cache/index.cjs +0 -241
  71. package/dist/cache/index.cjs.map +0 -1
  72. package/dist/cache/index.d.cts +0 -202
  73. package/dist/cache-redis/index.cjs +0 -84
  74. package/dist/cache-redis/index.cjs.map +0 -1
  75. package/dist/cache-redis/index.d.cts +0 -40
  76. package/dist/cli/chunk-DSlc6foC.cjs +0 -43
  77. package/dist/cli/dist-BBPjuQ56.js +0 -2778
  78. package/dist/cli/dist-Sz2EXvQX.cjs.map +0 -1
  79. package/dist/cli/index.cjs +0 -1241
  80. package/dist/cli/index.cjs.map +0 -1
  81. package/dist/cli/index.d.cts +0 -422
  82. package/dist/command/index.cjs +0 -693
  83. package/dist/command/index.cjs.map +0 -1
  84. package/dist/command/index.d.cts +0 -340
  85. package/dist/core/index.cjs +0 -2264
  86. package/dist/core/index.cjs.map +0 -1
  87. package/dist/core/index.d.cts +0 -1927
  88. package/dist/datetime/index.cjs +0 -318
  89. package/dist/datetime/index.cjs.map +0 -1
  90. package/dist/datetime/index.d.cts +0 -145
  91. package/dist/email/index.cjs +0 -10874
  92. package/dist/email/index.cjs.map +0 -1
  93. package/dist/email/index.d.cts +0 -186
  94. package/dist/fake/index.cjs +0 -34641
  95. package/dist/fake/index.cjs.map +0 -1
  96. package/dist/fake/index.d.cts +0 -74
  97. package/dist/file/index.cjs +0 -1212
  98. package/dist/file/index.cjs.map +0 -1
  99. package/dist/file/index.d.cts +0 -698
  100. package/dist/lock/index.cjs +0 -226
  101. package/dist/lock/index.cjs.map +0 -1
  102. package/dist/lock/index.d.cts +0 -361
  103. package/dist/lock-redis/index.cjs +0 -113
  104. package/dist/lock-redis/index.cjs.map +0 -1
  105. package/dist/lock-redis/index.d.cts +0 -24
  106. package/dist/logger/index.cjs +0 -521
  107. package/dist/logger/index.cjs.map +0 -1
  108. package/dist/logger/index.d.cts +0 -281
  109. package/dist/orm/index.cjs +0 -2986
  110. package/dist/orm/index.cjs.map +0 -1
  111. package/dist/orm/index.d.cts +0 -2213
  112. package/dist/queue/index.cjs +0 -1044
  113. package/dist/queue/index.cjs.map +0 -1
  114. package/dist/queue/index.d.cts +0 -1265
  115. package/dist/queue-redis/index.cjs +0 -873
  116. package/dist/queue-redis/index.cjs.map +0 -1
  117. package/dist/queue-redis/index.d.cts +0 -82
  118. package/dist/redis/index.cjs +0 -153
  119. package/dist/redis/index.cjs.map +0 -1
  120. package/dist/redis/index.d.cts +0 -82
  121. package/dist/retry/index.cjs +0 -146
  122. package/dist/retry/index.cjs.map +0 -1
  123. package/dist/retry/index.d.cts +0 -172
  124. package/dist/router/index.cjs +0 -111
  125. package/dist/router/index.cjs.map +0 -1
  126. package/dist/router/index.d.cts +0 -46
  127. package/dist/scheduler/index.cjs +0 -576
  128. package/dist/scheduler/index.cjs.map +0 -1
  129. package/dist/scheduler/index.d.cts +0 -145
  130. package/dist/security/index.cjs +0 -2402
  131. package/dist/security/index.cjs.map +0 -1
  132. package/dist/security/index.d.cts +0 -598
  133. package/dist/server/index.cjs +0 -1680
  134. package/dist/server/index.cjs.map +0 -1
  135. package/dist/server/index.d.cts +0 -810
  136. package/dist/server-auth/index.cjs +0 -3146
  137. package/dist/server-auth/index.cjs.map +0 -1
  138. package/dist/server-auth/index.d.cts +0 -1164
  139. package/dist/server-cache/index.cjs +0 -252
  140. package/dist/server-cache/index.cjs.map +0 -1
  141. package/dist/server-cache/index.d.cts +0 -164
  142. package/dist/server-compress/index.cjs +0 -141
  143. package/dist/server-compress/index.cjs.map +0 -1
  144. package/dist/server-compress/index.d.cts +0 -38
  145. package/dist/server-cookies/index.cjs +0 -234
  146. package/dist/server-cookies/index.cjs.map +0 -1
  147. package/dist/server-cookies/index.d.cts +0 -144
  148. package/dist/server-cors/index.cjs +0 -201
  149. package/dist/server-cors/index.cjs.map +0 -1
  150. package/dist/server-cors/index.d.cts +0 -140
  151. package/dist/server-health/index.cjs +0 -62
  152. package/dist/server-health/index.cjs.map +0 -1
  153. package/dist/server-health/index.d.cts +0 -58
  154. package/dist/server-helmet/index.cjs +0 -131
  155. package/dist/server-helmet/index.cjs.map +0 -1
  156. package/dist/server-helmet/index.d.cts +0 -97
  157. package/dist/server-links/index.cjs +0 -992
  158. package/dist/server-links/index.cjs.map +0 -1
  159. package/dist/server-links/index.d.cts +0 -513
  160. package/dist/server-metrics/index.cjs +0 -4535
  161. package/dist/server-metrics/index.cjs.map +0 -1
  162. package/dist/server-metrics/index.d.cts +0 -35
  163. package/dist/server-multipart/index.cjs +0 -237
  164. package/dist/server-multipart/index.cjs.map +0 -1
  165. package/dist/server-multipart/index.d.cts +0 -50
  166. package/dist/server-proxy/index.cjs +0 -186
  167. package/dist/server-proxy/index.cjs.map +0 -1
  168. package/dist/server-proxy/index.d.cts +0 -234
  169. package/dist/server-rate-limit/index.cjs +0 -241
  170. package/dist/server-rate-limit/index.cjs.map +0 -1
  171. package/dist/server-rate-limit/index.d.cts +0 -183
  172. package/dist/server-security/index.cjs +0 -316
  173. package/dist/server-security/index.cjs.map +0 -1
  174. package/dist/server-security/index.d.cts +0 -173
  175. package/dist/server-static/index.cjs +0 -170
  176. package/dist/server-static/index.cjs.map +0 -1
  177. package/dist/server-static/index.d.cts +0 -121
  178. package/dist/server-swagger/index.cjs +0 -1021
  179. package/dist/server-swagger/index.cjs.map +0 -1
  180. package/dist/server-swagger/index.d.cts +0 -382
  181. package/dist/sms/index.cjs +0 -221
  182. package/dist/sms/index.cjs.map +0 -1
  183. package/dist/sms/index.d.cts +0 -130
  184. package/dist/thread/index.cjs +0 -350
  185. package/dist/thread/index.cjs.map +0 -1
  186. package/dist/thread/index.d.cts +0 -260
  187. package/dist/topic/index.cjs +0 -282
  188. package/dist/topic/index.cjs.map +0 -1
  189. package/dist/topic/index.d.cts +0 -523
  190. package/dist/topic-redis/index.cjs +0 -71
  191. package/dist/topic-redis/index.cjs.map +0 -1
  192. package/dist/topic-redis/index.d.cts +0 -42
  193. package/dist/vite/index.cjs +0 -1077
  194. package/dist/vite/index.cjs.map +0 -1
  195. package/dist/vite/index.d.cts +0 -542
  196. package/dist/websocket/index.cjs +0 -1117
  197. package/dist/websocket/index.cjs.map +0 -1
  198. 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