test
This commit is contained in:
		
							
								
								
									
										5
									
								
								dist/container.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								dist/container.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
export default class Container {
 | 
			
		||||
    private services;
 | 
			
		||||
    register<T>(name: string, factory: () => T): void;
 | 
			
		||||
    resolve<T>(name: string): T;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								dist/container.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								dist/container.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
export default class Container {
 | 
			
		||||
    constructor() {
 | 
			
		||||
        this.services = new Map();
 | 
			
		||||
    }
 | 
			
		||||
    register(name, factory) {
 | 
			
		||||
        this.services.set(name, factory);
 | 
			
		||||
    }
 | 
			
		||||
    resolve(name) {
 | 
			
		||||
        const factory = this.services.get(name);
 | 
			
		||||
        if (!factory)
 | 
			
		||||
            throw new Error(`Service "${name}" not found.`);
 | 
			
		||||
        return factory();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								dist/index.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								dist/index.d.ts
									
									
									
									
										vendored
									
									
								
							@@ -3,11 +3,12 @@ import Tpl from "./template.js";
 | 
			
		||||
export { Router, Tpl, Request, Response, Handler };
 | 
			
		||||
export default class Flummpress {
 | 
			
		||||
    private server?;
 | 
			
		||||
    private container;
 | 
			
		||||
    private middleware;
 | 
			
		||||
    router: Router;
 | 
			
		||||
    tpl: Tpl;
 | 
			
		||||
    middleware: Handler[];
 | 
			
		||||
    constructor();
 | 
			
		||||
    use(plugin: Router | Tpl | Handler): this;
 | 
			
		||||
    use<T>(nameOrRouter: string | Router, factory?: () => T): this;
 | 
			
		||||
    resolve<T>(name: string): T;
 | 
			
		||||
    private processPipeline;
 | 
			
		||||
    listen(...args: any[]): this;
 | 
			
		||||
    private parseRequest;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										23
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							@@ -11,23 +11,27 @@ import http from "node:http";
 | 
			
		||||
import { URL } from "node:url";
 | 
			
		||||
import querystring from "node:querystring";
 | 
			
		||||
import Router from "./router.js";
 | 
			
		||||
import Container from "./container.js";
 | 
			
		||||
import Tpl from "./template.js";
 | 
			
		||||
export { Router, Tpl };
 | 
			
		||||
export default class Flummpress {
 | 
			
		||||
    constructor() {
 | 
			
		||||
        this.container = new Container();
 | 
			
		||||
        this.router = new Router();
 | 
			
		||||
        this.tpl = new Tpl();
 | 
			
		||||
        this.middleware = [];
 | 
			
		||||
    }
 | 
			
		||||
    use(plugin) {
 | 
			
		||||
        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);
 | 
			
		||||
    use(nameOrRouter, factory) {
 | 
			
		||||
        if (typeof nameOrRouter === "string" && factory)
 | 
			
		||||
            this.container.register(nameOrRouter, factory);
 | 
			
		||||
        else if (nameOrRouter instanceof Router)
 | 
			
		||||
            this.router.use(nameOrRouter);
 | 
			
		||||
        else
 | 
			
		||||
            throw new TypeError("Invalid arguments provided to use()");
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
    resolve(name) {
 | 
			
		||||
        return this.container.resolve(name);
 | 
			
		||||
    }
 | 
			
		||||
    processPipeline(handlers, req, res) {
 | 
			
		||||
        return __awaiter(this, void 0, void 0, function* () {
 | 
			
		||||
            for (const handler of handlers) {
 | 
			
		||||
@@ -69,7 +73,8 @@ export default class Flummpress {
 | 
			
		||||
                `${req.method} ${res.statusCode}`,
 | 
			
		||||
                req.url.pathname,
 | 
			
		||||
            ].join(" | "));
 | 
			
		||||
        })).listen(...args);
 | 
			
		||||
        }));
 | 
			
		||||
        this.server.listen(...args);
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
    parseRequest(request) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								dist/router.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								dist/router.d.ts
									
									
									
									
										vendored
									
									
								
							@@ -1,5 +1,4 @@
 | 
			
		||||
import { IncomingMessage, ServerResponse } from "node:http";
 | 
			
		||||
import Tpl from "./template.js";
 | 
			
		||||
export interface Request extends Omit<IncomingMessage, 'url'> {
 | 
			
		||||
    url: {
 | 
			
		||||
        pathname: string;
 | 
			
		||||
@@ -25,13 +24,12 @@ export interface Response extends ServerResponse {
 | 
			
		||||
export type Handler = (req: Request, res: Response, next?: () => void) => void | Promise<void>;
 | 
			
		||||
export default class Router {
 | 
			
		||||
    private routes;
 | 
			
		||||
    private tpl?;
 | 
			
		||||
    private mimes;
 | 
			
		||||
    constructor();
 | 
			
		||||
    importRoutesFromPath(p: string, tpl?: Tpl | false): Promise<this>;
 | 
			
		||||
    importRoutesFromPath(p: string): Promise<this>;
 | 
			
		||||
    group(basePath: string | RegExp, callback: (methods: any) => void): this;
 | 
			
		||||
    private combinePaths;
 | 
			
		||||
    use(obj: Router | Tpl): void;
 | 
			
		||||
    use(obj: Router): void;
 | 
			
		||||
    get(path: string | RegExp, ...callback: Handler[]): this;
 | 
			
		||||
    post(path: string | RegExp, ...callback: Handler[]): this;
 | 
			
		||||
    head(path: string | RegExp, ...callback: Handler[]): this;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								dist/router.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								dist/router.js
									
									
									
									
										vendored
									
									
								
							@@ -9,19 +9,18 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
 | 
			
		||||
};
 | 
			
		||||
import fs from "node:fs";
 | 
			
		||||
import path from "node:path";
 | 
			
		||||
import Tpl from "./template.js";
 | 
			
		||||
export default class Router {
 | 
			
		||||
    constructor() {
 | 
			
		||||
        this.routes = [];
 | 
			
		||||
        this.mimes = new Map();
 | 
			
		||||
    }
 | 
			
		||||
    importRoutesFromPath(p_1) {
 | 
			
		||||
        return __awaiter(this, arguments, void 0, function* (p, tpl = false) {
 | 
			
		||||
    importRoutesFromPath(p) {
 | 
			
		||||
        return __awaiter(this, void 0, void 0, function* () {
 | 
			
		||||
            const dirEntries = yield fs.promises.readdir(path.resolve() + '/' + p, { withFileTypes: true });
 | 
			
		||||
            for (const tmp of dirEntries) {
 | 
			
		||||
                if (tmp.isFile() && (tmp.name.endsWith(".mjs") || tmp.name.endsWith(".js"))) {
 | 
			
		||||
                    const routeModule = (yield import(`${path.resolve()}/${p}/${tmp.name}`)).default;
 | 
			
		||||
                    this.use(routeModule(this, tpl));
 | 
			
		||||
                    this.use(routeModule(this));
 | 
			
		||||
                }
 | 
			
		||||
                else if (tmp.isDirectory()) {
 | 
			
		||||
                    yield this.importRoutesFromPath(p + '/' + tmp.name);
 | 
			
		||||
@@ -75,9 +74,6 @@ export default class Router {
 | 
			
		||||
            this.routes = [...this.routes, ...obj.routes];
 | 
			
		||||
            this.sortRoutes();
 | 
			
		||||
        }
 | 
			
		||||
        if (obj instanceof Tpl) {
 | 
			
		||||
            this.tpl = obj;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    get(path, ...callback) {
 | 
			
		||||
        this.registerRoute(path, "get", callback);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										14
									
								
								src/container.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/container.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
export default class Container {
 | 
			
		||||
  private services: Map<string, () => any> = new Map();
 | 
			
		||||
 | 
			
		||||
  register<T>(name: string, factory: () => T): void {
 | 
			
		||||
    this.services.set(name, factory);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  resolve<T>(name: string): T {
 | 
			
		||||
    const factory = this.services.get(name);
 | 
			
		||||
    if(!factory)
 | 
			
		||||
      throw new Error(`Service "${name}" not found.`);
 | 
			
		||||
    return factory();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										86
									
								
								src/index.ts
									
									
									
									
									
								
							
							
						
						
									
										86
									
								
								src/index.ts
									
									
									
									
									
								
							@@ -3,61 +3,44 @@ import { URL } from "node:url";
 | 
			
		||||
import querystring from "node:querystring";
 | 
			
		||||
 | 
			
		||||
import Router, { Request, Response, Handler } from "./router.js";
 | 
			
		||||
import Container from "./container.js";
 | 
			
		||||
import Tpl from "./template.js";
 | 
			
		||||
 | 
			
		||||
export { Router, Tpl, Request, Response, Handler };
 | 
			
		||||
 | 
			
		||||
export default class Flummpress {
 | 
			
		||||
  private server?: http.Server;
 | 
			
		||||
  router: Router;
 | 
			
		||||
  tpl: Tpl;
 | 
			
		||||
  middleware: Handler[];
 | 
			
		||||
  private container: Container;
 | 
			
		||||
  private middleware: Handler[];
 | 
			
		||||
  public router: Router;
 | 
			
		||||
 | 
			
		||||
  constructor() {
 | 
			
		||||
    this.container = new Container();
 | 
			
		||||
    this.router = new Router();
 | 
			
		||||
    this.tpl = new Tpl();
 | 
			
		||||
    this.middleware = [];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 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(plugin: Router | Tpl | Handler): this {
 | 
			
		||||
  /*public use(plugin: Router | 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;
 | 
			
		||||
  }*/
 | 
			
		||||
 | 
			
		||||
  public use<T>(nameOrRouter: string | Router, factory?: () => T): this {
 | 
			
		||||
    if(typeof nameOrRouter === "string" && factory)
 | 
			
		||||
      this.container.register(nameOrRouter, factory);
 | 
			
		||||
    else if(nameOrRouter instanceof Router)
 | 
			
		||||
      this.router.use(nameOrRouter);
 | 
			
		||||
    else
 | 
			
		||||
      throw new TypeError("Invalid arguments provided to use()");
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
  public resolve<T>(name: string): T {
 | 
			
		||||
    return this.container.resolve(name);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 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")
 | 
			
		||||
@@ -70,12 +53,7 @@ export default class Flummpress {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Starts the HTTP server and begins listening for incoming requests.
 | 
			
		||||
   * @param {...any} args - Arguments passed to `http.Server.listen`.
 | 
			
		||||
   * @returns {this} - The current instance for chaining.
 | 
			
		||||
   */
 | 
			
		||||
  listen(...args: any[]): this {
 | 
			
		||||
  public 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);
 | 
			
		||||
@@ -108,22 +86,12 @@ export default class Flummpress {
 | 
			
		||||
        `${req.method} ${res.statusCode}`,
 | 
			
		||||
        req.url.pathname,
 | 
			
		||||
      ].join(" | "));
 | 
			
		||||
    }).listen(...args);
 | 
			
		||||
    })
 | 
			
		||||
    this.server.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;
 | 
			
		||||
@@ -144,11 +112,6 @@ export default class Flummpress {
 | 
			
		||||
    return req;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Reads and parses the body of an incoming HTTP request.
 | 
			
		||||
   * @param {Request} req - The HTTP request object.
 | 
			
		||||
   * @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) => {
 | 
			
		||||
      let body: string = "";
 | 
			
		||||
@@ -174,11 +137,6 @@ 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;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										102
									
								
								src/router.ts
									
									
									
									
									
								
							
							
						
						
									
										102
									
								
								src/router.ts
									
									
									
									
									
								
							@@ -2,8 +2,6 @@ import fs from "node:fs";
 | 
			
		||||
import path from "node:path";
 | 
			
		||||
import { IncomingMessage, ServerResponse } from "node:http";
 | 
			
		||||
 | 
			
		||||
import Tpl from "./template.js";
 | 
			
		||||
 | 
			
		||||
export interface Request extends Omit<IncomingMessage, 'url'> {
 | 
			
		||||
  url: {
 | 
			
		||||
    pathname: string;
 | 
			
		||||
@@ -28,25 +26,18 @@ export type Handler = (req: Request, res: Response, next?: () => void) => void |
 | 
			
		||||
 | 
			
		||||
export default class Router {
 | 
			
		||||
  private routes: Array<{ path: string | RegExp; methods: { [method: string]: Handler[] } }> = [];
 | 
			
		||||
  private tpl?: Tpl;
 | 
			
		||||
  private mimes: Map<string, string>;
 | 
			
		||||
 | 
			
		||||
  constructor() {
 | 
			
		||||
    this.mimes = new Map();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Dynamically imports routes from a directory and registers them.
 | 
			
		||||
   * @param p - Path to the directory containing route files.
 | 
			
		||||
   * @param tpl - Optional template instance to use with the routes.
 | 
			
		||||
   * @returns The Router instance for chaining.
 | 
			
		||||
   */
 | 
			
		||||
  async importRoutesFromPath(p: string, tpl: Tpl | false = false): Promise<this> {
 | 
			
		||||
  async importRoutesFromPath(p: string): Promise<this> {
 | 
			
		||||
    const dirEntries = await fs.promises.readdir(path.resolve() + '/' + p, { withFileTypes: true });
 | 
			
		||||
    for(const tmp of dirEntries) {
 | 
			
		||||
      if(tmp.isFile() && (tmp.name.endsWith(".mjs") || tmp.name.endsWith(".js"))) {
 | 
			
		||||
        const routeModule = (await import(`${path.resolve()}/${p}/${tmp.name}`)).default;
 | 
			
		||||
        this.use(routeModule(this, tpl));
 | 
			
		||||
        this.use(routeModule(this));
 | 
			
		||||
      }
 | 
			
		||||
      else if(tmp.isDirectory()) {
 | 
			
		||||
        await this.importRoutesFromPath(p + '/' + tmp.name);
 | 
			
		||||
@@ -55,12 +46,6 @@ export default class Router {
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 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(basePath: string | RegExp, callback: (methods: any) => void): this {
 | 
			
		||||
    const self = this;
 | 
			
		||||
 | 
			
		||||
@@ -92,12 +77,6 @@ export default class Router {
 | 
			
		||||
    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(/^\//, "")}`;
 | 
			
		||||
@@ -111,99 +90,45 @@ export default class Router {
 | 
			
		||||
    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.
 | 
			
		||||
   * @param obj - An instance of Router or Tpl.
 | 
			
		||||
   */
 | 
			
		||||
  use(obj: Router | Tpl): void {
 | 
			
		||||
  use(obj: Router): void {
 | 
			
		||||
    if(obj instanceof Router) {
 | 
			
		||||
      if(!Array.isArray(obj.routes))
 | 
			
		||||
        throw new TypeError("Routes must be an array.");
 | 
			
		||||
      this.routes = [ ...this.routes, ...obj.routes ];
 | 
			
		||||
      this.sortRoutes();
 | 
			
		||||
    }
 | 
			
		||||
    if(obj instanceof Tpl) {
 | 
			
		||||
      this.tpl = obj;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Registers a route for HTTP GET requests.
 | 
			
		||||
   * @param {string | RegExp} path - The URL path or pattern for the route.
 | 
			
		||||
   * @param {Handler[]} callback - An array of middleware or handler functions to execute for this route.
 | 
			
		||||
   * @returns {this} The current instance for method chaining.
 | 
			
		||||
   */
 | 
			
		||||
  get(path: string | RegExp, ...callback: Handler[]): this {
 | 
			
		||||
    this.registerRoute(path, "get", callback);
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Registers a route for HTTP POST requests.
 | 
			
		||||
   * @param {string | RegExp} path - The URL path or pattern for the route.
 | 
			
		||||
   * @param {Handler[]} callback - An array of middleware or handler functions to execute for this route.
 | 
			
		||||
   * @returns {this} The current instance for method chaining.
 | 
			
		||||
   */
 | 
			
		||||
  post(path: string | RegExp, ...callback: Handler[]): this {
 | 
			
		||||
    this.registerRoute(path, "post", callback);
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Registers a route for HTTP HEAD requests.
 | 
			
		||||
   * @param {string | RegExp} path - The URL path or pattern for the route.
 | 
			
		||||
   * @param {Handler[]} callback - An array of middleware or handler functions to execute for this route.
 | 
			
		||||
   * @returns {this} The current instance for method chaining.
 | 
			
		||||
   */
 | 
			
		||||
  head(path: string | RegExp, ...callback: Handler[]): this {
 | 
			
		||||
    this.registerRoute(path, "head", callback);
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Registers a route for HTTP PUT requests.
 | 
			
		||||
   * @param {string | RegExp} path - The URL path or pattern for the route.
 | 
			
		||||
   * @param {Handler[]} callback - An array of middleware or handler functions to execute for this route.
 | 
			
		||||
   * @returns {this} The current instance for method chaining.
 | 
			
		||||
   */
 | 
			
		||||
  put(path: string | RegExp, ...callback: Handler[]): this {
 | 
			
		||||
    this.registerRoute(path, "put", callback);
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Registers a route for HTTP DELETE requests.
 | 
			
		||||
   * @param {string | RegExp} path - The URL path or pattern for the route.
 | 
			
		||||
   * @param {Handler[]} callback - An array of middleware or handler functions to execute for this route.
 | 
			
		||||
   * @returns {this} The current instance for method chaining.
 | 
			
		||||
   */
 | 
			
		||||
  delete(path: string | RegExp, ...callback: Handler[]): this {
 | 
			
		||||
    this.registerRoute(path, "delete", callback);
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Registers a route for HTTP PATCH requests.
 | 
			
		||||
   * @param {string | RegExp} path - The URL path or pattern for the route.
 | 
			
		||||
   * @param {Handler[]} callback - An array of middleware or handler functions to execute for this route.
 | 
			
		||||
   * @returns {this} The current instance for method chaining.
 | 
			
		||||
   */
 | 
			
		||||
  patch(path: string | RegExp, ...callback: Handler[]): this {
 | 
			
		||||
    this.registerRoute(path, "patch", callback);
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Registers a route with a specified path, HTTP method, and handler(s).
 | 
			
		||||
   * If the route already exists, the provided handler(s) will be appended
 | 
			
		||||
   * to the existing method's handlers.
 | 
			
		||||
   *
 | 
			
		||||
   * @private
 | 
			
		||||
   * @param {string|RegExp} path - The path of the route, which can be a string or a RegExp.
 | 
			
		||||
   * @param {string} method - The HTTP method for the route (e.g., "GET", "POST").
 | 
			
		||||
   * @param {Handler[]} handler - An array of handler functions to be associated with the route and method.
 | 
			
		||||
   * @returns {this} Returns the current instance to allow method chaining.
 | 
			
		||||
   */
 | 
			
		||||
  private registerRoute(
 | 
			
		||||
    path: string | RegExp,
 | 
			
		||||
    method: string,
 | 
			
		||||
@@ -233,12 +158,6 @@ export default class Router {
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Finds and returns the route matching the given path and method.
 | 
			
		||||
   * @param path - The requested path.
 | 
			
		||||
   * @param method - The HTTP method (e.g., "GET").
 | 
			
		||||
   * @returns The matching route or undefined.
 | 
			
		||||
   */
 | 
			
		||||
  getRoute(path: string, method: string): any {
 | 
			
		||||
    return this.routes
 | 
			
		||||
      .find(r => typeof r.path === "string"
 | 
			
		||||
@@ -248,10 +167,6 @@ export default class Router {
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Sorts the routes by their keys in reverse order.
 | 
			
		||||
   * @returns The Router instance for chaining.
 | 
			
		||||
   */
 | 
			
		||||
  private sortRoutes(): this {
 | 
			
		||||
    this.routes.sort((a, b) => {
 | 
			
		||||
      const aLength = typeof a.path === "string" ? a.path.length : a.path.source.length;
 | 
			
		||||
@@ -268,10 +183,6 @@ export default class Router {
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Reads MIME types from a file and stores them in a map.
 | 
			
		||||
   * @param file - Path to the MIME types file.
 | 
			
		||||
   */
 | 
			
		||||
  private readMimes(file: string = "/etc/mime.types"): void {
 | 
			
		||||
    fs.readFileSync(file, "utf-8")
 | 
			
		||||
      .split("\n")
 | 
			
		||||
@@ -282,13 +193,6 @@ export default class Router {
 | 
			
		||||
      });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Serves static files from a specified directory.
 | 
			
		||||
   * @param options - Options for serving static files.
 | 
			
		||||
   * @param options.dir - Directory containing the static files.
 | 
			
		||||
   * @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/
 | 
			
		||||
 
 | 
			
		||||
@@ -16,44 +16,23 @@ export default class Template {
 | 
			
		||||
    this.templates = new Map();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Enables or disables debug mode.
 | 
			
		||||
   * @param debug - If true, enables debug mode.
 | 
			
		||||
   */
 | 
			
		||||
  setDebug(debug: boolean): void {
 | 
			
		||||
    this.debug = debug;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Sets the directory for template files and preloads all templates.
 | 
			
		||||
   * @param views - The directory path for template files.
 | 
			
		||||
   */
 | 
			
		||||
  setViews(views: string): void {
 | 
			
		||||
    this.views = views;
 | 
			
		||||
    this.readdir(views);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Sets global variables to be used in all templates.
 | 
			
		||||
   * @param globals - An object containing global variables.
 | 
			
		||||
   */
 | 
			
		||||
  setGlobals(globals: Record<string, any>): void {
 | 
			
		||||
    this.globals = globals;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Enables or disables the template caching mechanism.
 | 
			
		||||
   * @param cache - If true, enables caching.
 | 
			
		||||
   */
 | 
			
		||||
  setCache(cache: boolean): void {
 | 
			
		||||
    this.cache = cache;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Recursively reads the specified directory and loads all templates into memory.
 | 
			
		||||
   * @param dir - The directory to read.
 | 
			
		||||
   * @param root - The root directory for relative paths.
 | 
			
		||||
   */
 | 
			
		||||
  private readdir(dir: string, root: string = dir): void {
 | 
			
		||||
    for(const d of fs.readdirSync(`${path.resolve()}/${dir}`)) {
 | 
			
		||||
      if(d.endsWith(".html")) {
 | 
			
		||||
@@ -68,11 +47,6 @@ export default class Template {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Retrieves a template from the cache or loads it from the file system if not cached.
 | 
			
		||||
   * @param tpl - The name of the template.
 | 
			
		||||
   * @returns The template code as a string.
 | 
			
		||||
   */
 | 
			
		||||
  private getTemplate(tpl: string): string {
 | 
			
		||||
    let template: { code: string; cached: Date };
 | 
			
		||||
    let cache = false;
 | 
			
		||||
@@ -93,13 +67,6 @@ export default class Template {
 | 
			
		||||
    ].join("");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Renders a template with the provided data and local variables.
 | 
			
		||||
   * @param file - The name of the template file (without extension).
 | 
			
		||||
   * @param data - Data object to inject into the template.
 | 
			
		||||
   * @param locals - Local variables to be used within the template.
 | 
			
		||||
   * @returns The rendered HTML string.
 | 
			
		||||
   */
 | 
			
		||||
  render(file: string, data: Record<string, any> = {}, locals: Record<string, any> = {}): string {
 | 
			
		||||
    data = { ...data, ...locals, ...this.globals };
 | 
			
		||||
    try {
 | 
			
		||||
@@ -139,11 +106,6 @@ export default class Template {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Escapes a string for safe usage in HTML.
 | 
			
		||||
   * @param str - The string to escape.
 | 
			
		||||
   * @returns The escaped string.
 | 
			
		||||
   */
 | 
			
		||||
  escape(str: string): string {
 | 
			
		||||
    return (str + "")
 | 
			
		||||
      .replace(/&/g, "&")
 | 
			
		||||
@@ -155,11 +117,6 @@ export default class Template {
 | 
			
		||||
      .replace(/}/g, "}");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Iterates over an object or array and applies a callback function.
 | 
			
		||||
   * @param o - The object or array to iterate over.
 | 
			
		||||
   * @param f - The callback function.
 | 
			
		||||
   */
 | 
			
		||||
  forEach(o: any, f: (value: any, key: string | number) => void): void {
 | 
			
		||||
    if(Array.isArray(o)) {
 | 
			
		||||
      o.forEach(f);
 | 
			
		||||
@@ -172,11 +129,6 @@ export default class Template {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Retrieves the last modification time of a file.
 | 
			
		||||
   * @param file - The file path to check.
 | 
			
		||||
   * @returns The last modification time in milliseconds.
 | 
			
		||||
   */
 | 
			
		||||
  getMtime(file: string): number {
 | 
			
		||||
    try {
 | 
			
		||||
      return +`${fs.statSync(path.normalize(process.cwd() + file)).mtimeMs}`.split(".")[0];
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user