UNPKG

ts5deco-express-controller

Version:

TypeScript 5 Modern Decorator Express Controller Framework

286 lines (268 loc) 19.5 kB
"use strict";var Y=Object.create;var w=Object.defineProperty;var Q=Object.getOwnPropertyDescriptor;var X=Object.getOwnPropertyNames;var Z=Object.getPrototypeOf,ee=Object.prototype.hasOwnProperty;var te=(e,t,s)=>t in e?w(e,t,{enumerable:!0,configurable:!0,writable:!0,value:s}):e[t]=s;var se=(e,t)=>{for(var s in t)w(e,s,{get:t[s],enumerable:!0})},N=(e,t,s,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of X(t))!ee.call(e,r)&&r!==s&&w(e,r,{get:()=>t[r],enumerable:!(a=Q(t,r))||a.enumerable});return e};var D=(e,t,s)=>(s=e!=null?Y(Z(e)):{},N(t||!e||!e.__esModule?w(s,"default",{value:e,enumerable:!0}):s,e)),ae=e=>N(w({},"__esModule",{value:!0}),e);var C=(e,t,s)=>te(e,typeof t!="symbol"?t+"":t,s);var qe={};se(qe,{All:()=>ue,Authenticated:()=>le,Authorized:()=>me,BaseResponse:()=>i,Controller:()=>ne,Delete:()=>pe,FileResponse:()=>R,FileResponses:()=>V,Get:()=>oe,Head:()=>ce,JsonResponse:()=>g,METADATA_KEYS:()=>E,NoContentResponse:()=>F,Options:()=>he,Patch:()=>Te,Post:()=>ie,Put:()=>de,RedirectResponse:()=>h,RedirectResponses:()=>I,ResponseFactory:()=>c,TextResponse:()=>U,TypedApiResponseFactory:()=>$,TypedDelete:()=>we,TypedGet:()=>ge,TypedPost:()=>Re,TypedPut:()=>Me,Use:()=>A,Validated:()=>xe,addRouteMetadata:()=>y,convertExpressPath:()=>je,convertExpressPathToOpenApiPath:()=>Pe,convertOpenAPIPath:()=>Ve,convertOpenApiPathToExpressPath:()=>k,createApiResponse:()=>O,createPathMatcher:()=>L,createResponseFor:()=>Se,createRouter:()=>z,createTypedRoutes:()=>Ee,extractPathParameterNames:()=>G,extractPathParameterValues:()=>ye,generateTypes:()=>Ae,getControllerMetadata:()=>q,getRouteMetadata:()=>f,initProject:()=>Ie,isBaseResponse:()=>be,isValidOpenApiPath:()=>fe,registerController:()=>K,registerControllers:()=>ve,setControllerMetadata:()=>j,setRouteMetadata:()=>b});module.exports=ae(qe);var v=new WeakMap;function re(e){return v.has(e)||v.set(e,new Map),v.get(e)}function B(e,t,s){re(s).set(e,t)}function J(e,t){return v.get(t)?.get(e)}var E={CONTROLLER:Symbol("controller"),ROUTES:Symbol("routes"),PARAMETERS:Symbol("parameters"),MIDDLEWARES:Symbol("middlewares")};function j(e,t){B(E.CONTROLLER,t,e)}function q(e){return J(E.CONTROLLER,e)}function b(e,t){B(E.ROUTES,t,e)}function f(e){return J(E.ROUTES,e)||[]}function y(e,t){let s=f(e);b(e,[...s,t])}function ne(e){return function(t,s){let a="",r=[];return typeof e=="string"?a=e:e&&(a=e.path||"",r=e.middlewares||[]),a=("/"+a).replace(/\/+/g,"/").replace(/\/$/,"")||"/",j(t,{path:a,middlewares:r}),t}}function x(e){return function(t){return function(s,a){let r="",n=[];typeof t=="string"?r=t:t&&(r=t.path||"",n=t.middlewares||[]),r=("/"+r).replace(/\/+/g,"/").replace(/\/$/,"")||"/",a.addInitializer(function(){y(this.constructor,{path:r,method:e,middlewares:n,propertyKey:a.name})})}}}var oe=x("GET"),ie=x("POST"),de=x("PUT"),pe=x("DELETE"),Te=x("PATCH"),ce=x("HEAD"),he=x("OPTIONS");function ue(e){return function(t,s){let a=["GET","POST","PUT","DELETE","PATCH","HEAD","OPTIONS"],r="",n=[];typeof e=="string"?r=e:e&&(r=e.path||"",n=e.middlewares||[]),r=("/"+r).replace(/\/+/g,"/").replace(/\/$/,"")||"/",s.addInitializer(function(){a.forEach(o=>{y(this.constructor,{path:r,method:o,middlewares:n,propertyKey:s.name})})})}}function A(...e){return function(t,s){s.addInitializer(function(){let r=f(this.constructor).map(n=>n.propertyKey===s.name?{...n,middlewares:[...e,...n.middlewares]}:n);r.length>0&&b(this.constructor,r)})}}function le(e){return A(e)}function me(e){return A(e)}function xe(e){return A(e)}function k(e){return e.replace(/{([^}]+)}/g,":$1")}function Pe(e){return e.replace(/:([^/]+)/g,"{$1}")}function G(e){let t=e.match(/{([^}]+)}/g);return t?t.map(s=>s.slice(1,-1)):[]}function fe(e){let t=(e.match(/{/g)||[]).length,s=(e.match(/}/g)||[]).length;if(t!==s)return!1;let a=0;for(let r of e)if(r==="{"){if(a++,a>1)return!1}else if(r==="}"&&(a--,a<0))return!1;return a===0}function L(e){let s=k(e).replace(/:[^/]+/g,"([^/]+)").replace(/\//g,"\\/");return new RegExp(`^${s}$`)}function ye(e,t){let s=G(e),a=L(e),r=t.match(a);if(!r||r.length!==s.length+1)return{};let n={};return s.forEach((o,d)=>{let T=r[d+1];T!==void 0&&(n[o]=T)}),n}var i=class{constructor(t){this.statusCode=t}};var g=class e extends i{constructor(s,a){super(s);this.data=a}send(s){s.status(this.statusCode).json(this.data)}static ok(s){return new e(200,s)}static created(s){return new e(201,s)}static badRequest(s){return new e(400,s)}static unauthorized(s){return new e(401,s)}static forbidden(s){return new e(403,s)}static notFound(s){return new e(404,s)}static internalError(s){return new e(500,s)}};var c=class{static create(t,s){let a=new g(t,s);return a.__brand=`api:${String(t)}:${String(t)}:${t}`,a.__statusCode=t,a.__data=s,a}static ok(t){return this.create(200,t)}static created(t){return this.create(201,t)}static badRequest(t){return this.create(400,t)}static notFound(t){return this.create(404,t)}static internalError(t){return this.create(500,t)}};function O(){return new $}var $=class{constructor(){C(this,"_allowedStatuses")}ok(t){return c.ok(t)}created(t){return c.created(t)}badRequest(t){return c.badRequest(t)}notFound(t){return c.notFound(t)}internalError(t){return c.internalError(t)}};function p(e){return function(t,s={}){return function(a,r){let{middlewares:n=[]}=s,o=t,d={},M=("/"+k(t)).replace(/\/+/g,"/").replace(/\/$/,"")||"/";r.addInitializer(function(){y(this.constructor,{path:M,method:e,middlewares:n,propertyKey:r.name})})}}}function ge(){return p("GET")}function Re(){return p("POST")}function Me(){return p("PUT")}function we(){return p("DELETE")}function Ee(){return{Get:p("GET"),Post:p("POST"),Put:p("PUT"),Delete:p("DELETE"),Patch:p("PATCH"),Head:p("HEAD"),Options:p("OPTIONS")}}function Se(){return O()}var H=require("express");function z(e){let t=(0,H.Router)();return e.forEach(s=>{K(t,s)}),t}function K(e,t){let s=q(t),a=f(t);if(!s){console.warn(`Controller ${t.name} does not have @Controller decorator`);return}let r=new t;a.forEach(n=>{let o=Ce(s.path,n.path),d=n.method.toLowerCase(),T=De(r,n.propertyKey),M=[...s.middlewares,...n.middlewares,T];typeof e[d]=="function"&&e[d](o,...M)})}function De(e,t){return async(s,a,r)=>{try{let n=await e[t](s,a,r);if(a.headersSent)return;if(n instanceof i){n.send(a);return}n!==void 0&&a.json(n)}catch(n){r(n)}}}function Ce(e,t){let s=`${e}${t}`.replace(/\/+/g,"/");return s==="/"?"/":s.replace(/\/$/,"")}function ve(e,t,s=""){let a=z(t);e.use(s,a)}var U=class e extends i{constructor(s,a){super(s);this.text=a}send(s){s.status(this.statusCode).send(this.text||"")}static ok(s){return new e(200,s||"OK")}static created(s){return new e(201,s||"Created")}static badRequest(s){return new e(400,s||"Bad Request")}static unauthorized(s){return new e(401,s||"Unauthorized")}static forbidden(s){return new e(403,s||"Forbidden")}static notFound(s){return new e(404,s||"Not Found")}static internalError(s){return new e(500,s||"Internal Server Error")}};var F=class extends i{constructor(){super(204)}send(t){t.status(204).end()}};var h=class extends i{constructor(s,a=!1){super(typeof a=="number"?a:a?301:302);C(this,"url");this.url=s}send(s){s.status(this.statusCode).redirect(this.url)}},I=class{static temporary(t){return new h(t,302)}static permanent(t){return new h(t,301)}static temporaryPreserveMethod(t){return new h(t,307)}static permanentPreserveMethod(t){return new h(t,308)}};var R=class extends i{constructor(s,a,r=!1){super(200);this.filePath=s;this.filename=a;this.asAttachment=r}send(s){this.asAttachment?this.filename?s.status(this.statusCode).download(this.filePath,this.filename):s.status(this.statusCode).download(this.filePath):this.filename?s.status(this.statusCode).sendFile(this.filePath,{headers:{"Content-Disposition":`inline; filename="${this.filename}"`}}):s.status(this.statusCode).sendFile(this.filePath)}},V=class{static inline(t,s){return new R(t,s,!1)}static attachment(t,s){return new R(t,s,!0)}};function be(e){return e instanceof i}var l=D(require("fs/promises")),u=D(require("path")),_=require("child_process");async function Ae(e){let{input:t,outputDir:s,apiTypesPath:a,utilsPath:r,generateAliases:n=!0,generateUtils:o=!0,openapiTypescriptOptions:d=[]}=e;try{await l.default.access(t)}catch{throw new Error(`OpenAPI specification file not found: ${t}`)}await l.default.mkdir(s,{recursive:!0});let T=u.default.join(s,"api.d.ts");console.log(`\u{1F4C4} Processing ${t}...`);let M=["npx openapi-typescript",`"${t}"`,"-o",`"${T}"`,...d].join(" ");try{(0,_.execSync)(M,{stdio:"inherit"}),console.log(`\u2705 Generated types: ${T}`)}catch(S){throw new Error(`Failed to generate types: ${S instanceof Error?S.message:String(S)}`)}let W=await ke(T);o&&r&&(await $e(r,T),console.log(`\u2705 Generated utility types: ${r}`)),n&&a&&(await Ue(a,W,r),console.log(`\u2705 Generated API type aliases: ${a}`))}async function ke(e){try{let s=(await l.default.readFile(e,"utf8")).match(/schemas:\s*{([^}]+)}/s);if(!s||!s[1])return[];let a=s[1],r=[],n=/(\w+):\s*{/g,o;for(;(o=n.exec(a))!==null;)o[1]&&r.push(o[1]);return r}catch(t){return console.warn("Warning: Could not extract schema names:",t instanceof Error?t.message:String(t)),[]}}async function $e(e,t){let a=`/** * OpenAPI \uD0C0\uC785 \uC720\uD2F8\uB9AC\uD2F0 * openapi-typescript\uB85C \uC0DD\uC131\uB41C \uD0C0\uC785\uB4E4\uC744 \uC27D\uAC8C \uC0AC\uC6A9\uD558\uAE30 \uC704\uD55C \uD5EC\uD37C\uB4E4 */ import type { paths, components } from './${u.default.relative(u.default.dirname(e),t).replace(/\.d\.ts$/,"").replace(/\\/g,"/")}'; /** * OpenAPI \uC2A4\uD0A4\uB9C8\uC5D0\uC11C \uD0C0\uC785\uC744 \uCD94\uCD9C\uD558\uB294 \uD5EC\uD37C \uD0C0\uC785 * @example * \`\`\`typescript * type User = ExtractSchema<'User'>; * type ErrorResponse = ExtractSchema<'Error'>; * \`\`\` */ export type ExtractSchema<T extends keyof components['schemas']> = components['schemas'][T]; /** * OpenAPI \uACBD\uB85C\uC5D0\uC11C \uC751\uB2F5 \uD0C0\uC785\uC744 \uCD94\uCD9C\uD558\uB294 \uD5EC\uD37C \uD0C0\uC785 * @example * \`\`\`typescript * type GetUserResponse = ExtractResponse<'/users/{id}', 'get'>; * type CreateUserResponse = ExtractResponse<'/users', 'post', 201>; * \`\`\` */ export type ExtractResponse< TPath extends keyof paths, TMethod extends keyof paths[TPath], TStatus extends number = 200 > = TPath extends keyof paths ? TMethod extends keyof paths[TPath] ? paths[TPath][TMethod] extends { responses: any } ? TStatus extends keyof paths[TPath][TMethod]['responses'] ? paths[TPath][TMethod]['responses'][TStatus] extends { content: { 'application/json': infer T } } ? T : never : never : never : never : never; /** * OpenAPI \uACBD\uB85C\uC5D0\uC11C \uC694\uCCAD \uBCF8\uBB38 \uD0C0\uC785\uC744 \uCD94\uCD9C\uD558\uB294 \uD5EC\uD37C \uD0C0\uC785 */ export type ExtractRequestBody< TPath extends keyof paths, TMethod extends keyof paths[TPath] > = TPath extends keyof paths ? TMethod extends keyof paths[TPath] ? paths[TPath][TMethod] extends { requestBody: { content: { 'application/json': infer T } } } ? T : never : never : never; /** * OpenAPI \uACBD\uB85C\uC5D0\uC11C \uD30C\uB77C\uBBF8\uD130 \uD0C0\uC785\uC744 \uCD94\uCD9C\uD558\uB294 \uD5EC\uD37C \uD0C0\uC785 */ export type ExtractParameters< TPath extends keyof paths, TMethod extends keyof paths[TPath] > = TPath extends keyof paths ? TMethod extends keyof paths[TPath] ? paths[TPath][TMethod] extends { parameters: infer P } ? P : never : never : never; // \uC6D0\uBCF8 \uD0C0\uC785\uB4E4\uB3C4 re-export export type { paths, components }; `;await l.default.mkdir(u.default.dirname(e),{recursive:!0}),await l.default.writeFile(e,a)}async function Ue(e,t,s){let r=`/** * API \uD0C0\uC785 \uBCC4\uCE6D * OpenAPI \uC2A4\uD399\uC5D0\uC11C \uC0DD\uC131\uB41C \uD0C0\uC785\uB4E4\uC744 \uC27D\uAC8C \uC0AC\uC6A9\uD558\uAE30 \uC704\uD55C \uBCC4\uCE6D \uC815\uC758 * * \uC774 \uD30C\uC77C\uC740 \uC790\uB3D9 \uC0DD\uC131\uB429\uB2C8\uB2E4. \uD544\uC694\uC5D0 \uB530\uB77C \uC218\uC815\uD558\uC5EC \uC0AC\uC6A9\uD558\uC138\uC694. */ ${s?`import type { ExtractSchema } from './${u.default.relative(u.default.dirname(e),s).replace(/\.ts$/,"").replace(/\\/g,"/")}';`:""} /** * \uC77C\uBC18\uC801\uC73C\uB85C \uC0AC\uC6A9\uB418\uB294 \uD0C0\uC785 \uBCC4\uCE6D\uB4E4 * \uD544\uC694\uC5D0 \uB530\uB77C \uCD94\uAC00\uD558\uAC70\uB098 \uC218\uC815\uD558\uC138\uC694 */ ${t.map(n=>{let o=Fe(n);return o?`export type ${o} = ExtractSchema<'${n}'>;`:`// export type ${n} = ExtractSchema<'${n}'>;`}).join(` `)} /** * \uBAA8\uB4E0 \uC2A4\uD0A4\uB9C8 \uD0C0\uC785\uB4E4 (\uCC38\uACE0\uC6A9) * \uC544\uB798 \uD0C0\uC785\uB4E4\uC744 \uC704\uC640 \uAC19\uC774 \uBCC4\uCE6D\uC73C\uB85C \uB9CC\uB4E4\uC5B4 \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4: * ${t.map(n=>` * - ${n}`).join(` `)} */ `;await l.default.mkdir(u.default.dirname(e),{recursive:!0}),await l.default.writeFile(e,r)}function Fe(e){return{User:"User",Error:"ErrorResponse",CreateUserRequest:"CreateUserRequest",UpdateUserRequest:"UpdateUserRequest",PaginatedUserResponse:"PaginatedResponse",ApiError:"ApiError",ValidationError:"ValidationError"}[e]||null}var P=D(require("fs/promises")),m=D(require("path"));async function Ie(e="."){let t=m.default.resolve(e),s=m.default.join(t,"api");await P.default.mkdir(s,{recursive:!0}),await P.default.writeFile(m.default.join(s,"openapi.yaml"),`openapi: 3.0.0 info: title: My API version: 1.0.0 description: API for my application servers: - url: http://localhost:3000/api description: Local development server paths: /users: get: summary: Get all users operationId: getUsers parameters: - name: page in: query schema: type: integer default: 1 - name: limit in: query schema: type: integer default: 10 responses: '200': description: Successful response content: application/json: schema: $ref: '#/components/schemas/PaginatedUserResponse' '500': description: Internal server error content: application/json: schema: $ref: '#/components/schemas/Error' post: summary: Create a new user operationId: createUser requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateUserRequest' responses: '201': description: User created successfully content: application/json: schema: $ref: '#/components/schemas/User' '400': description: Bad request content: application/json: schema: $ref: '#/components/schemas/Error' /users/{id}: get: summary: Get a user by ID operationId: getUserById parameters: - name: id in: path required: true schema: type: string responses: '200': description: Successful response content: application/json: schema: $ref: '#/components/schemas/User' '404': description: User not found content: application/json: schema: $ref: '#/components/schemas/Error' components: schemas: User: type: object required: - id - name - email properties: id: type: string example: "123" name: type: string example: "John Doe" email: type: string format: email example: "john@example.com" createdAt: type: string format: date-time updatedAt: type: string format: date-time CreateUserRequest: type: object required: - name - email - password properties: name: type: string example: "John Doe" email: type: string format: email example: "john@example.com" password: type: string format: password minLength: 8 Error: type: object required: - error - message properties: error: type: string example: "NOT_FOUND" message: type: string example: "The requested resource was not found" code: type: string example: "USER_NOT_FOUND" statusCode: type: integer example: 404 PaginatedUserResponse: type: object required: - data - total - page - limit - hasNext - hasPrev properties: data: type: array items: $ref: '#/components/schemas/User' total: type: integer example: 100 page: type: integer example: 1 limit: type: integer example: 10 hasNext: type: boolean example: true hasPrev: type: boolean example: false `);let r=m.default.join(t,"package.json"),n=!1;try{let o=JSON.parse(await P.default.readFile(r,"utf8"));o.scripts||(o.scripts={}),o.scripts["generate:types"]||(o.scripts["generate:types"]="ts5deco-express-controller generate",n=!0),o.scripts.generate||(o.scripts.generate="npm run generate:types",n=!0),n&&(await P.default.writeFile(r,JSON.stringify(o,null,2)),console.log("\u2705 Updated package.json with recommended scripts"))}catch{console.log("\u{1F4A1} Recommended package.json scripts:"),console.log(' "generate:types": "ts5deco-express-controller generate"'),console.log(' "generate": "npm run generate:types"')}try{let o=m.default.join(t,".gitignore"),d="";try{d=await P.default.readFile(o,"utf8")}catch{}d.includes("src/types/generated/")||(d+=` # Generated OpenAPI types src/types/generated/ `,await P.default.writeFile(o,d),console.log("\u2705 Updated .gitignore to exclude generated types"))}catch{console.log("\u{1F4A1} Recommended .gitignore addition:"),console.log(" src/types/generated/")}console.log(` \u{1F4C1} Created files:`),console.log(` ${m.default.relative(process.cwd(),m.default.join(s,"openapi.yaml"))}`)}function Ve(e){return e.replace(/{([^}]+)}/g,":$1")}function je(e){return e.replace(/:([^/]+)/g,"{$1}")}0&&(module.exports={All,Authenticated,Authorized,BaseResponse,Controller,Delete,FileResponse,FileResponses,Get,Head,JsonResponse,METADATA_KEYS,NoContentResponse,Options,Patch,Post,Put,RedirectResponse,RedirectResponses,ResponseFactory,TextResponse,TypedApiResponseFactory,TypedDelete,TypedGet,TypedPost,TypedPut,Use,Validated,addRouteMetadata,convertExpressPath,convertExpressPathToOpenApiPath,convertOpenAPIPath,convertOpenApiPathToExpressPath,createApiResponse,createPathMatcher,createResponseFor,createRouter,createTypedRoutes,extractPathParameterNames,extractPathParameterValues,generateTypes,getControllerMetadata,getRouteMetadata,initProject,isBaseResponse,isValidOpenApiPath,registerController,registerControllers,setControllerMetadata,setRouteMetadata}); //# sourceMappingURL=index.js.map