UNPKG

@worker-tools/router

Version:

A router for Worker Runtimes such as Cloudflare Workers and Service Workers.

179 lines (178 loc) 11.4 kB
import { Context } from '@worker-tools/middleware'; import type { URLPatternInit, URLPatternComponentResult, URLPatternInput, URLPatternResult } from '@worker-tools/middleware'; export type { URLPatternInit, URLPatternComponentResult, URLPatternInput, URLPatternResult }; export declare type Awaitable<T> = T | PromiseLike<T>; export interface RouteContext extends Context { /** * The match that resulted in the execution of this route. It is the full result produced by the URL Pattern API. * If you are looking for a `params`-like object similar to outer routers, use the `basics` middleware * or `match.pathname.groups`. */ match: URLPatternResult; } export interface ErrorContext extends RouteContext { /** * If the exception is well-known and caused by middleware, this property is populated with a `Response` object * with an appropriate status code and text set. * * You can use it to customize the error response, e.g.: `new Response('...', response)`. */ response: Response; /** * If an unknown error occurred, the sibling `response` property is set to be an "internal server error" while * the `error` property contains thrown error. */ error?: unknown; } export declare type Middleware<RX extends RouteContext, X extends RouteContext> = (x: Awaitable<RX>) => Awaitable<X>; export declare type Handler<X extends RouteContext> = (request: Request, ctx: X) => Awaitable<Response>; export declare type ErrorHandler<X extends ErrorContext> = (request: Request, ctx: X) => Awaitable<Response>; export declare type Method = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS'; export interface WorkerRouterOptions { fatal?: boolean; } export declare class WorkerRouter<RX extends RouteContext = RouteContext> extends EventTarget implements EventListenerObject { #private; constructor(middleware?: Middleware<RouteContext, RX> | null, opts?: WorkerRouterOptions); get fatal(): boolean; /** Add a route that matches *any* HTTP method. */ any<X extends RX>(path: string, handler: Handler<X>): this; any<X extends RX>(path: string, middleware: Middleware<RX, X>, handler: Handler<X>): this; /** Alias for for the more appropriately named `any` method */ all<X extends RX>(path: string, handler: Handler<X>): this; all<X extends RX>(path: string, middleware: Middleware<RX, X>, handler: Handler<X>): this; /** Add a route that matches the `GET` method. */ get<X extends RX>(path: string, handler: Handler<X>): this; get<X extends RX>(path: string, middleware: Middleware<RX, X>, handler: Handler<X>): this; /** Add a route that matches the `POST` method. */ post<X extends RX>(path: string, handler: Handler<X>): this; post<X extends RX>(path: string, middleware: Middleware<RX, X>, handler: Handler<X>): this; /** Add a route that matches the `PUT` method. */ put<X extends RX>(path: string, handler: Handler<X>): this; put<X extends RX>(path: string, middleware: Middleware<RX, X>, handler: Handler<X>): this; /** Add a route that matches the `PATCH` method. */ patch<X extends RX>(path: string, handler: Handler<X>): this; patch<X extends RX>(path: string, middleware: Middleware<RX, X>, handler: Handler<X>): this; /** Add a route that matches the `DELETE` method. */ delete<X extends RX>(path: string, handler: Handler<X>): this; delete<X extends RX>(path: string, middleware: Middleware<RX, X>, handler: Handler<X>): this; /** Add a route that matches the `HEAD` method. */ head<X extends RX>(path: string, handler: Handler<X>): this; head<X extends RX>(path: string, middleware: Middleware<RX, X>, handler: Handler<X>): this; /** Add a route that matches the `OPTIONS` method. */ options<X extends RX>(path: string, handler: Handler<X>): this; options<X extends RX>(path: string, middleware: Middleware<RX, X>, handler: Handler<X>): this; /** * Add a route that matches *any* method with the provided pattern. * Note that the pattern here is interpreted as a `URLPatternInit` which has important implication for matching. * Mostly, this is for use in Service Workers to intercept requests to external resources. * * The name `external` is a bit of a misnomer. It simply forwards `init` to the `URLPattern` constructor, * instead of being limited to the `pathname` property in the general case. * @deprecated Might change name/API */ external<X extends RX>(init: string | URLPatternInit, handler: Handler<X>): this; external<X extends RX>(init: string | URLPatternInit, middleware: Middleware<RX, X>, handler: Handler<X>): this; /** Like `.external`, but only matches `GET` * @deprecated Might change name/API */ externalGET<X extends RX>(init: string | URLPatternInit, handler: Handler<X>): this; externalGET<X extends RX>(init: string | URLPatternInit, middleware: Middleware<RX, X>, handler: Handler<X>): this; /** Like `.external`, but only matches `POST` * @deprecated Might change name/API */ externalPOST<X extends RX>(init: string | URLPatternInit, handler: Handler<X>): this; externalPOST<X extends RX>(init: string | URLPatternInit, middleware: Middleware<RX, X>, handler: Handler<X>): this; /** Like `.external`, but only matches `PUT` * @deprecated Might change name/API */ externalPUT<X extends RX>(init: string | URLPatternInit, handler: Handler<X>): this; externalPUT<X extends RX>(init: string | URLPatternInit, middleware: Middleware<RX, X>, handler: Handler<X>): this; /** Like `.external`, but only matches `PATCH` * @deprecated Might change name/API */ externalPATCH<X extends RX>(init: string | URLPatternInit, handler: Handler<X>): this; externalPATCH<X extends RX>(init: string | URLPatternInit, middleware: Middleware<RX, X>, handler: Handler<X>): this; /** Like `.external`, but only matches `DELETE` * @deprecated Might change name/API */ externalDELETE<X extends RX>(init: string | URLPatternInit, handler: Handler<X>): this; externalDELETE<X extends RX>(init: string | URLPatternInit, middleware: Middleware<RX, X>, handler: Handler<X>): this; /** Like `.external`, but only matches `OPTIONS` * @deprecated Might change name/API */ externalOPTIONS<X extends RX>(init: string | URLPatternInit, handler: Handler<X>): this; externalOPTIONS<X extends RX>(init: string | URLPatternInit, middleware: Middleware<RX, X>, handler: Handler<X>): this; /** Like `.external`, but only matches `HEAD` * @deprecated Might change name/API */ externalHEAD<X extends RX>(init: string | URLPatternInit, handler: Handler<X>): this; externalHEAD<X extends RX>(init: string | URLPatternInit, middleware: Middleware<RX, X>, handler: Handler<X>): this; /** * Use a different `WorkerRouter` for the provided pattern. Keep in mind that: * * - The pattern must end in a wildcard `*` * - The corresponding match is the only part used for matching in the `subRouter` * - Forwards all HTTP methods * - Does not apply any middleware * * #### Why does it not apply middleware? * * There are 2 reasons: First, it interferes with type inference of middleware. * As a developer you'd have to provide the correct types at the point of defining the sub router, * which is at least as cumbersome as providing the middleware itself. * * Second, without this there would be no way to opt a route out of the router-level middleware. * For example you might want to opt out all `/public*` urls from cookie parsing, authentication, etc. * but add a different caching policy instead. * * @param path A pattern ending in a wildcard, e.g. `/items*` * @param subRouter A `WorkerRouter` that handles the remaining part of the URL, e.g. `/:category/:id` * @deprecated The name of this method might change to avoid confusion with `use` method known from other routers. */ use<Y extends RouteContext>(path: string, subRouter: WorkerRouter<Y>): this; /** * See `.external` and `.use`. * @deprecated Might change name/API */ useExternal<Y extends RouteContext>(init: string | URLPatternInit, subRouter: WorkerRouter<Y>): this; /** * Register a special route to recover from an error during execution of a regular route. * * In addition to the usual context properties, the provided handler receives a `response` and `error` property. * In case of a well-known error (typically caused by middleware), * the `response` contains a Fetch API `Response` object with matching status and status text set. * In case of an unknown error, the `response` is a generic "internal server error" and the `error` property * contains the value caught by the catch block. * * Recover routes don't execute the router-level middleware (which might have caused the error), but * can have middleware specifically for this route. Note that if another error occurs during the execution of * this middleware, there are no more safety nets and an internal server error response is returned. * * If a global `DEBUG` variable is set (or `process.env.NODE_ENV` is set to `development` in case of webpack) * the router will throw on an unhandled error. This is to make it easier to spot problems during development. * Otherwise, the router will not throw but instead dispatch a `error` event on itself before returning an empty * internal server error response. */ recover(path: string, handler: Handler<ErrorContext>): this; recover<X extends ErrorContext>(path: string, middleware: Middleware<ErrorContext, X>, handler: Handler<X>): this; recoverExternal(init: string | URLPatternInit, handler: Handler<ErrorContext>): this; recoverExternal<X extends ErrorContext>(init: string | URLPatternInit, middleware: Middleware<ErrorContext, X>, handler: Handler<X>): this; /** @deprecated Name/API might change */ handle: (request: Request, ctx?: Omit<Context, 'effects'>) => Promise<Response>; /** * Implements the (ancient) event listener object interface to allow passing to fetch event directly, * e.g. `self.addEventListener('fetch', router)`. */ handleEvent: (object: Event) => void; /** * Callback compatible with Cloudflare Worker's `fetch` module export. * E.g. `export default router`. */ fetch: (request: Request, env?: any, ctx?: any) => Promise<Response>; /** * Callback that is compatible with Deno's `serve` function. * E.g. `serve(router.serveCallback)`. */ serveCallback: (request: Request, connInfo: any) => Promise<Response>; addEventListener(type: 'error', listener: TypedEventListenerOrEventListenerObject<ErrorEvent> | null, options?: boolean | AddEventListenerOptions): void; removeEventListener(type: 'error', listener: TypedEventListenerOrEventListenerObject<ErrorEvent> | null, options?: EventListenerOptions | boolean): void; } declare type TypedEventListener<E extends Event> = (evt: E) => void | Promise<void>; declare type TypedEventListenerObject<E extends Event> = { handleEvent(evt: E): void | Promise<void>; }; declare type TypedEventListenerOrEventListenerObject<E extends Event> = TypedEventListener<E> | TypedEventListenerObject<E>;