UNPKG

dewy

Version:

Dewy(dǝw-y) is a minimalist HTTP server framework with a small codebase, utilizing built-in URLPattern for efficient routing.

211 lines (210 loc) 7.63 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Router = void 0; const dntShim = __importStar(require("./_dnt.shims.js")); const route_not_found_error_js_1 = require("./error/route_not_found_error.js"); const server_error_js_1 = require("./error/server_error.js"); /** @internal */ function normalizePath(path) { return "/" + path.replace(/^\/+|\/+$/g, ""); } /** @internal */ function joinPath(path, otherPath) { return normalizePath(path.replace(/\/+$/g, "") + "/" + otherPath.replace(/^\/+/g, "")); } /** @internal */ function normalizePatterns(group, pattern) { const prefix = group?.prefix ?? null; const domains = group?.domains ?? []; if (domains.length === 0) { domains.push(null); } return domains.map((domain) => { if (prefix) { if (typeof pattern === "string") { return new dntShim.URLPattern({ ...domain ? { hostname: domain } : {}, pathname: joinPath(prefix, pattern), }); } return new dntShim.URLPattern({ ...pattern, ...domain ? { hostname: domain } : {}, pathname: joinPath(prefix, pattern.pathname ?? ""), }); } if (typeof pattern === "string") { return new dntShim.URLPattern({ ...domain ? { hostname: domain } : {}, pathname: normalizePath(pattern), }); } if (domain) { return new dntShim.URLPattern({ ...pattern, hostname: domain, }); } if (pattern instanceof dntShim.URLPattern) { return pattern; } return new dntShim.URLPattern(pattern); }); } const ALL_METHOD = ["GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"]; const defaultErrorHandler = (error) => { if (error instanceof server_error_js_1.ServerError) { return new Response(error.message, error.init); } if (error instanceof route_not_found_error_js_1.RouteNotFoundError) { return new Response("Not Found", { status: 404 }); } return new Response("Internal Server Error", { status: 500, }); }; class Router { constructor(options = {}) { Object.defineProperty(this, "_groups", { enumerable: true, configurable: true, writable: true, value: [] }); Object.defineProperty(this, "_definedMiddlewares", { enumerable: true, configurable: true, writable: true, value: [] }); Object.defineProperty(this, "_routes", { enumerable: true, configurable: true, writable: true, value: new Map() }); Object.defineProperty(this, "_errorHandler", { enumerable: true, configurable: true, writable: true, value: void 0 }); this._errorHandler = options.errorHandler ?? defaultErrorHandler; } use(...middlewares) { this._definedMiddlewares.push(...middlewares); } group({ domain, prefix, middleware }, handler) { const currentDomains = domain ? (Array.isArray(domain) ? domain : [domain]) : []; const currentMiddlewares = middleware ? (Array.isArray(middleware) ? middleware : [middleware]) : []; const lastGroup = this._groups.at(-1) ?? null; const group = { domains: (lastGroup?.domains ?? []).concat(currentDomains), prefix: joinPath(lastGroup?.prefix ?? "", prefix ?? ""), middlewares: (lastGroup?.middlewares ?? []).concat(currentMiddlewares), }; this._groups.push(group); handler(); this._groups.pop(); } get(pattern, fn) { return this.addRoute({ method: ["GET", "HEAD"], pattern }, fn); } head(pattern, fn) { return this.addRoute({ method: ["HEAD"], pattern }, fn); } post(pattern, fn) { return this.addRoute({ method: ["POST"], pattern }, fn); } put(pattern, fn) { return this.addRoute({ method: ["PUT"], pattern }, fn); } del(pattern, fn) { return this.addRoute({ method: ["DELETE"], pattern }, fn); } options(pattern, fn) { return this.addRoute({ method: ["OPTIONS"], pattern }, fn); } patch(pattern, fn) { return this.addRoute({ method: ["PATCH"], pattern }, fn); } all(pattern, fn) { return this.addRoute({ method: ALL_METHOD, pattern }, fn); } addRoute(route, fn) { const methods = Array.isArray(route.method) ? route.method : [route.method]; const middlewares = route.middleware ? (Array.isArray(route.middleware) ? route.middleware : [route.middleware]) : []; for (let method of methods) { method = method.trim().toUpperCase(); let routeGroup = this._routes.get(method); if (!routeGroup) { routeGroup = []; this._routes.set(method, routeGroup); } const group = this._groups.at(-1) ?? null; for (const pattern of normalizePatterns(group, route.pattern)) { routeGroup.push([pattern, fn, [ ...this._definedMiddlewares, ...group?.middlewares ?? [], ...middlewares, ]]); } } } async dispatch(request) { async function execute(middlewares, fn, ctx) { if (middlewares.length === 0) { return await fn(ctx); } const [middleware, ...nextMiddlewares] = middlewares; return await middleware(ctx, async (nextCtx) => { return await execute(nextMiddlewares, fn, nextCtx ?? ctx); }); } try { const routes = this._routes.get(request.method.toUpperCase()) ?? []; for (const [pattern, fn, middlewares] of routes) { const match = pattern.exec(request.url); if (match) { return await execute(middlewares, fn, { match, request }); } } throw new route_not_found_error_js_1.RouteNotFoundError("Not Found"); } catch (e) { return await this._errorHandler(e); } } } exports.Router = Router;