@ngcompass/engine
Advanced tools
+2
-2
@@ -1,3 +0,3 @@ | ||
| 'use strict';var ast=require('@ngcompass/ast'),common=require('@ngcompass/common'),T=require('typescript'),V=require('os'),promises=require('fs/promises'),module$1=require('module'),child_process=require('child_process'),F=require('path'),url=require('url'),fs=require('fs'),ve=require('v8'),ee=require('p-limit'),planner=require('@ngcompass/planner'),lruCache=require('lru-cache');var _documentCurrentScript=typeof document!=='undefined'?document.currentScript:null;function _interopDefault(e){return e&&e.__esModule?e:{default:e}}function _interopNamespace(e){if(e&&e.__esModule)return e;var n=Object.create(null);if(e){Object.keys(e).forEach(function(k){if(k!=='default'){var d=Object.getOwnPropertyDescriptor(e,k);Object.defineProperty(n,k,d.get?d:{enumerable:true,get:function(){return e[k]}});}})}n.default=e;return Object.freeze(n)}var T__default=/*#__PURE__*/_interopDefault(T);var V__default=/*#__PURE__*/_interopDefault(V);var F__namespace=/*#__PURE__*/_interopNamespace(F);var ve__default=/*#__PURE__*/_interopDefault(ve);var ee__default=/*#__PURE__*/_interopDefault(ee);var Ot=(e,t,r)=>({name:e,streamType:"AngularClass",handle:t,meta:r}),Bt=(e,t,r)=>({name:e,streamType:"AnyAngularClass",handle:t,meta:r}),Ht=(e,t)=>({name:e,streamType:"DecoratedProperty",handle:t}),Gt=(e,t,r)=>({name:e,streamType:"TemplateExpression",handle:t,meta:r}),Kt=(e,t,r)=>({name:e,streamType:"TemplateAttribute",handle:t,meta:r}),Ut=(e,t,r)=>({name:e,streamType:"CallExpression",handle:t,meta:r}),Vt=(e,t,r)=>({name:e,streamType:"NewExpression",handle:t,meta:r}),Yt=(e,t,r)=>({name:e,streamType:"TemplateBlock",handle:t,meta:r}),qt=(e,t,r)=>({name:e,streamType:"Template",handle:t,meta:r});var $e={AngularClass:"ClassDeclaration",AnyAngularClass:"ClassDeclaration",DecoratedProperty:"PropertyDefinition",TemplateExpression:"__template_expression__",TemplateAttribute:"__template_attribute__",TemplateBlock:"__template_block__",Template:"__template_analysis__",CallExpression:"CallExpression",NewExpression:"NewExpression"};function ae(e,t){let r=new Map;for(let o of e){let n=$e[o.streamType];if(!n||n.startsWith("__"))continue;let s=t[o.streamType];if(!s)continue;let l={ruleName:o.name,filter:s,handle:o.handle.bind(o)},a=r.get(n);a?a.push(l):r.set(n,[l]);}return r}var Fe=2,Ne=80,oe=6e4,Re=2,_e=5;var G=(e,t,r,o,n,s,l)=>{if(t.length!==0)for(let a of e)for(let i=0;i<t.length;i++){let d=t[i],f=s?performance.now():0;try{let p=d.handle(a,r);if(p){let u=o.get(d.name)??[];Array.isArray(p)?u.push(...p):u.push(p),o.set(d.name,u);}}catch(p){l?.record(common.createInfrastructureError("RuleExecutionError",{cause:`Rule ${d.name} failed on template node: ${p instanceof Error?p.message:String(p)}`,recoverable:true,phase:"engine",details:{ruleName:d.name,errorName:p instanceof Error?p.name:void 0}}));}if(s){let p=performance.now()-f,u=n.get(d.name);u&&(u.totalMs+=p,u.invocations++);}}},rr=(e,t,r)=>{let{program:o}=t;if(!o)return {results:e.map(c=>({ruleName:c.name,failures:[]})),performance:{traversalMs:0,nodesVisited:0,ruleTimings:[],cacheStats:{hits:0,misses:0},budgetViolations:[],hasBudgetViolations:false}};let n=performance.now(),s=ae(e,{AngularClass:ast.toAngularClassStream,AnyAngularClass:ast.toAnyAngularClassStream,DecoratedProperty:ast.toDecoratedPropertyStream,CallExpression:ast.toCallExpressionStream,NewExpression:ast.toNewExpressionStream}),l=e.filter(c=>c.streamType==="TemplateExpression"),a=e.filter(c=>c.streamType==="TemplateAttribute"),i=e.filter(c=>c.streamType==="TemplateBlock"),d=e.filter(c=>c.streamType==="Template"),f=new Map,p=new Map,u=r?.collectRuleTimings??common.isDebugEnabled(),x=0;for(let c of e)p.set(c.name,{ruleName:c.name,totalMs:0,invocations:0});if(ast.resetComponentCacheStats(),ast.walkProgram(o,c=>{if(!c?.type)return;x++;let h=s.get(c.type);if(h)for(let S=0;S<h.length;S++){let m=h[S],E=u?performance.now():0;try{let C=m.filter(c);if(C!==null){let v=m.handle(C,t);if(v){let $=f.get(m.ruleName)??[];Array.isArray(v)?$.push(...v):$.push(v),f.set(m.ruleName,$);}}}catch(C){r?.errorCollector?.record(common.createInfrastructureError("RuleExecutionError",{cause:`Rule ${m.ruleName} failed: ${C instanceof Error?C.message:String(C)}`,recoverable:true,phase:"engine",details:{ruleName:m.ruleName,errorName:C instanceof Error?C.name:void 0}}));}if(u){let C=performance.now()-E,v=p.get(m.ruleName);v&&(v.totalMs+=C,v.invocations++);}}}),t.template&&(l.length>0||a.length>0||i.length>0||d.length>0)){let c=ast.analyzeTemplate(t.template),h=t.templateFilePath&&t.templateFileContent&&t.templateLocator?{...t,filePath:t.templateFilePath,fileContent:t.templateFileContent,locator:t.templateLocator}:t;G(c.expressions,l,h,f,p,u,r?.errorCollector),G(c.attributes,a,h,f,p,u,r?.errorCollector),G(c.blocks,i,h,f,p,u,r?.errorCollector),G([c],d,h,f,p,u,r?.errorCollector);}let w=[];for(let c of e)w.push({ruleName:c.name,failures:f.get(c.name)??[]});let k=performance.now()-n,y=[],g=t.typeChecker?5:2;if(k>g&&y.push(`Total traversal time ${k.toFixed(2)}ms exceeds budget ${g}ms`),u)for(let c of p.values()){if(c.invocations===0)continue;let h=c.totalMs/c.invocations;h>1&&y.push(`Rule ${c.ruleName} averages ${h.toFixed(2)}ms per invocation (threshold: 1ms)`);}return {results:w,performance:{traversalMs:k,nodesVisited:x,ruleTimings:Array.from(p.values()),cacheStats:ast.getComponentCacheStats(),budgetViolations:y,hasBudgetViolations:y.length>0}}};var K=class{context;constructor(t){this.context=t;}async build(t,r,o){let n,s,l,a,i,[d,f]=await Promise.all([this.context.readFile(t),this.context.getProgram(t)]),p=new common.Locator(d);this.context.getTypeChecker&&(n=await this.context.getTypeChecker(t)),o&&(s=await this.context.getTemplate(t));let u=this.context.getProjectContext?.();o&&!s&&u&&(l=this.resolveExternalTemplatePath(t,u))&&(s=await this.context.getTemplate(l),i=new common.Locator(a=await this.context.readFile(l)));let x=u?await this.buildCrossRef(t,s,u):void 0;return {filePath:t,fileContent:d,locator:p,program:f,typeChecker:n,template:s,templateFilePath:l,templateFileContent:a,templateLocator:i,options:r,project:u,crossRef:x}}resolveExternalTemplatePath(t,r){let o=t.endsWith(".component.ts")?t:r.templateToComponent.get(t);if(o)return r.componentGraph.get(o)?.templatePath}async buildCrossRef(t,r,o){let n,s,l,a=t.endsWith(".component.ts")?t:o.templateToComponent.get(t);if(!a)return;let i=o.componentGraph.get(a),d=i?.templatePath,f=i?.stylePaths??[],p=i?.specPath;try{let x=await this.getComponentSourceFile(a);x&&(n=(function(w){let k=new Set;for(let y of w.statements)if(T__default.default.isClassDeclaration(y))for(let g of y.members){let c=T__default.default.canHaveModifiers(g)?T__default.default.getModifiers(g):void 0;if(c&&c.some(S=>S.kind===T__default.default.SyntaxKind.PrivateKeyword||S.kind===T__default.default.SyntaxKind.ProtectedKeyword))continue;let h=g.name;h&&!T__default.default.isPrivateIdentifier(h)&&(T__default.default.isIdentifier(h)||T__default.default.isStringLiteral(h))&&k.add(h.text);}return k})(x),s=(function(w){let k=new Set;for(let y of w.statements)if(T__default.default.isClassDeclaration(y))for(let g of y.members){if(!T__default.default.isPropertyDeclaration(g)||!g.initializer||!g.name||T__default.default.isPrivateIdentifier(g.name))continue;let c=(function(h){let S=T__default.default.isAsExpression(h)||T__default.default.isSatisfiesExpression(h)?h.expression:h;if(!T__default.default.isCallExpression(S))return;let m=S.expression;return T__default.default.isIdentifier(m)?m.text:T__default.default.isPropertyAccessExpression(m)?T__default.default.isIdentifier(m.expression)&&m.expression.text==="input"?"input":m.name.text:void 0})(g.initializer);c&&Ke.has(c)&&(T__default.default.isIdentifier(g.name)||T__default.default.isStringLiteral(g.name))&&k.add(g.name.text);}return k})(x));}catch{}let u=r;if(!u&&d)try{u=await this.context.getTemplate(d);}catch{}if(u)try{l=(function(x){let w=new Set,k=/\{\{([\s\S]*?)\}\}/g;for(let y of x.rootNodes)(function g(c){if(c&&typeof c=="object"){if(Array.isArray(c.attrs))for(let h of c.attrs){if((typeof h.name=="string"?h.name:"").startsWith("("))continue;let S=h.value;typeof S=="string"&&S&&le(S,w);}if(typeof c.value=="string"&&c.value){let h;for(k.lastIndex=0;(h=k.exec(c.value))!==null;)le(h[1],w);}if(Array.isArray(c.children))for(let h of c.children)g(h);}})(y);return w})(u);}catch{}return {componentPath:a,templatePath:d,stylePaths:f,specPath:p,publicMembers:n,signalMembers:s,templateReferences:l}}async getComponentSourceFile(t){let r=this.context.getTsSourceFile?.(t);if(r)return r;let o=await this.context.readFile(t);return T__default.default.createSourceFile(t,o,T__default.default.ScriptTarget.Latest,true)}},Ke=new Set(["signal","computed","linkedSignal","input","model","toSignal"]),ie=/\b([a-zA-Z_$][a-zA-Z0-9_$]*)\b/g;function le(e,t){let r;for(ie.lastIndex=0;(r=ie.exec(e))!==null;)t.add(r[1]);}var Xe=new Set([".css",".scss",".sass",".less"]),_=e=>{let t=new lruCache.LRUCache({max:128}),r=new Map,o=new Map,n=new Map,s=a=>{let i=t.get(a);if(i)return i;let d=Qe(e,a);return t.set(a,d),d},l=a=>{let i=r.get(a);if(i)return i;let d=(async()=>ast.parseTs(await s(a),a).program)();return r.set(a,d),d};return {rootDir:e,readFile:s,getProgram:l,getTemplate:a=>{let i=o.get(a);if(i)return i;let d=(async()=>{let f=await et(a,s,l);if(f&&f.content)return ast.parseHtml(f.content,f.startOffset)})();return o.set(a,d),d},getStyle:a=>{let i=n.get(a);if(i)return i;let d=F__namespace.extname(a).toLowerCase();if(!Xe.has(d)){let p=Promise.resolve(void 0);return n.set(a,p),p}let f=(async()=>ast.parseCss(await s(a),a))();return n.set(a,f),f},evict:a=>{t.delete(a),r.delete(a),o.delete(a),n.delete(a);},dispose:()=>{t.clear(),r.clear(),o.clear(),n.clear();}}},Qe=async(e,t)=>{try{return await promises.readFile(F__namespace.resolve(e,t),"utf-8")}catch(r){let o=r instanceof Error?r.message:String(r);throw Error(`Cannot read file: ${t}. ${o}`)}},et=async(e,t,r)=>{let o=F__namespace.extname(e);if(o===".html")return {content:await t(e),startOffset:0};if(o===".ts"){let n=ast.extractTemplateFromProgram(await r(e));return n.content?n:null}return null};function q(e,t,r,o){let n=performance.now(),s=(function(y,g,c){var h;let S=new Set;for(let E of y)S.add(F__namespace.resolve(g,E));let m=(h=g).endsWith(F__namespace.sep)?h:h+F__namespace.sep;for(let E of c.getSourceFiles())!E.isDeclarationFile&&E.fileName.startsWith(m)&&S.add(E.fileName);return S})(t,r,e),{importGraph:l,reverseImportGraph:a,externalDeps:i}=(function(y,g){let c=y.getCompilerOptions(),h=T__default.default.createModuleResolutionCache(y.getCurrentDirectory(),T__default.default.sys.useCaseSensitiveFileNames?C=>C:C=>C.toLowerCase(),c),S=new Map,m=new Map,E=new Map;for(let C of g)S.set(C,new Set),m.set(C,new Set);for(let C of y.getSourceFiles()){if(C.isDeclarationFile||!g.has(C.fileName))continue;let v=C.fileName;for(let $ of (function(R){let A=[];return T__default.default.forEachChild(R,function N(P){T__default.default.isImportDeclaration(P)&&T__default.default.isStringLiteral(P.moduleSpecifier)||T__default.default.isExportDeclaration(P)&&P.moduleSpecifier&&T__default.default.isStringLiteral(P.moduleSpecifier)?A.push(P.moduleSpecifier.text):(T__default.default.isCallExpression(P)&&P.expression.kind===T__default.default.SyntaxKind.ImportKeyword&&P.arguments.length>0&&T__default.default.isStringLiteralLike(P.arguments[0])&&A.push(P.arguments[0].text),T__default.default.forEachChild(P,N));}),A})(C)){let R=T__default.default.resolveModuleName($,v,c,T__default.default.sys,h),A=R.resolvedModule?.resolvedFileName;if(A&&g.has(A)){S.get(v).add(A);let N=m.get(A);N||(N=new Set,m.set(A,N)),N.add(v);}else {let N=(function(P){if(P.startsWith(".")||F__namespace.isAbsolute(P))return null;if(P.startsWith("@")){let D=P.indexOf("/",1);if(D===-1)return null;let H=P.indexOf("/",D+1);return H===-1?P:P.slice(0,H)}let W=P.indexOf("/");return W===-1?P:P.slice(0,W)})($);if(N){let P=E.get(v);P||(P=new Set,E.set(v,P)),P.add(N);}}}}return {importGraph:S,reverseImportGraph:m,externalDeps:E}})(e,s),d=(function(y,g){let c=new Set;for(let h of y.getSourceFiles()){if(h.isDeclarationFile||!g.has(h.fileName))continue;let{statements:S}=h;S.length!==0&&S.every(m=>T__default.default.isExportDeclaration(m)&&m.moduleSpecifier!=null)&&c.add(h.fileName);}return c})(e,s),f=o??(function(y){let g=new Map,c=[".scss",".sass",".css",".less"];for(let h of y){if(!h.endsWith(".component.ts"))continue;let S=F__namespace.dirname(h),m=F__namespace.basename(h,".ts"),E=F__namespace.join(S,m+".html"),C=y.has(E)?E:void 0,v=[];for(let A of c){let N=F__namespace.join(S,m+A);y.has(N)&&v.push(N);}let $=F__namespace.join(S,m+".spec.ts"),R=y.has($)?$:void 0;g.set(h,{tsPath:h,templatePath:C,stylePaths:v,specPath:R});}return g})(s),p=(function(y){let g=new Map;for(let[c,h]of y)h.templatePath&&g.set(h.templatePath,c);return g})(f),{ngModuleMap:u,standaloneComponents:x,classToFile:w}=(function(y,g){let c=new Map,h=new Set,S=(function(m,E){let C=new Map;for(let v of m.getSourceFiles())if(!v.isDeclarationFile&&E.has(v.fileName))for(let $ of v.statements){if(!T__default.default.isClassDeclaration($)||!$.name)continue;(T__default.default.canHaveModifiers($)?T__default.default.getModifiers($):void 0)?.some(A=>A.kind===T__default.default.SyntaxKind.ExportKeyword)&&C.set($.name.text,v.fileName);}return C})(y,g);for(let m of y.getSourceFiles())if(!m.isDeclarationFile&&g.has(m.fileName))for(let E of m.statements){if(!T__default.default.isClassDeclaration(E))continue;let C=T__default.default.canHaveDecorators(E)?T__default.default.getDecorators(E):void 0;if(C?.length)for(let v of C){let $=(function(N){let P=N.expression;if(!T__default.default.isCallExpression(P))return;let W=P.expression;if(!T__default.default.isIdentifier(W))return;let D=P.arguments[0],H=D&&T__default.default.isObjectLiteralExpression(D)?D:void 0;return {name:W.text,arg:H}})(v);if(!$)continue;let{name:R,arg:A}=$;if(R==="NgModule"){A&&c.set(m.fileName,{filePath:m.fileName,declarations:new Set(j(A,"declarations")),imports:new Set(j(A,"imports")),exports:new Set(j(A,"exports")),providers:new Set(j(A,"providers")),isStandalone:false});break}if(R==="Component"){A&&pe(A,"standalone")&&(h.add(m.fileName),c.set(m.fileName,{filePath:m.fileName,declarations:new Set,imports:new Set(A?j(A,"imports"):[]),exports:new Set,providers:new Set(A?j(A,"providers"):[]),isStandalone:true}));break}if(R==="Directive"||R==="Pipe"){A&&pe(A,"standalone")&&h.add(m.fileName);break}}}return {ngModuleMap:c,standaloneComponents:h,classToFile:S}})(e,s),k=(function(y){let g=0;for(let c of y.values())g+=c.size;return g})(l);return common.debug("engine",`ProjectContext built in ${(performance.now()-n).toFixed(2)}ms \u2014 ${s.size} files, ${k} import edges, ${d.size} barrels, ${i.size} files with ext deps, ${f.size} components, ${p.size} template\u2194component links, ${u.size} NgModules/standalone, ${x.size} standalone`),{importGraph:l,reverseImportGraph:a,ngModuleMap:u,standaloneComponents:x,classToFile:w,componentGraph:f,projectFiles:s,rootDir:r,barrelFiles:d,externalDeps:i,templateToComponent:p}}function j(e,t){for(let r of e.properties){if(!T__default.default.isPropertyAssignment(r)||!T__default.default.isIdentifier(r.name)||r.name.text!==t)continue;let o=r.initializer;if(!T__default.default.isArrayLiteralExpression(o))break;let n=[];for(let s of o.elements)n.push(...(function l(a){return T__default.default.isIdentifier(a)?[a.text]:T__default.default.isPropertyAccessExpression(a)||T__default.default.isCallExpression(a)?l(a.expression):[]})(s));return n}return []}function pe(e,t){for(let r of e.properties)if(T__default.default.isPropertyAssignment(r)&&T__default.default.isIdentifier(r.name)&&r.name.text===t)return r.initializer.kind===T__default.default.SyntaxKind.TrueKeyword;return false}var Z=(e,t=[],r,o={})=>{let n,s,l=_(e);common.debug("engine","Initializing Type-Aware Context (ts.createProgram)...");let a=performance.now(),i=r?.tsconfigRootDir?F__namespace.default.resolve(e,r.tsconfigRootDir):e,d=r?.project?F__namespace.default.resolve(i,r.project):T__default.default.findConfigFile(i,T__default.default.sys.fileExists,"tsconfig.json");if(d){let p=T__default.default.readConfigFile(d,T__default.default.sys.readFile),u=T__default.default.parseJsonConfigFileContent(p.config,T__default.default.sys,i),x=(o.programRootFiles??t).filter(k=>(k.endsWith(".ts")||k.endsWith(".tsx"))&&!k.endsWith(".d.ts")),w=x.length>0?x:u.fileNames;n=T__default.default.createProgram({rootNames:w,options:u.options}),common.debug("engine",`TypeScript Program initialized in ${(performance.now()-a).toFixed(2)}ms with ${w.length} roots (tsconfig had ${u.fileNames.length})`);}else common.debug("engine","Could not find tsconfig.json; TypeChecker and ProjectContext will be unavailable.");let f=n?.getTypeChecker();if(n&&o.buildProjectContext!==false)try{s=q(n,t,e);}catch(p){let u=p instanceof Error?p.message:String(p);common.debug("engine",`ProjectContext build failed (non-fatal): ${u}`);}else n&&common.debug("engine","Skipping ProjectContext build for type-checker-only chunk");return {...l,getTypeChecker:async p=>f,getProjectContext:()=>s,getTsSourceFile:p=>n?.getSourceFile(p),warmup:async()=>{common.debug("engine",`Type-aware context ready: ${n?`ts.Program with ${n.getSourceFiles().length} source files`:"no tsconfig found, TypeChecker unavailable"}`);},dispose:()=>{s=void 0,f=void 0,n=void 0,l.dispose();}}};var O=(e,t,r)=>{let o=performance.now()-t,n=e.flatMap(i=>i.failures),s=new Set(n.map(i=>i.filePath)),l=n.filter(i=>rt(i.severity)).length,a=n.filter(i=>at(i.severity)).length;return {totalFiles:s.size,totalErrors:l,totalWarnings:a,duration:o,cacheHitRate:r}},rt=e=>e==="error",at=e=>e==="warn";var ue=()=>{throw Error("[ngcompass] Rule executor not configured. Call configureRuleExecutor(executeBatchedNewEngineRules, isNewEngineRule) before running analysis.")},me=()=>false,ot=(e,t)=>{ue=e,me=t;},fe=()=>ue,he=()=>me;var B=async(e,t,r)=>{if(e.length===0)return [];let o=e[0].filePath,n=new K(t),s=new Map,l=[];for(let a of e){let i;if(!he()(a.ruleName)){common.debug("engine",`Skipping task ${a.taskId}: Rule "${a.ruleName}" not registered in engine.`),l.push({ruleName:a.ruleName,taskId:a.taskId,failures:[]});continue}try{i=common.stableSerialize(a.options||{});}catch(f){let p=f instanceof common.SerializationError?f.message:String(f);common.debug("engine",`Skipping task ${a.taskId}: failed to serialize options \u2014 ${p}`),r?.record(common.createInfrastructureError("SerializationError",{cause:p,phase:"engine",recoverable:true})),l.push({ruleName:a.ruleName,taskId:a.taskId,failures:[]});continue}let d=s.get(i)??{options:a.options,ruleNames:[],taskIds:[],severities:new Map};d.ruleNames.push(a.ruleName),d.taskIds.push(a.taskId),d.severities.set(a.ruleName,a.severity),s.set(i,d);}for(let a of s.values())try{let i=new Set(a.taskIds),d=e.some(x=>i.has(x.taskId)&&x.inputs.template?.needsAst),f=await n.build(o,a.options,d),p=fe()(a.ruleNames,f),u=new Map;for(let x=0;x<a.ruleNames.length;x++){let w=a.ruleNames[x],k=a.taskIds[x],y=u.get(w)??[];y.push(k),u.set(w,y);}for(let x of p){let w=u.get(x.ruleName),k=a.severities.get(x.ruleName),y=k?{...x,failures:x.failures.map(g=>({...g,severity:k}))}:x;if(w&&w.length>0){let g=w.shift();l.push({...y,taskId:g});}else l.push(y);}}catch(i){let d=i instanceof Error?i.message:String(i);common.debug("engine",`Failed to execute batch for ${o}: ${d}`),r?.record(common.createInfrastructureError("ParseError",{filePath:o,cause:d,phase:"engine",recoverable:true}));for(let f=0;f<a.ruleNames.length;f++)l.push({ruleName:a.ruleNames[f],failures:[],taskId:a.taskIds[f]});}return l};var ke=async(e,t,r,o,n,s)=>{let l,{Worker:a}=await import('worker_threads'),i=o??Math.max(2,V__default.default.cpus().length),d=await gt();if(!d)return common.debug("workers","Execution worker not found, falling back to local execution."),ft(e,t,r,n??i,s);let f=yt(e,i),p=0,u=()=>{p++,common.debug("workers",`Worker progress: ${p}/${i} complete`);},x=[],w=f.map(y=>new Promise((g,c)=>{let h=false,S=new a(d,{workerData:{rootDir:t,tasks:y}});x.push(S);let m=()=>{clearTimeout(E),S.removeAllListeners();},E=setTimeout(()=>{h||(h=true,m(),u(),S.terminate(),c(Error(`Worker timed out after ${6e4/1e3}s`)));},6e4);S.on("message",C=>{ht(C)?s?.(C):h||(h=true,m(),C.errors&&C.errors.length>0&&C.errors.forEach(v=>{common.debug("workers",`Worker failed task ${v.task.taskId}: ${v.error}`);}),u(),g(C.results));}),S.on("error",C=>{h||(h=true,m(),u(),c(C instanceof Error?C:Error(String(C))));}),S.on("exit",C=>{if(h||C===0)return;h=true,m();let v=common.createInfrastructureError("WorkerCrash",{cause:`Worker exited with code ${C}`,phase:"engine",recoverable:true,details:{exitCode:C}});common.debug("workers",`Worker crashed: ${v.cause}`),c(Error(v.cause));});}));try{l=await Promise.all(w);}catch(y){throw await Promise.allSettled(x.map(g=>g.terminate())),y}let k=l.flat();return common.Ok({results:k,parseErrors:[],stats:O(k,r)})},ft=async(e,t,r,o,n)=>{let s=_(t),l=ee__default.default(o),a=planner.groupTasksByFile(e),i=(await Promise.all(Array.from(a.values()).map(d=>l(async()=>{let f=d[0]?.filePath,p=performance.now(),u=await B(d,s);return f&&s.evict(f),f&&n?.(dt(f,d.length,u,performance.now()-p)),u})))).flat();return common.Ok({results:i,parseErrors:[],stats:O(i,r)})},ht=e=>!!e&&typeof e=="object"&&e.kind==="file-progress",dt=(e,t,r,o)=>{let n=0,s=0;for(let l of r)for(let a of l.failures)a.severity==="error"?n++:a.severity==="warn"&&s++;return {filePath:e,taskCount:t,issueCount:n+s,errorCount:n,warningCount:s,duration:o}},gt=async()=>{let e=F.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));try{let t=module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))).resolve("@ngcompass/rules/execution-worker");if(fs.existsSync(t))return t}catch{}for(let t of [F.join(e,"..","..","rules","dist","execution-worker.js"),F.join(e,"..","..","rules","dist","execution-worker.cjs")])if(fs.existsSync(t))return t;return null},yt=(e,t)=>{let r=Array.from(planner.groupTasksByFile(e).values()).sort((s,l)=>l.length-s.length),o=Array.from({length:t},()=>[]),n=Array(t).fill(0);for(let s of r){let l=0,a=n[0];for(let i=1;i<t;i++)n[i]<a&&(a=n[i],l=i);o[l].push(...s),n[l]+=s.length;}return o};function Te(e){if(!e||typeof e!="object"||typeof e.ruleName!="string"||!Array.isArray(e.failures))return false;for(let t of e.failures)if(!t||typeof t!="object"||typeof t.filePath!="string"||typeof t.line!="number"||typeof t.column!="number"||typeof t.severity!="string")return false;return true}var ea=async(e,t)=>{try{if(e.precomputedAnalysis){if((function(m){if(!m||typeof m!="object"||!Array.isArray(m.results)||!Array.isArray(m.parseErrors)||!m.stats||typeof m.stats!="object")return !1;let E=m.stats;return typeof E.totalFiles=="number"&&typeof E.totalErrors=="number"&&typeof E.totalWarnings=="number"&&typeof E.duration=="number"})(e.precomputedAnalysis))return common.debug("engine","Returning precomputed analysis from cache (global hash match)"),common.Ok(e.precomputedAnalysis);common.debug("engine","Precomputed analysis failed schema validation \u2014 discarding stale cache entry and re-running analysis");}let r=performance.now(),{tasks:o,skippedTasks:n,cachedResults:s}=e,l=V__default.default.cpus().length,a=Math.max(1,Math.min(4,l-1)),i=Math.max(1,Math.min(t.maxWorkers??a,l)),d=t.parallelThreshold??150,f=o.filter(m=>!!m.needsTypeChecker||!!m.needsProjectContext),p=o.filter(m=>!m.needsTypeChecker&&!m.needsProjectContext);common.debug("engine",`workerTasks: ${p.length}, typeAwareTasks: ${f.length}`);let u=o.length+n.length,x=n.length,w=m=>{x=Math.min(x+m,u),t.onProgress?.(x,u);};n.length>0&&t.onProgress?.(x,u);let k=[];if(p.length>0)if(p.length>d){common.debug("engine",`Running analysis on ${p.length} syntax-only tasks using workers (max: ${i})...`);let m=await ke(p,t.rootDir,r,i,void 0,t.onFileProgress);if(!m.ok)return m;k=m.data.results,w(p.length);}else common.debug("engine",`Running analysis on ${p.length} syntax-only tasks locally with batching (concurrency: ${i})...`),k=await re(p,t.rootDir,i,!1,t.errorCollector,void 0,void 0,!0,void 0,w,t.onFileProgress);if(f.length>0&&!t.skipTypeCheck){let m=planner.groupTasksByFile(f).size,E=t.typeAwareChunkSize??(m>=1e3?50:100),C=vt(t.typeAwareConcurrency,i),v=Pt(t.typeAwareFileConcurrency,i),$=await Tt(f,t.rootDir,C,v,E,t,w,t.onFileProgress);k=[...k,...$];}else f.length>0&&t.skipTypeCheck&&common.debug("engine",`Skipping ${f.length} type-aware tasks (--skip-type-check)`);let y=await Lt(n,s,t.cache),g=[...k,...y],c=o.length+n.length,h=c>0?y.length/c:void 0,S={results:g,parseErrors:[],stats:O(g,r,h)};if(t.cache&&e.globalHash&&S.results.length<=2e4){common.debug("engine","Caching full analysis result for global hash..."),t.debug&&(common.debug("engine",`Analysis Results: ${S.results.length} items`),S.results.length>0&&common.debug("engine",`Sample item keys: ${Object.keys(S.results[0]).join(", ")}`));try{await t.cache.analysis.set(e.globalHash,S);}catch(m){let E=m instanceof Error?m.message:String(m);common.debug("engine",`Failed to cache analysis result: ${E}`),t.errorCollector?.record(common.createInfrastructureError("IOError",{cause:`Failed to write analysis cache: ${E}`,phase:"engine",recoverable:!0}));}}else t.cache&&e.globalHash&&common.debug("engine",`Skipping full analysis cache: ${S.results.length} results exceeds 20000 result safety limit`);return common.Ok(S)}catch(r){return common.Err(r instanceof Error?r:Error(String(r)))}},re=async(e,t,r,o,n,s,l,a=true,i,d,f)=>{let p=o?Z(t,s??[],l,{buildProjectContext:a,programRootFiles:i}):_(t);o&&(await p.warmup(),common.debug("engine",`Phase 1 complete \u2014 TypeScript Program ready. Starting Phase 2: ${r} concurrent file batches.`));let u=planner.groupTasksByFile(e);common.debug("engine",`Grouped ${e.length} tasks into ${u.size} file batches`);let x=ee__default.default(r),w=await Promise.all(Array.from(u.values()).map(k=>x(async()=>{let y=k[0]?.filePath,g=performance.now();try{let c=await B(k,p);return p.evict(y),d?.(k.length),y&&f?.(Ee(y,k.length,c,performance.now()-g,o)),c}catch(c){let h=c instanceof Error?c.message:String(c);return p.evict(y),d?.(k.length),y&&f?.(Ee(y,k.length,[],performance.now()-g,o)),n?.record(common.createInfrastructureError("IOError",{filePath:y,cause:`Batch execution failed: ${h}`,phase:"engine",recoverable:true})),[]}})));return p.dispose(),w.flat().filter(k=>k!==null)},Tt=async(e,t,r,o,n,s,l,a)=>{let i=planner.groupTasksByFile(e),d=await Ft(i,t,s.typeAwareChunkStrategy??"dependency"),f=s.typeAwareIsolation??"auto",p=f==="process"||f==="auto"&&d.length>=500,u=Mt();common.debug("engine",`Type-aware: ${e.length} tasks across ${d.length} files; requested chunk size ${n}; adaptive cap ${u}; chunk concurrency=${r}; file concurrency=${o}; isolation=${p?"process":"in-process"}`);let x=[],w=ee__default.default(r);for(let k of bt(d,n,u,r)){let y=await Promise.all(k.map(g=>w(async()=>{common.debug("engine",`Type-aware chunk ${g.index}: ${g.files.length} files, ${g.programRootFiles.length} TS roots, ${g.tasks.length} tasks`);let c=p?await It(g.tasks,t,g.files,g.programRootFiles,g.buildProjectContext,o,s,a):await re(g.tasks,t,o,true,s.errorCollector,g.files,s.parserOptions,g.buildProjectContext,g.programRootFiles,l,a);return p&&l?.(g.tasks.length),c})));x.push(...y.flat()),Et();}return x},Et=()=>{let e=globalThis.gc;typeof e=="function"&&e();},vt=(e,t)=>Math.max(1,Math.min(e==null||Number.isNaN(e)?1:Math.floor(e),t,4)),Pt=(e,t)=>Math.max(1,Math.min(e==null||Number.isNaN(e)?1:Math.floor(e),t,8)),bt=(e,t,r,o)=>{let n=[],s=At(t,r),l=0,a=1;for(let i=0;i<e.length;){let d=[];for(let p=0;p<o&&i<e.length;p++){let u=e.slice(i,i+s),x=u.flatMap(([,k])=>k),w=be(x);w.length>0?d.push({index:a,tasks:x,files:u.map(([k])=>k),programRootFiles:w,buildProjectContext:x.some(k=>!!k.needsProjectContext)}):common.debug("engine",`Skipping type-aware chunk ${a} with no TypeScript roots (${x.length} tasks)`),a++,i+=u.length;}d.length>0&&n.push(d);let f=$t(s,l,r);s=f.chunkSize,l=f.lowHeapStreak;}return n},At=(e,t)=>Number.isFinite(e)?Math.max(10,Math.min(t,Math.floor(e))):t,Mt=()=>{let e,t=V__default.default.totalmem()/1073741824,r=V__default.default.freemem()/1073741824,o=ve__default.default.getHeapStatistics().heap_size_limit/1073741824,n=V__default.default.cpus().length;return e=r<1.5||t<4?100:r<3||t<8?300:r<6||t<16?650:r<12||t<32?1e3:1500,n<=4?e=Math.min(e,500):n>=12&&(e=Math.min(2e3,Math.round(1.2*e))),o<2?e=Math.min(e,250):o<4&&(e=Math.min(e,650)),e=Math.max(10,Math.min(2e3,e)),common.debug("engine",`Adaptive type-aware chunk cap: ${e} files (free memory ${r.toFixed(1)}GB, total memory ${t.toFixed(1)}GB, V8 heap limit ${o.toFixed(1)}GB, CPUs ${n})`),e},$t=(e,t,r)=>{let o=process.memoryUsage(),n=ve__default.default.getHeapStatistics().heap_size_limit,s=n>0?o.heapUsed/n:0;if(s>=.94&&e>10){let l=Math.max(10,Math.floor(e/2));return common.debug("engine",`Critical heap pressure after type-aware chunk (${Math.round(100*s)}% of V8 heap limit); reducing chunk size to ${l}`),{chunkSize:l,lowHeapStreak:0}}if(s>=.88&&e>10){let l=Math.max(10,Math.floor(.8*e));return common.debug("engine",`High heap pressure after type-aware chunk (${Math.round(100*s)}% of V8 heap limit); reducing chunk size to ${l}`),{chunkSize:l,lowHeapStreak:0}}if(s<=.35&&e<r){let l=t+1;if(l<3)return {chunkSize:e,lowHeapStreak:l};let a=Math.min(r,e+Math.max(10,Math.floor(.1*e)));return common.debug("engine",`Sustained low heap pressure after type-aware chunk (${Math.round(100*s)}% of V8 heap limit); increasing chunk size to ${a}`),{chunkSize:a,lowHeapStreak:0}}return {chunkSize:e,lowHeapStreak:0}},be=e=>{let t=new Set;for(let r of e){let o=r.inputs.typescript.path;(o.endsWith(".ts")||o.endsWith(".tsx"))&&!o.endsWith(".d.ts")&&t.add(o);}return [...t]},Ft=async(e,t,r)=>{let o=Array.from(e.entries());if(r==="simple")return common.debug("engine",`Type-aware chunk ordering: simple path sort for ${o.length} files`),Q(o);common.debug("engine",`Type-aware chunk ordering: dependency pre-pass for ${o.length} files`);let n=performance.now(),s=await Nt(o,t,n+5e3);return s?(common.debug("engine",`Dependency chunk ordering complete in ${(performance.now()-n).toFixed(1)}ms`),s):(common.debug("engine","Dependency chunk ordering exceeded 5000ms; falling back to simple path sort"),Q(o))},Nt=async(e,t,r)=>{let o=new Map,n=ee__default.default(64);for(let s=0;s<e.length;s+=64){if(performance.now()>r)return null;let l=e.slice(s,s+64);await Promise.all(l.map(a=>n(async()=>{let[i,d]=a,f=be(d)[0]??i,p=await Rt(f,t),u=o.get(p)??[];u.push(a),o.set(p,u);})));}return [...o.entries()].sort(([s],[l])=>s.localeCompare(l)).flatMap(([,s])=>Q(s))},Q=e=>[...e].sort(([t],[r])=>t.localeCompare(r)),Rt=async(e,t)=>{let r=F.dirname(e);try{let o=await promises.readFile(e,"utf8"),n=_t(o);return n?F.join(r,n.split("/")[0]??""):r}catch{return F.dirname(F.join(t,e))}},_t=e=>/\b(?:import|export)\b[^'"]*['"](\.{1,2}\/[^'"]+)['"]/g.exec(e)?.[1]??null,It=async(e,t,r,o,n,s,l,a)=>{let i=await Dt();return i?new Promise((d,f)=>{let p=child_process.fork(i,[],{cwd:t,execArgv:[],stdio:["ignore","pipe","pipe","ipc"]}),u=false,x=setTimeout(()=>{u||(u=true,p.kill(),f(Error("Type-aware child process timed out after 600s")));},6e5);p.stdout?.on("data",w=>common.debug("engine",`[type-aware-child] ${String(w).trim()}`)),p.stderr?.on("data",w=>common.debug("engine",`[type-aware-child:stderr] ${String(w).trim()}`)),p.on("message",w=>{if(jt(w))return void a?.(w);if(zt(w)){if(u)return;u=true,clearTimeout(x),d(w.results);return}if(Wt(w)){if(u)return;u=true,clearTimeout(x),f(Error(w.error));}}),p.on("error",w=>{u||(u=true,clearTimeout(x),f(w));}),p.on("exit",w=>{u||(u=true,clearTimeout(x),f(Error(`Type-aware child process exited before completion with code ${w}`)));}),p.send({rootDir:t,tasks:e,files:r,programRootFiles:o,parserOptions:l.parserOptions,buildProjectContext:n,fileConcurrency:s});}):(common.debug("engine","Type-aware child worker not found; falling back to in-process execution"),re(e,t,s,true,l.errorCollector,r,l.parserOptions,n,o,void 0,a))},Dt=async()=>{let e=F.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));try{let t=module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))).resolve("@ngcompass/rules/type-aware-worker");if(fs.existsSync(t))return t}catch{}for(let t of [F.join(e,"..","..","rules","dist","type-aware-worker.js"),F.join(e,"..","..","rules","dist","type-aware-worker.cjs")])if(fs.existsSync(t))return t;return null},jt=e=>!!e&&typeof e=="object"&&e.kind==="file-progress"&&typeof e.filePath=="string"&&typeof e.taskCount=="number"&&typeof e.issueCount=="number"&&typeof e.errorCount=="number"&&typeof e.warningCount=="number"&&typeof e.duration=="number",zt=e=>!!e&&typeof e=="object"&&e.kind==="complete"&&Array.isArray(e.results),Wt=e=>!!e&&typeof e=="object"&&e.kind==="error"&&typeof e.error=="string",Ee=(e,t,r,o,n)=>{let s=0,l=0;for(let a of r)for(let i of a.failures)i.severity==="error"?s++:i.severity==="warn"&&l++;return {filePath:e,taskCount:t,issueCount:s+l,errorCount:s,warningCount:l,duration:o,typeAware:n}},Lt=async(e,t,r)=>{if(e.length===0)return [];common.debug("engine",`Retrieving results for ${e.length} skipped tasks...`);let o=[],n=[];if(t)for(let s of e){let l=t.get(s.taskId);l&&Te(l)?o.push(l):n.push(s);}else n.push(...e);if(n.length>0&&r){common.debug("engine",`Fetching ${n.length} results from cache service...`);let s=n.map(a=>a.taskId),l=await r.results.getMany(s);for(let a of n){let i=l.get(a.taskId);i&&Te(i)&&o.push(i);}}return common.debug("engine",`Retrieved ${o.length} results from cache`),o};var Ae=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],Me=class{frameIndex=0;interval=null;message="";start(t){this.message=t,this.frameIndex=0,process.stdout.write("\x1B[?25l"),this.interval=setInterval(()=>{process.stdout.write(`\r${Ae[this.frameIndex]} ${this.message}`),this.frameIndex=(this.frameIndex+1)%Ae.length;},80);}update(t){this.message=t;}stop(t){this.interval&&(clearInterval(this.interval),this.interval=null),process.stdout.write("\r\x1B[K"),t&&process.stdout.write(t+` | ||
| `),process.stdout.write("\x1B[?25h");}};exports.BUDGET_MS_PER_FILE_WITHOUT_TYPES=Re;exports.BUDGET_MS_PER_FILE_WITH_TYPES=_e;exports.MIN_WORKER_COUNT=Fe;exports.RuleContextFactory=K;exports.SPINNER_FRAME_INTERVAL_MS=Ne;exports.STREAM_TO_NODE_TYPE=$e;exports.Spinner=Me;exports.WORKER_TIMEOUT_MS=oe;exports.buildProjectContext=q;exports.buildVisitorMap=ae;exports.calculateStats=O;exports.configureRuleExecutor=ot;exports.createAnalysisContext=_;exports.createAnyAngularClassRule=Bt;exports.createCallExpressionRule=Ut;exports.createComponentRule=Ot;exports.createDecoratedPropertyRule=Ht;exports.createNewExpressionRule=Vt;exports.createTemplateAttributeRule=Kt;exports.createTemplateBlockRule=Yt;exports.createTemplateExpressionRule=Gt;exports.createTemplateRule=qt;exports.createTypeAwareAnalysisContext=Z;exports.executeBatchedTasks=B;exports.runAnalysis=ea;exports.runAnalysisParallel=ke;exports.runSinglePassAnalysis=rr;//# sourceMappingURL=index.cjs.map | ||
| 'use strict';var ast=require('@ngcompass/ast'),common=require('@ngcompass/common'),C=require('typescript'),child_process=require('child_process'),fs=require('fs'),promises=require('fs/promises'),module$1=require('module'),re=require('os'),$=require('path'),url=require('url'),Ue=require('v8'),planner=require('@ngcompass/planner'),ye=require('p-limit'),lruCache=require('lru-cache'),Z=require('process');var _documentCurrentScript=typeof document!=='undefined'?document.currentScript:null;function _interopDefault(e){return e&&e.__esModule?e:{default:e}}function _interopNamespace(e){if(e&&e.__esModule)return e;var n=Object.create(null);if(e){Object.keys(e).forEach(function(k){if(k!=='default'){var d=Object.getOwnPropertyDescriptor(e,k);Object.defineProperty(n,k,d.get?d:{enumerable:true,get:function(){return e[k]}});}})}n.default=e;return Object.freeze(n)}var C__default=/*#__PURE__*/_interopDefault(C);var re__default=/*#__PURE__*/_interopDefault(re);var $__namespace=/*#__PURE__*/_interopNamespace($);var Ue__default=/*#__PURE__*/_interopDefault(Ue);var ye__default=/*#__PURE__*/_interopDefault(ye);var Z__default=/*#__PURE__*/_interopDefault(Z);var fr=(e,t,r)=>({name:e,streamType:"AngularClass",handle:t,meta:r}),gr=(e,t,r)=>({name:e,streamType:"AnyAngularClass",handle:t,meta:r}),hr=(e,t)=>({name:e,streamType:"DecoratedProperty",handle:t}),dr=(e,t,r)=>({name:e,streamType:"TemplateExpression",handle:t,meta:r}),yr=(e,t,r)=>({name:e,streamType:"TemplateAttribute",handle:t,meta:r}),xr=(e,t,r)=>({name:e,streamType:"CallExpression",handle:t,meta:r}),wr=(e,t,r)=>({name:e,streamType:"NewExpression",handle:t,meta:r}),kr=(e,t,r)=>({name:e,streamType:"TemplateBlock",handle:t,meta:r}),Sr=(e,t,r)=>({name:e,streamType:"Template",handle:t,meta:r});var Je={AngularClass:"ClassDeclaration",AnyAngularClass:"ClassDeclaration",DecoratedProperty:"PropertyDefinition",TemplateExpression:"__template_expression__",TemplateAttribute:"__template_attribute__",TemplateBlock:"__template_block__",Template:"__template_analysis__",CallExpression:"CallExpression",NewExpression:"NewExpression"};function Ce(e,t){let r=new Map;for(let s of e){let n=Je[s.streamType];if(!n||n.startsWith("__"))continue;let a=t[s.streamType];if(!a)continue;let i={ruleName:s.name,filter:a,handle:s.handle.bind(s)},o=r.get(n);o?o.push(i):r.set(n,[i]);}return r}var Xe=2,Ze=80,Te=6e4,Qe=2,et=5;var Q=(e,t,r,s,n,a,i)=>{if(t.length!==0)for(let o of e)for(let l=0;l<t.length;l++){let u=t[l],g=a?performance.now():0;try{let p=u.handle(o,r);if(p){let m=s.get(u.name)??[];Array.isArray(p)?m.push(...p):m.push(p),s.set(u.name,m);}}catch(p){i?.record(common.createInfrastructureError("RuleExecutionError",{cause:`Rule ${u.name} failed on template node: ${p instanceof Error?p.message:String(p)}`,recoverable:true,phase:"engine",details:{ruleName:u.name,errorName:p instanceof Error?p.name:void 0}}));}if(a){let p=performance.now()-g,m=n.get(u.name);m&&(m.totalMs+=p,m.invocations++);}}},Ar=(e,t,r)=>{let{program:s}=t;if(!s)return {results:e.map(c=>({ruleName:c.name,failures:[]})),performance:{traversalMs:0,nodesVisited:0,ruleTimings:[],cacheStats:{hits:0,misses:0},budgetViolations:[],hasBudgetViolations:false}};let n=performance.now(),a=Ce(e,{AngularClass:ast.toAngularClassStream,AnyAngularClass:ast.toAnyAngularClassStream,DecoratedProperty:ast.toDecoratedPropertyStream,CallExpression:ast.toCallExpressionStream,NewExpression:ast.toNewExpressionStream}),i=e.filter(c=>c.streamType==="TemplateExpression"),o=e.filter(c=>c.streamType==="TemplateAttribute"),l=e.filter(c=>c.streamType==="TemplateBlock"),u=e.filter(c=>c.streamType==="Template"),g=new Map,p=new Map,m=r?.collectRuleTimings??common.isDebugEnabled(),d=0;for(let c of e)p.set(c.name,{ruleName:c.name,totalMs:0,invocations:0});if(ast.resetComponentCacheStats(),ast.walkProgram(s,c=>{if(!c?.type)return;d++;let h=a.get(c.type);if(h)for(let S=0;S<h.length;S++){let f=h[S],E=m?performance.now():0;try{let b=f.filter(c);if(b!==null){let v=f.handle(b,t);if(v){let F=g.get(f.ruleName)??[];Array.isArray(v)?F.push(...v):F.push(v),g.set(f.ruleName,F);}}}catch(b){r?.errorCollector?.record(common.createInfrastructureError("RuleExecutionError",{cause:`Rule ${f.ruleName} failed: ${b instanceof Error?b.message:String(b)}`,recoverable:true,phase:"engine",details:{ruleName:f.ruleName,errorName:b instanceof Error?b.name:void 0}}));}if(m){let b=performance.now()-E,v=p.get(f.ruleName);v&&(v.totalMs+=b,v.invocations++);}}}),t.template&&(i.length>0||o.length>0||l.length>0||u.length>0)){let c=ast.analyzeTemplate(t.template),h=t.templateFilePath&&t.templateFileContent&&t.templateLocator?{...t,filePath:t.templateFilePath,fileContent:t.templateFileContent,locator:t.templateLocator}:t;Q(c.expressions,i,h,g,p,m,r?.errorCollector),Q(c.attributes,o,h,g,p,m,r?.errorCollector),Q(c.blocks,l,h,g,p,m,r?.errorCollector),Q([c],u,h,g,p,m,r?.errorCollector);}let w=[];for(let c of e)w.push({ruleName:c.name,failures:g.get(c.name)??[]});let k=performance.now()-n,y=[],x=t.typeChecker?5:2;if(k>x&&y.push(`Total traversal time ${k.toFixed(2)}ms exceeds budget ${x}ms`),m)for(let c of p.values()){if(c.invocations===0)continue;let h=c.totalMs/c.invocations;h>1&&y.push(`Rule ${c.ruleName} averages ${h.toFixed(2)}ms per invocation (threshold: 1ms)`);}return {results:w,performance:{traversalMs:k,nodesVisited:d,ruleTimings:Array.from(p.values()),cacheStats:ast.getComponentCacheStats(),budgetViolations:y,hasBudgetViolations:y.length>0}}};var ee=class{context;constructor(t){this.context=t;}async build(t,r,s){let n,a,i,o,l,[u,g]=await Promise.all([this.context.readFile(t),this.context.getProgram(t)]),p=new common.Locator(u);this.context.getTypeChecker&&(n=await this.context.getTypeChecker(t)),s&&(a=await this.context.getTemplate(t));let m=this.context.getProjectContext?.(),d=this.context.getAngularTypes?.();s&&!a&&m&&(i=this.resolveExternalTemplatePath(t,m))&&(a=await this.context.getTemplate(i),l=new common.Locator(o=await this.context.readFile(i)));let w=m?await this.buildCrossRef(t,a,m):void 0;return {filePath:t,fileContent:u,locator:p,program:g,typeChecker:n,template:a,templateFilePath:i,templateFileContent:o,templateLocator:l,options:r,project:m,crossRef:w,angularTypes:d}}resolveExternalTemplatePath(t,r){let s=t.endsWith(".component.ts")?t:r.templateToComponent.get(t);if(s)return r.componentGraph.get(s)?.templatePath}async buildCrossRef(t,r,s){let n,a,i,o=t.endsWith(".component.ts")?t:s.templateToComponent.get(t);if(!o)return;let l=s.componentGraph.get(o),u=l?.templatePath,g=l?.stylePaths??[],p=l?.specPath;try{let d=await this.getComponentSourceFile(o);d&&(n=(function(w){let k=new Set;for(let y of w.statements)if(C__default.default.isClassDeclaration(y))for(let x of y.members){let c=C__default.default.canHaveModifiers(x)?C__default.default.getModifiers(x):void 0;if(c&&c.some(S=>S.kind===C__default.default.SyntaxKind.PrivateKeyword||S.kind===C__default.default.SyntaxKind.ProtectedKeyword))continue;let h=x.name;h&&!C__default.default.isPrivateIdentifier(h)&&(C__default.default.isIdentifier(h)||C__default.default.isStringLiteral(h))&&k.add(h.text);}return k})(d),a=(function(w){let k=new Set;for(let y of w.statements)if(C__default.default.isClassDeclaration(y))for(let x of y.members){if(!C__default.default.isPropertyDeclaration(x)||!x.initializer||!x.name||C__default.default.isPrivateIdentifier(x.name))continue;let c=(function(h){let S=C__default.default.isAsExpression(h)||C__default.default.isSatisfiesExpression(h)?h.expression:h;if(!C__default.default.isCallExpression(S))return;let f=S.expression;return C__default.default.isIdentifier(f)?f.text:C__default.default.isPropertyAccessExpression(f)?C__default.default.isIdentifier(f.expression)&&f.expression.text==="input"?"input":f.name.text:void 0})(x.initializer);c&&ut.has(c)&&(C__default.default.isIdentifier(x.name)||C__default.default.isStringLiteral(x.name))&&k.add(x.name.text);}return k})(d));}catch{}let m=r;if(!m&&u)try{m=await this.context.getTemplate(u);}catch{}if(m)try{i=(function(d){let w=new Set,k=/\{\{([\s\S]*?)\}\}/g;for(let y of d.rootNodes)(function x(c){if(c&&typeof c=="object"){if(Array.isArray(c.attrs))for(let h of c.attrs){if((typeof h.name=="string"?h.name:"").startsWith("("))continue;let S=h.value;typeof S=="string"&&S&&ve(S,w);}if(typeof c.value=="string"&&c.value){let h;for(k.lastIndex=0;(h=k.exec(c.value))!==null;)ve(h[1],w);}if(Array.isArray(c.children))for(let h of c.children)x(h);}})(y);return w})(m);}catch{}return {componentPath:o,templatePath:u,stylePaths:g,specPath:p,publicMembers:n,signalMembers:a,templateReferences:i}}async getComponentSourceFile(t){let r=this.context.getTsSourceFile?.(t);if(r)return r;let s=await this.context.readFile(t);return C__default.default.createSourceFile(t,s,C__default.default.ScriptTarget.Latest,true)}},ut=new Set(["signal","computed","linkedSignal","input","model","toSignal"]),Pe=/\b([a-zA-Z_$][a-zA-Z0-9_$]*)\b/g;function ve(e,t){let r;for(Pe.lastIndex=0;(r=Pe.exec(e))!==null;)t.add(r[1]);}var yt=new Set([".css",".scss",".sass",".less"]),B=e=>{let t=new lruCache.LRUCache({max:128}),r=new lruCache.LRUCache({max:64}),s=new lruCache.LRUCache({max:64}),n=new lruCache.LRUCache({max:64}),a=o=>{let l=t.get(o);if(l)return l;let u=xt(e,o);return t.set(o,u),u},i=o=>{let l=r.get(o);if(l)return l;let u=a(o).then(g=>ast.parseTs(g,o).program);return r.set(o,u),u};return {rootDir:e,readFile:a,getProgram:i,getTemplate:o=>{let l=s.get(o);if(l)return l;let u=wt(o,a,i);return s.set(o,u),u},getStyle:o=>{let l=n.get(o);if(l)return l;let u=$__namespace.extname(o).toLowerCase();if(!yt.has(u)){let p=Promise.resolve(void 0);return n.set(o,p),p}let g=a(o).then(p=>ast.parseCss(p,o));return n.set(o,g),g},evict:o=>{t.delete(o),r.delete(o),s.delete(o),n.delete(o);},dispose:()=>{t.clear(),r.clear(),s.clear(),n.clear();}}},xt=async(e,t)=>{try{return await promises.readFile($__namespace.resolve(e,t),"utf-8")}catch(r){let s=r instanceof Error?r.message:String(r);throw Error(`Cannot read file: ${t}. ${s}`)}},wt=async(e,t,r)=>{let s=await kt(e,t,r);if(s&&s.content)return ast.parseHtml(s.content,s.startOffset)},kt=async(e,t,r)=>{let s=$__namespace.extname(e);if(s===".html")return {content:await t(e),startOffset:0};if(s===".ts"){let n=ast.extractTemplateFromProgram(await r(e));return n.content?n:null}return null};var Y=(e,t,r)=>{let s=performance.now()-t,n=e.flatMap(l=>l.failures),a=new Set(n.map(l=>l.filePath)),i=0,o=0;for(let l of n)St(l.severity)?i++:Ct(l.severity)&&o++;return {totalFiles:a.size,totalErrors:i,totalWarnings:o,duration:s,cacheHitRate:r}},St=e=>e==="error",Ct=e=>e==="warn";var U=(e,t,r,s,n)=>{let a=0,i=0;for(let o of r)for(let l of o.failures)l.severity==="error"?a++:l.severity==="warn"&&i++;return {filePath:e,taskCount:t,issueCount:a+i,errorCount:a,warningCount:i,duration:s,typeAware:n}},se=e=>!!e&&typeof e=="object"&&e.kind==="file-progress",ie=e=>!!e&&typeof e=="object"&&e.kind==="file-progress"&&typeof e.filePath=="string"&&typeof e.taskCount=="number"&&typeof e.issueCount=="number"&&typeof e.errorCount=="number"&&typeof e.warningCount=="number"&&typeof e.duration=="number";var Ae=()=>{throw Error("[ngcompass] Rule executor not configured. Call configureRuleExecutor(executeBatchedNewEngineRules, isNewEngineRule) before running analysis.")},Fe=()=>false,Tt=(e,t)=>{Ae=e,Fe=t;},Me=()=>Ae,Ne=()=>Fe;var q=async(e,t,r)=>{if(e.length===0)return [];let s=e[0].filePath,n=new ee(t),a=new Map,i=[];for(let o of e){if(!Ne()(o.ruleName)){common.debug("engine",`Skipping task ${o.taskId}: Rule "${o.ruleName}" not registered in engine.`),i.push({ruleName:o.ruleName,taskId:o.taskId,failures:[]});continue}let l=vt(o,r);if(l===null){i.push({ruleName:o.ruleName,taskId:o.taskId,failures:[]});continue}let u=a.get(l)??Pt(o.options);u.ruleNames.push(o.ruleName),u.taskIds.push(o.taskId),u.severities.set(o.ruleName,o.severity),a.set(l,u);}for(let o of a.values()){let l=await At(o,e,s,n,r);i.push(...l);}return i},Pt=e=>({options:e,ruleNames:[],taskIds:[],severities:new Map}),vt=(e,t)=>{try{return common.stableSerialize(e.options||{})}catch(r){let s=r instanceof common.SerializationError?r.message:String(r);return common.debug("engine",`Skipping task ${e.taskId}: failed to serialize options \u2014 ${s}`),t?.record(common.createInfrastructureError("SerializationError",{cause:s,phase:"engine",recoverable:true})),null}},At=async(e,t,r,s,n)=>{try{let a=new Set(e.taskIds),i=t.some(u=>a.has(u.taskId)&&u.inputs.template?.needsAst),o=await s.build(r,e.options,i),l=Me()(e.ruleNames,o);return Ft(e,l)}catch(a){let i=a instanceof Error?a.message:String(a);return common.debug("engine",`Failed to execute batch for ${r}: ${i}`),n?.record(common.createInfrastructureError("ParseError",{filePath:r,cause:i,phase:"engine",recoverable:true})),e.ruleNames.map((o,l)=>({ruleName:o,failures:[],taskId:e.taskIds[l]}))}},Ft=(e,t)=>{let r=new Map;for(let n=0;n<e.ruleNames.length;n++){let a=e.ruleNames[n],i=r.get(a);i?i.ids.push(e.taskIds[n]):r.set(a,{ids:[e.taskIds[n]],cursor:0});}let s=[];for(let n of t){let a=e.severities.get(n.ruleName),i=a?{...n,failures:n.failures.map(l=>({...l,severity:a}))}:n,o=r.get(n.ruleName);o&&o.cursor<o.ids.length?s.push({...i,taskId:o.ids[o.cursor++]}):s.push(i);}return s};function pe(e,t,r,s){let n=performance.now(),a=(function(y,x,c){var h;let S=new Set;for(let E of y)S.add($__namespace.resolve(x,E));let f=(h=x).endsWith($__namespace.sep)?h:h+$__namespace.sep;for(let E of c.getSourceFiles())!E.isDeclarationFile&&E.fileName.startsWith(f)&&S.add(E.fileName);return S})(t,r,e),{importGraph:i,reverseImportGraph:o,externalDeps:l}=(function(y,x){let c=y.getCompilerOptions(),h=C__default.default.createModuleResolutionCache(y.getCurrentDirectory(),C__default.default.sys.useCaseSensitiveFileNames?b=>b:b=>b.toLowerCase(),c),S=new Map,f=new Map,E=new Map;for(let b of x)S.set(b,new Set),f.set(b,new Set);for(let b of y.getSourceFiles()){if(b.isDeclarationFile||!x.has(b.fileName))continue;let v=b.fileName;for(let F of (function(I){let A=[];return C__default.default.forEachChild(I,function N(T){C__default.default.isImportDeclaration(T)&&C__default.default.isStringLiteral(T.moduleSpecifier)||C__default.default.isExportDeclaration(T)&&T.moduleSpecifier&&C__default.default.isStringLiteral(T.moduleSpecifier)?A.push(T.moduleSpecifier.text):(C__default.default.isCallExpression(T)&&T.expression.kind===C__default.default.SyntaxKind.ImportKeyword&&T.arguments.length>0&&C__default.default.isStringLiteralLike(T.arguments[0])&&A.push(T.arguments[0].text),C__default.default.forEachChild(T,N));}),A})(b)){let I=C__default.default.resolveModuleName(F,v,c,C__default.default.sys,h),A=I.resolvedModule?.resolvedFileName;if(A&&x.has(A)){S.get(v).add(A);let N=f.get(A);N||(N=new Set,f.set(A,N)),N.add(v);}else {let N=(function(T){if(T.startsWith(".")||$__namespace.isAbsolute(T))return null;if(T.startsWith("@")){let _=T.indexOf("/",1);if(_===-1)return null;let W=T.indexOf("/",_+1);return W===-1?T:T.slice(0,W)}let z=T.indexOf("/");return z===-1?T:T.slice(0,z)})(F);if(N){let T=E.get(v);T||(T=new Set,E.set(v,T)),T.add(N);}}}}return {importGraph:S,reverseImportGraph:f,externalDeps:E}})(e,a),u=(function(y,x){let c=new Set;for(let h of y.getSourceFiles()){if(h.isDeclarationFile||!x.has(h.fileName))continue;let{statements:S}=h;S.length!==0&&S.every(f=>C__default.default.isExportDeclaration(f)&&f.moduleSpecifier!=null)&&c.add(h.fileName);}return c})(e,a),g=s??(function(y,x){let c=new Map,h=[".scss",".sass",".css",".less"];for(let S of y){if(!S.endsWith(".component.ts"))continue;let f=$__namespace.dirname(S),E=$__namespace.basename(S,".ts"),b=$__namespace.join(f,E+".html"),v=y.has(b)?b:void 0,F=[];for(let N of h){let T=$__namespace.join(f,E+N);y.has(T)&&F.push(T);}if(!v||F.length===0){let N=(function(T,z,_,W){let we,ke=T.getSourceFile(z);if(!ke)return {templatePath:void 0,stylePaths:[]};let Se=(function(j){for(let L of j.statements){if(!C__default.default.isClassDeclaration(L))continue;let R=C__default.default.canHaveDecorators(L)?C__default.default.getDecorators(L):void 0;if(R)for(let D of R){let O=D.expression;if(!C__default.default.isCallExpression(O)||!C__default.default.isIdentifier(O.expression)||O.expression.text!=="Component")continue;let oe=O.arguments[0];if(oe&&C__default.default.isObjectLiteralExpression(oe))return oe}}})(ke);if(!Se)return {templatePath:void 0,stylePaths:[]};let ne=[];for(let j of Se.properties){if(!C__default.default.isPropertyAssignment(j)||!C__default.default.isIdentifier(j.name))continue;let L=j.name.text;if(L==="templateUrl"){let R=ce(j.initializer);if(R){let D=$__namespace.resolve(_,R);W.has(D)&&(we=D);}continue}if(L==="styleUrl"){let R=ce(j.initializer);if(R){let D=$__namespace.resolve(_,R);W.has(D)&&ne.push(D);}continue}if(L==="styleUrls"&&C__default.default.isArrayLiteralExpression(j.initializer))for(let R of j.initializer.elements){let D=ce(R);if(!D)continue;let O=$__namespace.resolve(_,D);W.has(O)&&ne.push(O);}}return {templatePath:we,stylePaths:ne}})(x,S,f,y);!v&&N.templatePath&&(v=N.templatePath),F.length===0&&N.stylePaths.length>0&&F.push(...N.stylePaths);}let I=$__namespace.join(f,E+".spec.ts"),A=y.has(I)?I:void 0;c.set(S,{tsPath:S,templatePath:v,stylePaths:F,specPath:A});}return c})(a,e),p=(function(y){let x=new Map;for(let[c,h]of y)h.templatePath&&x.set(h.templatePath,c);return x})(g),{ngModuleMap:m,standaloneComponents:d,classToFile:w}=(function(y,x){let c=new Map,h=new Set,S=(function(f,E){let b=new Map;for(let v of f.getSourceFiles())if(!v.isDeclarationFile&&E.has(v.fileName))for(let F of v.statements){if(!C__default.default.isClassDeclaration(F)||!F.name)continue;(C__default.default.canHaveModifiers(F)?C__default.default.getModifiers(F):void 0)?.some(A=>A.kind===C__default.default.SyntaxKind.ExportKeyword)&&b.set(F.name.text,v.fileName);}return b})(y,x);for(let f of y.getSourceFiles())if(!f.isDeclarationFile&&x.has(f.fileName))for(let E of f.statements){if(!C__default.default.isClassDeclaration(E))continue;let b=C__default.default.canHaveDecorators(E)?C__default.default.getDecorators(E):void 0;if(b?.length)for(let v of b){let F=(function(N){let T=N.expression;if(!C__default.default.isCallExpression(T))return;let z=T.expression;if(!C__default.default.isIdentifier(z))return;let _=T.arguments[0],W=_&&C__default.default.isObjectLiteralExpression(_)?_:void 0;return {name:z.text,arg:W}})(v);if(!F)continue;let{name:I,arg:A}=F;if(I==="NgModule"){A&&c.set(f.fileName,{filePath:f.fileName,declarations:new Set(K(A,"declarations")),imports:new Set(K(A,"imports")),exports:new Set(K(A,"exports")),providers:new Set(K(A,"providers")),isStandalone:false});break}if(I==="Component"){A&&Ie(A,"standalone")&&(h.add(f.fileName),c.set(f.fileName,{filePath:f.fileName,declarations:new Set,imports:new Set(A?K(A,"imports"):[]),exports:new Set,providers:new Set(A?K(A,"providers"):[]),isStandalone:true}));break}if(I==="Directive"||I==="Pipe"){A&&Ie(A,"standalone")&&h.add(f.fileName);break}}}return {ngModuleMap:c,standaloneComponents:h,classToFile:S}})(e,a),k=(function(y){let x=0;for(let c of y.values())x+=c.size;return x})(i);return common.debug("engine",`ProjectContext built in ${(performance.now()-n).toFixed(2)}ms \u2014 ${a.size} files, ${k} import edges, ${u.size} barrels, ${l.size} files with ext deps, ${g.size} components, ${p.size} template\u2194component links, ${m.size} NgModules/standalone, ${d.size} standalone`),{importGraph:i,reverseImportGraph:o,ngModuleMap:m,standaloneComponents:d,classToFile:w,componentGraph:g,projectFiles:a,rootDir:r,barrelFiles:u,externalDeps:l,templateToComponent:p}}function ce(e){if(C__default.default.isStringLiteralLike(e)||C__default.default.isNoSubstitutionTemplateLiteral(e))return e.text}function K(e,t){for(let r of e.properties){if(!C__default.default.isPropertyAssignment(r)||!C__default.default.isIdentifier(r.name)||r.name.text!==t)continue;let s=r.initializer;if(!C__default.default.isArrayLiteralExpression(s))break;let n=[];for(let a of s.elements)n.push(...(function i(o){return C__default.default.isIdentifier(o)?[o.text]:C__default.default.isPropertyAccessExpression(o)||C__default.default.isCallExpression(o)?i(o.expression):[]})(a));return n}return []}function Ie(e,t){for(let r of e.properties)if(C__default.default.isPropertyAssignment(r)&&C__default.default.isIdentifier(r.name)&&r.name.text===t)return r.initializer.kind===C__default.default.SyntaxKind.TrueKeyword;return false}var Nt=new Set(["Signal","WritableSignal","InputSignal","ModelSignal","OutputEmitterRef"]),$t=new Set(["WritableSignal","InputSignal","ModelSignal"]),It=new Set(["Subject","BehaviorSubject","ReplaySubject","AsyncSubject"]);function ue(e){if(!e)return _t;let t=new Map,r=(n,a)=>{if(!n)return false;let i=t.get(a);i||(i=new WeakMap,t.set(a,i));let o=i.get(n);if(o!==void 0)return o;let l=(function(u,g){let p=u.getDeclarations();if(!p||p.length===0)return false;let m=`/node_modules/${g}/`;for(let d of p)if(d.getSourceFile().fileName.replace(_e,"/").includes(m))return true;return false})(n,a);return i.set(n,l),l},s=(n,a,i)=>{if(!n)return false;let o=n.aliasSymbol??n.symbol;return !!o&&!!a.has(o.name)&&r(o,i)};return {isFromPackage:r,isFromAngularCore:n=>r(n,"@angular/core"),isSignal:n=>s(n,Nt,"@angular/core"),isWritableSignal:n=>s(n,$t,"@angular/core"),isObservable:n=>{if(!n)return false;let a=n.aliasSymbol??n.symbol;return !!a&&a.name==="Observable"&&r(a,"rxjs")},isSubjectLike:n=>s(n,It,"rxjs"),isHttpClient:n=>{if(!n)return false;let a=n.aliasSymbol??n.symbol;return !!a&&a.name==="HttpClient"&&r(a,"@angular/common")},isInjectionToken:n=>{if(!n)return false;let a=n.aliasSymbol??n.symbol;return !!a&&a.name==="InjectionToken"&&r(a,"@angular/core")},isEventEmitter:n=>{if(!n)return false;let a=n.aliasSymbol??n.symbol;return !!a&&a.name==="EventEmitter"&&r(a,"@angular/core")},isChangeDetectorRef:n=>{if(!n)return false;let a=n.aliasSymbol??n.symbol;return !!a&&a.name==="ChangeDetectorRef"&&r(a,"@angular/core")},isInjectableClass:n=>!!n&&(!!(function(a){let i=a.getDeclarations()?.[0];if(!i||!C__default.default.isClassDeclaration(i)||!C__default.default.canHaveDecorators(i))return false;let o=C__default.default.getDecorators(i);if(!o)return false;for(let l of o){let u=l.expression,g=C__default.default.isCallExpression(u)?u.expression:u;if(C__default.default.isIdentifier(g)&&g.text==="Injectable")return true}return false})(n)||(function(a){let i=a.getDeclarations();if(!i||i.length===0)return false;for(let o of i)if(o.getSourceFile().fileName.replace(_e,"/").includes("/node_modules/@angular/"))return true;return false})(n))}}var _e=/\\/g,_t={isFromPackage:()=>false,isFromAngularCore:()=>false,isSignal:()=>false,isWritableSignal:()=>false,isObservable:()=>false,isSubjectLike:()=>false,isHttpClient:()=>false,isInjectionToken:()=>false,isEventEmitter:()=>false,isChangeDetectorRef:()=>false,isInjectableClass:()=>false};var me=(e,t=[],r,s={})=>{let n,a=B(e);common.debug("engine","Initializing Type-Aware Context (ts.createProgram)...");let i=performance.now(),{program:o}=(function(p,m,d,w){let k=d?.tsconfigRootDir?$__namespace.default.resolve(p,d.tsconfigRootDir):p,y=d?.project?$__namespace.default.resolve(k,d.project):C__default.default.findConfigFile(k,C__default.default.sys.fileExists,"tsconfig.json");if(!y)return {program:void 0};let x=C__default.default.readConfigFile(y,C__default.default.sys.readFile),c=C__default.default.parseJsonConfigFileContent(x.config,C__default.default.sys,k),h=(w.programRootFiles??m).filter(fe),S=h.length>0?h:c.fileNames,f=C__default.default.createProgram({rootNames:S,options:c.options});return common.debug("engine",`Program rootNames: ${S.length} (tsconfig had ${c.fileNames.length})`),{program:f}})(e,t,r,s);o?common.debug("engine",`TypeScript Program ready in ${(performance.now()-i).toFixed(2)}ms (${o.getSourceFiles().length} source files)`):common.debug("engine","Could not find tsconfig.json; TypeChecker and ProjectContext will be unavailable.");let l=o?.getTypeChecker(),u=ue(o);if(o&&s.buildProjectContext!==false)try{n=pe(o,t,e);}catch(p){let m=p instanceof Error?p.message:String(p);common.debug("engine",`ProjectContext build failed (non-fatal): ${m}`);}else o&&common.debug("engine","Skipping ProjectContext build for type-checker-only chunk");let g=o;return {...a,getTypeChecker:p=>Promise.resolve(l),getProjectContext:()=>n,getTsSourceFile:p=>g?.getSourceFile(p),getAngularTypes:()=>u,warmup:()=>(common.debug("engine",`Type-aware context ready: ${g?`ts.Program with ${g.getSourceFiles().length} source files`:"no tsconfig found, TypeChecker unavailable"}`),Promise.resolve()),dispose:()=>{n=void 0,l=void 0,u=void 0,g=void 0,a.dispose();}}},fe=e=>(e.endsWith(".ts")||e.endsWith(".tsx"))&&!e.endsWith(".d.ts");var Oe=async(e,t,r,s,n,a)=>{let i,{Worker:o}=await import('worker_threads'),l=s??Math.max(2,re__default.default.cpus().length),u=await Bt();if(!u)return common.debug("workers","Execution worker not found, falling back to local execution."),We(e,t,r,n??l,a);let g=Ht(e,l),p=[],m=0,d=()=>{m++,common.debug("workers",`Worker progress: ${m}/${l} complete`);},w=g.map(x=>Ot(o,u,t,x,p,d,a));try{i=await Promise.all(w);}catch(x){var k;return await Promise.allSettled(p.map(c=>c.terminate())),common.debug("workers",`Worker execution failed, retrying locally with one file batch at a time: ${(k=x)instanceof Error?k.message:String(k)}`),We(e,t,r,1,a)}let y=i.flat();return common.Ok({results:y,parseErrors:[],stats:Y(y,r)})},Ot=(e,t,r,s,n,a,i)=>new Promise((o,l)=>{let u=false,g=new e(t,{workerData:{rootDir:r,tasks:s}});n.push(g);let p=()=>{clearTimeout(m),g.removeAllListeners();},m=setTimeout(()=>{u||(u=true,p(),a(),g.terminate(),l(Error(`Worker timed out after ${6e4/1e3}s`)));},6e4);g.on("message",d=>{se(d)?i?.(d):u||(u=true,p(),d.errors&&d.errors.length>0&&d.errors.forEach(w=>{common.debug("workers",`Worker failed task ${w.task.taskId}: ${w.error}`);}),a(),o(d.results));}),g.on("error",d=>{u||(u=true,p(),a(),l(d instanceof Error?d:Error(String(d))));}),g.on("exit",d=>{if(u||d===0)return;u=true,p();let w=common.createInfrastructureError("WorkerCrash",{cause:`Worker exited with code ${d}`,phase:"engine",recoverable:true,details:{exitCode:d}});common.debug("workers",`Worker crashed: ${w.cause}`),l(Error(w.cause));});}),We=async(e,t,r,s,n)=>{let a=B(t),i=ye__default.default(s),o=planner.groupTasksByFile(e),l=(await Promise.all(Array.from(o.values()).map(u=>i(async()=>{let g=u[0]?.filePath,p=performance.now(),m=await q(u,a);return g&&(a.evict(g),n?.(U(g,u.length,m,performance.now()-p))),m})))).flat();return common.Ok({results:l,parseErrors:[],stats:Y(l,r)})},Bt=async()=>{let e=$.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));try{let t=module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))).resolve("@ngcompass/rules/execution-worker");if(fs.existsSync(t))return t}catch{}return [$.join(e,"..","..","rules","dist","execution-worker.js"),$.join(e,"..","..","rules","dist","execution-worker.cjs")].find(t=>fs.existsSync(t))??null},Ht=(e,t)=>{let r=Array.from(planner.groupTasksByFile(e).values()).sort((a,i)=>i.length-a.length),s=Array.from({length:t},()=>[]),n=Array(t).fill(0);for(let a of r){let i=0,o=n[0];for(let l=1;l<t;l++)n[l]<o&&(o=n[l],i=l);s[i].push(...a),n[i]+=a.length;}return s};function Ge(e){if(!e||typeof e!="object"||typeof e.ruleName!="string"||!Array.isArray(e.failures))return false;for(let t of e.failures)if(!t||typeof t!="object"||typeof t.filePath!="string"||typeof t.line!="number"||typeof t.column!="number"||typeof t.severity!="string")return false;return true}var $a=async(e,t)=>{try{if(e.precomputedAnalysis){if((function(f){if(!f||typeof f!="object"||!Array.isArray(f.results)||!Array.isArray(f.parseErrors)||!f.stats||typeof f.stats!="object")return !1;let E=f.stats;return typeof E.totalFiles=="number"&&typeof E.totalErrors=="number"&&typeof E.totalWarnings=="number"&&typeof E.duration=="number"})(e.precomputedAnalysis))return common.debug("engine","Returning precomputed analysis from cache (global hash match)"),common.Ok(e.precomputedAnalysis);common.debug("engine","Precomputed analysis failed schema validation \u2014 discarding stale cache entry and re-running analysis");}let r=performance.now(),{tasks:s,skippedTasks:n,cachedResults:a}=e,i=re__default.default.cpus().length,o=Math.max(1,Math.min(4,i-1)),l=Math.max(1,Math.min(t.maxWorkers??o,i)),u=t.parallelThreshold??150,g=s.filter(f=>!!f.needsTypeChecker||!!f.needsProjectContext),p=s.filter(f=>!f.needsTypeChecker&&!f.needsProjectContext);common.debug("engine",`workerTasks: ${p.length}, typeAwareTasks: ${g.length}`);let m=s.length+n.length,d=n.length,w=f=>{d=Math.min(d+f,m),t.onProgress?.(d,m);};n.length>0&&t.onProgress?.(d,m);let k=[];if(p.length>0)if(p.length>u){common.debug("engine",`Running analysis on ${p.length} syntax-only tasks using workers (max: ${l})...`);let f=await Oe(p,t.rootDir,r,l,void 0,t.onFileProgress);if(!f.ok)return f;k=f.data.results,w(p.length);}else common.debug("engine",`Running analysis on ${p.length} syntax-only tasks locally with batching (concurrency: ${l})...`),k=await xe(p,t.rootDir,l,!1,t.errorCollector,void 0,void 0,!0,void 0,w,t.onFileProgress);if(g.length>0&&!t.skipTypeCheck){let f=planner.groupTasksByFile(g).size,E=t.typeAwareChunkSize??(f>=1e3?50:100),b=Zt(t.typeAwareConcurrency,l),v=Qt(t.typeAwareFileConcurrency,l),F=await qt(g,t.rootDir,b,v,E,t,w,t.onFileProgress);k=[...k,...F];}else g.length>0&&t.skipTypeCheck&&common.debug("engine",`Skipping ${g.length} type-aware tasks (--skip-type-check)`);let y=await mr(n,a,t.cache),x=[...k,...y],c=s.length+n.length,h=c>0?y.length/c:void 0,S={results:x,parseErrors:[],stats:Y(x,r,h)};if(t.cache&&e.globalHash&&S.results.length<=2e4){common.debug("engine","Caching full analysis result for global hash..."),t.debug&&(common.debug("engine",`Analysis Results: ${S.results.length} items`),S.results.length>0&&common.debug("engine",`Sample item keys: ${Object.keys(S.results[0]).join(", ")}`));try{await t.cache.analysis.set(e.globalHash,S);}catch(f){let E=f instanceof Error?f.message:String(f);common.debug("engine",`Failed to cache analysis result: ${E}`),t.errorCollector?.record(common.createInfrastructureError("IOError",{cause:`Failed to write analysis cache: ${E}`,phase:"engine",recoverable:!0}));}}else t.cache&&e.globalHash&&common.debug("engine",`Skipping full analysis cache: ${S.results.length} results exceeds 20000 result safety limit`);return common.Ok(S)}catch(r){return common.Err(r instanceof Error?r:Error(String(r)))}},xe=async(e,t,r,s,n,a,i,o=true,l,u,g)=>{let p=s?me(t,a??[],i,{buildProjectContext:o,programRootFiles:l}):B(t);s&&(await p.warmup(),common.debug("engine",`Phase 1 complete \u2014 TypeScript Program ready. Starting Phase 2: ${r} concurrent file batches.`));let m=planner.groupTasksByFile(e);common.debug("engine",`Grouped ${e.length} tasks into ${m.size} file batches`);let d=ye__default.default(r),w=await Promise.all(Array.from(m.values()).map(k=>d(async()=>{let y=k[0]?.filePath,x=performance.now();try{let c=await q(k,p);return p.evict(y),u?.(k.length),y&&g?.(U(y,k.length,c,performance.now()-x,s)),c}catch(c){let h=c instanceof Error?c.message:String(c);return p.evict(y),u?.(k.length),y&&g?.(U(y,k.length,[],performance.now()-x,s)),n?.record(common.createInfrastructureError("IOError",{filePath:y,cause:`Batch execution failed: ${h}`,phase:"engine",recoverable:true})),[]}})));return p.dispose(),w.flat().filter(k=>k!==null)},qt=async(e,t,r,s,n,a,i,o)=>{let l=planner.groupTasksByFile(e),u=await nr(l,t,a.typeAwareChunkStrategy??"dependency"),g=a.typeAwareIsolation??"auto",p=g==="process"||g==="auto"&&u.length>=150,m=rr();common.debug("engine",`Type-aware: ${e.length} tasks across ${u.length} files; requested chunk size ${n}; adaptive cap ${m}; chunk concurrency=${r}; file concurrency=${s}; isolation=${p?"process":"in-process"}`);let d=[],w=ye__default.default(r),k=a.files&&a.files.length>0?a.files:void 0;for(let y of er(u,n,m,r)){let x=await Promise.all(y.map(c=>w(async()=>{common.debug("engine",`Type-aware chunk ${c.index}: ${c.files.length} files, ${c.programRootFiles.length} TS roots, ${c.tasks.length} tasks`);let h=Xt(k,c),S=p?await lr(c.tasks,t,h,c.programRootFiles,c.buildProjectContext,s,a,o):await xe(c.tasks,t,s,true,a.errorCollector,h,a.parserOptions,c.buildProjectContext,c.programRootFiles,i,o);return p&&i?.(c.tasks.length),S})));d.push(...x.flat()),Jt();}return d},Jt=()=>{let e=globalThis.gc;typeof e=="function"&&e();},Xt=(e,t)=>t.buildProjectContext&&e?e:t.files,Zt=(e,t)=>Math.max(1,Math.min(e==null||Number.isNaN(e)?1:Math.floor(e),t,4)),Qt=(e,t)=>Math.max(1,Math.min(e==null||Number.isNaN(e)?1:Math.floor(e),t,8)),er=(e,t,r,s)=>{let n=[],a=tr(t,r),i=0,o=1;for(let l=0;l<e.length;){let u=[];for(let p=0;p<s&&l<e.length;p++){let m=e.slice(l,l+a),d=m.flatMap(([,k])=>k),w=Ve(d);w.length>0?u.push({index:o,tasks:d,files:m.map(([k])=>k),programRootFiles:w,buildProjectContext:d.some(k=>!!k.needsProjectContext)}):common.debug("engine",`Skipping type-aware chunk ${o} with no TypeScript roots (${d.length} tasks)`),o++,l+=m.length;}u.length>0&&n.push(u);let g=ar(a,i,r);a=g.chunkSize,i=g.lowHeapStreak;}return n},tr=(e,t)=>Number.isFinite(e)?Math.max(10,Math.min(t,Math.floor(e))):t,rr=()=>{let e,t=re__default.default.totalmem()/1073741824,r=re__default.default.freemem()/1073741824,s=Ue__default.default.getHeapStatistics().heap_size_limit/1073741824,n=re__default.default.cpus().length;return e=r<1.5||t<4?100:r<3||t<8?300:r<6||t<16?650:r<12||t<32?1e3:1500,n<=4?e=Math.min(e,500):n>=12&&(e=Math.min(2e3,Math.round(1.2*e))),s<2?e=Math.min(e,250):s<4&&(e=Math.min(e,650)),e=Math.max(10,Math.min(2e3,e)),common.debug("engine",`Adaptive type-aware chunk cap: ${e} files (free memory ${r.toFixed(1)}GB, total memory ${t.toFixed(1)}GB, V8 heap limit ${s.toFixed(1)}GB, CPUs ${n})`),e},ar=(e,t,r)=>{let s=process.memoryUsage(),n=Ue__default.default.getHeapStatistics().heap_size_limit,a=n>0?s.heapUsed/n:0;if(a>=.94&&e>10){let i=Math.max(10,Math.floor(e/2));return common.debug("engine",`Critical heap pressure after type-aware chunk (${Math.round(100*a)}% of V8 heap limit); reducing chunk size to ${i}`),{chunkSize:i,lowHeapStreak:0}}if(a>=.88&&e>10){let i=Math.max(10,Math.floor(.8*e));return common.debug("engine",`High heap pressure after type-aware chunk (${Math.round(100*a)}% of V8 heap limit); reducing chunk size to ${i}`),{chunkSize:i,lowHeapStreak:0}}if(a<=.35&&e<r){let i=t+1;if(i<3)return {chunkSize:e,lowHeapStreak:i};let o=Math.min(r,e+Math.max(10,Math.floor(.1*e)));return common.debug("engine",`Sustained low heap pressure after type-aware chunk (${Math.round(100*a)}% of V8 heap limit); increasing chunk size to ${o}`),{chunkSize:o,lowHeapStreak:0}}return {chunkSize:e,lowHeapStreak:0}},Ve=e=>{let t=new Set;for(let r of e){let s=r.inputs.typescript.path;fe(s)&&t.add(s);}return [...t]},nr=async(e,t,r)=>{let s=Array.from(e.entries());if(r==="simple")return common.debug("engine",`Type-aware chunk ordering: simple path sort for ${s.length} files`),he(s);common.debug("engine",`Type-aware chunk ordering: dependency pre-pass for ${s.length} files`);let n=performance.now(),a=await or(s,t,n+5e3);return a?(common.debug("engine",`Dependency chunk ordering complete in ${(performance.now()-n).toFixed(1)}ms`),a):(common.debug("engine","Dependency chunk ordering exceeded 5000ms; falling back to simple path sort"),he(s))},or=async(e,t,r)=>{let s=new Map,n=ye__default.default(64);for(let a=0;a<e.length;a+=64){if(performance.now()>r)return null;let i=e.slice(a,a+64);await Promise.all(i.map(o=>n(async()=>{let[l,u]=o,g=Ve(u)[0]??l,p=await sr(g,t),m=s.get(p)??[];m.push(o),s.set(p,m);})));}return [...s.entries()].sort(([a],[i])=>a.localeCompare(i)).flatMap(([,a])=>he(a))},he=e=>[...e].sort(([t],[r])=>t.localeCompare(r)),sr=async(e,t)=>{let r=$.dirname(e);try{let s=await promises.readFile(e,"utf8"),n=ir(s);return n?$.join(r,n.split("/")[0]??""):r}catch{return $.dirname($.join(t,e))}},ir=e=>/\b(?:import|export)\b[^'"]*['"](\.{1,2}\/[^'"]+)['"]/g.exec(e)?.[1]??null,lr=async(e,t,r,s,n,a,i,o)=>{let l=await cr();return l?new Promise((u,g)=>{let p=child_process.fork(l,[],{cwd:t,execArgv:[],stdio:["ignore","pipe","pipe","ipc"]}),m=false,d=setTimeout(()=>{m||(m=true,p.kill(),g(Error("Type-aware child process timed out after 600s")));},6e5);p.stdout?.on("data",w=>common.debug("engine",`[type-aware-child] ${String(w).trim()}`)),p.stderr?.on("data",w=>common.debug("engine",`[type-aware-child:stderr] ${String(w).trim()}`)),p.on("message",w=>{if(ie(w))return void o?.(w);if(pr(w)){if(m)return;m=true,clearTimeout(d),u(w.results);return}if(ur(w)){if(m)return;m=true,clearTimeout(d),g(Error(w.error));}}),p.on("error",w=>{m||(m=true,clearTimeout(d),g(w));}),p.on("exit",w=>{m||(m=true,clearTimeout(d),g(Error(`Type-aware child process exited before completion with code ${w}`)));}),p.send({rootDir:t,tasks:e,files:r,programRootFiles:s,parserOptions:i.parserOptions,buildProjectContext:n,fileConcurrency:a});}):(common.debug("engine","Type-aware child worker not found; falling back to in-process execution"),xe(e,t,a,true,i.errorCollector,r,i.parserOptions,n,s,void 0,o))},cr=async()=>{let e=$.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));try{let t=module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))).resolve("@ngcompass/rules/type-aware-worker");if(fs.existsSync(t))return t}catch{}for(let t of [$.join(e,"..","..","rules","dist","type-aware-worker.js"),$.join(e,"..","..","rules","dist","type-aware-worker.cjs")])if(fs.existsSync(t))return t;return null},pr=e=>!!e&&typeof e=="object"&&e.kind==="complete"&&Array.isArray(e.results),ur=e=>!!e&&typeof e=="object"&&e.kind==="error"&&typeof e.error=="string",mr=async(e,t,r)=>{if(e.length===0)return [];common.debug("engine",`Retrieving results for ${e.length} skipped tasks...`);let s=[],n=[];if(t)for(let a of e){let i=t.get(a.taskId);i&&Ge(i)?s.push(i):n.push(a);}else n.push(...e);if(n.length>0&&r){common.debug("engine",`Fetching ${n.length} results from cache service...`);let a=n.map(o=>o.taskId),i=await r.results.getMany(a);for(let o of n){let l=i.get(o.taskId);l&&Ge(l)&&s.push(l);}}return common.debug("engine",`Retrieved ${s.length} results from cache`),s};var Ye=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],qe=class{frameIndex=0;interval=null;message="";start(t){this.message=t,this.interval||(this.frameIndex=0,Z__default.default.stderr.write("\x1B[?25l"),this.interval=setInterval(()=>{Z__default.default.stderr.write(`\r${Ye[this.frameIndex]} ${this.message}`),this.frameIndex=(this.frameIndex+1)%Ye.length;},80));}update(t){this.message=t;}stop(t){this.interval&&(clearInterval(this.interval),this.interval=null),Z__default.default.stderr.write("\r\x1B[K"),t&&Z__default.default.stderr.write(t+` | ||
| `),Z__default.default.stderr.write("\x1B[?25h");}};exports.BUDGET_MS_PER_FILE_WITHOUT_TYPES=Qe;exports.BUDGET_MS_PER_FILE_WITH_TYPES=et;exports.MIN_WORKER_COUNT=Xe;exports.RuleContextFactory=ee;exports.SPINNER_FRAME_INTERVAL_MS=Ze;exports.STREAM_TO_NODE_TYPE=Je;exports.Spinner=qe;exports.WORKER_TIMEOUT_MS=Te;exports.buildFileProgress=U;exports.buildProjectContext=pe;exports.buildVisitorMap=Ce;exports.calculateStats=Y;exports.configureRuleExecutor=Tt;exports.createAnalysisContext=B;exports.createAngularTypeIndex=ue;exports.createAnyAngularClassRule=gr;exports.createCallExpressionRule=xr;exports.createComponentRule=fr;exports.createDecoratedPropertyRule=hr;exports.createNewExpressionRule=wr;exports.createTemplateAttributeRule=yr;exports.createTemplateBlockRule=kr;exports.createTemplateExpressionRule=dr;exports.createTemplateRule=Sr;exports.createTypeAwareAnalysisContext=me;exports.executeBatchedTasks=q;exports.isAnalysisFileProgress=ie;exports.isWorkerFileProgress=se;exports.runAnalysis=$a;exports.runAnalysisParallel=Oe;exports.runSinglePassAnalysis=Ar;//# sourceMappingURL=index.cjs.map | ||
| //# sourceMappingURL=index.cjs.map |
+19
-468
@@ -1,99 +0,25 @@ | ||
| import { RuleContext, RuleFailure, RuleMetadata, InfrastructureErrorCollector, RuleResult, TemplateAst, StyleAst, ProjectContext, ParserOptions, Result, AnalysisResult, ComponentFiles } from '@ngcompass/common'; | ||
| import { RuleContext, RuleFailure, RuleMetadata, InfrastructureErrorCollector, RuleResult, TemplateAst, StyleAst, ProjectContext, AngularTypeIndex, WorkerFileProgress, ParserOptions, Result, AnalysisResult, ComponentFiles } from '@ngcompass/common'; | ||
| import { AnyAngularClassNode, CallExpression, AngularClassNode, DecoratedPropertyNode, NewExpression, TemplateAttributeNode, TemplateBlockNode, TemplateExpressionNode, TemplateAnalysis } from '@ngcompass/ast'; | ||
| import { Program } from 'oxc-parser'; | ||
| import ts, { TypeChecker } from 'typescript'; | ||
| import { CacheContext } from '@ngcompass/cache'; | ||
| import { ExecutionPlanOutput, Task } from '@ngcompass/planner'; | ||
| import { CacheContext } from '@ngcompass/cache'; | ||
| /** | ||
| * @fileoverview | ||
| * Facilitates the definition of analytical rules as passive observers. | ||
| * | ||
| * Rules implemented via this interface are designed for maximum performance, | ||
| * operating on pre-filtered node streams without performing independent | ||
| * AST traversals or resource allocations. | ||
| */ | ||
| type StreamType = 'AngularClass' | 'AnyAngularClass' | 'DecoratedProperty' | 'TemplateExpression' | 'TemplateAttribute' | 'TemplateBlock' | 'Template' | 'CallExpression' | 'NewExpression'; | ||
| /** | ||
| * Rule handler for a specific stream type. | ||
| * | ||
| * @template TNode - Node type from stream (pre-filtered, pre-analyzed) | ||
| */ | ||
| interface RuleHandler<TNode> { | ||
| readonly name: string; | ||
| readonly streamType: StreamType; | ||
| /** | ||
| * Evaluates a node from a pre-filtered analytical stream. | ||
| * | ||
| * Implementation must adhere to strict performance constraints, ensuring | ||
| * O(1) or near-constant time complexity and zero memory allocation. | ||
| * | ||
| * @param node The pre-analyzed AST node to evaluate. | ||
| * @param context The analytical context for the current file. | ||
| * @returns A rule failure, a collection of failures, or null if compliant. | ||
| */ | ||
| handle(node: TNode, context: RuleContext): RuleFailure | RuleFailure[] | null; | ||
| readonly meta?: Partial<RuleMetadata>; | ||
| } | ||
| /** | ||
| * Helper to create component rule handlers. | ||
| */ | ||
| declare const createComponentRule: (name: string, handler: (node: AngularClassNode, context: RuleContext) => RuleFailure | RuleFailure[] | null, meta?: Partial<RuleMetadata>) => RuleHandler<AngularClassNode>; | ||
| /** | ||
| * Helper to create rules that handle ANY Angular-decorated class: | ||
| * @Component, @Directive, @Pipe, @Injectable, @NgModule. | ||
| * | ||
| * Use this instead of createComponentRule when the rule applies to | ||
| * classes beyond just @Component and @Directive. | ||
| */ | ||
| declare const createAnyAngularClassRule: (name: string, handler: (node: AnyAngularClassNode, context: RuleContext) => RuleFailure | RuleFailure[] | null, meta?: Partial<RuleMetadata>) => RuleHandler<AnyAngularClassNode>; | ||
| /** | ||
| * Helper to create decorated property rule handlers. | ||
| */ | ||
| declare const createDecoratedPropertyRule: (name: string, handler: (node: DecoratedPropertyNode, context: RuleContext) => RuleFailure | RuleFailure[] | null) => RuleHandler<DecoratedPropertyNode>; | ||
| /** | ||
| * Helper to create template expression rule handlers. | ||
| */ | ||
| declare const createTemplateExpressionRule: (name: string, handler: (node: TemplateExpressionNode, context: RuleContext) => RuleFailure | RuleFailure[] | null, meta?: Partial<RuleMetadata>) => RuleHandler<TemplateExpressionNode>; | ||
| /** | ||
| * Helper to create template attribute rule handlers. | ||
| */ | ||
| declare const createTemplateAttributeRule: (name: string, handler: (node: TemplateAttributeNode, context: RuleContext) => RuleFailure | RuleFailure[] | null, meta?: Partial<RuleMetadata>) => RuleHandler<TemplateAttributeNode>; | ||
| /** | ||
| * Helper to create call expression rule handlers. | ||
| * | ||
| * Receives every CallExpression node in the file's AST exactly once. | ||
| * The handler is responsible for its own callee-shape filtering | ||
| * (e.g. checking for a StaticMemberExpression callee and a specific method name). | ||
| */ | ||
| declare const createCallExpressionRule: (name: string, handler: (node: CallExpression, context: RuleContext) => RuleFailure | RuleFailure[] | null, meta?: Partial<RuleMetadata>) => RuleHandler<CallExpression>; | ||
| /** | ||
| * Helper to create new expression rule handlers. | ||
| */ | ||
| declare const createNewExpressionRule: (name: string, handler: (node: NewExpression, context: RuleContext) => RuleFailure | RuleFailure[] | null, meta?: Partial<RuleMetadata>) => RuleHandler<NewExpression>; | ||
| /** | ||
| * Helper to create template block rule handlers. | ||
| */ | ||
| declare const createTemplateBlockRule: (name: string, handler: (node: TemplateBlockNode, context: RuleContext) => RuleFailure | RuleFailure[] | null, meta?: Partial<RuleMetadata>) => RuleHandler<TemplateBlockNode>; | ||
| /** | ||
| * Helper to create template rules that receive the full template analysis. | ||
| */ | ||
| declare const createTemplateRule: (name: string, handler: (node: TemplateAnalysis, context: RuleContext) => RuleFailure | RuleFailure[] | null, meta?: Partial<RuleMetadata>) => RuleHandler<TemplateAnalysis>; | ||
| /** | ||
| * @fileoverview | ||
| * Implements a high-performance visitor registry for O(1) node dispatch. | ||
| * | ||
| * This module facilitates the routing of AST nodes to their respective rule | ||
| * handlers by utilizing a static dispatch table, significantly optimizing the | ||
| * throughput of the single-pass engine. | ||
| */ | ||
| /** | ||
| * Compile-time exhaustiveness check. | ||
| * Every StreamType MUST have an entry here. | ||
| * Template streams use a sentinel prefix '__' — they are dispatched | ||
| * separately after the main AST walk and are skipped in buildVisitorMap. | ||
| */ | ||
| type StreamToNodeType = { | ||
@@ -103,48 +29,10 @@ [K in StreamType]: string; | ||
| declare const STREAM_TO_NODE_TYPE: StreamToNodeType; | ||
| /** | ||
| * A visitor entry bundles: | ||
| * - the raw-node → stream-node filter function | ||
| * - the rule handler | ||
| * - failure collection reference | ||
| * - per-rule timing reference | ||
| * | ||
| * This avoids closure allocation on the hot path by pre-binding everything | ||
| * at registry build time. | ||
| */ | ||
| interface VisitorEntry { | ||
| /** Rule name (for failure collection and timing) */ | ||
| readonly ruleName: string; | ||
| /** Converts raw Oxc node → typed stream node (or null if not applicable) */ | ||
| readonly filter: (rawNode: any) => unknown; | ||
| /** The rule handler's handle function (pre-bound) */ | ||
| readonly handle: (streamNode: any, ctx: RuleContext) => RuleFailure | RuleFailure[] | null; | ||
| } | ||
| /** | ||
| * Maps Oxc node.type → array of VisitorEntry. | ||
| * Lookup is O(1) (Map.get), dispatch is O(H) where H = handlers for that type. | ||
| */ | ||
| type VisitorMap = ReadonlyMap<string, ReadonlyArray<VisitorEntry>>; | ||
| /** | ||
| * Constructs a high-speed dispatch map from a collection of rule handlers. | ||
| * | ||
| * @param handlers A collection of rule handlers to include in the registry. | ||
| * @param streamFilters A mapping of stream types to their respective node filters. | ||
| * @returns A read-only VisitorMap optimized for O(1) lookup. | ||
| */ | ||
| declare function buildVisitorMap(handlers: ReadonlyArray<RuleHandler<any>>, streamFilters: Partial<Record<StreamType, (rawNode: any) => unknown>>): VisitorMap; | ||
| /** | ||
| * @fileoverview | ||
| * Implements the performance-optimized single-pass analytical engine. | ||
| * | ||
| * This component is designed for maximum throughput, performing exactly one | ||
| * traversal of the Abstract Syntax Tree (AST) while dispatching nodes to | ||
| * passive observers via an O(1) visitor infrastructure. | ||
| * | ||
| * Key Performance Characteristics: | ||
| * - Linear Complexity: O(N) traversal in relation to the number of AST nodes. | ||
| * - Optimized Dispatch: Leverages a high-speed visitor map for node-type routing. | ||
| * - Resource Efficiency: Maintains strict execution budgets for individual rules. | ||
| */ | ||
| interface RuleTiming { | ||
@@ -166,10 +54,2 @@ ruleName: string; | ||
| } | ||
| /** | ||
| * Executes a comprehensive analytical pass over a provided AST program. | ||
| * | ||
| * @param rules A collection of rule handlers to evaluate during the pass. | ||
| * @param context The analytical context containing the program and metadata. | ||
| * @param options Operational configuration, including error collection. | ||
| * @returns A consolidated result set and performance diagnostic report. | ||
| */ | ||
| declare const runSinglePassAnalysis: (rules: ReadonlyArray<RuleHandler<any>>, context: RuleContext, options?: { | ||
@@ -183,15 +63,2 @@ errorCollector?: InfrastructureErrorCollector; | ||
| /** | ||
| * @fileoverview | ||
| * Provides the RuleContextFactory for initializing rule execution contexts. | ||
| * | ||
| * This component is responsible for aggregating various analytical artifacts—including | ||
| * file content, AST structures, type information, and project metadata—into a | ||
| * unified RuleContext suitable for rule evaluation. | ||
| */ | ||
| /** | ||
| * Defines the operational boundaries for context construction. | ||
| * Encapsulates resource access requirements for both main-thread and worker-thread execution. | ||
| */ | ||
| interface ExecutionContext { | ||
@@ -204,35 +71,10 @@ readonly rootDir: string; | ||
| readonly getStyle: (filePath: string) => Promise<StyleAst | undefined>; | ||
| /** | ||
| * Retrieves the project-wide analytical context, if available. | ||
| */ | ||
| readonly getProjectContext?: () => ProjectContext | undefined; | ||
| /** | ||
| * Retrieves the TypeScript SourceFile representation for a given path. | ||
| */ | ||
| readonly getTsSourceFile?: (filePath: string) => ts.SourceFile | undefined; | ||
| readonly getAngularTypes?: () => AngularTypeIndex | undefined; | ||
| } | ||
| /** | ||
| * Orschestrates the assembly of fully-resolved RuleContext objects. | ||
| * | ||
| * Decouples the rule execution logic from the underlying storage and retrieval | ||
| * mechanisms by utilizing the ExecutionContext abstraction. | ||
| */ | ||
| declare class RuleContextFactory { | ||
| private readonly context; | ||
| constructor(context: ExecutionContext); | ||
| /** | ||
| * Builds a RuleContext for the given file. | ||
| * | ||
| * @param filePath - Absolute path to the file being analysed | ||
| * @param options - Rule-specific options from the configuration | ||
| * @param needsTemplate - Whether the rule(s) in this batch require the template | ||
| * @returns A fully resolved RuleContext | ||
| */ | ||
| build(filePath: string, options: Record<string, unknown>, needsTemplate: boolean): Promise<RuleContext>; | ||
| /** | ||
| * Builds a `ComponentCrossRef` for the given file path when that file is | ||
| * either a `.component.ts` class or its associated template. | ||
| * | ||
| * Returns `undefined` for all other file types. | ||
| */ | ||
| private resolveExternalTemplatePath; | ||
@@ -243,20 +85,16 @@ private buildCrossRef; | ||
| /** | ||
| * @fileoverview | ||
| * Provides the AnalysisContext abstraction for ngcompass. | ||
| * | ||
| * This module facilitates deterministic, memoized access to file system resources | ||
| * and abstract syntax tree (AST) artifacts, ensuring peak performance during | ||
| * analysis by minimizing redundant I/O and parsing operations. | ||
| * | ||
| * Implementation Strategy: | ||
| * - Memoization: Caches raw file content, TypeScript Programs, Template ASTs, and Style ASTs. | ||
| * - Resource Management: Implements an explicit eviction mechanism to control heap usage. | ||
| * - Determinism: Ensures the same file path always yields consistent artifacts across a run. | ||
| */ | ||
| interface AnalysisFileProgress { | ||
| readonly filePath: string; | ||
| readonly taskCount: number; | ||
| readonly issueCount: number; | ||
| readonly errorCount: number; | ||
| readonly warningCount: number; | ||
| readonly duration: number; | ||
| readonly cached?: boolean; | ||
| readonly typeAware?: boolean; | ||
| } | ||
| declare const buildFileProgress: (filePath: string, taskCount: number, results: ReadonlyArray<RuleResult>, duration: number, typeAware?: boolean) => AnalysisFileProgress; | ||
| declare const isWorkerFileProgress: (message: unknown) => message is WorkerFileProgress; | ||
| declare const isAnalysisFileProgress: (message: unknown) => message is AnalysisFileProgress; | ||
| /** | ||
| * Encapsulates the state and logic required for analyzing a set of source files. | ||
| * Provides unified access to file content and parsed metadata with internal memoization. | ||
| */ | ||
| interface AnalysisContext { | ||
@@ -268,202 +106,35 @@ readonly rootDir: string; | ||
| readonly getStyle: (filePath: string) => Promise<StyleAst | undefined>; | ||
| /** | ||
| * Purges all cached artifacts associated with the specified file path. | ||
| * | ||
| * This method is critical for memory governance in large-scale repositories. | ||
| * It should be invoked immediately upon completion of a file's analysis tasks | ||
| * to release references to raw strings and AST structures, enabling garbage | ||
| * collection. | ||
| * | ||
| * @param filePath The path of the file to evict from internal caches. | ||
| */ | ||
| readonly evict: (filePath: string) => void; | ||
| /** | ||
| * Clears every cached artifact owned by this context. | ||
| */ | ||
| readonly dispose: () => void; | ||
| } | ||
| /** | ||
| * Instantiates an AnalysisContext provider with integrated memoization logic. | ||
| * | ||
| * @param rootDir The base directory used to resolve relative file transitions. | ||
| * @returns A stateful AnalysisContext instance. | ||
| */ | ||
| declare const createAnalysisContext: (rootDir: string) => AnalysisContext; | ||
| /** | ||
| * @fileoverview | ||
| * Implements the high-level analysis orchestration pipeline. | ||
| * | ||
| * The orchestrator manages the lifecycle of an analysis run, coordinating | ||
| * task distribution across local threads and worker pools, while managing | ||
| * analytical state, result aggregation, and caching strategies. | ||
| */ | ||
| interface AnalysisFileProgress { | ||
| readonly filePath: string; | ||
| readonly taskCount: number; | ||
| readonly issueCount: number; | ||
| readonly errorCount: number; | ||
| readonly warningCount: number; | ||
| readonly duration: number; | ||
| readonly cached?: boolean; | ||
| readonly typeAware?: boolean; | ||
| } | ||
| /** | ||
| * Configuration parameters for the analysis orchestration process. | ||
| */ | ||
| interface AnalysisOptions { | ||
| /** Root directory for resolving file paths */ | ||
| readonly rootDir: string; | ||
| /** Optional cache context for retrieving skipped task results */ | ||
| readonly cache?: CacheContext; | ||
| /** Enable debug logging */ | ||
| readonly debug?: boolean; | ||
| /** | ||
| * Maximum number of worker threads to use. | ||
| * Clamped to [1, os.cpus().length] (RFC §7.3 unified concurrency model). | ||
| * Defaults to os.cpus().length. | ||
| */ | ||
| readonly maxWorkers?: number; | ||
| /** | ||
| * Number of tasks above which the worker pool is used instead of local | ||
| * pLimit execution. Default: 150. | ||
| */ | ||
| readonly parallelThreshold?: number; | ||
| /** | ||
| * Integrates an optional infrastructure error sink. | ||
| * When provided, operational errors are encapsulated and reported through | ||
| * this collector instead of triggering terminal exceptions. | ||
| */ | ||
| readonly errorCollector?: InfrastructureErrorCollector; | ||
| /** | ||
| * All files discovered by the scanner for this run. | ||
| * | ||
| * CTX-001: Forwarded to `createTypeAwareAnalysisContext()` so the | ||
| * `ProjectContext` import-graph builder can restrict edges to intra-project | ||
| * imports and correctly populate `ProjectContext.projectFiles`. | ||
| * | ||
| * Optional for backward compatibility — when omitted the ProjectContext | ||
| * will still be built but `projectFiles` may be less complete (derived | ||
| * solely from the TypeScript Program's source-file list). | ||
| */ | ||
| readonly files?: ReadonlyArray<string>; | ||
| /** Optional TypeScript parser settings from config. */ | ||
| readonly parserOptions?: ParserOptions; | ||
| /** | ||
| * Maximum number of files per type-aware execution chunk. | ||
| * | ||
| * Type-aware tasks run on the main thread with a shared `ts.Program`. | ||
| * For large repos the Program can consume several GB. Chunking splits | ||
| * the work: each chunk creates its own scoped Program, processes its | ||
| * tasks, then lets the Program be garbage-collected before the next chunk | ||
| * starts. Smaller values reduce peak memory; larger values reduce the | ||
| * number of (expensive) `ts.createProgram` calls. | ||
| * | ||
| * Default: 100 files per chunk, or 50 for very large type-aware runs. | ||
| * Set to `Infinity` to disable chunking. | ||
| */ | ||
| readonly typeAwareChunkSize?: number; | ||
| /** | ||
| * Runs type-aware chunks in a separate Node process. This is slower than | ||
| * in-process execution, but gives the OS a hard memory boundary because | ||
| * each chunk process exits after producing results. | ||
| */ | ||
| readonly typeAwareIsolation?: 'auto' | 'process' | 'off'; | ||
| /** | ||
| * Controls how type-aware files are ordered before chunking. | ||
| * `dependency` attempts to keep nearby dependency groups together. | ||
| * `simple` skips the dependency pre-pass and sorts files by path. | ||
| */ | ||
| readonly typeAwareChunkStrategy?: 'dependency' | 'simple'; | ||
| /** | ||
| * Maximum number of type-aware chunks allowed to run at the same time. | ||
| * Each concurrent chunk may create its own TypeScript Program, so values | ||
| * above one trade memory for speed. | ||
| */ | ||
| readonly typeAwareConcurrency?: number; | ||
| /** | ||
| * Maximum number of files to process concurrently inside a single | ||
| * type-aware chunk. This reuses one TypeScript Program but may hold more | ||
| * file/template ASTs at once. | ||
| */ | ||
| readonly typeAwareFileConcurrency?: number; | ||
| /** | ||
| * When true, type-checker-dependent rules are skipped entirely. | ||
| * Use this as an escape valve for very large repos where even a chunked | ||
| * TS Program is too memory-intensive to build. | ||
| */ | ||
| readonly skipTypeCheck?: boolean; | ||
| /** | ||
| * Called whenever a batch of tasks completes. | ||
| * @param completed - Total tasks finished so far (including cached). | ||
| * @param total - Grand total tasks for this run (executed + cached). | ||
| */ | ||
| readonly onProgress?: (completed: number, total: number) => void; | ||
| /** Called whenever all tasks for a file have completed. */ | ||
| readonly onFileProgress?: (event: AnalysisFileProgress) => void; | ||
| } | ||
| /** | ||
| * Primary entry point for executing an analysis plan. | ||
| * | ||
| * Coordinates task execution strategies, manages result caching, and | ||
| * synthesizes the final analytical report. | ||
| * | ||
| * @param plan The execution plan containing tasks and cached metadata. | ||
| * @param options Configuration options for the orchestrator. | ||
| * @returns A promise resolving to an AnalysisResult encapsulation. | ||
| */ | ||
| declare const runAnalysis: (plan: ExecutionPlanOutput, options: AnalysisOptions) => Promise<Result<AnalysisResult>>; | ||
| /** | ||
| * @fileoverview | ||
| * Facilitates the construction of the ProjectContext for ngcompass. | ||
| * | ||
| * This module performs a traversal of the TypeScript module graph to assemble | ||
| * a comprehensive, read-only representation of the project structure. This | ||
| * includes import relationships, component clusters, and Angular-specific | ||
| * metadata (e.g., NgModule declarations). | ||
| * | ||
| * Implementation Strategy: | ||
| * - Efficient Traversal: Analyzes the resolved module graph in O(F + E) time. | ||
| * - Structural Disambiguation: Detects barrel files and component clusters. | ||
| * - Angular Awareness: Integrates decorator metadata for standalone and NgModule discovery. | ||
| */ | ||
| /** | ||
| * Constructs a ProjectContext immutable structure from a TypeScript Program. | ||
| * | ||
| * @param program The ts.Program instance used for type-aware analysis. | ||
| * @param files A collection of file paths identified during the scanning phase. | ||
| * @param rootDir The absolute path to the project root directory. | ||
| * @param componentFiles An optional mapping of component-related file clusters. | ||
| * @returns A fully initialized ProjectContext instance. | ||
| */ | ||
| declare function buildProjectContext(program: ts.Program, files: ReadonlyArray<string>, rootDir: string, componentFiles?: ReadonlyMap<string, ComponentFiles>): ProjectContext; | ||
| /** | ||
| * Enhances the baseline AnalysisContext with semantic and project-wide data. | ||
| */ | ||
| interface TypeAwareAnalysisContext extends AnalysisContext { | ||
| /** | ||
| * Retrieves the TypeScript TypeChecker for semantic analysis. | ||
| */ | ||
| readonly getTypeChecker: (filePath: string) => Promise<ts.TypeChecker | undefined>; | ||
| /** | ||
| * Retrieves the pre-built, immutable ProjectContext for the current run. | ||
| */ | ||
| readonly getProjectContext: () => ProjectContext | undefined; | ||
| /** | ||
| * Retrieves the TypeScript SourceFile representation for a given path. | ||
| */ | ||
| readonly getTsSourceFile: (filePath: string) => ts.SourceFile | undefined; | ||
| /** | ||
| * Performs an explicit initialization phase for type-aware resources. | ||
| */ | ||
| readonly getAngularTypes: () => AngularTypeIndex | undefined; | ||
| readonly warmup: () => Promise<void>; | ||
| /** | ||
| * Releases references to the TypeScript Program, TypeChecker, ProjectContext, | ||
| * and baseline parsed artifacts after a type-aware chunk finishes. | ||
| */ | ||
| readonly dispose: () => void; | ||
@@ -475,132 +146,20 @@ } | ||
| } | ||
| /** | ||
| * Initializes a TypeAwareAnalysisContext for the specified root directory. | ||
| * | ||
| * @param rootDir The absolute path to the project root. | ||
| * @param files A collection of file paths identified during the scanning phase. | ||
| * @returns A fully initialized TypeAwareAnalysisContext instance. | ||
| */ | ||
| declare const createTypeAwareAnalysisContext: (rootDir: string, files?: ReadonlyArray<string>, parserOptions?: ParserOptions, contextOptions?: TypeAwareAnalysisContextOptions) => TypeAwareAnalysisContext; | ||
| /** | ||
| * @fileoverview | ||
| * Provides the core execution runner for ngcompass. | ||
| * | ||
| * This module consolidates the rule execution logic utilized by both local | ||
| * sequential processes and worker-based parallel execution. It manages task | ||
| * batching, result mapping, and operational error handling. | ||
| */ | ||
| declare function createAngularTypeIndex(program: ts.Program | undefined): AngularTypeIndex; | ||
| /** | ||
| * Executes a collection of analysis tasks for a specific file. | ||
| * | ||
| * Processes tasks by grouping them into optimized execution batches. Utilizes | ||
| * the RuleContextFactory for resource initialization and coordinates with | ||
| * the configured rule executor for evaluation. | ||
| * | ||
| * @param tasks A collection of tasks to execute against a single file path. | ||
| * @param context The operational context providing resource access. | ||
| * @param errorCollector An optional sink for operational error reporting. | ||
| * @returns A promise resolving to a collection of rule execution results. | ||
| */ | ||
| declare const executeBatchedTasks: (tasks: ReadonlyArray<Task>, context: ExecutionContext, errorCollector?: InfrastructureErrorCollector) => Promise<RuleResult[]>; | ||
| /** | ||
| * @fileoverview | ||
| * Establishes a dependency injection boundary for rule execution. | ||
| * | ||
| * This module facilitates the decoupling of the engine's core orchestration layer | ||
| * from the concrete rule implementations, mitigating circular dependency risks. | ||
| */ | ||
| /** | ||
| * Executes multiple rules in a single AST pass and returns their results. | ||
| * Implemented by rules/src/engine/adapter.ts → executeBatchedNewEngineRules. | ||
| */ | ||
| type BatchRuleExecutorFn = (ruleNames: ReadonlyArray<string>, context: RuleContext) => ReadonlyArray<RuleResult>; | ||
| /** | ||
| * Returns true if a rule with the given name is registered in the engine. | ||
| * Implemented by rules/src/engine/adapter.ts → isNewEngineRule. | ||
| */ | ||
| type RuleCheckerFn = (ruleName: string) => boolean; | ||
| /** | ||
| * Configures the analytical engine with specific rule execution and validation logic. | ||
| * | ||
| * This method must be invoked during the initialization phase of the process | ||
| * to register the underlying rule processing capabilities. | ||
| * | ||
| * @param executor The implementation responsible for batched rule evaluation. | ||
| * @param checker The implementation responsible for rule presence verification. | ||
| */ | ||
| declare const configureRuleExecutor: (executor: BatchRuleExecutorFn, checker: RuleCheckerFn) => void; | ||
| /** | ||
| * @fileoverview | ||
| * Facilitates the calculation of aggregate analytical metrics. | ||
| * | ||
| * This module provides utilities to derive summary statistics from rule execution | ||
| * results, including file counts, severity distributions, and execution durations. | ||
| */ | ||
| /** | ||
| * Computes comprehensive analytical statistics based on raw rule results. | ||
| * | ||
| * @param results A collection of rule execution results. | ||
| * @param startTime The high-resolution timestamp indicating the start of execution. | ||
| * @param cacheHitRate An optional precalculated cache hit ratio [0, 1]. | ||
| * @returns An object encapsulating the aggregated statistical data. | ||
| */ | ||
| declare const calculateStats: (results: ReadonlyArray<RuleResult>, startTime: number, cacheHitRate?: number) => AnalysisResult["stats"]; | ||
| /** | ||
| * @fileoverview | ||
| * Defines engine-level constants and performance benchmarks for ngcompass. | ||
| * | ||
| * This module serves as the centralized source of truth for architectural | ||
| * thresholds, concurrency limits, and performance budgets used across the | ||
| * orchestration and execution layers. | ||
| */ | ||
| /** | ||
| * The minimum number of worker threads to maintain in the execution pool, | ||
| * ensuring availability regardless of the host system's CPU topology. | ||
| */ | ||
| declare const MIN_WORKER_COUNT = 2; | ||
| /** | ||
| * The refresh interval for the command-line interface progress visualization. | ||
| */ | ||
| declare const SPINNER_FRAME_INTERVAL_MS = 80; | ||
| /** | ||
| * Maximum wall-clock time a single worker thread is allowed to run before it | ||
| * is forcibly terminated and its results replaced with empty failures. | ||
| * | ||
| * 60 seconds is intentionally generous — the timeout is a last-resort safety | ||
| * net for hung workers (e.g. infinite loops or deadlocked TypeScript programs), | ||
| * not a performance budget. | ||
| */ | ||
| declare const WORKER_TIMEOUT_MS = 60000; | ||
| /** | ||
| * Performance Budgets: | ||
| * Thresholds used to monitor and report execution efficiency. | ||
| */ | ||
| /** | ||
| * The target execution time (p95) per file for rules that do not utilize | ||
| * type-checking services. | ||
| */ | ||
| declare const BUDGET_MS_PER_FILE_WITHOUT_TYPES = 2; | ||
| /** | ||
| * The target execution time (p95) per file for rules requiring full | ||
| * type-checking capabilities. | ||
| */ | ||
| declare const BUDGET_MS_PER_FILE_WITH_TYPES = 5; | ||
| /** | ||
| * @fileoverview | ||
| * Provides a terminal-based progress visualization utility. | ||
| * | ||
| * Implements a high-efficiency visual indicator (spinner) for long-running | ||
| * command-line operations, isolated from core analytical logic. | ||
| */ | ||
| /** | ||
| * Manages the state and rendering of a terminal-based progress indicator. | ||
| */ | ||
| declare class Spinner { | ||
@@ -615,12 +174,4 @@ private frameIndex; | ||
| /** | ||
| * @fileoverview | ||
| * Manages parallel analysis execution utilizing worker thread pools. | ||
| * | ||
| * Coordinates the distribution of analytical tasks across multiple workers to | ||
| * maximize CPU utilization. Provides a resilient fallback to local concurrent | ||
| * execution if worker resources are unavailable. | ||
| */ | ||
| declare const runAnalysisParallel: (tasks: ReadonlyArray<Task>, rootDir: string, startTime: number, maxWorkers?: number, concurrency?: number, onFileProgress?: (event: AnalysisFileProgress) => void) => Promise<Result<AnalysisResult>>; | ||
| export { type AnalysisContext, type AnalysisFileProgress, type AnalysisOptions, BUDGET_MS_PER_FILE_WITHOUT_TYPES, BUDGET_MS_PER_FILE_WITH_TYPES, type BatchRuleExecutorFn, type ExecutionContext, MIN_WORKER_COUNT, type PerformanceReport, type RuleCheckerFn, RuleContextFactory, type RuleHandler, SPINNER_FRAME_INTERVAL_MS, STREAM_TO_NODE_TYPE, Spinner, type StreamType, type TypeAwareAnalysisContext, type VisitorEntry, type VisitorMap, WORKER_TIMEOUT_MS, buildProjectContext, buildVisitorMap, calculateStats, configureRuleExecutor, createAnalysisContext, createAnyAngularClassRule, createCallExpressionRule, createComponentRule, createDecoratedPropertyRule, createNewExpressionRule, createTemplateAttributeRule, createTemplateBlockRule, createTemplateExpressionRule, createTemplateRule, createTypeAwareAnalysisContext, executeBatchedTasks, runAnalysis, runAnalysisParallel, runSinglePassAnalysis }; | ||
| export { type AnalysisContext, type AnalysisFileProgress, type AnalysisOptions, BUDGET_MS_PER_FILE_WITHOUT_TYPES, BUDGET_MS_PER_FILE_WITH_TYPES, type BatchRuleExecutorFn, type ExecutionContext, MIN_WORKER_COUNT, type PerformanceReport, type RuleCheckerFn, RuleContextFactory, type RuleHandler, SPINNER_FRAME_INTERVAL_MS, STREAM_TO_NODE_TYPE, Spinner, type StreamType, type TypeAwareAnalysisContext, type VisitorEntry, type VisitorMap, WORKER_TIMEOUT_MS, buildFileProgress, buildProjectContext, buildVisitorMap, calculateStats, configureRuleExecutor, createAnalysisContext, createAngularTypeIndex, createAnyAngularClassRule, createCallExpressionRule, createComponentRule, createDecoratedPropertyRule, createNewExpressionRule, createTemplateAttributeRule, createTemplateBlockRule, createTemplateExpressionRule, createTemplateRule, createTypeAwareAnalysisContext, executeBatchedTasks, isAnalysisFileProgress, isWorkerFileProgress, runAnalysis, runAnalysisParallel, runSinglePassAnalysis }; |
+19
-468
@@ -1,99 +0,25 @@ | ||
| import { RuleContext, RuleFailure, RuleMetadata, InfrastructureErrorCollector, RuleResult, TemplateAst, StyleAst, ProjectContext, ParserOptions, Result, AnalysisResult, ComponentFiles } from '@ngcompass/common'; | ||
| import { RuleContext, RuleFailure, RuleMetadata, InfrastructureErrorCollector, RuleResult, TemplateAst, StyleAst, ProjectContext, AngularTypeIndex, WorkerFileProgress, ParserOptions, Result, AnalysisResult, ComponentFiles } from '@ngcompass/common'; | ||
| import { AnyAngularClassNode, CallExpression, AngularClassNode, DecoratedPropertyNode, NewExpression, TemplateAttributeNode, TemplateBlockNode, TemplateExpressionNode, TemplateAnalysis } from '@ngcompass/ast'; | ||
| import { Program } from 'oxc-parser'; | ||
| import ts, { TypeChecker } from 'typescript'; | ||
| import { CacheContext } from '@ngcompass/cache'; | ||
| import { ExecutionPlanOutput, Task } from '@ngcompass/planner'; | ||
| import { CacheContext } from '@ngcompass/cache'; | ||
| /** | ||
| * @fileoverview | ||
| * Facilitates the definition of analytical rules as passive observers. | ||
| * | ||
| * Rules implemented via this interface are designed for maximum performance, | ||
| * operating on pre-filtered node streams without performing independent | ||
| * AST traversals or resource allocations. | ||
| */ | ||
| type StreamType = 'AngularClass' | 'AnyAngularClass' | 'DecoratedProperty' | 'TemplateExpression' | 'TemplateAttribute' | 'TemplateBlock' | 'Template' | 'CallExpression' | 'NewExpression'; | ||
| /** | ||
| * Rule handler for a specific stream type. | ||
| * | ||
| * @template TNode - Node type from stream (pre-filtered, pre-analyzed) | ||
| */ | ||
| interface RuleHandler<TNode> { | ||
| readonly name: string; | ||
| readonly streamType: StreamType; | ||
| /** | ||
| * Evaluates a node from a pre-filtered analytical stream. | ||
| * | ||
| * Implementation must adhere to strict performance constraints, ensuring | ||
| * O(1) or near-constant time complexity and zero memory allocation. | ||
| * | ||
| * @param node The pre-analyzed AST node to evaluate. | ||
| * @param context The analytical context for the current file. | ||
| * @returns A rule failure, a collection of failures, or null if compliant. | ||
| */ | ||
| handle(node: TNode, context: RuleContext): RuleFailure | RuleFailure[] | null; | ||
| readonly meta?: Partial<RuleMetadata>; | ||
| } | ||
| /** | ||
| * Helper to create component rule handlers. | ||
| */ | ||
| declare const createComponentRule: (name: string, handler: (node: AngularClassNode, context: RuleContext) => RuleFailure | RuleFailure[] | null, meta?: Partial<RuleMetadata>) => RuleHandler<AngularClassNode>; | ||
| /** | ||
| * Helper to create rules that handle ANY Angular-decorated class: | ||
| * @Component, @Directive, @Pipe, @Injectable, @NgModule. | ||
| * | ||
| * Use this instead of createComponentRule when the rule applies to | ||
| * classes beyond just @Component and @Directive. | ||
| */ | ||
| declare const createAnyAngularClassRule: (name: string, handler: (node: AnyAngularClassNode, context: RuleContext) => RuleFailure | RuleFailure[] | null, meta?: Partial<RuleMetadata>) => RuleHandler<AnyAngularClassNode>; | ||
| /** | ||
| * Helper to create decorated property rule handlers. | ||
| */ | ||
| declare const createDecoratedPropertyRule: (name: string, handler: (node: DecoratedPropertyNode, context: RuleContext) => RuleFailure | RuleFailure[] | null) => RuleHandler<DecoratedPropertyNode>; | ||
| /** | ||
| * Helper to create template expression rule handlers. | ||
| */ | ||
| declare const createTemplateExpressionRule: (name: string, handler: (node: TemplateExpressionNode, context: RuleContext) => RuleFailure | RuleFailure[] | null, meta?: Partial<RuleMetadata>) => RuleHandler<TemplateExpressionNode>; | ||
| /** | ||
| * Helper to create template attribute rule handlers. | ||
| */ | ||
| declare const createTemplateAttributeRule: (name: string, handler: (node: TemplateAttributeNode, context: RuleContext) => RuleFailure | RuleFailure[] | null, meta?: Partial<RuleMetadata>) => RuleHandler<TemplateAttributeNode>; | ||
| /** | ||
| * Helper to create call expression rule handlers. | ||
| * | ||
| * Receives every CallExpression node in the file's AST exactly once. | ||
| * The handler is responsible for its own callee-shape filtering | ||
| * (e.g. checking for a StaticMemberExpression callee and a specific method name). | ||
| */ | ||
| declare const createCallExpressionRule: (name: string, handler: (node: CallExpression, context: RuleContext) => RuleFailure | RuleFailure[] | null, meta?: Partial<RuleMetadata>) => RuleHandler<CallExpression>; | ||
| /** | ||
| * Helper to create new expression rule handlers. | ||
| */ | ||
| declare const createNewExpressionRule: (name: string, handler: (node: NewExpression, context: RuleContext) => RuleFailure | RuleFailure[] | null, meta?: Partial<RuleMetadata>) => RuleHandler<NewExpression>; | ||
| /** | ||
| * Helper to create template block rule handlers. | ||
| */ | ||
| declare const createTemplateBlockRule: (name: string, handler: (node: TemplateBlockNode, context: RuleContext) => RuleFailure | RuleFailure[] | null, meta?: Partial<RuleMetadata>) => RuleHandler<TemplateBlockNode>; | ||
| /** | ||
| * Helper to create template rules that receive the full template analysis. | ||
| */ | ||
| declare const createTemplateRule: (name: string, handler: (node: TemplateAnalysis, context: RuleContext) => RuleFailure | RuleFailure[] | null, meta?: Partial<RuleMetadata>) => RuleHandler<TemplateAnalysis>; | ||
| /** | ||
| * @fileoverview | ||
| * Implements a high-performance visitor registry for O(1) node dispatch. | ||
| * | ||
| * This module facilitates the routing of AST nodes to their respective rule | ||
| * handlers by utilizing a static dispatch table, significantly optimizing the | ||
| * throughput of the single-pass engine. | ||
| */ | ||
| /** | ||
| * Compile-time exhaustiveness check. | ||
| * Every StreamType MUST have an entry here. | ||
| * Template streams use a sentinel prefix '__' — they are dispatched | ||
| * separately after the main AST walk and are skipped in buildVisitorMap. | ||
| */ | ||
| type StreamToNodeType = { | ||
@@ -103,48 +29,10 @@ [K in StreamType]: string; | ||
| declare const STREAM_TO_NODE_TYPE: StreamToNodeType; | ||
| /** | ||
| * A visitor entry bundles: | ||
| * - the raw-node → stream-node filter function | ||
| * - the rule handler | ||
| * - failure collection reference | ||
| * - per-rule timing reference | ||
| * | ||
| * This avoids closure allocation on the hot path by pre-binding everything | ||
| * at registry build time. | ||
| */ | ||
| interface VisitorEntry { | ||
| /** Rule name (for failure collection and timing) */ | ||
| readonly ruleName: string; | ||
| /** Converts raw Oxc node → typed stream node (or null if not applicable) */ | ||
| readonly filter: (rawNode: any) => unknown; | ||
| /** The rule handler's handle function (pre-bound) */ | ||
| readonly handle: (streamNode: any, ctx: RuleContext) => RuleFailure | RuleFailure[] | null; | ||
| } | ||
| /** | ||
| * Maps Oxc node.type → array of VisitorEntry. | ||
| * Lookup is O(1) (Map.get), dispatch is O(H) where H = handlers for that type. | ||
| */ | ||
| type VisitorMap = ReadonlyMap<string, ReadonlyArray<VisitorEntry>>; | ||
| /** | ||
| * Constructs a high-speed dispatch map from a collection of rule handlers. | ||
| * | ||
| * @param handlers A collection of rule handlers to include in the registry. | ||
| * @param streamFilters A mapping of stream types to their respective node filters. | ||
| * @returns A read-only VisitorMap optimized for O(1) lookup. | ||
| */ | ||
| declare function buildVisitorMap(handlers: ReadonlyArray<RuleHandler<any>>, streamFilters: Partial<Record<StreamType, (rawNode: any) => unknown>>): VisitorMap; | ||
| /** | ||
| * @fileoverview | ||
| * Implements the performance-optimized single-pass analytical engine. | ||
| * | ||
| * This component is designed for maximum throughput, performing exactly one | ||
| * traversal of the Abstract Syntax Tree (AST) while dispatching nodes to | ||
| * passive observers via an O(1) visitor infrastructure. | ||
| * | ||
| * Key Performance Characteristics: | ||
| * - Linear Complexity: O(N) traversal in relation to the number of AST nodes. | ||
| * - Optimized Dispatch: Leverages a high-speed visitor map for node-type routing. | ||
| * - Resource Efficiency: Maintains strict execution budgets for individual rules. | ||
| */ | ||
| interface RuleTiming { | ||
@@ -166,10 +54,2 @@ ruleName: string; | ||
| } | ||
| /** | ||
| * Executes a comprehensive analytical pass over a provided AST program. | ||
| * | ||
| * @param rules A collection of rule handlers to evaluate during the pass. | ||
| * @param context The analytical context containing the program and metadata. | ||
| * @param options Operational configuration, including error collection. | ||
| * @returns A consolidated result set and performance diagnostic report. | ||
| */ | ||
| declare const runSinglePassAnalysis: (rules: ReadonlyArray<RuleHandler<any>>, context: RuleContext, options?: { | ||
@@ -183,15 +63,2 @@ errorCollector?: InfrastructureErrorCollector; | ||
| /** | ||
| * @fileoverview | ||
| * Provides the RuleContextFactory for initializing rule execution contexts. | ||
| * | ||
| * This component is responsible for aggregating various analytical artifacts—including | ||
| * file content, AST structures, type information, and project metadata—into a | ||
| * unified RuleContext suitable for rule evaluation. | ||
| */ | ||
| /** | ||
| * Defines the operational boundaries for context construction. | ||
| * Encapsulates resource access requirements for both main-thread and worker-thread execution. | ||
| */ | ||
| interface ExecutionContext { | ||
@@ -204,35 +71,10 @@ readonly rootDir: string; | ||
| readonly getStyle: (filePath: string) => Promise<StyleAst | undefined>; | ||
| /** | ||
| * Retrieves the project-wide analytical context, if available. | ||
| */ | ||
| readonly getProjectContext?: () => ProjectContext | undefined; | ||
| /** | ||
| * Retrieves the TypeScript SourceFile representation for a given path. | ||
| */ | ||
| readonly getTsSourceFile?: (filePath: string) => ts.SourceFile | undefined; | ||
| readonly getAngularTypes?: () => AngularTypeIndex | undefined; | ||
| } | ||
| /** | ||
| * Orschestrates the assembly of fully-resolved RuleContext objects. | ||
| * | ||
| * Decouples the rule execution logic from the underlying storage and retrieval | ||
| * mechanisms by utilizing the ExecutionContext abstraction. | ||
| */ | ||
| declare class RuleContextFactory { | ||
| private readonly context; | ||
| constructor(context: ExecutionContext); | ||
| /** | ||
| * Builds a RuleContext for the given file. | ||
| * | ||
| * @param filePath - Absolute path to the file being analysed | ||
| * @param options - Rule-specific options from the configuration | ||
| * @param needsTemplate - Whether the rule(s) in this batch require the template | ||
| * @returns A fully resolved RuleContext | ||
| */ | ||
| build(filePath: string, options: Record<string, unknown>, needsTemplate: boolean): Promise<RuleContext>; | ||
| /** | ||
| * Builds a `ComponentCrossRef` for the given file path when that file is | ||
| * either a `.component.ts` class or its associated template. | ||
| * | ||
| * Returns `undefined` for all other file types. | ||
| */ | ||
| private resolveExternalTemplatePath; | ||
@@ -243,20 +85,16 @@ private buildCrossRef; | ||
| /** | ||
| * @fileoverview | ||
| * Provides the AnalysisContext abstraction for ngcompass. | ||
| * | ||
| * This module facilitates deterministic, memoized access to file system resources | ||
| * and abstract syntax tree (AST) artifacts, ensuring peak performance during | ||
| * analysis by minimizing redundant I/O and parsing operations. | ||
| * | ||
| * Implementation Strategy: | ||
| * - Memoization: Caches raw file content, TypeScript Programs, Template ASTs, and Style ASTs. | ||
| * - Resource Management: Implements an explicit eviction mechanism to control heap usage. | ||
| * - Determinism: Ensures the same file path always yields consistent artifacts across a run. | ||
| */ | ||
| interface AnalysisFileProgress { | ||
| readonly filePath: string; | ||
| readonly taskCount: number; | ||
| readonly issueCount: number; | ||
| readonly errorCount: number; | ||
| readonly warningCount: number; | ||
| readonly duration: number; | ||
| readonly cached?: boolean; | ||
| readonly typeAware?: boolean; | ||
| } | ||
| declare const buildFileProgress: (filePath: string, taskCount: number, results: ReadonlyArray<RuleResult>, duration: number, typeAware?: boolean) => AnalysisFileProgress; | ||
| declare const isWorkerFileProgress: (message: unknown) => message is WorkerFileProgress; | ||
| declare const isAnalysisFileProgress: (message: unknown) => message is AnalysisFileProgress; | ||
| /** | ||
| * Encapsulates the state and logic required for analyzing a set of source files. | ||
| * Provides unified access to file content and parsed metadata with internal memoization. | ||
| */ | ||
| interface AnalysisContext { | ||
@@ -268,202 +106,35 @@ readonly rootDir: string; | ||
| readonly getStyle: (filePath: string) => Promise<StyleAst | undefined>; | ||
| /** | ||
| * Purges all cached artifacts associated with the specified file path. | ||
| * | ||
| * This method is critical for memory governance in large-scale repositories. | ||
| * It should be invoked immediately upon completion of a file's analysis tasks | ||
| * to release references to raw strings and AST structures, enabling garbage | ||
| * collection. | ||
| * | ||
| * @param filePath The path of the file to evict from internal caches. | ||
| */ | ||
| readonly evict: (filePath: string) => void; | ||
| /** | ||
| * Clears every cached artifact owned by this context. | ||
| */ | ||
| readonly dispose: () => void; | ||
| } | ||
| /** | ||
| * Instantiates an AnalysisContext provider with integrated memoization logic. | ||
| * | ||
| * @param rootDir The base directory used to resolve relative file transitions. | ||
| * @returns A stateful AnalysisContext instance. | ||
| */ | ||
| declare const createAnalysisContext: (rootDir: string) => AnalysisContext; | ||
| /** | ||
| * @fileoverview | ||
| * Implements the high-level analysis orchestration pipeline. | ||
| * | ||
| * The orchestrator manages the lifecycle of an analysis run, coordinating | ||
| * task distribution across local threads and worker pools, while managing | ||
| * analytical state, result aggregation, and caching strategies. | ||
| */ | ||
| interface AnalysisFileProgress { | ||
| readonly filePath: string; | ||
| readonly taskCount: number; | ||
| readonly issueCount: number; | ||
| readonly errorCount: number; | ||
| readonly warningCount: number; | ||
| readonly duration: number; | ||
| readonly cached?: boolean; | ||
| readonly typeAware?: boolean; | ||
| } | ||
| /** | ||
| * Configuration parameters for the analysis orchestration process. | ||
| */ | ||
| interface AnalysisOptions { | ||
| /** Root directory for resolving file paths */ | ||
| readonly rootDir: string; | ||
| /** Optional cache context for retrieving skipped task results */ | ||
| readonly cache?: CacheContext; | ||
| /** Enable debug logging */ | ||
| readonly debug?: boolean; | ||
| /** | ||
| * Maximum number of worker threads to use. | ||
| * Clamped to [1, os.cpus().length] (RFC §7.3 unified concurrency model). | ||
| * Defaults to os.cpus().length. | ||
| */ | ||
| readonly maxWorkers?: number; | ||
| /** | ||
| * Number of tasks above which the worker pool is used instead of local | ||
| * pLimit execution. Default: 150. | ||
| */ | ||
| readonly parallelThreshold?: number; | ||
| /** | ||
| * Integrates an optional infrastructure error sink. | ||
| * When provided, operational errors are encapsulated and reported through | ||
| * this collector instead of triggering terminal exceptions. | ||
| */ | ||
| readonly errorCollector?: InfrastructureErrorCollector; | ||
| /** | ||
| * All files discovered by the scanner for this run. | ||
| * | ||
| * CTX-001: Forwarded to `createTypeAwareAnalysisContext()` so the | ||
| * `ProjectContext` import-graph builder can restrict edges to intra-project | ||
| * imports and correctly populate `ProjectContext.projectFiles`. | ||
| * | ||
| * Optional for backward compatibility — when omitted the ProjectContext | ||
| * will still be built but `projectFiles` may be less complete (derived | ||
| * solely from the TypeScript Program's source-file list). | ||
| */ | ||
| readonly files?: ReadonlyArray<string>; | ||
| /** Optional TypeScript parser settings from config. */ | ||
| readonly parserOptions?: ParserOptions; | ||
| /** | ||
| * Maximum number of files per type-aware execution chunk. | ||
| * | ||
| * Type-aware tasks run on the main thread with a shared `ts.Program`. | ||
| * For large repos the Program can consume several GB. Chunking splits | ||
| * the work: each chunk creates its own scoped Program, processes its | ||
| * tasks, then lets the Program be garbage-collected before the next chunk | ||
| * starts. Smaller values reduce peak memory; larger values reduce the | ||
| * number of (expensive) `ts.createProgram` calls. | ||
| * | ||
| * Default: 100 files per chunk, or 50 for very large type-aware runs. | ||
| * Set to `Infinity` to disable chunking. | ||
| */ | ||
| readonly typeAwareChunkSize?: number; | ||
| /** | ||
| * Runs type-aware chunks in a separate Node process. This is slower than | ||
| * in-process execution, but gives the OS a hard memory boundary because | ||
| * each chunk process exits after producing results. | ||
| */ | ||
| readonly typeAwareIsolation?: 'auto' | 'process' | 'off'; | ||
| /** | ||
| * Controls how type-aware files are ordered before chunking. | ||
| * `dependency` attempts to keep nearby dependency groups together. | ||
| * `simple` skips the dependency pre-pass and sorts files by path. | ||
| */ | ||
| readonly typeAwareChunkStrategy?: 'dependency' | 'simple'; | ||
| /** | ||
| * Maximum number of type-aware chunks allowed to run at the same time. | ||
| * Each concurrent chunk may create its own TypeScript Program, so values | ||
| * above one trade memory for speed. | ||
| */ | ||
| readonly typeAwareConcurrency?: number; | ||
| /** | ||
| * Maximum number of files to process concurrently inside a single | ||
| * type-aware chunk. This reuses one TypeScript Program but may hold more | ||
| * file/template ASTs at once. | ||
| */ | ||
| readonly typeAwareFileConcurrency?: number; | ||
| /** | ||
| * When true, type-checker-dependent rules are skipped entirely. | ||
| * Use this as an escape valve for very large repos where even a chunked | ||
| * TS Program is too memory-intensive to build. | ||
| */ | ||
| readonly skipTypeCheck?: boolean; | ||
| /** | ||
| * Called whenever a batch of tasks completes. | ||
| * @param completed - Total tasks finished so far (including cached). | ||
| * @param total - Grand total tasks for this run (executed + cached). | ||
| */ | ||
| readonly onProgress?: (completed: number, total: number) => void; | ||
| /** Called whenever all tasks for a file have completed. */ | ||
| readonly onFileProgress?: (event: AnalysisFileProgress) => void; | ||
| } | ||
| /** | ||
| * Primary entry point for executing an analysis plan. | ||
| * | ||
| * Coordinates task execution strategies, manages result caching, and | ||
| * synthesizes the final analytical report. | ||
| * | ||
| * @param plan The execution plan containing tasks and cached metadata. | ||
| * @param options Configuration options for the orchestrator. | ||
| * @returns A promise resolving to an AnalysisResult encapsulation. | ||
| */ | ||
| declare const runAnalysis: (plan: ExecutionPlanOutput, options: AnalysisOptions) => Promise<Result<AnalysisResult>>; | ||
| /** | ||
| * @fileoverview | ||
| * Facilitates the construction of the ProjectContext for ngcompass. | ||
| * | ||
| * This module performs a traversal of the TypeScript module graph to assemble | ||
| * a comprehensive, read-only representation of the project structure. This | ||
| * includes import relationships, component clusters, and Angular-specific | ||
| * metadata (e.g., NgModule declarations). | ||
| * | ||
| * Implementation Strategy: | ||
| * - Efficient Traversal: Analyzes the resolved module graph in O(F + E) time. | ||
| * - Structural Disambiguation: Detects barrel files and component clusters. | ||
| * - Angular Awareness: Integrates decorator metadata for standalone and NgModule discovery. | ||
| */ | ||
| /** | ||
| * Constructs a ProjectContext immutable structure from a TypeScript Program. | ||
| * | ||
| * @param program The ts.Program instance used for type-aware analysis. | ||
| * @param files A collection of file paths identified during the scanning phase. | ||
| * @param rootDir The absolute path to the project root directory. | ||
| * @param componentFiles An optional mapping of component-related file clusters. | ||
| * @returns A fully initialized ProjectContext instance. | ||
| */ | ||
| declare function buildProjectContext(program: ts.Program, files: ReadonlyArray<string>, rootDir: string, componentFiles?: ReadonlyMap<string, ComponentFiles>): ProjectContext; | ||
| /** | ||
| * Enhances the baseline AnalysisContext with semantic and project-wide data. | ||
| */ | ||
| interface TypeAwareAnalysisContext extends AnalysisContext { | ||
| /** | ||
| * Retrieves the TypeScript TypeChecker for semantic analysis. | ||
| */ | ||
| readonly getTypeChecker: (filePath: string) => Promise<ts.TypeChecker | undefined>; | ||
| /** | ||
| * Retrieves the pre-built, immutable ProjectContext for the current run. | ||
| */ | ||
| readonly getProjectContext: () => ProjectContext | undefined; | ||
| /** | ||
| * Retrieves the TypeScript SourceFile representation for a given path. | ||
| */ | ||
| readonly getTsSourceFile: (filePath: string) => ts.SourceFile | undefined; | ||
| /** | ||
| * Performs an explicit initialization phase for type-aware resources. | ||
| */ | ||
| readonly getAngularTypes: () => AngularTypeIndex | undefined; | ||
| readonly warmup: () => Promise<void>; | ||
| /** | ||
| * Releases references to the TypeScript Program, TypeChecker, ProjectContext, | ||
| * and baseline parsed artifacts after a type-aware chunk finishes. | ||
| */ | ||
| readonly dispose: () => void; | ||
@@ -475,132 +146,20 @@ } | ||
| } | ||
| /** | ||
| * Initializes a TypeAwareAnalysisContext for the specified root directory. | ||
| * | ||
| * @param rootDir The absolute path to the project root. | ||
| * @param files A collection of file paths identified during the scanning phase. | ||
| * @returns A fully initialized TypeAwareAnalysisContext instance. | ||
| */ | ||
| declare const createTypeAwareAnalysisContext: (rootDir: string, files?: ReadonlyArray<string>, parserOptions?: ParserOptions, contextOptions?: TypeAwareAnalysisContextOptions) => TypeAwareAnalysisContext; | ||
| /** | ||
| * @fileoverview | ||
| * Provides the core execution runner for ngcompass. | ||
| * | ||
| * This module consolidates the rule execution logic utilized by both local | ||
| * sequential processes and worker-based parallel execution. It manages task | ||
| * batching, result mapping, and operational error handling. | ||
| */ | ||
| declare function createAngularTypeIndex(program: ts.Program | undefined): AngularTypeIndex; | ||
| /** | ||
| * Executes a collection of analysis tasks for a specific file. | ||
| * | ||
| * Processes tasks by grouping them into optimized execution batches. Utilizes | ||
| * the RuleContextFactory for resource initialization and coordinates with | ||
| * the configured rule executor for evaluation. | ||
| * | ||
| * @param tasks A collection of tasks to execute against a single file path. | ||
| * @param context The operational context providing resource access. | ||
| * @param errorCollector An optional sink for operational error reporting. | ||
| * @returns A promise resolving to a collection of rule execution results. | ||
| */ | ||
| declare const executeBatchedTasks: (tasks: ReadonlyArray<Task>, context: ExecutionContext, errorCollector?: InfrastructureErrorCollector) => Promise<RuleResult[]>; | ||
| /** | ||
| * @fileoverview | ||
| * Establishes a dependency injection boundary for rule execution. | ||
| * | ||
| * This module facilitates the decoupling of the engine's core orchestration layer | ||
| * from the concrete rule implementations, mitigating circular dependency risks. | ||
| */ | ||
| /** | ||
| * Executes multiple rules in a single AST pass and returns their results. | ||
| * Implemented by rules/src/engine/adapter.ts → executeBatchedNewEngineRules. | ||
| */ | ||
| type BatchRuleExecutorFn = (ruleNames: ReadonlyArray<string>, context: RuleContext) => ReadonlyArray<RuleResult>; | ||
| /** | ||
| * Returns true if a rule with the given name is registered in the engine. | ||
| * Implemented by rules/src/engine/adapter.ts → isNewEngineRule. | ||
| */ | ||
| type RuleCheckerFn = (ruleName: string) => boolean; | ||
| /** | ||
| * Configures the analytical engine with specific rule execution and validation logic. | ||
| * | ||
| * This method must be invoked during the initialization phase of the process | ||
| * to register the underlying rule processing capabilities. | ||
| * | ||
| * @param executor The implementation responsible for batched rule evaluation. | ||
| * @param checker The implementation responsible for rule presence verification. | ||
| */ | ||
| declare const configureRuleExecutor: (executor: BatchRuleExecutorFn, checker: RuleCheckerFn) => void; | ||
| /** | ||
| * @fileoverview | ||
| * Facilitates the calculation of aggregate analytical metrics. | ||
| * | ||
| * This module provides utilities to derive summary statistics from rule execution | ||
| * results, including file counts, severity distributions, and execution durations. | ||
| */ | ||
| /** | ||
| * Computes comprehensive analytical statistics based on raw rule results. | ||
| * | ||
| * @param results A collection of rule execution results. | ||
| * @param startTime The high-resolution timestamp indicating the start of execution. | ||
| * @param cacheHitRate An optional precalculated cache hit ratio [0, 1]. | ||
| * @returns An object encapsulating the aggregated statistical data. | ||
| */ | ||
| declare const calculateStats: (results: ReadonlyArray<RuleResult>, startTime: number, cacheHitRate?: number) => AnalysisResult["stats"]; | ||
| /** | ||
| * @fileoverview | ||
| * Defines engine-level constants and performance benchmarks for ngcompass. | ||
| * | ||
| * This module serves as the centralized source of truth for architectural | ||
| * thresholds, concurrency limits, and performance budgets used across the | ||
| * orchestration and execution layers. | ||
| */ | ||
| /** | ||
| * The minimum number of worker threads to maintain in the execution pool, | ||
| * ensuring availability regardless of the host system's CPU topology. | ||
| */ | ||
| declare const MIN_WORKER_COUNT = 2; | ||
| /** | ||
| * The refresh interval for the command-line interface progress visualization. | ||
| */ | ||
| declare const SPINNER_FRAME_INTERVAL_MS = 80; | ||
| /** | ||
| * Maximum wall-clock time a single worker thread is allowed to run before it | ||
| * is forcibly terminated and its results replaced with empty failures. | ||
| * | ||
| * 60 seconds is intentionally generous — the timeout is a last-resort safety | ||
| * net for hung workers (e.g. infinite loops or deadlocked TypeScript programs), | ||
| * not a performance budget. | ||
| */ | ||
| declare const WORKER_TIMEOUT_MS = 60000; | ||
| /** | ||
| * Performance Budgets: | ||
| * Thresholds used to monitor and report execution efficiency. | ||
| */ | ||
| /** | ||
| * The target execution time (p95) per file for rules that do not utilize | ||
| * type-checking services. | ||
| */ | ||
| declare const BUDGET_MS_PER_FILE_WITHOUT_TYPES = 2; | ||
| /** | ||
| * The target execution time (p95) per file for rules requiring full | ||
| * type-checking capabilities. | ||
| */ | ||
| declare const BUDGET_MS_PER_FILE_WITH_TYPES = 5; | ||
| /** | ||
| * @fileoverview | ||
| * Provides a terminal-based progress visualization utility. | ||
| * | ||
| * Implements a high-efficiency visual indicator (spinner) for long-running | ||
| * command-line operations, isolated from core analytical logic. | ||
| */ | ||
| /** | ||
| * Manages the state and rendering of a terminal-based progress indicator. | ||
| */ | ||
| declare class Spinner { | ||
@@ -615,12 +174,4 @@ private frameIndex; | ||
| /** | ||
| * @fileoverview | ||
| * Manages parallel analysis execution utilizing worker thread pools. | ||
| * | ||
| * Coordinates the distribution of analytical tasks across multiple workers to | ||
| * maximize CPU utilization. Provides a resilient fallback to local concurrent | ||
| * execution if worker resources are unavailable. | ||
| */ | ||
| declare const runAnalysisParallel: (tasks: ReadonlyArray<Task>, rootDir: string, startTime: number, maxWorkers?: number, concurrency?: number, onFileProgress?: (event: AnalysisFileProgress) => void) => Promise<Result<AnalysisResult>>; | ||
| export { type AnalysisContext, type AnalysisFileProgress, type AnalysisOptions, BUDGET_MS_PER_FILE_WITHOUT_TYPES, BUDGET_MS_PER_FILE_WITH_TYPES, type BatchRuleExecutorFn, type ExecutionContext, MIN_WORKER_COUNT, type PerformanceReport, type RuleCheckerFn, RuleContextFactory, type RuleHandler, SPINNER_FRAME_INTERVAL_MS, STREAM_TO_NODE_TYPE, Spinner, type StreamType, type TypeAwareAnalysisContext, type VisitorEntry, type VisitorMap, WORKER_TIMEOUT_MS, buildProjectContext, buildVisitorMap, calculateStats, configureRuleExecutor, createAnalysisContext, createAnyAngularClassRule, createCallExpressionRule, createComponentRule, createDecoratedPropertyRule, createNewExpressionRule, createTemplateAttributeRule, createTemplateBlockRule, createTemplateExpressionRule, createTemplateRule, createTypeAwareAnalysisContext, executeBatchedTasks, runAnalysis, runAnalysisParallel, runSinglePassAnalysis }; | ||
| export { type AnalysisContext, type AnalysisFileProgress, type AnalysisOptions, BUDGET_MS_PER_FILE_WITHOUT_TYPES, BUDGET_MS_PER_FILE_WITH_TYPES, type BatchRuleExecutorFn, type ExecutionContext, MIN_WORKER_COUNT, type PerformanceReport, type RuleCheckerFn, RuleContextFactory, type RuleHandler, SPINNER_FRAME_INTERVAL_MS, STREAM_TO_NODE_TYPE, Spinner, type StreamType, type TypeAwareAnalysisContext, type VisitorEntry, type VisitorMap, WORKER_TIMEOUT_MS, buildFileProgress, buildProjectContext, buildVisitorMap, calculateStats, configureRuleExecutor, createAnalysisContext, createAngularTypeIndex, createAnyAngularClassRule, createCallExpressionRule, createComponentRule, createDecoratedPropertyRule, createNewExpressionRule, createTemplateAttributeRule, createTemplateBlockRule, createTemplateExpressionRule, createTemplateRule, createTypeAwareAnalysisContext, executeBatchedTasks, isAnalysisFileProgress, isWorkerFileProgress, runAnalysis, runAnalysisParallel, runSinglePassAnalysis }; |
+2
-2
@@ -1,3 +0,3 @@ | ||
| import {toNewExpressionStream,toCallExpressionStream,toDecoratedPropertyStream,toAnyAngularClassStream,toAngularClassStream,resetComponentCacheStats,walkProgram,analyzeTemplate,getComponentCacheStats,parseCss,parseHtml,extractTemplateFromProgram,parseTs}from'@ngcompass/ast';import {isDebugEnabled,createInfrastructureError,Locator,debug,stableSerialize,SerializationError,Ok,Err}from'@ngcompass/common';import T from'typescript';import V from'os';import {readFile}from'fs/promises';import {createRequire}from'module';import {fork}from'child_process';import*as F from'path';import F__default,{dirname,join}from'path';import {fileURLToPath}from'url';import {existsSync}from'fs';import ve from'v8';import ee from'p-limit';import {groupTasksByFile}from'@ngcompass/planner';import {LRUCache}from'lru-cache';var Ot=(e,t,r)=>({name:e,streamType:"AngularClass",handle:t,meta:r}),Bt=(e,t,r)=>({name:e,streamType:"AnyAngularClass",handle:t,meta:r}),Ht=(e,t)=>({name:e,streamType:"DecoratedProperty",handle:t}),Gt=(e,t,r)=>({name:e,streamType:"TemplateExpression",handle:t,meta:r}),Kt=(e,t,r)=>({name:e,streamType:"TemplateAttribute",handle:t,meta:r}),Ut=(e,t,r)=>({name:e,streamType:"CallExpression",handle:t,meta:r}),Vt=(e,t,r)=>({name:e,streamType:"NewExpression",handle:t,meta:r}),Yt=(e,t,r)=>({name:e,streamType:"TemplateBlock",handle:t,meta:r}),qt=(e,t,r)=>({name:e,streamType:"Template",handle:t,meta:r});var $e={AngularClass:"ClassDeclaration",AnyAngularClass:"ClassDeclaration",DecoratedProperty:"PropertyDefinition",TemplateExpression:"__template_expression__",TemplateAttribute:"__template_attribute__",TemplateBlock:"__template_block__",Template:"__template_analysis__",CallExpression:"CallExpression",NewExpression:"NewExpression"};function ae(e,t){let r=new Map;for(let o of e){let n=$e[o.streamType];if(!n||n.startsWith("__"))continue;let s=t[o.streamType];if(!s)continue;let l={ruleName:o.name,filter:s,handle:o.handle.bind(o)},a=r.get(n);a?a.push(l):r.set(n,[l]);}return r}var Fe=2,Ne=80,oe=6e4,Re=2,_e=5;var G=(e,t,r,o,n,s,l)=>{if(t.length!==0)for(let a of e)for(let i=0;i<t.length;i++){let d=t[i],f=s?performance.now():0;try{let p=d.handle(a,r);if(p){let u=o.get(d.name)??[];Array.isArray(p)?u.push(...p):u.push(p),o.set(d.name,u);}}catch(p){l?.record(createInfrastructureError("RuleExecutionError",{cause:`Rule ${d.name} failed on template node: ${p instanceof Error?p.message:String(p)}`,recoverable:true,phase:"engine",details:{ruleName:d.name,errorName:p instanceof Error?p.name:void 0}}));}if(s){let p=performance.now()-f,u=n.get(d.name);u&&(u.totalMs+=p,u.invocations++);}}},rr=(e,t,r)=>{let{program:o}=t;if(!o)return {results:e.map(c=>({ruleName:c.name,failures:[]})),performance:{traversalMs:0,nodesVisited:0,ruleTimings:[],cacheStats:{hits:0,misses:0},budgetViolations:[],hasBudgetViolations:false}};let n=performance.now(),s=ae(e,{AngularClass:toAngularClassStream,AnyAngularClass:toAnyAngularClassStream,DecoratedProperty:toDecoratedPropertyStream,CallExpression:toCallExpressionStream,NewExpression:toNewExpressionStream}),l=e.filter(c=>c.streamType==="TemplateExpression"),a=e.filter(c=>c.streamType==="TemplateAttribute"),i=e.filter(c=>c.streamType==="TemplateBlock"),d=e.filter(c=>c.streamType==="Template"),f=new Map,p=new Map,u=r?.collectRuleTimings??isDebugEnabled(),x=0;for(let c of e)p.set(c.name,{ruleName:c.name,totalMs:0,invocations:0});if(resetComponentCacheStats(),walkProgram(o,c=>{if(!c?.type)return;x++;let h=s.get(c.type);if(h)for(let S=0;S<h.length;S++){let m=h[S],E=u?performance.now():0;try{let C=m.filter(c);if(C!==null){let v=m.handle(C,t);if(v){let $=f.get(m.ruleName)??[];Array.isArray(v)?$.push(...v):$.push(v),f.set(m.ruleName,$);}}}catch(C){r?.errorCollector?.record(createInfrastructureError("RuleExecutionError",{cause:`Rule ${m.ruleName} failed: ${C instanceof Error?C.message:String(C)}`,recoverable:true,phase:"engine",details:{ruleName:m.ruleName,errorName:C instanceof Error?C.name:void 0}}));}if(u){let C=performance.now()-E,v=p.get(m.ruleName);v&&(v.totalMs+=C,v.invocations++);}}}),t.template&&(l.length>0||a.length>0||i.length>0||d.length>0)){let c=analyzeTemplate(t.template),h=t.templateFilePath&&t.templateFileContent&&t.templateLocator?{...t,filePath:t.templateFilePath,fileContent:t.templateFileContent,locator:t.templateLocator}:t;G(c.expressions,l,h,f,p,u,r?.errorCollector),G(c.attributes,a,h,f,p,u,r?.errorCollector),G(c.blocks,i,h,f,p,u,r?.errorCollector),G([c],d,h,f,p,u,r?.errorCollector);}let w=[];for(let c of e)w.push({ruleName:c.name,failures:f.get(c.name)??[]});let k=performance.now()-n,y=[],g=t.typeChecker?5:2;if(k>g&&y.push(`Total traversal time ${k.toFixed(2)}ms exceeds budget ${g}ms`),u)for(let c of p.values()){if(c.invocations===0)continue;let h=c.totalMs/c.invocations;h>1&&y.push(`Rule ${c.ruleName} averages ${h.toFixed(2)}ms per invocation (threshold: 1ms)`);}return {results:w,performance:{traversalMs:k,nodesVisited:x,ruleTimings:Array.from(p.values()),cacheStats:getComponentCacheStats(),budgetViolations:y,hasBudgetViolations:y.length>0}}};var K=class{context;constructor(t){this.context=t;}async build(t,r,o){let n,s,l,a,i,[d,f]=await Promise.all([this.context.readFile(t),this.context.getProgram(t)]),p=new Locator(d);this.context.getTypeChecker&&(n=await this.context.getTypeChecker(t)),o&&(s=await this.context.getTemplate(t));let u=this.context.getProjectContext?.();o&&!s&&u&&(l=this.resolveExternalTemplatePath(t,u))&&(s=await this.context.getTemplate(l),i=new Locator(a=await this.context.readFile(l)));let x=u?await this.buildCrossRef(t,s,u):void 0;return {filePath:t,fileContent:d,locator:p,program:f,typeChecker:n,template:s,templateFilePath:l,templateFileContent:a,templateLocator:i,options:r,project:u,crossRef:x}}resolveExternalTemplatePath(t,r){let o=t.endsWith(".component.ts")?t:r.templateToComponent.get(t);if(o)return r.componentGraph.get(o)?.templatePath}async buildCrossRef(t,r,o){let n,s,l,a=t.endsWith(".component.ts")?t:o.templateToComponent.get(t);if(!a)return;let i=o.componentGraph.get(a),d=i?.templatePath,f=i?.stylePaths??[],p=i?.specPath;try{let x=await this.getComponentSourceFile(a);x&&(n=(function(w){let k=new Set;for(let y of w.statements)if(T.isClassDeclaration(y))for(let g of y.members){let c=T.canHaveModifiers(g)?T.getModifiers(g):void 0;if(c&&c.some(S=>S.kind===T.SyntaxKind.PrivateKeyword||S.kind===T.SyntaxKind.ProtectedKeyword))continue;let h=g.name;h&&!T.isPrivateIdentifier(h)&&(T.isIdentifier(h)||T.isStringLiteral(h))&&k.add(h.text);}return k})(x),s=(function(w){let k=new Set;for(let y of w.statements)if(T.isClassDeclaration(y))for(let g of y.members){if(!T.isPropertyDeclaration(g)||!g.initializer||!g.name||T.isPrivateIdentifier(g.name))continue;let c=(function(h){let S=T.isAsExpression(h)||T.isSatisfiesExpression(h)?h.expression:h;if(!T.isCallExpression(S))return;let m=S.expression;return T.isIdentifier(m)?m.text:T.isPropertyAccessExpression(m)?T.isIdentifier(m.expression)&&m.expression.text==="input"?"input":m.name.text:void 0})(g.initializer);c&&Ke.has(c)&&(T.isIdentifier(g.name)||T.isStringLiteral(g.name))&&k.add(g.name.text);}return k})(x));}catch{}let u=r;if(!u&&d)try{u=await this.context.getTemplate(d);}catch{}if(u)try{l=(function(x){let w=new Set,k=/\{\{([\s\S]*?)\}\}/g;for(let y of x.rootNodes)(function g(c){if(c&&typeof c=="object"){if(Array.isArray(c.attrs))for(let h of c.attrs){if((typeof h.name=="string"?h.name:"").startsWith("("))continue;let S=h.value;typeof S=="string"&&S&&le(S,w);}if(typeof c.value=="string"&&c.value){let h;for(k.lastIndex=0;(h=k.exec(c.value))!==null;)le(h[1],w);}if(Array.isArray(c.children))for(let h of c.children)g(h);}})(y);return w})(u);}catch{}return {componentPath:a,templatePath:d,stylePaths:f,specPath:p,publicMembers:n,signalMembers:s,templateReferences:l}}async getComponentSourceFile(t){let r=this.context.getTsSourceFile?.(t);if(r)return r;let o=await this.context.readFile(t);return T.createSourceFile(t,o,T.ScriptTarget.Latest,true)}},Ke=new Set(["signal","computed","linkedSignal","input","model","toSignal"]),ie=/\b([a-zA-Z_$][a-zA-Z0-9_$]*)\b/g;function le(e,t){let r;for(ie.lastIndex=0;(r=ie.exec(e))!==null;)t.add(r[1]);}var Xe=new Set([".css",".scss",".sass",".less"]),_=e=>{let t=new LRUCache({max:128}),r=new Map,o=new Map,n=new Map,s=a=>{let i=t.get(a);if(i)return i;let d=Qe(e,a);return t.set(a,d),d},l=a=>{let i=r.get(a);if(i)return i;let d=(async()=>parseTs(await s(a),a).program)();return r.set(a,d),d};return {rootDir:e,readFile:s,getProgram:l,getTemplate:a=>{let i=o.get(a);if(i)return i;let d=(async()=>{let f=await et(a,s,l);if(f&&f.content)return parseHtml(f.content,f.startOffset)})();return o.set(a,d),d},getStyle:a=>{let i=n.get(a);if(i)return i;let d=F.extname(a).toLowerCase();if(!Xe.has(d)){let p=Promise.resolve(void 0);return n.set(a,p),p}let f=(async()=>parseCss(await s(a),a))();return n.set(a,f),f},evict:a=>{t.delete(a),r.delete(a),o.delete(a),n.delete(a);},dispose:()=>{t.clear(),r.clear(),o.clear(),n.clear();}}},Qe=async(e,t)=>{try{return await readFile(F.resolve(e,t),"utf-8")}catch(r){let o=r instanceof Error?r.message:String(r);throw Error(`Cannot read file: ${t}. ${o}`)}},et=async(e,t,r)=>{let o=F.extname(e);if(o===".html")return {content:await t(e),startOffset:0};if(o===".ts"){let n=extractTemplateFromProgram(await r(e));return n.content?n:null}return null};function q(e,t,r,o){let n=performance.now(),s=(function(y,g,c){var h;let S=new Set;for(let E of y)S.add(F.resolve(g,E));let m=(h=g).endsWith(F.sep)?h:h+F.sep;for(let E of c.getSourceFiles())!E.isDeclarationFile&&E.fileName.startsWith(m)&&S.add(E.fileName);return S})(t,r,e),{importGraph:l,reverseImportGraph:a,externalDeps:i}=(function(y,g){let c=y.getCompilerOptions(),h=T.createModuleResolutionCache(y.getCurrentDirectory(),T.sys.useCaseSensitiveFileNames?C=>C:C=>C.toLowerCase(),c),S=new Map,m=new Map,E=new Map;for(let C of g)S.set(C,new Set),m.set(C,new Set);for(let C of y.getSourceFiles()){if(C.isDeclarationFile||!g.has(C.fileName))continue;let v=C.fileName;for(let $ of (function(R){let A=[];return T.forEachChild(R,function N(P){T.isImportDeclaration(P)&&T.isStringLiteral(P.moduleSpecifier)||T.isExportDeclaration(P)&&P.moduleSpecifier&&T.isStringLiteral(P.moduleSpecifier)?A.push(P.moduleSpecifier.text):(T.isCallExpression(P)&&P.expression.kind===T.SyntaxKind.ImportKeyword&&P.arguments.length>0&&T.isStringLiteralLike(P.arguments[0])&&A.push(P.arguments[0].text),T.forEachChild(P,N));}),A})(C)){let R=T.resolveModuleName($,v,c,T.sys,h),A=R.resolvedModule?.resolvedFileName;if(A&&g.has(A)){S.get(v).add(A);let N=m.get(A);N||(N=new Set,m.set(A,N)),N.add(v);}else {let N=(function(P){if(P.startsWith(".")||F.isAbsolute(P))return null;if(P.startsWith("@")){let D=P.indexOf("/",1);if(D===-1)return null;let H=P.indexOf("/",D+1);return H===-1?P:P.slice(0,H)}let W=P.indexOf("/");return W===-1?P:P.slice(0,W)})($);if(N){let P=E.get(v);P||(P=new Set,E.set(v,P)),P.add(N);}}}}return {importGraph:S,reverseImportGraph:m,externalDeps:E}})(e,s),d=(function(y,g){let c=new Set;for(let h of y.getSourceFiles()){if(h.isDeclarationFile||!g.has(h.fileName))continue;let{statements:S}=h;S.length!==0&&S.every(m=>T.isExportDeclaration(m)&&m.moduleSpecifier!=null)&&c.add(h.fileName);}return c})(e,s),f=o??(function(y){let g=new Map,c=[".scss",".sass",".css",".less"];for(let h of y){if(!h.endsWith(".component.ts"))continue;let S=F.dirname(h),m=F.basename(h,".ts"),E=F.join(S,m+".html"),C=y.has(E)?E:void 0,v=[];for(let A of c){let N=F.join(S,m+A);y.has(N)&&v.push(N);}let $=F.join(S,m+".spec.ts"),R=y.has($)?$:void 0;g.set(h,{tsPath:h,templatePath:C,stylePaths:v,specPath:R});}return g})(s),p=(function(y){let g=new Map;for(let[c,h]of y)h.templatePath&&g.set(h.templatePath,c);return g})(f),{ngModuleMap:u,standaloneComponents:x,classToFile:w}=(function(y,g){let c=new Map,h=new Set,S=(function(m,E){let C=new Map;for(let v of m.getSourceFiles())if(!v.isDeclarationFile&&E.has(v.fileName))for(let $ of v.statements){if(!T.isClassDeclaration($)||!$.name)continue;(T.canHaveModifiers($)?T.getModifiers($):void 0)?.some(A=>A.kind===T.SyntaxKind.ExportKeyword)&&C.set($.name.text,v.fileName);}return C})(y,g);for(let m of y.getSourceFiles())if(!m.isDeclarationFile&&g.has(m.fileName))for(let E of m.statements){if(!T.isClassDeclaration(E))continue;let C=T.canHaveDecorators(E)?T.getDecorators(E):void 0;if(C?.length)for(let v of C){let $=(function(N){let P=N.expression;if(!T.isCallExpression(P))return;let W=P.expression;if(!T.isIdentifier(W))return;let D=P.arguments[0],H=D&&T.isObjectLiteralExpression(D)?D:void 0;return {name:W.text,arg:H}})(v);if(!$)continue;let{name:R,arg:A}=$;if(R==="NgModule"){A&&c.set(m.fileName,{filePath:m.fileName,declarations:new Set(j(A,"declarations")),imports:new Set(j(A,"imports")),exports:new Set(j(A,"exports")),providers:new Set(j(A,"providers")),isStandalone:false});break}if(R==="Component"){A&&pe(A,"standalone")&&(h.add(m.fileName),c.set(m.fileName,{filePath:m.fileName,declarations:new Set,imports:new Set(A?j(A,"imports"):[]),exports:new Set,providers:new Set(A?j(A,"providers"):[]),isStandalone:true}));break}if(R==="Directive"||R==="Pipe"){A&&pe(A,"standalone")&&h.add(m.fileName);break}}}return {ngModuleMap:c,standaloneComponents:h,classToFile:S}})(e,s),k=(function(y){let g=0;for(let c of y.values())g+=c.size;return g})(l);return debug("engine",`ProjectContext built in ${(performance.now()-n).toFixed(2)}ms \u2014 ${s.size} files, ${k} import edges, ${d.size} barrels, ${i.size} files with ext deps, ${f.size} components, ${p.size} template\u2194component links, ${u.size} NgModules/standalone, ${x.size} standalone`),{importGraph:l,reverseImportGraph:a,ngModuleMap:u,standaloneComponents:x,classToFile:w,componentGraph:f,projectFiles:s,rootDir:r,barrelFiles:d,externalDeps:i,templateToComponent:p}}function j(e,t){for(let r of e.properties){if(!T.isPropertyAssignment(r)||!T.isIdentifier(r.name)||r.name.text!==t)continue;let o=r.initializer;if(!T.isArrayLiteralExpression(o))break;let n=[];for(let s of o.elements)n.push(...(function l(a){return T.isIdentifier(a)?[a.text]:T.isPropertyAccessExpression(a)||T.isCallExpression(a)?l(a.expression):[]})(s));return n}return []}function pe(e,t){for(let r of e.properties)if(T.isPropertyAssignment(r)&&T.isIdentifier(r.name)&&r.name.text===t)return r.initializer.kind===T.SyntaxKind.TrueKeyword;return false}var Z=(e,t=[],r,o={})=>{let n,s,l=_(e);debug("engine","Initializing Type-Aware Context (ts.createProgram)...");let a=performance.now(),i=r?.tsconfigRootDir?F__default.resolve(e,r.tsconfigRootDir):e,d=r?.project?F__default.resolve(i,r.project):T.findConfigFile(i,T.sys.fileExists,"tsconfig.json");if(d){let p=T.readConfigFile(d,T.sys.readFile),u=T.parseJsonConfigFileContent(p.config,T.sys,i),x=(o.programRootFiles??t).filter(k=>(k.endsWith(".ts")||k.endsWith(".tsx"))&&!k.endsWith(".d.ts")),w=x.length>0?x:u.fileNames;n=T.createProgram({rootNames:w,options:u.options}),debug("engine",`TypeScript Program initialized in ${(performance.now()-a).toFixed(2)}ms with ${w.length} roots (tsconfig had ${u.fileNames.length})`);}else debug("engine","Could not find tsconfig.json; TypeChecker and ProjectContext will be unavailable.");let f=n?.getTypeChecker();if(n&&o.buildProjectContext!==false)try{s=q(n,t,e);}catch(p){let u=p instanceof Error?p.message:String(p);debug("engine",`ProjectContext build failed (non-fatal): ${u}`);}else n&&debug("engine","Skipping ProjectContext build for type-checker-only chunk");return {...l,getTypeChecker:async p=>f,getProjectContext:()=>s,getTsSourceFile:p=>n?.getSourceFile(p),warmup:async()=>{debug("engine",`Type-aware context ready: ${n?`ts.Program with ${n.getSourceFiles().length} source files`:"no tsconfig found, TypeChecker unavailable"}`);},dispose:()=>{s=void 0,f=void 0,n=void 0,l.dispose();}}};var O=(e,t,r)=>{let o=performance.now()-t,n=e.flatMap(i=>i.failures),s=new Set(n.map(i=>i.filePath)),l=n.filter(i=>rt(i.severity)).length,a=n.filter(i=>at(i.severity)).length;return {totalFiles:s.size,totalErrors:l,totalWarnings:a,duration:o,cacheHitRate:r}},rt=e=>e==="error",at=e=>e==="warn";var ue=()=>{throw Error("[ngcompass] Rule executor not configured. Call configureRuleExecutor(executeBatchedNewEngineRules, isNewEngineRule) before running analysis.")},me=()=>false,ot=(e,t)=>{ue=e,me=t;},fe=()=>ue,he=()=>me;var B=async(e,t,r)=>{if(e.length===0)return [];let o=e[0].filePath,n=new K(t),s=new Map,l=[];for(let a of e){let i;if(!he()(a.ruleName)){debug("engine",`Skipping task ${a.taskId}: Rule "${a.ruleName}" not registered in engine.`),l.push({ruleName:a.ruleName,taskId:a.taskId,failures:[]});continue}try{i=stableSerialize(a.options||{});}catch(f){let p=f instanceof SerializationError?f.message:String(f);debug("engine",`Skipping task ${a.taskId}: failed to serialize options \u2014 ${p}`),r?.record(createInfrastructureError("SerializationError",{cause:p,phase:"engine",recoverable:true})),l.push({ruleName:a.ruleName,taskId:a.taskId,failures:[]});continue}let d=s.get(i)??{options:a.options,ruleNames:[],taskIds:[],severities:new Map};d.ruleNames.push(a.ruleName),d.taskIds.push(a.taskId),d.severities.set(a.ruleName,a.severity),s.set(i,d);}for(let a of s.values())try{let i=new Set(a.taskIds),d=e.some(x=>i.has(x.taskId)&&x.inputs.template?.needsAst),f=await n.build(o,a.options,d),p=fe()(a.ruleNames,f),u=new Map;for(let x=0;x<a.ruleNames.length;x++){let w=a.ruleNames[x],k=a.taskIds[x],y=u.get(w)??[];y.push(k),u.set(w,y);}for(let x of p){let w=u.get(x.ruleName),k=a.severities.get(x.ruleName),y=k?{...x,failures:x.failures.map(g=>({...g,severity:k}))}:x;if(w&&w.length>0){let g=w.shift();l.push({...y,taskId:g});}else l.push(y);}}catch(i){let d=i instanceof Error?i.message:String(i);debug("engine",`Failed to execute batch for ${o}: ${d}`),r?.record(createInfrastructureError("ParseError",{filePath:o,cause:d,phase:"engine",recoverable:true}));for(let f=0;f<a.ruleNames.length;f++)l.push({ruleName:a.ruleNames[f],failures:[],taskId:a.taskIds[f]});}return l};var ke=async(e,t,r,o,n,s)=>{let l,{Worker:a}=await import('worker_threads'),i=o??Math.max(2,V.cpus().length),d=await gt();if(!d)return debug("workers","Execution worker not found, falling back to local execution."),ft(e,t,r,n??i,s);let f=yt(e,i),p=0,u=()=>{p++,debug("workers",`Worker progress: ${p}/${i} complete`);},x=[],w=f.map(y=>new Promise((g,c)=>{let h=false,S=new a(d,{workerData:{rootDir:t,tasks:y}});x.push(S);let m=()=>{clearTimeout(E),S.removeAllListeners();},E=setTimeout(()=>{h||(h=true,m(),u(),S.terminate(),c(Error(`Worker timed out after ${6e4/1e3}s`)));},6e4);S.on("message",C=>{ht(C)?s?.(C):h||(h=true,m(),C.errors&&C.errors.length>0&&C.errors.forEach(v=>{debug("workers",`Worker failed task ${v.task.taskId}: ${v.error}`);}),u(),g(C.results));}),S.on("error",C=>{h||(h=true,m(),u(),c(C instanceof Error?C:Error(String(C))));}),S.on("exit",C=>{if(h||C===0)return;h=true,m();let v=createInfrastructureError("WorkerCrash",{cause:`Worker exited with code ${C}`,phase:"engine",recoverable:true,details:{exitCode:C}});debug("workers",`Worker crashed: ${v.cause}`),c(Error(v.cause));});}));try{l=await Promise.all(w);}catch(y){throw await Promise.allSettled(x.map(g=>g.terminate())),y}let k=l.flat();return Ok({results:k,parseErrors:[],stats:O(k,r)})},ft=async(e,t,r,o,n)=>{let s=_(t),l=ee(o),a=groupTasksByFile(e),i=(await Promise.all(Array.from(a.values()).map(d=>l(async()=>{let f=d[0]?.filePath,p=performance.now(),u=await B(d,s);return f&&s.evict(f),f&&n?.(dt(f,d.length,u,performance.now()-p)),u})))).flat();return Ok({results:i,parseErrors:[],stats:O(i,r)})},ht=e=>!!e&&typeof e=="object"&&e.kind==="file-progress",dt=(e,t,r,o)=>{let n=0,s=0;for(let l of r)for(let a of l.failures)a.severity==="error"?n++:a.severity==="warn"&&s++;return {filePath:e,taskCount:t,issueCount:n+s,errorCount:n,warningCount:s,duration:o}},gt=async()=>{let e=dirname(fileURLToPath(import.meta.url));try{let t=createRequire(import.meta.url).resolve("@ngcompass/rules/execution-worker");if(existsSync(t))return t}catch{}for(let t of [join(e,"..","..","rules","dist","execution-worker.js"),join(e,"..","..","rules","dist","execution-worker.cjs")])if(existsSync(t))return t;return null},yt=(e,t)=>{let r=Array.from(groupTasksByFile(e).values()).sort((s,l)=>l.length-s.length),o=Array.from({length:t},()=>[]),n=Array(t).fill(0);for(let s of r){let l=0,a=n[0];for(let i=1;i<t;i++)n[i]<a&&(a=n[i],l=i);o[l].push(...s),n[l]+=s.length;}return o};function Te(e){if(!e||typeof e!="object"||typeof e.ruleName!="string"||!Array.isArray(e.failures))return false;for(let t of e.failures)if(!t||typeof t!="object"||typeof t.filePath!="string"||typeof t.line!="number"||typeof t.column!="number"||typeof t.severity!="string")return false;return true}var ea=async(e,t)=>{try{if(e.precomputedAnalysis){if((function(m){if(!m||typeof m!="object"||!Array.isArray(m.results)||!Array.isArray(m.parseErrors)||!m.stats||typeof m.stats!="object")return !1;let E=m.stats;return typeof E.totalFiles=="number"&&typeof E.totalErrors=="number"&&typeof E.totalWarnings=="number"&&typeof E.duration=="number"})(e.precomputedAnalysis))return debug("engine","Returning precomputed analysis from cache (global hash match)"),Ok(e.precomputedAnalysis);debug("engine","Precomputed analysis failed schema validation \u2014 discarding stale cache entry and re-running analysis");}let r=performance.now(),{tasks:o,skippedTasks:n,cachedResults:s}=e,l=V.cpus().length,a=Math.max(1,Math.min(4,l-1)),i=Math.max(1,Math.min(t.maxWorkers??a,l)),d=t.parallelThreshold??150,f=o.filter(m=>!!m.needsTypeChecker||!!m.needsProjectContext),p=o.filter(m=>!m.needsTypeChecker&&!m.needsProjectContext);debug("engine",`workerTasks: ${p.length}, typeAwareTasks: ${f.length}`);let u=o.length+n.length,x=n.length,w=m=>{x=Math.min(x+m,u),t.onProgress?.(x,u);};n.length>0&&t.onProgress?.(x,u);let k=[];if(p.length>0)if(p.length>d){debug("engine",`Running analysis on ${p.length} syntax-only tasks using workers (max: ${i})...`);let m=await ke(p,t.rootDir,r,i,void 0,t.onFileProgress);if(!m.ok)return m;k=m.data.results,w(p.length);}else debug("engine",`Running analysis on ${p.length} syntax-only tasks locally with batching (concurrency: ${i})...`),k=await re(p,t.rootDir,i,!1,t.errorCollector,void 0,void 0,!0,void 0,w,t.onFileProgress);if(f.length>0&&!t.skipTypeCheck){let m=groupTasksByFile(f).size,E=t.typeAwareChunkSize??(m>=1e3?50:100),C=vt(t.typeAwareConcurrency,i),v=Pt(t.typeAwareFileConcurrency,i),$=await Tt(f,t.rootDir,C,v,E,t,w,t.onFileProgress);k=[...k,...$];}else f.length>0&&t.skipTypeCheck&&debug("engine",`Skipping ${f.length} type-aware tasks (--skip-type-check)`);let y=await Lt(n,s,t.cache),g=[...k,...y],c=o.length+n.length,h=c>0?y.length/c:void 0,S={results:g,parseErrors:[],stats:O(g,r,h)};if(t.cache&&e.globalHash&&S.results.length<=2e4){debug("engine","Caching full analysis result for global hash..."),t.debug&&(debug("engine",`Analysis Results: ${S.results.length} items`),S.results.length>0&&debug("engine",`Sample item keys: ${Object.keys(S.results[0]).join(", ")}`));try{await t.cache.analysis.set(e.globalHash,S);}catch(m){let E=m instanceof Error?m.message:String(m);debug("engine",`Failed to cache analysis result: ${E}`),t.errorCollector?.record(createInfrastructureError("IOError",{cause:`Failed to write analysis cache: ${E}`,phase:"engine",recoverable:!0}));}}else t.cache&&e.globalHash&&debug("engine",`Skipping full analysis cache: ${S.results.length} results exceeds 20000 result safety limit`);return Ok(S)}catch(r){return Err(r instanceof Error?r:Error(String(r)))}},re=async(e,t,r,o,n,s,l,a=true,i,d,f)=>{let p=o?Z(t,s??[],l,{buildProjectContext:a,programRootFiles:i}):_(t);o&&(await p.warmup(),debug("engine",`Phase 1 complete \u2014 TypeScript Program ready. Starting Phase 2: ${r} concurrent file batches.`));let u=groupTasksByFile(e);debug("engine",`Grouped ${e.length} tasks into ${u.size} file batches`);let x=ee(r),w=await Promise.all(Array.from(u.values()).map(k=>x(async()=>{let y=k[0]?.filePath,g=performance.now();try{let c=await B(k,p);return p.evict(y),d?.(k.length),y&&f?.(Ee(y,k.length,c,performance.now()-g,o)),c}catch(c){let h=c instanceof Error?c.message:String(c);return p.evict(y),d?.(k.length),y&&f?.(Ee(y,k.length,[],performance.now()-g,o)),n?.record(createInfrastructureError("IOError",{filePath:y,cause:`Batch execution failed: ${h}`,phase:"engine",recoverable:true})),[]}})));return p.dispose(),w.flat().filter(k=>k!==null)},Tt=async(e,t,r,o,n,s,l,a)=>{let i=groupTasksByFile(e),d=await Ft(i,t,s.typeAwareChunkStrategy??"dependency"),f=s.typeAwareIsolation??"auto",p=f==="process"||f==="auto"&&d.length>=500,u=Mt();debug("engine",`Type-aware: ${e.length} tasks across ${d.length} files; requested chunk size ${n}; adaptive cap ${u}; chunk concurrency=${r}; file concurrency=${o}; isolation=${p?"process":"in-process"}`);let x=[],w=ee(r);for(let k of bt(d,n,u,r)){let y=await Promise.all(k.map(g=>w(async()=>{debug("engine",`Type-aware chunk ${g.index}: ${g.files.length} files, ${g.programRootFiles.length} TS roots, ${g.tasks.length} tasks`);let c=p?await It(g.tasks,t,g.files,g.programRootFiles,g.buildProjectContext,o,s,a):await re(g.tasks,t,o,true,s.errorCollector,g.files,s.parserOptions,g.buildProjectContext,g.programRootFiles,l,a);return p&&l?.(g.tasks.length),c})));x.push(...y.flat()),Et();}return x},Et=()=>{let e=globalThis.gc;typeof e=="function"&&e();},vt=(e,t)=>Math.max(1,Math.min(e==null||Number.isNaN(e)?1:Math.floor(e),t,4)),Pt=(e,t)=>Math.max(1,Math.min(e==null||Number.isNaN(e)?1:Math.floor(e),t,8)),bt=(e,t,r,o)=>{let n=[],s=At(t,r),l=0,a=1;for(let i=0;i<e.length;){let d=[];for(let p=0;p<o&&i<e.length;p++){let u=e.slice(i,i+s),x=u.flatMap(([,k])=>k),w=be(x);w.length>0?d.push({index:a,tasks:x,files:u.map(([k])=>k),programRootFiles:w,buildProjectContext:x.some(k=>!!k.needsProjectContext)}):debug("engine",`Skipping type-aware chunk ${a} with no TypeScript roots (${x.length} tasks)`),a++,i+=u.length;}d.length>0&&n.push(d);let f=$t(s,l,r);s=f.chunkSize,l=f.lowHeapStreak;}return n},At=(e,t)=>Number.isFinite(e)?Math.max(10,Math.min(t,Math.floor(e))):t,Mt=()=>{let e,t=V.totalmem()/1073741824,r=V.freemem()/1073741824,o=ve.getHeapStatistics().heap_size_limit/1073741824,n=V.cpus().length;return e=r<1.5||t<4?100:r<3||t<8?300:r<6||t<16?650:r<12||t<32?1e3:1500,n<=4?e=Math.min(e,500):n>=12&&(e=Math.min(2e3,Math.round(1.2*e))),o<2?e=Math.min(e,250):o<4&&(e=Math.min(e,650)),e=Math.max(10,Math.min(2e3,e)),debug("engine",`Adaptive type-aware chunk cap: ${e} files (free memory ${r.toFixed(1)}GB, total memory ${t.toFixed(1)}GB, V8 heap limit ${o.toFixed(1)}GB, CPUs ${n})`),e},$t=(e,t,r)=>{let o=process.memoryUsage(),n=ve.getHeapStatistics().heap_size_limit,s=n>0?o.heapUsed/n:0;if(s>=.94&&e>10){let l=Math.max(10,Math.floor(e/2));return debug("engine",`Critical heap pressure after type-aware chunk (${Math.round(100*s)}% of V8 heap limit); reducing chunk size to ${l}`),{chunkSize:l,lowHeapStreak:0}}if(s>=.88&&e>10){let l=Math.max(10,Math.floor(.8*e));return debug("engine",`High heap pressure after type-aware chunk (${Math.round(100*s)}% of V8 heap limit); reducing chunk size to ${l}`),{chunkSize:l,lowHeapStreak:0}}if(s<=.35&&e<r){let l=t+1;if(l<3)return {chunkSize:e,lowHeapStreak:l};let a=Math.min(r,e+Math.max(10,Math.floor(.1*e)));return debug("engine",`Sustained low heap pressure after type-aware chunk (${Math.round(100*s)}% of V8 heap limit); increasing chunk size to ${a}`),{chunkSize:a,lowHeapStreak:0}}return {chunkSize:e,lowHeapStreak:0}},be=e=>{let t=new Set;for(let r of e){let o=r.inputs.typescript.path;(o.endsWith(".ts")||o.endsWith(".tsx"))&&!o.endsWith(".d.ts")&&t.add(o);}return [...t]},Ft=async(e,t,r)=>{let o=Array.from(e.entries());if(r==="simple")return debug("engine",`Type-aware chunk ordering: simple path sort for ${o.length} files`),Q(o);debug("engine",`Type-aware chunk ordering: dependency pre-pass for ${o.length} files`);let n=performance.now(),s=await Nt(o,t,n+5e3);return s?(debug("engine",`Dependency chunk ordering complete in ${(performance.now()-n).toFixed(1)}ms`),s):(debug("engine","Dependency chunk ordering exceeded 5000ms; falling back to simple path sort"),Q(o))},Nt=async(e,t,r)=>{let o=new Map,n=ee(64);for(let s=0;s<e.length;s+=64){if(performance.now()>r)return null;let l=e.slice(s,s+64);await Promise.all(l.map(a=>n(async()=>{let[i,d]=a,f=be(d)[0]??i,p=await Rt(f,t),u=o.get(p)??[];u.push(a),o.set(p,u);})));}return [...o.entries()].sort(([s],[l])=>s.localeCompare(l)).flatMap(([,s])=>Q(s))},Q=e=>[...e].sort(([t],[r])=>t.localeCompare(r)),Rt=async(e,t)=>{let r=dirname(e);try{let o=await readFile(e,"utf8"),n=_t(o);return n?join(r,n.split("/")[0]??""):r}catch{return dirname(join(t,e))}},_t=e=>/\b(?:import|export)\b[^'"]*['"](\.{1,2}\/[^'"]+)['"]/g.exec(e)?.[1]??null,It=async(e,t,r,o,n,s,l,a)=>{let i=await Dt();return i?new Promise((d,f)=>{let p=fork(i,[],{cwd:t,execArgv:[],stdio:["ignore","pipe","pipe","ipc"]}),u=false,x=setTimeout(()=>{u||(u=true,p.kill(),f(Error("Type-aware child process timed out after 600s")));},6e5);p.stdout?.on("data",w=>debug("engine",`[type-aware-child] ${String(w).trim()}`)),p.stderr?.on("data",w=>debug("engine",`[type-aware-child:stderr] ${String(w).trim()}`)),p.on("message",w=>{if(jt(w))return void a?.(w);if(zt(w)){if(u)return;u=true,clearTimeout(x),d(w.results);return}if(Wt(w)){if(u)return;u=true,clearTimeout(x),f(Error(w.error));}}),p.on("error",w=>{u||(u=true,clearTimeout(x),f(w));}),p.on("exit",w=>{u||(u=true,clearTimeout(x),f(Error(`Type-aware child process exited before completion with code ${w}`)));}),p.send({rootDir:t,tasks:e,files:r,programRootFiles:o,parserOptions:l.parserOptions,buildProjectContext:n,fileConcurrency:s});}):(debug("engine","Type-aware child worker not found; falling back to in-process execution"),re(e,t,s,true,l.errorCollector,r,l.parserOptions,n,o,void 0,a))},Dt=async()=>{let e=dirname(fileURLToPath(import.meta.url));try{let t=createRequire(import.meta.url).resolve("@ngcompass/rules/type-aware-worker");if(existsSync(t))return t}catch{}for(let t of [join(e,"..","..","rules","dist","type-aware-worker.js"),join(e,"..","..","rules","dist","type-aware-worker.cjs")])if(existsSync(t))return t;return null},jt=e=>!!e&&typeof e=="object"&&e.kind==="file-progress"&&typeof e.filePath=="string"&&typeof e.taskCount=="number"&&typeof e.issueCount=="number"&&typeof e.errorCount=="number"&&typeof e.warningCount=="number"&&typeof e.duration=="number",zt=e=>!!e&&typeof e=="object"&&e.kind==="complete"&&Array.isArray(e.results),Wt=e=>!!e&&typeof e=="object"&&e.kind==="error"&&typeof e.error=="string",Ee=(e,t,r,o,n)=>{let s=0,l=0;for(let a of r)for(let i of a.failures)i.severity==="error"?s++:i.severity==="warn"&&l++;return {filePath:e,taskCount:t,issueCount:s+l,errorCount:s,warningCount:l,duration:o,typeAware:n}},Lt=async(e,t,r)=>{if(e.length===0)return [];debug("engine",`Retrieving results for ${e.length} skipped tasks...`);let o=[],n=[];if(t)for(let s of e){let l=t.get(s.taskId);l&&Te(l)?o.push(l):n.push(s);}else n.push(...e);if(n.length>0&&r){debug("engine",`Fetching ${n.length} results from cache service...`);let s=n.map(a=>a.taskId),l=await r.results.getMany(s);for(let a of n){let i=l.get(a.taskId);i&&Te(i)&&o.push(i);}}return debug("engine",`Retrieved ${o.length} results from cache`),o};var Ae=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],Me=class{frameIndex=0;interval=null;message="";start(t){this.message=t,this.frameIndex=0,process.stdout.write("\x1B[?25l"),this.interval=setInterval(()=>{process.stdout.write(`\r${Ae[this.frameIndex]} ${this.message}`),this.frameIndex=(this.frameIndex+1)%Ae.length;},80);}update(t){this.message=t;}stop(t){this.interval&&(clearInterval(this.interval),this.interval=null),process.stdout.write("\r\x1B[K"),t&&process.stdout.write(t+` | ||
| `),process.stdout.write("\x1B[?25h");}};export{Re as BUDGET_MS_PER_FILE_WITHOUT_TYPES,_e as BUDGET_MS_PER_FILE_WITH_TYPES,Fe as MIN_WORKER_COUNT,K as RuleContextFactory,Ne as SPINNER_FRAME_INTERVAL_MS,$e as STREAM_TO_NODE_TYPE,Me as Spinner,oe as WORKER_TIMEOUT_MS,q as buildProjectContext,ae as buildVisitorMap,O as calculateStats,ot as configureRuleExecutor,_ as createAnalysisContext,Bt as createAnyAngularClassRule,Ut as createCallExpressionRule,Ot as createComponentRule,Ht as createDecoratedPropertyRule,Vt as createNewExpressionRule,Kt as createTemplateAttributeRule,Yt as createTemplateBlockRule,Gt as createTemplateExpressionRule,qt as createTemplateRule,Z as createTypeAwareAnalysisContext,B as executeBatchedTasks,ea as runAnalysis,ke as runAnalysisParallel,rr as runSinglePassAnalysis};//# sourceMappingURL=index.js.map | ||
| import {toNewExpressionStream,toCallExpressionStream,toDecoratedPropertyStream,toAnyAngularClassStream,toAngularClassStream,resetComponentCacheStats,walkProgram,analyzeTemplate,getComponentCacheStats,parseCss,parseHtml,extractTemplateFromProgram,parseTs}from'@ngcompass/ast';import {isDebugEnabled,createInfrastructureError,Locator,debug,stableSerialize,SerializationError,Ok,Err}from'@ngcompass/common';import C from'typescript';import {fork}from'child_process';import {existsSync}from'fs';import {readFile}from'fs/promises';import {createRequire}from'module';import re from'os';import*as $ from'path';import $__default,{dirname,join}from'path';import {fileURLToPath}from'url';import Ue from'v8';import {groupTasksByFile}from'@ngcompass/planner';import ye from'p-limit';import {LRUCache}from'lru-cache';import Z from'process';var fr=(e,t,r)=>({name:e,streamType:"AngularClass",handle:t,meta:r}),gr=(e,t,r)=>({name:e,streamType:"AnyAngularClass",handle:t,meta:r}),hr=(e,t)=>({name:e,streamType:"DecoratedProperty",handle:t}),dr=(e,t,r)=>({name:e,streamType:"TemplateExpression",handle:t,meta:r}),yr=(e,t,r)=>({name:e,streamType:"TemplateAttribute",handle:t,meta:r}),xr=(e,t,r)=>({name:e,streamType:"CallExpression",handle:t,meta:r}),wr=(e,t,r)=>({name:e,streamType:"NewExpression",handle:t,meta:r}),kr=(e,t,r)=>({name:e,streamType:"TemplateBlock",handle:t,meta:r}),Sr=(e,t,r)=>({name:e,streamType:"Template",handle:t,meta:r});var Je={AngularClass:"ClassDeclaration",AnyAngularClass:"ClassDeclaration",DecoratedProperty:"PropertyDefinition",TemplateExpression:"__template_expression__",TemplateAttribute:"__template_attribute__",TemplateBlock:"__template_block__",Template:"__template_analysis__",CallExpression:"CallExpression",NewExpression:"NewExpression"};function Ce(e,t){let r=new Map;for(let s of e){let n=Je[s.streamType];if(!n||n.startsWith("__"))continue;let a=t[s.streamType];if(!a)continue;let i={ruleName:s.name,filter:a,handle:s.handle.bind(s)},o=r.get(n);o?o.push(i):r.set(n,[i]);}return r}var Xe=2,Ze=80,Te=6e4,Qe=2,et=5;var Q=(e,t,r,s,n,a,i)=>{if(t.length!==0)for(let o of e)for(let l=0;l<t.length;l++){let u=t[l],g=a?performance.now():0;try{let p=u.handle(o,r);if(p){let m=s.get(u.name)??[];Array.isArray(p)?m.push(...p):m.push(p),s.set(u.name,m);}}catch(p){i?.record(createInfrastructureError("RuleExecutionError",{cause:`Rule ${u.name} failed on template node: ${p instanceof Error?p.message:String(p)}`,recoverable:true,phase:"engine",details:{ruleName:u.name,errorName:p instanceof Error?p.name:void 0}}));}if(a){let p=performance.now()-g,m=n.get(u.name);m&&(m.totalMs+=p,m.invocations++);}}},Ar=(e,t,r)=>{let{program:s}=t;if(!s)return {results:e.map(c=>({ruleName:c.name,failures:[]})),performance:{traversalMs:0,nodesVisited:0,ruleTimings:[],cacheStats:{hits:0,misses:0},budgetViolations:[],hasBudgetViolations:false}};let n=performance.now(),a=Ce(e,{AngularClass:toAngularClassStream,AnyAngularClass:toAnyAngularClassStream,DecoratedProperty:toDecoratedPropertyStream,CallExpression:toCallExpressionStream,NewExpression:toNewExpressionStream}),i=e.filter(c=>c.streamType==="TemplateExpression"),o=e.filter(c=>c.streamType==="TemplateAttribute"),l=e.filter(c=>c.streamType==="TemplateBlock"),u=e.filter(c=>c.streamType==="Template"),g=new Map,p=new Map,m=r?.collectRuleTimings??isDebugEnabled(),d=0;for(let c of e)p.set(c.name,{ruleName:c.name,totalMs:0,invocations:0});if(resetComponentCacheStats(),walkProgram(s,c=>{if(!c?.type)return;d++;let h=a.get(c.type);if(h)for(let S=0;S<h.length;S++){let f=h[S],E=m?performance.now():0;try{let b=f.filter(c);if(b!==null){let v=f.handle(b,t);if(v){let F=g.get(f.ruleName)??[];Array.isArray(v)?F.push(...v):F.push(v),g.set(f.ruleName,F);}}}catch(b){r?.errorCollector?.record(createInfrastructureError("RuleExecutionError",{cause:`Rule ${f.ruleName} failed: ${b instanceof Error?b.message:String(b)}`,recoverable:true,phase:"engine",details:{ruleName:f.ruleName,errorName:b instanceof Error?b.name:void 0}}));}if(m){let b=performance.now()-E,v=p.get(f.ruleName);v&&(v.totalMs+=b,v.invocations++);}}}),t.template&&(i.length>0||o.length>0||l.length>0||u.length>0)){let c=analyzeTemplate(t.template),h=t.templateFilePath&&t.templateFileContent&&t.templateLocator?{...t,filePath:t.templateFilePath,fileContent:t.templateFileContent,locator:t.templateLocator}:t;Q(c.expressions,i,h,g,p,m,r?.errorCollector),Q(c.attributes,o,h,g,p,m,r?.errorCollector),Q(c.blocks,l,h,g,p,m,r?.errorCollector),Q([c],u,h,g,p,m,r?.errorCollector);}let w=[];for(let c of e)w.push({ruleName:c.name,failures:g.get(c.name)??[]});let k=performance.now()-n,y=[],x=t.typeChecker?5:2;if(k>x&&y.push(`Total traversal time ${k.toFixed(2)}ms exceeds budget ${x}ms`),m)for(let c of p.values()){if(c.invocations===0)continue;let h=c.totalMs/c.invocations;h>1&&y.push(`Rule ${c.ruleName} averages ${h.toFixed(2)}ms per invocation (threshold: 1ms)`);}return {results:w,performance:{traversalMs:k,nodesVisited:d,ruleTimings:Array.from(p.values()),cacheStats:getComponentCacheStats(),budgetViolations:y,hasBudgetViolations:y.length>0}}};var ee=class{context;constructor(t){this.context=t;}async build(t,r,s){let n,a,i,o,l,[u,g]=await Promise.all([this.context.readFile(t),this.context.getProgram(t)]),p=new Locator(u);this.context.getTypeChecker&&(n=await this.context.getTypeChecker(t)),s&&(a=await this.context.getTemplate(t));let m=this.context.getProjectContext?.(),d=this.context.getAngularTypes?.();s&&!a&&m&&(i=this.resolveExternalTemplatePath(t,m))&&(a=await this.context.getTemplate(i),l=new Locator(o=await this.context.readFile(i)));let w=m?await this.buildCrossRef(t,a,m):void 0;return {filePath:t,fileContent:u,locator:p,program:g,typeChecker:n,template:a,templateFilePath:i,templateFileContent:o,templateLocator:l,options:r,project:m,crossRef:w,angularTypes:d}}resolveExternalTemplatePath(t,r){let s=t.endsWith(".component.ts")?t:r.templateToComponent.get(t);if(s)return r.componentGraph.get(s)?.templatePath}async buildCrossRef(t,r,s){let n,a,i,o=t.endsWith(".component.ts")?t:s.templateToComponent.get(t);if(!o)return;let l=s.componentGraph.get(o),u=l?.templatePath,g=l?.stylePaths??[],p=l?.specPath;try{let d=await this.getComponentSourceFile(o);d&&(n=(function(w){let k=new Set;for(let y of w.statements)if(C.isClassDeclaration(y))for(let x of y.members){let c=C.canHaveModifiers(x)?C.getModifiers(x):void 0;if(c&&c.some(S=>S.kind===C.SyntaxKind.PrivateKeyword||S.kind===C.SyntaxKind.ProtectedKeyword))continue;let h=x.name;h&&!C.isPrivateIdentifier(h)&&(C.isIdentifier(h)||C.isStringLiteral(h))&&k.add(h.text);}return k})(d),a=(function(w){let k=new Set;for(let y of w.statements)if(C.isClassDeclaration(y))for(let x of y.members){if(!C.isPropertyDeclaration(x)||!x.initializer||!x.name||C.isPrivateIdentifier(x.name))continue;let c=(function(h){let S=C.isAsExpression(h)||C.isSatisfiesExpression(h)?h.expression:h;if(!C.isCallExpression(S))return;let f=S.expression;return C.isIdentifier(f)?f.text:C.isPropertyAccessExpression(f)?C.isIdentifier(f.expression)&&f.expression.text==="input"?"input":f.name.text:void 0})(x.initializer);c&&ut.has(c)&&(C.isIdentifier(x.name)||C.isStringLiteral(x.name))&&k.add(x.name.text);}return k})(d));}catch{}let m=r;if(!m&&u)try{m=await this.context.getTemplate(u);}catch{}if(m)try{i=(function(d){let w=new Set,k=/\{\{([\s\S]*?)\}\}/g;for(let y of d.rootNodes)(function x(c){if(c&&typeof c=="object"){if(Array.isArray(c.attrs))for(let h of c.attrs){if((typeof h.name=="string"?h.name:"").startsWith("("))continue;let S=h.value;typeof S=="string"&&S&&ve(S,w);}if(typeof c.value=="string"&&c.value){let h;for(k.lastIndex=0;(h=k.exec(c.value))!==null;)ve(h[1],w);}if(Array.isArray(c.children))for(let h of c.children)x(h);}})(y);return w})(m);}catch{}return {componentPath:o,templatePath:u,stylePaths:g,specPath:p,publicMembers:n,signalMembers:a,templateReferences:i}}async getComponentSourceFile(t){let r=this.context.getTsSourceFile?.(t);if(r)return r;let s=await this.context.readFile(t);return C.createSourceFile(t,s,C.ScriptTarget.Latest,true)}},ut=new Set(["signal","computed","linkedSignal","input","model","toSignal"]),Pe=/\b([a-zA-Z_$][a-zA-Z0-9_$]*)\b/g;function ve(e,t){let r;for(Pe.lastIndex=0;(r=Pe.exec(e))!==null;)t.add(r[1]);}var yt=new Set([".css",".scss",".sass",".less"]),B=e=>{let t=new LRUCache({max:128}),r=new LRUCache({max:64}),s=new LRUCache({max:64}),n=new LRUCache({max:64}),a=o=>{let l=t.get(o);if(l)return l;let u=xt(e,o);return t.set(o,u),u},i=o=>{let l=r.get(o);if(l)return l;let u=a(o).then(g=>parseTs(g,o).program);return r.set(o,u),u};return {rootDir:e,readFile:a,getProgram:i,getTemplate:o=>{let l=s.get(o);if(l)return l;let u=wt(o,a,i);return s.set(o,u),u},getStyle:o=>{let l=n.get(o);if(l)return l;let u=$.extname(o).toLowerCase();if(!yt.has(u)){let p=Promise.resolve(void 0);return n.set(o,p),p}let g=a(o).then(p=>parseCss(p,o));return n.set(o,g),g},evict:o=>{t.delete(o),r.delete(o),s.delete(o),n.delete(o);},dispose:()=>{t.clear(),r.clear(),s.clear(),n.clear();}}},xt=async(e,t)=>{try{return await readFile($.resolve(e,t),"utf-8")}catch(r){let s=r instanceof Error?r.message:String(r);throw Error(`Cannot read file: ${t}. ${s}`)}},wt=async(e,t,r)=>{let s=await kt(e,t,r);if(s&&s.content)return parseHtml(s.content,s.startOffset)},kt=async(e,t,r)=>{let s=$.extname(e);if(s===".html")return {content:await t(e),startOffset:0};if(s===".ts"){let n=extractTemplateFromProgram(await r(e));return n.content?n:null}return null};var Y=(e,t,r)=>{let s=performance.now()-t,n=e.flatMap(l=>l.failures),a=new Set(n.map(l=>l.filePath)),i=0,o=0;for(let l of n)St(l.severity)?i++:Ct(l.severity)&&o++;return {totalFiles:a.size,totalErrors:i,totalWarnings:o,duration:s,cacheHitRate:r}},St=e=>e==="error",Ct=e=>e==="warn";var U=(e,t,r,s,n)=>{let a=0,i=0;for(let o of r)for(let l of o.failures)l.severity==="error"?a++:l.severity==="warn"&&i++;return {filePath:e,taskCount:t,issueCount:a+i,errorCount:a,warningCount:i,duration:s,typeAware:n}},se=e=>!!e&&typeof e=="object"&&e.kind==="file-progress",ie=e=>!!e&&typeof e=="object"&&e.kind==="file-progress"&&typeof e.filePath=="string"&&typeof e.taskCount=="number"&&typeof e.issueCount=="number"&&typeof e.errorCount=="number"&&typeof e.warningCount=="number"&&typeof e.duration=="number";var Ae=()=>{throw Error("[ngcompass] Rule executor not configured. Call configureRuleExecutor(executeBatchedNewEngineRules, isNewEngineRule) before running analysis.")},Fe=()=>false,Tt=(e,t)=>{Ae=e,Fe=t;},Me=()=>Ae,Ne=()=>Fe;var q=async(e,t,r)=>{if(e.length===0)return [];let s=e[0].filePath,n=new ee(t),a=new Map,i=[];for(let o of e){if(!Ne()(o.ruleName)){debug("engine",`Skipping task ${o.taskId}: Rule "${o.ruleName}" not registered in engine.`),i.push({ruleName:o.ruleName,taskId:o.taskId,failures:[]});continue}let l=vt(o,r);if(l===null){i.push({ruleName:o.ruleName,taskId:o.taskId,failures:[]});continue}let u=a.get(l)??Pt(o.options);u.ruleNames.push(o.ruleName),u.taskIds.push(o.taskId),u.severities.set(o.ruleName,o.severity),a.set(l,u);}for(let o of a.values()){let l=await At(o,e,s,n,r);i.push(...l);}return i},Pt=e=>({options:e,ruleNames:[],taskIds:[],severities:new Map}),vt=(e,t)=>{try{return stableSerialize(e.options||{})}catch(r){let s=r instanceof SerializationError?r.message:String(r);return debug("engine",`Skipping task ${e.taskId}: failed to serialize options \u2014 ${s}`),t?.record(createInfrastructureError("SerializationError",{cause:s,phase:"engine",recoverable:true})),null}},At=async(e,t,r,s,n)=>{try{let a=new Set(e.taskIds),i=t.some(u=>a.has(u.taskId)&&u.inputs.template?.needsAst),o=await s.build(r,e.options,i),l=Me()(e.ruleNames,o);return Ft(e,l)}catch(a){let i=a instanceof Error?a.message:String(a);return debug("engine",`Failed to execute batch for ${r}: ${i}`),n?.record(createInfrastructureError("ParseError",{filePath:r,cause:i,phase:"engine",recoverable:true})),e.ruleNames.map((o,l)=>({ruleName:o,failures:[],taskId:e.taskIds[l]}))}},Ft=(e,t)=>{let r=new Map;for(let n=0;n<e.ruleNames.length;n++){let a=e.ruleNames[n],i=r.get(a);i?i.ids.push(e.taskIds[n]):r.set(a,{ids:[e.taskIds[n]],cursor:0});}let s=[];for(let n of t){let a=e.severities.get(n.ruleName),i=a?{...n,failures:n.failures.map(l=>({...l,severity:a}))}:n,o=r.get(n.ruleName);o&&o.cursor<o.ids.length?s.push({...i,taskId:o.ids[o.cursor++]}):s.push(i);}return s};function pe(e,t,r,s){let n=performance.now(),a=(function(y,x,c){var h;let S=new Set;for(let E of y)S.add($.resolve(x,E));let f=(h=x).endsWith($.sep)?h:h+$.sep;for(let E of c.getSourceFiles())!E.isDeclarationFile&&E.fileName.startsWith(f)&&S.add(E.fileName);return S})(t,r,e),{importGraph:i,reverseImportGraph:o,externalDeps:l}=(function(y,x){let c=y.getCompilerOptions(),h=C.createModuleResolutionCache(y.getCurrentDirectory(),C.sys.useCaseSensitiveFileNames?b=>b:b=>b.toLowerCase(),c),S=new Map,f=new Map,E=new Map;for(let b of x)S.set(b,new Set),f.set(b,new Set);for(let b of y.getSourceFiles()){if(b.isDeclarationFile||!x.has(b.fileName))continue;let v=b.fileName;for(let F of (function(I){let A=[];return C.forEachChild(I,function N(T){C.isImportDeclaration(T)&&C.isStringLiteral(T.moduleSpecifier)||C.isExportDeclaration(T)&&T.moduleSpecifier&&C.isStringLiteral(T.moduleSpecifier)?A.push(T.moduleSpecifier.text):(C.isCallExpression(T)&&T.expression.kind===C.SyntaxKind.ImportKeyword&&T.arguments.length>0&&C.isStringLiteralLike(T.arguments[0])&&A.push(T.arguments[0].text),C.forEachChild(T,N));}),A})(b)){let I=C.resolveModuleName(F,v,c,C.sys,h),A=I.resolvedModule?.resolvedFileName;if(A&&x.has(A)){S.get(v).add(A);let N=f.get(A);N||(N=new Set,f.set(A,N)),N.add(v);}else {let N=(function(T){if(T.startsWith(".")||$.isAbsolute(T))return null;if(T.startsWith("@")){let _=T.indexOf("/",1);if(_===-1)return null;let W=T.indexOf("/",_+1);return W===-1?T:T.slice(0,W)}let z=T.indexOf("/");return z===-1?T:T.slice(0,z)})(F);if(N){let T=E.get(v);T||(T=new Set,E.set(v,T)),T.add(N);}}}}return {importGraph:S,reverseImportGraph:f,externalDeps:E}})(e,a),u=(function(y,x){let c=new Set;for(let h of y.getSourceFiles()){if(h.isDeclarationFile||!x.has(h.fileName))continue;let{statements:S}=h;S.length!==0&&S.every(f=>C.isExportDeclaration(f)&&f.moduleSpecifier!=null)&&c.add(h.fileName);}return c})(e,a),g=s??(function(y,x){let c=new Map,h=[".scss",".sass",".css",".less"];for(let S of y){if(!S.endsWith(".component.ts"))continue;let f=$.dirname(S),E=$.basename(S,".ts"),b=$.join(f,E+".html"),v=y.has(b)?b:void 0,F=[];for(let N of h){let T=$.join(f,E+N);y.has(T)&&F.push(T);}if(!v||F.length===0){let N=(function(T,z,_,W){let we,ke=T.getSourceFile(z);if(!ke)return {templatePath:void 0,stylePaths:[]};let Se=(function(j){for(let L of j.statements){if(!C.isClassDeclaration(L))continue;let R=C.canHaveDecorators(L)?C.getDecorators(L):void 0;if(R)for(let D of R){let O=D.expression;if(!C.isCallExpression(O)||!C.isIdentifier(O.expression)||O.expression.text!=="Component")continue;let oe=O.arguments[0];if(oe&&C.isObjectLiteralExpression(oe))return oe}}})(ke);if(!Se)return {templatePath:void 0,stylePaths:[]};let ne=[];for(let j of Se.properties){if(!C.isPropertyAssignment(j)||!C.isIdentifier(j.name))continue;let L=j.name.text;if(L==="templateUrl"){let R=ce(j.initializer);if(R){let D=$.resolve(_,R);W.has(D)&&(we=D);}continue}if(L==="styleUrl"){let R=ce(j.initializer);if(R){let D=$.resolve(_,R);W.has(D)&&ne.push(D);}continue}if(L==="styleUrls"&&C.isArrayLiteralExpression(j.initializer))for(let R of j.initializer.elements){let D=ce(R);if(!D)continue;let O=$.resolve(_,D);W.has(O)&&ne.push(O);}}return {templatePath:we,stylePaths:ne}})(x,S,f,y);!v&&N.templatePath&&(v=N.templatePath),F.length===0&&N.stylePaths.length>0&&F.push(...N.stylePaths);}let I=$.join(f,E+".spec.ts"),A=y.has(I)?I:void 0;c.set(S,{tsPath:S,templatePath:v,stylePaths:F,specPath:A});}return c})(a,e),p=(function(y){let x=new Map;for(let[c,h]of y)h.templatePath&&x.set(h.templatePath,c);return x})(g),{ngModuleMap:m,standaloneComponents:d,classToFile:w}=(function(y,x){let c=new Map,h=new Set,S=(function(f,E){let b=new Map;for(let v of f.getSourceFiles())if(!v.isDeclarationFile&&E.has(v.fileName))for(let F of v.statements){if(!C.isClassDeclaration(F)||!F.name)continue;(C.canHaveModifiers(F)?C.getModifiers(F):void 0)?.some(A=>A.kind===C.SyntaxKind.ExportKeyword)&&b.set(F.name.text,v.fileName);}return b})(y,x);for(let f of y.getSourceFiles())if(!f.isDeclarationFile&&x.has(f.fileName))for(let E of f.statements){if(!C.isClassDeclaration(E))continue;let b=C.canHaveDecorators(E)?C.getDecorators(E):void 0;if(b?.length)for(let v of b){let F=(function(N){let T=N.expression;if(!C.isCallExpression(T))return;let z=T.expression;if(!C.isIdentifier(z))return;let _=T.arguments[0],W=_&&C.isObjectLiteralExpression(_)?_:void 0;return {name:z.text,arg:W}})(v);if(!F)continue;let{name:I,arg:A}=F;if(I==="NgModule"){A&&c.set(f.fileName,{filePath:f.fileName,declarations:new Set(K(A,"declarations")),imports:new Set(K(A,"imports")),exports:new Set(K(A,"exports")),providers:new Set(K(A,"providers")),isStandalone:false});break}if(I==="Component"){A&&Ie(A,"standalone")&&(h.add(f.fileName),c.set(f.fileName,{filePath:f.fileName,declarations:new Set,imports:new Set(A?K(A,"imports"):[]),exports:new Set,providers:new Set(A?K(A,"providers"):[]),isStandalone:true}));break}if(I==="Directive"||I==="Pipe"){A&&Ie(A,"standalone")&&h.add(f.fileName);break}}}return {ngModuleMap:c,standaloneComponents:h,classToFile:S}})(e,a),k=(function(y){let x=0;for(let c of y.values())x+=c.size;return x})(i);return debug("engine",`ProjectContext built in ${(performance.now()-n).toFixed(2)}ms \u2014 ${a.size} files, ${k} import edges, ${u.size} barrels, ${l.size} files with ext deps, ${g.size} components, ${p.size} template\u2194component links, ${m.size} NgModules/standalone, ${d.size} standalone`),{importGraph:i,reverseImportGraph:o,ngModuleMap:m,standaloneComponents:d,classToFile:w,componentGraph:g,projectFiles:a,rootDir:r,barrelFiles:u,externalDeps:l,templateToComponent:p}}function ce(e){if(C.isStringLiteralLike(e)||C.isNoSubstitutionTemplateLiteral(e))return e.text}function K(e,t){for(let r of e.properties){if(!C.isPropertyAssignment(r)||!C.isIdentifier(r.name)||r.name.text!==t)continue;let s=r.initializer;if(!C.isArrayLiteralExpression(s))break;let n=[];for(let a of s.elements)n.push(...(function i(o){return C.isIdentifier(o)?[o.text]:C.isPropertyAccessExpression(o)||C.isCallExpression(o)?i(o.expression):[]})(a));return n}return []}function Ie(e,t){for(let r of e.properties)if(C.isPropertyAssignment(r)&&C.isIdentifier(r.name)&&r.name.text===t)return r.initializer.kind===C.SyntaxKind.TrueKeyword;return false}var Nt=new Set(["Signal","WritableSignal","InputSignal","ModelSignal","OutputEmitterRef"]),$t=new Set(["WritableSignal","InputSignal","ModelSignal"]),It=new Set(["Subject","BehaviorSubject","ReplaySubject","AsyncSubject"]);function ue(e){if(!e)return _t;let t=new Map,r=(n,a)=>{if(!n)return false;let i=t.get(a);i||(i=new WeakMap,t.set(a,i));let o=i.get(n);if(o!==void 0)return o;let l=(function(u,g){let p=u.getDeclarations();if(!p||p.length===0)return false;let m=`/node_modules/${g}/`;for(let d of p)if(d.getSourceFile().fileName.replace(_e,"/").includes(m))return true;return false})(n,a);return i.set(n,l),l},s=(n,a,i)=>{if(!n)return false;let o=n.aliasSymbol??n.symbol;return !!o&&!!a.has(o.name)&&r(o,i)};return {isFromPackage:r,isFromAngularCore:n=>r(n,"@angular/core"),isSignal:n=>s(n,Nt,"@angular/core"),isWritableSignal:n=>s(n,$t,"@angular/core"),isObservable:n=>{if(!n)return false;let a=n.aliasSymbol??n.symbol;return !!a&&a.name==="Observable"&&r(a,"rxjs")},isSubjectLike:n=>s(n,It,"rxjs"),isHttpClient:n=>{if(!n)return false;let a=n.aliasSymbol??n.symbol;return !!a&&a.name==="HttpClient"&&r(a,"@angular/common")},isInjectionToken:n=>{if(!n)return false;let a=n.aliasSymbol??n.symbol;return !!a&&a.name==="InjectionToken"&&r(a,"@angular/core")},isEventEmitter:n=>{if(!n)return false;let a=n.aliasSymbol??n.symbol;return !!a&&a.name==="EventEmitter"&&r(a,"@angular/core")},isChangeDetectorRef:n=>{if(!n)return false;let a=n.aliasSymbol??n.symbol;return !!a&&a.name==="ChangeDetectorRef"&&r(a,"@angular/core")},isInjectableClass:n=>!!n&&(!!(function(a){let i=a.getDeclarations()?.[0];if(!i||!C.isClassDeclaration(i)||!C.canHaveDecorators(i))return false;let o=C.getDecorators(i);if(!o)return false;for(let l of o){let u=l.expression,g=C.isCallExpression(u)?u.expression:u;if(C.isIdentifier(g)&&g.text==="Injectable")return true}return false})(n)||(function(a){let i=a.getDeclarations();if(!i||i.length===0)return false;for(let o of i)if(o.getSourceFile().fileName.replace(_e,"/").includes("/node_modules/@angular/"))return true;return false})(n))}}var _e=/\\/g,_t={isFromPackage:()=>false,isFromAngularCore:()=>false,isSignal:()=>false,isWritableSignal:()=>false,isObservable:()=>false,isSubjectLike:()=>false,isHttpClient:()=>false,isInjectionToken:()=>false,isEventEmitter:()=>false,isChangeDetectorRef:()=>false,isInjectableClass:()=>false};var me=(e,t=[],r,s={})=>{let n,a=B(e);debug("engine","Initializing Type-Aware Context (ts.createProgram)...");let i=performance.now(),{program:o}=(function(p,m,d,w){let k=d?.tsconfigRootDir?$__default.resolve(p,d.tsconfigRootDir):p,y=d?.project?$__default.resolve(k,d.project):C.findConfigFile(k,C.sys.fileExists,"tsconfig.json");if(!y)return {program:void 0};let x=C.readConfigFile(y,C.sys.readFile),c=C.parseJsonConfigFileContent(x.config,C.sys,k),h=(w.programRootFiles??m).filter(fe),S=h.length>0?h:c.fileNames,f=C.createProgram({rootNames:S,options:c.options});return debug("engine",`Program rootNames: ${S.length} (tsconfig had ${c.fileNames.length})`),{program:f}})(e,t,r,s);o?debug("engine",`TypeScript Program ready in ${(performance.now()-i).toFixed(2)}ms (${o.getSourceFiles().length} source files)`):debug("engine","Could not find tsconfig.json; TypeChecker and ProjectContext will be unavailable.");let l=o?.getTypeChecker(),u=ue(o);if(o&&s.buildProjectContext!==false)try{n=pe(o,t,e);}catch(p){let m=p instanceof Error?p.message:String(p);debug("engine",`ProjectContext build failed (non-fatal): ${m}`);}else o&&debug("engine","Skipping ProjectContext build for type-checker-only chunk");let g=o;return {...a,getTypeChecker:p=>Promise.resolve(l),getProjectContext:()=>n,getTsSourceFile:p=>g?.getSourceFile(p),getAngularTypes:()=>u,warmup:()=>(debug("engine",`Type-aware context ready: ${g?`ts.Program with ${g.getSourceFiles().length} source files`:"no tsconfig found, TypeChecker unavailable"}`),Promise.resolve()),dispose:()=>{n=void 0,l=void 0,u=void 0,g=void 0,a.dispose();}}},fe=e=>(e.endsWith(".ts")||e.endsWith(".tsx"))&&!e.endsWith(".d.ts");var Oe=async(e,t,r,s,n,a)=>{let i,{Worker:o}=await import('worker_threads'),l=s??Math.max(2,re.cpus().length),u=await Bt();if(!u)return debug("workers","Execution worker not found, falling back to local execution."),We(e,t,r,n??l,a);let g=Ht(e,l),p=[],m=0,d=()=>{m++,debug("workers",`Worker progress: ${m}/${l} complete`);},w=g.map(x=>Ot(o,u,t,x,p,d,a));try{i=await Promise.all(w);}catch(x){var k;return await Promise.allSettled(p.map(c=>c.terminate())),debug("workers",`Worker execution failed, retrying locally with one file batch at a time: ${(k=x)instanceof Error?k.message:String(k)}`),We(e,t,r,1,a)}let y=i.flat();return Ok({results:y,parseErrors:[],stats:Y(y,r)})},Ot=(e,t,r,s,n,a,i)=>new Promise((o,l)=>{let u=false,g=new e(t,{workerData:{rootDir:r,tasks:s}});n.push(g);let p=()=>{clearTimeout(m),g.removeAllListeners();},m=setTimeout(()=>{u||(u=true,p(),a(),g.terminate(),l(Error(`Worker timed out after ${6e4/1e3}s`)));},6e4);g.on("message",d=>{se(d)?i?.(d):u||(u=true,p(),d.errors&&d.errors.length>0&&d.errors.forEach(w=>{debug("workers",`Worker failed task ${w.task.taskId}: ${w.error}`);}),a(),o(d.results));}),g.on("error",d=>{u||(u=true,p(),a(),l(d instanceof Error?d:Error(String(d))));}),g.on("exit",d=>{if(u||d===0)return;u=true,p();let w=createInfrastructureError("WorkerCrash",{cause:`Worker exited with code ${d}`,phase:"engine",recoverable:true,details:{exitCode:d}});debug("workers",`Worker crashed: ${w.cause}`),l(Error(w.cause));});}),We=async(e,t,r,s,n)=>{let a=B(t),i=ye(s),o=groupTasksByFile(e),l=(await Promise.all(Array.from(o.values()).map(u=>i(async()=>{let g=u[0]?.filePath,p=performance.now(),m=await q(u,a);return g&&(a.evict(g),n?.(U(g,u.length,m,performance.now()-p))),m})))).flat();return Ok({results:l,parseErrors:[],stats:Y(l,r)})},Bt=async()=>{let e=dirname(fileURLToPath(import.meta.url));try{let t=createRequire(import.meta.url).resolve("@ngcompass/rules/execution-worker");if(existsSync(t))return t}catch{}return [join(e,"..","..","rules","dist","execution-worker.js"),join(e,"..","..","rules","dist","execution-worker.cjs")].find(t=>existsSync(t))??null},Ht=(e,t)=>{let r=Array.from(groupTasksByFile(e).values()).sort((a,i)=>i.length-a.length),s=Array.from({length:t},()=>[]),n=Array(t).fill(0);for(let a of r){let i=0,o=n[0];for(let l=1;l<t;l++)n[l]<o&&(o=n[l],i=l);s[i].push(...a),n[i]+=a.length;}return s};function Ge(e){if(!e||typeof e!="object"||typeof e.ruleName!="string"||!Array.isArray(e.failures))return false;for(let t of e.failures)if(!t||typeof t!="object"||typeof t.filePath!="string"||typeof t.line!="number"||typeof t.column!="number"||typeof t.severity!="string")return false;return true}var $a=async(e,t)=>{try{if(e.precomputedAnalysis){if((function(f){if(!f||typeof f!="object"||!Array.isArray(f.results)||!Array.isArray(f.parseErrors)||!f.stats||typeof f.stats!="object")return !1;let E=f.stats;return typeof E.totalFiles=="number"&&typeof E.totalErrors=="number"&&typeof E.totalWarnings=="number"&&typeof E.duration=="number"})(e.precomputedAnalysis))return debug("engine","Returning precomputed analysis from cache (global hash match)"),Ok(e.precomputedAnalysis);debug("engine","Precomputed analysis failed schema validation \u2014 discarding stale cache entry and re-running analysis");}let r=performance.now(),{tasks:s,skippedTasks:n,cachedResults:a}=e,i=re.cpus().length,o=Math.max(1,Math.min(4,i-1)),l=Math.max(1,Math.min(t.maxWorkers??o,i)),u=t.parallelThreshold??150,g=s.filter(f=>!!f.needsTypeChecker||!!f.needsProjectContext),p=s.filter(f=>!f.needsTypeChecker&&!f.needsProjectContext);debug("engine",`workerTasks: ${p.length}, typeAwareTasks: ${g.length}`);let m=s.length+n.length,d=n.length,w=f=>{d=Math.min(d+f,m),t.onProgress?.(d,m);};n.length>0&&t.onProgress?.(d,m);let k=[];if(p.length>0)if(p.length>u){debug("engine",`Running analysis on ${p.length} syntax-only tasks using workers (max: ${l})...`);let f=await Oe(p,t.rootDir,r,l,void 0,t.onFileProgress);if(!f.ok)return f;k=f.data.results,w(p.length);}else debug("engine",`Running analysis on ${p.length} syntax-only tasks locally with batching (concurrency: ${l})...`),k=await xe(p,t.rootDir,l,!1,t.errorCollector,void 0,void 0,!0,void 0,w,t.onFileProgress);if(g.length>0&&!t.skipTypeCheck){let f=groupTasksByFile(g).size,E=t.typeAwareChunkSize??(f>=1e3?50:100),b=Zt(t.typeAwareConcurrency,l),v=Qt(t.typeAwareFileConcurrency,l),F=await qt(g,t.rootDir,b,v,E,t,w,t.onFileProgress);k=[...k,...F];}else g.length>0&&t.skipTypeCheck&&debug("engine",`Skipping ${g.length} type-aware tasks (--skip-type-check)`);let y=await mr(n,a,t.cache),x=[...k,...y],c=s.length+n.length,h=c>0?y.length/c:void 0,S={results:x,parseErrors:[],stats:Y(x,r,h)};if(t.cache&&e.globalHash&&S.results.length<=2e4){debug("engine","Caching full analysis result for global hash..."),t.debug&&(debug("engine",`Analysis Results: ${S.results.length} items`),S.results.length>0&&debug("engine",`Sample item keys: ${Object.keys(S.results[0]).join(", ")}`));try{await t.cache.analysis.set(e.globalHash,S);}catch(f){let E=f instanceof Error?f.message:String(f);debug("engine",`Failed to cache analysis result: ${E}`),t.errorCollector?.record(createInfrastructureError("IOError",{cause:`Failed to write analysis cache: ${E}`,phase:"engine",recoverable:!0}));}}else t.cache&&e.globalHash&&debug("engine",`Skipping full analysis cache: ${S.results.length} results exceeds 20000 result safety limit`);return Ok(S)}catch(r){return Err(r instanceof Error?r:Error(String(r)))}},xe=async(e,t,r,s,n,a,i,o=true,l,u,g)=>{let p=s?me(t,a??[],i,{buildProjectContext:o,programRootFiles:l}):B(t);s&&(await p.warmup(),debug("engine",`Phase 1 complete \u2014 TypeScript Program ready. Starting Phase 2: ${r} concurrent file batches.`));let m=groupTasksByFile(e);debug("engine",`Grouped ${e.length} tasks into ${m.size} file batches`);let d=ye(r),w=await Promise.all(Array.from(m.values()).map(k=>d(async()=>{let y=k[0]?.filePath,x=performance.now();try{let c=await q(k,p);return p.evict(y),u?.(k.length),y&&g?.(U(y,k.length,c,performance.now()-x,s)),c}catch(c){let h=c instanceof Error?c.message:String(c);return p.evict(y),u?.(k.length),y&&g?.(U(y,k.length,[],performance.now()-x,s)),n?.record(createInfrastructureError("IOError",{filePath:y,cause:`Batch execution failed: ${h}`,phase:"engine",recoverable:true})),[]}})));return p.dispose(),w.flat().filter(k=>k!==null)},qt=async(e,t,r,s,n,a,i,o)=>{let l=groupTasksByFile(e),u=await nr(l,t,a.typeAwareChunkStrategy??"dependency"),g=a.typeAwareIsolation??"auto",p=g==="process"||g==="auto"&&u.length>=150,m=rr();debug("engine",`Type-aware: ${e.length} tasks across ${u.length} files; requested chunk size ${n}; adaptive cap ${m}; chunk concurrency=${r}; file concurrency=${s}; isolation=${p?"process":"in-process"}`);let d=[],w=ye(r),k=a.files&&a.files.length>0?a.files:void 0;for(let y of er(u,n,m,r)){let x=await Promise.all(y.map(c=>w(async()=>{debug("engine",`Type-aware chunk ${c.index}: ${c.files.length} files, ${c.programRootFiles.length} TS roots, ${c.tasks.length} tasks`);let h=Xt(k,c),S=p?await lr(c.tasks,t,h,c.programRootFiles,c.buildProjectContext,s,a,o):await xe(c.tasks,t,s,true,a.errorCollector,h,a.parserOptions,c.buildProjectContext,c.programRootFiles,i,o);return p&&i?.(c.tasks.length),S})));d.push(...x.flat()),Jt();}return d},Jt=()=>{let e=globalThis.gc;typeof e=="function"&&e();},Xt=(e,t)=>t.buildProjectContext&&e?e:t.files,Zt=(e,t)=>Math.max(1,Math.min(e==null||Number.isNaN(e)?1:Math.floor(e),t,4)),Qt=(e,t)=>Math.max(1,Math.min(e==null||Number.isNaN(e)?1:Math.floor(e),t,8)),er=(e,t,r,s)=>{let n=[],a=tr(t,r),i=0,o=1;for(let l=0;l<e.length;){let u=[];for(let p=0;p<s&&l<e.length;p++){let m=e.slice(l,l+a),d=m.flatMap(([,k])=>k),w=Ve(d);w.length>0?u.push({index:o,tasks:d,files:m.map(([k])=>k),programRootFiles:w,buildProjectContext:d.some(k=>!!k.needsProjectContext)}):debug("engine",`Skipping type-aware chunk ${o} with no TypeScript roots (${d.length} tasks)`),o++,l+=m.length;}u.length>0&&n.push(u);let g=ar(a,i,r);a=g.chunkSize,i=g.lowHeapStreak;}return n},tr=(e,t)=>Number.isFinite(e)?Math.max(10,Math.min(t,Math.floor(e))):t,rr=()=>{let e,t=re.totalmem()/1073741824,r=re.freemem()/1073741824,s=Ue.getHeapStatistics().heap_size_limit/1073741824,n=re.cpus().length;return e=r<1.5||t<4?100:r<3||t<8?300:r<6||t<16?650:r<12||t<32?1e3:1500,n<=4?e=Math.min(e,500):n>=12&&(e=Math.min(2e3,Math.round(1.2*e))),s<2?e=Math.min(e,250):s<4&&(e=Math.min(e,650)),e=Math.max(10,Math.min(2e3,e)),debug("engine",`Adaptive type-aware chunk cap: ${e} files (free memory ${r.toFixed(1)}GB, total memory ${t.toFixed(1)}GB, V8 heap limit ${s.toFixed(1)}GB, CPUs ${n})`),e},ar=(e,t,r)=>{let s=process.memoryUsage(),n=Ue.getHeapStatistics().heap_size_limit,a=n>0?s.heapUsed/n:0;if(a>=.94&&e>10){let i=Math.max(10,Math.floor(e/2));return debug("engine",`Critical heap pressure after type-aware chunk (${Math.round(100*a)}% of V8 heap limit); reducing chunk size to ${i}`),{chunkSize:i,lowHeapStreak:0}}if(a>=.88&&e>10){let i=Math.max(10,Math.floor(.8*e));return debug("engine",`High heap pressure after type-aware chunk (${Math.round(100*a)}% of V8 heap limit); reducing chunk size to ${i}`),{chunkSize:i,lowHeapStreak:0}}if(a<=.35&&e<r){let i=t+1;if(i<3)return {chunkSize:e,lowHeapStreak:i};let o=Math.min(r,e+Math.max(10,Math.floor(.1*e)));return debug("engine",`Sustained low heap pressure after type-aware chunk (${Math.round(100*a)}% of V8 heap limit); increasing chunk size to ${o}`),{chunkSize:o,lowHeapStreak:0}}return {chunkSize:e,lowHeapStreak:0}},Ve=e=>{let t=new Set;for(let r of e){let s=r.inputs.typescript.path;fe(s)&&t.add(s);}return [...t]},nr=async(e,t,r)=>{let s=Array.from(e.entries());if(r==="simple")return debug("engine",`Type-aware chunk ordering: simple path sort for ${s.length} files`),he(s);debug("engine",`Type-aware chunk ordering: dependency pre-pass for ${s.length} files`);let n=performance.now(),a=await or(s,t,n+5e3);return a?(debug("engine",`Dependency chunk ordering complete in ${(performance.now()-n).toFixed(1)}ms`),a):(debug("engine","Dependency chunk ordering exceeded 5000ms; falling back to simple path sort"),he(s))},or=async(e,t,r)=>{let s=new Map,n=ye(64);for(let a=0;a<e.length;a+=64){if(performance.now()>r)return null;let i=e.slice(a,a+64);await Promise.all(i.map(o=>n(async()=>{let[l,u]=o,g=Ve(u)[0]??l,p=await sr(g,t),m=s.get(p)??[];m.push(o),s.set(p,m);})));}return [...s.entries()].sort(([a],[i])=>a.localeCompare(i)).flatMap(([,a])=>he(a))},he=e=>[...e].sort(([t],[r])=>t.localeCompare(r)),sr=async(e,t)=>{let r=dirname(e);try{let s=await readFile(e,"utf8"),n=ir(s);return n?join(r,n.split("/")[0]??""):r}catch{return dirname(join(t,e))}},ir=e=>/\b(?:import|export)\b[^'"]*['"](\.{1,2}\/[^'"]+)['"]/g.exec(e)?.[1]??null,lr=async(e,t,r,s,n,a,i,o)=>{let l=await cr();return l?new Promise((u,g)=>{let p=fork(l,[],{cwd:t,execArgv:[],stdio:["ignore","pipe","pipe","ipc"]}),m=false,d=setTimeout(()=>{m||(m=true,p.kill(),g(Error("Type-aware child process timed out after 600s")));},6e5);p.stdout?.on("data",w=>debug("engine",`[type-aware-child] ${String(w).trim()}`)),p.stderr?.on("data",w=>debug("engine",`[type-aware-child:stderr] ${String(w).trim()}`)),p.on("message",w=>{if(ie(w))return void o?.(w);if(pr(w)){if(m)return;m=true,clearTimeout(d),u(w.results);return}if(ur(w)){if(m)return;m=true,clearTimeout(d),g(Error(w.error));}}),p.on("error",w=>{m||(m=true,clearTimeout(d),g(w));}),p.on("exit",w=>{m||(m=true,clearTimeout(d),g(Error(`Type-aware child process exited before completion with code ${w}`)));}),p.send({rootDir:t,tasks:e,files:r,programRootFiles:s,parserOptions:i.parserOptions,buildProjectContext:n,fileConcurrency:a});}):(debug("engine","Type-aware child worker not found; falling back to in-process execution"),xe(e,t,a,true,i.errorCollector,r,i.parserOptions,n,s,void 0,o))},cr=async()=>{let e=dirname(fileURLToPath(import.meta.url));try{let t=createRequire(import.meta.url).resolve("@ngcompass/rules/type-aware-worker");if(existsSync(t))return t}catch{}for(let t of [join(e,"..","..","rules","dist","type-aware-worker.js"),join(e,"..","..","rules","dist","type-aware-worker.cjs")])if(existsSync(t))return t;return null},pr=e=>!!e&&typeof e=="object"&&e.kind==="complete"&&Array.isArray(e.results),ur=e=>!!e&&typeof e=="object"&&e.kind==="error"&&typeof e.error=="string",mr=async(e,t,r)=>{if(e.length===0)return [];debug("engine",`Retrieving results for ${e.length} skipped tasks...`);let s=[],n=[];if(t)for(let a of e){let i=t.get(a.taskId);i&&Ge(i)?s.push(i):n.push(a);}else n.push(...e);if(n.length>0&&r){debug("engine",`Fetching ${n.length} results from cache service...`);let a=n.map(o=>o.taskId),i=await r.results.getMany(a);for(let o of n){let l=i.get(o.taskId);l&&Ge(l)&&s.push(l);}}return debug("engine",`Retrieved ${s.length} results from cache`),s};var Ye=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],qe=class{frameIndex=0;interval=null;message="";start(t){this.message=t,this.interval||(this.frameIndex=0,Z.stderr.write("\x1B[?25l"),this.interval=setInterval(()=>{Z.stderr.write(`\r${Ye[this.frameIndex]} ${this.message}`),this.frameIndex=(this.frameIndex+1)%Ye.length;},80));}update(t){this.message=t;}stop(t){this.interval&&(clearInterval(this.interval),this.interval=null),Z.stderr.write("\r\x1B[K"),t&&Z.stderr.write(t+` | ||
| `),Z.stderr.write("\x1B[?25h");}};export{Qe as BUDGET_MS_PER_FILE_WITHOUT_TYPES,et as BUDGET_MS_PER_FILE_WITH_TYPES,Xe as MIN_WORKER_COUNT,ee as RuleContextFactory,Ze as SPINNER_FRAME_INTERVAL_MS,Je as STREAM_TO_NODE_TYPE,qe as Spinner,Te as WORKER_TIMEOUT_MS,U as buildFileProgress,pe as buildProjectContext,Ce as buildVisitorMap,Y as calculateStats,Tt as configureRuleExecutor,B as createAnalysisContext,ue as createAngularTypeIndex,gr as createAnyAngularClassRule,xr as createCallExpressionRule,fr as createComponentRule,hr as createDecoratedPropertyRule,wr as createNewExpressionRule,yr as createTemplateAttributeRule,kr as createTemplateBlockRule,dr as createTemplateExpressionRule,Sr as createTemplateRule,me as createTypeAwareAnalysisContext,q as executeBatchedTasks,ie as isAnalysisFileProgress,se as isWorkerFileProgress,$a as runAnalysis,Oe as runAnalysisParallel,Ar as runSinglePassAnalysis};//# sourceMappingURL=index.js.map | ||
| //# sourceMappingURL=index.js.map |
+5
-5
| { | ||
| "name": "@ngcompass/engine", | ||
| "version": "0.1.6-beta", | ||
| "version": "0.1.7-beta", | ||
| "description": "Rule execution engine for ngcompass", | ||
@@ -27,6 +27,6 @@ "sideEffects": false, | ||
| "p-limit": "^6.1.0", | ||
| "@ngcompass/common": "0.1.6-beta", | ||
| "@ngcompass/cache": "0.1.6-beta", | ||
| "@ngcompass/planner": "0.1.6-beta", | ||
| "@ngcompass/ast": "0.1.6-beta" | ||
| "@ngcompass/common": "0.1.7-beta", | ||
| "@ngcompass/ast": "0.1.7-beta", | ||
| "@ngcompass/planner": "0.1.7-beta", | ||
| "@ngcompass/cache": "0.1.7-beta" | ||
| }, | ||
@@ -33,0 +33,0 @@ "peerDependencies": { |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
398937
4.79%450
-47.43%2
100%+ Added
+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
Updated
Updated
Updated