nestjs-doctor 0.4.30 → 0.4.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/index.d.mts +1 -0
- package/dist/api/index.mjs +5 -5
- package/dist/cli/index.mjs +6 -6
- package/package.json +1 -1
package/dist/api/index.d.mts
CHANGED
package/dist/api/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import{existsSync as e,readFileSync as t,readdirSync as n,statSync as r}from"node:fs";import{dirname as i,join as a,relative as o,resolve as s}from"node:path";import{readFile as c}from"node:fs/promises";import{glob as l}from"tinyglobby";import{performance as u}from"node:perf_hooks";import{Project as d,SyntaxKind as f,ts as p}from"ts-morph";import{createJiti as m}from"jiti";import h from"picomatch";var g=class extends Error{constructor(e){super(e),this.name=`NestjsDoctorError`}},_=class extends g{constructor(e){super(e),this.name=`ConfigurationError`}},v=class extends g{constructor(e){super(e),this.name=`ScanError`}},y=class extends g{constructor(e){super(e),this.name=`ValidationError`}};const b=/^packages\s*:/,x=/^packages\s*:\s*\[(.+)\]/,S=/^\S/,ee=/^-\s+['"]?([^'"]+)['"]?\s*$/,te=/^['"]|['"]$/g;function ne(e){let t=[],n=e.split(`
|
|
2
|
-
`),r=!1;for(let e of n){let n=e.trim();if(b.test(n)){let e=n.match(x);if(e){for(let n of e[1].split(`,`)){let e=n.trim().replace(te,``);e&&t.push(e)}return t}r=!0;continue}if(r){if(S.test(e)&&n!==``)break;let r=n.match(ee);r&&t.push(r[1])}}return t}async function re(e){let t=a(e,`nest-cli.json`);try{let e=await c(t,`utf-8`),n=JSON.parse(e);if(!(n.monorepo&&n.projects))return null;let r=new Map;for(let[e,t]of Object.entries(n.projects)){let n=t.root??e;r.set(e,n)}return r.size===0?null:{projects:r}}catch{return null}}function ie(e){let t={...e.dependencies,...e.devDependencies,...e.peerDependencies};return!!(t[`@nestjs/core`]||t[`@nestjs/common`])}async function ae(e,t){let n=await l(t.map(e=>`${e}/package.json`),{cwd:e,absolute:!0,ignore:[`**/node_modules/**`]}),r=new Map;for(let t of n)try{let n=await c(t,`utf-8`),a=JSON.parse(n);if(ie(a)){let n=o(e,i(t)),s=a.name??n;r.set(s,n)}}catch{}return r.size===0?null:{projects:r}}async function oe(e){let t=a(e,`pnpm-workspace.yaml`),n;try{n=await c(t,`utf-8`)}catch{return null}let r=ne(n);return r.length===0?null:ae(e,r)}function se(e){let t=e.workspaces;if(!t)return[];if(Array.isArray(t))return t.filter(e=>typeof e==`string`);if(typeof t==`object`&&t){let e=t;if(Array.isArray(e.packages))return e.packages.filter(e=>typeof e==`string`)}return[]}async function ce(e){let t=a(e,`package.json`),n;try{n=await c(t,`utf-8`)}catch{return null}let r=se(JSON.parse(n));return r.length===0?null:ae(e,r)}async function le(e){let t=a(e,`lerna.json`),n;try{n=await c(t,`utf-8`)}catch{return null}let r=JSON.parse(n);if(r.useWorkspaces)return null;let i=r.packages??[`packages/*`];return i.length===0?null:ae(e,i)}async function ue(e){let t=a(e,`nx.json`);try{await c(t,`utf-8`)}catch{return null}let n=await l([`**/project.json`],{cwd:e,absolute:!0,ignore:[`node_modules/**`]}),r=new Map;for(let t of n){let n=i(t),s=o(e,n);if(s===``)continue;let l=a(n,`package.json`);try{let e=await c(l,`utf-8`),t=JSON.parse(e);if(ie(t)){let e=t.name??s;r.set(e,s)}}catch{}}return r.size===0?null:{projects:r}}async function de(e){try{return await c(a(e,`pnpm-workspace.yaml`),`utf-8`),!0}catch{return!1}}async function fe(e){let t=await re(e);if(t)return t;let n=await oe(e);if(n)return n;if(!await de(e)){let t=await ce(e);if(t)return t}return await ue(e)||le(e)}async function pe(e){let t=a(e,`package.json`),n={};try{let e=await c(t,`utf-8`);n=JSON.parse(e)}catch{}let r={...n.dependencies,...n.devDependencies},i=me(r[`@nestjs/core`]),o=he(r),s=ge(r);return{name:n.name??`unknown`,nestVersion:i,orm:o,framework:s,moduleCount:0,fileCount:0}}function me(e){return e?e.replace(/[\^~>=<]/g,``):null}function he(e){return e[`@prisma/client`]?`prisma`:e.typeorm?`typeorm`:e[`@mikro-orm/core`]?`mikro-orm`:e.sequelize?`sequelize`:e.mongoose?`mongoose`:e[`drizzle-orm`]?`drizzle`:null}function ge(e){return e[`@nestjs/platform-fastify`]?`fastify`:e[`@nestjs/platform-express`]||e[`@nestjs/core`]?`express`:null}const C={include:[`**/*.ts`],exclude:`**/node_modules/**,**/dist/**,**/build/**,**/coverage/**,**/*.spec.ts,**/*.test.ts,**/*.e2e-spec.ts,**/*.e2e-test.ts,**/*.d.ts,**/test/**,**/tests/**,**/__tests__/**,**/__mocks__/**,**/__fixtures__/**,**/mock/**,**/mocks/**,**/*.mock.ts,**/seeder/**,**/seeders/**,**/*.seed.ts,**/*.seeder.ts,*.config.ts,*.config.js,*.config.mjs,*.config.cjs,*.config.mts,*.config.cts`.split(`,`)},_e=[`nestjs-doctor.config.json`,`.nestjs-doctor.json`];async function ve(e,t){if(t)return ye(t);for(let t of _e)try{return await ye(a(e,t))}catch{}try{let t=await c(a(e,`package.json`),`utf-8`),n=JSON.parse(t);if(n[`nestjs-doctor`]&&typeof n[`nestjs-doctor`]==`object`)return be(n[`nestjs-doctor`])}catch{}return{...C}}async function ye(e){let t=await c(e,`utf-8`);return be(JSON.parse(t))}function be(e){return{...C,...e,exclude:[...C.exclude??[],...e.exclude??[]]}}async function xe(e,t){try{return await ve(e)}catch{return t}}const Se=[/Repository$/,/\.repository$/,/\.entity$/,/\.schema$/,/\.guard$/,/\.interceptor$/,/\.pipe$/,/\.filter$/,/\.strategy$/],Ce={meta:{id:`architecture/no-barrel-export-internals`,category:`architecture`,severity:`info`,description:`Don't re-export internal implementation details from barrel files`,help:`Only export the module's public API (services, DTOs, interfaces) from index.ts files.`},check(e){if(e.filePath.endsWith(`/index.ts`))for(let t of e.sourceFile.getExportDeclarations()){let n=t.getModuleSpecifierValue();if(n){Se.some(e=>e.test(n))&&e.report({filePath:e.filePath,message:`Barrel file re-exports internal module '${n}'.`,help:this.meta.help,line:t.getStartLineNumber(),column:1});for(let n of t.getNamedExports()){let t=n.getName();(t.endsWith(`Repository`)||t.endsWith(`Entity`)||t.endsWith(`Schema`))&&e.report({filePath:e.filePath,message:`Barrel file re-exports internal type '${t}'.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}}}},w=new Set([`Get`,`Post`,`Put`,`Patch`,`Delete`,`Head`,`Options`,`All`]);function T(e,t){return e.getDecorator(t)!==void 0}function E(e){return T(e,`Controller`)}function we(e){return T(e,`Injectable`)}function Te(e){return T(e,`Injectable`)||T(e,`Controller`)||T(e,`Resolver`)||T(e,`WebSocketGateway`)}function Ee(e){return T(e,`Module`)}function D(e){return e.getDecorators().some(e=>w.has(e.getName()))}const De=new Set([`TsRestHandler`,`GrpcMethod`,`GrpcStreamMethod`]);function Oe(e){return e.getDecorators().some(e=>De.has(e.getName()))}const ke={meta:{id:`architecture/no-business-logic-in-controllers`,category:`architecture`,severity:`error`,description:`Controllers should only handle HTTP concerns — move business logic to services`,help:`Extract branches, loops, and complex calculations into a service method.`},check(e){for(let t of e.sourceFile.getClasses())if(E(t))for(let n of t.getMethods()){if(!n.getDecorators().some(e=>w.has(e.getName())))continue;let t=n.getBody();if(!t)continue;let r=t.getDescendantsOfKind(f.IfStatement),i=t.getDescendantsOfKind(f.ForStatement),a=t.getDescendantsOfKind(f.ForInStatement),o=t.getDescendantsOfKind(f.ForOfStatement),s=t.getDescendantsOfKind(f.WhileStatement),c=t.getDescendantsOfKind(f.SwitchStatement),l=i.length+a.length+o.length+s.length;(r.length>1||l>0||c.length>0)&&e.report({filePath:e.filePath,message:`Controller method '${n.getName()}' contains business logic (${r.length} if, ${l} loops, ${c.length} switch). Move to a service.`,help:this.meta.help,line:n.getStartLineNumber(),column:1});let u=t.getDescendantsOfKind(f.CallExpression).filter(e=>{let t=e.getExpression();if(t.getKind()===f.PropertyAccessExpression){let e=t.asKind(f.PropertyAccessExpression)?.getName();return e===`map`||e===`filter`||e===`reduce`||e===`sort`||e===`flatMap`}return!1});u.length>1&&e.report({filePath:e.filePath,message:`Controller method '${n.getName()}' contains data transformation logic (${u.length} array operations). Move to a service.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}};function Ae(e){let t=new Map;try{let n=p.findConfigFile(e,p.sys.fileExists,`tsconfig.json`);if(!n)return t;let{config:r,error:a}=p.readConfigFile(n,p.sys.readFile);if(a||!r)return t;let o=i(n),c=p.parseJsonConfigFileContent(r,p.sys,o),l=c.options.paths;if(!l)return t;let u=c.options.baseUrl??o;for(let[e,n]of Object.entries(l)){let r=n.map(e=>s(u,e));t.set(e,r)}}catch{return t}return t}function je(e,t){for(let[n,r]of t){if(r.length===0)continue;let t=n.indexOf(`*`);if(t===-1){if(e===n)return r[0];continue}let i=n.slice(0,t),a=n.slice(t+1);if(e.startsWith(i)&&e.endsWith(a)&&e.length>=i.length+a.length){let t=e.slice(i.length,e.length-a.length),n=r[0],o=n.indexOf(`*`);return o===-1?n:n.slice(0,o)+t+n.slice(o+1)}}}const Me=/=>\s*(\w+)/,Ne=/\.js$/;function Pe(e,t,n){let r=[];for(let i of e.getClasses()){let e=i.getDecorator(`Module`);if(!e)continue;let a=i.getName()??`AnonymousModule`,o=e.getArguments()[0],s={name:a,filePath:t,classDeclaration:i,imports:[],exports:[],providers:[],controllers:[]};if(o&&o.getKind()===f.ObjectLiteralExpression){let e=o.asKind(f.ObjectLiteralExpression);e&&(s.imports=O(e,`imports`,n),s.exports=O(e,`exports`,n),s.providers=O(e,`providers`,n),s.controllers=O(e,`controllers`,n))}r.push(s)}return r}function Fe(e,t,n=new Map){let r=new Map,i=new Map;for(let i of t){let t=e.getSourceFile(i);if(t)for(let e of Pe(t,i,n))r.set(e.name,e)}for(let[e,t]of r){let n=new Set;for(let e of t.imports)r.has(e)&&n.add(e);i.set(e,n)}let a=new Map;for(let e of r.values())for(let t of e.providers)a.set(t,e);return{modules:r,edges:i,providerToModule:a}}const Ie=new Set([`forRoot`,`forRootAsync`,`forFeature`,`forFeatureAsync`,`forChild`,`forChildAsync`,`register`,`registerAsync`]);function O(e,t,n){let r=e.getProperty(t);if(!r)return[];let i=r.asKind(f.PropertyAssignment);if(!i)return[];let a=i.getInitializer();return a?k(a,e.getSourceFile(),0,n):[]}function k(e,t,n,r){if(n>5)return[];let i=e.getKind();if(i===f.ArrayLiteralExpression){let i=e.asKindOrThrow(f.ArrayLiteralExpression),a=[];for(let e of i.getElements())a.push(...Le(e,t,n,r));return a}return i===f.CallExpression?Re(e.asKindOrThrow(f.CallExpression),t,n,r):i===f.Identifier?Ve(e.getText(),t,n+1,r):[]}function Le(e,t,n,r){let i=e.getText();if(i.startsWith(`forwardRef`)){let e=i.match(Me);return e?[e[1]]:[i]}let a=e.getKind();return a===f.SpreadElement?k(e.asKindOrThrow(f.SpreadElement).getExpression(),t,n,r):a===f.CallExpression?Re(e.asKindOrThrow(f.CallExpression),t,n,r):a===f.PropertyAccessExpression?[e.asKindOrThrow(f.PropertyAccessExpression).getExpression().getText()]:(f.Identifier,[i])}function Re(e,t,n,r){let i=e.getExpression();if(i.getKind()===f.PropertyAccessExpression){let a=i.asKindOrThrow(f.PropertyAccessExpression),o=a.getName();if(o===`concat`){let i=k(a.getExpression(),t,n,r),o=[];for(let i of e.getArguments())o.push(...k(i,t,n,r));return[...i,...o]}return Ie.has(o),[a.getExpression().getText()]}return i.getKind()===f.Identifier?Ue(i.getText(),t,n+1,r):[]}function ze(e,t,n){if(!e.startsWith(`.`)){let r=je(e,n);if(!r)return;let i=t.getProject(),a=[`${r}.ts`,`${r}/index.ts`,r,r.replace(Ne,`.ts`)];for(let e of a){let t=i.getSourceFile(e);if(t)return t}return}let r=s(i(t.getFilePath()),e),a=t.getProject(),o=[`${r}.ts`,`${r}/index.ts`,r,r.replace(Ne,`.ts`)];for(let e of o){let t=a.getSourceFile(e);if(t)return t}}function Be(e,t,n){for(let r of t.getImportDeclarations())for(let i of r.getNamedImports())if((i.getAliasNode()?i.getAliasNode().getText():i.getName())===e){let e=ze(r.getModuleSpecifierValue(),t,n);return e?{sourceFile:e,localName:i.getName()}:void 0}for(let r of t.getExportDeclarations())if(r.getModuleSpecifierValue()){for(let i of r.getNamedExports())if((i.getAliasNode()?i.getAliasNode().getText():i.getName())===e){let e=ze(r.getModuleSpecifierValue(),t,n);return e?{sourceFile:e,localName:i.getName()}:void 0}}}function Ve(e,t,n,r){if(n>5)return[];for(let i of t.getStatements()){if(i.getKind()!==f.VariableStatement)continue;let a=i.asKindOrThrow(f.VariableStatement);for(let i of a.getDeclarations())if(i.getName()===e){let e=i.getInitializer();if(e)return k(e,t,n,r)}}let i=Be(e,t,r);return i?Ve(i.localName,i.sourceFile,n+1,r):[]}function He(e,t,n,r){for(let i of t.getStatements()){if(i.getKind()!==f.VariableStatement)continue;let a=i.asKindOrThrow(f.VariableStatement);for(let i of a.getDeclarations()){if(i.getName()!==e)continue;let a=i.getInitializer();if(!a||a.getKind()!==f.ArrowFunction)continue;let o=a.asKindOrThrow(f.ArrowFunction).getBody();if(o.getKind()!==f.Block)return k(o,t,n,r);let s=[];for(let e of o.getDescendantsOfKind(f.ReturnStatement)){let i=e.getExpression();i&&s.push(...k(i,t,n,r))}return s}}}function Ue(e,t,n,r){if(n>5)return[];for(let i of t.getStatements()){if(i.getKind()!==f.FunctionDeclaration)continue;let a=i.asKindOrThrow(f.FunctionDeclaration);if(a.getName()!==e)continue;let o=[];for(let e of a.getDescendantsOfKind(f.ReturnStatement)){let i=e.getExpression();i&&o.push(...k(i,t,n,r))}return o}let i=He(e,t,n,r);if(i)return i;let a=Be(e,t,r);return a?Ue(a.localName,a.sourceFile,n+1,r):[]}function We(e,t,n,r=new Map){for(let[t,r]of e.modules)if(r.filePath===n){e.modules.delete(t),e.edges.delete(t);for(let t of r.providers)e.providerToModule.get(t)===r&&e.providerToModule.delete(t);for(let n of e.edges.values())n.delete(t)}let i=t.getSourceFile(n);if(!i)return;let a=Pe(i,n,r);for(let t of a)e.modules.set(t.name,t);for(let t of a){let n=new Set;for(let r of t.imports)e.modules.has(r)&&n.add(r);e.edges.set(t.name,n);for(let n of t.providers)e.providerToModule.set(n,t)}for(let[t,r]of e.modules){if(r.filePath===n)continue;let i=new Set;for(let t of r.imports)e.modules.has(t)&&i.add(t);e.edges.set(t,i)}}function Ge(e){let t=[],n=new Set,r=new Set;function i(a,o){n.add(a),r.add(a);let s=e.edges.get(a)??new Set;for(let e of s)if(!n.has(e))i(e,[...o,e]);else if(r.has(e)){let n=o.indexOf(e);n===-1?t.push([...o,e]):t.push(o.slice(n))}r.delete(a)}for(let t of e.modules.keys())n.has(t)||i(t,[t]);return t}function Ke(e,t,n,r,i,a){let o=[];for(let i of e.providers){let e=n.get(i);if(e)for(let n of e.dependencies){let e=r.get(n);e&&e.name===t.name&&o.push({consumer:i,dependency:n})}}for(let n of e.controllers)for(let e of a){let a=i.getSourceFile(e);if(a)for(let e of a.getClasses()){if(e.getName()!==n)continue;let i=e.getConstructors()[0];if(i)for(let e of i.getParameters()){let i=e.getTypeNode(),a=i?i.getText():e.getType().getText(),s=a.split(`.`).pop()?.split(`<`)[0]??a,c=r.get(s);c&&c.name===t.name&&o.push({consumer:n,dependency:s})}}}return o}const qe=`Break the cycle by extracting shared logic into a separate module or using forwardRef().`;function Je(e,t){let{moduleGraph:n,providers:r,project:i,files:a}=t,o=[],s;for(let t=0;t<e.length;t++){let c=e[t],l=e[(t+1)%e.length],u=n.modules.get(c),d=n.modules.get(l);if(!(u&&d))continue;let f=Ke(u,d,r,n.providerToModule,i,a);if(f.length===0)continue;let p=new Map;for(let e of f){let t=p.get(e.consumer);t?t.push(e.dependency):p.set(e.consumer,[e.dependency])}let m=[];for(let[e,t]of p){let n=t.map(e=>`${e} (from ${l})`).join(`, `);m.push(`${e} (in ${c}) injects ${n}`)}let h=`${c} -> ${l}: ${m.join(`; `)}`;o.push(h),(!s||f.length<s.count)&&(s={description:`${c} -> ${l}`,count:f.length})}if(o.length===0)return qe;let c=o.join(`
|
|
3
|
-
`);if(s){let e=s.count===1?`dependency`:`dependencies`,t=s.description.split(` -> `)[0],o=s.description.split(` -> `)[1],l=n.modules.get(t),u=n.modules.get(o);if(l&&u){let t=Ke(l,u,r,n.providerToModule,i,a),o=[...new Set(t.map(e=>e.dependency))].join(`, `);c+=`\nConsider extracting ${o} into a shared module — it would break the ${s.description} edge (${s.count} ${e}).`}}return c}const Ye={meta:{id:`architecture/no-circular-module-deps`,category:`architecture`,severity:`error`,description:`Module import graph must not contain circular dependencies`,help:qe,scope:`project`},check(e){let t=Ge(e.moduleGraph);for(let n of t){let t=n.join(` -> `),r=e.moduleGraph.modules.get(n[0]),i=Je(n,e);e.report({filePath:r?.filePath??`unknown`,message:`Circular module dependency detected: ${t}`,help:i,line:r?.classDeclaration.getStartLineNumber()??1,column:1})}}},Xe=[`Service`,`Repository`,`Gateway`,`Resolver`],Ze=[`Guard`,`Interceptor`,`Pipe`,`Filter`];function Qe(e){return typeof e==`object`&&!!e}function $e(e){if(!Qe(e))return new Set;let t=e.excludeClasses;if(Array.isArray(t))return new Set(t.filter(e=>typeof e==`string`));let n=e.options;if(!Qe(n))return new Set;let r=n.excludeClasses;return Array.isArray(r)?new Set(r.filter(e=>typeof e==`string`)):new Set}const et={meta:{id:`architecture/no-manual-instantiation`,category:`architecture`,severity:`error`,description:`Do not manually instantiate @Injectable classes — use NestJS dependency injection`,help:`Register the class as a provider in a module and inject it via the constructor.`},check(e){let t=$e(e.config?.rules?.[this.meta.id]),n=e.sourceFile.getDescendantsOfKind(f.NewExpression);for(let r of n){let n=r.getExpression().getText(),i=n.split(`.`).pop()??n;if(t.has(n)||t.has(i))continue;let a=Xe.some(e=>n.endsWith(e)),o=Ze.some(e=>n.endsWith(e));if(a||o){if(o){if(r.getFirstAncestorByKind(f.Decorator))continue;let e=r.getFirstAncestorByKind(f.MethodDeclaration),t=r.getFirstAncestorByKind(f.Constructor);if(!(e||t))continue}e.report({filePath:e.filePath,message:`Manual instantiation of '${n}' detected. Use dependency injection instead.`,help:this.meta.help,line:r.getStartLineNumber(),column:1})}}}},tt=/\.(\w+)$/,nt=/^(\w+)</,rt=new Set([`PrismaService`,`PrismaClient`,`EntityManager`,`DataSource`,`Repository`,`Connection`,`MongooseModel`,`InjectModel`,`InjectRepository`,`MikroORM`,`DrizzleService`]),it={meta:{id:`architecture/no-orm-in-controllers`,category:`architecture`,severity:`error`,description:`Controllers must not inject ORM services directly — use a service layer`,help:`Inject a service that wraps the ORM instead of using the ORM directly in controllers.`},check(e){for(let t of e.sourceFile.getClasses()){if(!E(t))continue;let n=t.getConstructors()[0];if(n){for(let t of n.getParameters()){let n=at(t.getType().getText());if(rt.has(n)){let r=t.getNameNode();e.report({filePath:e.filePath,message:`Controller injects ORM type '${n}' directly. Use a service layer.`,help:this.meta.help,line:r.getStartLineNumber(),column:r.getStartLinePos()+1})}}for(let n of t.getConstructors()[0]?.getParameters()??[])for(let t of n.getDecorators()){let n=t.getName();(n===`InjectRepository`||n===`InjectModel`)&&e.report({filePath:e.filePath,message:`Controller uses @${n}() decorator. Move data access to a service.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}}}};function at(e){let t=e.match(tt);if(t)return t[1];let n=e.match(nt);return n?n[1]:e}const ot=/\.(\w+)$/,st=/^(\w+)</,ct=new Set([`PrismaService`,`PrismaClient`,`EntityManager`,`DataSource`,`Connection`,`MikroORM`]),lt={meta:{id:`architecture/no-orm-in-services`,category:`architecture`,severity:`info`,description:`Services should use repository abstractions instead of ORM directly`,help:`Create a repository class that wraps ORM calls and inject that instead. Note: If your project follows the official NestJS Prisma recipe (injecting PrismaService directly), you can disable this rule.`},check(e){for(let t of e.sourceFile.getClasses()){if(!we(t))continue;let n=t.getName()??``;if(n.endsWith(`Repository`)||n.endsWith(`Repo`))continue;let r=t.getConstructors()[0];if(r)for(let t of r.getParameters()){let n=ut(t.getType().getText());if(ct.has(n)){let r=t.getNameNode();e.report({filePath:e.filePath,message:`Service injects ORM type '${n}' directly. Consider using a repository abstraction.`,help:this.meta.help,line:r.getStartLineNumber(),column:r.getStartLinePos()+1})}for(let n of t.getDecorators()){let t=n.getName();(t===`InjectRepository`||t===`InjectModel`)&&e.report({filePath:e.filePath,message:`Service uses @${t}() directly. Consider wrapping in a repository class.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}}}};function ut(e){let t=e.match(ot);if(t)return t[1];let n=e.match(st);return n?n[1]:e}const dt=/\.(\w+)$/,ft=/^(\w+)</,pt=[/Repository$/,/Repo$/],mt={meta:{id:`architecture/no-repository-in-controllers`,category:`architecture`,severity:`error`,description:`Controllers must not inject repositories directly — use the service layer`,help:`Move database access to a service and inject the service into the controller instead.`},check(e){for(let t of e.sourceFile.getClasses()){if(!E(t))continue;let n=t.getConstructors()[0];if(n){for(let t of n.getParameters()){let n=ht(t.getType().getText());if(pt.some(e=>e.test(n))){let r=t.getNameNode();e.report({filePath:e.filePath,message:`Controller injects repository '${n}' directly. Use a service layer instead.`,help:this.meta.help,line:r.getStartLineNumber(),column:r.getStartLinePos()+1})}}for(let t of e.sourceFile.getImportDeclarations()){let n=t.getModuleSpecifierValue();(n.includes(`/repositories/`)||n.includes(`/repositories`))&&e.report({filePath:e.filePath,message:`Controller imports from repository path '${n}'.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}}}};function ht(e){let t=e.match(dt);if(t)return t[1];let n=e.match(ft);return n?n[1]:e}const gt={meta:{id:`architecture/no-service-locator`,category:`architecture`,severity:`warning`,description:`Avoid using ModuleRef.get() or ModuleRef.resolve() — prefer explicit constructor injection`,help:`Replace ModuleRef.get()/resolve() with constructor injection for explicit, testable dependencies.`},check(e){let t=e.sourceFile.getDescendantsOfKind(f.CallExpression);for(let n of t){let t=n.getExpression();if(t.getKind()!==f.PropertyAccessExpression)continue;let r=t.asKind(f.PropertyAccessExpression);if(!r)continue;let i=r.getName();if(i!==`get`&&i!==`resolve`)continue;let a=r.getExpression().getText();(a===`moduleRef`||a===`this.moduleRef`)&&e.report({filePath:e.filePath,message:`Service locator pattern: '${a}.${i}()' hides dependencies. Use constructor injection instead.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},_t={meta:{id:`architecture/prefer-constructor-injection`,category:`architecture`,severity:`warning`,description:`Prefer constructor injection over @Inject() property injection`,help:`Move the dependency to a constructor parameter instead of using property injection.`},check(e){for(let t of e.sourceFile.getClasses())if(Te(t))for(let n of t.getProperties())n.getDecorator(`Inject`)&&e.report({filePath:e.filePath,message:`Property '${n.getName()}' uses @Inject() decorator. Prefer constructor injection.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}},vt=[`/repositories/`,`/entities/`,`/dto/`,`/guards/`,`/interceptors/`,`/pipes/`,`/strategies/`],yt={meta:{id:`architecture/require-module-boundaries`,category:`architecture`,severity:`info`,description:`Avoid deep imports into other feature modules' internals`,help:`Import from the module's public API (barrel export) instead of reaching into its internals.`},check(e){for(let t of e.sourceFile.getImportDeclarations()){let n=t.getModuleSpecifierValue();n.startsWith(`.`)&&n.includes(`../`)&&vt.some(e=>n.includes(e))&&e.report({filePath:e.filePath,message:`Import '${n}' reaches into another module's internals.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}},bt={meta:{id:`correctness/factory-inject-matches-params`,category:`correctness`,severity:`error`,description:`useFactory inject array length must match the factory function parameter count`,help:`Ensure the 'inject' array has one entry per factory function parameter.`},check(e){for(let t of e.sourceFile.getClasses()){if(!Ee(t))continue;let n=t.getDecorator(`Module`);if(!n)continue;let r=n.getArguments()[0];if(!r||r.getKind()!==f.ObjectLiteralExpression)continue;let i=r.asKind(f.ObjectLiteralExpression);if(!i)continue;let a=i.getProperty(`providers`);if(!a)continue;let o=a.getChildrenOfKind(f.ArrayLiteralExpression)[0];if(o)for(let t of o.getElements()){if(t.getKind()!==f.ObjectLiteralExpression)continue;let n=t.asKind(f.ObjectLiteralExpression);if(!n)continue;let r=n.getProperty(`useFactory`),i=n.getProperty(`inject`);if(!(r&&i))continue;let a=i.getChildrenOfKind(f.ArrayLiteralExpression)[0];if(!a)continue;let o=a.getElements().length,s,c=r.asKind(f.MethodDeclaration);if(c)s=c.getParameters().length;else{let e=r.asKind(f.PropertyAssignment);if(!e)continue;let t=e.getInitializer();if(!t)continue;t.getKind()===f.ArrowFunction?s=t.asKind(f.ArrowFunction)?.getParameters().length:t.getKind()===f.FunctionExpression&&(s=t.asKind(f.FunctionExpression)?.getParameters().length)}s!==void 0&&o!==s&&e.report({filePath:e.filePath,message:`Factory has ${s} parameter(s) but inject array has ${o} element(s).`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}}},xt=[`Guard`,`Interceptor`,`Filter`,`Pipe`,`Middleware`,`Strategy`,`Subscriber`,`Listener`,`Processor`,`Consumer`,`Worker`,`Scheduler`,`Cron`,`HealthIndicator`],St={meta:{id:`correctness/injectable-must-be-provided`,category:`correctness`,severity:`info`,description:`@Injectable() classes should be registered in at least one module's providers array`,help:`Add this class to a module's providers array, or remove the @Injectable() decorator if unused.`,scope:`project`},check(e){let t=new Set;for(let n of e.moduleGraph.modules.values()){for(let e of n.providers)t.add(e);for(let e of n.controllers)t.add(e)}for(let n of e.files){let r=e.project.getSourceFile(n);if(r)for(let e of r.getClasses()){if(!Ee(e))continue;let n=e.getDecorator(`Module`);if(!n)continue;let r=n.getArguments()[0];if(!r||r.getKind()!==f.ObjectLiteralExpression)continue;let i=r.asKind(f.ObjectLiteralExpression);if(!i)continue;let a=i.getProperty(`providers`);if(!a)continue;let o=a.getChildrenOfKind(f.ArrayLiteralExpression)[0];if(o)for(let e of o.getElements()){if(e.getKind()!==f.ObjectLiteralExpression)continue;let n=e.asKind(f.ObjectLiteralExpression);if(n)for(let e of[`useClass`,`useExisting`]){let r=n.getProperty(e);if(!r)continue;let i=r.asKind(f.PropertyAssignment);if(!i)continue;let a=i.getInitializer();a&&t.add(a.getText())}}}}for(let n of e.files){if(n.includes(`.spec.`)||n.includes(`.test.`)||n.includes(`__test__`)||n.includes(`__tests__`))continue;let r=e.project.getSourceFile(n);if(r)for(let i of r.getClasses()){if(!i.getDecorator(`Injectable`))continue;let r=i.getName();r&&(xt.some(e=>r.endsWith(e))||t.has(r)||e.report({filePath:n,message:`@Injectable() class '${r}' is not registered in any module's providers array.`,help:this.meta.help,line:i.getStartLineNumber(),column:1}))}}}};function Ct(e){return e.getDescendantsOfKind(f.ReturnStatement).some(e=>{let t=e.getExpression();return!t||t.getKind()!==f.NewExpression?!1:t.asKindOrThrow(f.NewExpression).getExpression().getText()===`Promise`})}const wt={meta:{id:`correctness/no-async-without-await`,category:`correctness`,severity:`warning`,description:`Async functions/methods should contain at least one await expression`,help:`Either add an await expression or remove the async keyword. HTTP handlers with route decorators are exempted, as async is conventional for controller methods.`},check(e){for(let t of e.sourceFile.getClasses())for(let n of t.getMethods()){if(!n.isAsync()||E(t)&&D(n)||Oe(n))continue;let r=n.getBody();if(r&&r.getDescendantsOfKind(f.AwaitExpression).filter(e=>{let t=e.getParent();for(;t&&t!==r;){if(t.getKind()===f.ArrowFunction||t.getKind()===f.FunctionExpression||t.getKind()===f.FunctionDeclaration)return!1;t=t.getParent()}return!0}).length===0){let t=n.getName();Ct(r)?e.report({filePath:e.filePath,message:`Async method '${t}()' returns a Promise directly — remove the async keyword.`,help:`The async keyword is unnecessary when you are already constructing a Promise manually. Remove async to avoid double-wrapping.`,line:n.getStartLineNumber(),column:1}):e.report({filePath:e.filePath,message:`Async method '${t}()' has no await expression.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}for(let t of e.sourceFile.getFunctions()){if(!t.isAsync())continue;let n=t.getBody();if(n&&n.getDescendantsOfKind(f.AwaitExpression).filter(e=>{let t=e.getParent();for(;t&&t!==n;){if(t.getKind()===f.ArrowFunction||t.getKind()===f.FunctionExpression||t.getKind()===f.FunctionDeclaration)return!1;t=t.getParent()}return!0}).length===0){let r=t.getName()??`anonymous`;Ct(n)?e.report({filePath:e.filePath,message:`Async function '${r}()' returns a Promise directly — remove the async keyword.`,help:`The async keyword is unnecessary when you are already constructing a Promise manually. Remove async to avoid double-wrapping.`,line:t.getStartLineNumber(),column:1}):e.report({filePath:e.filePath,message:`Async function '${r}()' has no await expression.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}}},Tt=new Set([`ApiResponse`,`ApiQuery`,`ApiParam`,`ApiHeader`,`ApiSecurity`,`SetMetadata`,`Roles`,`Header`,`Throttle`]),Et={meta:{id:`correctness/no-duplicate-decorators`,category:`correctness`,severity:`warning`,description:`Same decorator should not appear twice on a single target`,help:`Remove the duplicate decorator — it was likely copy-pasted by mistake.`},check(e){for(let t of e.sourceFile.getClasses()){A(t.getDecorators(),e,this.meta.help);for(let n of t.getMethods())A(n.getDecorators(),e,this.meta.help);for(let n of t.getProperties())A(n.getDecorators(),e,this.meta.help);for(let n of t.getConstructors())for(let t of n.getParameters())A(t.getDecorators(),e,this.meta.help)}}};function A(e,t,n){let r=new Set;for(let i of e){let e=i.getName();Tt.has(e)||(r.has(e)?t.report({filePath:t.filePath,message:`Duplicate @${e}() decorator on the same target.`,help:n,line:i.getStartLineNumber(),column:1}):r.add(e))}}const Dt=[`providers`,`controllers`,`imports`,`exports`],Ot={meta:{id:`correctness/no-duplicate-module-metadata`,category:`correctness`,severity:`warning`,description:`Same identifier should not appear twice in a module metadata array`,help:`Remove the duplicate entry from the module metadata.`},check(e){for(let t of e.sourceFile.getClasses()){if(!Ee(t))continue;let n=t.getDecorator(`Module`);if(!n)continue;let r=n.getArguments()[0];if(!r||r.getKind()!==f.ObjectLiteralExpression)continue;let i=r.asKind(f.ObjectLiteralExpression);if(i)for(let t of Dt){let n=i.getProperty(t);if(!n)continue;let r=n.getChildrenOfKind(f.ArrayLiteralExpression)[0];if(!r)continue;let a=new Set;for(let n of r.getElements()){let r=n.getText();a.has(r)?e.report({filePath:e.filePath,message:`Duplicate '${r}' in @Module() ${t} array.`,help:this.meta.help,line:n.getStartLineNumber(),column:1}):a.add(r)}}}}},kt={meta:{id:`correctness/no-duplicate-routes`,category:`correctness`,severity:`error`,description:`Same HTTP method + route path + version should not appear twice in a single controller`,help:`Remove or rename one of the duplicate route handlers.`},check(e){for(let t of e.sourceFile.getClasses()){if(!E(t))continue;let n=new Map;for(let r of t.getMethods())for(let t of r.getDecorators()){let i=t.getName();if(!w.has(i))continue;let a=t.getArguments(),o=a.length>0?a[0].getText():`""`,s=r.getDecorator(`Version`),c=`${i}:${o}:${s?s.getArguments()[0]?.getText()??``:``}`,l=n.get(c);l?e.report({filePath:e.filePath,message:`Duplicate route: @${i}(${o}) is already defined in '${l}()'.`,help:this.meta.help,line:r.getStartLineNumber(),column:1}):n.set(c,r.getName())}}}},At={meta:{id:`correctness/no-empty-handlers`,category:`correctness`,severity:`info`,description:`Controller HTTP handlers should not have empty bodies`,help:`Add implementation to the handler method or remove it if unnecessary.`},check(e){for(let t of e.sourceFile.getClasses())if(E(t))for(let n of t.getMethods()){if(!n.getDecorators().some(e=>w.has(e.getName())))continue;let t=n.getBody();if(!t)continue;let r=t.asKind(f.Block);r&&r.getStatements().length===0&&e.report({filePath:e.filePath,message:`Handler '${n.getName()}()' has an empty body.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}};function jt(e){let t=e.getReturnType().getText();return t.startsWith(`Promise<`)||t===`Promise`?!0:t===`any`||t===`error`?`unknown`:!1}const Mt=new Set([`save`,`create`,`insert`,`update`,`delete`,`remove`,`send`,`emit`,`publish`,`dispatch`,`execute`,`fetch`,`load`,`upload`,`download`,`process`]),Nt={meta:{id:`correctness/no-fire-and-forget-async`,category:`correctness`,severity:`warning`,description:`Calling async functions without await leads to unhandled promise rejections`,help:`Add await before the async call, or use void with explicit error handling if fire-and-forget is intentional.`},check(e){for(let t of e.sourceFile.getClasses())for(let n of t.getMethods()){if(D(n))continue;let t=n.getBody();if(!t)continue;let r=t.getDescendantsOfKind(f.ExpressionStatement);for(let t of r){let r=t.getExpression();if(r.getKind()===f.VoidExpression||r.getKind()===f.AwaitExpression||r.getKind()!==f.CallExpression)continue;let i=r.asKind(f.CallExpression);if(!i)continue;let a=i.getExpression().getText().split(`.`).pop()??``,o=jt(i);if(o!==!1){if(o===`unknown`){let e=a.toLowerCase();if(!(Mt.has(e)||[...Mt].some(t=>e.startsWith(t)&&e!==t)))continue}t.getFirstAncestorByKind(f.MethodDeclaration)===n&&e.report({filePath:e.filePath,message:`Async call '${a}()' is not awaited — unhandled rejections will crash the process.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}}}},Pt={meta:{id:`correctness/no-missing-filter-catch`,category:`correctness`,severity:`error`,description:`Exception filter classes decorated with @Catch() must implement the catch() method`,help:`Add a catch(exception, host: ArgumentsHost) method to the filter class.`},check(e){for(let t of e.sourceFile.getClasses())T(t,`Catch`)&&(t.getExtends()||t.getMethods().some(e=>e.getName()===`catch`)||e.report({filePath:e.filePath,message:`Exception filter '${t.getName()}' has @Catch() but is missing the 'catch()' method.`,help:this.meta.help,line:t.getStartLineNumber(),column:1}))}},Ft={meta:{id:`correctness/no-missing-guard-method`,category:`correctness`,severity:`error`,description:`Guard classes must implement the canActivate() method`,help:`Add a canActivate(context: ExecutionContext) method to the guard class. Note: This rule identifies guards by the 'Guard' class name suffix.`},check(e){for(let t of e.sourceFile.getClasses()){let n=t.getName()??``;n.endsWith(`Guard`)&&T(t,`Injectable`)&&(t.getExtends()||t.getMethods().some(e=>e.getName()===`canActivate`)||e.report({filePath:e.filePath,message:`Guard '${n}' is missing the 'canActivate()' method.`,help:this.meta.help,line:t.getStartLineNumber(),column:1}))}}},It={meta:{id:`correctness/no-missing-injectable`,category:`correctness`,severity:`error`,description:`Provider classes with constructor dependencies must have the @Injectable() decorator`,help:`Add @Injectable() to providers that inject constructor dependencies.`,scope:`project`},check(e){let t=new Set([...e.providers.values()].map(e=>e.name)),n=new Map;for(let t of e.files){let r=e.project.getSourceFile(t);if(r)for(let e of r.getClasses()){let r=e.getName();if(r){let i=n.get(r)??[];i.push({cls:e,filePath:t}),n.set(r,i)}}}for(let r of e.moduleGraph.modules.values())for(let i of r.providers){if(t.has(i))continue;let a=n.get(i);if(a)for(let{cls:t,filePath:n}of a){let a=(t.getConstructors()[0]?.getParameters().length??0)>0;!(t.getDecorator(`Injectable`)||t.getDecorator(`Resolver`)||t.getDecorator(`WebSocketGateway`))&&a&&e.report({filePath:n,message:`Class '${i}' is listed in '${r.name}' providers but is missing @Injectable() decorator.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}}},Lt={meta:{id:`correctness/no-missing-interceptor-method`,category:`correctness`,severity:`error`,description:`Interceptor classes must implement the intercept() method`,help:`Add an intercept(context: ExecutionContext, next: CallHandler) method to the interceptor class.`},check(e){for(let t of e.sourceFile.getClasses()){let n=t.getName()??``;n.endsWith(`Interceptor`)&&T(t,`Injectable`)&&(t.getExtends()||t.getMethods().some(e=>e.getName()===`intercept`)||e.report({filePath:e.filePath,message:`Interceptor '${n}' is missing the 'intercept()' method.`,help:this.meta.help,line:t.getStartLineNumber(),column:1}))}}},Rt={meta:{id:`correctness/no-missing-module-decorator`,category:`correctness`,severity:`warning`,description:`Classes named *Module should have a @Module() decorator`,help:`Add @Module({}) decorator to the class, or rename it if it is not a NestJS module.`},check(e){for(let t of e.sourceFile.getClasses()){let n=t.getName()??``;n.endsWith(`Module`)&&(n===`Module`||n===`DynamicModule`||T(t,`Module`)||e.report({filePath:e.filePath,message:`Class '${n}' is named like a module but is missing the @Module() decorator.`,help:this.meta.help,line:t.getStartLineNumber(),column:1}))}}},zt={meta:{id:`correctness/no-missing-pipe-method`,category:`correctness`,severity:`error`,description:`Pipe classes must implement the transform() method`,help:`Add a transform(value: any, metadata: ArgumentMetadata) method to the pipe class.`},check(e){for(let t of e.sourceFile.getClasses()){let n=t.getName()??``;n.endsWith(`Pipe`)&&T(t,`Injectable`)&&(t.getExtends()||t.getMethods().some(e=>e.getName()===`transform`)||e.report({filePath:e.filePath,message:`Pipe '${n}' is missing the 'transform()' method.`,help:this.meta.help,line:t.getStartLineNumber(),column:1}))}}},Bt=/:(\w+)/g,Vt={meta:{id:`correctness/param-decorator-matches-route`,category:`correctness`,severity:`error`,description:`@Param() decorator name must match a :param in the route path`,help:`Ensure the @Param('name') argument matches a ':name' segment in the route path (including controller prefix).`},check(e){for(let t of e.sourceFile.getClasses()){if(!E(t))continue;let n=t.getDecorator(`Controller`),r=``;if(n){let e=n.getArguments();if(e.length>0){let t=e[0];if(t.getKind()===f.ObjectLiteralExpression){let e=t.asKind(f.ObjectLiteralExpression);if(e){let t=e.getProperty(`path`);if(t){let e=t.asKind(f.PropertyAssignment);if(e){let t=e.getInitializer();t&&(r=t.getText().replace(/^['"`]|['"`]$/g,``))}}}}else r=t.getText().replace(/^['"`]|['"`]$/g,``)}}let i=new Set;for(let e of r.matchAll(Bt))i.add(e[1]);for(let n of t.getMethods()){let t=``,r=!1;for(let e of n.getDecorators())if(w.has(e.getName())){r=!0;let n=e.getArguments();n.length>0&&(t=n[0].getText().replace(/^['"`]|['"`]$/g,``));break}if(!r)continue;let a=new Set;for(let e of t.matchAll(Bt))a.add(e[1]);let o=new Set([...i,...a]);for(let t of n.getParameters())for(let n of t.getDecorators()){if(n.getName()!==`Param`)continue;let t=n.getArguments();if(t.length===0)continue;let r=t[0].getText().replace(/^['"`]|['"`]$/g,``);o.has(r)||e.report({filePath:e.filePath,message:`@Param('${r}') does not match any route parameter. Available: ${o.size>0?[...o].map(e=>`:${e}`).join(`, `):`(none)`}.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}}}},Ht={meta:{id:`correctness/prefer-readonly-injection`,category:`correctness`,severity:`warning`,description:`Constructor DI parameters should be readonly to prevent accidental reassignment`,help:`Add the 'readonly' modifier to the constructor parameter.`},check(e){for(let t of e.sourceFile.getClasses()){if(!(we(t)||E(t)))continue;let n=t.getConstructors()[0];if(n){for(let t of n.getParameters())if((t.hasModifier(`private`)||t.hasModifier(`protected`)||t.hasModifier(`public`))&&!t.isReadonly()){let n=t.getNameNode();e.report({filePath:e.filePath,message:`Constructor parameter '${t.getName()}' should be readonly.`,help:this.meta.help,line:n.getStartLineNumber(),column:n.getStartLinePos()+1})}}}}},Ut={meta:{id:`correctness/require-inject-decorator`,category:`correctness`,severity:`error`,description:`Constructor parameters without type annotations must have @Inject() decorator for NestJS DI to resolve them`,help:`Add a type annotation or @Inject() decorator to the constructor parameter.`},check(e){for(let t of e.sourceFile.getClasses()){if(!Te(t))continue;let n=t.getConstructors()[0];if(n)for(let r of n.getParameters()){let n=r.getTypeNode(),i=r.getDecorators().some(e=>e.getName()===`Inject`);n||i||e.report({filePath:e.filePath,message:`Constructor parameter '${r.getName()}' in '${t.getName()}' has no type annotation and no @Inject() decorator — NestJS cannot resolve it.`,help:this.meta.help,line:r.getStartLineNumber(),column:1})}}}},Wt={onModuleInit:`OnModuleInit`,onModuleDestroy:`OnModuleDestroy`,onApplicationBootstrap:`OnApplicationBootstrap`,onApplicationShutdown:`OnApplicationShutdown`,beforeApplicationShutdown:`BeforeApplicationShutdown`},Gt={meta:{id:`correctness/require-lifecycle-interface`,category:`correctness`,severity:`warning`,description:`Classes with lifecycle methods should implement the corresponding NestJS interface`,help:`Add 'implements OnModuleInit' (or the appropriate interface) to the class declaration.`},check(e){for(let t of e.sourceFile.getClasses()){let n=t.getImplements().map(e=>e.getText());for(let r of t.getMethods()){let i=r.getName(),a=Wt[i];a&&(n.some(e=>e===a||e.startsWith(`${a}<`))||e.report({filePath:e.filePath,message:`Class '${t.getName()}' has '${i}()' but does not implement '${a}'.`,help:this.meta.help,line:r.getStartLineNumber(),column:1}))}}}},Kt=/each\s*:\s*true/,qt={meta:{id:`correctness/validate-nested-array-each`,category:`correctness`,severity:`warning`,description:`@ValidateNested() on array-typed properties must use { each: true }`,help:`Change @ValidateNested() to @ValidateNested({ each: true }) for array properties.`},check(e){for(let t of e.sourceFile.getClasses())for(let n of t.getProperties()){let t=n.getDecorators(),r=t.find(e=>e.getName()===`ValidateNested`);if(!r)continue;let i=Jt(n),a=t.some(e=>e.getName()===`IsArray`);(i||a)&&(Yt(r)||e.report({filePath:e.filePath,message:`Property '${n.getName()}' is an array with @ValidateNested() but missing { each: true }.`,help:this.meta.help,line:r.getStartLineNumber(),column:1}))}}};function Jt(e){let t=e.getTypeNode();if(!t)return!1;let n=t.getText().replace(/\s/g,``);return!!(n.endsWith(`[]`)||n.startsWith(`Array<`))}function Yt(e){let t=e.getArguments();if(t.length===0)return!1;let n=t[0];if(n.getKind()!==f.ObjectLiteralExpression)return!1;let r=n.getText();return Kt.test(r)}const Xt=new Set(`ValidateNested.IsString.IsNumber.IsBoolean.IsEmail.IsArray.IsEnum.IsNotEmpty.IsDefined.IsOptional.IsDate.IsObject.IsInt.IsPositive.IsNegative.IsUUID.IsUrl.IsISO8601.Matches.Min.Max.MinLength.MaxLength.ArrayMinSize.ArrayMaxSize.ArrayNotEmpty.IsIn.IsNotIn.Length.Contains.IsAlpha.IsAlphanumeric.IsDecimal.IsHexColor.IsJSON.IsPhoneNumber.IsIP.IsCreditCard.IsDateString.IsMilitaryTime.IsMongoId.IsPort.IsSemVer.IsStrongPassword`.split(`.`)),Zt=new Set([`string`,`number`,`boolean`,`Date`,`any`,`unknown`,`bigint`,`symbol`,`undefined`,`null`,`void`,`never`]),Qt=/\s/g,$t=/\[\]$/,en=/^Array<(.+)>$/,tn=/^["']/,nn=/^\d+$/;function j(e){let t=e.replace(Qt,``);if(t.includes(`|`))return t.split(`|`).every(e=>j(e));if(Zt.has(t)||$t.test(t)&&j(t.replace($t,``)))return!0;let n=t.match(en);return!!(n&&j(n[1])||tn.test(t)||nn.test(t))}const rn={meta:{id:`correctness/validated-non-primitive-needs-type`,category:`correctness`,severity:`warning`,description:`DTO properties with class-validator decorators on non-primitive types must have @Type() from class-transformer`,help:`Add @Type(() => ClassName) from 'class-transformer' to ensure proper transformation.`},check(e){for(let t of e.sourceFile.getClasses())for(let n of t.getProperties()){let t=n.getDecorators();if(t.length===0||!t.some(e=>Xt.has(e.getName()))||t.some(e=>e.getName()===`Type`)||t.some(e=>e.getName()===`IsEnum`))continue;let r=n.getTypeNode();if(!r)continue;let i=r.getText();j(i)||e.report({filePath:e.filePath,message:`Property '${n.getName()}' has type '${i}' with class-validator decorators but is missing @Type() decorator.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},an=new Set([f.ForStatement,f.ForOfStatement,f.ForInStatement,f.WhileStatement,f.DoStatement]),on={meta:{id:`performance/no-blocking-constructor`,category:`performance`,severity:`warning`,description:`Constructors in Injectable/Controller classes should not contain heavy operations`,help:`Move heavy initialization logic to the onModuleInit() lifecycle method. Constructors cannot be async, so asynchronous work should always use lifecycle hooks.`},check(e){for(let t of e.sourceFile.getClasses()){if(!(T(t,`Injectable`)||T(t,`Controller`)))continue;let n=t.getConstructors()[0];if(!n)continue;let r=n.getBody();if(r){for(let i of r.getDescendants())if(an.has(i.getKind())){e.report({filePath:e.filePath,message:`Constructor in '${t.getName()}' contains blocking operation — use onModuleInit() instead.`,help:this.meta.help,line:n.getStartLineNumber(),column:1});break}}}}},sn={meta:{id:`performance/no-dynamic-require`,category:`performance`,severity:`warning`,description:`Dynamic require() with variable arguments prevents bundler optimization`,help:`Use static import statements or dynamic import() with string literals.`},check(e){let t=e.sourceFile.getDescendantsOfKind(f.CallExpression);for(let n of t){if(n.getExpression().getText()!==`require`)continue;let t=n.getArguments();t.length!==0&&t[0].getKind()!==f.StringLiteral&&e.report({filePath:e.filePath,message:`Dynamic require() with non-literal argument prevents bundler optimization.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},cn={meta:{id:`performance/no-orphan-modules`,category:`performance`,severity:`info`,description:`Module is never imported by any other module and may be dead code`,help:`Import this module in another module or remove it if it is unused.`,scope:`project`},check(e){let t=new Set;for(let n of e.moduleGraph.modules.values())for(let e of n.imports)t.add(e);for(let n of e.moduleGraph.modules.values())n.name!==`AppModule`&&(t.has(n.name)||e.report({filePath:n.filePath,message:`Module '${n.name}' is never imported by any other module.`,help:this.meta.help,line:n.classDeclaration.getStartLineNumber(),column:1}))}},ln={meta:{id:`performance/no-request-scope-abuse`,category:`performance`,severity:`warning`,description:`Scope.REQUEST creates a new provider instance per request — use only when necessary`,help:`Remove Scope.REQUEST unless the provider genuinely needs per-request state (e.g., request-scoped context). Consider Scope.DEFAULT or Scope.TRANSIENT instead.`},check(e){let t=e.sourceFile.getDescendantsOfKind(f.PropertyAccessExpression);for(let n of t)n.getName()===`REQUEST`&&n.getExpression().getText()===`Scope`&&e.report({filePath:e.filePath,message:`Scope.REQUEST creates a new instance per request, which impacts performance and propagates request scope to all dependents.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}},un=new Set([`readFileSync`,`writeFileSync`,`existsSync`,`mkdirSync`,`readdirSync`,`statSync`,`accessSync`,`appendFileSync`,`copyFileSync`,`renameSync`,`unlinkSync`]),dn={meta:{id:`performance/no-sync-io`,category:`performance`,severity:`warning`,description:`Synchronous I/O calls block the event loop and should be avoided in NestJS applications`,help:`Use the async variant (e.g., readFile instead of readFileSync) with await.`},check(e){let t=e.sourceFile.getDescendantsOfKind(f.CallExpression);for(let n of t){let t=n.getExpression().getText().split(`.`).pop()??``;un.has(t)&&e.report({filePath:e.filePath,message:`Synchronous I/O call '${t}()' blocks the event loop.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},fn={meta:{id:`performance/no-unused-module-exports`,category:`performance`,severity:`info`,description:`Module exports a provider that no importing module actually uses`,help:`Remove the unused export or use the provider in an importing module.`,scope:`project`},check(e){for(let t of e.moduleGraph.modules.values()){if(t.exports.length===0)continue;let n=[];for(let r of e.moduleGraph.modules.values())r.name!==t.name&&r.imports.includes(t.name)&&n.push(r.name);if(n.length===0)continue;let r=new Set;for(let i of n){let n=e.moduleGraph.modules.get(i);if(n){for(let t of n.providers){let n=e.providers.get(t);if(n)for(let e of n.dependencies)r.add(e)}if(n.exports.includes(t.name))for(let e of t.exports)r.add(e);for(let t of n.controllers)for(let n of e.files){let i=e.project.getSourceFile(n);if(i)for(let e of i.getClasses()){if(e.getName()!==t)continue;let n=e.getConstructors()[0];if(n)for(let e of n.getParameters()){let t=e.getTypeNode(),n=t?t.getText():e.getType().getText(),i=n.split(`.`).pop()?.split(`<`)[0]??n;r.add(i)}}}}}for(let n of t.exports)e.moduleGraph.modules.has(n)||r.has(n)||e.report({filePath:t.filePath,message:`Module '${t.name}' exports '${n}' but no importing module uses it.`,help:this.meta.help,line:t.classDeclaration.getStartLineNumber(),column:1})}}},pn=new Set([`Cron`,`Interval`,`Timeout`,`OnEvent`,`Process`,`OnQueueEvent`,`EventSubscriber`,`SubscribeMessage`,`WebSocketGateway`]);function mn(e){for(let t of e.getDecorators())if(pn.has(t.getName()))return!0;for(let t of e.getMethods())for(let e of t.getDecorators())if(pn.has(e.getName()))return!0;return!1}const hn={meta:{id:`performance/no-unused-providers`,category:`performance`,severity:`warning`,description:`Injectable providers that are never injected and have no self-activating decorators may be dead code`,help:`Remove the unused provider, inject it where needed, or verify it is activated by a framework decorator (e.g. @Cron, @OnEvent).`,scope:`project`},check(e){let t=new Set;for(let n of e.providers.values())for(let e of n.dependencies)t.add(e);let n=[`Controller`,`Resolver`,`WebSocketGateway`];for(let r of e.files){let i=e.project.getSourceFile(r);if(i)for(let e of i.getClasses()){if(!n.some(t=>e.getDecorator(t)!==void 0))continue;let r=e.getConstructors()[0];if(r)for(let e of r.getParameters()){let n=e.getTypeNode(),r=n?n.getText():e.getType().getText(),i=r.split(`.`).pop()?.split(`<`)[0]??r;t.add(i)}}}for(let n of e.providers.values()){let r=n.name;if(xt.some(e=>r.endsWith(e))||t.has(r)||mn(n.classDeclaration))continue;let i=!1;for(let t of e.moduleGraph.modules.values())if(t.exports.includes(r)){i=!0;break}i||e.report({filePath:n.filePath,message:`Provider '${r}' is never injected by any other provider or controller.`,help:this.meta.help,line:n.classDeclaration.getStartLineNumber(),column:1})}}},gn={meta:{id:`schema/require-cascade-rule`,category:`schema`,scope:`schema`,severity:`info`,description:`Relations should have explicit onDelete/cascade behavior defined`,help:`Add an explicit onDelete option (e.g. CASCADE, SET NULL) to avoid relying on database defaults.`},check(e){for(let t of e.schemaGraph.relations)if(!(t.type!==`many-to-one`&&t.type!==`one-to-one`)&&!t.onDelete){let n=e.schemaGraph.entities.get(t.fromEntity);if(!n)continue;e.report({filePath:n.filePath,entity:n.name,message:`Relation '${t.propertyName}' on '${t.fromEntity}' has no explicit onDelete behavior.`,help:this.meta.help})}}},_n={meta:{id:`schema/require-primary-key`,category:`schema`,scope:`schema`,severity:`error`,description:`Every entity must have at least one primary key column`,help:`Add a primary key column (e.g. @id in Prisma, @PrimaryColumn/@PrimaryGeneratedColumn in TypeORM).`},check(e){for(let t of e.schemaGraph.entities.values())t.columns.some(e=>e.isPrimary)||e.report({filePath:t.filePath,entity:t.name,message:`Entity '${t.name}' has no primary key column.`,help:this.meta.help})}},vn=/delete/i;function yn(e,t){let n=new Set(e.columns.map(e=>e.name.toLowerCase()));return n.has(`createdat`)||n.has(`created_at`)?!0:t===`typeorm`?e.columns.some(e=>e.type===`timestamp`&&e.isGenerated&&!vn.test(e.name)):t===`prisma`?e.columns.some(e=>e.type===`DateTime`&&e.defaultValue!==void 0&&e.defaultValue.includes(`now()`)):t===`drizzle`?e.columns.some(e=>(e.type===`timestamp`||e.type===`date`||e.type===`datetime`)&&e.defaultValue!==void 0&&e.defaultValue.includes(`now()`)):!1}const bn={meta:{id:`schema/require-timestamps`,category:`schema`,scope:`schema`,severity:`warning`,description:`Entities should have timestamp columns (createdAt/updatedAt)`,help:`Add createdAt/updatedAt columns to track when records are created and modified.`},check(e){for(let t of e.schemaGraph.entities.values())yn(t,e.orm)||e.report({filePath:t.filePath,entity:t.name,message:`Entity '${t.name}' has no timestamp columns (createdAt/updatedAt).`,help:this.meta.help})}},xn={meta:{id:`security/no-csrf-disabled`,category:`security`,severity:`error`,description:`CSRF protection should not be explicitly disabled`,help:`Enable CSRF protection or remove the explicit disabling of it.`},check(e){let t=e.sourceFile.getDescendantsOfKind(f.PropertyAssignment);for(let n of t){let t=n.getName();if(t!==`csrf`&&t!==`csrfProtection`)continue;let r=n.getInitializer();r&&r.getText()===`false`&&e.report({filePath:e.filePath,message:`CSRF protection explicitly disabled (${t}: false).`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},Sn={meta:{id:`security/no-dangerous-redirects`,category:`security`,severity:`error`,description:`Redirects using user-controlled input (from @Query/@Param) are an open redirect vulnerability`,help:`Validate redirect URLs against an allowlist of safe destinations.`},check(e){for(let t of e.sourceFile.getClasses())if(E(t))for(let n of t.getMethods()){let t=new Set;for(let e of n.getParameters())e.getDecorators().some(e=>e.getName()===`Query`||e.getName()===`Param`)&&t.add(e.getName());if(t.size===0)continue;let r=n.getDescendantsOfKind(f.CallExpression);for(let n of r)if(n.getExpression().getText().endsWith(`redirect`))for(let r of n.getArguments()){let i=r.getText();t.has(i)&&e.report({filePath:e.filePath,message:`Redirect uses user-controlled parameter '${i}' — open redirect risk.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}let i=n.getDecorators().find(e=>e.getName()===`Redirect`);if(i)for(let n of i.getArguments()){let r=n.getText();t.has(r)&&e.report({filePath:e.filePath,message:`@Redirect() uses user-controlled parameter '${r}' — open redirect risk.`,help:this.meta.help,line:i.getStartLineNumber(),column:1})}}}},Cn={meta:{id:`security/no-eval`,category:`security`,severity:`error`,description:`Usage of eval() or new Function() is a security risk and should be avoided`,help:`Refactor to avoid eval() and new Function(). Use safer alternatives like JSON.parse() or a sandboxed interpreter.`},check(e){let t=e.sourceFile.getDescendantsOfKind(f.CallExpression);for(let n of t)n.getExpression().getText()===`eval`&&e.report({filePath:e.filePath,message:`Usage of eval() is a security risk.`,help:this.meta.help,line:n.getStartLineNumber(),column:1});let n=e.sourceFile.getDescendantsOfKind(f.NewExpression);for(let t of n)t.getExpression().getText()===`Function`&&e.report({filePath:e.filePath,message:`Usage of new Function() is a security risk.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}},wn={meta:{id:`security/no-exposed-env-vars`,category:`security`,severity:`warning`,description:`Use NestJS ConfigService instead of direct process.env access in Injectable/Controller classes`,help:`Inject ConfigService and use configService.get('VAR_NAME') instead of process.env.VAR_NAME.`},check(e){for(let t of e.sourceFile.getClasses()){if(!(T(t,`Injectable`)||T(t,`Controller`)))continue;let n=t.getDescendantsOfKind(f.PropertyAccessExpression);for(let r of n)r.getExpression().getText()===`process.env`&&e.report({filePath:e.filePath,message:`Direct 'process.env.${r.getName()}' access in '${t.getName()}'. Use ConfigService instead.`,help:this.meta.help,line:r.getStartLineNumber(),column:1})}}},Tn=/^(error|err|e|ex|exception)$/,En={meta:{id:`security/no-exposed-stack-trace`,category:`security`,severity:`warning`,description:`Stack traces should not be exposed in responses — they leak internal implementation details`,help:`Log the stack trace internally and return a generic error message to the client.`},check(e){let t=e.sourceFile.getDescendantsOfKind(f.PropertyAccessExpression);for(let n of t){if(n.getName()!==`stack`)continue;let t=n.getExpression().getText();if(!(Tn.test(t)||t.endsWith(`.error`)||t.endsWith(`.err`)))continue;let r=n.getParent();if(!r)continue;let i=r.getKind();(i===f.ReturnStatement||i===f.PropertyAssignment||i===f.ShorthandPropertyAssignment||i===f.CallExpression)&&e.report({filePath:e.filePath,message:`Stack trace '${t}.stack' may be exposed in response — leaks implementation details.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},Dn=[{pattern:/^(?=.*\d)[A-Za-z0-9+/]{40,}={0,2}$/,name:`Base64 key`},{pattern:/^sk[-_][a-zA-Z0-9]{20,}$/,name:`Secret key`},{pattern:/^pk[-_][a-zA-Z0-9]{20,}$/,name:`Public key (in source)`},{pattern:/^ghp_[a-zA-Z0-9]{36,}$/,name:`GitHub personal access token`},{pattern:/^github_pat_[a-zA-Z0-9_]{22,}$/,name:`GitHub fine-grained PAT`},{pattern:/^gho_[a-zA-Z0-9]{36,}$/,name:`GitHub OAuth token`},{pattern:/^xox[bpras]-[a-zA-Z0-9-]+$/,name:`Slack token`},{pattern:/^eyJ[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,}\./,name:`JWT token`},{pattern:/^AKIA[0-9A-Z]{16}$/,name:`AWS Access Key ID`},{pattern:/^[a-f0-9]{64}$/,name:`Hex-encoded secret (64 chars)`}],On=[/secret/i,/password/i,/passwd/i,/api[_-]?key/i,/auth[_-]?token/i,/private[_-]?key/i,/access[_-]?key/i,/client[_-]?secret/i],kn=new Set([`your-secret-here`,`changeme`,`password`]),An=/^[A-Za-z][A-Za-z0-9_]*(\.[A-Za-z][A-Za-z0-9_]*)+$/,jn=new Set([`cursor`,`nextCursor`,`prevCursor`,`previousCursor`,`startCursor`,`endCursor`,`pageToken`,`nextPageToken`,`continuationToken`,`continuation`,`nextPage`,`afterCursor`,`beforeCursor`]);function Mn(e){return!(e.length<8||e.includes("${")||e.startsWith(`process.env`)||kn.has(e)||e.includes(` `)||An.test(e))}function Nn(e){return On.some(t=>t.test(e))}function Pn(e){try{let t=Buffer.from(e,`base64`).toString(`utf-8`);return JSON.parse(t),!0}catch{return!1}}function Fn(e){let t=new Map;for(let n of e)t.set(n,(t.get(n)??0)+1);let n=0;for(let r of t.values()){let t=r/e.length;n-=t*Math.log2(t)}return n}const In=new Set([...`aeiouyAEIOUY`]),Ln=/^[A-Z]{2,4}_/,Rn=/(?<=[a-z])(?=[A-Z])|(?<=[A-Za-z])(?=\d)|(?<=\d)(?=[A-Za-z])|_/,zn=/[a-zA-Z]/;function Bn(e){let t=e.includes(`_`),n=e.split(Rn).filter(e=>e.length>0).filter(e=>zn.test(e)),r=n.filter(e=>e.length>=4&&[...e].some(e=>In.has(e)));return n.slice(0,6).filter(e=>e.length>=4&&[...e].some(e=>In.has(e))).length>=2||t&&e.split(`_`).filter(e=>e.length>=3).length>=2||Ln.test(e)?!0:(Fn(e)>4.9&&!t&&r.length,!1)}function Vn(e){let t=e.getParent();if(!t)return!1;let n=t.asKind(f.PropertyAssignment);if(n)return jn.has(n.getName());let r=t.asKind(f.VariableDeclaration);return r?jn.has(r.getName()):!1}const Hn={meta:{id:`security/no-hardcoded-secrets`,category:`security`,severity:`error`,description:`Detect hardcoded secrets, API keys, and tokens in source code`,help:`Move secrets to environment variables and access them via ConfigService.`},check(e){let t=e.sourceFile.getDescendantsOfKind(f.StringLiteral);for(let n of t){let t=n.getLiteralValue();if(!(t.length<16)&&n.getParent()?.getKind()!==f.ImportDeclaration){for(let{pattern:r,name:i}of Dn)if(r.test(t)){if(i===`Base64 key`&&(Pn(t)||Vn(n)||Bn(t)))break;e.report({filePath:e.filePath,message:`Possible hardcoded ${i} detected.`,help:this.meta.help,line:n.getStartLineNumber(),column:1});break}}}let n=e.sourceFile.getDescendantsOfKind(f.VariableDeclaration);for(let t of n){let n=t.getName(),r=t.getInitializer();!r||r.getKind()!==f.StringLiteral||Nn(n)&&Mn(r.getText().slice(1,-1))&&e.report({filePath:e.filePath,message:`Variable '${n}' appears to contain a hardcoded secret.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}let r=e.sourceFile.getDescendantsOfKind(f.PropertyAssignment);for(let t of r){let n=t.getName(),r=t.getInitializer();!r||r.getKind()!==f.StringLiteral||Nn(n)&&Mn(r.getText().slice(1,-1))&&e.report({filePath:e.filePath,message:`Property '${n}' appears to contain a hardcoded secret.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}},Un=RegExp(`(?:^|[^a-zA-Z])\\w*(?:${[`Entity`,`Model`].join(`|`)})(?:[^a-zA-Z]|$)`),Wn={meta:{id:`security/no-raw-entity-in-response`,category:`security`,severity:`warning`,description:`Returning ORM entities directly from controllers can leak internal fields like passwords or IDs`,help:`Map entities to DTOs or use class-transformer's @Exclude()/@Expose() decorators before returning.`},check(e){for(let t of e.sourceFile.getClasses())if(E(t))for(let n of t.getMethods()){if(!D(n))continue;let t=n.getReturnType().getText();Un.test(t)&&!t.includes(`DTO`)&&!t.includes(`Dto`)&&!t.includes(`Response`)&&e.report({filePath:e.filePath,message:`Controller method '${n.getName()}' returns a raw entity type. This may leak internal fields.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},Gn={meta:{id:`security/no-synchronize-in-production`,category:`security`,severity:`error`,description:`TypeORM synchronize: true auto-syncs schema and can drop columns or tables in production`,help:`Set synchronize: false and use migrations for production schema changes.`},check(e){let t=e.sourceFile.getDescendantsOfKind(f.PropertyAssignment);for(let n of t){if(n.getName()!==`synchronize`)continue;let t=n.getInitializer();t&&t.getText()===`true`&&e.report({filePath:e.filePath,message:`TypeORM 'synchronize: true' can auto-drop columns and tables in production.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},Kn=new Set([`md5`,`sha1`]),qn={meta:{id:`security/no-weak-crypto`,category:`security`,severity:`warning`,description:`Weak hashing algorithms (MD5, SHA1) should not be used for security purposes`,help:`Use a stronger algorithm like SHA-256 or bcrypt for password hashing.`},check(e){let t=e.sourceFile.getDescendantsOfKind(f.CallExpression);for(let n of t){if(!n.getExpression().getText().endsWith(`createHash`))continue;let t=n.getArguments();if(t.length===0)continue;let r=t[0];if(r.getKind()!==f.StringLiteral)continue;let i=r.getText().slice(1,-1).toLowerCase();Kn.has(i)&&e.report({filePath:e.filePath,message:`Weak hashing algorithm '${i}' used in createHash().`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},Jn=new Set([`Public`,`AllowAnonymous`,`SkipAuth`,`IsPublic`]),Yn=[ke,mt,it,lt,et,gt,_t,yt,Ce,Ye,Ht,Gt,At,kt,Ft,zt,Pt,Lt,wt,Ot,Rt,Ut,Nt,Vt,bt,rn,Et,qt,It,St,Hn,Cn,qn,wn,xn,En,Sn,Gn,Wn,{meta:{id:`security/require-guards-on-endpoints`,category:`security`,severity:`warning`,description:`Controller endpoints should be protected by @UseGuards() at class or method level`,help:`Add @UseGuards(AuthGuard) to the controller class or individual route handlers, or mark routes as @Public(). If you use a global guard via APP_GUARD, you can disable this rule.`},check(e){for(let t of e.sourceFile.getClasses())if(E(t)&&t.getDecorator(`UseGuards`)===void 0&&!t.getDecorators().some(e=>Jn.has(e.getName())))for(let n of t.getMethods())D(n)&&n.getDecorator(`UseGuards`)===void 0&&(n.getDecorators().some(e=>Jn.has(e.getName()))||e.report({filePath:e.filePath,message:`Endpoint '${n.getName()}' has no @UseGuards() at class or method level.`,help:this.meta.help,line:n.getStartLineNumber(),column:1}))}},dn,on,sn,ln,hn,fn,cn,_n,bn,gn];function Xn(){return[...Yn]}function Zn(e){return e.meta.scope===`project`}function Qn(e){return e.meta.scope===`schema`}function $n(e,t,n){if(t.length===0)return e;let r=new Set(e.map(e=>e.meta.id)),i=[...e];for(let e of t){if(r.has(e.meta.id)){n.push(`Custom rule "${e.meta.id}" conflicts with a built-in rule and was skipped`);continue}i.push(e)}return i}function er(e,t){return t.filter(t=>{let n=e.rules?.[t.meta.id];return!(n===!1||typeof n==`object`&&n.enabled===!1||e.categories?.[t.meta.category]===!1)})}function tr(e){let t=[],n=[],r=[];for(let i of e)Qn(i)?r.push(i):Zn(i)?n.push(i):t.push(i);return{fileRules:t,projectRules:n,schemaRules:r}}const nr=new Set([`security`,`performance`,`correctness`,`architecture`]),rr=new Set([`error`,`warning`,`info`]),ir=new Set([`file`,`project`]),ar=`custom/`;function or(e){if(typeof e!=`object`||!e)return!1;let t=e;if(typeof t.check!=`function`||typeof t.meta!=`object`||t.meta===null)return!1;let n=t.meta;return!(typeof n.id!=`string`||n.id.trim()===``||typeof n.description!=`string`||typeof n.help!=`string`||!nr.has(n.category)||!rr.has(n.severity)||n.scope!==void 0&&!ir.has(n.scope))}function sr(e){return e.meta.id.startsWith(ar)?e:{...e,meta:{...e.meta,id:`${ar}${e.meta.id}`}}}async function cr(t,i){let a=[],o=[],c=s(i,t);if(!e(c))return o.push(`Custom rules directory not found: ${c}`),{rules:a,warnings:o};if(!r(c).isDirectory())return o.push(`Custom rules path is not a directory: ${c}`),{rules:a,warnings:o};let l;try{l=n(c)}catch(e){return o.push(`Failed to read custom rules directory: ${e instanceof Error?e.message:String(e)}`),{rules:a,warnings:o}}let u=l.filter(e=>e.endsWith(`.ts`));if(u.length===0)return o.push(`No rule files (.ts) found in: ${c}`),{rules:a,warnings:o};let d=m(c,{interopDefault:!0});for(let e of u){let t=s(c,e),n;try{n=await d.import(t)}catch(t){o.push(`Failed to load custom rule file "${e}": ${t instanceof Error?t.message:String(t)}`);continue}let r=!1;for(let[t,i]of Object.entries(n))or(i)?(a.push(sr(i)),r=!0):t!==`__esModule`&&typeof i==`object`&&i&&`meta`in i&&o.push(`Invalid rule export "${t}" in "${e}": missing or invalid required fields (check, meta.id, meta.description, meta.help, meta.category, meta.severity)`);!r&&Object.keys(n).length>0&&(Object.values(n).some(e=>typeof e==`object`&&!!e&&(`meta`in e||`check`in e))||o.push(`No valid rule exports found in "${e}"`))}return{rules:a,warnings:o}}function lr(e,t){return e.customRulesDir?cr(e.customRulesDir,t):Promise.resolve({rules:[],warnings:[]})}async function M(e,t){let n=await ve(e,t),{rules:r,warnings:i}=await lr(n,e),a=$n(Yn,r,i),{fileRules:o,projectRules:s,schemaRules:c}=tr(er(n,a));return{combinedRules:a,config:n,customRuleWarnings:i,fileRules:o,projectRules:s,schemaRules:c}}async function ur(e,t={}){return(await l(t.include??C.include,{cwd:e,absolute:!0,ignore:t.exclude??C.exclude})).sort()}async function dr(e,t,n={}){let r=await Promise.all([...t.projects.entries()].map(async([t,r])=>[t,await ur(a(e,r),n)])),i=new Map;for(let[e,t]of r)i.set(e,t);return i}function fr(e){let t=new d({compilerOptions:{strict:!0,target:99,module:99,skipFileDependencyResolution:!0},skipAddingFilesFromTsConfig:!0});for(let n of e)t.addSourceFileAtPath(n);return t}const pr=/import\([^)]+\)\.(\w+)/,mr=/^(\w+)</;function hr(e,t){let n=[];for(let r of e.getClasses()){if(!r.getDecorator(`Injectable`))continue;let e=r.getName();if(!e)continue;let i=r.getConstructors()[0],a=i?i.getParameters().map(e=>{let t=e.getTypeNode();return N(t?t.getText():e.getType().getText())}):[],o=r.getMethods().filter(e=>{let t=e.getScope();return!t||t===`public`}).length;n.push({name:e,filePath:t,classDeclaration:r,dependencies:a,publicMethodCount:o})}return n}function gr(e,t){let n=new Map;for(let r of t){let t=e.getSourceFile(r);if(t)for(let e of hr(t,r))n.set(e.name,e)}return n}function _r(e,t,n){for(let[t,r]of e)r.filePath===n&&e.delete(t);let r=t.getSourceFile(n);if(r)for(let t of hr(r,n))e.set(t.name,t)}function N(e){let t=e.match(pr);if(t)return t[1];let n=e.match(mr);return n?n[1]:e}const P=/^['"`]|['"`]$/g,vr=/\/+/g,yr=/\/$/,br=new Set([`Query`,`Mutation`,`Subscription`]),xr=new Set([`ApiOperation`,`ApiParam`,`ApiQuery`,`ApiResponse`,`ApiBody`]),Sr=new Set([`map`,`forEach`,`filter`,`find`,`some`,`every`,`flatMap`,`reduce`]);var F=class{scanResults=new Map;injectionMaps=new Map;methodLookups=new Map;getScan(e){return this.scanResults.get(e)}setScan(e,t){this.scanResults.set(e,t)}getInjMap(e){return this.injectionMaps.get(e)}setInjMap(e,t){this.injectionMaps.set(e,t)}getMethod(e){return this.methodLookups.has(e)?this.methodLookups.get(e):void 0}hasMethod(e){return this.methodLookups.has(e)}setMethod(e,t){this.methodLookups.set(e,t)}};function I(e){let t=e.replace(/\s+/g,` `).trim();return t.length>50?`${t.slice(0,50)}\u2026`:t}function L(e,t){let n={isConditional:!1,conditionText:null,branchKind:null,statementLine:null},r=e;for(;r&&r!==t;){let e=r.getParent();if(!e||e===t)break;let n=e.getKind();if(n===f.IfStatement){let t=e.asKindOrThrow(f.IfStatement);if(r===t.getThenStatement()){let n=e.getParent();if(n&&n.getKind()===f.IfStatement){let r=n.asKindOrThrow(f.IfStatement);if(e===r.getElseStatement())return{isConditional:!0,conditionText:I(t.getExpression().getText()),branchKind:`else-if`,statementLine:r.getStartLineNumber()}}return{isConditional:!0,conditionText:I(t.getExpression().getText()),branchKind:`if`,statementLine:t.getStartLineNumber()}}if(r===t.getElseStatement())return{isConditional:!0,conditionText:I(t.getExpression().getText()),branchKind:`else`,statementLine:t.getStartLineNumber()}}if(n===f.ConditionalExpression){let t=e.asKindOrThrow(f.ConditionalExpression);if(r===t.getWhenTrue())return{isConditional:!0,conditionText:I(t.getCondition().getText()),branchKind:`ternary-true`,statementLine:t.getStartLineNumber()};if(r===t.getWhenFalse())return{isConditional:!0,conditionText:I(t.getCondition().getText()),branchKind:`ternary-false`,statementLine:t.getStartLineNumber()}}let i=r.getKind();if(i===f.CaseClause){let e=r.asKindOrThrow(f.CaseClause),t=r.getParentOrThrow().getParentOrThrow();return{isConditional:!0,conditionText:I(e.getExpression().getText()),branchKind:`case`,statementLine:t.getStartLineNumber()}}if(i===f.DefaultClause)return{isConditional:!0,conditionText:null,branchKind:`default`,statementLine:r.getParentOrThrow().getParentOrThrow().getStartLineNumber()};if(i===f.CatchClause)return{isConditional:!0,conditionText:null,branchKind:`catch`,statementLine:r.getParentOrThrow().getStartLineNumber()};r=e}return n}const Cr=new Map([[f.ForStatement,`for`],[f.ForOfStatement,`for-of`],[f.ForInStatement,`for-in`],[f.WhileStatement,`while`],[f.DoStatement,`do-while`]]);function R(e,t){let n={iterationKind:null,iterationLabel:null},r=e;for(;r&&r!==t;){let e=r.getParent();if(!e||e===t)break;let n=e.getKind(),i=Cr.get(n);if(i){let t=!1;if(n===f.ForStatement){let n=e.asKindOrThrow(f.ForStatement);t=r!==n.getInitializer()&&r!==n.getCondition()&&r!==n.getIncrementor()&&r===n.getStatement()}else n===f.ForOfStatement?t=r===e.asKindOrThrow(f.ForOfStatement).getStatement():n===f.ForInStatement?t=r===e.asKindOrThrow(f.ForInStatement).getStatement():n===f.WhileStatement?t=r===e.asKindOrThrow(f.WhileStatement).getStatement():n===f.DoStatement&&(t=r===e.asKindOrThrow(f.DoStatement).getStatement());if(t)return{iterationKind:`loop`,iterationLabel:i}}let a=r.getKind();if(a===f.ArrowFunction||a===f.FunctionExpression){if(n===f.CallExpression){let t=e.asKindOrThrow(f.CallExpression);if(t.getArguments().some(e=>e===r)){let e=t.getExpression();if(e.getKind()===f.PropertyAccessExpression){let t=e.asKindOrThrow(f.PropertyAccessExpression).getName();if(Sr.has(t))return{iterationKind:`callback`,iterationLabel:t}}}}break}if(n===f.ArrayLiteralExpression){let t=e.getParent();if(t&&t.getKind()===f.CallExpression){let n=t.asKindOrThrow(f.CallExpression),r=n.getArguments();if(r.length>0&&r[0]===e){let e=n.getExpression();if(e.getKind()===f.PropertyAccessExpression){let t=e.asKindOrThrow(f.PropertyAccessExpression);if(t.getName()===`all`&&t.getExpression().getText().endsWith(`Promise`))return{iterationKind:`concurrent`,iterationLabel:`all`}}}}}r=e}return n}function z(e){let t=e;for(;t;){let e=t.getKind();if(e===f.ExpressionStatement||e===f.VariableStatement||e===f.ReturnStatement||e===f.ThrowStatement)break;t=t.getParent()}if(!t)return null;let n=t.getSourceFile(),r=t.getFullStart(),i=t.getStart(),a=n.getFullText().slice(r,i).split(`
|
|
4
|
-
`);for(let e=a.length-1;e>=0;e--){let t=a[e].trim();if(t.startsWith(`//`))return t.slice(2).trim();if(t.length>0)break}return null}function wr(e){let t=e.asKindOrThrow(f.ThrowStatement).getExpression();return t&&t.getKind()===f.NewExpression?N(t.asKindOrThrow(f.NewExpression).getExpression().getText()):`Error`}function Tr(e){let t=e.asKindOrThrow(f.ThrowStatement).getExpression();if(!t||t.getKind()!==f.NewExpression)return null;let n=t.asKindOrThrow(f.NewExpression).getArguments();if(n.length===0)return null;let r=n[0],i=r.getKind(),a;if(i===f.StringLiteral||i===f.NoSubstitutionTemplateLiteral)a=r.asKindOrThrow(i).getLiteralValue();else if(i===f.TemplateExpression){let e=r.getText();a=e.startsWith("`")?e.slice(1,-1):e}else a=r.getText();return a.length>80?`${a.slice(0,80)}\u2026`:a}function Er(e){let t=e.getParent();for(;t;){let e=t.getKind();if(e===f.AwaitExpression||e===f.ParenthesizedExpression||e===f.AsExpression||e===f.NonNullExpression){t=t.getParent();continue}if(e===f.VariableDeclaration){let e=t.asKindOrThrow(f.VariableDeclaration).getNameNode();return e.getKind()===f.Identifier?e.getText():null}return null}return null}function Dr(e,t){let n;try{n=e.getBaseClass()}catch{}if(!n&&t){let r=e.getExtends();if(r){let e=N(r.getExpression().getText()),i=t.get(e);i&&(n=i.classDeclaration)}}return n}function B(e,t,n,r){let i=`${e.getName()??``}.${t}`;if(r?.hasMethod(i))return r.getMethod(i);let a=e,o=new Set;for(;a;){let e=a.getName();if(e&&o.has(e))break;e&&o.add(e);let s=a.getInstanceMethod(t);if(s)return r?.setMethod(i,s),s;a=Dr(a,n)}r?.setMethod(i,void 0)}function Or(e){let t=e.getDecorator(`Controller`);if(!t)return``;let n=t.getArguments();if(n.length===0)return``;let r=n[0];if(r.getKind()===f.ObjectLiteralExpression){let e=r.asKindOrThrow(f.ObjectLiteralExpression).getProperty(`path`);if(!e)return``;let t=e.asKind(f.PropertyAssignment);if(!t)return``;let n=t.getInitializer();return n?n.getText().replace(P,``):``}return r.getText().replace(P,``)}function kr(e){for(let t of e.getDecorators()){let e=t.getName();if(!w.has(e))continue;let n=t.getArguments(),r=n.length>0?n[0].getText().replace(P,``):``;return{httpMethod:e.toUpperCase(),path:r}}}function Ar(e,t){return`/${[e,t].filter(Boolean).join(`/`)}`.replace(vr,`/`).replace(yr,``)||`/`}function V(e,t){let n=e.asKind(f.ObjectLiteralExpression);if(!n)return null;let r=n.getProperty(t);if(!r)return null;let i=r.asKind(f.PropertyAssignment);if(!i)return null;let a=i.getInitializer();return a?a.getText().replace(P,``):null}function jr(e,t){let n=V(e,t);if(n===null)return null;let r=Number(n);return Number.isNaN(r)?null:r}function Mr(e,t,n){let r=V(e,t);return r===null?n:r===`true`}function Nr(e){let t=null,n=null,r=[],i=[],a=[],o=null,s=!1;for(let c of e.getDecorators()){let e=c.getName();if(!xr.has(e))continue;s=!0;let l=c.getArguments();if(l.length===0)continue;let u=l[0];if(e===`ApiOperation`)t=V(u,`summary`),n=V(u,`description`);else if(e===`ApiParam`){let e=V(u,`name`);e&&r.push({description:V(u,`description`),name:e,required:Mr(u,`required`,!0),type:V(u,`type`)})}else if(e===`ApiQuery`){let e=V(u,`name`);e&&i.push({description:V(u,`description`),name:e,required:Mr(u,`required`,!1),type:V(u,`type`)})}else if(e===`ApiResponse`){let e=jr(u,`status`)??200,t=V(u,`type`);t?.startsWith(`[`)&&t.endsWith(`]`)&&(t=`${t.slice(1,-1)}[]`),a.push({description:V(u,`description`),status:e,type:t})}else e===`ApiBody`&&(o={description:V(u,`description`),type:V(u,`type`)})}if(!o){for(let t of e.getParameters())if(t.getDecorators().some(e=>e.getName()===`Body`)){let e=t.getTypeNode();e&&(o={description:null,type:e.getText()},s=!0);break}}return s?{body:o,description:n,params:r,queryParams:i,responses:a,summary:t}:null}const Pr=/^(?:Promise|Observable)<(.+)>$/;function H(e){let t=e.getReturnTypeNode();if(!t)return null;let n=t.getText().trim(),r=Pr.exec(n);return r&&(n=r[1]),n===`void`||n===`any`||n===`unknown`?null:n}function Fr(e){return e.getParameters().filter(e=>e.getName()!==`this`).map(e=>({name:e.getName(),type:e.getTypeNode()?.getText()??null}))}const Ir=/=>\s*\{[^}]*\}/g,Lr=/\(([^)]{20,})\)\s*=>/g,Rr=/\s+/g;function zr(e){let t=e.replace(Rr,` `).trim();return t=t.replace(Ir,`=> …`),t=t.replace(Lr,`(…) =>`),t.length>50&&(t=`${t.slice(0,47)}…`),t}function Br(e,t){let n=e.getKind();if(n!==f.VariableStatement&&n!==f.ExpressionStatement)return!1;let r=[...e.getDescendantsOfKind(f.CallExpression),...e.getDescendantsOfKind(f.NewExpression)];if(r.length===0)return!1;for(let e of r)if(t.has(e.getStart()))return!1;for(let e of r){let t=e.getText();if(t.startsWith(`console.`)||t.startsWith(`this.logger.`))return!1}return!0}function Vr(e){if(e.getKind()===f.VariableStatement){let t=e.asKindOrThrow(f.VariableStatement).getDeclarationList().getDeclarations();if(t.length===0)return null;let n=t[0],r=n.getNameNode().getText(),i=n.getInitializer();return i?{assignedTo:r,text:`${r} = ${zr(i.getText())}`}:null}return e.getKind()===f.ExpressionStatement?{assignedTo:null,text:zr(e.asKindOrThrow(f.ExpressionStatement).getExpression().getText())}:null}function U(e,t,n){let r=e.getName()??``;if(n){let e=n.getInjMap(r);if(e)return e}let i=new Map,a=e,o=new Set;for(;a;){let e=a.getName();if(e&&o.has(e))break;e&&o.add(e);let n=a.getConstructors()[0];if(n){for(let e of n.getParameters()){let t=e.getName();if(!i.has(t)){let n=e.getTypeNode(),r=n?n.getText():e.getType().getText();i.set(t,N(r))}}break}a=Dr(a,t)}for(let t of e.getProperties())if(t.getDecorator(`Inject`)){let e=t.getName();if(!i.has(e)){let n=t.getTypeNode();n&&i.set(e,N(n.getText()))}}return n&&n.setInjMap(r,i),i}function Hr(e,t){for(let n of e){if(!n.assignedTo)continue;let e=RegExp(`\\b${n.assignedTo}\\b`);for(let r of t)if(!r.merged&&!(r.order<=n.order)&&r.conditional&&r.conditionText&&e.test(r.conditionText)){n.guardThrow={branchKind:r.branchKind,callSiteLine:r.callSiteLine,className:r.exceptionClassName,conditionText:r.conditionText,message:r.message},r.merged=!0;break}}let n=t.filter(e=>!e.merged);t.length=0;for(let e of n)t.push(e)}function W(e,t,n,r,i){let a=`${n?.getName()??``}::${e.getName()}`;if(!r&&i){let e=i.getScan(a);if(e)return e}let o={deps:[],sameClassCalls:[],steps:[],throws:[]},s=e.getBody();if(!s)return o;let c=r??new Set,l=e.getName();if(c.has(l))return o;c.add(l);let u=new Map;for(let e of s.getDescendantsOfKind(f.VariableDeclaration)){let n=e.getInitializer();if(n&&n.getKind()===f.PropertyAccessExpression){let r=n.asKindOrThrow(f.PropertyAccessExpression);if(r.getExpression().getKind()===f.ThisKeyword){let n=r.getName();t.has(n)&&u.set(e.getName(),n)}}}let d=[],p=[],m=[],h=0,g=s.getDescendantsOfKind(f.CallExpression),_=s.getDescendantsOfKind(f.ThrowStatement),v=[...g.map(e=>({kind:`call`,node:e})),..._.map(e=>({kind:`throw`,node:e}))];v.sort((e,t)=>e.node.getStart()-t.node.getStart());for(let e of v){if(e.kind===`throw`){let t=L(e.node,s),n=R(e.node,s);p.push({branchGroupId:t.statementLine?`L${t.statementLine}`:null,branchKind:t.branchKind,callSiteLine:e.node.getStartLineNumber(),comment:z(e.node),conditional:t.isConditional,conditionText:t.conditionText,exceptionClassName:wr(e.node),iterationKind:n.iterationKind,iterationLabel:n.iterationLabel,message:Tr(e.node),order:h++});continue}let r=e.node,i=r.getExpression();if(i.getKind()!==f.PropertyAccessExpression)continue;let a=i.asKindOrThrow(f.PropertyAccessExpression),o=a.getName(),l=a.getExpression(),g;if(l.getKind()===f.PropertyAccessExpression){let e=l.asKindOrThrow(f.PropertyAccessExpression);if(e.getExpression().getKind()===f.ThisKeyword){let n=e.getName();t.has(n)&&(g=n)}}if(!g&&l.getKind()===f.Identifier){let e=l.getText(),t=u.get(e);t&&(g=t)}if(g){let e=L(r,s),t=R(r,s);m.push({assignedTo:Er(r),paramName:g,methodName:o,order:h++,callSiteLine:r.getStartLineNumber(),comment:z(r),condInfo:e,iterInfo:t,guardThrow:null});continue}if(l.getKind()===f.ThisKeyword&&n){let e=n.getInstanceMethod(o);if(e&&!c.has(o)){let i=L(r,s),a=R(r,s),l=W(e,t,n,new Set(c));d.push({assignedTo:Er(r),branchGroupId:i.statementLine?`L${i.statementLine}`:null,branchKind:i.branchKind,callSiteLine:r.getStartLineNumber(),childResult:l,comment:z(r),conditional:i.isConditional,conditionText:i.conditionText,iterationKind:a.iterationKind,iterationLabel:a.iterationLabel,methodName:o,order:h++})}}}Hr(m,p);let y=[];if(s.getKind()===f.Block){let e=new Set;for(let t of m)for(let n of g)n.getStartLineNumber()===t.callSiteLine&&e.add(n.getStart());for(let t of p)for(let n of _)n.getStartLineNumber()===t.callSiteLine&&e.add(n.getStart());for(let t of d)for(let n of g)n.getStartLineNumber()===t.callSiteLine&&e.add(n.getStart());let t=s.asKindOrThrow(f.Block).getStatements(),n=[],r=()=>{if(n.length===0)return;let e=n[0].stmt,t=L(e,s),r=R(e,s);y.push({branchGroupId:t.statementLine?`L${t.statementLine}`:null,branchKind:t.branchKind,callSiteLine:e.getStartLineNumber(),comment:z(e),conditional:t.isConditional,conditionText:t.conditionText,iterationKind:r.iterationKind,iterationLabel:r.iterationLabel,order:0,statements:n.map(e=>e.info)}),n=[]};for(let i of t){let t=i.getStart(),a=i.getEnd(),o=!1;for(let n of e)if(n>=t&&n<=a){o=!0;break}if(o){r();continue}if(Br(i,e)){let e=Vr(i);if(e){n.push({info:e,stmt:i});continue}}r()}r()}if(y.length>0){let e=[];for(let t of m)e.push({kind:`call`,item:t});for(let t of p)e.push({kind:`throw`,item:t});for(let t of d)e.push({kind:`scc`,item:t});for(let t of y)e.push({kind:`step`,item:t});e.sort((e,t)=>e.item.callSiteLine-t.item.callSiteLine);let t=0;for(let n of e)n.item.order=t++}let b=[],x=new Map;for(let e of m){let n=t.get(e.paramName);x.has(n)||x.set(n,[]);let r=e.condInfo.isConditional;x.get(n).push({assignedTo:e.assignedTo,branchGroupId:r&&e.condInfo.statementLine?`L${e.condInfo.statementLine}`:null,branchKind:r?e.condInfo.branchKind:null,callSiteLine:e.callSiteLine,comment:e.comment,conditional:r,conditionText:r?e.condInfo.conditionText:null,guardThrow:e.guardThrow,iterationKind:e.iterInfo.iterationKind,iterationLabel:e.iterInfo.iterationLabel,name:e.methodName,order:e.order})}for(let[e,t]of x)t.sort((e,t)=>e.order-t.order),b.push({className:e,methodsCalled:t});let S={deps:b,sameClassCalls:d,steps:y,throws:p};return!r&&i&&i.setScan(a,S),S}function G(e){return e.endsWith(`Repository`)?`repository`:e.endsWith(`Guard`)?`guard`:e.endsWith(`Interceptor`)?`interceptor`:e.endsWith(`Pipe`)?`pipe`:e.endsWith(`Filter`)?`filter`:e.endsWith(`Gateway`)?`gateway`:`service`}function K(e,t,n,r,i){let a=[],o=new Set,s=new Map,c=e.deps,l=[],u=[];for(let e of c)if(e.methodsCalled.length===0)u.push(e);else for(let t of e.methodsCalled)l.push({className:e.className,mc:t,dep:e});l.sort((e,t)=>e.mc.order-t.mc.order);for(let e of u){if(r.has(e.className)||o.has(e.className))continue;o.add(e.className),r.add(e.className);let t=n.get(e.className),s=t?t.dependencies.map(e=>({className:e,methodsCalled:[]})):[];a.push({assignedTo:null,branchGroupId:null,branchKind:null,callSiteLine:0,className:e.className,comment:null,conditional:!1,conditionText:null,dependencies:K({deps:s,sameClassCalls:[],steps:[],throws:[]},e.className,n,new Set(r),i),endLine:0,filePath:t?.filePath??``,guardThrow:null,iterationKind:null,iterationLabel:null,line:0,methodName:null,order:0,parameters:[],returnType:null,stepStatements:[],throwMessage:null,totalMethods:t?.publicMethodCount??0,type:G(e.className)})}let d=new Map;for(let{className:e,mc:t}of l)d.has(e)||d.set(e,new Set),d.get(e).add(t.name);for(let{className:e,mc:t}of l){if(r.has(e))continue;let c=n.get(e),l=!o.has(e);l&&o.add(e);let u=[];if(l&&c){let t=new Set(r);t.add(e);let a=U(c.classDeclaration,n,i),o=[],l=0,f=[],p=[],m=d.get(e)??new Set;for(let e of m){let t=B(c.classDeclaration,e,n,i);if(!t)continue;let r=W(t,a,c.classDeclaration,void 0,i),s=[];for(let e of r.deps)for(let t of e.methodsCalled)s.push({kind:`dep`,depClassName:e.className,m:t});for(let e of r.throws)s.push({kind:`throw`,t:e});for(let e of r.sameClassCalls)s.push({kind:`scc`,scc:e});function u(e){return e.kind===`dep`?e.m.order:e.kind===`throw`?e.t.order:e.scc.order}s.sort((e,t)=>u(e)-u(t));for(let e of s)e.kind===`dep`?o.push({assignedTo:e.m.assignedTo,depClassName:e.depClassName,methodName:e.m.name,order:l++,callSiteLine:e.m.callSiteLine,comment:e.m.comment,conditional:e.m.conditional,branchKind:e.m.conditional?e.m.branchKind:null,conditionText:e.m.conditional?e.m.conditionText:null,branchGroupId:e.m.conditional?e.m.branchGroupId:null,guardThrow:e.m.guardThrow,iterationKind:e.m.iterationKind,iterationLabel:e.m.iterationLabel}):e.kind===`throw`?p.push({...e.t,order:l++}):f.push(e.scc)}Hr(o,p);let h=new Map;for(let e of o)h.has(e.depClassName)||h.set(e.depClassName,[]),h.get(e.depClassName).push({assignedTo:e.assignedTo,branchGroupId:e.branchGroupId,branchKind:e.branchKind,callSiteLine:e.callSiteLine,comment:e.comment,conditional:e.conditional,conditionText:e.conditionText,guardThrow:e.guardThrow,iterationKind:e.iterationKind,iterationLabel:e.iterationLabel,name:e.methodName,order:e.order});let g=[];for(let[e,t]of h)t.sort((e,t)=>e.order-t.order),g.push({className:e,methodsCalled:t});u=K({deps:g,sameClassCalls:f,steps:[],throws:p},e,n,t,i),s.set(e,u)}else l||(u=s.get(e)??[]);let f=0,p=0,m=null,h=[];if(c){let e=B(c.classDeclaration,t.name,n,i);e&&(f=e.getStartLineNumber(),p=e.getEndLineNumber(),m=H(e),h=Fr(e))}a.push({assignedTo:t.assignedTo,branchGroupId:t.branchGroupId,branchKind:t.branchKind,callSiteLine:t.callSiteLine,className:e,comment:t.comment,conditional:t.conditional,conditionText:t.conditionText,dependencies:u,endLine:p,filePath:c?.filePath??``,guardThrow:t.guardThrow,iterationKind:t.iterationKind,iterationLabel:t.iterationLabel,line:f,methodName:t.name,order:t.order,parameters:h,returnType:m,stepStatements:[],throwMessage:null,totalMethods:c?.publicMethodCount??0,type:G(e)})}let f=n.get(t);for(let o of e.sameClassCalls){let e=0,s=0,c=null,l=[];if(f){let t=B(f.classDeclaration,o.methodName,n,i);t&&(e=t.getStartLineNumber(),s=t.getEndLineNumber(),c=H(t),l=Fr(t))}let u=K(o.childResult,t,n,new Set(r),i);a.push({assignedTo:o.assignedTo,branchGroupId:o.branchGroupId,branchKind:o.branchKind,callSiteLine:o.callSiteLine,className:t,comment:o.comment,conditional:o.conditional,conditionText:o.conditionText,dependencies:u,endLine:s,filePath:f?.filePath??``,guardThrow:null,iterationKind:o.iterationKind,iterationLabel:o.iterationLabel,line:e,methodName:o.methodName,order:o.order,parameters:l,returnType:c,stepStatements:[],throwMessage:null,totalMethods:f?.publicMethodCount??0,type:G(t)})}for(let t of e.throws)a.push({assignedTo:null,branchGroupId:t.branchGroupId,branchKind:t.branchKind,callSiteLine:t.callSiteLine,className:t.exceptionClassName,comment:t.comment,conditional:t.conditional,conditionText:t.conditionText,dependencies:[],endLine:t.callSiteLine,filePath:f?.filePath??``,guardThrow:null,iterationKind:t.iterationKind,iterationLabel:t.iterationLabel,line:t.callSiteLine,methodName:null,order:t.order,parameters:[],returnType:null,stepStatements:[],throwMessage:t.message,totalMethods:0,type:`throw`});for(let t of e.steps)a.push({assignedTo:null,branchGroupId:t.branchGroupId,branchKind:t.branchKind,callSiteLine:t.callSiteLine,className:`local`,comment:t.comment,conditional:t.conditional,conditionText:t.conditionText,dependencies:[],endLine:t.callSiteLine,filePath:f?.filePath??``,guardThrow:null,iterationKind:t.iterationKind,iterationLabel:t.iterationLabel,line:t.callSiteLine,methodName:null,order:t.order,parameters:[],returnType:null,stepStatements:t.statements,throwMessage:null,totalMethods:0,type:`step`});return a.sort((e,t)=>e.order-t.order),a}function Ur(e){for(let t of e.getDecorators()){let n=t.getName();if(br.has(n))return{httpMethod:n.toUpperCase(),path:e.getName()}}}function Wr(e,t,n,r){let i=[];for(let a of e.getClasses()){let e=E(a),o=T(a,`Resolver`);if(!(e||o))continue;let s=e?Or(a):``,c=a.getName()??(e?`AnonymousController`:`AnonymousResolver`),l=U(a,n,r);for(let o of a.getMethods()){let u=e?kr(o):Ur(o);if(!u)continue;let d=e?Ar(s,u.path):u.path,f=K(W(o,l,a,void 0,r),c,n,new Set,r),p=Nr(o),m=H(o);i.push({controllerClass:c,dependencies:f,endLine:o.getEndLineNumber(),filePath:t,handlerMethod:o.getName(),httpMethod:u.httpMethod,line:o.getStartLineNumber(),returnType:m,routePath:d,swagger:p})}}return i}function q(e,t,n){let r=[],i=new F;for(let a of t){let t=e.getSourceFile(a);t&&r.push(...Wr(t,a,n,i))}return{endpoints:r}}function Gr(e,t,n,r,i,a,o){if(i>10)return[];let s=e.getBody();if(!s)return[];let c=[],l=s.getDescendantsOfKind(f.CallExpression);for(let e of l){let s=e.getExpression();if(s.getKind()!==f.PropertyAccessExpression)continue;let l=s.asKindOrThrow(f.PropertyAccessExpression),u=l.getName(),d=l.getExpression();if(d.getKind()===f.PropertyAccessExpression){let e=d.asKindOrThrow(f.PropertyAccessExpression);if(e.getExpression().getKind()!==f.ThisKeyword)continue;let a=e.getName(),s=t.get(a);if(!s)continue;let l=`${s}.${u}`;if(r.has(l)){c.push({calls:[],circular:!0,className:s,filePath:``,line:0,methodName:u});continue}r.add(l);let p=n.get(s),m=[],h=``,g=0;if(p){h=p.filePath;let e=p.classDeclaration.getInstanceMethod(u);e&&(g=e.getStartLineNumber(),m=Gr(e,U(p.classDeclaration,void 0,o),n,new Set(r),i+1,p.classDeclaration,o))}c.push({calls:m,className:s,filePath:h,line:g,methodName:u})}else if(d.getKind()===f.ThisKeyword&&a){let e=a.getInstanceMethod(u);if(!e)continue;let s=`${a.getName()??`Anonymous`}.${u}`;if(r.has(s))continue;r.add(s);let l=Gr(e,t,n,new Set(r),i+1,a,o);c.push(...l)}}return c}function Kr(e,t,n){let r=n.getSourceFile(e.filePath);if(!r)return[];let i=r.getClasses().find(t=>t.getName()===e.controllerClass);if(!i)return[];let a=i.getInstanceMethod(e.handlerMethod);if(!a)return[];let o=new F;return Gr(a,U(i,void 0,o),t,new Set,0,i,o)}function qr(e,t,n,r){e.endpoints=e.endpoints.filter(e=>e.filePath!==n);let i=t.getSourceFile(n);if(!i)return;let a=new F;e.endpoints.push(...Wr(i,n,r,a))}const Jr=new Set([`pgTable`,`mysqlTable`,`sqliteTable`]),Yr=new Set([`serial`,`bigserial`,`smallserial`]),Xr=/=>\s*(\w+)/;function Zr(e){let t={type:`unknown`,isPrimary:!1,isNullable:!0,isGenerated:!1,isUnique:!1};function n(e){if(e.getKind()===f.CallExpression){let r=e.asKindOrThrow(f.CallExpression),i=r.getExpression();if(i.getKind()===f.PropertyAccessExpression){let e=i.asKindOrThrow(f.PropertyAccessExpression);switch(e.getName()){case`primaryKey`:t.isPrimary=!0;break;case`notNull`:t.isNullable=!1;break;case`unique`:t.isUnique=!0;break;case`default`:{let e=r.getArguments();e.length>0&&(t.defaultValue=e[0].getText().replace(/['"]/g,``));break}case`defaultNow`:t.defaultValue=`now()`;break;case`generatedAlwaysAsIdentity`:case`autoincrement`:t.isGenerated=!0;break;case`references`:{let e=r.getArguments();if(e.length>0){let n=e[0].getText(),r=Xr.exec(n);if(r&&(t.reference={toEntity:r[1]}),e.length>1){let n=e[1];if(n.getKind()===f.ObjectLiteralExpression){let e=n.asKindOrThrow(f.ObjectLiteralExpression);for(let n of e.getProperties())if(n.getKind()===f.PropertyAssignment){let e=n.asKindOrThrow(f.PropertyAssignment);if(e.getName()===`onDelete`){let n=e.getInitializer()?.getText();n&&(t.reference.onDelete=n.replace(/['"]/g,``))}}}}}break}default:break}n(e.getExpression())}else if(i.getKind()===f.Identifier){let e=i.getText();t.type=e,Yr.has(e)&&(t.isGenerated=!0)}}}return n(e),t}function Qr(e){let t=[];for(let n of e.getProperties()){if(n.getKind()!==f.PropertyAssignment)continue;let e=n.asKindOrThrow(f.PropertyAssignment),r=e.getName(),i=e.getInitializer();if(!i)continue;let a=Zr(i);t.push({name:r,type:a.type,isPrimary:a.isPrimary,isNullable:a.isNullable,isGenerated:a.isGenerated,isUnique:a.isUnique,defaultValue:a.defaultValue})}return t}function $r(e,t){let n=[];for(let r of e.getProperties()){if(r.getKind()!==f.PropertyAssignment)continue;let e=r.asKindOrThrow(f.PropertyAssignment),i=e.getName(),a=e.getInitializer();if(!a)continue;let o=Zr(a);o.reference&&n.push({type:`many-to-one`,fromEntity:t,toEntity:o.reference.toEntity,propertyName:i,isNullable:o.isNullable,...o.reference.onDelete?{onDelete:o.reference.onDelete}:{}})}return n}function ei(e){let t=[],n=e.getDescendantsOfKind(f.CallExpression);for(let e of n){let n=e.getExpression();if(n.getKind()!==f.PropertyAccessExpression||n.asKindOrThrow(f.PropertyAccessExpression).getName()!==`on`)continue;let r=[];for(let t of e.getArguments())if(t.getKind()===f.PropertyAccessExpression){let e=t.asKindOrThrow(f.PropertyAccessExpression);r.push(e.getName())}if(r.length===0)continue;let i=e.getText().includes(`uniqueIndex`);t.push({columns:r,isUnique:i})}return t}function ti(e){let t=[],n=e.getFilePath();for(let r of e.getDescendantsOfKind(f.VariableDeclaration)){let e=r.getInitializer();if(!e||e.getKind()!==f.CallExpression)continue;let i=e.asKindOrThrow(f.CallExpression),a=i.getExpression();if(a.getKind()!==f.Identifier)continue;let o=a.getText();if(!Jr.has(o))continue;let s=i.getArguments();if(s.length<2)continue;let c=s[0],l=r.getName();c.getKind()===f.StringLiteral&&(l=c.asKindOrThrow(f.StringLiteral).getLiteralValue());let u=s[1];if(u.getKind()!==f.ObjectLiteralExpression)continue;let d=u.asKindOrThrow(f.ObjectLiteralExpression),p=r.getName(),m=Qr(d),h=$r(d,p),g;if(s.length>=3&&(g=ei(s[2]),g))for(let e of g)for(let t of e.columns){let e=m.find(e=>e.name===t);e&&(e.hasIndex=!0)}t.push({name:p,tableName:l,filePath:n,columns:m,relations:h,indexes:g})}return t}const ni={supportsIncrementalUpdate:!0,extract(e,t){let n=[];for(let r of t){let t=e.getSourceFile(r);t&&n.push(...ti(t))}return n}},ri=/^model\s+(\w+)\s*\{/,ii=/^enum\s+(\w+)\s*\{/,ai=/^(\w+)\s+(\w+)(\?)?(\[\])?(.*)$/,oi=/@(\w+)(\((?:[^()]*|\([^()]*\))*\))?/g,si=/@default\(((?:[^()]*|\([^()]*\))*)\)/,ci=/^@@map\(\s*"([^"]+)"\s*\)/;function li(r){let i=a(r,`prisma`,`schema.prisma`);if(e(i)){let e=a(r,`prisma`),t=n(e).filter(e=>e.endsWith(`.prisma`));return t.length>1?t.map(t=>a(e,t)):[i]}let o=a(r,`schema.prisma`);if(e(o))return[o];try{let n=a(r,`package.json`),i=JSON.parse(t(n,`utf-8`)).prisma?.schema;if(i){let t=a(r,i);if(e(t))return[t]}}catch{}return[]}function ui(e){let n=[],r=new Set;for(let i of e){let e;try{e=t(i,`utf-8`)}catch{continue}let a=e.split(`
|
|
5
|
-
`),o=null,s=[],c=[],l=[],u;for(let e of a){let t=e.trim(),a=
|
|
6
|
-
`);for(let e of n){let n={config:r,sourceFile:o,filePath:t,report(t){let n=[],r=Math.max(0,t.line-6),a=Math.min(s.length,t.line+5);for(let e=r;e<a;e++)n.push({line:e+1,text:s[e]});i.push({...t,rule:e.meta.id,category:e.meta.category,scope:`file`,severity:e.meta.severity,sourceLines:n})}};try{e.check(n)}catch(t){a.push({ruleId:e.meta.id,error:t})}}return{diagnostics:i,errors:a}}function
|
|
2
|
+
`),r=!1;for(let e of n){let n=e.trim();if(b.test(n)){let e=n.match(x);if(e){for(let n of e[1].split(`,`)){let e=n.trim().replace(te,``);e&&t.push(e)}return t}r=!0;continue}if(r){if(S.test(e)&&n!==``)break;let r=n.match(ee);r&&t.push(r[1])}}return t}async function re(e){let t=a(e,`nest-cli.json`);try{let e=await c(t,`utf-8`),n=JSON.parse(e);if(!(n.monorepo&&n.projects))return null;let r=new Map;for(let[e,t]of Object.entries(n.projects)){let n=t.root??e;r.set(e,n)}return r.size===0?null:{projects:r}}catch{return null}}function ie(e){let t={...e.dependencies,...e.devDependencies,...e.peerDependencies};return!!(t[`@nestjs/core`]||t[`@nestjs/common`])}async function ae(e,t){let n=await l(t.map(e=>`${e}/package.json`),{cwd:e,absolute:!0,ignore:[`**/node_modules/**`]}),r=new Map;for(let t of n)try{let n=await c(t,`utf-8`),a=JSON.parse(n);if(ie(a)){let n=o(e,i(t)),s=a.name??n;r.set(s,n)}}catch{}return r.size===0?null:{projects:r}}async function oe(e){let t=a(e,`pnpm-workspace.yaml`),n;try{n=await c(t,`utf-8`)}catch{return null}let r=ne(n);return r.length===0?null:ae(e,r)}function se(e){let t=e.workspaces;if(!t)return[];if(Array.isArray(t))return t.filter(e=>typeof e==`string`);if(typeof t==`object`&&t){let e=t;if(Array.isArray(e.packages))return e.packages.filter(e=>typeof e==`string`)}return[]}async function ce(e){let t=a(e,`package.json`),n;try{n=await c(t,`utf-8`)}catch{return null}let r=se(JSON.parse(n));return r.length===0?null:ae(e,r)}async function le(e){let t=a(e,`lerna.json`),n;try{n=await c(t,`utf-8`)}catch{return null}let r=JSON.parse(n);if(r.useWorkspaces)return null;let i=r.packages??[`packages/*`];return i.length===0?null:ae(e,i)}async function ue(e){let t=a(e,`nx.json`);try{await c(t,`utf-8`)}catch{return null}let n=await l([`**/project.json`],{cwd:e,absolute:!0,ignore:[`node_modules/**`]}),r=new Map;for(let t of n){let n=i(t),s=o(e,n);if(s===``)continue;let l=a(n,`package.json`);try{let e=await c(l,`utf-8`),t=JSON.parse(e);if(ie(t)){let e=t.name??s;r.set(e,s)}}catch{}}return r.size===0?null:{projects:r}}async function de(e){try{return await c(a(e,`pnpm-workspace.yaml`),`utf-8`),!0}catch{return!1}}async function fe(e){let t=await re(e);if(t)return t;let n=await oe(e);if(n)return n;if(!await de(e)){let t=await ce(e);if(t)return t}return await ue(e)||le(e)}async function pe(e){let t=a(e,`package.json`),n={};try{let e=await c(t,`utf-8`);n=JSON.parse(e)}catch{}let r={...n.dependencies,...n.devDependencies},i=me(r[`@nestjs/core`]),o=he(r),s=ge(r);return{name:n.name??`unknown`,nestVersion:i,orm:o,framework:s,moduleCount:0,fileCount:0}}function me(e){return e?e.replace(/[\^~>=<]/g,``):null}function he(e){return e[`@prisma/client`]?`prisma`:e.typeorm?`typeorm`:e[`@mikro-orm/core`]?`mikro-orm`:e.sequelize?`sequelize`:e.mongoose?`mongoose`:e[`drizzle-orm`]?`drizzle`:null}function ge(e){return e[`@nestjs/platform-fastify`]?`fastify`:e[`@nestjs/platform-express`]||e[`@nestjs/core`]?`express`:null}const C={include:[`**/*.ts`],exclude:`**/node_modules/**,**/dist/**,**/build/**,**/coverage/**,**/*.spec.ts,**/*.test.ts,**/*.e2e-spec.ts,**/*.e2e-test.ts,**/*.d.ts,**/test/**,**/tests/**,**/__tests__/**,**/__mocks__/**,**/__fixtures__/**,**/mock/**,**/mocks/**,**/*.mock.ts,**/seeder/**,**/seeders/**,**/*.seed.ts,**/*.seeder.ts,*.config.ts,*.config.js,*.config.mjs,*.config.cjs,*.config.mts,*.config.cts`.split(`,`)},_e=[`nestjs-doctor.config.json`,`.nestjs-doctor.json`];async function ve(e,t){if(t)return ye(t);for(let t of _e)try{return await ye(a(e,t))}catch{}try{let t=await c(a(e,`package.json`),`utf-8`),n=JSON.parse(t);if(n[`nestjs-doctor`]&&typeof n[`nestjs-doctor`]==`object`)return be(n[`nestjs-doctor`])}catch{}return{...C}}async function ye(e){let t=await c(e,`utf-8`);return be(JSON.parse(t))}function be(e){return{...C,...e,exclude:[...C.exclude??[],...e.exclude??[]]}}async function xe(e,t){try{return await ve(e)}catch{return t}}const Se=[/Repository$/,/\.repository$/,/\.entity$/,/\.schema$/,/\.guard$/,/\.interceptor$/,/\.pipe$/,/\.filter$/,/\.strategy$/],Ce={meta:{id:`architecture/no-barrel-export-internals`,category:`architecture`,severity:`info`,description:`Don't re-export internal implementation details from barrel files`,help:`Only export the module's public API (services, DTOs, interfaces) from index.ts files.`},check(e){if(e.filePath.endsWith(`/index.ts`))for(let t of e.sourceFile.getExportDeclarations()){let n=t.getModuleSpecifierValue();if(n){Se.some(e=>e.test(n))&&e.report({filePath:e.filePath,message:`Barrel file re-exports internal module '${n}'.`,help:this.meta.help,line:t.getStartLineNumber(),column:1});for(let n of t.getNamedExports()){let t=n.getName();(t.endsWith(`Repository`)||t.endsWith(`Entity`)||t.endsWith(`Schema`))&&e.report({filePath:e.filePath,message:`Barrel file re-exports internal type '${t}'.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}}}},w=new Set([`Get`,`Post`,`Put`,`Patch`,`Delete`,`Head`,`Options`,`All`]);function T(e,t){return e.getDecorator(t)!==void 0}function E(e){return T(e,`Controller`)}function we(e){return T(e,`Injectable`)}function Te(e){return T(e,`Injectable`)||T(e,`Controller`)||T(e,`Resolver`)||T(e,`WebSocketGateway`)}function D(e){return T(e,`Module`)}function O(e){return e.getDecorators().some(e=>w.has(e.getName()))}const Ee=new Set([`TsRestHandler`,`GrpcMethod`,`GrpcStreamMethod`]);function De(e){return e.getDecorators().some(e=>Ee.has(e.getName()))}const Oe={meta:{id:`architecture/no-business-logic-in-controllers`,category:`architecture`,severity:`error`,description:`Controllers should only handle HTTP concerns — move business logic to services`,help:`Extract branches, loops, and complex calculations into a service method.`},check(e){for(let t of e.sourceFile.getClasses())if(E(t))for(let n of t.getMethods()){if(!n.getDecorators().some(e=>w.has(e.getName())))continue;let t=n.getBody();if(!t)continue;let r=t.getDescendantsOfKind(f.IfStatement),i=t.getDescendantsOfKind(f.ForStatement),a=t.getDescendantsOfKind(f.ForInStatement),o=t.getDescendantsOfKind(f.ForOfStatement),s=t.getDescendantsOfKind(f.WhileStatement),c=t.getDescendantsOfKind(f.SwitchStatement),l=i.length+a.length+o.length+s.length;(r.length>1||l>0||c.length>0)&&e.report({filePath:e.filePath,message:`Controller method '${n.getName()}' contains business logic (${r.length} if, ${l} loops, ${c.length} switch). Move to a service.`,help:this.meta.help,line:n.getStartLineNumber(),column:1});let u=t.getDescendantsOfKind(f.CallExpression).filter(e=>{let t=e.getExpression();if(t.getKind()===f.PropertyAccessExpression){let e=t.asKind(f.PropertyAccessExpression)?.getName();return e===`map`||e===`filter`||e===`reduce`||e===`sort`||e===`flatMap`}return!1});u.length>1&&e.report({filePath:e.filePath,message:`Controller method '${n.getName()}' contains data transformation logic (${u.length} array operations). Move to a service.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}};function ke(e){let t=new Map;try{let n=p.findConfigFile(e,p.sys.fileExists,`tsconfig.json`);if(!n)return t;let{config:r,error:a}=p.readConfigFile(n,p.sys.readFile);if(a||!r)return t;let o=i(n),c=p.parseJsonConfigFileContent(r,p.sys,o),l=c.options.paths;if(!l)return t;let u=c.options.baseUrl??o;for(let[e,n]of Object.entries(l)){let r=n.map(e=>s(u,e));t.set(e,r)}}catch{return t}return t}function Ae(e,t){for(let[n,r]of t){if(r.length===0)continue;let t=n.indexOf(`*`);if(t===-1){if(e===n)return r[0];continue}let i=n.slice(0,t),a=n.slice(t+1);if(e.startsWith(i)&&e.endsWith(a)&&e.length>=i.length+a.length){let t=e.slice(i.length,e.length-a.length),n=r[0],o=n.indexOf(`*`);return o===-1?n:n.slice(0,o)+t+n.slice(o+1)}}}const je=/\.js$/;function Me(e,t,n){let r=[];for(let i of e.getClasses()){let e=i.getDecorator(`Module`);if(!e)continue;let a=i.getName()??`AnonymousModule`,o=e.getArguments()[0],s={name:a,filePath:t,classDeclaration:i,imports:[],forwardRefImports:new Set,exports:[],providers:[],controllers:[]};if(o&&o.getKind()===f.ObjectLiteralExpression){let e=o.asKind(f.ObjectLiteralExpression);if(e){let t=A(e,`imports`,n);s.imports=t.map(e=>e.name);for(let e of t)e.viaForwardRef&&s.forwardRefImports.add(e.name);s.exports=A(e,`exports`,n).map(e=>e.name),s.providers=A(e,`providers`,n).map(e=>e.name),s.controllers=A(e,`controllers`,n).map(e=>e.name)}}r.push(s)}return r}function Ne(e,t,n=new Map){let r=new Map,i=new Map;for(let i of t){let t=e.getSourceFile(i);if(t)for(let e of Me(t,i,n))r.set(e.name,e)}for(let[e,t]of r){let n=new Set;for(let e of t.imports)r.has(e)&&n.add(e);i.set(e,n)}let a=new Map;for(let e of r.values())for(let t of e.providers)a.set(t,e);return{modules:r,edges:i,providerToModule:a}}const Pe=new Set([`forRoot`,`forRootAsync`,`forFeature`,`forFeatureAsync`,`forChild`,`forChildAsync`,`register`,`registerAsync`]);function k(e){return{name:e,viaForwardRef:!1}}function A(e,t,n){let r=e.getProperty(t);if(!r)return[];let i=r.asKind(f.PropertyAssignment);if(!i)return[];let a=i.getInitializer();return a?j(a,e.getSourceFile(),0,n):[]}function j(e,t,n,r){if(n>5)return[];let i=e.getKind();if(i===f.ArrayLiteralExpression){let i=e.asKindOrThrow(f.ArrayLiteralExpression),a=[];for(let e of i.getElements())a.push(...Fe(e,t,n,r));return a}return i===f.CallExpression?Ie(e.asKindOrThrow(f.CallExpression),t,n,r):i===f.Identifier?ze(e.getText(),t,n+1,r):[]}function Fe(e,t,n,r){let i=e.getKind();return i===f.SpreadElement?j(e.asKindOrThrow(f.SpreadElement).getExpression(),t,n,r):i===f.CallExpression?Ie(e.asKindOrThrow(f.CallExpression),t,n,r):i===f.PropertyAccessExpression?[k(e.asKindOrThrow(f.PropertyAccessExpression).getExpression().getText())]:(f.Identifier,[k(e.getText())])}function Ie(e,t,n,r){let i=e.getExpression();if(i.getKind()===f.Identifier&&i.getText()===`forwardRef`){let i=e.getArguments();if(i.length===0)return[];let a=i[0];if(a.getKind()===f.ArrowFunction){let e=a.asKindOrThrow(f.ArrowFunction).getBody();if(e.getKind()===f.Identifier)return[{name:e.getText(),viaForwardRef:!0}];if(e.getKind()===f.Block){let i=e.asKindOrThrow(f.Block),a=[];for(let e of i.getDescendantsOfKind(f.ReturnStatement)){let i=e.getExpression();if(i)for(let e of j(i,t,n,r))a.push({...e,viaForwardRef:!0})}return a}return j(e,t,n,r).map(e=>({...e,viaForwardRef:!0}))}return[k(a.getText())]}if(i.getKind()===f.PropertyAccessExpression){let a=i.asKindOrThrow(f.PropertyAccessExpression),o=a.getName();if(o===`concat`){let i=j(a.getExpression(),t,n,r),o=[];for(let i of e.getArguments())o.push(...j(i,t,n,r));return[...i,...o]}return Pe.has(o),[k(a.getExpression().getText())]}return i.getKind()===f.Identifier?Ve(i.getText(),t,n+1,r):[]}function Le(e,t,n){if(!e.startsWith(`.`)){let r=Ae(e,n);if(!r)return;let i=t.getProject(),a=[`${r}.ts`,`${r}/index.ts`,r,r.replace(je,`.ts`)];for(let e of a){let t=i.getSourceFile(e);if(t)return t}return}let r=s(i(t.getFilePath()),e),a=t.getProject(),o=[`${r}.ts`,`${r}/index.ts`,r,r.replace(je,`.ts`)];for(let e of o){let t=a.getSourceFile(e);if(t)return t}}function Re(e,t,n){for(let r of t.getImportDeclarations())for(let i of r.getNamedImports())if((i.getAliasNode()?i.getAliasNode().getText():i.getName())===e){let e=Le(r.getModuleSpecifierValue(),t,n);return e?{sourceFile:e,localName:i.getName()}:void 0}for(let r of t.getExportDeclarations())if(r.getModuleSpecifierValue()){for(let i of r.getNamedExports())if((i.getAliasNode()?i.getAliasNode().getText():i.getName())===e){let e=Le(r.getModuleSpecifierValue(),t,n);return e?{sourceFile:e,localName:i.getName()}:void 0}}}function ze(e,t,n,r){if(n>5)return[];for(let i of t.getStatements()){if(i.getKind()!==f.VariableStatement)continue;let a=i.asKindOrThrow(f.VariableStatement);for(let i of a.getDeclarations())if(i.getName()===e){let e=i.getInitializer();if(e)return j(e,t,n,r)}}let i=Re(e,t,r);return i?ze(i.localName,i.sourceFile,n+1,r):[]}function Be(e,t,n,r){for(let i of t.getStatements()){if(i.getKind()!==f.VariableStatement)continue;let a=i.asKindOrThrow(f.VariableStatement);for(let i of a.getDeclarations()){if(i.getName()!==e)continue;let a=i.getInitializer();if(!a||a.getKind()!==f.ArrowFunction)continue;let o=a.asKindOrThrow(f.ArrowFunction).getBody();if(o.getKind()!==f.Block)return j(o,t,n,r);let s=[];for(let e of o.getDescendantsOfKind(f.ReturnStatement)){let i=e.getExpression();i&&s.push(...j(i,t,n,r))}return s}}}function Ve(e,t,n,r){if(n>5)return[];for(let i of t.getStatements()){if(i.getKind()!==f.FunctionDeclaration)continue;let a=i.asKindOrThrow(f.FunctionDeclaration);if(a.getName()!==e)continue;let o=[];for(let e of a.getDescendantsOfKind(f.ReturnStatement)){let i=e.getExpression();i&&o.push(...j(i,t,n,r))}return o}let i=Be(e,t,n,r);if(i)return i;let a=Re(e,t,r);return a?Ve(a.localName,a.sourceFile,n+1,r):[]}function He(e,t,n,r=new Map){for(let[t,r]of e.modules)if(r.filePath===n){e.modules.delete(t),e.edges.delete(t);for(let t of r.providers)e.providerToModule.get(t)===r&&e.providerToModule.delete(t);for(let n of e.edges.values())n.delete(t)}let i=t.getSourceFile(n);if(!i)return;let a=Me(i,n,r);for(let t of a)e.modules.set(t.name,t);for(let t of a){let n=new Set;for(let r of t.imports)e.modules.has(r)&&n.add(r);e.edges.set(t.name,n);for(let n of t.providers)e.providerToModule.set(n,t)}for(let[t,r]of e.modules){if(r.filePath===n)continue;let i=new Set;for(let t of r.imports)e.modules.has(t)&&i.add(t);e.edges.set(t,i)}}function Ue(e){let t=[],n=new Set,r=new Set;function i(a,o){n.add(a),r.add(a);let s=e.edges.get(a)??new Set;for(let e of s)if(!n.has(e))i(e,[...o,e]);else if(r.has(e)){let n=o.indexOf(e);n===-1?t.push([...o,e]):t.push(o.slice(n))}r.delete(a)}for(let t of e.modules.keys())n.has(t)||i(t,[t]);return t}function We(e,t,n,r,i,a){let o=[];for(let i of e.providers){let e=n.get(i);if(e)for(let n of e.dependencies){let e=r.get(n);e&&e.name===t.name&&o.push({consumer:i,dependency:n})}}for(let n of e.controllers)for(let e of a){let a=i.getSourceFile(e);if(a)for(let e of a.getClasses()){if(e.getName()!==n)continue;let i=e.getConstructors()[0];if(i)for(let e of i.getParameters()){let i=e.getTypeNode(),a=i?i.getText():e.getType().getText(),s=a.split(`.`).pop()?.split(`<`)[0]??a,c=r.get(s);c&&c.name===t.name&&o.push({consumer:n,dependency:s})}}}return o}const Ge=`Break the cycle by extracting the coupling providers into a shared module. forwardRef() only defers resolution at runtime — it does not address the architectural coupling. Use forwardRef() as a last resort and enable rules.architecture/no-circular-module-deps.options.ignoreForwardRefCycles to opt out of these reports.`;function Ke(e){let t=e.config.rules?.[`architecture/no-circular-module-deps`];return typeof t!=`object`||!t?!1:t.options?.ignoreForwardRefCycles===!0}function qe(e,t){let n=e.length>1&&e[0]===e.at(-1)?e.slice(0,-1):e;for(let e=0;e<n.length;e++){let r=n[e],i=n[(e+1)%n.length],a=t.modules.get(r),o=t.modules.get(i);if(!(a&&o)||!a.forwardRefImports.has(i))return!1}return!0}function Je(e,t){let{moduleGraph:n,providers:r,project:i,files:a}=t,o=[],s;for(let t=0;t<e.length;t++){let c=e[t],l=e[(t+1)%e.length],u=n.modules.get(c),d=n.modules.get(l);if(!(u&&d))continue;let f=We(u,d,r,n.providerToModule,i,a);if(f.length===0)continue;let p=new Map;for(let e of f){let t=p.get(e.consumer);t?t.push(e.dependency):p.set(e.consumer,[e.dependency])}let m=[];for(let[e,t]of p){let n=t.map(e=>`${e} (from ${l})`).join(`, `);m.push(`${e} (in ${c}) injects ${n}`)}let h=`${c} -> ${l}: ${m.join(`; `)}`;o.push(h),(!s||f.length<s.count)&&(s={description:`${c} -> ${l}`,count:f.length})}if(o.length===0)return Ge;let c=o.join(`
|
|
3
|
+
`);if(s){let e=s.count===1?`dependency`:`dependencies`,t=s.description.split(` -> `)[0],o=s.description.split(` -> `)[1],l=n.modules.get(t),u=n.modules.get(o);if(l&&u){let t=We(l,u,r,n.providerToModule,i,a),o=[...new Set(t.map(e=>e.dependency))].join(`, `);c+=`\nConsider extracting ${o} into a shared module — it would break the ${s.description} edge (${s.count} ${e}).`}}return c}const Ye={meta:{id:`architecture/no-circular-module-deps`,category:`architecture`,severity:`error`,description:`Module import graph must not contain circular dependencies`,help:Ge,scope:`project`},check(e){let t=Ue(e.moduleGraph),n=Ke(e);for(let r of t){if(n&&qe(r,e.moduleGraph))continue;let t=r.join(` -> `),i=e.moduleGraph.modules.get(r[0]),a=Je(r,e);e.report({filePath:i?.filePath??`unknown`,message:`Circular module dependency detected: ${t}`,help:a,line:i?.classDeclaration.getStartLineNumber()??1,column:1})}}},Xe=[`Service`,`Repository`,`Gateway`,`Resolver`],Ze=[`Guard`,`Interceptor`,`Pipe`,`Filter`];function Qe(e){return typeof e==`object`&&!!e}function $e(e){if(!Qe(e))return new Set;let t=e.excludeClasses;if(Array.isArray(t))return new Set(t.filter(e=>typeof e==`string`));let n=e.options;if(!Qe(n))return new Set;let r=n.excludeClasses;return Array.isArray(r)?new Set(r.filter(e=>typeof e==`string`)):new Set}const et={meta:{id:`architecture/no-manual-instantiation`,category:`architecture`,severity:`error`,description:`Do not manually instantiate @Injectable classes — use NestJS dependency injection`,help:`Register the class as a provider in a module and inject it via the constructor.`},check(e){let t=$e(e.config?.rules?.[this.meta.id]),n=e.sourceFile.getDescendantsOfKind(f.NewExpression);for(let r of n){let n=r.getExpression().getText(),i=n.split(`.`).pop()??n;if(t.has(n)||t.has(i))continue;let a=Xe.some(e=>n.endsWith(e)),o=Ze.some(e=>n.endsWith(e));if(a||o){if(o){if(r.getFirstAncestorByKind(f.Decorator))continue;let e=r.getFirstAncestorByKind(f.MethodDeclaration),t=r.getFirstAncestorByKind(f.Constructor);if(!(e||t))continue}e.report({filePath:e.filePath,message:`Manual instantiation of '${n}' detected. Use dependency injection instead.`,help:this.meta.help,line:r.getStartLineNumber(),column:1})}}}},tt=/\.(\w+)$/,nt=/^(\w+)</,rt=new Set([`PrismaService`,`PrismaClient`,`EntityManager`,`DataSource`,`Repository`,`Connection`,`MongooseModel`,`InjectModel`,`InjectRepository`,`MikroORM`,`DrizzleService`]),it={meta:{id:`architecture/no-orm-in-controllers`,category:`architecture`,severity:`error`,description:`Controllers must not inject ORM services directly — use a service layer`,help:`Inject a service that wraps the ORM instead of using the ORM directly in controllers.`},check(e){for(let t of e.sourceFile.getClasses()){if(!E(t))continue;let n=t.getConstructors()[0];if(n){for(let t of n.getParameters()){let n=at(t.getType().getText());if(rt.has(n)){let r=t.getNameNode();e.report({filePath:e.filePath,message:`Controller injects ORM type '${n}' directly. Use a service layer.`,help:this.meta.help,line:r.getStartLineNumber(),column:r.getStartLinePos()+1})}}for(let n of t.getConstructors()[0]?.getParameters()??[])for(let t of n.getDecorators()){let n=t.getName();(n===`InjectRepository`||n===`InjectModel`)&&e.report({filePath:e.filePath,message:`Controller uses @${n}() decorator. Move data access to a service.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}}}};function at(e){let t=e.match(tt);if(t)return t[1];let n=e.match(nt);return n?n[1]:e}const ot=/\.(\w+)$/,st=/^(\w+)</,ct=new Set([`PrismaService`,`PrismaClient`,`EntityManager`,`DataSource`,`Connection`,`MikroORM`]),lt={meta:{id:`architecture/no-orm-in-services`,category:`architecture`,severity:`info`,description:`Services should use repository abstractions instead of ORM directly`,help:`Create a repository class that wraps ORM calls and inject that instead. Note: If your project follows the official NestJS Prisma recipe (injecting PrismaService directly), you can disable this rule.`},check(e){for(let t of e.sourceFile.getClasses()){if(!we(t))continue;let n=t.getName()??``;if(n.endsWith(`Repository`)||n.endsWith(`Repo`))continue;let r=t.getConstructors()[0];if(r)for(let t of r.getParameters()){let n=ut(t.getType().getText());if(ct.has(n)){let r=t.getNameNode();e.report({filePath:e.filePath,message:`Service injects ORM type '${n}' directly. Consider using a repository abstraction.`,help:this.meta.help,line:r.getStartLineNumber(),column:r.getStartLinePos()+1})}for(let n of t.getDecorators()){let t=n.getName();(t===`InjectRepository`||t===`InjectModel`)&&e.report({filePath:e.filePath,message:`Service uses @${t}() directly. Consider wrapping in a repository class.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}}}};function ut(e){let t=e.match(ot);if(t)return t[1];let n=e.match(st);return n?n[1]:e}const dt=/\.(\w+)$/,ft=/^(\w+)</,pt=[/Repository$/,/Repo$/],mt={meta:{id:`architecture/no-repository-in-controllers`,category:`architecture`,severity:`error`,description:`Controllers must not inject repositories directly — use the service layer`,help:`Move database access to a service and inject the service into the controller instead.`},check(e){for(let t of e.sourceFile.getClasses()){if(!E(t))continue;let n=t.getConstructors()[0];if(n){for(let t of n.getParameters()){let n=ht(t.getType().getText());if(pt.some(e=>e.test(n))){let r=t.getNameNode();e.report({filePath:e.filePath,message:`Controller injects repository '${n}' directly. Use a service layer instead.`,help:this.meta.help,line:r.getStartLineNumber(),column:r.getStartLinePos()+1})}}for(let t of e.sourceFile.getImportDeclarations()){let n=t.getModuleSpecifierValue();(n.includes(`/repositories/`)||n.includes(`/repositories`))&&e.report({filePath:e.filePath,message:`Controller imports from repository path '${n}'.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}}}};function ht(e){let t=e.match(dt);if(t)return t[1];let n=e.match(ft);return n?n[1]:e}const gt={meta:{id:`architecture/no-service-locator`,category:`architecture`,severity:`warning`,description:`Avoid using ModuleRef.get() or ModuleRef.resolve() — prefer explicit constructor injection`,help:`Replace ModuleRef.get()/resolve() with constructor injection for explicit, testable dependencies.`},check(e){let t=e.sourceFile.getDescendantsOfKind(f.CallExpression);for(let n of t){let t=n.getExpression();if(t.getKind()!==f.PropertyAccessExpression)continue;let r=t.asKind(f.PropertyAccessExpression);if(!r)continue;let i=r.getName();if(i!==`get`&&i!==`resolve`)continue;let a=r.getExpression().getText();(a===`moduleRef`||a===`this.moduleRef`)&&e.report({filePath:e.filePath,message:`Service locator pattern: '${a}.${i}()' hides dependencies. Use constructor injection instead.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},_t={meta:{id:`architecture/prefer-constructor-injection`,category:`architecture`,severity:`warning`,description:`Prefer constructor injection over @Inject() property injection`,help:`Move the dependency to a constructor parameter instead of using property injection.`},check(e){for(let t of e.sourceFile.getClasses())if(Te(t))for(let n of t.getProperties())n.getDecorator(`Inject`)&&e.report({filePath:e.filePath,message:`Property '${n.getName()}' uses @Inject() decorator. Prefer constructor injection.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}},vt=[`/repositories/`,`/entities/`,`/dto/`,`/guards/`,`/interceptors/`,`/pipes/`,`/strategies/`],yt={meta:{id:`architecture/require-module-boundaries`,category:`architecture`,severity:`info`,description:`Avoid deep imports into other feature modules' internals`,help:`Import from the module's public API (barrel export) instead of reaching into its internals.`},check(e){for(let t of e.sourceFile.getImportDeclarations()){let n=t.getModuleSpecifierValue();n.startsWith(`.`)&&n.includes(`../`)&&vt.some(e=>n.includes(e))&&e.report({filePath:e.filePath,message:`Import '${n}' reaches into another module's internals.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}},bt={meta:{id:`correctness/factory-inject-matches-params`,category:`correctness`,severity:`error`,description:`useFactory inject array length must match the factory function parameter count`,help:`Ensure the 'inject' array has one entry per factory function parameter.`},check(e){for(let t of e.sourceFile.getClasses()){if(!D(t))continue;let n=t.getDecorator(`Module`);if(!n)continue;let r=n.getArguments()[0];if(!r||r.getKind()!==f.ObjectLiteralExpression)continue;let i=r.asKind(f.ObjectLiteralExpression);if(!i)continue;let a=i.getProperty(`providers`);if(!a)continue;let o=a.getChildrenOfKind(f.ArrayLiteralExpression)[0];if(o)for(let t of o.getElements()){if(t.getKind()!==f.ObjectLiteralExpression)continue;let n=t.asKind(f.ObjectLiteralExpression);if(!n)continue;let r=n.getProperty(`useFactory`),i=n.getProperty(`inject`);if(!(r&&i))continue;let a=i.getChildrenOfKind(f.ArrayLiteralExpression)[0];if(!a)continue;let o=a.getElements().length,s,c=r.asKind(f.MethodDeclaration);if(c)s=c.getParameters().length;else{let e=r.asKind(f.PropertyAssignment);if(!e)continue;let t=e.getInitializer();if(!t)continue;t.getKind()===f.ArrowFunction?s=t.asKind(f.ArrowFunction)?.getParameters().length:t.getKind()===f.FunctionExpression&&(s=t.asKind(f.FunctionExpression)?.getParameters().length)}s!==void 0&&o!==s&&e.report({filePath:e.filePath,message:`Factory has ${s} parameter(s) but inject array has ${o} element(s).`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}}},xt=[`Guard`,`Interceptor`,`Filter`,`Pipe`,`Middleware`,`Strategy`,`Subscriber`,`Listener`,`Processor`,`Consumer`,`Worker`,`Scheduler`,`Cron`,`HealthIndicator`],St={meta:{id:`correctness/injectable-must-be-provided`,category:`correctness`,severity:`info`,description:`@Injectable() classes should be registered in at least one module's providers array`,help:`Add this class to a module's providers array, or remove the @Injectable() decorator if unused.`,scope:`project`},check(e){let t=new Set;for(let n of e.moduleGraph.modules.values()){for(let e of n.providers)t.add(e);for(let e of n.controllers)t.add(e)}for(let n of e.files){let r=e.project.getSourceFile(n);if(r)for(let e of r.getClasses()){if(!D(e))continue;let n=e.getDecorator(`Module`);if(!n)continue;let r=n.getArguments()[0];if(!r||r.getKind()!==f.ObjectLiteralExpression)continue;let i=r.asKind(f.ObjectLiteralExpression);if(!i)continue;let a=i.getProperty(`providers`);if(!a)continue;let o=a.getChildrenOfKind(f.ArrayLiteralExpression)[0];if(o)for(let e of o.getElements()){if(e.getKind()!==f.ObjectLiteralExpression)continue;let n=e.asKind(f.ObjectLiteralExpression);if(n)for(let e of[`useClass`,`useExisting`]){let r=n.getProperty(e);if(!r)continue;let i=r.asKind(f.PropertyAssignment);if(!i)continue;let a=i.getInitializer();a&&t.add(a.getText())}}}}for(let n of e.files){if(n.includes(`.spec.`)||n.includes(`.test.`)||n.includes(`__test__`)||n.includes(`__tests__`))continue;let r=e.project.getSourceFile(n);if(r)for(let i of r.getClasses()){if(!i.getDecorator(`Injectable`))continue;let r=i.getName();r&&(xt.some(e=>r.endsWith(e))||t.has(r)||e.report({filePath:n,message:`@Injectable() class '${r}' is not registered in any module's providers array.`,help:this.meta.help,line:i.getStartLineNumber(),column:1}))}}}};function Ct(e){return e.getDescendantsOfKind(f.ReturnStatement).some(e=>{let t=e.getExpression();return!t||t.getKind()!==f.NewExpression?!1:t.asKindOrThrow(f.NewExpression).getExpression().getText()===`Promise`})}const wt={meta:{id:`correctness/no-async-without-await`,category:`correctness`,severity:`warning`,description:`Async functions/methods should contain at least one await expression`,help:`Either add an await expression or remove the async keyword. HTTP handlers with route decorators are exempted, as async is conventional for controller methods.`},check(e){for(let t of e.sourceFile.getClasses())for(let n of t.getMethods()){if(!n.isAsync()||E(t)&&O(n)||De(n))continue;let r=n.getBody();if(r&&r.getDescendantsOfKind(f.AwaitExpression).filter(e=>{let t=e.getParent();for(;t&&t!==r;){if(t.getKind()===f.ArrowFunction||t.getKind()===f.FunctionExpression||t.getKind()===f.FunctionDeclaration)return!1;t=t.getParent()}return!0}).length===0){let t=n.getName();Ct(r)?e.report({filePath:e.filePath,message:`Async method '${t}()' returns a Promise directly — remove the async keyword.`,help:`The async keyword is unnecessary when you are already constructing a Promise manually. Remove async to avoid double-wrapping.`,line:n.getStartLineNumber(),column:1}):e.report({filePath:e.filePath,message:`Async method '${t}()' has no await expression.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}for(let t of e.sourceFile.getFunctions()){if(!t.isAsync())continue;let n=t.getBody();if(n&&n.getDescendantsOfKind(f.AwaitExpression).filter(e=>{let t=e.getParent();for(;t&&t!==n;){if(t.getKind()===f.ArrowFunction||t.getKind()===f.FunctionExpression||t.getKind()===f.FunctionDeclaration)return!1;t=t.getParent()}return!0}).length===0){let r=t.getName()??`anonymous`;Ct(n)?e.report({filePath:e.filePath,message:`Async function '${r}()' returns a Promise directly — remove the async keyword.`,help:`The async keyword is unnecessary when you are already constructing a Promise manually. Remove async to avoid double-wrapping.`,line:t.getStartLineNumber(),column:1}):e.report({filePath:e.filePath,message:`Async function '${r}()' has no await expression.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}}},Tt=new Set([`ApiResponse`,`ApiQuery`,`ApiParam`,`ApiHeader`,`ApiSecurity`,`SetMetadata`,`Roles`,`Header`,`Throttle`]),Et={meta:{id:`correctness/no-duplicate-decorators`,category:`correctness`,severity:`warning`,description:`Same decorator should not appear twice on a single target`,help:`Remove the duplicate decorator — it was likely copy-pasted by mistake.`},check(e){for(let t of e.sourceFile.getClasses()){M(t.getDecorators(),e,this.meta.help);for(let n of t.getMethods())M(n.getDecorators(),e,this.meta.help);for(let n of t.getProperties())M(n.getDecorators(),e,this.meta.help);for(let n of t.getConstructors())for(let t of n.getParameters())M(t.getDecorators(),e,this.meta.help)}}};function M(e,t,n){let r=new Set;for(let i of e){let e=i.getName();Tt.has(e)||(r.has(e)?t.report({filePath:t.filePath,message:`Duplicate @${e}() decorator on the same target.`,help:n,line:i.getStartLineNumber(),column:1}):r.add(e))}}const Dt=[`providers`,`controllers`,`imports`,`exports`],Ot={meta:{id:`correctness/no-duplicate-module-metadata`,category:`correctness`,severity:`warning`,description:`Same identifier should not appear twice in a module metadata array`,help:`Remove the duplicate entry from the module metadata.`},check(e){for(let t of e.sourceFile.getClasses()){if(!D(t))continue;let n=t.getDecorator(`Module`);if(!n)continue;let r=n.getArguments()[0];if(!r||r.getKind()!==f.ObjectLiteralExpression)continue;let i=r.asKind(f.ObjectLiteralExpression);if(i)for(let t of Dt){let n=i.getProperty(t);if(!n)continue;let r=n.getChildrenOfKind(f.ArrayLiteralExpression)[0];if(!r)continue;let a=new Set;for(let n of r.getElements()){let r=n.getText();a.has(r)?e.report({filePath:e.filePath,message:`Duplicate '${r}' in @Module() ${t} array.`,help:this.meta.help,line:n.getStartLineNumber(),column:1}):a.add(r)}}}}},kt={meta:{id:`correctness/no-duplicate-routes`,category:`correctness`,severity:`error`,description:`Same HTTP method + route path + version should not appear twice in a single controller`,help:`Remove or rename one of the duplicate route handlers.`},check(e){for(let t of e.sourceFile.getClasses()){if(!E(t))continue;let n=new Map;for(let r of t.getMethods())for(let t of r.getDecorators()){let i=t.getName();if(!w.has(i))continue;let a=t.getArguments(),o=a.length>0?a[0].getText():`""`,s=r.getDecorator(`Version`),c=`${i}:${o}:${s?s.getArguments()[0]?.getText()??``:``}`,l=n.get(c);l?e.report({filePath:e.filePath,message:`Duplicate route: @${i}(${o}) is already defined in '${l}()'.`,help:this.meta.help,line:r.getStartLineNumber(),column:1}):n.set(c,r.getName())}}}},At={meta:{id:`correctness/no-empty-handlers`,category:`correctness`,severity:`info`,description:`Controller HTTP handlers should not have empty bodies`,help:`Add implementation to the handler method or remove it if unnecessary.`},check(e){for(let t of e.sourceFile.getClasses())if(E(t))for(let n of t.getMethods()){if(!n.getDecorators().some(e=>w.has(e.getName())))continue;let t=n.getBody();if(!t)continue;let r=t.asKind(f.Block);r&&r.getStatements().length===0&&e.report({filePath:e.filePath,message:`Handler '${n.getName()}()' has an empty body.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}};function jt(e){let t=e.getReturnType().getText();return t.startsWith(`Promise<`)||t===`Promise`?!0:t===`any`||t===`error`?`unknown`:!1}const Mt=new Set([`save`,`create`,`insert`,`update`,`delete`,`remove`,`send`,`emit`,`publish`,`dispatch`,`execute`,`fetch`,`load`,`upload`,`download`,`process`]),Nt={meta:{id:`correctness/no-fire-and-forget-async`,category:`correctness`,severity:`warning`,description:`Calling async functions without await leads to unhandled promise rejections`,help:`Add await before the async call, or use void with explicit error handling if fire-and-forget is intentional.`},check(e){for(let t of e.sourceFile.getClasses())for(let n of t.getMethods()){if(O(n))continue;let t=n.getBody();if(!t)continue;let r=t.getDescendantsOfKind(f.ExpressionStatement);for(let t of r){let r=t.getExpression();if(r.getKind()===f.VoidExpression||r.getKind()===f.AwaitExpression||r.getKind()!==f.CallExpression)continue;let i=r.asKind(f.CallExpression);if(!i)continue;let a=i.getExpression().getText().split(`.`).pop()??``,o=jt(i);if(o!==!1){if(o===`unknown`){let e=a.toLowerCase();if(!(Mt.has(e)||[...Mt].some(t=>e.startsWith(t)&&e!==t)))continue}t.getFirstAncestorByKind(f.MethodDeclaration)===n&&e.report({filePath:e.filePath,message:`Async call '${a}()' is not awaited — unhandled rejections will crash the process.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}}}},Pt={meta:{id:`correctness/no-missing-filter-catch`,category:`correctness`,severity:`error`,description:`Exception filter classes decorated with @Catch() must implement the catch() method`,help:`Add a catch(exception, host: ArgumentsHost) method to the filter class.`},check(e){for(let t of e.sourceFile.getClasses())T(t,`Catch`)&&(t.getExtends()||t.getMethods().some(e=>e.getName()===`catch`)||e.report({filePath:e.filePath,message:`Exception filter '${t.getName()}' has @Catch() but is missing the 'catch()' method.`,help:this.meta.help,line:t.getStartLineNumber(),column:1}))}},Ft={meta:{id:`correctness/no-missing-guard-method`,category:`correctness`,severity:`error`,description:`Guard classes must implement the canActivate() method`,help:`Add a canActivate(context: ExecutionContext) method to the guard class. Note: This rule identifies guards by the 'Guard' class name suffix.`},check(e){for(let t of e.sourceFile.getClasses()){let n=t.getName()??``;n.endsWith(`Guard`)&&T(t,`Injectable`)&&(t.getExtends()||t.getMethods().some(e=>e.getName()===`canActivate`)||e.report({filePath:e.filePath,message:`Guard '${n}' is missing the 'canActivate()' method.`,help:this.meta.help,line:t.getStartLineNumber(),column:1}))}}},It={meta:{id:`correctness/no-missing-injectable`,category:`correctness`,severity:`error`,description:`Provider classes with constructor dependencies must have the @Injectable() decorator`,help:`Add @Injectable() to providers that inject constructor dependencies.`,scope:`project`},check(e){let t=new Set([...e.providers.values()].map(e=>e.name)),n=new Map;for(let t of e.files){let r=e.project.getSourceFile(t);if(r)for(let e of r.getClasses()){let r=e.getName();if(r){let i=n.get(r)??[];i.push({cls:e,filePath:t}),n.set(r,i)}}}for(let r of e.moduleGraph.modules.values())for(let i of r.providers){if(t.has(i))continue;let a=n.get(i);if(a)for(let{cls:t,filePath:n}of a){let a=(t.getConstructors()[0]?.getParameters().length??0)>0;!(t.getDecorator(`Injectable`)||t.getDecorator(`Resolver`)||t.getDecorator(`WebSocketGateway`))&&a&&e.report({filePath:n,message:`Class '${i}' is listed in '${r.name}' providers but is missing @Injectable() decorator.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}}},Lt={meta:{id:`correctness/no-missing-interceptor-method`,category:`correctness`,severity:`error`,description:`Interceptor classes must implement the intercept() method`,help:`Add an intercept(context: ExecutionContext, next: CallHandler) method to the interceptor class.`},check(e){for(let t of e.sourceFile.getClasses()){let n=t.getName()??``;n.endsWith(`Interceptor`)&&T(t,`Injectable`)&&(t.getExtends()||t.getMethods().some(e=>e.getName()===`intercept`)||e.report({filePath:e.filePath,message:`Interceptor '${n}' is missing the 'intercept()' method.`,help:this.meta.help,line:t.getStartLineNumber(),column:1}))}}},Rt={meta:{id:`correctness/no-missing-module-decorator`,category:`correctness`,severity:`warning`,description:`Classes named *Module should have a @Module() decorator`,help:`Add @Module({}) decorator to the class, or rename it if it is not a NestJS module.`},check(e){for(let t of e.sourceFile.getClasses()){let n=t.getName()??``;n.endsWith(`Module`)&&(n===`Module`||n===`DynamicModule`||T(t,`Module`)||e.report({filePath:e.filePath,message:`Class '${n}' is named like a module but is missing the @Module() decorator.`,help:this.meta.help,line:t.getStartLineNumber(),column:1}))}}},zt={meta:{id:`correctness/no-missing-pipe-method`,category:`correctness`,severity:`error`,description:`Pipe classes must implement the transform() method`,help:`Add a transform(value: any, metadata: ArgumentMetadata) method to the pipe class.`},check(e){for(let t of e.sourceFile.getClasses()){let n=t.getName()??``;n.endsWith(`Pipe`)&&T(t,`Injectable`)&&(t.getExtends()||t.getMethods().some(e=>e.getName()===`transform`)||e.report({filePath:e.filePath,message:`Pipe '${n}' is missing the 'transform()' method.`,help:this.meta.help,line:t.getStartLineNumber(),column:1}))}}},Bt=/:(\w+)/g,Vt={meta:{id:`correctness/param-decorator-matches-route`,category:`correctness`,severity:`error`,description:`@Param() decorator name must match a :param in the route path`,help:`Ensure the @Param('name') argument matches a ':name' segment in the route path (including controller prefix).`},check(e){for(let t of e.sourceFile.getClasses()){if(!E(t))continue;let n=t.getDecorator(`Controller`),r=``;if(n){let e=n.getArguments();if(e.length>0){let t=e[0];if(t.getKind()===f.ObjectLiteralExpression){let e=t.asKind(f.ObjectLiteralExpression);if(e){let t=e.getProperty(`path`);if(t){let e=t.asKind(f.PropertyAssignment);if(e){let t=e.getInitializer();t&&(r=t.getText().replace(/^['"`]|['"`]$/g,``))}}}}else r=t.getText().replace(/^['"`]|['"`]$/g,``)}}let i=new Set;for(let e of r.matchAll(Bt))i.add(e[1]);for(let n of t.getMethods()){let t=``,r=!1;for(let e of n.getDecorators())if(w.has(e.getName())){r=!0;let n=e.getArguments();n.length>0&&(t=n[0].getText().replace(/^['"`]|['"`]$/g,``));break}if(!r)continue;let a=new Set;for(let e of t.matchAll(Bt))a.add(e[1]);let o=new Set([...i,...a]);for(let t of n.getParameters())for(let n of t.getDecorators()){if(n.getName()!==`Param`)continue;let t=n.getArguments();if(t.length===0)continue;let r=t[0].getText().replace(/^['"`]|['"`]$/g,``);o.has(r)||e.report({filePath:e.filePath,message:`@Param('${r}') does not match any route parameter. Available: ${o.size>0?[...o].map(e=>`:${e}`).join(`, `):`(none)`}.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}}}},Ht={meta:{id:`correctness/prefer-readonly-injection`,category:`correctness`,severity:`warning`,description:`Constructor DI parameters should be readonly to prevent accidental reassignment`,help:`Add the 'readonly' modifier to the constructor parameter.`},check(e){for(let t of e.sourceFile.getClasses()){if(!(we(t)||E(t)))continue;let n=t.getConstructors()[0];if(n){for(let t of n.getParameters())if((t.hasModifier(`private`)||t.hasModifier(`protected`)||t.hasModifier(`public`))&&!t.isReadonly()){let n=t.getNameNode();e.report({filePath:e.filePath,message:`Constructor parameter '${t.getName()}' should be readonly.`,help:this.meta.help,line:n.getStartLineNumber(),column:n.getStartLinePos()+1})}}}}},Ut={meta:{id:`correctness/require-inject-decorator`,category:`correctness`,severity:`error`,description:`Constructor parameters without type annotations must have @Inject() decorator for NestJS DI to resolve them`,help:`Add a type annotation or @Inject() decorator to the constructor parameter.`},check(e){for(let t of e.sourceFile.getClasses()){if(!Te(t))continue;let n=t.getConstructors()[0];if(n)for(let r of n.getParameters()){let n=r.getTypeNode(),i=r.getDecorators().some(e=>e.getName()===`Inject`);n||i||e.report({filePath:e.filePath,message:`Constructor parameter '${r.getName()}' in '${t.getName()}' has no type annotation and no @Inject() decorator — NestJS cannot resolve it.`,help:this.meta.help,line:r.getStartLineNumber(),column:1})}}}},Wt={onModuleInit:`OnModuleInit`,onModuleDestroy:`OnModuleDestroy`,onApplicationBootstrap:`OnApplicationBootstrap`,onApplicationShutdown:`OnApplicationShutdown`,beforeApplicationShutdown:`BeforeApplicationShutdown`},Gt={meta:{id:`correctness/require-lifecycle-interface`,category:`correctness`,severity:`warning`,description:`Classes with lifecycle methods should implement the corresponding NestJS interface`,help:`Add 'implements OnModuleInit' (or the appropriate interface) to the class declaration.`},check(e){for(let t of e.sourceFile.getClasses()){let n=t.getImplements().map(e=>e.getText());for(let r of t.getMethods()){let i=r.getName(),a=Wt[i];a&&(n.some(e=>e===a||e.startsWith(`${a}<`))||e.report({filePath:e.filePath,message:`Class '${t.getName()}' has '${i}()' but does not implement '${a}'.`,help:this.meta.help,line:r.getStartLineNumber(),column:1}))}}}},Kt=/each\s*:\s*true/,qt={meta:{id:`correctness/validate-nested-array-each`,category:`correctness`,severity:`warning`,description:`@ValidateNested() on array-typed properties must use { each: true }`,help:`Change @ValidateNested() to @ValidateNested({ each: true }) for array properties.`},check(e){for(let t of e.sourceFile.getClasses())for(let n of t.getProperties()){let t=n.getDecorators(),r=t.find(e=>e.getName()===`ValidateNested`);if(!r)continue;let i=Jt(n),a=t.some(e=>e.getName()===`IsArray`);(i||a)&&(Yt(r)||e.report({filePath:e.filePath,message:`Property '${n.getName()}' is an array with @ValidateNested() but missing { each: true }.`,help:this.meta.help,line:r.getStartLineNumber(),column:1}))}}};function Jt(e){let t=e.getTypeNode();if(!t)return!1;let n=t.getText().replace(/\s/g,``);return!!(n.endsWith(`[]`)||n.startsWith(`Array<`))}function Yt(e){let t=e.getArguments();if(t.length===0)return!1;let n=t[0];if(n.getKind()!==f.ObjectLiteralExpression)return!1;let r=n.getText();return Kt.test(r)}const Xt=new Set(`ValidateNested.IsString.IsNumber.IsBoolean.IsEmail.IsArray.IsEnum.IsNotEmpty.IsDefined.IsOptional.IsDate.IsObject.IsInt.IsPositive.IsNegative.IsUUID.IsUrl.IsISO8601.Matches.Min.Max.MinLength.MaxLength.ArrayMinSize.ArrayMaxSize.ArrayNotEmpty.IsIn.IsNotIn.Length.Contains.IsAlpha.IsAlphanumeric.IsDecimal.IsHexColor.IsJSON.IsPhoneNumber.IsIP.IsCreditCard.IsDateString.IsMilitaryTime.IsMongoId.IsPort.IsSemVer.IsStrongPassword`.split(`.`)),Zt=new Set([`string`,`number`,`boolean`,`Date`,`any`,`unknown`,`bigint`,`symbol`,`undefined`,`null`,`void`,`never`]),Qt=/\s/g,$t=/\[\]$/,en=/^Array<(.+)>$/,tn=/^["']/,nn=/^\d+$/;function N(e){let t=e.replace(Qt,``);if(t.includes(`|`))return t.split(`|`).every(e=>N(e));if(Zt.has(t)||$t.test(t)&&N(t.replace($t,``)))return!0;let n=t.match(en);return!!(n&&N(n[1])||tn.test(t)||nn.test(t))}const rn={meta:{id:`correctness/validated-non-primitive-needs-type`,category:`correctness`,severity:`warning`,description:`DTO properties with class-validator decorators on non-primitive types must have @Type() from class-transformer`,help:`Add @Type(() => ClassName) from 'class-transformer' to ensure proper transformation.`},check(e){for(let t of e.sourceFile.getClasses())for(let n of t.getProperties()){let t=n.getDecorators();if(t.length===0||!t.some(e=>Xt.has(e.getName()))||t.some(e=>e.getName()===`Type`)||t.some(e=>e.getName()===`IsEnum`))continue;let r=n.getTypeNode();if(!r)continue;let i=r.getText();N(i)||e.report({filePath:e.filePath,message:`Property '${n.getName()}' has type '${i}' with class-validator decorators but is missing @Type() decorator.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},an=new Set([f.ForStatement,f.ForOfStatement,f.ForInStatement,f.WhileStatement,f.DoStatement]),on={meta:{id:`performance/no-blocking-constructor`,category:`performance`,severity:`warning`,description:`Constructors in Injectable/Controller classes should not contain heavy operations`,help:`Move heavy initialization logic to the onModuleInit() lifecycle method. Constructors cannot be async, so asynchronous work should always use lifecycle hooks.`},check(e){for(let t of e.sourceFile.getClasses()){if(!(T(t,`Injectable`)||T(t,`Controller`)))continue;let n=t.getConstructors()[0];if(!n)continue;let r=n.getBody();if(r){for(let i of r.getDescendants())if(an.has(i.getKind())){e.report({filePath:e.filePath,message:`Constructor in '${t.getName()}' contains blocking operation — use onModuleInit() instead.`,help:this.meta.help,line:n.getStartLineNumber(),column:1});break}}}}},sn={meta:{id:`performance/no-dynamic-require`,category:`performance`,severity:`warning`,description:`Dynamic require() with variable arguments prevents bundler optimization`,help:`Use static import statements or dynamic import() with string literals.`},check(e){let t=e.sourceFile.getDescendantsOfKind(f.CallExpression);for(let n of t){if(n.getExpression().getText()!==`require`)continue;let t=n.getArguments();t.length!==0&&t[0].getKind()!==f.StringLiteral&&e.report({filePath:e.filePath,message:`Dynamic require() with non-literal argument prevents bundler optimization.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},cn={meta:{id:`performance/no-orphan-modules`,category:`performance`,severity:`info`,description:`Module is never imported by any other module and may be dead code`,help:`Import this module in another module or remove it if it is unused.`,scope:`project`},check(e){let t=new Set;for(let n of e.moduleGraph.modules.values())for(let e of n.imports)t.add(e);for(let n of e.moduleGraph.modules.values())n.name!==`AppModule`&&(t.has(n.name)||e.report({filePath:n.filePath,message:`Module '${n.name}' is never imported by any other module.`,help:this.meta.help,line:n.classDeclaration.getStartLineNumber(),column:1}))}},ln={meta:{id:`performance/no-request-scope-abuse`,category:`performance`,severity:`warning`,description:`Scope.REQUEST creates a new provider instance per request — use only when necessary`,help:`Remove Scope.REQUEST unless the provider genuinely needs per-request state (e.g., request-scoped context). Consider Scope.DEFAULT or Scope.TRANSIENT instead.`},check(e){let t=e.sourceFile.getDescendantsOfKind(f.PropertyAccessExpression);for(let n of t)n.getName()===`REQUEST`&&n.getExpression().getText()===`Scope`&&e.report({filePath:e.filePath,message:`Scope.REQUEST creates a new instance per request, which impacts performance and propagates request scope to all dependents.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}},un=new Set([`readFileSync`,`writeFileSync`,`existsSync`,`mkdirSync`,`readdirSync`,`statSync`,`accessSync`,`appendFileSync`,`copyFileSync`,`renameSync`,`unlinkSync`]),dn={meta:{id:`performance/no-sync-io`,category:`performance`,severity:`warning`,description:`Synchronous I/O calls block the event loop and should be avoided in NestJS applications`,help:`Use the async variant (e.g., readFile instead of readFileSync) with await.`},check(e){let t=e.sourceFile.getDescendantsOfKind(f.CallExpression);for(let n of t){let t=n.getExpression().getText().split(`.`).pop()??``;un.has(t)&&e.report({filePath:e.filePath,message:`Synchronous I/O call '${t}()' blocks the event loop.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},fn={meta:{id:`performance/no-unused-module-exports`,category:`performance`,severity:`info`,description:`Module exports a provider that no importing module actually uses`,help:`Remove the unused export or use the provider in an importing module.`,scope:`project`},check(e){for(let t of e.moduleGraph.modules.values()){if(t.exports.length===0)continue;let n=[];for(let r of e.moduleGraph.modules.values())r.name!==t.name&&r.imports.includes(t.name)&&n.push(r.name);if(n.length===0)continue;let r=new Set;for(let i of n){let n=e.moduleGraph.modules.get(i);if(n){for(let t of n.providers){let n=e.providers.get(t);if(n)for(let e of n.dependencies)r.add(e)}if(n.exports.includes(t.name))for(let e of t.exports)r.add(e);for(let t of n.controllers)for(let n of e.files){let i=e.project.getSourceFile(n);if(i)for(let e of i.getClasses()){if(e.getName()!==t)continue;let n=e.getConstructors()[0];if(n)for(let e of n.getParameters()){let t=e.getTypeNode(),n=t?t.getText():e.getType().getText(),i=n.split(`.`).pop()?.split(`<`)[0]??n;r.add(i)}}}}}for(let n of t.exports)e.moduleGraph.modules.has(n)||r.has(n)||e.report({filePath:t.filePath,message:`Module '${t.name}' exports '${n}' but no importing module uses it.`,help:this.meta.help,line:t.classDeclaration.getStartLineNumber(),column:1})}}},pn=new Set([`Cron`,`Interval`,`Timeout`,`OnEvent`,`Process`,`OnQueueEvent`,`EventSubscriber`,`SubscribeMessage`,`WebSocketGateway`]);function mn(e){for(let t of e.getDecorators())if(pn.has(t.getName()))return!0;for(let t of e.getMethods())for(let e of t.getDecorators())if(pn.has(e.getName()))return!0;return!1}const hn={meta:{id:`performance/no-unused-providers`,category:`performance`,severity:`warning`,description:`Injectable providers that are never injected and have no self-activating decorators may be dead code`,help:`Remove the unused provider, inject it where needed, or verify it is activated by a framework decorator (e.g. @Cron, @OnEvent).`,scope:`project`},check(e){let t=new Set;for(let n of e.providers.values())for(let e of n.dependencies)t.add(e);let n=[`Controller`,`Resolver`,`WebSocketGateway`];for(let r of e.files){let i=e.project.getSourceFile(r);if(i)for(let e of i.getClasses()){if(!n.some(t=>e.getDecorator(t)!==void 0))continue;let r=e.getConstructors()[0];if(r)for(let e of r.getParameters()){let n=e.getTypeNode(),r=n?n.getText():e.getType().getText(),i=r.split(`.`).pop()?.split(`<`)[0]??r;t.add(i)}}}for(let n of e.providers.values()){let r=n.name;if(xt.some(e=>r.endsWith(e))||t.has(r)||mn(n.classDeclaration))continue;let i=!1;for(let t of e.moduleGraph.modules.values())if(t.exports.includes(r)){i=!0;break}i||e.report({filePath:n.filePath,message:`Provider '${r}' is never injected by any other provider or controller.`,help:this.meta.help,line:n.classDeclaration.getStartLineNumber(),column:1})}}},gn={meta:{id:`schema/require-cascade-rule`,category:`schema`,scope:`schema`,severity:`info`,description:`Relations should have explicit onDelete/cascade behavior defined`,help:`Add an explicit onDelete option (e.g. CASCADE, SET NULL) to avoid relying on database defaults.`},check(e){for(let t of e.schemaGraph.relations)if(!(t.type!==`many-to-one`&&t.type!==`one-to-one`)&&!t.onDelete){let n=e.schemaGraph.entities.get(t.fromEntity);if(!n)continue;e.report({filePath:n.filePath,entity:n.name,message:`Relation '${t.propertyName}' on '${t.fromEntity}' has no explicit onDelete behavior.`,help:this.meta.help})}}},_n={meta:{id:`schema/require-primary-key`,category:`schema`,scope:`schema`,severity:`error`,description:`Every entity must have at least one primary key column`,help:`Add a primary key column (e.g. @id in Prisma, @PrimaryColumn/@PrimaryGeneratedColumn in TypeORM).`},check(e){for(let t of e.schemaGraph.entities.values())t.columns.some(e=>e.isPrimary)||e.report({filePath:t.filePath,entity:t.name,message:`Entity '${t.name}' has no primary key column.`,help:this.meta.help})}},vn=/delete/i;function yn(e,t){let n=new Set(e.columns.map(e=>e.name.toLowerCase()));return n.has(`createdat`)||n.has(`created_at`)?!0:t===`typeorm`?e.columns.some(e=>e.type===`timestamp`&&e.isGenerated&&!vn.test(e.name)):t===`prisma`?e.columns.some(e=>e.type===`DateTime`&&e.defaultValue!==void 0&&e.defaultValue.includes(`now()`)):t===`drizzle`?e.columns.some(e=>(e.type===`timestamp`||e.type===`date`||e.type===`datetime`)&&e.defaultValue!==void 0&&e.defaultValue.includes(`now()`)):!1}const bn={meta:{id:`schema/require-timestamps`,category:`schema`,scope:`schema`,severity:`warning`,description:`Entities should have timestamp columns (createdAt/updatedAt)`,help:`Add createdAt/updatedAt columns to track when records are created and modified.`},check(e){for(let t of e.schemaGraph.entities.values())yn(t,e.orm)||e.report({filePath:t.filePath,entity:t.name,message:`Entity '${t.name}' has no timestamp columns (createdAt/updatedAt).`,help:this.meta.help})}},xn={meta:{id:`security/no-csrf-disabled`,category:`security`,severity:`error`,description:`CSRF protection should not be explicitly disabled`,help:`Enable CSRF protection or remove the explicit disabling of it.`},check(e){let t=e.sourceFile.getDescendantsOfKind(f.PropertyAssignment);for(let n of t){let t=n.getName();if(t!==`csrf`&&t!==`csrfProtection`)continue;let r=n.getInitializer();r&&r.getText()===`false`&&e.report({filePath:e.filePath,message:`CSRF protection explicitly disabled (${t}: false).`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},Sn={meta:{id:`security/no-dangerous-redirects`,category:`security`,severity:`error`,description:`Redirects using user-controlled input (from @Query/@Param) are an open redirect vulnerability`,help:`Validate redirect URLs against an allowlist of safe destinations.`},check(e){for(let t of e.sourceFile.getClasses())if(E(t))for(let n of t.getMethods()){let t=new Set;for(let e of n.getParameters())e.getDecorators().some(e=>e.getName()===`Query`||e.getName()===`Param`)&&t.add(e.getName());if(t.size===0)continue;let r=n.getDescendantsOfKind(f.CallExpression);for(let n of r)if(n.getExpression().getText().endsWith(`redirect`))for(let r of n.getArguments()){let i=r.getText();t.has(i)&&e.report({filePath:e.filePath,message:`Redirect uses user-controlled parameter '${i}' — open redirect risk.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}let i=n.getDecorators().find(e=>e.getName()===`Redirect`);if(i)for(let n of i.getArguments()){let r=n.getText();t.has(r)&&e.report({filePath:e.filePath,message:`@Redirect() uses user-controlled parameter '${r}' — open redirect risk.`,help:this.meta.help,line:i.getStartLineNumber(),column:1})}}}},Cn={meta:{id:`security/no-eval`,category:`security`,severity:`error`,description:`Usage of eval() or new Function() is a security risk and should be avoided`,help:`Refactor to avoid eval() and new Function(). Use safer alternatives like JSON.parse() or a sandboxed interpreter.`},check(e){let t=e.sourceFile.getDescendantsOfKind(f.CallExpression);for(let n of t)n.getExpression().getText()===`eval`&&e.report({filePath:e.filePath,message:`Usage of eval() is a security risk.`,help:this.meta.help,line:n.getStartLineNumber(),column:1});let n=e.sourceFile.getDescendantsOfKind(f.NewExpression);for(let t of n)t.getExpression().getText()===`Function`&&e.report({filePath:e.filePath,message:`Usage of new Function() is a security risk.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}},wn={meta:{id:`security/no-exposed-env-vars`,category:`security`,severity:`warning`,description:`Use NestJS ConfigService instead of direct process.env access in Injectable/Controller classes`,help:`Inject ConfigService and use configService.get('VAR_NAME') instead of process.env.VAR_NAME.`},check(e){for(let t of e.sourceFile.getClasses()){if(!(T(t,`Injectable`)||T(t,`Controller`)))continue;let n=t.getDescendantsOfKind(f.PropertyAccessExpression);for(let r of n)r.getExpression().getText()===`process.env`&&e.report({filePath:e.filePath,message:`Direct 'process.env.${r.getName()}' access in '${t.getName()}'. Use ConfigService instead.`,help:this.meta.help,line:r.getStartLineNumber(),column:1})}}},Tn=/^(error|err|e|ex|exception)$/,En={meta:{id:`security/no-exposed-stack-trace`,category:`security`,severity:`warning`,description:`Stack traces should not be exposed in responses — they leak internal implementation details`,help:`Log the stack trace internally and return a generic error message to the client.`},check(e){let t=e.sourceFile.getDescendantsOfKind(f.PropertyAccessExpression);for(let n of t){if(n.getName()!==`stack`)continue;let t=n.getExpression().getText();if(!(Tn.test(t)||t.endsWith(`.error`)||t.endsWith(`.err`)))continue;let r=n.getParent();if(!r)continue;let i=r.getKind();(i===f.ReturnStatement||i===f.PropertyAssignment||i===f.ShorthandPropertyAssignment||i===f.CallExpression)&&e.report({filePath:e.filePath,message:`Stack trace '${t}.stack' may be exposed in response — leaks implementation details.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},Dn=[{pattern:/^(?=.*\d)[A-Za-z0-9+/]{40,}={0,2}$/,name:`Base64 key`},{pattern:/^sk[-_][a-zA-Z0-9]{20,}$/,name:`Secret key`},{pattern:/^pk[-_][a-zA-Z0-9]{20,}$/,name:`Public key (in source)`},{pattern:/^ghp_[a-zA-Z0-9]{36,}$/,name:`GitHub personal access token`},{pattern:/^github_pat_[a-zA-Z0-9_]{22,}$/,name:`GitHub fine-grained PAT`},{pattern:/^gho_[a-zA-Z0-9]{36,}$/,name:`GitHub OAuth token`},{pattern:/^xox[bpras]-[a-zA-Z0-9-]+$/,name:`Slack token`},{pattern:/^eyJ[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,}\./,name:`JWT token`},{pattern:/^AKIA[0-9A-Z]{16}$/,name:`AWS Access Key ID`},{pattern:/^[a-f0-9]{64}$/,name:`Hex-encoded secret (64 chars)`}],On=[/secret/i,/password/i,/passwd/i,/api[_-]?key/i,/auth[_-]?token/i,/private[_-]?key/i,/access[_-]?key/i,/client[_-]?secret/i],kn=new Set([`your-secret-here`,`changeme`,`password`]),An=/^[A-Za-z][A-Za-z0-9_]*(\.[A-Za-z][A-Za-z0-9_]*)+$/,jn=new Set([`cursor`,`nextCursor`,`prevCursor`,`previousCursor`,`startCursor`,`endCursor`,`pageToken`,`nextPageToken`,`continuationToken`,`continuation`,`nextPage`,`afterCursor`,`beforeCursor`]);function Mn(e){return!(e.length<8||e.includes("${")||e.startsWith(`process.env`)||kn.has(e)||e.includes(` `)||An.test(e))}function Nn(e){return On.some(t=>t.test(e))}function Pn(e){try{let t=Buffer.from(e,`base64`).toString(`utf-8`);return JSON.parse(t),!0}catch{return!1}}function Fn(e){let t=new Map;for(let n of e)t.set(n,(t.get(n)??0)+1);let n=0;for(let r of t.values()){let t=r/e.length;n-=t*Math.log2(t)}return n}const In=new Set([...`aeiouyAEIOUY`]),Ln=/^[A-Z]{2,4}_/,Rn=/(?<=[a-z])(?=[A-Z])|(?<=[A-Za-z])(?=\d)|(?<=\d)(?=[A-Za-z])|_/,zn=/[a-zA-Z]/;function Bn(e){let t=e.includes(`_`),n=e.split(Rn).filter(e=>e.length>0).filter(e=>zn.test(e)),r=n.filter(e=>e.length>=4&&[...e].some(e=>In.has(e)));return n.slice(0,6).filter(e=>e.length>=4&&[...e].some(e=>In.has(e))).length>=2||t&&e.split(`_`).filter(e=>e.length>=3).length>=2||Ln.test(e)?!0:(Fn(e)>4.9&&!t&&r.length,!1)}function Vn(e){let t=e.getParent();if(!t)return!1;let n=t.asKind(f.PropertyAssignment);if(n)return jn.has(n.getName());let r=t.asKind(f.VariableDeclaration);return r?jn.has(r.getName()):!1}const Hn={meta:{id:`security/no-hardcoded-secrets`,category:`security`,severity:`error`,description:`Detect hardcoded secrets, API keys, and tokens in source code`,help:`Move secrets to environment variables and access them via ConfigService.`},check(e){let t=e.sourceFile.getDescendantsOfKind(f.StringLiteral);for(let n of t){let t=n.getLiteralValue();if(!(t.length<16)&&n.getParent()?.getKind()!==f.ImportDeclaration){for(let{pattern:r,name:i}of Dn)if(r.test(t)){if(i===`Base64 key`&&(Pn(t)||Vn(n)||Bn(t)))break;e.report({filePath:e.filePath,message:`Possible hardcoded ${i} detected.`,help:this.meta.help,line:n.getStartLineNumber(),column:1});break}}}let n=e.sourceFile.getDescendantsOfKind(f.VariableDeclaration);for(let t of n){let n=t.getName(),r=t.getInitializer();!r||r.getKind()!==f.StringLiteral||Nn(n)&&Mn(r.getText().slice(1,-1))&&e.report({filePath:e.filePath,message:`Variable '${n}' appears to contain a hardcoded secret.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}let r=e.sourceFile.getDescendantsOfKind(f.PropertyAssignment);for(let t of r){let n=t.getName(),r=t.getInitializer();!r||r.getKind()!==f.StringLiteral||Nn(n)&&Mn(r.getText().slice(1,-1))&&e.report({filePath:e.filePath,message:`Property '${n}' appears to contain a hardcoded secret.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}},Un=RegExp(`(?:^|[^a-zA-Z])\\w*(?:${[`Entity`,`Model`].join(`|`)})(?:[^a-zA-Z]|$)`),Wn={meta:{id:`security/no-raw-entity-in-response`,category:`security`,severity:`warning`,description:`Returning ORM entities directly from controllers can leak internal fields like passwords or IDs`,help:`Map entities to DTOs or use class-transformer's @Exclude()/@Expose() decorators before returning.`},check(e){for(let t of e.sourceFile.getClasses())if(E(t))for(let n of t.getMethods()){if(!O(n))continue;let t=n.getReturnType().getText();Un.test(t)&&!t.includes(`DTO`)&&!t.includes(`Dto`)&&!t.includes(`Response`)&&e.report({filePath:e.filePath,message:`Controller method '${n.getName()}' returns a raw entity type. This may leak internal fields.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},Gn={meta:{id:`security/no-synchronize-in-production`,category:`security`,severity:`error`,description:`TypeORM synchronize: true auto-syncs schema and can drop columns or tables in production`,help:`Set synchronize: false and use migrations for production schema changes.`},check(e){let t=e.sourceFile.getDescendantsOfKind(f.PropertyAssignment);for(let n of t){if(n.getName()!==`synchronize`)continue;let t=n.getInitializer();t&&t.getText()===`true`&&e.report({filePath:e.filePath,message:`TypeORM 'synchronize: true' can auto-drop columns and tables in production.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},Kn=new Set([`md5`,`sha1`]),qn={meta:{id:`security/no-weak-crypto`,category:`security`,severity:`warning`,description:`Weak hashing algorithms (MD5, SHA1) should not be used for security purposes`,help:`Use a stronger algorithm like SHA-256 or bcrypt for password hashing.`},check(e){let t=e.sourceFile.getDescendantsOfKind(f.CallExpression);for(let n of t){if(!n.getExpression().getText().endsWith(`createHash`))continue;let t=n.getArguments();if(t.length===0)continue;let r=t[0];if(r.getKind()!==f.StringLiteral)continue;let i=r.getText().slice(1,-1).toLowerCase();Kn.has(i)&&e.report({filePath:e.filePath,message:`Weak hashing algorithm '${i}' used in createHash().`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},Jn=new Set([`Public`,`AllowAnonymous`,`SkipAuth`,`IsPublic`]),Yn=[Oe,mt,it,lt,et,gt,_t,yt,Ce,Ye,Ht,Gt,At,kt,Ft,zt,Pt,Lt,wt,Ot,Rt,Ut,Nt,Vt,bt,rn,Et,qt,It,St,Hn,Cn,qn,wn,xn,En,Sn,Gn,Wn,{meta:{id:`security/require-guards-on-endpoints`,category:`security`,severity:`warning`,description:`Controller endpoints should be protected by @UseGuards() at class or method level`,help:`Add @UseGuards(AuthGuard) to the controller class or individual route handlers, or mark routes as @Public(). If you use a global guard via APP_GUARD, you can disable this rule.`},check(e){for(let t of e.sourceFile.getClasses())if(E(t)&&t.getDecorator(`UseGuards`)===void 0&&!t.getDecorators().some(e=>Jn.has(e.getName())))for(let n of t.getMethods())O(n)&&n.getDecorator(`UseGuards`)===void 0&&(n.getDecorators().some(e=>Jn.has(e.getName()))||e.report({filePath:e.filePath,message:`Endpoint '${n.getName()}' has no @UseGuards() at class or method level.`,help:this.meta.help,line:n.getStartLineNumber(),column:1}))}},dn,on,sn,ln,hn,fn,cn,_n,bn,gn];function Xn(){return[...Yn]}function Zn(e){return e.meta.scope===`project`}function Qn(e){return e.meta.scope===`schema`}function $n(e,t,n){if(t.length===0)return e;let r=new Set(e.map(e=>e.meta.id)),i=[...e];for(let e of t){if(r.has(e.meta.id)){n.push(`Custom rule "${e.meta.id}" conflicts with a built-in rule and was skipped`);continue}i.push(e)}return i}function er(e,t){return t.filter(t=>{let n=e.rules?.[t.meta.id];return!(n===!1||typeof n==`object`&&n.enabled===!1||e.categories?.[t.meta.category]===!1)})}function tr(e){let t=[],n=[],r=[];for(let i of e)Qn(i)?r.push(i):Zn(i)?n.push(i):t.push(i);return{fileRules:t,projectRules:n,schemaRules:r}}const nr=new Set([`security`,`performance`,`correctness`,`architecture`]),rr=new Set([`error`,`warning`,`info`]),ir=new Set([`file`,`project`]),ar=`custom/`;function or(e){if(typeof e!=`object`||!e)return!1;let t=e;if(typeof t.check!=`function`||typeof t.meta!=`object`||t.meta===null)return!1;let n=t.meta;return!(typeof n.id!=`string`||n.id.trim()===``||typeof n.description!=`string`||typeof n.help!=`string`||!nr.has(n.category)||!rr.has(n.severity)||n.scope!==void 0&&!ir.has(n.scope))}function sr(e){return e.meta.id.startsWith(ar)?e:{...e,meta:{...e.meta,id:`${ar}${e.meta.id}`}}}async function cr(t,i){let a=[],o=[],c=s(i,t);if(!e(c))return o.push(`Custom rules directory not found: ${c}`),{rules:a,warnings:o};if(!r(c).isDirectory())return o.push(`Custom rules path is not a directory: ${c}`),{rules:a,warnings:o};let l;try{l=n(c)}catch(e){return o.push(`Failed to read custom rules directory: ${e instanceof Error?e.message:String(e)}`),{rules:a,warnings:o}}let u=l.filter(e=>e.endsWith(`.ts`));if(u.length===0)return o.push(`No rule files (.ts) found in: ${c}`),{rules:a,warnings:o};let d=m(c,{interopDefault:!0});for(let e of u){let t=s(c,e),n;try{n=await d.import(t)}catch(t){o.push(`Failed to load custom rule file "${e}": ${t instanceof Error?t.message:String(t)}`);continue}let r=!1;for(let[t,i]of Object.entries(n))or(i)?(a.push(sr(i)),r=!0):t!==`__esModule`&&typeof i==`object`&&i&&`meta`in i&&o.push(`Invalid rule export "${t}" in "${e}": missing or invalid required fields (check, meta.id, meta.description, meta.help, meta.category, meta.severity)`);!r&&Object.keys(n).length>0&&(Object.values(n).some(e=>typeof e==`object`&&!!e&&(`meta`in e||`check`in e))||o.push(`No valid rule exports found in "${e}"`))}return{rules:a,warnings:o}}function lr(e,t){return e.customRulesDir?cr(e.customRulesDir,t):Promise.resolve({rules:[],warnings:[]})}async function P(e,t){let n=await ve(e,t),{rules:r,warnings:i}=await lr(n,e),a=$n(Yn,r,i),{fileRules:o,projectRules:s,schemaRules:c}=tr(er(n,a));return{combinedRules:a,config:n,customRuleWarnings:i,fileRules:o,projectRules:s,schemaRules:c}}async function ur(e,t={}){return(await l(t.include??C.include,{cwd:e,absolute:!0,ignore:t.exclude??C.exclude})).sort()}async function dr(e,t,n={}){let r=await Promise.all([...t.projects.entries()].map(async([t,r])=>[t,await ur(a(e,r),n)])),i=new Map;for(let[e,t]of r)i.set(e,t);return i}function fr(e){let t=new d({compilerOptions:{strict:!0,target:99,module:99,skipFileDependencyResolution:!0},skipAddingFilesFromTsConfig:!0});for(let n of e)t.addSourceFileAtPath(n);return t}const pr=/import\([^)]+\)\.(\w+)/,mr=/^(\w+)</;function hr(e,t){let n=[];for(let r of e.getClasses()){if(!r.getDecorator(`Injectable`))continue;let e=r.getName();if(!e)continue;let i=r.getConstructors()[0],a=i?i.getParameters().map(e=>{let t=e.getTypeNode();return F(t?t.getText():e.getType().getText())}):[],o=r.getMethods().filter(e=>{let t=e.getScope();return!t||t===`public`}).length;n.push({name:e,filePath:t,classDeclaration:r,dependencies:a,publicMethodCount:o})}return n}function gr(e,t){let n=new Map;for(let r of t){let t=e.getSourceFile(r);if(t)for(let e of hr(t,r))n.set(e.name,e)}return n}function _r(e,t,n){for(let[t,r]of e)r.filePath===n&&e.delete(t);let r=t.getSourceFile(n);if(r)for(let t of hr(r,n))e.set(t.name,t)}function F(e){let t=e.match(pr);if(t)return t[1];let n=e.match(mr);return n?n[1]:e}const I=/^['"`]|['"`]$/g,vr=/\/+/g,yr=/\/$/,br=new Set([`Query`,`Mutation`,`Subscription`]),xr=new Set([`ApiOperation`,`ApiParam`,`ApiQuery`,`ApiResponse`,`ApiBody`]),Sr=new Set([`map`,`forEach`,`filter`,`find`,`some`,`every`,`flatMap`,`reduce`]);var L=class{scanResults=new Map;injectionMaps=new Map;methodLookups=new Map;getScan(e){return this.scanResults.get(e)}setScan(e,t){this.scanResults.set(e,t)}getInjMap(e){return this.injectionMaps.get(e)}setInjMap(e,t){this.injectionMaps.set(e,t)}getMethod(e){return this.methodLookups.has(e)?this.methodLookups.get(e):void 0}hasMethod(e){return this.methodLookups.has(e)}setMethod(e,t){this.methodLookups.set(e,t)}};function R(e){let t=e.replace(/\s+/g,` `).trim();return t.length>50?`${t.slice(0,50)}\u2026`:t}function z(e,t){let n={isConditional:!1,conditionText:null,branchKind:null,statementLine:null},r=e;for(;r&&r!==t;){let e=r.getParent();if(!e||e===t)break;let n=e.getKind();if(n===f.IfStatement){let t=e.asKindOrThrow(f.IfStatement);if(r===t.getThenStatement()){let n=e.getParent();if(n&&n.getKind()===f.IfStatement){let r=n.asKindOrThrow(f.IfStatement);if(e===r.getElseStatement())return{isConditional:!0,conditionText:R(t.getExpression().getText()),branchKind:`else-if`,statementLine:r.getStartLineNumber()}}return{isConditional:!0,conditionText:R(t.getExpression().getText()),branchKind:`if`,statementLine:t.getStartLineNumber()}}if(r===t.getElseStatement())return{isConditional:!0,conditionText:R(t.getExpression().getText()),branchKind:`else`,statementLine:t.getStartLineNumber()}}if(n===f.ConditionalExpression){let t=e.asKindOrThrow(f.ConditionalExpression);if(r===t.getWhenTrue())return{isConditional:!0,conditionText:R(t.getCondition().getText()),branchKind:`ternary-true`,statementLine:t.getStartLineNumber()};if(r===t.getWhenFalse())return{isConditional:!0,conditionText:R(t.getCondition().getText()),branchKind:`ternary-false`,statementLine:t.getStartLineNumber()}}let i=r.getKind();if(i===f.CaseClause){let e=r.asKindOrThrow(f.CaseClause),t=r.getParentOrThrow().getParentOrThrow();return{isConditional:!0,conditionText:R(e.getExpression().getText()),branchKind:`case`,statementLine:t.getStartLineNumber()}}if(i===f.DefaultClause)return{isConditional:!0,conditionText:null,branchKind:`default`,statementLine:r.getParentOrThrow().getParentOrThrow().getStartLineNumber()};if(i===f.CatchClause)return{isConditional:!0,conditionText:null,branchKind:`catch`,statementLine:r.getParentOrThrow().getStartLineNumber()};r=e}return n}const Cr=new Map([[f.ForStatement,`for`],[f.ForOfStatement,`for-of`],[f.ForInStatement,`for-in`],[f.WhileStatement,`while`],[f.DoStatement,`do-while`]]);function B(e,t){let n={iterationKind:null,iterationLabel:null},r=e;for(;r&&r!==t;){let e=r.getParent();if(!e||e===t)break;let n=e.getKind(),i=Cr.get(n);if(i){let t=!1;if(n===f.ForStatement){let n=e.asKindOrThrow(f.ForStatement);t=r!==n.getInitializer()&&r!==n.getCondition()&&r!==n.getIncrementor()&&r===n.getStatement()}else n===f.ForOfStatement?t=r===e.asKindOrThrow(f.ForOfStatement).getStatement():n===f.ForInStatement?t=r===e.asKindOrThrow(f.ForInStatement).getStatement():n===f.WhileStatement?t=r===e.asKindOrThrow(f.WhileStatement).getStatement():n===f.DoStatement&&(t=r===e.asKindOrThrow(f.DoStatement).getStatement());if(t)return{iterationKind:`loop`,iterationLabel:i}}let a=r.getKind();if(a===f.ArrowFunction||a===f.FunctionExpression){if(n===f.CallExpression){let t=e.asKindOrThrow(f.CallExpression);if(t.getArguments().some(e=>e===r)){let e=t.getExpression();if(e.getKind()===f.PropertyAccessExpression){let t=e.asKindOrThrow(f.PropertyAccessExpression).getName();if(Sr.has(t))return{iterationKind:`callback`,iterationLabel:t}}}}break}if(n===f.ArrayLiteralExpression){let t=e.getParent();if(t&&t.getKind()===f.CallExpression){let n=t.asKindOrThrow(f.CallExpression),r=n.getArguments();if(r.length>0&&r[0]===e){let e=n.getExpression();if(e.getKind()===f.PropertyAccessExpression){let t=e.asKindOrThrow(f.PropertyAccessExpression);if(t.getName()===`all`&&t.getExpression().getText().endsWith(`Promise`))return{iterationKind:`concurrent`,iterationLabel:`all`}}}}}r=e}return n}function V(e){let t=e;for(;t;){let e=t.getKind();if(e===f.ExpressionStatement||e===f.VariableStatement||e===f.ReturnStatement||e===f.ThrowStatement)break;t=t.getParent()}if(!t)return null;let n=t.getSourceFile(),r=t.getFullStart(),i=t.getStart(),a=n.getFullText().slice(r,i).split(`
|
|
4
|
+
`);for(let e=a.length-1;e>=0;e--){let t=a[e].trim();if(t.startsWith(`//`))return t.slice(2).trim();if(t.length>0)break}return null}function wr(e){let t=e.asKindOrThrow(f.ThrowStatement).getExpression();return t&&t.getKind()===f.NewExpression?F(t.asKindOrThrow(f.NewExpression).getExpression().getText()):`Error`}function Tr(e){let t=e.asKindOrThrow(f.ThrowStatement).getExpression();if(!t||t.getKind()!==f.NewExpression)return null;let n=t.asKindOrThrow(f.NewExpression).getArguments();if(n.length===0)return null;let r=n[0],i=r.getKind(),a;if(i===f.StringLiteral||i===f.NoSubstitutionTemplateLiteral)a=r.asKindOrThrow(i).getLiteralValue();else if(i===f.TemplateExpression){let e=r.getText();a=e.startsWith("`")?e.slice(1,-1):e}else a=r.getText();return a.length>80?`${a.slice(0,80)}\u2026`:a}function Er(e){let t=e.getParent();for(;t;){let e=t.getKind();if(e===f.AwaitExpression||e===f.ParenthesizedExpression||e===f.AsExpression||e===f.NonNullExpression){t=t.getParent();continue}if(e===f.VariableDeclaration){let e=t.asKindOrThrow(f.VariableDeclaration).getNameNode();return e.getKind()===f.Identifier?e.getText():null}return null}return null}function Dr(e,t){let n;try{n=e.getBaseClass()}catch{}if(!n&&t){let r=e.getExtends();if(r){let e=F(r.getExpression().getText()),i=t.get(e);i&&(n=i.classDeclaration)}}return n}function H(e,t,n,r){let i=`${e.getName()??``}.${t}`;if(r?.hasMethod(i))return r.getMethod(i);let a=e,o=new Set;for(;a;){let e=a.getName();if(e&&o.has(e))break;e&&o.add(e);let s=a.getInstanceMethod(t);if(s)return r?.setMethod(i,s),s;a=Dr(a,n)}r?.setMethod(i,void 0)}function Or(e){let t=e.getDecorator(`Controller`);if(!t)return``;let n=t.getArguments();if(n.length===0)return``;let r=n[0];if(r.getKind()===f.ObjectLiteralExpression){let e=r.asKindOrThrow(f.ObjectLiteralExpression).getProperty(`path`);if(!e)return``;let t=e.asKind(f.PropertyAssignment);if(!t)return``;let n=t.getInitializer();return n?n.getText().replace(I,``):``}return r.getText().replace(I,``)}function kr(e){for(let t of e.getDecorators()){let e=t.getName();if(!w.has(e))continue;let n=t.getArguments(),r=n.length>0?n[0].getText().replace(I,``):``;return{httpMethod:e.toUpperCase(),path:r}}}function Ar(e,t){return`/${[e,t].filter(Boolean).join(`/`)}`.replace(vr,`/`).replace(yr,``)||`/`}function U(e,t){let n=e.asKind(f.ObjectLiteralExpression);if(!n)return null;let r=n.getProperty(t);if(!r)return null;let i=r.asKind(f.PropertyAssignment);if(!i)return null;let a=i.getInitializer();return a?a.getText().replace(I,``):null}function jr(e,t){let n=U(e,t);if(n===null)return null;let r=Number(n);return Number.isNaN(r)?null:r}function Mr(e,t,n){let r=U(e,t);return r===null?n:r===`true`}function Nr(e){let t=null,n=null,r=[],i=[],a=[],o=null,s=!1;for(let c of e.getDecorators()){let e=c.getName();if(!xr.has(e))continue;s=!0;let l=c.getArguments();if(l.length===0)continue;let u=l[0];if(e===`ApiOperation`)t=U(u,`summary`),n=U(u,`description`);else if(e===`ApiParam`){let e=U(u,`name`);e&&r.push({description:U(u,`description`),name:e,required:Mr(u,`required`,!0),type:U(u,`type`)})}else if(e===`ApiQuery`){let e=U(u,`name`);e&&i.push({description:U(u,`description`),name:e,required:Mr(u,`required`,!1),type:U(u,`type`)})}else if(e===`ApiResponse`){let e=jr(u,`status`)??200,t=U(u,`type`);t?.startsWith(`[`)&&t.endsWith(`]`)&&(t=`${t.slice(1,-1)}[]`),a.push({description:U(u,`description`),status:e,type:t})}else e===`ApiBody`&&(o={description:U(u,`description`),type:U(u,`type`)})}if(!o){for(let t of e.getParameters())if(t.getDecorators().some(e=>e.getName()===`Body`)){let e=t.getTypeNode();e&&(o={description:null,type:e.getText()},s=!0);break}}return s?{body:o,description:n,params:r,queryParams:i,responses:a,summary:t}:null}const Pr=/^(?:Promise|Observable)<(.+)>$/;function W(e){let t=e.getReturnTypeNode();if(!t)return null;let n=t.getText().trim(),r=Pr.exec(n);return r&&(n=r[1]),n===`void`||n===`any`||n===`unknown`?null:n}function Fr(e){return e.getParameters().filter(e=>e.getName()!==`this`).map(e=>({name:e.getName(),type:e.getTypeNode()?.getText()??null}))}const Ir=/=>\s*\{[^}]*\}/g,Lr=/\(([^)]{20,})\)\s*=>/g,Rr=/\s+/g;function zr(e){let t=e.replace(Rr,` `).trim();return t=t.replace(Ir,`=> …`),t=t.replace(Lr,`(…) =>`),t.length>50&&(t=`${t.slice(0,47)}…`),t}function Br(e,t){let n=e.getKind();if(n!==f.VariableStatement&&n!==f.ExpressionStatement)return!1;let r=[...e.getDescendantsOfKind(f.CallExpression),...e.getDescendantsOfKind(f.NewExpression)];if(r.length===0)return!1;for(let e of r)if(t.has(e.getStart()))return!1;for(let e of r){let t=e.getText();if(t.startsWith(`console.`)||t.startsWith(`this.logger.`))return!1}return!0}function Vr(e){if(e.getKind()===f.VariableStatement){let t=e.asKindOrThrow(f.VariableStatement).getDeclarationList().getDeclarations();if(t.length===0)return null;let n=t[0],r=n.getNameNode().getText(),i=n.getInitializer();return i?{assignedTo:r,text:`${r} = ${zr(i.getText())}`}:null}return e.getKind()===f.ExpressionStatement?{assignedTo:null,text:zr(e.asKindOrThrow(f.ExpressionStatement).getExpression().getText())}:null}function G(e,t,n){let r=e.getName()??``;if(n){let e=n.getInjMap(r);if(e)return e}let i=new Map,a=e,o=new Set;for(;a;){let e=a.getName();if(e&&o.has(e))break;e&&o.add(e);let n=a.getConstructors()[0];if(n){for(let e of n.getParameters()){let t=e.getName();if(!i.has(t)){let n=e.getTypeNode(),r=n?n.getText():e.getType().getText();i.set(t,F(r))}}break}a=Dr(a,t)}for(let t of e.getProperties())if(t.getDecorator(`Inject`)){let e=t.getName();if(!i.has(e)){let n=t.getTypeNode();n&&i.set(e,F(n.getText()))}}return n&&n.setInjMap(r,i),i}function Hr(e,t){for(let n of e){if(!n.assignedTo)continue;let e=RegExp(`\\b${n.assignedTo}\\b`);for(let r of t)if(!r.merged&&!(r.order<=n.order)&&r.conditional&&r.conditionText&&e.test(r.conditionText)){n.guardThrow={branchKind:r.branchKind,callSiteLine:r.callSiteLine,className:r.exceptionClassName,conditionText:r.conditionText,message:r.message},r.merged=!0;break}}let n=t.filter(e=>!e.merged);t.length=0;for(let e of n)t.push(e)}function K(e,t,n,r,i){let a=`${n?.getName()??``}::${e.getName()}`;if(!r&&i){let e=i.getScan(a);if(e)return e}let o={deps:[],sameClassCalls:[],steps:[],throws:[]},s=e.getBody();if(!s)return o;let c=r??new Set,l=e.getName();if(c.has(l))return o;c.add(l);let u=new Map;for(let e of s.getDescendantsOfKind(f.VariableDeclaration)){let n=e.getInitializer();if(n&&n.getKind()===f.PropertyAccessExpression){let r=n.asKindOrThrow(f.PropertyAccessExpression);if(r.getExpression().getKind()===f.ThisKeyword){let n=r.getName();t.has(n)&&u.set(e.getName(),n)}}}let d=[],p=[],m=[],h=0,g=s.getDescendantsOfKind(f.CallExpression),_=s.getDescendantsOfKind(f.ThrowStatement),v=[...g.map(e=>({kind:`call`,node:e})),..._.map(e=>({kind:`throw`,node:e}))];v.sort((e,t)=>e.node.getStart()-t.node.getStart());for(let e of v){if(e.kind===`throw`){let t=z(e.node,s),n=B(e.node,s);p.push({branchGroupId:t.statementLine?`L${t.statementLine}`:null,branchKind:t.branchKind,callSiteLine:e.node.getStartLineNumber(),comment:V(e.node),conditional:t.isConditional,conditionText:t.conditionText,exceptionClassName:wr(e.node),iterationKind:n.iterationKind,iterationLabel:n.iterationLabel,message:Tr(e.node),order:h++});continue}let r=e.node,i=r.getExpression();if(i.getKind()!==f.PropertyAccessExpression)continue;let a=i.asKindOrThrow(f.PropertyAccessExpression),o=a.getName(),l=a.getExpression(),g;if(l.getKind()===f.PropertyAccessExpression){let e=l.asKindOrThrow(f.PropertyAccessExpression);if(e.getExpression().getKind()===f.ThisKeyword){let n=e.getName();t.has(n)&&(g=n)}}if(!g&&l.getKind()===f.Identifier){let e=l.getText(),t=u.get(e);t&&(g=t)}if(g){let e=z(r,s),t=B(r,s);m.push({assignedTo:Er(r),paramName:g,methodName:o,order:h++,callSiteLine:r.getStartLineNumber(),comment:V(r),condInfo:e,iterInfo:t,guardThrow:null});continue}if(l.getKind()===f.ThisKeyword&&n){let e=n.getInstanceMethod(o);if(e&&!c.has(o)){let i=z(r,s),a=B(r,s),l=K(e,t,n,new Set(c));d.push({assignedTo:Er(r),branchGroupId:i.statementLine?`L${i.statementLine}`:null,branchKind:i.branchKind,callSiteLine:r.getStartLineNumber(),childResult:l,comment:V(r),conditional:i.isConditional,conditionText:i.conditionText,iterationKind:a.iterationKind,iterationLabel:a.iterationLabel,methodName:o,order:h++})}}}Hr(m,p);let y=[];if(s.getKind()===f.Block){let e=new Set;for(let t of m)for(let n of g)n.getStartLineNumber()===t.callSiteLine&&e.add(n.getStart());for(let t of p)for(let n of _)n.getStartLineNumber()===t.callSiteLine&&e.add(n.getStart());for(let t of d)for(let n of g)n.getStartLineNumber()===t.callSiteLine&&e.add(n.getStart());let t=s.asKindOrThrow(f.Block).getStatements(),n=[],r=()=>{if(n.length===0)return;let e=n[0].stmt,t=z(e,s),r=B(e,s);y.push({branchGroupId:t.statementLine?`L${t.statementLine}`:null,branchKind:t.branchKind,callSiteLine:e.getStartLineNumber(),comment:V(e),conditional:t.isConditional,conditionText:t.conditionText,iterationKind:r.iterationKind,iterationLabel:r.iterationLabel,order:0,statements:n.map(e=>e.info)}),n=[]};for(let i of t){let t=i.getStart(),a=i.getEnd(),o=!1;for(let n of e)if(n>=t&&n<=a){o=!0;break}if(o){r();continue}if(Br(i,e)){let e=Vr(i);if(e){n.push({info:e,stmt:i});continue}}r()}r()}if(y.length>0){let e=[];for(let t of m)e.push({kind:`call`,item:t});for(let t of p)e.push({kind:`throw`,item:t});for(let t of d)e.push({kind:`scc`,item:t});for(let t of y)e.push({kind:`step`,item:t});e.sort((e,t)=>e.item.callSiteLine-t.item.callSiteLine);let t=0;for(let n of e)n.item.order=t++}let b=[],x=new Map;for(let e of m){let n=t.get(e.paramName);x.has(n)||x.set(n,[]);let r=e.condInfo.isConditional;x.get(n).push({assignedTo:e.assignedTo,branchGroupId:r&&e.condInfo.statementLine?`L${e.condInfo.statementLine}`:null,branchKind:r?e.condInfo.branchKind:null,callSiteLine:e.callSiteLine,comment:e.comment,conditional:r,conditionText:r?e.condInfo.conditionText:null,guardThrow:e.guardThrow,iterationKind:e.iterInfo.iterationKind,iterationLabel:e.iterInfo.iterationLabel,name:e.methodName,order:e.order})}for(let[e,t]of x)t.sort((e,t)=>e.order-t.order),b.push({className:e,methodsCalled:t});let S={deps:b,sameClassCalls:d,steps:y,throws:p};return!r&&i&&i.setScan(a,S),S}function Ur(e){return e.endsWith(`Repository`)?`repository`:e.endsWith(`Guard`)?`guard`:e.endsWith(`Interceptor`)?`interceptor`:e.endsWith(`Pipe`)?`pipe`:e.endsWith(`Filter`)?`filter`:e.endsWith(`Gateway`)?`gateway`:`service`}function q(e,t,n,r,i){let a=[],o=new Set,s=new Map,c=e.deps,l=[],u=[];for(let e of c)if(e.methodsCalled.length===0)u.push(e);else for(let t of e.methodsCalled)l.push({className:e.className,mc:t,dep:e});l.sort((e,t)=>e.mc.order-t.mc.order);for(let e of u){if(r.has(e.className)||o.has(e.className))continue;o.add(e.className),r.add(e.className);let t=n.get(e.className),s=t?t.dependencies.map(e=>({className:e,methodsCalled:[]})):[];a.push({assignedTo:null,branchGroupId:null,branchKind:null,callSiteLine:0,className:e.className,comment:null,conditional:!1,conditionText:null,dependencies:q({deps:s,sameClassCalls:[],steps:[],throws:[]},e.className,n,new Set(r),i),endLine:0,filePath:t?.filePath??``,guardThrow:null,iterationKind:null,iterationLabel:null,line:0,methodName:null,order:0,parameters:[],returnType:null,stepStatements:[],throwMessage:null,totalMethods:t?.publicMethodCount??0,type:Ur(e.className)})}let d=new Map;for(let{className:e,mc:t}of l)d.has(e)||d.set(e,new Set),d.get(e).add(t.name);for(let{className:e,mc:t}of l){if(r.has(e))continue;let c=n.get(e),l=!o.has(e);l&&o.add(e);let u=[];if(l&&c){let t=new Set(r);t.add(e);let a=G(c.classDeclaration,n,i),o=[],l=0,f=[],p=[],m=d.get(e)??new Set;for(let e of m){let t=H(c.classDeclaration,e,n,i);if(!t)continue;let r=K(t,a,c.classDeclaration,void 0,i),s=[];for(let e of r.deps)for(let t of e.methodsCalled)s.push({kind:`dep`,depClassName:e.className,m:t});for(let e of r.throws)s.push({kind:`throw`,t:e});for(let e of r.sameClassCalls)s.push({kind:`scc`,scc:e});function u(e){return e.kind===`dep`?e.m.order:e.kind===`throw`?e.t.order:e.scc.order}s.sort((e,t)=>u(e)-u(t));for(let e of s)e.kind===`dep`?o.push({assignedTo:e.m.assignedTo,depClassName:e.depClassName,methodName:e.m.name,order:l++,callSiteLine:e.m.callSiteLine,comment:e.m.comment,conditional:e.m.conditional,branchKind:e.m.conditional?e.m.branchKind:null,conditionText:e.m.conditional?e.m.conditionText:null,branchGroupId:e.m.conditional?e.m.branchGroupId:null,guardThrow:e.m.guardThrow,iterationKind:e.m.iterationKind,iterationLabel:e.m.iterationLabel}):e.kind===`throw`?p.push({...e.t,order:l++}):f.push(e.scc)}Hr(o,p);let h=new Map;for(let e of o)h.has(e.depClassName)||h.set(e.depClassName,[]),h.get(e.depClassName).push({assignedTo:e.assignedTo,branchGroupId:e.branchGroupId,branchKind:e.branchKind,callSiteLine:e.callSiteLine,comment:e.comment,conditional:e.conditional,conditionText:e.conditionText,guardThrow:e.guardThrow,iterationKind:e.iterationKind,iterationLabel:e.iterationLabel,name:e.methodName,order:e.order});let g=[];for(let[e,t]of h)t.sort((e,t)=>e.order-t.order),g.push({className:e,methodsCalled:t});u=q({deps:g,sameClassCalls:f,steps:[],throws:p},e,n,t,i),s.set(e,u)}else l||(u=s.get(e)??[]);let f=0,p=0,m=null,h=[];if(c){let e=H(c.classDeclaration,t.name,n,i);e&&(f=e.getStartLineNumber(),p=e.getEndLineNumber(),m=W(e),h=Fr(e))}a.push({assignedTo:t.assignedTo,branchGroupId:t.branchGroupId,branchKind:t.branchKind,callSiteLine:t.callSiteLine,className:e,comment:t.comment,conditional:t.conditional,conditionText:t.conditionText,dependencies:u,endLine:p,filePath:c?.filePath??``,guardThrow:t.guardThrow,iterationKind:t.iterationKind,iterationLabel:t.iterationLabel,line:f,methodName:t.name,order:t.order,parameters:h,returnType:m,stepStatements:[],throwMessage:null,totalMethods:c?.publicMethodCount??0,type:Ur(e)})}let f=n.get(t);for(let o of e.sameClassCalls){let e=0,s=0,c=null,l=[];if(f){let t=H(f.classDeclaration,o.methodName,n,i);t&&(e=t.getStartLineNumber(),s=t.getEndLineNumber(),c=W(t),l=Fr(t))}let u=q(o.childResult,t,n,new Set(r),i);a.push({assignedTo:o.assignedTo,branchGroupId:o.branchGroupId,branchKind:o.branchKind,callSiteLine:o.callSiteLine,className:t,comment:o.comment,conditional:o.conditional,conditionText:o.conditionText,dependencies:u,endLine:s,filePath:f?.filePath??``,guardThrow:null,iterationKind:o.iterationKind,iterationLabel:o.iterationLabel,line:e,methodName:o.methodName,order:o.order,parameters:l,returnType:c,stepStatements:[],throwMessage:null,totalMethods:f?.publicMethodCount??0,type:Ur(t)})}for(let t of e.throws)a.push({assignedTo:null,branchGroupId:t.branchGroupId,branchKind:t.branchKind,callSiteLine:t.callSiteLine,className:t.exceptionClassName,comment:t.comment,conditional:t.conditional,conditionText:t.conditionText,dependencies:[],endLine:t.callSiteLine,filePath:f?.filePath??``,guardThrow:null,iterationKind:t.iterationKind,iterationLabel:t.iterationLabel,line:t.callSiteLine,methodName:null,order:t.order,parameters:[],returnType:null,stepStatements:[],throwMessage:t.message,totalMethods:0,type:`throw`});for(let t of e.steps)a.push({assignedTo:null,branchGroupId:t.branchGroupId,branchKind:t.branchKind,callSiteLine:t.callSiteLine,className:`local`,comment:t.comment,conditional:t.conditional,conditionText:t.conditionText,dependencies:[],endLine:t.callSiteLine,filePath:f?.filePath??``,guardThrow:null,iterationKind:t.iterationKind,iterationLabel:t.iterationLabel,line:t.callSiteLine,methodName:null,order:t.order,parameters:[],returnType:null,stepStatements:t.statements,throwMessage:null,totalMethods:0,type:`step`});return a.sort((e,t)=>e.order-t.order),a}function Wr(e){for(let t of e.getDecorators()){let n=t.getName();if(br.has(n))return{httpMethod:n.toUpperCase(),path:e.getName()}}}function Gr(e,t,n,r){let i=[];for(let a of e.getClasses()){let e=E(a),o=T(a,`Resolver`);if(!(e||o))continue;let s=e?Or(a):``,c=a.getName()??(e?`AnonymousController`:`AnonymousResolver`),l=G(a,n,r);for(let o of a.getMethods()){let u=e?kr(o):Wr(o);if(!u)continue;let d=e?Ar(s,u.path):u.path,f=q(K(o,l,a,void 0,r),c,n,new Set,r),p=Nr(o),m=W(o);i.push({controllerClass:c,dependencies:f,endLine:o.getEndLineNumber(),filePath:t,handlerMethod:o.getName(),httpMethod:u.httpMethod,line:o.getStartLineNumber(),returnType:m,routePath:d,swagger:p})}}return i}function Kr(e,t,n){let r=[],i=new L;for(let a of t){let t=e.getSourceFile(a);t&&r.push(...Gr(t,a,n,i))}return{endpoints:r}}function qr(e,t,n,r,i,a,o){if(i>10)return[];let s=e.getBody();if(!s)return[];let c=[],l=s.getDescendantsOfKind(f.CallExpression);for(let e of l){let s=e.getExpression();if(s.getKind()!==f.PropertyAccessExpression)continue;let l=s.asKindOrThrow(f.PropertyAccessExpression),u=l.getName(),d=l.getExpression();if(d.getKind()===f.PropertyAccessExpression){let e=d.asKindOrThrow(f.PropertyAccessExpression);if(e.getExpression().getKind()!==f.ThisKeyword)continue;let a=e.getName(),s=t.get(a);if(!s)continue;let l=`${s}.${u}`;if(r.has(l)){c.push({calls:[],circular:!0,className:s,filePath:``,line:0,methodName:u});continue}r.add(l);let p=n.get(s),m=[],h=``,g=0;if(p){h=p.filePath;let e=p.classDeclaration.getInstanceMethod(u);e&&(g=e.getStartLineNumber(),m=qr(e,G(p.classDeclaration,void 0,o),n,new Set(r),i+1,p.classDeclaration,o))}c.push({calls:m,className:s,filePath:h,line:g,methodName:u})}else if(d.getKind()===f.ThisKeyword&&a){let e=a.getInstanceMethod(u);if(!e)continue;let s=`${a.getName()??`Anonymous`}.${u}`;if(r.has(s))continue;r.add(s);let l=qr(e,t,n,new Set(r),i+1,a,o);c.push(...l)}}return c}function Jr(e,t,n){let r=n.getSourceFile(e.filePath);if(!r)return[];let i=r.getClasses().find(t=>t.getName()===e.controllerClass);if(!i)return[];let a=i.getInstanceMethod(e.handlerMethod);if(!a)return[];let o=new L;return qr(a,G(i,void 0,o),t,new Set,0,i,o)}function Yr(e,t,n,r){e.endpoints=e.endpoints.filter(e=>e.filePath!==n);let i=t.getSourceFile(n);if(!i)return;let a=new L;e.endpoints.push(...Gr(i,n,r,a))}const Xr=new Set([`pgTable`,`mysqlTable`,`sqliteTable`]),Zr=new Set([`serial`,`bigserial`,`smallserial`]),Qr=/=>\s*(\w+)/;function $r(e){let t={type:`unknown`,isPrimary:!1,isNullable:!0,isGenerated:!1,isUnique:!1};function n(e){if(e.getKind()===f.CallExpression){let r=e.asKindOrThrow(f.CallExpression),i=r.getExpression();if(i.getKind()===f.PropertyAccessExpression){let e=i.asKindOrThrow(f.PropertyAccessExpression);switch(e.getName()){case`primaryKey`:t.isPrimary=!0;break;case`notNull`:t.isNullable=!1;break;case`unique`:t.isUnique=!0;break;case`default`:{let e=r.getArguments();e.length>0&&(t.defaultValue=e[0].getText().replace(/['"]/g,``));break}case`defaultNow`:t.defaultValue=`now()`;break;case`generatedAlwaysAsIdentity`:case`autoincrement`:t.isGenerated=!0;break;case`references`:{let e=r.getArguments();if(e.length>0){let n=e[0].getText(),r=Qr.exec(n);if(r&&(t.reference={toEntity:r[1]}),e.length>1){let n=e[1];if(n.getKind()===f.ObjectLiteralExpression){let e=n.asKindOrThrow(f.ObjectLiteralExpression);for(let n of e.getProperties())if(n.getKind()===f.PropertyAssignment){let e=n.asKindOrThrow(f.PropertyAssignment);if(e.getName()===`onDelete`){let n=e.getInitializer()?.getText();n&&(t.reference.onDelete=n.replace(/['"]/g,``))}}}}}break}default:break}n(e.getExpression())}else if(i.getKind()===f.Identifier){let e=i.getText();t.type=e,Zr.has(e)&&(t.isGenerated=!0)}}}return n(e),t}function ei(e){let t=[];for(let n of e.getProperties()){if(n.getKind()!==f.PropertyAssignment)continue;let e=n.asKindOrThrow(f.PropertyAssignment),r=e.getName(),i=e.getInitializer();if(!i)continue;let a=$r(i);t.push({name:r,type:a.type,isPrimary:a.isPrimary,isNullable:a.isNullable,isGenerated:a.isGenerated,isUnique:a.isUnique,defaultValue:a.defaultValue})}return t}function ti(e,t){let n=[];for(let r of e.getProperties()){if(r.getKind()!==f.PropertyAssignment)continue;let e=r.asKindOrThrow(f.PropertyAssignment),i=e.getName(),a=e.getInitializer();if(!a)continue;let o=$r(a);o.reference&&n.push({type:`many-to-one`,fromEntity:t,toEntity:o.reference.toEntity,propertyName:i,isNullable:o.isNullable,...o.reference.onDelete?{onDelete:o.reference.onDelete}:{}})}return n}function ni(e){let t=[],n=e.getDescendantsOfKind(f.CallExpression);for(let e of n){let n=e.getExpression();if(n.getKind()!==f.PropertyAccessExpression||n.asKindOrThrow(f.PropertyAccessExpression).getName()!==`on`)continue;let r=[];for(let t of e.getArguments())if(t.getKind()===f.PropertyAccessExpression){let e=t.asKindOrThrow(f.PropertyAccessExpression);r.push(e.getName())}if(r.length===0)continue;let i=e.getText().includes(`uniqueIndex`);t.push({columns:r,isUnique:i})}return t}function ri(e){let t=[],n=e.getFilePath();for(let r of e.getDescendantsOfKind(f.VariableDeclaration)){let e=r.getInitializer();if(!e||e.getKind()!==f.CallExpression)continue;let i=e.asKindOrThrow(f.CallExpression),a=i.getExpression();if(a.getKind()!==f.Identifier)continue;let o=a.getText();if(!Xr.has(o))continue;let s=i.getArguments();if(s.length<2)continue;let c=s[0],l=r.getName();c.getKind()===f.StringLiteral&&(l=c.asKindOrThrow(f.StringLiteral).getLiteralValue());let u=s[1];if(u.getKind()!==f.ObjectLiteralExpression)continue;let d=u.asKindOrThrow(f.ObjectLiteralExpression),p=r.getName(),m=ei(d),h=ti(d,p),g;if(s.length>=3&&(g=ni(s[2]),g))for(let e of g)for(let t of e.columns){let e=m.find(e=>e.name===t);e&&(e.hasIndex=!0)}t.push({name:p,tableName:l,filePath:n,columns:m,relations:h,indexes:g})}return t}const ii={supportsIncrementalUpdate:!0,extract(e,t){let n=[];for(let r of t){let t=e.getSourceFile(r);t&&n.push(...ri(t))}return n}},ai=/^model\s+(\w+)\s*\{/,oi=/^enum\s+(\w+)\s*\{/,si=/^(\w+)\s+(\w+)(\?)?(\[\])?(.*)$/,ci=/@(\w+)(\((?:[^()]*|\([^()]*\))*\))?/g,li=/@default\(((?:[^()]*|\([^()]*\))*)\)/,ui=/^@@map\(\s*"([^"]+)"\s*\)/;function di(r){let i=a(r,`prisma`,`schema.prisma`);if(e(i)){let e=a(r,`prisma`),t=n(e).filter(e=>e.endsWith(`.prisma`));return t.length>1?t.map(t=>a(e,t)):[i]}let o=a(r,`schema.prisma`);if(e(o))return[o];try{let n=a(r,`package.json`),i=JSON.parse(t(n,`utf-8`)).prisma?.schema;if(i){let t=a(r,i);if(e(t))return[t]}}catch{}return[]}function fi(e){let n=[],r=new Set;for(let i of e){let e;try{e=t(i,`utf-8`)}catch{continue}let a=e.split(`
|
|
5
|
+
`),o=null,s=[],c=[],l=[],u;for(let e of a){let t=e.trim(),a=ai.exec(t);if(a){o={type:`model`,name:a[1]},s=[],c=[],l=[],u=void 0;continue}let d=oi.exec(t);if(d){o={type:`enum`,name:d[1]},r.add(d[1]);continue}if(t===`}`){o?.type===`model`&&n.push({name:o.name,fields:s,indexes:c,compositeIdColumns:l,filePath:i,tableName:u}),o=null,s=[],c=[],l=[],u=void 0;continue}if(o?.type===`model`&&t&&!t.startsWith(`//`)){if(t.startsWith(`@@`)){let e=mi.exec(t);e&&(l=e[1].split(`,`).map(e=>e.trim()));let n=hi(t);n&&c.push(n);let r=ui.exec(t);r&&(u=r[1]);continue}let e=gi(t);e&&s.push(e)}}}return{models:n,enums:r}}const pi=/^@@(index|unique)\(\[([^\]]*)\]\)/,mi=/^@@id\(\[([^\]]*)\]\)/;function hi(e){let t=pi.exec(e);if(!t)return null;let n=t[1]===`unique`,r=t[2].split(`,`).map(e=>e.trim()).filter(Boolean);return r.length===0?null:{columns:r,isUnique:n}}function gi(e){let t=si.exec(e);if(!t)return null;let n=t[1],r=t[2],i=t[3]===`?`,a=t[4]===`[]`,o=t[5]??``,s=[],c=new RegExp(ci.source,ci.flags),l=c.exec(o);for(;l!==null;)s.push(`@${l[1]}${l[2]??``}`),l=c.exec(o);return{name:n,type:r,isOptional:i,isList:a,attributes:s}}function _i(e){let t=e.attributes.some(e=>e.startsWith(`@id`)),n=e.attributes.some(e=>e.startsWith(`@unique`)),r=e.attributes.find(e=>e.startsWith(`@default(`)),i=!1,a;if(r){let e=li.exec(r);if(e){let t=e[1];a=t,(t===`autoincrement()`||t===`uuid()`||t===`cuid()`||t===`dbgenerated()`)&&(i=!0)}}return{name:e.name,type:e.type,isPrimary:t,isNullable:e.isOptional,isGenerated:i,isUnique:n,defaultValue:a}}const vi=/onDelete:\s*(\w+)/;function yi(e){let t=e.attributes.find(e=>e.startsWith(`@relation`));if(!t)return;let n=vi.exec(t);return n?n[1]:void 0}function bi(e,t){let n=new Set(e.map(e=>e.name));return e.map(r=>{let i=[],a=[],o=new Set;for(let e of r.indexes)for(let t of e.columns)o.add(t);let s=new Set(r.compositeIdColumns);for(let c of r.fields)if(n.has(c.type)&&!t.has(c.type)){let t;t=c.isList?`one-to-many`:`many-to-one`;let n=c.isOptional;c.isList&&e.find(e=>e.name===c.type)?.fields.find(e=>e!==c&&e.type===r.name&&e.isList)&&(t=`many-to-many`);let i=yi(c);a.push({type:t,fromEntity:r.name,toEntity:c.type,propertyName:c.name,isNullable:n??!1,...i?{onDelete:i}:{}})}else if(!c.attributes.some(e=>e.startsWith(`@relation`))){let e=_i(c);s.has(c.name)&&(e.isPrimary=!0),(o.has(c.name)||c.attributes.some(e=>e.startsWith(`@unique`)))&&(e.hasIndex=!0),i.push(e)}return{name:r.name,tableName:r.tableName??r.name,filePath:r.filePath,columns:i,relations:a,indexes:r.indexes}})}const xi={supportsIncrementalUpdate:!1,extract(e,t,n){let r=di(n);if(r.length===0)return[];let{models:i,enums:a}=fi(r);return bi(i,a)}},Si=/=>\s*(\w+)/,Ci=new Set([`Column`,`PrimaryColumn`,`PrimaryGeneratedColumn`,`CreateDateColumn`,`UpdateDateColumn`,`DeleteDateColumn`,`VersionColumn`]),wi={OneToOne:`one-to-one`,OneToMany:`one-to-many`,ManyToOne:`many-to-one`,ManyToMany:`many-to-many`};function J(e){let t=e.getArguments();for(let e of t)if(e.getKind()===f.ObjectLiteralExpression){let t={},n=e.asKind(f.ObjectLiteralExpression);if(!n)continue;for(let e of n.getProperties())if(e.getKind()===f.PropertyAssignment){let n=e.asKind(f.PropertyAssignment);n&&(t[n.getName()]=n.getInitializer()?.getText()??``)}return t}return null}function Ti(e){let t=e.getArguments();if(t.length===0)return null;let n=t[0];return n.getKind()===f.StringLiteral?n.asKind(f.StringLiteral)?.getLiteralValue()??null:null}function Ei(e){let t=e.getDecorator(`Entity`);if(!t)return e.getName()??`UnknownEntity`;let n=Ti(t);if(n)return n;let r=J(t);return r?.name?r.name.replace(/['"]/g,``):e.getName()??`UnknownEntity`}function Di(e,t){let n=t.getName(),r=n===`PrimaryColumn`||n===`PrimaryGeneratedColumn`,i=n===`PrimaryGeneratedColumn`||n===`CreateDateColumn`||n===`UpdateDateColumn`||n===`DeleteDateColumn`||n===`VersionColumn`,a=`unknown`,o=!1,s=!1,c,l=Ti(t);l&&(a=l);let u=J(t);return u&&(u.type&&(a=u.type.replace(/['"]/g,``)),u.nullable===`true`&&(o=!0),u.unique===`true`&&(s=!0),u.default!==void 0&&(c=u.default)),a===`unknown`&&(n===`PrimaryGeneratedColumn`?a=`integer`:n===`CreateDateColumn`||n===`UpdateDateColumn`||n===`DeleteDateColumn`?a=`timestamp`:n===`VersionColumn`&&(a=`integer`)),{name:e,type:a,isPrimary:r,isNullable:o,isGenerated:i,isUnique:s,defaultValue:c}}function Oi(e,t,n){let r=wi[n.getName()];if(!r)return null;let i=n.getArguments();if(i.length===0)return null;let a=i[0].getText(),o=Si.exec(a);if(!o)return null;let s=o[1],c=J(n),l=c?.nullable===`true`,u=c?.onDelete?.replace(/['"]/g,``);return{type:r,fromEntity:e,toEntity:s,propertyName:t,isNullable:l,...u?{onDelete:u}:{}}}function ki(e){if(!T(e,`Entity`))return null;let t=e.getName();if(!t)return null;let n=Ei(e),r=e.getSourceFile().getFilePath(),i=[],a=[],o=[];for(let t of e.getDecorators())if(t.getName()===`Index`){let e=t.getArguments();for(let n of e)if(n.getKind()===f.ArrayLiteralExpression){let e=n.asKind(f.ArrayLiteralExpression);if(e){let n=e.getElements().map(e=>e.getKind()===f.StringLiteral?e.asKind(f.StringLiteral)?.getLiteralValue()??``:``).filter(Boolean);if(n.length>0){let e=J(t);o.push({columns:n,isUnique:e?.unique===`true`})}}}}let s=new Set;for(let n of e.getProperties()){let e=n.getName(),r=n.getDecorators(),c=r.some(e=>e.getName()===`Index`);c&&(s.add(e),o.push({columns:[e],isUnique:!1}));for(let n of r){let r=n.getName();if(Ci.has(r)){let t=Di(e,n);c&&(t.hasIndex=!0),i.push(t);break}if(r in wi){let r=Oi(t,e,n);r&&a.push(r);break}}}for(let e of o)for(let t of e.columns){let e=i.find(e=>e.name===t);e&&(e.hasIndex=!0)}return{name:t,tableName:n,filePath:r,columns:i,relations:a,indexes:o}}const Ai={prisma:xi,typeorm:{supportsIncrementalUpdate:!0,extract(e,t){let n=[];for(let r of t){let t=e.getSourceFile(r);if(t)for(let e of t.getClasses()){let t=ki(e);t&&n.push(t)}}return n}},drizzle:ii};function Y(e,t,n,r){let i={entities:new Map,relations:[],orm:n??`unknown`};if(!n)return i;let a=Ai[n];if(!a)return i;let o=a.extract(e,t,r),s=new Map,c=[];for(let e of o)s.set(e.name,e),c.push(...e.relations);return{entities:s,relations:c,orm:n}}function ji(e){return{entities:[...e.entities.values()],relations:e.relations,orm:e.orm}}function Mi(e,t,n,r){for(let[t,r]of e.entities)r.filePath===n&&e.entities.delete(t);let i=Ai[e.orm];if(!i?.supportsIncrementalUpdate){Ni(e);return}let a=i.extract(t,[n],r);for(let t of a)e.entities.set(t.name,t);Ni(e)}function Ni(e){let t=[];for(let n of e.entities.values())t.push(...n.relations);e.relations=t}async function X(e,t){let{config:n,fileRules:r,projectRules:i,schemaRules:a}=t,[o,s]=await Promise.all([ur(e,n),pe(e)]),c=fr(o),l=ke(e),u=Ne(c,o,l),d=gr(c,o);return{astProject:c,config:n,endpointGraph:Kr(c,o,d),fileRules:r,files:o,moduleGraph:u,pathAliases:l,project:s,projectRules:i,providers:d,schemaGraph:Y(c,o,s.orm,e),schemaRules:a,targetPath:e}}async function Pi(e,t={}){let n=await P(e,t.config);return{context:await X(e,n),customRuleWarnings:n.customRuleWarnings}}function Fi(e,t){let n=e.astProject.getSourceFile(t);n&&e.astProject.removeSourceFile(n),e.astProject.addSourceFileAtPath(t),e.files.includes(t)||e.files.push(t),He(e.moduleGraph,e.astProject,t,e.pathAliases),_r(e.providers,e.astProject,t),Yr(e.endpointGraph,e.astProject,t,e.providers),e.schemaGraph&&Mi(e.schemaGraph,e.astProject,t,e.targetPath)}async function Ii(e,t,n){let{config:r,combinedRules:i}=t,o=await dr(e,n,r),s=await Promise.all([...o.entries()].filter(([,e])=>e.length>0).map(async([t,o])=>{let s=a(e,n.projects.get(t)),[c,l]=await Promise.all([pe(s),xe(s,r)]),u=fr(o),d=ke(s),f=Ne(u,o,d),p=gr(u,o),m=Kr(u,o,p),h=Y(u,o,c.orm,s),{fileRules:g,projectRules:_,schemaRules:v}=tr(er(l,i));return[t,{astProject:u,config:l,endpointGraph:m,fileRules:g,files:o,moduleGraph:f,pathAliases:d,project:c,projectRules:_,providers:p,schemaGraph:h,schemaRules:v,targetPath:s}]}));return{subProjects:new Map(s)}}const Li=e=>h.makeRe(e,{windows:!1}),Ri=/\\/g,zi=/\/$/,Bi=(e,t,n)=>{let r=new Set(Array.isArray(t.ignore?.rules)?t.ignore.rules:[]),i=Array.isArray(t.ignore?.files)?t.ignore.files.map(Li):[];if(r.size===0&&i.length===0)return e;let a=n.replace(Ri,`/`).replace(zi,``);return e.filter(e=>{if(r.has(e.rule))return!1;let t=e.filePath.replace(Ri,`/`),n=t.startsWith(`${a}/`)?t.slice(a.length+1):t;return!i.some(e=>e.test(n))})};function Vi(e,t,n,r){let i=[],a=[],o=e.getSourceFile(t);if(!o)return{diagnostics:i,errors:a};let s=o.getFullText().split(`
|
|
6
|
+
`);for(let e of n){let n={config:r,sourceFile:o,filePath:t,report(t){let n=[],r=Math.max(0,t.line-6),a=Math.min(s.length,t.line+5);for(let e=r;e<a;e++)n.push({line:e+1,text:s[e]});i.push({...t,rule:e.meta.id,category:e.meta.category,scope:`file`,severity:e.meta.severity,sourceLines:n})}};try{e.check(n)}catch(t){a.push({ruleId:e.meta.id,error:t})}}return{diagnostics:i,errors:a}}function Hi(e,t,n,r){let i=[],a=[];for(let o of t){let t=Vi(e,o,n,r);i.push(...t.diagnostics),a.push(...t.errors)}return{diagnostics:i,errors:a}}function Ui(e,t,n,r){let i=[],a=[];for(let o of n){let n={project:e,files:t,moduleGraph:r.moduleGraph,providers:r.providers,config:r.config,report(e){i.push({...e,rule:o.meta.id,category:o.meta.category,scope:`project`,severity:o.meta.severity})}};try{o.check(n)}catch(e){a.push({ruleId:o.meta.id,error:e})}}return{diagnostics:i,errors:a}}function Wi(e,t){let n=[],r=[];for(let i of t){let t={schemaGraph:e,orm:e.orm,report(e){n.push({...e,rule:i.meta.id,category:i.meta.category,scope:`schema`,severity:i.meta.severity})}};try{i.check(t)}catch(e){r.push({ruleId:i.meta.id,error:e})}}return{diagnostics:n,errors:r}}function Gi(e){return e instanceof Error?e.message:String(e)}function Z(e,t,n){return{diagnostics:Bi(e,n.config,n.targetPath),errors:t.map(e=>({ruleId:e.ruleId,error:Gi(e.error)}))}}function Ki(e,t){let n=Hi(e.astProject,[t],e.fileRules,e.config);return Z(n.diagnostics,n.errors,e)}function qi(e){let t=Hi(e.astProject,e.files,e.fileRules,e.config);return Z(t.diagnostics,t.errors,e)}function Ji(e){let t={moduleGraph:e.moduleGraph,providers:e.providers,config:e.config},n=Ui(e.astProject,e.files,e.projectRules,t),{diagnostics:r,errors:i}=Z(n.diagnostics,n.errors,e),a=Yi(e);return r.push(...a.diagnostics),i.push(...a.errors),{diagnostics:r,errors:i}}function Yi(e){if(!e.schemaGraph||e.schemaRules.length===0||e.schemaGraph.entities.size===0)return{diagnostics:[],errors:[]};let t=Wi(e.schemaGraph,e.schemaRules);return Z(t.diagnostics,t.errors,e)}function Q(e){let t=u.now(),n=qi(e),r=Ji(e),i=u.now()-t;return{diagnostics:[...n.diagnostics,...r.diagnostics],elapsedMs:i,ruleErrors:[...n.errors,...r.errors]}}function Xi(e){return e>=90?`Excellent`:e>=75?`Good`:e>=50?`Fair`:e>=25?`Poor`:`Critical`}const Zi={error:3,warning:1.5,info:.5},Qi={security:1.5,correctness:1.3,schema:1.1,architecture:1,performance:.8};function $i(e,t){if(t===0)return{value:100,label:Xi(100)};let n=0;for(let t of e){let e=Zi[t.severity],r=Qi[t.category];n+=e*r}let r=n/t,i=Math.max(0,Math.min(100,Math.round(100-r*10)));return{value:i,label:Xi(i)}}function ea(e){let t={total:0,errors:0,warnings:0,info:0,byCategory:{security:0,performance:0,correctness:0,architecture:0,schema:0}};for(let n of e)t.total++,n.severity===`error`?t.errors++:n.severity===`warning`?t.warnings++:t.info++,t.byCategory[n.category]++;return t}function $(e,t,n=[]){let{diagnostics:r,ruleErrors:i,elapsedMs:a}=t,o=e.schemaGraph??Y(e.astProject,e.files,e.project.orm,e.targetPath),s=$i(r,e.files.length),c=ea(r);return{result:{score:s,diagnostics:r,endpoints:e.endpointGraph,project:{...e.project,fileCount:e.files.length,moduleCount:e.moduleGraph.modules.size},summary:c,ruleErrors:i,elapsedMs:a,schema:ji(o)},moduleGraph:e.moduleGraph,schemaGraph:o,customRuleWarnings:n,files:e.files,providers:e.providers}}function ta(e,t,n,r){let i=[],a=[],o=[],s=new Map,c=[],l=0,u=[],d=[],f=``;for(let[n,r]of e.subProjects){let e=$(r,t.get(n));i.push({name:n,result:e.result}),s.set(n,e.moduleGraph),a.push(...e.result.diagnostics),o.push(...e.result.ruleErrors),l+=e.result.project.fileCount,e.result.endpoints&&c.push(...e.result.endpoints.endpoints),e.result.schema&&(u.push(...e.result.schema.entities),d.push(...e.result.schema.relations),e.result.schema.orm&&e.result.schema.orm!==`unknown`&&(f=e.result.schema.orm))}let p=$i(a,l),m=ea(a);return{moduleGraphs:s,customRuleWarnings:n,result:{isMonorepo:!0,subProjects:i,combined:{score:p,diagnostics:a,endpoints:c.length>0?{endpoints:c}:void 0,project:{name:`monorepo`,nestVersion:i[0]?.result.project.nestVersion??null,orm:f||(i[0]?.result.project.orm??null),framework:i[0]?.result.project.framework??null,fileCount:l,moduleCount:i.reduce((e,t)=>e+t.result.project.moduleCount,0)},summary:m,ruleErrors:o,elapsedMs:r,schema:u.length>0?{entities:u,relations:d,orm:f||`unknown`}:void 0},elapsedMs:r}}}async function na(e,t,n){let r=u.now(),i=await Ii(e,t,n),a=new Map;for(let[e,t]of i.subProjects)a.set(e,Q(t));let o=u.now()-r;return ta(i,a,t.customRuleWarnings,o)}async function ra(e,t={}){let n=await P(e,t.config),r=await fe(e);if(r)return{isMonorepo:!0,monorepo:await na(e,n,r)};let i=await X(e,n);return{isMonorepo:!1,single:$(i,Q(i),n.customRuleWarnings)}}function ia(e){return`line`in e}function aa(e){return`entity`in e}function oa(t){if(!t||t.trim()===``)throw new y(`Path must be a non-empty string. Received an empty path.`);let n=s(t);if(!e(n))throw new y(`Path does not exist: ${n}`);if(!r(n).isDirectory())throw new y(`Path must be a directory, not a file: ${n}`);return n}async function sa(e,t={}){let n=oa(e),r=await P(n,t.config),i=await X(n,r),{result:a}=$(i,Q(i),r.customRuleWarnings);return a}async function ca(e,t={}){let n=oa(e),r=await P(n,t.config),i=await fe(n);if(!i){let e=await X(n,r),{result:t}=$(e,Q(e),r.customRuleWarnings);return{isMonorepo:!1,subProjects:[{name:`default`,result:t}],combined:t,elapsedMs:t.elapsedMs}}let{result:a}=await na(n,r,i);return a}export{_ as ConfigurationError,g as NestjsDoctorError,v as ScanError,y as ValidationError,ra as autoScan,X as buildAnalysisContext,Kr as buildEndpointGraph,$ as buildResult,qi as checkAllFiles,Ki as checkFile,Ji as checkProject,Yi as checkSchema,sa as diagnose,ca as diagnoseMonorepo,Y as extractSchema,Xn as getRules,ia as isCodeDiagnostic,aa as isSchemaDiagnostic,Pi as prepareAnalysis,P as resolveScanConfig,Jr as traceEndpointCalls,Yr as updateEndpointGraphForFile,Fi as updateFile,He as updateModuleGraphForFile,_r as updateProvidersForFile};
|
package/dist/cli/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import{createRequire as e}from"node:module";import{defineCommand as t,runMain as n}from"citty";import{readFile as r}from"node:fs/promises";import{dirname as i,join as a,relative as o,resolve as s}from"node:path";import{glob as c}from"tinyglobby";import{performance as l}from"node:perf_hooks";import{Project as u,SyntaxKind as d,ts as f}from"ts-morph";import{existsSync as p,readFileSync as m,readdirSync as h,statSync as g}from"node:fs";import{createJiti as _}from"jiti";import v from"picomatch";import y from"picocolors";import b from"ora";const x=/^packages\s*:/,ee=/^packages\s*:\s*\[(.+)\]/,te=/^\S/,ne=/^-\s+['"]?([^'"]+)['"]?\s*$/,re=/^['"]|['"]$/g;function ie(e){let t=[],n=e.split(`
|
|
3
|
-
`),r=!1;for(let e of n){let n=e.trim();if(x.test(n)){let e=n.match(ee);if(e){for(let n of e[1].split(`,`)){let e=n.trim().replace(re,``);e&&t.push(e)}return t}r=!0;continue}if(r){if(te.test(e)&&n!==``)break;let r=n.match(ne);r&&t.push(r[1])}}return t}async function ae(e){let t=a(e,`nest-cli.json`);try{let e=await r(t,`utf-8`),n=JSON.parse(e);if(!(n.monorepo&&n.projects))return null;let i=new Map;for(let[e,t]of Object.entries(n.projects)){let n=t.root??e;i.set(e,n)}return i.size===0?null:{projects:i}}catch{return null}}function oe(e){let t={...e.dependencies,...e.devDependencies,...e.peerDependencies};return!!(t[`@nestjs/core`]||t[`@nestjs/common`])}async function S(e,t){let n=await c(t.map(e=>`${e}/package.json`),{cwd:e,absolute:!0,ignore:[`**/node_modules/**`]}),a=new Map;for(let t of n)try{let n=await r(t,`utf-8`),s=JSON.parse(n);if(oe(s)){let n=o(e,i(t)),r=s.name??n;a.set(r,n)}}catch{}return a.size===0?null:{projects:a}}async function se(e){let t=a(e,`pnpm-workspace.yaml`),n;try{n=await r(t,`utf-8`)}catch{return null}let i=ie(n);return i.length===0?null:S(e,i)}function ce(e){let t=e.workspaces;if(!t)return[];if(Array.isArray(t))return t.filter(e=>typeof e==`string`);if(typeof t==`object`&&t){let e=t;if(Array.isArray(e.packages))return e.packages.filter(e=>typeof e==`string`)}return[]}async function le(e){let t=a(e,`package.json`),n;try{n=await r(t,`utf-8`)}catch{return null}let i=ce(JSON.parse(n));return i.length===0?null:S(e,i)}async function ue(e){let t=a(e,`lerna.json`),n;try{n=await r(t,`utf-8`)}catch{return null}let i=JSON.parse(n);if(i.useWorkspaces)return null;let o=i.packages??[`packages/*`];return o.length===0?null:S(e,o)}async function de(e){let t=a(e,`nx.json`);try{await r(t,`utf-8`)}catch{return null}let n=await c([`**/project.json`],{cwd:e,absolute:!0,ignore:[`node_modules/**`]}),s=new Map;for(let t of n){let n=i(t),c=o(e,n);if(c===``)continue;let l=a(n,`package.json`);try{let e=await r(l,`utf-8`),t=JSON.parse(e);if(oe(t)){let e=t.name??c;s.set(e,c)}}catch{}}return s.size===0?null:{projects:s}}async function fe(e){try{return await r(a(e,`pnpm-workspace.yaml`),`utf-8`),!0}catch{return!1}}async function pe(e){let t=await ae(e);if(t)return t;let n=await se(e);if(n)return n;if(!await fe(e)){let t=await le(e);if(t)return t}return await de(e)||ue(e)}async function me(e){for(let t of[`lerna.json`,`turbo.json`,`nx.json`,`pnpm-workspace.yaml`])try{return await r(a(e,t),`utf-8`),!0}catch{}try{let t=await r(a(e,`package.json`),`utf-8`);if(JSON.parse(t).workspaces)return!0}catch{}return!1}async function he(e){let t=a(e,`package.json`),n={};try{let e=await r(t,`utf-8`);n=JSON.parse(e)}catch{}let i={...n.dependencies,...n.devDependencies},o=ge(i[`@nestjs/core`]),s=_e(i),c=ve(i);return{name:n.name??`unknown`,nestVersion:o,orm:s,framework:c,moduleCount:0,fileCount:0}}function ge(e){return e?e.replace(/[\^~>=<]/g,``):null}function _e(e){return e[`@prisma/client`]?`prisma`:e.typeorm?`typeorm`:e[`@mikro-orm/core`]?`mikro-orm`:e.sequelize?`sequelize`:e.mongoose?`mongoose`:e[`drizzle-orm`]?`drizzle`:null}function ve(e){return e[`@nestjs/platform-fastify`]?`fastify`:e[`@nestjs/platform-express`]||e[`@nestjs/core`]?`express`:null}const ye={verbose:{type:`boolean`,description:`Show file paths and line numbers per diagnostic`,default:!1},score:{type:`boolean`,description:`Output only the numeric score (for CI)`,default:!1},json:{type:`boolean`,description:`JSON output`,default:!1},"min-score":{type:`string`,description:`Minimum passing score (0-100). Exits with code 1 if below threshold`},config:{type:`string`,description:`Config file path`},report:{type:`boolean`,alias:`graph`,description:`Generate an interactive HTML report (summary, diagnostics, module graph, rule lab)`,default:!1},init:{type:`boolean`,description:`Set up the nestjs-doctor skill for AI coding agents (Claude Code, Cursor, Codex, etc.)`,default:!1}},C={include:[`**/*.ts`],exclude:`**/node_modules/**,**/dist/**,**/build/**,**/coverage/**,**/*.spec.ts,**/*.test.ts,**/*.e2e-spec.ts,**/*.e2e-test.ts,**/*.d.ts,**/test/**,**/tests/**,**/__tests__/**,**/__mocks__/**,**/__fixtures__/**,**/mock/**,**/mocks/**,**/*.mock.ts,**/seeder/**,**/seeders/**,**/*.seed.ts,**/*.seeder.ts,*.config.ts,*.config.js,*.config.mjs,*.config.cjs,*.config.mts,*.config.cts`.split(`,`)},be=[`nestjs-doctor.config.json`,`.nestjs-doctor.json`];async function xe(e,t){if(t)return Se(t);for(let t of be)try{return await Se(a(e,t))}catch{}try{let t=await r(a(e,`package.json`),`utf-8`),n=JSON.parse(t);if(n[`nestjs-doctor`]&&typeof n[`nestjs-doctor`]==`object`)return Ce(n[`nestjs-doctor`])}catch{}return{...C}}async function Se(e){let t=await r(e,`utf-8`);return Ce(JSON.parse(t))}function Ce(e){return{...C,...e,exclude:[...C.exclude??[],...e.exclude??[]]}}async function we(e,t){try{return await xe(e)}catch{return t}}const Te=[/Repository$/,/\.repository$/,/\.entity$/,/\.schema$/,/\.guard$/,/\.interceptor$/,/\.pipe$/,/\.filter$/,/\.strategy$/],Ee={meta:{id:`architecture/no-barrel-export-internals`,category:`architecture`,severity:`info`,description:`Don't re-export internal implementation details from barrel files`,help:`Only export the module's public API (services, DTOs, interfaces) from index.ts files.`},check(e){if(e.filePath.endsWith(`/index.ts`))for(let t of e.sourceFile.getExportDeclarations()){let n=t.getModuleSpecifierValue();if(n){Te.some(e=>e.test(n))&&e.report({filePath:e.filePath,message:`Barrel file re-exports internal module '${n}'.`,help:this.meta.help,line:t.getStartLineNumber(),column:1});for(let n of t.getNamedExports()){let t=n.getName();(t.endsWith(`Repository`)||t.endsWith(`Entity`)||t.endsWith(`Schema`))&&e.report({filePath:e.filePath,message:`Barrel file re-exports internal type '${t}'.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}}}},w=new Set([`Get`,`Post`,`Put`,`Patch`,`Delete`,`Head`,`Options`,`All`]);function T(e,t){return e.getDecorator(t)!==void 0}function E(e){return T(e,`Controller`)}function De(e){return T(e,`Injectable`)}function Oe(e){return T(e,`Injectable`)||T(e,`Controller`)||T(e,`Resolver`)||T(e,`WebSocketGateway`)}function D(e){return T(e,`Module`)}function O(e){return e.getDecorators().some(e=>w.has(e.getName()))}const ke=new Set([`TsRestHandler`,`GrpcMethod`,`GrpcStreamMethod`]);function Ae(e){return e.getDecorators().some(e=>ke.has(e.getName()))}const je={meta:{id:`architecture/no-business-logic-in-controllers`,category:`architecture`,severity:`error`,description:`Controllers should only handle HTTP concerns — move business logic to services`,help:`Extract branches, loops, and complex calculations into a service method.`},check(e){for(let t of e.sourceFile.getClasses())if(E(t))for(let n of t.getMethods()){if(!n.getDecorators().some(e=>w.has(e.getName())))continue;let t=n.getBody();if(!t)continue;let r=t.getDescendantsOfKind(d.IfStatement),i=t.getDescendantsOfKind(d.ForStatement),a=t.getDescendantsOfKind(d.ForInStatement),o=t.getDescendantsOfKind(d.ForOfStatement),s=t.getDescendantsOfKind(d.WhileStatement),c=t.getDescendantsOfKind(d.SwitchStatement),l=i.length+a.length+o.length+s.length;(r.length>1||l>0||c.length>0)&&e.report({filePath:e.filePath,message:`Controller method '${n.getName()}' contains business logic (${r.length} if, ${l} loops, ${c.length} switch). Move to a service.`,help:this.meta.help,line:n.getStartLineNumber(),column:1});let u=t.getDescendantsOfKind(d.CallExpression).filter(e=>{let t=e.getExpression();if(t.getKind()===d.PropertyAccessExpression){let e=t.asKind(d.PropertyAccessExpression)?.getName();return e===`map`||e===`filter`||e===`reduce`||e===`sort`||e===`flatMap`}return!1});u.length>1&&e.report({filePath:e.filePath,message:`Controller method '${n.getName()}' contains data transformation logic (${u.length} array operations). Move to a service.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}};function Me(e){let t=new Map;try{let n=f.findConfigFile(e,f.sys.fileExists,`tsconfig.json`);if(!n)return t;let{config:r,error:a}=f.readConfigFile(n,f.sys.readFile);if(a||!r)return t;let o=i(n),c=f.parseJsonConfigFileContent(r,f.sys,o),l=c.options.paths;if(!l)return t;let u=c.options.baseUrl??o;for(let[e,n]of Object.entries(l)){let r=n.map(e=>s(u,e));t.set(e,r)}}catch{return t}return t}function Ne(e,t){for(let[n,r]of t){if(r.length===0)continue;let t=n.indexOf(`*`);if(t===-1){if(e===n)return r[0];continue}let i=n.slice(0,t),a=n.slice(t+1);if(e.startsWith(i)&&e.endsWith(a)&&e.length>=i.length+a.length){let t=e.slice(i.length,e.length-a.length),n=r[0],o=n.indexOf(`*`);return o===-1?n:n.slice(0,o)+t+n.slice(o+1)}}}const Pe=/=>\s*(\w+)/,Fe=/\.js$/;function Ie(e,t,n){let r=[];for(let i of e.getClasses()){let e=i.getDecorator(`Module`);if(!e)continue;let a=i.getName()??`AnonymousModule`,o=e.getArguments()[0],s={name:a,filePath:t,classDeclaration:i,imports:[],exports:[],providers:[],controllers:[]};if(o&&o.getKind()===d.ObjectLiteralExpression){let e=o.asKind(d.ObjectLiteralExpression);e&&(s.imports=k(e,`imports`,n),s.exports=k(e,`exports`,n),s.providers=k(e,`providers`,n),s.controllers=k(e,`controllers`,n))}r.push(s)}return r}function Le(e,t,n=new Map){let r=new Map,i=new Map;for(let i of t){let t=e.getSourceFile(i);if(t)for(let e of Ie(t,i,n))r.set(e.name,e)}for(let[e,t]of r){let n=new Set;for(let e of t.imports)r.has(e)&&n.add(e);i.set(e,n)}let a=new Map;for(let e of r.values())for(let t of e.providers)a.set(t,e);return{modules:r,edges:i,providerToModule:a}}const Re=new Set([`forRoot`,`forRootAsync`,`forFeature`,`forFeatureAsync`,`forChild`,`forChildAsync`,`register`,`registerAsync`]);function k(e,t,n){let r=e.getProperty(t);if(!r)return[];let i=r.asKind(d.PropertyAssignment);if(!i)return[];let a=i.getInitializer();return a?A(a,e.getSourceFile(),0,n):[]}function A(e,t,n,r){if(n>5)return[];let i=e.getKind();if(i===d.ArrayLiteralExpression){let i=e.asKindOrThrow(d.ArrayLiteralExpression),a=[];for(let e of i.getElements())a.push(...ze(e,t,n,r));return a}return i===d.CallExpression?Be(e.asKindOrThrow(d.CallExpression),t,n,r):i===d.Identifier?Ue(e.getText(),t,n+1,r):[]}function ze(e,t,n,r){let i=e.getText();if(i.startsWith(`forwardRef`)){let e=i.match(Pe);return e?[e[1]]:[i]}let a=e.getKind();return a===d.SpreadElement?A(e.asKindOrThrow(d.SpreadElement).getExpression(),t,n,r):a===d.CallExpression?Be(e.asKindOrThrow(d.CallExpression),t,n,r):a===d.PropertyAccessExpression?[e.asKindOrThrow(d.PropertyAccessExpression).getExpression().getText()]:(d.Identifier,[i])}function Be(e,t,n,r){let i=e.getExpression();if(i.getKind()===d.PropertyAccessExpression){let a=i.asKindOrThrow(d.PropertyAccessExpression),o=a.getName();if(o===`concat`){let i=A(a.getExpression(),t,n,r),o=[];for(let i of e.getArguments())o.push(...A(i,t,n,r));return[...i,...o]}return Re.has(o),[a.getExpression().getText()]}return i.getKind()===d.Identifier?Ge(i.getText(),t,n+1,r):[]}function Ve(e,t,n){if(!e.startsWith(`.`)){let r=Ne(e,n);if(!r)return;let i=t.getProject(),a=[`${r}.ts`,`${r}/index.ts`,r,r.replace(Fe,`.ts`)];for(let e of a){let t=i.getSourceFile(e);if(t)return t}return}let r=s(i(t.getFilePath()),e),a=t.getProject(),o=[`${r}.ts`,`${r}/index.ts`,r,r.replace(Fe,`.ts`)];for(let e of o){let t=a.getSourceFile(e);if(t)return t}}function He(e,t,n){for(let r of t.getImportDeclarations())for(let i of r.getNamedImports())if((i.getAliasNode()?i.getAliasNode().getText():i.getName())===e){let e=Ve(r.getModuleSpecifierValue(),t,n);return e?{sourceFile:e,localName:i.getName()}:void 0}for(let r of t.getExportDeclarations())if(r.getModuleSpecifierValue()){for(let i of r.getNamedExports())if((i.getAliasNode()?i.getAliasNode().getText():i.getName())===e){let e=Ve(r.getModuleSpecifierValue(),t,n);return e?{sourceFile:e,localName:i.getName()}:void 0}}}function Ue(e,t,n,r){if(n>5)return[];for(let i of t.getStatements()){if(i.getKind()!==d.VariableStatement)continue;let a=i.asKindOrThrow(d.VariableStatement);for(let i of a.getDeclarations())if(i.getName()===e){let e=i.getInitializer();if(e)return A(e,t,n,r)}}let i=He(e,t,r);return i?Ue(i.localName,i.sourceFile,n+1,r):[]}function We(e,t,n,r){for(let i of t.getStatements()){if(i.getKind()!==d.VariableStatement)continue;let a=i.asKindOrThrow(d.VariableStatement);for(let i of a.getDeclarations()){if(i.getName()!==e)continue;let a=i.getInitializer();if(!a||a.getKind()!==d.ArrowFunction)continue;let o=a.asKindOrThrow(d.ArrowFunction).getBody();if(o.getKind()!==d.Block)return A(o,t,n,r);let s=[];for(let e of o.getDescendantsOfKind(d.ReturnStatement)){let i=e.getExpression();i&&s.push(...A(i,t,n,r))}return s}}}function Ge(e,t,n,r){if(n>5)return[];for(let i of t.getStatements()){if(i.getKind()!==d.FunctionDeclaration)continue;let a=i.asKindOrThrow(d.FunctionDeclaration);if(a.getName()!==e)continue;let o=[];for(let e of a.getDescendantsOfKind(d.ReturnStatement)){let i=e.getExpression();i&&o.push(...A(i,t,n,r))}return o}let i=We(e,t,n,r);if(i)return i;let a=He(e,t,r);return a?Ge(a.localName,a.sourceFile,n+1,r):[]}function Ke(e){let t=new Map,n=new Map,r=new Map;for(let[i,a]of e){for(let[e,n]of a.modules){let r=`${i}/${e}`,o={...n,name:r,imports:n.imports.map(e=>a.modules.has(e)?`${i}/${e}`:e),exports:n.exports.map(e=>a.modules.has(e)?`${i}/${e}`:e)};t.set(r,o)}for(let[e,t]of a.edges){let r=`${i}/${e}`,a=new Set;for(let e of t)a.add(`${i}/${e}`);n.set(r,a)}for(let[e,n]of a.providerToModule){let a=`${i}/${n.name}`,o=t.get(a);o&&r.set(`${i}/${e}`,o)}}return{modules:t,edges:n,providerToModule:r}}function qe(e){let t=[],n=new Set,r=new Set;function i(a,o){n.add(a),r.add(a);let s=e.edges.get(a)??new Set;for(let e of s)if(!n.has(e))i(e,[...o,e]);else if(r.has(e)){let n=o.indexOf(e);n===-1?t.push([...o,e]):t.push(o.slice(n))}r.delete(a)}for(let t of e.modules.keys())n.has(t)||i(t,[t]);return t}function Je(e,t,n,r,i,a){let o=[];for(let i of e.providers){let e=n.get(i);if(e)for(let n of e.dependencies){let e=r.get(n);e&&e.name===t.name&&o.push({consumer:i,dependency:n})}}for(let n of e.controllers)for(let e of a){let a=i.getSourceFile(e);if(a)for(let e of a.getClasses()){if(e.getName()!==n)continue;let i=e.getConstructors()[0];if(i)for(let e of i.getParameters()){let i=e.getTypeNode(),a=i?i.getText():e.getType().getText(),s=a.split(`.`).pop()?.split(`<`)[0]??a,c=r.get(s);c&&c.name===t.name&&o.push({consumer:n,dependency:s})}}}return o}const Ye=`Break the cycle by extracting shared logic into a separate module or using forwardRef().`;function Xe(e,t){let{moduleGraph:n,providers:r,project:i,files:a}=t,o=[],s;for(let t=0;t<e.length;t++){let c=e[t],l=e[(t+1)%e.length],u=n.modules.get(c),d=n.modules.get(l);if(!(u&&d))continue;let f=Je(u,d,r,n.providerToModule,i,a);if(f.length===0)continue;let p=new Map;for(let e of f){let t=p.get(e.consumer);t?t.push(e.dependency):p.set(e.consumer,[e.dependency])}let m=[];for(let[e,t]of p){let n=t.map(e=>`${e} (from ${l})`).join(`, `);m.push(`${e} (in ${c}) injects ${n}`)}let h=`${c} -> ${l}: ${m.join(`; `)}`;o.push(h),(!s||f.length<s.count)&&(s={description:`${c} -> ${l}`,count:f.length})}if(o.length===0)return Ye;let c=o.join(`
|
|
4
|
-
`);if(s){let e=s.count===1?`dependency`:`dependencies`,t=s.description.split(` -> `)[0],o=s.description.split(` -> `)[1],l=n.modules.get(t),u=n.modules.get(o);if(l&&u){let t=Je(l,u,r,n.providerToModule,i,a),o=[...new Set(t.map(e=>e.dependency))].join(`, `);c+=`\nConsider extracting ${o} into a shared module — it would break the ${s.description} edge (${s.count} ${e}).`}}return c}const Ze={meta:{id:`architecture/no-circular-module-deps`,category:`architecture`,severity:`error`,description:`Module import graph must not contain circular dependencies`,help:Ye,scope:`project`},check(e){let t=qe(e.moduleGraph);for(let n of t){let t=n.join(` -> `),r=e.moduleGraph.modules.get(n[0]),i=Xe(n,e);e.report({filePath:r?.filePath??`unknown`,message:`Circular module dependency detected: ${t}`,help:i,line:r?.classDeclaration.getStartLineNumber()??1,column:1})}}},Qe=[`Service`,`Repository`,`Gateway`,`Resolver`],$e=[`Guard`,`Interceptor`,`Pipe`,`Filter`];function et(e){return typeof e==`object`&&!!e}function tt(e){if(!et(e))return new Set;let t=e.excludeClasses;if(Array.isArray(t))return new Set(t.filter(e=>typeof e==`string`));let n=e.options;if(!et(n))return new Set;let r=n.excludeClasses;return Array.isArray(r)?new Set(r.filter(e=>typeof e==`string`)):new Set}const nt={meta:{id:`architecture/no-manual-instantiation`,category:`architecture`,severity:`error`,description:`Do not manually instantiate @Injectable classes — use NestJS dependency injection`,help:`Register the class as a provider in a module and inject it via the constructor.`},check(e){let t=tt(e.config?.rules?.[this.meta.id]),n=e.sourceFile.getDescendantsOfKind(d.NewExpression);for(let r of n){let n=r.getExpression().getText(),i=n.split(`.`).pop()??n;if(t.has(n)||t.has(i))continue;let a=Qe.some(e=>n.endsWith(e)),o=$e.some(e=>n.endsWith(e));if(a||o){if(o){if(r.getFirstAncestorByKind(d.Decorator))continue;let e=r.getFirstAncestorByKind(d.MethodDeclaration),t=r.getFirstAncestorByKind(d.Constructor);if(!(e||t))continue}e.report({filePath:e.filePath,message:`Manual instantiation of '${n}' detected. Use dependency injection instead.`,help:this.meta.help,line:r.getStartLineNumber(),column:1})}}}},rt=/\.(\w+)$/,it=/^(\w+)</,at=new Set([`PrismaService`,`PrismaClient`,`EntityManager`,`DataSource`,`Repository`,`Connection`,`MongooseModel`,`InjectModel`,`InjectRepository`,`MikroORM`,`DrizzleService`]),ot={meta:{id:`architecture/no-orm-in-controllers`,category:`architecture`,severity:`error`,description:`Controllers must not inject ORM services directly — use a service layer`,help:`Inject a service that wraps the ORM instead of using the ORM directly in controllers.`},check(e){for(let t of e.sourceFile.getClasses()){if(!E(t))continue;let n=t.getConstructors()[0];if(n){for(let t of n.getParameters()){let n=st(t.getType().getText());if(at.has(n)){let r=t.getNameNode();e.report({filePath:e.filePath,message:`Controller injects ORM type '${n}' directly. Use a service layer.`,help:this.meta.help,line:r.getStartLineNumber(),column:r.getStartLinePos()+1})}}for(let n of t.getConstructors()[0]?.getParameters()??[])for(let t of n.getDecorators()){let n=t.getName();(n===`InjectRepository`||n===`InjectModel`)&&e.report({filePath:e.filePath,message:`Controller uses @${n}() decorator. Move data access to a service.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}}}};function st(e){let t=e.match(rt);if(t)return t[1];let n=e.match(it);return n?n[1]:e}const ct=/\.(\w+)$/,lt=/^(\w+)</,ut=new Set([`PrismaService`,`PrismaClient`,`EntityManager`,`DataSource`,`Connection`,`MikroORM`]),dt={meta:{id:`architecture/no-orm-in-services`,category:`architecture`,severity:`info`,description:`Services should use repository abstractions instead of ORM directly`,help:`Create a repository class that wraps ORM calls and inject that instead. Note: If your project follows the official NestJS Prisma recipe (injecting PrismaService directly), you can disable this rule.`},check(e){for(let t of e.sourceFile.getClasses()){if(!De(t))continue;let n=t.getName()??``;if(n.endsWith(`Repository`)||n.endsWith(`Repo`))continue;let r=t.getConstructors()[0];if(r)for(let t of r.getParameters()){let n=ft(t.getType().getText());if(ut.has(n)){let r=t.getNameNode();e.report({filePath:e.filePath,message:`Service injects ORM type '${n}' directly. Consider using a repository abstraction.`,help:this.meta.help,line:r.getStartLineNumber(),column:r.getStartLinePos()+1})}for(let n of t.getDecorators()){let t=n.getName();(t===`InjectRepository`||t===`InjectModel`)&&e.report({filePath:e.filePath,message:`Service uses @${t}() directly. Consider wrapping in a repository class.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}}}};function ft(e){let t=e.match(ct);if(t)return t[1];let n=e.match(lt);return n?n[1]:e}const pt=/\.(\w+)$/,mt=/^(\w+)</,ht=[/Repository$/,/Repo$/],gt={meta:{id:`architecture/no-repository-in-controllers`,category:`architecture`,severity:`error`,description:`Controllers must not inject repositories directly — use the service layer`,help:`Move database access to a service and inject the service into the controller instead.`},check(e){for(let t of e.sourceFile.getClasses()){if(!E(t))continue;let n=t.getConstructors()[0];if(n){for(let t of n.getParameters()){let n=_t(t.getType().getText());if(ht.some(e=>e.test(n))){let r=t.getNameNode();e.report({filePath:e.filePath,message:`Controller injects repository '${n}' directly. Use a service layer instead.`,help:this.meta.help,line:r.getStartLineNumber(),column:r.getStartLinePos()+1})}}for(let t of e.sourceFile.getImportDeclarations()){let n=t.getModuleSpecifierValue();(n.includes(`/repositories/`)||n.includes(`/repositories`))&&e.report({filePath:e.filePath,message:`Controller imports from repository path '${n}'.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}}}};function _t(e){let t=e.match(pt);if(t)return t[1];let n=e.match(mt);return n?n[1]:e}const vt={meta:{id:`architecture/no-service-locator`,category:`architecture`,severity:`warning`,description:`Avoid using ModuleRef.get() or ModuleRef.resolve() — prefer explicit constructor injection`,help:`Replace ModuleRef.get()/resolve() with constructor injection for explicit, testable dependencies.`},check(e){let t=e.sourceFile.getDescendantsOfKind(d.CallExpression);for(let n of t){let t=n.getExpression();if(t.getKind()!==d.PropertyAccessExpression)continue;let r=t.asKind(d.PropertyAccessExpression);if(!r)continue;let i=r.getName();if(i!==`get`&&i!==`resolve`)continue;let a=r.getExpression().getText();(a===`moduleRef`||a===`this.moduleRef`)&&e.report({filePath:e.filePath,message:`Service locator pattern: '${a}.${i}()' hides dependencies. Use constructor injection instead.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},yt={meta:{id:`architecture/prefer-constructor-injection`,category:`architecture`,severity:`warning`,description:`Prefer constructor injection over @Inject() property injection`,help:`Move the dependency to a constructor parameter instead of using property injection.`},check(e){for(let t of e.sourceFile.getClasses())if(Oe(t))for(let n of t.getProperties())n.getDecorator(`Inject`)&&e.report({filePath:e.filePath,message:`Property '${n.getName()}' uses @Inject() decorator. Prefer constructor injection.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}},bt=[`/repositories/`,`/entities/`,`/dto/`,`/guards/`,`/interceptors/`,`/pipes/`,`/strategies/`],xt={meta:{id:`architecture/require-module-boundaries`,category:`architecture`,severity:`info`,description:`Avoid deep imports into other feature modules' internals`,help:`Import from the module's public API (barrel export) instead of reaching into its internals.`},check(e){for(let t of e.sourceFile.getImportDeclarations()){let n=t.getModuleSpecifierValue();n.startsWith(`.`)&&n.includes(`../`)&&bt.some(e=>n.includes(e))&&e.report({filePath:e.filePath,message:`Import '${n}' reaches into another module's internals.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}},St={meta:{id:`correctness/factory-inject-matches-params`,category:`correctness`,severity:`error`,description:`useFactory inject array length must match the factory function parameter count`,help:`Ensure the 'inject' array has one entry per factory function parameter.`},check(e){for(let t of e.sourceFile.getClasses()){if(!D(t))continue;let n=t.getDecorator(`Module`);if(!n)continue;let r=n.getArguments()[0];if(!r||r.getKind()!==d.ObjectLiteralExpression)continue;let i=r.asKind(d.ObjectLiteralExpression);if(!i)continue;let a=i.getProperty(`providers`);if(!a)continue;let o=a.getChildrenOfKind(d.ArrayLiteralExpression)[0];if(o)for(let t of o.getElements()){if(t.getKind()!==d.ObjectLiteralExpression)continue;let n=t.asKind(d.ObjectLiteralExpression);if(!n)continue;let r=n.getProperty(`useFactory`),i=n.getProperty(`inject`);if(!(r&&i))continue;let a=i.getChildrenOfKind(d.ArrayLiteralExpression)[0];if(!a)continue;let o=a.getElements().length,s,c=r.asKind(d.MethodDeclaration);if(c)s=c.getParameters().length;else{let e=r.asKind(d.PropertyAssignment);if(!e)continue;let t=e.getInitializer();if(!t)continue;t.getKind()===d.ArrowFunction?s=t.asKind(d.ArrowFunction)?.getParameters().length:t.getKind()===d.FunctionExpression&&(s=t.asKind(d.FunctionExpression)?.getParameters().length)}s!==void 0&&o!==s&&e.report({filePath:e.filePath,message:`Factory has ${s} parameter(s) but inject array has ${o} element(s).`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}}},Ct=[`Guard`,`Interceptor`,`Filter`,`Pipe`,`Middleware`,`Strategy`,`Subscriber`,`Listener`,`Processor`,`Consumer`,`Worker`,`Scheduler`,`Cron`,`HealthIndicator`],wt={meta:{id:`correctness/injectable-must-be-provided`,category:`correctness`,severity:`info`,description:`@Injectable() classes should be registered in at least one module's providers array`,help:`Add this class to a module's providers array, or remove the @Injectable() decorator if unused.`,scope:`project`},check(e){let t=new Set;for(let n of e.moduleGraph.modules.values()){for(let e of n.providers)t.add(e);for(let e of n.controllers)t.add(e)}for(let n of e.files){let r=e.project.getSourceFile(n);if(r)for(let e of r.getClasses()){if(!D(e))continue;let n=e.getDecorator(`Module`);if(!n)continue;let r=n.getArguments()[0];if(!r||r.getKind()!==d.ObjectLiteralExpression)continue;let i=r.asKind(d.ObjectLiteralExpression);if(!i)continue;let a=i.getProperty(`providers`);if(!a)continue;let o=a.getChildrenOfKind(d.ArrayLiteralExpression)[0];if(o)for(let e of o.getElements()){if(e.getKind()!==d.ObjectLiteralExpression)continue;let n=e.asKind(d.ObjectLiteralExpression);if(n)for(let e of[`useClass`,`useExisting`]){let r=n.getProperty(e);if(!r)continue;let i=r.asKind(d.PropertyAssignment);if(!i)continue;let a=i.getInitializer();a&&t.add(a.getText())}}}}for(let n of e.files){if(n.includes(`.spec.`)||n.includes(`.test.`)||n.includes(`__test__`)||n.includes(`__tests__`))continue;let r=e.project.getSourceFile(n);if(r)for(let i of r.getClasses()){if(!i.getDecorator(`Injectable`))continue;let r=i.getName();r&&(Ct.some(e=>r.endsWith(e))||t.has(r)||e.report({filePath:n,message:`@Injectable() class '${r}' is not registered in any module's providers array.`,help:this.meta.help,line:i.getStartLineNumber(),column:1}))}}}};function Tt(e){return e.getDescendantsOfKind(d.ReturnStatement).some(e=>{let t=e.getExpression();return!t||t.getKind()!==d.NewExpression?!1:t.asKindOrThrow(d.NewExpression).getExpression().getText()===`Promise`})}const Et={meta:{id:`correctness/no-async-without-await`,category:`correctness`,severity:`warning`,description:`Async functions/methods should contain at least one await expression`,help:`Either add an await expression or remove the async keyword. HTTP handlers with route decorators are exempted, as async is conventional for controller methods.`},check(e){for(let t of e.sourceFile.getClasses())for(let n of t.getMethods()){if(!n.isAsync()||E(t)&&O(n)||Ae(n))continue;let r=n.getBody();if(r&&r.getDescendantsOfKind(d.AwaitExpression).filter(e=>{let t=e.getParent();for(;t&&t!==r;){if(t.getKind()===d.ArrowFunction||t.getKind()===d.FunctionExpression||t.getKind()===d.FunctionDeclaration)return!1;t=t.getParent()}return!0}).length===0){let t=n.getName();Tt(r)?e.report({filePath:e.filePath,message:`Async method '${t}()' returns a Promise directly — remove the async keyword.`,help:`The async keyword is unnecessary when you are already constructing a Promise manually. Remove async to avoid double-wrapping.`,line:n.getStartLineNumber(),column:1}):e.report({filePath:e.filePath,message:`Async method '${t}()' has no await expression.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}for(let t of e.sourceFile.getFunctions()){if(!t.isAsync())continue;let n=t.getBody();if(n&&n.getDescendantsOfKind(d.AwaitExpression).filter(e=>{let t=e.getParent();for(;t&&t!==n;){if(t.getKind()===d.ArrowFunction||t.getKind()===d.FunctionExpression||t.getKind()===d.FunctionDeclaration)return!1;t=t.getParent()}return!0}).length===0){let r=t.getName()??`anonymous`;Tt(n)?e.report({filePath:e.filePath,message:`Async function '${r}()' returns a Promise directly — remove the async keyword.`,help:`The async keyword is unnecessary when you are already constructing a Promise manually. Remove async to avoid double-wrapping.`,line:t.getStartLineNumber(),column:1}):e.report({filePath:e.filePath,message:`Async function '${r}()' has no await expression.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}}},Dt=new Set([`ApiResponse`,`ApiQuery`,`ApiParam`,`ApiHeader`,`ApiSecurity`,`SetMetadata`,`Roles`,`Header`,`Throttle`]),Ot={meta:{id:`correctness/no-duplicate-decorators`,category:`correctness`,severity:`warning`,description:`Same decorator should not appear twice on a single target`,help:`Remove the duplicate decorator — it was likely copy-pasted by mistake.`},check(e){for(let t of e.sourceFile.getClasses()){j(t.getDecorators(),e,this.meta.help);for(let n of t.getMethods())j(n.getDecorators(),e,this.meta.help);for(let n of t.getProperties())j(n.getDecorators(),e,this.meta.help);for(let n of t.getConstructors())for(let t of n.getParameters())j(t.getDecorators(),e,this.meta.help)}}};function j(e,t,n){let r=new Set;for(let i of e){let e=i.getName();Dt.has(e)||(r.has(e)?t.report({filePath:t.filePath,message:`Duplicate @${e}() decorator on the same target.`,help:n,line:i.getStartLineNumber(),column:1}):r.add(e))}}const kt=[`providers`,`controllers`,`imports`,`exports`],At={meta:{id:`correctness/no-duplicate-module-metadata`,category:`correctness`,severity:`warning`,description:`Same identifier should not appear twice in a module metadata array`,help:`Remove the duplicate entry from the module metadata.`},check(e){for(let t of e.sourceFile.getClasses()){if(!D(t))continue;let n=t.getDecorator(`Module`);if(!n)continue;let r=n.getArguments()[0];if(!r||r.getKind()!==d.ObjectLiteralExpression)continue;let i=r.asKind(d.ObjectLiteralExpression);if(i)for(let t of kt){let n=i.getProperty(t);if(!n)continue;let r=n.getChildrenOfKind(d.ArrayLiteralExpression)[0];if(!r)continue;let a=new Set;for(let n of r.getElements()){let r=n.getText();a.has(r)?e.report({filePath:e.filePath,message:`Duplicate '${r}' in @Module() ${t} array.`,help:this.meta.help,line:n.getStartLineNumber(),column:1}):a.add(r)}}}}},jt={meta:{id:`correctness/no-duplicate-routes`,category:`correctness`,severity:`error`,description:`Same HTTP method + route path + version should not appear twice in a single controller`,help:`Remove or rename one of the duplicate route handlers.`},check(e){for(let t of e.sourceFile.getClasses()){if(!E(t))continue;let n=new Map;for(let r of t.getMethods())for(let t of r.getDecorators()){let i=t.getName();if(!w.has(i))continue;let a=t.getArguments(),o=a.length>0?a[0].getText():`""`,s=r.getDecorator(`Version`),c=`${i}:${o}:${s?s.getArguments()[0]?.getText()??``:``}`,l=n.get(c);l?e.report({filePath:e.filePath,message:`Duplicate route: @${i}(${o}) is already defined in '${l}()'.`,help:this.meta.help,line:r.getStartLineNumber(),column:1}):n.set(c,r.getName())}}}},Mt={meta:{id:`correctness/no-empty-handlers`,category:`correctness`,severity:`info`,description:`Controller HTTP handlers should not have empty bodies`,help:`Add implementation to the handler method or remove it if unnecessary.`},check(e){for(let t of e.sourceFile.getClasses())if(E(t))for(let n of t.getMethods()){if(!n.getDecorators().some(e=>w.has(e.getName())))continue;let t=n.getBody();if(!t)continue;let r=t.asKind(d.Block);r&&r.getStatements().length===0&&e.report({filePath:e.filePath,message:`Handler '${n.getName()}()' has an empty body.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}};function Nt(e){let t=e.getReturnType().getText();return t.startsWith(`Promise<`)||t===`Promise`?!0:t===`any`||t===`error`?`unknown`:!1}const Pt=new Set([`save`,`create`,`insert`,`update`,`delete`,`remove`,`send`,`emit`,`publish`,`dispatch`,`execute`,`fetch`,`load`,`upload`,`download`,`process`]),Ft={meta:{id:`correctness/no-fire-and-forget-async`,category:`correctness`,severity:`warning`,description:`Calling async functions without await leads to unhandled promise rejections`,help:`Add await before the async call, or use void with explicit error handling if fire-and-forget is intentional.`},check(e){for(let t of e.sourceFile.getClasses())for(let n of t.getMethods()){if(O(n))continue;let t=n.getBody();if(!t)continue;let r=t.getDescendantsOfKind(d.ExpressionStatement);for(let t of r){let r=t.getExpression();if(r.getKind()===d.VoidExpression||r.getKind()===d.AwaitExpression||r.getKind()!==d.CallExpression)continue;let i=r.asKind(d.CallExpression);if(!i)continue;let a=i.getExpression().getText().split(`.`).pop()??``,o=Nt(i);if(o!==!1){if(o===`unknown`){let e=a.toLowerCase();if(!(Pt.has(e)||[...Pt].some(t=>e.startsWith(t)&&e!==t)))continue}t.getFirstAncestorByKind(d.MethodDeclaration)===n&&e.report({filePath:e.filePath,message:`Async call '${a}()' is not awaited — unhandled rejections will crash the process.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}}}},It={meta:{id:`correctness/no-missing-filter-catch`,category:`correctness`,severity:`error`,description:`Exception filter classes decorated with @Catch() must implement the catch() method`,help:`Add a catch(exception, host: ArgumentsHost) method to the filter class.`},check(e){for(let t of e.sourceFile.getClasses())T(t,`Catch`)&&(t.getExtends()||t.getMethods().some(e=>e.getName()===`catch`)||e.report({filePath:e.filePath,message:`Exception filter '${t.getName()}' has @Catch() but is missing the 'catch()' method.`,help:this.meta.help,line:t.getStartLineNumber(),column:1}))}},Lt={meta:{id:`correctness/no-missing-guard-method`,category:`correctness`,severity:`error`,description:`Guard classes must implement the canActivate() method`,help:`Add a canActivate(context: ExecutionContext) method to the guard class. Note: This rule identifies guards by the 'Guard' class name suffix.`},check(e){for(let t of e.sourceFile.getClasses()){let n=t.getName()??``;n.endsWith(`Guard`)&&T(t,`Injectable`)&&(t.getExtends()||t.getMethods().some(e=>e.getName()===`canActivate`)||e.report({filePath:e.filePath,message:`Guard '${n}' is missing the 'canActivate()' method.`,help:this.meta.help,line:t.getStartLineNumber(),column:1}))}}},Rt={meta:{id:`correctness/no-missing-injectable`,category:`correctness`,severity:`error`,description:`Provider classes with constructor dependencies must have the @Injectable() decorator`,help:`Add @Injectable() to providers that inject constructor dependencies.`,scope:`project`},check(e){let t=new Set([...e.providers.values()].map(e=>e.name)),n=new Map;for(let t of e.files){let r=e.project.getSourceFile(t);if(r)for(let e of r.getClasses()){let r=e.getName();if(r){let i=n.get(r)??[];i.push({cls:e,filePath:t}),n.set(r,i)}}}for(let r of e.moduleGraph.modules.values())for(let i of r.providers){if(t.has(i))continue;let a=n.get(i);if(a)for(let{cls:t,filePath:n}of a){let a=(t.getConstructors()[0]?.getParameters().length??0)>0;!(t.getDecorator(`Injectable`)||t.getDecorator(`Resolver`)||t.getDecorator(`WebSocketGateway`))&&a&&e.report({filePath:n,message:`Class '${i}' is listed in '${r.name}' providers but is missing @Injectable() decorator.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}}},zt={meta:{id:`correctness/no-missing-interceptor-method`,category:`correctness`,severity:`error`,description:`Interceptor classes must implement the intercept() method`,help:`Add an intercept(context: ExecutionContext, next: CallHandler) method to the interceptor class.`},check(e){for(let t of e.sourceFile.getClasses()){let n=t.getName()??``;n.endsWith(`Interceptor`)&&T(t,`Injectable`)&&(t.getExtends()||t.getMethods().some(e=>e.getName()===`intercept`)||e.report({filePath:e.filePath,message:`Interceptor '${n}' is missing the 'intercept()' method.`,help:this.meta.help,line:t.getStartLineNumber(),column:1}))}}},Bt={meta:{id:`correctness/no-missing-module-decorator`,category:`correctness`,severity:`warning`,description:`Classes named *Module should have a @Module() decorator`,help:`Add @Module({}) decorator to the class, or rename it if it is not a NestJS module.`},check(e){for(let t of e.sourceFile.getClasses()){let n=t.getName()??``;n.endsWith(`Module`)&&(n===`Module`||n===`DynamicModule`||T(t,`Module`)||e.report({filePath:e.filePath,message:`Class '${n}' is named like a module but is missing the @Module() decorator.`,help:this.meta.help,line:t.getStartLineNumber(),column:1}))}}},Vt={meta:{id:`correctness/no-missing-pipe-method`,category:`correctness`,severity:`error`,description:`Pipe classes must implement the transform() method`,help:`Add a transform(value: any, metadata: ArgumentMetadata) method to the pipe class.`},check(e){for(let t of e.sourceFile.getClasses()){let n=t.getName()??``;n.endsWith(`Pipe`)&&T(t,`Injectable`)&&(t.getExtends()||t.getMethods().some(e=>e.getName()===`transform`)||e.report({filePath:e.filePath,message:`Pipe '${n}' is missing the 'transform()' method.`,help:this.meta.help,line:t.getStartLineNumber(),column:1}))}}},Ht=/:(\w+)/g,Ut={meta:{id:`correctness/param-decorator-matches-route`,category:`correctness`,severity:`error`,description:`@Param() decorator name must match a :param in the route path`,help:`Ensure the @Param('name') argument matches a ':name' segment in the route path (including controller prefix).`},check(e){for(let t of e.sourceFile.getClasses()){if(!E(t))continue;let n=t.getDecorator(`Controller`),r=``;if(n){let e=n.getArguments();if(e.length>0){let t=e[0];if(t.getKind()===d.ObjectLiteralExpression){let e=t.asKind(d.ObjectLiteralExpression);if(e){let t=e.getProperty(`path`);if(t){let e=t.asKind(d.PropertyAssignment);if(e){let t=e.getInitializer();t&&(r=t.getText().replace(/^['"`]|['"`]$/g,``))}}}}else r=t.getText().replace(/^['"`]|['"`]$/g,``)}}let i=new Set;for(let e of r.matchAll(Ht))i.add(e[1]);for(let n of t.getMethods()){let t=``,r=!1;for(let e of n.getDecorators())if(w.has(e.getName())){r=!0;let n=e.getArguments();n.length>0&&(t=n[0].getText().replace(/^['"`]|['"`]$/g,``));break}if(!r)continue;let a=new Set;for(let e of t.matchAll(Ht))a.add(e[1]);let o=new Set([...i,...a]);for(let t of n.getParameters())for(let n of t.getDecorators()){if(n.getName()!==`Param`)continue;let t=n.getArguments();if(t.length===0)continue;let r=t[0].getText().replace(/^['"`]|['"`]$/g,``);o.has(r)||e.report({filePath:e.filePath,message:`@Param('${r}') does not match any route parameter. Available: ${o.size>0?[...o].map(e=>`:${e}`).join(`, `):`(none)`}.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}}}},Wt={meta:{id:`correctness/prefer-readonly-injection`,category:`correctness`,severity:`warning`,description:`Constructor DI parameters should be readonly to prevent accidental reassignment`,help:`Add the 'readonly' modifier to the constructor parameter.`},check(e){for(let t of e.sourceFile.getClasses()){if(!(De(t)||E(t)))continue;let n=t.getConstructors()[0];if(n){for(let t of n.getParameters())if((t.hasModifier(`private`)||t.hasModifier(`protected`)||t.hasModifier(`public`))&&!t.isReadonly()){let n=t.getNameNode();e.report({filePath:e.filePath,message:`Constructor parameter '${t.getName()}' should be readonly.`,help:this.meta.help,line:n.getStartLineNumber(),column:n.getStartLinePos()+1})}}}}},Gt={meta:{id:`correctness/require-inject-decorator`,category:`correctness`,severity:`error`,description:`Constructor parameters without type annotations must have @Inject() decorator for NestJS DI to resolve them`,help:`Add a type annotation or @Inject() decorator to the constructor parameter.`},check(e){for(let t of e.sourceFile.getClasses()){if(!Oe(t))continue;let n=t.getConstructors()[0];if(n)for(let r of n.getParameters()){let n=r.getTypeNode(),i=r.getDecorators().some(e=>e.getName()===`Inject`);n||i||e.report({filePath:e.filePath,message:`Constructor parameter '${r.getName()}' in '${t.getName()}' has no type annotation and no @Inject() decorator — NestJS cannot resolve it.`,help:this.meta.help,line:r.getStartLineNumber(),column:1})}}}},Kt={onModuleInit:`OnModuleInit`,onModuleDestroy:`OnModuleDestroy`,onApplicationBootstrap:`OnApplicationBootstrap`,onApplicationShutdown:`OnApplicationShutdown`,beforeApplicationShutdown:`BeforeApplicationShutdown`},qt={meta:{id:`correctness/require-lifecycle-interface`,category:`correctness`,severity:`warning`,description:`Classes with lifecycle methods should implement the corresponding NestJS interface`,help:`Add 'implements OnModuleInit' (or the appropriate interface) to the class declaration.`},check(e){for(let t of e.sourceFile.getClasses()){let n=t.getImplements().map(e=>e.getText());for(let r of t.getMethods()){let i=r.getName(),a=Kt[i];a&&(n.some(e=>e===a||e.startsWith(`${a}<`))||e.report({filePath:e.filePath,message:`Class '${t.getName()}' has '${i}()' but does not implement '${a}'.`,help:this.meta.help,line:r.getStartLineNumber(),column:1}))}}}},Jt=/each\s*:\s*true/,Yt={meta:{id:`correctness/validate-nested-array-each`,category:`correctness`,severity:`warning`,description:`@ValidateNested() on array-typed properties must use { each: true }`,help:`Change @ValidateNested() to @ValidateNested({ each: true }) for array properties.`},check(e){for(let t of e.sourceFile.getClasses())for(let n of t.getProperties()){let t=n.getDecorators(),r=t.find(e=>e.getName()===`ValidateNested`);if(!r)continue;let i=Xt(n),a=t.some(e=>e.getName()===`IsArray`);(i||a)&&(Zt(r)||e.report({filePath:e.filePath,message:`Property '${n.getName()}' is an array with @ValidateNested() but missing { each: true }.`,help:this.meta.help,line:r.getStartLineNumber(),column:1}))}}};function Xt(e){let t=e.getTypeNode();if(!t)return!1;let n=t.getText().replace(/\s/g,``);return!!(n.endsWith(`[]`)||n.startsWith(`Array<`))}function Zt(e){let t=e.getArguments();if(t.length===0)return!1;let n=t[0];if(n.getKind()!==d.ObjectLiteralExpression)return!1;let r=n.getText();return Jt.test(r)}const Qt=new Set(`ValidateNested.IsString.IsNumber.IsBoolean.IsEmail.IsArray.IsEnum.IsNotEmpty.IsDefined.IsOptional.IsDate.IsObject.IsInt.IsPositive.IsNegative.IsUUID.IsUrl.IsISO8601.Matches.Min.Max.MinLength.MaxLength.ArrayMinSize.ArrayMaxSize.ArrayNotEmpty.IsIn.IsNotIn.Length.Contains.IsAlpha.IsAlphanumeric.IsDecimal.IsHexColor.IsJSON.IsPhoneNumber.IsIP.IsCreditCard.IsDateString.IsMilitaryTime.IsMongoId.IsPort.IsSemVer.IsStrongPassword`.split(`.`)),$t=new Set([`string`,`number`,`boolean`,`Date`,`any`,`unknown`,`bigint`,`symbol`,`undefined`,`null`,`void`,`never`]),en=/\s/g,tn=/\[\]$/,nn=/^Array<(.+)>$/,rn=/^["']/,an=/^\d+$/;function M(e){let t=e.replace(en,``);if(t.includes(`|`))return t.split(`|`).every(e=>M(e));if($t.has(t)||tn.test(t)&&M(t.replace(tn,``)))return!0;let n=t.match(nn);return!!(n&&M(n[1])||rn.test(t)||an.test(t))}const on={meta:{id:`correctness/validated-non-primitive-needs-type`,category:`correctness`,severity:`warning`,description:`DTO properties with class-validator decorators on non-primitive types must have @Type() from class-transformer`,help:`Add @Type(() => ClassName) from 'class-transformer' to ensure proper transformation.`},check(e){for(let t of e.sourceFile.getClasses())for(let n of t.getProperties()){let t=n.getDecorators();if(t.length===0||!t.some(e=>Qt.has(e.getName()))||t.some(e=>e.getName()===`Type`)||t.some(e=>e.getName()===`IsEnum`))continue;let r=n.getTypeNode();if(!r)continue;let i=r.getText();M(i)||e.report({filePath:e.filePath,message:`Property '${n.getName()}' has type '${i}' with class-validator decorators but is missing @Type() decorator.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},sn=new Set([d.ForStatement,d.ForOfStatement,d.ForInStatement,d.WhileStatement,d.DoStatement]),cn={meta:{id:`performance/no-blocking-constructor`,category:`performance`,severity:`warning`,description:`Constructors in Injectable/Controller classes should not contain heavy operations`,help:`Move heavy initialization logic to the onModuleInit() lifecycle method. Constructors cannot be async, so asynchronous work should always use lifecycle hooks.`},check(e){for(let t of e.sourceFile.getClasses()){if(!(T(t,`Injectable`)||T(t,`Controller`)))continue;let n=t.getConstructors()[0];if(!n)continue;let r=n.getBody();if(r){for(let i of r.getDescendants())if(sn.has(i.getKind())){e.report({filePath:e.filePath,message:`Constructor in '${t.getName()}' contains blocking operation — use onModuleInit() instead.`,help:this.meta.help,line:n.getStartLineNumber(),column:1});break}}}}},ln={meta:{id:`performance/no-dynamic-require`,category:`performance`,severity:`warning`,description:`Dynamic require() with variable arguments prevents bundler optimization`,help:`Use static import statements or dynamic import() with string literals.`},check(e){let t=e.sourceFile.getDescendantsOfKind(d.CallExpression);for(let n of t){if(n.getExpression().getText()!==`require`)continue;let t=n.getArguments();t.length!==0&&t[0].getKind()!==d.StringLiteral&&e.report({filePath:e.filePath,message:`Dynamic require() with non-literal argument prevents bundler optimization.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},un={meta:{id:`performance/no-orphan-modules`,category:`performance`,severity:`info`,description:`Module is never imported by any other module and may be dead code`,help:`Import this module in another module or remove it if it is unused.`,scope:`project`},check(e){let t=new Set;for(let n of e.moduleGraph.modules.values())for(let e of n.imports)t.add(e);for(let n of e.moduleGraph.modules.values())n.name!==`AppModule`&&(t.has(n.name)||e.report({filePath:n.filePath,message:`Module '${n.name}' is never imported by any other module.`,help:this.meta.help,line:n.classDeclaration.getStartLineNumber(),column:1}))}},dn={meta:{id:`performance/no-request-scope-abuse`,category:`performance`,severity:`warning`,description:`Scope.REQUEST creates a new provider instance per request — use only when necessary`,help:`Remove Scope.REQUEST unless the provider genuinely needs per-request state (e.g., request-scoped context). Consider Scope.DEFAULT or Scope.TRANSIENT instead.`},check(e){let t=e.sourceFile.getDescendantsOfKind(d.PropertyAccessExpression);for(let n of t)n.getName()===`REQUEST`&&n.getExpression().getText()===`Scope`&&e.report({filePath:e.filePath,message:`Scope.REQUEST creates a new instance per request, which impacts performance and propagates request scope to all dependents.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}},fn=new Set([`readFileSync`,`writeFileSync`,`existsSync`,`mkdirSync`,`readdirSync`,`statSync`,`accessSync`,`appendFileSync`,`copyFileSync`,`renameSync`,`unlinkSync`]),pn={meta:{id:`performance/no-sync-io`,category:`performance`,severity:`warning`,description:`Synchronous I/O calls block the event loop and should be avoided in NestJS applications`,help:`Use the async variant (e.g., readFile instead of readFileSync) with await.`},check(e){let t=e.sourceFile.getDescendantsOfKind(d.CallExpression);for(let n of t){let t=n.getExpression().getText().split(`.`).pop()??``;fn.has(t)&&e.report({filePath:e.filePath,message:`Synchronous I/O call '${t}()' blocks the event loop.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},mn={meta:{id:`performance/no-unused-module-exports`,category:`performance`,severity:`info`,description:`Module exports a provider that no importing module actually uses`,help:`Remove the unused export or use the provider in an importing module.`,scope:`project`},check(e){for(let t of e.moduleGraph.modules.values()){if(t.exports.length===0)continue;let n=[];for(let r of e.moduleGraph.modules.values())r.name!==t.name&&r.imports.includes(t.name)&&n.push(r.name);if(n.length===0)continue;let r=new Set;for(let i of n){let n=e.moduleGraph.modules.get(i);if(n){for(let t of n.providers){let n=e.providers.get(t);if(n)for(let e of n.dependencies)r.add(e)}if(n.exports.includes(t.name))for(let e of t.exports)r.add(e);for(let t of n.controllers)for(let n of e.files){let i=e.project.getSourceFile(n);if(i)for(let e of i.getClasses()){if(e.getName()!==t)continue;let n=e.getConstructors()[0];if(n)for(let e of n.getParameters()){let t=e.getTypeNode(),n=t?t.getText():e.getType().getText(),i=n.split(`.`).pop()?.split(`<`)[0]??n;r.add(i)}}}}}for(let n of t.exports)e.moduleGraph.modules.has(n)||r.has(n)||e.report({filePath:t.filePath,message:`Module '${t.name}' exports '${n}' but no importing module uses it.`,help:this.meta.help,line:t.classDeclaration.getStartLineNumber(),column:1})}}},hn=new Set([`Cron`,`Interval`,`Timeout`,`OnEvent`,`Process`,`OnQueueEvent`,`EventSubscriber`,`SubscribeMessage`,`WebSocketGateway`]);function gn(e){for(let t of e.getDecorators())if(hn.has(t.getName()))return!0;for(let t of e.getMethods())for(let e of t.getDecorators())if(hn.has(e.getName()))return!0;return!1}const _n={meta:{id:`performance/no-unused-providers`,category:`performance`,severity:`warning`,description:`Injectable providers that are never injected and have no self-activating decorators may be dead code`,help:`Remove the unused provider, inject it where needed, or verify it is activated by a framework decorator (e.g. @Cron, @OnEvent).`,scope:`project`},check(e){let t=new Set;for(let n of e.providers.values())for(let e of n.dependencies)t.add(e);let n=[`Controller`,`Resolver`,`WebSocketGateway`];for(let r of e.files){let i=e.project.getSourceFile(r);if(i)for(let e of i.getClasses()){if(!n.some(t=>e.getDecorator(t)!==void 0))continue;let r=e.getConstructors()[0];if(r)for(let e of r.getParameters()){let n=e.getTypeNode(),r=n?n.getText():e.getType().getText(),i=r.split(`.`).pop()?.split(`<`)[0]??r;t.add(i)}}}for(let n of e.providers.values()){let r=n.name;if(Ct.some(e=>r.endsWith(e))||t.has(r)||gn(n.classDeclaration))continue;let i=!1;for(let t of e.moduleGraph.modules.values())if(t.exports.includes(r)){i=!0;break}i||e.report({filePath:n.filePath,message:`Provider '${r}' is never injected by any other provider or controller.`,help:this.meta.help,line:n.classDeclaration.getStartLineNumber(),column:1})}}},vn={meta:{id:`schema/require-cascade-rule`,category:`schema`,scope:`schema`,severity:`info`,description:`Relations should have explicit onDelete/cascade behavior defined`,help:`Add an explicit onDelete option (e.g. CASCADE, SET NULL) to avoid relying on database defaults.`},check(e){for(let t of e.schemaGraph.relations)if(!(t.type!==`many-to-one`&&t.type!==`one-to-one`)&&!t.onDelete){let n=e.schemaGraph.entities.get(t.fromEntity);if(!n)continue;e.report({filePath:n.filePath,entity:n.name,message:`Relation '${t.propertyName}' on '${t.fromEntity}' has no explicit onDelete behavior.`,help:this.meta.help})}}},yn={meta:{id:`schema/require-primary-key`,category:`schema`,scope:`schema`,severity:`error`,description:`Every entity must have at least one primary key column`,help:`Add a primary key column (e.g. @id in Prisma, @PrimaryColumn/@PrimaryGeneratedColumn in TypeORM).`},check(e){for(let t of e.schemaGraph.entities.values())t.columns.some(e=>e.isPrimary)||e.report({filePath:t.filePath,entity:t.name,message:`Entity '${t.name}' has no primary key column.`,help:this.meta.help})}},bn=/delete/i;function xn(e,t){let n=new Set(e.columns.map(e=>e.name.toLowerCase()));return n.has(`createdat`)||n.has(`created_at`)?!0:t===`typeorm`?e.columns.some(e=>e.type===`timestamp`&&e.isGenerated&&!bn.test(e.name)):t===`prisma`?e.columns.some(e=>e.type===`DateTime`&&e.defaultValue!==void 0&&e.defaultValue.includes(`now()`)):t===`drizzle`?e.columns.some(e=>(e.type===`timestamp`||e.type===`date`||e.type===`datetime`)&&e.defaultValue!==void 0&&e.defaultValue.includes(`now()`)):!1}const Sn={meta:{id:`schema/require-timestamps`,category:`schema`,scope:`schema`,severity:`warning`,description:`Entities should have timestamp columns (createdAt/updatedAt)`,help:`Add createdAt/updatedAt columns to track when records are created and modified.`},check(e){for(let t of e.schemaGraph.entities.values())xn(t,e.orm)||e.report({filePath:t.filePath,entity:t.name,message:`Entity '${t.name}' has no timestamp columns (createdAt/updatedAt).`,help:this.meta.help})}},Cn={meta:{id:`security/no-csrf-disabled`,category:`security`,severity:`error`,description:`CSRF protection should not be explicitly disabled`,help:`Enable CSRF protection or remove the explicit disabling of it.`},check(e){let t=e.sourceFile.getDescendantsOfKind(d.PropertyAssignment);for(let n of t){let t=n.getName();if(t!==`csrf`&&t!==`csrfProtection`)continue;let r=n.getInitializer();r&&r.getText()===`false`&&e.report({filePath:e.filePath,message:`CSRF protection explicitly disabled (${t}: false).`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},wn={meta:{id:`security/no-dangerous-redirects`,category:`security`,severity:`error`,description:`Redirects using user-controlled input (from @Query/@Param) are an open redirect vulnerability`,help:`Validate redirect URLs against an allowlist of safe destinations.`},check(e){for(let t of e.sourceFile.getClasses())if(E(t))for(let n of t.getMethods()){let t=new Set;for(let e of n.getParameters())e.getDecorators().some(e=>e.getName()===`Query`||e.getName()===`Param`)&&t.add(e.getName());if(t.size===0)continue;let r=n.getDescendantsOfKind(d.CallExpression);for(let n of r)if(n.getExpression().getText().endsWith(`redirect`))for(let r of n.getArguments()){let i=r.getText();t.has(i)&&e.report({filePath:e.filePath,message:`Redirect uses user-controlled parameter '${i}' — open redirect risk.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}let i=n.getDecorators().find(e=>e.getName()===`Redirect`);if(i)for(let n of i.getArguments()){let r=n.getText();t.has(r)&&e.report({filePath:e.filePath,message:`@Redirect() uses user-controlled parameter '${r}' — open redirect risk.`,help:this.meta.help,line:i.getStartLineNumber(),column:1})}}}},Tn={meta:{id:`security/no-eval`,category:`security`,severity:`error`,description:`Usage of eval() or new Function() is a security risk and should be avoided`,help:`Refactor to avoid eval() and new Function(). Use safer alternatives like JSON.parse() or a sandboxed interpreter.`},check(e){let t=e.sourceFile.getDescendantsOfKind(d.CallExpression);for(let n of t)n.getExpression().getText()===`eval`&&e.report({filePath:e.filePath,message:`Usage of eval() is a security risk.`,help:this.meta.help,line:n.getStartLineNumber(),column:1});let n=e.sourceFile.getDescendantsOfKind(d.NewExpression);for(let t of n)t.getExpression().getText()===`Function`&&e.report({filePath:e.filePath,message:`Usage of new Function() is a security risk.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}},En={meta:{id:`security/no-exposed-env-vars`,category:`security`,severity:`warning`,description:`Use NestJS ConfigService instead of direct process.env access in Injectable/Controller classes`,help:`Inject ConfigService and use configService.get('VAR_NAME') instead of process.env.VAR_NAME.`},check(e){for(let t of e.sourceFile.getClasses()){if(!(T(t,`Injectable`)||T(t,`Controller`)))continue;let n=t.getDescendantsOfKind(d.PropertyAccessExpression);for(let r of n)r.getExpression().getText()===`process.env`&&e.report({filePath:e.filePath,message:`Direct 'process.env.${r.getName()}' access in '${t.getName()}'. Use ConfigService instead.`,help:this.meta.help,line:r.getStartLineNumber(),column:1})}}},Dn=/^(error|err|e|ex|exception)$/,On={meta:{id:`security/no-exposed-stack-trace`,category:`security`,severity:`warning`,description:`Stack traces should not be exposed in responses — they leak internal implementation details`,help:`Log the stack trace internally and return a generic error message to the client.`},check(e){let t=e.sourceFile.getDescendantsOfKind(d.PropertyAccessExpression);for(let n of t){if(n.getName()!==`stack`)continue;let t=n.getExpression().getText();if(!(Dn.test(t)||t.endsWith(`.error`)||t.endsWith(`.err`)))continue;let r=n.getParent();if(!r)continue;let i=r.getKind();(i===d.ReturnStatement||i===d.PropertyAssignment||i===d.ShorthandPropertyAssignment||i===d.CallExpression)&&e.report({filePath:e.filePath,message:`Stack trace '${t}.stack' may be exposed in response — leaks implementation details.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},kn=[{pattern:/^(?=.*\d)[A-Za-z0-9+/]{40,}={0,2}$/,name:`Base64 key`},{pattern:/^sk[-_][a-zA-Z0-9]{20,}$/,name:`Secret key`},{pattern:/^pk[-_][a-zA-Z0-9]{20,}$/,name:`Public key (in source)`},{pattern:/^ghp_[a-zA-Z0-9]{36,}$/,name:`GitHub personal access token`},{pattern:/^github_pat_[a-zA-Z0-9_]{22,}$/,name:`GitHub fine-grained PAT`},{pattern:/^gho_[a-zA-Z0-9]{36,}$/,name:`GitHub OAuth token`},{pattern:/^xox[bpras]-[a-zA-Z0-9-]+$/,name:`Slack token`},{pattern:/^eyJ[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,}\./,name:`JWT token`},{pattern:/^AKIA[0-9A-Z]{16}$/,name:`AWS Access Key ID`},{pattern:/^[a-f0-9]{64}$/,name:`Hex-encoded secret (64 chars)`}],An=[/secret/i,/password/i,/passwd/i,/api[_-]?key/i,/auth[_-]?token/i,/private[_-]?key/i,/access[_-]?key/i,/client[_-]?secret/i],jn=new Set([`your-secret-here`,`changeme`,`password`]),Mn=/^[A-Za-z][A-Za-z0-9_]*(\.[A-Za-z][A-Za-z0-9_]*)+$/,Nn=new Set([`cursor`,`nextCursor`,`prevCursor`,`previousCursor`,`startCursor`,`endCursor`,`pageToken`,`nextPageToken`,`continuationToken`,`continuation`,`nextPage`,`afterCursor`,`beforeCursor`]);function Pn(e){return!(e.length<8||e.includes("${")||e.startsWith(`process.env`)||jn.has(e)||e.includes(` `)||Mn.test(e))}function Fn(e){return An.some(t=>t.test(e))}function In(e){try{let t=Buffer.from(e,`base64`).toString(`utf-8`);return JSON.parse(t),!0}catch{return!1}}function Ln(e){let t=new Map;for(let n of e)t.set(n,(t.get(n)??0)+1);let n=0;for(let r of t.values()){let t=r/e.length;n-=t*Math.log2(t)}return n}const Rn=new Set([...`aeiouyAEIOUY`]),zn=/^[A-Z]{2,4}_/,Bn=/(?<=[a-z])(?=[A-Z])|(?<=[A-Za-z])(?=\d)|(?<=\d)(?=[A-Za-z])|_/,Vn=/[a-zA-Z]/;function Hn(e){let t=e.includes(`_`),n=e.split(Bn).filter(e=>e.length>0).filter(e=>Vn.test(e)),r=n.filter(e=>e.length>=4&&[...e].some(e=>Rn.has(e)));return n.slice(0,6).filter(e=>e.length>=4&&[...e].some(e=>Rn.has(e))).length>=2||t&&e.split(`_`).filter(e=>e.length>=3).length>=2||zn.test(e)?!0:(Ln(e)>4.9&&!t&&r.length,!1)}function Un(e){let t=e.getParent();if(!t)return!1;let n=t.asKind(d.PropertyAssignment);if(n)return Nn.has(n.getName());let r=t.asKind(d.VariableDeclaration);return r?Nn.has(r.getName()):!1}const Wn={meta:{id:`security/no-hardcoded-secrets`,category:`security`,severity:`error`,description:`Detect hardcoded secrets, API keys, and tokens in source code`,help:`Move secrets to environment variables and access them via ConfigService.`},check(e){let t=e.sourceFile.getDescendantsOfKind(d.StringLiteral);for(let n of t){let t=n.getLiteralValue();if(!(t.length<16)&&n.getParent()?.getKind()!==d.ImportDeclaration){for(let{pattern:r,name:i}of kn)if(r.test(t)){if(i===`Base64 key`&&(In(t)||Un(n)||Hn(t)))break;e.report({filePath:e.filePath,message:`Possible hardcoded ${i} detected.`,help:this.meta.help,line:n.getStartLineNumber(),column:1});break}}}let n=e.sourceFile.getDescendantsOfKind(d.VariableDeclaration);for(let t of n){let n=t.getName(),r=t.getInitializer();!r||r.getKind()!==d.StringLiteral||Fn(n)&&Pn(r.getText().slice(1,-1))&&e.report({filePath:e.filePath,message:`Variable '${n}' appears to contain a hardcoded secret.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}let r=e.sourceFile.getDescendantsOfKind(d.PropertyAssignment);for(let t of r){let n=t.getName(),r=t.getInitializer();!r||r.getKind()!==d.StringLiteral||Fn(n)&&Pn(r.getText().slice(1,-1))&&e.report({filePath:e.filePath,message:`Property '${n}' appears to contain a hardcoded secret.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}},Gn=RegExp(`(?:^|[^a-zA-Z])\\w*(?:${[`Entity`,`Model`].join(`|`)})(?:[^a-zA-Z]|$)`),Kn={meta:{id:`security/no-raw-entity-in-response`,category:`security`,severity:`warning`,description:`Returning ORM entities directly from controllers can leak internal fields like passwords or IDs`,help:`Map entities to DTOs or use class-transformer's @Exclude()/@Expose() decorators before returning.`},check(e){for(let t of e.sourceFile.getClasses())if(E(t))for(let n of t.getMethods()){if(!O(n))continue;let t=n.getReturnType().getText();Gn.test(t)&&!t.includes(`DTO`)&&!t.includes(`Dto`)&&!t.includes(`Response`)&&e.report({filePath:e.filePath,message:`Controller method '${n.getName()}' returns a raw entity type. This may leak internal fields.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},qn={meta:{id:`security/no-synchronize-in-production`,category:`security`,severity:`error`,description:`TypeORM synchronize: true auto-syncs schema and can drop columns or tables in production`,help:`Set synchronize: false and use migrations for production schema changes.`},check(e){let t=e.sourceFile.getDescendantsOfKind(d.PropertyAssignment);for(let n of t){if(n.getName()!==`synchronize`)continue;let t=n.getInitializer();t&&t.getText()===`true`&&e.report({filePath:e.filePath,message:`TypeORM 'synchronize: true' can auto-drop columns and tables in production.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},Jn=new Set([`md5`,`sha1`]),Yn={meta:{id:`security/no-weak-crypto`,category:`security`,severity:`warning`,description:`Weak hashing algorithms (MD5, SHA1) should not be used for security purposes`,help:`Use a stronger algorithm like SHA-256 or bcrypt for password hashing.`},check(e){let t=e.sourceFile.getDescendantsOfKind(d.CallExpression);for(let n of t){if(!n.getExpression().getText().endsWith(`createHash`))continue;let t=n.getArguments();if(t.length===0)continue;let r=t[0];if(r.getKind()!==d.StringLiteral)continue;let i=r.getText().slice(1,-1).toLowerCase();Jn.has(i)&&e.report({filePath:e.filePath,message:`Weak hashing algorithm '${i}' used in createHash().`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},Xn=new Set([`Public`,`AllowAnonymous`,`SkipAuth`,`IsPublic`]),Zn=[je,gt,ot,dt,nt,vt,yt,xt,Ee,Ze,Wt,qt,Mt,jt,Lt,Vt,It,zt,Et,At,Bt,Gt,Ft,Ut,St,on,Ot,Yt,Rt,wt,Wn,Tn,Yn,En,Cn,On,wn,qn,Kn,{meta:{id:`security/require-guards-on-endpoints`,category:`security`,severity:`warning`,description:`Controller endpoints should be protected by @UseGuards() at class or method level`,help:`Add @UseGuards(AuthGuard) to the controller class or individual route handlers, or mark routes as @Public(). If you use a global guard via APP_GUARD, you can disable this rule.`},check(e){for(let t of e.sourceFile.getClasses())if(E(t)&&t.getDecorator(`UseGuards`)===void 0&&!t.getDecorators().some(e=>Xn.has(e.getName())))for(let n of t.getMethods())O(n)&&n.getDecorator(`UseGuards`)===void 0&&(n.getDecorators().some(e=>Xn.has(e.getName()))||e.report({filePath:e.filePath,message:`Endpoint '${n.getName()}' has no @UseGuards() at class or method level.`,help:this.meta.help,line:n.getStartLineNumber(),column:1}))}},pn,cn,ln,dn,_n,mn,un,yn,Sn,vn];function Qn(e){return e.meta.scope===`project`}function $n(e){return e.meta.scope===`schema`}function er(e,t,n){if(t.length===0)return e;let r=new Set(e.map(e=>e.meta.id)),i=[...e];for(let e of t){if(r.has(e.meta.id)){n.push(`Custom rule "${e.meta.id}" conflicts with a built-in rule and was skipped`);continue}i.push(e)}return i}function tr(e,t){return t.filter(t=>{let n=e.rules?.[t.meta.id];return!(n===!1||typeof n==`object`&&n.enabled===!1||e.categories?.[t.meta.category]===!1)})}function nr(e){let t=[],n=[],r=[];for(let i of e)$n(i)?r.push(i):Qn(i)?n.push(i):t.push(i);return{fileRules:t,projectRules:n,schemaRules:r}}const rr=new Set([`security`,`performance`,`correctness`,`architecture`]),ir=new Set([`error`,`warning`,`info`]),ar=new Set([`file`,`project`]),or=`custom/`;function sr(e){if(typeof e!=`object`||!e)return!1;let t=e;if(typeof t.check!=`function`||typeof t.meta!=`object`||t.meta===null)return!1;let n=t.meta;return!(typeof n.id!=`string`||n.id.trim()===``||typeof n.description!=`string`||typeof n.help!=`string`||!rr.has(n.category)||!ir.has(n.severity)||n.scope!==void 0&&!ar.has(n.scope))}function cr(e){return e.meta.id.startsWith(or)?e:{...e,meta:{...e.meta,id:`${or}${e.meta.id}`}}}async function lr(e,t){let n=[],r=[],i=s(t,e);if(!p(i))return r.push(`Custom rules directory not found: ${i}`),{rules:n,warnings:r};if(!g(i).isDirectory())return r.push(`Custom rules path is not a directory: ${i}`),{rules:n,warnings:r};let a;try{a=h(i)}catch(e){return r.push(`Failed to read custom rules directory: ${e instanceof Error?e.message:String(e)}`),{rules:n,warnings:r}}let o=a.filter(e=>e.endsWith(`.ts`));if(o.length===0)return r.push(`No rule files (.ts) found in: ${i}`),{rules:n,warnings:r};let c=_(i,{interopDefault:!0});for(let e of o){let t=s(i,e),a;try{a=await c.import(t)}catch(t){r.push(`Failed to load custom rule file "${e}": ${t instanceof Error?t.message:String(t)}`);continue}let o=!1;for(let[t,i]of Object.entries(a))sr(i)?(n.push(cr(i)),o=!0):t!==`__esModule`&&typeof i==`object`&&i&&`meta`in i&&r.push(`Invalid rule export "${t}" in "${e}": missing or invalid required fields (check, meta.id, meta.description, meta.help, meta.category, meta.severity)`);!o&&Object.keys(a).length>0&&(Object.values(a).some(e=>typeof e==`object`&&!!e&&(`meta`in e||`check`in e))||r.push(`No valid rule exports found in "${e}"`))}return{rules:n,warnings:r}}function ur(e,t){return e.customRulesDir?lr(e.customRulesDir,t):Promise.resolve({rules:[],warnings:[]})}async function dr(e,t){let n=await xe(e,t),{rules:r,warnings:i}=await ur(n,e),a=er(Zn,r,i),{fileRules:o,projectRules:s,schemaRules:c}=nr(tr(n,a));return{combinedRules:a,config:n,customRuleWarnings:i,fileRules:o,projectRules:s,schemaRules:c}}async function fr(e,t={}){return(await c(t.include??C.include,{cwd:e,absolute:!0,ignore:t.exclude??C.exclude})).sort()}async function pr(e,t,n={}){let r=await Promise.all([...t.projects.entries()].map(async([t,r])=>[t,await fr(a(e,r),n)])),i=new Map;for(let[e,t]of r)i.set(e,t);return i}function mr(e){let t=new u({compilerOptions:{strict:!0,target:99,module:99,skipFileDependencyResolution:!0},skipAddingFilesFromTsConfig:!0});for(let n of e)t.addSourceFileAtPath(n);return t}const hr=/import\([^)]+\)\.(\w+)/,gr=/^(\w+)</;function _r(e,t){let n=[];for(let r of e.getClasses()){if(!r.getDecorator(`Injectable`))continue;let e=r.getName();if(!e)continue;let i=r.getConstructors()[0],a=i?i.getParameters().map(e=>{let t=e.getTypeNode();return N(t?t.getText():e.getType().getText())}):[],o=r.getMethods().filter(e=>{let t=e.getScope();return!t||t===`public`}).length;n.push({name:e,filePath:t,classDeclaration:r,dependencies:a,publicMethodCount:o})}return n}function vr(e,t){let n=new Map;for(let r of t){let t=e.getSourceFile(r);if(t)for(let e of _r(t,r))n.set(e.name,e)}return n}function N(e){let t=e.match(hr);if(t)return t[1];let n=e.match(gr);return n?n[1]:e}const P=/^['"`]|['"`]$/g,yr=/\/+/g,br=/\/$/,xr=new Set([`Query`,`Mutation`,`Subscription`]),Sr=new Set([`ApiOperation`,`ApiParam`,`ApiQuery`,`ApiResponse`,`ApiBody`]),Cr=new Set([`map`,`forEach`,`filter`,`find`,`some`,`every`,`flatMap`,`reduce`]);var wr=class{scanResults=new Map;injectionMaps=new Map;methodLookups=new Map;getScan(e){return this.scanResults.get(e)}setScan(e,t){this.scanResults.set(e,t)}getInjMap(e){return this.injectionMaps.get(e)}setInjMap(e,t){this.injectionMaps.set(e,t)}getMethod(e){return this.methodLookups.has(e)?this.methodLookups.get(e):void 0}hasMethod(e){return this.methodLookups.has(e)}setMethod(e,t){this.methodLookups.set(e,t)}};function F(e){let t=e.replace(/\s+/g,` `).trim();return t.length>50?`${t.slice(0,50)}\u2026`:t}function I(e,t){let n={isConditional:!1,conditionText:null,branchKind:null,statementLine:null},r=e;for(;r&&r!==t;){let e=r.getParent();if(!e||e===t)break;let n=e.getKind();if(n===d.IfStatement){let t=e.asKindOrThrow(d.IfStatement);if(r===t.getThenStatement()){let n=e.getParent();if(n&&n.getKind()===d.IfStatement){let r=n.asKindOrThrow(d.IfStatement);if(e===r.getElseStatement())return{isConditional:!0,conditionText:F(t.getExpression().getText()),branchKind:`else-if`,statementLine:r.getStartLineNumber()}}return{isConditional:!0,conditionText:F(t.getExpression().getText()),branchKind:`if`,statementLine:t.getStartLineNumber()}}if(r===t.getElseStatement())return{isConditional:!0,conditionText:F(t.getExpression().getText()),branchKind:`else`,statementLine:t.getStartLineNumber()}}if(n===d.ConditionalExpression){let t=e.asKindOrThrow(d.ConditionalExpression);if(r===t.getWhenTrue())return{isConditional:!0,conditionText:F(t.getCondition().getText()),branchKind:`ternary-true`,statementLine:t.getStartLineNumber()};if(r===t.getWhenFalse())return{isConditional:!0,conditionText:F(t.getCondition().getText()),branchKind:`ternary-false`,statementLine:t.getStartLineNumber()}}let i=r.getKind();if(i===d.CaseClause){let e=r.asKindOrThrow(d.CaseClause),t=r.getParentOrThrow().getParentOrThrow();return{isConditional:!0,conditionText:F(e.getExpression().getText()),branchKind:`case`,statementLine:t.getStartLineNumber()}}if(i===d.DefaultClause)return{isConditional:!0,conditionText:null,branchKind:`default`,statementLine:r.getParentOrThrow().getParentOrThrow().getStartLineNumber()};if(i===d.CatchClause)return{isConditional:!0,conditionText:null,branchKind:`catch`,statementLine:r.getParentOrThrow().getStartLineNumber()};r=e}return n}const Tr=new Map([[d.ForStatement,`for`],[d.ForOfStatement,`for-of`],[d.ForInStatement,`for-in`],[d.WhileStatement,`while`],[d.DoStatement,`do-while`]]);function L(e,t){let n={iterationKind:null,iterationLabel:null},r=e;for(;r&&r!==t;){let e=r.getParent();if(!e||e===t)break;let n=e.getKind(),i=Tr.get(n);if(i){let t=!1;if(n===d.ForStatement){let n=e.asKindOrThrow(d.ForStatement);t=r!==n.getInitializer()&&r!==n.getCondition()&&r!==n.getIncrementor()&&r===n.getStatement()}else n===d.ForOfStatement?t=r===e.asKindOrThrow(d.ForOfStatement).getStatement():n===d.ForInStatement?t=r===e.asKindOrThrow(d.ForInStatement).getStatement():n===d.WhileStatement?t=r===e.asKindOrThrow(d.WhileStatement).getStatement():n===d.DoStatement&&(t=r===e.asKindOrThrow(d.DoStatement).getStatement());if(t)return{iterationKind:`loop`,iterationLabel:i}}let a=r.getKind();if(a===d.ArrowFunction||a===d.FunctionExpression){if(n===d.CallExpression){let t=e.asKindOrThrow(d.CallExpression);if(t.getArguments().some(e=>e===r)){let e=t.getExpression();if(e.getKind()===d.PropertyAccessExpression){let t=e.asKindOrThrow(d.PropertyAccessExpression).getName();if(Cr.has(t))return{iterationKind:`callback`,iterationLabel:t}}}}break}if(n===d.ArrayLiteralExpression){let t=e.getParent();if(t&&t.getKind()===d.CallExpression){let n=t.asKindOrThrow(d.CallExpression),r=n.getArguments();if(r.length>0&&r[0]===e){let e=n.getExpression();if(e.getKind()===d.PropertyAccessExpression){let t=e.asKindOrThrow(d.PropertyAccessExpression);if(t.getName()===`all`&&t.getExpression().getText().endsWith(`Promise`))return{iterationKind:`concurrent`,iterationLabel:`all`}}}}}r=e}return n}function R(e){let t=e;for(;t;){let e=t.getKind();if(e===d.ExpressionStatement||e===d.VariableStatement||e===d.ReturnStatement||e===d.ThrowStatement)break;t=t.getParent()}if(!t)return null;let n=t.getSourceFile(),r=t.getFullStart(),i=t.getStart(),a=n.getFullText().slice(r,i).split(`
|
|
5
|
-
`);for(let e=a.length-1;e>=0;e--){let t=a[e].trim();if(t.startsWith(`//`))return t.slice(2).trim();if(t.length>0)break}return null}function Er(e){let t=e.asKindOrThrow(d.ThrowStatement).getExpression();return t&&t.getKind()===d.NewExpression?N(t.asKindOrThrow(d.NewExpression).getExpression().getText()):`Error`}function Dr(e){let t=e.asKindOrThrow(d.ThrowStatement).getExpression();if(!t||t.getKind()!==d.NewExpression)return null;let n=t.asKindOrThrow(d.NewExpression).getArguments();if(n.length===0)return null;let r=n[0],i=r.getKind(),a;if(i===d.StringLiteral||i===d.NoSubstitutionTemplateLiteral)a=r.asKindOrThrow(i).getLiteralValue();else if(i===d.TemplateExpression){let e=r.getText();a=e.startsWith("`")?e.slice(1,-1):e}else a=r.getText();return a.length>80?`${a.slice(0,80)}\u2026`:a}function Or(e){let t=e.getParent();for(;t;){let e=t.getKind();if(e===d.AwaitExpression||e===d.ParenthesizedExpression||e===d.AsExpression||e===d.NonNullExpression){t=t.getParent();continue}if(e===d.VariableDeclaration){let e=t.asKindOrThrow(d.VariableDeclaration).getNameNode();return e.getKind()===d.Identifier?e.getText():null}return null}return null}function kr(e,t){let n;try{n=e.getBaseClass()}catch{}if(!n&&t){let r=e.getExtends();if(r){let e=N(r.getExpression().getText()),i=t.get(e);i&&(n=i.classDeclaration)}}return n}function z(e,t,n,r){let i=`${e.getName()??``}.${t}`;if(r?.hasMethod(i))return r.getMethod(i);let a=e,o=new Set;for(;a;){let e=a.getName();if(e&&o.has(e))break;e&&o.add(e);let s=a.getInstanceMethod(t);if(s)return r?.setMethod(i,s),s;a=kr(a,n)}r?.setMethod(i,void 0)}function Ar(e){let t=e.getDecorator(`Controller`);if(!t)return``;let n=t.getArguments();if(n.length===0)return``;let r=n[0];if(r.getKind()===d.ObjectLiteralExpression){let e=r.asKindOrThrow(d.ObjectLiteralExpression).getProperty(`path`);if(!e)return``;let t=e.asKind(d.PropertyAssignment);if(!t)return``;let n=t.getInitializer();return n?n.getText().replace(P,``):``}return r.getText().replace(P,``)}function jr(e){for(let t of e.getDecorators()){let e=t.getName();if(!w.has(e))continue;let n=t.getArguments(),r=n.length>0?n[0].getText().replace(P,``):``;return{httpMethod:e.toUpperCase(),path:r}}}function Mr(e,t){return`/${[e,t].filter(Boolean).join(`/`)}`.replace(yr,`/`).replace(br,``)||`/`}function B(e,t){let n=e.asKind(d.ObjectLiteralExpression);if(!n)return null;let r=n.getProperty(t);if(!r)return null;let i=r.asKind(d.PropertyAssignment);if(!i)return null;let a=i.getInitializer();return a?a.getText().replace(P,``):null}function Nr(e,t){let n=B(e,t);if(n===null)return null;let r=Number(n);return Number.isNaN(r)?null:r}function Pr(e,t,n){let r=B(e,t);return r===null?n:r===`true`}function Fr(e){let t=null,n=null,r=[],i=[],a=[],o=null,s=!1;for(let c of e.getDecorators()){let e=c.getName();if(!Sr.has(e))continue;s=!0;let l=c.getArguments();if(l.length===0)continue;let u=l[0];if(e===`ApiOperation`)t=B(u,`summary`),n=B(u,`description`);else if(e===`ApiParam`){let e=B(u,`name`);e&&r.push({description:B(u,`description`),name:e,required:Pr(u,`required`,!0),type:B(u,`type`)})}else if(e===`ApiQuery`){let e=B(u,`name`);e&&i.push({description:B(u,`description`),name:e,required:Pr(u,`required`,!1),type:B(u,`type`)})}else if(e===`ApiResponse`){let e=Nr(u,`status`)??200,t=B(u,`type`);t?.startsWith(`[`)&&t.endsWith(`]`)&&(t=`${t.slice(1,-1)}[]`),a.push({description:B(u,`description`),status:e,type:t})}else e===`ApiBody`&&(o={description:B(u,`description`),type:B(u,`type`)})}if(!o){for(let t of e.getParameters())if(t.getDecorators().some(e=>e.getName()===`Body`)){let e=t.getTypeNode();e&&(o={description:null,type:e.getText()},s=!0);break}}return s?{body:o,description:n,params:r,queryParams:i,responses:a,summary:t}:null}const Ir=/^(?:Promise|Observable)<(.+)>$/;function V(e){let t=e.getReturnTypeNode();if(!t)return null;let n=t.getText().trim(),r=Ir.exec(n);return r&&(n=r[1]),n===`void`||n===`any`||n===`unknown`?null:n}function Lr(e){return e.getParameters().filter(e=>e.getName()!==`this`).map(e=>({name:e.getName(),type:e.getTypeNode()?.getText()??null}))}const Rr=/=>\s*\{[^}]*\}/g,zr=/\(([^)]{20,})\)\s*=>/g,Br=/\s+/g;function Vr(e){let t=e.replace(Br,` `).trim();return t=t.replace(Rr,`=> …`),t=t.replace(zr,`(…) =>`),t.length>50&&(t=`${t.slice(0,47)}…`),t}function Hr(e,t){let n=e.getKind();if(n!==d.VariableStatement&&n!==d.ExpressionStatement)return!1;let r=[...e.getDescendantsOfKind(d.CallExpression),...e.getDescendantsOfKind(d.NewExpression)];if(r.length===0)return!1;for(let e of r)if(t.has(e.getStart()))return!1;for(let e of r){let t=e.getText();if(t.startsWith(`console.`)||t.startsWith(`this.logger.`))return!1}return!0}function Ur(e){if(e.getKind()===d.VariableStatement){let t=e.asKindOrThrow(d.VariableStatement).getDeclarationList().getDeclarations();if(t.length===0)return null;let n=t[0],r=n.getNameNode().getText(),i=n.getInitializer();return i?{assignedTo:r,text:`${r} = ${Vr(i.getText())}`}:null}return e.getKind()===d.ExpressionStatement?{assignedTo:null,text:Vr(e.asKindOrThrow(d.ExpressionStatement).getExpression().getText())}:null}function Wr(e,t,n){let r=e.getName()??``;if(n){let e=n.getInjMap(r);if(e)return e}let i=new Map,a=e,o=new Set;for(;a;){let e=a.getName();if(e&&o.has(e))break;e&&o.add(e);let n=a.getConstructors()[0];if(n){for(let e of n.getParameters()){let t=e.getName();if(!i.has(t)){let n=e.getTypeNode(),r=n?n.getText():e.getType().getText();i.set(t,N(r))}}break}a=kr(a,t)}for(let t of e.getProperties())if(t.getDecorator(`Inject`)){let e=t.getName();if(!i.has(e)){let n=t.getTypeNode();n&&i.set(e,N(n.getText()))}}return n&&n.setInjMap(r,i),i}function Gr(e,t){for(let n of e){if(!n.assignedTo)continue;let e=RegExp(`\\b${n.assignedTo}\\b`);for(let r of t)if(!r.merged&&!(r.order<=n.order)&&r.conditional&&r.conditionText&&e.test(r.conditionText)){n.guardThrow={branchKind:r.branchKind,callSiteLine:r.callSiteLine,className:r.exceptionClassName,conditionText:r.conditionText,message:r.message},r.merged=!0;break}}let n=t.filter(e=>!e.merged);t.length=0;for(let e of n)t.push(e)}function Kr(e,t,n,r,i){let a=`${n?.getName()??``}::${e.getName()}`;if(!r&&i){let e=i.getScan(a);if(e)return e}let o={deps:[],sameClassCalls:[],steps:[],throws:[]},s=e.getBody();if(!s)return o;let c=r??new Set,l=e.getName();if(c.has(l))return o;c.add(l);let u=new Map;for(let e of s.getDescendantsOfKind(d.VariableDeclaration)){let n=e.getInitializer();if(n&&n.getKind()===d.PropertyAccessExpression){let r=n.asKindOrThrow(d.PropertyAccessExpression);if(r.getExpression().getKind()===d.ThisKeyword){let n=r.getName();t.has(n)&&u.set(e.getName(),n)}}}let f=[],p=[],m=[],h=0,g=s.getDescendantsOfKind(d.CallExpression),_=s.getDescendantsOfKind(d.ThrowStatement),v=[...g.map(e=>({kind:`call`,node:e})),..._.map(e=>({kind:`throw`,node:e}))];v.sort((e,t)=>e.node.getStart()-t.node.getStart());for(let e of v){if(e.kind===`throw`){let t=I(e.node,s),n=L(e.node,s);p.push({branchGroupId:t.statementLine?`L${t.statementLine}`:null,branchKind:t.branchKind,callSiteLine:e.node.getStartLineNumber(),comment:R(e.node),conditional:t.isConditional,conditionText:t.conditionText,exceptionClassName:Er(e.node),iterationKind:n.iterationKind,iterationLabel:n.iterationLabel,message:Dr(e.node),order:h++});continue}let r=e.node,i=r.getExpression();if(i.getKind()!==d.PropertyAccessExpression)continue;let a=i.asKindOrThrow(d.PropertyAccessExpression),o=a.getName(),l=a.getExpression(),g;if(l.getKind()===d.PropertyAccessExpression){let e=l.asKindOrThrow(d.PropertyAccessExpression);if(e.getExpression().getKind()===d.ThisKeyword){let n=e.getName();t.has(n)&&(g=n)}}if(!g&&l.getKind()===d.Identifier){let e=l.getText(),t=u.get(e);t&&(g=t)}if(g){let e=I(r,s),t=L(r,s);m.push({assignedTo:Or(r),paramName:g,methodName:o,order:h++,callSiteLine:r.getStartLineNumber(),comment:R(r),condInfo:e,iterInfo:t,guardThrow:null});continue}if(l.getKind()===d.ThisKeyword&&n){let e=n.getInstanceMethod(o);if(e&&!c.has(o)){let i=I(r,s),a=L(r,s),l=Kr(e,t,n,new Set(c));f.push({assignedTo:Or(r),branchGroupId:i.statementLine?`L${i.statementLine}`:null,branchKind:i.branchKind,callSiteLine:r.getStartLineNumber(),childResult:l,comment:R(r),conditional:i.isConditional,conditionText:i.conditionText,iterationKind:a.iterationKind,iterationLabel:a.iterationLabel,methodName:o,order:h++})}}}Gr(m,p);let y=[];if(s.getKind()===d.Block){let e=new Set;for(let t of m)for(let n of g)n.getStartLineNumber()===t.callSiteLine&&e.add(n.getStart());for(let t of p)for(let n of _)n.getStartLineNumber()===t.callSiteLine&&e.add(n.getStart());for(let t of f)for(let n of g)n.getStartLineNumber()===t.callSiteLine&&e.add(n.getStart());let t=s.asKindOrThrow(d.Block).getStatements(),n=[],r=()=>{if(n.length===0)return;let e=n[0].stmt,t=I(e,s),r=L(e,s);y.push({branchGroupId:t.statementLine?`L${t.statementLine}`:null,branchKind:t.branchKind,callSiteLine:e.getStartLineNumber(),comment:R(e),conditional:t.isConditional,conditionText:t.conditionText,iterationKind:r.iterationKind,iterationLabel:r.iterationLabel,order:0,statements:n.map(e=>e.info)}),n=[]};for(let i of t){let t=i.getStart(),a=i.getEnd(),o=!1;for(let n of e)if(n>=t&&n<=a){o=!0;break}if(o){r();continue}if(Hr(i,e)){let e=Ur(i);if(e){n.push({info:e,stmt:i});continue}}r()}r()}if(y.length>0){let e=[];for(let t of m)e.push({kind:`call`,item:t});for(let t of p)e.push({kind:`throw`,item:t});for(let t of f)e.push({kind:`scc`,item:t});for(let t of y)e.push({kind:`step`,item:t});e.sort((e,t)=>e.item.callSiteLine-t.item.callSiteLine);let t=0;for(let n of e)n.item.order=t++}let b=[],x=new Map;for(let e of m){let n=t.get(e.paramName);x.has(n)||x.set(n,[]);let r=e.condInfo.isConditional;x.get(n).push({assignedTo:e.assignedTo,branchGroupId:r&&e.condInfo.statementLine?`L${e.condInfo.statementLine}`:null,branchKind:r?e.condInfo.branchKind:null,callSiteLine:e.callSiteLine,comment:e.comment,conditional:r,conditionText:r?e.condInfo.conditionText:null,guardThrow:e.guardThrow,iterationKind:e.iterInfo.iterationKind,iterationLabel:e.iterInfo.iterationLabel,name:e.methodName,order:e.order})}for(let[e,t]of x)t.sort((e,t)=>e.order-t.order),b.push({className:e,methodsCalled:t});let ee={deps:b,sameClassCalls:f,steps:y,throws:p};return!r&&i&&i.setScan(a,ee),ee}function qr(e){return e.endsWith(`Repository`)?`repository`:e.endsWith(`Guard`)?`guard`:e.endsWith(`Interceptor`)?`interceptor`:e.endsWith(`Pipe`)?`pipe`:e.endsWith(`Filter`)?`filter`:e.endsWith(`Gateway`)?`gateway`:`service`}function H(e,t,n,r,i){let a=[],o=new Set,s=new Map,c=e.deps,l=[],u=[];for(let e of c)if(e.methodsCalled.length===0)u.push(e);else for(let t of e.methodsCalled)l.push({className:e.className,mc:t,dep:e});l.sort((e,t)=>e.mc.order-t.mc.order);for(let e of u){if(r.has(e.className)||o.has(e.className))continue;o.add(e.className),r.add(e.className);let t=n.get(e.className),s=t?t.dependencies.map(e=>({className:e,methodsCalled:[]})):[];a.push({assignedTo:null,branchGroupId:null,branchKind:null,callSiteLine:0,className:e.className,comment:null,conditional:!1,conditionText:null,dependencies:H({deps:s,sameClassCalls:[],steps:[],throws:[]},e.className,n,new Set(r),i),endLine:0,filePath:t?.filePath??``,guardThrow:null,iterationKind:null,iterationLabel:null,line:0,methodName:null,order:0,parameters:[],returnType:null,stepStatements:[],throwMessage:null,totalMethods:t?.publicMethodCount??0,type:qr(e.className)})}let d=new Map;for(let{className:e,mc:t}of l)d.has(e)||d.set(e,new Set),d.get(e).add(t.name);for(let{className:e,mc:t}of l){if(r.has(e))continue;let c=n.get(e),l=!o.has(e);l&&o.add(e);let u=[];if(l&&c){let t=new Set(r);t.add(e);let a=Wr(c.classDeclaration,n,i),o=[],l=0,f=[],p=[],m=d.get(e)??new Set;for(let e of m){let t=z(c.classDeclaration,e,n,i);if(!t)continue;let r=Kr(t,a,c.classDeclaration,void 0,i),s=[];for(let e of r.deps)for(let t of e.methodsCalled)s.push({kind:`dep`,depClassName:e.className,m:t});for(let e of r.throws)s.push({kind:`throw`,t:e});for(let e of r.sameClassCalls)s.push({kind:`scc`,scc:e});function u(e){return e.kind===`dep`?e.m.order:e.kind===`throw`?e.t.order:e.scc.order}s.sort((e,t)=>u(e)-u(t));for(let e of s)e.kind===`dep`?o.push({assignedTo:e.m.assignedTo,depClassName:e.depClassName,methodName:e.m.name,order:l++,callSiteLine:e.m.callSiteLine,comment:e.m.comment,conditional:e.m.conditional,branchKind:e.m.conditional?e.m.branchKind:null,conditionText:e.m.conditional?e.m.conditionText:null,branchGroupId:e.m.conditional?e.m.branchGroupId:null,guardThrow:e.m.guardThrow,iterationKind:e.m.iterationKind,iterationLabel:e.m.iterationLabel}):e.kind===`throw`?p.push({...e.t,order:l++}):f.push(e.scc)}Gr(o,p);let h=new Map;for(let e of o)h.has(e.depClassName)||h.set(e.depClassName,[]),h.get(e.depClassName).push({assignedTo:e.assignedTo,branchGroupId:e.branchGroupId,branchKind:e.branchKind,callSiteLine:e.callSiteLine,comment:e.comment,conditional:e.conditional,conditionText:e.conditionText,guardThrow:e.guardThrow,iterationKind:e.iterationKind,iterationLabel:e.iterationLabel,name:e.methodName,order:e.order});let g=[];for(let[e,t]of h)t.sort((e,t)=>e.order-t.order),g.push({className:e,methodsCalled:t});u=H({deps:g,sameClassCalls:f,steps:[],throws:p},e,n,t,i),s.set(e,u)}else l||(u=s.get(e)??[]);let f=0,p=0,m=null,h=[];if(c){let e=z(c.classDeclaration,t.name,n,i);e&&(f=e.getStartLineNumber(),p=e.getEndLineNumber(),m=V(e),h=Lr(e))}a.push({assignedTo:t.assignedTo,branchGroupId:t.branchGroupId,branchKind:t.branchKind,callSiteLine:t.callSiteLine,className:e,comment:t.comment,conditional:t.conditional,conditionText:t.conditionText,dependencies:u,endLine:p,filePath:c?.filePath??``,guardThrow:t.guardThrow,iterationKind:t.iterationKind,iterationLabel:t.iterationLabel,line:f,methodName:t.name,order:t.order,parameters:h,returnType:m,stepStatements:[],throwMessage:null,totalMethods:c?.publicMethodCount??0,type:qr(e)})}let f=n.get(t);for(let o of e.sameClassCalls){let e=0,s=0,c=null,l=[];if(f){let t=z(f.classDeclaration,o.methodName,n,i);t&&(e=t.getStartLineNumber(),s=t.getEndLineNumber(),c=V(t),l=Lr(t))}let u=H(o.childResult,t,n,new Set(r),i);a.push({assignedTo:o.assignedTo,branchGroupId:o.branchGroupId,branchKind:o.branchKind,callSiteLine:o.callSiteLine,className:t,comment:o.comment,conditional:o.conditional,conditionText:o.conditionText,dependencies:u,endLine:s,filePath:f?.filePath??``,guardThrow:null,iterationKind:o.iterationKind,iterationLabel:o.iterationLabel,line:e,methodName:o.methodName,order:o.order,parameters:l,returnType:c,stepStatements:[],throwMessage:null,totalMethods:f?.publicMethodCount??0,type:qr(t)})}for(let t of e.throws)a.push({assignedTo:null,branchGroupId:t.branchGroupId,branchKind:t.branchKind,callSiteLine:t.callSiteLine,className:t.exceptionClassName,comment:t.comment,conditional:t.conditional,conditionText:t.conditionText,dependencies:[],endLine:t.callSiteLine,filePath:f?.filePath??``,guardThrow:null,iterationKind:t.iterationKind,iterationLabel:t.iterationLabel,line:t.callSiteLine,methodName:null,order:t.order,parameters:[],returnType:null,stepStatements:[],throwMessage:t.message,totalMethods:0,type:`throw`});for(let t of e.steps)a.push({assignedTo:null,branchGroupId:t.branchGroupId,branchKind:t.branchKind,callSiteLine:t.callSiteLine,className:`local`,comment:t.comment,conditional:t.conditional,conditionText:t.conditionText,dependencies:[],endLine:t.callSiteLine,filePath:f?.filePath??``,guardThrow:null,iterationKind:t.iterationKind,iterationLabel:t.iterationLabel,line:t.callSiteLine,methodName:null,order:t.order,parameters:[],returnType:null,stepStatements:t.statements,throwMessage:null,totalMethods:0,type:`step`});return a.sort((e,t)=>e.order-t.order),a}function Jr(e){for(let t of e.getDecorators()){let n=t.getName();if(xr.has(n))return{httpMethod:n.toUpperCase(),path:e.getName()}}}function Yr(e,t,n,r){let i=[];for(let a of e.getClasses()){let e=E(a),o=T(a,`Resolver`);if(!(e||o))continue;let s=e?Ar(a):``,c=a.getName()??(e?`AnonymousController`:`AnonymousResolver`),l=Wr(a,n,r);for(let o of a.getMethods()){let u=e?jr(o):Jr(o);if(!u)continue;let d=e?Mr(s,u.path):u.path,f=H(Kr(o,l,a,void 0,r),c,n,new Set,r),p=Fr(o),m=V(o);i.push({controllerClass:c,dependencies:f,endLine:o.getEndLineNumber(),filePath:t,handlerMethod:o.getName(),httpMethod:u.httpMethod,line:o.getStartLineNumber(),returnType:m,routePath:d,swagger:p})}}return i}function Xr(e,t,n){let r=[],i=new wr;for(let a of t){let t=e.getSourceFile(a);t&&r.push(...Yr(t,a,n,i))}return{endpoints:r}}const Zr=new Set([`pgTable`,`mysqlTable`,`sqliteTable`]),Qr=new Set([`serial`,`bigserial`,`smallserial`]),$r=/=>\s*(\w+)/;function ei(e){let t={type:`unknown`,isPrimary:!1,isNullable:!0,isGenerated:!1,isUnique:!1};function n(e){if(e.getKind()===d.CallExpression){let r=e.asKindOrThrow(d.CallExpression),i=r.getExpression();if(i.getKind()===d.PropertyAccessExpression){let e=i.asKindOrThrow(d.PropertyAccessExpression);switch(e.getName()){case`primaryKey`:t.isPrimary=!0;break;case`notNull`:t.isNullable=!1;break;case`unique`:t.isUnique=!0;break;case`default`:{let e=r.getArguments();e.length>0&&(t.defaultValue=e[0].getText().replace(/['"]/g,``));break}case`defaultNow`:t.defaultValue=`now()`;break;case`generatedAlwaysAsIdentity`:case`autoincrement`:t.isGenerated=!0;break;case`references`:{let e=r.getArguments();if(e.length>0){let n=e[0].getText(),r=$r.exec(n);if(r&&(t.reference={toEntity:r[1]}),e.length>1){let n=e[1];if(n.getKind()===d.ObjectLiteralExpression){let e=n.asKindOrThrow(d.ObjectLiteralExpression);for(let n of e.getProperties())if(n.getKind()===d.PropertyAssignment){let e=n.asKindOrThrow(d.PropertyAssignment);if(e.getName()===`onDelete`){let n=e.getInitializer()?.getText();n&&(t.reference.onDelete=n.replace(/['"]/g,``))}}}}}break}default:break}n(e.getExpression())}else if(i.getKind()===d.Identifier){let e=i.getText();t.type=e,Qr.has(e)&&(t.isGenerated=!0)}}}return n(e),t}function ti(e){let t=[];for(let n of e.getProperties()){if(n.getKind()!==d.PropertyAssignment)continue;let e=n.asKindOrThrow(d.PropertyAssignment),r=e.getName(),i=e.getInitializer();if(!i)continue;let a=ei(i);t.push({name:r,type:a.type,isPrimary:a.isPrimary,isNullable:a.isNullable,isGenerated:a.isGenerated,isUnique:a.isUnique,defaultValue:a.defaultValue})}return t}function ni(e,t){let n=[];for(let r of e.getProperties()){if(r.getKind()!==d.PropertyAssignment)continue;let e=r.asKindOrThrow(d.PropertyAssignment),i=e.getName(),a=e.getInitializer();if(!a)continue;let o=ei(a);o.reference&&n.push({type:`many-to-one`,fromEntity:t,toEntity:o.reference.toEntity,propertyName:i,isNullable:o.isNullable,...o.reference.onDelete?{onDelete:o.reference.onDelete}:{}})}return n}function ri(e){let t=[],n=e.getDescendantsOfKind(d.CallExpression);for(let e of n){let n=e.getExpression();if(n.getKind()!==d.PropertyAccessExpression||n.asKindOrThrow(d.PropertyAccessExpression).getName()!==`on`)continue;let r=[];for(let t of e.getArguments())if(t.getKind()===d.PropertyAccessExpression){let e=t.asKindOrThrow(d.PropertyAccessExpression);r.push(e.getName())}if(r.length===0)continue;let i=e.getText().includes(`uniqueIndex`);t.push({columns:r,isUnique:i})}return t}function ii(e){let t=[],n=e.getFilePath();for(let r of e.getDescendantsOfKind(d.VariableDeclaration)){let e=r.getInitializer();if(!e||e.getKind()!==d.CallExpression)continue;let i=e.asKindOrThrow(d.CallExpression),a=i.getExpression();if(a.getKind()!==d.Identifier)continue;let o=a.getText();if(!Zr.has(o))continue;let s=i.getArguments();if(s.length<2)continue;let c=s[0],l=r.getName();c.getKind()===d.StringLiteral&&(l=c.asKindOrThrow(d.StringLiteral).getLiteralValue());let u=s[1];if(u.getKind()!==d.ObjectLiteralExpression)continue;let f=u.asKindOrThrow(d.ObjectLiteralExpression),p=r.getName(),m=ti(f),h=ni(f,p),g;if(s.length>=3&&(g=ri(s[2]),g))for(let e of g)for(let t of e.columns){let e=m.find(e=>e.name===t);e&&(e.hasIndex=!0)}t.push({name:p,tableName:l,filePath:n,columns:m,relations:h,indexes:g})}return t}const ai={supportsIncrementalUpdate:!0,extract(e,t){let n=[];for(let r of t){let t=e.getSourceFile(r);t&&n.push(...ii(t))}return n}},oi=/^model\s+(\w+)\s*\{/,si=/^enum\s+(\w+)\s*\{/,ci=/^(\w+)\s+(\w+)(\?)?(\[\])?(.*)$/,li=/@(\w+)(\((?:[^()]*|\([^()]*\))*\))?/g,ui=/@default\(((?:[^()]*|\([^()]*\))*)\)/,di=/^@@map\(\s*"([^"]+)"\s*\)/;function fi(e){let t=a(e,`prisma`,`schema.prisma`);if(p(t)){let n=a(e,`prisma`),r=h(n).filter(e=>e.endsWith(`.prisma`));return r.length>1?r.map(e=>a(n,e)):[t]}let n=a(e,`schema.prisma`);if(p(n))return[n];try{let t=a(e,`package.json`),n=JSON.parse(m(t,`utf-8`)).prisma?.schema;if(n){let t=a(e,n);if(p(t))return[t]}}catch{}return[]}function pi(e){let t=[],n=new Set;for(let r of e){let e;try{e=m(r,`utf-8`)}catch{continue}let i=e.split(`
|
|
6
|
-
`),a=null,o=[],s=[],c=[],l;for(let e of i){let i=e.trim(),u=
|
|
7
|
-
`);for(let e of n){let n={config:r,sourceFile:o,filePath:t,report(t){let n=[],r=Math.max(0,t.line-6),a=Math.min(s.length,t.line+5);for(let e=r;e<a;e++)n.push({line:e+1,text:s[e]});i.push({...t,rule:e.meta.id,category:e.meta.category,scope:`file`,severity:e.meta.severity,sourceLines:n})}};try{e.check(n)}catch(t){a.push({ruleId:e.meta.id,error:t})}}return{diagnostics:i,errors:a}}function Vi(e,t,n,r){let i=[],a=[];for(let o of t){let t=Bi(e,o,n,r);i.push(...t.diagnostics),a.push(...t.errors)}return{diagnostics:i,errors:a}}function Hi(e,t,n,r){let i=[],a=[];for(let o of n){let n={project:e,files:t,moduleGraph:r.moduleGraph,providers:r.providers,config:r.config,report(e){i.push({...e,rule:o.meta.id,category:o.meta.category,scope:`project`,severity:o.meta.severity})}};try{o.check(n)}catch(e){a.push({ruleId:o.meta.id,error:e})}}return{diagnostics:i,errors:a}}function Ui(e,t){let n=[],r=[];for(let i of t){let t={schemaGraph:e,orm:e.orm,report(e){n.push({...e,rule:i.meta.id,category:i.meta.category,scope:`schema`,severity:i.meta.severity})}};try{i.check(t)}catch(e){r.push({ruleId:i.meta.id,error:e})}}return{diagnostics:n,errors:r}}function Wi(e){return e instanceof Error?e.message:String(e)}function Gi(e,t,n){return{diagnostics:zi(e,n.config,n.targetPath),errors:t.map(e=>({ruleId:e.ruleId,error:Wi(e.error)}))}}function Ki(e){let t=Vi(e.astProject,e.files,e.fileRules,e.config);return Gi(t.diagnostics,t.errors,e)}function qi(e){let t={moduleGraph:e.moduleGraph,providers:e.providers,config:e.config},n=Hi(e.astProject,e.files,e.projectRules,t),{diagnostics:r,errors:i}=Gi(n.diagnostics,n.errors,e),a=Ji(e);return r.push(...a.diagnostics),i.push(...a.errors),{diagnostics:r,errors:i}}function Ji(e){if(!e.schemaGraph||e.schemaRules.length===0||e.schemaGraph.entities.size===0)return{diagnostics:[],errors:[]};let t=Ui(e.schemaGraph,e.schemaRules);return Gi(t.diagnostics,t.errors,e)}function W(e){let t=l.now(),n=Ki(e),r=qi(e),i=l.now()-t;return{diagnostics:[...n.diagnostics,...r.diagnostics],elapsedMs:i,ruleErrors:[...n.errors,...r.errors]}}function Yi(e){return e>=90?`Excellent`:e>=75?`Good`:e>=50?`Fair`:e>=25?`Poor`:`Critical`}const Xi={error:3,warning:1.5,info:.5},Zi={security:1.5,correctness:1.3,schema:1.1,architecture:1,performance:.8};function Qi(e,t){if(t===0)return{value:100,label:Yi(100)};let n=0;for(let t of e){let e=Xi[t.severity],r=Zi[t.category];n+=e*r}let r=n/t,i=Math.max(0,Math.min(100,Math.round(100-r*10)));return{value:i,label:Yi(i)}}function $i(e){let t={total:0,errors:0,warnings:0,info:0,byCategory:{security:0,performance:0,correctness:0,architecture:0,schema:0}};for(let n of e)t.total++,n.severity===`error`?t.errors++:n.severity===`warning`?t.warnings++:t.info++,t.byCategory[n.category]++;return t}function ea(e,t,n=[]){let{diagnostics:r,ruleErrors:i,elapsedMs:a}=t,o=e.schemaGraph??Mi(e.astProject,e.files,e.project.orm,e.targetPath),s=Qi(r,e.files.length),c=$i(r);return{result:{score:s,diagnostics:r,endpoints:e.endpointGraph,project:{...e.project,fileCount:e.files.length,moduleCount:e.moduleGraph.modules.size},summary:c,ruleErrors:i,elapsedMs:a,schema:Ni(o)},moduleGraph:e.moduleGraph,schemaGraph:o,customRuleWarnings:n,files:e.files,providers:e.providers}}function ta(e,t,n,r){let i=[],a=[],o=[],s=new Map,c=[],l=0,u=[],d=[],f=``;for(let[n,r]of e.subProjects){let e=ea(r,t.get(n));i.push({name:n,result:e.result}),s.set(n,e.moduleGraph),a.push(...e.result.diagnostics),o.push(...e.result.ruleErrors),l+=e.result.project.fileCount,e.result.endpoints&&c.push(...e.result.endpoints.endpoints),e.result.schema&&(u.push(...e.result.schema.entities),d.push(...e.result.schema.relations),e.result.schema.orm&&e.result.schema.orm!==`unknown`&&(f=e.result.schema.orm))}let p=Qi(a,l),m=$i(a);return{moduleGraphs:s,customRuleWarnings:n,result:{isMonorepo:!0,subProjects:i,combined:{score:p,diagnostics:a,endpoints:c.length>0?{endpoints:c}:void 0,project:{name:`monorepo`,nestVersion:i[0]?.result.project.nestVersion??null,orm:f||(i[0]?.result.project.orm??null),framework:i[0]?.result.project.framework??null,fileCount:l,moduleCount:i.reduce((e,t)=>e+t.result.project.moduleCount,0)},summary:m,ruleErrors:o,elapsedMs:r,schema:u.length>0?{entities:u,relations:d,orm:f||`unknown`}:void 0},elapsedMs:r}}}const na=e=>{if(e.trim()===``)return`Invalid --min-score value: "${e}". Must be an integer between 0 and 100.`;let t=Number(e);return!Number.isInteger(t)||t<0||t>100?`Invalid --min-score value: "${e}". Must be an integer between 0 and 100.`:null},ra=(e,t)=>e===void 0?t:Number(e),ia=(e,t)=>t===void 0?!0:e>=t,G={error:y.red,warn:y.yellow,info:y.cyan,success:y.green,dim:y.dim},K=e=>{console.log(e)},q={error(...e){K(G.error(e.join(` `)))},warn(...e){K(G.warn(e.join(` `)))},info(...e){K(G.info(e.join(` `)))},success(...e){K(G.success(e.join(` `)))},dim(...e){K(G.dim(e.join(` `)))},log(...e){K(e.join(` `))},break(){K(``)}},aa=1e3,oa={error:0,warning:1,info:2},J=(e,t=e)=>({plainText:e,renderedText:t}),Y=(e,t)=>t>=75?G.success(e):t>=50?G.warn(e):G.error(e),sa=(e,t)=>t===`error`?G.error(e):t===`warning`?G.warn(e):G.info(e),ca=e=>e===`error`?`✗`:e===`warning`?`⚠`:`●`,la=e=>e<aa?`${Math.round(e)}ms`:`${(e/aa).toFixed(1)}s`,ua=e=>e>=75?[`◠ ◠ ◠`,`╰───╯`]:e>=50?[`• • •`,`╰───╯`]:[`x x x`,`╰───╯`],da=e=>e>=90?`★★★★★`:e>=75?`★★★★☆`:e>=50?`★★★☆☆`:e>=25?`★★☆☆☆`:`★☆☆☆☆`,fa=e=>{let t=Math.round(e/100*50),n=50-t;return{filled:`█`.repeat(t),empty:`░`.repeat(n)}},pa=e=>{let{filled:t,empty:n}=fa(e);return`${t}${n}`},ma=e=>{let{filled:t,empty:n}=fa(e);return Y(t,e)+G.dim(n)},ha=e=>{if(e.length===0)return;let t=G.dim,n=` `.repeat(2),r=` `.repeat(1),i=Math.max(...e.map(e=>e.plainText.length)),a=`─`.repeat(i+2);q.log(`${n}${t(`┌${a}┐`)}`);for(let a of e){let e=` `.repeat(i-a.plainText.length);q.log(`${n}${t(`│`)}${r}${a.renderedText}${e}${r}${t(`│`)}`)}q.log(`${n}${t(`└${a}┘`)}`)},ga=e=>{let t=new Map;for(let n of e){let e=n.rule,r=t.get(e)??[];r.push(n),t.set(e,r)}return t},_a=e=>[...e].sort(([,e],[,t])=>oa[e[0].severity]-oa[t[0].severity]),va=e=>{let t=new Map;for(let n of e){let e=t.get(n.filePath)??[];`line`in n&&n.line>0&&e.push(n.line),t.set(n.filePath,e)}return t},ya=e=>new Set(e.map(e=>e.filePath)),ba=(e,t)=>{let n=_a([...ga(e).entries()]);for(let[,e]of n){let n=e[0],r=sa(ca(n.severity),n.severity),i=e.length,a=i>1?sa(` (${i})`,n.severity):``;if(q.log(` ${r} ${n.message}${a}`),n.help&&q.dim(` ${n.help}`),t){let t=va(e);for(let[e,n]of t){let t=n.length>0?`: ${n.join(`, `)}`:``;q.dim(` ${e}${t}`)}}q.break()}};function xa(e,t){let{score:n,diagnostics:r,project:i,summary:a,elapsedMs:o}=e;q.break();let s=[],c=e=>Y(e,n.value),[l,u]=ua(n.value);s.push(J(`┌───────┐`,c(`┌───────┐`))),s.push(J(`│ ${l} │ NestJS Doctor`,`${c(`│ ${l} │`)} NestJS Doctor`)),s.push(J(`│ ${u} │`,c(`│ ${u} │`))),s.push(J(`└───────┘`,c(`└───────┘`))),s.push(J(``));let d=da(n.value),f=`${n.value} / 100 ${d} ${n.label}`,p=`${Y(String(n.value),n.value)} / 100 ${Y(d,n.value)} ${Y(n.label,n.value)}`;s.push(J(f,p)),s.push(J(``)),s.push(J(pa(n.value),ma(n.value))),s.push(J(``));let m=la(o),h=ya(r).size,g=[],_=[];if(a.errors>0){let e=`✗ ${a.errors} error${a.errors===1?``:`s`}`;_.push(e),g.push(G.error(e))}if(a.warnings>0){let e=`⚠ ${a.warnings} warning${a.warnings===1?``:`s`}`;_.push(e),g.push(G.warn(e))}if(a.info>0){let e=`● ${a.info} info`;_.push(e),g.push(G.info(e))}if(r.length===0){let e=`No issues found!`;_.push(e),g.push(G.success(e))}let v=r.length>0?`across ${h}/${i.fileCount} files`:`${i.fileCount} files scanned`,y=`in ${m}`;_.push(v),_.push(y),g.push(G.dim(v)),g.push(G.dim(y)),s.push(J(_.join(` `),g.join(` `))),ha(s),q.break();let b=[`Project: ${i.name}`];if(i.nestVersion&&b.push(`NestJS ${i.nestVersion}`),i.orm&&b.push(i.orm),b.push(`${i.moduleCount} modules`),q.dim(` ${b.join(` | `)}`),q.break(),r.length!==0){if(ba(r,t),t&&e.ruleErrors.length>0){q.warn(` ${e.ruleErrors.length} rule(s) failed during execution:`);for(let t of e.ruleErrors)q.dim(` ${t.ruleId}: ${t.error}`);q.break()}t||(q.dim(` Run with --verbose for file paths and line numbers`),q.break())}}function Sa(e,t){xa(e.combined,t),q.log(` Sub-project breakdown:`),q.break();for(let t of e.subProjects){let{name:e,result:n}=t,r=Y(String(n.score.value),n.score.value),i=[`${G.info(e)}: ${r}/100`,`${n.project.fileCount} files`];n.summary.errors>0&&i.push(G.error(`${n.summary.errors} errors`)),n.summary.warnings>0&&i.push(G.warn(`${n.summary.warnings} warnings`)),n.summary.info>0&&i.push(`${n.summary.info} info`),n.diagnostics.length===0&&i.push(G.success(`clean`)),q.log(` ${i.join(` | `)}`)}q.break()}function Ca(e){console.log(JSON.stringify(e,null,2))}const X=(e,t,n)=>{let r=e.score.value;ia(r,t)||(n||q.error(`Score ${r} is below the minimum threshold of ${t}.`),process.exit(1))},wa=e=>{e.summary.errors>0&&process.exit(1)},Ta=(e,t,n,r)=>{let{result:i}=e;if(r.score){console.log(i.combined.score.value),X(i.combined,t,n);return}if(r.json){Ca(i.combined),X(i.combined,t,n);return}Sa(i,r.verbose),X(i.combined,t,n),wa(i.combined)},Ea=(e,t,n,r)=>{let{result:i}=e;if(r.score){console.log(i.score.value),X(i,t,n);return}if(r.json){Ca(i),X(i,t,n);return}xa(i,r.verbose),X(i,t,n),wa(i)};let Z=null,Q=0;const $=new Set,Da=(e,t,n)=>{if($.delete(t),Q--,Q<=0||!Z){Z?.[e](n),Z=null,Q=0;return}Z.stop(),b(n).start()[e](n);let[r]=$;r&&(Z.text=r),Z.start()},Oa=e=>({start(){return Q++,$.add(e),Z?Z.text=e:Z=b({text:e}).start(),{succeed:t=>Da(`succeed`,e,t),fail:t=>Da(`fail`,e,t)}}}),ka=(e,t)=>{if(!t)for(let t of e)q.warn(t)};var Aa=class{options;resolvedMinimumScore;scanConfig;steps=[];targetPath;constructor(e,t){this.targetPath=e,this.options=t}resolveConfig(){return this.steps.push(async()=>{this.scanConfig=await dr(this.targetPath,this.options.configPath),this.resolvedMinimumScore=ra(this.options.minScore,this.scanConfig.config.minScore)}),this}warnCustomRules(){return this.steps.push(()=>{ka(this.scanConfig.customRuleWarnings,this.options.isMachineReadable)}),this}async run(){let e=this.options.isMachineReadable?null:Oa(`Scanning...`).start();for(let e of this.steps)await e();e?.succeed(`Scan complete`)}},ja=class extends Aa{monorepo;monorepoCtx;rawOutputs=new Map;result;scanStartTime;constructor(e,t,n){super(e,n),this.monorepo=t}buildContext(){return this.steps.push(async()=>{this.scanStartTime=l.now(),this.monorepoCtx=await Fi(this.targetPath,this.scanConfig,this.monorepo)}),this}runRules(){return this.steps.push(()=>{for(let[e,t]of this.monorepoCtx.subProjects)this.rawOutputs.set(e,W(t))}),this}buildResult(){return this.steps.push(()=>{let e=l.now()-this.scanStartTime;this.result=ta(this.monorepoCtx,this.rawOutputs,this.scanConfig.customRuleWarnings,e)}),this}output(){return this.steps.push(()=>{Ta(this.result,this.resolvedMinimumScore,this.options.isMachineReadable,{json:this.options.json,score:this.options.score,verbose:this.options.verbose})}),this}},Ma=class extends Aa{context;rawOutput;result;buildContext(){return this.steps.push(async()=>{this.context=await Pi(this.targetPath,this.scanConfig)}),this}runRules(){return this.steps.push(()=>{this.rawOutput=W(this.context)}),this}buildResult(){return this.steps.push(()=>{this.result=ea(this.context,this.rawOutput,this.scanConfig.customRuleWarnings)}),this}output(){return this.steps.push(()=>{Ea(this.result,this.resolvedMinimumScore,this.options.isMachineReadable,{json:this.options.json,score:this.options.score,verbose:this.options.verbose})}),this}},Na=class{args;steps=[];version;targetPath=``;constructor(e,t){this.args=e,this.version=t}resolveTargetPath(){return this.steps.push(()=>(this.targetPath=s(this.args.path??`.`),!0)),this}handleInit(){return this.steps.push(async()=>{if(this.args.init){let{initSkill:e}=await import(`../init-DX6EkkPo.mjs`);return await e(this.targetPath,this.version),!1}return!0}),this}handleReport(){return this.steps.push(async()=>{if(this.args.report){let{runReport:e}=await import(`../setup-CN4uKQl2.mjs`);return await e(this.targetPath,this.args.config),!1}return!0}),this}validateMinScore(){return this.steps.push(()=>{if(this.args[`min-score`]!==void 0){let e=na(this.args[`min-score`]);e&&(q.error(e),process.exit(2))}return!0}),this}async run(){for(let e of this.steps)if(!await e())return null;return{targetPath:this.targetPath,options:{configPath:this.args.config,isMachineReadable:this.args.score||this.args.json,json:this.args.json??!1,minScore:this.args[`min-score`],score:this.args.score??!1,verbose:this.args.verbose??!1}}}};const{version:Pa}=e(import.meta.url)(`../../package.json`);n(t({meta:{name:`nestjs-doctor`,version:Pa,description:`Static analysis tool for NestJS — health score, diagnostics, and interactive HTML report`},args:{path:{type:`positional`,description:`Path to the NestJS project (defaults to current directory)`,default:`.`,required:!1},...ye},async run({args:e}){let t=await new Na(e,Pa).resolveTargetPath().handleInit().handleReport().validateMinScore().run();if(!t)return;let{targetPath:n,options:r}=t,i=await pe(n);if(i){await new ja(n,i,r).resolveConfig().buildContext().runRules().buildResult().warnCustomRules().output().run();return}await me(n)&&console.warn(`Warning: This directory appears to be a monorepo, but no NestJS packages were found.
|
|
8
|
-
Consider running on a specific sub-project instead.`),await new
|
|
3
|
+
`),r=!1;for(let e of n){let n=e.trim();if(x.test(n)){let e=n.match(ee);if(e){for(let n of e[1].split(`,`)){let e=n.trim().replace(re,``);e&&t.push(e)}return t}r=!0;continue}if(r){if(te.test(e)&&n!==``)break;let r=n.match(ne);r&&t.push(r[1])}}return t}async function ae(e){let t=a(e,`nest-cli.json`);try{let e=await r(t,`utf-8`),n=JSON.parse(e);if(!(n.monorepo&&n.projects))return null;let i=new Map;for(let[e,t]of Object.entries(n.projects)){let n=t.root??e;i.set(e,n)}return i.size===0?null:{projects:i}}catch{return null}}function oe(e){let t={...e.dependencies,...e.devDependencies,...e.peerDependencies};return!!(t[`@nestjs/core`]||t[`@nestjs/common`])}async function se(e,t){let n=await c(t.map(e=>`${e}/package.json`),{cwd:e,absolute:!0,ignore:[`**/node_modules/**`]}),a=new Map;for(let t of n)try{let n=await r(t,`utf-8`),s=JSON.parse(n);if(oe(s)){let n=o(e,i(t)),r=s.name??n;a.set(r,n)}}catch{}return a.size===0?null:{projects:a}}async function ce(e){let t=a(e,`pnpm-workspace.yaml`),n;try{n=await r(t,`utf-8`)}catch{return null}let i=ie(n);return i.length===0?null:se(e,i)}function le(e){let t=e.workspaces;if(!t)return[];if(Array.isArray(t))return t.filter(e=>typeof e==`string`);if(typeof t==`object`&&t){let e=t;if(Array.isArray(e.packages))return e.packages.filter(e=>typeof e==`string`)}return[]}async function ue(e){let t=a(e,`package.json`),n;try{n=await r(t,`utf-8`)}catch{return null}let i=le(JSON.parse(n));return i.length===0?null:se(e,i)}async function de(e){let t=a(e,`lerna.json`),n;try{n=await r(t,`utf-8`)}catch{return null}let i=JSON.parse(n);if(i.useWorkspaces)return null;let o=i.packages??[`packages/*`];return o.length===0?null:se(e,o)}async function fe(e){let t=a(e,`nx.json`);try{await r(t,`utf-8`)}catch{return null}let n=await c([`**/project.json`],{cwd:e,absolute:!0,ignore:[`node_modules/**`]}),s=new Map;for(let t of n){let n=i(t),c=o(e,n);if(c===``)continue;let l=a(n,`package.json`);try{let e=await r(l,`utf-8`),t=JSON.parse(e);if(oe(t)){let e=t.name??c;s.set(e,c)}}catch{}}return s.size===0?null:{projects:s}}async function pe(e){try{return await r(a(e,`pnpm-workspace.yaml`),`utf-8`),!0}catch{return!1}}async function me(e){let t=await ae(e);if(t)return t;let n=await ce(e);if(n)return n;if(!await pe(e)){let t=await ue(e);if(t)return t}return await fe(e)||de(e)}async function he(e){for(let t of[`lerna.json`,`turbo.json`,`nx.json`,`pnpm-workspace.yaml`])try{return await r(a(e,t),`utf-8`),!0}catch{}try{let t=await r(a(e,`package.json`),`utf-8`);if(JSON.parse(t).workspaces)return!0}catch{}return!1}async function ge(e){let t=a(e,`package.json`),n={};try{let e=await r(t,`utf-8`);n=JSON.parse(e)}catch{}let i={...n.dependencies,...n.devDependencies},o=_e(i[`@nestjs/core`]),s=ve(i),c=ye(i);return{name:n.name??`unknown`,nestVersion:o,orm:s,framework:c,moduleCount:0,fileCount:0}}function _e(e){return e?e.replace(/[\^~>=<]/g,``):null}function ve(e){return e[`@prisma/client`]?`prisma`:e.typeorm?`typeorm`:e[`@mikro-orm/core`]?`mikro-orm`:e.sequelize?`sequelize`:e.mongoose?`mongoose`:e[`drizzle-orm`]?`drizzle`:null}function ye(e){return e[`@nestjs/platform-fastify`]?`fastify`:e[`@nestjs/platform-express`]||e[`@nestjs/core`]?`express`:null}const be={verbose:{type:`boolean`,description:`Show file paths and line numbers per diagnostic`,default:!1},score:{type:`boolean`,description:`Output only the numeric score (for CI)`,default:!1},json:{type:`boolean`,description:`JSON output`,default:!1},"min-score":{type:`string`,description:`Minimum passing score (0-100). Exits with code 1 if below threshold`},config:{type:`string`,description:`Config file path`},report:{type:`boolean`,alias:`graph`,description:`Generate an interactive HTML report (summary, diagnostics, module graph, rule lab)`,default:!1},init:{type:`boolean`,description:`Set up the nestjs-doctor skill for AI coding agents (Claude Code, Cursor, Codex, etc.)`,default:!1}},S={include:[`**/*.ts`],exclude:`**/node_modules/**,**/dist/**,**/build/**,**/coverage/**,**/*.spec.ts,**/*.test.ts,**/*.e2e-spec.ts,**/*.e2e-test.ts,**/*.d.ts,**/test/**,**/tests/**,**/__tests__/**,**/__mocks__/**,**/__fixtures__/**,**/mock/**,**/mocks/**,**/*.mock.ts,**/seeder/**,**/seeders/**,**/*.seed.ts,**/*.seeder.ts,*.config.ts,*.config.js,*.config.mjs,*.config.cjs,*.config.mts,*.config.cts`.split(`,`)},xe=[`nestjs-doctor.config.json`,`.nestjs-doctor.json`];async function Se(e,t){if(t)return Ce(t);for(let t of xe)try{return await Ce(a(e,t))}catch{}try{let t=await r(a(e,`package.json`),`utf-8`),n=JSON.parse(t);if(n[`nestjs-doctor`]&&typeof n[`nestjs-doctor`]==`object`)return we(n[`nestjs-doctor`])}catch{}return{...S}}async function Ce(e){let t=await r(e,`utf-8`);return we(JSON.parse(t))}function we(e){return{...S,...e,exclude:[...S.exclude??[],...e.exclude??[]]}}async function Te(e,t){try{return await Se(e)}catch{return t}}const Ee=[/Repository$/,/\.repository$/,/\.entity$/,/\.schema$/,/\.guard$/,/\.interceptor$/,/\.pipe$/,/\.filter$/,/\.strategy$/],De={meta:{id:`architecture/no-barrel-export-internals`,category:`architecture`,severity:`info`,description:`Don't re-export internal implementation details from barrel files`,help:`Only export the module's public API (services, DTOs, interfaces) from index.ts files.`},check(e){if(e.filePath.endsWith(`/index.ts`))for(let t of e.sourceFile.getExportDeclarations()){let n=t.getModuleSpecifierValue();if(n){Ee.some(e=>e.test(n))&&e.report({filePath:e.filePath,message:`Barrel file re-exports internal module '${n}'.`,help:this.meta.help,line:t.getStartLineNumber(),column:1});for(let n of t.getNamedExports()){let t=n.getName();(t.endsWith(`Repository`)||t.endsWith(`Entity`)||t.endsWith(`Schema`))&&e.report({filePath:e.filePath,message:`Barrel file re-exports internal type '${t}'.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}}}},C=new Set([`Get`,`Post`,`Put`,`Patch`,`Delete`,`Head`,`Options`,`All`]);function w(e,t){return e.getDecorator(t)!==void 0}function T(e){return w(e,`Controller`)}function Oe(e){return w(e,`Injectable`)}function ke(e){return w(e,`Injectable`)||w(e,`Controller`)||w(e,`Resolver`)||w(e,`WebSocketGateway`)}function Ae(e){return w(e,`Module`)}function E(e){return e.getDecorators().some(e=>C.has(e.getName()))}const je=new Set([`TsRestHandler`,`GrpcMethod`,`GrpcStreamMethod`]);function Me(e){return e.getDecorators().some(e=>je.has(e.getName()))}const Ne={meta:{id:`architecture/no-business-logic-in-controllers`,category:`architecture`,severity:`error`,description:`Controllers should only handle HTTP concerns — move business logic to services`,help:`Extract branches, loops, and complex calculations into a service method.`},check(e){for(let t of e.sourceFile.getClasses())if(T(t))for(let n of t.getMethods()){if(!n.getDecorators().some(e=>C.has(e.getName())))continue;let t=n.getBody();if(!t)continue;let r=t.getDescendantsOfKind(d.IfStatement),i=t.getDescendantsOfKind(d.ForStatement),a=t.getDescendantsOfKind(d.ForInStatement),o=t.getDescendantsOfKind(d.ForOfStatement),s=t.getDescendantsOfKind(d.WhileStatement),c=t.getDescendantsOfKind(d.SwitchStatement),l=i.length+a.length+o.length+s.length;(r.length>1||l>0||c.length>0)&&e.report({filePath:e.filePath,message:`Controller method '${n.getName()}' contains business logic (${r.length} if, ${l} loops, ${c.length} switch). Move to a service.`,help:this.meta.help,line:n.getStartLineNumber(),column:1});let u=t.getDescendantsOfKind(d.CallExpression).filter(e=>{let t=e.getExpression();if(t.getKind()===d.PropertyAccessExpression){let e=t.asKind(d.PropertyAccessExpression)?.getName();return e===`map`||e===`filter`||e===`reduce`||e===`sort`||e===`flatMap`}return!1});u.length>1&&e.report({filePath:e.filePath,message:`Controller method '${n.getName()}' contains data transformation logic (${u.length} array operations). Move to a service.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}};function Pe(e){let t=new Map;try{let n=f.findConfigFile(e,f.sys.fileExists,`tsconfig.json`);if(!n)return t;let{config:r,error:a}=f.readConfigFile(n,f.sys.readFile);if(a||!r)return t;let o=i(n),c=f.parseJsonConfigFileContent(r,f.sys,o),l=c.options.paths;if(!l)return t;let u=c.options.baseUrl??o;for(let[e,n]of Object.entries(l)){let r=n.map(e=>s(u,e));t.set(e,r)}}catch{return t}return t}function Fe(e,t){for(let[n,r]of t){if(r.length===0)continue;let t=n.indexOf(`*`);if(t===-1){if(e===n)return r[0];continue}let i=n.slice(0,t),a=n.slice(t+1);if(e.startsWith(i)&&e.endsWith(a)&&e.length>=i.length+a.length){let t=e.slice(i.length,e.length-a.length),n=r[0],o=n.indexOf(`*`);return o===-1?n:n.slice(0,o)+t+n.slice(o+1)}}}const Ie=/\.js$/;function Le(e,t,n){let r=[];for(let i of e.getClasses()){let e=i.getDecorator(`Module`);if(!e)continue;let a=i.getName()??`AnonymousModule`,o=e.getArguments()[0],s={name:a,filePath:t,classDeclaration:i,imports:[],forwardRefImports:new Set,exports:[],providers:[],controllers:[]};if(o&&o.getKind()===d.ObjectLiteralExpression){let e=o.asKind(d.ObjectLiteralExpression);if(e){let t=O(e,`imports`,n);s.imports=t.map(e=>e.name);for(let e of t)e.viaForwardRef&&s.forwardRefImports.add(e.name);s.exports=O(e,`exports`,n).map(e=>e.name),s.providers=O(e,`providers`,n).map(e=>e.name),s.controllers=O(e,`controllers`,n).map(e=>e.name)}}r.push(s)}return r}function Re(e,t,n=new Map){let r=new Map,i=new Map;for(let i of t){let t=e.getSourceFile(i);if(t)for(let e of Le(t,i,n))r.set(e.name,e)}for(let[e,t]of r){let n=new Set;for(let e of t.imports)r.has(e)&&n.add(e);i.set(e,n)}let a=new Map;for(let e of r.values())for(let t of e.providers)a.set(t,e);return{modules:r,edges:i,providerToModule:a}}const ze=new Set([`forRoot`,`forRootAsync`,`forFeature`,`forFeatureAsync`,`forChild`,`forChildAsync`,`register`,`registerAsync`]);function D(e){return{name:e,viaForwardRef:!1}}function O(e,t,n){let r=e.getProperty(t);if(!r)return[];let i=r.asKind(d.PropertyAssignment);if(!i)return[];let a=i.getInitializer();return a?k(a,e.getSourceFile(),0,n):[]}function k(e,t,n,r){if(n>5)return[];let i=e.getKind();if(i===d.ArrayLiteralExpression){let i=e.asKindOrThrow(d.ArrayLiteralExpression),a=[];for(let e of i.getElements())a.push(...Be(e,t,n,r));return a}return i===d.CallExpression?Ve(e.asKindOrThrow(d.CallExpression),t,n,r):i===d.Identifier?We(e.getText(),t,n+1,r):[]}function Be(e,t,n,r){let i=e.getKind();return i===d.SpreadElement?k(e.asKindOrThrow(d.SpreadElement).getExpression(),t,n,r):i===d.CallExpression?Ve(e.asKindOrThrow(d.CallExpression),t,n,r):i===d.PropertyAccessExpression?[D(e.asKindOrThrow(d.PropertyAccessExpression).getExpression().getText())]:(d.Identifier,[D(e.getText())])}function Ve(e,t,n,r){let i=e.getExpression();if(i.getKind()===d.Identifier&&i.getText()===`forwardRef`){let i=e.getArguments();if(i.length===0)return[];let a=i[0];if(a.getKind()===d.ArrowFunction){let e=a.asKindOrThrow(d.ArrowFunction).getBody();if(e.getKind()===d.Identifier)return[{name:e.getText(),viaForwardRef:!0}];if(e.getKind()===d.Block){let i=e.asKindOrThrow(d.Block),a=[];for(let e of i.getDescendantsOfKind(d.ReturnStatement)){let i=e.getExpression();if(i)for(let e of k(i,t,n,r))a.push({...e,viaForwardRef:!0})}return a}return k(e,t,n,r).map(e=>({...e,viaForwardRef:!0}))}return[D(a.getText())]}if(i.getKind()===d.PropertyAccessExpression){let a=i.asKindOrThrow(d.PropertyAccessExpression),o=a.getName();if(o===`concat`){let i=k(a.getExpression(),t,n,r),o=[];for(let i of e.getArguments())o.push(...k(i,t,n,r));return[...i,...o]}return ze.has(o),[D(a.getExpression().getText())]}return i.getKind()===d.Identifier?Ke(i.getText(),t,n+1,r):[]}function He(e,t,n){if(!e.startsWith(`.`)){let r=Fe(e,n);if(!r)return;let i=t.getProject(),a=[`${r}.ts`,`${r}/index.ts`,r,r.replace(Ie,`.ts`)];for(let e of a){let t=i.getSourceFile(e);if(t)return t}return}let r=s(i(t.getFilePath()),e),a=t.getProject(),o=[`${r}.ts`,`${r}/index.ts`,r,r.replace(Ie,`.ts`)];for(let e of o){let t=a.getSourceFile(e);if(t)return t}}function Ue(e,t,n){for(let r of t.getImportDeclarations())for(let i of r.getNamedImports())if((i.getAliasNode()?i.getAliasNode().getText():i.getName())===e){let e=He(r.getModuleSpecifierValue(),t,n);return e?{sourceFile:e,localName:i.getName()}:void 0}for(let r of t.getExportDeclarations())if(r.getModuleSpecifierValue()){for(let i of r.getNamedExports())if((i.getAliasNode()?i.getAliasNode().getText():i.getName())===e){let e=He(r.getModuleSpecifierValue(),t,n);return e?{sourceFile:e,localName:i.getName()}:void 0}}}function We(e,t,n,r){if(n>5)return[];for(let i of t.getStatements()){if(i.getKind()!==d.VariableStatement)continue;let a=i.asKindOrThrow(d.VariableStatement);for(let i of a.getDeclarations())if(i.getName()===e){let e=i.getInitializer();if(e)return k(e,t,n,r)}}let i=Ue(e,t,r);return i?We(i.localName,i.sourceFile,n+1,r):[]}function Ge(e,t,n,r){for(let i of t.getStatements()){if(i.getKind()!==d.VariableStatement)continue;let a=i.asKindOrThrow(d.VariableStatement);for(let i of a.getDeclarations()){if(i.getName()!==e)continue;let a=i.getInitializer();if(!a||a.getKind()!==d.ArrowFunction)continue;let o=a.asKindOrThrow(d.ArrowFunction).getBody();if(o.getKind()!==d.Block)return k(o,t,n,r);let s=[];for(let e of o.getDescendantsOfKind(d.ReturnStatement)){let i=e.getExpression();i&&s.push(...k(i,t,n,r))}return s}}}function Ke(e,t,n,r){if(n>5)return[];for(let i of t.getStatements()){if(i.getKind()!==d.FunctionDeclaration)continue;let a=i.asKindOrThrow(d.FunctionDeclaration);if(a.getName()!==e)continue;let o=[];for(let e of a.getDescendantsOfKind(d.ReturnStatement)){let i=e.getExpression();i&&o.push(...k(i,t,n,r))}return o}let i=Ge(e,t,n,r);if(i)return i;let a=Ue(e,t,r);return a?Ke(a.localName,a.sourceFile,n+1,r):[]}function qe(e){let t=new Map,n=new Map,r=new Map;for(let[i,a]of e){for(let[e,n]of a.modules){let r=`${i}/${e}`,o=new Set;for(let e of n.forwardRefImports)o.add(a.modules.has(e)?`${i}/${e}`:e);let s={...n,name:r,imports:n.imports.map(e=>a.modules.has(e)?`${i}/${e}`:e),forwardRefImports:o,exports:n.exports.map(e=>a.modules.has(e)?`${i}/${e}`:e)};t.set(r,s)}for(let[e,t]of a.edges){let r=`${i}/${e}`,a=new Set;for(let e of t)a.add(`${i}/${e}`);n.set(r,a)}for(let[e,n]of a.providerToModule){let a=`${i}/${n.name}`,o=t.get(a);o&&r.set(`${i}/${e}`,o)}}return{modules:t,edges:n,providerToModule:r}}function Je(e){let t=[],n=new Set,r=new Set;function i(a,o){n.add(a),r.add(a);let s=e.edges.get(a)??new Set;for(let e of s)if(!n.has(e))i(e,[...o,e]);else if(r.has(e)){let n=o.indexOf(e);n===-1?t.push([...o,e]):t.push(o.slice(n))}r.delete(a)}for(let t of e.modules.keys())n.has(t)||i(t,[t]);return t}function Ye(e,t,n,r,i,a){let o=[];for(let i of e.providers){let e=n.get(i);if(e)for(let n of e.dependencies){let e=r.get(n);e&&e.name===t.name&&o.push({consumer:i,dependency:n})}}for(let n of e.controllers)for(let e of a){let a=i.getSourceFile(e);if(a)for(let e of a.getClasses()){if(e.getName()!==n)continue;let i=e.getConstructors()[0];if(i)for(let e of i.getParameters()){let i=e.getTypeNode(),a=i?i.getText():e.getType().getText(),s=a.split(`.`).pop()?.split(`<`)[0]??a,c=r.get(s);c&&c.name===t.name&&o.push({consumer:n,dependency:s})}}}return o}const Xe=`Break the cycle by extracting the coupling providers into a shared module. forwardRef() only defers resolution at runtime — it does not address the architectural coupling. Use forwardRef() as a last resort and enable rules.architecture/no-circular-module-deps.options.ignoreForwardRefCycles to opt out of these reports.`;function Ze(e){let t=e.config.rules?.[`architecture/no-circular-module-deps`];return typeof t!=`object`||!t?!1:t.options?.ignoreForwardRefCycles===!0}function Qe(e,t){let n=e.length>1&&e[0]===e.at(-1)?e.slice(0,-1):e;for(let e=0;e<n.length;e++){let r=n[e],i=n[(e+1)%n.length],a=t.modules.get(r),o=t.modules.get(i);if(!(a&&o)||!a.forwardRefImports.has(i))return!1}return!0}function $e(e,t){let{moduleGraph:n,providers:r,project:i,files:a}=t,o=[],s;for(let t=0;t<e.length;t++){let c=e[t],l=e[(t+1)%e.length],u=n.modules.get(c),d=n.modules.get(l);if(!(u&&d))continue;let f=Ye(u,d,r,n.providerToModule,i,a);if(f.length===0)continue;let p=new Map;for(let e of f){let t=p.get(e.consumer);t?t.push(e.dependency):p.set(e.consumer,[e.dependency])}let m=[];for(let[e,t]of p){let n=t.map(e=>`${e} (from ${l})`).join(`, `);m.push(`${e} (in ${c}) injects ${n}`)}let h=`${c} -> ${l}: ${m.join(`; `)}`;o.push(h),(!s||f.length<s.count)&&(s={description:`${c} -> ${l}`,count:f.length})}if(o.length===0)return Xe;let c=o.join(`
|
|
4
|
+
`);if(s){let e=s.count===1?`dependency`:`dependencies`,t=s.description.split(` -> `)[0],o=s.description.split(` -> `)[1],l=n.modules.get(t),u=n.modules.get(o);if(l&&u){let t=Ye(l,u,r,n.providerToModule,i,a),o=[...new Set(t.map(e=>e.dependency))].join(`, `);c+=`\nConsider extracting ${o} into a shared module — it would break the ${s.description} edge (${s.count} ${e}).`}}return c}const et={meta:{id:`architecture/no-circular-module-deps`,category:`architecture`,severity:`error`,description:`Module import graph must not contain circular dependencies`,help:Xe,scope:`project`},check(e){let t=Je(e.moduleGraph),n=Ze(e);for(let r of t){if(n&&Qe(r,e.moduleGraph))continue;let t=r.join(` -> `),i=e.moduleGraph.modules.get(r[0]),a=$e(r,e);e.report({filePath:i?.filePath??`unknown`,message:`Circular module dependency detected: ${t}`,help:a,line:i?.classDeclaration.getStartLineNumber()??1,column:1})}}},tt=[`Service`,`Repository`,`Gateway`,`Resolver`],nt=[`Guard`,`Interceptor`,`Pipe`,`Filter`];function rt(e){return typeof e==`object`&&!!e}function it(e){if(!rt(e))return new Set;let t=e.excludeClasses;if(Array.isArray(t))return new Set(t.filter(e=>typeof e==`string`));let n=e.options;if(!rt(n))return new Set;let r=n.excludeClasses;return Array.isArray(r)?new Set(r.filter(e=>typeof e==`string`)):new Set}const at={meta:{id:`architecture/no-manual-instantiation`,category:`architecture`,severity:`error`,description:`Do not manually instantiate @Injectable classes — use NestJS dependency injection`,help:`Register the class as a provider in a module and inject it via the constructor.`},check(e){let t=it(e.config?.rules?.[this.meta.id]),n=e.sourceFile.getDescendantsOfKind(d.NewExpression);for(let r of n){let n=r.getExpression().getText(),i=n.split(`.`).pop()??n;if(t.has(n)||t.has(i))continue;let a=tt.some(e=>n.endsWith(e)),o=nt.some(e=>n.endsWith(e));if(a||o){if(o){if(r.getFirstAncestorByKind(d.Decorator))continue;let e=r.getFirstAncestorByKind(d.MethodDeclaration),t=r.getFirstAncestorByKind(d.Constructor);if(!(e||t))continue}e.report({filePath:e.filePath,message:`Manual instantiation of '${n}' detected. Use dependency injection instead.`,help:this.meta.help,line:r.getStartLineNumber(),column:1})}}}},ot=/\.(\w+)$/,st=/^(\w+)</,ct=new Set([`PrismaService`,`PrismaClient`,`EntityManager`,`DataSource`,`Repository`,`Connection`,`MongooseModel`,`InjectModel`,`InjectRepository`,`MikroORM`,`DrizzleService`]),lt={meta:{id:`architecture/no-orm-in-controllers`,category:`architecture`,severity:`error`,description:`Controllers must not inject ORM services directly — use a service layer`,help:`Inject a service that wraps the ORM instead of using the ORM directly in controllers.`},check(e){for(let t of e.sourceFile.getClasses()){if(!T(t))continue;let n=t.getConstructors()[0];if(n){for(let t of n.getParameters()){let n=ut(t.getType().getText());if(ct.has(n)){let r=t.getNameNode();e.report({filePath:e.filePath,message:`Controller injects ORM type '${n}' directly. Use a service layer.`,help:this.meta.help,line:r.getStartLineNumber(),column:r.getStartLinePos()+1})}}for(let n of t.getConstructors()[0]?.getParameters()??[])for(let t of n.getDecorators()){let n=t.getName();(n===`InjectRepository`||n===`InjectModel`)&&e.report({filePath:e.filePath,message:`Controller uses @${n}() decorator. Move data access to a service.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}}}};function ut(e){let t=e.match(ot);if(t)return t[1];let n=e.match(st);return n?n[1]:e}const dt=/\.(\w+)$/,ft=/^(\w+)</,pt=new Set([`PrismaService`,`PrismaClient`,`EntityManager`,`DataSource`,`Connection`,`MikroORM`]),mt={meta:{id:`architecture/no-orm-in-services`,category:`architecture`,severity:`info`,description:`Services should use repository abstractions instead of ORM directly`,help:`Create a repository class that wraps ORM calls and inject that instead. Note: If your project follows the official NestJS Prisma recipe (injecting PrismaService directly), you can disable this rule.`},check(e){for(let t of e.sourceFile.getClasses()){if(!Oe(t))continue;let n=t.getName()??``;if(n.endsWith(`Repository`)||n.endsWith(`Repo`))continue;let r=t.getConstructors()[0];if(r)for(let t of r.getParameters()){let n=ht(t.getType().getText());if(pt.has(n)){let r=t.getNameNode();e.report({filePath:e.filePath,message:`Service injects ORM type '${n}' directly. Consider using a repository abstraction.`,help:this.meta.help,line:r.getStartLineNumber(),column:r.getStartLinePos()+1})}for(let n of t.getDecorators()){let t=n.getName();(t===`InjectRepository`||t===`InjectModel`)&&e.report({filePath:e.filePath,message:`Service uses @${t}() directly. Consider wrapping in a repository class.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}}}};function ht(e){let t=e.match(dt);if(t)return t[1];let n=e.match(ft);return n?n[1]:e}const gt=/\.(\w+)$/,_t=/^(\w+)</,vt=[/Repository$/,/Repo$/],yt={meta:{id:`architecture/no-repository-in-controllers`,category:`architecture`,severity:`error`,description:`Controllers must not inject repositories directly — use the service layer`,help:`Move database access to a service and inject the service into the controller instead.`},check(e){for(let t of e.sourceFile.getClasses()){if(!T(t))continue;let n=t.getConstructors()[0];if(n){for(let t of n.getParameters()){let n=bt(t.getType().getText());if(vt.some(e=>e.test(n))){let r=t.getNameNode();e.report({filePath:e.filePath,message:`Controller injects repository '${n}' directly. Use a service layer instead.`,help:this.meta.help,line:r.getStartLineNumber(),column:r.getStartLinePos()+1})}}for(let t of e.sourceFile.getImportDeclarations()){let n=t.getModuleSpecifierValue();(n.includes(`/repositories/`)||n.includes(`/repositories`))&&e.report({filePath:e.filePath,message:`Controller imports from repository path '${n}'.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}}}};function bt(e){let t=e.match(gt);if(t)return t[1];let n=e.match(_t);return n?n[1]:e}const xt={meta:{id:`architecture/no-service-locator`,category:`architecture`,severity:`warning`,description:`Avoid using ModuleRef.get() or ModuleRef.resolve() — prefer explicit constructor injection`,help:`Replace ModuleRef.get()/resolve() with constructor injection for explicit, testable dependencies.`},check(e){let t=e.sourceFile.getDescendantsOfKind(d.CallExpression);for(let n of t){let t=n.getExpression();if(t.getKind()!==d.PropertyAccessExpression)continue;let r=t.asKind(d.PropertyAccessExpression);if(!r)continue;let i=r.getName();if(i!==`get`&&i!==`resolve`)continue;let a=r.getExpression().getText();(a===`moduleRef`||a===`this.moduleRef`)&&e.report({filePath:e.filePath,message:`Service locator pattern: '${a}.${i}()' hides dependencies. Use constructor injection instead.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},St={meta:{id:`architecture/prefer-constructor-injection`,category:`architecture`,severity:`warning`,description:`Prefer constructor injection over @Inject() property injection`,help:`Move the dependency to a constructor parameter instead of using property injection.`},check(e){for(let t of e.sourceFile.getClasses())if(ke(t))for(let n of t.getProperties())n.getDecorator(`Inject`)&&e.report({filePath:e.filePath,message:`Property '${n.getName()}' uses @Inject() decorator. Prefer constructor injection.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}},Ct=[`/repositories/`,`/entities/`,`/dto/`,`/guards/`,`/interceptors/`,`/pipes/`,`/strategies/`],wt={meta:{id:`architecture/require-module-boundaries`,category:`architecture`,severity:`info`,description:`Avoid deep imports into other feature modules' internals`,help:`Import from the module's public API (barrel export) instead of reaching into its internals.`},check(e){for(let t of e.sourceFile.getImportDeclarations()){let n=t.getModuleSpecifierValue();n.startsWith(`.`)&&n.includes(`../`)&&Ct.some(e=>n.includes(e))&&e.report({filePath:e.filePath,message:`Import '${n}' reaches into another module's internals.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}},Tt={meta:{id:`correctness/factory-inject-matches-params`,category:`correctness`,severity:`error`,description:`useFactory inject array length must match the factory function parameter count`,help:`Ensure the 'inject' array has one entry per factory function parameter.`},check(e){for(let t of e.sourceFile.getClasses()){if(!Ae(t))continue;let n=t.getDecorator(`Module`);if(!n)continue;let r=n.getArguments()[0];if(!r||r.getKind()!==d.ObjectLiteralExpression)continue;let i=r.asKind(d.ObjectLiteralExpression);if(!i)continue;let a=i.getProperty(`providers`);if(!a)continue;let o=a.getChildrenOfKind(d.ArrayLiteralExpression)[0];if(o)for(let t of o.getElements()){if(t.getKind()!==d.ObjectLiteralExpression)continue;let n=t.asKind(d.ObjectLiteralExpression);if(!n)continue;let r=n.getProperty(`useFactory`),i=n.getProperty(`inject`);if(!(r&&i))continue;let a=i.getChildrenOfKind(d.ArrayLiteralExpression)[0];if(!a)continue;let o=a.getElements().length,s,c=r.asKind(d.MethodDeclaration);if(c)s=c.getParameters().length;else{let e=r.asKind(d.PropertyAssignment);if(!e)continue;let t=e.getInitializer();if(!t)continue;t.getKind()===d.ArrowFunction?s=t.asKind(d.ArrowFunction)?.getParameters().length:t.getKind()===d.FunctionExpression&&(s=t.asKind(d.FunctionExpression)?.getParameters().length)}s!==void 0&&o!==s&&e.report({filePath:e.filePath,message:`Factory has ${s} parameter(s) but inject array has ${o} element(s).`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}}},Et=[`Guard`,`Interceptor`,`Filter`,`Pipe`,`Middleware`,`Strategy`,`Subscriber`,`Listener`,`Processor`,`Consumer`,`Worker`,`Scheduler`,`Cron`,`HealthIndicator`],Dt={meta:{id:`correctness/injectable-must-be-provided`,category:`correctness`,severity:`info`,description:`@Injectable() classes should be registered in at least one module's providers array`,help:`Add this class to a module's providers array, or remove the @Injectable() decorator if unused.`,scope:`project`},check(e){let t=new Set;for(let n of e.moduleGraph.modules.values()){for(let e of n.providers)t.add(e);for(let e of n.controllers)t.add(e)}for(let n of e.files){let r=e.project.getSourceFile(n);if(r)for(let e of r.getClasses()){if(!Ae(e))continue;let n=e.getDecorator(`Module`);if(!n)continue;let r=n.getArguments()[0];if(!r||r.getKind()!==d.ObjectLiteralExpression)continue;let i=r.asKind(d.ObjectLiteralExpression);if(!i)continue;let a=i.getProperty(`providers`);if(!a)continue;let o=a.getChildrenOfKind(d.ArrayLiteralExpression)[0];if(o)for(let e of o.getElements()){if(e.getKind()!==d.ObjectLiteralExpression)continue;let n=e.asKind(d.ObjectLiteralExpression);if(n)for(let e of[`useClass`,`useExisting`]){let r=n.getProperty(e);if(!r)continue;let i=r.asKind(d.PropertyAssignment);if(!i)continue;let a=i.getInitializer();a&&t.add(a.getText())}}}}for(let n of e.files){if(n.includes(`.spec.`)||n.includes(`.test.`)||n.includes(`__test__`)||n.includes(`__tests__`))continue;let r=e.project.getSourceFile(n);if(r)for(let i of r.getClasses()){if(!i.getDecorator(`Injectable`))continue;let r=i.getName();r&&(Et.some(e=>r.endsWith(e))||t.has(r)||e.report({filePath:n,message:`@Injectable() class '${r}' is not registered in any module's providers array.`,help:this.meta.help,line:i.getStartLineNumber(),column:1}))}}}};function Ot(e){return e.getDescendantsOfKind(d.ReturnStatement).some(e=>{let t=e.getExpression();return!t||t.getKind()!==d.NewExpression?!1:t.asKindOrThrow(d.NewExpression).getExpression().getText()===`Promise`})}const kt={meta:{id:`correctness/no-async-without-await`,category:`correctness`,severity:`warning`,description:`Async functions/methods should contain at least one await expression`,help:`Either add an await expression or remove the async keyword. HTTP handlers with route decorators are exempted, as async is conventional for controller methods.`},check(e){for(let t of e.sourceFile.getClasses())for(let n of t.getMethods()){if(!n.isAsync()||T(t)&&E(n)||Me(n))continue;let r=n.getBody();if(r&&r.getDescendantsOfKind(d.AwaitExpression).filter(e=>{let t=e.getParent();for(;t&&t!==r;){if(t.getKind()===d.ArrowFunction||t.getKind()===d.FunctionExpression||t.getKind()===d.FunctionDeclaration)return!1;t=t.getParent()}return!0}).length===0){let t=n.getName();Ot(r)?e.report({filePath:e.filePath,message:`Async method '${t}()' returns a Promise directly — remove the async keyword.`,help:`The async keyword is unnecessary when you are already constructing a Promise manually. Remove async to avoid double-wrapping.`,line:n.getStartLineNumber(),column:1}):e.report({filePath:e.filePath,message:`Async method '${t}()' has no await expression.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}for(let t of e.sourceFile.getFunctions()){if(!t.isAsync())continue;let n=t.getBody();if(n&&n.getDescendantsOfKind(d.AwaitExpression).filter(e=>{let t=e.getParent();for(;t&&t!==n;){if(t.getKind()===d.ArrowFunction||t.getKind()===d.FunctionExpression||t.getKind()===d.FunctionDeclaration)return!1;t=t.getParent()}return!0}).length===0){let r=t.getName()??`anonymous`;Ot(n)?e.report({filePath:e.filePath,message:`Async function '${r}()' returns a Promise directly — remove the async keyword.`,help:`The async keyword is unnecessary when you are already constructing a Promise manually. Remove async to avoid double-wrapping.`,line:t.getStartLineNumber(),column:1}):e.report({filePath:e.filePath,message:`Async function '${r}()' has no await expression.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}}},At=new Set([`ApiResponse`,`ApiQuery`,`ApiParam`,`ApiHeader`,`ApiSecurity`,`SetMetadata`,`Roles`,`Header`,`Throttle`]),jt={meta:{id:`correctness/no-duplicate-decorators`,category:`correctness`,severity:`warning`,description:`Same decorator should not appear twice on a single target`,help:`Remove the duplicate decorator — it was likely copy-pasted by mistake.`},check(e){for(let t of e.sourceFile.getClasses()){A(t.getDecorators(),e,this.meta.help);for(let n of t.getMethods())A(n.getDecorators(),e,this.meta.help);for(let n of t.getProperties())A(n.getDecorators(),e,this.meta.help);for(let n of t.getConstructors())for(let t of n.getParameters())A(t.getDecorators(),e,this.meta.help)}}};function A(e,t,n){let r=new Set;for(let i of e){let e=i.getName();At.has(e)||(r.has(e)?t.report({filePath:t.filePath,message:`Duplicate @${e}() decorator on the same target.`,help:n,line:i.getStartLineNumber(),column:1}):r.add(e))}}const Mt=[`providers`,`controllers`,`imports`,`exports`],Nt={meta:{id:`correctness/no-duplicate-module-metadata`,category:`correctness`,severity:`warning`,description:`Same identifier should not appear twice in a module metadata array`,help:`Remove the duplicate entry from the module metadata.`},check(e){for(let t of e.sourceFile.getClasses()){if(!Ae(t))continue;let n=t.getDecorator(`Module`);if(!n)continue;let r=n.getArguments()[0];if(!r||r.getKind()!==d.ObjectLiteralExpression)continue;let i=r.asKind(d.ObjectLiteralExpression);if(i)for(let t of Mt){let n=i.getProperty(t);if(!n)continue;let r=n.getChildrenOfKind(d.ArrayLiteralExpression)[0];if(!r)continue;let a=new Set;for(let n of r.getElements()){let r=n.getText();a.has(r)?e.report({filePath:e.filePath,message:`Duplicate '${r}' in @Module() ${t} array.`,help:this.meta.help,line:n.getStartLineNumber(),column:1}):a.add(r)}}}}},Pt={meta:{id:`correctness/no-duplicate-routes`,category:`correctness`,severity:`error`,description:`Same HTTP method + route path + version should not appear twice in a single controller`,help:`Remove or rename one of the duplicate route handlers.`},check(e){for(let t of e.sourceFile.getClasses()){if(!T(t))continue;let n=new Map;for(let r of t.getMethods())for(let t of r.getDecorators()){let i=t.getName();if(!C.has(i))continue;let a=t.getArguments(),o=a.length>0?a[0].getText():`""`,s=r.getDecorator(`Version`),c=`${i}:${o}:${s?s.getArguments()[0]?.getText()??``:``}`,l=n.get(c);l?e.report({filePath:e.filePath,message:`Duplicate route: @${i}(${o}) is already defined in '${l}()'.`,help:this.meta.help,line:r.getStartLineNumber(),column:1}):n.set(c,r.getName())}}}},Ft={meta:{id:`correctness/no-empty-handlers`,category:`correctness`,severity:`info`,description:`Controller HTTP handlers should not have empty bodies`,help:`Add implementation to the handler method or remove it if unnecessary.`},check(e){for(let t of e.sourceFile.getClasses())if(T(t))for(let n of t.getMethods()){if(!n.getDecorators().some(e=>C.has(e.getName())))continue;let t=n.getBody();if(!t)continue;let r=t.asKind(d.Block);r&&r.getStatements().length===0&&e.report({filePath:e.filePath,message:`Handler '${n.getName()}()' has an empty body.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}};function It(e){let t=e.getReturnType().getText();return t.startsWith(`Promise<`)||t===`Promise`?!0:t===`any`||t===`error`?`unknown`:!1}const Lt=new Set([`save`,`create`,`insert`,`update`,`delete`,`remove`,`send`,`emit`,`publish`,`dispatch`,`execute`,`fetch`,`load`,`upload`,`download`,`process`]),Rt={meta:{id:`correctness/no-fire-and-forget-async`,category:`correctness`,severity:`warning`,description:`Calling async functions without await leads to unhandled promise rejections`,help:`Add await before the async call, or use void with explicit error handling if fire-and-forget is intentional.`},check(e){for(let t of e.sourceFile.getClasses())for(let n of t.getMethods()){if(E(n))continue;let t=n.getBody();if(!t)continue;let r=t.getDescendantsOfKind(d.ExpressionStatement);for(let t of r){let r=t.getExpression();if(r.getKind()===d.VoidExpression||r.getKind()===d.AwaitExpression||r.getKind()!==d.CallExpression)continue;let i=r.asKind(d.CallExpression);if(!i)continue;let a=i.getExpression().getText().split(`.`).pop()??``,o=It(i);if(o!==!1){if(o===`unknown`){let e=a.toLowerCase();if(!(Lt.has(e)||[...Lt].some(t=>e.startsWith(t)&&e!==t)))continue}t.getFirstAncestorByKind(d.MethodDeclaration)===n&&e.report({filePath:e.filePath,message:`Async call '${a}()' is not awaited — unhandled rejections will crash the process.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}}}},zt={meta:{id:`correctness/no-missing-filter-catch`,category:`correctness`,severity:`error`,description:`Exception filter classes decorated with @Catch() must implement the catch() method`,help:`Add a catch(exception, host: ArgumentsHost) method to the filter class.`},check(e){for(let t of e.sourceFile.getClasses())w(t,`Catch`)&&(t.getExtends()||t.getMethods().some(e=>e.getName()===`catch`)||e.report({filePath:e.filePath,message:`Exception filter '${t.getName()}' has @Catch() but is missing the 'catch()' method.`,help:this.meta.help,line:t.getStartLineNumber(),column:1}))}},Bt={meta:{id:`correctness/no-missing-guard-method`,category:`correctness`,severity:`error`,description:`Guard classes must implement the canActivate() method`,help:`Add a canActivate(context: ExecutionContext) method to the guard class. Note: This rule identifies guards by the 'Guard' class name suffix.`},check(e){for(let t of e.sourceFile.getClasses()){let n=t.getName()??``;n.endsWith(`Guard`)&&w(t,`Injectable`)&&(t.getExtends()||t.getMethods().some(e=>e.getName()===`canActivate`)||e.report({filePath:e.filePath,message:`Guard '${n}' is missing the 'canActivate()' method.`,help:this.meta.help,line:t.getStartLineNumber(),column:1}))}}},Vt={meta:{id:`correctness/no-missing-injectable`,category:`correctness`,severity:`error`,description:`Provider classes with constructor dependencies must have the @Injectable() decorator`,help:`Add @Injectable() to providers that inject constructor dependencies.`,scope:`project`},check(e){let t=new Set([...e.providers.values()].map(e=>e.name)),n=new Map;for(let t of e.files){let r=e.project.getSourceFile(t);if(r)for(let e of r.getClasses()){let r=e.getName();if(r){let i=n.get(r)??[];i.push({cls:e,filePath:t}),n.set(r,i)}}}for(let r of e.moduleGraph.modules.values())for(let i of r.providers){if(t.has(i))continue;let a=n.get(i);if(a)for(let{cls:t,filePath:n}of a){let a=(t.getConstructors()[0]?.getParameters().length??0)>0;!(t.getDecorator(`Injectable`)||t.getDecorator(`Resolver`)||t.getDecorator(`WebSocketGateway`))&&a&&e.report({filePath:n,message:`Class '${i}' is listed in '${r.name}' providers but is missing @Injectable() decorator.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}}},Ht={meta:{id:`correctness/no-missing-interceptor-method`,category:`correctness`,severity:`error`,description:`Interceptor classes must implement the intercept() method`,help:`Add an intercept(context: ExecutionContext, next: CallHandler) method to the interceptor class.`},check(e){for(let t of e.sourceFile.getClasses()){let n=t.getName()??``;n.endsWith(`Interceptor`)&&w(t,`Injectable`)&&(t.getExtends()||t.getMethods().some(e=>e.getName()===`intercept`)||e.report({filePath:e.filePath,message:`Interceptor '${n}' is missing the 'intercept()' method.`,help:this.meta.help,line:t.getStartLineNumber(),column:1}))}}},Ut={meta:{id:`correctness/no-missing-module-decorator`,category:`correctness`,severity:`warning`,description:`Classes named *Module should have a @Module() decorator`,help:`Add @Module({}) decorator to the class, or rename it if it is not a NestJS module.`},check(e){for(let t of e.sourceFile.getClasses()){let n=t.getName()??``;n.endsWith(`Module`)&&(n===`Module`||n===`DynamicModule`||w(t,`Module`)||e.report({filePath:e.filePath,message:`Class '${n}' is named like a module but is missing the @Module() decorator.`,help:this.meta.help,line:t.getStartLineNumber(),column:1}))}}},Wt={meta:{id:`correctness/no-missing-pipe-method`,category:`correctness`,severity:`error`,description:`Pipe classes must implement the transform() method`,help:`Add a transform(value: any, metadata: ArgumentMetadata) method to the pipe class.`},check(e){for(let t of e.sourceFile.getClasses()){let n=t.getName()??``;n.endsWith(`Pipe`)&&w(t,`Injectable`)&&(t.getExtends()||t.getMethods().some(e=>e.getName()===`transform`)||e.report({filePath:e.filePath,message:`Pipe '${n}' is missing the 'transform()' method.`,help:this.meta.help,line:t.getStartLineNumber(),column:1}))}}},Gt=/:(\w+)/g,Kt={meta:{id:`correctness/param-decorator-matches-route`,category:`correctness`,severity:`error`,description:`@Param() decorator name must match a :param in the route path`,help:`Ensure the @Param('name') argument matches a ':name' segment in the route path (including controller prefix).`},check(e){for(let t of e.sourceFile.getClasses()){if(!T(t))continue;let n=t.getDecorator(`Controller`),r=``;if(n){let e=n.getArguments();if(e.length>0){let t=e[0];if(t.getKind()===d.ObjectLiteralExpression){let e=t.asKind(d.ObjectLiteralExpression);if(e){let t=e.getProperty(`path`);if(t){let e=t.asKind(d.PropertyAssignment);if(e){let t=e.getInitializer();t&&(r=t.getText().replace(/^['"`]|['"`]$/g,``))}}}}else r=t.getText().replace(/^['"`]|['"`]$/g,``)}}let i=new Set;for(let e of r.matchAll(Gt))i.add(e[1]);for(let n of t.getMethods()){let t=``,r=!1;for(let e of n.getDecorators())if(C.has(e.getName())){r=!0;let n=e.getArguments();n.length>0&&(t=n[0].getText().replace(/^['"`]|['"`]$/g,``));break}if(!r)continue;let a=new Set;for(let e of t.matchAll(Gt))a.add(e[1]);let o=new Set([...i,...a]);for(let t of n.getParameters())for(let n of t.getDecorators()){if(n.getName()!==`Param`)continue;let t=n.getArguments();if(t.length===0)continue;let r=t[0].getText().replace(/^['"`]|['"`]$/g,``);o.has(r)||e.report({filePath:e.filePath,message:`@Param('${r}') does not match any route parameter. Available: ${o.size>0?[...o].map(e=>`:${e}`).join(`, `):`(none)`}.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}}}},qt={meta:{id:`correctness/prefer-readonly-injection`,category:`correctness`,severity:`warning`,description:`Constructor DI parameters should be readonly to prevent accidental reassignment`,help:`Add the 'readonly' modifier to the constructor parameter.`},check(e){for(let t of e.sourceFile.getClasses()){if(!(Oe(t)||T(t)))continue;let n=t.getConstructors()[0];if(n){for(let t of n.getParameters())if((t.hasModifier(`private`)||t.hasModifier(`protected`)||t.hasModifier(`public`))&&!t.isReadonly()){let n=t.getNameNode();e.report({filePath:e.filePath,message:`Constructor parameter '${t.getName()}' should be readonly.`,help:this.meta.help,line:n.getStartLineNumber(),column:n.getStartLinePos()+1})}}}}},Jt={meta:{id:`correctness/require-inject-decorator`,category:`correctness`,severity:`error`,description:`Constructor parameters without type annotations must have @Inject() decorator for NestJS DI to resolve them`,help:`Add a type annotation or @Inject() decorator to the constructor parameter.`},check(e){for(let t of e.sourceFile.getClasses()){if(!ke(t))continue;let n=t.getConstructors()[0];if(n)for(let r of n.getParameters()){let n=r.getTypeNode(),i=r.getDecorators().some(e=>e.getName()===`Inject`);n||i||e.report({filePath:e.filePath,message:`Constructor parameter '${r.getName()}' in '${t.getName()}' has no type annotation and no @Inject() decorator — NestJS cannot resolve it.`,help:this.meta.help,line:r.getStartLineNumber(),column:1})}}}},Yt={onModuleInit:`OnModuleInit`,onModuleDestroy:`OnModuleDestroy`,onApplicationBootstrap:`OnApplicationBootstrap`,onApplicationShutdown:`OnApplicationShutdown`,beforeApplicationShutdown:`BeforeApplicationShutdown`},Xt={meta:{id:`correctness/require-lifecycle-interface`,category:`correctness`,severity:`warning`,description:`Classes with lifecycle methods should implement the corresponding NestJS interface`,help:`Add 'implements OnModuleInit' (or the appropriate interface) to the class declaration.`},check(e){for(let t of e.sourceFile.getClasses()){let n=t.getImplements().map(e=>e.getText());for(let r of t.getMethods()){let i=r.getName(),a=Yt[i];a&&(n.some(e=>e===a||e.startsWith(`${a}<`))||e.report({filePath:e.filePath,message:`Class '${t.getName()}' has '${i}()' but does not implement '${a}'.`,help:this.meta.help,line:r.getStartLineNumber(),column:1}))}}}},Zt=/each\s*:\s*true/,Qt={meta:{id:`correctness/validate-nested-array-each`,category:`correctness`,severity:`warning`,description:`@ValidateNested() on array-typed properties must use { each: true }`,help:`Change @ValidateNested() to @ValidateNested({ each: true }) for array properties.`},check(e){for(let t of e.sourceFile.getClasses())for(let n of t.getProperties()){let t=n.getDecorators(),r=t.find(e=>e.getName()===`ValidateNested`);if(!r)continue;let i=$t(n),a=t.some(e=>e.getName()===`IsArray`);(i||a)&&(en(r)||e.report({filePath:e.filePath,message:`Property '${n.getName()}' is an array with @ValidateNested() but missing { each: true }.`,help:this.meta.help,line:r.getStartLineNumber(),column:1}))}}};function $t(e){let t=e.getTypeNode();if(!t)return!1;let n=t.getText().replace(/\s/g,``);return!!(n.endsWith(`[]`)||n.startsWith(`Array<`))}function en(e){let t=e.getArguments();if(t.length===0)return!1;let n=t[0];if(n.getKind()!==d.ObjectLiteralExpression)return!1;let r=n.getText();return Zt.test(r)}const tn=new Set(`ValidateNested.IsString.IsNumber.IsBoolean.IsEmail.IsArray.IsEnum.IsNotEmpty.IsDefined.IsOptional.IsDate.IsObject.IsInt.IsPositive.IsNegative.IsUUID.IsUrl.IsISO8601.Matches.Min.Max.MinLength.MaxLength.ArrayMinSize.ArrayMaxSize.ArrayNotEmpty.IsIn.IsNotIn.Length.Contains.IsAlpha.IsAlphanumeric.IsDecimal.IsHexColor.IsJSON.IsPhoneNumber.IsIP.IsCreditCard.IsDateString.IsMilitaryTime.IsMongoId.IsPort.IsSemVer.IsStrongPassword`.split(`.`)),nn=new Set([`string`,`number`,`boolean`,`Date`,`any`,`unknown`,`bigint`,`symbol`,`undefined`,`null`,`void`,`never`]),rn=/\s/g,an=/\[\]$/,on=/^Array<(.+)>$/,sn=/^["']/,cn=/^\d+$/;function j(e){let t=e.replace(rn,``);if(t.includes(`|`))return t.split(`|`).every(e=>j(e));if(nn.has(t)||an.test(t)&&j(t.replace(an,``)))return!0;let n=t.match(on);return!!(n&&j(n[1])||sn.test(t)||cn.test(t))}const ln={meta:{id:`correctness/validated-non-primitive-needs-type`,category:`correctness`,severity:`warning`,description:`DTO properties with class-validator decorators on non-primitive types must have @Type() from class-transformer`,help:`Add @Type(() => ClassName) from 'class-transformer' to ensure proper transformation.`},check(e){for(let t of e.sourceFile.getClasses())for(let n of t.getProperties()){let t=n.getDecorators();if(t.length===0||!t.some(e=>tn.has(e.getName()))||t.some(e=>e.getName()===`Type`)||t.some(e=>e.getName()===`IsEnum`))continue;let r=n.getTypeNode();if(!r)continue;let i=r.getText();j(i)||e.report({filePath:e.filePath,message:`Property '${n.getName()}' has type '${i}' with class-validator decorators but is missing @Type() decorator.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},un=new Set([d.ForStatement,d.ForOfStatement,d.ForInStatement,d.WhileStatement,d.DoStatement]),dn={meta:{id:`performance/no-blocking-constructor`,category:`performance`,severity:`warning`,description:`Constructors in Injectable/Controller classes should not contain heavy operations`,help:`Move heavy initialization logic to the onModuleInit() lifecycle method. Constructors cannot be async, so asynchronous work should always use lifecycle hooks.`},check(e){for(let t of e.sourceFile.getClasses()){if(!(w(t,`Injectable`)||w(t,`Controller`)))continue;let n=t.getConstructors()[0];if(!n)continue;let r=n.getBody();if(r){for(let i of r.getDescendants())if(un.has(i.getKind())){e.report({filePath:e.filePath,message:`Constructor in '${t.getName()}' contains blocking operation — use onModuleInit() instead.`,help:this.meta.help,line:n.getStartLineNumber(),column:1});break}}}}},fn={meta:{id:`performance/no-dynamic-require`,category:`performance`,severity:`warning`,description:`Dynamic require() with variable arguments prevents bundler optimization`,help:`Use static import statements or dynamic import() with string literals.`},check(e){let t=e.sourceFile.getDescendantsOfKind(d.CallExpression);for(let n of t){if(n.getExpression().getText()!==`require`)continue;let t=n.getArguments();t.length!==0&&t[0].getKind()!==d.StringLiteral&&e.report({filePath:e.filePath,message:`Dynamic require() with non-literal argument prevents bundler optimization.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},pn={meta:{id:`performance/no-orphan-modules`,category:`performance`,severity:`info`,description:`Module is never imported by any other module and may be dead code`,help:`Import this module in another module or remove it if it is unused.`,scope:`project`},check(e){let t=new Set;for(let n of e.moduleGraph.modules.values())for(let e of n.imports)t.add(e);for(let n of e.moduleGraph.modules.values())n.name!==`AppModule`&&(t.has(n.name)||e.report({filePath:n.filePath,message:`Module '${n.name}' is never imported by any other module.`,help:this.meta.help,line:n.classDeclaration.getStartLineNumber(),column:1}))}},mn={meta:{id:`performance/no-request-scope-abuse`,category:`performance`,severity:`warning`,description:`Scope.REQUEST creates a new provider instance per request — use only when necessary`,help:`Remove Scope.REQUEST unless the provider genuinely needs per-request state (e.g., request-scoped context). Consider Scope.DEFAULT or Scope.TRANSIENT instead.`},check(e){let t=e.sourceFile.getDescendantsOfKind(d.PropertyAccessExpression);for(let n of t)n.getName()===`REQUEST`&&n.getExpression().getText()===`Scope`&&e.report({filePath:e.filePath,message:`Scope.REQUEST creates a new instance per request, which impacts performance and propagates request scope to all dependents.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}},hn=new Set([`readFileSync`,`writeFileSync`,`existsSync`,`mkdirSync`,`readdirSync`,`statSync`,`accessSync`,`appendFileSync`,`copyFileSync`,`renameSync`,`unlinkSync`]),gn={meta:{id:`performance/no-sync-io`,category:`performance`,severity:`warning`,description:`Synchronous I/O calls block the event loop and should be avoided in NestJS applications`,help:`Use the async variant (e.g., readFile instead of readFileSync) with await.`},check(e){let t=e.sourceFile.getDescendantsOfKind(d.CallExpression);for(let n of t){let t=n.getExpression().getText().split(`.`).pop()??``;hn.has(t)&&e.report({filePath:e.filePath,message:`Synchronous I/O call '${t}()' blocks the event loop.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},_n={meta:{id:`performance/no-unused-module-exports`,category:`performance`,severity:`info`,description:`Module exports a provider that no importing module actually uses`,help:`Remove the unused export or use the provider in an importing module.`,scope:`project`},check(e){for(let t of e.moduleGraph.modules.values()){if(t.exports.length===0)continue;let n=[];for(let r of e.moduleGraph.modules.values())r.name!==t.name&&r.imports.includes(t.name)&&n.push(r.name);if(n.length===0)continue;let r=new Set;for(let i of n){let n=e.moduleGraph.modules.get(i);if(n){for(let t of n.providers){let n=e.providers.get(t);if(n)for(let e of n.dependencies)r.add(e)}if(n.exports.includes(t.name))for(let e of t.exports)r.add(e);for(let t of n.controllers)for(let n of e.files){let i=e.project.getSourceFile(n);if(i)for(let e of i.getClasses()){if(e.getName()!==t)continue;let n=e.getConstructors()[0];if(n)for(let e of n.getParameters()){let t=e.getTypeNode(),n=t?t.getText():e.getType().getText(),i=n.split(`.`).pop()?.split(`<`)[0]??n;r.add(i)}}}}}for(let n of t.exports)e.moduleGraph.modules.has(n)||r.has(n)||e.report({filePath:t.filePath,message:`Module '${t.name}' exports '${n}' but no importing module uses it.`,help:this.meta.help,line:t.classDeclaration.getStartLineNumber(),column:1})}}},vn=new Set([`Cron`,`Interval`,`Timeout`,`OnEvent`,`Process`,`OnQueueEvent`,`EventSubscriber`,`SubscribeMessage`,`WebSocketGateway`]);function yn(e){for(let t of e.getDecorators())if(vn.has(t.getName()))return!0;for(let t of e.getMethods())for(let e of t.getDecorators())if(vn.has(e.getName()))return!0;return!1}const bn={meta:{id:`performance/no-unused-providers`,category:`performance`,severity:`warning`,description:`Injectable providers that are never injected and have no self-activating decorators may be dead code`,help:`Remove the unused provider, inject it where needed, or verify it is activated by a framework decorator (e.g. @Cron, @OnEvent).`,scope:`project`},check(e){let t=new Set;for(let n of e.providers.values())for(let e of n.dependencies)t.add(e);let n=[`Controller`,`Resolver`,`WebSocketGateway`];for(let r of e.files){let i=e.project.getSourceFile(r);if(i)for(let e of i.getClasses()){if(!n.some(t=>e.getDecorator(t)!==void 0))continue;let r=e.getConstructors()[0];if(r)for(let e of r.getParameters()){let n=e.getTypeNode(),r=n?n.getText():e.getType().getText(),i=r.split(`.`).pop()?.split(`<`)[0]??r;t.add(i)}}}for(let n of e.providers.values()){let r=n.name;if(Et.some(e=>r.endsWith(e))||t.has(r)||yn(n.classDeclaration))continue;let i=!1;for(let t of e.moduleGraph.modules.values())if(t.exports.includes(r)){i=!0;break}i||e.report({filePath:n.filePath,message:`Provider '${r}' is never injected by any other provider or controller.`,help:this.meta.help,line:n.classDeclaration.getStartLineNumber(),column:1})}}},xn={meta:{id:`schema/require-cascade-rule`,category:`schema`,scope:`schema`,severity:`info`,description:`Relations should have explicit onDelete/cascade behavior defined`,help:`Add an explicit onDelete option (e.g. CASCADE, SET NULL) to avoid relying on database defaults.`},check(e){for(let t of e.schemaGraph.relations)if(!(t.type!==`many-to-one`&&t.type!==`one-to-one`)&&!t.onDelete){let n=e.schemaGraph.entities.get(t.fromEntity);if(!n)continue;e.report({filePath:n.filePath,entity:n.name,message:`Relation '${t.propertyName}' on '${t.fromEntity}' has no explicit onDelete behavior.`,help:this.meta.help})}}},Sn={meta:{id:`schema/require-primary-key`,category:`schema`,scope:`schema`,severity:`error`,description:`Every entity must have at least one primary key column`,help:`Add a primary key column (e.g. @id in Prisma, @PrimaryColumn/@PrimaryGeneratedColumn in TypeORM).`},check(e){for(let t of e.schemaGraph.entities.values())t.columns.some(e=>e.isPrimary)||e.report({filePath:t.filePath,entity:t.name,message:`Entity '${t.name}' has no primary key column.`,help:this.meta.help})}},Cn=/delete/i;function wn(e,t){let n=new Set(e.columns.map(e=>e.name.toLowerCase()));return n.has(`createdat`)||n.has(`created_at`)?!0:t===`typeorm`?e.columns.some(e=>e.type===`timestamp`&&e.isGenerated&&!Cn.test(e.name)):t===`prisma`?e.columns.some(e=>e.type===`DateTime`&&e.defaultValue!==void 0&&e.defaultValue.includes(`now()`)):t===`drizzle`?e.columns.some(e=>(e.type===`timestamp`||e.type===`date`||e.type===`datetime`)&&e.defaultValue!==void 0&&e.defaultValue.includes(`now()`)):!1}const Tn={meta:{id:`schema/require-timestamps`,category:`schema`,scope:`schema`,severity:`warning`,description:`Entities should have timestamp columns (createdAt/updatedAt)`,help:`Add createdAt/updatedAt columns to track when records are created and modified.`},check(e){for(let t of e.schemaGraph.entities.values())wn(t,e.orm)||e.report({filePath:t.filePath,entity:t.name,message:`Entity '${t.name}' has no timestamp columns (createdAt/updatedAt).`,help:this.meta.help})}},En={meta:{id:`security/no-csrf-disabled`,category:`security`,severity:`error`,description:`CSRF protection should not be explicitly disabled`,help:`Enable CSRF protection or remove the explicit disabling of it.`},check(e){let t=e.sourceFile.getDescendantsOfKind(d.PropertyAssignment);for(let n of t){let t=n.getName();if(t!==`csrf`&&t!==`csrfProtection`)continue;let r=n.getInitializer();r&&r.getText()===`false`&&e.report({filePath:e.filePath,message:`CSRF protection explicitly disabled (${t}: false).`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},Dn={meta:{id:`security/no-dangerous-redirects`,category:`security`,severity:`error`,description:`Redirects using user-controlled input (from @Query/@Param) are an open redirect vulnerability`,help:`Validate redirect URLs against an allowlist of safe destinations.`},check(e){for(let t of e.sourceFile.getClasses())if(T(t))for(let n of t.getMethods()){let t=new Set;for(let e of n.getParameters())e.getDecorators().some(e=>e.getName()===`Query`||e.getName()===`Param`)&&t.add(e.getName());if(t.size===0)continue;let r=n.getDescendantsOfKind(d.CallExpression);for(let n of r)if(n.getExpression().getText().endsWith(`redirect`))for(let r of n.getArguments()){let i=r.getText();t.has(i)&&e.report({filePath:e.filePath,message:`Redirect uses user-controlled parameter '${i}' — open redirect risk.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}let i=n.getDecorators().find(e=>e.getName()===`Redirect`);if(i)for(let n of i.getArguments()){let r=n.getText();t.has(r)&&e.report({filePath:e.filePath,message:`@Redirect() uses user-controlled parameter '${r}' — open redirect risk.`,help:this.meta.help,line:i.getStartLineNumber(),column:1})}}}},On={meta:{id:`security/no-eval`,category:`security`,severity:`error`,description:`Usage of eval() or new Function() is a security risk and should be avoided`,help:`Refactor to avoid eval() and new Function(). Use safer alternatives like JSON.parse() or a sandboxed interpreter.`},check(e){let t=e.sourceFile.getDescendantsOfKind(d.CallExpression);for(let n of t)n.getExpression().getText()===`eval`&&e.report({filePath:e.filePath,message:`Usage of eval() is a security risk.`,help:this.meta.help,line:n.getStartLineNumber(),column:1});let n=e.sourceFile.getDescendantsOfKind(d.NewExpression);for(let t of n)t.getExpression().getText()===`Function`&&e.report({filePath:e.filePath,message:`Usage of new Function() is a security risk.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}},kn={meta:{id:`security/no-exposed-env-vars`,category:`security`,severity:`warning`,description:`Use NestJS ConfigService instead of direct process.env access in Injectable/Controller classes`,help:`Inject ConfigService and use configService.get('VAR_NAME') instead of process.env.VAR_NAME.`},check(e){for(let t of e.sourceFile.getClasses()){if(!(w(t,`Injectable`)||w(t,`Controller`)))continue;let n=t.getDescendantsOfKind(d.PropertyAccessExpression);for(let r of n)r.getExpression().getText()===`process.env`&&e.report({filePath:e.filePath,message:`Direct 'process.env.${r.getName()}' access in '${t.getName()}'. Use ConfigService instead.`,help:this.meta.help,line:r.getStartLineNumber(),column:1})}}},An=/^(error|err|e|ex|exception)$/,jn={meta:{id:`security/no-exposed-stack-trace`,category:`security`,severity:`warning`,description:`Stack traces should not be exposed in responses — they leak internal implementation details`,help:`Log the stack trace internally and return a generic error message to the client.`},check(e){let t=e.sourceFile.getDescendantsOfKind(d.PropertyAccessExpression);for(let n of t){if(n.getName()!==`stack`)continue;let t=n.getExpression().getText();if(!(An.test(t)||t.endsWith(`.error`)||t.endsWith(`.err`)))continue;let r=n.getParent();if(!r)continue;let i=r.getKind();(i===d.ReturnStatement||i===d.PropertyAssignment||i===d.ShorthandPropertyAssignment||i===d.CallExpression)&&e.report({filePath:e.filePath,message:`Stack trace '${t}.stack' may be exposed in response — leaks implementation details.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},Mn=[{pattern:/^(?=.*\d)[A-Za-z0-9+/]{40,}={0,2}$/,name:`Base64 key`},{pattern:/^sk[-_][a-zA-Z0-9]{20,}$/,name:`Secret key`},{pattern:/^pk[-_][a-zA-Z0-9]{20,}$/,name:`Public key (in source)`},{pattern:/^ghp_[a-zA-Z0-9]{36,}$/,name:`GitHub personal access token`},{pattern:/^github_pat_[a-zA-Z0-9_]{22,}$/,name:`GitHub fine-grained PAT`},{pattern:/^gho_[a-zA-Z0-9]{36,}$/,name:`GitHub OAuth token`},{pattern:/^xox[bpras]-[a-zA-Z0-9-]+$/,name:`Slack token`},{pattern:/^eyJ[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,}\./,name:`JWT token`},{pattern:/^AKIA[0-9A-Z]{16}$/,name:`AWS Access Key ID`},{pattern:/^[a-f0-9]{64}$/,name:`Hex-encoded secret (64 chars)`}],Nn=[/secret/i,/password/i,/passwd/i,/api[_-]?key/i,/auth[_-]?token/i,/private[_-]?key/i,/access[_-]?key/i,/client[_-]?secret/i],Pn=new Set([`your-secret-here`,`changeme`,`password`]),Fn=/^[A-Za-z][A-Za-z0-9_]*(\.[A-Za-z][A-Za-z0-9_]*)+$/,In=new Set([`cursor`,`nextCursor`,`prevCursor`,`previousCursor`,`startCursor`,`endCursor`,`pageToken`,`nextPageToken`,`continuationToken`,`continuation`,`nextPage`,`afterCursor`,`beforeCursor`]);function Ln(e){return!(e.length<8||e.includes("${")||e.startsWith(`process.env`)||Pn.has(e)||e.includes(` `)||Fn.test(e))}function Rn(e){return Nn.some(t=>t.test(e))}function zn(e){try{let t=Buffer.from(e,`base64`).toString(`utf-8`);return JSON.parse(t),!0}catch{return!1}}function Bn(e){let t=new Map;for(let n of e)t.set(n,(t.get(n)??0)+1);let n=0;for(let r of t.values()){let t=r/e.length;n-=t*Math.log2(t)}return n}const Vn=new Set([...`aeiouyAEIOUY`]),Hn=/^[A-Z]{2,4}_/,Un=/(?<=[a-z])(?=[A-Z])|(?<=[A-Za-z])(?=\d)|(?<=\d)(?=[A-Za-z])|_/,Wn=/[a-zA-Z]/;function Gn(e){let t=e.includes(`_`),n=e.split(Un).filter(e=>e.length>0).filter(e=>Wn.test(e)),r=n.filter(e=>e.length>=4&&[...e].some(e=>Vn.has(e)));return n.slice(0,6).filter(e=>e.length>=4&&[...e].some(e=>Vn.has(e))).length>=2||t&&e.split(`_`).filter(e=>e.length>=3).length>=2||Hn.test(e)?!0:(Bn(e)>4.9&&!t&&r.length,!1)}function Kn(e){let t=e.getParent();if(!t)return!1;let n=t.asKind(d.PropertyAssignment);if(n)return In.has(n.getName());let r=t.asKind(d.VariableDeclaration);return r?In.has(r.getName()):!1}const qn={meta:{id:`security/no-hardcoded-secrets`,category:`security`,severity:`error`,description:`Detect hardcoded secrets, API keys, and tokens in source code`,help:`Move secrets to environment variables and access them via ConfigService.`},check(e){let t=e.sourceFile.getDescendantsOfKind(d.StringLiteral);for(let n of t){let t=n.getLiteralValue();if(!(t.length<16)&&n.getParent()?.getKind()!==d.ImportDeclaration){for(let{pattern:r,name:i}of Mn)if(r.test(t)){if(i===`Base64 key`&&(zn(t)||Kn(n)||Gn(t)))break;e.report({filePath:e.filePath,message:`Possible hardcoded ${i} detected.`,help:this.meta.help,line:n.getStartLineNumber(),column:1});break}}}let n=e.sourceFile.getDescendantsOfKind(d.VariableDeclaration);for(let t of n){let n=t.getName(),r=t.getInitializer();!r||r.getKind()!==d.StringLiteral||Rn(n)&&Ln(r.getText().slice(1,-1))&&e.report({filePath:e.filePath,message:`Variable '${n}' appears to contain a hardcoded secret.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}let r=e.sourceFile.getDescendantsOfKind(d.PropertyAssignment);for(let t of r){let n=t.getName(),r=t.getInitializer();!r||r.getKind()!==d.StringLiteral||Rn(n)&&Ln(r.getText().slice(1,-1))&&e.report({filePath:e.filePath,message:`Property '${n}' appears to contain a hardcoded secret.`,help:this.meta.help,line:t.getStartLineNumber(),column:1})}}},Jn=RegExp(`(?:^|[^a-zA-Z])\\w*(?:${[`Entity`,`Model`].join(`|`)})(?:[^a-zA-Z]|$)`),Yn={meta:{id:`security/no-raw-entity-in-response`,category:`security`,severity:`warning`,description:`Returning ORM entities directly from controllers can leak internal fields like passwords or IDs`,help:`Map entities to DTOs or use class-transformer's @Exclude()/@Expose() decorators before returning.`},check(e){for(let t of e.sourceFile.getClasses())if(T(t))for(let n of t.getMethods()){if(!E(n))continue;let t=n.getReturnType().getText();Jn.test(t)&&!t.includes(`DTO`)&&!t.includes(`Dto`)&&!t.includes(`Response`)&&e.report({filePath:e.filePath,message:`Controller method '${n.getName()}' returns a raw entity type. This may leak internal fields.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},Xn={meta:{id:`security/no-synchronize-in-production`,category:`security`,severity:`error`,description:`TypeORM synchronize: true auto-syncs schema and can drop columns or tables in production`,help:`Set synchronize: false and use migrations for production schema changes.`},check(e){let t=e.sourceFile.getDescendantsOfKind(d.PropertyAssignment);for(let n of t){if(n.getName()!==`synchronize`)continue;let t=n.getInitializer();t&&t.getText()===`true`&&e.report({filePath:e.filePath,message:`TypeORM 'synchronize: true' can auto-drop columns and tables in production.`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},Zn=new Set([`md5`,`sha1`]),Qn={meta:{id:`security/no-weak-crypto`,category:`security`,severity:`warning`,description:`Weak hashing algorithms (MD5, SHA1) should not be used for security purposes`,help:`Use a stronger algorithm like SHA-256 or bcrypt for password hashing.`},check(e){let t=e.sourceFile.getDescendantsOfKind(d.CallExpression);for(let n of t){if(!n.getExpression().getText().endsWith(`createHash`))continue;let t=n.getArguments();if(t.length===0)continue;let r=t[0];if(r.getKind()!==d.StringLiteral)continue;let i=r.getText().slice(1,-1).toLowerCase();Zn.has(i)&&e.report({filePath:e.filePath,message:`Weak hashing algorithm '${i}' used in createHash().`,help:this.meta.help,line:n.getStartLineNumber(),column:1})}}},$n=new Set([`Public`,`AllowAnonymous`,`SkipAuth`,`IsPublic`]),er=[Ne,yt,lt,mt,at,xt,St,wt,De,et,qt,Xt,Ft,Pt,Bt,Wt,zt,Ht,kt,Nt,Ut,Jt,Rt,Kt,Tt,ln,jt,Qt,Vt,Dt,qn,On,Qn,kn,En,jn,Dn,Xn,Yn,{meta:{id:`security/require-guards-on-endpoints`,category:`security`,severity:`warning`,description:`Controller endpoints should be protected by @UseGuards() at class or method level`,help:`Add @UseGuards(AuthGuard) to the controller class or individual route handlers, or mark routes as @Public(). If you use a global guard via APP_GUARD, you can disable this rule.`},check(e){for(let t of e.sourceFile.getClasses())if(T(t)&&t.getDecorator(`UseGuards`)===void 0&&!t.getDecorators().some(e=>$n.has(e.getName())))for(let n of t.getMethods())E(n)&&n.getDecorator(`UseGuards`)===void 0&&(n.getDecorators().some(e=>$n.has(e.getName()))||e.report({filePath:e.filePath,message:`Endpoint '${n.getName()}' has no @UseGuards() at class or method level.`,help:this.meta.help,line:n.getStartLineNumber(),column:1}))}},gn,dn,fn,mn,bn,_n,pn,Sn,Tn,xn];function tr(e){return e.meta.scope===`project`}function nr(e){return e.meta.scope===`schema`}function rr(e,t,n){if(t.length===0)return e;let r=new Set(e.map(e=>e.meta.id)),i=[...e];for(let e of t){if(r.has(e.meta.id)){n.push(`Custom rule "${e.meta.id}" conflicts with a built-in rule and was skipped`);continue}i.push(e)}return i}function ir(e,t){return t.filter(t=>{let n=e.rules?.[t.meta.id];return!(n===!1||typeof n==`object`&&n.enabled===!1||e.categories?.[t.meta.category]===!1)})}function ar(e){let t=[],n=[],r=[];for(let i of e)nr(i)?r.push(i):tr(i)?n.push(i):t.push(i);return{fileRules:t,projectRules:n,schemaRules:r}}const or=new Set([`security`,`performance`,`correctness`,`architecture`]),sr=new Set([`error`,`warning`,`info`]),cr=new Set([`file`,`project`]),lr=`custom/`;function ur(e){if(typeof e!=`object`||!e)return!1;let t=e;if(typeof t.check!=`function`||typeof t.meta!=`object`||t.meta===null)return!1;let n=t.meta;return!(typeof n.id!=`string`||n.id.trim()===``||typeof n.description!=`string`||typeof n.help!=`string`||!or.has(n.category)||!sr.has(n.severity)||n.scope!==void 0&&!cr.has(n.scope))}function dr(e){return e.meta.id.startsWith(lr)?e:{...e,meta:{...e.meta,id:`${lr}${e.meta.id}`}}}async function fr(e,t){let n=[],r=[],i=s(t,e);if(!p(i))return r.push(`Custom rules directory not found: ${i}`),{rules:n,warnings:r};if(!g(i).isDirectory())return r.push(`Custom rules path is not a directory: ${i}`),{rules:n,warnings:r};let a;try{a=h(i)}catch(e){return r.push(`Failed to read custom rules directory: ${e instanceof Error?e.message:String(e)}`),{rules:n,warnings:r}}let o=a.filter(e=>e.endsWith(`.ts`));if(o.length===0)return r.push(`No rule files (.ts) found in: ${i}`),{rules:n,warnings:r};let c=_(i,{interopDefault:!0});for(let e of o){let t=s(i,e),a;try{a=await c.import(t)}catch(t){r.push(`Failed to load custom rule file "${e}": ${t instanceof Error?t.message:String(t)}`);continue}let o=!1;for(let[t,i]of Object.entries(a))ur(i)?(n.push(dr(i)),o=!0):t!==`__esModule`&&typeof i==`object`&&i&&`meta`in i&&r.push(`Invalid rule export "${t}" in "${e}": missing or invalid required fields (check, meta.id, meta.description, meta.help, meta.category, meta.severity)`);!o&&Object.keys(a).length>0&&(Object.values(a).some(e=>typeof e==`object`&&!!e&&(`meta`in e||`check`in e))||r.push(`No valid rule exports found in "${e}"`))}return{rules:n,warnings:r}}function pr(e,t){return e.customRulesDir?fr(e.customRulesDir,t):Promise.resolve({rules:[],warnings:[]})}async function mr(e,t){let n=await Se(e,t),{rules:r,warnings:i}=await pr(n,e),a=rr(er,r,i),{fileRules:o,projectRules:s,schemaRules:c}=ar(ir(n,a));return{combinedRules:a,config:n,customRuleWarnings:i,fileRules:o,projectRules:s,schemaRules:c}}async function hr(e,t={}){return(await c(t.include??S.include,{cwd:e,absolute:!0,ignore:t.exclude??S.exclude})).sort()}async function gr(e,t,n={}){let r=await Promise.all([...t.projects.entries()].map(async([t,r])=>[t,await hr(a(e,r),n)])),i=new Map;for(let[e,t]of r)i.set(e,t);return i}function _r(e){let t=new u({compilerOptions:{strict:!0,target:99,module:99,skipFileDependencyResolution:!0},skipAddingFilesFromTsConfig:!0});for(let n of e)t.addSourceFileAtPath(n);return t}const vr=/import\([^)]+\)\.(\w+)/,yr=/^(\w+)</;function br(e,t){let n=[];for(let r of e.getClasses()){if(!r.getDecorator(`Injectable`))continue;let e=r.getName();if(!e)continue;let i=r.getConstructors()[0],a=i?i.getParameters().map(e=>{let t=e.getTypeNode();return M(t?t.getText():e.getType().getText())}):[],o=r.getMethods().filter(e=>{let t=e.getScope();return!t||t===`public`}).length;n.push({name:e,filePath:t,classDeclaration:r,dependencies:a,publicMethodCount:o})}return n}function xr(e,t){let n=new Map;for(let r of t){let t=e.getSourceFile(r);if(t)for(let e of br(t,r))n.set(e.name,e)}return n}function M(e){let t=e.match(vr);if(t)return t[1];let n=e.match(yr);return n?n[1]:e}const N=/^['"`]|['"`]$/g,Sr=/\/+/g,Cr=/\/$/,wr=new Set([`Query`,`Mutation`,`Subscription`]),Tr=new Set([`ApiOperation`,`ApiParam`,`ApiQuery`,`ApiResponse`,`ApiBody`]),Er=new Set([`map`,`forEach`,`filter`,`find`,`some`,`every`,`flatMap`,`reduce`]);var Dr=class{scanResults=new Map;injectionMaps=new Map;methodLookups=new Map;getScan(e){return this.scanResults.get(e)}setScan(e,t){this.scanResults.set(e,t)}getInjMap(e){return this.injectionMaps.get(e)}setInjMap(e,t){this.injectionMaps.set(e,t)}getMethod(e){return this.methodLookups.has(e)?this.methodLookups.get(e):void 0}hasMethod(e){return this.methodLookups.has(e)}setMethod(e,t){this.methodLookups.set(e,t)}};function P(e){let t=e.replace(/\s+/g,` `).trim();return t.length>50?`${t.slice(0,50)}\u2026`:t}function F(e,t){let n={isConditional:!1,conditionText:null,branchKind:null,statementLine:null},r=e;for(;r&&r!==t;){let e=r.getParent();if(!e||e===t)break;let n=e.getKind();if(n===d.IfStatement){let t=e.asKindOrThrow(d.IfStatement);if(r===t.getThenStatement()){let n=e.getParent();if(n&&n.getKind()===d.IfStatement){let r=n.asKindOrThrow(d.IfStatement);if(e===r.getElseStatement())return{isConditional:!0,conditionText:P(t.getExpression().getText()),branchKind:`else-if`,statementLine:r.getStartLineNumber()}}return{isConditional:!0,conditionText:P(t.getExpression().getText()),branchKind:`if`,statementLine:t.getStartLineNumber()}}if(r===t.getElseStatement())return{isConditional:!0,conditionText:P(t.getExpression().getText()),branchKind:`else`,statementLine:t.getStartLineNumber()}}if(n===d.ConditionalExpression){let t=e.asKindOrThrow(d.ConditionalExpression);if(r===t.getWhenTrue())return{isConditional:!0,conditionText:P(t.getCondition().getText()),branchKind:`ternary-true`,statementLine:t.getStartLineNumber()};if(r===t.getWhenFalse())return{isConditional:!0,conditionText:P(t.getCondition().getText()),branchKind:`ternary-false`,statementLine:t.getStartLineNumber()}}let i=r.getKind();if(i===d.CaseClause){let e=r.asKindOrThrow(d.CaseClause),t=r.getParentOrThrow().getParentOrThrow();return{isConditional:!0,conditionText:P(e.getExpression().getText()),branchKind:`case`,statementLine:t.getStartLineNumber()}}if(i===d.DefaultClause)return{isConditional:!0,conditionText:null,branchKind:`default`,statementLine:r.getParentOrThrow().getParentOrThrow().getStartLineNumber()};if(i===d.CatchClause)return{isConditional:!0,conditionText:null,branchKind:`catch`,statementLine:r.getParentOrThrow().getStartLineNumber()};r=e}return n}const Or=new Map([[d.ForStatement,`for`],[d.ForOfStatement,`for-of`],[d.ForInStatement,`for-in`],[d.WhileStatement,`while`],[d.DoStatement,`do-while`]]);function I(e,t){let n={iterationKind:null,iterationLabel:null},r=e;for(;r&&r!==t;){let e=r.getParent();if(!e||e===t)break;let n=e.getKind(),i=Or.get(n);if(i){let t=!1;if(n===d.ForStatement){let n=e.asKindOrThrow(d.ForStatement);t=r!==n.getInitializer()&&r!==n.getCondition()&&r!==n.getIncrementor()&&r===n.getStatement()}else n===d.ForOfStatement?t=r===e.asKindOrThrow(d.ForOfStatement).getStatement():n===d.ForInStatement?t=r===e.asKindOrThrow(d.ForInStatement).getStatement():n===d.WhileStatement?t=r===e.asKindOrThrow(d.WhileStatement).getStatement():n===d.DoStatement&&(t=r===e.asKindOrThrow(d.DoStatement).getStatement());if(t)return{iterationKind:`loop`,iterationLabel:i}}let a=r.getKind();if(a===d.ArrowFunction||a===d.FunctionExpression){if(n===d.CallExpression){let t=e.asKindOrThrow(d.CallExpression);if(t.getArguments().some(e=>e===r)){let e=t.getExpression();if(e.getKind()===d.PropertyAccessExpression){let t=e.asKindOrThrow(d.PropertyAccessExpression).getName();if(Er.has(t))return{iterationKind:`callback`,iterationLabel:t}}}}break}if(n===d.ArrayLiteralExpression){let t=e.getParent();if(t&&t.getKind()===d.CallExpression){let n=t.asKindOrThrow(d.CallExpression),r=n.getArguments();if(r.length>0&&r[0]===e){let e=n.getExpression();if(e.getKind()===d.PropertyAccessExpression){let t=e.asKindOrThrow(d.PropertyAccessExpression);if(t.getName()===`all`&&t.getExpression().getText().endsWith(`Promise`))return{iterationKind:`concurrent`,iterationLabel:`all`}}}}}r=e}return n}function L(e){let t=e;for(;t;){let e=t.getKind();if(e===d.ExpressionStatement||e===d.VariableStatement||e===d.ReturnStatement||e===d.ThrowStatement)break;t=t.getParent()}if(!t)return null;let n=t.getSourceFile(),r=t.getFullStart(),i=t.getStart(),a=n.getFullText().slice(r,i).split(`
|
|
5
|
+
`);for(let e=a.length-1;e>=0;e--){let t=a[e].trim();if(t.startsWith(`//`))return t.slice(2).trim();if(t.length>0)break}return null}function kr(e){let t=e.asKindOrThrow(d.ThrowStatement).getExpression();return t&&t.getKind()===d.NewExpression?M(t.asKindOrThrow(d.NewExpression).getExpression().getText()):`Error`}function Ar(e){let t=e.asKindOrThrow(d.ThrowStatement).getExpression();if(!t||t.getKind()!==d.NewExpression)return null;let n=t.asKindOrThrow(d.NewExpression).getArguments();if(n.length===0)return null;let r=n[0],i=r.getKind(),a;if(i===d.StringLiteral||i===d.NoSubstitutionTemplateLiteral)a=r.asKindOrThrow(i).getLiteralValue();else if(i===d.TemplateExpression){let e=r.getText();a=e.startsWith("`")?e.slice(1,-1):e}else a=r.getText();return a.length>80?`${a.slice(0,80)}\u2026`:a}function jr(e){let t=e.getParent();for(;t;){let e=t.getKind();if(e===d.AwaitExpression||e===d.ParenthesizedExpression||e===d.AsExpression||e===d.NonNullExpression){t=t.getParent();continue}if(e===d.VariableDeclaration){let e=t.asKindOrThrow(d.VariableDeclaration).getNameNode();return e.getKind()===d.Identifier?e.getText():null}return null}return null}function Mr(e,t){let n;try{n=e.getBaseClass()}catch{}if(!n&&t){let r=e.getExtends();if(r){let e=M(r.getExpression().getText()),i=t.get(e);i&&(n=i.classDeclaration)}}return n}function R(e,t,n,r){let i=`${e.getName()??``}.${t}`;if(r?.hasMethod(i))return r.getMethod(i);let a=e,o=new Set;for(;a;){let e=a.getName();if(e&&o.has(e))break;e&&o.add(e);let s=a.getInstanceMethod(t);if(s)return r?.setMethod(i,s),s;a=Mr(a,n)}r?.setMethod(i,void 0)}function Nr(e){let t=e.getDecorator(`Controller`);if(!t)return``;let n=t.getArguments();if(n.length===0)return``;let r=n[0];if(r.getKind()===d.ObjectLiteralExpression){let e=r.asKindOrThrow(d.ObjectLiteralExpression).getProperty(`path`);if(!e)return``;let t=e.asKind(d.PropertyAssignment);if(!t)return``;let n=t.getInitializer();return n?n.getText().replace(N,``):``}return r.getText().replace(N,``)}function Pr(e){for(let t of e.getDecorators()){let e=t.getName();if(!C.has(e))continue;let n=t.getArguments(),r=n.length>0?n[0].getText().replace(N,``):``;return{httpMethod:e.toUpperCase(),path:r}}}function Fr(e,t){return`/${[e,t].filter(Boolean).join(`/`)}`.replace(Sr,`/`).replace(Cr,``)||`/`}function z(e,t){let n=e.asKind(d.ObjectLiteralExpression);if(!n)return null;let r=n.getProperty(t);if(!r)return null;let i=r.asKind(d.PropertyAssignment);if(!i)return null;let a=i.getInitializer();return a?a.getText().replace(N,``):null}function Ir(e,t){let n=z(e,t);if(n===null)return null;let r=Number(n);return Number.isNaN(r)?null:r}function Lr(e,t,n){let r=z(e,t);return r===null?n:r===`true`}function Rr(e){let t=null,n=null,r=[],i=[],a=[],o=null,s=!1;for(let c of e.getDecorators()){let e=c.getName();if(!Tr.has(e))continue;s=!0;let l=c.getArguments();if(l.length===0)continue;let u=l[0];if(e===`ApiOperation`)t=z(u,`summary`),n=z(u,`description`);else if(e===`ApiParam`){let e=z(u,`name`);e&&r.push({description:z(u,`description`),name:e,required:Lr(u,`required`,!0),type:z(u,`type`)})}else if(e===`ApiQuery`){let e=z(u,`name`);e&&i.push({description:z(u,`description`),name:e,required:Lr(u,`required`,!1),type:z(u,`type`)})}else if(e===`ApiResponse`){let e=Ir(u,`status`)??200,t=z(u,`type`);t?.startsWith(`[`)&&t.endsWith(`]`)&&(t=`${t.slice(1,-1)}[]`),a.push({description:z(u,`description`),status:e,type:t})}else e===`ApiBody`&&(o={description:z(u,`description`),type:z(u,`type`)})}if(!o){for(let t of e.getParameters())if(t.getDecorators().some(e=>e.getName()===`Body`)){let e=t.getTypeNode();e&&(o={description:null,type:e.getText()},s=!0);break}}return s?{body:o,description:n,params:r,queryParams:i,responses:a,summary:t}:null}const zr=/^(?:Promise|Observable)<(.+)>$/;function B(e){let t=e.getReturnTypeNode();if(!t)return null;let n=t.getText().trim(),r=zr.exec(n);return r&&(n=r[1]),n===`void`||n===`any`||n===`unknown`?null:n}function Br(e){return e.getParameters().filter(e=>e.getName()!==`this`).map(e=>({name:e.getName(),type:e.getTypeNode()?.getText()??null}))}const Vr=/=>\s*\{[^}]*\}/g,Hr=/\(([^)]{20,})\)\s*=>/g,Ur=/\s+/g;function Wr(e){let t=e.replace(Ur,` `).trim();return t=t.replace(Vr,`=> …`),t=t.replace(Hr,`(…) =>`),t.length>50&&(t=`${t.slice(0,47)}…`),t}function Gr(e,t){let n=e.getKind();if(n!==d.VariableStatement&&n!==d.ExpressionStatement)return!1;let r=[...e.getDescendantsOfKind(d.CallExpression),...e.getDescendantsOfKind(d.NewExpression)];if(r.length===0)return!1;for(let e of r)if(t.has(e.getStart()))return!1;for(let e of r){let t=e.getText();if(t.startsWith(`console.`)||t.startsWith(`this.logger.`))return!1}return!0}function Kr(e){if(e.getKind()===d.VariableStatement){let t=e.asKindOrThrow(d.VariableStatement).getDeclarationList().getDeclarations();if(t.length===0)return null;let n=t[0],r=n.getNameNode().getText(),i=n.getInitializer();return i?{assignedTo:r,text:`${r} = ${Wr(i.getText())}`}:null}return e.getKind()===d.ExpressionStatement?{assignedTo:null,text:Wr(e.asKindOrThrow(d.ExpressionStatement).getExpression().getText())}:null}function qr(e,t,n){let r=e.getName()??``;if(n){let e=n.getInjMap(r);if(e)return e}let i=new Map,a=e,o=new Set;for(;a;){let e=a.getName();if(e&&o.has(e))break;e&&o.add(e);let n=a.getConstructors()[0];if(n){for(let e of n.getParameters()){let t=e.getName();if(!i.has(t)){let n=e.getTypeNode(),r=n?n.getText():e.getType().getText();i.set(t,M(r))}}break}a=Mr(a,t)}for(let t of e.getProperties())if(t.getDecorator(`Inject`)){let e=t.getName();if(!i.has(e)){let n=t.getTypeNode();n&&i.set(e,M(n.getText()))}}return n&&n.setInjMap(r,i),i}function Jr(e,t){for(let n of e){if(!n.assignedTo)continue;let e=RegExp(`\\b${n.assignedTo}\\b`);for(let r of t)if(!r.merged&&!(r.order<=n.order)&&r.conditional&&r.conditionText&&e.test(r.conditionText)){n.guardThrow={branchKind:r.branchKind,callSiteLine:r.callSiteLine,className:r.exceptionClassName,conditionText:r.conditionText,message:r.message},r.merged=!0;break}}let n=t.filter(e=>!e.merged);t.length=0;for(let e of n)t.push(e)}function V(e,t,n,r,i){let a=`${n?.getName()??``}::${e.getName()}`;if(!r&&i){let e=i.getScan(a);if(e)return e}let o={deps:[],sameClassCalls:[],steps:[],throws:[]},s=e.getBody();if(!s)return o;let c=r??new Set,l=e.getName();if(c.has(l))return o;c.add(l);let u=new Map;for(let e of s.getDescendantsOfKind(d.VariableDeclaration)){let n=e.getInitializer();if(n&&n.getKind()===d.PropertyAccessExpression){let r=n.asKindOrThrow(d.PropertyAccessExpression);if(r.getExpression().getKind()===d.ThisKeyword){let n=r.getName();t.has(n)&&u.set(e.getName(),n)}}}let f=[],p=[],m=[],h=0,g=s.getDescendantsOfKind(d.CallExpression),_=s.getDescendantsOfKind(d.ThrowStatement),v=[...g.map(e=>({kind:`call`,node:e})),..._.map(e=>({kind:`throw`,node:e}))];v.sort((e,t)=>e.node.getStart()-t.node.getStart());for(let e of v){if(e.kind===`throw`){let t=F(e.node,s),n=I(e.node,s);p.push({branchGroupId:t.statementLine?`L${t.statementLine}`:null,branchKind:t.branchKind,callSiteLine:e.node.getStartLineNumber(),comment:L(e.node),conditional:t.isConditional,conditionText:t.conditionText,exceptionClassName:kr(e.node),iterationKind:n.iterationKind,iterationLabel:n.iterationLabel,message:Ar(e.node),order:h++});continue}let r=e.node,i=r.getExpression();if(i.getKind()!==d.PropertyAccessExpression)continue;let a=i.asKindOrThrow(d.PropertyAccessExpression),o=a.getName(),l=a.getExpression(),g;if(l.getKind()===d.PropertyAccessExpression){let e=l.asKindOrThrow(d.PropertyAccessExpression);if(e.getExpression().getKind()===d.ThisKeyword){let n=e.getName();t.has(n)&&(g=n)}}if(!g&&l.getKind()===d.Identifier){let e=l.getText(),t=u.get(e);t&&(g=t)}if(g){let e=F(r,s),t=I(r,s);m.push({assignedTo:jr(r),paramName:g,methodName:o,order:h++,callSiteLine:r.getStartLineNumber(),comment:L(r),condInfo:e,iterInfo:t,guardThrow:null});continue}if(l.getKind()===d.ThisKeyword&&n){let e=n.getInstanceMethod(o);if(e&&!c.has(o)){let i=F(r,s),a=I(r,s),l=V(e,t,n,new Set(c));f.push({assignedTo:jr(r),branchGroupId:i.statementLine?`L${i.statementLine}`:null,branchKind:i.branchKind,callSiteLine:r.getStartLineNumber(),childResult:l,comment:L(r),conditional:i.isConditional,conditionText:i.conditionText,iterationKind:a.iterationKind,iterationLabel:a.iterationLabel,methodName:o,order:h++})}}}Jr(m,p);let y=[];if(s.getKind()===d.Block){let e=new Set;for(let t of m)for(let n of g)n.getStartLineNumber()===t.callSiteLine&&e.add(n.getStart());for(let t of p)for(let n of _)n.getStartLineNumber()===t.callSiteLine&&e.add(n.getStart());for(let t of f)for(let n of g)n.getStartLineNumber()===t.callSiteLine&&e.add(n.getStart());let t=s.asKindOrThrow(d.Block).getStatements(),n=[],r=()=>{if(n.length===0)return;let e=n[0].stmt,t=F(e,s),r=I(e,s);y.push({branchGroupId:t.statementLine?`L${t.statementLine}`:null,branchKind:t.branchKind,callSiteLine:e.getStartLineNumber(),comment:L(e),conditional:t.isConditional,conditionText:t.conditionText,iterationKind:r.iterationKind,iterationLabel:r.iterationLabel,order:0,statements:n.map(e=>e.info)}),n=[]};for(let i of t){let t=i.getStart(),a=i.getEnd(),o=!1;for(let n of e)if(n>=t&&n<=a){o=!0;break}if(o){r();continue}if(Gr(i,e)){let e=Kr(i);if(e){n.push({info:e,stmt:i});continue}}r()}r()}if(y.length>0){let e=[];for(let t of m)e.push({kind:`call`,item:t});for(let t of p)e.push({kind:`throw`,item:t});for(let t of f)e.push({kind:`scc`,item:t});for(let t of y)e.push({kind:`step`,item:t});e.sort((e,t)=>e.item.callSiteLine-t.item.callSiteLine);let t=0;for(let n of e)n.item.order=t++}let b=[],x=new Map;for(let e of m){let n=t.get(e.paramName);x.has(n)||x.set(n,[]);let r=e.condInfo.isConditional;x.get(n).push({assignedTo:e.assignedTo,branchGroupId:r&&e.condInfo.statementLine?`L${e.condInfo.statementLine}`:null,branchKind:r?e.condInfo.branchKind:null,callSiteLine:e.callSiteLine,comment:e.comment,conditional:r,conditionText:r?e.condInfo.conditionText:null,guardThrow:e.guardThrow,iterationKind:e.iterInfo.iterationKind,iterationLabel:e.iterInfo.iterationLabel,name:e.methodName,order:e.order})}for(let[e,t]of x)t.sort((e,t)=>e.order-t.order),b.push({className:e,methodsCalled:t});let ee={deps:b,sameClassCalls:f,steps:y,throws:p};return!r&&i&&i.setScan(a,ee),ee}function H(e){return e.endsWith(`Repository`)?`repository`:e.endsWith(`Guard`)?`guard`:e.endsWith(`Interceptor`)?`interceptor`:e.endsWith(`Pipe`)?`pipe`:e.endsWith(`Filter`)?`filter`:e.endsWith(`Gateway`)?`gateway`:`service`}function U(e,t,n,r,i){let a=[],o=new Set,s=new Map,c=e.deps,l=[],u=[];for(let e of c)if(e.methodsCalled.length===0)u.push(e);else for(let t of e.methodsCalled)l.push({className:e.className,mc:t,dep:e});l.sort((e,t)=>e.mc.order-t.mc.order);for(let e of u){if(r.has(e.className)||o.has(e.className))continue;o.add(e.className),r.add(e.className);let t=n.get(e.className),s=t?t.dependencies.map(e=>({className:e,methodsCalled:[]})):[];a.push({assignedTo:null,branchGroupId:null,branchKind:null,callSiteLine:0,className:e.className,comment:null,conditional:!1,conditionText:null,dependencies:U({deps:s,sameClassCalls:[],steps:[],throws:[]},e.className,n,new Set(r),i),endLine:0,filePath:t?.filePath??``,guardThrow:null,iterationKind:null,iterationLabel:null,line:0,methodName:null,order:0,parameters:[],returnType:null,stepStatements:[],throwMessage:null,totalMethods:t?.publicMethodCount??0,type:H(e.className)})}let d=new Map;for(let{className:e,mc:t}of l)d.has(e)||d.set(e,new Set),d.get(e).add(t.name);for(let{className:e,mc:t}of l){if(r.has(e))continue;let c=n.get(e),l=!o.has(e);l&&o.add(e);let u=[];if(l&&c){let t=new Set(r);t.add(e);let a=qr(c.classDeclaration,n,i),o=[],l=0,f=[],p=[],m=d.get(e)??new Set;for(let e of m){let t=R(c.classDeclaration,e,n,i);if(!t)continue;let r=V(t,a,c.classDeclaration,void 0,i),s=[];for(let e of r.deps)for(let t of e.methodsCalled)s.push({kind:`dep`,depClassName:e.className,m:t});for(let e of r.throws)s.push({kind:`throw`,t:e});for(let e of r.sameClassCalls)s.push({kind:`scc`,scc:e});function u(e){return e.kind===`dep`?e.m.order:e.kind===`throw`?e.t.order:e.scc.order}s.sort((e,t)=>u(e)-u(t));for(let e of s)e.kind===`dep`?o.push({assignedTo:e.m.assignedTo,depClassName:e.depClassName,methodName:e.m.name,order:l++,callSiteLine:e.m.callSiteLine,comment:e.m.comment,conditional:e.m.conditional,branchKind:e.m.conditional?e.m.branchKind:null,conditionText:e.m.conditional?e.m.conditionText:null,branchGroupId:e.m.conditional?e.m.branchGroupId:null,guardThrow:e.m.guardThrow,iterationKind:e.m.iterationKind,iterationLabel:e.m.iterationLabel}):e.kind===`throw`?p.push({...e.t,order:l++}):f.push(e.scc)}Jr(o,p);let h=new Map;for(let e of o)h.has(e.depClassName)||h.set(e.depClassName,[]),h.get(e.depClassName).push({assignedTo:e.assignedTo,branchGroupId:e.branchGroupId,branchKind:e.branchKind,callSiteLine:e.callSiteLine,comment:e.comment,conditional:e.conditional,conditionText:e.conditionText,guardThrow:e.guardThrow,iterationKind:e.iterationKind,iterationLabel:e.iterationLabel,name:e.methodName,order:e.order});let g=[];for(let[e,t]of h)t.sort((e,t)=>e.order-t.order),g.push({className:e,methodsCalled:t});u=U({deps:g,sameClassCalls:f,steps:[],throws:p},e,n,t,i),s.set(e,u)}else l||(u=s.get(e)??[]);let f=0,p=0,m=null,h=[];if(c){let e=R(c.classDeclaration,t.name,n,i);e&&(f=e.getStartLineNumber(),p=e.getEndLineNumber(),m=B(e),h=Br(e))}a.push({assignedTo:t.assignedTo,branchGroupId:t.branchGroupId,branchKind:t.branchKind,callSiteLine:t.callSiteLine,className:e,comment:t.comment,conditional:t.conditional,conditionText:t.conditionText,dependencies:u,endLine:p,filePath:c?.filePath??``,guardThrow:t.guardThrow,iterationKind:t.iterationKind,iterationLabel:t.iterationLabel,line:f,methodName:t.name,order:t.order,parameters:h,returnType:m,stepStatements:[],throwMessage:null,totalMethods:c?.publicMethodCount??0,type:H(e)})}let f=n.get(t);for(let o of e.sameClassCalls){let e=0,s=0,c=null,l=[];if(f){let t=R(f.classDeclaration,o.methodName,n,i);t&&(e=t.getStartLineNumber(),s=t.getEndLineNumber(),c=B(t),l=Br(t))}let u=U(o.childResult,t,n,new Set(r),i);a.push({assignedTo:o.assignedTo,branchGroupId:o.branchGroupId,branchKind:o.branchKind,callSiteLine:o.callSiteLine,className:t,comment:o.comment,conditional:o.conditional,conditionText:o.conditionText,dependencies:u,endLine:s,filePath:f?.filePath??``,guardThrow:null,iterationKind:o.iterationKind,iterationLabel:o.iterationLabel,line:e,methodName:o.methodName,order:o.order,parameters:l,returnType:c,stepStatements:[],throwMessage:null,totalMethods:f?.publicMethodCount??0,type:H(t)})}for(let t of e.throws)a.push({assignedTo:null,branchGroupId:t.branchGroupId,branchKind:t.branchKind,callSiteLine:t.callSiteLine,className:t.exceptionClassName,comment:t.comment,conditional:t.conditional,conditionText:t.conditionText,dependencies:[],endLine:t.callSiteLine,filePath:f?.filePath??``,guardThrow:null,iterationKind:t.iterationKind,iterationLabel:t.iterationLabel,line:t.callSiteLine,methodName:null,order:t.order,parameters:[],returnType:null,stepStatements:[],throwMessage:t.message,totalMethods:0,type:`throw`});for(let t of e.steps)a.push({assignedTo:null,branchGroupId:t.branchGroupId,branchKind:t.branchKind,callSiteLine:t.callSiteLine,className:`local`,comment:t.comment,conditional:t.conditional,conditionText:t.conditionText,dependencies:[],endLine:t.callSiteLine,filePath:f?.filePath??``,guardThrow:null,iterationKind:t.iterationKind,iterationLabel:t.iterationLabel,line:t.callSiteLine,methodName:null,order:t.order,parameters:[],returnType:null,stepStatements:t.statements,throwMessage:null,totalMethods:0,type:`step`});return a.sort((e,t)=>e.order-t.order),a}function Yr(e){for(let t of e.getDecorators()){let n=t.getName();if(wr.has(n))return{httpMethod:n.toUpperCase(),path:e.getName()}}}function Xr(e,t,n,r){let i=[];for(let a of e.getClasses()){let e=T(a),o=w(a,`Resolver`);if(!(e||o))continue;let s=e?Nr(a):``,c=a.getName()??(e?`AnonymousController`:`AnonymousResolver`),l=qr(a,n,r);for(let o of a.getMethods()){let u=e?Pr(o):Yr(o);if(!u)continue;let d=e?Fr(s,u.path):u.path,f=U(V(o,l,a,void 0,r),c,n,new Set,r),p=Rr(o),m=B(o);i.push({controllerClass:c,dependencies:f,endLine:o.getEndLineNumber(),filePath:t,handlerMethod:o.getName(),httpMethod:u.httpMethod,line:o.getStartLineNumber(),returnType:m,routePath:d,swagger:p})}}return i}function Zr(e,t,n){let r=[],i=new Dr;for(let a of t){let t=e.getSourceFile(a);t&&r.push(...Xr(t,a,n,i))}return{endpoints:r}}const Qr=new Set([`pgTable`,`mysqlTable`,`sqliteTable`]),$r=new Set([`serial`,`bigserial`,`smallserial`]),ei=/=>\s*(\w+)/;function ti(e){let t={type:`unknown`,isPrimary:!1,isNullable:!0,isGenerated:!1,isUnique:!1};function n(e){if(e.getKind()===d.CallExpression){let r=e.asKindOrThrow(d.CallExpression),i=r.getExpression();if(i.getKind()===d.PropertyAccessExpression){let e=i.asKindOrThrow(d.PropertyAccessExpression);switch(e.getName()){case`primaryKey`:t.isPrimary=!0;break;case`notNull`:t.isNullable=!1;break;case`unique`:t.isUnique=!0;break;case`default`:{let e=r.getArguments();e.length>0&&(t.defaultValue=e[0].getText().replace(/['"]/g,``));break}case`defaultNow`:t.defaultValue=`now()`;break;case`generatedAlwaysAsIdentity`:case`autoincrement`:t.isGenerated=!0;break;case`references`:{let e=r.getArguments();if(e.length>0){let n=e[0].getText(),r=ei.exec(n);if(r&&(t.reference={toEntity:r[1]}),e.length>1){let n=e[1];if(n.getKind()===d.ObjectLiteralExpression){let e=n.asKindOrThrow(d.ObjectLiteralExpression);for(let n of e.getProperties())if(n.getKind()===d.PropertyAssignment){let e=n.asKindOrThrow(d.PropertyAssignment);if(e.getName()===`onDelete`){let n=e.getInitializer()?.getText();n&&(t.reference.onDelete=n.replace(/['"]/g,``))}}}}}break}default:break}n(e.getExpression())}else if(i.getKind()===d.Identifier){let e=i.getText();t.type=e,$r.has(e)&&(t.isGenerated=!0)}}}return n(e),t}function ni(e){let t=[];for(let n of e.getProperties()){if(n.getKind()!==d.PropertyAssignment)continue;let e=n.asKindOrThrow(d.PropertyAssignment),r=e.getName(),i=e.getInitializer();if(!i)continue;let a=ti(i);t.push({name:r,type:a.type,isPrimary:a.isPrimary,isNullable:a.isNullable,isGenerated:a.isGenerated,isUnique:a.isUnique,defaultValue:a.defaultValue})}return t}function ri(e,t){let n=[];for(let r of e.getProperties()){if(r.getKind()!==d.PropertyAssignment)continue;let e=r.asKindOrThrow(d.PropertyAssignment),i=e.getName(),a=e.getInitializer();if(!a)continue;let o=ti(a);o.reference&&n.push({type:`many-to-one`,fromEntity:t,toEntity:o.reference.toEntity,propertyName:i,isNullable:o.isNullable,...o.reference.onDelete?{onDelete:o.reference.onDelete}:{}})}return n}function ii(e){let t=[],n=e.getDescendantsOfKind(d.CallExpression);for(let e of n){let n=e.getExpression();if(n.getKind()!==d.PropertyAccessExpression||n.asKindOrThrow(d.PropertyAccessExpression).getName()!==`on`)continue;let r=[];for(let t of e.getArguments())if(t.getKind()===d.PropertyAccessExpression){let e=t.asKindOrThrow(d.PropertyAccessExpression);r.push(e.getName())}if(r.length===0)continue;let i=e.getText().includes(`uniqueIndex`);t.push({columns:r,isUnique:i})}return t}function ai(e){let t=[],n=e.getFilePath();for(let r of e.getDescendantsOfKind(d.VariableDeclaration)){let e=r.getInitializer();if(!e||e.getKind()!==d.CallExpression)continue;let i=e.asKindOrThrow(d.CallExpression),a=i.getExpression();if(a.getKind()!==d.Identifier)continue;let o=a.getText();if(!Qr.has(o))continue;let s=i.getArguments();if(s.length<2)continue;let c=s[0],l=r.getName();c.getKind()===d.StringLiteral&&(l=c.asKindOrThrow(d.StringLiteral).getLiteralValue());let u=s[1];if(u.getKind()!==d.ObjectLiteralExpression)continue;let f=u.asKindOrThrow(d.ObjectLiteralExpression),p=r.getName(),m=ni(f),h=ri(f,p),g;if(s.length>=3&&(g=ii(s[2]),g))for(let e of g)for(let t of e.columns){let e=m.find(e=>e.name===t);e&&(e.hasIndex=!0)}t.push({name:p,tableName:l,filePath:n,columns:m,relations:h,indexes:g})}return t}const oi={supportsIncrementalUpdate:!0,extract(e,t){let n=[];for(let r of t){let t=e.getSourceFile(r);t&&n.push(...ai(t))}return n}},si=/^model\s+(\w+)\s*\{/,ci=/^enum\s+(\w+)\s*\{/,li=/^(\w+)\s+(\w+)(\?)?(\[\])?(.*)$/,ui=/@(\w+)(\((?:[^()]*|\([^()]*\))*\))?/g,di=/@default\(((?:[^()]*|\([^()]*\))*)\)/,fi=/^@@map\(\s*"([^"]+)"\s*\)/;function pi(e){let t=a(e,`prisma`,`schema.prisma`);if(p(t)){let n=a(e,`prisma`),r=h(n).filter(e=>e.endsWith(`.prisma`));return r.length>1?r.map(e=>a(n,e)):[t]}let n=a(e,`schema.prisma`);if(p(n))return[n];try{let t=a(e,`package.json`),n=JSON.parse(m(t,`utf-8`)).prisma?.schema;if(n){let t=a(e,n);if(p(t))return[t]}}catch{}return[]}function mi(e){let t=[],n=new Set;for(let r of e){let e;try{e=m(r,`utf-8`)}catch{continue}let i=e.split(`
|
|
6
|
+
`),a=null,o=[],s=[],c=[],l;for(let e of i){let i=e.trim(),u=si.exec(i);if(u){a={type:`model`,name:u[1]},o=[],s=[],c=[],l=void 0;continue}let d=ci.exec(i);if(d){a={type:`enum`,name:d[1]},n.add(d[1]);continue}if(i===`}`){a?.type===`model`&&t.push({name:a.name,fields:o,indexes:s,compositeIdColumns:c,filePath:r,tableName:l}),a=null,o=[],s=[],c=[],l=void 0;continue}if(a?.type===`model`&&i&&!i.startsWith(`//`)){if(i.startsWith(`@@`)){let e=gi.exec(i);e&&(c=e[1].split(`,`).map(e=>e.trim()));let t=_i(i);t&&s.push(t);let n=fi.exec(i);n&&(l=n[1]);continue}let e=vi(i);e&&o.push(e)}}}return{models:t,enums:n}}const hi=/^@@(index|unique)\(\[([^\]]*)\]\)/,gi=/^@@id\(\[([^\]]*)\]\)/;function _i(e){let t=hi.exec(e);if(!t)return null;let n=t[1]===`unique`,r=t[2].split(`,`).map(e=>e.trim()).filter(Boolean);return r.length===0?null:{columns:r,isUnique:n}}function vi(e){let t=li.exec(e);if(!t)return null;let n=t[1],r=t[2],i=t[3]===`?`,a=t[4]===`[]`,o=t[5]??``,s=[],c=new RegExp(ui.source,ui.flags),l=c.exec(o);for(;l!==null;)s.push(`@${l[1]}${l[2]??``}`),l=c.exec(o);return{name:n,type:r,isOptional:i,isList:a,attributes:s}}function yi(e){let t=e.attributes.some(e=>e.startsWith(`@id`)),n=e.attributes.some(e=>e.startsWith(`@unique`)),r=e.attributes.find(e=>e.startsWith(`@default(`)),i=!1,a;if(r){let e=di.exec(r);if(e){let t=e[1];a=t,(t===`autoincrement()`||t===`uuid()`||t===`cuid()`||t===`dbgenerated()`)&&(i=!0)}}return{name:e.name,type:e.type,isPrimary:t,isNullable:e.isOptional,isGenerated:i,isUnique:n,defaultValue:a}}const bi=/onDelete:\s*(\w+)/;function xi(e){let t=e.attributes.find(e=>e.startsWith(`@relation`));if(!t)return;let n=bi.exec(t);return n?n[1]:void 0}function Si(e,t){let n=new Set(e.map(e=>e.name));return e.map(r=>{let i=[],a=[],o=new Set;for(let e of r.indexes)for(let t of e.columns)o.add(t);let s=new Set(r.compositeIdColumns);for(let c of r.fields)if(n.has(c.type)&&!t.has(c.type)){let t;t=c.isList?`one-to-many`:`many-to-one`;let n=c.isOptional;c.isList&&e.find(e=>e.name===c.type)?.fields.find(e=>e!==c&&e.type===r.name&&e.isList)&&(t=`many-to-many`);let i=xi(c);a.push({type:t,fromEntity:r.name,toEntity:c.type,propertyName:c.name,isNullable:n??!1,...i?{onDelete:i}:{}})}else if(!c.attributes.some(e=>e.startsWith(`@relation`))){let e=yi(c);s.has(c.name)&&(e.isPrimary=!0),(o.has(c.name)||c.attributes.some(e=>e.startsWith(`@unique`)))&&(e.hasIndex=!0),i.push(e)}return{name:r.name,tableName:r.tableName??r.name,filePath:r.filePath,columns:i,relations:a,indexes:r.indexes}})}const Ci={supportsIncrementalUpdate:!1,extract(e,t,n){let r=pi(n);if(r.length===0)return[];let{models:i,enums:a}=mi(r);return Si(i,a)}},wi=/=>\s*(\w+)/,Ti=new Set([`Column`,`PrimaryColumn`,`PrimaryGeneratedColumn`,`CreateDateColumn`,`UpdateDateColumn`,`DeleteDateColumn`,`VersionColumn`]),Ei={OneToOne:`one-to-one`,OneToMany:`one-to-many`,ManyToOne:`many-to-one`,ManyToMany:`many-to-many`};function W(e){let t=e.getArguments();for(let e of t)if(e.getKind()===d.ObjectLiteralExpression){let t={},n=e.asKind(d.ObjectLiteralExpression);if(!n)continue;for(let e of n.getProperties())if(e.getKind()===d.PropertyAssignment){let n=e.asKind(d.PropertyAssignment);n&&(t[n.getName()]=n.getInitializer()?.getText()??``)}return t}return null}function Di(e){let t=e.getArguments();if(t.length===0)return null;let n=t[0];return n.getKind()===d.StringLiteral?n.asKind(d.StringLiteral)?.getLiteralValue()??null:null}function Oi(e){let t=e.getDecorator(`Entity`);if(!t)return e.getName()??`UnknownEntity`;let n=Di(t);if(n)return n;let r=W(t);return r?.name?r.name.replace(/['"]/g,``):e.getName()??`UnknownEntity`}function ki(e,t){let n=t.getName(),r=n===`PrimaryColumn`||n===`PrimaryGeneratedColumn`,i=n===`PrimaryGeneratedColumn`||n===`CreateDateColumn`||n===`UpdateDateColumn`||n===`DeleteDateColumn`||n===`VersionColumn`,a=`unknown`,o=!1,s=!1,c,l=Di(t);l&&(a=l);let u=W(t);return u&&(u.type&&(a=u.type.replace(/['"]/g,``)),u.nullable===`true`&&(o=!0),u.unique===`true`&&(s=!0),u.default!==void 0&&(c=u.default)),a===`unknown`&&(n===`PrimaryGeneratedColumn`?a=`integer`:n===`CreateDateColumn`||n===`UpdateDateColumn`||n===`DeleteDateColumn`?a=`timestamp`:n===`VersionColumn`&&(a=`integer`)),{name:e,type:a,isPrimary:r,isNullable:o,isGenerated:i,isUnique:s,defaultValue:c}}function Ai(e,t,n){let r=Ei[n.getName()];if(!r)return null;let i=n.getArguments();if(i.length===0)return null;let a=i[0].getText(),o=wi.exec(a);if(!o)return null;let s=o[1],c=W(n),l=c?.nullable===`true`,u=c?.onDelete?.replace(/['"]/g,``);return{type:r,fromEntity:e,toEntity:s,propertyName:t,isNullable:l,...u?{onDelete:u}:{}}}function ji(e){if(!w(e,`Entity`))return null;let t=e.getName();if(!t)return null;let n=Oi(e),r=e.getSourceFile().getFilePath(),i=[],a=[],o=[];for(let t of e.getDecorators())if(t.getName()===`Index`){let e=t.getArguments();for(let n of e)if(n.getKind()===d.ArrayLiteralExpression){let e=n.asKind(d.ArrayLiteralExpression);if(e){let n=e.getElements().map(e=>e.getKind()===d.StringLiteral?e.asKind(d.StringLiteral)?.getLiteralValue()??``:``).filter(Boolean);if(n.length>0){let e=W(t);o.push({columns:n,isUnique:e?.unique===`true`})}}}}let s=new Set;for(let n of e.getProperties()){let e=n.getName(),r=n.getDecorators(),c=r.some(e=>e.getName()===`Index`);c&&(s.add(e),o.push({columns:[e],isUnique:!1}));for(let n of r){let r=n.getName();if(Ti.has(r)){let t=ki(e,n);c&&(t.hasIndex=!0),i.push(t);break}if(r in Ei){let r=Ai(t,e,n);r&&a.push(r);break}}}for(let e of o)for(let t of e.columns){let e=i.find(e=>e.name===t);e&&(e.hasIndex=!0)}return{name:t,tableName:n,filePath:r,columns:i,relations:a,indexes:o}}const Mi={prisma:Ci,typeorm:{supportsIncrementalUpdate:!0,extract(e,t){let n=[];for(let r of t){let t=e.getSourceFile(r);if(t)for(let e of t.getClasses()){let t=ji(e);t&&n.push(t)}}return n}},drizzle:oi};function G(e,t,n,r){let i={entities:new Map,relations:[],orm:n??`unknown`};if(!n)return i;let a=Mi[n];if(!a)return i;let o=a.extract(e,t,r),s=new Map,c=[];for(let e of o)s.set(e.name,e),c.push(...e.relations);return{entities:s,relations:c,orm:n}}function Ni(e){return{entities:[...e.entities.values()],relations:e.relations,orm:e.orm}}async function Pi(e,t){let{config:n,fileRules:r,projectRules:i,schemaRules:a}=t,[o,s]=await Promise.all([hr(e,n),ge(e)]),c=_r(o),l=Pe(e),u=Re(c,o,l),d=xr(c,o);return{astProject:c,config:n,endpointGraph:Zr(c,o,d),fileRules:r,files:o,moduleGraph:u,pathAliases:l,project:s,projectRules:i,providers:d,schemaGraph:G(c,o,s.orm,e),schemaRules:a,targetPath:e}}async function Fi(e,t,n){let{config:r,combinedRules:i}=t,o=await gr(e,n,r),s=await Promise.all([...o.entries()].filter(([,e])=>e.length>0).map(async([t,o])=>{let s=a(e,n.projects.get(t)),[c,l]=await Promise.all([ge(s),Te(s,r)]),u=_r(o),d=Pe(s),f=Re(u,o,d),p=xr(u,o),m=Zr(u,o,p),h=G(u,o,c.orm,s),{fileRules:g,projectRules:_,schemaRules:v}=ar(ir(l,i));return[t,{astProject:u,config:l,endpointGraph:m,fileRules:g,files:o,moduleGraph:f,pathAliases:d,project:c,projectRules:_,providers:p,schemaGraph:h,schemaRules:v,targetPath:s}]}));return{subProjects:new Map(s)}}const Ii=e=>v.makeRe(e,{windows:!1}),Li=/\\/g,Ri=/\/$/,zi=(e,t,n)=>{let r=new Set(Array.isArray(t.ignore?.rules)?t.ignore.rules:[]),i=Array.isArray(t.ignore?.files)?t.ignore.files.map(Ii):[];if(r.size===0&&i.length===0)return e;let a=n.replace(Li,`/`).replace(Ri,``);return e.filter(e=>{if(r.has(e.rule))return!1;let t=e.filePath.replace(Li,`/`),n=t.startsWith(`${a}/`)?t.slice(a.length+1):t;return!i.some(e=>e.test(n))})};function Bi(e,t,n,r){let i=[],a=[],o=e.getSourceFile(t);if(!o)return{diagnostics:i,errors:a};let s=o.getFullText().split(`
|
|
7
|
+
`);for(let e of n){let n={config:r,sourceFile:o,filePath:t,report(t){let n=[],r=Math.max(0,t.line-6),a=Math.min(s.length,t.line+5);for(let e=r;e<a;e++)n.push({line:e+1,text:s[e]});i.push({...t,rule:e.meta.id,category:e.meta.category,scope:`file`,severity:e.meta.severity,sourceLines:n})}};try{e.check(n)}catch(t){a.push({ruleId:e.meta.id,error:t})}}return{diagnostics:i,errors:a}}function Vi(e,t,n,r){let i=[],a=[];for(let o of t){let t=Bi(e,o,n,r);i.push(...t.diagnostics),a.push(...t.errors)}return{diagnostics:i,errors:a}}function Hi(e,t,n,r){let i=[],a=[];for(let o of n){let n={project:e,files:t,moduleGraph:r.moduleGraph,providers:r.providers,config:r.config,report(e){i.push({...e,rule:o.meta.id,category:o.meta.category,scope:`project`,severity:o.meta.severity})}};try{o.check(n)}catch(e){a.push({ruleId:o.meta.id,error:e})}}return{diagnostics:i,errors:a}}function Ui(e,t){let n=[],r=[];for(let i of t){let t={schemaGraph:e,orm:e.orm,report(e){n.push({...e,rule:i.meta.id,category:i.meta.category,scope:`schema`,severity:i.meta.severity})}};try{i.check(t)}catch(e){r.push({ruleId:i.meta.id,error:e})}}return{diagnostics:n,errors:r}}function Wi(e){return e instanceof Error?e.message:String(e)}function Gi(e,t,n){return{diagnostics:zi(e,n.config,n.targetPath),errors:t.map(e=>({ruleId:e.ruleId,error:Wi(e.error)}))}}function Ki(e){let t=Vi(e.astProject,e.files,e.fileRules,e.config);return Gi(t.diagnostics,t.errors,e)}function qi(e){let t={moduleGraph:e.moduleGraph,providers:e.providers,config:e.config},n=Hi(e.astProject,e.files,e.projectRules,t),{diagnostics:r,errors:i}=Gi(n.diagnostics,n.errors,e),a=Ji(e);return r.push(...a.diagnostics),i.push(...a.errors),{diagnostics:r,errors:i}}function Ji(e){if(!e.schemaGraph||e.schemaRules.length===0||e.schemaGraph.entities.size===0)return{diagnostics:[],errors:[]};let t=Ui(e.schemaGraph,e.schemaRules);return Gi(t.diagnostics,t.errors,e)}function Yi(e){let t=l.now(),n=Ki(e),r=qi(e),i=l.now()-t;return{diagnostics:[...n.diagnostics,...r.diagnostics],elapsedMs:i,ruleErrors:[...n.errors,...r.errors]}}function Xi(e){return e>=90?`Excellent`:e>=75?`Good`:e>=50?`Fair`:e>=25?`Poor`:`Critical`}const Zi={error:3,warning:1.5,info:.5},Qi={security:1.5,correctness:1.3,schema:1.1,architecture:1,performance:.8};function $i(e,t){if(t===0)return{value:100,label:Xi(100)};let n=0;for(let t of e){let e=Zi[t.severity],r=Qi[t.category];n+=e*r}let r=n/t,i=Math.max(0,Math.min(100,Math.round(100-r*10)));return{value:i,label:Xi(i)}}function ea(e){let t={total:0,errors:0,warnings:0,info:0,byCategory:{security:0,performance:0,correctness:0,architecture:0,schema:0}};for(let n of e)t.total++,n.severity===`error`?t.errors++:n.severity===`warning`?t.warnings++:t.info++,t.byCategory[n.category]++;return t}function ta(e,t,n=[]){let{diagnostics:r,ruleErrors:i,elapsedMs:a}=t,o=e.schemaGraph??G(e.astProject,e.files,e.project.orm,e.targetPath),s=$i(r,e.files.length),c=ea(r);return{result:{score:s,diagnostics:r,endpoints:e.endpointGraph,project:{...e.project,fileCount:e.files.length,moduleCount:e.moduleGraph.modules.size},summary:c,ruleErrors:i,elapsedMs:a,schema:Ni(o)},moduleGraph:e.moduleGraph,schemaGraph:o,customRuleWarnings:n,files:e.files,providers:e.providers}}function na(e,t,n,r){let i=[],a=[],o=[],s=new Map,c=[],l=0,u=[],d=[],f=``;for(let[n,r]of e.subProjects){let e=ta(r,t.get(n));i.push({name:n,result:e.result}),s.set(n,e.moduleGraph),a.push(...e.result.diagnostics),o.push(...e.result.ruleErrors),l+=e.result.project.fileCount,e.result.endpoints&&c.push(...e.result.endpoints.endpoints),e.result.schema&&(u.push(...e.result.schema.entities),d.push(...e.result.schema.relations),e.result.schema.orm&&e.result.schema.orm!==`unknown`&&(f=e.result.schema.orm))}let p=$i(a,l),m=ea(a);return{moduleGraphs:s,customRuleWarnings:n,result:{isMonorepo:!0,subProjects:i,combined:{score:p,diagnostics:a,endpoints:c.length>0?{endpoints:c}:void 0,project:{name:`monorepo`,nestVersion:i[0]?.result.project.nestVersion??null,orm:f||(i[0]?.result.project.orm??null),framework:i[0]?.result.project.framework??null,fileCount:l,moduleCount:i.reduce((e,t)=>e+t.result.project.moduleCount,0)},summary:m,ruleErrors:o,elapsedMs:r,schema:u.length>0?{entities:u,relations:d,orm:f||`unknown`}:void 0},elapsedMs:r}}}const ra=e=>{if(e.trim()===``)return`Invalid --min-score value: "${e}". Must be an integer between 0 and 100.`;let t=Number(e);return!Number.isInteger(t)||t<0||t>100?`Invalid --min-score value: "${e}". Must be an integer between 0 and 100.`:null},ia=(e,t)=>e===void 0?t:Number(e),aa=(e,t)=>t===void 0?!0:e>=t,K={error:y.red,warn:y.yellow,info:y.cyan,success:y.green,dim:y.dim},q=e=>{console.log(e)},J={error(...e){q(K.error(e.join(` `)))},warn(...e){q(K.warn(e.join(` `)))},info(...e){q(K.info(e.join(` `)))},success(...e){q(K.success(e.join(` `)))},dim(...e){q(K.dim(e.join(` `)))},log(...e){q(e.join(` `))},break(){q(``)}},oa=1e3,sa={error:0,warning:1,info:2},Y=(e,t=e)=>({plainText:e,renderedText:t}),X=(e,t)=>t>=75?K.success(e):t>=50?K.warn(e):K.error(e),ca=(e,t)=>t===`error`?K.error(e):t===`warning`?K.warn(e):K.info(e),la=e=>e===`error`?`✗`:e===`warning`?`⚠`:`●`,ua=e=>e<oa?`${Math.round(e)}ms`:`${(e/oa).toFixed(1)}s`,da=e=>e>=75?[`◠ ◠ ◠`,`╰───╯`]:e>=50?[`• • •`,`╰───╯`]:[`x x x`,`╰───╯`],fa=e=>e>=90?`★★★★★`:e>=75?`★★★★☆`:e>=50?`★★★☆☆`:e>=25?`★★☆☆☆`:`★☆☆☆☆`,pa=e=>{let t=Math.round(e/100*50),n=50-t;return{filled:`█`.repeat(t),empty:`░`.repeat(n)}},ma=e=>{let{filled:t,empty:n}=pa(e);return`${t}${n}`},ha=e=>{let{filled:t,empty:n}=pa(e);return X(t,e)+K.dim(n)},ga=e=>{if(e.length===0)return;let t=K.dim,n=` `.repeat(2),r=` `.repeat(1),i=Math.max(...e.map(e=>e.plainText.length)),a=`─`.repeat(i+2);J.log(`${n}${t(`┌${a}┐`)}`);for(let a of e){let e=` `.repeat(i-a.plainText.length);J.log(`${n}${t(`│`)}${r}${a.renderedText}${e}${r}${t(`│`)}`)}J.log(`${n}${t(`└${a}┘`)}`)},_a=e=>{let t=new Map;for(let n of e){let e=n.rule,r=t.get(e)??[];r.push(n),t.set(e,r)}return t},va=e=>[...e].sort(([,e],[,t])=>sa[e[0].severity]-sa[t[0].severity]),ya=e=>{let t=new Map;for(let n of e){let e=t.get(n.filePath)??[];`line`in n&&n.line>0&&e.push(n.line),t.set(n.filePath,e)}return t},ba=e=>new Set(e.map(e=>e.filePath)),xa=(e,t)=>{let n=va([..._a(e).entries()]);for(let[,e]of n){let n=e[0],r=ca(la(n.severity),n.severity),i=e.length,a=i>1?ca(` (${i})`,n.severity):``;if(J.log(` ${r} ${n.message}${a}`),n.help&&J.dim(` ${n.help}`),t){let t=ya(e);for(let[e,n]of t){let t=n.length>0?`: ${n.join(`, `)}`:``;J.dim(` ${e}${t}`)}}J.break()}};function Sa(e,t){let{score:n,diagnostics:r,project:i,summary:a,elapsedMs:o}=e;J.break();let s=[],c=e=>X(e,n.value),[l,u]=da(n.value);s.push(Y(`┌───────┐`,c(`┌───────┐`))),s.push(Y(`│ ${l} │ NestJS Doctor`,`${c(`│ ${l} │`)} NestJS Doctor`)),s.push(Y(`│ ${u} │`,c(`│ ${u} │`))),s.push(Y(`└───────┘`,c(`└───────┘`))),s.push(Y(``));let d=fa(n.value),f=`${n.value} / 100 ${d} ${n.label}`,p=`${X(String(n.value),n.value)} / 100 ${X(d,n.value)} ${X(n.label,n.value)}`;s.push(Y(f,p)),s.push(Y(``)),s.push(Y(ma(n.value),ha(n.value))),s.push(Y(``));let m=ua(o),h=ba(r).size,g=[],_=[];if(a.errors>0){let e=`✗ ${a.errors} error${a.errors===1?``:`s`}`;_.push(e),g.push(K.error(e))}if(a.warnings>0){let e=`⚠ ${a.warnings} warning${a.warnings===1?``:`s`}`;_.push(e),g.push(K.warn(e))}if(a.info>0){let e=`● ${a.info} info`;_.push(e),g.push(K.info(e))}if(r.length===0){let e=`No issues found!`;_.push(e),g.push(K.success(e))}let v=r.length>0?`across ${h}/${i.fileCount} files`:`${i.fileCount} files scanned`,y=`in ${m}`;_.push(v),_.push(y),g.push(K.dim(v)),g.push(K.dim(y)),s.push(Y(_.join(` `),g.join(` `))),ga(s),J.break();let b=[`Project: ${i.name}`];if(i.nestVersion&&b.push(`NestJS ${i.nestVersion}`),i.orm&&b.push(i.orm),b.push(`${i.moduleCount} modules`),J.dim(` ${b.join(` | `)}`),J.break(),r.length!==0){if(xa(r,t),t&&e.ruleErrors.length>0){J.warn(` ${e.ruleErrors.length} rule(s) failed during execution:`);for(let t of e.ruleErrors)J.dim(` ${t.ruleId}: ${t.error}`);J.break()}t||(J.dim(` Run with --verbose for file paths and line numbers`),J.break())}}function Ca(e,t){Sa(e.combined,t),J.log(` Sub-project breakdown:`),J.break();for(let t of e.subProjects){let{name:e,result:n}=t,r=X(String(n.score.value),n.score.value),i=[`${K.info(e)}: ${r}/100`,`${n.project.fileCount} files`];n.summary.errors>0&&i.push(K.error(`${n.summary.errors} errors`)),n.summary.warnings>0&&i.push(K.warn(`${n.summary.warnings} warnings`)),n.summary.info>0&&i.push(`${n.summary.info} info`),n.diagnostics.length===0&&i.push(K.success(`clean`)),J.log(` ${i.join(` | `)}`)}J.break()}function wa(e){console.log(JSON.stringify(e,null,2))}const Z=(e,t,n)=>{let r=e.score.value;aa(r,t)||(n||J.error(`Score ${r} is below the minimum threshold of ${t}.`),process.exit(1))},Ta=e=>{e.summary.errors>0&&process.exit(1)},Ea=(e,t,n,r)=>{let{result:i}=e;if(r.score){console.log(i.combined.score.value),Z(i.combined,t,n);return}if(r.json){wa(i.combined),Z(i.combined,t,n);return}Ca(i,r.verbose),Z(i.combined,t,n),Ta(i.combined)},Da=(e,t,n,r)=>{let{result:i}=e;if(r.score){console.log(i.score.value),Z(i,t,n);return}if(r.json){wa(i),Z(i,t,n);return}Sa(i,r.verbose),Z(i,t,n),Ta(i)};let Q=null,$=0;const Oa=new Set,ka=(e,t,n)=>{if(Oa.delete(t),$--,$<=0||!Q){Q?.[e](n),Q=null,$=0;return}Q.stop(),b(n).start()[e](n);let[r]=Oa;r&&(Q.text=r),Q.start()},Aa=e=>({start(){return $++,Oa.add(e),Q?Q.text=e:Q=b({text:e}).start(),{succeed:t=>ka(`succeed`,e,t),fail:t=>ka(`fail`,e,t)}}}),ja=(e,t)=>{if(!t)for(let t of e)J.warn(t)};var Ma=class{options;resolvedMinimumScore;scanConfig;steps=[];targetPath;constructor(e,t){this.targetPath=e,this.options=t}resolveConfig(){return this.steps.push(async()=>{this.scanConfig=await mr(this.targetPath,this.options.configPath),this.resolvedMinimumScore=ia(this.options.minScore,this.scanConfig.config.minScore)}),this}warnCustomRules(){return this.steps.push(()=>{ja(this.scanConfig.customRuleWarnings,this.options.isMachineReadable)}),this}async run(){let e=this.options.isMachineReadable?null:Aa(`Scanning...`).start();for(let e of this.steps)await e();e?.succeed(`Scan complete`)}},Na=class extends Ma{monorepo;monorepoCtx;rawOutputs=new Map;result;scanStartTime;constructor(e,t,n){super(e,n),this.monorepo=t}buildContext(){return this.steps.push(async()=>{this.scanStartTime=l.now(),this.monorepoCtx=await Fi(this.targetPath,this.scanConfig,this.monorepo)}),this}runRules(){return this.steps.push(()=>{for(let[e,t]of this.monorepoCtx.subProjects)this.rawOutputs.set(e,Yi(t))}),this}buildResult(){return this.steps.push(()=>{let e=l.now()-this.scanStartTime;this.result=na(this.monorepoCtx,this.rawOutputs,this.scanConfig.customRuleWarnings,e)}),this}output(){return this.steps.push(()=>{Ea(this.result,this.resolvedMinimumScore,this.options.isMachineReadable,{json:this.options.json,score:this.options.score,verbose:this.options.verbose})}),this}},Pa=class extends Ma{context;rawOutput;result;buildContext(){return this.steps.push(async()=>{this.context=await Pi(this.targetPath,this.scanConfig)}),this}runRules(){return this.steps.push(()=>{this.rawOutput=Yi(this.context)}),this}buildResult(){return this.steps.push(()=>{this.result=ta(this.context,this.rawOutput,this.scanConfig.customRuleWarnings)}),this}output(){return this.steps.push(()=>{Da(this.result,this.resolvedMinimumScore,this.options.isMachineReadable,{json:this.options.json,score:this.options.score,verbose:this.options.verbose})}),this}},Fa=class{args;steps=[];version;targetPath=``;constructor(e,t){this.args=e,this.version=t}resolveTargetPath(){return this.steps.push(()=>(this.targetPath=s(this.args.path??`.`),!0)),this}handleInit(){return this.steps.push(async()=>{if(this.args.init){let{initSkill:e}=await import(`../init-DX6EkkPo.mjs`);return await e(this.targetPath,this.version),!1}return!0}),this}handleReport(){return this.steps.push(async()=>{if(this.args.report){let{runReport:e}=await import(`../setup-CN4uKQl2.mjs`);return await e(this.targetPath,this.args.config),!1}return!0}),this}validateMinScore(){return this.steps.push(()=>{if(this.args[`min-score`]!==void 0){let e=ra(this.args[`min-score`]);e&&(J.error(e),process.exit(2))}return!0}),this}async run(){for(let e of this.steps)if(!await e())return null;return{targetPath:this.targetPath,options:{configPath:this.args.config,isMachineReadable:this.args.score||this.args.json,json:this.args.json??!1,minScore:this.args[`min-score`],score:this.args.score??!1,verbose:this.args.verbose??!1}}}};const{version:Ia}=e(import.meta.url)(`../../package.json`);n(t({meta:{name:`nestjs-doctor`,version:Ia,description:`Static analysis tool for NestJS — health score, diagnostics, and interactive HTML report`},args:{path:{type:`positional`,description:`Path to the NestJS project (defaults to current directory)`,default:`.`,required:!1},...be},async run({args:e}){let t=await new Fa(e,Ia).resolveTargetPath().handleInit().handleReport().validateMinScore().run();if(!t)return;let{targetPath:n,options:r}=t,i=await me(n);if(i){await new Na(n,i,r).resolveConfig().buildContext().runRules().buildResult().warnCustomRules().output().run();return}await he(n)&&console.warn(`Warning: This directory appears to be a monorepo, but no NestJS packages were found.
|
|
8
|
+
Consider running on a specific sub-project instead.`),await new Pa(n,r).resolveConfig().buildContext().runRules().buildResult().warnCustomRules().output().run()}}));export{ta as a,Fi as c,qe as d,me as f,na as i,mr as l,J as n,Yi as o,K as r,Pi as s,Aa as t,Je as u};
|
package/package.json
CHANGED