var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import fs from "node:fs"; import path from "node:path"; export default class Router { constructor() { this.routes = []; this.mimes = new Map(); } importRoutesFromPath(p) { return __awaiter(this, void 0, void 0, function* () { const dirEntries = yield fs.promises.readdir(path.resolve() + '/' + p, { withFileTypes: true }); for (const tmp of dirEntries) { if (tmp.isFile() && (tmp.name.endsWith(".mjs") || tmp.name.endsWith(".js"))) { const routeModule = (yield import(`${path.resolve()}/${p}/${tmp.name}`)).default; this.use(routeModule(this)); } else if (tmp.isDirectory()) { yield this.importRoutesFromPath(p + '/' + tmp.name); } } return this; }); } group(basePath, callback) { const self = this; const methods = { get(path, ...handlers) { const fullPath = self.combinePaths(basePath, path); return self.registerRoute(fullPath, "get", handlers); }, post(path, ...handlers) { const fullPath = self.combinePaths(basePath, path); return self.registerRoute(fullPath, "post", handlers); }, put(path, ...handlers) { const fullPath = self.combinePaths(basePath, path); return self.registerRoute(fullPath, "put", handlers); }, delete(path, ...handlers) { const fullPath = self.combinePaths(basePath, path); return self.registerRoute(fullPath, "delete", handlers); }, patch(path, ...handlers) { const fullPath = self.combinePaths(basePath, path); return self.registerRoute(fullPath, "patch", handlers); } }; callback(methods); return this; } combinePaths(basePath, subPath) { if (typeof basePath === "string" && typeof subPath === "string") return `${basePath.replace(/\/$/, "")}/${subPath.replace(/^\//, "")}`; if (basePath instanceof RegExp && typeof subPath === "string") return new RegExp(`${basePath.source}${subPath.replace(/^\//, "")}`); if (typeof basePath === "string" && subPath instanceof RegExp) return new RegExp(`${basePath.replace(/\/$/, "")}${subPath.source}`); if (basePath instanceof RegExp && subPath instanceof RegExp) return new RegExp(`${basePath.source}${subPath.source}`); throw new TypeError("Invalid path types. Both basePath and subPath must be either string or RegExp."); } use(obj) { if (obj instanceof Router) { if (!Array.isArray(obj.routes)) throw new TypeError("Routes must be an array."); this.routes = [...this.routes, ...obj.routes]; this.sortRoutes(); } } get(path, ...callback) { this.registerRoute(path, "get", callback); return this; } post(path, ...callback) { this.registerRoute(path, "post", callback); return this; } head(path, ...callback) { this.registerRoute(path, "head", callback); return this; } put(path, ...callback) { this.registerRoute(path, "put", callback); return this; } delete(path, ...callback) { this.registerRoute(path, "delete", callback); return this; } patch(path, ...callback) { this.registerRoute(path, "patch", callback); return this; } registerRoute(path, method, handler) { const route = this.routes.find(route => typeof route.path === "string" ? route.path === path : route.path instanceof RegExp && path instanceof RegExp && route.path.toString() === path.toString()); if (route) { route.methods[method] = [ ...(route.methods[method] || []), ...handler ]; } else { this.routes.push({ path, methods: { [method]: handler } }); } console.log("route set:", method.toUpperCase(), path); this.sortRoutes(); return this; } getRoute(path, method) { return this.routes .find(r => { var _a, _b; return typeof r.path === "string" ? r.path === path : ((_b = (_a = r.path).exec) === null || _b === void 0 ? void 0 : _b.call(_a, path)) && r.methods[method.toLowerCase()]; }); } sortRoutes() { this.routes.sort((a, b) => { const aLength = typeof a.path === "string" ? a.path.length : a.path.source.length; const bLength = typeof b.path === "string" ? b.path.length : b.path.source.length; if (typeof a.path === "string" && typeof b.path === "string") return bLength - aLength; if (typeof a.path === "string") return -1; if (typeof b.path === "string") return 1; return bLength - aLength; }); return this; } readMimes(file = "/etc/mime.types") { fs.readFileSync(file, "utf-8") .split("\n") .filter(line => !line.startsWith("#") && line) .forEach(line => { const [mimeType, extensions] = line.split(/\s+/); extensions === null || extensions === void 0 ? void 0 : extensions.split(" ").forEach(ext => this.mimes.set(ext, mimeType)); }); } static({ dir = path.resolve() + "/public", route = /^\/public/ }) { if (!this.mimes.size) this.readMimes(); this.get(route, (req, res, next) => { try { const filename = req.url.pathname.replace(route, "") || "index.html"; const mime = this.mimes.get(filename.split(".").pop() || ""); const file = path.join(dir, filename); const stat = fs.statSync(file); if (req.headers.range) { const [startStr, endStr] = req.headers.range.replace(/bytes=/, "").split("-"); const start = parseInt(startStr, 10); const end = endStr ? parseInt(endStr, 10) : stat.size - 1; res.writeHead(206, { "Content-Range": `bytes ${start}-${end}/${stat.size}`, "Accept-Ranges": "bytes", "Content-Length": end - start + 1, "Content-Type": mime, }); fs.createReadStream(file, { start, end }).pipe(res); } else { res.writeHead(200, { "Content-Length": stat.size, "Content-Type": mime, }); fs.createReadStream(file).pipe(res); } } catch (err) { console.error(err); res.reply({ code: 404, body: "404 - File not found" }); } }); return this; } }