@worker-tools/middleware
Version:
A suite of standalone HTTP server middlewares for Worker Runtimes.
172 lines (171 loc) • 7.39 kB
TypeScript
export { pipe as combine } from 'ts-functional-pipe';
import { AppendOnlyList } from "./utils/append-only-list.js";
import { Awaitable, Callable } from "./utils/common-types.js";
export declare type ResponseEffect = (r: Response) => void | Awaitable<Response>;
export declare class EffectsList extends AppendOnlyList<ResponseEffect> {
}
export interface Context {
/**
* The original request for use in middleware. Also accessible via first argument to user handler.
*/
request: Request;
/**
* A list of effects/transforms applied to the `Response` after the application handler completes.
* Middleware can add effects to the list. Application handlers should ignore it.
* @deprecated Prop might change name
*/
effects: AppendOnlyList<ResponseEffect>;
/**
* TODO
*/
waitUntil: (f: any) => void;
/**
* A promise that resolves when middleware is done applying effects.
* Related: https://github.com/w3c/ServiceWorker/issues/1397
*/
handled: Promise<Response>;
/**
* The URL pattern match that caused this handler to run. See the URL Pattern API for more.
*/
match?: URLPatternResult;
/**
* Only available if the router is used via `fetchEventListener`.
* Many Worker Runtimes such as Deno an CF module workers don't provide fetch events.
*/
event?: FetchEvent;
/** Might be present based on usage */
env?: any;
/** Might be present based on usage */
ctx?: any;
/** Might be present based on usage */
connInfo?: any;
/** Might be present based on usage */
args?: any[];
}
/**
* Helper type to get the context type of a given middleware function.
*
* Example:
* ```ts
* const mw = combine(basics(), contentTypes(['text/html', 'application/json']))
* type MWContext = ContextOf<typeof mw>;
* const handler = (req: Request, context: MWContext) => ok()
* new WorkerRouter().get('/', mw, handler)
* ```
*/
export declare type ContextOf<MW extends (...args: any[]) => Awaitable<Context>> = Awaited<ReturnType<MW>>;
/**
* @deprecated Function might change name
*/
export declare function executeEffects(effects: EffectsList, response: Awaitable<Response>): Awaitable<Response>;
/** Any record of unknown values */
export declare type Rec = Record<PropertyKey, any>;
/**
* A helper function to create user-defined middleware.
*
* Its main purpose is to allow developers to create correctly typed middleware without dealing with generics.
* This is achieved via the `_defaultExt` parameter, which is used to infer the types of the *extension* added to the *context*.
* As the `_` prefix implies, it is not actually used.
* The purpose of the default extension object is solely to tell the type checker which additional keys to expect on the context object after this middleware is applied.
* The job of adding (default) values to the context belongs to the middleware function.
*
* Here are some example usages. All are valid in JavaScript and TypeScript:
*
* ```ts
* const fn = createMiddleware({}, _ => _)
* const gn = createMiddleware({}, async ax => ({ ...await ax }))
* const hn = createMiddleware({ foo: '' }, async ax => ({ ...await ax, foo: 'star' }))
* const jn = createMiddleware({ bar: '' }, async ax => {
* const x = await ax;
* x.effects.push(resp => {
* resp.headers.set('x-middleware', 'jn')
* })
* return { ...x, bar: 'star' }
* })
* const myMW = combine(fn, hn, jn, gn)
* //=> Context & { foo: string } & { bar: string }
* ```
*
* @param _defaultExt The default extension to the current context. Can also be a function that returns the extension object, to avoid unnecessary memory allocation.
* @param middlewareFn A middleware functions: Adds the keys listed in `defaultExt` to the context
* @returns The provided `middlewareFn` with type annotations inferred based on `defaultExt`
*/
export declare function createMiddleware<Etx extends Rec>(_defaultExt: Callable<Etx>, middlewareFn: <Ctx extends Context>(ax: Awaitable<Ctx>) => Awaitable<Ctx & Etx>): <Ctx extends Context>(ax: Awaitable<Ctx>) => Awaitable<Ctx & Etx>;
/** @deprecated Name might change */
export declare type ErrorContext = Context & {
error: Error;
response: Response;
};
/** @deprecated Name might change */
export declare type Handler<X extends Context> = (request: Request, ctx: X) => Awaitable<Response>;
/** @deprecated Name might change */
export declare type ErrorHandler<X extends ErrorContext> = (request: Request, ctx: X) => Awaitable<Response>;
/** @deprecated Name might change */
export declare type Middleware<X extends Context, Y extends Context> = (x: Awaitable<X>) => Awaitable<Y>;
/**
* Takes a handler function of the form `(x: Request, ctx: Context) => Awaitable<Response>` and applies middleware to it.
* @deprecated Name might change, errorHandler not implemented
*/
export declare function withMiddleware<X extends Context, EX extends ErrorContext>(middleware: Middleware<Context, X>, handler: Handler<X>, _errorHandler?: ErrorHandler<EX>): (request: Request, ...args: any[]) => Promise<Response>;
/**
* Extends the lifetime of the install and activate events dispatched on the global scope as part of the
* service worker lifecycle. This ensures that any functional events (like FetchEvent) are not dispatched until it
* upgrades database schemas and deletes the outdated cache entries.
*/
export interface ExtendableEvent extends Event {
waitUntil(f: any): void;
}
export interface ExtendableEventInit extends EventInit {
new (type: string, eventInitDict?: ExtendableEventInit): ExtendableEvent;
}
export interface FetchEventInit extends ExtendableEventInit {
new (type: string, eventInitDict: FetchEventInit): FetchEvent;
clientId?: string;
preloadResponse?: Promise<any>;
replacesClientId?: string;
request: Request;
resultingClientId?: string;
}
/**
* This is the event type for fetch events dispatched on the service worker global scope.
* It contains information about the fetch, including the request and how the receiver will treat the response.
* It provides the event.respondWith() method, which allows us to provide a response to this fetch.
*/
export interface FetchEvent extends ExtendableEvent {
readonly clientId: string;
readonly preloadResponse: Promise<any>;
readonly replacesClientId: string;
readonly request: Request;
readonly resultingClientId: string;
readonly handled: Promise<void>;
respondWith(r: Response | Promise<Response>): void;
}
export declare type URLPatternInput = URLPatternInit | string;
export interface URLPatternInit {
baseURL?: string;
username?: string;
password?: string;
protocol?: string;
hostname?: string;
port?: string;
pathname?: string;
search?: string;
hash?: string;
}
export interface URLPatternResult {
inputs: [URLPatternInit] | [URLPatternInit, string];
protocol: URLPatternComponentResult;
username: URLPatternComponentResult;
password: URLPatternComponentResult;
hostname: URLPatternComponentResult;
port: URLPatternComponentResult;
pathname: URLPatternComponentResult;
search: URLPatternComponentResult;
hash: URLPatternComponentResult;
}
export interface URLPatternComponentResult {
input: string;
groups: {
[key: string]: string | undefined;
};
}