@lottojs/lotto
Version:
Simple, lightweight and dependency-free NodeJS web application framework.
270 lines • 12.1 kB
JavaScript
;
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());
});
};
var __asyncValues = (this && this.__asyncValues) || function (o) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var m = o[Symbol.asyncIterator], i;
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Routing = void 0;
const router_utils_1 = require("./router.utils");
const router_errors_1 = require("./router.errors");
const body_parser_1 = require("@lottojs/body-parser");
const params_parser_1 = require("@lottojs/params-parser");
const utils_1 = require("../utils/utils");
const cors_1 = require("@lottojs/cors");
const secure_headers_1 = require("@lottojs/secure-headers");
const debug = (0, utils_1.toDebug)("router");
class Routing {
constructor() {
this.routes = [];
this.prefix = "/";
this.useCalls = 0;
}
match(path, byRegex, method) {
if (path.length > 1 && path.endsWith("/")) {
path = path.slice(0, -1);
}
const isMatch = this.routes.find((route) => {
if (route.method === method || !method) {
if (byRegex) {
return route.method === method && route.regExpPath.test(path);
}
return route.method === method && route.path === path;
}
return false;
});
if (!path || method === "MIDDLEWARE") {
debug(`General middleware route${!isMatch ? " not" : ""} found.`);
}
else {
debug(`Route [${method}] - ${path}${!isMatch ? " not" : ""} found.`);
}
return isMatch;
}
register(method, path, handler) {
if (this.prefix === "/") {
path = (0, utils_1.cleanPath)(path);
}
else if (!path.startsWith(this.prefix)) {
path = (0, utils_1.cleanPath)(`/${this.prefix}/${path}`);
}
else {
path = (0, utils_1.cleanPath)(path);
}
if (path.length > 1 && path.endsWith("/")) {
path = path.slice(0, -1);
}
const regExpPath = (0, router_utils_1.buildRouteParameters)(path);
const matchsRoute = this.match(path, false, method);
if (!matchsRoute) {
this.routes.push({
method,
path,
regExpPath,
handler,
middlewares: [],
group: this.useCalls,
});
if (method === "ALL" && path.includes("*")) {
debug("General middleware route, succesfully registered.");
}
else {
debug(`Route [${method}] - ${path}, succesfully registered.`);
}
}
return Boolean(matchsRoute);
}
middleware(path, middleware, method) {
if (this.prefix === "/") {
path = (0, utils_1.cleanPath)(path);
}
else if (!path.startsWith(this.prefix)) {
path = (0, utils_1.cleanPath)(`/${this.prefix}/${path}`);
}
else {
path = (0, utils_1.cleanPath)(path);
}
const matchsRoute = this.match(path, path.includes("*"), method);
if (matchsRoute) {
matchsRoute.middlewares.push(middleware);
if (path.includes("*")) {
debug("General middleware registered.");
}
else {
debug(`Middleware added to route [${matchsRoute.method}] - ${path}.`);
}
}
}
use(...input) {
if (input.length === 0)
return this;
let path;
if ((0, utils_1.isPath)(input[0]))
path = input.shift();
const mountPoint = path || this.prefix;
for (let i = 0; i < input.length; i++) {
const item = input[i];
if ((0, utils_1.isInstanceOf)(item, "Router")) {
const matchsRoute = this.match(mountPoint, false);
if (!matchsRoute) {
item.routes.forEach((route) => {
let fullRoutePath = (0, utils_1.cleanPath)(`${mountPoint}/${route.path}`);
if (fullRoutePath.length > 1 && fullRoutePath.endsWith("/")) {
fullRoutePath = fullRoutePath.slice(0, -1);
}
this.register(route.method, fullRoutePath, route.handler);
route.middlewares.forEach((mid) => {
this.middleware(fullRoutePath, {
handler: mid.handler,
group: mid.group,
}, route.method);
});
});
}
continue;
}
if (typeof item === "function") {
const matchsRoute = this.match(mountPoint, false, "ALL");
if (!matchsRoute)
this.register("ALL", "*", () => { });
this.middleware("*", { handler: item, group: this.useCalls }, "ALL");
}
}
this.useCalls = this.useCalls + 1;
return this;
}
serve(method, ...input) {
if (input.length === 2 && typeof input[1] === "function") {
this.register(method, input[0], input[1]);
}
else if (input.length === 3 &&
typeof input[1] === "object" &&
typeof input[2] === "function") {
const alreadyRegistered = this.register(method, input[0], input[2]);
if (!alreadyRegistered) {
for (const mid of input[1]) {
this.middleware(input[0], { handler: mid, group: this.useCalls }, method);
}
}
}
return this;
}
handle(ctx) {
var _a, e_1, _b, _c;
var _d, _e;
return __awaiter(this, void 0, void 0, function* () {
const { method, url } = ctx.req;
if (url === undefined)
throw new Error("@lottojs/router: Invalid URL");
(0, router_utils_1.handlerUtils)(ctx.req, ctx.res);
const isPreflight = method === "OPTIONS";
if (isPreflight) {
return (0, cors_1.cors)((_d = this.cors) !== null && _d !== void 0 ? _d : {})(ctx);
}
const route = this.match(url, true, method);
if (route) {
const allMiddlewares = [];
const rootRoute = this.match(`${this.prefix}*`, true, "ALL");
if (rootRoute) {
const { middlewares } = rootRoute;
middlewares.reverse();
try {
for (var _f = true, _g = __asyncValues(middlewares.entries()), _h; _h = yield _g.next(), _a = _h.done, !_a; _f = true) {
_c = _h.value;
_f = false;
const [idx, mid] = _c;
if (mid.group <= route.group) {
allMiddlewares.unshift(mid.handler);
debug(`Adds general middleware [${idx}] to route [${route.method}] - ${route.path}.`);
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (!_f && !_a && (_b = _g.return)) yield _b.call(_g);
}
finally { if (e_1) throw e_1.error; }
}
}
const { middlewares: routeMiddlewares, handler } = route;
for (const mid of routeMiddlewares)
allMiddlewares.push(mid.handler);
let index = 0;
const middlewareWithParsedRequest = [
(0, secure_headers_1.secureHeaders)(this.secureHeaders),
(0, cors_1.cors)((_e = this.cors) !== null && _e !== void 0 ? _e : {}),
(0, params_parser_1.paramsParser)(route.regExpPath.source),
];
if (["POST", "PUT", "PATCH"].includes(ctx.req.method)) {
middlewareWithParsedRequest.push((0, body_parser_1.bodyParser)());
}
middlewareWithParsedRequest.push(...allMiddlewares);
const next = (error) => {
if (index < middlewareWithParsedRequest.length) {
const idx = index++;
const middleware = middlewareWithParsedRequest[idx];
debug(`Calls middleware [${idx}] for route [${route.method}] - ${route.path}.`);
const regex = /(?<!["'{}])\({[^{}]*\berror\b[^{}]*}\)(?![^{}]*})/g;
const isErrorHandlingCustomMiddleware = regex.test(middleware.toString());
if ((isErrorHandlingCustomMiddleware && error) ||
(!isErrorHandlingCustomMiddleware && !error)) {
try {
if (typeof middleware === "function") {
middleware({
error,
next,
req: ctx.req,
res: ctx.res,
});
}
else if (middleware && typeof middleware === "function") {
middleware({
error,
next,
req: ctx.req,
res: ctx.res,
});
}
else {
next(new Error(`Invalid middleware at index ${idx}`));
}
}
catch (middlewareError) {
next(middlewareError);
}
}
else {
next(error);
}
}
else {
if (error) {
throw error;
}
else {
handler({ req: ctx.req, res: ctx.res, next });
}
}
};
next();
}
else {
(0, router_errors_1.notFoundError)(ctx.res);
}
});
}
}
exports.Routing = Routing;
//# sourceMappingURL=routing.js.map