exstack
Version:
A utility library designed to simplify and enhance express.js applications.
171 lines (170 loc) • 5.86 kB
JavaScript
import { a as HttpError, c as UnAuthorizedError, i as ForbiddenError, l as createHttpErrorClass, n as ConflictError, o as InternalServerError, r as ContentTooLargeError, s as NotFoundError, t as BadRequestError, u as HttpStatus } from "./errors-CuuCGJ3N.mjs";
import { Router } from "express";
//#region src/helps/api-res.ts
/** ApiRes class for standardizing API responses. */
var ApiRes = class ApiRes {
result;
status;
message;
constructor(result = null, status = HttpStatus.OK, message = "Operation successful") {
this.result = result;
this.status = status;
this.message = message;
}
/**
* Returns the Body (JSON) representation of the response.
* @returns The Body (JSON) representation of the response
*
* @example
* new ApiRes('Hello World', 200).body;
*/
get body() {
return {
status: this.status,
message: this.message,
result: this.result
};
}
/** Set message (chainable) */
msg = (message) => {
this.message = message;
return this;
};
/** Set result/data (chainable) */
data = (result) => {
this.result = result;
return this;
};
/**
* Send the json of HTTP response.
* @param {Response} res - The Express response object.
*
* @example
* new ApiRes('Hello World', 200).toJson(res);
*/
toJson = (res) => {
res?.status(this.status)?.json(this.body);
};
/** Clone self with a different status */
static status = (code) => new ApiRes(null, code);
/** Creates an OK (200) response. */
static ok = (result, message = "Request processed successfully") => new ApiRes(result, HttpStatus.OK, message);
/** Creates a Created (201) response. */
static created = (result, message = "Resource created successfully") => new ApiRes(result, HttpStatus.CREATED, message);
/** Creates a paginated OK (200) response. */
static paginated = (data, meta, message = "Data retrieved successfully") => new ApiRes({
...meta,
data
}, HttpStatus.OK, message);
};
//#endregion
//#region src/helps/handler.ts
/**
* Sends the result from a route handler to the client.
*
* - If `result` is an {@link ApiRes}, calls its `.toJson(res)` method.
* - Otherwise, sends it directly (unless it *is* the response object itself).
*
* @param result - The value returned by a route handler.
* @param res - The Express response object.
*/
const handleResult = (result, res) => {
if (result instanceof ApiRes) result.toJson(res);
else if (typeof result === "string") res.type("text/plain").send(result);
else if (result && result !== res) res.send(result);
};
/**
* Wraps a route handler (sync or async) and automatically:
* - Invokes the handler with `(req, res, next)`
* - Catches synchronous and asynchronous errors
* - Passes any returned value into {@link handleResult}
*
* @param callback - A function that handles a request, returning a value or `Promise`.
* @returns An Express-compatible request handler.
*
* @example
* app.get('/ping', handler(async () => ApiRes.ok({ alive: true }).msg('Pong')));
*
* @example
* app.post('/login', handler(async (req, res) => {
* const { username, password } = req.body;
* return new ApiRes(200, { user: username });
* }));
*/
const handler = (callback) => async (req, res, next) => {
try {
const result = callback(req, res, next);
if (result instanceof Promise) result.then((value) => handleResult(value, res)).catch(next);
else handleResult(result, res);
} catch (error) {
next(error);
}
};
//#endregion
//#region src/middle/x-powered.ts
/**
* Middleware to customize or override the `X-Powered-By` HTTP header.
* Often used for branding or hiding technology stack.
*
* @param name - Value to be set for the `X-Powered-By` header
*/
const poweredBy = (name) => (_, res, next) => {
res.setHeader("x-powered-by", name);
next();
};
//#endregion
//#region src/middle/error-handler.ts
/**
* Express middleware to handle `HttpError` and unknown errors.
*
* - Sends JSON response for `HttpError` instances.
* - Logs unknown errors and sends generic error response.
* - Includes detailed error info in development (`isDev`).
*
* @param {Boolean} [isDev = true] - Flag to indicate if the environment is development.
* @param {(error: unknown) => void} [logger = console.error] - Function to log errors.
* @returns {ErrorRequestHandler} - Middleware for handling errors.
*
* @example
* // Basic usage with default options:
* app.use(errorHandler(process.env.NODE_ENV !== 'production'));
* // Custom usage with a logging function in production mode:
* app.use(errorHandler(conf.isDev, logger.error));
*/
const errorHandler = (isDev = true, logger = console.error) => (err, _req, res, _next) => {
if (HttpError.isHttpError(err)) {
if (err.options.cause) logger?.(err.options.cause);
return err.toJson(res);
}
logger?.(err);
const unknown = {
status: HttpStatus.INTERNAL_SERVER_ERROR,
error: "InternalServerError",
message: isDev ? err.message || "Unexpected error" : "Something went wrong",
stack: isDev ? err.stack : void 0
};
res.status(unknown.status).json(unknown);
};
/**
* Middleware to handle 404 Not Found errors.
*
* This function creates an Express router that catches all requests to
* undefined routes and returns a JSON response with a 404 error.
*
* @param {string} [path='*'] - The route pattern to match (default: '*').
* @returns {Router} Express router instance handling 404 errors.
*
* @example
* app.use(notFound("*")) // v4
* app.use(notFound("*splat")) // v5
*/
const notFound = (path) => Router().all(path, (req, res) => new HttpError(HttpStatus.NOT_FOUND, {
message: "Wrong Path",
code: "NOT_FOUND",
meta: {
path: req.path,
method: req.method
}
}).toJson(res));
//#endregion
export { ApiRes, BadRequestError, ConflictError, ContentTooLargeError, ForbiddenError, HttpError, HttpStatus, InternalServerError, NotFoundError, UnAuthorizedError, createHttpErrorClass, errorHandler, handleResult, handler, notFound, poweredBy };