UNPKG

@ajayos/server

Version:

A lightweight Express-based HTTP/HTTPS server wrapper with built-in middleware, lifecycle hooks, logging, and utility helpers.

347 lines (334 loc) 10.3 kB
// src/server.ts import express2 from "express"; import * as http from "http"; import * as https from "https"; import * as os from "os"; import "dotenv/config"; // src/plugins/cors.ts import corsObject from "cors"; var cors = (options) => ({ name: "cors", setup: (app) => app.use(corsObject(options)) }); // src/plugins/static.ts import express from "express"; var staticPlugin = (root, options) => ({ name: "static", setup: (app) => app.use(express.static(root, options)) }); // src/plugins/morgan.ts import morganObject from "morgan"; var morgan = (format = "dev") => ({ name: "request-logger", setup: (app) => app.use(morganObject(format)) }); // src/plugins/helmet.ts import helmetObject from "helmet"; var helmet = (options) => ({ name: "helmet", setup: (app) => app.use(helmetObject(options)) }); // src/plugins/timeout.ts import timeoutObject from "connect-timeout"; var timeout = (time, options) => ({ name: "timeout", setup: (app) => app.use(timeoutObject(time, options)) }); // src/plugins/jsonError.ts var jsonError = (handler) => ({ name: "json-error", setup: (app) => { app.use((err, req, res, next) => { if (err instanceof SyntaxError && "body" in err && req.headers["content-type"]?.includes("application/json")) { if (typeof handler === "function") { return handler(err, req, res); } return res.status(400).json(handler ?? { error: "Invalid JSON payload" }); } next(err); }); } }); // src/plugins/rateLimit.ts import rateLimitObject from "express-rate-limit"; var rateLimit = (options) => ({ name: "rate-limit", setup: (app) => app.use(rateLimitObject(options)) }); // src/plugins/bodyParser.ts import { json, urlencoded } from "body-parser"; var bodyParser = (options) => ({ name: "body-parser", setup: (app) => app.use(json(options), urlencoded({ extended: true })) }); // src/plugins/compression.ts import compressionObject from "compression"; var compression = (options) => ({ name: "compression", setup: (app) => app.use(compressionObject(options)) }); // src/plugins/cookieParser.ts import cookieParserObject from "cookie-parser"; var cookieParser = (secret, options) => ({ name: "cookieParser", async setup(app) { app.use(cookieParserObject(secret, options)); } }); // src/server.ts var BUILTIN_PLUGINS = { cors, helmet, morgan, static: staticPlugin, jsonError, rateLimit, bodyParser, compression }; var SERVER = class { /** * Initializes the SERVER instance. * Supports flexible arguments to allow instantiation via Port, Config, or Callbacks in various orders. * * @param arg1 - Port number OR Configuration object. * @param arg2 - Configuration object OR Callback function. * @param arg3 - Callback function. */ constructor(arg1, arg2, arg3) { /** Callback executed when the server starts listening. */ this.onServerStart = () => { }; /** Callback executed when a server error occurs. */ this.onServerError = () => { }; /** Manager instance for handling server plugins. */ this.plugins = []; /** Configuration object for the server. */ this.config = {}; this.app = express2(); let callback; if (typeof arg1 === "number") { this.port = arg1; if (typeof arg2 === "function") callback = arg2; if (typeof arg2 === "object") this.config = arg2; if (arg3) callback = arg3; } else if (typeof arg1 === "object") { this.config = arg1; this.port = arg1.port ?? Number(process.env.PORT) ?? 8123; if (typeof arg2 === "function") callback = arg2; } else { this.port = Number(process.env.PORT) ?? 8123; } for (const plugin of this.config.plugins ?? []) { this.plugins.push(plugin); } if (callback) { this.onServerStart = callback; } else if (this.config.onServerStart) { this.onServerStart = this.config.onServerStart; } if (this.config.onServerError) { this.onServerError = this.config.onServerError; } } /** * Starts the server. * * 1. Applies built-in middleware (CORS, BodyParser, etc.). * 2. Applies registered plugins. * 3. Creates the HTTP or HTTPS server. * 4. Listens on the specified port. * * @throws {Error} If HTTPS is enabled but SSL options are missing. */ start(callback) { this.applyBuiltins(); for (const plugin of this.plugins) { plugin.setup(this.app); } if (this.config.https) { const httpsOptions = typeof this.config.https === "object" ? this.config.https : void 0; if (!httpsOptions) { throw new Error("HTTPS enabled but no SSL options provided (key/cert)"); } this.server = https.createServer(httpsOptions, this.app); } else { this.server = http.createServer(this.app); } this.server.listen(this.port, this.onServerStart); this.server.on("error", this.onServerError); callback?.(); } /** * Closes the server and exits the process. */ close() { this.server?.close(() => { process.exit(0); }); } /** * Registers a new plugin to the server. * Plugins are applied to the Express app when `start()` is called. * * @param plugin - The plugin instance or function to register. */ usePlugin(plugin) { this.plugins.push(plugin); } /** * Applies built-in middleware based on the server configuration. * Supported built-ins include CORS, Helmet, BodyParser, CookieParser, and Static file serving. */ applyBuiltins() { for (const key in BUILTIN_PLUGINS) { const value = this.config[key]; if (!value) continue; const pluginFactory = BUILTIN_PLUGINS[key]; if (value === true) { this.plugins.push(pluginFactory()); } else { this.plugins.push(pluginFactory(value)); } } } /** * Retrieves a list of active IPv4 network interfaces (excluding internal localhost). * Useful for displaying access URLs in the console (e.g., http://192.168.1.5:8080). * * @returns A record where keys are interface names and values are arrays of IP addresses. */ getActiveNetworkInterfaces() { const nets = os.networkInterfaces(); const result = {}; for (const name in nets) { for (const net of nets[name] ?? []) { if (net.family === "IPv4" && !net.internal) { result[name] ??= []; result[name].push(net.address); } } } return result; } /** * Mounts specified middleware function(s) at the specified path. * If the path is not specified, it defaults to "/". * * @param handlers - Request handlers (middleware). */ use(...handlers) { this.app.use(...handlers); } /** * Routes HTTP GET requests to the specified path with the specified callback functions. * * @param path - The route path. * @param handlers - Callback functions to handle the request. */ get(path, ...handlers) { this.app.get(path, ...handlers); } /** * Routes HTTP POST requests to the specified path with the specified callback functions. * * @param path - The route path. * @param handlers - Callback functions to handle the request. */ post(path, ...handlers) { this.app.post(path, ...handlers); } /** * Routes HTTP PUT requests to the specified path with the specified callback functions. * * @param path - The route path. * @param handlers - Callback functions to handle the request. */ put(path, ...handlers) { this.app.put(path, ...handlers); } /** * Routes HTTP DELETE requests to the specified path with the specified callback functions. * * @param path - The route path. * @param handlers - Callback functions to handle the request. */ delete(path, ...handlers) { this.app.delete(path, ...handlers); } /** * Routes HTTP PATCH requests to the specified path with the specified callback functions. * * @param path - The route path. * @param handlers - Callback functions to handle the request. */ patch(path, ...handlers) { this.app.patch(path, ...handlers); } /** * Routes HTTP requests (all methods) to the specified path with the specified callback functions. * * @param path - The route path. * @param handlers - Callback functions to handle the request. */ all(path, ...handlers) { this.app.all(path, ...handlers); } /** * Registers an event listener on the underlying Express application. * * @param event - The event name. * @param listener - The callback function to execute when the event is emitted. */ on(event, listener) { this.server.on(event, listener); } /** * Removes an event listener from the underlying Express application. * @param event - The event name. * @param listener - The callback function to remove. */ off(event, listener) { this.server.off(event, listener); } /** * Registers a one-time event listener on the underlying Express application. * @param event - The event name. * @param listener - The callback function to execute when the event is emitted. */ once(event, listener) { this.server.once(event, listener); } /** * Registers an event listener on the underlying Express application. * @param event - The event name. * @param listener - The callback function to execute when the event is emitted. */ onApp(event, listener) { this.app.on(event, listener); } /** * Removes an event listener from the underlying Express application. * @param event - The event name. * @param listener - The callback function to remove. */ offApp(event, listener) { this.app.off(event, listener); } /** * Registers a one-time event listener on the underlying Express application. * @param event - The event name. * @param listener - The callback function to execute when the event is emitted. */ onceApp(event, listener) { this.app.once(event, listener); } }; // src/ext.ts import { Router, Application as Application2 } from "express"; import * as express3 from "express"; export { Application2 as Application, Router, SERVER, bodyParser, compression, cookieParser, cors, SERVER as default, express3 as express, helmet, jsonError, morgan, rateLimit, staticPlugin as static, timeout };