@catbee/utils
Version:
A modular, production-grade utility toolkit for Node.js and TypeScript, designed for robust, scalable applications (including Express-based services). All utilities are tree-shakable and can be imported independently.
165 lines • 7.45 kB
JavaScript
;
/* eslint-disable @typescript-eslint/no-unused-vars */
Object.defineProperty(exports, "__esModule", { value: true });
exports.Res = exports.Req = exports.Body = exports.Param = exports.Query = exports.Connect = exports.Trace = exports.Head = exports.Options = exports.Delete = exports.Patch = exports.Put = exports.Post = exports.Get = void 0;
exports.Controller = Controller;
exports.Use = Use;
exports.HttpCode = HttpCode;
exports.Header = Header;
exports.Before = Before;
exports.After = After;
exports.registerControllers = registerControllers;
require("reflect-metadata");
// --- End sample Express interfaces ---
const ROUTES_KEY = Symbol("routes");
const MIDDLEWARE_KEY = Symbol("middlewares");
const PARAMS_KEY = Symbol("params");
// ---- Route Decorators ----
function createRouteDecorator(method) {
return (path) => {
return (target, propertyKey, descriptor) => {
const routes = Reflect.getMetadata(ROUTES_KEY, target.constructor) || [];
routes.push({
path,
method,
handlerName: propertyKey,
});
Reflect.defineMetadata(ROUTES_KEY, routes, target.constructor);
};
};
}
exports.Get = createRouteDecorator("get");
exports.Post = createRouteDecorator("post");
exports.Put = createRouteDecorator("put");
exports.Patch = createRouteDecorator("patch");
exports.Delete = createRouteDecorator("delete");
exports.Options = createRouteDecorator("options");
exports.Head = createRouteDecorator("head");
exports.Trace = createRouteDecorator("trace");
exports.Connect = createRouteDecorator("connect");
// ---- Controller Decorator ----
function Controller(basePath) {
return (target) => {
Reflect.defineMetadata("basePath", basePath, target);
};
}
// ---- Middleware Decorator ----
function Use(...middlewares) {
return (target, propertyKey, descriptor) => {
const existing = Reflect.getMetadata(MIDDLEWARE_KEY, target, propertyKey) || [];
Reflect.defineMetadata(MIDDLEWARE_KEY, [...existing, ...middlewares], target, propertyKey);
};
}
// ---- Parameter Decorators ----
function createParamDecorator(type, key) {
return (paramKey) => {
return (target, propertyKey, parameterIndex) => {
const params = Reflect.getMetadata(PARAMS_KEY, target, propertyKey) || [];
// Ensure parameters are ordered by index
params.push({ index: parameterIndex, type, key: paramKey || key });
params.sort((a, b) => a.index - b.index);
Reflect.defineMetadata(PARAMS_KEY, params, target, propertyKey);
};
};
}
exports.Query = createParamDecorator("query");
exports.Param = createParamDecorator("param");
exports.Body = createParamDecorator("body");
exports.Req = createParamDecorator("req");
exports.Res = createParamDecorator("res");
// Custom HTTP status code decorator
const HTTP_CODE_KEY = Symbol("httpCode");
function HttpCode(status) {
return (target, propertyKey, descriptor) => {
Reflect.defineMetadata(HTTP_CODE_KEY, status, target, propertyKey);
};
}
// Custom header decorator
const HEADER_KEY = Symbol("headers");
function Header(name, value) {
return (target, propertyKey, descriptor) => {
const headers = Reflect.getMetadata(HEADER_KEY, target, propertyKey) || {};
headers[name] = value;
Reflect.defineMetadata(HEADER_KEY, headers, target, propertyKey);
};
}
// Before/After hooks (not Express middleware, but can be used for logging, etc.)
const BEFORE_KEY = Symbol("before");
const AFTER_KEY = Symbol("after");
function Before(fn) {
return (target, propertyKey, descriptor) => {
const hooks = Reflect.getMetadata(BEFORE_KEY, target, propertyKey) || [];
hooks.push(fn);
Reflect.defineMetadata(BEFORE_KEY, hooks, target, propertyKey);
};
}
function After(fn) {
return (target, propertyKey, descriptor) => {
const hooks = Reflect.getMetadata(AFTER_KEY, target, propertyKey) || [];
hooks.push(fn);
Reflect.defineMetadata(AFTER_KEY, hooks, target, propertyKey);
};
}
// ---- Register Controllers ----
function registerControllers(router, controllers) {
controllers.forEach((ControllerClass) => {
const instance = new ControllerClass();
const basePath = Reflect.getMetadata("basePath", ControllerClass) || "";
const routes = Reflect.getMetadata(ROUTES_KEY, ControllerClass) || [];
routes.forEach(({ path, method, handlerName }) => {
const middlewares = Reflect.getMetadata(MIDDLEWARE_KEY, instance, handlerName) || [];
const params = Reflect.getMetadata(PARAMS_KEY, instance, handlerName) || [];
const httpCode = Reflect.getMetadata(HTTP_CODE_KEY, instance, handlerName);
const headers = Reflect.getMetadata(HEADER_KEY, instance, handlerName) || {};
const beforeHooks = Reflect.getMetadata(BEFORE_KEY, instance, handlerName) || [];
const afterHooks = Reflect.getMetadata(AFTER_KEY, instance, handlerName) || [];
const handler = async (req, res, next) => {
var _a, _b;
try {
for (const fn of beforeHooks)
await fn(req, res);
const args = [];
if (params.length) {
params.forEach(({ index, type, key }) => {
var _a;
switch (type) {
case "query":
args[index] = key ? req.query[key] : req.query;
break;
case "param":
args[index] = key ? req.params[key] : req.params;
break;
case "body":
args[index] = key ? (_a = req.body) === null || _a === void 0 ? void 0 : _a[key] : req.body;
break;
case "req":
args[index] = req;
break;
case "res":
args[index] = res;
break;
}
});
}
const result = instance[handlerName](...args);
// Support both sync and async handlers
const awaited = result instanceof Promise ? await result : result;
if (!res.headersSent && typeof awaited !== "undefined") {
if (httpCode)
(_a = res.status) === null || _a === void 0 ? void 0 : _a.call(res, httpCode);
for (const [k, v] of Object.entries(headers))
(_b = res.set) === null || _b === void 0 ? void 0 : _b.call(res, k, v);
res.json(awaited);
}
for (const fn of afterHooks)
await fn(req, res, awaited);
}
catch (err) {
next(err);
}
};
router[method](basePath + path, ...middlewares, handler);
});
});
}
//# sourceMappingURL=decorators.utils.js.map