alepha
Version:
Easy-to-use modern TypeScript framework for building many kind of applications.
305 lines (259 loc) • 7.59 kB
text/typescript
import type {
IncomingMessage,
ServerResponse as NodeServerResponse,
} from "node:http";
import type { Readable as NodeStream } from "node:stream";
import type { ReadableStream as NodeWebStream } from "node:stream/web";
import type {
Async,
PipelineHandler,
SchemaOutput,
StreamLike,
TArray,
TFile,
TObject,
TRecord,
TStream,
TString,
TVoid,
} from "alepha";
import type { Route } from "alepha/router";
import type { RouteMethod } from "../constants/routeMethods.ts";
import type { ServerReply } from "../helpers/ServerReply.ts";
import type { UserAgentInfo } from "../services/UserAgentParser.ts";
export type TRequestBody = TObject | TString | TArray | TRecord | TStream;
export type TResponseBody =
| TObject
| TString
| TRecord
| TFile
| TArray
| TStream
| TVoid;
export interface RequestConfigSchema {
body?: TRequestBody;
params?: TObject;
query?: TObject;
headers?: TObject;
response?: TResponseBody;
}
export 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>>;
}
export type ServerRequestConfigEntry<
TConfig extends RequestConfigSchema = RequestConfigSchema,
> = Partial<ServerRequestConfig<TConfig>>;
// ---------------------------------------------------------------------------------------------------------------------
export 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;
}
// ---------------------------------------------------------------------------------------------------------------------
export 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()`.
*/
export type ServerRouteInput<
TConfig extends RequestConfigSchema = RequestConfigSchema,
> = Omit<ServerRoute<TConfig>, "handler"> & {
handler: PipelineHandler | ServerHandler<TConfig>;
};
// ---------------------------------------------------------------------------------------------------------------------
export type ServerResponseBody<
TConfig extends RequestConfigSchema = RequestConfigSchema,
> = SchemaOutput<TConfig["response"], ResponseBodyType>;
export type ResponseKind = "json" | "text" | "void" | "file" | "any";
export type ResponseBodyType =
// not: object is not allowed, you want object ? add schema !
// biome-ignore lint/suspicious/noConfusingVoidType: handlers may return void (no return statement)
string | Buffer | StreamLike | undefined | null | void;
export type ServerHandler<
TConfig extends RequestConfigSchema = RequestConfigSchema,
> = (request: ServerRequest<TConfig>) => Async<ServerResponseBody<TConfig>>;
export interface ServerResponse {
body: string | Buffer | ArrayBuffer | NodeStream | NodeWebStream;
headers: Record<string, string>;
status: number;
}
export type ServerRouteRequestHandler = (
request: ServerRequestData,
) => Promise<ServerResponse>;
export interface ServerRouteMatcher extends Route {
handler: ServerRouteRequestHandler;
}
export 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;
}
export interface ServerRawRequest {
node?: NodeRequestEvent;
web?: WebRequestEvent;
}
export interface NodeRequestEvent {
req: IncomingMessage;
res: NodeServerResponse;
}
export interface WebRequestEvent {
req: Request;
res?: Response;
}
// ---------------------------------------------------------------------------------------------------------------------
/**
* Geolocation information from proxy headers (Cloudflare, Vercel, etc.)
*/
export 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.
*/
export interface RequestReferer {
/**
* Full referer URL.
*/
url: string;
/**
* Hostname of the referer (e.g., "google.com").
*/
hostname: string;
/**
* Path of the referer URL.
*/
pathname: string;
}