bun-route
Version:
A fast, Express-like router for the high-performance bun.serve() HTTP server.
926 lines (922 loc) • 29.5 kB
JavaScript
// 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
var L = Object.create;
var h = Object.defineProperty;
var D = Object.getOwnPropertyDescriptor;
var T = Object.getOwnPropertyNames;
var _ = Object.getPrototypeOf;
var E = Object.prototype.hasOwnProperty;
var R = (s, e) => () => (e || s((e = { exports: {} }).exports, e), e.exports);
var N = (s, e, r, t) => {
if (e && typeof e == "object" || typeof e == "function")
for (let i of T(e))
!E.call(s, i) && i !== r && h(s, i, { get: () => e[i], enumerable: !(t = D(e, i)) || t.enumerable });
return s;
};
var j = (s, e, r) => (r = s != null ? L(_(s)) : {}, N(e || !s || !s.__esModule ? h(r, "default", { value: s, enumerable: true }) : r, s));
var k = R((W, w) => {
function v(s) {
if (typeof s != "string")
throw new TypeError("Path must be a string. Received " + JSON.stringify(s));
}
function C(s, e) {
for (var r = "", t = 0, i = -1, a = 0, n, l = 0;l <= s.length; ++l) {
if (l < s.length)
n = s.charCodeAt(l);
else {
if (n === 47)
break;
n = 47;
}
if (n === 47) {
if (!(i === l - 1 || a === 1))
if (i !== l - 1 && a === 2) {
if (r.length < 2 || t !== 2 || r.charCodeAt(r.length - 1) !== 46 || r.charCodeAt(r.length - 2) !== 46) {
if (r.length > 2) {
var f = r.lastIndexOf("/");
if (f !== r.length - 1) {
f === -1 ? (r = "", t = 0) : (r = r.slice(0, f), t = r.length - 1 - r.lastIndexOf("/")), i = l, a = 0;
continue;
}
} else if (r.length === 2 || r.length === 1) {
r = "", t = 0, i = l, a = 0;
continue;
}
}
e && (r.length > 0 ? r += "/.." : r = "..", t = 2);
} else
r.length > 0 ? r += "/" + s.slice(i + 1, l) : r = s.slice(i + 1, l), t = l - i - 1;
i = l, a = 0;
} else
n === 46 && a !== -1 ? ++a : a = -1;
}
return r;
}
function F(s, e) {
var r = e.dir || e.root, t = e.base || (e.name || "") + (e.ext || "");
return r ? r === e.root ? r + t : r + s + t : t;
}
var m = { resolve: function() {
for (var e = "", r = false, t, i = arguments.length - 1;i >= -1 && !r; i--) {
var a;
i >= 0 ? a = arguments[i] : (t === undefined && (t = process.cwd()), a = t), v(a), a.length !== 0 && (e = a + "/" + e, r = a.charCodeAt(0) === 47);
}
return e = C(e, !r), r ? e.length > 0 ? "/" + e : "/" : e.length > 0 ? e : ".";
}, normalize: function(e) {
if (v(e), e.length === 0)
return ".";
var r = e.charCodeAt(0) === 47, t = e.charCodeAt(e.length - 1) === 47;
return e = C(e, !r), e.length === 0 && !r && (e = "."), e.length > 0 && t && (e += "/"), r ? "/" + e : e;
}, isAbsolute: function(e) {
return v(e), e.length > 0 && e.charCodeAt(0) === 47;
}, join: function() {
if (arguments.length === 0)
return ".";
for (var e, r = 0;r < arguments.length; ++r) {
var t = arguments[r];
v(t), t.length > 0 && (e === undefined ? e = t : e += "/" + t);
}
return e === undefined ? "." : m.normalize(e);
}, relative: function(e, r) {
if (v(e), v(r), e === r || (e = m.resolve(e), r = m.resolve(r), e === r))
return "";
for (var t = 1;t < e.length && e.charCodeAt(t) === 47; ++t)
;
for (var i = e.length, a = i - t, n = 1;n < r.length && r.charCodeAt(n) === 47; ++n)
;
for (var l = r.length, f = l - n, c = a < f ? a : f, d = -1, o = 0;o <= c; ++o) {
if (o === c) {
if (f > c) {
if (r.charCodeAt(n + o) === 47)
return r.slice(n + o + 1);
if (o === 0)
return r.slice(n + o);
} else
a > c && (e.charCodeAt(t + o) === 47 ? d = o : o === 0 && (d = 0));
break;
}
var A = e.charCodeAt(t + o), z = r.charCodeAt(n + o);
if (A !== z)
break;
A === 47 && (d = o);
}
var b = "";
for (o = t + d + 1;o <= i; ++o)
(o === i || e.charCodeAt(o) === 47) && (b.length === 0 ? b += ".." : b += "/..");
return b.length > 0 ? b + r.slice(n + d) : (n += d, r.charCodeAt(n) === 47 && ++n, r.slice(n));
}, _makeLong: function(e) {
return e;
}, dirname: function(e) {
if (v(e), e.length === 0)
return ".";
for (var r = e.charCodeAt(0), t = r === 47, i = -1, a = true, n = e.length - 1;n >= 1; --n)
if (r = e.charCodeAt(n), r === 47) {
if (!a) {
i = n;
break;
}
} else
a = false;
return i === -1 ? t ? "/" : "." : t && i === 1 ? "//" : e.slice(0, i);
}, basename: function(e, r) {
if (r !== undefined && typeof r != "string")
throw new TypeError('"ext" argument must be a string');
v(e);
var t = 0, i = -1, a = true, n;
if (r !== undefined && r.length > 0 && r.length <= e.length) {
if (r.length === e.length && r === e)
return "";
var l = r.length - 1, f = -1;
for (n = e.length - 1;n >= 0; --n) {
var c = e.charCodeAt(n);
if (c === 47) {
if (!a) {
t = n + 1;
break;
}
} else
f === -1 && (a = false, f = n + 1), l >= 0 && (c === r.charCodeAt(l) ? --l === -1 && (i = n) : (l = -1, i = f));
}
return t === i ? i = f : i === -1 && (i = e.length), e.slice(t, i);
} else {
for (n = e.length - 1;n >= 0; --n)
if (e.charCodeAt(n) === 47) {
if (!a) {
t = n + 1;
break;
}
} else
i === -1 && (a = false, i = n + 1);
return i === -1 ? "" : e.slice(t, i);
}
}, extname: function(e) {
v(e);
for (var r = -1, t = 0, i = -1, a = true, n = 0, l = e.length - 1;l >= 0; --l) {
var f = e.charCodeAt(l);
if (f === 47) {
if (!a) {
t = l + 1;
break;
}
continue;
}
i === -1 && (a = false, i = l + 1), f === 46 ? r === -1 ? r = l : n !== 1 && (n = 1) : r !== -1 && (n = -1);
}
return r === -1 || i === -1 || n === 0 || n === 1 && r === i - 1 && r === t + 1 ? "" : e.slice(r, i);
}, format: function(e) {
if (e === null || typeof e != "object")
throw new TypeError('The "pathObject" argument must be of type Object. Received type ' + typeof e);
return F("/", e);
}, parse: function(e) {
v(e);
var r = { root: "", dir: "", base: "", ext: "", name: "" };
if (e.length === 0)
return r;
var t = e.charCodeAt(0), i = t === 47, a;
i ? (r.root = "/", a = 1) : a = 0;
for (var n = -1, l = 0, f = -1, c = true, d = e.length - 1, o = 0;d >= a; --d) {
if (t = e.charCodeAt(d), t === 47) {
if (!c) {
l = d + 1;
break;
}
continue;
}
f === -1 && (c = false, f = d + 1), t === 46 ? n === -1 ? n = d : o !== 1 && (o = 1) : n !== -1 && (o = -1);
}
return n === -1 || f === -1 || o === 0 || o === 1 && n === f - 1 && n === l + 1 ? f !== -1 && (l === 0 && i ? r.base = r.name = e.slice(1, f) : r.base = r.name = e.slice(l, f)) : (l === 0 && i ? (r.name = e.slice(1, n), r.base = e.slice(1, f)) : (r.name = e.slice(l, n), r.base = e.slice(l, f)), r.ext = e.slice(n, f)), l > 0 ? r.dir = e.slice(0, l - 1) : i && (r.dir = "/"), r;
}, sep: "/", delimiter: ":", win32: null, posix: null };
m.posix = m;
w.exports = m;
});
var x = j(k());
var u = x;
var J = x;
var P = function(s) {
return s;
};
var S = function() {
throw new Error("Not implemented");
};
u.parse ??= S;
J.parse ??= S;
var g = { resolve: u.resolve.bind(u), normalize: u.normalize.bind(u), isAbsolute: u.isAbsolute.bind(u), join: u.join.bind(u), relative: u.relative.bind(u), toNamespacedPath: P, dirname: u.dirname.bind(u), basename: u.basename.bind(u), extname: u.extname.bind(u), format: u.format.bind(u), parse: u.parse.bind(u), sep: "/", delimiter: ":", win32: undefined, posix: undefined, _makeLong: P };
var y = { sep: "\\", delimiter: ";", win32: undefined, ...g, posix: g };
g.win32 = y.win32 = y;
g.posix = g;
var { resolve: B, normalize: G, isAbsolute: H, join: K, relative: Q, toNamespacedPath: U, dirname: V, basename: X, extname: Y, format: Z, parse: $, sep: I, delimiter: O } = g;
// 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 = (_2, 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 = K(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 (_2) {
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
};