UNPKG

nodecaf

Version:

Nodecaf is a light framework for developing RESTful Apps in a quick and convenient manner.

293 lines (260 loc) 12.4 kB
import { Server, ServerResponse, IncomingMessage } from 'http' import WebSocket from 'ws' declare namespace Nodecaf { type ConfObject = { /** Controls logging output. */ log?: { /** Define fields to be added to all log entries */ defaults?: Record<string, unknown>, /** Only output log entries with specified `level` or above */ level?: 'debug' | 'info' | 'warn' | 'error' | 'fatal', /** Only output log entries matching any `type` */ only?: string | string[], /** Only output log entries not matching any `type` */ except?: string | string[] }, /** Creates an HTTP server that will be managed on the given port. */ port?: number } & Record<string, unknown> type RunOptions = { /** Single or array of Conf object or file path */ conf: ConfObject | string | (ConfObject | string)[], /** Path to the nodecaf module to be run */ path: string } class Logger { debug(...args: unknown[]): void info(...args: unknown[]): void warn(...args: unknown[]): void error(...args: unknown[]): void fatal(...args: unknown[]): void } type CookieOpts = { expires?: Date, maxAge?: number, /** * @deprecated Setting `signed` cookies is deprecated. This option will be dropped on `v0.14.0`. Cookie signing must be done manually instead. */ signed?: boolean, path?: string, domain?: string secure?: boolean httpOnly?: boolean overwrite?: boolean sameSite?: "Strict" | "Lax" | "None" } class Response extends ServerResponse { /** In case `cond` is falsy, throws HTTP error with `status` and `message` as body printf-formated with `args` */ assert(status: number, cond: boolean, message?: string, ...args: unknown[]): this /** In case `cond` is falsy, throws Error 400 with `message` as body printf-formated with `args` */ badRequest(cond: boolean, message?: string, ...args: unknown[]): this /** In case `cond` is falsy, throws Error 401 with `message` as body printf-formated with `args` */ unauthorized(cond: boolean, message?: string, ...args: unknown[]): this /** In case `cond` is falsy, throws Error 403 with `message` as body printf-formated with `args` */ forbidden(cond: boolean, message?: string, ...args: unknown[]): this /** In case `cond` is falsy, throws Error 404 with `message` as body printf-formated with `args` */ notFound(cond: boolean, message?: string, ...args: unknown[]): this /** In case `cond` is falsy, throws Error 409 with `message` as body printf-formated with `args` */ conflict(cond: boolean, message?: string, ...args: unknown[]): this /** In case `cond` is falsy, throws Error 410 with `message` as body printf-formated with `args` */ gone(cond: boolean, message?: string, ...args: unknown[]): this /** In case `cond` is falsy, throws Error 415 with `message` as body printf-formated with `args` */ badType(cond: boolean, message?: string, ...args: unknown[]): this /** Respond with an HTTP error in `status` and `message` as body printf-formated with `args` */ error(status: string | number, message?: string, ...args: unknown[]): void /** Respond with an Error 500 */ error(status: unknown): void /** Append `chunk` to the response stream. */ write(chunk: string | Buffer): this /** Finishes the request. If set, append `body` to the response stream. */ end(body?: string | Buffer): void /** Respond with a json body and finishes the request. */ json(data: unknown): void /** Respond with a text body and finishes the request. */ text(data: number | boolean | string): void /** Set a request header. */ set(k: string, v: string): this /** Append the header to the response. Allow sending duplicated header keys */ append(k: string, v: string): this /** Set content-type header to a know type (json, text, urlencoded) or any mime-type */ type(ct: string): this /** Send the request status to client. */ status(s: number): this /** Set a cookie according to options. */ cookie(name: string, value: string, opts?: CookieOpts): this /** Clear the cookie identified by `name` and `opts`. */ clearCookie(name: string, opts?: CookieOpts): this } class RequestBody extends IncomingMessage { raw(): Promise<Buffer | unknown> text(): Promise<string> urlencoded(): Promise<Record<string, string>> json(): Promise<unknown> parse(): Promise<unknown> } type RouteHandlerArgs = { /** Object containing request headers as key-values. */ headers: Record<string, string>, /** Object containing request URL query string as key-values. */ query: Record<string, string>, /** A logging utility to output JSON lines to stdout. */ log: Logger, /** Request body object (in case `opts.autoParseBody` is `true`, will contain the parsed data instead). */ body: RequestBody | unknown, /** Request URL path. */ path: string, /** Request HTTP method. */ method: 'POST' | 'DELETE' | 'PATCH' | 'PUT' | 'GET', /** Response object used to compose a response to the client. */ res: Response, /** Call `fn` with the request handler args as the first parameter and spreading `args`. */ call: <T>(fn: (input: RouteHandlerArgs, ...args: unknown[]) => T, ...args: unknown[]) => T /** The current app configuration. */ conf: ConfObject, /** Object containing the request cookies as key-values. */ cookies: Record<string, string>, /** * Object containing the request signed cookies as key-values. * @deprecated `signedCookies` is deprecated. This option will be dropped on `v0.14.0`. Signed cookies must be handled manually instead. **/ signedCookies: Record<string, string>, /** Object containing params parsed from URL segments as key-values. */ params: Record<string, string> /** The remote address of the client performing the request. Standard proxy headers are considered. */ ip: string, /** Store `value` under the name `key` in the handler args for the lifetime of the request. */ keep: (key: string, value: unknown) => void, /** Accept WebSocket connection on upgrade. Only available when `opts.websocket` is set. */ websocket: () => Promise<WebSocket.WebSocket> } & Record<string, unknown> type RouteHandler = (this: Nodecaf, input: RouteHandlerArgs) => Promise<void> | void type EndpointBuilders = { post: (path: string, handler: RouteHandler) => void, put: (path: string, handler: RouteHandler) => void, patch: (path: string, handler: RouteHandler) => void, get: (path: string, handler: RouteHandler) => void, del: (path: string, handler: RouteHandler) => void, all: (handler: RouteHandler) => void } type Route = { /** Endpoint HTTP method */ method: string, /** Endpoint path starting with slash (e.g `/foo/:bar`) */ path: string, /** Function to be called when endpoint is triggered */ handler: RouteHandler } type AppOpts = { /** An array with your api endpoints */ routes: Route[], /** * A function to build your api endpoints * @deprecated This option will be removed on `v0.14.0`. Use `routes` instead. **/ api?: (this: Nodecaf, methods: Nodecaf.EndpointBuilders) => void, /** A function to run whenever the app is starting */ startup?: (args: Nodecaf) => Promise<void> | void, /** A function to run whenever the app is stopping */ shutdown?: (args: Nodecaf) => Promise<void> | void, /** App name, mainly used in log entries */ name?: string, /** App version, mainly used in log entries */ version?: string, /** Default config object or file path */ conf?: Nodecaf.ConfObject | string, /** Whether request bodies should be parsed for known mime-types (json, text, urlencoded). Defaults to `false`. */ autoParseBody?: boolean, /** A function that returns a custom HTTP server to be used by the app */ server?: (args: Nodecaf) => Server, /** Whether to handle websocket upgrade requests. Defaults to `false`. */ websocket?: boolean } } /** * A light RESTful App * * Example usage: * ```js * const app = new Nodecaf({ * api({ get }){ * get('/foo', function({ res }){ * res.text('bar'); * }); * } * }); * await app.start(); * const { status, body } = await app.trigger('get', '/bar'); * console.log(status, body); * await app.stop(); * ``` */ declare class Nodecaf { /** Define a POST endpoint to `path` that when triggered will run the `handler` function */ static post(path: string, handler: Nodecaf.RouteHandler): Nodecaf.Route /** Define a PUT endpoint to `path` that when triggered will run the `handler` function */ static put(path: string, handler: Nodecaf.RouteHandler): Nodecaf.Route /** Define a PATCH endpoint to `path` that when triggered will run the `handler` function */ static patch(path: string, handler: Nodecaf.RouteHandler): Nodecaf.Route /** Define a GET endpoint to `path` that when triggered will run the `handler` function */ static get(path: string, handler: Nodecaf.RouteHandler): Nodecaf.Route /** Define a DELETE endpoint to `path` that when triggered will run the `handler` function */ static del(path: string, handler: Nodecaf.RouteHandler): Nodecaf.Route /** Define a fallback `handler` function to be triggered when there are no matching routes */ static all(handler: Nodecaf.RouteHandler): Nodecaf.Route /** * Run a given nodecaf app handling uncaught errors and node process signals * @deprecated This function will be dropped on `v0.14.0`. Use `app.run()` instead. */ static run(opts: Nodecaf.RunOptions): void /** A user controlled object whose properties wil be spread in route handler args. */ global: Record<string, unknown> /** The current app configuration. */ conf: Nodecaf.ConfObject /** A logging utility to output JSON lines to stdout. */ log: Nodecaf.Logger /** Call `fn` with the app global args as the first parameter and spreading `args`. */ call: <T>(fn: (app: Nodecaf, ...args: unknown[]) => T, ...args: unknown[]) => T /** * Creates a new instance of an app in standby. */ constructor(opts: Nodecaf.AppOpts) /** * Run a standby app. The returned `Promise` is resolved after the startup is * complete. */ start(): Promise<'running' | 'starting'> /** * Stop a running app. The returned `Promise` is resolved after the shutdown * is complete. */ stop(): Promise<'standby' | 'stopping'> /** * Restart a running app, applying configuration if sent. The returned * `Promise` is resolved once the app is fully started up. */ restart(conf: Nodecaf.ConfObject | string): Promise<void> /** * Apply configuration from an object or reading from a config file in one * of the supported formats (JSON, TOML, YAML). */ setup(...conf: (Nodecaf.ConfObject | string)[]): void /** * Trigger an app endpoint with given input data. Returns a `Promise` * resolving to the normalized response data. */ trigger(method: string, path: string, input?: { body?: BodyInit, headers?: Record<string, string>, query?: Record<string, string>, cookies?: Record<string, string> }): Promise<{ status: number, headers: Record<string, string>, body: unknown }> /** * Run the app handling uncaught errors and node process signals. */ run(opts: Nodecaf.RunOptions): Promise<Nodecaf> } export = Nodecaf