UNPKG

tspace-spear

Version:

tspace-spear is a lightweight, high-performance API framework for Node.js that leverages the native HTTP server and supports uWebSockets.js (C++) for maximum speed and efficiency.

300 lines 8.76 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.FastRouter = void 0; const METHODS = [ "GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD" ]; const parseValue = (value) => { if (/^-?\d+(\.\d+)?$/.test(value)) { const num = Number(value); if (Number.isFinite(num) && Number.isSafeInteger(num)) { return num; } if (Number.isFinite(num) && !Number.isInteger(num)) { return num; } } return value; }; class FastRouter { trees = Object.create(null); _routes = []; constructor() { for (const m of METHODS) { this.trees[m] = this._createNode(); } } /** * Get all registered routes in the router. * * Returns the internal route registry used for debugging, * inspection, or serialization of the router tree. * * @returns Internal routes structure */ get routes() { return this._routes; } /** * Register a GET route. * * Handles HTTP GET requests for the specified path. * * @param path Route path (e.g. "/users/:id") * @param handler Function executed when route matches * @returns Router instance for chaining * * @example * router.get("/users", (req, res) => {}); */ get(path, handler) { this._add("GET", path, handler); return this; } /** * Register a POST route. * * Used for creating resources or submitting data. * * @param path Route path * @param handler Route handler function * @returns Router instance for chaining */ post(path, handler) { this._add("POST", path, handler); return this; } /** * Register a PUT route. * * Typically used for full resource replacement. * * @param path Route path * @param handler Route handler function * @returns Router instance for chaining */ put(path, handler) { this._add("PUT", path, handler); return this; } /** * Register a PATCH route. * * Used for partial updates to a resource. * * @param path Route path * @param handler Route handler function * @returns Router instance for chaining */ patch(path, handler) { this._add("PATCH", path, handler); return this; } /** * Register a DELETE route. * * Used for removing a resource. * * @param path Route path * @param handler Route handler function * @returns Router instance for chaining */ delete(path, handler) { this._add("DELETE", path, handler); return this; } /** * Register an OPTIONS route. * * Used for CORS preflight requests or capability discovery. * * @param path Route path * @param handler Route handler function * @returns Router instance for chaining */ options(path, handler) { this._add("OPTIONS", path, handler); return this; } /** * Register a HEAD route. * * Same as GET but returns headers only (no body). * * @param path Route path * @param handler Route handler function * @returns Router instance for chaining */ head(path, handler) { this._add("HEAD", path, handler); return this; } /** * Register a route for all HTTP methods. * * This registers the same handler for every supported HTTP method. * Useful for middleware-like or catch-all behavior. * * @param path Route path * @param handler Route handler function * @returns Router instance for chaining * * @example * router.all("/health", (req, res) => res.send("OK")); */ all(path, handler) { for (const method of METHODS) { this._add(method, path, handler); } return this; } /** * Lookup a route handler based on the incoming request. * * This method is responsible for resolving the correct route * from the registered router tree and executing the matched handler. * * It supports parameterized routes, static routes, and (optionally) * wildcard matching depending on router implementation. * * @param {IncomingMessage} req Incoming HTTP request object * @param {ServerResponse} res Server response object used to send output * * @returns void * * @example * router.lookup(req, res); * * @internal * This is typically called by the HTTP server layer and should not * be invoked directly in most application code. */ lookup(req, res) { const method = req.method; let node = this.trees[method]; if (!node) { if (res.writableEnded) { return; } res.statusCode = 405; return res.end("Method Not Allowed"); } let url = req.url || "/"; let q = url.indexOf("?"); if (q !== -1) url = url.slice(0, q); const params = Object.create(null); const rootWildcard = node.wildcard; let start = 1; for (let i = 1; i <= url.length; i++) { if (url[i] === "/" || i === url.length) { const part = url.slice(start, i); if (!part) { start = i + 1; continue; } let next = node.static[part]; if (next) { node = next; start = i + 1; continue; } if (node.param) { params[node.param.paramName] = parseValue(part); node = node.param; start = i + 1; continue; } if (node.wildcard) { params["*"] = url.slice(start); node = node.wildcard; break; } if (rootWildcard?.handler) { return rootWildcard.handler(req, res, params); } if (res.writableEnded) { return; } res.statusCode = 404; res.end("Not Found"); return; } } if (!node.handler) { if (rootWildcard?.handler) { return rootWildcard.handler(req, res, params); } if (res.writableEnded) { return; } res.statusCode = 404; res.end("Not Found"); return; } return node.handler(req, res, params); } _createNode() { return { static: Object.create(null) }; } _add(method, path, handler) { let node = this.trees[method]; let start = 1; for (let i = 1; i <= path.length; i++) { if (path[i] === "/" || i === path.length) { const part = path.slice(start, i); if (!part) { start = i + 1; continue; } if (part[0] === ":") { if (!node.param) { node.param = this._createNode(); node.param.paramName = part.slice(1); } node = node.param; } else if (part === "*") { if (!node.wildcard) { node.wildcard = this._createNode(); } node = node.wildcard; break; } else { if (!node.static[part]) { node.static[part] = this._createNode(); } node = node.static[part]; } start = i + 1; } } node.handler = handler; this._routes.push({ path, method, params: this._extractParams(path) }); return; } _extractParams(path) { const params = []; let start = 1; for (let i = 1; i <= path.length; i++) { if (path[i] === "/" || i === path.length) { const part = path.slice(start, i); if (part && part[0] === ":") { params.push(part.slice(1)); } start = i + 1; } } return params; } } exports.FastRouter = FastRouter; //# sourceMappingURL=fast-router.js.map