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
JavaScript
;
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;