UNPKG

bun-route

Version:

A fast, Express-like router for the high-performance bun.serve() HTTP server.

1,039 lines (1,035 loc) 31.3 kB
// src/method.ts var HttpMethod; ((HttpMethod2) => { HttpMethod2[HttpMethod2["ALL"] = 1] = "ALL"; HttpMethod2[HttpMethod2["GET"] = 2] = "GET"; HttpMethod2[HttpMethod2["PUT"] = 3] = "PUT"; HttpMethod2[HttpMethod2["POST"] = 4] = "POST"; HttpMethod2[HttpMethod2["PATCH"] = 5] = "PATCH"; HttpMethod2[HttpMethod2["DELETE"] = 6] = "DELETE"; HttpMethod2[HttpMethod2["HEAD"] = 7] = "HEAD"; HttpMethod2[HttpMethod2["OPTIONS"] = 8] = "OPTIONS"; HttpMethod2[HttpMethod2["TRACE"] = 9] = "TRACE"; HttpMethod2[HttpMethod2["CONNECT"] = 10] = "CONNECT"; HttpMethod2[HttpMethod2["UNKNOWN"] = 11] = "UNKNOWN"; })(HttpMethod ||= {}); function parseHttpMethods(method) { switch (method) { case "*": return 1 /* ALL */; case "GET": return 2 /* GET */; case "PUT": return 3 /* PUT */; case "POST": return 4 /* POST */; case "PATCH": return 5 /* PATCH */; case "DELETE": return 6 /* DELETE */; case "HEAD": return 7 /* HEAD */; case "OPTIONS": return 8 /* OPTIONS */; case "TRACE": return 9 /* TRACE */; case "CONNECT": return 10 /* CONNECT */; default: return 11 /* UNKNOWN */; } } function stringifyHttpMethods(method) { switch (method) { case 1 /* ALL */: return "ALL"; case 2 /* GET */: return "GET"; case 3 /* PUT */: return "PUT"; case 4 /* POST */: return "POST"; case 5 /* PATCH */: return "PATCH"; case 6 /* DELETE */: return "DELETE"; case 7 /* HEAD */: return "HEAD"; case 8 /* OPTIONS */: return "OPTIONS"; case 9 /* TRACE */: return "TRACE"; case 10 /* CONNECT */: return "CONNECT"; case undefined: return "ALL"; default: return "UNKNOWN"; } } // src/middleware.ts function unmergeRequestMiddleware(...middlewares) { const foundMiddlewares = []; for (const middleware of middlewares) { if (isMergedRequestMiddleware(middleware)) { foundMiddlewares.push(...unmergeRequestMiddleware(...middleware.base)); } else { foundMiddlewares.push(middleware); } } return foundMiddlewares; } function mergeRequestMiddlewares(...middlewares) { if (middlewares.length == 0) { throw new Error("no middlewares specified"); } else if (middlewares.length == 1) { return middlewares[0]; } middlewares = unmergeRequestMiddleware(...middlewares); const mergedAsync = async (initialDefIndex, promise, req, res) => { await promise; if (res.submit === true || req.upgraded === true) { return; } for (let i = initialDefIndex + 1;i < middlewares.length; i++) { const middleware = middlewares[i]; const p = middleware(req, res); if (p && p.then != null) { await p; } if (res.submit === true || req.upgraded === true) { return; } } }; const baseMerged = (req, res) => { for (let i = 0;i < middlewares.length; i++) { const middleware = middlewares[i]; const p = middleware(req, res); if (p && p.then != null) { return mergedAsync(i, p, req, res); } if (res.submit === true || req.upgraded === true) { return; } } }; const merged = baseMerged; merged.base = middlewares; return merged; } function isMergedRequestMiddleware(middleware) { return Array.isArray(middleware.base); } function isMergeableEndpointRoute(route, route2) { if (route.method !== route2.method) { return false; } if (route.splitPath == undefined && route2.splitPath == undefined) { return true; } else if (route.splitPath != null && route2.splitPath != null && route.splitPath.join("/") == route2.splitPath.join("/")) { return true; } return false; } // src/responseBuilder.ts var notFoundResponse = new Response("Not Found", { status: 404, statusText: "Not Found" }); class ResponseBuilder { submit = false; statusCode = 200; statusText; bodyInit = null; headers = []; beforeSentHooks; beforeSent(hook) { if (!this.beforeSentHooks) { this.beforeSentHooks = []; } this.beforeSentHooks.push(hook); return this; } async startBeforeSentHookAsync(p) { await p; let hook = this.beforeSentHooks?.shift(); while (hook != null) { const p2 = hook(this); if (p2 && p2.then != null) { await p2; } } } startBeforeSentHook() { if (this.beforeSentHooks) { let hook = this.beforeSentHooks.pop(); while (hook != null) { const p = hook(this); if (p && p.then != null) { return this.startBeforeSentHookAsync(p); } hook = this.beforeSentHooks.pop(); } } } build() { return new Response(this.bodyInit, { status: this.statusCode, statusText: this.statusText, headers: this.headers }); } reset() { this.submit = false; this.statusCode = 200; this.statusText = undefined; this.bodyInit = null; this.headers = []; return this; } status(statusCode, statusText) { this.statusCode = statusCode; if (statusText) { this.statusText = statusText; } return this; } unsetHeader(name) { this.headers = this.headers.filter((header) => header[0].toLowerCase() !== name.toLowerCase()); return this; } setHeader(name, value, overwrite = true) { if (overwrite) { this.unsetHeader(name); } this.headers.push([name, value]); return this; } setCookie(name, value, options = {}) { const cookieParts = [`${name}=${encodeURIComponent(value)}`]; if (options.MaxAge) { cookieParts.push(`Max-Age=${options.MaxAge}`); } if (options.Path) { cookieParts.push(`Path=${options.Path}`); } if (options.HttpOnly) { cookieParts.push(`HttpOnly`); } if (options.Secure) { cookieParts.push(`Secure`); } if (options.SameSite) { cookieParts.push(`SameSite=${options.SameSite}`); } this.setHeader("Set-Cookie", cookieParts.join("; "), false); return this; } unsetCookie(name) { this.setHeader("Set-Cookie", name + "=; Expires=Thu, 01 Jan 1970 00:00:00 GMT", false); return this; } body(bodyInit = null) { this.bodyInit = bodyInit; return this; } send(bodyInit = null) { this.bodyInit = bodyInit; this.submit = true; } sendRedirect(url, perma = false) { this.reset(); this.statusCode = perma ? 308 : 307; this.headers.push(["location", url]); this.submit = true; } sendRedirectCustom(url, status) { this.reset(); this.statusCode = status; this.headers.push(["location", url]); this.submit = true; } sendBasicAuth(bodyInit = null, realm = "User Visible Realm", charset = "UTF-8") { this.reset(); this.statusCode = 401; this.setHeader("WWW-Authenticate", 'Basic realm="' + realm + '", charset="' + charset + '"'); this.bodyInit = bodyInit; this.submit = true; } } // src/router.ts var {statSync} = (() => ({})); // node:path function assertPath(path) { if (typeof path !== "string") throw new TypeError("Path must be a string. Received " + JSON.stringify(path)); } function normalizeStringPosix(path, allowAboveRoot) { var res = "", lastSegmentLength = 0, lastSlash = -1, dots = 0, code; for (var i = 0;i <= path.length; ++i) { if (i < path.length) code = path.charCodeAt(i); else if (code === 47) break; else code = 47; if (code === 47) { if (lastSlash === i - 1 || dots === 1) ; else if (lastSlash !== i - 1 && dots === 2) { if (res.length < 2 || lastSegmentLength !== 2 || res.charCodeAt(res.length - 1) !== 46 || res.charCodeAt(res.length - 2) !== 46) { if (res.length > 2) { var lastSlashIndex = res.lastIndexOf("/"); if (lastSlashIndex !== res.length - 1) { if (lastSlashIndex === -1) res = "", lastSegmentLength = 0; else res = res.slice(0, lastSlashIndex), lastSegmentLength = res.length - 1 - res.lastIndexOf("/"); lastSlash = i, dots = 0; continue; } } else if (res.length === 2 || res.length === 1) { res = "", lastSegmentLength = 0, lastSlash = i, dots = 0; continue; } } if (allowAboveRoot) { if (res.length > 0) res += "/.."; else res = ".."; lastSegmentLength = 2; } } else { if (res.length > 0) res += "/" + path.slice(lastSlash + 1, i); else res = path.slice(lastSlash + 1, i); lastSegmentLength = i - lastSlash - 1; } lastSlash = i, dots = 0; } else if (code === 46 && dots !== -1) ++dots; else dots = -1; } return res; } function _format(sep, pathObject) { var dir = pathObject.dir || pathObject.root, base = pathObject.base || (pathObject.name || "") + (pathObject.ext || ""); if (!dir) return base; if (dir === pathObject.root) return dir + base; return dir + sep + base; } function resolve() { var resolvedPath = "", resolvedAbsolute = false, cwd; for (var i = arguments.length - 1;i >= -1 && !resolvedAbsolute; i--) { var path; if (i >= 0) path = arguments[i]; else { if (cwd === undefined) cwd = process.cwd(); path = cwd; } if (assertPath(path), path.length === 0) continue; resolvedPath = path + "/" + resolvedPath, resolvedAbsolute = path.charCodeAt(0) === 47; } if (resolvedPath = normalizeStringPosix(resolvedPath, !resolvedAbsolute), resolvedAbsolute) if (resolvedPath.length > 0) return "/" + resolvedPath; else return "/"; else if (resolvedPath.length > 0) return resolvedPath; else return "."; } function normalize(path) { if (assertPath(path), path.length === 0) return "."; var isAbsolute = path.charCodeAt(0) === 47, trailingSeparator = path.charCodeAt(path.length - 1) === 47; if (path = normalizeStringPosix(path, !isAbsolute), path.length === 0 && !isAbsolute) path = "."; if (path.length > 0 && trailingSeparator) path += "/"; if (isAbsolute) return "/" + path; return path; } function isAbsolute(path) { return assertPath(path), path.length > 0 && path.charCodeAt(0) === 47; } function join() { if (arguments.length === 0) return "."; var joined; for (var i = 0;i < arguments.length; ++i) { var arg = arguments[i]; if (assertPath(arg), arg.length > 0) if (joined === undefined) joined = arg; else joined += "/" + arg; } if (joined === undefined) return "."; return normalize(joined); } function relative(from, to) { if (assertPath(from), assertPath(to), from === to) return ""; if (from = resolve(from), to = resolve(to), from === to) return ""; var fromStart = 1; for (;fromStart < from.length; ++fromStart) if (from.charCodeAt(fromStart) !== 47) break; var fromEnd = from.length, fromLen = fromEnd - fromStart, toStart = 1; for (;toStart < to.length; ++toStart) if (to.charCodeAt(toStart) !== 47) break; var toEnd = to.length, toLen = toEnd - toStart, length = fromLen < toLen ? fromLen : toLen, lastCommonSep = -1, i = 0; for (;i <= length; ++i) { if (i === length) { if (toLen > length) { if (to.charCodeAt(toStart + i) === 47) return to.slice(toStart + i + 1); else if (i === 0) return to.slice(toStart + i); } else if (fromLen > length) { if (from.charCodeAt(fromStart + i) === 47) lastCommonSep = i; else if (i === 0) lastCommonSep = 0; } break; } var fromCode = from.charCodeAt(fromStart + i), toCode = to.charCodeAt(toStart + i); if (fromCode !== toCode) break; else if (fromCode === 47) lastCommonSep = i; } var out = ""; for (i = fromStart + lastCommonSep + 1;i <= fromEnd; ++i) if (i === fromEnd || from.charCodeAt(i) === 47) if (out.length === 0) out += ".."; else out += "/.."; if (out.length > 0) return out + to.slice(toStart + lastCommonSep); else { if (toStart += lastCommonSep, to.charCodeAt(toStart) === 47) ++toStart; return to.slice(toStart); } } function _makeLong(path) { return path; } function dirname(path) { if (assertPath(path), path.length === 0) return "."; var code = path.charCodeAt(0), hasRoot = code === 47, end = -1, matchedSlash = true; for (var i = path.length - 1;i >= 1; --i) if (code = path.charCodeAt(i), code === 47) { if (!matchedSlash) { end = i; break; } } else matchedSlash = false; if (end === -1) return hasRoot ? "/" : "."; if (hasRoot && end === 1) return "//"; return path.slice(0, end); } function basename(path, ext) { if (ext !== undefined && typeof ext !== "string") throw new TypeError('"ext" argument must be a string'); assertPath(path); var start = 0, end = -1, matchedSlash = true, i; if (ext !== undefined && ext.length > 0 && ext.length <= path.length) { if (ext.length === path.length && ext === path) return ""; var extIdx = ext.length - 1, firstNonSlashEnd = -1; for (i = path.length - 1;i >= 0; --i) { var code = path.charCodeAt(i); if (code === 47) { if (!matchedSlash) { start = i + 1; break; } } else { if (firstNonSlashEnd === -1) matchedSlash = false, firstNonSlashEnd = i + 1; if (extIdx >= 0) if (code === ext.charCodeAt(extIdx)) { if (--extIdx === -1) end = i; } else extIdx = -1, end = firstNonSlashEnd; } } if (start === end) end = firstNonSlashEnd; else if (end === -1) end = path.length; return path.slice(start, end); } else { for (i = path.length - 1;i >= 0; --i) if (path.charCodeAt(i) === 47) { if (!matchedSlash) { start = i + 1; break; } } else if (end === -1) matchedSlash = false, end = i + 1; if (end === -1) return ""; return path.slice(start, end); } } function extname(path) { assertPath(path); var startDot = -1, startPart = 0, end = -1, matchedSlash = true, preDotState = 0; for (var i = path.length - 1;i >= 0; --i) { var code = path.charCodeAt(i); if (code === 47) { if (!matchedSlash) { startPart = i + 1; break; } continue; } if (end === -1) matchedSlash = false, end = i + 1; if (code === 46) { if (startDot === -1) startDot = i; else if (preDotState !== 1) preDotState = 1; } else if (startDot !== -1) preDotState = -1; } if (startDot === -1 || end === -1 || preDotState === 0 || preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) return ""; return path.slice(startDot, end); } function format(pathObject) { if (pathObject === null || typeof pathObject !== "object") throw new TypeError('The "pathObject" argument must be of type Object. Received type ' + typeof pathObject); return _format("/", pathObject); } function parse(path) { assertPath(path); var ret = { root: "", dir: "", base: "", ext: "", name: "" }; if (path.length === 0) return ret; var code = path.charCodeAt(0), isAbsolute2 = code === 47, start; if (isAbsolute2) ret.root = "/", start = 1; else start = 0; var startDot = -1, startPart = 0, end = -1, matchedSlash = true, i = path.length - 1, preDotState = 0; for (;i >= start; --i) { if (code = path.charCodeAt(i), code === 47) { if (!matchedSlash) { startPart = i + 1; break; } continue; } if (end === -1) matchedSlash = false, end = i + 1; if (code === 46) { if (startDot === -1) startDot = i; else if (preDotState !== 1) preDotState = 1; } else if (startDot !== -1) preDotState = -1; } if (startDot === -1 || end === -1 || preDotState === 0 || preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) { if (end !== -1) if (startPart === 0 && isAbsolute2) ret.base = ret.name = path.slice(1, end); else ret.base = ret.name = path.slice(startPart, end); } else { if (startPart === 0 && isAbsolute2) ret.name = path.slice(1, startDot), ret.base = path.slice(1, end); else ret.name = path.slice(startPart, startDot), ret.base = path.slice(startPart, end); ret.ext = path.slice(startDot, end); } if (startPart > 0) ret.dir = path.slice(0, startPart - 1); else if (isAbsolute2) ret.dir = "/"; return ret; } var sep = "/"; var delimiter = ":"; var posix = ((p) => (p.posix = p, p))({ resolve, normalize, isAbsolute, join, relative, _makeLong, dirname, basename, extname, format, parse, sep, delimiter, win32: null, posix: null }); // src/router.ts class Router { routes = []; mergeHandlers = true; static parseCookies(req, forceReload = false) { if (!req.originCookies) { req.cookies = {}; const cookieHeader = req.headers.get("cookie"); if (!cookieHeader) { return; } const pairs = cookieHeader.split(/; */); for (const pair of pairs) { const splitted = pair.split("="); const name = trimSpaces(splitted[0]); if (name.length != 0) { req.cookies[name] = decodeURIComponent(splitted.slice(1).join("=")); } } req.originCookies = { ...req.cookies }; } else if (forceReload) { req.cookies = { ...req.originCookies }; } } static storeCookies(req, res) { if (!req.cookies) { res.reset().status(500).send("Request cookies store error"); return; } const newCookies = req.cookies; const oldCookies = req.originCookies ?? {}; const newCookieKeys = Object.keys(newCookies); for (const cookieKey of newCookieKeys) { if (newCookies[cookieKey] && (!oldCookies[cookieKey] || oldCookies[cookieKey] !== newCookies[cookieKey])) { res.setCookie(cookieKey, newCookies[cookieKey]); } } for (const cookieKey of Object.keys(oldCookies)) { if (!newCookieKeys.includes(cookieKey)) { res.unsetCookie(cookieKey); } } req.cookies = newCookies; } static getDefinitionString(route, handler, mergedToTop) { let parts = ["/", "X", "/"]; if (mergedToTop) { parts[0] = "^ (M)"; } else { parts[0] = stringifyHttpMethods(route.method); } if (route.splitPath) { parts[1] = "/" + route.splitPath.join("/"); } else { parts[1] = "/"; } if (isMergedRequestMiddleware(handler)) { parts[2] = "[merged]"; } else if (handler && typeof handler.name == "string" && handler.name.length != 0) { parts[2] = handler.name; } else if (handler && handler.prototype && typeof handler.prototype.name == "string" && handler.prototype.name.length != 0) { parts[2] = handler.prototype.name; } else { parts[2] = "[anonym]"; } return parts; } dump(...servers) { if (this.routes.length == 0) { throw new Error("No endpoint routes defined"); } let unmergedParts = []; let mergedParts = []; for (const route of this.routes) { mergedParts.push(Router.getDefinitionString(route, route.handler, false)); unmergedParts.push(...unmergeRequestMiddleware(route.handler).map((middleware, index) => Router.getDefinitionString(route, middleware, index != 0))); } const both = [ ...unmergedParts, ...mergedParts ]; const part1MinLen = both.sort((a, b) => b[0].length - a[0].length)[0][0].length; const part2MinLen = both.sort((a, b) => b[1].length - a[1].length)[0][1].length; const part3MinLen = both.sort((a, b) => b[2].length - a[2].length)[0][2].length; const lines = []; if (servers && servers.length != 0) { if (servers.length == 1) { lines.push("Server is listening on " + servers[0].url); } else { lines.push("Server is listening on:"); lines.push(...servers.map((server) => "- " + server.url)); } } lines.push("", "# Defined endpoints:", ...unmergedParts.map(([part1, part2, part3]) => "| " + part1.padEnd(part1MinLen) + " | " + part2.padEnd(part2MinLen) + " | " + part3.padEnd(part3MinLen) + " |"), ""); if (unmergedParts.length != mergedParts.length) { lines.push("# Merged endpoints:", ...mergedParts.map(([part1, part2, part3]) => "| " + part1.padEnd(part1MinLen) + " | " + part2.padEnd(part2MinLen) + " | " + part3.padEnd(part3MinLen) + " |"), ""); } return lines.join(` `); } handle = (request, server) => this.innerHandle(request, server); innerHandle(request, server) { const res = new ResponseBuilder; const req = request; req.httpMethod = parseHttpMethods(req.method); req.server = server; req.cookies = {}; req.path = new URL(req.url).pathname; req.splitPath = splitPath(req.path); const sock = req.server.requestIP(req); if (!sock) { return new Response("Request closed to early", { status: 500 }); } req.sock = sock; const p = this.route(req, res); if (p && p.then != null) { return p.then(() => { if (req.upgraded) { return; } const p3 = res.startBeforeSentHook(); if (p3 && p3.then != null) { return p3.then(() => { return res.build(); }); } return res.build(); }); } if (req.upgraded) { return; } const p2 = res.startBeforeSentHook(); if (p2 && p2.then != null) { return p2.then(() => { return res.build(); }); } return res.build(); } route(req, res) { for (let i = 0;i < this.routes.length; i++) { if (this.routes[i].method != 1 /* ALL */ && this.routes[i].method != req.httpMethod) { continue; } const pathParams = requestPathMatchesRouteDefinition(req.splitPath, this.routes[i].splitPath); if (pathParams === false) { continue; } else if (pathParams !== true) { req.pathParams = pathParams; } const p = this.routes[i].handler(req, res); if (p != null && p.then != null) { return this.routeAsync(i, p, req, res); } if (res.submit === true || req.upgraded === true) { return; } } if (req.upgraded) { return; } res.reset().status(404).body("Not found"); } async routeAsync(initialDefIndex, promise, req, res) { await promise; if (res.submit === true || req.upgraded === true) { return; } for (let i = initialDefIndex + 1;i < this.routes.length; i++) { if (this.routes[i].method != null && this.routes[i].method != req.httpMethod) { continue; } const pathParams = requestPathMatchesRouteDefinition(req.splitPath, this.routes[i].splitPath); if (pathParams === false) { continue; } else if (pathParams !== true) { req.pathParams = pathParams; } const p = this.routes[i].handler(req, res); if (p && p.then != null) { await p; } if (res.submit === true || req.upgraded === true) { return; } } if (req.upgraded) { return; } res.reset().status(404).body("Not found"); } use(method, path, handler, ...handlers) { if (typeof handler != "function") { throw new Error("no handler provided, type: " + typeof handler); } handlers = [ handler, ...handlers ]; const route = { splitPath: splitRoutePath(path), method: parseHttpMethods(method), handler }; if (this.mergeHandlers) { const lastDef = this.routes.pop(); if (lastDef) { if (isMergeableEndpointRoute(lastDef, route)) { handlers.unshift(lastDef.handler); } else { this.routes.push(lastDef); } } } route.handler = mergeRequestMiddlewares(...unmergeRequestMiddleware(...handlers)); this.routes.push(route); return this; } get(path, handler, ...handlers) { return this.use("GET", path, handler, ...handlers); } post(path, handler, ...handlers) { return this.use("POST", path, handler, ...handlers); } put(path, handler, ...handlers) { return this.use("PUT", path, handler, ...handlers); } delete(path, handler, ...handlers) { return this.use("DELETE", path, handler, ...handlers); } patch(path, handler, ...handlers) { return this.use("PATCH", path, handler, ...handlers); } trace(path, handler, ...handlers) { return this.use("TRACE", path, handler, ...handlers); } head(path, handler, ...handlers) { return this.use("HEAD", path, handler, ...handlers); } connect(path, handler, ...handlers) { return this.use("CONNECT", path, handler, ...handlers); } options(path, handler, ...handlers) { return this.use("OPTIONS", path, handler, ...handlers); } ws(path) { const wsMiddleware = (req, res) => { if (req.server.upgrade(req)) { req.upgraded = true; } }; this.use("GET", path, wsMiddleware); return this; } redirect(method, path, redirectTarget, perma = false) { const redirectMiddleware = (_, res) => res.sendRedirect(redirectTarget, perma); this.use(method, path, redirectMiddleware); return this; } static(path, targetDir, indexFile = "index.html", deepestLevel = 10) { if (!statSync(targetDir).isDirectory()) { throw new Error("static target is not a directory: " + targetDir); } const staticMiddleware = (req, res) => { if (req.path.endsWith("/" + indexFile)) { res.sendRedirect(req.path.slice(0, -indexFile.length), true); return; } let targetPath = join(targetDir, req.splitPath == undefined ? "/" : req.path); if (targetPath.endsWith("/")) { targetPath += indexFile; } if (req.splitPath != null && req.splitPath?.length > deepestLevel) { return; } try { const file = Bun.file(targetPath); return file.exists().then(async (exist) => { if (exist) { res.send(await file.arrayBuffer()); } else { res.status(404); } }).catch(() => { res.status(500, "Error while loading response content"); }); } catch (_) { res.status(500, "Error while init response content"); } }; this.use("GET", path, staticMiddleware); return this; } basicAuth(method, path, validator, realm = "User Visible Realm", charset = "UTF-8") { const basicAuthMiddleware = (req, res) => { const auth = req.headers.get("authorization"); if (!auth) { res.sendBasicAuth("Missing authorization header", realm, charset); return; } let splitIndex = auth.indexOf(" "); if (splitIndex === -1) { res.sendBasicAuth("Unprocessable authorization header", realm, charset); return; } const schema = auth.slice(0, splitIndex); if (schema !== "Basic") { res.sendBasicAuth("Unprocessable basic auth schema", realm, charset); return; } const credentials = atob(auth.slice(splitIndex + 1)); splitIndex = credentials.indexOf(":"); if (splitIndex === -1) { res.sendBasicAuth("Unprocessable basic auth credentials", realm, charset); return; } if (!validator(credentials.slice(0, splitIndex), credentials.slice(splitIndex + 1))) { res.sendBasicAuth("Invalid credentials", realm, charset); return; } }; this.use(method, path, basicAuthMiddleware); return this; } cookies(method, path, autoResponseHeaders = false) { const cookiesMiddleware = autoResponseHeaders ? (req, res) => { res.beforeSent((res2) => Router.storeCookies(req, res2)); Router.parseCookies(req); } : (req) => Router.parseCookies(req); this.use(method, path, cookiesMiddleware); return this; } } function trimSpaces(value) { while (value.startsWith(" ") || value.startsWith("\t") || value.startsWith(` `)) { value = value.slice(1); } if (value.length == 0) { return ""; } while (value.endsWith(" ") || value.endsWith("\t") || value.endsWith(` `)) { value = value.slice(0, -1); } return value; } function splitPath(path) { if (path == undefined) { return; } while (path.startsWith("/") || path.startsWith(" ")) { path = path.slice(1); } if (path.length == 0) { return; } while (path.endsWith("/") || path.endsWith(" ")) { path = path.slice(0, -1); } const splitPath2 = path.split("/").map((part) => { while (part.startsWith("/") || part.startsWith(" ")) { part = part.slice(1); } if (part.length == 0) { return ""; } while (part.endsWith("/") || part.endsWith(" ")) { part = part.slice(0, -1); } return part; }).filter((v) => v.length != 0); if (splitPath2.length == 0) { return; } return splitPath2; } function splitRoutePath(path) { const splittedPath = splitPath(path); if (splittedPath && splittedPath.length > 1 && splittedPath.slice(0, -1).includes("**")) { throw new Error("Invalid router path, ** must be the last part"); } return splittedPath; } function requestPathMatchesRouteDefinition(requestPath, routeSelector) { if (requestPath == undefined && routeSelector == undefined) { return []; } else if (routeSelector == undefined) { return false; } else if (requestPath == undefined) { if (routeSelector[0] == "**") { return true; } return false; } else if (requestPath.length == 0) { throw new Error("Invalid requestPath SplitPath length, got 0, expected at least 1"); } else if (routeSelector.length == 0) { throw new Error("Invalid routeSelector SplitPath length, got 0, expected at least 1"); } else if (routeSelector[0] == "**") { return requestPath; } else if (routeSelector.length < requestPath.length) { if (routeSelector[routeSelector.length - 1] != "**") { return false; } } let pathParams = true; for (let i = 0;i < routeSelector.length; i++) { switch (routeSelector[i]) { case "*": if (requestPath.length <= i) { return false; } if (pathParams === true) { pathParams = []; } pathParams.push(requestPath[i]); break; case "**": if (requestPath.length - i > 0) { if (pathParams === true) { pathParams = []; } pathParams.push(...requestPath.slice(i)); } return pathParams; case requestPath[i]: break; default: return false; } } return pathParams; } export { unmergeRequestMiddleware, trimSpaces, stringifyHttpMethods, splitRoutePath, splitPath, requestPathMatchesRouteDefinition, parseHttpMethods, notFoundResponse, mergeRequestMiddlewares, isMergedRequestMiddleware, isMergeableEndpointRoute, Router, ResponseBuilder, HttpMethod };