alepha 0.15.3 → 0.15.5
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 +26 -11
- package/dist/api/audits/index.d.ts +335 -335
- package/dist/api/audits/index.d.ts.map +1 -1
- package/dist/api/audits/index.js +11 -3
- package/dist/api/audits/index.js.map +1 -1
- package/dist/api/files/index.d.ts +3 -3
- package/dist/api/files/index.js +4 -3
- package/dist/api/files/index.js.map +1 -1
- package/dist/api/jobs/index.d.ts +198 -155
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/jobs/index.js +103 -5
- package/dist/api/jobs/index.js.map +1 -1
- package/dist/api/keys/index.d.ts +198 -198
- package/dist/api/keys/index.d.ts.map +1 -1
- package/dist/api/keys/index.js +3 -3
- package/dist/api/keys/index.js.map +1 -1
- package/dist/api/notifications/index.browser.js +1 -0
- package/dist/api/notifications/index.browser.js.map +1 -1
- package/dist/api/notifications/index.d.ts +3 -3
- package/dist/api/notifications/index.js +4 -3
- package/dist/api/notifications/index.js.map +1 -1
- package/dist/api/parameters/index.d.ts +263 -263
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/parameters/index.js +41 -30
- package/dist/api/parameters/index.js.map +1 -1
- package/dist/api/users/index.d.ts +383 -77
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +284 -72
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/verifications/index.d.ts +131 -131
- package/dist/api/verifications/index.d.ts.map +1 -1
- package/dist/api/verifications/index.js +3 -3
- package/dist/api/verifications/index.js.map +1 -1
- package/dist/batch/index.d.ts +3 -3
- package/dist/batch/index.js +3 -3
- package/dist/batch/index.js.map +1 -1
- package/dist/bucket/index.d.ts +3 -3
- package/dist/bucket/index.js +6 -6
- package/dist/bucket/index.js.map +1 -1
- package/dist/cache/core/index.d.ts +3 -3
- package/dist/cache/core/index.js +3 -3
- package/dist/cache/core/index.js.map +1 -1
- package/dist/cli/index.d.ts +5612 -20
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +122 -91
- package/dist/cli/index.js.map +1 -1
- package/dist/command/index.d.ts +11 -4
- package/dist/command/index.d.ts.map +1 -1
- package/dist/command/index.js +8 -6
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +4 -8
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +3 -3
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js.map +1 -1
- package/dist/datetime/index.d.ts +3 -3
- package/dist/datetime/index.js +3 -3
- package/dist/datetime/index.js.map +1 -1
- package/dist/email/index.d.ts +16 -16
- package/dist/email/index.d.ts.map +1 -1
- package/dist/email/index.js +10562 -10
- package/dist/email/index.js.map +1 -1
- package/dist/fake/index.d.ts +3 -3
- package/dist/fake/index.js +3 -3
- package/dist/fake/index.js.map +1 -1
- package/dist/lock/core/index.d.ts +9 -4
- package/dist/lock/core/index.d.ts.map +1 -1
- package/dist/lock/core/index.js +12 -4
- package/dist/lock/core/index.js.map +1 -1
- package/dist/logger/index.d.ts +3 -3
- package/dist/logger/index.js +6 -3
- package/dist/logger/index.js.map +1 -1
- package/dist/mcp/index.d.ts +3 -3
- package/dist/mcp/index.js +3 -3
- package/dist/mcp/index.js.map +1 -1
- package/dist/orm/index.d.ts +12 -12
- package/dist/orm/index.js +4 -4
- package/dist/orm/index.js.map +1 -1
- package/dist/queue/core/index.d.ts +3 -3
- package/dist/queue/core/index.js +3 -3
- package/dist/queue/core/index.js.map +1 -1
- package/dist/react/auth/index.browser.js +2 -1
- package/dist/react/auth/index.browser.js.map +1 -1
- package/dist/react/auth/index.d.ts +3 -3
- package/dist/react/auth/index.js +5 -4
- package/dist/react/auth/index.js.map +1 -1
- package/dist/react/core/index.d.ts +6 -6
- package/dist/react/core/index.js +3 -3
- package/dist/react/core/index.js.map +1 -1
- package/dist/react/form/index.d.ts +3 -3
- package/dist/react/form/index.js +3 -3
- package/dist/react/form/index.js.map +1 -1
- package/dist/react/head/index.d.ts +3 -3
- package/dist/react/head/index.js +3 -3
- package/dist/react/head/index.js.map +1 -1
- package/dist/react/i18n/index.d.ts +3 -3
- package/dist/react/i18n/index.js +3 -3
- package/dist/react/i18n/index.js.map +1 -1
- package/dist/react/intro/index.css +337 -0
- package/dist/react/intro/index.css.map +1 -0
- package/dist/react/intro/index.d.ts +10 -0
- package/dist/react/intro/index.d.ts.map +1 -0
- package/dist/react/intro/index.js +222 -0
- package/dist/react/intro/index.js.map +1 -0
- package/dist/react/router/index.browser.js +2 -2
- package/dist/react/router/index.browser.js.map +1 -1
- package/dist/react/router/index.d.ts +11 -1
- package/dist/react/router/index.d.ts.map +1 -1
- package/dist/react/router/index.js +21 -11
- package/dist/react/router/index.js.map +1 -1
- package/dist/redis/index.d.ts +22 -22
- package/dist/redis/index.js +3 -3
- package/dist/redis/index.js.map +1 -1
- package/dist/retry/index.d.ts +3 -3
- package/dist/retry/index.js +3 -3
- package/dist/retry/index.js.map +1 -1
- package/dist/scheduler/index.d.ts +16 -4
- package/dist/scheduler/index.d.ts.map +1 -1
- package/dist/scheduler/index.js +45 -7
- package/dist/scheduler/index.js.map +1 -1
- package/dist/security/index.d.ts +3 -3
- package/dist/security/index.js +5 -5
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.d.ts +3 -3
- package/dist/server/auth/index.js +3 -3
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cache/index.d.ts +3 -3
- package/dist/server/cache/index.js +3 -3
- package/dist/server/cache/index.js.map +1 -1
- package/dist/server/compress/index.d.ts +3 -3
- package/dist/server/compress/index.d.ts.map +1 -1
- package/dist/server/compress/index.js +4 -3
- package/dist/server/compress/index.js.map +1 -1
- package/dist/server/cookies/index.d.ts +3 -3
- package/dist/server/cookies/index.js +3 -3
- package/dist/server/cookies/index.js.map +1 -1
- package/dist/server/core/index.d.ts +14 -25
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js +13 -29
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/cors/index.d.ts +3 -3
- package/dist/server/cors/index.js +3 -3
- package/dist/server/cors/index.js.map +1 -1
- package/dist/server/health/index.d.ts +20 -20
- package/dist/server/health/index.js +3 -3
- package/dist/server/health/index.js.map +1 -1
- package/dist/server/helmet/index.d.ts +3 -3
- package/dist/server/helmet/index.js +3 -3
- package/dist/server/helmet/index.js.map +1 -1
- package/dist/server/links/index.d.ts +42 -42
- package/dist/server/links/index.d.ts.map +1 -1
- package/dist/server/links/index.js +4 -4
- package/dist/server/links/index.js.map +1 -1
- package/dist/server/metrics/index.d.ts +3 -3
- package/dist/server/metrics/index.js +3 -3
- package/dist/server/metrics/index.js.map +1 -1
- package/dist/server/multipart/index.d.ts +3 -3
- package/dist/server/multipart/index.js +3 -3
- package/dist/server/multipart/index.js.map +1 -1
- package/dist/server/proxy/index.d.ts +3 -3
- package/dist/server/proxy/index.js +3 -3
- package/dist/server/proxy/index.js.map +1 -1
- package/dist/server/rate-limit/index.d.ts +3 -3
- package/dist/server/rate-limit/index.js +3 -3
- package/dist/server/rate-limit/index.js.map +1 -1
- package/dist/server/static/index.d.ts +3 -3
- package/dist/server/static/index.js +6 -6
- package/dist/server/static/index.js.map +1 -1
- package/dist/server/swagger/index.d.ts +3 -3
- package/dist/server/swagger/index.js +6 -6
- package/dist/server/swagger/index.js.map +1 -1
- package/dist/sms/index.d.ts +3 -3
- package/dist/sms/index.js +6 -6
- package/dist/sms/index.js.map +1 -1
- package/dist/system/index.d.ts +3 -3
- package/dist/system/index.js +3 -3
- package/dist/system/index.js.map +1 -1
- package/dist/thread/index.d.ts +3 -3
- package/dist/thread/index.js +3 -3
- package/dist/thread/index.js.map +1 -1
- package/dist/topic/core/index.d.ts +3 -3
- package/dist/topic/core/index.js +3 -3
- package/dist/topic/core/index.js.map +1 -1
- package/dist/vite/index.d.ts +6286 -4
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +28 -2
- package/dist/vite/index.js.map +1 -1
- package/dist/websocket/index.d.ts +37 -37
- package/dist/websocket/index.d.ts.map +1 -1
- package/dist/websocket/index.js +3 -3
- package/dist/websocket/index.js.map +1 -1
- package/package.json +12 -4
- package/src/api/audits/controllers/AdminAuditController.ts +8 -0
- package/src/api/audits/index.ts +3 -3
- package/src/api/files/controllers/AdminFileStatsController.ts +1 -0
- package/src/api/files/index.ts +3 -3
- package/src/api/jobs/controllers/AdminJobController.ts +18 -2
- package/src/api/jobs/index.ts +4 -3
- package/src/api/jobs/services/JobAudits.spec.ts +89 -0
- package/src/api/jobs/services/JobAudits.ts +101 -0
- package/src/api/keys/index.ts +3 -3
- package/src/api/notifications/controllers/AdminNotificationController.ts +1 -0
- package/src/api/notifications/index.ts +3 -3
- package/src/api/parameters/controllers/AdminConfigController.ts +10 -0
- package/src/api/parameters/index.ts +5 -3
- package/src/api/users/__tests__/ApiKeys-integration.spec.ts +1 -1
- package/src/api/users/__tests__/ApiKeys.spec.ts +1 -1
- package/src/api/users/__tests__/EmailVerification.spec.ts +16 -1
- package/src/api/users/__tests__/PasswordReset.spec.ts +11 -0
- package/src/api/users/atoms/realmAuthSettingsAtom.ts +10 -0
- package/src/api/users/controllers/AdminIdentityController.ts +3 -0
- package/src/api/users/controllers/AdminSessionController.ts +3 -0
- package/src/api/users/controllers/AdminUserController.ts +5 -0
- package/src/api/users/index.ts +8 -9
- package/src/api/users/primitives/$realm.ts +117 -19
- package/src/api/users/providers/RealmProvider.ts +15 -7
- package/src/api/users/services/CredentialService.spec.ts +11 -0
- package/src/api/users/services/CredentialService.ts +47 -24
- package/src/api/users/services/IdentityService.ts +12 -4
- package/src/api/users/services/RegistrationService.spec.ts +11 -0
- package/src/api/users/services/RegistrationService.ts +33 -12
- package/src/api/users/services/SessionService.ts +83 -12
- package/src/api/users/services/UserAudits.ts +47 -0
- package/src/api/users/services/UserFiles.ts +19 -0
- package/src/api/users/services/UserJobs.spec.ts +107 -0
- package/src/api/users/services/UserJobs.ts +62 -0
- package/src/api/users/services/UserParameters.ts +23 -0
- package/src/api/users/services/UserService.ts +34 -17
- package/src/api/verifications/index.ts +3 -3
- package/src/batch/index.ts +3 -3
- package/src/bucket/index.ts +3 -3
- package/src/cache/core/index.ts +3 -3
- package/src/cli/commands/build.ts +1 -0
- package/src/cli/commands/db.ts +9 -0
- package/src/cli/commands/init.spec.ts +2 -17
- package/src/cli/commands/init.ts +37 -1
- package/src/cli/providers/ViteDevServerProvider.ts +36 -2
- package/src/cli/services/AlephaCliUtils.ts +17 -0
- package/src/cli/services/PackageManagerUtils.ts +15 -1
- package/src/cli/services/ProjectScaffolder.ts +8 -13
- package/src/cli/templates/agentMd.ts +2 -25
- package/src/cli/templates/apiAppSecurityTs.ts +37 -2
- package/src/cli/templates/mainCss.ts +2 -32
- package/src/cli/templates/webAppRouterTs.ts +5 -5
- package/src/cli/templates/webHomeComponentTsx.ts +10 -0
- package/src/command/helpers/Runner.ts +14 -1
- package/src/command/index.ts +3 -3
- package/src/core/helpers/primitive.ts +0 -5
- package/src/core/index.ts +3 -3
- package/src/datetime/index.ts +3 -3
- package/src/email/index.ts +3 -3
- package/src/email/index.workerd.ts +36 -0
- package/src/email/providers/LocalEmailProvider.ts +2 -2
- package/src/email/providers/WorkermailerEmailProvider.ts +221 -0
- package/src/fake/index.ts +3 -3
- package/src/lock/core/index.ts +3 -3
- package/src/lock/core/primitives/$lock.ts +13 -1
- package/src/logger/index.ts +3 -3
- package/src/logger/providers/PrettyFormatterProvider.ts +7 -0
- package/src/mcp/index.ts +3 -3
- package/src/orm/index.ts +3 -3
- package/src/orm/providers/drivers/NodeSqliteProvider.ts +1 -1
- package/src/queue/core/index.ts +3 -3
- package/src/react/auth/index.ts +3 -3
- package/src/react/auth/services/ReactAuth.ts +3 -1
- package/src/react/core/index.ts +3 -3
- package/src/react/form/index.ts +3 -3
- package/src/react/head/index.ts +3 -3
- package/src/react/i18n/index.ts +3 -3
- package/src/react/intro/components/GettingStarted.css +334 -0
- package/src/react/intro/components/GettingStarted.tsx +276 -0
- package/src/react/intro/index.ts +1 -0
- package/src/react/router/atoms/ssrManifestAtom.ts +7 -0
- package/src/react/router/index.browser.ts +2 -0
- package/src/react/router/index.ts +2 -0
- package/src/react/router/providers/ReactServerProvider.ts +14 -4
- package/src/react/router/providers/SSRManifestProvider.ts +7 -0
- package/src/redis/index.ts +3 -3
- package/src/retry/index.ts +3 -3
- package/src/router/index.ts +3 -3
- package/src/scheduler/index.ts +3 -3
- package/src/scheduler/index.workerd.ts +43 -0
- package/src/scheduler/providers/CronProvider.ts +53 -6
- package/src/scheduler/providers/WorkerdCronProvider.ts +102 -0
- package/src/security/index.ts +3 -3
- package/src/security/providers/JwtProvider.ts +2 -2
- package/src/server/auth/index.ts +3 -3
- package/src/server/cache/index.ts +3 -3
- package/src/server/compress/index.ts +3 -3
- package/src/server/compress/providers/ServerCompressProvider.ts +6 -0
- package/src/server/cookies/index.ts +3 -3
- package/src/server/core/index.ts +3 -3
- package/src/server/core/primitives/$action.spec.ts +3 -2
- package/src/server/core/primitives/$action.ts +6 -2
- package/src/server/core/providers/NodeHttpServerProvider.ts +2 -15
- package/src/server/core/providers/ServerProvider.ts +4 -2
- package/src/server/core/providers/ServerRouterProvider.ts +5 -27
- package/src/server/cors/index.ts +3 -3
- package/src/server/health/index.ts +3 -3
- package/src/server/helmet/index.ts +3 -3
- package/src/server/links/index.ts +3 -3
- package/src/server/links/providers/ServerLinksProvider.spec.ts +332 -0
- package/src/server/links/providers/ServerLinksProvider.ts +1 -1
- package/src/server/metrics/index.ts +3 -3
- package/src/server/multipart/index.ts +3 -3
- package/src/server/proxy/index.ts +3 -3
- package/src/server/rate-limit/index.ts +3 -3
- package/src/server/static/index.ts +3 -3
- package/src/server/swagger/index.ts +3 -3
- package/src/sms/index.ts +3 -3
- package/src/system/index.ts +3 -3
- package/src/thread/index.ts +3 -3
- package/src/topic/core/index.ts +3 -3
- package/src/vite/tasks/generateCloudflare.ts +38 -2
- package/src/websocket/index.ts +3 -3
- package/src/cli/templates/webHelloComponentTsx.ts +0 -30
- /package/src/api/users/{notifications → services}/UserNotifications.ts +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["envSchema","NodeWebStream","NodeStream","envSchema","envSchema","envSchema","WebStream"],"sources":["../../../src/server/core/helpers/isMultipart.ts","../../../src/server/core/helpers/ServerReply.ts","../../../src/server/core/errors/HttpError.ts","../../../src/server/core/errors/ValidationError.ts","../../../src/server/core/services/UserAgentParser.ts","../../../src/server/core/services/ServerRequestParser.ts","../../../src/server/core/providers/ServerTimingProvider.ts","../../../src/server/core/providers/ServerRouterProvider.ts","../../../src/server/core/providers/ServerProvider.ts","../../../src/server/core/schemas/errorSchema.ts","../../../src/server/core/services/HttpClient.ts","../../../src/server/core/primitives/$action.ts","../../../src/server/core/primitives/$route.ts","../../../src/server/core/providers/BunHttpServerProvider.ts","../../../src/server/core/providers/NodeHttpServerProvider.ts","../../../src/server/core/providers/ServerBodyParserProvider.ts","../../../src/server/core/providers/ServerLoggerProvider.ts","../../../src/server/core/providers/ServerNotReadyProvider.ts","../../../src/server/core/constants/routeMethods.ts","../../../src/server/core/errors/BadRequestError.ts","../../../src/server/core/errors/ConflictError.ts","../../../src/server/core/errors/ForbiddenError.ts","../../../src/server/core/errors/NotFoundError.ts","../../../src/server/core/errors/UnauthorizedError.ts","../../../src/server/core/schemas/okSchema.ts","../../../src/server/core/index.ts"],"sourcesContent":["import type { RequestConfigSchema } from \"../interfaces/ServerRequest.ts\";\n\n/**\n * Checks if the route has multipart/form-data request body.\n */\nexport const isMultipart = (options: {\n schema?: RequestConfigSchema;\n requestBodyType?: string;\n}): boolean => {\n if (options.requestBodyType === \"multipart/form-data\") {\n return true;\n }\n\n if (options.schema?.body && \"properties\" in options.schema.body) {\n const properties: Record<string, any> = options.schema.body.properties;\n for (const key in properties) {\n if (properties[key].format === \"binary\") {\n return true;\n }\n }\n }\n\n return false;\n};\n","/**\n * Helper for building server replies.\n */\nexport class ServerReply {\n // TODO: make it private\n public headers: Record<string, string> & {\n \"set-cookie\"?: string[];\n } = {};\n\n public status?: number; // default 200, or 204 (no content)\n\n public body?: any;\n\n /**\n * Redirect to a given URL with optional status code (default 301).\n */\n public redirect(url: string, status: number = 301): void {\n this.status = status;\n this.headers.location = url;\n }\n\n // TODO: check if status / header is already set and throw an error if so (for allow to override with force flag)\n\n /**\n * Set the response status code.\n */\n public setStatus(status: number): this {\n this.status = status;\n return this;\n }\n\n /**\n * Set a response header.\n */\n public setHeader(name: string, value: string): this {\n this.headers[name.toLowerCase()] = value;\n return this;\n }\n\n /**\n * Set the response body.\n */\n public setBody(body: any): this {\n this.body = body;\n return this;\n }\n}\n","import { AlephaError } from \"alepha\";\nimport type { ErrorSchema } from \"../schemas/errorSchema.ts\";\n\nexport const isHttpError = (\n error: unknown,\n status?: number,\n): error is HttpErrorLike => {\n const isError =\n !!error &&\n typeof error === \"object\" &&\n \"message\" in error &&\n typeof error.message === \"string\" &&\n \"status\" in error &&\n typeof error.status === \"number\";\n\n if (!isError) {\n return false;\n }\n\n if (status) {\n return (error as HttpErrorLike).status === status;\n }\n\n return true;\n};\n\nexport class HttpError extends AlephaError {\n public name = \"HttpError\";\n\n static is = isHttpError;\n\n static toJSON(error: HttpError): ErrorSchema {\n const json: Record<string, unknown> = {\n error: error.error,\n status: error.status,\n message: error.message,\n };\n\n if (error.details) json.details = error.details;\n if (error.requestId) json.requestId = error.requestId;\n if (error.reason) json.cause = error.reason;\n\n return json as ErrorSchema;\n }\n\n public readonly error: string;\n public readonly status: number;\n\n public readonly requestId?: string;\n public readonly details?: string;\n public readonly reason?: {\n name: string;\n message: string;\n };\n\n constructor(options: Partial<ErrorSchema>, cause?: unknown) {\n super(options.message, {\n cause,\n });\n\n this.status = options.status ?? 500;\n this.details = options.details;\n this.requestId = options.requestId;\n\n if (typeof options.cause === \"object\") {\n this.reason = {\n name: (options.cause as { name: string }).name,\n message: (options.cause as { message: string }).message,\n };\n } else if (cause instanceof Error) {\n this.reason = {\n name: cause.name,\n message: cause.message,\n };\n }\n\n if (this.constructor.name === \"HttpError\") {\n this.error =\n options.error ?? errorNameByStatus[this.status] ?? \"HttpError\";\n } else {\n this.error = this.constructor.name;\n }\n }\n}\n\nexport const errorNameByStatus: Record<number, string> = {\n 400: \"BadRequestError\",\n 401: \"UnauthorizedError\",\n 403: \"ForbiddenError\",\n 404: \"NotFoundError\",\n 405: \"MethodNotAllowedError\",\n 409: \"ConflictError\",\n 410: \"GoneError\",\n 413: \"PayloadTooLargeError\",\n 415: \"UnsupportedMediaTypeError\",\n 429: \"TooManyRequestsError\",\n 500: \"InternalServerError\",\n 501: \"NotImplementedError\",\n 502: \"BadGatewayError\",\n 503: \"ServiceUnavailableError\",\n 504: \"GatewayTimeoutError\",\n};\n\nexport interface HttpErrorLike extends Error {\n status: number;\n}\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class ValidationError extends HttpError {\n constructor(message = \"Validation has failed\", cause?: unknown) {\n super(\n {\n message,\n status: 400,\n },\n cause,\n );\n }\n}\n","export interface UserAgentInfo {\n os:\n | \"Windows\"\n | \"Android\"\n | \"Ubuntu\"\n | \"MacOS\"\n | \"iOS\"\n | \"Linux\"\n | \"FreeBSD\"\n | \"OpenBSD\"\n | \"ChromeOS\"\n | \"BlackBerry\"\n | \"Symbian\"\n | \"Windows Phone\";\n browser:\n | \"Chrome\"\n | \"Firefox\"\n | \"Safari\"\n | \"Edge\"\n | \"Opera\"\n | \"Internet Explorer\"\n | \"Brave\"\n | \"Vivaldi\"\n | \"Samsung Browser\"\n | \"UC Browser\"\n | \"Yandex\";\n device: \"MOBILE\" | \"DESKTOP\" | \"TABLET\";\n}\n\n/**\n * Simple User-Agent parser to detect OS, browser, and device type.\n * This parser is not exhaustive and may not cover all edge cases.\n *\n * Use result for non\n */\nexport class UserAgentParser {\n public parse(userAgent: string = \"\"): UserAgentInfo {\n const ua = userAgent.toLowerCase();\n\n // Default values\n let os: UserAgentInfo[\"os\"] = \"Windows\";\n let browser: UserAgentInfo[\"browser\"] = \"Chrome\";\n let device: UserAgentInfo[\"device\"] = \"DESKTOP\";\n\n // Detect OS - Order matters for specificity\n if (ua.includes(\"windows phone\")) {\n os = \"Windows Phone\";\n } else if (ua.includes(\"windows\")) {\n os = \"Windows\";\n } else if (ua.includes(\"android\")) {\n os = \"Android\";\n } else if (\n ua.includes(\"iphone\") ||\n ua.includes(\"ipad\") ||\n ua.includes(\"ipod\") ||\n (ua.includes(\"ios\") && !ua.includes(\"android\"))\n ) {\n os = \"iOS\";\n } else if (\n ua.includes(\"mac os\") ||\n ua.includes(\"macos\") ||\n ua.includes(\"macintosh\")\n ) {\n os = \"MacOS\";\n } else if (ua.includes(\"cros\") || ua.includes(\"chromeos\")) {\n os = \"ChromeOS\";\n } else if (ua.includes(\"ubuntu\")) {\n os = \"Ubuntu\";\n } else if (ua.includes(\"freebsd\")) {\n os = \"FreeBSD\";\n } else if (ua.includes(\"openbsd\")) {\n os = \"OpenBSD\";\n } else if (ua.includes(\"blackberry\") || ua.includes(\"bb10\")) {\n os = \"BlackBerry\";\n } else if (ua.includes(\"symbian\") || ua.includes(\"symbos\")) {\n os = \"Symbian\";\n } else if (ua.includes(\"linux\") || ua.includes(\"x11\")) {\n os = \"Linux\";\n }\n\n // Detect Browser/browser - Order matters, check most specific first\n if (ua.includes(\"yabrowser\") || ua.includes(\"yandex\")) {\n browser = \"Yandex\";\n } else if (ua.includes(\"brave\")) {\n browser = \"Brave\";\n } else if (ua.includes(\"vivaldi\")) {\n browser = \"Vivaldi\";\n } else if (ua.includes(\"samsungbrowser\") || ua.includes(\"samsung\")) {\n browser = \"Samsung Browser\";\n } else if (ua.includes(\"ucbrowser\") || ua.includes(\"uc browser\")) {\n browser = \"UC Browser\";\n } else if (\n ua.includes(\"opera\") ||\n ua.includes(\"opr/\") ||\n ua.includes(\"opios\")\n ) {\n browser = \"Opera\";\n } else if (\n ua.includes(\"edg/\") ||\n ua.includes(\"edge\") ||\n ua.includes(\"edgios\")\n ) {\n browser = \"Edge\";\n } else if (ua.includes(\"firefox\") && !ua.includes(\"seamonkey\")) {\n browser = \"Firefox\";\n } else if (ua.includes(\"trident\") || ua.includes(\"msie\")) {\n browser = \"Internet Explorer\";\n } else if (\n ua.includes(\"safari\") &&\n !ua.includes(\"chrome\") &&\n !ua.includes(\"chromium\")\n ) {\n browser = \"Safari\";\n } else if (\n ua.includes(\"chrome\") ||\n ua.includes(\"chromium\") ||\n ua.includes(\"crios\")\n ) {\n browser = \"Chrome\";\n }\n\n // Detect Device Type\n const mobileKeywords = [\n \"mobile\",\n \"android\",\n \"iphone\",\n \"ipod\",\n \"blackberry\",\n \"windows phone\",\n \"opera mini\",\n \"iemobile\",\n \"mobile safari\",\n \"nokia\",\n \"symbian\",\n ];\n\n const tabletKeywords = [\n \"ipad\",\n \"tablet\",\n \"kindle\",\n \"silk\",\n \"gt-p\",\n \"sm-t\",\n \"nexus 7\",\n \"nexus 10\",\n ];\n\n const isTablet = tabletKeywords.some((keyword) => ua.includes(keyword));\n const isMobile = mobileKeywords.some((keyword) => ua.includes(keyword));\n\n if (isTablet) {\n device = \"TABLET\";\n } else if (isMobile) {\n device = \"MOBILE\";\n } else {\n device = \"DESKTOP\";\n }\n\n return { os, browser, device };\n }\n}\n","import { $env, $inject, Alepha, t } from \"alepha\";\nimport { ServerReply } from \"../helpers/ServerReply.ts\";\nimport type {\n RequestGeo,\n RequestReferer,\n ServerRequest,\n ServerRequestData,\n} from \"../interfaces/ServerRequest.ts\";\nimport { UserAgentParser } from \"./UserAgentParser.ts\";\n\nconst envSchema = t.object({\n /**\n * Trust proxy headers (X-Forwarded-For, X-Real-IP) for client IP resolution.\n *\n * Default: true (modern deployments are typically behind a reverse proxy)\n *\n * Set to false only if your server accepts direct connections without a proxy\n * and you want to use the raw connection IP.\n */\n TRUST_PROXY: t.boolean({\n default: true,\n description: \"Trust proxy headers for client IP\",\n }),\n});\n\nexport class ServerRequestParser {\n protected readonly alepha = $inject(Alepha);\n protected readonly userAgentParser = $inject(UserAgentParser);\n protected readonly env = $env(envSchema);\n\n public createServerRequest(rawRequest: ServerRequestData): ServerRequest {\n const self = this;\n return {\n method: rawRequest.method,\n url: rawRequest.url,\n raw: rawRequest.raw,\n headers: rawRequest.headers,\n query: rawRequest.query,\n params: rawRequest.params,\n // body will be filled by body parser middleware\n body: null,\n metadata: {},\n requestId: this.getRequestId(rawRequest) || crypto.randomUUID(),\n reply: this.alepha.inject(ServerReply, { lifetime: \"transient\" }),\n get ip() {\n return self.getRequestIp(rawRequest);\n },\n get userAgent() {\n return self.getRequestUserAgent(rawRequest);\n },\n get geo() {\n return self.getRequestGeo(rawRequest);\n },\n get isBot() {\n return self.getIsBot(rawRequest);\n },\n get isMobile() {\n return self.getIsMobile(rawRequest);\n },\n get protocol() {\n return self.getProtocol(rawRequest);\n },\n get language() {\n return self.getLanguage(rawRequest);\n },\n get referer() {\n return self.getReferer(rawRequest);\n },\n } as ServerRequest;\n }\n\n public getRequestId(request: ServerRequestData): string | undefined {\n return request.headers[\"x-request-id\"];\n }\n\n public getRequestUserAgent(request: ServerRequestData) {\n return this.userAgentParser.parse(request.headers[\"user-agent\"]);\n }\n\n public getRequestIp(request: ServerRequestData): string | undefined {\n // Only trust proxy headers when explicitly configured\n if (this.env.TRUST_PROXY) {\n const headers = request.headers;\n\n // X-Forwarded-For: standard proxy header (Cloudflare, Vercel, Nginx, etc.)\n const forwardedFor = headers[\"x-forwarded-for\"];\n if (forwardedFor) {\n return Array.isArray(forwardedFor)\n ? forwardedFor[0]\n : forwardedFor.split(\",\")[0].trim();\n }\n\n // X-Real-IP: alternative proxy header\n const xRealIP = headers[\"x-real-ip\"];\n if (xRealIP) {\n return Array.isArray(xRealIP) ? xRealIP[0] : xRealIP;\n }\n }\n\n // Default: use raw connection IP\n return this.getConnectionIp(request);\n }\n\n protected getConnectionIp(request: ServerRequestData): string | undefined {\n // Get IP from raw connection (Node.js socket)\n const nodeReq = request.raw.node?.req;\n if (nodeReq) {\n return nodeReq.socket?.remoteAddress;\n }\n return undefined;\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Geolocation\n // ─────────────────────────────────────────────────────────────────────────────\n\n public getRequestGeo(request: ServerRequestData): RequestGeo {\n const headers = request.headers;\n\n return {\n // Country: Cloudflare, Vercel, AWS CloudFront\n country:\n headers[\"cf-ipcountry\"] ||\n headers[\"x-vercel-ip-country\"] ||\n headers[\"cloudfront-viewer-country\"],\n\n // City: Cloudflare, Vercel\n city: headers[\"cf-ipcity\"] || headers[\"x-vercel-ip-city\"],\n\n // Region: Cloudflare, Vercel\n region:\n headers[\"cf-region\"] ||\n headers[\"cf-region-code\"] ||\n headers[\"x-vercel-ip-country-region\"],\n\n // Coordinates: Cloudflare, Vercel\n latitude: headers[\"cf-iplatitude\"] || headers[\"x-vercel-ip-latitude\"],\n longitude: headers[\"cf-iplongitude\"] || headers[\"x-vercel-ip-longitude\"],\n };\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Bot detection\n // ─────────────────────────────────────────────────────────────────────────────\n\n protected static readonly BOT_PATTERNS = [\n /bot/i,\n /crawl/i,\n /spider/i,\n /slurp/i,\n /googlebot/i,\n /bingbot/i,\n /yandex/i,\n /baiduspider/i,\n /facebookexternalhit/i,\n /twitterbot/i,\n /linkedinbot/i,\n /whatsapp/i,\n /telegrambot/i,\n /discordbot/i,\n /slackbot/i,\n /applebot/i,\n /duckduckbot/i,\n /semrush/i,\n /ahrefsbot/i,\n /mj12bot/i,\n /dotbot/i,\n /petalbot/i,\n /bytespider/i,\n /gptbot/i,\n /claudebot/i,\n /anthropic/i,\n /curl/i,\n /wget/i,\n /python-requests/i,\n /axios/i,\n /node-fetch/i,\n /go-http-client/i,\n /java\\//i,\n /libwww/i,\n /httpunit/i,\n /nutch/i,\n /phpcrawl/i,\n /biglotron/i,\n /teoma/i,\n /convera/i,\n /gigablast/i,\n /ia_archiver/i,\n /webmon/i,\n /httrack/i,\n /grub\\.org/i,\n /netresearchserver/i,\n /speedy/i,\n /fluffy/i,\n /findlink/i,\n /panscient/i,\n /ips-agent/i,\n /yanga/i,\n /cyberpatrol/i,\n /postrank/i,\n /page2rss/i,\n /linkdex/i,\n /ezooms/i,\n /heritrix/i,\n /findthatfile/i,\n /europarchive\\.org/i,\n /mappydata/i,\n /eright/i,\n /apercite/i,\n /aboundex/i,\n /domaincrawler/i,\n /wbsearchbot/i,\n /summify/i,\n /ccbot/i,\n /edisterbot/i,\n /seznambot/i,\n /ec2linkfinder/i,\n /gslfbot/i,\n /aihitbot/i,\n /intelium_bot/i,\n /yeti/i,\n /retrevopageanalyzer/i,\n /lb-spider/i,\n /sogou/i,\n /lssbot/i,\n /careerbot/i,\n /wotbox/i,\n /wocbot/i,\n /ichiro/i,\n /duckduckgo/i,\n /lssrocketcrawler/i,\n /drupact/i,\n /webcompanycrawler/i,\n /acoonbot/i,\n /openindexspider/i,\n /screaming frog/i,\n /pingdom/i,\n /uptimerobot/i,\n /headlesschrome/i,\n /phantomjs/i,\n /prerender/i,\n /lighthouse/i,\n /pagespeed/i,\n ];\n\n public getIsBot(request: ServerRequestData): boolean {\n const ua = request.headers[\"user-agent\"];\n if (!ua) return false;\n\n return ServerRequestParser.BOT_PATTERNS.some((pattern) => pattern.test(ua));\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Mobile detection\n // ─────────────────────────────────────────────────────────────────────────────\n\n protected static readonly MOBILE_PATTERNS = [\n /android/i,\n /webos/i,\n /iphone/i,\n /ipad/i,\n /ipod/i,\n /blackberry/i,\n /iemobile/i,\n /opera mini/i,\n /mobile/i,\n /tablet/i,\n /kindle/i,\n /silk/i,\n /fennec/i,\n /windows phone/i,\n /windows ce/i,\n /symbian/i,\n /palm/i,\n /webmate/i,\n ];\n\n public getIsMobile(request: ServerRequestData): boolean {\n const ua = request.headers[\"user-agent\"];\n if (!ua) return false;\n\n return ServerRequestParser.MOBILE_PATTERNS.some((pattern) =>\n pattern.test(ua),\n );\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Protocol detection\n // ─────────────────────────────────────────────────────────────────────────────\n\n public getProtocol(request: ServerRequestData): \"http\" | \"https\" {\n // Check proxy headers first\n const forwardedProto = request.headers[\"x-forwarded-proto\"];\n if (forwardedProto) {\n return forwardedProto.toLowerCase() === \"https\" ? \"https\" : \"http\";\n }\n\n // Cloudflare-specific header\n const cfVisitorHeader = request.headers[\"cf-visitor\"];\n if (cfVisitorHeader) {\n try {\n const parsed = JSON.parse(cfVisitorHeader);\n if (parsed.scheme === \"https\") return \"https\";\n } catch {\n // Ignore parse errors\n }\n }\n\n // Check URL scheme\n if (request.url.protocol === \"https:\") {\n return \"https\";\n }\n\n return \"http\";\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Language detection\n // ─────────────────────────────────────────────────────────────────────────────\n\n public getLanguage(request: ServerRequestData): string | undefined {\n const acceptLanguage = request.headers[\"accept-language\"];\n if (!acceptLanguage) return undefined;\n\n // Parse Accept-Language header\n // Format: \"en-US,en;q=0.9,fr;q=0.8\"\n const firstLang = acceptLanguage.split(\",\")[0];\n if (!firstLang) return undefined;\n\n // Remove quality value if present (e.g., \"en;q=0.9\" -> \"en\")\n const lang = firstLang.split(\";\")[0].trim();\n\n return lang || undefined;\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Referer parsing\n // ─────────────────────────────────────────────────────────────────────────────\n\n public getReferer(request: ServerRequestData): RequestReferer | undefined {\n const referer = request.headers.referer || request.headers.referrer;\n if (!referer) return undefined;\n\n try {\n const url = new URL(referer);\n return {\n url: referer,\n hostname: url.hostname,\n pathname: url.pathname,\n };\n } catch {\n // Invalid URL\n return undefined;\n }\n }\n}\n","import { $hook, $inject, Alepha } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { ServerRequest } from \"../interfaces/ServerRequest.ts\";\n\ntype TimingMap = Record<string, [number, number]>;\n\nexport class ServerTimingProvider {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n\n public options = {\n prefix: this.alepha.env.APP_NAME\n ? `${this.alepha.env.APP_NAME.toLowerCase()}-`\n : \"\",\n disabled: this.alepha.isProduction(),\n };\n\n public readonly onRequest = $hook({\n priority: \"first\",\n on: \"server:onRequest\",\n handler: ({ request }) => {\n if (this.options.disabled) {\n return;\n }\n\n request.metadata.timing = {};\n request.metadata.timing[this.handlerName] = [Date.now()];\n },\n });\n\n public readonly onResponse = $hook({\n priority: \"last\",\n on: \"server:onResponse\",\n handler: ({ request }) => {\n if (this.options.disabled) {\n return;\n }\n\n if (request.metadata.timing) {\n this.setDuration(this.handlerName, request.metadata.timing);\n\n let timingHeader = \"\";\n\n for (const [name, [start, duration]] of Object.entries(\n request.metadata.timing as TimingMap,\n )) {\n if (typeof start !== \"number\" || typeof duration !== \"number\") {\n this.log.warn(\n `Invalid timing for '${name}': [${start}, ${duration}]`,\n );\n continue;\n }\n\n const formattedName =\n this.options.prefix + name.replace(/[^a-zA-Z0-9-]/g, \"-\");\n timingHeader += `${formattedName};dur=${duration}, `;\n }\n\n if (request.reply.headers[\"Server-Timing\"]) {\n request.reply.headers[\"Server-Timing\"] += `, ${timingHeader}`;\n } else {\n request.reply.headers[\"Server-Timing\"] = timingHeader;\n }\n }\n },\n });\n\n protected get handlerName() {\n return `request`;\n }\n\n public beginTiming(name: string): void {\n if (this.options.disabled) {\n return;\n }\n\n const request = this.alepha.context.get<ServerRequest>(\"request\");\n if (!request) {\n return;\n }\n\n request.metadata ??= {};\n request.metadata.timing ??= {};\n request.metadata.timing[name] = [Date.now()];\n }\n\n public endTiming(name: string): void {\n if (this.options.disabled) {\n return;\n }\n\n const request = this.alepha.context.get<ServerRequest>(\"request\");\n if (!request) {\n return;\n }\n\n if (!request.metadata?.timing || !(name in request.metadata.timing)) {\n this.log.warn(`No timing found for '${name}'.`);\n return;\n }\n\n const start = request.metadata.timing[name]?.[0];\n if (typeof start !== \"number\") {\n this.log.warn(`Invalid timing start for '${name}': ${start}`);\n return;\n }\n\n this.setDuration(name, request.metadata.timing);\n }\n\n protected setDuration(name: string, timing: TimingMap): void {\n timing[name] = [timing[name][0], Date.now() - timing[name][0]];\n }\n}\n","import { randomUUID } from \"node:crypto\";\nimport { Readable as NodeStream } from \"node:stream\";\nimport { ReadableStream as NodeWebStream } from \"node:stream/web\";\nimport type { CompiledEventExecutor, Hooks } from \"alepha\";\nimport { $inject, Alepha, isFileLike, isTypeFile, t } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { RouterProvider } from \"alepha/router\";\nimport type { RouteMethod } from \"../constants/routeMethods.ts\";\nimport { errorNameByStatus, HttpError } from \"../errors/HttpError.ts\";\nimport { ValidationError } from \"../errors/ValidationError.ts\";\nimport type { ServerReply } from \"../helpers/ServerReply.ts\";\nimport type {\n RequestConfigSchema,\n ResponseKind,\n ServerRequest,\n ServerRequestConfig,\n ServerRoute,\n ServerRouteMatcher,\n} from \"../interfaces/ServerRequest.ts\";\nimport { ServerRequestParser } from \"../services/ServerRequestParser.ts\";\nimport { ServerTimingProvider } from \"./ServerTimingProvider.ts\";\n\n/**\n * Main router for all routes server side.\n *\n * Reminder:\n * - $route => generic route\n * - $action => action route (for API calls)\n * - $page => React route (for React SSR)\n */\nexport class ServerRouterProvider extends RouterProvider<ServerRouteMatcher> {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly routes: ServerRoute[] = [];\n protected readonly serverTimingProvider = $inject(ServerTimingProvider);\n protected readonly serverRequestParser = $inject(ServerRequestParser);\n\n // Compiled event executors - initialized on first request\n protected compiledOnRequest?: CompiledEventExecutor<\n Hooks[\"server:onRequest\"]\n >;\n protected compiledOnSend?: CompiledEventExecutor<Hooks[\"server:onSend\"]>;\n protected compiledOnResponse?: CompiledEventExecutor<\n Hooks[\"server:onResponse\"]\n >;\n protected compiledOnError?: CompiledEventExecutor<Hooks[\"server:onError\"]>;\n\n // Reusable context.run options object - mutated per request\n // Includes slots for request data to avoid closure allocation in context.run\n protected readonly contextRunOptions = {\n context: \"\",\n // Request data slots - populated before context.run, read by processRequestBound\n _request: null as unknown as ServerRequest,\n _route: null as unknown as ServerRoute,\n _responseKind: \"any\" as ResponseKind,\n };\n\n // Pre-bound method reference - created once at instantiation, reused for all requests\n // Reads arguments from contextRunOptions to avoid closure allocation per request\n protected readonly processRequestBound = (): Promise<any> => {\n const opts = this.contextRunOptions;\n return this.processRequest(opts._request, opts._route, opts._responseKind);\n };\n\n // Cache query schema keys to avoid property enumeration on every request\n // WeakMap allows GC of schemas that are no longer referenced\n protected readonly queryKeysCache = new WeakMap<object, string[]>();\n\n /**\n * Get cached keys for a query schema, computing them lazily on first access.\n */\n protected getQuerySchemaKeys(schema: { properties: object }): string[] {\n let keys = this.queryKeysCache.get(schema.properties);\n if (!keys) {\n keys = Object.keys(schema.properties);\n this.queryKeysCache.set(schema.properties, keys);\n }\n return keys;\n }\n\n /**\n * Compile event executors for optimal performance.\n * Called lazily on first request after all hooks are registered.\n */\n protected compileEvents(): void {\n if (this.compiledOnRequest) return; // Already compiled\n\n this.compiledOnRequest = this.alepha.events.compile(\"server:onRequest\");\n this.compiledOnSend = this.alepha.events.compile(\"server:onSend\", {\n catch: true,\n });\n this.compiledOnResponse = this.alepha.events.compile(\"server:onResponse\", {\n catch: true,\n });\n this.compiledOnError = this.alepha.events.compile(\"server:onError\");\n }\n\n /**\n * Get all registered routes, optionally filtered by a pattern.\n *\n * Pattern accept simple wildcard '*' at the end.\n * Example: '/api/*' will match all routes starting with '/api/' but '/api/' will match only that exact route.\n */\n public getRoutes(pattern?: string): ServerRoute[] {\n if (pattern) {\n if (pattern.endsWith(\"*\")) {\n const basePattern = pattern.slice(0, -1);\n return this.routes.filter((route) =>\n route.path.startsWith(basePattern),\n );\n } else {\n return this.routes.filter((route) => route.path === pattern);\n }\n }\n return this.routes;\n }\n\n /**\n * Create a new server route.\n */\n public createRoute<TConfig extends RequestConfigSchema = RequestConfigSchema>(\n route: ServerRoute<TConfig>,\n ): void {\n route.method ??= \"GET\";\n route.method = route.method.toUpperCase() as RouteMethod;\n\n this.routes.push(route);\n\n const path = `/${route.method}/${route.path}`.replace(/\\/+/g, \"/\");\n const responseKind = this.getResponseType(route.schema);\n\n this.log.trace(`Create route ${path}`);\n\n this.push({\n path,\n handler: (rawRequest) => {\n const request =\n this.serverRequestParser.createServerRequest(rawRequest);\n\n // Populate pre-allocated options with request data\n // This avoids closure allocation - processRequestBound reads from these slots\n const opts = this.contextRunOptions;\n opts.context = this.getContextId(rawRequest.headers);\n opts._request = request;\n opts._route = route;\n opts._responseKind = responseKind;\n\n // Use pre-bound method reference instead of creating closure per request\n return this.alepha.context.run(this.processRequestBound, opts);\n },\n });\n }\n\n /**\n * Get or generate a context ID from request headers.\n */\n protected getContextId(headers: Record<string, string>): string {\n // Use constant header names to reduce string allocation\n const contextId =\n headers[HEADER_REQUEST_ID] || headers[HEADER_CORRELATION_ID];\n if (contextId) {\n return contextId;\n }\n\n return randomUUID();\n }\n\n /**\n * Process an incoming request through the full lifecycle:\n * - onRequest hooks\n * - route handler\n * - onSend hooks\n * - response serialization\n * - onResponse hooks\n */\n protected async processRequest(\n request: ServerRequest,\n route: ServerRoute,\n responseKind: ResponseKind,\n ) {\n // Compile events on first request (after all hooks are registered)\n this.compileEvents();\n\n // Use try/catch instead of .catch() to avoid function creation overhead\n try {\n await this.runRouteHandler(route, request, responseKind);\n } catch (error) {\n await this.errorHandler(route, request, error as Error);\n }\n\n // Local reference to reduce property lookups\n const reply = request.reply;\n\n // Create payload per request to avoid race conditions with concurrent requests\n // (async hooks may hold references while other requests modify shared payloads)\n const payload = { request, route, response: undefined as any };\n\n // Use compiled executor - only await if returns promise\n const onSendResult = this.compiledOnSend!(payload);\n if (onSendResult) await onSendResult;\n\n // Create response\n const response = {\n status: reply.status ?? (reply.body ? 200 : 204),\n headers: reply.headers,\n body: reply.body as any,\n };\n\n payload.response = response;\n\n // Use compiled executor - only await if returns promise\n const onResponseResult = this.compiledOnResponse!(payload);\n if (onResponseResult) await onResponseResult;\n\n return response;\n }\n\n /**\n * Run the route handler with request validation and response serialization.\n */\n protected async runRouteHandler(\n route: ServerRoute,\n request: ServerRequest,\n responseKind: ResponseKind,\n ) {\n // there are some built-in hooks that are called before the request is handled\n // - ServerBodyParserProvider (parse body)\n // - ServerSecurityProvider (build user from headers)\n // - ServerLoggerProvider (log request)\n\n // Local reference for timing provider\n const timing = this.serverTimingProvider;\n\n // Create payload per request to avoid race conditions with concurrent requests\n const payload = { request, route };\n\n // Use compiled executor - only await if returns promise\n const onRequestResult = this.compiledOnRequest!(payload);\n if (onRequestResult) await onRequestResult;\n\n // Local reference to reduce property lookups\n const reply = request.reply;\n if (reply.body || (reply.status && reply.status >= 200)) {\n // if the body is already set, we can skip the handler\n // this is useful for middlewares that set the body\n return;\n }\n\n // request is ready to be used -> inject to context\n this.alepha.context.set<ServerRequest>(CTX_REQUEST, request);\n\n // validate request\n timing.beginTiming(TIMING_VALIDATE);\n try {\n this.validateRequest(route, request);\n } finally {\n timing.endTiming(TIMING_VALIDATE);\n }\n\n // call the handler only if the body is not set yet\n timing.beginTiming(TIMING_HANDLER);\n try {\n const result = await route.handler(request);\n if (result) {\n request.reply.body = result;\n }\n } finally {\n timing.endTiming(TIMING_HANDLER);\n }\n\n // serialize response\n timing.beginTiming(TIMING_SERIALIZE);\n try {\n this.serializeResponse(route, request.reply, responseKind);\n } finally {\n timing.endTiming(TIMING_SERIALIZE);\n }\n }\n\n /**\n * Transform reply body based on response kind and route schema.\n */\n public serializeResponse(\n route: ServerRoute,\n reply: ServerReply,\n responseKind: ResponseKind,\n ): void {\n // Local reference to reduce property lookups\n const headers = reply.headers;\n\n if (responseKind === \"json\" && route.schema?.response) {\n headers[HEADER_CONTENT_TYPE] = CONTENT_TYPE_JSON;\n reply.body = this.alepha.codec.encode(\n route.schema.response,\n reply.body,\n ENCODE_OPTIONS_STRING,\n );\n return;\n }\n\n if (responseKind === \"file\") {\n if (!isFileLike(reply.body)) {\n throw new HttpError({\n status: 500,\n message: ERROR_NOT_FILE,\n });\n }\n headers[HEADER_CONTENT_TYPE] = reply.body.type;\n headers[HEADER_CONTENT_DISPOSITION] =\n `attachment; filename=\"${reply.body.name.replaceAll('\"', \"\")}\"`;\n reply.body = reply.body.stream();\n return;\n }\n\n if (responseKind === \"text\") {\n reply.body = String(reply.body);\n if (\n reply.body.length > 15 &&\n reply.body.charCodeAt(0) === 60 &&\n reply.body.startsWith(\"<!DOCTYPE html>\")\n ) {\n headers[HEADER_CONTENT_TYPE] ??= CONTENT_TYPE_HTML;\n } else {\n headers[HEADER_CONTENT_TYPE] ??= CONTENT_TYPE_TEXT;\n }\n return;\n }\n\n if (reply.body == null || responseKind === \"void\") {\n delete headers[HEADER_CONTENT_TYPE];\n reply.body = undefined;\n return;\n }\n\n if (Buffer.isBuffer(reply.body)) {\n headers[HEADER_CONTENT_TYPE] ??= CONTENT_TYPE_OCTET;\n return;\n }\n\n if (\n reply.body instanceof NodeWebStream ||\n reply.body instanceof NodeStream\n ) {\n // set content-type to application/octet-stream if not set\n headers[HEADER_CONTENT_TYPE] ??= CONTENT_TYPE_OCTET;\n return;\n }\n\n headers[HEADER_CONTENT_TYPE] ??= CONTENT_TYPE_TEXT;\n reply.body = String(reply.body);\n return;\n }\n\n /**\n * Determine response type based on route schema.\n */\n protected getResponseType(schema?: RequestConfigSchema): ResponseKind {\n if (schema?.response) {\n if (\n t.schema.isObject(schema.response) ||\n t.schema.isRecord(schema.response) ||\n t.schema.isArray(schema.response)\n ) {\n return \"json\";\n }\n\n if (\n t.schema.isString(schema.response) ||\n t.schema.isInteger(schema.response) ||\n t.schema.isNumber(schema.response) ||\n t.schema.isBoolean(schema.response)\n ) {\n return \"text\";\n }\n\n if (isTypeFile(schema.response)) {\n return \"file\";\n }\n\n if (t.schema.isVoid(schema.response)) {\n return \"void\";\n }\n }\n\n return \"any\";\n }\n\n /**\n * When an error occurs during request processing, this method is called.\n */\n protected async errorHandler(\n route: ServerRoute,\n request: ServerRequest,\n error: Error,\n ) {\n // Local references to reduce property lookups\n const reply = request.reply;\n const headers = reply.headers;\n const requestId = request.requestId;\n\n // Reset body, which is probably invalid!\n // It can be filled by server:onError hook or by the default handler below\n reply.body = null;\n\n // Use compiled executor - only await if returns promise\n const onErrorResult = this.compiledOnError!({ request, route, error });\n if (onErrorResult) {\n await onErrorResult;\n }\n\n if (!reply.body && !reply.status) {\n if (error instanceof HttpError) {\n reply.status = error.status;\n headers[HEADER_CONTENT_TYPE] = CONTENT_TYPE_JSON;\n // Avoid spread operator - directly mutate the error JSON\n const errorJson = HttpError.toJSON(error);\n errorJson.requestId = requestId;\n reply.body = JSON.stringify(errorJson);\n } else {\n if (\n \"status\" in error &&\n typeof error.status === \"number\" &&\n !!errorNameByStatus[error.status]\n ) {\n const status = error.status;\n reply.status = status;\n headers[HEADER_CONTENT_TYPE] = CONTENT_TYPE_JSON;\n reply.body = JSON.stringify({\n status,\n error: errorNameByStatus[status],\n message: error.message,\n requestId,\n });\n return;\n }\n\n reply.status = 500;\n headers[HEADER_CONTENT_TYPE] = CONTENT_TYPE_JSON;\n reply.body = JSON.stringify({\n status: 500,\n error: ERROR_INTERNAL,\n message: error.message,\n requestId,\n });\n }\n }\n }\n\n /**\n * Validate incoming request against route schema.\n */\n public validateRequest(\n route: { schema?: RequestConfigSchema },\n request: ServerRequestConfig,\n ) {\n // Validate params (path parameters)\n if (route.schema?.params) {\n try {\n request.params = this.alepha.codec.validate(\n route.schema.params,\n request.params,\n ) as any;\n } catch (error) {\n throw new ValidationError(\"Invalid request params\", error);\n }\n }\n\n // Validate query parameters (?key=value&key2=value2)\n if (route.schema?.query) {\n try {\n // Use cached keys instead of for...in enumeration\n const schemaQuery = route.schema.query;\n const keys = this.getQuerySchemaKeys(schemaQuery);\n const query: Record<string, any> = {};\n\n // Use indexed loop for better performance than for...of\n for (let i = 0; i < keys.length; i++) {\n const key = keys[i];\n if (request.query[key] != null) {\n query[key] = this.alepha.codec.decode(\n schemaQuery.properties[key],\n request.query[key],\n );\n }\n }\n\n request.query = query;\n } catch (error) {\n throw new ValidationError(\"Invalid request query\", error);\n }\n }\n\n // Validate headers\n if (route.schema?.headers) {\n try {\n request.headers = this.alepha.codec.validate(\n route.schema.headers,\n request.headers,\n ) as any;\n } catch (error) {\n throw new ValidationError(\"Invalid request header\", error);\n }\n }\n\n // Validate body\n if (route.schema?.body) {\n if (t.schema.isString(route.schema.body)) {\n if (typeof request.body !== \"string\") {\n throw new ValidationError(\"Request body is not a string\");\n }\n } else {\n try {\n request.body = this.alepha.codec.decode(\n route.schema.body,\n request.body,\n );\n } catch (error) {\n throw new ValidationError(\"Invalid request body\", error);\n }\n }\n }\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n// Pre-allocated encode options for response serialization\nconst ENCODE_OPTIONS_STRING = Object.freeze({\n as: \"string\" as const,\n});\n\n// HTTP Headers\nconst HEADER_CONTENT_TYPE = \"content-type\";\nconst HEADER_CONTENT_DISPOSITION = \"content-disposition\";\nconst HEADER_REQUEST_ID = \"x-request-id\";\nconst HEADER_CORRELATION_ID = \"x-correlation-id\";\n\n// Content Types\nconst CONTENT_TYPE_JSON = \"application/json\";\nconst CONTENT_TYPE_TEXT = \"text/plain\";\nconst CONTENT_TYPE_HTML = \"text/html; charset=UTF-8\";\nconst CONTENT_TYPE_OCTET = \"application/octet-stream\";\n\n// Timing Keys\nconst TIMING_VALIDATE = \"validateRequest\";\nconst TIMING_HANDLER = \"runHandler\";\nconst TIMING_SERIALIZE = \"serializeResponse\";\n\n// Context Keys\nconst CTX_REQUEST = \"request\";\n\n// Error Messages\nconst ERROR_INTERNAL = \"InternalServerError\";\nconst ERROR_NOT_FILE = \"Invalid response body - not a file\";\n","import { Readable } from \"node:stream\";\nimport { $hook, $inject, Alepha } from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport type { Route } from \"alepha/router\";\nimport type { RouteMethod } from \"../constants/routeMethods.ts\";\nimport type {\n NodeRequestEvent,\n ServerRequestData,\n WebRequestEvent,\n} from \"../interfaces/ServerRequest.ts\";\nimport { ServerRouterProvider } from \"./ServerRouterProvider.ts\";\n\n// ============================================================================\n// Performance Constants\n// ============================================================================\n\n// Note: We cannot use frozen/shared empty objects here because downstream code\n// may mutate params/query (e.g., validation, React page rendering)\n\n// Header constants for fast property access\nconst HEADER_X_FORWARDED_PROTO = \"x-forwarded-proto\";\nconst HEADER_HOST = \"host\";\n\n// Protocol prefixes\nconst PROTO_HTTPS = \"https://\";\nconst PROTO_HTTP = \"http://\";\n\n/**\n * Base server provider to handle incoming requests and route them.\n *\n * This is the default implementation for serverless environments.\n *\n * ServerProvider supports both Node.js HTTP requests and Web (Fetch API) requests.\n */\nexport class ServerProvider {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly router = $inject(ServerRouterProvider);\n\n protected readonly internalServerErrorMessage = \"Internal Server Error\";\n\n // Pre-allocated error response to avoid object creation per failed request\n protected readonly internalErrorResponse = Object.freeze({\n status: 500,\n headers: Object.freeze({ \"content-type\": \"text/plain\" }),\n body: this.internalServerErrorMessage,\n });\n\n // Pre-bound error handler to avoid function allocation per request\n protected readonly handleInternalError = () => this.internalErrorResponse;\n\n // ============================================================================\n // P1: URL Base Cache - cache protocol+host per unique host header\n // Avoids string concatenation on every request\n // ============================================================================\n protected readonly urlBaseCache = new Map<string, string>();\n\n /**\n * Get cached URL base (protocol + host) for a given host header.\n * Caches the result to avoid repeated string concatenation.\n */\n protected getUrlBase(headers: Record<string, string>): string {\n const host = headers[HEADER_HOST];\n let base = this.urlBaseCache.get(host);\n if (!base) {\n const proto =\n headers[HEADER_X_FORWARDED_PROTO] === \"https\"\n ? PROTO_HTTPS\n : PROTO_HTTP;\n base = proto + host;\n // Limit cache size to prevent memory leaks from many unique hosts\n if (this.urlBaseCache.size < 100) {\n this.urlBaseCache.set(host, base);\n }\n }\n return base;\n }\n\n // ============================================================================\n // P0: Manual Query String Parser - faster than URLSearchParams\n // Parses query string without creating URL object\n // ============================================================================\n\n /**\n * Parse query string manually - faster than new URL() + URLSearchParams.\n * Returns empty object if no query string.\n */\n protected parseQueryString(rawUrl: string): Record<string, string> {\n const qIndex = rawUrl.indexOf(\"?\");\n if (qIndex === -1) {\n return {};\n }\n\n const qs = rawUrl.slice(qIndex + 1);\n if (!qs) {\n return {};\n }\n\n const query: Record<string, string> = {};\n let start = 0;\n let eqIdx = -1;\n\n for (let i = 0; i <= qs.length; i++) {\n const char = i < qs.length ? qs.charCodeAt(i) : 38; // '&' at end\n\n if (char === 61) {\n // '='\n eqIdx = i;\n } else if (char === 38) {\n // '&'\n if (eqIdx !== -1 && eqIdx > start) {\n const key = qs.slice(start, eqIdx);\n const value = qs.slice(eqIdx + 1, i);\n // Only decode if necessary (contains % or +)\n query[this.fastDecode(key)] = this.fastDecode(value);\n }\n start = i + 1;\n eqIdx = -1;\n }\n }\n\n return query;\n }\n\n /**\n * Fast decode - only calls decodeURIComponent if needed.\n */\n protected fastDecode(str: string): string {\n // Fast path: no encoding needed\n if (str.indexOf(\"%\") === -1 && str.indexOf(\"+\") === -1) {\n return str;\n }\n // Replace + with space before decoding\n try {\n return decodeURIComponent(str.replace(/\\+/g, \" \"));\n } catch {\n return str;\n }\n }\n\n public get hostname(): string {\n if (this.alepha.isViteDev()) {\n return `http://localhost:${this.alepha.env.SERVER_PORT}`;\n }\n return \"\"; // no hostname in serverless mode\n }\n\n /**\n * When a Node.js HTTP request is received from outside. (Vercel, AWS Lambda, etc.)\n */\n protected readonly onNodeRequest = $hook({\n on: \"node:request\",\n handler: (ev) => this.handleNodeRequest(ev),\n });\n\n /**\n * When a Web (Fetch API) request is received from outside. (Netlify, Cloudflare Workers, etc.)\n */\n protected readonly onWebRequest = $hook({\n on: \"web:request\",\n handler: (ev) => {\n return this.handleWebRequest(ev);\n },\n });\n\n /**\n * Handle Node.js HTTP request event.\n *\n * Optimized to avoid expensive URL parsing when possible.\n */\n public async handleNodeRequest(\n nodeRequestEvent: NodeRequestEvent,\n ): Promise<void> {\n const { req, res } = nodeRequestEvent;\n const rawUrl = req.url!;\n const { route, params } = this.router.match(`/${req.method}${rawUrl}`);\n\n if (!route) {\n // Skip if response was already sent (e.g., by Vite middleware)\n if (res.headersSent) {\n return;\n }\n // if no route is found, return basic 404\n // note: you should not use this in production, use a custom 404 page instead by adding a route /*\n res.writeHead(404, { \"content-type\": \"text/plain\" });\n res.end(\"Not Found\");\n return;\n }\n\n const headers = (req.headers ?? {}) as Record<string, string>;\n const method = (req.method?.toUpperCase() ?? \"GET\") as RouteMethod;\n\n // P0: Use manual query parsing - much faster than new URL() + URLSearchParams\n const query = this.parseQueryString(rawUrl);\n\n // P1: Use cached URL base - avoids string concat on every request\n // Create URL object (still needed for downstream code that uses url.pathname, etc.)\n const urlBase = this.getUrlBase(headers);\n const url = new URL(rawUrl, urlBase);\n\n const request: ServerRequestData = {\n method,\n url,\n headers,\n params: params ?? {},\n query,\n raw: { node: nodeRequestEvent },\n };\n\n const response = await route\n .handler(request)\n .catch(this.handleInternalError);\n\n // Skip if response was already sent (e.g., by Vite middleware)\n if (res.headersSent) {\n return;\n }\n\n // empty body - just send status & headers\n if (!response.body) {\n res.writeHead(response.status, response.headers).end();\n return;\n }\n\n // if response.body is string or buffer\n if (typeof response.body === \"string\" || Buffer.isBuffer(response.body)) {\n res.writeHead(response.status, response.headers).end(response.body);\n return;\n }\n\n // if response.body is node stream\n if (response.body instanceof Readable) {\n res.writeHead(response.status, response.headers);\n response.body.pipe(res);\n return;\n }\n\n // if response.body is web stream\n if (response.body instanceof ReadableStream) {\n res.writeHead(response.status, response.headers);\n // Flush headers immediately and disable Nagle's algorithm for streaming\n res.flushHeaders();\n res.socket?.setNoDelay(true);\n try {\n for await (const chunk of response.body) {\n const canContinue = res.write(chunk);\n // If the internal buffer is full, wait for it to drain\n if (!canContinue) {\n await new Promise<void>((resolve) => res.once(\"drain\", resolve));\n }\n }\n } catch (error) {\n this.log.error(\"Error piping proxy response stream\", error);\n } finally {\n res.end();\n }\n return;\n }\n\n // not supported response body type\n\n this.log.error(\"Unknown response body type:\", typeof response.body);\n res.writeHead(500, { \"content-type\": \"text/plain\" });\n res.end(this.internalServerErrorMessage);\n }\n\n /**\n * Handle Web (Fetch API) request event.\n */\n public async handleWebRequest(ev: WebRequestEvent): Promise<void> {\n const req = ev.req;\n const url = new URL(req.url);\n const { route, params } = this.router.match(\n `/${req.method}${url.pathname}`,\n );\n\n if (this.isViteNotFound(req.url, route, params)) {\n return;\n }\n\n if (!route) {\n // if no route is found, return basic 404\n // note: you should not use this in production, use a custom 404 page instead by adding a route /*\n ev.res = new Response(\"Not Found\", {\n status: 404,\n headers: { \"content-type\": \"text/plain\" },\n });\n return;\n }\n\n const headers: Record<string, string> = {};\n\n req.headers.forEach((value, key) => {\n headers[key] = value;\n });\n\n // Optimize: only parse query params if there are any\n const query = url.search\n ? Object.fromEntries(url.searchParams.entries())\n : {};\n const method = (req.method.toUpperCase() ?? \"GET\") as RouteMethod;\n const request: ServerRequestData = {\n method,\n url,\n headers,\n params: params || {},\n query,\n raw: { web: ev },\n };\n\n const response = await route\n .handler(request)\n .catch(this.handleInternalError);\n\n // empty body - just send status & headers\n if (!response.body) {\n ev.res = new Response(null, {\n status: response.status,\n headers: response.headers,\n });\n return;\n }\n\n // if response.body is string or buffer\n if (typeof response.body === \"string\") {\n ev.res = new Response(response.body, {\n status: response.status,\n headers: response.headers,\n });\n return;\n }\n\n if (Buffer.isBuffer(response.body)) {\n // Use Uint8Array to avoid Buffer pooling issues where .buffer returns\n // the entire underlying ArrayBuffer which may be larger than the actual data\n ev.res = new Response(new Uint8Array(response.body), {\n status: response.status,\n headers: response.headers,\n });\n return;\n }\n\n // if response.body is node stream\n if (response.body instanceof Readable) {\n ev.res = new Response(\n Readable.toWeb(response.body) as unknown as ReadableStream,\n {\n status: response.status,\n headers: response.headers,\n },\n );\n return;\n }\n\n // if response.body is web stream\n if (response.body instanceof ReadableStream) {\n ev.res = new Response(response.body, {\n status: response.status,\n headers: response.headers,\n });\n return;\n }\n\n // not supported response body type\n this.log.error(`Unknown response body type: ${typeof response.body}`);\n ev.res = new Response(this.internalServerErrorMessage, {\n status: 500,\n headers: { \"content-type\": \"text/plain\" },\n });\n }\n\n /**\n * Helper for Vite development mode to let Vite handle (or not) 404.\n */\n protected isViteNotFound(\n url?: string,\n route?: Route,\n params?: Record<string, string>,\n ): boolean {\n if (this.alepha.isViteDev()) {\n if (!route) {\n return true;\n }\n\n url = url?.split(\"?\")[0];\n\n if (!!params?.[\"*\"] && `/${params?.[\"*\"]}` === url) {\n return true;\n }\n }\n\n return false;\n }\n}\n","import { type Static, t } from \"alepha\";\n\nexport const errorSchema = t.object(\n {\n error: t.text({ description: \"HTTP error name\" }),\n status: t.integer({\n description: \"HTTP status code\",\n }),\n message: t.text({\n description: \"Short text which describe the error\",\n size: \"rich\",\n }),\n details: t.optional(\n t.text({\n description: \"Detailed description of the error\",\n size: \"rich\",\n }),\n ),\n requestId: t.optional(t.text()),\n cause: t.optional(\n t.object({\n name: t.text(),\n message: t.text({\n description: \"Cause Error message\",\n size: \"rich\",\n }),\n }),\n ),\n },\n {\n title: \"HttpError\",\n description: \"Generic response after a failed operation\",\n },\n);\n\nexport type ErrorSchema = Static<typeof errorSchema>;\n","import {\n $inject,\n Alepha,\n type FileLike,\n isFileLike,\n type Static,\n type TObject,\n type TSchema,\n} from \"alepha\";\nimport { $cache } from \"alepha/cache\";\nimport type { DurationLike } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { HttpError } from \"../errors/HttpError.ts\";\nimport { isMultipart } from \"../helpers/isMultipart.ts\";\nimport type {\n ServerRequestConfigEntry,\n TRequestBody,\n TResponseBody,\n} from \"../interfaces/ServerRequest.ts\";\nimport type { ClientRequestOptions } from \"../primitives/$action.ts\";\nimport { errorSchema } from \"../schemas/errorSchema.ts\";\n\nexport class HttpClient {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n\n public readonly cache = $cache<HttpClientCache>();\n\n protected readonly pendingRequests: HttpClientPendingRequests = {};\n\n public async fetchAction(args: FetchActionArgs): Promise<FetchResponse> {\n const route = args.action; // our link to fetch\n const options = args.options ?? {}; // fetch standard options, cache, etc.\n const config = args.config ?? {}; // params, query, body, etc.\n const host = args.host ?? \"\"; // remote host, e.g. \"https://api.example.com\" or empty (for browser)\n\n const request: RequestInit = {\n ...options.request,\n };\n\n const method = route.method;\n const headers: Record<string, string> = {};\n const url = this.url(host, route, config);\n\n await this.alepha.events.emit(\"client:onRequest\", {\n route,\n config,\n options,\n headers,\n request,\n });\n\n request.method ??= method;\n\n await this.body(request, headers, route, config);\n\n request.headers = {\n ...config.headers,\n ...Object.fromEntries(new Headers(request.headers).entries()),\n ...headers,\n };\n\n return await this.fetch(url, {\n ...request,\n schema: route.schema,\n ...options,\n });\n }\n\n public async fetch<T extends TSchema>(\n url: string,\n request: RequestInitWithOptions<T> = {}, // standard options\n ): Promise<FetchResponse<Static<T>>> {\n const options = {\n cache: request.localCache,\n schema: request.schema?.response,\n key: request.key,\n };\n\n request.method ??= \"GET\";\n\n this.log.trace(\"Request\", {\n url,\n method: request.method,\n body: request.body,\n headers: request.headers,\n options,\n });\n\n // Only add automatic ETag if user didn't explicitly provide headers\n const cached = await this.cache.get(url);\n if (cached && request.method === \"GET\") {\n if (cached.etag) {\n request.headers = new Headers(request.headers);\n if (!request.headers.has(\"if-none-match\")) {\n request.headers.set(\"if-none-match\", cached.etag);\n }\n } else {\n return {\n data: cached.data as Static<T>,\n status: 200,\n statusText: \"OK\",\n headers: new Headers(),\n };\n }\n }\n\n await this.alepha.events.emit(\"client:beforeFetch\", {\n url,\n options,\n request,\n });\n\n // make a key for the request\n // this will be used to check if the request is already pending\n const key =\n options.key ??\n JSON.stringify({\n url,\n method: request.method,\n body: request.body,\n });\n\n const existing = this.pendingRequests[key];\n if (existing) {\n this.log.info(\"Request already pending\", key);\n return existing;\n }\n\n this.pendingRequests[key] = fetch(url, request)\n .then(async (response) => {\n this.log.debug(\"Response\", {\n url,\n status: response.status,\n });\n\n const fetchResponse: FetchResponse = {\n data: await this.responseData(response, options),\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n raw: response,\n };\n\n if (request.method === \"GET\") {\n if (options.cache) {\n await this.cache.set(\n url,\n { data: fetchResponse.data },\n typeof options.cache === \"boolean\" ? undefined : options.cache,\n );\n } else if (!this.alepha.isBrowser()) {\n // only cache etag on server, browser can handle etag itself\n const etag = response.headers.get(\"etag\") ?? undefined;\n if (etag) {\n await this.cache.set(url, { data: fetchResponse.data, etag });\n }\n }\n }\n\n return fetchResponse;\n })\n .finally(() => {\n delete this.pendingRequests[key];\n });\n\n return this.pendingRequests[key];\n }\n\n protected url(\n host: string,\n action: HttpAction,\n args: ServerRequestConfigEntry,\n ) {\n let url = host;\n\n if (action.prefix) {\n url += action.prefix;\n }\n\n url += action.path;\n url = this.pathVariables(url, action, args);\n url = this.queryParams(url, action, args);\n\n return url;\n }\n\n protected async body(\n init: RequestInit,\n headers: Record<string, string>,\n action: HttpAction,\n args: ServerRequestConfigEntry = {},\n ) {\n const hasHeader =\n typeof init.headers === \"object\" &&\n \"content-type\" in init.headers &&\n init.headers[\"content-type\"] === \"multipart/form-data\";\n\n if (hasHeader || isMultipart(action)) {\n if (typeof init.headers === \"object\" && \"content-type\" in init.headers) {\n delete init.headers[\"content-type\"]; // fetch() will fill this for us\n }\n\n const formData = new FormData();\n\n for (const [key, value] of Object.entries(args.body ?? {})) {\n if (typeof value === \"string\") {\n formData.append(key, value);\n continue;\n }\n if (value instanceof Blob) {\n formData.append(key, value);\n continue;\n }\n if (isFileLike(value)) {\n // FileLike must be transformed to WebFile\n formData.append(\n key,\n new File([await value.arrayBuffer()], value.name, {\n type: value.type,\n }),\n );\n }\n }\n\n init.body = formData;\n\n return;\n }\n\n if (!init.body && action.schema?.body) {\n headers[\"content-type\"] = \"application/json\";\n init.body = this.alepha.codec.encode(action.schema?.body, args.body, {\n as: \"string\",\n });\n }\n }\n\n protected async responseData(\n response: Response,\n options: FetchOptions,\n ): Promise<any> {\n if (response.status === 304) {\n let cacheKey = response.url;\n if (typeof window !== \"undefined\") {\n cacheKey = cacheKey.replace(window.location.origin, \"\");\n }\n\n const cached = await this.cache.get(cacheKey);\n if (cached) {\n return cached.data;\n }\n\n // if no cached data (etag-only routes), return empty string\n return \"\";\n }\n\n if (response.status === 204) {\n return;\n }\n\n if (this.isMaybeFile(response)) {\n return this.createFileLike(response);\n }\n\n if (response.headers.get(\"Content-Type\")?.startsWith(\"text/\")) {\n return await response.text();\n }\n\n if (response.headers.get(\"Content-Type\") === \"application/json\") {\n const json = await response.json();\n\n if (response.status >= 400) {\n const jsonError = this.alepha.codec.decode(errorSchema, json);\n const error = new HttpError(jsonError);\n\n await this.alepha.events.emit(\"client:onError\", {\n error,\n });\n\n throw error;\n }\n\n if (options.schema) {\n return this.alepha.codec.decode(options.schema, json);\n }\n\n return json;\n }\n\n if (response.status >= 400) {\n const error = new HttpError({\n status: response.status,\n message: `An error occurred while fetching the resource. (${response.statusText})`,\n });\n\n await this.alepha.events.emit(\"client:onError\", {\n error,\n });\n\n throw error;\n }\n\n return response;\n }\n\n protected isMaybeFile(response: Response): boolean {\n const contentType = response.headers.get(\"Content-Type\");\n if (!contentType) {\n return false;\n }\n\n if (response.headers.get(\"Content-Disposition\")?.includes(\"attachment\")) {\n return true; // If Content-Disposition indicates an attachment, treat it as a file\n }\n\n return (\n contentType.startsWith(\"application/octet-stream\") ||\n contentType.startsWith(\"application/pdf\") ||\n contentType.startsWith(\"application/zip\") ||\n contentType.startsWith(\"image/\") ||\n contentType.startsWith(\"video/\") ||\n contentType.startsWith(\"audio/\")\n );\n }\n\n protected createFileLike(response: Response, defaultFileName = \"\"): FileLike {\n const match = (response.headers.get(\"Content-Disposition\") ?? \"\").match(\n /filename=\"(.+)\"/,\n );\n return {\n name: match?.[1] ? match[1] : defaultFileName,\n type: response.headers.get(\"Content-Type\") ?? \"application/octet-stream\",\n size: Number(response.headers.get(\"Content-Length\") ?? 0),\n lastModified: Date.now(),\n stream: () => {\n throw new Error(\"Not implemented\");\n },\n arrayBuffer: async () => {\n return await response.arrayBuffer();\n },\n text: async () => {\n return await response.text();\n },\n };\n }\n\n public pathVariables(\n url: string,\n action: { schema?: { params?: TObject } },\n args: ServerRequestConfigEntry = {},\n ): string {\n if (typeof args.params === \"object\") {\n const params = action.schema?.params\n ? (this.alepha.codec.decode(\n action.schema.params,\n args.params,\n ) as Record<string, any>)\n : args.params;\n\n for (const key of Object.keys(params)) {\n url = url.replace(`:${key}`, params[key]);\n url = url.replace(`{${key}}`, params[key]);\n }\n }\n\n return url;\n }\n\n public queryParams(\n url: string,\n action: { schema?: { query?: TObject } },\n args: ServerRequestConfigEntry = {},\n ): string {\n if (typeof args.query === \"object\") {\n const query = action.schema?.query\n ? this.alepha.codec.decode(action.schema.query, args.query ?? {})\n : args.query;\n\n for (const key of Object.keys(query)) {\n if (query[key] === undefined) {\n delete query[key];\n }\n if (typeof query[key] === \"object\") {\n query[key] = JSON.stringify(query[key]);\n }\n }\n\n return `${url}?${new URLSearchParams(\n query as Record<string, string>,\n ).toString()}`;\n }\n return url;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface FetchOptions<T extends TSchema = TSchema> {\n /**\n * Key to identify the request in the pending requests.\n */\n key?: string;\n\n /**\n * The schema to validate the response against.\n */\n schema?: {\n response?: T;\n };\n\n /**\n * Built-in cache options.\n */\n localCache?: boolean | number | DurationLike;\n}\n\nexport type RequestInitWithOptions<T extends TSchema = TSchema> = RequestInit &\n FetchOptions<T>;\n\nexport interface FetchResponse<T = any> {\n data: T;\n status: number;\n statusText: string;\n headers: Headers;\n raw?: Response;\n}\n\nexport type HttpClientPendingRequests = Record<\n string,\n Promise<any> | undefined\n>;\n\ninterface HttpClientCache {\n data: any;\n etag?: string;\n}\n\nexport interface FetchActionArgs {\n action: HttpAction;\n host?: string;\n config?: ServerRequestConfigEntry;\n options?: ClientRequestOptions;\n}\n\nexport interface HttpAction {\n method?: string;\n prefix?: string;\n path: string;\n requestBodyType?: string;\n schema?: {\n params?: TObject;\n query?: TObject;\n body?: TRequestBody;\n response?: TResponseBody;\n };\n}\n","import {\n $env,\n $inject,\n type Async,\n createPrimitive,\n isTypeFile,\n KIND,\n Primitive,\n type Static,\n type TObject,\n type TSchema,\n t,\n} from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { RouteMethod } from \"../constants/routeMethods.ts\";\nimport { isMultipart } from \"../helpers/isMultipart.ts\";\nimport { ServerReply } from \"../helpers/ServerReply.ts\";\nimport type {\n RequestConfigSchema,\n ServerRequest,\n ServerResponseBody,\n ServerRoute,\n TRequestBody,\n} from \"../interfaces/ServerRequest.ts\";\nimport { ServerProvider } from \"../providers/ServerProvider.ts\";\nimport { ServerRouterProvider } from \"../providers/ServerRouterProvider.ts\";\nimport {\n type FetchOptions,\n type FetchResponse,\n HttpClient,\n} from \"../services/HttpClient.ts\";\n\n/**\n * Creates a server action primitive for defining type-safe HTTP endpoints.\n *\n * Server actions are the core building blocks for REST APIs in Alepha, providing declarative\n * HTTP endpoints with full type safety, automatic validation, and OpenAPI documentation.\n *\n * **Key Features**\n * - Full TypeScript inference for request/response types\n * - Automatic schema validation using TypeBox\n * - Convention-based URL generation with customizable paths\n * - Direct invocation (`run()`) or HTTP requests (`fetch()`)\n * - Built-in authentication and authorization support\n * - Automatic content-type handling (JSON, form-data, plain text)\n *\n * **URL Generation**\n *\n * **Important:** All `$action` paths are automatically prefixed with `/api`.\n *\n * ```ts\n * $action({ path: \"/users\" }) // → GET /api/users\n * $action({ path: \"/users/:id\" }) // → GET /api/users/:id\n * $action({ path: \"/hello\" }) // → GET /api/hello\n * ```\n *\n * This prefix is configurable via the `SERVER_API_PREFIX` environment variable.\n * HTTP method defaults to GET, or POST if body schema is provided.\n *\n * **Common Use Cases**\n * - CRUD operations with type safety\n * - File upload and download endpoints\n * - Microservice communication\n *\n * @example\n * ```ts\n * class UserController {\n * getUsers = $action({\n * path: \"/users\",\n * schema: {\n * query: t.object({\n * page: t.optional(t.number({ default: 1 })),\n * limit: t.optional(t.number({ default: 10 }))\n * }),\n * response: t.object({\n * users: t.array(t.object({\n * id: t.text(),\n * name: t.text(),\n * email: t.text()\n * })),\n * total: t.number()\n * })\n * },\n * handler: async ({ query }) => {\n * const users = await this.userService.findUsers(query);\n * return { users: users.items, total: users.total };\n * }\n * });\n *\n * createUser = $action({\n * method: \"POST\",\n * path: \"/users\",\n * schema: {\n * body: t.object({\n * name: t.text(),\n * email: t.text({ format: \"email\" })\n * }),\n * response: t.object({ id: t.text(), name: t.text() })\n * },\n * handler: async ({ body }) => {\n * return await this.userService.create(body);\n * }\n * });\n * }\n * ```\n */\nexport const $action = <TConfig extends RequestConfigSchema>(\n options: ActionPrimitiveOptions<TConfig>,\n): ActionPrimitiveFn<TConfig> => {\n const instance = createPrimitive(ActionPrimitive<TConfig>, options);\n const fn = (\n config?: ClientRequestEntry<TConfig>,\n options?: ClientRequestOptions,\n ) => {\n return instance.run(config, options);\n };\n Object.defineProperty(fn, \"name\", {\n get(): string {\n return instance.options.name || instance.config.propertyKey;\n },\n });\n return Object.setPrototypeOf(fn, instance) as ActionPrimitiveFn<TConfig>;\n};\n\n// ----------------------------------------------------------------------------------------------------------\n\nexport interface ActionPrimitiveOptions<TConfig extends RequestConfigSchema>\n extends Omit<ServerRoute, \"handler\" | \"path\" | \"schema\" | \"mapParams\"> {\n /**\n * Name of the action.\n *\n * - It will be used to generate the route path if `path` is not provided.\n * - It will be used to generate the permission name if `security` is enabled.\n */\n name?: string;\n\n /**\n * Group actions together.\n *\n * - If not provided, the service name containing the route will be used.\n * - It will be used as Tag for documentation purposes.\n * - It will be used for permission name generation if `security` is enabled.\n *\n * @example\n * ```ts\n * // group = \"MyController\"\n * class MyController {\n * \thello = $action({ handler: () => \"Hello World\" });\n * }\n *\n * // group = \"users\"\n * class MyOtherController {\n * group = \"users\";\n * a1 = $action({ handler: () => \"Action 1\", group: this.group });\n * a2 = $action({ handler: () => \"Action 2\", group: this.group });\n * }\n * ```\n */\n group?: string;\n\n /**\n * Pathname of the route. If not provided, property key is used.\n */\n path?: string;\n\n /**\n * The route method.\n *\n * - If not provided, it will be set to \"GET\" by default.\n * - If not provider and a body is provided, it will be set to \"POST\".\n *\n * Wildcard methods are not supported for now. (e.g. \"ALL\", \"ANY\", etc.)\n */\n method?: RouteMethod;\n\n /**\n * The config schema of the route.\n * - body: The request body schema.\n * - params: Path variables schema.\n * - query: The request query-params schema.\n * - response: The response schema.\n */\n schema?: TConfig;\n\n /**\n * A short description of the action. Used for documentation purposes.\n */\n description?: string;\n\n /**\n * Disable the route. Useful with env variables do disable one specific route.\n * Route won't be available in the API but can still be called locally!\n */\n disabled?: boolean;\n\n /**\n * Main route handler. This is where the route logic is implemented.\n */\n handler: ServerActionHandler<TConfig>;\n}\n\n// ----------------------------------------------------------------------------------------------------------\n\nconst envSchema = t.object({\n SERVER_API_PREFIX: t.text({\n description: \"Prefix for all API routes (e.g. $action).\",\n default: \"/api\",\n }),\n});\n\nexport class ActionPrimitive<\n TConfig extends RequestConfigSchema,\n> extends Primitive<ActionPrimitiveOptions<TConfig>> {\n protected readonly log = $logger();\n protected readonly env = $env(envSchema);\n protected readonly httpClient = $inject(HttpClient);\n protected readonly serverProvider = $inject(ServerProvider);\n protected readonly serverRouterProvider = $inject(ServerRouterProvider);\n\n protected onInit() {\n if (this.options.disabled) {\n this.log.debug(\n `Action '${this.name}' is disabled. It won't be available in the API.`,\n );\n return;\n }\n this.serverRouterProvider.createRoute(this.route);\n }\n\n public get prefix() {\n return this.env.SERVER_API_PREFIX;\n }\n\n public get route(): ServerRoute {\n return {\n ...(this.options as any), // TODO: fix schema.header mapping\n method: this.method,\n path: `${this.prefix}${this.path}`,\n } as ServerRoute;\n }\n\n /**\n * Returns the name of the action.\n */\n public get name(): string {\n return this.options.name || this.config.propertyKey;\n }\n\n /**\n * Returns the group of the action. (e.g. \"orders\", \"admin\", etc.)\n */\n public get group(): string {\n return this.options.group || this.config.service.name;\n }\n\n /**\n * Returns the HTTP method of the action.\n */\n public get method(): RouteMethod {\n return this.options.method || (this.options.schema?.body ? \"POST\" : \"GET\");\n }\n\n /**\n * Returns the path of the action.\n *\n * Path is prefixed by `/api` by default.\n */\n public get path(): string {\n if (this.options.path) {\n return this.options.path;\n }\n\n let path = `/${this.name}`;\n\n if (this.options.schema?.params) {\n for (const [key] of Object.entries(\n this.options.schema.params.properties,\n )) {\n path += `/:${key}`;\n }\n }\n\n return path;\n }\n\n public get schema(): TConfig | undefined {\n return this.options.schema;\n }\n\n public getBodyContentType(): string | undefined {\n if (this.options.schema?.body) {\n // TODO: move to `alepha.server.multipart` module ?\n if (isMultipart(this.options)) {\n return \"multipart/form-data\";\n }\n\n if (t.schema.isString(this.options.schema.body)) {\n // if body is a string, we assume it's plain text\n return \"text/plain\";\n }\n\n if (\n t.schema.isObject(this.options.schema.body) ||\n t.schema.isArray(this.options.schema.body) ||\n t.schema.isRecord(this.options.schema.body)\n )\n // if body is an object or array, we assume it's JSON\n return \"application/json\";\n }\n }\n\n /**\n * Call the action handler directly.\n * There is no HTTP layer involved.\n */\n public async run(\n config?: ClientRequestEntry<TConfig>,\n options: ClientRequestOptions = {}, // most of the options are ignored here\n ): Promise<ClientRequestResponse<TConfig>> {\n const handler = this.options.handler;\n const {\n body,\n params = {},\n query = {},\n headers = {},\n } = (config ?? {}) as ClientRequestEntryContainer<RequestConfigSchema>;\n const reply = new ServerReply();\n const method = this.method;\n\n // we use localhost as the base URL for the action\n const url = new URL(`http://localhost${this.path ?? \"\"}`);\n\n const serverActionRequest: Partial<ServerRequest> = {\n method,\n url,\n body,\n params,\n query,\n headers,\n reply,\n metadata: {},\n };\n\n await this.alepha.events.emit(\"action:onRequest\", {\n action: this,\n request: serverActionRequest as ServerRequest,\n options,\n });\n\n if (serverActionRequest.reply?.body) {\n return serverActionRequest.reply.body as ClientRequestResponse<TConfig>;\n }\n\n if (serverActionRequest.query && this.options.schema?.query) {\n serverActionRequest.query = this.alepha.codec.encode(\n this.options.schema.query,\n serverActionRequest.query,\n );\n }\n\n if (serverActionRequest.headers && this.options.schema?.headers) {\n serverActionRequest.headers = this.alepha.codec.encode(\n this.options.schema.headers,\n serverActionRequest.headers,\n ) as Record<string, any>;\n }\n\n if (serverActionRequest.body && this.options.schema?.body) {\n serverActionRequest.body = this.alepha.codec.encode(\n this.options.schema.body,\n serverActionRequest.body,\n ) as unknown;\n }\n\n if (serverActionRequest.params && this.options.schema?.params) {\n serverActionRequest.params = this.alepha.codec.encode(\n this.options.schema.params,\n serverActionRequest.params,\n ) as Record<string, any>;\n }\n\n this.serverRouterProvider.validateRequest(\n this.options,\n serverActionRequest as ServerRequest,\n );\n\n let response: any = await handler(\n serverActionRequest as ServerActionRequest<TConfig>,\n );\n\n // we validate response just to remove undeclared properties from response\n if (\n this.options.schema?.response &&\n // skip validation if response is expected as file\n !isTypeFile(this.options.schema.response)\n ) {\n response = this.alepha.codec.validate(\n this.options.schema.response,\n response,\n );\n }\n\n await this.alepha.events.emit(\"action:onResponse\", {\n action: this,\n request: serverActionRequest as ServerRequest,\n options,\n response,\n });\n\n return response;\n }\n\n /**\n * Works like `run`, but always fetches (http request) the route.\n */\n public fetch(\n config?: ClientRequestEntry<TConfig>,\n options?: ClientRequestOptions,\n ): Promise<FetchResponse<ClientRequestResponse<TConfig>>> {\n return this.httpClient.fetchAction({\n host: this.serverProvider.hostname, // that's the trick, we just use the server hostname\n action: this,\n config,\n options,\n });\n }\n}\n\nexport interface ActionPrimitiveFn<TConfig extends RequestConfigSchema>\n extends ActionPrimitive<TConfig> {\n (\n config?: ClientRequestEntry<TConfig>,\n options?: ClientRequestOptions,\n ): Promise<ClientRequestResponse<TConfig>>;\n}\n\n$action[KIND] = ActionPrimitive;\n\n// ----------------------------------------------------------------------------------------------------------\n\nexport type ClientRequestEntry<\n TConfig extends RequestConfigSchema,\n T = ClientRequestEntryContainer<TConfig>,\n> = {\n [K in keyof T as T[K] extends undefined ? never : K]: T[K];\n};\n\nexport type ClientRequestEntryContainer<TConfig extends RequestConfigSchema> = {\n body: TConfig[\"body\"] extends TRequestBody\n ? Static<TConfig[\"body\"]>\n : undefined;\n\n params: TConfig[\"params\"] extends TObject\n ? Static<TConfig[\"params\"]>\n : undefined;\n\n headers?: TConfig[\"headers\"] extends TObject\n ? Static<TConfig[\"headers\"]>\n : Record<string, string>;\n\n query?: TConfig[\"query\"] extends TObject\n ? Partial<Static<TConfig[\"query\"]>>\n : Record<string, string>;\n};\n\nexport interface ClientRequestOptions extends FetchOptions {\n /**\n * Standard request fetch options.\n */\n request?: RequestInit;\n\n query?: Record<string, string | number | boolean>;\n}\n\nexport type ClientRequestResponse<TConfig extends RequestConfigSchema> =\n TConfig[\"response\"] extends TSchema ? Static<TConfig[\"response\"]> : any;\n\n/**\n * Specific handler for server actions.\n */\nexport type ServerActionHandler<\n TConfig extends RequestConfigSchema = RequestConfigSchema,\n> = (\n request: ServerActionRequest<TConfig>,\n) => Async<ServerResponseBody<TConfig>>;\n\n/**\n * Server Action Request Interface\n *\n * Can be extended with module augmentation to add custom properties (like `user` in Server Security).\n *\n * This is NOT Server Request, but a specific type for actions.\n */\nexport interface ServerActionRequest<TConfig extends RequestConfigSchema>\n extends ServerRequest<TConfig> {}\n","import { $inject, createPrimitive, KIND, Primitive } from \"alepha\";\nimport type {\n RequestConfigSchema,\n ServerRoute,\n} from \"../interfaces/ServerRequest.ts\";\nimport { ServerRouterProvider } from \"../providers/ServerRouterProvider.ts\";\n\n/**\n * Create a basic endpoint.\n *\n * It's a low level primitive. You probably want to use `$action` instead.\n *\n * @see {@link $action}\n * @see {@link $page}\n */\nexport const $route = <TConfig extends RequestConfigSchema>(\n options: RoutePrimitiveOptions<TConfig>,\n): RoutePrimitive<TConfig> => {\n return createPrimitive(RoutePrimitive<TConfig>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface RoutePrimitiveOptions<\n TConfig extends RequestConfigSchema = RequestConfigSchema,\n> extends ServerRoute<TConfig> {}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class RoutePrimitive<\n TConfig extends RequestConfigSchema,\n> extends Primitive<RoutePrimitiveOptions<TConfig>> {\n protected readonly serverRouterProvider = $inject(ServerRouterProvider);\n\n protected onInit() {\n this.serverRouterProvider.createRoute(this.options);\n }\n}\n\n$route[KIND] = RoutePrimitive;\n","import { $env, $hook, $inject, Alepha, type Static, t } from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { ServerProvider } from \"./ServerProvider.ts\";\nimport { ServerRouterProvider } from \"./ServerRouterProvider.ts\";\n\nconst envSchema = t.object({\n SERVER_PORT: t.integer({\n default: 3000,\n min: 0,\n max: 65535,\n description: \"Set 0 to listen on a random port.\",\n }),\n SERVER_HOST: t.text({\n default: \"localhost\",\n description: \"Set 0.0.0.0 to listen on all interfaces.\",\n }),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\nexport class BunHttpServerProvider extends ServerProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly log = $logger();\n protected readonly env = $env(envSchema);\n protected readonly router = $inject(ServerRouterProvider);\n\n protected bunServer?: ReturnType<typeof Bun.serve>;\n\n public get hostname(): string {\n if (this.bunServer) {\n return `http://${this.bunServer.hostname}:${this.bunServer.port}`;\n }\n return `http://${this.env.SERVER_HOST}:${this.env.SERVER_PORT}`;\n }\n\n public readonly start = $hook({\n on: \"start\",\n handler: async () => {\n await this.listen();\n },\n });\n\n protected readonly stop = $hook({\n on: \"stop\",\n handler: async () => {\n if (this.alepha.isProduction()) {\n await this.close();\n return;\n }\n\n // do not await in development & test\n this.close().catch(() => {});\n },\n });\n\n protected async listen() {\n let port = this.env.SERVER_PORT;\n\n // for testing, use a random port if port is 3000 (default)\n if (this.alepha.isTest() && port === 3000) {\n port = 0;\n }\n\n try {\n this.bunServer = Bun.serve({\n port,\n hostname: this.env.SERVER_HOST,\n fetch: async (request: Request) => {\n this.log.trace(`Incoming Bun request -> ${request.url}`);\n\n // Create WebRequestEvent for handleWebRequest\n const webRequestEvent = {\n req: request,\n res: undefined as Response | undefined,\n };\n\n try {\n await this.handleWebRequest(webRequestEvent);\n\n if (!webRequestEvent.res) {\n // No response set, return 500\n return new Response(\"Internal Server Error\", {\n status: 500,\n headers: { \"content-type\": \"text/plain\" },\n });\n }\n\n return webRequestEvent.res;\n } catch (err) {\n this.log.error(\"Error handling request\", err);\n return new Response(\"Internal Server Error\", {\n status: 500,\n headers: { \"content-type\": \"text/plain\" },\n });\n }\n },\n error: (error: Error) => {\n this.log.error(\"Bun server error\", error);\n return new Response(\"Internal Server Error\", {\n status: 500,\n headers: { \"content-type\": \"text/plain\" },\n });\n },\n });\n\n this.log.info(`Server listening on ${this.hostname}/`);\n } catch (err) {\n this.log.error(\"Failed to start Bun server\", err);\n throw err;\n }\n }\n\n protected async close() {\n if (!this.bunServer) {\n return;\n }\n\n try {\n // Bun's server.stop() returns a promise that resolves when all connections are closed\n const stopPromise = this.bunServer.stop();\n\n // Race between stop completion and timeout\n await Promise.race([this.dateTimeProvider.wait(10000), stopPromise]);\n\n this.bunServer = undefined;\n this.log.info(\"Server closed\");\n } catch (err) {\n this.log.error(\"Error closing Bun server\", err);\n throw err;\n }\n }\n}\n","import {\n createServer,\n type IncomingMessage,\n type Server,\n type ServerResponse,\n} from \"node:http\";\nimport type { Socket } from \"node:net\";\nimport { $env, $hook, $inject, Alepha, type Static, t } from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { ServerProvider } from \"./ServerProvider.ts\";\nimport { ServerRouterProvider } from \"./ServerRouterProvider.ts\";\n\nconst envSchema = t.object({\n SERVER_PORT: t.integer({\n default: 3000,\n min: 0,\n max: 65535,\n description: \"Set 0 to listen on a random port.\",\n }),\n SERVER_HOST: t.text({\n default: \"localhost\",\n description: \"Set 0.0.0.0 to listen on all interfaces.\",\n }),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\nexport class NodeHttpServerProvider extends ServerProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly log = $logger();\n protected readonly env = $env(envSchema);\n protected readonly router = $inject(ServerRouterProvider);\n\n /**\n * Track active connections for fast shutdown.\n */\n protected readonly connections = new Set<Socket>();\n\n /**\n * Get number of active connections.\n */\n public getConnectionsCount(): number {\n return this.connections.size;\n }\n\n /**\n * Server options.\n */\n public readonly options = {\n /**\n * Graceful shutdown timeout in ms.\n * After this, remaining connections are forcefully closed.\n * @default 30000\n */\n shutdownTimeout: 10000,\n };\n\n public get hostname(): string {\n if (this.server.listening) {\n const address = this.server.address();\n if (typeof address === \"object\" && address !== null) {\n return `http://${this.env.SERVER_HOST}:${address.port}`;\n }\n }\n return `http://${this.env.SERVER_HOST}:${this.env.SERVER_PORT}`;\n }\n\n // Pre-bound error handler to avoid function allocation per request\n protected readonly handleRequestError = (res: ServerResponse, err: Error) => {\n this.log.error(\"Error handling request\", err);\n res.statusCode = 500;\n res.end(\"Internal Server Error\");\n };\n\n // P3: Pre-allocated event object to avoid { req, res } allocation per request\n // Safe because handleNodeRequest completes before the next request reuses this object\n protected readonly nodeRequestEvent = {\n req: null as unknown as IncomingMessage,\n res: null as unknown as ServerResponse,\n };\n\n public readonly server = this.createHttpServer((req, res) => {\n // Reuse pre-allocated event object instead of creating { req, res } per request\n const ev = this.nodeRequestEvent;\n ev.req = req;\n ev.res = res;\n // Note: handleNodeRequest returns a promise that resolves after response is sent\n this.handleNodeRequest(ev).catch((err) =>\n this.handleRequestError(res, err),\n );\n });\n\n public readonly start = $hook({\n on: \"start\",\n handler: async () => {\n await this.listen();\n this.alepha.store.set(\"alepha.node.server\", this.server);\n },\n });\n\n protected createHttpServer(\n func: (req: IncomingMessage, res: ServerResponse) => void,\n ): Server {\n const server = createServer(\n {\n // nov 25 - keep connections alive for better performance, cuz we http/1.1 by default\n keepAlive: this.alepha.isProduction(),\n },\n func,\n );\n\n // Track connections for fast shutdown\n server.on(\"connection\", (socket) => {\n this.connections.add(socket);\n socket.on(\"close\", () => this.connections.delete(socket));\n });\n\n return server;\n }\n\n protected readonly stop = $hook({\n on: \"stop\",\n handler: async () => {\n await this.close();\n },\n });\n\n protected async listen() {\n let port = this.env.SERVER_PORT;\n\n // for testing, use a random port if port is 3000 (default)\n if (this.alepha.isTest() && port === 3000) {\n port = 0;\n }\n\n await new Promise<void>((resolve, reject) => {\n this.server?.listen(port, this.env.SERVER_HOST, () => {\n this.log.info(`Server listening on ${this.hostname}/`);\n resolve();\n });\n\n this.server?.on(\"error\", (err) => {\n reject(err);\n });\n });\n }\n\n protected async close() {\n // Dev/Test: instant shutdown (destroy connections immediately)\n // Production: graceful shutdown (wait for requests to complete, then close)\n if (!this.alepha.isProduction()) {\n this.destroyAllConnections();\n }\n\n // Stop accepting new connections\n const closePromise = new Promise<void>((resolve, reject) => {\n this.server?.close((err) => (err ? reject(err) : resolve()));\n });\n\n if (this.alepha.isProduction() && this.connections.size > 0) {\n // In production, wait for connections with timeout\n const timeout = this.options.shutdownTimeout;\n\n // Set up timeout to force-close connections\n const timeoutId = setTimeout(() => {\n if (this.connections.size > 0) {\n this.log.warn(\n `Shutdown timeout (${timeout}ms) reached, forcing ${this.connections.size} connections to close`,\n );\n // Destroy sockets - this triggers 'close' events which eventually resolves closePromise\n for (const socket of this.connections) {\n socket.destroy();\n }\n }\n }, timeout);\n\n // Wait for server to fully close (all connections closed)\n await closePromise;\n clearTimeout(timeoutId);\n this.connections.clear();\n } else {\n await closePromise;\n }\n\n this.log.info(\"Server closed\");\n }\n\n protected destroyAllConnections() {\n for (const socket of this.connections) {\n socket.destroy();\n }\n this.connections.clear();\n }\n}\n","import { ReadableStream as WebStream } from \"node:stream/web\";\nimport { createBrotliDecompress, createGunzip, createInflate } from \"node:zlib\";\nimport type { TSchema } from \"alepha\";\nimport { $env, $hook, $inject, Alepha, t } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { HttpError } from \"../errors/HttpError.ts\";\n\nconst envSchema = t.object({\n SERVER_BODY_PARSER_INFLATE: t.boolean({\n default: true,\n description: \"Enable decompression of request body.\",\n }),\n SERVER_BODY_PARSER_LIMIT: t.integer({\n default: 100_000, // 100KB\n min: 0,\n description: \"Maximum size of request body in bytes.\",\n }),\n});\n\nexport class ServerBodyParserProvider {\n protected readonly env = $env(envSchema);\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n\n public readonly onRequest = $hook({\n on: \"server:onRequest\",\n handler: ({ route, request }) => {\n if (request.body) {\n return; // already parsed\n }\n\n let stream: ReadableStream | undefined;\n\n if (request.raw.web?.req.body) {\n stream = request.raw.web.req.body;\n } else if (request.raw.node?.req) {\n // convert Node.js IncomingMessage to Web ReadableStream\n // TODO: check performance impact, it's better to directly read from Node stream!\n stream = WebStream.from(\n request.raw.node.req,\n ) as unknown as ReadableStream;\n }\n\n if (!stream) {\n return;\n }\n\n if (route.schema?.body) {\n return this.parse(stream, request.headers, route.schema.body)\n .then((body) => {\n if (body) {\n request.body = body;\n }\n })\n .catch((error) => {\n if (error instanceof HttpError) {\n throw error;\n }\n throw new HttpError(\n {\n status: 400,\n message: \"Failed to parse request body\",\n },\n error,\n );\n });\n }\n },\n });\n\n public async parse(\n stream: ReadableStream,\n headers: Record<string, string>,\n schema: TSchema,\n ): Promise<object | string | undefined> {\n const contentType = headers[\"content-type\"];\n const contentEncoding = headers[\"content-encoding\"];\n\n if (!contentType) return undefined;\n\n if (contentType.startsWith(\"text/plain\") || t.schema.isString(schema)) {\n return this.parseText(stream, contentEncoding);\n }\n\n if (contentType.startsWith(\"application/json\")) {\n return this.parseJson(stream, contentEncoding);\n }\n\n if (contentType.startsWith(\"application/x-www-form-urlencoded\")) {\n return this.parseUrlEncoded(stream, contentEncoding);\n }\n\n return undefined;\n }\n\n public async parseText(\n stream: ReadableStream,\n contentEncoding?: string,\n ): Promise<string> {\n const buffer = await this.streamToBuffer(stream);\n const bufferInflated = await this.maybeDecompress(buffer, contentEncoding);\n return bufferInflated.toString(\"utf-8\");\n }\n\n public async parseUrlEncoded(\n stream: ReadableStream,\n contentEncoding?: string,\n ): Promise<object> {\n const text = await this.parseText(stream, contentEncoding);\n const params = new URLSearchParams(text);\n const result: Record<string, string> = {};\n for (const [key, value] of params.entries()) {\n result[key] = value;\n }\n\n return result;\n }\n\n public async parseJson(\n stream: ReadableStream,\n contentEncoding?: string,\n ): Promise<object> {\n const text = await this.parseText(stream, contentEncoding);\n return JSON.parse(text);\n }\n\n protected async maybeDecompress(\n buffer: Buffer,\n encoding: string | undefined,\n ): Promise<Buffer> {\n if (!this.env.SERVER_BODY_PARSER_INFLATE && encoding) {\n throw new HttpError({\n status: 415,\n message: `Content-Encoding ${encoding} not allowed`,\n });\n }\n\n switch (encoding) {\n case \"gzip\":\n return new Promise((res, rej) =>\n createGunzip()\n .end(buffer, () => {})\n .on(\"data\", res)\n .on(\"error\", rej),\n );\n case \"deflate\":\n return new Promise((res, rej) =>\n createInflate()\n .end(buffer, () => {})\n .on(\"data\", res)\n .on(\"error\", rej),\n );\n case \"br\":\n return new Promise((res, rej) =>\n createBrotliDecompress()\n .end(buffer, () => {})\n .on(\"data\", res)\n .on(\"error\", rej),\n );\n case undefined:\n case \"identity\":\n return buffer;\n default:\n throw new Error(`Unsupported Content-Encoding: ${encoding}`);\n }\n }\n\n /**\n * Convert Web ReadableStream to Buffer, with a size limit.\n *\n * TODO: move to alepha/file FileUtils\n */\n protected async streamToBuffer(stream: ReadableStream): Promise<Buffer> {\n const chunks: Uint8Array[] = [];\n let totalLength = 0;\n\n const reader = stream.getReader();\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) {\n break;\n }\n\n if (value) {\n totalLength += value.length;\n\n if (totalLength > this.env.SERVER_BODY_PARSER_LIMIT) {\n this.log.error(\n `Body size limit exceeded: ${totalLength} > ${this.env.SERVER_BODY_PARSER_LIMIT}`,\n );\n\n await reader.cancel(); // Cancel the stream\n\n throw new HttpError({\n status: 413,\n message: `Request body size limit exceeded`,\n });\n }\n\n chunks.push(value);\n }\n }\n\n // Combine all chunks into a single Buffer\n const combinedLength = chunks.reduce(\n (sum, chunk) => sum + chunk.length,\n 0,\n );\n const combined = new Uint8Array(combinedLength);\n let offset = 0;\n\n for (const chunk of chunks) {\n combined.set(chunk, offset);\n offset += chunk.length;\n }\n\n return Buffer.from(combined);\n } catch (error) {\n // Make sure to release the reader lock\n reader.releaseLock();\n throw error;\n }\n }\n}\n","import { $hook, $inject, Alepha } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\n\nexport class ServerLoggerProvider {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n\n public readonly onRequest = $hook({\n on: \"server:onRequest\",\n priority: \"first\",\n handler: ({ route, request }) => {\n if (route.silent || request.metadata.vite) {\n return;\n }\n\n request.metadata.now = Date.now();\n\n const data: Record<string, string> = {\n method: request.method,\n path: request.url.pathname,\n };\n\n if (this.alepha.isProduction()) {\n data.agent = request.headers[\"user-agent\"];\n const ip = request.ip;\n if (ip) {\n data.ip = ip;\n }\n }\n\n this.log.info(\"Incoming request\", data);\n },\n });\n\n public readonly onError = $hook({\n on: \"server:onError\",\n priority: \"last\",\n handler: ({ error }) => {\n this.log.error(\"Request has failed\", error);\n },\n });\n\n public readonly onResponse = $hook({\n on: \"server:onResponse\",\n priority: \"last\",\n handler: ({ route, request, response }) => {\n if (route.silent || request.metadata.vite) {\n return;\n }\n\n const ms = Date.now() - request.metadata.now;\n this.log.info(\"Request completed\", { status: response.status, ms });\n },\n });\n}\n","import { $hook, $inject, Alepha } from \"alepha\";\nimport { HttpError } from \"../errors/HttpError.ts\";\n\n/**\n * On every request, this provider checks if the server is ready.\n *\n * If the server is not ready, it responds with a 503 status code and a message indicating that the server is not ready yet.\n *\n * The response also includes a `Retry-After` header indicating that the client should retry after 5 seconds.\n */\nexport class ServerNotReadyProvider {\n protected readonly alepha = $inject(Alepha);\n\n public readonly onRequest = $hook({\n on: \"server:onRequest\",\n priority: \"first\",\n handler: ({ request: { reply } }) => {\n if (this.alepha.isReady()) {\n return;\n }\n\n reply.headers[\"Retry-After\"] = \"5\"; // Retry after 5 seconds\n\n throw new HttpError({\n status: 503,\n message: \"Server is not ready yet. Please try again later.\",\n });\n },\n });\n}\n","export const routeMethods = [\n // list of supported http methods\n \"GET\",\n \"POST\",\n \"PUT\",\n \"PATCH\",\n \"DELETE\",\n \"HEAD\",\n \"OPTIONS\",\n \"CONNECT\",\n \"TRACE\",\n] as const;\n\nexport type RouteMethod = (typeof routeMethods)[number];\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class BadRequestError extends HttpError {\n constructor(message = \"Invalid request body\", cause?: unknown) {\n super(\n {\n message,\n status: 400,\n },\n cause,\n );\n }\n}\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class ConflictError extends HttpError {\n constructor(message = \"Entity already exists\", cause?: unknown) {\n super(\n {\n message,\n status: 409,\n },\n cause,\n );\n }\n}\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class ForbiddenError extends HttpError {\n constructor(\n message = \"No permission to access this resource\",\n cause?: unknown,\n ) {\n super(\n {\n message,\n status: 403,\n },\n cause,\n );\n }\n}\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class NotFoundError extends HttpError {\n constructor(message = \"Resource not found\", cause?: unknown) {\n super(\n {\n message,\n status: 404,\n },\n cause,\n );\n }\n}\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class UnauthorizedError extends HttpError {\n readonly name = \"UnauthorizedError\";\n\n constructor(\n message = \"Not allowed to access this resource\",\n cause?: unknown,\n ) {\n super(\n {\n message,\n status: 401,\n },\n cause,\n );\n }\n}\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const okSchema = t.object(\n {\n ok: t.boolean({ description: \"True when operation succeed\" }),\n id: t.optional(t.union([t.text(), t.integer()])),\n count: t.optional(\n t.number({ description: \"Number of resources affected\" }),\n ),\n },\n {\n title: \"Ok\",\n description: \"Generic response after a successful operation on a resource\",\n },\n);\n\nexport type Ok = Static<typeof okSchema>;\n","import type { Server } from \"node:http\";\nimport { $module, type Alepha, type PrimitiveFactoryLike } from \"alepha\";\nimport type { HttpError } from \"./errors/HttpError.ts\";\nimport type {\n NodeRequestEvent,\n RequestConfigSchema,\n ServerRequest,\n ServerRequestConfigEntry,\n ServerResponse,\n ServerRoute,\n WebRequestEvent,\n} from \"./interfaces/ServerRequest.ts\";\nimport {\n $action,\n type ActionPrimitive,\n type ClientRequestOptions,\n} from \"./primitives/$action.ts\";\nimport { $route } from \"./primitives/$route.ts\";\nimport { BunHttpServerProvider } from \"./providers/BunHttpServerProvider.ts\";\nimport { NodeHttpServerProvider } from \"./providers/NodeHttpServerProvider.ts\";\nimport { ServerBodyParserProvider } from \"./providers/ServerBodyParserProvider.ts\";\nimport { ServerLoggerProvider } from \"./providers/ServerLoggerProvider.ts\";\nimport { ServerNotReadyProvider } from \"./providers/ServerNotReadyProvider.ts\";\nimport { ServerProvider } from \"./providers/ServerProvider.ts\";\nimport { ServerRouterProvider } from \"./providers/ServerRouterProvider.ts\";\nimport { ServerTimingProvider } from \"./providers/ServerTimingProvider.ts\";\nimport type { FetchOptions, HttpAction } from \"./services/HttpClient.ts\";\nimport { HttpClient } from \"./services/HttpClient.ts\";\nimport { ServerRequestParser } from \"./services/ServerRequestParser.ts\";\nimport { UserAgentParser } from \"./services/UserAgentParser.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha\" {\n interface State {\n \"alepha.node.server\"?: Server;\n }\n interface Hooks {\n // -----------------------------------------------------------------------------------------------------------------\n // Local Actions hooks\n \"action:onRequest\": {\n action: ActionPrimitive<RequestConfigSchema>;\n request: ServerRequest;\n options: ClientRequestOptions;\n };\n \"action:onResponse\": {\n action: ActionPrimitive<RequestConfigSchema>;\n request: ServerRequest;\n options: ClientRequestOptions;\n response: any;\n };\n // -----------------------------------------------------------------------------------------------------------------\n // Server hooks\n \"server:onRequest\": {\n route: ServerRoute;\n request: ServerRequest;\n };\n \"server:onError\": {\n route: ServerRoute;\n request: ServerRequest;\n error: Error;\n };\n // last chance to modify the response -\n // TODO: probably not really needed, we can also update the response in the onResponse hook...\n \"server:onSend\": {\n route: ServerRoute;\n request: ServerRequest;\n };\n // response is ready\n \"server:onResponse\": {\n route: ServerRoute;\n request: ServerRequest;\n response: ServerResponse;\n };\n // -----------------------------------------------------------------------------------------------------------------\n // Http client hooks\n \"client:onRequest\": {\n route: HttpAction;\n config: ServerRequestConfigEntry;\n options: ClientRequestOptions;\n headers: Record<string, string>;\n request: RequestInit;\n };\n \"client:beforeFetch\": {\n url: string;\n options: FetchOptions;\n request: RequestInit;\n };\n \"client:onError\": {\n route?: HttpAction;\n error: HttpError;\n };\n // -----------------------------------------------------------------------------------------------------------------\n // Internal hooks\n \"node:request\": NodeRequestEvent;\n \"web:request\": WebRequestEvent;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./index.shared.ts\";\nexport * from \"./primitives/$action.ts\";\nexport * from \"./primitives/$route.ts\";\nexport * from \"./providers/BunHttpServerProvider.ts\";\nexport * from \"./providers/NodeHttpServerProvider.ts\";\nexport * from \"./providers/ServerLoggerProvider.ts\";\nexport * from \"./providers/ServerNotReadyProvider.ts\";\nexport * from \"./providers/ServerProvider.ts\";\nexport * from \"./providers/ServerRouterProvider.ts\";\nexport * from \"./providers/ServerTimingProvider.ts\";\nexport * from \"./services/UserAgentParser.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * | type | quality | stability |\n * |------|---------|-----------|\n * | backend | epic | stable |\n *\n * Convention-driven HTTP server with automatic validation and type inference.\n *\n * **Features:**\n * - Type-safe API endpoints with schema validation\n * - Lower-level HTTP route definitions\n * - Automatic request/response validation via TypeBox\n * - Convention-based URL generation (`/api/{ActionName}`)\n * - Direct invocation (`run()`) or HTTP (`fetch()`)\n * - Built-in authentication integration\n * - Multipart file upload handling\n * - Content-type auto-negotiation (JSON, form-data, text)\n * - HTTP methods: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS\n * - Error handling: BadRequestError, ValidationError, ForbiddenError, UnauthorizedError, ConflictError, NotFoundError\n *\n * @module alepha.server\n */\nexport const AlephaServer = $module({\n name: \"alepha.server\",\n primitives: [$route, $action as PrimitiveFactoryLike],\n services: [\n ServerProvider,\n BunHttpServerProvider,\n NodeHttpServerProvider,\n ServerBodyParserProvider,\n ServerLoggerProvider,\n ServerNotReadyProvider,\n ServerTimingProvider,\n HttpClient,\n UserAgentParser,\n ServerRequestParser,\n ServerRouterProvider,\n ],\n register: (alepha: Alepha) => {\n if (!alepha.isServerless()) {\n if (alepha.isBun()) {\n alepha.with({\n optional: true,\n provide: ServerProvider,\n use: BunHttpServerProvider,\n });\n } else {\n alepha.with({\n optional: true,\n provide: ServerProvider,\n use: NodeHttpServerProvider,\n });\n }\n } else {\n alepha.with(ServerProvider);\n }\n\n alepha.with(ServerBodyParserProvider);\n alepha.with(ServerLoggerProvider);\n alepha.with(ServerNotReadyProvider);\n\n if (!alepha.isProduction()) {\n alepha.with(ServerTimingProvider);\n }\n },\n});\n"],"mappings":"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,QAAQ;GAGV,UAAU,QAAQ,oBAAoB,QAAQ;GAC9C,WAAW,QAAQ,qBAAqB,QAAQ;GACjD;;CAOH,OAA0B,eAAe;EACvC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;CAED,AAAO,SAAS,SAAqC;EACnD,MAAM,KAAK,QAAQ,QAAQ;AAC3B,MAAI,CAAC,GAAI,QAAO;AAEhB,SAAO,oBAAoB,aAAa,MAAM,YAAY,QAAQ,KAAK,GAAG,CAAC;;CAO7E,OAA0B,kBAAkB;EAC1C;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;CAED,AAAO,YAAY,SAAqC;EACtD,MAAM,KAAK,QAAQ,QAAQ;AAC3B,MAAI,CAAC,GAAI,QAAO;AAEhB,SAAO,oBAAoB,gBAAgB,MAAM,YAC/C,QAAQ,KAAK,GAAG,CACjB;;CAOH,AAAO,YAAY,SAA8C;EAE/D,MAAM,iBAAiB,QAAQ,QAAQ;AACvC,MAAI,eACF,QAAO,eAAe,aAAa,KAAK,UAAU,UAAU;EAI9D,MAAM,kBAAkB,QAAQ,QAAQ;AACxC,MAAI,gBACF,KAAI;AAEF,OADe,KAAK,MAAM,gBAAgB,CAC/B,WAAW,QAAS,QAAO;UAChC;AAMV,MAAI,QAAQ,IAAI,aAAa,SAC3B,QAAO;AAGT,SAAO;;CAOT,A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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["envSchema","NodeWebStream","NodeStream","envSchema","envSchema","envSchema","WebStream"],"sources":["../../../src/server/core/helpers/isMultipart.ts","../../../src/server/core/helpers/ServerReply.ts","../../../src/server/core/errors/HttpError.ts","../../../src/server/core/errors/ValidationError.ts","../../../src/server/core/services/UserAgentParser.ts","../../../src/server/core/services/ServerRequestParser.ts","../../../src/server/core/providers/ServerTimingProvider.ts","../../../src/server/core/providers/ServerRouterProvider.ts","../../../src/server/core/providers/ServerProvider.ts","../../../src/server/core/schemas/errorSchema.ts","../../../src/server/core/services/HttpClient.ts","../../../src/server/core/primitives/$action.ts","../../../src/server/core/primitives/$route.ts","../../../src/server/core/providers/BunHttpServerProvider.ts","../../../src/server/core/providers/NodeHttpServerProvider.ts","../../../src/server/core/providers/ServerBodyParserProvider.ts","../../../src/server/core/providers/ServerLoggerProvider.ts","../../../src/server/core/providers/ServerNotReadyProvider.ts","../../../src/server/core/constants/routeMethods.ts","../../../src/server/core/errors/BadRequestError.ts","../../../src/server/core/errors/ConflictError.ts","../../../src/server/core/errors/ForbiddenError.ts","../../../src/server/core/errors/NotFoundError.ts","../../../src/server/core/errors/UnauthorizedError.ts","../../../src/server/core/schemas/okSchema.ts","../../../src/server/core/index.ts"],"sourcesContent":["import type { RequestConfigSchema } from \"../interfaces/ServerRequest.ts\";\n\n/**\n * Checks if the route has multipart/form-data request body.\n */\nexport const isMultipart = (options: {\n schema?: RequestConfigSchema;\n requestBodyType?: string;\n}): boolean => {\n if (options.requestBodyType === \"multipart/form-data\") {\n return true;\n }\n\n if (options.schema?.body && \"properties\" in options.schema.body) {\n const properties: Record<string, any> = options.schema.body.properties;\n for (const key in properties) {\n if (properties[key].format === \"binary\") {\n return true;\n }\n }\n }\n\n return false;\n};\n","/**\n * Helper for building server replies.\n */\nexport class ServerReply {\n // TODO: make it private\n public headers: Record<string, string> & {\n \"set-cookie\"?: string[];\n } = {};\n\n public status?: number; // default 200, or 204 (no content)\n\n public body?: any;\n\n /**\n * Redirect to a given URL with optional status code (default 301).\n */\n public redirect(url: string, status: number = 301): void {\n this.status = status;\n this.headers.location = url;\n }\n\n // TODO: check if status / header is already set and throw an error if so (for allow to override with force flag)\n\n /**\n * Set the response status code.\n */\n public setStatus(status: number): this {\n this.status = status;\n return this;\n }\n\n /**\n * Set a response header.\n */\n public setHeader(name: string, value: string): this {\n this.headers[name.toLowerCase()] = value;\n return this;\n }\n\n /**\n * Set the response body.\n */\n public setBody(body: any): this {\n this.body = body;\n return this;\n }\n}\n","import { AlephaError } from \"alepha\";\nimport type { ErrorSchema } from \"../schemas/errorSchema.ts\";\n\nexport const isHttpError = (\n error: unknown,\n status?: number,\n): error is HttpErrorLike => {\n const isError =\n !!error &&\n typeof error === \"object\" &&\n \"message\" in error &&\n typeof error.message === \"string\" &&\n \"status\" in error &&\n typeof error.status === \"number\";\n\n if (!isError) {\n return false;\n }\n\n if (status) {\n return (error as HttpErrorLike).status === status;\n }\n\n return true;\n};\n\nexport class HttpError extends AlephaError {\n public name = \"HttpError\";\n\n static is = isHttpError;\n\n static toJSON(error: HttpError): ErrorSchema {\n const json: Record<string, unknown> = {\n error: error.error,\n status: error.status,\n message: error.message,\n };\n\n if (error.details) json.details = error.details;\n if (error.requestId) json.requestId = error.requestId;\n if (error.reason) json.cause = error.reason;\n\n return json as ErrorSchema;\n }\n\n public readonly error: string;\n public readonly status: number;\n\n public readonly requestId?: string;\n public readonly details?: string;\n public readonly reason?: {\n name: string;\n message: string;\n };\n\n constructor(options: Partial<ErrorSchema>, cause?: unknown) {\n super(options.message, {\n cause,\n });\n\n this.status = options.status ?? 500;\n this.details = options.details;\n this.requestId = options.requestId;\n\n if (typeof options.cause === \"object\") {\n this.reason = {\n name: (options.cause as { name: string }).name,\n message: (options.cause as { message: string }).message,\n };\n } else if (cause instanceof Error) {\n this.reason = {\n name: cause.name,\n message: cause.message,\n };\n }\n\n if (this.constructor.name === \"HttpError\") {\n this.error =\n options.error ?? errorNameByStatus[this.status] ?? \"HttpError\";\n } else {\n this.error = this.constructor.name;\n }\n }\n}\n\nexport const errorNameByStatus: Record<number, string> = {\n 400: \"BadRequestError\",\n 401: \"UnauthorizedError\",\n 403: \"ForbiddenError\",\n 404: \"NotFoundError\",\n 405: \"MethodNotAllowedError\",\n 409: \"ConflictError\",\n 410: \"GoneError\",\n 413: \"PayloadTooLargeError\",\n 415: \"UnsupportedMediaTypeError\",\n 429: \"TooManyRequestsError\",\n 500: \"InternalServerError\",\n 501: \"NotImplementedError\",\n 502: \"BadGatewayError\",\n 503: \"ServiceUnavailableError\",\n 504: \"GatewayTimeoutError\",\n};\n\nexport interface HttpErrorLike extends Error {\n status: number;\n}\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class ValidationError extends HttpError {\n constructor(message = \"Validation has failed\", cause?: unknown) {\n super(\n {\n message,\n status: 400,\n },\n cause,\n );\n }\n}\n","export interface UserAgentInfo {\n os:\n | \"Windows\"\n | \"Android\"\n | \"Ubuntu\"\n | \"MacOS\"\n | \"iOS\"\n | \"Linux\"\n | \"FreeBSD\"\n | \"OpenBSD\"\n | \"ChromeOS\"\n | \"BlackBerry\"\n | \"Symbian\"\n | \"Windows Phone\";\n browser:\n | \"Chrome\"\n | \"Firefox\"\n | \"Safari\"\n | \"Edge\"\n | \"Opera\"\n | \"Internet Explorer\"\n | \"Brave\"\n | \"Vivaldi\"\n | \"Samsung Browser\"\n | \"UC Browser\"\n | \"Yandex\";\n device: \"MOBILE\" | \"DESKTOP\" | \"TABLET\";\n}\n\n/**\n * Simple User-Agent parser to detect OS, browser, and device type.\n * This parser is not exhaustive and may not cover all edge cases.\n *\n * Use result for non\n */\nexport class UserAgentParser {\n public parse(userAgent: string = \"\"): UserAgentInfo {\n const ua = userAgent.toLowerCase();\n\n // Default values\n let os: UserAgentInfo[\"os\"] = \"Windows\";\n let browser: UserAgentInfo[\"browser\"] = \"Chrome\";\n let device: UserAgentInfo[\"device\"] = \"DESKTOP\";\n\n // Detect OS - Order matters for specificity\n if (ua.includes(\"windows phone\")) {\n os = \"Windows Phone\";\n } else if (ua.includes(\"windows\")) {\n os = \"Windows\";\n } else if (ua.includes(\"android\")) {\n os = \"Android\";\n } else if (\n ua.includes(\"iphone\") ||\n ua.includes(\"ipad\") ||\n ua.includes(\"ipod\") ||\n (ua.includes(\"ios\") && !ua.includes(\"android\"))\n ) {\n os = \"iOS\";\n } else if (\n ua.includes(\"mac os\") ||\n ua.includes(\"macos\") ||\n ua.includes(\"macintosh\")\n ) {\n os = \"MacOS\";\n } else if (ua.includes(\"cros\") || ua.includes(\"chromeos\")) {\n os = \"ChromeOS\";\n } else if (ua.includes(\"ubuntu\")) {\n os = \"Ubuntu\";\n } else if (ua.includes(\"freebsd\")) {\n os = \"FreeBSD\";\n } else if (ua.includes(\"openbsd\")) {\n os = \"OpenBSD\";\n } else if (ua.includes(\"blackberry\") || ua.includes(\"bb10\")) {\n os = \"BlackBerry\";\n } else if (ua.includes(\"symbian\") || ua.includes(\"symbos\")) {\n os = \"Symbian\";\n } else if (ua.includes(\"linux\") || ua.includes(\"x11\")) {\n os = \"Linux\";\n }\n\n // Detect Browser/browser - Order matters, check most specific first\n if (ua.includes(\"yabrowser\") || ua.includes(\"yandex\")) {\n browser = \"Yandex\";\n } else if (ua.includes(\"brave\")) {\n browser = \"Brave\";\n } else if (ua.includes(\"vivaldi\")) {\n browser = \"Vivaldi\";\n } else if (ua.includes(\"samsungbrowser\") || ua.includes(\"samsung\")) {\n browser = \"Samsung Browser\";\n } else if (ua.includes(\"ucbrowser\") || ua.includes(\"uc browser\")) {\n browser = \"UC Browser\";\n } else if (\n ua.includes(\"opera\") ||\n ua.includes(\"opr/\") ||\n ua.includes(\"opios\")\n ) {\n browser = \"Opera\";\n } else if (\n ua.includes(\"edg/\") ||\n ua.includes(\"edge\") ||\n ua.includes(\"edgios\")\n ) {\n browser = \"Edge\";\n } else if (ua.includes(\"firefox\") && !ua.includes(\"seamonkey\")) {\n browser = \"Firefox\";\n } else if (ua.includes(\"trident\") || ua.includes(\"msie\")) {\n browser = \"Internet Explorer\";\n } else if (\n ua.includes(\"safari\") &&\n !ua.includes(\"chrome\") &&\n !ua.includes(\"chromium\")\n ) {\n browser = \"Safari\";\n } else if (\n ua.includes(\"chrome\") ||\n ua.includes(\"chromium\") ||\n ua.includes(\"crios\")\n ) {\n browser = \"Chrome\";\n }\n\n // Detect Device Type\n const mobileKeywords = [\n \"mobile\",\n \"android\",\n \"iphone\",\n \"ipod\",\n \"blackberry\",\n \"windows phone\",\n \"opera mini\",\n \"iemobile\",\n \"mobile safari\",\n \"nokia\",\n \"symbian\",\n ];\n\n const tabletKeywords = [\n \"ipad\",\n \"tablet\",\n \"kindle\",\n \"silk\",\n \"gt-p\",\n \"sm-t\",\n \"nexus 7\",\n \"nexus 10\",\n ];\n\n const isTablet = tabletKeywords.some((keyword) => ua.includes(keyword));\n const isMobile = mobileKeywords.some((keyword) => ua.includes(keyword));\n\n if (isTablet) {\n device = \"TABLET\";\n } else if (isMobile) {\n device = \"MOBILE\";\n } else {\n device = \"DESKTOP\";\n }\n\n return { os, browser, device };\n }\n}\n","import { $env, $inject, Alepha, t } from \"alepha\";\nimport { ServerReply } from \"../helpers/ServerReply.ts\";\nimport type {\n RequestGeo,\n RequestReferer,\n ServerRequest,\n ServerRequestData,\n} from \"../interfaces/ServerRequest.ts\";\nimport { UserAgentParser } from \"./UserAgentParser.ts\";\n\nconst envSchema = t.object({\n /**\n * Trust proxy headers (X-Forwarded-For, X-Real-IP) for client IP resolution.\n *\n * Default: true (modern deployments are typically behind a reverse proxy)\n *\n * Set to false only if your server accepts direct connections without a proxy\n * and you want to use the raw connection IP.\n */\n TRUST_PROXY: t.boolean({\n default: true,\n description: \"Trust proxy headers for client IP\",\n }),\n});\n\nexport class ServerRequestParser {\n protected readonly alepha = $inject(Alepha);\n protected readonly userAgentParser = $inject(UserAgentParser);\n protected readonly env = $env(envSchema);\n\n public createServerRequest(rawRequest: ServerRequestData): ServerRequest {\n const self = this;\n return {\n method: rawRequest.method,\n url: rawRequest.url,\n raw: rawRequest.raw,\n headers: rawRequest.headers,\n query: rawRequest.query,\n params: rawRequest.params,\n // body will be filled by body parser middleware\n body: null,\n metadata: {},\n requestId: this.getRequestId(rawRequest) || crypto.randomUUID(),\n reply: this.alepha.inject(ServerReply, { lifetime: \"transient\" }),\n get ip() {\n return self.getRequestIp(rawRequest);\n },\n get userAgent() {\n return self.getRequestUserAgent(rawRequest);\n },\n get geo() {\n return self.getRequestGeo(rawRequest);\n },\n get isBot() {\n return self.getIsBot(rawRequest);\n },\n get isMobile() {\n return self.getIsMobile(rawRequest);\n },\n get protocol() {\n return self.getProtocol(rawRequest);\n },\n get language() {\n return self.getLanguage(rawRequest);\n },\n get referer() {\n return self.getReferer(rawRequest);\n },\n } as ServerRequest;\n }\n\n public getRequestId(request: ServerRequestData): string | undefined {\n return request.headers[\"x-request-id\"];\n }\n\n public getRequestUserAgent(request: ServerRequestData) {\n return this.userAgentParser.parse(request.headers[\"user-agent\"]);\n }\n\n public getRequestIp(request: ServerRequestData): string | undefined {\n // Only trust proxy headers when explicitly configured\n if (this.env.TRUST_PROXY) {\n const headers = request.headers;\n\n // X-Forwarded-For: standard proxy header (Cloudflare, Vercel, Nginx, etc.)\n const forwardedFor = headers[\"x-forwarded-for\"];\n if (forwardedFor) {\n return Array.isArray(forwardedFor)\n ? forwardedFor[0]\n : forwardedFor.split(\",\")[0].trim();\n }\n\n // X-Real-IP: alternative proxy header\n const xRealIP = headers[\"x-real-ip\"];\n if (xRealIP) {\n return Array.isArray(xRealIP) ? xRealIP[0] : xRealIP;\n }\n }\n\n // Default: use raw connection IP\n return this.getConnectionIp(request);\n }\n\n protected getConnectionIp(request: ServerRequestData): string | undefined {\n // Get IP from raw connection (Node.js socket)\n const nodeReq = request.raw.node?.req;\n if (nodeReq) {\n return nodeReq.socket?.remoteAddress;\n }\n return undefined;\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Geolocation\n // ─────────────────────────────────────────────────────────────────────────────\n\n public getRequestGeo(request: ServerRequestData): RequestGeo {\n const headers = request.headers;\n\n return {\n // Country: Cloudflare, Vercel, AWS CloudFront\n country:\n headers[\"cf-ipcountry\"] ||\n headers[\"x-vercel-ip-country\"] ||\n headers[\"cloudfront-viewer-country\"],\n\n // City: Cloudflare, Vercel\n city: headers[\"cf-ipcity\"] || headers[\"x-vercel-ip-city\"],\n\n // Region: Cloudflare, Vercel\n region:\n headers[\"cf-region\"] ||\n headers[\"cf-region-code\"] ||\n headers[\"x-vercel-ip-country-region\"],\n\n // Coordinates: Cloudflare, Vercel\n latitude: headers[\"cf-iplatitude\"] || headers[\"x-vercel-ip-latitude\"],\n longitude: headers[\"cf-iplongitude\"] || headers[\"x-vercel-ip-longitude\"],\n };\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Bot detection\n // ─────────────────────────────────────────────────────────────────────────────\n\n protected static readonly BOT_PATTERNS = [\n /bot/i,\n /crawl/i,\n /spider/i,\n /slurp/i,\n /googlebot/i,\n /bingbot/i,\n /yandex/i,\n /baiduspider/i,\n /facebookexternalhit/i,\n /twitterbot/i,\n /linkedinbot/i,\n /whatsapp/i,\n /telegrambot/i,\n /discordbot/i,\n /slackbot/i,\n /applebot/i,\n /duckduckbot/i,\n /semrush/i,\n /ahrefsbot/i,\n /mj12bot/i,\n /dotbot/i,\n /petalbot/i,\n /bytespider/i,\n /gptbot/i,\n /claudebot/i,\n /anthropic/i,\n /curl/i,\n /wget/i,\n /python-requests/i,\n /axios/i,\n /node-fetch/i,\n /go-http-client/i,\n /java\\//i,\n /libwww/i,\n /httpunit/i,\n /nutch/i,\n /phpcrawl/i,\n /biglotron/i,\n /teoma/i,\n /convera/i,\n /gigablast/i,\n /ia_archiver/i,\n /webmon/i,\n /httrack/i,\n /grub\\.org/i,\n /netresearchserver/i,\n /speedy/i,\n /fluffy/i,\n /findlink/i,\n /panscient/i,\n /ips-agent/i,\n /yanga/i,\n /cyberpatrol/i,\n /postrank/i,\n /page2rss/i,\n /linkdex/i,\n /ezooms/i,\n /heritrix/i,\n /findthatfile/i,\n /europarchive\\.org/i,\n /mappydata/i,\n /eright/i,\n /apercite/i,\n /aboundex/i,\n /domaincrawler/i,\n /wbsearchbot/i,\n /summify/i,\n /ccbot/i,\n /edisterbot/i,\n /seznambot/i,\n /ec2linkfinder/i,\n /gslfbot/i,\n /aihitbot/i,\n /intelium_bot/i,\n /yeti/i,\n /retrevopageanalyzer/i,\n /lb-spider/i,\n /sogou/i,\n /lssbot/i,\n /careerbot/i,\n /wotbox/i,\n /wocbot/i,\n /ichiro/i,\n /duckduckgo/i,\n /lssrocketcrawler/i,\n /drupact/i,\n /webcompanycrawler/i,\n /acoonbot/i,\n /openindexspider/i,\n /screaming frog/i,\n /pingdom/i,\n /uptimerobot/i,\n /headlesschrome/i,\n /phantomjs/i,\n /prerender/i,\n /lighthouse/i,\n /pagespeed/i,\n ];\n\n public getIsBot(request: ServerRequestData): boolean {\n const ua = request.headers[\"user-agent\"];\n if (!ua) return false;\n\n return ServerRequestParser.BOT_PATTERNS.some((pattern) => pattern.test(ua));\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Mobile detection\n // ─────────────────────────────────────────────────────────────────────────────\n\n protected static readonly MOBILE_PATTERNS = [\n /android/i,\n /webos/i,\n /iphone/i,\n /ipad/i,\n /ipod/i,\n /blackberry/i,\n /iemobile/i,\n /opera mini/i,\n /mobile/i,\n /tablet/i,\n /kindle/i,\n /silk/i,\n /fennec/i,\n /windows phone/i,\n /windows ce/i,\n /symbian/i,\n /palm/i,\n /webmate/i,\n ];\n\n public getIsMobile(request: ServerRequestData): boolean {\n const ua = request.headers[\"user-agent\"];\n if (!ua) return false;\n\n return ServerRequestParser.MOBILE_PATTERNS.some((pattern) =>\n pattern.test(ua),\n );\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Protocol detection\n // ─────────────────────────────────────────────────────────────────────────────\n\n public getProtocol(request: ServerRequestData): \"http\" | \"https\" {\n // Check proxy headers first\n const forwardedProto = request.headers[\"x-forwarded-proto\"];\n if (forwardedProto) {\n return forwardedProto.toLowerCase() === \"https\" ? \"https\" : \"http\";\n }\n\n // Cloudflare-specific header\n const cfVisitorHeader = request.headers[\"cf-visitor\"];\n if (cfVisitorHeader) {\n try {\n const parsed = JSON.parse(cfVisitorHeader);\n if (parsed.scheme === \"https\") return \"https\";\n } catch {\n // Ignore parse errors\n }\n }\n\n // Check URL scheme\n if (request.url.protocol === \"https:\") {\n return \"https\";\n }\n\n return \"http\";\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Language detection\n // ─────────────────────────────────────────────────────────────────────────────\n\n public getLanguage(request: ServerRequestData): string | undefined {\n const acceptLanguage = request.headers[\"accept-language\"];\n if (!acceptLanguage) return undefined;\n\n // Parse Accept-Language header\n // Format: \"en-US,en;q=0.9,fr;q=0.8\"\n const firstLang = acceptLanguage.split(\",\")[0];\n if (!firstLang) return undefined;\n\n // Remove quality value if present (e.g., \"en;q=0.9\" -> \"en\")\n const lang = firstLang.split(\";\")[0].trim();\n\n return lang || undefined;\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Referer parsing\n // ─────────────────────────────────────────────────────────────────────────────\n\n public getReferer(request: ServerRequestData): RequestReferer | undefined {\n const referer = request.headers.referer || request.headers.referrer;\n if (!referer) return undefined;\n\n try {\n const url = new URL(referer);\n return {\n url: referer,\n hostname: url.hostname,\n pathname: url.pathname,\n };\n } catch {\n // Invalid URL\n return undefined;\n }\n }\n}\n","import { $hook, $inject, Alepha } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { ServerRequest } from \"../interfaces/ServerRequest.ts\";\n\ntype TimingMap = Record<string, [number, number]>;\n\nexport class ServerTimingProvider {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n\n public options = {\n prefix: this.alepha.env.APP_NAME\n ? `${this.alepha.env.APP_NAME.toLowerCase()}-`\n : \"\",\n disabled: this.alepha.isProduction(),\n };\n\n public readonly onRequest = $hook({\n priority: \"first\",\n on: \"server:onRequest\",\n handler: ({ request }) => {\n if (this.options.disabled) {\n return;\n }\n\n request.metadata.timing = {};\n request.metadata.timing[this.handlerName] = [Date.now()];\n },\n });\n\n public readonly onResponse = $hook({\n priority: \"last\",\n on: \"server:onResponse\",\n handler: ({ request }) => {\n if (this.options.disabled) {\n return;\n }\n\n if (request.metadata.timing) {\n this.setDuration(this.handlerName, request.metadata.timing);\n\n let timingHeader = \"\";\n\n for (const [name, [start, duration]] of Object.entries(\n request.metadata.timing as TimingMap,\n )) {\n if (typeof start !== \"number\" || typeof duration !== \"number\") {\n this.log.warn(\n `Invalid timing for '${name}': [${start}, ${duration}]`,\n );\n continue;\n }\n\n const formattedName =\n this.options.prefix + name.replace(/[^a-zA-Z0-9-]/g, \"-\");\n timingHeader += `${formattedName};dur=${duration}, `;\n }\n\n if (request.reply.headers[\"Server-Timing\"]) {\n request.reply.headers[\"Server-Timing\"] += `, ${timingHeader}`;\n } else {\n request.reply.headers[\"Server-Timing\"] = timingHeader;\n }\n }\n },\n });\n\n protected get handlerName() {\n return `request`;\n }\n\n public beginTiming(name: string): void {\n if (this.options.disabled) {\n return;\n }\n\n const request = this.alepha.context.get<ServerRequest>(\"request\");\n if (!request) {\n return;\n }\n\n request.metadata ??= {};\n request.metadata.timing ??= {};\n request.metadata.timing[name] = [Date.now()];\n }\n\n public endTiming(name: string): void {\n if (this.options.disabled) {\n return;\n }\n\n const request = this.alepha.context.get<ServerRequest>(\"request\");\n if (!request) {\n return;\n }\n\n if (!request.metadata?.timing || !(name in request.metadata.timing)) {\n this.log.warn(`No timing found for '${name}'.`);\n return;\n }\n\n const start = request.metadata.timing[name]?.[0];\n if (typeof start !== \"number\") {\n this.log.warn(`Invalid timing start for '${name}': ${start}`);\n return;\n }\n\n this.setDuration(name, request.metadata.timing);\n }\n\n protected setDuration(name: string, timing: TimingMap): void {\n timing[name] = [timing[name][0], Date.now() - timing[name][0]];\n }\n}\n","import { randomUUID } from \"node:crypto\";\nimport { Readable as NodeStream } from \"node:stream\";\nimport { ReadableStream as NodeWebStream } from \"node:stream/web\";\nimport type { CompiledEventExecutor, Hooks } from \"alepha\";\nimport { $inject, Alepha, isFileLike, isTypeFile, t } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { RouterProvider } from \"alepha/router\";\nimport type { RouteMethod } from \"../constants/routeMethods.ts\";\nimport { errorNameByStatus, HttpError } from \"../errors/HttpError.ts\";\nimport { ValidationError } from \"../errors/ValidationError.ts\";\nimport type { ServerReply } from \"../helpers/ServerReply.ts\";\nimport type {\n RequestConfigSchema,\n ResponseKind,\n ServerRequest,\n ServerRequestConfig,\n ServerRoute,\n ServerRouteMatcher,\n} from \"../interfaces/ServerRequest.ts\";\nimport { ServerRequestParser } from \"../services/ServerRequestParser.ts\";\nimport { ServerTimingProvider } from \"./ServerTimingProvider.ts\";\n\n/**\n * Main router for all routes server side.\n *\n * Reminder:\n * - $route => generic route\n * - $action => action route (for API calls)\n * - $page => React route (for React SSR)\n */\nexport class ServerRouterProvider extends RouterProvider<ServerRouteMatcher> {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly routes: ServerRoute[] = [];\n protected readonly serverTimingProvider = $inject(ServerTimingProvider);\n protected readonly serverRequestParser = $inject(ServerRequestParser);\n\n // Compiled event executors - initialized on first request\n protected compiledOnRequest?: CompiledEventExecutor<\n Hooks[\"server:onRequest\"]\n >;\n protected compiledOnSend?: CompiledEventExecutor<Hooks[\"server:onSend\"]>;\n protected compiledOnResponse?: CompiledEventExecutor<\n Hooks[\"server:onResponse\"]\n >;\n protected compiledOnError?: CompiledEventExecutor<Hooks[\"server:onError\"]>;\n\n // Cache query schema keys to avoid property enumeration on every request\n // WeakMap allows GC of schemas that are no longer referenced\n protected readonly queryKeysCache = new WeakMap<object, string[]>();\n\n /**\n * Get cached keys for a query schema, computing them lazily on first access.\n */\n protected getQuerySchemaKeys(schema: { properties: object }): string[] {\n let keys = this.queryKeysCache.get(schema.properties);\n if (!keys) {\n keys = Object.keys(schema.properties);\n this.queryKeysCache.set(schema.properties, keys);\n }\n return keys;\n }\n\n /**\n * Compile event executors for optimal performance.\n * Called lazily on first request after all hooks are registered.\n */\n protected compileEvents(): void {\n if (this.compiledOnRequest) return; // Already compiled\n\n this.compiledOnRequest = this.alepha.events.compile(\"server:onRequest\");\n this.compiledOnSend = this.alepha.events.compile(\"server:onSend\", {\n catch: true,\n });\n this.compiledOnResponse = this.alepha.events.compile(\"server:onResponse\", {\n catch: true,\n });\n this.compiledOnError = this.alepha.events.compile(\"server:onError\");\n }\n\n /**\n * Get all registered routes, optionally filtered by a pattern.\n *\n * Pattern accept simple wildcard '*' at the end.\n * Example: '/api/*' will match all routes starting with '/api/' but '/api/' will match only that exact route.\n */\n public getRoutes(pattern?: string): ServerRoute[] {\n if (pattern) {\n if (pattern.endsWith(\"*\")) {\n const basePattern = pattern.slice(0, -1);\n return this.routes.filter((route) =>\n route.path.startsWith(basePattern),\n );\n } else {\n return this.routes.filter((route) => route.path === pattern);\n }\n }\n return this.routes;\n }\n\n /**\n * Create a new server route.\n */\n public createRoute<TConfig extends RequestConfigSchema = RequestConfigSchema>(\n route: ServerRoute<TConfig>,\n ): void {\n route.method ??= \"GET\";\n route.method = route.method.toUpperCase() as RouteMethod;\n\n this.routes.push(route);\n\n const path = `/${route.method}/${route.path}`.replace(/\\/+/g, \"/\");\n const responseKind = this.getResponseType(route.schema);\n\n this.log.trace(`Create route ${path}`);\n\n this.push({\n path,\n handler: (rawRequest) => {\n const request =\n this.serverRequestParser.createServerRequest(rawRequest);\n\n // Create context options per request to avoid race conditions with concurrent requests\n return this.alepha.context.run(\n () => this.processRequest(request, route, responseKind),\n { context: this.getContextId(rawRequest.headers) },\n );\n },\n });\n }\n\n /**\n * Get or generate a context ID from request headers.\n */\n protected getContextId(headers: Record<string, string>): string {\n // Use constant header names to reduce string allocation\n const contextId =\n headers[HEADER_REQUEST_ID] || headers[HEADER_CORRELATION_ID];\n if (contextId) {\n return contextId;\n }\n\n return randomUUID();\n }\n\n /**\n * Process an incoming request through the full lifecycle:\n * - onRequest hooks\n * - route handler\n * - onSend hooks\n * - response serialization\n * - onResponse hooks\n */\n protected async processRequest(\n request: ServerRequest,\n route: ServerRoute,\n responseKind: ResponseKind,\n ) {\n // Compile events on first request (after all hooks are registered)\n this.compileEvents();\n\n // Use try/catch instead of .catch() to avoid function creation overhead\n try {\n await this.runRouteHandler(route, request, responseKind);\n } catch (error) {\n await this.errorHandler(route, request, error as Error);\n }\n\n // Local reference to reduce property lookups\n const reply = request.reply;\n\n // Create payload per request to avoid race conditions with concurrent requests\n // (async hooks may hold references while other requests modify shared payloads)\n const payload = { request, route, response: undefined as any };\n\n // Use compiled executor - only await if returns promise\n const onSendResult = this.compiledOnSend!(payload);\n if (onSendResult) await onSendResult;\n\n // Create response\n const response = {\n status: reply.status ?? (reply.body ? 200 : 204),\n headers: reply.headers,\n body: reply.body as any,\n };\n\n payload.response = response;\n\n // Use compiled executor - only await if returns promise\n const onResponseResult = this.compiledOnResponse!(payload);\n if (onResponseResult) await onResponseResult;\n\n return response;\n }\n\n /**\n * Run the route handler with request validation and response serialization.\n */\n protected async runRouteHandler(\n route: ServerRoute,\n request: ServerRequest,\n responseKind: ResponseKind,\n ) {\n // there are some built-in hooks that are called before the request is handled\n // - ServerBodyParserProvider (parse body)\n // - ServerSecurityProvider (build user from headers)\n // - ServerLoggerProvider (log request)\n\n // Local reference for timing provider\n const timing = this.serverTimingProvider;\n\n // Create payload per request to avoid race conditions with concurrent requests\n const payload = { request, route };\n\n // Use compiled executor - only await if returns promise\n const onRequestResult = this.compiledOnRequest!(payload);\n if (onRequestResult) await onRequestResult;\n\n // Local reference to reduce property lookups\n const reply = request.reply;\n if (reply.body || (reply.status && reply.status >= 200)) {\n // if the body is already set, we can skip the handler\n // this is useful for middlewares that set the body\n return;\n }\n\n // request is ready to be used -> inject to context\n this.alepha.context.set<ServerRequest>(CTX_REQUEST, request);\n\n // validate request\n timing.beginTiming(TIMING_VALIDATE);\n try {\n this.validateRequest(route, request);\n } finally {\n timing.endTiming(TIMING_VALIDATE);\n }\n\n // call the handler only if the body is not set yet\n timing.beginTiming(TIMING_HANDLER);\n try {\n const result = await route.handler(request);\n if (result) {\n request.reply.body = result;\n }\n } finally {\n timing.endTiming(TIMING_HANDLER);\n }\n\n // serialize response\n timing.beginTiming(TIMING_SERIALIZE);\n try {\n this.serializeResponse(route, request.reply, responseKind);\n } finally {\n timing.endTiming(TIMING_SERIALIZE);\n }\n }\n\n /**\n * Transform reply body based on response kind and route schema.\n */\n public serializeResponse(\n route: ServerRoute,\n reply: ServerReply,\n responseKind: ResponseKind,\n ): void {\n // Local reference to reduce property lookups\n const headers = reply.headers;\n\n if (responseKind === \"json\" && route.schema?.response) {\n headers[HEADER_CONTENT_TYPE] = CONTENT_TYPE_JSON;\n reply.body = this.alepha.codec.encode(\n route.schema.response,\n reply.body,\n ENCODE_OPTIONS_STRING,\n );\n return;\n }\n\n if (responseKind === \"file\") {\n if (!isFileLike(reply.body)) {\n throw new HttpError({\n status: 500,\n message: ERROR_NOT_FILE,\n });\n }\n headers[HEADER_CONTENT_TYPE] = reply.body.type;\n headers[HEADER_CONTENT_DISPOSITION] =\n `attachment; filename=\"${reply.body.name.replaceAll('\"', \"\")}\"`;\n reply.body = reply.body.stream();\n return;\n }\n\n if (responseKind === \"text\") {\n reply.body = String(reply.body);\n if (\n reply.body.length > 15 &&\n reply.body.charCodeAt(0) === 60 &&\n reply.body.startsWith(\"<!DOCTYPE html>\")\n ) {\n headers[HEADER_CONTENT_TYPE] ??= CONTENT_TYPE_HTML;\n } else {\n headers[HEADER_CONTENT_TYPE] ??= CONTENT_TYPE_TEXT;\n }\n return;\n }\n\n if (reply.body == null || responseKind === \"void\") {\n delete headers[HEADER_CONTENT_TYPE];\n reply.body = undefined;\n return;\n }\n\n if (Buffer.isBuffer(reply.body)) {\n headers[HEADER_CONTENT_TYPE] ??= CONTENT_TYPE_OCTET;\n return;\n }\n\n if (\n reply.body instanceof NodeWebStream ||\n reply.body instanceof NodeStream\n ) {\n // set content-type to application/octet-stream if not set\n headers[HEADER_CONTENT_TYPE] ??= CONTENT_TYPE_OCTET;\n return;\n }\n\n headers[HEADER_CONTENT_TYPE] ??= CONTENT_TYPE_TEXT;\n reply.body = String(reply.body);\n return;\n }\n\n /**\n * Determine response type based on route schema.\n */\n protected getResponseType(schema?: RequestConfigSchema): ResponseKind {\n if (schema?.response) {\n if (\n t.schema.isObject(schema.response) ||\n t.schema.isRecord(schema.response) ||\n t.schema.isArray(schema.response)\n ) {\n return \"json\";\n }\n\n if (\n t.schema.isString(schema.response) ||\n t.schema.isInteger(schema.response) ||\n t.schema.isNumber(schema.response) ||\n t.schema.isBoolean(schema.response)\n ) {\n return \"text\";\n }\n\n if (isTypeFile(schema.response)) {\n return \"file\";\n }\n\n if (t.schema.isVoid(schema.response)) {\n return \"void\";\n }\n }\n\n return \"any\";\n }\n\n /**\n * When an error occurs during request processing, this method is called.\n */\n protected async errorHandler(\n route: ServerRoute,\n request: ServerRequest,\n error: Error,\n ) {\n // Local references to reduce property lookups\n const reply = request.reply;\n const headers = reply.headers;\n const requestId = request.requestId;\n\n // Reset body, which is probably invalid!\n // It can be filled by server:onError hook or by the default handler below\n reply.body = null;\n\n // Use compiled executor - only await if returns promise\n const onErrorResult = this.compiledOnError!({ request, route, error });\n if (onErrorResult) {\n await onErrorResult;\n }\n\n if (!reply.body && !reply.status) {\n if (error instanceof HttpError) {\n reply.status = error.status;\n headers[HEADER_CONTENT_TYPE] = CONTENT_TYPE_JSON;\n // Avoid spread operator - directly mutate the error JSON\n const errorJson = HttpError.toJSON(error);\n errorJson.requestId = requestId;\n reply.body = JSON.stringify(errorJson);\n } else {\n if (\n \"status\" in error &&\n typeof error.status === \"number\" &&\n !!errorNameByStatus[error.status]\n ) {\n const status = error.status;\n reply.status = status;\n headers[HEADER_CONTENT_TYPE] = CONTENT_TYPE_JSON;\n reply.body = JSON.stringify({\n status,\n error: errorNameByStatus[status],\n message: error.message,\n requestId,\n });\n return;\n }\n\n reply.status = 500;\n headers[HEADER_CONTENT_TYPE] = CONTENT_TYPE_JSON;\n reply.body = JSON.stringify({\n status: 500,\n error: ERROR_INTERNAL,\n message: error.message,\n requestId,\n });\n }\n }\n }\n\n /**\n * Validate incoming request against route schema.\n */\n public validateRequest(\n route: { schema?: RequestConfigSchema },\n request: ServerRequestConfig,\n ) {\n // Validate params (path parameters)\n if (route.schema?.params) {\n try {\n request.params = this.alepha.codec.validate(\n route.schema.params,\n request.params,\n ) as any;\n } catch (error) {\n throw new ValidationError(\"Invalid request params\", error);\n }\n }\n\n // Validate query parameters (?key=value&key2=value2)\n if (route.schema?.query) {\n try {\n // Use cached keys instead of for...in enumeration\n const schemaQuery = route.schema.query;\n const keys = this.getQuerySchemaKeys(schemaQuery);\n const query: Record<string, any> = {};\n\n // Use indexed loop for better performance than for...of\n for (let i = 0; i < keys.length; i++) {\n const key = keys[i];\n if (request.query[key] != null) {\n query[key] = this.alepha.codec.decode(\n schemaQuery.properties[key],\n request.query[key],\n );\n }\n }\n\n request.query = query;\n } catch (error) {\n throw new ValidationError(\"Invalid request query\", error);\n }\n }\n\n // Validate headers\n if (route.schema?.headers) {\n try {\n request.headers = this.alepha.codec.validate(\n route.schema.headers,\n request.headers,\n ) as any;\n } catch (error) {\n throw new ValidationError(\"Invalid request header\", error);\n }\n }\n\n // Validate body\n if (route.schema?.body) {\n if (t.schema.isString(route.schema.body)) {\n if (typeof request.body !== \"string\") {\n throw new ValidationError(\"Request body is not a string\");\n }\n } else {\n try {\n request.body = this.alepha.codec.decode(\n route.schema.body,\n request.body,\n );\n } catch (error) {\n throw new ValidationError(\"Invalid request body\", error);\n }\n }\n }\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n// Pre-allocated encode options for response serialization\nconst ENCODE_OPTIONS_STRING = Object.freeze({\n as: \"string\" as const,\n});\n\n// HTTP Headers\nconst HEADER_CONTENT_TYPE = \"content-type\";\nconst HEADER_CONTENT_DISPOSITION = \"content-disposition\";\nconst HEADER_REQUEST_ID = \"x-request-id\";\nconst HEADER_CORRELATION_ID = \"x-correlation-id\";\n\n// Content Types\nconst CONTENT_TYPE_JSON = \"application/json\";\nconst CONTENT_TYPE_TEXT = \"text/plain\";\nconst CONTENT_TYPE_HTML = \"text/html; charset=UTF-8\";\nconst CONTENT_TYPE_OCTET = \"application/octet-stream\";\n\n// Timing Keys\nconst TIMING_VALIDATE = \"validateRequest\";\nconst TIMING_HANDLER = \"runHandler\";\nconst TIMING_SERIALIZE = \"serializeResponse\";\n\n// Context Keys\nconst CTX_REQUEST = \"request\";\n\n// Error Messages\nconst ERROR_INTERNAL = \"InternalServerError\";\nconst ERROR_NOT_FILE = \"Invalid response body - not a file\";\n","import { Readable } from \"node:stream\";\nimport { $hook, $inject, Alepha } from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport type { Route } from \"alepha/router\";\nimport type { RouteMethod } from \"../constants/routeMethods.ts\";\nimport type {\n NodeRequestEvent,\n ServerRequestData,\n WebRequestEvent,\n} from \"../interfaces/ServerRequest.ts\";\nimport { ServerRouterProvider } from \"./ServerRouterProvider.ts\";\n\n// ============================================================================\n// Performance Constants\n// ============================================================================\n\n// Note: We cannot use frozen/shared empty objects here because downstream code\n// may mutate params/query (e.g., validation, React page rendering)\n\n// Header constants for fast property access\nconst HEADER_X_FORWARDED_PROTO = \"x-forwarded-proto\";\nconst HEADER_HOST = \"host\";\n\n// Protocol prefixes\nconst PROTO_HTTPS = \"https://\";\nconst PROTO_HTTP = \"http://\";\n\n/**\n * Base server provider to handle incoming requests and route them.\n *\n * This is the default implementation for serverless environments.\n *\n * ServerProvider supports both Node.js HTTP requests and Web (Fetch API) requests.\n */\nexport class ServerProvider {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly router = $inject(ServerRouterProvider);\n\n protected readonly internalServerErrorMessage = \"Internal Server Error\";\n\n // Pre-allocated error response to avoid object creation per failed request\n protected readonly internalErrorResponse = Object.freeze({\n status: 500,\n headers: Object.freeze({ \"content-type\": \"text/plain\" }),\n body: this.internalServerErrorMessage,\n });\n\n // Pre-bound error handler to avoid function allocation per request\n protected readonly handleInternalError = () => this.internalErrorResponse;\n\n // ============================================================================\n // P1: URL Base Cache - cache protocol+host per unique host header\n // Avoids string concatenation on every request\n // ============================================================================\n protected readonly urlBaseCache = new Map<string, string>();\n\n /**\n * Get cached URL base (protocol + host) for a given host header.\n * Caches the result to avoid repeated string concatenation.\n */\n protected getUrlBase(headers: Record<string, string>): string {\n const host = headers[HEADER_HOST];\n let base = this.urlBaseCache.get(host);\n if (!base) {\n const proto =\n headers[HEADER_X_FORWARDED_PROTO] === \"https\"\n ? PROTO_HTTPS\n : PROTO_HTTP;\n base = proto + host;\n // Limit cache size to prevent memory leaks from many unique hosts\n if (this.urlBaseCache.size < 100) {\n this.urlBaseCache.set(host, base);\n }\n }\n return base;\n }\n\n // ============================================================================\n // P0: Manual Query String Parser - faster than URLSearchParams\n // Parses query string without creating URL object\n // ============================================================================\n\n /**\n * Parse query string manually - faster than new URL() + URLSearchParams.\n * Returns empty object if no query string.\n */\n protected parseQueryString(rawUrl: string): Record<string, string> {\n const qIndex = rawUrl.indexOf(\"?\");\n if (qIndex === -1) {\n return {};\n }\n\n const qs = rawUrl.slice(qIndex + 1);\n if (!qs) {\n return {};\n }\n\n const query: Record<string, string> = {};\n let start = 0;\n let eqIdx = -1;\n\n for (let i = 0; i <= qs.length; i++) {\n const char = i < qs.length ? qs.charCodeAt(i) : 38; // '&' at end\n\n if (char === 61) {\n // '='\n eqIdx = i;\n } else if (char === 38) {\n // '&'\n if (eqIdx !== -1 && eqIdx > start) {\n const key = qs.slice(start, eqIdx);\n const value = qs.slice(eqIdx + 1, i);\n // Only decode if necessary (contains % or +)\n query[this.fastDecode(key)] = this.fastDecode(value);\n }\n start = i + 1;\n eqIdx = -1;\n }\n }\n\n return query;\n }\n\n /**\n * Fast decode - only calls decodeURIComponent if needed.\n */\n protected fastDecode(str: string): string {\n // Fast path: no encoding needed\n if (str.indexOf(\"%\") === -1 && str.indexOf(\"+\") === -1) {\n return str;\n }\n // Replace + with space before decoding\n try {\n return decodeURIComponent(str.replace(/\\+/g, \" \"));\n } catch {\n return str;\n }\n }\n\n public get hostname(): string {\n if (this.alepha.isViteDev()) {\n return `http://localhost:${this.alepha.env.SERVER_PORT}`;\n }\n return \"\"; // no hostname in serverless mode\n }\n\n /**\n * When a Node.js HTTP request is received from outside. (Vercel, AWS Lambda, etc.)\n */\n protected readonly onNodeRequest = $hook({\n on: \"node:request\",\n handler: (ev) => this.handleNodeRequest(ev),\n });\n\n /**\n * When a Web (Fetch API) request is received from outside. (Netlify, Cloudflare Workers, etc.)\n */\n protected readonly onWebRequest = $hook({\n on: \"web:request\",\n handler: (ev) => {\n return this.handleWebRequest(ev);\n },\n });\n\n /**\n * Handle Node.js HTTP request event.\n *\n * Optimized to avoid expensive URL parsing when possible.\n */\n public async handleNodeRequest(\n nodeRequestEvent: NodeRequestEvent,\n ): Promise<void> {\n const { req, res } = nodeRequestEvent;\n const rawUrl = req.url!;\n const { route, params } = this.router.match(`/${req.method}${rawUrl}`);\n\n if (!route) {\n // Skip if response was already sent (e.g., by Vite middleware)\n if (res.headersSent) {\n return;\n }\n // if no route is found, return basic 404\n // note: you should not use this in production, use a custom 404 page instead by adding a route /*\n res.writeHead(404, { \"content-type\": \"text/plain\" });\n res.end(\"Not Found\");\n return;\n }\n\n const headers = (req.headers ?? {}) as Record<string, string>;\n const method = (req.method?.toUpperCase() ?? \"GET\") as RouteMethod;\n\n // P0: Use manual query parsing - much faster than new URL() + URLSearchParams\n const query = this.parseQueryString(rawUrl);\n\n // P1: Use cached URL base - avoids string concat on every request\n // Create URL object (still needed for downstream code that uses url.pathname, etc.)\n const urlBase = this.getUrlBase(headers);\n const url = new URL(rawUrl, urlBase);\n\n const request: ServerRequestData = {\n method,\n url,\n headers,\n params: params ?? {},\n query,\n raw: { node: nodeRequestEvent },\n };\n\n const response = await route\n .handler(request)\n .catch(this.handleInternalError);\n\n // Skip if response was already sent (e.g., by Vite middleware)\n if (res.headersSent) {\n return;\n }\n\n // empty body - just send status & headers\n if (!response.body) {\n res.writeHead(response.status, response.headers);\n res.end();\n return;\n }\n\n // if response.body is string or buffer\n if (typeof response.body === \"string\" || Buffer.isBuffer(response.body)) {\n res.writeHead(response.status, response.headers);\n res.end(response.body);\n return;\n }\n\n // if response.body is node stream\n if (response.body instanceof Readable) {\n res.writeHead(response.status, response.headers);\n response.body.pipe(res);\n return;\n }\n\n // if response.body is web stream\n if (response.body instanceof ReadableStream) {\n res.writeHead(response.status, response.headers);\n // Flush headers immediately and disable Nagle's algorithm for streaming\n res.flushHeaders();\n res.socket?.setNoDelay(true);\n try {\n for await (const chunk of response.body) {\n const canContinue = res.write(chunk);\n // If the internal buffer is full, wait for it to drain\n if (!canContinue) {\n await new Promise<void>((resolve) => res.once(\"drain\", resolve));\n }\n }\n } catch (error) {\n this.log.error(\"Error piping proxy response stream\", error);\n } finally {\n res.end();\n }\n return;\n }\n\n // not supported response body type\n\n this.log.error(\"Unknown response body type:\", typeof response.body);\n res.writeHead(500, { \"content-type\": \"text/plain\" });\n res.end(this.internalServerErrorMessage);\n }\n\n /**\n * Handle Web (Fetch API) request event.\n */\n public async handleWebRequest(ev: WebRequestEvent): Promise<void> {\n const req = ev.req;\n const url = new URL(req.url);\n const { route, params } = this.router.match(\n `/${req.method}${url.pathname}`,\n );\n\n if (this.isViteNotFound(req.url, route, params)) {\n return;\n }\n\n if (!route) {\n // if no route is found, return basic 404\n // note: you should not use this in production, use a custom 404 page instead by adding a route /*\n ev.res = new Response(\"Not Found\", {\n status: 404,\n headers: { \"content-type\": \"text/plain\" },\n });\n return;\n }\n\n const headers: Record<string, string> = {};\n\n req.headers.forEach((value, key) => {\n headers[key] = value;\n });\n\n // Optimize: only parse query params if there are any\n const query = url.search\n ? Object.fromEntries(url.searchParams.entries())\n : {};\n const method = (req.method.toUpperCase() ?? \"GET\") as RouteMethod;\n const request: ServerRequestData = {\n method,\n url,\n headers,\n params: params || {},\n query,\n raw: { web: ev },\n };\n\n const response = await route\n .handler(request)\n .catch(this.handleInternalError);\n\n // empty body - just send status & headers\n if (!response.body) {\n ev.res = new Response(null, {\n status: response.status,\n headers: response.headers,\n });\n return;\n }\n\n // if response.body is string or buffer\n if (typeof response.body === \"string\") {\n ev.res = new Response(response.body, {\n status: response.status,\n headers: response.headers,\n });\n return;\n }\n\n if (Buffer.isBuffer(response.body)) {\n // Use Uint8Array to avoid Buffer pooling issues where .buffer returns\n // the entire underlying ArrayBuffer which may be larger than the actual data\n ev.res = new Response(new Uint8Array(response.body), {\n status: response.status,\n headers: response.headers,\n });\n return;\n }\n\n // if response.body is node stream\n if (response.body instanceof Readable) {\n ev.res = new Response(\n Readable.toWeb(response.body) as unknown as ReadableStream,\n {\n status: response.status,\n headers: response.headers,\n },\n );\n return;\n }\n\n // if response.body is web stream\n if (response.body instanceof ReadableStream) {\n ev.res = new Response(response.body, {\n status: response.status,\n headers: response.headers,\n });\n return;\n }\n\n // not supported response body type\n this.log.error(`Unknown response body type: ${typeof response.body}`);\n ev.res = new Response(this.internalServerErrorMessage, {\n status: 500,\n headers: { \"content-type\": \"text/plain\" },\n });\n }\n\n /**\n * Helper for Vite development mode to let Vite handle (or not) 404.\n */\n protected isViteNotFound(\n url?: string,\n route?: Route,\n params?: Record<string, string>,\n ): boolean {\n if (this.alepha.isViteDev()) {\n if (!route) {\n return true;\n }\n\n url = url?.split(\"?\")[0];\n\n if (!!params?.[\"*\"] && `/${params?.[\"*\"]}` === url) {\n return true;\n }\n }\n\n return false;\n }\n}\n","import { type Static, t } from \"alepha\";\n\nexport const errorSchema = t.object(\n {\n error: t.text({ description: \"HTTP error name\" }),\n status: t.integer({\n description: \"HTTP status code\",\n }),\n message: t.text({\n description: \"Short text which describe the error\",\n size: \"rich\",\n }),\n details: t.optional(\n t.text({\n description: \"Detailed description of the error\",\n size: \"rich\",\n }),\n ),\n requestId: t.optional(t.text()),\n cause: t.optional(\n t.object({\n name: t.text(),\n message: t.text({\n description: \"Cause Error message\",\n size: \"rich\",\n }),\n }),\n ),\n },\n {\n title: \"HttpError\",\n description: \"Generic response after a failed operation\",\n },\n);\n\nexport type ErrorSchema = Static<typeof errorSchema>;\n","import {\n $inject,\n Alepha,\n type FileLike,\n isFileLike,\n type Static,\n type TObject,\n type TSchema,\n} from \"alepha\";\nimport { $cache } from \"alepha/cache\";\nimport type { DurationLike } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { HttpError } from \"../errors/HttpError.ts\";\nimport { isMultipart } from \"../helpers/isMultipart.ts\";\nimport type {\n ServerRequestConfigEntry,\n TRequestBody,\n TResponseBody,\n} from \"../interfaces/ServerRequest.ts\";\nimport type { ClientRequestOptions } from \"../primitives/$action.ts\";\nimport { errorSchema } from \"../schemas/errorSchema.ts\";\n\nexport class HttpClient {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n\n public readonly cache = $cache<HttpClientCache>();\n\n protected readonly pendingRequests: HttpClientPendingRequests = {};\n\n public async fetchAction(args: FetchActionArgs): Promise<FetchResponse> {\n const route = args.action; // our link to fetch\n const options = args.options ?? {}; // fetch standard options, cache, etc.\n const config = args.config ?? {}; // params, query, body, etc.\n const host = args.host ?? \"\"; // remote host, e.g. \"https://api.example.com\" or empty (for browser)\n\n const request: RequestInit = {\n ...options.request,\n };\n\n const method = route.method;\n const headers: Record<string, string> = {};\n const url = this.url(host, route, config);\n\n await this.alepha.events.emit(\"client:onRequest\", {\n route,\n config,\n options,\n headers,\n request,\n });\n\n request.method ??= method;\n\n await this.body(request, headers, route, config);\n\n request.headers = {\n ...config.headers,\n ...Object.fromEntries(new Headers(request.headers).entries()),\n ...headers,\n };\n\n return await this.fetch(url, {\n ...request,\n schema: route.schema,\n ...options,\n });\n }\n\n public async fetch<T extends TSchema>(\n url: string,\n request: RequestInitWithOptions<T> = {}, // standard options\n ): Promise<FetchResponse<Static<T>>> {\n const options = {\n cache: request.localCache,\n schema: request.schema?.response,\n key: request.key,\n };\n\n request.method ??= \"GET\";\n\n this.log.trace(\"Request\", {\n url,\n method: request.method,\n body: request.body,\n headers: request.headers,\n options,\n });\n\n // Only add automatic ETag if user didn't explicitly provide headers\n const cached = await this.cache.get(url);\n if (cached && request.method === \"GET\") {\n if (cached.etag) {\n request.headers = new Headers(request.headers);\n if (!request.headers.has(\"if-none-match\")) {\n request.headers.set(\"if-none-match\", cached.etag);\n }\n } else {\n return {\n data: cached.data as Static<T>,\n status: 200,\n statusText: \"OK\",\n headers: new Headers(),\n };\n }\n }\n\n await this.alepha.events.emit(\"client:beforeFetch\", {\n url,\n options,\n request,\n });\n\n // make a key for the request\n // this will be used to check if the request is already pending\n const key =\n options.key ??\n JSON.stringify({\n url,\n method: request.method,\n body: request.body,\n });\n\n const existing = this.pendingRequests[key];\n if (existing) {\n this.log.info(\"Request already pending\", key);\n return existing;\n }\n\n this.pendingRequests[key] = fetch(url, request)\n .then(async (response) => {\n this.log.debug(\"Response\", {\n url,\n status: response.status,\n });\n\n const fetchResponse: FetchResponse = {\n data: await this.responseData(response, options),\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n raw: response,\n };\n\n if (request.method === \"GET\") {\n if (options.cache) {\n await this.cache.set(\n url,\n { data: fetchResponse.data },\n typeof options.cache === \"boolean\" ? undefined : options.cache,\n );\n } else if (!this.alepha.isBrowser()) {\n // only cache etag on server, browser can handle etag itself\n const etag = response.headers.get(\"etag\") ?? undefined;\n if (etag) {\n await this.cache.set(url, { data: fetchResponse.data, etag });\n }\n }\n }\n\n return fetchResponse;\n })\n .finally(() => {\n delete this.pendingRequests[key];\n });\n\n return this.pendingRequests[key];\n }\n\n protected url(\n host: string,\n action: HttpAction,\n args: ServerRequestConfigEntry,\n ) {\n let url = host;\n\n if (action.prefix) {\n url += action.prefix;\n }\n\n url += action.path;\n url = this.pathVariables(url, action, args);\n url = this.queryParams(url, action, args);\n\n return url;\n }\n\n protected async body(\n init: RequestInit,\n headers: Record<string, string>,\n action: HttpAction,\n args: ServerRequestConfigEntry = {},\n ) {\n const hasHeader =\n typeof init.headers === \"object\" &&\n \"content-type\" in init.headers &&\n init.headers[\"content-type\"] === \"multipart/form-data\";\n\n if (hasHeader || isMultipart(action)) {\n if (typeof init.headers === \"object\" && \"content-type\" in init.headers) {\n delete init.headers[\"content-type\"]; // fetch() will fill this for us\n }\n\n const formData = new FormData();\n\n for (const [key, value] of Object.entries(args.body ?? {})) {\n if (typeof value === \"string\") {\n formData.append(key, value);\n continue;\n }\n if (value instanceof Blob) {\n formData.append(key, value);\n continue;\n }\n if (isFileLike(value)) {\n // FileLike must be transformed to WebFile\n formData.append(\n key,\n new File([await value.arrayBuffer()], value.name, {\n type: value.type,\n }),\n );\n }\n }\n\n init.body = formData;\n\n return;\n }\n\n if (!init.body && action.schema?.body) {\n headers[\"content-type\"] = \"application/json\";\n init.body = this.alepha.codec.encode(action.schema?.body, args.body, {\n as: \"string\",\n });\n }\n }\n\n protected async responseData(\n response: Response,\n options: FetchOptions,\n ): Promise<any> {\n if (response.status === 304) {\n let cacheKey = response.url;\n if (typeof window !== \"undefined\") {\n cacheKey = cacheKey.replace(window.location.origin, \"\");\n }\n\n const cached = await this.cache.get(cacheKey);\n if (cached) {\n return cached.data;\n }\n\n // if no cached data (etag-only routes), return empty string\n return \"\";\n }\n\n if (response.status === 204) {\n return;\n }\n\n if (this.isMaybeFile(response)) {\n return this.createFileLike(response);\n }\n\n if (response.headers.get(\"Content-Type\")?.startsWith(\"text/\")) {\n return await response.text();\n }\n\n if (response.headers.get(\"Content-Type\") === \"application/json\") {\n const json = await response.json();\n\n if (response.status >= 400) {\n const jsonError = this.alepha.codec.decode(errorSchema, json);\n const error = new HttpError(jsonError);\n\n await this.alepha.events.emit(\"client:onError\", {\n error,\n });\n\n throw error;\n }\n\n if (options.schema) {\n return this.alepha.codec.decode(options.schema, json);\n }\n\n return json;\n }\n\n if (response.status >= 400) {\n const error = new HttpError({\n status: response.status,\n message: `An error occurred while fetching the resource. (${response.statusText})`,\n });\n\n await this.alepha.events.emit(\"client:onError\", {\n error,\n });\n\n throw error;\n }\n\n return response;\n }\n\n protected isMaybeFile(response: Response): boolean {\n const contentType = response.headers.get(\"Content-Type\");\n if (!contentType) {\n return false;\n }\n\n if (response.headers.get(\"Content-Disposition\")?.includes(\"attachment\")) {\n return true; // If Content-Disposition indicates an attachment, treat it as a file\n }\n\n return (\n contentType.startsWith(\"application/octet-stream\") ||\n contentType.startsWith(\"application/pdf\") ||\n contentType.startsWith(\"application/zip\") ||\n contentType.startsWith(\"image/\") ||\n contentType.startsWith(\"video/\") ||\n contentType.startsWith(\"audio/\")\n );\n }\n\n protected createFileLike(response: Response, defaultFileName = \"\"): FileLike {\n const match = (response.headers.get(\"Content-Disposition\") ?? \"\").match(\n /filename=\"(.+)\"/,\n );\n return {\n name: match?.[1] ? match[1] : defaultFileName,\n type: response.headers.get(\"Content-Type\") ?? \"application/octet-stream\",\n size: Number(response.headers.get(\"Content-Length\") ?? 0),\n lastModified: Date.now(),\n stream: () => {\n throw new Error(\"Not implemented\");\n },\n arrayBuffer: async () => {\n return await response.arrayBuffer();\n },\n text: async () => {\n return await response.text();\n },\n };\n }\n\n public pathVariables(\n url: string,\n action: { schema?: { params?: TObject } },\n args: ServerRequestConfigEntry = {},\n ): string {\n if (typeof args.params === \"object\") {\n const params = action.schema?.params\n ? (this.alepha.codec.decode(\n action.schema.params,\n args.params,\n ) as Record<string, any>)\n : args.params;\n\n for (const key of Object.keys(params)) {\n url = url.replace(`:${key}`, params[key]);\n url = url.replace(`{${key}}`, params[key]);\n }\n }\n\n return url;\n }\n\n public queryParams(\n url: string,\n action: { schema?: { query?: TObject } },\n args: ServerRequestConfigEntry = {},\n ): string {\n if (typeof args.query === \"object\") {\n const query = action.schema?.query\n ? this.alepha.codec.decode(action.schema.query, args.query ?? {})\n : args.query;\n\n for (const key of Object.keys(query)) {\n if (query[key] === undefined) {\n delete query[key];\n }\n if (typeof query[key] === \"object\") {\n query[key] = JSON.stringify(query[key]);\n }\n }\n\n return `${url}?${new URLSearchParams(\n query as Record<string, string>,\n ).toString()}`;\n }\n return url;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface FetchOptions<T extends TSchema = TSchema> {\n /**\n * Key to identify the request in the pending requests.\n */\n key?: string;\n\n /**\n * The schema to validate the response against.\n */\n schema?: {\n response?: T;\n };\n\n /**\n * Built-in cache options.\n */\n localCache?: boolean | number | DurationLike;\n}\n\nexport type RequestInitWithOptions<T extends TSchema = TSchema> = RequestInit &\n FetchOptions<T>;\n\nexport interface FetchResponse<T = any> {\n data: T;\n status: number;\n statusText: string;\n headers: Headers;\n raw?: Response;\n}\n\nexport type HttpClientPendingRequests = Record<\n string,\n Promise<any> | undefined\n>;\n\ninterface HttpClientCache {\n data: any;\n etag?: string;\n}\n\nexport interface FetchActionArgs {\n action: HttpAction;\n host?: string;\n config?: ServerRequestConfigEntry;\n options?: ClientRequestOptions;\n}\n\nexport interface HttpAction {\n method?: string;\n prefix?: string;\n path: string;\n requestBodyType?: string;\n schema?: {\n params?: TObject;\n query?: TObject;\n body?: TRequestBody;\n response?: TResponseBody;\n };\n}\n","import {\n $env,\n $inject,\n AlephaError,\n type Async,\n createPrimitive,\n isTypeFile,\n KIND,\n Primitive,\n type Static,\n type TObject,\n type TSchema,\n t,\n} from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { RouteMethod } from \"../constants/routeMethods.ts\";\nimport { isMultipart } from \"../helpers/isMultipart.ts\";\nimport { ServerReply } from \"../helpers/ServerReply.ts\";\nimport type {\n RequestConfigSchema,\n ServerRequest,\n ServerResponseBody,\n ServerRoute,\n TRequestBody,\n} from \"../interfaces/ServerRequest.ts\";\nimport { ServerProvider } from \"../providers/ServerProvider.ts\";\nimport { ServerRouterProvider } from \"../providers/ServerRouterProvider.ts\";\nimport {\n type FetchOptions,\n type FetchResponse,\n HttpClient,\n} from \"../services/HttpClient.ts\";\n\n/**\n * Creates a server action primitive for defining type-safe HTTP endpoints.\n *\n * Server actions are the core building blocks for REST APIs in Alepha, providing declarative\n * HTTP endpoints with full type safety, automatic validation, and OpenAPI documentation.\n *\n * **Key Features**\n * - Full TypeScript inference for request/response types\n * - Automatic schema validation using TypeBox\n * - Convention-based URL generation with customizable paths\n * - Direct invocation (`run()`) or HTTP requests (`fetch()`)\n * - Built-in authentication and authorization support\n * - Automatic content-type handling (JSON, form-data, plain text)\n *\n * **URL Generation**\n *\n * **Important:** All `$action` paths are automatically prefixed with `/api`.\n *\n * ```ts\n * $action({ path: \"/users\" }) // → GET /api/users\n * $action({ path: \"/users/:id\" }) // → GET /api/users/:id\n * $action({ path: \"/hello\" }) // → GET /api/hello\n * ```\n *\n * This prefix is configurable via the `SERVER_API_PREFIX` environment variable.\n * HTTP method defaults to GET, or POST if body schema is provided.\n *\n * **Common Use Cases**\n * - CRUD operations with type safety\n * - File upload and download endpoints\n * - Microservice communication\n *\n * @example\n * ```ts\n * class UserController {\n * getUsers = $action({\n * path: \"/users\",\n * schema: {\n * query: t.object({\n * page: t.optional(t.number({ default: 1 })),\n * limit: t.optional(t.number({ default: 10 }))\n * }),\n * response: t.object({\n * users: t.array(t.object({\n * id: t.text(),\n * name: t.text(),\n * email: t.text()\n * })),\n * total: t.number()\n * })\n * },\n * handler: async ({ query }) => {\n * const users = await this.userService.findUsers(query);\n * return { users: users.items, total: users.total };\n * }\n * });\n *\n * createUser = $action({\n * method: \"POST\",\n * path: \"/users\",\n * schema: {\n * body: t.object({\n * name: t.text(),\n * email: t.text({ format: \"email\" })\n * }),\n * response: t.object({ id: t.text(), name: t.text() })\n * },\n * handler: async ({ body }) => {\n * return await this.userService.create(body);\n * }\n * });\n * }\n * ```\n */\nexport const $action = <TConfig extends RequestConfigSchema>(\n options: ActionPrimitiveOptions<TConfig>,\n): ActionPrimitiveFn<TConfig> => {\n const instance = createPrimitive(ActionPrimitive<TConfig>, options);\n const fn = (\n config?: ClientRequestEntry<TConfig>,\n options?: ClientRequestOptions,\n ) => {\n return instance.run(config, options);\n };\n Object.defineProperty(fn, \"name\", {\n get(): string {\n return instance.options.name || instance.config.propertyKey;\n },\n });\n return Object.setPrototypeOf(fn, instance) as ActionPrimitiveFn<TConfig>;\n};\n\n// ----------------------------------------------------------------------------------------------------------\n\nexport interface ActionPrimitiveOptions<TConfig extends RequestConfigSchema>\n extends Omit<ServerRoute, \"handler\" | \"path\" | \"schema\" | \"mapParams\"> {\n /**\n * Name of the action.\n *\n * - It will be used to generate the route path if `path` is not provided.\n * - It will be used to generate the permission name if `security` is enabled.\n */\n name?: string;\n\n /**\n * Group actions together.\n *\n * - If not provided, the service name containing the route will be used.\n * - It will be used as Tag for documentation purposes.\n * - It will be used for permission name generation if `security` is enabled.\n *\n * @example\n * ```ts\n * // group = \"MyController\"\n * class MyController {\n * \thello = $action({ handler: () => \"Hello World\" });\n * }\n *\n * // group = \"users\"\n * class MyOtherController {\n * group = \"users\";\n * a1 = $action({ handler: () => \"Action 1\", group: this.group });\n * a2 = $action({ handler: () => \"Action 2\", group: this.group });\n * }\n * ```\n */\n group?: string;\n\n /**\n * Pathname of the route. If not provided, property key is used.\n */\n path?: string;\n\n /**\n * The route method.\n *\n * - If not provided, it will be set to \"GET\" by default.\n * - If not provider and a body is provided, it will be set to \"POST\".\n *\n * Wildcard methods are not supported for now. (e.g. \"ALL\", \"ANY\", etc.)\n */\n method?: RouteMethod;\n\n /**\n * The config schema of the route.\n * - body: The request body schema.\n * - params: Path variables schema.\n * - query: The request query-params schema.\n * - response: The response schema.\n */\n schema?: TConfig;\n\n /**\n * A short description of the action. Used for documentation purposes.\n */\n description?: string;\n\n /**\n * Disable the route. Useful with env variables to disable one specific route.\n * Route won't be available in the API nor locally.\n */\n disabled?: boolean;\n\n /**\n * Main route handler. This is where the route logic is implemented.\n */\n handler: ServerActionHandler<TConfig>;\n}\n\n// ----------------------------------------------------------------------------------------------------------\n\nconst envSchema = t.object({\n SERVER_API_PREFIX: t.text({\n description: \"Prefix for all API routes (e.g. $action).\",\n default: \"/api\",\n }),\n});\n\nexport class ActionPrimitive<\n TConfig extends RequestConfigSchema,\n> extends Primitive<ActionPrimitiveOptions<TConfig>> {\n protected readonly log = $logger();\n protected readonly env = $env(envSchema);\n protected readonly httpClient = $inject(HttpClient);\n protected readonly serverProvider = $inject(ServerProvider);\n protected readonly serverRouterProvider = $inject(ServerRouterProvider);\n\n protected onInit() {\n if (this.options.disabled) {\n this.log.debug(\n `Action '${this.name}' is disabled. It won't be available in the API.`,\n );\n return;\n }\n this.serverRouterProvider.createRoute(this.route);\n }\n\n public get prefix() {\n return this.env.SERVER_API_PREFIX;\n }\n\n public get route(): ServerRoute {\n return {\n ...(this.options as any), // TODO: fix schema.header mapping\n method: this.method,\n path: `${this.prefix}${this.path}`,\n } as ServerRoute;\n }\n\n /**\n * Returns the name of the action.\n */\n public get name(): string {\n return this.options.name || this.config.propertyKey;\n }\n\n /**\n * Returns the group of the action. (e.g. \"orders\", \"admin\", etc.)\n */\n public get group(): string {\n return this.options.group || this.config.service.name;\n }\n\n /**\n * Returns the HTTP method of the action.\n */\n public get method(): RouteMethod {\n return this.options.method || (this.options.schema?.body ? \"POST\" : \"GET\");\n }\n\n /**\n * Returns the path of the action.\n *\n * Path is prefixed by `/api` by default.\n */\n public get path(): string {\n if (this.options.path) {\n return this.options.path;\n }\n\n let path = `/${this.name}`;\n\n if (this.options.schema?.params) {\n for (const [key] of Object.entries(\n this.options.schema.params.properties,\n )) {\n path += `/:${key}`;\n }\n }\n\n return path;\n }\n\n public get schema(): TConfig | undefined {\n return this.options.schema;\n }\n\n public getBodyContentType(): string | undefined {\n if (this.options.schema?.body) {\n // TODO: move to `alepha.server.multipart` module ?\n if (isMultipart(this.options)) {\n return \"multipart/form-data\";\n }\n\n if (t.schema.isString(this.options.schema.body)) {\n // if body is a string, we assume it's plain text\n return \"text/plain\";\n }\n\n if (\n t.schema.isObject(this.options.schema.body) ||\n t.schema.isArray(this.options.schema.body) ||\n t.schema.isRecord(this.options.schema.body)\n )\n // if body is an object or array, we assume it's JSON\n return \"application/json\";\n }\n }\n\n /**\n * Call the action handler directly.\n * There is no HTTP layer involved.\n */\n public async run(\n config?: ClientRequestEntry<TConfig>,\n options: ClientRequestOptions = {}, // most of the options are ignored here\n ): Promise<ClientRequestResponse<TConfig>> {\n if (this.options.disabled) {\n throw new AlephaError(`Action '${this.name}' is disabled.`);\n }\n const handler = this.options.handler;\n const {\n body,\n params = {},\n query = {},\n headers = {},\n } = (config ?? {}) as ClientRequestEntryContainer<RequestConfigSchema>;\n const reply = new ServerReply();\n const method = this.method;\n\n // we use localhost as the base URL for the action\n const url = new URL(`http://localhost${this.path ?? \"\"}`);\n\n const serverActionRequest: Partial<ServerRequest> = {\n method,\n url,\n body,\n params,\n query,\n headers,\n reply,\n metadata: {},\n };\n\n await this.alepha.events.emit(\"action:onRequest\", {\n action: this,\n request: serverActionRequest as ServerRequest,\n options,\n });\n\n if (serverActionRequest.reply?.body) {\n return serverActionRequest.reply.body as ClientRequestResponse<TConfig>;\n }\n\n if (serverActionRequest.query && this.options.schema?.query) {\n serverActionRequest.query = this.alepha.codec.encode(\n this.options.schema.query,\n serverActionRequest.query,\n );\n }\n\n if (serverActionRequest.headers && this.options.schema?.headers) {\n serverActionRequest.headers = this.alepha.codec.encode(\n this.options.schema.headers,\n serverActionRequest.headers,\n ) as Record<string, any>;\n }\n\n if (serverActionRequest.body && this.options.schema?.body) {\n serverActionRequest.body = this.alepha.codec.encode(\n this.options.schema.body,\n serverActionRequest.body,\n ) as unknown;\n }\n\n if (serverActionRequest.params && this.options.schema?.params) {\n serverActionRequest.params = this.alepha.codec.encode(\n this.options.schema.params,\n serverActionRequest.params,\n ) as Record<string, any>;\n }\n\n this.serverRouterProvider.validateRequest(\n this.options,\n serverActionRequest as ServerRequest,\n );\n\n let response: any = await handler(\n serverActionRequest as ServerActionRequest<TConfig>,\n );\n\n // we validate response just to remove undeclared properties from response\n if (\n this.options.schema?.response &&\n // skip validation if response is expected as file\n !isTypeFile(this.options.schema.response)\n ) {\n response = this.alepha.codec.validate(\n this.options.schema.response,\n response,\n );\n }\n\n await this.alepha.events.emit(\"action:onResponse\", {\n action: this,\n request: serverActionRequest as ServerRequest,\n options,\n response,\n });\n\n return response;\n }\n\n /**\n * Works like `run`, but always fetches (http request) the route.\n */\n public fetch(\n config?: ClientRequestEntry<TConfig>,\n options?: ClientRequestOptions,\n ): Promise<FetchResponse<ClientRequestResponse<TConfig>>> {\n return this.httpClient.fetchAction({\n host: this.serverProvider.hostname, // that's the trick, we just use the server hostname\n action: this,\n config,\n options,\n });\n }\n}\n\nexport interface ActionPrimitiveFn<TConfig extends RequestConfigSchema>\n extends ActionPrimitive<TConfig> {\n (\n config?: ClientRequestEntry<TConfig>,\n options?: ClientRequestOptions,\n ): Promise<ClientRequestResponse<TConfig>>;\n}\n\n$action[KIND] = ActionPrimitive;\n\n// ----------------------------------------------------------------------------------------------------------\n\nexport type ClientRequestEntry<\n TConfig extends RequestConfigSchema,\n T = ClientRequestEntryContainer<TConfig>,\n> = {\n [K in keyof T as T[K] extends undefined ? never : K]: T[K];\n};\n\nexport type ClientRequestEntryContainer<TConfig extends RequestConfigSchema> = {\n body: TConfig[\"body\"] extends TRequestBody\n ? Static<TConfig[\"body\"]>\n : undefined;\n\n params: TConfig[\"params\"] extends TObject\n ? Static<TConfig[\"params\"]>\n : undefined;\n\n headers?: TConfig[\"headers\"] extends TObject\n ? Static<TConfig[\"headers\"]>\n : Record<string, string>;\n\n query?: TConfig[\"query\"] extends TObject\n ? Partial<Static<TConfig[\"query\"]>>\n : Record<string, string>;\n};\n\nexport interface ClientRequestOptions extends FetchOptions {\n /**\n * Standard request fetch options.\n */\n request?: RequestInit;\n\n query?: Record<string, string | number | boolean>;\n}\n\nexport type ClientRequestResponse<TConfig extends RequestConfigSchema> =\n TConfig[\"response\"] extends TSchema ? Static<TConfig[\"response\"]> : any;\n\n/**\n * Specific handler for server actions.\n */\nexport type ServerActionHandler<\n TConfig extends RequestConfigSchema = RequestConfigSchema,\n> = (\n request: ServerActionRequest<TConfig>,\n) => Async<ServerResponseBody<TConfig>>;\n\n/**\n * Server Action Request Interface\n *\n * Can be extended with module augmentation to add custom properties (like `user` in Server Security).\n *\n * This is NOT Server Request, but a specific type for actions.\n */\nexport interface ServerActionRequest<TConfig extends RequestConfigSchema>\n extends ServerRequest<TConfig> {}\n","import { $inject, createPrimitive, KIND, Primitive } from \"alepha\";\nimport type {\n RequestConfigSchema,\n ServerRoute,\n} from \"../interfaces/ServerRequest.ts\";\nimport { ServerRouterProvider } from \"../providers/ServerRouterProvider.ts\";\n\n/**\n * Create a basic endpoint.\n *\n * It's a low level primitive. You probably want to use `$action` instead.\n *\n * @see {@link $action}\n * @see {@link $page}\n */\nexport const $route = <TConfig extends RequestConfigSchema>(\n options: RoutePrimitiveOptions<TConfig>,\n): RoutePrimitive<TConfig> => {\n return createPrimitive(RoutePrimitive<TConfig>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface RoutePrimitiveOptions<\n TConfig extends RequestConfigSchema = RequestConfigSchema,\n> extends ServerRoute<TConfig> {}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class RoutePrimitive<\n TConfig extends RequestConfigSchema,\n> extends Primitive<RoutePrimitiveOptions<TConfig>> {\n protected readonly serverRouterProvider = $inject(ServerRouterProvider);\n\n protected onInit() {\n this.serverRouterProvider.createRoute(this.options);\n }\n}\n\n$route[KIND] = RoutePrimitive;\n","import { $env, $hook, $inject, Alepha, type Static, t } from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { ServerProvider } from \"./ServerProvider.ts\";\nimport { ServerRouterProvider } from \"./ServerRouterProvider.ts\";\n\nconst envSchema = t.object({\n SERVER_PORT: t.integer({\n default: 3000,\n min: 0,\n max: 65535,\n description: \"Set 0 to listen on a random port.\",\n }),\n SERVER_HOST: t.text({\n default: \"localhost\",\n description: \"Set 0.0.0.0 to listen on all interfaces.\",\n }),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\nexport class BunHttpServerProvider extends ServerProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly log = $logger();\n protected readonly env = $env(envSchema);\n protected readonly router = $inject(ServerRouterProvider);\n\n protected bunServer?: ReturnType<typeof Bun.serve>;\n\n public get hostname(): string {\n if (this.bunServer) {\n return `http://${this.bunServer.hostname}:${this.bunServer.port}`;\n }\n return `http://${this.env.SERVER_HOST}:${this.env.SERVER_PORT}`;\n }\n\n public readonly start = $hook({\n on: \"start\",\n handler: async () => {\n await this.listen();\n },\n });\n\n protected readonly stop = $hook({\n on: \"stop\",\n handler: async () => {\n if (this.alepha.isProduction()) {\n await this.close();\n return;\n }\n\n // do not await in development & test\n this.close().catch(() => {});\n },\n });\n\n protected async listen() {\n let port = this.env.SERVER_PORT;\n\n // for testing, use a random port if port is 3000 (default)\n if (this.alepha.isTest() && port === 3000) {\n port = 0;\n }\n\n try {\n this.bunServer = Bun.serve({\n port,\n hostname: this.env.SERVER_HOST,\n fetch: async (request: Request) => {\n this.log.trace(`Incoming Bun request -> ${request.url}`);\n\n // Create WebRequestEvent for handleWebRequest\n const webRequestEvent = {\n req: request,\n res: undefined as Response | undefined,\n };\n\n try {\n await this.handleWebRequest(webRequestEvent);\n\n if (!webRequestEvent.res) {\n // No response set, return 500\n return new Response(\"Internal Server Error\", {\n status: 500,\n headers: { \"content-type\": \"text/plain\" },\n });\n }\n\n return webRequestEvent.res;\n } catch (err) {\n this.log.error(\"Error handling request\", err);\n return new Response(\"Internal Server Error\", {\n status: 500,\n headers: { \"content-type\": \"text/plain\" },\n });\n }\n },\n error: (error: Error) => {\n this.log.error(\"Bun server error\", error);\n return new Response(\"Internal Server Error\", {\n status: 500,\n headers: { \"content-type\": \"text/plain\" },\n });\n },\n });\n\n this.log.info(`Server listening on ${this.hostname}/`);\n } catch (err) {\n this.log.error(\"Failed to start Bun server\", err);\n throw err;\n }\n }\n\n protected async close() {\n if (!this.bunServer) {\n return;\n }\n\n try {\n // Bun's server.stop() returns a promise that resolves when all connections are closed\n const stopPromise = this.bunServer.stop();\n\n // Race between stop completion and timeout\n await Promise.race([this.dateTimeProvider.wait(10000), stopPromise]);\n\n this.bunServer = undefined;\n this.log.info(\"Server closed\");\n } catch (err) {\n this.log.error(\"Error closing Bun server\", err);\n throw err;\n }\n }\n}\n","import {\n createServer,\n type IncomingMessage,\n type Server,\n type ServerResponse,\n} from \"node:http\";\nimport type { Socket } from \"node:net\";\nimport { $env, $hook, $inject, Alepha, type Static, t } from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { ServerProvider } from \"./ServerProvider.ts\";\nimport { ServerRouterProvider } from \"./ServerRouterProvider.ts\";\n\nconst envSchema = t.object({\n SERVER_PORT: t.integer({\n default: 3000,\n min: 0,\n max: 65535,\n description: \"Set 0 to listen on a random port.\",\n }),\n SERVER_HOST: t.text({\n default: \"localhost\",\n description: \"Set 0.0.0.0 to listen on all interfaces.\",\n }),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\nexport class NodeHttpServerProvider extends ServerProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly log = $logger();\n protected readonly env = $env(envSchema);\n protected readonly router = $inject(ServerRouterProvider);\n\n /**\n * Track active connections for fast shutdown.\n */\n protected readonly connections = new Set<Socket>();\n\n /**\n * Get number of active connections.\n */\n public getConnectionsCount(): number {\n return this.connections.size;\n }\n\n /**\n * Server options.\n */\n public readonly options = {\n /**\n * Graceful shutdown timeout in ms.\n * After this, remaining connections are forcefully closed.\n * @default 30000\n */\n shutdownTimeout: 10000,\n };\n\n public get hostname(): string {\n if (this.server.listening) {\n const address = this.server.address();\n if (typeof address === \"object\" && address !== null) {\n return `http://${this.env.SERVER_HOST}:${address.port}`;\n }\n }\n return `http://${this.env.SERVER_HOST}:${this.env.SERVER_PORT}`;\n }\n\n // Pre-bound error handler to avoid function allocation per request\n protected readonly handleRequestError = (res: ServerResponse, err: Error) => {\n this.log.error(\"Error handling request\", err);\n res.statusCode = 500;\n res.end(\"Internal Server Error\");\n };\n\n public readonly server = this.createHttpServer((req, res) => {\n const promise = this.handleNodeRequest({ req, res });\n promise.catch((err) => this.handleRequestError(res, err));\n });\n\n public readonly start = $hook({\n on: \"start\",\n handler: async () => {\n await this.listen();\n this.alepha.store.set(\"alepha.node.server\", this.server);\n },\n });\n\n protected createHttpServer(\n func: (req: IncomingMessage, res: ServerResponse) => void,\n ): Server {\n const server = createServer(\n {\n // nov 25 - keep connections alive for better performance, cuz we http/1.1 by default\n keepAlive: this.alepha.isProduction(),\n },\n func,\n );\n\n // Track connections for fast shutdown\n server.on(\"connection\", (socket) => {\n this.connections.add(socket);\n socket.on(\"close\", () => this.connections.delete(socket));\n });\n\n return server;\n }\n\n protected readonly stop = $hook({\n on: \"stop\",\n handler: async () => {\n await this.close();\n },\n });\n\n protected async listen() {\n let port = this.env.SERVER_PORT;\n\n // for testing, use a random port if port is 3000 (default)\n if (this.alepha.isTest() && port === 3000) {\n port = 0;\n }\n\n await new Promise<void>((resolve, reject) => {\n this.server?.listen(port, this.env.SERVER_HOST, () => {\n this.log.info(`Server listening on ${this.hostname}/`);\n resolve();\n });\n\n this.server?.on(\"error\", (err) => {\n reject(err);\n });\n });\n }\n\n protected async close() {\n // Dev/Test: instant shutdown (destroy connections immediately)\n // Production: graceful shutdown (wait for requests to complete, then close)\n if (!this.alepha.isProduction()) {\n this.destroyAllConnections();\n }\n\n // Stop accepting new connections\n const closePromise = new Promise<void>((resolve, reject) => {\n this.server?.close((err) => (err ? reject(err) : resolve()));\n });\n\n if (this.alepha.isProduction() && this.connections.size > 0) {\n // In production, wait for connections with timeout\n const timeout = this.options.shutdownTimeout;\n\n // Set up timeout to force-close connections\n const timeoutId = setTimeout(() => {\n if (this.connections.size > 0) {\n this.log.warn(\n `Shutdown timeout (${timeout}ms) reached, forcing ${this.connections.size} connections to close`,\n );\n // Destroy sockets - this triggers 'close' events which eventually resolves closePromise\n for (const socket of this.connections) {\n socket.destroy();\n }\n }\n }, timeout);\n\n // Wait for server to fully close (all connections closed)\n await closePromise;\n clearTimeout(timeoutId);\n this.connections.clear();\n } else {\n await closePromise;\n }\n\n this.log.info(\"Server closed\");\n }\n\n protected destroyAllConnections() {\n for (const socket of this.connections) {\n socket.destroy();\n }\n this.connections.clear();\n }\n}\n","import { ReadableStream as WebStream } from \"node:stream/web\";\nimport { createBrotliDecompress, createGunzip, createInflate } from \"node:zlib\";\nimport type { TSchema } from \"alepha\";\nimport { $env, $hook, $inject, Alepha, t } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { HttpError } from \"../errors/HttpError.ts\";\n\nconst envSchema = t.object({\n SERVER_BODY_PARSER_INFLATE: t.boolean({\n default: true,\n description: \"Enable decompression of request body.\",\n }),\n SERVER_BODY_PARSER_LIMIT: t.integer({\n default: 100_000, // 100KB\n min: 0,\n description: \"Maximum size of request body in bytes.\",\n }),\n});\n\nexport class ServerBodyParserProvider {\n protected readonly env = $env(envSchema);\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n\n public readonly onRequest = $hook({\n on: \"server:onRequest\",\n handler: ({ route, request }) => {\n if (request.body) {\n return; // already parsed\n }\n\n let stream: ReadableStream | undefined;\n\n if (request.raw.web?.req.body) {\n stream = request.raw.web.req.body;\n } else if (request.raw.node?.req) {\n // convert Node.js IncomingMessage to Web ReadableStream\n // TODO: check performance impact, it's better to directly read from Node stream!\n stream = WebStream.from(\n request.raw.node.req,\n ) as unknown as ReadableStream;\n }\n\n if (!stream) {\n return;\n }\n\n if (route.schema?.body) {\n return this.parse(stream, request.headers, route.schema.body)\n .then((body) => {\n if (body) {\n request.body = body;\n }\n })\n .catch((error) => {\n if (error instanceof HttpError) {\n throw error;\n }\n throw new HttpError(\n {\n status: 400,\n message: \"Failed to parse request body\",\n },\n error,\n );\n });\n }\n },\n });\n\n public async parse(\n stream: ReadableStream,\n headers: Record<string, string>,\n schema: TSchema,\n ): Promise<object | string | undefined> {\n const contentType = headers[\"content-type\"];\n const contentEncoding = headers[\"content-encoding\"];\n\n if (!contentType) return undefined;\n\n if (contentType.startsWith(\"text/plain\") || t.schema.isString(schema)) {\n return this.parseText(stream, contentEncoding);\n }\n\n if (contentType.startsWith(\"application/json\")) {\n return this.parseJson(stream, contentEncoding);\n }\n\n if (contentType.startsWith(\"application/x-www-form-urlencoded\")) {\n return this.parseUrlEncoded(stream, contentEncoding);\n }\n\n return undefined;\n }\n\n public async parseText(\n stream: ReadableStream,\n contentEncoding?: string,\n ): Promise<string> {\n const buffer = await this.streamToBuffer(stream);\n const bufferInflated = await this.maybeDecompress(buffer, contentEncoding);\n return bufferInflated.toString(\"utf-8\");\n }\n\n public async parseUrlEncoded(\n stream: ReadableStream,\n contentEncoding?: string,\n ): Promise<object> {\n const text = await this.parseText(stream, contentEncoding);\n const params = new URLSearchParams(text);\n const result: Record<string, string> = {};\n for (const [key, value] of params.entries()) {\n result[key] = value;\n }\n\n return result;\n }\n\n public async parseJson(\n stream: ReadableStream,\n contentEncoding?: string,\n ): Promise<object> {\n const text = await this.parseText(stream, contentEncoding);\n return JSON.parse(text);\n }\n\n protected async maybeDecompress(\n buffer: Buffer,\n encoding: string | undefined,\n ): Promise<Buffer> {\n if (!this.env.SERVER_BODY_PARSER_INFLATE && encoding) {\n throw new HttpError({\n status: 415,\n message: `Content-Encoding ${encoding} not allowed`,\n });\n }\n\n switch (encoding) {\n case \"gzip\":\n return new Promise((res, rej) =>\n createGunzip()\n .end(buffer, () => {})\n .on(\"data\", res)\n .on(\"error\", rej),\n );\n case \"deflate\":\n return new Promise((res, rej) =>\n createInflate()\n .end(buffer, () => {})\n .on(\"data\", res)\n .on(\"error\", rej),\n );\n case \"br\":\n return new Promise((res, rej) =>\n createBrotliDecompress()\n .end(buffer, () => {})\n .on(\"data\", res)\n .on(\"error\", rej),\n );\n case undefined:\n case \"identity\":\n return buffer;\n default:\n throw new Error(`Unsupported Content-Encoding: ${encoding}`);\n }\n }\n\n /**\n * Convert Web ReadableStream to Buffer, with a size limit.\n *\n * TODO: move to alepha/file FileUtils\n */\n protected async streamToBuffer(stream: ReadableStream): Promise<Buffer> {\n const chunks: Uint8Array[] = [];\n let totalLength = 0;\n\n const reader = stream.getReader();\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) {\n break;\n }\n\n if (value) {\n totalLength += value.length;\n\n if (totalLength > this.env.SERVER_BODY_PARSER_LIMIT) {\n this.log.error(\n `Body size limit exceeded: ${totalLength} > ${this.env.SERVER_BODY_PARSER_LIMIT}`,\n );\n\n await reader.cancel(); // Cancel the stream\n\n throw new HttpError({\n status: 413,\n message: `Request body size limit exceeded`,\n });\n }\n\n chunks.push(value);\n }\n }\n\n // Combine all chunks into a single Buffer\n const combinedLength = chunks.reduce(\n (sum, chunk) => sum + chunk.length,\n 0,\n );\n const combined = new Uint8Array(combinedLength);\n let offset = 0;\n\n for (const chunk of chunks) {\n combined.set(chunk, offset);\n offset += chunk.length;\n }\n\n return Buffer.from(combined);\n } catch (error) {\n // Make sure to release the reader lock\n reader.releaseLock();\n throw error;\n }\n }\n}\n","import { $hook, $inject, Alepha } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\n\nexport class ServerLoggerProvider {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n\n public readonly onRequest = $hook({\n on: \"server:onRequest\",\n priority: \"first\",\n handler: ({ route, request }) => {\n if (route.silent || request.metadata.vite) {\n return;\n }\n\n request.metadata.now = Date.now();\n\n const data: Record<string, string> = {\n method: request.method,\n path: request.url.pathname,\n };\n\n if (this.alepha.isProduction()) {\n data.agent = request.headers[\"user-agent\"];\n const ip = request.ip;\n if (ip) {\n data.ip = ip;\n }\n }\n\n this.log.info(\"Incoming request\", data);\n },\n });\n\n public readonly onError = $hook({\n on: \"server:onError\",\n priority: \"last\",\n handler: ({ error }) => {\n this.log.error(\"Request has failed\", error);\n },\n });\n\n public readonly onResponse = $hook({\n on: \"server:onResponse\",\n priority: \"last\",\n handler: ({ route, request, response }) => {\n if (route.silent || request.metadata.vite) {\n return;\n }\n\n const ms = Date.now() - request.metadata.now;\n this.log.info(\"Request completed\", { status: response.status, ms });\n },\n });\n}\n","import { $hook, $inject, Alepha } from \"alepha\";\nimport { HttpError } from \"../errors/HttpError.ts\";\n\n/**\n * On every request, this provider checks if the server is ready.\n *\n * If the server is not ready, it responds with a 503 status code and a message indicating that the server is not ready yet.\n *\n * The response also includes a `Retry-After` header indicating that the client should retry after 5 seconds.\n */\nexport class ServerNotReadyProvider {\n protected readonly alepha = $inject(Alepha);\n\n public readonly onRequest = $hook({\n on: \"server:onRequest\",\n priority: \"first\",\n handler: ({ request: { reply } }) => {\n if (this.alepha.isReady()) {\n return;\n }\n\n reply.headers[\"Retry-After\"] = \"5\"; // Retry after 5 seconds\n\n throw new HttpError({\n status: 503,\n message: \"Server is not ready yet. Please try again later.\",\n });\n },\n });\n}\n","export const routeMethods = [\n // list of supported http methods\n \"GET\",\n \"POST\",\n \"PUT\",\n \"PATCH\",\n \"DELETE\",\n \"HEAD\",\n \"OPTIONS\",\n \"CONNECT\",\n \"TRACE\",\n] as const;\n\nexport type RouteMethod = (typeof routeMethods)[number];\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class BadRequestError extends HttpError {\n constructor(message = \"Invalid request body\", cause?: unknown) {\n super(\n {\n message,\n status: 400,\n },\n cause,\n );\n }\n}\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class ConflictError extends HttpError {\n constructor(message = \"Entity already exists\", cause?: unknown) {\n super(\n {\n message,\n status: 409,\n },\n cause,\n );\n }\n}\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class ForbiddenError extends HttpError {\n constructor(\n message = \"No permission to access this resource\",\n cause?: unknown,\n ) {\n super(\n {\n message,\n status: 403,\n },\n cause,\n );\n }\n}\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class NotFoundError extends HttpError {\n constructor(message = \"Resource not found\", cause?: unknown) {\n super(\n {\n message,\n status: 404,\n },\n cause,\n );\n }\n}\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class UnauthorizedError extends HttpError {\n readonly name = \"UnauthorizedError\";\n\n constructor(\n message = \"Not allowed to access this resource\",\n cause?: unknown,\n ) {\n super(\n {\n message,\n status: 401,\n },\n cause,\n );\n }\n}\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const okSchema = t.object(\n {\n ok: t.boolean({ description: \"True when operation succeed\" }),\n id: t.optional(t.union([t.text(), t.integer()])),\n count: t.optional(\n t.number({ description: \"Number of resources affected\" }),\n ),\n },\n {\n title: \"Ok\",\n description: \"Generic response after a successful operation on a resource\",\n },\n);\n\nexport type Ok = Static<typeof okSchema>;\n","import type { Server } from \"node:http\";\nimport { $module, type Alepha, type PrimitiveFactoryLike } from \"alepha\";\nimport type { HttpError } from \"./errors/HttpError.ts\";\nimport type {\n NodeRequestEvent,\n RequestConfigSchema,\n ServerRequest,\n ServerRequestConfigEntry,\n ServerResponse,\n ServerRoute,\n WebRequestEvent,\n} from \"./interfaces/ServerRequest.ts\";\nimport {\n $action,\n type ActionPrimitive,\n type ClientRequestOptions,\n} from \"./primitives/$action.ts\";\nimport { $route } from \"./primitives/$route.ts\";\nimport { BunHttpServerProvider } from \"./providers/BunHttpServerProvider.ts\";\nimport { NodeHttpServerProvider } from \"./providers/NodeHttpServerProvider.ts\";\nimport { ServerBodyParserProvider } from \"./providers/ServerBodyParserProvider.ts\";\nimport { ServerLoggerProvider } from \"./providers/ServerLoggerProvider.ts\";\nimport { ServerNotReadyProvider } from \"./providers/ServerNotReadyProvider.ts\";\nimport { ServerProvider } from \"./providers/ServerProvider.ts\";\nimport { ServerRouterProvider } from \"./providers/ServerRouterProvider.ts\";\nimport { ServerTimingProvider } from \"./providers/ServerTimingProvider.ts\";\nimport type { FetchOptions, HttpAction } from \"./services/HttpClient.ts\";\nimport { HttpClient } from \"./services/HttpClient.ts\";\nimport { ServerRequestParser } from \"./services/ServerRequestParser.ts\";\nimport { UserAgentParser } from \"./services/UserAgentParser.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha\" {\n interface State {\n \"alepha.node.server\"?: Server;\n }\n interface Hooks {\n // -----------------------------------------------------------------------------------------------------------------\n // Local Actions hooks\n \"action:onRequest\": {\n action: ActionPrimitive<RequestConfigSchema>;\n request: ServerRequest;\n options: ClientRequestOptions;\n };\n \"action:onResponse\": {\n action: ActionPrimitive<RequestConfigSchema>;\n request: ServerRequest;\n options: ClientRequestOptions;\n response: any;\n };\n // -----------------------------------------------------------------------------------------------------------------\n // Server hooks\n \"server:onRequest\": {\n route: ServerRoute;\n request: ServerRequest;\n };\n \"server:onError\": {\n route: ServerRoute;\n request: ServerRequest;\n error: Error;\n };\n // last chance to modify the response -\n // TODO: probably not really needed, we can also update the response in the onResponse hook...\n \"server:onSend\": {\n route: ServerRoute;\n request: ServerRequest;\n };\n // response is ready\n \"server:onResponse\": {\n route: ServerRoute;\n request: ServerRequest;\n response: ServerResponse;\n };\n // -----------------------------------------------------------------------------------------------------------------\n // Http client hooks\n \"client:onRequest\": {\n route: HttpAction;\n config: ServerRequestConfigEntry;\n options: ClientRequestOptions;\n headers: Record<string, string>;\n request: RequestInit;\n };\n \"client:beforeFetch\": {\n url: string;\n options: FetchOptions;\n request: RequestInit;\n };\n \"client:onError\": {\n route?: HttpAction;\n error: HttpError;\n };\n // -----------------------------------------------------------------------------------------------------------------\n // Internal hooks\n \"node:request\": NodeRequestEvent;\n \"web:request\": WebRequestEvent;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./index.shared.ts\";\nexport * from \"./primitives/$action.ts\";\nexport * from \"./primitives/$route.ts\";\nexport * from \"./providers/BunHttpServerProvider.ts\";\nexport * from \"./providers/NodeHttpServerProvider.ts\";\nexport * from \"./providers/ServerLoggerProvider.ts\";\nexport * from \"./providers/ServerNotReadyProvider.ts\";\nexport * from \"./providers/ServerProvider.ts\";\nexport * from \"./providers/ServerRouterProvider.ts\";\nexport * from \"./providers/ServerTimingProvider.ts\";\nexport * from \"./services/UserAgentParser.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * | Stability | Since | Runtime |\n * |-----------|-------|---------|\n * | 3 - stable | 0.1.0 | node, bun, workerd|\n *\n * Convention-driven HTTP server with automatic validation and type inference.\n *\n * **Features:**\n * - Type-safe API endpoints with schema validation\n * - Lower-level HTTP route definitions\n * - Automatic request/response validation via TypeBox\n * - Convention-based URL generation (`/api/{ActionName}`)\n * - Direct invocation (`run()`) or HTTP (`fetch()`)\n * - Built-in authentication integration\n * - Multipart file upload handling\n * - Content-type auto-negotiation (JSON, form-data, text)\n * - HTTP methods: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS\n * - Error handling: BadRequestError, ValidationError, ForbiddenError, UnauthorizedError, ConflictError, NotFoundError\n *\n * @module alepha.server\n */\nexport const AlephaServer = $module({\n name: \"alepha.server\",\n primitives: [$route, $action as PrimitiveFactoryLike],\n services: [\n ServerProvider,\n BunHttpServerProvider,\n NodeHttpServerProvider,\n ServerBodyParserProvider,\n ServerLoggerProvider,\n ServerNotReadyProvider,\n ServerTimingProvider,\n HttpClient,\n UserAgentParser,\n ServerRequestParser,\n ServerRouterProvider,\n ],\n register: (alepha: Alepha) => {\n if (!alepha.isServerless()) {\n if (alepha.isBun()) {\n alepha.with({\n optional: true,\n provide: ServerProvider,\n use: BunHttpServerProvider,\n });\n } else {\n alepha.with({\n optional: true,\n provide: ServerProvider,\n use: NodeHttpServerProvider,\n });\n }\n } else {\n alepha.with(ServerProvider);\n }\n\n alepha.with(ServerBodyParserProvider);\n alepha.with(ServerLoggerProvider);\n alepha.with(ServerNotReadyProvider);\n\n if (!alepha.isProduction()) {\n alepha.with(ServerTimingProvider);\n }\n },\n});\n"],"mappings":";;;;;;;;;;;;;;;AAKA,MAAa,eAAe,YAGb;AACb,KAAI,QAAQ,oBAAoB,sBAC9B,QAAO;AAGT,KAAI,QAAQ,QAAQ,QAAQ,gBAAgB,QAAQ,OAAO,MAAM;EAC/D,MAAM,aAAkC,QAAQ,OAAO,KAAK;AAC5D,OAAK,MAAM,OAAO,WAChB,KAAI,WAAW,KAAK,WAAW,SAC7B,QAAO;;AAKb,QAAO;;;;;;;;ACnBT,IAAa,cAAb,MAAyB;CAEvB,AAAO,UAEH,EAAE;CAEN,AAAO;CAEP,AAAO;;;;CAKP,AAAO,SAAS,KAAa,SAAiB,KAAW;AACvD,OAAK,SAAS;AACd,OAAK,QAAQ,WAAW;;;;;CAQ1B,AAAO,UAAU,QAAsB;AACrC,OAAK,SAAS;AACd,SAAO;;;;;CAMT,AAAO,UAAU,MAAc,OAAqB;AAClD,OAAK,QAAQ,KAAK,aAAa,IAAI;AACnC,SAAO;;;;;CAMT,AAAO,QAAQ,MAAiB;AAC9B,OAAK,OAAO;AACZ,SAAO;;;;;;ACzCX,MAAa,eACX,OACA,WAC2B;AAS3B,KAAI,EAPF,CAAC,CAAC,SACF,OAAO,UAAU,YACjB,aAAa,SACb,OAAO,MAAM,YAAY,YACzB,YAAY,SACZ,OAAO,MAAM,WAAW,UAGxB,QAAO;AAGT,KAAI,OACF,QAAQ,MAAwB,WAAW;AAG7C,QAAO;;AAGT,IAAa,YAAb,cAA+B,YAAY;CACzC,AAAO,OAAO;CAEd,OAAO,KAAK;CAEZ,OAAO,OAAO,OAA+B;EAC3C,MAAM,OAAgC;GACpC,OAAO,MAAM;GACb,QAAQ,MAAM;GACd,SAAS,MAAM;GAChB;AAED,MAAI,MAAM,QAAS,MAAK,UAAU,MAAM;AACxC,MAAI,MAAM,UAAW,MAAK,YAAY,MAAM;AAC5C,MAAI,MAAM,OAAQ,MAAK,QAAQ,MAAM;AAErC,SAAO;;CAGT,AAAgB;CAChB,AAAgB;CAEhB,AAAgB;CAChB,AAAgB;CAChB,AAAgB;CAKhB,YAAY,SAA+B,OAAiB;AAC1D,QAAM,QAAQ,SAAS,EACrB,OACD,CAAC;AAEF,OAAK,SAAS,QAAQ,UAAU;AAChC,OAAK,UAAU,QAAQ;AACvB,OAAK,YAAY,QAAQ;AAEzB,MAAI,OAAO,QAAQ,UAAU,SAC3B,MAAK,SAAS;GACZ,MAAO,QAAQ,MAA2B;GAC1C,SAAU,QAAQ,MAA8B;GACjD;WACQ,iBAAiB,MAC1B,MAAK,SAAS;GACZ,MAAM,MAAM;GACZ,SAAS,MAAM;GAChB;AAGH,MAAI,KAAK,YAAY,SAAS,YAC5B,MAAK,QACH,QAAQ,SAAS,kBAAkB,KAAK,WAAW;MAErD,MAAK,QAAQ,KAAK,YAAY;;;AAKpC,MAAa,oBAA4C;CACvD,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACN;;;;ACnGD,IAAa,kBAAb,cAAqC,UAAU;CAC7C,YAAY,UAAU,yBAAyB,OAAiB;AAC9D,QACE;GACE;GACA,QAAQ;GACT,EACD,MACD;;;;;;;;;;;;ACyBL,IAAa,kBAAb,MAA6B;CAC3B,AAAO,MAAM,YAAoB,IAAmB;EAClD,MAAM,KAAK,UAAU,aAAa;EAGlC,IAAI,KAA0B;EAC9B,IAAI,UAAoC;EACxC,IAAI,SAAkC;AAGtC,MAAI,GAAG,SAAS,gBAAgB,CAC9B,MAAK;WACI,GAAG,SAAS,UAAU,CAC/B,MAAK;WACI,GAAG,SAAS,UAAU,CAC/B,MAAK;WAEL,GAAG,SAAS,SAAS,IACrB,GAAG,SAAS,OAAO,IACnB,GAAG,SAAS,OAAO,IAClB,GAAG,SAAS,MAAM,IAAI,CAAC,GAAG,SAAS,UAAU,CAE9C,MAAK;WAEL,GAAG,SAAS,SAAS,IACrB,GAAG,SAAS,QAAQ,IACpB,GAAG,SAAS,YAAY,CAExB,MAAK;WACI,GAAG,SAAS,OAAO,IAAI,GAAG,SAAS,WAAW,CACvD,MAAK;WACI,GAAG,SAAS,SAAS,CAC9B,MAAK;WACI,GAAG,SAAS,UAAU,CAC/B,MAAK;WACI,GAAG,SAAS,UAAU,CAC/B,MAAK;WACI,GAAG,SAAS,aAAa,IAAI,GAAG,SAAS,OAAO,CACzD,MAAK;WACI,GAAG,SAAS,UAAU,IAAI,GAAG,SAAS,SAAS,CACxD,MAAK;WACI,GAAG,SAAS,QAAQ,IAAI,GAAG,SAAS,MAAM,CACnD,MAAK;AAIP,MAAI,GAAG,SAAS,YAAY,IAAI,GAAG,SAAS,SAAS,CACnD,WAAU;WACD,GAAG,SAAS,QAAQ,CAC7B,WAAU;WACD,GAAG,SAAS,UAAU,CAC/B,WAAU;WACD,GAAG,SAAS,iBAAiB,IAAI,GAAG,SAAS,UAAU,CAChE,WAAU;WACD,GAAG,SAAS,YAAY,IAAI,GAAG,SAAS,aAAa,CAC9D,WAAU;WAEV,GAAG,SAAS,QAAQ,IACpB,GAAG,SAAS,OAAO,IACnB,GAAG,SAAS,QAAQ,CAEpB,WAAU;WAEV,GAAG,SAAS,OAAO,IACnB,GAAG,SAAS,OAAO,IACnB,GAAG,SAAS,SAAS,CAErB,WAAU;WACD,GAAG,SAAS,UAAU,IAAI,CAAC,GAAG,SAAS,YAAY,CAC5D,WAAU;WACD,GAAG,SAAS,UAAU,IAAI,GAAG,SAAS,OAAO,CACtD,WAAU;WAEV,GAAG,SAAS,SAAS,IACrB,CAAC,GAAG,SAAS,SAAS,IACtB,CAAC,GAAG,SAAS,WAAW,CAExB,WAAU;WAEV,GAAG,SAAS,SAAS,IACrB,GAAG,SAAS,WAAW,IACvB,GAAG,SAAS,QAAQ,CAEpB,WAAU;EAIZ,MAAM,iBAAiB;GACrB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD;EAaD,MAAM,WAXiB;GACrB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAE+B,MAAM,YAAY,GAAG,SAAS,QAAQ,CAAC;EACvE,MAAM,WAAW,eAAe,MAAM,YAAY,GAAG,SAAS,QAAQ,CAAC;AAEvE,MAAI,SACF,UAAS;WACA,SACT,UAAS;MAET,UAAS;AAGX,SAAO;GAAE;GAAI;GAAS;GAAQ;;;;;;ACpJlC,MAAMA,cAAY,EAAE,OAAO,EASzB,aAAa,EAAE,QAAQ;CACrB,SAAS;CACT,aAAa;CACd,CAAC,EACH,CAAC;AAEF,IAAa,sBAAb,MAAa,oBAAoB;CAC/B,AAAmB,SAAS,QAAQ,OAAO;CAC3C,AAAmB,kBAAkB,QAAQ,gBAAgB;CAC7D,AAAmB,MAAM,KAAKA,YAAU;CAExC,AAAO,oBAAoB,YAA8C;EACvE,MAAM,OAAO;AACb,SAAO;GACL,QAAQ,WAAW;GACnB,KAAK,WAAW;GAChB,KAAK,WAAW;GAChB,SAAS,WAAW;GACpB,OAAO,WAAW;GAClB,QAAQ,WAAW;GAEnB,MAAM;GACN,UAAU,EAAE;GACZ,WAAW,KAAK,aAAa,WAAW,IAAI,OAAO,YAAY;GAC/D,OAAO,KAAK,OAAO,OAAO,aAAa,EAAE,UAAU,aAAa,CAAC;GACjE,IAAI,KAAK;AACP,WAAO,KAAK,aAAa,WAAW;;GAEtC,IAAI,YAAY;AACd,WAAO,KAAK,oBAAoB,WAAW;;GAE7C,IAAI,MAAM;AACR,WAAO,KAAK,cAAc,WAAW;;GAEvC,IAAI,QAAQ;AACV,WAAO,KAAK,SAAS,WAAW;;GAElC,IAAI,WAAW;AACb,WAAO,KAAK,YAAY,WAAW;;GAErC,IAAI,WAAW;AACb,WAAO,KAAK,YAAY,WAAW;;GAErC,IAAI,WAAW;AACb,WAAO,KAAK,YAAY,WAAW;;GAErC,IAAI,UAAU;AACZ,WAAO,KAAK,WAAW,WAAW;;GAErC;;CAGH,AAAO,aAAa,SAAgD;AAClE,SAAO,QAAQ,QAAQ;;CAGzB,AAAO,oBAAoB,SAA4B;AACrD,SAAO,KAAK,gBAAgB,MAAM,QAAQ,QAAQ,cAAc;;CAGlE,AAAO,aAAa,SAAgD;AAElE,MAAI,KAAK,IAAI,aAAa;GACxB,MAAM,UAAU,QAAQ;GAGxB,MAAM,eAAe,QAAQ;AAC7B,OAAI,aACF,QAAO,MAAM,QAAQ,aAAa,GAC9B,aAAa,KACb,aAAa,MAAM,IAAI,CAAC,GAAG,MAAM;GAIvC,MAAM,UAAU,QAAQ;AACxB,OAAI,QACF,QAAO,MAAM,QAAQ,QAAQ,GAAG,QAAQ,KAAK;;AAKjD,SAAO,KAAK,gBAAgB,QAAQ;;CAGtC,AAAU,gBAAgB,SAAgD;EAExE,MAAM,UAAU,QAAQ,IAAI,MAAM;AAClC,MAAI,QACF,QAAO,QAAQ,QAAQ;;CAS3B,AAAO,cAAc,SAAwC;EAC3D,MAAM,UAAU,QAAQ;AAExB,SAAO;GAEL,SACE,QAAQ,mBACR,QAAQ,0BACR,QAAQ;GAGV,MAAM,QAAQ,gBAAgB,QAAQ;GAGtC,QACE,QAAQ,gBACR,QAAQ,qBACR,QAAQ;GAGV,UAAU,QAAQ,oBAAoB,QAAQ;GAC9C,WAAW,QAAQ,qBAAqB,QAAQ;GACjD;;CAOH,OAA0B,eAAe;EACvC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;CAED,AAAO,SAAS,SAAqC;EACnD,MAAM,KAAK,QAAQ,QAAQ;AAC3B,MAAI,CAAC,GAAI,QAAO;AAEhB,SAAO,oBAAoB,aAAa,MAAM,YAAY,QAAQ,KAAK,GAAG,CAAC;;CAO7E,OAA0B,kBAAkB;EAC1C;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;CAED,AAAO,YAAY,SAAqC;EACtD,MAAM,KAAK,QAAQ,QAAQ;AAC3B,MAAI,CAAC,GAAI,QAAO;AAEhB,SAAO,oBAAoB,gBAAgB,MAAM,YAC/C,QAAQ,KAAK,GAAG,CACjB;;CAOH,AAAO,YAAY,SAA8C;EAE/D,MAAM,iBAAiB,QAAQ,QAAQ;AACvC,MAAI,eACF,QAAO,eAAe,aAAa,KAAK,UAAU,UAAU;EAI9D,MAAM,kBAAkB,QAAQ,QAAQ;AACxC,MAAI,gBACF,KAAI;AAEF,OADe,KAAK,MAAM,gBAAgB,CAC/B,WAAW,QAAS,QAAO;UAChC;AAMV,MAAI,QAAQ,IAAI,aAAa,SAC3B,QAAO;AAGT,SAAO;;CAOT,A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"}
|