Compare commits
1 Commits
main
...
f0ea182d2f
Author | SHA1 | Date | |
---|---|---|---|
f0ea182d2f |
@@ -80,10 +80,10 @@ describe("Router", () => {
|
|||||||
it("should execute global middleware", async () => {
|
it("should execute global middleware", async () => {
|
||||||
let middlewareExecuted = false;
|
let middlewareExecuted = false;
|
||||||
|
|
||||||
// router.use(async (_ctx, next) => {
|
router.use(async (_, _ctx, next) => {
|
||||||
// middlewareExecuted = true;
|
middlewareExecuted = true;
|
||||||
// return await next();
|
return await next();
|
||||||
// });
|
});
|
||||||
|
|
||||||
router.route("/test")
|
router.route("/test")
|
||||||
.get(async () => new Response("test"));
|
.get(async () => new Response("test"));
|
||||||
@@ -101,8 +101,7 @@ describe("Router", () => {
|
|||||||
|
|
||||||
router.use("/test", async (_, _ctx, next) => {
|
router.use("/test", async (_, _ctx, next) => {
|
||||||
middlewareExecuted = true;
|
middlewareExecuted = true;
|
||||||
console.log("middlware happened");
|
return await next();
|
||||||
return await next() ?? new Response("unresolved stack");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
router.route("/test")
|
router.route("/test")
|
||||||
@@ -121,7 +120,7 @@ describe("Router", () => {
|
|||||||
|
|
||||||
router.use("/:version/.*", async (_, ctx, next) => {
|
router.use("/:version/.*", async (_, ctx, next) => {
|
||||||
capturedParams.version = ctx.params.version;
|
capturedParams.version = ctx.params.version;
|
||||||
return await next() ?? new Response("unresolved stack");
|
return await next();
|
||||||
});
|
});
|
||||||
|
|
||||||
router.route("/:version/test")
|
router.route("/:version/test")
|
||||||
@@ -136,16 +135,16 @@ describe("Router", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should execute middleware in correct order", async () => {
|
it("should execute middleware in correct order", async () => {
|
||||||
const order: number[] = [1];
|
const order: number[] = [];
|
||||||
|
|
||||||
// router.use(async (_, _ctx, next) => {
|
router.use(async (_, _ctx, next) => {
|
||||||
// order.push(1);
|
order.push(1);
|
||||||
// return await next()?? new Response("unresolved stack");
|
return await next();
|
||||||
// });
|
});
|
||||||
|
|
||||||
router.use("/test", async (_, _ctx, next) => {
|
router.use("/test", async (_, _ctx, next) => {
|
||||||
order.push(2);
|
order.push(2);
|
||||||
return await next() ?? new Response("unresolved stack");
|
return await next();
|
||||||
});
|
});
|
||||||
|
|
||||||
router.route("/test")
|
router.route("/test")
|
||||||
@@ -198,10 +197,10 @@ describe("Router", () => {
|
|||||||
const apiRouter = new Router();
|
const apiRouter = new Router();
|
||||||
let middlewareExecuted = false;
|
let middlewareExecuted = false;
|
||||||
|
|
||||||
// apiRouter.use(async (_,_ctx, next) => {
|
apiRouter.use(async (_, _ctx, next) => {
|
||||||
// middlewareExecuted = true;
|
middlewareExecuted = true;
|
||||||
// return await next();
|
return await next();
|
||||||
// });
|
});
|
||||||
|
|
||||||
apiRouter.route("/test")
|
apiRouter.route("/test")
|
||||||
.get(async () => new Response("test"));
|
.get(async () => new Response("test"));
|
||||||
@@ -219,10 +218,10 @@ describe("Router", () => {
|
|||||||
|
|
||||||
describe("Context State", () => {
|
describe("Context State", () => {
|
||||||
it("should maintain state across middleware chain", async () => {
|
it("should maintain state across middleware chain", async () => {
|
||||||
// router.use(async (_,ctx, next) => {
|
router.use(async (_, ctx, next) => {
|
||||||
// ctx.state.test = "value";
|
ctx.state.test = "value";
|
||||||
// return await next();
|
return await next();
|
||||||
// });
|
});
|
||||||
|
|
||||||
router.route("/test")
|
router.route("/test")
|
||||||
.get(async (_, ctx) => {
|
.get(async (_, ctx) => {
|
||||||
@@ -255,9 +254,9 @@ describe("Router", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should handle errors in middleware", async () => {
|
it("should handle errors in middleware", async () => {
|
||||||
// router.use(async () => {
|
router.use(async () => {
|
||||||
// throw new Error("Middleware error");
|
throw new Error("Middleware error");
|
||||||
// });
|
});
|
||||||
|
|
||||||
router.route("/test")
|
router.route("/test")
|
||||||
.get(async () => new Response("test"));
|
.get(async () => new Response("test"));
|
||||||
|
115
router.new.ts
115
router.new.ts
@@ -1,11 +1,12 @@
|
|||||||
import type { RouterContext as oldContext } from "@bearmetal/router/types";
|
import type { RouterContext as oldContext } from "@bearmetal/router/types";
|
||||||
import { joinPaths } from "./util/join.ts";
|
import { joinPaths } from "./util/join.ts";
|
||||||
|
import { InternalError, NotFound } from "@bearmetal/router/util/response";
|
||||||
|
|
||||||
type RouterContext = Omit<oldContext, "pattern">;
|
type RouterContext = Omit<oldContext, "pattern">;
|
||||||
type Handler = (
|
type Handler = (
|
||||||
req: Request,
|
req: Request,
|
||||||
ctx: RouterContext,
|
ctx: RouterContext,
|
||||||
next: () => Promise<void | Response> | null,
|
next: () => Promise<Response>,
|
||||||
) => Promise<Response>;
|
) => Promise<Response>;
|
||||||
type RouteConfigurator = {
|
type RouteConfigurator = {
|
||||||
get(handler: Handler): RouteConfigurator;
|
get(handler: Handler): RouteConfigurator;
|
||||||
@@ -95,44 +96,93 @@ export class Router {
|
|||||||
return configurator;
|
return configurator;
|
||||||
}
|
}
|
||||||
|
|
||||||
get(path: string, handler: Handler) {
|
get(handler: Handler): void;
|
||||||
path = fixPath(path);
|
get(path: string, handler: Handler): void;
|
||||||
|
get(path: string | Handler, handler?: Handler): void {
|
||||||
|
if (typeof path !== "string") {
|
||||||
|
handler = path;
|
||||||
|
path = "/.*";
|
||||||
|
} else {
|
||||||
|
path = fixPath(path);
|
||||||
|
}
|
||||||
const config = this.getOrCreateConfig(path);
|
const config = this.getOrCreateConfig(path);
|
||||||
this.getOrCreateConfigHandlers(GET, config).push(handler);
|
this.getOrCreateConfigHandlers(GET, config).push(handler!);
|
||||||
}
|
}
|
||||||
post(path: string, handler: Handler) {
|
post(handler: Handler): void;
|
||||||
path = fixPath(path);
|
post(path: string, handler: Handler): void;
|
||||||
|
post(path: string | Handler, handler?: Handler): void {
|
||||||
|
if (typeof path !== "string") {
|
||||||
|
handler = path;
|
||||||
|
path = "/.*";
|
||||||
|
} else {
|
||||||
|
path = fixPath(path);
|
||||||
|
}
|
||||||
const config = this.getOrCreateConfig(path);
|
const config = this.getOrCreateConfig(path);
|
||||||
this.getOrCreateConfigHandlers(POST, config).push(handler);
|
this.getOrCreateConfigHandlers(POST, config).push(handler!);
|
||||||
}
|
}
|
||||||
put(path: string, handler: Handler) {
|
put(handler: Handler): void;
|
||||||
path = fixPath(path);
|
put(path: string, handler: Handler): void;
|
||||||
|
put(path: string | Handler, handler?: Handler): void {
|
||||||
|
if (typeof path !== "string") {
|
||||||
|
handler = path;
|
||||||
|
path = "/.*";
|
||||||
|
} else {
|
||||||
|
path = fixPath(path);
|
||||||
|
}
|
||||||
const config = this.getOrCreateConfig(path);
|
const config = this.getOrCreateConfig(path);
|
||||||
this.getOrCreateConfigHandlers(PUT, config).push(handler);
|
this.getOrCreateConfigHandlers(PUT, config).push(handler!);
|
||||||
}
|
}
|
||||||
patch(path: string, handler: Handler) {
|
patch(handler: Handler): void;
|
||||||
path = fixPath(path);
|
patch(path: string, handler: Handler): void;
|
||||||
|
patch(path: string | Handler, handler?: Handler): void {
|
||||||
|
if (typeof path !== "string") {
|
||||||
|
handler = path;
|
||||||
|
path = "/.*";
|
||||||
|
} else {
|
||||||
|
path = fixPath(path);
|
||||||
|
}
|
||||||
const config = this.getOrCreateConfig(path);
|
const config = this.getOrCreateConfig(path);
|
||||||
this.getOrCreateConfigHandlers(PATCH, config).push(handler);
|
this.getOrCreateConfigHandlers(PATCH, config).push(handler!);
|
||||||
}
|
}
|
||||||
delete(path: string, handler: Handler) {
|
delete(handler: Handler): void;
|
||||||
path = fixPath(path);
|
delete(path: string, handler: Handler): void;
|
||||||
|
delete(path: string | Handler, handler?: Handler): void {
|
||||||
|
if (typeof path !== "string") {
|
||||||
|
handler = path;
|
||||||
|
path = "/.*";
|
||||||
|
} else {
|
||||||
|
path = fixPath(path);
|
||||||
|
}
|
||||||
const config = this.getOrCreateConfig(path);
|
const config = this.getOrCreateConfig(path);
|
||||||
this.getOrCreateConfigHandlers(DELETE, config).push(handler);
|
this.getOrCreateConfigHandlers(DELETE, config).push(handler!);
|
||||||
}
|
}
|
||||||
options(path: string, handler: Handler) {
|
options(handler: Handler): void;
|
||||||
path = fixPath(path);
|
options(path: string, handler: Handler): void;
|
||||||
|
options(path: string | Handler, handler?: Handler): void {
|
||||||
|
if (typeof path !== "string") {
|
||||||
|
handler = path;
|
||||||
|
path = "/.*";
|
||||||
|
} else {
|
||||||
|
path = fixPath(path);
|
||||||
|
}
|
||||||
const config = this.getOrCreateConfig(path);
|
const config = this.getOrCreateConfig(path);
|
||||||
this.getOrCreateConfigHandlers(OPTIONS, config).push(handler);
|
this.getOrCreateConfigHandlers(OPTIONS, config).push(handler!);
|
||||||
}
|
}
|
||||||
|
|
||||||
use(path: string, handler: Handler | Router) {
|
use(handler: Handler | Router): void;
|
||||||
path = fixPath(path);
|
use(path: string, handler: Handler | Router): void;
|
||||||
|
use(path: string | Handler | Router, handler?: Handler | Router): void {
|
||||||
|
if (typeof path !== "string") {
|
||||||
|
handler = path;
|
||||||
|
path = "/.*";
|
||||||
|
} else {
|
||||||
|
path = fixPath(path);
|
||||||
|
}
|
||||||
if (handler instanceof Router) {
|
if (handler instanceof Router) {
|
||||||
return this.resolveRouterHandlerStack(path, handler);
|
return this.resolveRouterHandlerStack(path, handler);
|
||||||
}
|
}
|
||||||
const config = this.getOrCreateConfig(path);
|
const config = this.getOrCreateConfig(path);
|
||||||
this.getOrCreateConfigHandlers(_use, config).push(handler);
|
this.getOrCreateConfigHandlers(_use, config).push(handler!);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getOrCreateConfig(path: string): RouteConfig {
|
private getOrCreateConfig(path: string): RouteConfig {
|
||||||
@@ -142,6 +192,7 @@ export class Router {
|
|||||||
handlers: {},
|
handlers: {},
|
||||||
pattern: new URLPattern({ pathname: path }),
|
pattern: new URLPattern({ pathname: path }),
|
||||||
};
|
};
|
||||||
|
this.routes.set(path, config);
|
||||||
}
|
}
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
@@ -172,12 +223,16 @@ export class Router {
|
|||||||
const url = new URL(req.url);
|
const url = new URL(req.url);
|
||||||
const method = req.method;
|
const method = req.method;
|
||||||
|
|
||||||
const matchingRoutes = this.findMatchingRoutes(url).filter((r) =>
|
const matchingRoutes = this.findMatchingRoutes(url);
|
||||||
Object.hasOwn(r.config.handlers, method) ||
|
if (!matchingRoutes.length) return NotFound();
|
||||||
Object.hasOwn(r.config.handlers, _use)
|
const matchingMethods = matchingRoutes.some((r) =>
|
||||||
|
Object.hasOwn(r.config.handlers, method)
|
||||||
);
|
);
|
||||||
|
if (!matchingMethods) {
|
||||||
|
return new Response("Method Not Allowed", { status: 405 });
|
||||||
|
}
|
||||||
const middlewareStack = matchingRoutes.flatMap((r) =>
|
const middlewareStack = matchingRoutes.flatMap((r) =>
|
||||||
r.config.handlers[method]
|
(r.config.handlers[_use] ?? []).concat(r.config.handlers[method] ?? [])
|
||||||
);
|
);
|
||||||
const ctx: RouterContext = {
|
const ctx: RouterContext = {
|
||||||
url,
|
url,
|
||||||
@@ -189,7 +244,11 @@ export class Router {
|
|||||||
let index = 0;
|
let index = 0;
|
||||||
const executeMiddleware = async (): Promise<Response> => {
|
const executeMiddleware = async (): Promise<Response> => {
|
||||||
if (index < middlewareStack.length) {
|
if (index < middlewareStack.length) {
|
||||||
const res = await middlewareStack[index++](req, ctx, executeMiddleware);
|
const res = await middlewareStack[index++]?.(
|
||||||
|
req,
|
||||||
|
ctx,
|
||||||
|
executeMiddleware,
|
||||||
|
);
|
||||||
if (res instanceof Response) return res;
|
if (res instanceof Response) return res;
|
||||||
}
|
}
|
||||||
return new Response("End of stack", { status: 501 });
|
return new Response("End of stack", { status: 501 });
|
||||||
@@ -197,7 +256,7 @@ export class Router {
|
|||||||
try {
|
try {
|
||||||
return await executeMiddleware();
|
return await executeMiddleware();
|
||||||
} catch {
|
} catch {
|
||||||
return new Response("Internal Server Error", { status: 500 });
|
return InternalError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,2 +1,3 @@
|
|||||||
export const joinPaths = (...args: string[]) =>
|
export const joinPaths = (...args: string[]) =>
|
||||||
args.map((a) => a.replace(/\/?\*?$/, "")).join();
|
args.map((a, i, l) => i === l.length - 1 ? a : a.replace(/\/?\.?\*?$/, ""))
|
||||||
|
.join("");
|
||||||
|
Reference in New Issue
Block a user