UNPKG

@rabbit-company/web

Version:
1,290 lines (1,288 loc) 38.3 kB
/** * Middleware function type that processes requests and can return responses. * Middleware functions receive a context object and a next function to call the next middleware in the chain. * * @template T - The type of the context state object * @param ctx - Context object containing request data and helper methods * @param next - Function to call the next middleware in the chain * @returns Response object, Promise resolving to Response, or void to continue to next middleware * * @example * ```typescript * const authMiddleware: Middleware<{ user: User }> = async (ctx, next) => { * const token = ctx.req.headers.get('authorization'); * if (!token) { * return ctx.json({ error: 'Unauthorized' }, 401); * } * * const user = await verifyToken(token); * ctx.set('user', user); * await next(); // Continue to next middleware/handler * }; * ``` */ export type Middleware<T extends Record<string, unknown> = Record<string, unknown>, B extends Record<string, unknown> = Record<string, unknown>> = (ctx: Context<T, B>, next: Next) => Response | Promise<Response | void>; /** * Function type for calling the next middleware in the chain. * Returns a Promise that resolves to a Response or void. * * @returns Promise that resolves when the next middleware completes * * @example * ```typescript * const loggingMiddleware: Middleware = async (ctx, next) => { * console.log(`${ctx.req.method} ${ctx.req.url} - Started`); * const startTime = Date.now(); * * await next(); // Call next middleware * * const duration = Date.now() - startTime; * console.log(`${ctx.req.method} ${ctx.req.url} - Completed in ${duration}ms`); * }; * ``` */ export type Next = () => Promise<Response | void>; /** * Interface representing a node in the trie data structure used for efficient route matching. * Each node can have static children, parameter children, or wildcard children. * * @template T - The type of the context state object * * @example * ```typescript * // Internal structure for route '/users/:id/posts' * // Root -> 'users' (static) -> ':id' (param) -> 'posts' (static) * ``` */ export interface TrieNode<T extends Record<string, unknown> = Record<string, unknown>, B extends Record<string, unknown> = Record<string, unknown>> { /** Map of static path segments to their corresponding child nodes */ children: Map<string, TrieNode<T, B>>; /** Parameter child node for capturing dynamic segments (e.g., ':id') */ paramChild?: TrieNode<T, B>; /** Name of the parameter without the ':' prefix */ paramName?: string; /** Wildcard child node for matching remaining path segments ('*') */ wildcardChild?: TrieNode<T, B>; /** Array of middleware handlers to execute when this node represents a complete route */ handlers?: Middleware<T, B>[]; } /** * HTTP methods supported by the framework. * * @example * ```typescript * const method: Method = 'GET'; * app.addRoute(method, '/users', handler); * ``` */ export type Method = "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "OPTIONS" | "HEAD"; /** * Result of attempting to match a URL path against a route pattern. * * @example * ```typescript * const result: MatchResult = { * matched: true, * params: { id: '123', category: 'electronics' } * }; * ``` */ export type MatchResult = { matched: boolean; params: Record<string, string>; }; /** * Internal representation of middleware with matching logic. * Used to determine which middleware should run for a given request. * * @template T - The type of the context state object */ export type MiddlewareRoute<T extends Record<string, unknown> = Record<string, unknown>, B extends Record<string, unknown> = Record<string, unknown>> = { /** Optional HTTP method this middleware applies to */ method?: Method; /** Optional path pattern this middleware applies to */ path?: string; /** Static prefix of the path for optimization */ pathPrefix?: string; /** Function to determine if this middleware matches a given URL */ match: (url: string) => MatchResult; /** The middleware handler function */ handler: Middleware<T, B>; }; /** * Context object passed to middleware and route handlers containing request data and helper methods. * The context provides a convenient API for handling common web operations like parsing request bodies, * setting response headers, and managing application state. * * @template T - The type of the context state object for sharing data between middleware * * @example * ```typescript * interface AppState { * user: User; * requestId: string; * } * * const handler: Middleware<AppState> = async (ctx) => { * // Access request data * const userId = ctx.params.id; * const query = ctx.query(); * * // Parse request body * const body = await ctx.body<CreateUserRequest>(); * * // Set state * ctx.set('requestId', generateId()); * * // Return response * return ctx.json({ success: true }); * }; * ``` */ export interface Context<T extends Record<string, unknown> = Record<string, unknown>, B extends Record<string, unknown> = Record<string, unknown>> { /** The original Request object */ req: Request; /** Optional Response object (for advanced use cases) */ res?: Response; /** Object containing URL parameters extracted from the route path */ params: Record<string, string>; /** Application state object for sharing data between middleware */ state: T; /** Only present in Cloudflare Workers */ env: B; /** Client IP address, populated by web server or `ip-extract` middleware */ clientIp?: string; /** * Returns a plain text response. * * @param body - Text content for the response body * @param status - HTTP status code (default: 200) * @param headers - Additional headers to include * @returns Response object with text content * * @example * ```typescript * return ctx.text('Hello World'); * return ctx.text('Not Found', 404); * return ctx.text('Created', 201, { 'X-Custom': 'value' }); * ``` */ text: (body: string | null | undefined, status?: number, headers?: Record<string, string>) => Response; /** * Context method to send a JSON response with proper headers. * * @param data - Data to be serialized as JSON * @param status - HTTP status code (default: 200) * @param headers - Additional headers to include * @returns {Response} Configured Response object * * @example * ```typescript * // Basic response * return ctx.json({ message: 'Success' }); * * // With status code * return ctx.json({ error: 'Not found' }, 404); * * // With custom headers * return ctx.json( * { data }, * 200, * { 'Cache-Control': 'max-age=3600' } * ); * * // With TypeScript type safety * return ctx.json<ApiResponse<User>>({ * status: 'success', * data: user * }); * ``` */ json: (data: unknown, status?: number, headers?: Record<string, string>) => Response; /** * Returns an HTML response. * * @param html - HTML content for the response body * @param status - HTTP status code (default: 200) * @param headers - Additional headers to include * @returns Response object with HTML content * * @example * ```typescript * return ctx.html('<h1>Welcome</h1>'); * return ctx.html('<h1>Error</h1>', 500); * return ctx.html(template, 200, { 'X-Frame-Options': 'DENY' }); * ``` */ html: (html: string | null | undefined, status?: number, headers?: Record<string, string>) => Response; /** * Returns the URL search parameters as a URLSearchParams object. * * @returns URLSearchParams object for accessing query parameters * * @example * ```typescript * // For URL: /users?page=2&limit=10 * const query = ctx.query(); * const page = query.get('page'); // '2' * const limit = query.get('limit'); // '10' * ``` */ query: () => URLSearchParams; /** * Context method to parse and return the request body as JSON. * Automatically handles content-type detection and parsing. * * @template U - Type of the expected response body * @returns {Promise<U>} Parsed request body * * @example * ```typescript * // Basic usage * const user = await ctx.body<User>(); * * // With error handling * try { * const data = await ctx.body<CreateUserRequest>(); * } catch (err) { * return ctx.json({ error: 'Invalid JSON' }, 400); * } * * // With validation * const raw = await ctx.body<unknown>(); * const valid = userSchema.parse(raw); * ``` */ body: <T>() => Promise<T>; /** * Sets a response header. * * @param name - Header name * @param value - Header value * * @example * ```typescript * ctx.header('X-Custom-Header', 'value'); * ``` */ header: (name: string, value: string) => void; /** * Sets a value in the context state. * * @template K - Key type from the state object * @param key - The state key to set * @param value - The value to set * * @example * ```typescript * ctx.set('user', currentUser); * ctx.set('requestId', uuid()); * ``` */ set: <K extends keyof T>(key: K, value: T[K]) => void; /** * Gets a value from the context state. * * @template K - Key type from the state object * @param key - The state key to retrieve * @returns The value associated with the key * * @example * ```typescript * const user = ctx.get('user'); * const requestId = ctx.get('requestId'); * ``` */ get: <K extends keyof T>(key: K) => T[K]; /** * Returns a redirect response. * * @param url - URL to redirect to * @param status - HTTP status code for redirect (default: 302) * @returns Response object with redirect headers * * @example * ```typescript * return ctx.redirect('/login'); * return ctx.redirect('https://example.com', 301); * ``` */ redirect: (url: string, status?: number) => Response; } /** * Internal representation of a route with its matching logic and handlers. * * @template T - The type of the context state object */ export interface Route<T extends Record<string, unknown> = Record<string, unknown>, B extends Record<string, unknown> = Record<string, unknown>> { /** HTTP method for this route */ method: Method; /** Path pattern for this route */ path: string; /** Function to match URLs against this route pattern */ match: (url: string) => { matched: boolean; params: Record<string, string>; }; /** Array of middleware handlers for this route */ handlers: Middleware<T, B>[]; } /** * Server instance returned by the listen method. * Provides a unified interface for controlling servers across different runtimes. */ export interface Server { /** The port number the server is listening on */ port: number; /** The hostname/IP address the server is bound to */ hostname: string; /** The runtime name ('bun', 'deno', 'node' or 'cloudflare-workers') */ runtime: "bun" | "deno" | "node" | "cloudflare-workers"; /** * Stops the server gracefully. * @returns {Promise<void>} Promise that resolves when the server is fully stopped */ stop(): Promise<void>; /** The underlying runtime-specific server instance */ instance: unknown; } /** * Node.js specific server options for HTTP/HTTPS configuration. */ export interface NodeServerOptions { /** * Enable HTTPS server instead of HTTP. * @default false */ https?: boolean; /** * TLS private key for HTTPS server. * Required when https is true. */ key?: string | Buffer; /** * TLS certificate for HTTPS server. * Required when https is true. */ cert?: string | Buffer; } /** * Deno specific server options. */ export interface DenoServerOptions { /** TLS private key file path or content */ key?: string; /** TLS certificate file path or content */ cert?: string; /** * Application-Layer Protocol Negotiation protocols. * @example ['h2', 'http/1.1'] */ alpnProtocols?: string[]; } /** * Bun specific TLS configuration. */ export interface BunTlsOptions { /** TLS private key */ key?: string | Buffer | Array<string | Buffer>; /** TLS certificate */ cert?: string | Buffer | Array<string | Buffer>; /** TLS certificate authority */ ca?: string | Buffer | Array<string | Buffer>; /** Passphrase for the private key */ passphrase?: string; /** Diffie-Hellman parameters */ dhParamsFile?: string; /** Minimum TLS version */ secureOptions?: number; } /** * Bun specific server options. */ export interface BunServerOptions { /** TLS configuration for HTTPS */ tls?: BunTlsOptions; /** * Maximum allowed request body size in bytes. * @default 128 * 1024 * 1024 (128MB) */ maxRequestBodySize?: number; /** WebSocket handler configuration */ websocket?: unknown; /** Server name for the Server header */ serverName?: string; /** Enable HTTP/2 support */ reusePort?: boolean; } /** * Callback function invoked when the server starts listening. */ export type ListenCallback = (info: { /** The port the server is listening on */ port: number; /** The hostname the server is bound to */ hostname: string; /** The runtime name */ runtime: "bun" | "deno" | "node" | "cloudflare-workers"; }) => void; /** * Configuration options for starting a server with the listen method. */ export interface ListenOptions { /** * Port number to listen on. * @default 3000 */ port?: number; /** * Hostname or IP address to bind to. * Use '0.0.0.0' to listen on all interfaces. * @default 'localhost' */ hostname?: string; /** * Callback function invoked when the server starts successfully. * Receives server information including port, hostname, and runtime. */ onListen?: ListenCallback; /** * Node.js specific server options. * Only used when running in Node.js runtime. */ node?: NodeServerOptions; /** * Deno specific server options. * Only used when running in Deno runtime. */ deno?: DenoServerOptions; /** * Bun specific server options. * Only used when running in Bun runtime. */ bun?: BunServerOptions; } /** * Type definition for Node.js HTTP/HTTPS server instance. * @internal */ export interface NodeServerInstance { listen(port: number, hostname: string, callback?: () => void): void; close(callback?: (err?: Error) => void): void; on(event: string, listener: (...args: any[]) => void): void; off(event: string, listener: (...args: any[]) => void): void; } /** * Type definition for Deno server instance. * @internal */ export interface DenoServerInstance { finished: Promise<void>; shutdown(): Promise<void>; } /** * Type definition for Bun server instance. * @internal */ export interface BunServerInstance { port: number; hostname?: string; stop(): void; } /** * High-performance web framework with trie-based routing, middleware support, and extensive caching. * * Features: * - Fast trie-based route matching * - Middleware support with method and path filtering * - Route scoping and sub-applications * - Built-in caching for improved performance * - Support for all standard HTTP methods * - Parameter extraction and wildcard routes * - Dynamic route and middleware removal * - Custom error and 404 handlers * * @template T - The type of the context state object that will be shared across middleware * * @example * ```typescript * const app = new Web<{ user: User }>(); * * app.get('/users/:id', async (ctx, next) => { * const user = await getUser(ctx.params.id); * ctx.set('user', user); * return ctx.json(user); * }); * * app.use('/admin', async (ctx, next) => { * // Authentication middleware * if (!ctx.get('user')?.isAdmin) { * return ctx.json({ error: 'Unauthorized' }, 401); * } * await next(); * }); * ``` */ export declare class Web<T extends Record<string, unknown> = Record<string, unknown>, B extends Record<string, unknown> = Record<string, unknown>> { /** Array of all registered routes */ private routes; /** Array of all registered middleware */ private middlewares; /** Cache for method-specific middleware to avoid filtering on each request */ private methodMiddlewareCache; /** Cache for parsed URLs to avoid repeated parsing */ private urlCache; /** Cache for path segments to avoid repeated splitting */ private segmentCache; /** Cache for compiled route matchers */ private matcherCache; /** Cache for frequently matched routes */ private routeMatchCache; /** Counter for generating unique IDs */ private idCounter; /** Trie roots for each HTTP method for fast route matching */ private roots; /** * Creates a new Web framework instance */ constructor(); /** * Generates a unique ID for routes and middleware * @private */ private generateId; /** * Clears all internal caches. Called automatically when routes or middleware are modified. * @private */ private clearCaches; /** * Rebuilds the trie structure from scratch. Used after route removal. * @private */ private rebuildTrie; /** * Adds a route to the trie structure (internal method) * @private */ private addRouteToTrie; /** Error handler function for handling uncaught errors */ private errorHandler?; /** 404 Not Found handler function */ private notFoundHandler?; /** * Sets a global error handler for the application. * This handler will be called whenever an unhandled error occurs during request processing. * * @param handler - Function that takes an error and context, returns a Response * @returns The Web instance for method chaining * * @example * ```typescript * app.onError((err, ctx) => { * console.error('Application error:', err); * return ctx.json({ error: 'Internal Server Error' }, 500); * }); * ``` */ onError(handler: (err: Error, ctx: Context<T, B>) => Response | Promise<Response>): this; /** * Sets a custom 404 Not Found handler for the application. * This handler will be called whenever a request doesn't match any registered routes. * * @param handler - Function that takes a context and returns a Response * @returns The Web instance for method chaining * * @example * ```typescript * // Simple text response * app.onNotFound((ctx) => { * return ctx.text('Page not found', 404); * }); * * // JSON response with request details * app.onNotFound((ctx) => { * return ctx.json({ * error: 'Not Found', * path: ctx.req.url, * method: ctx.req.method * }, 404); * }); * * // HTML response with custom 404 page * app.onNotFound((ctx) => { * return ctx.html(` * <!DOCTYPE html> * <html> * <head> * <title>404 - Page Not Found</title> * <style> * body { font-family: Arial, sans-serif; text-align: center; padding: 50px; } * h1 { color: #ff6b6b; } * </style> * </head> * <body> * <h1>404 - Page Not Found</h1> * <p>The page you're looking for doesn't exist.</p> * <a href="/">Go back home</a> * </body> * </html> * `, 404); * }); * ``` */ onNotFound(handler: (ctx: Context<T, B>) => Response | Promise<Response>): this; /** * Splits a path into segments and caches the result for performance. * * @param path - The URL path to split * @returns Array of path segments (empty segments filtered out) * @private */ private getPathSegments; /** * Parses a URL into pathname and search parameters with caching for performance. * Handles both absolute and relative URLs. * * @param url - The URL to parse * @returns Object containing pathname and optional URLSearchParams * @private */ private parseUrl; /** * Registers middleware that will run for matching requests. * Middleware can be global, path-specific, or method and path specific. * * @param args - Variable arguments for different middleware registration patterns: * - `[handler]` - Global middleware that runs for all requests * - `[path, handler]` - Path-specific middleware * - `[method, path, handler]` - Method and path-specific middleware * @returns The Web instance for method chaining * * @example * ```typescript * // Global middleware * app.use(async (ctx, next) => { * console.log(`${ctx.req.method} ${ctx.req.url}`); * await next(); * }); * * // Path-specific middleware * app.use('/api', async (ctx, next) => { * ctx.set('apiVersion', '1.0'); * await next(); * }); * * // Method and path-specific middleware * app.use('POST', '/users', async (ctx, next) => { * // Validate request body * await next(); * }); * ``` */ use(...args: [ Middleware<T, B> ] | [ string, Middleware<T, B> ] | [ Method, string, Middleware<T, B> ]): this; /** * Removes middleware by its ID. * * @param id - The middleware ID returned from the use() method * @returns true if middleware was found and removed, false otherwise * * @example * ```typescript * const middlewareId = app.use('/api', authMiddleware); * * // Later remove it * const removed = app.removeMiddleware(middlewareId); * console.log(removed ? 'Middleware removed' : 'Middleware not found'); * ``` */ removeMiddleware(id: string): boolean; /** * Removes all middleware matching the given criteria. * * @param criteria - Object with optional method and/or path to match * @returns Number of middleware items removed * * @example * ```typescript * // Remove all middleware for a specific path * const removed = app.removeMiddlewareBy({ path: '/api' }); * * // Remove all POST middleware * app.removeMiddlewareBy({ method: 'POST' }); * * // Remove specific method and path combination * app.removeMiddlewareBy({ method: 'GET', path: '/users' }); * ``` */ removeMiddlewareBy(criteria: { method?: Method; path?: string; }): number; /** * Adds middleware using the specified pattern and returns an ID for later removal. * This is an alternative to use() that returns an ID instead of the Web instance. * * @param args - Variable arguments for different middleware registration patterns * @returns The middleware ID for later removal * * @example * ```typescript * // Global middleware * const globalId = app.addMiddleware(async (ctx, next) => { * console.log(`${ctx.req.method} ${ctx.req.url}`); * await next(); * }); * * // Path-specific middleware * const pathId = app.addMiddleware('/api', async (ctx, next) => { * ctx.set('apiVersion', '1.0'); * await next(); * }); * * // Remove middleware later * app.removeMiddleware(globalId); * ``` */ addMiddleware(...args: [ Middleware<T, B> ] | [ string, Middleware<T, B> ] | [ Method, string, Middleware<T, B> ]): string; /** * Gets all registered middleware with their IDs and metadata. * * @returns Array of middleware information objects * * @example * ```typescript * const middlewares = app.getMiddlewares(); * middlewares.forEach(mw => { * console.log(`ID: ${mw.id}, Method: ${mw.method || 'ALL'}, Path: ${mw.path || 'ALL'}`); * }); * ``` */ getMiddlewares(): Array<{ id: string; method?: Method; path?: string; }>; /** * Gets or creates a cached matcher function for the given path pattern * @private */ private getCachedMatcher; /** * Adds a route with the specified method, path, and handlers to the trie structure. * * @param method - HTTP method (GET, POST, etc.) * @param path - URL path pattern (supports :param and * wildcards) * @param handlers - One or more middleware handlers for this route * @returns The route ID for later removal * * @example * ```typescript * const routeId = app.addRoute('GET', '/users/:id', async (ctx) => { * return ctx.json({ id: ctx.params.id }); * }); * * // Remove it later * app.removeRoute(routeId); * ``` */ addRoute(method: Method, path: string, ...handlers: Middleware<T, B>[]): string; /** * Removes a route by its ID. * * @param id - The route ID returned from route registration methods * @returns true if route was found and removed, false otherwise * * @example * ```typescript * const routeId = app.get('/users/:id', getUserHandler); * * // Later remove it * const removed = app.removeRoute(routeId); * console.log(removed ? 'Route removed' : 'Route not found'); * ``` */ removeRoute(id: string): boolean; /** * Removes all routes matching the given criteria. * * @param criteria - Object with optional method and/or path to match * @returns Number of routes removed * * @example * ```typescript * // Remove all routes for a specific path * const removed = app.removeRoutesBy({ path: '/users/:id' }); * * // Remove all GET routes * app.removeRoutesBy({ method: 'GET' }); * * // Remove specific method and path combination * app.removeRoutesBy({ method: 'POST', path: '/users' }); * ``` */ removeRoutesBy(criteria: { method?: Method; path?: string; }): number; /** * Gets all registered routes with their IDs and metadata. * * @returns Array of route information objects * * @example * ```typescript * const routes = app.getRoutes(); * routes.forEach(route => { * console.log(`ID: ${route.id}, ${route.method} ${route.path}`); * }); * ``` */ getRoutes(): Array<{ id: string; method: Method; path: string; }>; /** * Removes all routes and middleware, effectively resetting the application. * * @example * ```typescript * // Clear everything and start fresh * app.clear(); * * // Now add new routes * app.get('/', handler); * ``` */ clear(): void; /** * Matches a method and path against the trie structure to find handlers and extract parameters. * * @param method - HTTP method to match * @param path - URL path to match * @returns Object with handlers and params if matched, null otherwise * * @example * ```typescript * const match = app.match('GET', '/users/123'); * if (match) { * console.log(match.params.id); // "123" * } * ``` */ match(method: Method, path: string): { handlers?: Middleware<T, B>[]; params: Record<string, string>; } | null; /** * Gets cached middleware that applies to a specific HTTP method. * * @param method - HTTP method to filter middleware for * @returns Array of middleware routes that apply to the method * @private */ private getMethodMiddlewares; /** * Creates a scoped sub-application that inherits middleware and routes with a path prefix. * Useful for organizing routes and creating modular applications. * * @param path - Base path for the scope * @param callback - Function that receives the scoped app instance to configure * @returns The Web instance for method chaining * * @example * ```typescript * app.scope('/api/v1', (api) => { * api.get('/users', async (ctx) => { * return ctx.json(await getUsers()); * }); * * api.post('/users', async (ctx) => { * const user = await ctx.body<User>(); * return ctx.json(await createUser(user)); * }); * }); * // Routes will be available at /api/v1/users * ``` */ scope(path: string, callback: (scopeApp: this) => void): this; /** * Mounts a sub-application at the specified path prefix. * All routes from the sub-application will be prefixed with the given path. * * @param prefix - Path prefix to mount the sub-application at * @param subApp - Web instance to mount * @returns The Web instance for method chaining * * @example * ```typescript * const adminApp = new Web(); * adminApp.get('/dashboard', handler); * * app.route('/admin', adminApp); * // Dashboard will be available at /admin/dashboard * ``` */ route(prefix: string, subApp: this): this; /** * Registers a GET route handler. * * @param path - URL path pattern (supports :param and * wildcards) * @param handlers - One or more middleware handlers * @returns The Web instance for method chaining * * @example * ```typescript * app.get('/users/:id', async (ctx) => { * const user = await getUserById(ctx.params.id); * return ctx.json(user); * }); * ``` */ get(path: string, ...handlers: Middleware<T, B>[]): this; /** * Registers a POST route handler. * * @param path - URL path pattern * @param handlers - One or more middleware handlers * @returns The Web instance for method chaining * * @example * ```typescript * app.post('/users', async (ctx) => { * const userData = await ctx.body<CreateUserRequest>(); * const user = await createUser(userData); * return ctx.json(user, 201); * }); * ``` */ post(path: string, ...handlers: Middleware<T, B>[]): this; /** * Registers a PUT route handler for complete resource updates. * PUT is typically used when you want to replace an entire resource. * * @param path - URL path pattern * @param handlers - One or more middleware handlers * @returns The Web instance for method chaining * * @example * ```typescript * // Basic PUT route * app.put('/users/:id', async (ctx) => { * const userData = await ctx.body<User>(); * const updatedUser = await replaceUser(ctx.params.id, userData); * return ctx.json(updatedUser); * }); * * // PUT with optimistic concurrency control * app.put('/documents/:id', * async (ctx) => { * const doc = await ctx.body<Document>(); * if (ctx.req.headers.get('If-Match') !== doc.version) { * return ctx.json({ error: 'Version mismatch' }, 412); * } * const savedDoc = await saveDocument(doc); * return ctx.json(savedDoc); * } * ); * ``` */ put(path: string, ...handlers: Middleware<T, B>[]): this; /** * Registers a DELETE route handler. * * @param path - URL path pattern * @param handlers - One or more middleware handlers * @returns The Web instance for method chaining * * @example * ```typescript * app.delete('/users/:id', async (ctx) => { * await deleteUser(ctx.params.id); * return new Response(null, { status: 204 }); * }); * ``` */ delete(path: string, ...handlers: Middleware<T, B>[]): this; /** * Registers a PATCH route handler for partial updates to resources. * PATCH is typically used when you want to update only specific fields of a resource. * * @param path - URL path pattern (supports :param and * wildcards) * @param handlers - One or more middleware handlers * @returns The Web instance for method chaining * * @example * ```typescript * // Basic PATCH route * app.patch('/users/:id', async (ctx) => { * const updates = await ctx.body<Partial<User>>(); * const updatedUser = await updateUser(ctx.params.id, updates); * return ctx.json(updatedUser); * }); * * // PATCH with validation middleware * app.patch('/articles/:id', * validateArticleUpdate, // middleware that validates the patch body * async (ctx) => { * const updates = ctx.get('validatedUpdates'); * const article = await updateArticle(ctx.params.id, updates); * return ctx.json(article); * } * ); * ``` */ patch(path: string, ...handlers: Middleware<T, B>[]): this; /** * Registers an OPTIONS route handler for CORS preflight requests. * OPTIONS requests are typically used to determine what HTTP methods * are supported for a given endpoint. * * @param path - URL path pattern * @param handlers - One or more middleware handlers * @returns The Web instance for method chaining * * @example * ```typescript * // Basic OPTIONS handler * app.options('/users', (ctx) => { * return new Response(null, { * headers: { * 'Allow': 'GET, POST, OPTIONS', * 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', * 'Access-Control-Allow-Headers': 'Content-Type' * } * }); * }); * * // Automatic CORS handling * app.options('*', (ctx) => { * return new Response(null, { * headers: { * 'Access-Control-Allow-Origin': '*', * 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS', * 'Access-Control-Allow-Headers': 'Content-Type, Authorization' * } * }); * }); * ``` */ options(path: string, ...handlers: Middleware<T, B>[]): this; /** * Registers a HEAD route handler. * HEAD responses automatically strip the response body while preserving headers and status. * * @param path - URL path pattern * @param handlers - One or more middleware handlers * @returns The Web instance for method chaining * * @example * ```typescript * app.head('/users/:id', async (ctx) => { * const exists = await userExists(ctx.params.id); * return exists ? new Response() : new Response(null, { status: 404 }); * }); * ``` */ head(path: string, ...handlers: Middleware<T, B>[]): this; /** * Creates a context object for the current request with helper methods. * * @param req - The incoming Request object * @param params - URL parameters extracted from the path * @param parsedUrl - Pre-parsed URL components * @param clientIp - Optional client IP address * @param env - Optional environment-specific bindings or variables (e.g., Cloudflare environment) * @returns Context object with request data and helper methods * @private */ private createContext; /** * Creates a 404 Not Found response using the custom handler if set. * @private */ private createNotFoundResponse; /** * Main request handler that processes incoming requests through the middleware chain and route handlers. * This method implements several optimization paths for different scenarios: * - Ultra-fast path: no middleware, no parameters, single handler * - Fast path: no middleware, might have parameters * - Full path: includes middleware processing * * @param req - The incoming Request object * @returns Promise that resolves to a Response object * * @example * ```typescript * // Use with a server * const server = Bun.serve({ * port: 3000, * fetch: app.handle, * }); * * // Or with other runtimes * addEventListener('fetch', (event) => { * event.respondWith(app.handle(event.request)); * }); * ``` */ handle(req: Request): Promise<Response>; /** * Internal handler that processes requests with a known client IP. * This is the common implementation used by both runtime-specific handlers. * * @param req - The incoming Request object * @param clientIp - The client IP address (optional) * @returns Promise that resolves to a Response object * * @internal */ private handleWithIp; /** * Request handler optimized for Bun runtime with automatic IP extraction. * Uses Bun's server request info for reliable client IP detection. * * @param req - The incoming Request object * @param server - Bun server instance * @returns Promise that resolves to a Response object * * @example * ```typescript * Bun.serve({ * port: 3000, * hostname: 'localhost', * fetch: app.handleBun * }); * ``` */ handleBun(req: Request, server: unknown): Promise<Response>; /** * Request handler optimized for Deno runtime with automatic IP extraction. * Uses Deno's ServeHandlerInfo for reliable client IP detection. * * @param req - The incoming Request object * @param info - Deno.ServeHandlerInfo instance for accessing runtime-specific features * @returns Promise that resolves to a Response object * * @example * ```typescript * Deno.serve({ * port: 3000 * }, * (req, info) => app.handleDeno(req, info) * ); * ``` */ handleDeno(req: Request, info: unknown): Promise<Response>; /** * Request handler optimized for Cloudflare Workers or Pages Functions. * Automatically extracts the client IP from the "CF-Connecting-IP" header. * * @param req - The incoming Request object from Cloudflare * @param env - Cloudflare environment bindings (type parameter B) * @param ctx - Optional execution context (used for async operations like waitUntil) * @returns Promise that resolves to a Response object * * @example * ```ts * export default { * fetch: app.handleCloudflare * }; * ``` */ handleCloudflare(req: Request, env?: unknown, ctx?: unknown): Promise<Response>; /** * Request handler for Node.js that handles both request and response. * Automatically converts between Node.js and Web APIs and extracts client IP. * * @param nodeReq - Node.js IncomingMessage object * @param nodeRes - Node.js ServerResponse object * @returns Promise that resolves when response is sent * * @example * ```typescript * import { createServer } from "http"; * * createServer((req, res) => app.handleNode(req, res)).listen(3000); * ``` */ handleNode(nodeReq: unknown, nodeRes: unknown): Promise<void>; /** * Starts a server with automatic runtime detection. * Automatically selects the appropriate server implementation based on the runtime environment. * * @param {ListenOptions} options - Server configuration options * @returns {Promise<Server>} Promise that resolves to a Server instance * @throws {Error} If the runtime is not supported * * @example * ```typescript * // Basic usage with default options * const server = await app.listen({ port: 3000 }); * console.log(`Server running on ${server.runtime} at http://localhost:${server.port}`); * * // With startup callback * await app.listen({ * port: 8080, * hostname: '0.0.0.0', * onListen: ({ port, hostname, runtime }) => { * console.log(`✅ ${runtime} server running at http://${hostname}:${port}`); * } * }); * * // With HTTPS in Node.js * await app.listen({ * port: 443, * node: { * https: true, * key: fs.readFileSync('./key.pem'), * cert: fs.readFileSync('./cert.pem') * } * }); * * // With TLS in Deno * await app.listen({ * port: 443, * deno: { * key: './key.pem', * cert: './cert.pem' * } * }); * * // With TLS in Bun * await app.listen({ * port: 443, * bun: { * tls: { * key: Bun.file('./key.pem'), * cert: Bun.file('./cert.pem') * } * } * }); * * // Gracefully stop the server * await server.stop(); * ``` */ listen(options?: ListenOptions): Promise<Server>; } export {};