UNPKG

alepha

Version:

Easy-to-use modern TypeScript framework for building many kind of applications.

1,449 lines 58.6 kB
import { Alepha, AlephaError, Async, FileLike, KIND, Middleware, PipelineHandler, PipelinePrimitive, PipelinePrimitiveOptions, Primitive, SchemaOutput, Static, StreamLike, TArray, TFile, TObject, TRecord, TSchema, TStream, TString, TVoid } from "alepha"; import { Readable, Transform } from "node:stream"; import { DateTimeProvider, DurationLike } from "alepha/datetime"; import { ReadableStream as ReadableStream$1 } from "node:stream/web"; import { CryptoProvider } from "alepha/crypto"; import { Route, RouterProvider } from "alepha/router"; import { IncomingMessage, Server, ServerResponse as ServerResponse$1 } from "node:http"; import { ServerResponse as ServerResponse$2, ServerRoute as ServerRoute$1 } from "alepha/server"; import { Socket } from "node:net"; //#region ../../src/server/core/schemas/errorSchema.d.ts declare const errorSchema: import("zod").ZodObject<{ error: import("zod").ZodString; status: import("zod").ZodInt; message: import("zod").ZodString; details: import("zod").ZodOptional<import("zod").ZodString>; requestId: import("zod").ZodOptional<import("zod").ZodString>; cause: import("zod").ZodOptional<import("zod").ZodObject<{ name: import("zod").ZodString; message: import("zod").ZodString; }, import("zod/v4/core").$strip>>; }, import("zod/v4/core").$strip>; type ErrorSchema = Static<typeof errorSchema>; //#endregion //#region ../../src/server/core/errors/HttpError.d.ts declare const isHttpError: (error: unknown, status?: number) => error is HttpErrorLike; declare class HttpError extends AlephaError { name: string; static is: typeof isHttpError; static toJSON(error: HttpError): ErrorSchema; readonly error: string; readonly status: number; readonly requestId?: string; readonly details?: string; readonly reason?: { name: string; message: string; }; constructor(options: Partial<ErrorSchema>, cause?: unknown); } declare const errorNameByStatus: Record<number, string>; interface HttpErrorLike extends Error { status: number; } //#endregion //#region ../../src/server/core/constants/routeMethods.d.ts declare const routeMethods: readonly ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS", "CONNECT", "TRACE"]; type RouteMethod = (typeof routeMethods)[number]; //#endregion //#region ../../src/server/core/helpers/ServerReply.d.ts /** * Helper for building server replies. */ declare class ServerReply { headers: Record<string, string> & { "set-cookie"?: string[]; }; status?: number; body?: any; /** * Redirect to a given URL with optional status code (default 302). */ redirect(url: string, status?: number): void; /** * Set the response status code. */ setStatus(status: number): this; /** * Set a response header. */ setHeader(name: string, value: string): this; /** * Set the response body. */ setBody(body: any): this; } //#endregion //#region ../../src/server/core/services/UserAgentParser.d.ts interface UserAgentInfo { os: "Windows" | "Android" | "Ubuntu" | "MacOS" | "iOS" | "Linux" | "FreeBSD" | "OpenBSD" | "ChromeOS" | "BlackBerry" | "Symbian" | "Windows Phone"; browser: "Chrome" | "Firefox" | "Safari" | "Edge" | "Opera" | "Internet Explorer" | "Brave" | "Vivaldi" | "Samsung Browser" | "UC Browser" | "Yandex"; device: "MOBILE" | "DESKTOP" | "TABLET"; } /** * Simple User-Agent parser to detect OS, browser, and device type. * This parser is not exhaustive and may not cover all edge cases. * * Use result for non */ declare class UserAgentParser { parse(userAgent?: string): UserAgentInfo; } //#endregion //#region ../../src/server/core/interfaces/ServerRequest.d.ts type TRequestBody = TObject | TString | TArray | TRecord | TStream; type TResponseBody = TObject | TString | TRecord | TFile | TArray | TStream | TVoid; interface RequestConfigSchema { body?: TRequestBody; params?: TObject; query?: TObject; headers?: TObject; response?: TResponseBody; } interface ServerRequestConfig<TConfig extends RequestConfigSchema = RequestConfigSchema> { body: SchemaOutput<TConfig["body"], any>; headers: SchemaOutput<TConfig["headers"], Record<string, string>>; params: SchemaOutput<TConfig["params"], Record<string, string>>; query: SchemaOutput<TConfig["query"], Record<string, any>>; } type ServerRequestConfigEntry<TConfig extends RequestConfigSchema = RequestConfigSchema> = Partial<ServerRequestConfig<TConfig>>; interface ServerRequest<TConfig extends RequestConfigSchema = RequestConfigSchema> extends ServerRequestConfig<TConfig> { /** * HTTP method used for this request. */ method: RouteMethod; /** * Full request URL. */ url: URL; /** * Unique request ID assigned to this request. */ requestId: string; /** * Client IP address. * Uses `X-Forwarded-For` header when `TRUST_PROXY=true`. */ ip?: string; /** * Value of the `Host` header sent by the client. */ host?: string; /** * Browser user agent information. * Information are not guaranteed to be accurate. Use with caution. * * @see {@link UserAgentParser} */ userAgent: UserAgentInfo; /** * Geolocation information derived from proxy headers. * Available when behind Cloudflare, Vercel, or similar CDNs. */ geo: RequestGeo; /** * Whether the request appears to be from a bot/crawler. * Based on user-agent analysis. */ isBot: boolean; /** * Whether the request is from a mobile device. * Based on user-agent analysis. */ isMobile: boolean; /** * Request protocol (http or https). * Uses `X-Forwarded-Proto` header when behind a proxy. */ protocol: "http" | "https"; /** * Preferred language from `Accept-Language` header. * Returns the first/most preferred language code (e.g., "en", "fr", "en-US"). */ language?: string; /** * Parsed referer information. * Undefined if no Referer header or invalid URL. */ referer?: RequestReferer; /** * Arbitrary metadata attached to the request. Can be used by middlewares to store information. */ metadata: Record<string, any>; /** * Reply object to be used to send response. */ reply: ServerReply; /** * The raw underlying request object (Web Request). */ raw: ServerRawRequest; } interface ServerRoute<TConfig extends RequestConfigSchema = RequestConfigSchema> extends Route { /** * Handler function for this route. */ handler: PipelineHandler; /** * HTTP method for this route. */ method?: RouteMethod; /** * Request/response schema for this route. * * Request schema contains: * - body, for POST/PUT/PATCH requests * - params, for URL parameters (e.g. /user/:id) * - query, for URL query parameters (e.g. /user?id=123) * - headers, for HTTP headers * * Response schema contains: * - response * * Response schema is used to validate and serialize the response sent by the handler. */ schema?: TConfig; /** * @see ServerLoggerProvider */ silent?: boolean; /** * Mark this `GET` route as prerenderable. When `true`, the build invokes the * handler in-process and writes its body verbatim to `dist/public/{path}`, * so the route is served as a static file (e.g. `sitemap.xml`, `robots.txt`). * The route still serves live at request time for runtimes that have a server. */ static?: boolean; } /** * Input type for `createRoute()`. Accepts both a `PipelineHandler` and a plain handler function. * Plain functions are auto-wrapped in `PipelineHandler` by `createRoute()`. */ type ServerRouteInput<TConfig extends RequestConfigSchema = RequestConfigSchema> = Omit<ServerRoute<TConfig>, "handler"> & { handler: PipelineHandler | ServerHandler<TConfig>; }; type ServerResponseBody<TConfig extends RequestConfigSchema = RequestConfigSchema> = SchemaOutput<TConfig["response"], ResponseBodyType>; type ResponseKind = "json" | "text" | "void" | "file" | "any"; type ResponseBodyType = string | Buffer | StreamLike | undefined | null | void; type ServerHandler<TConfig extends RequestConfigSchema = RequestConfigSchema> = (request: ServerRequest<TConfig>) => Async<ServerResponseBody<TConfig>>; interface ServerResponse { body: string | Buffer | ArrayBuffer | Readable | ReadableStream$1; headers: Record<string, string>; status: number; } type ServerRouteRequestHandler = (request: ServerRequestData) => Promise<ServerResponse>; interface ServerRouteMatcher extends Route { handler: ServerRouteRequestHandler; } interface ServerRequestData { readonly method: RouteMethod; readonly url: URL; readonly headers: Record<string, string>; readonly query: Record<string, string>; readonly params: Record<string, string>; raw: ServerRawRequest; } interface ServerRawRequest { node?: NodeRequestEvent; web?: WebRequestEvent; } interface NodeRequestEvent { req: IncomingMessage; res: ServerResponse$1; } interface WebRequestEvent { req: Request; res?: Response; } /** * Geolocation information from proxy headers (Cloudflare, Vercel, etc.) */ interface RequestGeo { /** * ISO 3166-1 alpha-2 country code (e.g., "US", "FR", "JP"). */ country?: string; /** * City name (e.g., "San Francisco", "Paris"). */ city?: string; /** * Region/state (e.g., "California", "Île-de-France"). */ region?: string; /** * Latitude (if available). */ latitude?: string; /** * Longitude (if available). */ longitude?: string; } /** * Parsed referer information. */ interface RequestReferer { /** * Full referer URL. */ url: string; /** * Hostname of the referer (e.g., "google.com"). */ hostname: string; /** * Path of the referer URL. */ pathname: string; } //#endregion //#region ../../src/server/core/services/ServerRequestParser.d.ts declare class ServerRequestParser { protected readonly alepha: Alepha; protected readonly userAgentParser: UserAgentParser; protected readonly cryptoProvider: CryptoProvider; protected readonly env: { TRUST_PROXY: boolean; }; protected readonly rootURL: URL; createServerRequest(partialRawRequest: Partial<ServerRequestData>): ServerRequest; getRequestId(request: ServerRequestData): string; getRequestUserAgent(request: ServerRequestData): import("alepha/server").UserAgentInfo; getRequestIp(request: ServerRequestData): string | undefined; protected getConnectionIp(request: ServerRequestData): string | undefined; getRequestGeo(request: ServerRequestData): RequestGeo; protected static readonly BOT_PATTERNS: RegExp[]; getIsBot(request: ServerRequestData): boolean; protected static readonly MOBILE_PATTERNS: RegExp[]; getIsMobile(request: ServerRequestData): boolean; getProtocol(request: ServerRequestData): "http" | "https"; getLanguage(request: ServerRequestData): string | undefined; getReferer(request: ServerRequestData): RequestReferer | undefined; } //#endregion //#region ../../src/server/core/providers/ServerTimingProvider.d.ts type TimingMap = Record<string, [number, number]>; declare class ServerTimingProvider { protected readonly log: import("alepha/logger").Logger; protected readonly dateTime: DateTimeProvider; protected readonly alepha: Alepha; options: { prefix: string; disabled: boolean; }; readonly onRequest: import("alepha").HookPrimitive<"server:onRequest">; readonly onResponse: import("alepha").HookPrimitive<"server:onResponse">; protected get handlerName(): string; beginTiming(name: string): void; endTiming(name: string): void; protected setDuration(name: string, timing: TimingMap): void; } //#endregion //#region ../../src/server/core/providers/ServerRouterProvider.d.ts /** * Main router for all routes server side. * * Reminder: * - $route => generic route * - $action => action route (for API calls) * - $page => React route (for React SSR) */ declare class ServerRouterProvider extends RouterProvider<ServerRouteMatcher> { protected readonly log: import("alepha/logger").Logger; protected readonly alepha: Alepha; protected readonly crypto: CryptoProvider; protected readonly routes: ServerRoute[]; protected readonly serverTimingProvider: ServerTimingProvider; protected readonly serverRequestParser: ServerRequestParser; protected readonly queryKeysCache: WeakMap<object, string[]>; protected readonly globalMiddlewareRegistry: GlobalMiddlewareEntry[]; /** * Get cached keys for a query schema, computing them lazily on first access. */ protected getQuerySchemaKeys(schema: { properties: object; }): string[]; pushMiddleware(pattern: string, middleware: Middleware[], options?: PushMiddlewareOptions): void; /** * Check if a route passes the middleware filter options (method, exclude). */ protected matchesMiddlewareFilter(route: ServerRoute, options?: PushMiddlewareOptions): boolean; /** * Check if a route path matches a pattern. * Wildcard `*` at end = prefix match, otherwise exact match. */ protected matchesPattern(routePath: string, pattern: string): boolean; /** * Get all registered routes, optionally filtered by a pattern. * * Pattern accept simple wildcard '*' at the end. * Example: '/api/*' will match all routes starting with '/api/' but '/api/' will match only that exact route. */ getRoutes(pattern?: string): ServerRoute[]; /** * Create a new server route. * * Accepts both `PipelineHandler` and plain handler functions. * Plain functions are auto-wrapped in `PipelineHandler`. */ createRoute<TConfig extends RequestConfigSchema = RequestConfigSchema>(input: ServerRouteInput<TConfig>): void; /** * Get or generate a context ID from request headers. * * It first checks for common headers like 'x-request-id' or 'x-correlation-id' that may be set by proxies or clients. * If none of these headers are present, it generates a new UUID to ensure uniqueness. * * Note: In production environments, it's recommended to have a proxy (e.g. Nginx, Cloudflare) that sets a consistent request ID header for better traceability across services. */ protected getContextId(headers: Record<string, string>): string; /** * Process an incoming request through the full lifecycle: * onRequest → handler → onSend → response → onResponse */ protected processRequest(request: ServerRequest, route: ServerRoute, responseKind: ResponseKind): Promise<{ status: number; headers: Record<string, string> & { "set-cookie"?: string[]; }; body: any; }>; /** * Run the route handler with request validation and response serialization. */ protected runRouteHandler(route: ServerRoute, request: ServerRequest, responseKind: ResponseKind): Promise<void>; /** * Transform reply body based on response kind and route schema. */ serializeResponse(route: ServerRoute, reply: ServerReply, responseKind: ResponseKind): void; /** * Determine response type based on route schema. */ protected getResponseType(schema?: RequestConfigSchema): ResponseKind; /** * Handle errors during request processing. */ protected errorHandler(route: ServerRoute, request: ServerRequest, error: Error): Promise<void>; /** * Validate incoming request against route schema. */ validateRequest(route: { schema?: RequestConfigSchema; }, request: ServerRequestConfig): void; /** * Coerce a raw query/header value (always a string on the wire) to the JS * type its schema declares, mirroring `z.coerce` at the HTTP boundary. * * Only the boundary coerces — request bodies and the ORM stay strict. Values * that can't be coerced are passed through unchanged so the subsequent * validation produces a proper rejection. Arrays coerce element-wise. */ protected coerceParam(schema: unknown, value: unknown): unknown; } interface PushMiddlewareOptions { method?: RouteMethod | RouteMethod[]; exclude?: string[]; } interface GlobalMiddlewareEntry extends PushMiddlewareOptions { pattern: string; middleware: Middleware[]; } //#endregion //#region ../../src/server/core/providers/ServerProvider.d.ts /** * Base server provider to handle incoming requests and route them. * * This is the default implementation for serverless environments. * * ServerProvider supports both Node.js HTTP requests and Web (Fetch API) requests. */ declare class ServerProvider { protected readonly log: import("alepha/logger").Logger; protected readonly alepha: Alepha; protected readonly dateTimeProvider: DateTimeProvider; protected readonly router: ServerRouterProvider; protected readonly internalServerErrorMessage = "Internal Server Error"; protected readonly internalErrorResponse: Readonly<{ status: 500; headers: Readonly<{ "content-type": "text/plain"; }>; body: "Internal Server Error"; }>; protected readonly handleInternalError: () => Readonly<{ status: 500; headers: Readonly<{ "content-type": "text/plain"; }>; body: "Internal Server Error"; }>; protected readonly urlBaseCache: Map<string, string>; /** * Get cached URL base (protocol + host) for a given host header. * Caches the result to avoid repeated string concatenation. */ protected getUrlBase(headers: Record<string, string>): string; /** * Parse query string manually - faster than new URL() + URLSearchParams. * Returns empty object if no query string. */ protected parseQueryString(rawUrl: string): Record<string, string>; /** * Fast decode - only calls decodeURIComponent if needed. */ protected fastDecode(str: string): string; get hostname(): string; /** * When a Node.js HTTP request is received from outside. (Vercel, AWS Lambda, etc.) */ protected readonly onNodeRequest: import("alepha").HookPrimitive<"node:request">; /** * When a Web (Fetch API) request is received from outside. (Netlify, Cloudflare Workers, etc.) */ protected readonly onWebRequest: import("alepha").HookPrimitive<"web:request">; /** * Handle Node.js HTTP request event. * * Optimized to avoid expensive URL parsing when possible. */ handleNodeRequest(nodeRequestEvent: NodeRequestEvent): Promise<void>; /** * Handle Web (Fetch API) request event. */ handleWebRequest(ev: WebRequestEvent): Promise<void>; /** * Convert response headers to Web API Headers. * * The `set-cookie` header requires special handling because it's stored * as `string[]` (one entry per cookie) but the `Response` constructor * would comma-join arrays, which breaks cookie parsing. We use * `Headers.append()` to emit each cookie as a separate header. */ protected toWebHeaders(headers: Record<string, string | string[]>): Headers; /** * Helper for Vite development mode to let Vite handle (or not) 404. */ protected isViteNotFound(url?: string, route?: Route, params?: Record<string, string>): boolean; } //#endregion //#region ../../src/server/core/services/HttpClient.d.ts declare class HttpClient { protected readonly log: import("alepha/logger").Logger; protected readonly alepha: Alepha; readonly cache: import("alepha/cache").CacheMiddlewareFn<HttpClientCache>; protected readonly pendingRequests: HttpClientPendingRequests; fetchAction(args: FetchActionArgs): Promise<FetchResponse>; fetch<T extends TSchema>(url: string, request?: RequestInitWithOptions<T>): Promise<FetchResponse<Static<T>>>; protected url(host: string, action: HttpAction, args: ServerRequestConfigEntry): string; protected body(init: RequestInit, headers: Record<string, string>, action: HttpAction, args?: ServerRequestConfigEntry): Promise<void>; protected responseData(response: Response, options: ResolvedFetchOptions): Promise<any>; protected isMaybeFile(response: Response): boolean; protected createFileLike(response: Response, defaultFileName?: string): FileLike; pathVariables(url: string, action: { schema?: { params?: TObject; }; }, args?: ServerRequestConfigEntry): string; queryParams(url: string, action: { schema?: { query?: TObject; }; }, args?: ServerRequestConfigEntry): string; } interface FetchOptions<T extends TSchema = TSchema> { /** * Key to identify the request in the pending requests. */ key?: string; /** * The schema to validate the response against. */ schema?: { response?: T; }; /** * Built-in cache options. */ localCache?: boolean | number | DurationLike; } type RequestInitWithOptions<T extends TSchema = TSchema> = RequestInit & FetchOptions<T>; /** * Internal resolved fetch options — built in {@link HttpClient.fetch}, * consumed by {@link HttpClient.responseData} and the `client:beforeFetch` * event. Distinct from the external {@link FetchOptions}: `schema` is the * already-unwrapped response schema and `cache` is the resolved cache directive. */ interface ResolvedFetchOptions { key?: string; schema?: TSchema; cache?: boolean | number | DurationLike; } interface FetchResponse<T = any> { data: T; status: number; statusText: string; headers: Headers; raw?: Response; } type HttpClientPendingRequests = Record<string, Promise<any> | undefined>; interface HttpClientCache { data: any; etag?: string; } interface FetchActionArgs { action: HttpAction; host?: string; config?: ServerRequestConfigEntry; options?: ClientRequestOptions; } interface HttpAction { method?: string; prefix?: string; path: string; contentType?: string; requestBodyType?: string; schema?: { params?: TObject; query?: TObject; body?: TRequestBody; response?: TResponseBody; }; } //#endregion //#region ../../src/server/core/primitives/$action.d.ts /** * Creates a server action primitive for defining type-safe HTTP endpoints. * * Server actions are the core building blocks for REST APIs in Alepha, providing declarative * HTTP endpoints with full type safety, automatic validation, and OpenAPI documentation. * * **Key Features** * - Full TypeScript inference for request/response types * - Automatic schema validation using TypeBox * - Convention-based URL generation with customizable paths * - Direct invocation (`run()`) or HTTP requests (`fetch()`) * - Built-in authentication and authorization support * - Automatic content-type handling (JSON, form-data, plain text) * * **URL Generation** * * **Important:** All `$action` paths are automatically prefixed with `/api`. * * ```ts * $action({ path: "/users" }) // → GET /api/users * $action({ path: "/users/:id" }) // → GET /api/users/:id * $action({ path: "/hello" }) // → GET /api/hello * ``` * * This prefix is configurable via the `SERVER_API_PREFIX` environment variable. * HTTP method defaults to GET, or POST if body schema is provided. * * **Common Use Cases** * - CRUD operations with type safety * - File upload and download endpoints * - Microservice communication * * @example * ```ts * class UserController { * getUsers = $action({ * path: "/users", * schema: { * query: z.object({ * page: z.number({ default: 1 }).optional(), * limit: z.number({ default: 10 }).optional() * }), * response: z.object({ * users: z.array(z.object({ * id: z.text(), * name: z.text(), * email: z.text() * })), * total: z.number() * }) * }, * handler: async ({ query }) => { * const users = await this.userService.findUsers(query); * return { users: users.items, total: users.total }; * } * }); * * createUser = $action({ * method: "POST", * path: "/users", * schema: { * body: z.object({ * name: z.text(), * email: z.text({ format: "email" }) * }), * response: z.object({ id: z.text(), name: z.text() }) * }, * handler: async ({ body }) => { * return await this.userService.create(body); * } * }); * } * ``` */ declare const $action: { <TConfig extends RequestConfigSchema>(options: ActionPrimitiveOptions<TConfig>): ActionPrimitiveFn<TConfig>; [KIND]: typeof ActionPrimitive; }; interface ActionPrimitiveOptions<TConfig extends RequestConfigSchema> extends Omit<ServerRoute, "handler" | "path" | "schema" | "mapParams">, PipelinePrimitiveOptions { /** * Name of the action. * * - It will be used to generate the route path if `path` is not provided. * - It will be used to generate the permission name if `security` is enabled. */ name?: string; /** * Group actions together. * * - If not provided, the service name containing the route will be used. * - It will be used as Tag for documentation purposes. * - It will be used for permission name generation if `security` is enabled. * * @example * ```ts * // group = "MyController" * class MyController { * hello = $action({ handler: () => "Hello World" }); * } * * // group = "users" * class MyOtherController { * group = "users"; * a1 = $action({ handler: () => "Action 1", group: this.group }); * a2 = $action({ handler: () => "Action 2", group: this.group }); * } * ``` */ group?: string; /** * Pathname of the route. If not provided, property key is used. */ path?: string; /** * The route method. * * - If not provided, it will be set to "GET" by default. * - If not provider and a body is provided, it will be set to "POST". * * Wildcard methods are not supported for now. (e.g. "ALL", "ANY", etc.) */ method?: RouteMethod; /** * The config schema of the route. * - body: The request body schema. * - params: Path variables schema. * - query: The request query-params schema. * - response: The response schema. */ schema?: TConfig; /** * A short description of the action. Used for documentation purposes. */ description?: string; /** * Disable the route. Useful with env variables to disable one specific route. * Route won't be available in the API nor locally. */ disabled?: boolean; /** * Main route handler. This is where the route logic is implemented. */ handler: ServerActionHandler<TConfig>; } /** * Server API configuration atom. */ declare const serverApiOptions: import("alepha").Atom<import("zod").ZodObject<{ prefix: import("zod").ZodString; }, import("zod/v4/core").$strip>, "alepha.server.api.options">; type ServerApiOptions = Static<typeof serverApiOptions.schema>; declare module "alepha" { interface State { [serverApiOptions.key]: ServerApiOptions; } } declare class ActionPrimitive<TConfig extends RequestConfigSchema> extends PipelinePrimitive<ActionPrimitiveOptions<TConfig>> { protected readonly log: import("alepha/logger").Logger; protected readonly settings: Readonly<{ prefix: string; }>; protected readonly httpClient: HttpClient; protected readonly serverProvider: ServerProvider; protected readonly serverRouterProvider: ServerRouterProvider; protected onInit(): void; get prefix(): string; get route(): ServerRoute; /** * Returns the name of the action. */ get name(): string; /** * Returns the group of the action. (e.g. "orders", "admin", etc.) */ get group(): string; /** * Returns the HTTP method of the action. */ get method(): RouteMethod; /** * Returns the path of the action. * * Path is prefixed by `/api` by default. */ get path(): string; get schema(): TConfig | undefined; getBodyContentType(): string | undefined; /** * Call the action handler directly. * There is no HTTP layer involved. */ run(config?: ClientRequestEntry<TConfig>, options?: ClientRequestOptions): Promise<ClientRequestResponse<TConfig>>; /** * Works like `run`, but always fetches (http request) the route. */ fetch(config?: ClientRequestEntry<TConfig>, options?: ClientRequestOptions): Promise<FetchResponse<ClientRequestResponse<TConfig>>>; } interface ActionPrimitiveFn<TConfig extends RequestConfigSchema> extends ActionPrimitive<TConfig> { (config?: ClientRequestEntry<TConfig>, options?: ClientRequestOptions): Promise<ClientRequestResponse<TConfig>>; } type ClientRequestEntry<TConfig extends RequestConfigSchema, T = ClientRequestEntryContainer<TConfig>> = { [K in keyof T as T[K] extends undefined ? never : K]: T[K]; }; type ClientRequestEntryContainer<TConfig extends RequestConfigSchema> = { body: SchemaOutput<TConfig["body"], undefined>; params: SchemaOutput<TConfig["params"], undefined>; headers?: SchemaOutput<TConfig["headers"], Record<string, string>>; query?: Partial<SchemaOutput<TConfig["query"], Record<string, string>>>; }; interface ClientRequestOptions extends FetchOptions { /** * Standard request fetch options. */ request?: RequestInit; /** * Add query parameters to the request URL. They will be merged with any query params defined in the action schema. * This is useful for adding dynamic query params at runtime. */ query?: Record<string, string | number | boolean>; } type ClientRequestResponse<TConfig extends RequestConfigSchema> = SchemaOutput<TConfig["response"], any>; /** * Specific handler for server actions. */ type ServerActionHandler<TConfig extends RequestConfigSchema = RequestConfigSchema> = (request: ServerActionRequest<TConfig>) => Async<ServerResponseBody<TConfig>>; /** * Server Action Request Interface * * Can be extended with module augmentation to add custom properties (like `user` in Server Security). * * This is NOT Server Request, but a specific type for actions. */ interface ServerActionRequest<TConfig extends RequestConfigSchema> extends ServerRequest<TConfig> {} //#endregion //#region ../../src/server/core/errors/BadRequestError.d.ts declare class BadRequestError extends HttpError { constructor(message?: string, cause?: unknown); } //#endregion //#region ../../src/server/core/errors/ConflictError.d.ts declare class ConflictError extends HttpError { constructor(message?: string, cause?: unknown); } //#endregion //#region ../../src/server/core/errors/ForbiddenError.d.ts declare class ForbiddenError extends HttpError { constructor(message?: string, cause?: unknown); } //#endregion //#region ../../src/server/core/errors/NotFoundError.d.ts declare class NotFoundError extends HttpError { constructor(message?: string, cause?: unknown); } //#endregion //#region ../../src/server/core/errors/UnauthorizedError.d.ts declare class UnauthorizedError extends HttpError { readonly name = "UnauthorizedError"; constructor(message?: string, cause?: unknown); } //#endregion //#region ../../src/server/core/errors/ValidationError.d.ts declare class ValidationError extends HttpError { constructor(message?: string, cause?: unknown); } //#endregion //#region ../../src/server/core/helpers/isMultipart.d.ts /** * Checks if the route has multipart/form-data request body. */ declare const isMultipart: (options: { schema?: RequestConfigSchema; contentType?: string; requestBodyType?: string; }) => boolean; //#endregion //#region ../../src/server/core/schemas/okSchema.d.ts declare const okSchema: import("zod").ZodObject<{ ok: import("zod").ZodBoolean; id: import("zod").ZodOptional<import("zod").ZodUnion<readonly [import("zod").ZodString, import("zod").ZodInt]>>; count: import("zod").ZodOptional<import("zod").ZodNumber>; }, import("zod/v4/core").$strip>; type Ok = Static<typeof okSchema>; //#endregion //#region ../../src/server/core/primitives/$circuit.d.ts interface CircuitBreakerOptions { /** * Consecutive failures before opening the circuit. */ threshold: number; /** * Cooldown before transitioning from open to half-open. */ reset: DurationLike; } /** * Middleware that implements the circuit breaker pattern. * * Three states: * - **Closed** (normal) — calls pass through. Failures are counted. * - **Open** (tripped) — calls are immediately rejected. No handler execution. * - **Half-open** (probing) — one call is allowed. Success closes, failure re-opens. * * ```typescript * class PaymentController { * charge = $action({ * use: [$circuit({ threshold: 5, reset: [30, "seconds"] })], * handler: async ({ body }) => this.paymentGateway.charge(body), * }); * } * ``` */ declare const $circuit: (options: CircuitBreakerOptions) => Middleware; //#endregion //#region ../../src/server/core/primitives/$middleware.d.ts declare const $middleware: { (options: ServerMiddlewarePrimitiveOptions): ServerMiddlewarePrimitive; [KIND]: typeof ServerMiddlewarePrimitive; }; interface ServerMiddlewarePrimitiveOptions { /** * Path prefix. Middleware applies to all routes starting with this path. * * @example "/api" — matches "/api/users", "/api/orders", etc. */ path: string; /** * Middleware functions to apply to matching routes. */ use: Middleware[]; /** * Limit middleware to specific HTTP methods. * If not set, middleware applies to all methods. */ method?: RouteMethod | RouteMethod[]; /** * Exclude specific route paths from middleware application. */ exclude?: string[]; } declare class ServerMiddlewarePrimitive extends Primitive<ServerMiddlewarePrimitiveOptions> { protected readonly serverRouterProvider: ServerRouterProvider; protected onInit(): void; } //#endregion //#region ../../src/server/core/primitives/$route.d.ts /** * Create a basic endpoint. * * It's a low level primitive. You probably want to use `$action` instead. * * @see {@link $action} * @see {@link $page} */ declare const $route: { <TConfig extends RequestConfigSchema>(options: RoutePrimitiveOptions<TConfig>): RoutePrimitive<TConfig>; [KIND]: typeof RoutePrimitive; }; interface RoutePrimitiveOptions<TConfig extends RequestConfigSchema = RequestConfigSchema> extends Omit<ServerRoute<TConfig>, "handler">, PipelinePrimitiveOptions<ServerHandler<TConfig>> {} declare class RoutePrimitive<TConfig extends RequestConfigSchema> extends PipelinePrimitive<RoutePrimitiveOptions<TConfig>> { protected readonly serverRouterProvider: ServerRouterProvider; protected onInit(): void; /** * Invoke this route's handler in-process (no HTTP server) and return its path * and body. Used by the build to snapshot `static` routes to files. * * Only meaningful for self-contained `GET` handlers that return a string or * Buffer (e.g. `sitemap.xml`, `robots.txt`). */ prerender(): Promise<{ path: string; body: string | Buffer; }>; } //#endregion //#region ../../src/server/core/primitives/$sse.d.ts /** * Schema configuration for an SSE endpoint. */ interface SseConfigSchema { /** * Request body schema. */ body?: TObject; /** * Path parameters schema. */ params?: TObject; /** * Query parameters schema. */ query?: TObject; /** * Request headers schema. */ headers?: TObject; /** * Schema for the data payload of each SSE event. */ data?: TSchema; } /** * Context object passed to the SSE handler function. */ interface SseHandlerContext<TConfig extends SseConfigSchema> { /** * Parsed request body. */ body: TConfig["body"] extends TObject ? Static<TConfig["body"]> : any; /** * Parsed path parameters. */ params: TConfig["params"] extends TObject ? Static<TConfig["params"]> : Record<string, string>; /** * Parsed query parameters. */ query: TConfig["query"] extends TObject ? Partial<Static<TConfig["query"]>> : Record<string, any>; /** * Parsed request headers. */ headers: TConfig["headers"] extends TObject ? Static<TConfig["headers"]> : Record<string, string>; /** * The underlying server request object. */ request: ServerRequest; /** * Emit an SSE event to the client. */ emit: (data: TConfig["data"] extends TSchema ? Static<TConfig["data"]> : any) => void; /** * Close the SSE stream. */ close: () => void; } /** * Handler function type for SSE endpoints. */ type SseHandler<TConfig extends SseConfigSchema = SseConfigSchema> = (context: SseHandlerContext<TConfig>) => Async<void>; /** * Options for the $sse primitive. */ interface SsePrimitiveOptions<TConfig extends SseConfigSchema> extends PipelinePrimitiveOptions { /** * Name of the SSE endpoint. */ name?: string; /** * Group SSE endpoints together. */ group?: string; /** * Pathname of the route. If not provided, property key is used. */ path?: string; /** * The config schema for the SSE endpoint. */ schema?: TConfig; /** * A short description of the endpoint. Used for documentation purposes. */ description?: string; /** * Disable the SSE endpoint. */ disabled?: boolean; /** * Main SSE handler. Receives context with emit/close functions. */ handler: SseHandler<TConfig>; } /** * Async iterable stream of SSE events. * * Supports push-based event delivery via `push()`, error propagation * via `fail()`, and clean termination via `end()`. */ declare class SseStream<T> implements AsyncIterable<T> { protected queue: T[]; protected error: Error | null; protected done: boolean; protected resolve: (() => void) | null; protected listeners: Array<(data: T) => void>; /** * Push a new event into the stream. */ push(data: T): void; /** * Signal an error on the stream. */ fail(error: Error): void; /** * End the stream gracefully. */ end(): void; /** * Subscribe to new events as they arrive. */ subscribe(listener: (data: T) => void): () => void; [Symbol.asyncIterator](): AsyncIterator<T>; } /** * Response wrapper for SSE fetch requests. * * Wraps a standard `Response` and parses the `text/event-stream` body * into an async iterable of typed events. */ declare class SseFetchResponse<T> implements AsyncIterable<T> { readonly response: Response; constructor(response: Response); /** * HTTP status code of the response. */ get status(): number; /** * HTTP status text of the response. */ get statusText(): string; /** * Response headers. */ get headers(): Headers; [Symbol.asyncIterator](): AsyncIterator<T>; } /** * Creates a Server-Sent Events (SSE) primitive for streaming typed events to clients. * * SSE endpoints provide a unidirectional stream from server to client over HTTP, * with full type safety for event data. The handler receives `emit()` and `close()` * functions to control the stream. * * **Key Features** * - Full TypeScript inference for event data types * - Automatic schema validation using TypeBox * - Convention-based URL generation with customizable paths * - Direct invocation (`run()`) returns an `SseStream` async iterable * - HTTP requests (`fetch()`) returns an `SseFetchResponse` async iterable * - Built-in `text/event-stream` content-type handling * * **URL Generation** * * All `$sse` paths are automatically prefixed with `/api`. * * ```ts * $sse({ path: "/events" }) // POST /api/events * $sse({ path: "/feed/:id" }) // POST /api/feed/:id * ``` * * The HTTP method is always POST. * * @example * ```ts * class NotificationController { * events = $sse({ * schema: { * data: z.object({ * type: z.text(), * message: z.text(), * }), * }, * handler: async ({ emit, close }) => { * emit({ type: "welcome", message: "Connected!" }); * // ... stream events ... * close(); * }, * }); * } * ``` */ declare const $sse: { <TConfig extends SseConfigSchema>(options: SsePrimitiveOptions<TConfig>): SsePrimitiveFn<TConfig>; [KIND]: typeof SsePrimitive; }; /** * The SSE primitive class extending PipelinePrimitive. * * Registers a POST route that streams `text/event-stream` responses. * Supports direct invocation via `run()` and HTTP via `fetch()`. */ declare class SsePrimitive<TConfig extends SseConfigSchema> extends PipelinePrimitive<SsePrimitiveOptions<TConfig>> { protected readonly log: import("alepha/logger").Logger; protected readonly settings: Readonly<{ prefix: string; }>; protected readonly serverProvider: ServerProvider; protected readonly serverRouterProvider: ServerRouterProvider; protected onInit(): void; /** * Returns the /api prefix. */ get prefix(): string; /** * Returns the name of the SSE endpoint. */ get name(): string; /** * Returns the group of the SSE endpoint. */ get group(): string; /** * Returns the HTTP method. SSE always uses POST. */ get method(): RouteMethod; /** * Returns the path of the SSE endpoint. */ get path(): string; /** * Returns the schema configuration. */ get schema(): TConfig | undefined; /** * Constructs a RequestConfigSchema from the SSE config for route registration. */ protected get requestConfigSchema(): RequestConfigSchema | undefined; /** * Call the SSE handler directly and return a typed async iterable stream. * There is no HTTP layer involved. */ run(config?: SseRequestEntry<TConfig>): SseStream<SseEventData<TConfig>>; /** * Works like `run`, but always fetches (http request) the route. * Returns an `SseFetchResponse` that can be async-iterated for typed events. */ fetch(config?: SseRequestEntry<TConfig>): Promise<SseFetchResponse<SseEventData<TConfig>>>; /** * HTTP handler for the registered route. * Returns a ReadableStream with SSE-formatted events. */ protected httpHandler(request: ServerRequest): ReadableStream; /** * Build the fetch URL with path variables and query params. */ protected buildFetchUrl(host: string, config?: SseRequestEntry<TConfig>): string; } /** * Combined callable + SsePrimitive interface. */ interface SsePrimitiveFn<TConfig extends SseConfigSchema> extends SsePrimitive<TConfig> { (config?: SseRequestEntry<TConfig>): SseStream<SseEventData<TConfig>>; } /** * Infer the event data type from an SSE config schema. */ type SseEventData<TConfig extends SseConfigSchema> = TConfig["data"] extends TSchema ? Static<TConfig["data"]> : any; /** * Request entry type for SSE endpoints (body, params, query, headers). */ type SseRequestEntry<TConfig extends SseConfigSchema, T = SseRequestEntryContainer<TConfig>> = { [K in keyof T as T[K] extends undefined ? never : K]: T[K]; }; /** * Full container type for SSE request entries. */ type SseRequestEntryContainer<TConfig extends SseConfigSchema> = { body: TConfig["body"] extends TObject ? Static<TConfig["body"]> : undefined; params: TConfig["params"] extends TObject ? Static<TConfig["params"]> : undefined; headers?: TConfig["headers"] extends TObject ? Static<TConfig["headers"]> : Record<string, string>; query?: TConfig["query"] extends TObject ? Partial<Static<TConfig["query"]>> : Record<string, string>; }; //#endregion //#region ../../src/server/core/providers/BunHttpServerProvider.d.ts declare const envSchema$1: import("zod").ZodObject<{ SERVER_PORT: import("zod").ZodDefault<import("zod").ZodInt>; SERVER_HOST: import("zod").ZodString; }, import("zod/v4/core").$strip>; declare module "alepha" { interface Env extends Partial<Static<typeof envSchema$1>> {} } declare class BunHttpServerProvider extends ServerProvider { protected readonly alepha: Alepha; protected readonly dateTimeProvider: DateTimeProvider; protected readonly log: import("alepha/logger").Logger; protected readonly env: { SERVER_PORT: number; SERVER_HOST: string; }; protected readonly router: ServerRouterProvider; protected bunServer?: ReturnType<typeof Bun.serve>; get hostname(): string; readonly start: import("alepha").HookPrimitive<"start">; protected readonly stop: import("alepha").HookPrimitive<"stop">; protected listen(): Promise<void>; protected close(): Promise<void>; } //#endregion //#region ../../src/server/core/providers/NodeHttpServerProvider.d.ts declare const envSchema: import("zod").ZodObject<{ SERVER_PORT: import("zod").ZodDefault<import("zod").ZodInt>; SERVER_HOST: import("zod").ZodString; }, import("zod/v4/core").$strip>; declare module "alepha" { interface Env extends Partial<Static<typeof envSchema>> {} } declare class NodeHttpServerProvider extends ServerProvider { protected readonly alepha: Alepha; protected readonly dateTimeProvider: DateTimeProvider; protected readonly log: import("alepha/logger").Logger; protected readonly env: { SERVER_PORT: number; SERVER_HOST: string; }; protected readonly router: ServerRouterProvider; /** * Track active connections for fast shutdown. */ protected readonly connections: Set<Socket>; /** * Get number of active connections. */ getConnectionsCount(): number; /** * Server options. */ readonly options: { /** * Graceful shutdown timeout in ms. * After this, remaining connections are forcefully closed. * @default 30000 */ shutdownTimeout: number; }; get hostname(): string; protected readonly handleRequestError: (res: ServerResponse$1, err: Error) => void; server: Server; readonly configure: import("alepha").HookPrimitive<"configure">; readonly start: import("alepha").HookPrimitive<"start">; protected requestListener: (req: IncomingMessage, res: ServerResponse$1) => void; protected connectionListener: (socket: Socket) => void; protected createHttpServer(): Server; protected readonly stop: import("alepha").HookPrimitive<"stop">; protected listen(): Promise<void>; protected close(): Promise<void>; protected destroyAllConnections(): void; } //#endregion //#region ../../src/server/core/providers/ServerCompressProvider.d.ts /** * Compression configuration atom. */ declare const compressOptions: import("alepha").Atom<import("zod").ZodObject<{ disabled: import("zod").ZodOptional<import("zod").ZodBoolean>; allowedContentTypes: import("zod").ZodArray<import("zod").ZodString>; }, import("zod/v4/core").$strip>, "alepha.server.compress.options">; type CompressOptions = Static<typeof compressOptions.schema>; declare module "alepha" { interface State { [compressOptions.key]: CompressOptions; } } declare class ServerCompressProvider { static compressors: Record<string, { compress: (...args: any[]) => Promise<Buffer>; stream: (options?: any) => Transform; } | undefined>; protected readonly alepha: Alepha; protected readonly options: Readonly<{ disabled?: boolean | undefined; allowedContentTypes: string[]; }>; readonly onResponse: import("alepha").HookPrimitive<"server:onResponse">; protected isAllowedContentType(contentType: string | undefined): boolean; protected compress(encoding: keyof typeof ServerCompressProvider.compressors, response: ServerResponse$2): Promise<void>; /** * Create a compressed stream that flushes after each chunk. * This is essential for streaming SSR - ensures each chunk is sent immediately. */ protected createFlushingCompressStream(input: ReadableStream$1, createCompressor: (options?: any) => Transform, encoding: string, params: Record<number, any>): ReadableStream$1<Uint8Array>; protected getParams(encoding: keyof typeof ServerCompressProvider.compressors): Record<number, any>; protected setHeaders(response: ServerResponse$2, encoding: keyof typeof ServerCompressProvider.compressors): void; } //#endregion //#region ../../src/server/core/providers/ServerHelmetProvider.d.ts /** * Helmet security headers configuration atom */ declare const helmetOptions: import("alepha").Atom<import("zod").ZodObject<{ disabled: import("zod").ZodOptional<import("zod").ZodBoolean>; isSecure: import("zod").ZodOptional<import("zod").ZodBoolean>; strictTransportSecurity: import("zod").ZodOptional<import("zod").ZodObject<{ maxAge: import("zod").ZodOptional<import("zod").ZodNumber>; includeSubDomains: import("zod").ZodOptional<import("zod").ZodBoolean>; preload: import("zod").ZodOptional<import("zod").ZodBoolean>; }, import("zod/v4/core").$strip>>; xContentTypeOptions: import("zod").ZodOptional<import("zod").ZodBoolean>; xFrameOptions: import("zod").ZodOptional<import("zod").ZodEnum<{ DENY: "DENY"; SAMEORIGIN: "SAMEORIGIN"; }>>; xXssProtection: import("zod").ZodOptional<import("zod").ZodBoolean>; contentSecurityPolicy: import("zod").ZodOptional<import("zod").ZodObject<{ directives: import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodAny>; }, import("zod/v4/core").$strip>>; referrerPolicy: import("zod").ZodOptional<import("zod").ZodEnum<{ "no-referrer": "no-referrer"; "no-referrer-when-downgrade": "no-referrer-when-downgrade"; origin: "origin"; "origin-when-cross-origin": "origin-when-cross-origin"; "same-origin": "same-origin"; "strict-origin": "strict-origin"; "strict-origin-when-cross-origin": "strict-origin-when-cross-origin"; "unsafe-url": "unsafe-url"; }>>; }, import("zod/v4/core").$strip>, "alepha.server.helmet.options">; type HelmetOptions = Static<typeof helmetOptions.schema>; declare module "alepha" { interface State { [helmetOptions.key]: HelmetOptions; } } type CspDirective = string | string[]; interface CspDirectives { "default-src"?: CspDirective; "script-src"?: CspDirective; "style-src"?: CspDirective; "img-src"?: CspDirective; "connect-src"?: CspDirective; "font-src"?: CspDirective; "object-src"?: CspDirective; "media-src"?: CspDirective; "frame-src"?: CspDirective; sandbox?: CspDirective | boolean; "report-uri"?: string; "child-src"?: CspDirective; "form-action"?: CspDirective; "frame-ancestors"?: CspDirective; "plugin-types"?: CspDirective; "base-uri"?: CspDirective; [key: string]: CspDirective | undefined | boolean; } interface CspOptions { directives: CspDirectives; } interface HstsOptions { maxAge?: number; includeSubDomains?: boolean; preload?: boolean; } /** * Provides a configurable way to apply essential HTTP security headers * to every se