@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
JavaScript
// 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
};