import {URL} from "node:url"; const degroup = path => Object.assign(path, path.groups); export default class Router { constructor() { this.routes = []; } route(req, res) { const pathname = new URL(req.url, "file:").pathname; // TODO double-check return this.routes.some(route => { const isMatch = route.method === req.method && route.re.test(pathname); if (isMatch) route.cb(req, res, degroup(route.re.exec(pathname))); return isMatch; }); } gather(cb, max) { // Handle POST data return (req, res, match) => { let data = ""; req.on("data", chunk => { if ((data += chunk).length > (max||1e6)) // ~1MB req.connection.destroy(); }).on("end", () => cb(req, res, match, data)); }; } gpost(re, cb, max) { this.post(re, this.gather(cb, max)); } jpost(re, cb, max) { // TODO check req content-type? set accepts? this.gpost(re, (req, res, match, data) => { try { data = JSON.parse(data); } catch (e) { res.writeHead(400, {"Content-Type": "text/plain"}); return res.end("400 Bad Request"); } cb(req, res, match, data); }, max); } } ["get", "post", "put", "delete"].forEach(method => Router.prototype[method] = function(re, cb) { this.routes.push({method: method.toUpperCase(), cb, re: (re instanceof RegExp)? re : new RegExp(`^${re}$`)})});