UNPKG

restifyx.js

Version:

Advanced API endpoint handler system with automatic documentation

271 lines 8.49 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.All = exports.Head = exports.Options = exports.Patch = exports.Delete = exports.Put = exports.Post = exports.Get = void 0; exports.Controller = Controller; exports.Use = Use; exports.RouteMetadata = RouteMetadata; exports.Validate = Validate; exports.RateLimit = RateLimit; exports.Cache = Cache; exports.WebSocketEvent = WebSocketEvent; exports.extractEndpoints = extractEndpoints; exports.extractWebSocketEvents = extractWebSocketEvents; require("reflect-metadata"); const MetadataKeys = { BASE_PATH: "restifyx:base-path", MIDDLEWARE: "restifyx:middleware", ENDPOINTS: "restifyx:endpoints", VALIDATION: "restifyx:validation", RATE_LIMIT: "restifyx:rate-limit", CACHE: "restifyx:cache", SWAGGER: "restifyx:swagger", WEBSOCKET: "restifyx:websocket", }; /** * Decorator for controller class * * @param basePath - Base path for all routes in the controller * @returns Class decorator * * @example * \`\`\`typescript * @Controller('/api/users') * class UserController { * // ... * } * \`\`\` */ function Controller(basePath) { return (target) => { Reflect.defineMetadata(MetadataKeys.BASE_PATH, basePath, target); }; } /** * Decorator for middleware on controller or method * * @param middleware - Middleware function(s) to apply * @returns Class or method decorator * * @example * \`\`\`typescript * @Controller('/api/users') * @Use(authMiddleware) * class UserController { * // ... * } * \`\`\` */ function Use(...middleware) { return (target, propertyKey) => { if (propertyKey) { const existingMiddleware = Reflect.getMetadata(MetadataKeys.MIDDLEWARE, target, propertyKey) || []; Reflect.defineMetadata(MetadataKeys.MIDDLEWARE, [...existingMiddleware, ...middleware], target, propertyKey); } else { const existingMiddleware = Reflect.getMetadata(MetadataKeys.MIDDLEWARE, target) || []; Reflect.defineMetadata(MetadataKeys.MIDDLEWARE, [...existingMiddleware, ...middleware], target); } }; } /** * Create a route decorator for a specific HTTP method * * @param method - HTTP method * @returns Method decorator factory */ function createRouteDecorator(method) { return (path = "") => { return (target, propertyKey, descriptor) => { const endpoints = Reflect.getMetadata(MetadataKeys.ENDPOINTS, target.constructor) || []; endpoints.push({ method, path, handler: descriptor.value, name: propertyKey.toString(), }); Reflect.defineMetadata(MetadataKeys.ENDPOINTS, endpoints, target.constructor); }; }; } /** * Decorator for GET routes * * @param path - Route path * @returns Method decorator * * @example * \`\`\`typescript * @Controller('/api/users') * class UserController { * @Get('/:id') * async getUser(req: Request, res: Response) { * // ... * } * } * \`\`\` */ exports.Get = createRouteDecorator("GET"); /** * Decorator for POST routes * * @param path - Route path * @returns Method decorator */ exports.Post = createRouteDecorator("POST"); /** * Decorator for PUT routes * * @param path - Route path * @returns Method decorator */ exports.Put = createRouteDecorator("PUT"); /** * Decorator for DELETE routes * * @param path - Route path * @returns Method decorator */ exports.Delete = createRouteDecorator("DELETE"); /** * Decorator for PATCH routes * * @param path - Route path * @returns Method decorator */ exports.Patch = createRouteDecorator("PATCH"); /** * Decorator for OPTIONS routes * * @param path - Route path * @returns Method decorator */ exports.Options = createRouteDecorator("OPTIONS"); /** * Decorator for HEAD routes * * @param path - Route path * @returns Method decorator */ exports.Head = createRouteDecorator("HEAD"); exports.All = createRouteDecorator("ALL"); /** * Decorator for defining route metadata * * @param metadata - Endpoint metadata * @returns Method decorator * * @example * \`\`\`typescript * @Controller('/api/users') * class UserController { * @Get('/:id') * @RouteMetadata({ * name: 'Get User', * description: 'Get a user by ID', * tags: ['users'], * responses: { * '200': { * description: 'User found', * }, * '404': { * description: 'User not found', * } * } * }) * async getUser(req: Request, res: Response) { * // ... * } * } * \`\`\` */ function RouteMetadata(metadata) { return (target, propertyKey, descriptor) => { const endpoints = Reflect.getMetadata(MetadataKeys.ENDPOINTS, target.constructor) || []; const endpointIndex = endpoints.findIndex((endpoint) => endpoint.handler === descriptor.value); if (endpointIndex !== -1) { endpoints[endpointIndex] = { ...endpoints[endpointIndex], ...metadata, }; Reflect.defineMetadata(MetadataKeys.ENDPOINTS, endpoints, target.constructor); } }; } function Validate(schema) { return (target, propertyKey, descriptor) => { Reflect.defineMetadata(MetadataKeys.VALIDATION, schema, target, propertyKey); }; } function RateLimit(options) { return (target, propertyKey, descriptor) => { Reflect.defineMetadata(MetadataKeys.RATE_LIMIT, options, target, propertyKey); }; } function Cache(options) { return (target, propertyKey, descriptor) => { Reflect.defineMetadata(MetadataKeys.CACHE, options, target, propertyKey); }; } function WebSocketEvent(eventName, namespace) { return (target, propertyKey, descriptor) => { Reflect.defineMetadata(MetadataKeys.WEBSOCKET, { name: eventName, namespace }, target, propertyKey); }; } /** * Extract endpoints from a controller class * * @param controllerClass - Controller class * @returns Array of endpoint metadata */ function extractEndpoints(controllerClass) { const basePath = Reflect.getMetadata(MetadataKeys.BASE_PATH, controllerClass) || ""; const controllerMiddleware = Reflect.getMetadata(MetadataKeys.MIDDLEWARE, controllerClass) || []; const endpoints = Reflect.getMetadata(MetadataKeys.ENDPOINTS, controllerClass) || []; return endpoints.map((endpoint) => { let methodMiddleware = []; let validationSchema = null; let rateLimit = null; let cache = null; if (endpoint.name !== undefined) { methodMiddleware = Reflect.getMetadata(MetadataKeys.MIDDLEWARE, controllerClass.prototype, endpoint.name) || []; validationSchema = Reflect.getMetadata(MetadataKeys.VALIDATION, controllerClass.prototype, endpoint.name); rateLimit = Reflect.getMetadata(MetadataKeys.RATE_LIMIT, controllerClass.prototype, endpoint.name); cache = Reflect.getMetadata(MetadataKeys.CACHE, controllerClass.prototype, endpoint.name); } if (validationSchema) { const { validateRequest } = require("../core/validation"); methodMiddleware.unshift(validateRequest(validationSchema)); } if (rateLimit) { const { rateLimitMiddleware } = require("../utils/rate-limiter"); methodMiddleware.unshift(rateLimitMiddleware(rateLimit)); } if (cache) { const { cacheMiddleware } = require("../utils/cache"); methodMiddleware.unshift(cacheMiddleware(cache)); } return { ...endpoint, path: `${basePath}${endpoint.path}`, middleware: [...controllerMiddleware, ...methodMiddleware], }; }); } function extractWebSocketEvents(controllerClass) { const prototype = controllerClass.prototype; const events = []; const methodNames = Object.getOwnPropertyNames(prototype).filter((name) => name !== "constructor" && typeof prototype[name] === "function"); methodNames.forEach((methodName) => { const wsMetadata = Reflect.getMetadata(MetadataKeys.WEBSOCKET, prototype, methodName); if (wsMetadata) { events.push({ handler: prototype[methodName], eventName: wsMetadata.name, namespace: wsMetadata.namespace, }); } }); return events; } //# sourceMappingURL=index.js.map