refactor types and improve handler processing
This commit is contained in:
		
							
								
								
									
										166
									
								
								src/index.ts
									
									
									
									
									
								
							
							
						
						
									
										166
									
								
								src/index.ts
									
									
									
									
									
								
							@@ -2,47 +2,74 @@ import http, { IncomingMessage, ServerResponse } from "node:http";
 | 
			
		||||
import { URL } from "node:url";
 | 
			
		||||
import querystring from "node:querystring";
 | 
			
		||||
 | 
			
		||||
import Router from "./router";
 | 
			
		||||
import Tpl from "./template";
 | 
			
		||||
import Router from "./router.js";
 | 
			
		||||
import Tpl from "./template.js";
 | 
			
		||||
 | 
			
		||||
import { Request, Response, Middleware } from "./types";
 | 
			
		||||
import { Request, Response, Handler } from "./types.js";
 | 
			
		||||
 | 
			
		||||
export { Router, Tpl };
 | 
			
		||||
 | 
			
		||||
export default class Flummpress {
 | 
			
		||||
  private server?: http.Server;
 | 
			
		||||
  private errorHandler: Middleware;
 | 
			
		||||
  router: Router;
 | 
			
		||||
  tpl: Tpl;
 | 
			
		||||
  middleware: Middleware[];
 | 
			
		||||
  middleware: Handler[];
 | 
			
		||||
 | 
			
		||||
  constructor() {
 | 
			
		||||
    this.router = new Router();
 | 
			
		||||
    this.tpl = new Tpl();
 | 
			
		||||
    this.middleware = [];
 | 
			
		||||
    this.errorHandler = async (req: Request, res: Response) => {
 | 
			
		||||
      res.writeHead(500).end("500 - Internal Server Error");
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Adds middleware, routes, or template systems to the server.
 | 
			
		||||
   * @param {any} obj - An instance of Router, Tpl, or a middleware function.
 | 
			
		||||
   * @returns {this} - The current instance for chaining.
 | 
			
		||||
   * Adds a plugin to the application, which can be a Router instance, Tpl instance,
 | 
			
		||||
   * or a middleware handler function. The method determines the type of the plugin
 | 
			
		||||
   * and performs the appropriate action.
 | 
			
		||||
   *
 | 
			
		||||
   * - If the plugin is an instance of `Router`, it is added to the application's router.
 | 
			
		||||
   * - If the plugin is an instance of `Tpl`, it sets the application's template engine.
 | 
			
		||||
   * - If the plugin is a middleware function, it is added to the middleware stack.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {Router | Tpl | Handler} plugin - The plugin to add, which can be a `Router` instance,
 | 
			
		||||
   * a `Tpl` instance, or a middleware handler function.
 | 
			
		||||
   * @returns {this} The current instance for method chaining.
 | 
			
		||||
   */
 | 
			
		||||
  use(obj: Router | Tpl | Middleware): this {
 | 
			
		||||
    if(obj instanceof Router)
 | 
			
		||||
      this.router.use(obj);
 | 
			
		||||
    else if(obj instanceof Tpl)
 | 
			
		||||
      this.tpl = obj;
 | 
			
		||||
    else
 | 
			
		||||
      this.middleware.push(obj);
 | 
			
		||||
  use(plugin: Router | Tpl | Handler): this {
 | 
			
		||||
    if(plugin instanceof Router)
 | 
			
		||||
      this.router.use(plugin);
 | 
			
		||||
    else if(plugin instanceof Tpl)
 | 
			
		||||
      this.tpl = plugin;
 | 
			
		||||
    else if(typeof plugin === "function")
 | 
			
		||||
      this.middleware.push(plugin);
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setErrorHandler(handler: Middleware): this {
 | 
			
		||||
    this.errorHandler = handler;
 | 
			
		||||
    return this;
 | 
			
		||||
  /**
 | 
			
		||||
   * Processes a series of handlers in a pipeline by invoking each handler asynchronously
 | 
			
		||||
   * in sequence with the provided request and response objects.
 | 
			
		||||
   *
 | 
			
		||||
   * Each handler is responsible for calling the `next` function to signal that
 | 
			
		||||
   * the pipeline should continue to the next handler. If `next` is not called 
 | 
			
		||||
   * by a handler or the response is ended, the pipeline execution stops.
 | 
			
		||||
   *
 | 
			
		||||
   * @private
 | 
			
		||||
   * @param {Handler[]} handlers - An array of handler functions to process. Each function 
 | 
			
		||||
   * should have the signature `(req: Request, res: Response, next: Function) => Promise<void>`.
 | 
			
		||||
   * @param {Request} req - The HTTP request object.
 | 
			
		||||
   * @param {Response} res - The HTTP response object.
 | 
			
		||||
   * @throws {TypeError} If any handler in the array is not a function.
 | 
			
		||||
   * @returns {Promise<void>} Resolves when all handlers have been executed or the pipeline is terminated early.
 | 
			
		||||
   */
 | 
			
		||||
  private async processPipeline(handlers: Handler[], req: Request, res: Response) {
 | 
			
		||||
    for(const handler of handlers) {
 | 
			
		||||
      if(typeof handler !== "function")
 | 
			
		||||
        throw new TypeError(`Handler is not a function: ${handler}`);
 | 
			
		||||
 | 
			
		||||
      let nextCalled = false;
 | 
			
		||||
      await handler(req, res, () => nextCalled = true);
 | 
			
		||||
      if(!nextCalled || res.writableEnded)
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
@@ -51,57 +78,55 @@ export default class Flummpress {
 | 
			
		||||
   * @returns {this} - The current instance for chaining.
 | 
			
		||||
   */
 | 
			
		||||
  listen(...args: any[]): this {
 | 
			
		||||
    this.server = http
 | 
			
		||||
      .createServer(async (request: IncomingMessage, response: ServerResponse) => {
 | 
			
		||||
        const req: Request = this.parseRequest(request);
 | 
			
		||||
        const res: Response = this.createResponse(response);
 | 
			
		||||
        const start = process.hrtime();
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
          for(const mw of this.middleware) {
 | 
			
		||||
            await mw(req, res, () => {});
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          const route = this.router.getRoute(req.url.pathname, req.method!);
 | 
			
		||||
 | 
			
		||||
          if(route) {
 | 
			
		||||
            const [pathPattern, methods] = route;
 | 
			
		||||
            const method = req.method?.toLowerCase();
 | 
			
		||||
            const handler = methods[method!];
 | 
			
		||||
            const middleware = methods[`${method}mw`];
 | 
			
		||||
 | 
			
		||||
            for(const mw of middleware) {
 | 
			
		||||
              await mw(req, res, () => {});
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if(handler) {
 | 
			
		||||
              req.params = req.url.pathname.match(new RegExp(pathPattern))?.groups || {};
 | 
			
		||||
              req.post = await this.readBody(req);
 | 
			
		||||
              handler(req, res);
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
              res.writeHead(405).end("405 - Method Not Allowed");
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
          else {
 | 
			
		||||
            res.writeHead(404).end("404 - Not Found");
 | 
			
		||||
          }
 | 
			
		||||
    this.server = http.createServer(async (request: IncomingMessage, response: ServerResponse) => {
 | 
			
		||||
      const req: Request = this.parseRequest(request);
 | 
			
		||||
      const res: Response = this.createResponse(response);
 | 
			
		||||
      const start = process.hrtime();
 | 
			
		||||
  
 | 
			
		||||
      try {
 | 
			
		||||
        await this.processPipeline(this.middleware, req, res);
 | 
			
		||||
  
 | 
			
		||||
        const route = this.router.getRoute(req.url.pathname, req.method!);
 | 
			
		||||
  
 | 
			
		||||
        if(route) {
 | 
			
		||||
          const [pathPattern, methods] = route;
 | 
			
		||||
          const handler = methods[req.method?.toLowerCase()!];
 | 
			
		||||
  
 | 
			
		||||
          req.params = req.url.pathname.match(new RegExp(pathPattern))?.groups || {};
 | 
			
		||||
          req.post = await this.readBody(req);
 | 
			
		||||
          await this.processPipeline(handler, req, res);
 | 
			
		||||
        }
 | 
			
		||||
        catch(err: any) {
 | 
			
		||||
          this.errorHandler(req, res, () => {});
 | 
			
		||||
        else {
 | 
			
		||||
          res.writeHead(404).end("404 - Not Found");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        console.log([
 | 
			
		||||
          `[${new Date().toISOString()}]`,
 | 
			
		||||
          `${(process.hrtime(start)[1] / 1e6).toFixed(2)}ms`,
 | 
			
		||||
          `${req.method} ${res.statusCode}`,
 | 
			
		||||
          req.url.pathname,
 | 
			
		||||
        ].join(" | "));
 | 
			
		||||
      }).listen(...args);
 | 
			
		||||
 | 
			
		||||
      }
 | 
			
		||||
      catch(err: any) {
 | 
			
		||||
        console.error(err);
 | 
			
		||||
        res.writeHead(500).end("500 - Internal Server Error");
 | 
			
		||||
      }
 | 
			
		||||
  
 | 
			
		||||
      console.log([
 | 
			
		||||
        `[${new Date().toISOString()}]`,
 | 
			
		||||
        `${(process.hrtime(start)[1] / 1e6).toFixed(2)}ms`,
 | 
			
		||||
        `${req.method} ${res.statusCode}`,
 | 
			
		||||
        req.url.pathname,
 | 
			
		||||
      ].join(" | "));
 | 
			
		||||
    }).listen(...args);
 | 
			
		||||
  
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Parses an incoming HTTP request and converts it into a custom Request object.
 | 
			
		||||
   *
 | 
			
		||||
   * This method extracts information from the incoming request, such as the URL,
 | 
			
		||||
   * query string parameters, and cookies, and structures them in the returned Request object.
 | 
			
		||||
   * Any malformed or extra trailing slashes in the URL are sanitized.
 | 
			
		||||
   *
 | 
			
		||||
   * @private
 | 
			
		||||
   * @param {IncomingMessage} request - The incoming HTTP request to parse.
 | 
			
		||||
   * @returns {Request} A structured Request object with parsed properties such as `url` and `cookies`.
 | 
			
		||||
   */
 | 
			
		||||
  private parseRequest(request: IncomingMessage): Request {
 | 
			
		||||
    const url = new URL(request.url!.replace(/(?!^.)(\/+)?$/, ""), "http://localhost");
 | 
			
		||||
    const req = request as unknown as Request;
 | 
			
		||||
@@ -125,7 +150,7 @@ export default class Flummpress {
 | 
			
		||||
  /**
 | 
			
		||||
   * Reads and parses the body of an incoming HTTP request.
 | 
			
		||||
   * @param {Request} req - The HTTP request object.
 | 
			
		||||
   * @returns {Promise<any>} - A promise that resolves to the parsed body data.
 | 
			
		||||
   * @returns {Promise<Record<string, string>>} - A promise that resolves to the parsed body data.
 | 
			
		||||
   */
 | 
			
		||||
  private async readBody(req: Request): Promise<Record<string, string>> {
 | 
			
		||||
    return new Promise((resolve, reject) => {
 | 
			
		||||
@@ -152,6 +177,11 @@ export default class Flummpress {
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Creates a custom Response object with additional utility methods.
 | 
			
		||||
   * @param {ServerResponse} response - The original HTTP response object.
 | 
			
		||||
   * @returns {Response} - A structured Response object with utility methods such as `json` and `html`.
 | 
			
		||||
   */
 | 
			
		||||
  private createResponse(response: ServerResponse): Response {
 | 
			
		||||
    const res: Response = response as Response;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										190
									
								
								src/router.ts
									
									
									
									
									
								
							
							
						
						
									
										190
									
								
								src/router.ts
									
									
									
									
									
								
							@@ -1,11 +1,11 @@
 | 
			
		||||
import fs from "node:fs";
 | 
			
		||||
import path from "node:path";
 | 
			
		||||
import Tpl from "./template";
 | 
			
		||||
import Tpl from "./template.js";
 | 
			
		||||
 | 
			
		||||
import { Request, Middleware, RouteCallback } from "./types";
 | 
			
		||||
import { Request, Response, Handler } from "./types.js";
 | 
			
		||||
 | 
			
		||||
export default class Router {
 | 
			
		||||
  private routes: Map<RegExp | string, { [key: string]: RouteCallback | RouteCallback[] }>;
 | 
			
		||||
  private routes: Map<RegExp | string, { [key: string]: Handler | Handler[] }>;
 | 
			
		||||
  private tpl?: Tpl;
 | 
			
		||||
  private mimes: Map<string, string>;
 | 
			
		||||
 | 
			
		||||
@@ -35,36 +35,60 @@ export default class Router {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Groups multiple routes under a shared base path.
 | 
			
		||||
   * @param path - Base path for the route group.
 | 
			
		||||
   * @param cb - Callback to define the grouped routes.
 | 
			
		||||
   * Registers a new route group with common base path and middleware.
 | 
			
		||||
   * @param basePath - The base path or RegExp.
 | 
			
		||||
   * @param callback - Callback to define routes within the group.
 | 
			
		||||
   * @returns The Router instance for chaining.
 | 
			
		||||
   */
 | 
			
		||||
  group(path: string, cb: (methods: any) => void): this {
 | 
			
		||||
    const methods: { [key: string]: (path: string | RegExp, cb: RouteCallback, middleware?: RouteCallback) => Router } = {
 | 
			
		||||
      get: this.get.bind(this),
 | 
			
		||||
      post: this.post.bind(this),
 | 
			
		||||
      head: this.head.bind(this),
 | 
			
		||||
      put: this.put.bind(this),
 | 
			
		||||
      delete: this.delete.bind(this),
 | 
			
		||||
      patch: this.patch.bind(this),
 | 
			
		||||
  group(basePath: string | RegExp, callback: (methods: any) => void): this {
 | 
			
		||||
    const self = this;
 | 
			
		||||
  
 | 
			
		||||
    const methods = {
 | 
			
		||||
      get(path: string | RegExp, ...handlers: Handler[]) {
 | 
			
		||||
        const fullPath = self.combinePaths(basePath, path);
 | 
			
		||||
        return self.registerRoute(fullPath, "get", handlers);
 | 
			
		||||
      },
 | 
			
		||||
      post(path: string | RegExp, ...handlers: Handler[]) {
 | 
			
		||||
        const fullPath = self.combinePaths(basePath, path);
 | 
			
		||||
        return self.registerRoute(fullPath, "post", handlers);
 | 
			
		||||
      },
 | 
			
		||||
      put(path: string | RegExp, ...handlers: Handler[]) {
 | 
			
		||||
        const fullPath = self.combinePaths(basePath, path);
 | 
			
		||||
        return self.registerRoute(fullPath, "put", handlers);
 | 
			
		||||
      },
 | 
			
		||||
      delete(path: string | RegExp, ...handlers: Handler[]) {
 | 
			
		||||
        const fullPath = self.combinePaths(basePath, path);
 | 
			
		||||
        return self.registerRoute(fullPath, "delete", handlers);
 | 
			
		||||
      },
 | 
			
		||||
      patch(path: string | RegExp, ...handlers: Handler[]) {
 | 
			
		||||
        const fullPath = self.combinePaths(basePath, path);
 | 
			
		||||
        return self.registerRoute(fullPath, "patch", handlers);
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
    const target = { path: new RegExp(path) };
 | 
			
		||||
    const handler = {
 | 
			
		||||
      get: (opt: any, method: string) => (p: string, ...args: any[]) =>
 | 
			
		||||
        methods[method](
 | 
			
		||||
          new RegExp(
 | 
			
		||||
            [opt.path, new RegExp(p === "/" ? "$" : p)]
 | 
			
		||||
              .map((regex) => regex.source)
 | 
			
		||||
              .join("")
 | 
			
		||||
              .replace(/(\\\/){1,}/g, "/")
 | 
			
		||||
          ),
 | 
			
		||||
          ...(args as [RouteCallback, RouteCallback?])
 | 
			
		||||
        ),
 | 
			
		||||
    };
 | 
			
		||||
    cb(new Proxy(target, handler));
 | 
			
		||||
  
 | 
			
		||||
    callback(methods);
 | 
			
		||||
  
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  /**
 | 
			
		||||
   * Combines a base path and a sub path into a single path.
 | 
			
		||||
   * @param basePath - The base path or RegExp.
 | 
			
		||||
   * @param subPath - The sub path or RegExp.
 | 
			
		||||
   * @returns The combined path as a string or RegExp.
 | 
			
		||||
   */
 | 
			
		||||
  private combinePaths(basePath: string | RegExp, subPath: string | RegExp): string | RegExp {
 | 
			
		||||
    if(typeof basePath === "string" && typeof subPath === "string")
 | 
			
		||||
      return `${basePath.replace(/\/$/, "")}/${subPath.replace(/^\//, "")}`;
 | 
			
		||||
    if(basePath instanceof RegExp && typeof subPath === "string")
 | 
			
		||||
      return new RegExp(`${basePath.source}${subPath.replace(/^\//, "")}`);
 | 
			
		||||
    if(typeof basePath === "string" && subPath instanceof RegExp)
 | 
			
		||||
      return new RegExp(`${basePath.replace(/\/$/, "")}${subPath.source}`);
 | 
			
		||||
    if(basePath instanceof RegExp && subPath instanceof RegExp)
 | 
			
		||||
      return new RegExp(`${basePath.source}${subPath.source}`);
 | 
			
		||||
  
 | 
			
		||||
    throw new TypeError("Invalid path types. Both basePath and subPath must be either string or RegExp.");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Merges routes or assigns a template instance to the Router.
 | 
			
		||||
@@ -81,104 +105,87 @@ export default class Router {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Registers a GET route.
 | 
			
		||||
   * @param path - Route path or RegExp.
 | 
			
		||||
   * @param cb - Callback to handle the route.
 | 
			
		||||
   * @param middleware - Optional middleware function.
 | 
			
		||||
   * @returns The Router instance for chaining.
 | 
			
		||||
   * Registers a route for HTTP GET requests.
 | 
			
		||||
   * @param {string | RegExp} path - The URL path or pattern for the route.
 | 
			
		||||
   * @param {Handler[]} cb - An array of middleware or handler functions to execute for this route.
 | 
			
		||||
   * @returns {this} The current instance for method chaining.
 | 
			
		||||
   */
 | 
			
		||||
  get(path: string | RegExp, cb: RouteCallback, middleware?: RouteCallback): this {
 | 
			
		||||
    this.registerRoute(path, cb, "get", middleware);
 | 
			
		||||
  get(path: string | RegExp, cb: Handler[]): this {
 | 
			
		||||
    this.registerRoute(path, "get", cb);
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Registers a POST route.
 | 
			
		||||
   * @param path - Route path or RegExp.
 | 
			
		||||
   * @param cb - Callback to handle the route.
 | 
			
		||||
   * @param middleware - Optional middleware function.
 | 
			
		||||
   * @returns The Router instance for chaining.
 | 
			
		||||
   * Registers a route for HTTP POST requests.
 | 
			
		||||
   * @param {string | RegExp} path - The URL path or pattern for the route.
 | 
			
		||||
   * @param {Handler[]} cb - An array of middleware or handler functions to execute for this route.
 | 
			
		||||
   * @returns {this} The current instance for method chaining.
 | 
			
		||||
   */
 | 
			
		||||
  post(path: string | RegExp, cb: RouteCallback, middleware?: RouteCallback): this {
 | 
			
		||||
    this.registerRoute(path, cb, "post", middleware);
 | 
			
		||||
  post(path: string | RegExp, cb: Handler[]): this {
 | 
			
		||||
    this.registerRoute(path, "post", cb);
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Registers a HEAD route.
 | 
			
		||||
   * @param path - Route path or RegExp.
 | 
			
		||||
   * @param cb - Callback to handle the route.
 | 
			
		||||
   * @param middleware - Optional middleware function.
 | 
			
		||||
   * @returns The Router instance for chaining.
 | 
			
		||||
   * Registers a route for HTTP HEAD requests.
 | 
			
		||||
   * @param {string | RegExp} path - The URL path or pattern for the route.
 | 
			
		||||
   * @param {Handler[]} cb - An array of middleware or handler functions to execute for this route.
 | 
			
		||||
   * @returns {this} The current instance for method chaining.
 | 
			
		||||
   */
 | 
			
		||||
  head(path: string | RegExp, cb: RouteCallback, middleware?: RouteCallback): this {
 | 
			
		||||
    this.registerRoute(path, cb, "head", middleware);
 | 
			
		||||
  head(path: string | RegExp, cb: Handler[]): this {
 | 
			
		||||
    this.registerRoute(path, "head", cb);
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Registers a PUT route.
 | 
			
		||||
   * @param path - Route path or RegExp.
 | 
			
		||||
   * @param cb - Callback to handle the route.
 | 
			
		||||
   * @param middleware - Optional middleware function.
 | 
			
		||||
   * @returns The Router instance for chaining.
 | 
			
		||||
   * Registers a route for HTTP PUT requests.
 | 
			
		||||
   * @param {string | RegExp} path - The URL path or pattern for the route.
 | 
			
		||||
   * @param {Handler[]} cb - An array of middleware or handler functions to execute for this route.
 | 
			
		||||
   * @returns {this} The current instance for method chaining.
 | 
			
		||||
   */
 | 
			
		||||
  put(path: string | RegExp, cb: RouteCallback, middleware?: RouteCallback): this {
 | 
			
		||||
    this.registerRoute(path, cb, "put", middleware);
 | 
			
		||||
  put(path: string | RegExp, cb: Handler[]): this {
 | 
			
		||||
    this.registerRoute(path, "put", cb);
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Registers a DELETE route.
 | 
			
		||||
   * @param path - Route path or RegExp.
 | 
			
		||||
   * @param cb - Callback to handle the route.
 | 
			
		||||
   * @param middleware - Optional middleware function.
 | 
			
		||||
   * @returns The Router instance for chaining.
 | 
			
		||||
   * Registers a route for HTTP DELETE requests.
 | 
			
		||||
   * @param {string | RegExp} path - The URL path or pattern for the route.
 | 
			
		||||
   * @param {Handler[]} cb - An array of middleware or handler functions to execute for this route.
 | 
			
		||||
   * @returns {this} The current instance for method chaining.
 | 
			
		||||
   */
 | 
			
		||||
  delete(path: string | RegExp, cb: RouteCallback, middleware?: RouteCallback): this {
 | 
			
		||||
    this.registerRoute(path, cb, "delete", middleware);
 | 
			
		||||
  delete(path: string | RegExp, cb: Handler[]): this {
 | 
			
		||||
    this.registerRoute(path, "delete", cb);
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Registers a PATCH route.
 | 
			
		||||
   * @param path - Route path or RegExp.
 | 
			
		||||
   * @param cb - Callback to handle the route.
 | 
			
		||||
   * @param middleware - Optional middleware function.
 | 
			
		||||
   * @returns The Router instance for chaining.
 | 
			
		||||
   * Registers a route for HTTP PATCH requests.
 | 
			
		||||
   * @param {string | RegExp} path - The URL path or pattern for the route.
 | 
			
		||||
   * @param {Handler[]} cb - An array of middleware or handler functions to execute for this route.
 | 
			
		||||
   * @returns {this} The current instance for method chaining.
 | 
			
		||||
   */
 | 
			
		||||
  patch(path: string | RegExp, cb: RouteCallback, middleware?: RouteCallback): this {
 | 
			
		||||
    this.registerRoute(path, cb, "patch", middleware);
 | 
			
		||||
  patch(path: string | RegExp, cb: Handler[]): this {
 | 
			
		||||
    this.registerRoute(path, "patch", cb);
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Registers a new route with an optional middleware.
 | 
			
		||||
   * @param path - The route path or RegExp.
 | 
			
		||||
   * @param cb - Callback to handle the route.
 | 
			
		||||
   * @param method - The HTTP method (e.g., "get", "post").
 | 
			
		||||
   * @param middleware - Optional middleware function.
 | 
			
		||||
   * @returns The Router instance for chaining.
 | 
			
		||||
   * Registers a route for HTTP OPTIONS requests.
 | 
			
		||||
   * @param {string | RegExp} path - The URL path or pattern for the route.
 | 
			
		||||
   * @param {Handler[]} cb - An array of middleware or handler functions to execute
 | 
			
		||||
   * @returns {this} The current instance for method chaining.
 | 
			
		||||
   */
 | 
			
		||||
  private registerRoute(
 | 
			
		||||
    path: string | RegExp,
 | 
			
		||||
    cb: RouteCallback,
 | 
			
		||||
    method: string,
 | 
			
		||||
    middleware: Middleware | Middleware[] = []
 | 
			
		||||
    handlers: Handler[]
 | 
			
		||||
  ): this {
 | 
			
		||||
    if(!Array.isArray(middleware)) {
 | 
			
		||||
      middleware = [ middleware ];
 | 
			
		||||
    }
 | 
			
		||||
  
 | 
			
		||||
    if(!this.routes.has(path)) {
 | 
			
		||||
      this.routes.set(path, {});
 | 
			
		||||
    }
 | 
			
		||||
  
 | 
			
		||||
    this.routes.set(path, {
 | 
			
		||||
      ...this.routes.get(path),
 | 
			
		||||
      [method]: cb,
 | 
			
		||||
      [`${method}mw`]: middleware as RouteCallback[],
 | 
			
		||||
    });
 | 
			
		||||
    this.routes.get(path)![method] = handlers.flat();
 | 
			
		||||
  
 | 
			
		||||
    console.log("route set:", method.toUpperCase(), path);
 | 
			
		||||
    this.sortRoutes();
 | 
			
		||||
@@ -228,11 +235,14 @@ export default class Router {
 | 
			
		||||
   * @param options.route - Regular expression to match the route for static files.
 | 
			
		||||
   * @returns The Router instance for chaining.
 | 
			
		||||
   */
 | 
			
		||||
  static({ dir = path.resolve() + "/public", route = /^\/public/ }: { dir?: string; route?: RegExp }): this {
 | 
			
		||||
  static({
 | 
			
		||||
    dir = path.resolve() + "/public",
 | 
			
		||||
    route = /^\/public/
 | 
			
		||||
  }: { dir?: string; route?: RegExp }): this {
 | 
			
		||||
    if(!this.mimes.size)
 | 
			
		||||
      this.readMimes();
 | 
			
		||||
 | 
			
		||||
    this.get(route, (req: Request, res: any) => {
 | 
			
		||||
    this.get(route, [(req: Request, res: Response) => {
 | 
			
		||||
      try {
 | 
			
		||||
        const filename = req.url.pathname.replace(route, "") || "index.html";
 | 
			
		||||
        const mime = this.mimes.get(filename.split(".").pop() || "");
 | 
			
		||||
@@ -264,7 +274,7 @@ export default class Router {
 | 
			
		||||
        console.error(err);
 | 
			
		||||
        res.reply({ code: 404, body: "404 - File not found" });
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    }]);
 | 
			
		||||
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								src/types.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								src/types.d.ts
									
									
									
									
										vendored
									
									
								
							@@ -19,5 +19,4 @@ export interface Response extends ServerResponse {
 | 
			
		||||
  redirect: (target: string, code?: number) => void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type Middleware = (req: Request, res: Response, next: () => void) => void;
 | 
			
		||||
export type RouteCallback = (req: Request, res: Response) => void;
 | 
			
		||||
export type Handler = (req: Request, res: Response, next?: () => void) => void | Promise<void>;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user