UNPKG

ufiber

Version:

Next-gen webserver for node-js developer

125 lines (123 loc) 3.57 kB
import { isPromise, parseBytes } from "./utils/tools.js"; import { kMatch } from "./consts.js"; import { Context } from "./http/context.js"; import "./http/index.js"; import { compose } from "./router/compose.js"; import { Router, kErrorHandler, kNotFound } from "./router/index.js"; import uws from "../uws"; import path from "node:path"; //#region src/ufiber.ts /** * The Fiber class extends the functionality of the Router class. * It sets up routing and allows for custom options to be passed. */ var Fiber = class extends Router { uws; #name; isSSL; #methods; bodyLimit; /** * Creates an instance of the Fiber class. * * @param options - Optional configuration options for the Fiber instance. */ constructor(options = Object.create(null)) { super(); this.#name = options.name || "Fiber"; this.#methods = options.methods; this.bodyLimit = parseBytes(options.bodyLimit || "16MB"); const opts = options.uwsOptions ?? {}; if (opts.key_file_name && opts.cert_file_name) { this.uws = uws.SSLApp(opts); this.isSSL = true; } else { this.uws = uws.App(opts); this.isSSL = false; } } /** * Add WebSocket support * * @param pattern - URL pattern for WebSocket endpoint * @param behavior - WebSocket behavior configuration * @returns this for chaining * * @example * ```ts * app.ws('/chat', { * message: (ws, message, opCode) => { * ws.send(message); * }, * open: (ws) => { * console.log('WebSocket connected'); * } * }); * ``` */ ws(pattern, behavior) { this.uws.ws(pattern, behavior); return this; } #dispatch = (res, req) => { const ctx = new Context({ req, res, isSSL: this.isSSL, appName: this.#name, bodyLimit: this.bodyLimit, methods: this.#methods }); const matchResult = this.router.match(ctx.method === "HEAD" ? "GET" : ctx.method, ctx.path); ctx[kMatch] = matchResult; if (!matchResult) return this[kNotFound](ctx); if (matchResult[0].length === 1) { try { const result$1 = matchResult[0][0][0][0](ctx, async () => await this[kNotFound](ctx)); if (isPromise(result$1)) result$1.catch((err) => this[kErrorHandler](err, ctx)); } catch (err) { this[kErrorHandler](err, ctx); } return; } const result = compose(matchResult[0], { onError: this[kErrorHandler], onNotFound: this[kNotFound] })(ctx); if (isPromise(result)) result.catch((err) => this[kErrorHandler](err, ctx)); }; listen(...args) { this.uws.any("/*", this.#dispatch); let port = 0; let host; let cb; if (typeof args[0] === "function") cb = args[0]; else if (typeof args[1] === "function") { port = args[0]; cb = args[1]; } else [port, host, cb] = args; const onListen = (socket) => { if (!socket) throw new Error(`Failed to listen on ${port}. No permission or address in use.`); let address; if (typeof port === "string" && isNaN(Number(port))) { const normalizedPath = port.startsWith("/") || port.startsWith("./") ? port : `./${port}`; address = path.resolve(normalizedPath); } else address = `${this.isSSL ? "https" : "http"}://${host ?? "0.0.0.0"}:${port}`; cb?.(address); }; if (typeof port === "string" && isNaN(Number(port))) this.uws.listen_unix(onListen, port); else { const numericPort = Number(port); if (host) this.uws.listen(host, numericPort, onListen); else this.uws.listen(numericPort, onListen); } const shutdown = () => { this.uws.close(); process.exit(0); }; process.on("SIGINT", shutdown); process.on("SIGTERM", shutdown); } }; //#endregion export { Fiber };