@ayonli/jsext
Version:
A JavaScript extension package for building strong and modern applications.
245 lines (244 loc) • 7.05 kB
TypeScript
/**
* Functions for handling HTTP related tasks, such as parsing headers and
* serving HTTP requests.
*
* Many functions in this module are designed to work in all environments, but
* some of them are only available in server runtimes such as Node.js, Deno,
* Bun and Cloudflare Workers.
*
* This module itself is a executable script that can be used to serve static
* files in the current working directory, or we can provide an entry module
* which has an default export that satisfies the {@link ServeOptions} to start
* a custom HTTP server.
*
* The script can be run directly with Deno, Bun, or Node.js.
*
* Deno:
* ```sh
* deno run --allow-net --allow-read jsr:@ayonli/jsext/http [--port PORT] [DIR]
* deno run --allow-net --allow-read jsr:@ayonli/jsext/http <entry.ts>
* ```
*
* Bun:
* ```sh
* bun run node_modules/@ayonli/jsext/http.ts [--port PORT] [DIR]
* bun run node_modules/@ayonli/jsext/http.ts <entry.ts>
* ```
*
* Node.js (tsx):
* ```sh
* tsx node_modules/@ayonli/jsext/http.ts [--port PORT] [DIR]
* tsx node_modules/@ayonli/jsext/http.ts <entry.ts>
* ```
*
* In Node.js, we can also do this:
*
* ```sh
* tsx --import=@ayonli/jsext/http <entry.ts> [--port PORT] [--parallel [NUM]]
* # or
* node -r @ayonli/jsext/http <entry.js> [--port PORT] [--parallel [NUM]]
* ```
* @module
* @experimental
*/
import { FileInfo } from "./fs.ts";
import { withWeb as _withWeb } from "./http/internal.ts";
import { NetAddress, RequestContext, RequestHandler, RequestErrorHandler, ServeOptions, ServeStaticOptions, Server } from "./http/server.ts";
/**
* @deprecated This function has been moved to `@ayonli/jsext/http/internal`.
*/
export declare const withWeb: typeof _withWeb;
export * from "./http/util.ts";
export type { NetAddress, RequestContext, RequestHandler, RequestErrorHandler, ServeOptions, ServeStaticOptions, Server, };
/**
* Calculates the ETag for a given entity.
*
* @example
* ```ts
* import { stat } from "@ayonli/jsext/fs";
* import { etag } from "@ayonli/jsext/http";
*
* const etag1 = await etag("Hello, World!");
*
* const data = new Uint8Array([1, 2, 3, 4, 5]);
* const etag2 = await etag(data);
*
* const info = await stat("file.txt");
* const etag3 = await etag(info);
* ```
*/
export declare function etag(data: string | Uint8Array | FileInfo): Promise<string>;
/**
* Returns a random port number that is available for listening.
*
* NOTE: This function is not available in the browser and worker runtimes such
* as Cloudflare Workers.
*
* @param prefer The preferred port number to return if it is available,
* otherwise a random port is returned.
*
* @param hostname The hostname to bind the port to. Default is "0.0.0.0", only
* used when `prefer` is set and not `0`.
*/
export declare function randomPort(prefer?: number | undefined, hostname?: string | undefined): Promise<number>;
/**
* Serves HTTP requests with the given options.
*
* This function provides a unified way to serve HTTP requests in all server
* runtimes, even worker runtimes. It's similar to the `Deno.serve` and
* `Bun.serve` functions, in fact, it calls them internally when running in the
* corresponding runtime. When running in Node.js, it uses the built-in `http`
* or `http2` modules to create the server.
*
* This function also provides easy ways to handle Server-sent Events and
* WebSockets inside the fetch handler without touching the underlying verbose
* APIs.
*
* Currently, the following runtimes are supported:
*
* - Node.js (v18.4.1 or above)
* - Deno
* - Bun
* - Cloudflare Workers
* - Fastly Compute
* - Service Worker in the browser
*
* NOTE: WebSocket is not supported in Fastly Compute and browser's Service
* Worker at the moment.
*
* @example
* ```ts
* // simple http server
* import { serve } from "@ayonli/jsext/http";
*
* serve({
* fetch(req) {
* return new Response("Hello, World!");
* },
* });
* ```
*
* @example
* ```ts
* // set the hostname and port
* import { serve } from "@ayonli/jsext/http";
*
* serve({
* hostname: "localhost",
* port: 8787, // same port as Wrangler dev
* fetch(req) {
* return new Response("Hello, World!");
* },
* });
* ```
*
* @example
* ```ts
* // serve HTTPS/HTTP2 requests
* import { readFileAsText } from "@ayonli/jsext/fs";
* import { serve } from "@ayonli/jsext/http";
*
* serve({
* key: await readFileAsText("./cert.key"),
* cert: await readFileAsText("./cert.pem"),
* fetch(req) {
* return new Response("Hello, World!");
* },
* });
* ```
*
* @example
* ```ts
* // respond Server-sent Events
* import { serve } from "@ayonli/jsext/http";
*
* serve({
* fetch(req, ctx) {
* const { events, response } = ctx.createEventEndpoint();
* let count = events.lastEventId ? Number(events.lastEventId) : 0;
*
* setInterval(() => {
* const lastEventId = String(++count);
* events.dispatchEvent(new MessageEvent("ping", {
* data: lastEventId,
* lastEventId,
* }));
* }, 5_000);
*
* return response;
* },
* });
* ```
*
* @example
* ```ts
* // upgrade to WebSocket
* import { serve } from "@ayonli/jsext/http";
*
* serve({
* fetch(req, ctx) {
* const { socket, response } = ctx.upgradeWebSocket();
*
* socket.addEventListener("message", (event) => {
* console.log(event.data);
* socket.send("Hello, Client!");
* });
*
* return response;
* },
* });
* ```
*
* @example
* ```ts
* // module mode (for `deno serve`, Bun and Cloudflare Workers)
* import { serve } from "@ayonli/jsext/http";
*
* export default serve({
* type: "module",
* fetch(req) {
* return new Response("Hello, World!");
* },
* });
* ```
*/
export declare function serve(options: ServeOptions): Server;
/**
* Serves static files from a file system directory or KV namespace (in
* Cloudflare Workers).
*
* NOTE: In Node.js, this function requires Node.js v18.4.1 or above.
*
* NOTE: In Cloudflare Workers, this function requires setting the
* `[site].bucket` option in the `wrangler.toml` file.
*
* @example
* ```ts
* import { serve, serveStatic } from "@ayonli/jsext/http";
*
* // use `serve()` so this program runs in all environments
* serve({
* async fetch(req: Request, ctx) {
* const { pathname } = new URL(req.url);
*
* if (pathname.startsWith("/assets")) {
* return await serveStatic(req, {
* fsDir: "./assets",
* kv: ctx.bindings?.__STATIC_CONTENT,
* urlPrefix: "/assets",
* });
* }
*
* return new Response("Hello, World!");
* }
* });
* ```
*
* @example
* ```toml
* # wrangler.toml
* [site]
* bucket = "./assets"
* ```
*/
export declare function serveStatic(req: Request, options?: ServeStaticOptions): Promise<Response>;