alepha 0.14.4 → 0.15.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.
- package/README.md +44 -102
- package/dist/api/audits/index.d.ts +331 -443
- package/dist/api/audits/index.d.ts.map +1 -1
- package/dist/api/audits/index.js +2 -2
- package/dist/api/audits/index.js.map +1 -1
- package/dist/api/files/index.d.ts +0 -113
- package/dist/api/files/index.d.ts.map +1 -1
- package/dist/api/files/index.js +2 -3
- package/dist/api/files/index.js.map +1 -1
- package/dist/api/jobs/index.d.ts +151 -262
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/notifications/index.browser.js +4 -4
- package/dist/api/notifications/index.browser.js.map +1 -1
- package/dist/api/notifications/index.d.ts +164 -276
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/notifications/index.js +4 -4
- package/dist/api/notifications/index.js.map +1 -1
- package/dist/api/parameters/index.d.ts +265 -377
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/users/index.browser.js +1 -2
- package/dist/api/users/index.browser.js.map +1 -1
- package/dist/api/users/index.d.ts +195 -301
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +203 -184
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/verifications/index.d.ts.map +1 -1
- package/dist/batch/index.d.ts.map +1 -1
- package/dist/batch/index.js +1 -2
- package/dist/batch/index.js.map +1 -1
- package/dist/bucket/index.d.ts.map +1 -1
- package/dist/cache/core/index.d.ts.map +1 -1
- package/dist/cache/redis/index.d.ts.map +1 -1
- package/dist/cache/redis/index.js +2 -2
- package/dist/cache/redis/index.js.map +1 -1
- package/dist/cli/index.d.ts +5900 -165
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +1481 -639
- package/dist/cli/index.js.map +1 -1
- package/dist/command/index.d.ts +8 -4
- package/dist/command/index.d.ts.map +1 -1
- package/dist/command/index.js +29 -25
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js +563 -54
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +175 -8
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +564 -54
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +563 -54
- package/dist/core/index.native.js.map +1 -1
- package/dist/datetime/index.d.ts.map +1 -1
- package/dist/datetime/index.js +4 -4
- package/dist/datetime/index.js.map +1 -1
- package/dist/email/index.d.ts +89 -42
- package/dist/email/index.d.ts.map +1 -1
- package/dist/email/index.js +129 -33
- package/dist/email/index.js.map +1 -1
- package/dist/fake/index.d.ts +7969 -2
- package/dist/fake/index.d.ts.map +1 -1
- package/dist/fake/index.js +22 -22
- package/dist/fake/index.js.map +1 -1
- package/dist/file/index.d.ts +134 -1
- package/dist/file/index.d.ts.map +1 -1
- package/dist/file/index.js +253 -1
- package/dist/file/index.js.map +1 -1
- package/dist/lock/core/index.d.ts.map +1 -1
- package/dist/lock/redis/index.d.ts.map +1 -1
- package/dist/logger/index.d.ts +1 -2
- package/dist/logger/index.d.ts.map +1 -1
- package/dist/logger/index.js +1 -5
- package/dist/logger/index.js.map +1 -1
- package/dist/mcp/index.d.ts +19 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +28 -4
- package/dist/mcp/index.js.map +1 -1
- package/dist/orm/chunk-DH6iiROE.js +38 -0
- package/dist/orm/index.browser.js +9 -9
- package/dist/orm/index.browser.js.map +1 -1
- package/dist/orm/index.bun.js +2821 -0
- package/dist/orm/index.bun.js.map +1 -0
- package/dist/orm/index.d.ts +318 -169
- package/dist/orm/index.d.ts.map +1 -1
- package/dist/orm/index.js +2086 -1776
- package/dist/orm/index.js.map +1 -1
- package/dist/queue/core/index.d.ts +4 -4
- package/dist/queue/core/index.d.ts.map +1 -1
- package/dist/queue/redis/index.d.ts.map +1 -1
- package/dist/redis/index.bun.js +285 -0
- package/dist/redis/index.bun.js.map +1 -0
- package/dist/redis/index.d.ts +13 -31
- package/dist/redis/index.d.ts.map +1 -1
- package/dist/redis/index.js +18 -38
- package/dist/redis/index.js.map +1 -1
- package/dist/retry/index.d.ts.map +1 -1
- package/dist/router/index.d.ts.map +1 -1
- package/dist/scheduler/index.d.ts +83 -1
- package/dist/scheduler/index.d.ts.map +1 -1
- package/dist/scheduler/index.js +393 -1
- package/dist/scheduler/index.js.map +1 -1
- package/dist/security/index.browser.js +5 -1
- package/dist/security/index.browser.js.map +1 -1
- package/dist/security/index.d.ts +598 -112
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +1808 -97
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.d.ts +1200 -175
- package/dist/server/auth/index.d.ts.map +1 -1
- package/dist/server/auth/index.js +1268 -37
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cache/index.d.ts +6 -3
- package/dist/server/cache/index.d.ts.map +1 -1
- package/dist/server/cache/index.js +1 -1
- package/dist/server/cache/index.js.map +1 -1
- package/dist/server/compress/index.d.ts.map +1 -1
- package/dist/server/cookies/index.d.ts.map +1 -1
- package/dist/server/cookies/index.js +3 -3
- package/dist/server/cookies/index.js.map +1 -1
- package/dist/server/core/index.d.ts +115 -13
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js +321 -139
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/cors/index.d.ts +0 -1
- package/dist/server/cors/index.d.ts.map +1 -1
- package/dist/server/health/index.d.ts +0 -1
- package/dist/server/health/index.d.ts.map +1 -1
- package/dist/server/helmet/index.d.ts.map +1 -1
- package/dist/server/links/index.browser.js +9 -1
- package/dist/server/links/index.browser.js.map +1 -1
- package/dist/server/links/index.d.ts +1 -2
- package/dist/server/links/index.d.ts.map +1 -1
- package/dist/server/links/index.js +14 -7
- package/dist/server/links/index.js.map +1 -1
- package/dist/server/metrics/index.d.ts +514 -1
- package/dist/server/metrics/index.d.ts.map +1 -1
- package/dist/server/metrics/index.js +4462 -4
- package/dist/server/metrics/index.js.map +1 -1
- package/dist/server/multipart/index.d.ts.map +1 -1
- package/dist/server/proxy/index.d.ts +0 -1
- package/dist/server/proxy/index.d.ts.map +1 -1
- package/dist/server/rate-limit/index.d.ts.map +1 -1
- package/dist/server/static/index.d.ts.map +1 -1
- package/dist/server/swagger/index.d.ts +1 -2
- package/dist/server/swagger/index.d.ts.map +1 -1
- package/dist/server/swagger/index.js +1 -2
- package/dist/server/swagger/index.js.map +1 -1
- package/dist/sms/index.d.ts +3 -1
- package/dist/sms/index.d.ts.map +1 -1
- package/dist/sms/index.js +10 -10
- package/dist/sms/index.js.map +1 -1
- package/dist/thread/index.d.ts +0 -1
- package/dist/thread/index.d.ts.map +1 -1
- package/dist/thread/index.js +2 -2
- package/dist/thread/index.js.map +1 -1
- package/dist/topic/core/index.d.ts.map +1 -1
- package/dist/topic/redis/index.d.ts.map +1 -1
- package/dist/vite/index.d.ts +6315 -149
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +140 -469
- package/dist/vite/index.js.map +1 -1
- package/dist/websocket/index.browser.js +9 -9
- package/dist/websocket/index.browser.js.map +1 -1
- package/dist/websocket/index.d.ts +28 -28
- package/dist/websocket/index.d.ts.map +1 -1
- package/dist/websocket/index.js +9 -9
- package/dist/websocket/index.js.map +1 -1
- package/package.json +13 -18
- package/src/api/files/controllers/AdminFileStatsController.ts +0 -1
- package/src/api/users/atoms/realmAuthSettingsAtom.ts +5 -0
- package/src/api/users/controllers/{UserRealmController.ts → RealmController.ts} +11 -11
- package/src/api/users/entities/users.ts +1 -1
- package/src/api/users/index.ts +8 -8
- package/src/api/users/primitives/{$userRealm.ts → $realm.ts} +17 -19
- package/src/api/users/providers/{UserRealmProvider.ts → RealmProvider.ts} +26 -30
- package/src/api/users/schemas/{userRealmConfigSchema.ts → realmConfigSchema.ts} +2 -2
- package/src/api/users/services/CredentialService.ts +7 -7
- package/src/api/users/services/IdentityService.ts +4 -4
- package/src/api/users/services/RegistrationService.spec.ts +25 -27
- package/src/api/users/services/RegistrationService.ts +38 -27
- package/src/api/users/services/SessionCrudService.ts +3 -3
- package/src/api/users/services/SessionService.spec.ts +3 -3
- package/src/api/users/services/SessionService.ts +27 -18
- package/src/api/users/services/UserService.ts +7 -7
- package/src/batch/providers/BatchProvider.ts +1 -2
- package/src/cli/apps/AlephaCli.ts +2 -2
- package/src/cli/apps/AlephaPackageBuilderCli.ts +47 -20
- package/src/cli/assets/apiHelloControllerTs.ts +19 -0
- package/src/cli/assets/apiIndexTs.ts +16 -0
- package/src/cli/assets/biomeJson.ts +2 -1
- package/src/cli/assets/claudeMd.ts +308 -0
- package/src/cli/assets/dummySpecTs.ts +2 -1
- package/src/cli/assets/editorconfig.ts +2 -1
- package/src/cli/assets/mainBrowserTs.ts +4 -3
- package/src/cli/assets/mainCss.ts +24 -0
- package/src/cli/assets/mainServerTs.ts +24 -0
- package/src/cli/assets/tsconfigJson.ts +2 -1
- package/src/cli/assets/webAppRouterTs.ts +16 -0
- package/src/cli/assets/webHelloComponentTsx.ts +20 -0
- package/src/cli/assets/webIndexTs.ts +16 -0
- package/src/cli/atoms/appEntryOptions.ts +13 -0
- package/src/cli/atoms/buildOptions.ts +1 -1
- package/src/cli/atoms/changelogOptions.ts +1 -1
- package/src/cli/commands/build.ts +97 -61
- package/src/cli/commands/db.ts +21 -18
- package/src/cli/commands/deploy.ts +17 -5
- package/src/cli/commands/dev.ts +26 -47
- package/src/cli/commands/gen/env.ts +1 -1
- package/src/cli/commands/init.ts +79 -25
- package/src/cli/commands/lint.ts +9 -3
- package/src/cli/commands/test.ts +8 -2
- package/src/cli/commands/typecheck.ts +5 -1
- package/src/cli/commands/verify.ts +4 -2
- package/src/cli/defineConfig.ts +9 -0
- package/src/cli/index.ts +2 -1
- package/src/cli/providers/AppEntryProvider.ts +131 -0
- package/src/cli/providers/ViteBuildProvider.ts +82 -0
- package/src/cli/providers/ViteDevServerProvider.ts +350 -0
- package/src/cli/providers/ViteTemplateProvider.ts +27 -0
- package/src/cli/services/AlephaCliUtils.ts +72 -602
- package/src/cli/services/PackageManagerUtils.ts +308 -0
- package/src/cli/services/ProjectScaffolder.ts +329 -0
- package/src/command/helpers/Runner.ts +15 -3
- package/src/core/Alepha.ts +2 -8
- package/src/core/__tests__/Alepha-graph.spec.ts +4 -0
- package/src/core/index.shared.ts +1 -0
- package/src/core/index.ts +2 -0
- package/src/core/primitives/$hook.ts +6 -2
- package/src/core/primitives/$module.spec.ts +4 -0
- package/src/core/primitives/$module.ts +12 -0
- package/src/core/providers/AlsProvider.ts +1 -1
- package/src/core/providers/CodecManager.spec.ts +12 -6
- package/src/core/providers/CodecManager.ts +26 -6
- package/src/core/providers/EventManager.ts +169 -13
- package/src/core/providers/KeylessJsonSchemaCodec.spec.ts +878 -0
- package/src/core/providers/KeylessJsonSchemaCodec.ts +789 -0
- package/src/core/providers/SchemaValidator.spec.ts +236 -0
- package/src/core/providers/StateManager.spec.ts +27 -16
- package/src/email/providers/LocalEmailProvider.spec.ts +111 -87
- package/src/email/providers/LocalEmailProvider.ts +52 -15
- package/src/email/providers/NodemailerEmailProvider.ts +167 -56
- package/src/file/errors/FileError.ts +7 -0
- package/src/file/index.ts +9 -1
- package/src/file/providers/MemoryFileSystemProvider.ts +393 -0
- package/src/logger/providers/PrettyFormatterProvider.ts +0 -9
- package/src/mcp/errors/McpError.ts +30 -0
- package/src/mcp/index.ts +3 -0
- package/src/mcp/transports/SseMcpTransport.ts +16 -6
- package/src/orm/index.browser.ts +1 -19
- package/src/orm/index.bun.ts +77 -0
- package/src/orm/index.shared-server.ts +22 -0
- package/src/orm/index.shared.ts +15 -0
- package/src/orm/index.ts +19 -39
- package/src/orm/providers/DrizzleKitProvider.ts +3 -5
- package/src/orm/providers/drivers/BunPostgresProvider.ts +3 -5
- package/src/orm/providers/drivers/BunSqliteProvider.ts +1 -1
- package/src/orm/providers/drivers/CloudflareD1Provider.ts +4 -0
- package/src/orm/providers/drivers/DatabaseProvider.ts +4 -0
- package/src/orm/providers/drivers/PglitePostgresProvider.ts +4 -0
- package/src/orm/services/Repository.ts +19 -0
- package/src/redis/index.bun.ts +35 -0
- package/src/redis/providers/BunRedisProvider.ts +12 -43
- package/src/redis/providers/BunRedisSubscriberProvider.ts +2 -3
- package/src/redis/providers/NodeRedisProvider.ts +16 -34
- package/src/{server/security → security}/__tests__/BasicAuth.spec.ts +11 -11
- package/src/{server/security → security}/__tests__/ServerSecurityProvider-realm.spec.ts +21 -16
- package/src/{server/security/providers → security/__tests__}/ServerSecurityProvider.spec.ts +5 -5
- package/src/security/index.browser.ts +5 -0
- package/src/security/index.ts +90 -7
- package/src/security/primitives/{$realm.spec.ts → $issuer.spec.ts} +11 -11
- package/src/security/primitives/{$realm.ts → $issuer.ts} +20 -17
- package/src/security/primitives/$role.ts +5 -5
- package/src/security/primitives/$serviceAccount.spec.ts +5 -5
- package/src/security/primitives/$serviceAccount.ts +3 -3
- package/src/{server/security → security}/providers/ServerSecurityProvider.ts +5 -7
- package/src/server/auth/primitives/$auth.ts +10 -10
- package/src/server/auth/primitives/$authCredentials.ts +3 -3
- package/src/server/auth/primitives/$authGithub.ts +3 -3
- package/src/server/auth/primitives/$authGoogle.ts +3 -3
- package/src/server/auth/providers/ServerAuthProvider.ts +13 -13
- package/src/server/cache/providers/ServerCacheProvider.ts +1 -1
- package/src/server/cookies/providers/ServerCookiesProvider.ts +3 -3
- package/src/server/core/index.ts +1 -1
- package/src/server/core/providers/BunHttpServerProvider.ts +1 -1
- package/src/server/core/providers/NodeHttpServerProvider.spec.ts +125 -0
- package/src/server/core/providers/NodeHttpServerProvider.ts +92 -24
- package/src/server/core/providers/ServerBodyParserProvider.ts +19 -23
- package/src/server/core/providers/ServerLoggerProvider.ts +23 -19
- package/src/server/core/providers/ServerProvider.ts +144 -24
- package/src/server/core/providers/ServerRouterProvider.ts +259 -115
- package/src/server/core/providers/ServerTimingProvider.ts +2 -2
- package/src/server/links/atoms/apiLinksAtom.ts +7 -0
- package/src/server/links/index.browser.ts +2 -0
- package/src/server/links/index.ts +3 -1
- package/src/server/links/providers/LinkProvider.ts +1 -1
- package/src/server/swagger/index.ts +1 -1
- package/src/sms/providers/LocalSmsProvider.spec.ts +153 -111
- package/src/sms/providers/LocalSmsProvider.ts +8 -7
- package/src/vite/index.ts +3 -2
- package/src/vite/tasks/buildClient.ts +0 -1
- package/src/vite/tasks/buildServer.ts +80 -22
- package/src/vite/tasks/copyAssets.ts +5 -4
- package/src/vite/tasks/generateCloudflare.ts +7 -0
- package/src/vite/tasks/generateSitemap.ts +64 -23
- package/src/vite/tasks/index.ts +0 -2
- package/src/vite/tasks/prerenderPages.ts +49 -24
- package/dist/server/security/index.browser.js +0 -13
- package/dist/server/security/index.browser.js.map +0 -1
- package/dist/server/security/index.d.ts +0 -173
- package/dist/server/security/index.d.ts.map +0 -1
- package/dist/server/security/index.js +0 -311
- package/dist/server/security/index.js.map +0 -1
- package/src/cli/assets/appRouterTs.ts +0 -9
- package/src/cli/assets/indexHtml.ts +0 -15
- package/src/cli/assets/mainTs.ts +0 -13
- package/src/cli/commands/format.ts +0 -17
- package/src/server/security/index.browser.ts +0 -10
- package/src/server/security/index.ts +0 -94
- package/src/vite/helpers/boot.ts +0 -106
- package/src/vite/plugins/viteAlephaDev.ts +0 -177
- package/src/vite/tasks/devServer.ts +0 -69
- package/src/vite/tasks/runAlepha.ts +0 -270
- /package/src/{server/security → security}/primitives/$basicAuth.ts +0 -0
- /package/src/{server/security → security}/providers/ServerBasicAuthProvider.ts +0 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { $env, $hook, $inject, $module, Alepha, AlephaError, KIND, Primitive, createPrimitive, isFileLike, isTypeFile,
|
|
1
|
+
import { $env, $hook, $inject, $module, Alepha, AlephaError, KIND, Primitive, createPrimitive, isFileLike, isTypeFile, t } from "alepha";
|
|
2
2
|
import { $logger } from "alepha/logger";
|
|
3
3
|
import { Readable } from "node:stream";
|
|
4
4
|
import { DateTimeProvider } from "alepha/datetime";
|
|
5
|
+
import { randomUUID } from "node:crypto";
|
|
5
6
|
import { ReadableStream as ReadableStream$1 } from "node:stream/web";
|
|
6
7
|
import { RouterProvider } from "alepha/router";
|
|
7
8
|
import { $cache } from "alepha/cache";
|
|
@@ -256,7 +257,7 @@ var ServerTimingProvider = class {
|
|
|
256
257
|
onRequest = $hook({
|
|
257
258
|
priority: "first",
|
|
258
259
|
on: "server:onRequest",
|
|
259
|
-
handler:
|
|
260
|
+
handler: ({ request }) => {
|
|
260
261
|
if (this.options.disabled) return;
|
|
261
262
|
request.metadata.timing = {};
|
|
262
263
|
request.metadata.timing[this.handlerName] = [Date.now()];
|
|
@@ -265,7 +266,7 @@ var ServerTimingProvider = class {
|
|
|
265
266
|
onResponse = $hook({
|
|
266
267
|
priority: "last",
|
|
267
268
|
on: "server:onResponse",
|
|
268
|
-
handler:
|
|
269
|
+
handler: ({ request }) => {
|
|
269
270
|
if (this.options.disabled) return;
|
|
270
271
|
if (request.metadata.timing) {
|
|
271
272
|
this.setDuration(this.handlerName, request.metadata.timing);
|
|
@@ -317,11 +318,12 @@ var ServerTimingProvider = class {
|
|
|
317
318
|
//#endregion
|
|
318
319
|
//#region ../../src/server/core/providers/ServerRouterProvider.ts
|
|
319
320
|
/**
|
|
320
|
-
* Main router for all routes
|
|
321
|
+
* Main router for all routes server side.
|
|
321
322
|
*
|
|
323
|
+
* Reminder:
|
|
322
324
|
* - $route => generic route
|
|
323
325
|
* - $action => action route (for API calls)
|
|
324
|
-
* - $page => React route (for SSR)
|
|
326
|
+
* - $page => React route (for React SSR)
|
|
325
327
|
*/
|
|
326
328
|
var ServerRouterProvider = class extends RouterProvider {
|
|
327
329
|
log = $logger();
|
|
@@ -329,6 +331,43 @@ var ServerRouterProvider = class extends RouterProvider {
|
|
|
329
331
|
routes = [];
|
|
330
332
|
serverTimingProvider = $inject(ServerTimingProvider);
|
|
331
333
|
serverRequestParser = $inject(ServerRequestParser);
|
|
334
|
+
compiledOnRequest;
|
|
335
|
+
compiledOnSend;
|
|
336
|
+
compiledOnResponse;
|
|
337
|
+
compiledOnError;
|
|
338
|
+
contextRunOptions = {
|
|
339
|
+
context: "",
|
|
340
|
+
_request: null,
|
|
341
|
+
_route: null,
|
|
342
|
+
_responseKind: "any"
|
|
343
|
+
};
|
|
344
|
+
processRequestBound = () => {
|
|
345
|
+
const opts = this.contextRunOptions;
|
|
346
|
+
return this.processRequest(opts._request, opts._route, opts._responseKind);
|
|
347
|
+
};
|
|
348
|
+
queryKeysCache = /* @__PURE__ */ new WeakMap();
|
|
349
|
+
/**
|
|
350
|
+
* Get cached keys for a query schema, computing them lazily on first access.
|
|
351
|
+
*/
|
|
352
|
+
getQuerySchemaKeys(schema) {
|
|
353
|
+
let keys = this.queryKeysCache.get(schema.properties);
|
|
354
|
+
if (!keys) {
|
|
355
|
+
keys = Object.keys(schema.properties);
|
|
356
|
+
this.queryKeysCache.set(schema.properties, keys);
|
|
357
|
+
}
|
|
358
|
+
return keys;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Compile event executors for optimal performance.
|
|
362
|
+
* Called lazily on first request after all hooks are registered.
|
|
363
|
+
*/
|
|
364
|
+
compileEvents() {
|
|
365
|
+
if (this.compiledOnRequest) return;
|
|
366
|
+
this.compiledOnRequest = this.alepha.events.compile("server:onRequest");
|
|
367
|
+
this.compiledOnSend = this.alepha.events.compile("server:onSend", { catch: true });
|
|
368
|
+
this.compiledOnResponse = this.alepha.events.compile("server:onResponse", { catch: true });
|
|
369
|
+
this.compiledOnError = this.alepha.events.compile("server:onError");
|
|
370
|
+
}
|
|
332
371
|
/**
|
|
333
372
|
* Get all registered routes, optionally filtered by a pattern.
|
|
334
373
|
*
|
|
@@ -342,6 +381,9 @@ var ServerRouterProvider = class extends RouterProvider {
|
|
|
342
381
|
} else return this.routes.filter((route) => route.path === pattern);
|
|
343
382
|
return this.routes;
|
|
344
383
|
}
|
|
384
|
+
/**
|
|
385
|
+
* Create a new server route.
|
|
386
|
+
*/
|
|
345
387
|
createRoute(route) {
|
|
346
388
|
route.method ??= "GET";
|
|
347
389
|
route.method = route.method.toUpperCase();
|
|
@@ -353,100 +395,135 @@ var ServerRouterProvider = class extends RouterProvider {
|
|
|
353
395
|
path,
|
|
354
396
|
handler: (rawRequest) => {
|
|
355
397
|
const request = this.serverRequestParser.createServerRequest(rawRequest);
|
|
356
|
-
|
|
398
|
+
const opts = this.contextRunOptions;
|
|
399
|
+
opts.context = this.getContextId(rawRequest.headers);
|
|
400
|
+
opts._request = request;
|
|
401
|
+
opts._route = route;
|
|
402
|
+
opts._responseKind = responseKind;
|
|
403
|
+
return this.alepha.context.run(this.processRequestBound, opts);
|
|
357
404
|
}
|
|
358
405
|
});
|
|
359
406
|
}
|
|
407
|
+
/**
|
|
408
|
+
* Get or generate a context ID from request headers.
|
|
409
|
+
*/
|
|
360
410
|
getContextId(headers) {
|
|
361
|
-
const contextId = headers[
|
|
362
|
-
if (
|
|
363
|
-
return
|
|
411
|
+
const contextId = headers[HEADER_REQUEST_ID] || headers[HEADER_CORRELATION_ID];
|
|
412
|
+
if (contextId) return contextId;
|
|
413
|
+
return randomUUID();
|
|
364
414
|
}
|
|
415
|
+
/**
|
|
416
|
+
* Process an incoming request through the full lifecycle:
|
|
417
|
+
* - onRequest hooks
|
|
418
|
+
* - route handler
|
|
419
|
+
* - onSend hooks
|
|
420
|
+
* - response serialization
|
|
421
|
+
* - onResponse hooks
|
|
422
|
+
*/
|
|
365
423
|
async processRequest(request, route, responseKind) {
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
424
|
+
this.compileEvents();
|
|
425
|
+
try {
|
|
426
|
+
await this.runRouteHandler(route, request, responseKind);
|
|
427
|
+
} catch (error) {
|
|
428
|
+
await this.errorHandler(route, request, error);
|
|
429
|
+
}
|
|
430
|
+
const reply = request.reply;
|
|
431
|
+
const payload = {
|
|
370
432
|
request,
|
|
371
|
-
route
|
|
372
|
-
|
|
433
|
+
route,
|
|
434
|
+
response: void 0
|
|
435
|
+
};
|
|
436
|
+
const onSendResult = this.compiledOnSend(payload);
|
|
437
|
+
if (onSendResult) await onSendResult;
|
|
373
438
|
const response = {
|
|
374
|
-
status:
|
|
375
|
-
headers:
|
|
376
|
-
body:
|
|
439
|
+
status: reply.status ?? (reply.body ? 200 : 204),
|
|
440
|
+
headers: reply.headers,
|
|
441
|
+
body: reply.body
|
|
377
442
|
};
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
response
|
|
382
|
-
}, { catch: true });
|
|
443
|
+
payload.response = response;
|
|
444
|
+
const onResponseResult = this.compiledOnResponse(payload);
|
|
445
|
+
if (onResponseResult) await onResponseResult;
|
|
383
446
|
return response;
|
|
384
447
|
}
|
|
448
|
+
/**
|
|
449
|
+
* Run the route handler with request validation and response serialization.
|
|
450
|
+
*/
|
|
385
451
|
async runRouteHandler(route, request, responseKind) {
|
|
386
|
-
|
|
452
|
+
const timing = this.serverTimingProvider;
|
|
453
|
+
const payload = {
|
|
387
454
|
request,
|
|
388
455
|
route
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
456
|
+
};
|
|
457
|
+
const onRequestResult = this.compiledOnRequest(payload);
|
|
458
|
+
if (onRequestResult) await onRequestResult;
|
|
459
|
+
const reply = request.reply;
|
|
460
|
+
if (reply.body || reply.status && reply.status >= 200) return;
|
|
461
|
+
this.alepha.context.set(CTX_REQUEST, request);
|
|
462
|
+
timing.beginTiming(TIMING_VALIDATE);
|
|
393
463
|
try {
|
|
394
464
|
this.validateRequest(route, request);
|
|
395
465
|
} finally {
|
|
396
|
-
|
|
466
|
+
timing.endTiming(TIMING_VALIDATE);
|
|
397
467
|
}
|
|
398
|
-
|
|
468
|
+
timing.beginTiming(TIMING_HANDLER);
|
|
399
469
|
try {
|
|
400
470
|
const result = await route.handler(request);
|
|
401
471
|
if (result) request.reply.body = result;
|
|
402
472
|
} finally {
|
|
403
|
-
|
|
473
|
+
timing.endTiming(TIMING_HANDLER);
|
|
404
474
|
}
|
|
405
|
-
|
|
475
|
+
timing.beginTiming(TIMING_SERIALIZE);
|
|
406
476
|
try {
|
|
407
477
|
this.serializeResponse(route, request.reply, responseKind);
|
|
408
478
|
} finally {
|
|
409
|
-
|
|
479
|
+
timing.endTiming(TIMING_SERIALIZE);
|
|
410
480
|
}
|
|
411
481
|
}
|
|
482
|
+
/**
|
|
483
|
+
* Transform reply body based on response kind and route schema.
|
|
484
|
+
*/
|
|
412
485
|
serializeResponse(route, reply, responseKind) {
|
|
486
|
+
const headers = reply.headers;
|
|
413
487
|
if (responseKind === "json" && route.schema?.response) {
|
|
414
|
-
|
|
415
|
-
reply.body = this.alepha.codec.encode(route.schema.response, reply.body,
|
|
488
|
+
headers[HEADER_CONTENT_TYPE] = CONTENT_TYPE_JSON;
|
|
489
|
+
reply.body = this.alepha.codec.encode(route.schema.response, reply.body, ENCODE_OPTIONS_STRING);
|
|
416
490
|
return;
|
|
417
491
|
}
|
|
418
492
|
if (responseKind === "file") {
|
|
419
493
|
if (!isFileLike(reply.body)) throw new HttpError({
|
|
420
494
|
status: 500,
|
|
421
|
-
message:
|
|
495
|
+
message: ERROR_NOT_FILE
|
|
422
496
|
});
|
|
423
|
-
|
|
424
|
-
|
|
497
|
+
headers[HEADER_CONTENT_TYPE] = reply.body.type;
|
|
498
|
+
headers[HEADER_CONTENT_DISPOSITION] = `attachment; filename="${reply.body.name.replaceAll("\"", "")}"`;
|
|
425
499
|
reply.body = reply.body.stream();
|
|
426
500
|
return;
|
|
427
501
|
}
|
|
428
502
|
if (responseKind === "text") {
|
|
429
503
|
reply.body = String(reply.body);
|
|
430
|
-
if (reply.body.startsWith("<!DOCTYPE html>"))
|
|
431
|
-
else
|
|
504
|
+
if (reply.body.length > 15 && reply.body.charCodeAt(0) === 60 && reply.body.startsWith("<!DOCTYPE html>")) headers[HEADER_CONTENT_TYPE] ??= CONTENT_TYPE_HTML;
|
|
505
|
+
else headers[HEADER_CONTENT_TYPE] ??= CONTENT_TYPE_TEXT;
|
|
432
506
|
return;
|
|
433
507
|
}
|
|
434
508
|
if (reply.body == null || responseKind === "void") {
|
|
435
|
-
delete
|
|
509
|
+
delete headers[HEADER_CONTENT_TYPE];
|
|
436
510
|
reply.body = void 0;
|
|
437
511
|
return;
|
|
438
512
|
}
|
|
439
513
|
if (Buffer.isBuffer(reply.body)) {
|
|
440
|
-
|
|
514
|
+
headers[HEADER_CONTENT_TYPE] ??= CONTENT_TYPE_OCTET;
|
|
441
515
|
return;
|
|
442
516
|
}
|
|
443
517
|
if (reply.body instanceof ReadableStream$1 || reply.body instanceof Readable) {
|
|
444
|
-
|
|
518
|
+
headers[HEADER_CONTENT_TYPE] ??= CONTENT_TYPE_OCTET;
|
|
445
519
|
return;
|
|
446
520
|
}
|
|
447
|
-
|
|
521
|
+
headers[HEADER_CONTENT_TYPE] ??= CONTENT_TYPE_TEXT;
|
|
448
522
|
reply.body = String(reply.body);
|
|
449
523
|
}
|
|
524
|
+
/**
|
|
525
|
+
* Determine response type based on route schema.
|
|
526
|
+
*/
|
|
450
527
|
getResponseType(schema) {
|
|
451
528
|
if (schema?.response) {
|
|
452
529
|
if (t.schema.isObject(schema.response) || t.schema.isRecord(schema.response) || t.schema.isArray(schema.response)) return "json";
|
|
@@ -456,42 +533,52 @@ var ServerRouterProvider = class extends RouterProvider {
|
|
|
456
533
|
}
|
|
457
534
|
return "any";
|
|
458
535
|
}
|
|
536
|
+
/**
|
|
537
|
+
* When an error occurs during request processing, this method is called.
|
|
538
|
+
*/
|
|
459
539
|
async errorHandler(route, request, error) {
|
|
460
|
-
|
|
461
|
-
|
|
540
|
+
const reply = request.reply;
|
|
541
|
+
const headers = reply.headers;
|
|
542
|
+
const requestId = request.requestId;
|
|
543
|
+
reply.body = null;
|
|
544
|
+
const onErrorResult = this.compiledOnError({
|
|
462
545
|
request,
|
|
463
546
|
route,
|
|
464
547
|
error
|
|
465
|
-
}
|
|
466
|
-
if (
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
548
|
+
});
|
|
549
|
+
if (onErrorResult) await onErrorResult;
|
|
550
|
+
if (!reply.body && !reply.status) if (error instanceof HttpError) {
|
|
551
|
+
reply.status = error.status;
|
|
552
|
+
headers[HEADER_CONTENT_TYPE] = CONTENT_TYPE_JSON;
|
|
553
|
+
const errorJson = HttpError.toJSON(error);
|
|
554
|
+
errorJson.requestId = requestId;
|
|
555
|
+
reply.body = JSON.stringify(errorJson);
|
|
473
556
|
} else {
|
|
474
557
|
if ("status" in error && typeof error.status === "number" && !!errorNameByStatus[error.status]) {
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
558
|
+
const status = error.status;
|
|
559
|
+
reply.status = status;
|
|
560
|
+
headers[HEADER_CONTENT_TYPE] = CONTENT_TYPE_JSON;
|
|
561
|
+
reply.body = JSON.stringify({
|
|
562
|
+
status,
|
|
563
|
+
error: errorNameByStatus[status],
|
|
480
564
|
message: error.message,
|
|
481
|
-
requestId
|
|
565
|
+
requestId
|
|
482
566
|
});
|
|
483
567
|
return;
|
|
484
568
|
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
569
|
+
reply.status = 500;
|
|
570
|
+
headers[HEADER_CONTENT_TYPE] = CONTENT_TYPE_JSON;
|
|
571
|
+
reply.body = JSON.stringify({
|
|
488
572
|
status: 500,
|
|
489
|
-
error:
|
|
573
|
+
error: ERROR_INTERNAL,
|
|
490
574
|
message: error.message,
|
|
491
|
-
requestId
|
|
575
|
+
requestId
|
|
492
576
|
});
|
|
493
577
|
}
|
|
494
578
|
}
|
|
579
|
+
/**
|
|
580
|
+
* Validate incoming request against route schema.
|
|
581
|
+
*/
|
|
495
582
|
validateRequest(route, request) {
|
|
496
583
|
if (route.schema?.params) try {
|
|
497
584
|
request.params = this.alepha.codec.validate(route.schema.params, request.params);
|
|
@@ -499,8 +586,13 @@ var ServerRouterProvider = class extends RouterProvider {
|
|
|
499
586
|
throw new ValidationError("Invalid request params", error);
|
|
500
587
|
}
|
|
501
588
|
if (route.schema?.query) try {
|
|
589
|
+
const schemaQuery = route.schema.query;
|
|
590
|
+
const keys = this.getQuerySchemaKeys(schemaQuery);
|
|
502
591
|
const query = {};
|
|
503
|
-
for (
|
|
592
|
+
for (let i = 0; i < keys.length; i++) {
|
|
593
|
+
const key = keys[i];
|
|
594
|
+
if (request.query[key] != null) query[key] = this.alepha.codec.decode(schemaQuery.properties[key], request.query[key]);
|
|
595
|
+
}
|
|
504
596
|
request.query = query;
|
|
505
597
|
} catch (error) {
|
|
506
598
|
throw new ValidationError("Invalid request query", error);
|
|
@@ -519,9 +611,28 @@ var ServerRouterProvider = class extends RouterProvider {
|
|
|
519
611
|
}
|
|
520
612
|
}
|
|
521
613
|
};
|
|
614
|
+
const ENCODE_OPTIONS_STRING = Object.freeze({ as: "string" });
|
|
615
|
+
const HEADER_CONTENT_TYPE = "content-type";
|
|
616
|
+
const HEADER_CONTENT_DISPOSITION = "content-disposition";
|
|
617
|
+
const HEADER_REQUEST_ID = "x-request-id";
|
|
618
|
+
const HEADER_CORRELATION_ID = "x-correlation-id";
|
|
619
|
+
const CONTENT_TYPE_JSON = "application/json";
|
|
620
|
+
const CONTENT_TYPE_TEXT = "text/plain";
|
|
621
|
+
const CONTENT_TYPE_HTML = "text/html; charset=UTF-8";
|
|
622
|
+
const CONTENT_TYPE_OCTET = "application/octet-stream";
|
|
623
|
+
const TIMING_VALIDATE = "validateRequest";
|
|
624
|
+
const TIMING_HANDLER = "runHandler";
|
|
625
|
+
const TIMING_SERIALIZE = "serializeResponse";
|
|
626
|
+
const CTX_REQUEST = "request";
|
|
627
|
+
const ERROR_INTERNAL = "InternalServerError";
|
|
628
|
+
const ERROR_NOT_FILE = "Invalid response body - not a file";
|
|
522
629
|
|
|
523
630
|
//#endregion
|
|
524
631
|
//#region ../../src/server/core/providers/ServerProvider.ts
|
|
632
|
+
const HEADER_X_FORWARDED_PROTO = "x-forwarded-proto";
|
|
633
|
+
const HEADER_HOST = "host";
|
|
634
|
+
const PROTO_HTTPS = "https://";
|
|
635
|
+
const PROTO_HTTP = "http://";
|
|
525
636
|
/**
|
|
526
637
|
* Base server provider to handle incoming requests and route them.
|
|
527
638
|
*
|
|
@@ -535,6 +646,64 @@ var ServerProvider = class {
|
|
|
535
646
|
dateTimeProvider = $inject(DateTimeProvider);
|
|
536
647
|
router = $inject(ServerRouterProvider);
|
|
537
648
|
internalServerErrorMessage = "Internal Server Error";
|
|
649
|
+
internalErrorResponse = Object.freeze({
|
|
650
|
+
status: 500,
|
|
651
|
+
headers: Object.freeze({ "content-type": "text/plain" }),
|
|
652
|
+
body: this.internalServerErrorMessage
|
|
653
|
+
});
|
|
654
|
+
handleInternalError = () => this.internalErrorResponse;
|
|
655
|
+
urlBaseCache = /* @__PURE__ */ new Map();
|
|
656
|
+
/**
|
|
657
|
+
* Get cached URL base (protocol + host) for a given host header.
|
|
658
|
+
* Caches the result to avoid repeated string concatenation.
|
|
659
|
+
*/
|
|
660
|
+
getUrlBase(headers) {
|
|
661
|
+
const host = headers[HEADER_HOST];
|
|
662
|
+
let base = this.urlBaseCache.get(host);
|
|
663
|
+
if (!base) {
|
|
664
|
+
base = (headers[HEADER_X_FORWARDED_PROTO] === "https" ? PROTO_HTTPS : PROTO_HTTP) + host;
|
|
665
|
+
if (this.urlBaseCache.size < 100) this.urlBaseCache.set(host, base);
|
|
666
|
+
}
|
|
667
|
+
return base;
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Parse query string manually - faster than new URL() + URLSearchParams.
|
|
671
|
+
* Returns empty object if no query string.
|
|
672
|
+
*/
|
|
673
|
+
parseQueryString(rawUrl) {
|
|
674
|
+
const qIndex = rawUrl.indexOf("?");
|
|
675
|
+
if (qIndex === -1) return {};
|
|
676
|
+
const qs = rawUrl.slice(qIndex + 1);
|
|
677
|
+
if (!qs) return {};
|
|
678
|
+
const query = {};
|
|
679
|
+
let start = 0;
|
|
680
|
+
let eqIdx = -1;
|
|
681
|
+
for (let i = 0; i <= qs.length; i++) {
|
|
682
|
+
const char = i < qs.length ? qs.charCodeAt(i) : 38;
|
|
683
|
+
if (char === 61) eqIdx = i;
|
|
684
|
+
else if (char === 38) {
|
|
685
|
+
if (eqIdx !== -1 && eqIdx > start) {
|
|
686
|
+
const key = qs.slice(start, eqIdx);
|
|
687
|
+
const value = qs.slice(eqIdx + 1, i);
|
|
688
|
+
query[this.fastDecode(key)] = this.fastDecode(value);
|
|
689
|
+
}
|
|
690
|
+
start = i + 1;
|
|
691
|
+
eqIdx = -1;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
return query;
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* Fast decode - only calls decodeURIComponent if needed.
|
|
698
|
+
*/
|
|
699
|
+
fastDecode(str) {
|
|
700
|
+
if (str.indexOf("%") === -1 && str.indexOf("+") === -1) return str;
|
|
701
|
+
try {
|
|
702
|
+
return decodeURIComponent(str.replace(/\+/g, " "));
|
|
703
|
+
} catch {
|
|
704
|
+
return str;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
538
707
|
get hostname() {
|
|
539
708
|
if (this.alepha.isViteDev()) return `http://localhost:${this.alepha.env.SERVER_PORT}`;
|
|
540
709
|
return "";
|
|
@@ -558,36 +727,32 @@ var ServerProvider = class {
|
|
|
558
727
|
/**
|
|
559
728
|
* Handle Node.js HTTP request event.
|
|
560
729
|
*
|
|
561
|
-
*
|
|
730
|
+
* Optimized to avoid expensive URL parsing when possible.
|
|
562
731
|
*/
|
|
563
732
|
async handleNodeRequest(nodeRequestEvent) {
|
|
564
733
|
const { req, res } = nodeRequestEvent;
|
|
565
|
-
const
|
|
566
|
-
|
|
734
|
+
const rawUrl = req.url;
|
|
735
|
+
const { route, params } = this.router.match(`/${req.method}${rawUrl}`);
|
|
567
736
|
if (!route) {
|
|
737
|
+
if (res.headersSent) return;
|
|
568
738
|
res.writeHead(404, { "content-type": "text/plain" });
|
|
569
739
|
res.end("Not Found");
|
|
570
740
|
return;
|
|
571
741
|
}
|
|
572
742
|
const headers = req.headers ?? {};
|
|
573
|
-
const
|
|
574
|
-
const
|
|
575
|
-
const
|
|
743
|
+
const method = req.method?.toUpperCase() ?? "GET";
|
|
744
|
+
const query = this.parseQueryString(rawUrl);
|
|
745
|
+
const urlBase = this.getUrlBase(headers);
|
|
576
746
|
const request = {
|
|
577
|
-
method
|
|
578
|
-
url,
|
|
747
|
+
method,
|
|
748
|
+
url: new URL(rawUrl, urlBase),
|
|
579
749
|
headers,
|
|
580
750
|
params: params ?? {},
|
|
581
751
|
query,
|
|
582
752
|
raw: { node: nodeRequestEvent }
|
|
583
753
|
};
|
|
584
|
-
const response = await route.handler(request).catch(
|
|
585
|
-
|
|
586
|
-
status: 500,
|
|
587
|
-
headers: { "content-type": "text/plain" },
|
|
588
|
-
body: this.internalServerErrorMessage
|
|
589
|
-
};
|
|
590
|
-
});
|
|
754
|
+
const response = await route.handler(request).catch(this.handleInternalError);
|
|
755
|
+
if (res.headersSent) return;
|
|
591
756
|
if (!response.body) {
|
|
592
757
|
res.writeHead(response.status, response.headers).end();
|
|
593
758
|
return;
|
|
@@ -637,7 +802,7 @@ var ServerProvider = class {
|
|
|
637
802
|
req.headers.forEach((value, key) => {
|
|
638
803
|
headers[key] = value;
|
|
639
804
|
});
|
|
640
|
-
const query = Object.fromEntries(url.searchParams.entries());
|
|
805
|
+
const query = url.search ? Object.fromEntries(url.searchParams.entries()) : {};
|
|
641
806
|
const request = {
|
|
642
807
|
method: req.method.toUpperCase() ?? "GET",
|
|
643
808
|
url,
|
|
@@ -646,13 +811,7 @@ var ServerProvider = class {
|
|
|
646
811
|
query,
|
|
647
812
|
raw: { web: ev }
|
|
648
813
|
};
|
|
649
|
-
const response = await route.handler(request).catch(
|
|
650
|
-
return {
|
|
651
|
-
status: 500,
|
|
652
|
-
headers: { "content-type": "text/plain" },
|
|
653
|
-
body: this.internalServerErrorMessage
|
|
654
|
-
};
|
|
655
|
-
});
|
|
814
|
+
const response = await route.handler(request).catch(this.handleInternalError);
|
|
656
815
|
if (!response.body) {
|
|
657
816
|
ev.res = new Response(null, {
|
|
658
817
|
status: response.status,
|
|
@@ -1023,8 +1182,8 @@ var HttpClient = class {
|
|
|
1023
1182
|
*/
|
|
1024
1183
|
const $action = (options) => {
|
|
1025
1184
|
const instance = createPrimitive(ActionPrimitive, options);
|
|
1026
|
-
const fn = (config, options
|
|
1027
|
-
return instance.run(config, options
|
|
1185
|
+
const fn = (config, options) => {
|
|
1186
|
+
return instance.run(config, options);
|
|
1028
1187
|
};
|
|
1029
1188
|
Object.defineProperty(fn, "name", { get() {
|
|
1030
1189
|
return instance.options.name || instance.config.propertyKey;
|
|
@@ -1248,7 +1407,7 @@ var BunHttpServerProvider = class extends ServerProvider {
|
|
|
1248
1407
|
});
|
|
1249
1408
|
}
|
|
1250
1409
|
});
|
|
1251
|
-
this.log.info(`Server listening on ${this.hostname}
|
|
1410
|
+
this.log.info(`Server listening on ${this.hostname}/`);
|
|
1252
1411
|
} catch (err) {
|
|
1253
1412
|
this.log.error("Failed to start Bun server", err);
|
|
1254
1413
|
throw err;
|
|
@@ -1288,6 +1447,14 @@ var NodeHttpServerProvider = class extends ServerProvider {
|
|
|
1288
1447
|
log = $logger();
|
|
1289
1448
|
env = $env(envSchema$1);
|
|
1290
1449
|
router = $inject(ServerRouterProvider);
|
|
1450
|
+
/** Track active connections for fast shutdown */
|
|
1451
|
+
connections = /* @__PURE__ */ new Set();
|
|
1452
|
+
/** Get number of active connections */
|
|
1453
|
+
getConnectionsCount() {
|
|
1454
|
+
return this.connections.size;
|
|
1455
|
+
}
|
|
1456
|
+
/** Server options */
|
|
1457
|
+
options = { shutdownTimeout: 1e4 };
|
|
1291
1458
|
get hostname() {
|
|
1292
1459
|
if (this.server.listening) {
|
|
1293
1460
|
const address = this.server.address();
|
|
@@ -1295,16 +1462,20 @@ var NodeHttpServerProvider = class extends ServerProvider {
|
|
|
1295
1462
|
}
|
|
1296
1463
|
return `http://${this.env.SERVER_HOST}:${this.env.SERVER_PORT}`;
|
|
1297
1464
|
}
|
|
1465
|
+
handleRequestError = (res, err) => {
|
|
1466
|
+
this.log.error("Error handling request", err);
|
|
1467
|
+
res.statusCode = 500;
|
|
1468
|
+
res.end("Internal Server Error");
|
|
1469
|
+
};
|
|
1470
|
+
nodeRequestEvent = {
|
|
1471
|
+
req: null,
|
|
1472
|
+
res: null
|
|
1473
|
+
};
|
|
1298
1474
|
server = this.createHttpServer((req, res) => {
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
}).catch((err) => {
|
|
1304
|
-
this.log.error("Error handling request", err);
|
|
1305
|
-
res.statusCode = 500;
|
|
1306
|
-
res.end("Internal Server Error");
|
|
1307
|
-
});
|
|
1475
|
+
const ev = this.nodeRequestEvent;
|
|
1476
|
+
ev.req = req;
|
|
1477
|
+
ev.res = res;
|
|
1478
|
+
this.handleNodeRequest(ev).catch((err) => this.handleRequestError(res, err));
|
|
1308
1479
|
});
|
|
1309
1480
|
start = $hook({
|
|
1310
1481
|
on: "start",
|
|
@@ -1314,16 +1485,17 @@ var NodeHttpServerProvider = class extends ServerProvider {
|
|
|
1314
1485
|
}
|
|
1315
1486
|
});
|
|
1316
1487
|
createHttpServer(func) {
|
|
1317
|
-
|
|
1488
|
+
const server = createServer({ keepAlive: this.alepha.isProduction() }, func);
|
|
1489
|
+
server.on("connection", (socket) => {
|
|
1490
|
+
this.connections.add(socket);
|
|
1491
|
+
socket.on("close", () => this.connections.delete(socket));
|
|
1492
|
+
});
|
|
1493
|
+
return server;
|
|
1318
1494
|
}
|
|
1319
1495
|
stop = $hook({
|
|
1320
1496
|
on: "stop",
|
|
1321
1497
|
handler: async () => {
|
|
1322
|
-
|
|
1323
|
-
await this.close();
|
|
1324
|
-
return;
|
|
1325
|
-
}
|
|
1326
|
-
this.close().catch(() => {});
|
|
1498
|
+
await this.close();
|
|
1327
1499
|
}
|
|
1328
1500
|
});
|
|
1329
1501
|
async listen() {
|
|
@@ -1331,7 +1503,7 @@ var NodeHttpServerProvider = class extends ServerProvider {
|
|
|
1331
1503
|
if (this.alepha.isTest() && port === 3e3) port = 0;
|
|
1332
1504
|
await new Promise((resolve, reject) => {
|
|
1333
1505
|
this.server?.listen(port, this.env.SERVER_HOST, () => {
|
|
1334
|
-
this.log.info(`Server listening on ${this.hostname}
|
|
1506
|
+
this.log.info(`Server listening on ${this.hostname}/`);
|
|
1335
1507
|
resolve();
|
|
1336
1508
|
});
|
|
1337
1509
|
this.server?.on("error", (err) => {
|
|
@@ -1340,15 +1512,28 @@ var NodeHttpServerProvider = class extends ServerProvider {
|
|
|
1340
1512
|
});
|
|
1341
1513
|
}
|
|
1342
1514
|
async close() {
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
else resolve();
|
|
1347
|
-
});
|
|
1515
|
+
if (!this.alepha.isProduction()) this.destroyAllConnections();
|
|
1516
|
+
const closePromise = new Promise((resolve, reject) => {
|
|
1517
|
+
this.server?.close((err) => err ? reject(err) : resolve());
|
|
1348
1518
|
});
|
|
1349
|
-
|
|
1519
|
+
if (this.alepha.isProduction() && this.connections.size > 0) {
|
|
1520
|
+
const timeout = this.options.shutdownTimeout;
|
|
1521
|
+
const timeoutId = setTimeout(() => {
|
|
1522
|
+
if (this.connections.size > 0) {
|
|
1523
|
+
this.log.warn(`Shutdown timeout (${timeout}ms) reached, forcing ${this.connections.size} connections to close`);
|
|
1524
|
+
for (const socket of this.connections) socket.destroy();
|
|
1525
|
+
}
|
|
1526
|
+
}, timeout);
|
|
1527
|
+
await closePromise;
|
|
1528
|
+
clearTimeout(timeoutId);
|
|
1529
|
+
this.connections.clear();
|
|
1530
|
+
} else await closePromise;
|
|
1350
1531
|
this.log.info("Server closed");
|
|
1351
1532
|
}
|
|
1533
|
+
destroyAllConnections() {
|
|
1534
|
+
for (const socket of this.connections) socket.destroy();
|
|
1535
|
+
this.connections.clear();
|
|
1536
|
+
}
|
|
1352
1537
|
};
|
|
1353
1538
|
|
|
1354
1539
|
//#endregion
|
|
@@ -1370,22 +1555,21 @@ var ServerBodyParserProvider = class {
|
|
|
1370
1555
|
log = $logger();
|
|
1371
1556
|
onRequest = $hook({
|
|
1372
1557
|
on: "server:onRequest",
|
|
1373
|
-
handler:
|
|
1558
|
+
handler: ({ route, request }) => {
|
|
1374
1559
|
if (request.body) return;
|
|
1375
1560
|
let stream;
|
|
1376
1561
|
if (request.raw.web?.req.body) stream = request.raw.web.req.body;
|
|
1377
1562
|
else if (request.raw.node?.req) stream = ReadableStream$1.from(request.raw.node.req);
|
|
1378
1563
|
if (!stream) return;
|
|
1379
|
-
if (route.schema?.body)
|
|
1380
|
-
const body = await this.parse(stream, request.headers, route.schema.body);
|
|
1564
|
+
if (route.schema?.body) return this.parse(stream, request.headers, route.schema.body).then((body) => {
|
|
1381
1565
|
if (body) request.body = body;
|
|
1382
|
-
}
|
|
1566
|
+
}).catch((error) => {
|
|
1383
1567
|
if (error instanceof HttpError) throw error;
|
|
1384
1568
|
throw new HttpError({
|
|
1385
1569
|
status: 400,
|
|
1386
1570
|
message: "Failed to parse request body"
|
|
1387
1571
|
}, error);
|
|
1388
|
-
}
|
|
1572
|
+
});
|
|
1389
1573
|
}
|
|
1390
1574
|
});
|
|
1391
1575
|
async parse(stream, headers, schema) {
|
|
@@ -1475,19 +1659,18 @@ var ServerLoggerProvider = class {
|
|
|
1475
1659
|
on: "server:onRequest",
|
|
1476
1660
|
priority: "first",
|
|
1477
1661
|
handler: ({ route, request }) => {
|
|
1478
|
-
if (
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
}
|
|
1489
|
-
this.log.info("Incoming request", data);
|
|
1662
|
+
if (route.silent || request.metadata.vite) return;
|
|
1663
|
+
request.metadata.now = Date.now();
|
|
1664
|
+
const data = {
|
|
1665
|
+
method: request.method,
|
|
1666
|
+
path: request.url.pathname
|
|
1667
|
+
};
|
|
1668
|
+
if (this.alepha.isProduction()) {
|
|
1669
|
+
data.agent = request.headers["user-agent"];
|
|
1670
|
+
const ip = request.ip;
|
|
1671
|
+
if (ip) data.ip = ip;
|
|
1490
1672
|
}
|
|
1673
|
+
this.log.info("Incoming request", data);
|
|
1491
1674
|
}
|
|
1492
1675
|
});
|
|
1493
1676
|
onError = $hook({
|
|
@@ -1501,13 +1684,12 @@ var ServerLoggerProvider = class {
|
|
|
1501
1684
|
on: "server:onResponse",
|
|
1502
1685
|
priority: "last",
|
|
1503
1686
|
handler: ({ route, request, response }) => {
|
|
1504
|
-
if (
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
}
|
|
1687
|
+
if (route.silent || request.metadata.vite) return;
|
|
1688
|
+
const ms = Date.now() - request.metadata.now;
|
|
1689
|
+
this.log.info("Request completed", {
|
|
1690
|
+
status: response.status,
|
|
1691
|
+
ms
|
|
1692
|
+
});
|
|
1511
1693
|
}
|
|
1512
1694
|
});
|
|
1513
1695
|
};
|
|
@@ -1648,7 +1830,7 @@ const AlephaServer = $module({
|
|
|
1648
1830
|
ServerRouterProvider
|
|
1649
1831
|
],
|
|
1650
1832
|
register: (alepha) => {
|
|
1651
|
-
if (!alepha.isServerless()
|
|
1833
|
+
if (!alepha.isServerless()) if (alepha.isBun()) alepha.with({
|
|
1652
1834
|
optional: true,
|
|
1653
1835
|
provide: ServerProvider,
|
|
1654
1836
|
use: BunHttpServerProvider
|