moonflower 1.5.0 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const j=require("crypto"),q=require("fs"),z=require("path"),U=require("url"),F=require("ts-morph"),f=require("../../utils/logger.cjs"),E=require("../discoveryModule/discoverImports/discoverImports.cjs"),I=require("../discoveryModule/discoverRouterFiles/discoverRouterFiles.cjs"),K=require("../discoveryModule/discoverRouters/discoverRouters.cjs"),W=require("../manager/OpenApiManager.cjs"),_=require("./getSourceFileTimestamp.cjs"),H=require("./nodeParsers.cjs"),k=require("./parseExposedModels.cjs"),A=require("./sourceFileCache.cjs"),D=require("./workerPool.cjs");var R=typeof document<"u"?document.currentScript:null;function B(s){const i=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(s){for(const n in s)if(n!=="default"){const a=Object.getOwnPropertyDescriptor(s,n);Object.defineProperty(i,n,a.get?a:{enumerable:!0,get:()=>s[n]})}}return i.default=s,Object.freeze(i)}const M=B(z);function V(){const s=["./analyzerWorker.mjs","./analyzerWorker.test.mjs"];for(const i of s){const n=new URL(i,typeof document>"u"?require("url").pathToFileURL(__filename).href:R&&R.tagName.toUpperCase()==="SCRIPT"&&R.src||new URL("openapi/analyzerModule/analyzerModule.cjs",document.baseURI).href);if(q.existsSync(U.fileURLToPath(n)))return n}throw new Error("analyzerWorker.mjs not found. Run yarn build, or run tests via yarn test (which compiles the worker first).")}const G=async({logLevel:s,tsconfigPath:i,sourceFilePaths:n,sourceFileDiscovery:a,incremental:l,profiling:m="stats"})=>{const r=W.OpenApiManager.getInstance();if(r.isReady())return;s&&f.Logger.setLevel(s),f.Logger.info("Preparing OpenAPI spec");const p=new F.Project({tsConfigFilePath:M.resolve(i),skipFileDependencyResolution:!0}),{explicitRouters:h,discoveredRouterFiles:g,allSourceFiles:N}=(()=>{const d=(n??[]).map(u=>M.resolve(u)).map(u=>p.getSourceFileOrThrow(u)),P=d.flatMap(u=>({fileName:u.getFilePath(),sourceFile:u,routers:K.discoverRouters(u)})),{discoveredRouterFiles:w,discoveredSourceFiles:L}=(()=>{if(a===!1)return{discoveredRouterFiles:[],discoveredSourceFiles:[]};const u=performance.now(),S=I.discoverRouterFiles({targetPath:typeof a=="object"?a.rootPath:".",tsConfigPath:i});return m!=="off"&&f.Logger.info(`File discovery took ${Math.round(performance.now()-u)}ms`),S})(),$=d.reduce((u,S)=>u.some(b=>b.getFilePath()===S.getFilePath())?u:u.concat(S),L);return{explicitRouters:P,discoveredRouterFiles:w,allSourceFiles:$}})(),T=h.reduce((t,c)=>t.some(d=>d.fileName===c.fileName)?t:t.concat(c),g),y=N.flatMap(t=>O(t)).filter(t=>!!t);y.length>0&&y[0]&&r.setHeader(y[0]);const x=N.flatMap(t=>v(t));r.setExposedModels(x);const e=typeof l=="object"&&l.cachePath?l.cachePath:M.resolve(process.cwd(),"node_modules",".cache","moonflower"),o=await C(T,{incremental:l!==!1,cachePath:e,timestampCache:{},profiling:m,tsconfigPath:M.resolve(i)});r.setStats({discoveredRouterFiles:g.map(t=>({path:t.fileName,routers:t.routers.named.map(c=>({name:c,endpoints:o.filter(d=>d.sourceFilePath===t.fileName).map(d=>`${d.method.toUpperCase()} ${d.path}`)}))})),explicitRouterFiles:h.map(t=>({path:t.fileName,routers:t.routers.named.map(c=>({name:c,endpoints:o.filter(d=>d.sourceFilePath===t.fileName).map(d=>`${d.method.toUpperCase()} ${d.path}`)}))}))}),r.setEndpoints(o),r.markAsReady()},C=async(s,i,n)=>{const a=i.profiling??"stats",l=performance.now(),m=[],r=[];for(const e of s){const o=_.getSourceFileTimestamp(e.sourceFile,i.timestampCache),t=i.incremental?A.SourceFileCache.getCachedResults(e.sourceFile,o,i.cachePath):null;t?(f.Logger.debug(`[${e.fileName}] Found cached results`),m.push({endpoints:t.endpoints,fileName:e.fileName,timing:0,endpointTimings:[]})):r.push({file:e,timestamp:o})}if(r.length===0)return a!=="off"&&f.Logger.info(`Router analysis took ${Math.round(performance.now()-l)}ms`),m.flatMap(e=>e.endpoints);const h=["get","post","put","delete","del","patch"].join("|"),g=[];for(const{file:e}of r)for(const o of e.routers.named){const t=new RegExp(`${o}\\.(?:${h})`);let c=0;e.sourceFile.forEachChild(d=>{const P=d.getText();t.test(P)&&(g.push({fileName:e.fileName,task:{taskId:j.randomUUID(),tsconfigPath:i.tsconfigPath,sourceFilePath:e.sourceFile.getFilePath(),routerName:o,endpointIndex:c}}),c++)})}const N=new D.WorkerPool(V());let T;try{T=await N.runAll(g.map(e=>e.task))}finally{N.terminate()}const y=new Map;for(const{file:e}of r)y.set(e.fileName,{endpoints:[],fileName:e.fileName,timing:0,endpointTimings:[]});for(let e=0;e<T.length;e++){const o=T[e],t=g[e].fileName,c=y.get(t);if("error"in o){f.Logger.error(`[${t}] Worker error: ${o.error}`);continue}c.endpoints.push(o.endpoint),c.timing+=o.timing,c.endpointTimings.push({method:o.endpoint.method,path:o.endpoint.path,timing:o.timing,sectionTimings:o.sectionTimings})}for(const{file:e,timestamp:o}of r){const t=y.get(e.fileName);t.endpoints.length>0&&A.SourceFileCache.cacheResults(e.sourceFile,o,i.cachePath,t.endpoints)}const x=[...m,...Array.from(y.values())];return a!=="off"&&f.Logger.info(`Router analysis took ${Math.round(performance.now()-l)}ms`),a==="stats"?x.map(e=>({fileName:e.fileName,timeTaken:e.timing})).sort((e,o)=>o.timeTaken-e.timeTaken).filter(e=>e.timeTaken>500).forEach(e=>{f.Logger.info(`- [${e.fileName}] Took ${Math.round(e.timeTaken)}ms to analyze`)}):a==="debug"&&x.map(e=>({fileName:e.fileName,timeTaken:e.timing,endpointTimings:e.endpointTimings})).sort((e,o)=>o.timeTaken-e.timeTaken).forEach(e=>{f.Logger.info(`- [${e.fileName}] Took ${Math.round(e.timeTaken)}ms to analyze`),e.endpointTimings.sort((o,t)=>t.timing-o.timing).forEach(o=>{f.Logger.info(` - ${o.method} ${o.path} (${Math.round(o.timing)}ms)`),o.sectionTimings.filter(t=>t.timing>=1).sort((t,c)=>c.timing-t.timing).forEach(t=>{f.Logger.info(` - ${t.section}: ${Math.round(t.timing)}ms`)})})}),x.flatMap(e=>e.endpoints)},O=s=>{const i=E.discoverImportedName({sourceFile:s,originalName:"useApiHeader"});if(!i)return null;const n=s.forEachChildAsArray().filter(r=>r.isKind(F.SyntaxKind.ExpressionStatement)).find(r=>i&&r.getText().startsWith(i));if(!n)return null;const a=n.getFirstDescendantByKindOrThrow(F.SyntaxKind.ObjectLiteralExpression),l=H.getValuesOfObjectLiteral(a),m=r=>typeof r=="string"||Array.isArray(r)&&r.every(p=>typeof p=="string")?r:r.reduce((p,h)=>typeof h=="string"?p:{...p,[h.identifier]:m(h.value)},{});return m(l)},v=s=>{const i=[],n=E.discoverImportedName({sourceFile:s,originalName:"useExposeApiModel"}),a=E.discoverImportedName({sourceFile:s,originalName:"useExposeNamedApiModels"});return s.forEachChildAsArray().filter(l=>l.isKind(F.SyntaxKind.ExpressionStatement)).map(l=>{if(n&&l.getText().startsWith(n)){const p=(l.getFirstChild()?.getChildrenOfKind(F.SyntaxKind.SyntaxList)||[])[0].getFirstChild();if(!p)return;i.push(k.parseExposedModel(p));return}if(a&&l.getText().startsWith(a)){const p=(l.getFirstChild()?.getChildrenOfKind(F.SyntaxKind.SyntaxList)||[])[0].getFirstChild();if(!p)return;k.parseNamedExposedModels(p).forEach(g=>i.push(g))}}),i};exports.analyzeMultipleSourceFiles=C;exports.analyzeSourceFileApiHeader=O;exports.analyzeSourceFileExposedModels=v;exports.prepareOpenApiSpec=G;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const j=require("crypto"),q=require("fs"),z=require("path"),U=require("url"),S=require("ts-morph"),f=require("../../utils/logger.cjs"),R=require("../discoveryModule/discoverImports/discoverImports.cjs"),I=require("../discoveryModule/discoverRouterFiles/discoverRouterFiles.cjs"),K=require("../discoveryModule/discoverRouters/discoverRouters.cjs"),W=require("../manager/OpenApiManager.cjs"),_=require("./getSourceFileTimestamp.cjs"),H=require("./nodeParsers.cjs"),k=require("./parseExposedModels.cjs"),C=require("./sourceFileCache.cjs"),D=require("./workerPool.cjs");var P=typeof document<"u"?document.currentScript:null;function B(s){const o=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(s){for(const a in s)if(a!=="default"){const l=Object.getOwnPropertyDescriptor(s,a);Object.defineProperty(o,a,l.get?l:{enumerable:!0,get:()=>s[a]})}}return o.default=s,Object.freeze(o)}const M=B(z);function V(){const s=["./analyzerWorker.mjs","./analyzerWorker.test.mjs"];for(const o of s){const a=new URL(o,typeof document>"u"?require("url").pathToFileURL(__filename).href:P&&P.tagName.toUpperCase()==="SCRIPT"&&P.src||new URL("openapi/analyzerModule/analyzerModule.cjs",document.baseURI).href);if(q.existsSync(U.fileURLToPath(a)))return a}throw new Error("analyzerWorker.mjs not found. Run yarn build, or run tests via yarn test (which compiles the worker first).")}const G=async({logLevel:s,tsconfigPath:o,sourceFilePaths:a,sourceFileDiscovery:l,incremental:c,profiling:m="stats"})=>{const t=W.OpenApiManager.getInstance();if(t.isReady())return;s&&f.Logger.setLevel(s),f.Logger.info("Preparing OpenAPI spec");const d=new S.Project({tsConfigFilePath:M.resolve(o),skipFileDependencyResolution:!0}),{explicitRouters:h,discoveredRouterFiles:F,allSourceFiles:N}=(()=>{const u=(a??[]).map(p=>M.resolve(p)).map(p=>d.getSourceFileOrThrow(p)),L=u.flatMap(p=>({fileName:p.getFilePath(),sourceFile:p,routers:K.discoverRouters(p)})),{discoveredRouterFiles:w,discoveredSourceFiles:O}=(()=>{if(l===!1)return{discoveredRouterFiles:[],discoveredSourceFiles:[]};const p=performance.now(),x=I.discoverRouterFiles({targetPath:typeof l=="object"?l.rootPath:".",tsConfigPath:o});return m!=="off"&&f.Logger.info(`File discovery took ${Math.round(performance.now()-p)}ms`),x})(),$=u.reduce((p,x)=>p.some(b=>b.getFilePath()===x.getFilePath())?p:p.concat(x),O);return{explicitRouters:L,discoveredRouterFiles:w,allSourceFiles:$}})(),T=h.reduce((i,y)=>i.some(u=>u.fileName===y.fileName)?i:i.concat(y),F),e=N.flatMap(i=>E(i)).filter(i=>!!i);e.length>0&&e[0]&&t.setHeader(e[0]);const r=N.flatMap(i=>v(i));t.setExposedModels(r);const n=typeof c=="object"&&c.cachePath?c.cachePath:M.resolve(process.cwd(),"node_modules",".cache","moonflower"),g=await A(T,{incremental:c!==!1,cachePath:n,timestampCache:{},profiling:m,tsconfigPath:M.resolve(o)});t.setStats({discoveredRouterFiles:F.map(i=>({path:i.fileName,routers:i.routers.named.map(y=>({name:y,endpoints:g.filter(u=>u.sourceFilePath===i.fileName).map(u=>`${u.method.toUpperCase()} ${u.path}`)}))})),explicitRouterFiles:h.map(i=>({path:i.fileName,routers:i.routers.named.map(y=>({name:y,endpoints:g.filter(u=>u.sourceFilePath===i.fileName).map(u=>`${u.method.toUpperCase()} ${u.path}`)}))}))}),t.setEndpoints(g),t.markAsReady()},A=async(s,o,a)=>{const l=o.profiling??"stats",c=performance.now(),m=[],t=[];for(const e of s){const r=_.getSourceFileTimestamp(e.sourceFile,o.timestampCache),n=o.incremental?C.SourceFileCache.getCachedResults(e.sourceFile,r,o.cachePath):null;n?(f.Logger.debug(`[${e.fileName}] Found cached results`),m.push({endpoints:n.endpoints,fileName:e.fileName,timing:0,endpointTimings:[]})):t.push({file:e,timestamp:r})}if(t.length===0)return l!=="off"&&f.Logger.info(`Router analysis took ${Math.round(performance.now()-c)}ms`),m.flatMap(e=>e.endpoints);const d=t.map(({file:e})=>({fileName:e.fileName,task:{taskId:j.randomUUID(),tsconfigPath:o.tsconfigPath,sourceFilePath:e.sourceFile.getFilePath(),routerNames:e.routers.named,filterEndpointPaths:a}})),h=new D.WorkerPool(V(),d.length);let F;try{F=await h.runAll(d.map(e=>e.task))}finally{h.terminate()}const N=new Map;for(const{file:e}of t)N.set(e.fileName,{endpoints:[],fileName:e.fileName,timing:0,endpointTimings:[]});for(let e=0;e<F.length;e++){const r=F[e],n=d[e].fileName,g=N.get(n);if("error"in r){f.Logger.error(`[${n}] Worker error: ${r.error}`);continue}g.endpoints=r.endpoints,g.endpointTimings=r.endpointTimings,g.timing=r.endpointTimings.reduce((i,y)=>i+y.timing,0)}for(const{file:e,timestamp:r}of t){const n=N.get(e.fileName);n.endpoints.length>0&&C.SourceFileCache.cacheResults(e.sourceFile,r,o.cachePath,n.endpoints)}const T=[...m,...Array.from(N.values())];return l!=="off"&&f.Logger.info(`Router analysis took ${Math.round(performance.now()-c)}ms`),l==="stats"?T.map(e=>({fileName:e.fileName,timeTaken:e.timing})).sort((e,r)=>r.timeTaken-e.timeTaken).filter(e=>e.timeTaken>500).forEach(e=>{f.Logger.info(`- [${e.fileName}] Took ${Math.round(e.timeTaken)}ms to analyze`)}):l==="debug"&&T.map(e=>({fileName:e.fileName,timeTaken:e.timing,endpointTimings:e.endpointTimings})).sort((e,r)=>r.timeTaken-e.timeTaken).forEach(e=>{f.Logger.info(`- [${e.fileName}] Took ${Math.round(e.timeTaken)}ms to analyze`),e.endpointTimings.sort((r,n)=>n.timing-r.timing).forEach(r=>{f.Logger.info(` - ${r.method} ${r.path} (${Math.round(r.timing)}ms)`),r.sectionTimings.filter(n=>n.timing>=1).sort((n,g)=>g.timing-n.timing).forEach(n=>{f.Logger.info(` - ${n.section}: ${Math.round(n.timing)}ms`)})})}),T.flatMap(e=>e.endpoints)},E=s=>{const o=R.discoverImportedName({sourceFile:s,originalName:"useApiHeader"});if(!o)return null;const a=s.forEachChildAsArray().filter(t=>t.isKind(S.SyntaxKind.ExpressionStatement)).find(t=>o&&t.getText().startsWith(o));if(!a)return null;const l=a.getFirstDescendantByKindOrThrow(S.SyntaxKind.ObjectLiteralExpression),c=H.getValuesOfObjectLiteral(l),m=t=>typeof t=="string"||Array.isArray(t)&&t.every(d=>typeof d=="string")?t:t.reduce((d,h)=>typeof h=="string"?d:{...d,[h.identifier]:m(h.value)},{});return m(c)},v=s=>{const o=[],a=R.discoverImportedName({sourceFile:s,originalName:"useExposeApiModel"}),l=R.discoverImportedName({sourceFile:s,originalName:"useExposeNamedApiModels"});return s.forEachChildAsArray().filter(c=>c.isKind(S.SyntaxKind.ExpressionStatement)).map(c=>{if(a&&c.getText().startsWith(a)){const d=(c.getFirstChild()?.getChildrenOfKind(S.SyntaxKind.SyntaxList)||[])[0].getFirstChild();if(!d)return;o.push(k.parseExposedModel(d));return}if(l&&c.getText().startsWith(l)){const d=(c.getFirstChild()?.getChildrenOfKind(S.SyntaxKind.SyntaxList)||[])[0].getFirstChild();if(!d)return;k.parseNamedExposedModels(d).forEach(F=>o.push(F))}}),o};exports.analyzeMultipleSourceFiles=A;exports.analyzeSourceFileApiHeader=E;exports.analyzeSourceFileExposedModels=v;exports.prepareOpenApiSpec=G;
2
2
  //# sourceMappingURL=analyzerModule.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"analyzerModule.cjs","sources":["../../../src/openapi/analyzerModule/analyzerModule.ts"],"sourcesContent":["import crypto from 'crypto'\nimport { existsSync } from 'fs'\nimport * as path from 'path'\nimport { fileURLToPath } from 'url'\n\nfunction resolveWorkerUrl(): URL {\n\tconst candidates = ['./analyzerWorker.mjs', './analyzerWorker.test.mjs']\n\tfor (const candidate of candidates) {\n\t\tconst url = new URL(candidate, import.meta.url)\n\t\tif (existsSync(fileURLToPath(url))) {\n\t\t\treturn url\n\t\t}\n\t}\n\tthrow new Error(\n\t\t'analyzerWorker.mjs not found. Run yarn build, or run tests via yarn test (which compiles the worker first).',\n\t)\n}\nimport { SourceFile, SyntaxKind } from 'ts-morph'\nimport { Project } from 'ts-morph'\n\nimport { Logger } from '../../utils/logger'\nimport { discoverImportedName } from '../discoveryModule/discoverImports/discoverImports'\nimport {\n\tDiscoveredSourceFile,\n\tdiscoverRouterFiles,\n} from '../discoveryModule/discoverRouterFiles/discoverRouterFiles'\nimport { discoverRouters } from '../discoveryModule/discoverRouters/discoverRouters'\nimport { ApiDocsHeader, OpenApiManager } from '../manager/OpenApiManager'\nimport { EndpointData, ExposedModelData } from '../types'\nimport { getSourceFileTimestamp, TimestampCache } from './getSourceFileTimestamp'\nimport { getValuesOfObjectLiteral, resolveEndpointPath } from './nodeParsers'\nimport { parseEndpoint, SectionTiming } from './parseEndpoint'\nimport { parseExposedModel, parseNamedExposedModels } from './parseExposedModels'\nimport { SourceFileCache } from './sourceFileCache'\nimport { WorkerPool, WorkerResult, WorkerTask } from './workerPool'\n\ntype Props = {\n\tlogLevel?: Parameters<(typeof Logger)['setLevel']>[0]\n\ttsconfigPath: string\n\tsourceFilePaths?: string[]\n\tsourceFileDiscovery?: boolean | FileDiscoveryConfig\n\tincremental?:\n\t\t| boolean\n\t\t| {\n\t\t\t\tcachePath: string\n\t\t }\n\tprofiling?: 'stats' | 'off' | 'debug'\n}\n\ntype FileDiscoveryConfig = {\n\trootPath: string\n}\n\ntype EndpointTiming = { method: string; path: string; timing: number; sectionTimings: SectionTiming[] }\n\n/**\n * @param tsconfigPath Path to tsconfig file relative to project root\n * @param sourceFilePaths Array of router source files relative to project root\n */\nexport const prepareOpenApiSpec = async ({\n\tlogLevel,\n\ttsconfigPath,\n\tsourceFilePaths,\n\tsourceFileDiscovery,\n\tincremental,\n\tprofiling = 'stats',\n}: Props): Promise<void> => {\n\tconst openApiManager = OpenApiManager.getInstance()\n\n\tif (openApiManager.isReady()) {\n\t\treturn\n\t}\n\n\tif (logLevel) {\n\t\tLogger.setLevel(logLevel)\n\t}\n\n\tLogger.info('Preparing OpenAPI spec')\n\n\tconst project = new Project({\n\t\ttsConfigFilePath: path.resolve(tsconfigPath),\n\t\tskipFileDependencyResolution: true,\n\t})\n\n\tconst { explicitRouters, discoveredRouterFiles, allSourceFiles } = (() => {\n\t\tconst sourceFilesToAdd = sourceFilePaths ?? []\n\t\tconst resolvedSourceFilePaths = sourceFilesToAdd.map((filepath) => path.resolve(filepath))\n\t\tconst sourceFiles = resolvedSourceFilePaths.map((filePath) => project.getSourceFileOrThrow(filePath))\n\t\tconst explicitRouters = sourceFiles.flatMap((file) => ({\n\t\t\tfileName: file.getFilePath(),\n\t\t\tsourceFile: file,\n\t\t\trouters: discoverRouters(file),\n\t\t}))\n\n\t\tconst { discoveredRouterFiles, discoveredSourceFiles } = (() => {\n\t\t\tif (sourceFileDiscovery === false) {\n\t\t\t\treturn { discoveredRouterFiles: [], discoveredSourceFiles: [] }\n\t\t\t}\n\n\t\t\tconst startTime = performance.now()\n\t\t\tconst files = discoverRouterFiles({\n\t\t\t\ttargetPath: typeof sourceFileDiscovery === 'object' ? sourceFileDiscovery.rootPath : '.',\n\t\t\t\ttsConfigPath: tsconfigPath,\n\t\t\t})\n\t\t\tif (profiling !== 'off') {\n\t\t\t\tLogger.info(`File discovery took ${Math.round(performance.now() - startTime)}ms`)\n\t\t\t}\n\t\t\treturn files\n\t\t})()\n\n\t\tconst allSourceFiles = sourceFiles.reduce(\n\t\t\t(acc, current) =>\n\t\t\t\tacc.some((r) => r.getFilePath() === current.getFilePath()) ? acc : acc.concat(current),\n\t\t\tdiscoveredSourceFiles,\n\t\t)\n\n\t\treturn { explicitRouters, discoveredRouterFiles, allSourceFiles }\n\t})()\n\n\tconst filesToAnalyze = explicitRouters.reduce(\n\t\t(acc, current) => (acc.some((r) => r.fileName === current.fileName) ? acc : acc.concat(current)),\n\t\tdiscoveredRouterFiles,\n\t)\n\n\tconst apiHeaders = allSourceFiles\n\t\t.flatMap((file) => analyzeSourceFileApiHeader(file))\n\t\t.filter((headers) => !!headers)\n\tif (apiHeaders.length > 0 && apiHeaders[0]) {\n\t\topenApiManager.setHeader(apiHeaders[0])\n\t}\n\n\tconst exposedModels = allSourceFiles.flatMap((file) => analyzeSourceFileExposedModels(file))\n\n\topenApiManager.setExposedModels(exposedModels)\n\n\tconst cachePath = (() => {\n\t\tif (typeof incremental === 'object' && incremental.cachePath) {\n\t\t\treturn incremental.cachePath\n\t\t}\n\t\treturn path.resolve(process.cwd(), 'node_modules', '.cache', 'moonflower')\n\t})()\n\tconst endpoints = await analyzeMultipleSourceFiles(filesToAnalyze, {\n\t\tincremental: incremental !== false,\n\t\tcachePath,\n\t\ttimestampCache: {},\n\t\tprofiling,\n\t\ttsconfigPath: path.resolve(tsconfigPath),\n\t})\n\n\topenApiManager.setStats({\n\t\tdiscoveredRouterFiles: discoveredRouterFiles.map((file) => ({\n\t\t\tpath: file.fileName,\n\t\t\trouters: file.routers.named.map((r) => ({\n\t\t\t\tname: r,\n\t\t\t\tendpoints: endpoints\n\t\t\t\t\t.filter((e) => e.sourceFilePath === file.fileName)\n\t\t\t\t\t.map((e) => `${e.method.toUpperCase()} ${e.path}`),\n\t\t\t})),\n\t\t})),\n\t\texplicitRouterFiles: explicitRouters.map((file) => ({\n\t\t\tpath: file.fileName,\n\t\t\trouters: file.routers.named.map((r) => ({\n\t\t\t\tname: r,\n\t\t\t\tendpoints: endpoints\n\t\t\t\t\t.filter((e) => e.sourceFilePath === file.fileName)\n\t\t\t\t\t.map((e) => `${e.method.toUpperCase()} ${e.path}`),\n\t\t\t})),\n\t\t})),\n\t})\n\n\topenApiManager.setEndpoints(endpoints)\n\topenApiManager.markAsReady()\n}\n\nexport const analyzeMultipleSourceFiles = async (\n\tfiles: DiscoveredSourceFile[],\n\tconfig: {\n\t\tincremental: boolean\n\t\tcachePath: string\n\t\ttimestampCache: TimestampCache\n\t\tprofiling?: 'stats' | 'off' | 'debug'\n\t\ttsconfigPath: string\n\t},\n\tfilterEndpointPaths?: string[],\n): Promise<EndpointData[]> => {\n\tconst profiling = config.profiling ?? 'stats'\n\tconst startTime = performance.now()\n\n\t// Separate cached files from those needing analysis\n\ttype CachedFile = { endpoints: EndpointData[]; fileName: string; timing: 0; endpointTimings: [] }\n\ttype UncachedFile = { file: DiscoveredSourceFile; timestamp: number }\n\n\tconst cached: CachedFile[] = []\n\tconst uncached: UncachedFile[] = []\n\n\tfor (const file of files) {\n\t\tconst timestamp = getSourceFileTimestamp(file.sourceFile, config.timestampCache)\n\t\tconst hit = config.incremental\n\t\t\t? SourceFileCache.getCachedResults(file.sourceFile, timestamp, config.cachePath)\n\t\t\t: null\n\t\tif (hit) {\n\t\t\tLogger.debug(`[${file.fileName}] Found cached results`)\n\t\t\tcached.push({ endpoints: hit.endpoints, fileName: file.fileName, timing: 0, endpointTimings: [] })\n\t\t} else {\n\t\t\tuncached.push({ file, timestamp })\n\t\t}\n\t}\n\n\tif (uncached.length === 0) {\n\t\tif (profiling !== 'off') {\n\t\t\tLogger.info(`Router analysis took ${Math.round(performance.now() - startTime)}ms`)\n\t\t}\n\t\treturn cached.flatMap((f) => f.endpoints)\n\t}\n\n\t// Build one task per endpoint across all uncached files\n\tconst OPERATIONS = ['get', 'post', 'put', 'delete', 'del', 'patch']\n\tconst operationsPattern = OPERATIONS.join('|')\n\n\ttype FileTask = { task: WorkerTask; fileName: string }\n\tconst allTasks: FileTask[] = []\n\n\tfor (const { file } of uncached) {\n\t\tfor (const routerName of file.routers.named) {\n\t\t\tconst routerPattern = new RegExp(`${routerName}\\\\.(?:${operationsPattern})`)\n\t\t\tlet endpointIndex = 0\n\t\t\tfile.sourceFile.forEachChild((node) => {\n\t\t\t\tconst nodeText = node.getText()\n\t\t\t\tif (routerPattern.test(nodeText)) {\n\t\t\t\t\tif (\n\t\t\t\t\t\t!filterEndpointPaths ||\n\t\t\t\t\t\tfilterEndpointPaths.some((p) => resolveEndpointPath(node)?.includes(p))\n\t\t\t\t\t) {\n\t\t\t\t\t\tallTasks.push({\n\t\t\t\t\t\t\tfileName: file.fileName,\n\t\t\t\t\t\t\ttask: {\n\t\t\t\t\t\t\t\ttaskId: crypto.randomUUID(),\n\t\t\t\t\t\t\t\ttsconfigPath: config.tsconfigPath,\n\t\t\t\t\t\t\t\tsourceFilePath: file.sourceFile.getFilePath(),\n\t\t\t\t\t\t\t\trouterName,\n\t\t\t\t\t\t\t\tendpointIndex,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t\tendpointIndex++\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t}\n\n\t// Dispatch all tasks to the worker pool\n\tconst pool = new WorkerPool(resolveWorkerUrl())\n\n\ttype EndpointTiming = { method: string; path: string; timing: number; sectionTimings: SectionTiming[] }\n\ttype FileResult = {\n\t\tendpoints: EndpointData[]\n\t\tfileName: string\n\t\ttiming: number\n\t\tendpointTimings: EndpointTiming[]\n\t}\n\n\tlet results: WorkerResult[]\n\ttry {\n\t\tresults = await pool.runAll(allTasks.map((ft) => ft.task))\n\t} finally {\n\t\tpool.terminate()\n\t}\n\n\t// Group results by file\n\tconst byFile = new Map<string, FileResult>()\n\tfor (const { file } of uncached) {\n\t\tbyFile.set(file.fileName, { endpoints: [], fileName: file.fileName, timing: 0, endpointTimings: [] })\n\t}\n\n\tfor (let i = 0; i < results.length; i++) {\n\t\tconst result = results[i]\n\t\tconst fileName = allTasks[i].fileName\n\t\tconst fileResult = byFile.get(fileName)!\n\n\t\tif ('error' in result) {\n\t\t\tLogger.error(`[${fileName}] Worker error: ${result.error}`)\n\t\t\tcontinue\n\t\t}\n\n\t\tfileResult.endpoints.push(result.endpoint)\n\t\tfileResult.timing += result.timing\n\t\tfileResult.endpointTimings.push({\n\t\t\tmethod: result.endpoint.method,\n\t\t\tpath: result.endpoint.path,\n\t\t\ttiming: result.timing,\n\t\t\tsectionTimings: result.sectionTimings,\n\t\t})\n\t}\n\n\t// Write cache for each uncached file\n\tfor (const { file, timestamp } of uncached) {\n\t\tconst fileResult = byFile.get(file.fileName)!\n\t\tif (fileResult.endpoints.length > 0) {\n\t\t\tSourceFileCache.cacheResults(file.sourceFile, timestamp, config.cachePath, fileResult.endpoints)\n\t\t}\n\t}\n\n\tconst analyzedFiles = [...cached, ...Array.from(byFile.values())]\n\n\tif (profiling !== 'off') {\n\t\tLogger.info(`Router analysis took ${Math.round(performance.now() - startTime)}ms`)\n\t}\n\n\tif (profiling === 'stats') {\n\t\tanalyzedFiles\n\t\t\t.map((f) => ({ fileName: f.fileName, timeTaken: f.timing }))\n\t\t\t.sort((a, b) => b.timeTaken - a.timeTaken)\n\t\t\t.filter((t) => t.timeTaken > 500)\n\t\t\t.forEach((t) => {\n\t\t\t\tLogger.info(`- [${t.fileName}] Took ${Math.round(t.timeTaken)}ms to analyze`)\n\t\t\t})\n\t} else if (profiling === 'debug') {\n\t\tanalyzedFiles\n\t\t\t.map((f) => ({ fileName: f.fileName, timeTaken: f.timing, endpointTimings: f.endpointTimings }))\n\t\t\t.sort((a, b) => b.timeTaken - a.timeTaken)\n\t\t\t.forEach((t) => {\n\t\t\t\tLogger.info(`- [${t.fileName}] Took ${Math.round(t.timeTaken)}ms to analyze`)\n\t\t\t\tt.endpointTimings\n\t\t\t\t\t.sort((a, b) => b.timing - a.timing)\n\t\t\t\t\t.forEach((ep) => {\n\t\t\t\t\t\tLogger.info(` - ${ep.method} ${ep.path} (${Math.round(ep.timing)}ms)`)\n\t\t\t\t\t\tep.sectionTimings\n\t\t\t\t\t\t\t.filter((s) => s.timing >= 1)\n\t\t\t\t\t\t\t.sort((a, b) => b.timing - a.timing)\n\t\t\t\t\t\t\t.forEach((s) => {\n\t\t\t\t\t\t\t\tLogger.info(` - ${s.section}: ${Math.round(s.timing)}ms`)\n\t\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t})\n\t}\n\n\treturn analyzedFiles.flatMap((f) => f.endpoints)\n}\n\nexport const analyzeSourceFileWithCache = (\n\tfile: DiscoveredSourceFile,\n\tconfig: {\n\t\tincremental: boolean\n\t\tcachePath: string\n\t\ttimestampCache: TimestampCache\n\t\tprofiling?: 'stats' | 'off' | 'debug'\n\t},\n\tfilterEndpointPaths?: string[],\n): { endpoints: EndpointData[]; timing: number; endpointTimings: EndpointTiming[] } => {\n\tconst timestamp = getSourceFileTimestamp(file.sourceFile, config.timestampCache)\n\tconst cachedResults = SourceFileCache.getCachedResults(file.sourceFile, timestamp, config.cachePath)\n\n\tif (cachedResults) {\n\t\tLogger.debug(`[${file.fileName}] Found cached results`)\n\t\treturn { endpoints: cachedResults.endpoints, timing: 0, endpointTimings: [] }\n\t}\n\tLogger.debug(`[${file.fileName}] Analyzing...`)\n\n\tconst t1 = performance.now()\n\tconst { endpoints, endpointTimings } = analyzeSourceFileEndpoints(file, filterEndpointPaths)\n\tconst t2 = performance.now()\n\tLogger.debug(`[${file.fileName}] Analyzed in ${t2 - t1}ms`)\n\tSourceFileCache.cacheResults(file.sourceFile, timestamp, config.cachePath, endpoints)\n\treturn { endpoints, timing: t2 - t1, endpointTimings }\n}\n\nexport const analyzeSourceFileEndpoints = (\n\tfile: DiscoveredSourceFile,\n\tfilterEndpointPaths?: string[],\n): { endpoints: EndpointData[]; endpointTimings: EndpointTiming[] } => {\n\tconst endpoints: EndpointData[] = []\n\tconst endpointTimings: EndpointTiming[] = []\n\tconst operations = ['get', 'post', 'put', 'delete', 'del', 'patch']\n\tconst joinedOperations = operations.join('|')\n\n\tfile.routers.named.forEach((routerName) => {\n\t\tconst routerPattern = new RegExp(`${routerName}\\\\.(?:${joinedOperations})`)\n\t\tfile.sourceFile.forEachChild((node) => {\n\t\t\tconst nodeText = node.getText()\n\n\t\t\tif (routerPattern.test(nodeText)) {\n\t\t\t\tconst endpointPath = resolveEndpointPath(node) ?? ''\n\n\t\t\t\tif (filterEndpointPaths && !filterEndpointPaths.some((path) => endpointPath.includes(path))) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst t1 = performance.now()\n\t\t\t\tconst { endpoint, sectionTimings } = parseEndpoint(node, file.fileName)\n\t\t\t\tendpointTimings.push({\n\t\t\t\t\tmethod: endpoint.method,\n\t\t\t\t\tpath: endpoint.path,\n\t\t\t\t\ttiming: performance.now() - t1,\n\t\t\t\t\tsectionTimings,\n\t\t\t\t})\n\t\t\t\tendpoints.push(endpoint)\n\t\t\t}\n\t\t})\n\t})\n\n\treturn { endpoints, endpointTimings }\n}\n\nexport const analyzeSourceFileApiHeader = (sourceFile: SourceFile): ApiDocsHeader | null => {\n\tconst nameOfUseApiHeader = discoverImportedName({\n\t\tsourceFile,\n\t\toriginalName: 'useApiHeader',\n\t})\n\n\tif (!nameOfUseApiHeader) {\n\t\treturn null\n\t}\n\n\tconst node = sourceFile\n\t\t.forEachChildAsArray()\n\t\t.filter((node) => node.isKind(SyntaxKind.ExpressionStatement))\n\t\t.find((node) => nameOfUseApiHeader && node.getText().startsWith(nameOfUseApiHeader))\n\n\tif (!node) {\n\t\treturn null\n\t}\n\n\tconst targetNode = node.getFirstDescendantByKindOrThrow(SyntaxKind.ObjectLiteralExpression)\n\tconst values = getValuesOfObjectLiteral(targetNode)\n\n\tconst collapseObject = (v: string | string[] | typeof values): any => {\n\t\tif (typeof v === 'string') {\n\t\t\treturn v\n\t\t}\n\t\tif (Array.isArray(v) && v.every((value) => typeof value === 'string')) {\n\t\t\treturn v\n\t\t}\n\n\t\treturn v.reduce((acc, current) => {\n\t\t\tif (typeof current === 'string') {\n\t\t\t\treturn acc\n\t\t\t}\n\t\t\treturn {\n\t\t\t\t...acc,\n\t\t\t\t[current.identifier]: collapseObject(current.value as string[]),\n\t\t\t}\n\t\t}, {})\n\t}\n\treturn collapseObject(values)\n}\n\nexport const analyzeSourceFileExposedModels = (sourceFile: SourceFile): ExposedModelData[] => {\n\tconst models: ExposedModelData[] = []\n\n\tconst nameOfUseExposeApiModel = discoverImportedName({\n\t\tsourceFile,\n\t\toriginalName: 'useExposeApiModel',\n\t})\n\n\tconst nameOfUseExposeNamedApiModels = discoverImportedName({\n\t\tsourceFile,\n\t\toriginalName: 'useExposeNamedApiModels',\n\t})\n\n\tsourceFile\n\t\t.forEachChildAsArray()\n\t\t.filter((node) => node.isKind(SyntaxKind.ExpressionStatement))\n\t\t.map((node) => {\n\t\t\tif (nameOfUseExposeApiModel && node.getText().startsWith(nameOfUseExposeApiModel)) {\n\t\t\t\tconst callExpressionNode = node.getFirstChild()\n\t\t\t\tconst syntaxListChildren = callExpressionNode?.getChildrenOfKind(SyntaxKind.SyntaxList) || []\n\n\t\t\t\tconst firstChild = syntaxListChildren[0].getFirstChild()\n\t\t\t\tif (!firstChild) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tmodels.push(parseExposedModel(firstChild))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (nameOfUseExposeNamedApiModels && node.getText().startsWith(nameOfUseExposeNamedApiModels)) {\n\t\t\t\tconst callExpressionNode = node.getFirstChild()\n\t\t\t\tconst syntaxListChildren = callExpressionNode?.getChildrenOfKind(SyntaxKind.SyntaxList) || []\n\n\t\t\t\tconst firstChild = syntaxListChildren[0].getFirstChild()\n\t\t\t\tif (!firstChild) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst parsedModels = parseNamedExposedModels(firstChild)\n\t\t\t\tparsedModels.forEach((model) => models.push(model))\n\t\t\t}\n\t\t})\n\treturn models\n}\n"],"names":["resolveWorkerUrl","candidates","candidate","url","_documentCurrentScript","existsSync","fileURLToPath","prepareOpenApiSpec","logLevel","tsconfigPath","sourceFilePaths","sourceFileDiscovery","incremental","profiling","openApiManager","OpenApiManager","Logger","project","Project","path","explicitRouters","discoveredRouterFiles","allSourceFiles","sourceFiles","filepath","filePath","file","discoverRouters","discoveredSourceFiles","startTime","files","discoverRouterFiles","acc","current","r","filesToAnalyze","apiHeaders","analyzeSourceFileApiHeader","headers","exposedModels","analyzeSourceFileExposedModels","cachePath","endpoints","analyzeMultipleSourceFiles","e","config","filterEndpointPaths","cached","uncached","timestamp","getSourceFileTimestamp","hit","SourceFileCache","f","operationsPattern","allTasks","routerName","routerPattern","endpointIndex","node","nodeText","crypto","pool","WorkerPool","results","ft","byFile","i","result","fileName","fileResult","analyzedFiles","a","b","t","ep","s","sourceFile","nameOfUseApiHeader","discoverImportedName","SyntaxKind","targetNode","values","getValuesOfObjectLiteral","collapseObject","v","value","models","nameOfUseExposeApiModel","nameOfUseExposeNamedApiModels","firstChild","parseExposedModel","parseNamedExposedModels","model"],"mappings":"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"}
1
+ {"version":3,"file":"analyzerModule.cjs","sources":["../../../src/openapi/analyzerModule/analyzerModule.ts"],"sourcesContent":["import crypto from 'crypto'\nimport { existsSync } from 'fs'\nimport * as path from 'path'\nimport { fileURLToPath } from 'url'\n\nfunction resolveWorkerUrl(): URL {\n\tconst candidates = ['./analyzerWorker.mjs', './analyzerWorker.test.mjs']\n\tfor (const candidate of candidates) {\n\t\tconst url = new URL(candidate, import.meta.url)\n\t\tif (existsSync(fileURLToPath(url))) {\n\t\t\treturn url\n\t\t}\n\t}\n\tthrow new Error(\n\t\t'analyzerWorker.mjs not found. Run yarn build, or run tests via yarn test (which compiles the worker first).',\n\t)\n}\nimport { SourceFile, SyntaxKind } from 'ts-morph'\nimport { Project } from 'ts-morph'\n\nimport { Logger } from '../../utils/logger'\nimport { discoverImportedName } from '../discoveryModule/discoverImports/discoverImports'\nimport {\n\tDiscoveredSourceFile,\n\tdiscoverRouterFiles,\n} from '../discoveryModule/discoverRouterFiles/discoverRouterFiles'\nimport { discoverRouters } from '../discoveryModule/discoverRouters/discoverRouters'\nimport { ApiDocsHeader, OpenApiManager } from '../manager/OpenApiManager'\nimport { EndpointData, ExposedModelData } from '../types'\nimport { getSourceFileTimestamp, TimestampCache } from './getSourceFileTimestamp'\nimport { getValuesOfObjectLiteral, resolveEndpointPath } from './nodeParsers'\nimport { parseEndpoint, SectionTiming } from './parseEndpoint'\nimport { parseExposedModel, parseNamedExposedModels } from './parseExposedModels'\nimport { SourceFileCache } from './sourceFileCache'\nimport { WorkerPool, WorkerResult, WorkerTask } from './workerPool'\n\ntype Props = {\n\tlogLevel?: Parameters<(typeof Logger)['setLevel']>[0]\n\ttsconfigPath: string\n\tsourceFilePaths?: string[]\n\tsourceFileDiscovery?: boolean | FileDiscoveryConfig\n\tincremental?:\n\t\t| boolean\n\t\t| {\n\t\t\t\tcachePath: string\n\t\t }\n\tprofiling?: 'stats' | 'off' | 'debug'\n}\n\ntype FileDiscoveryConfig = {\n\trootPath: string\n}\n\ntype EndpointTiming = { method: string; path: string; timing: number; sectionTimings: SectionTiming[] }\n\n/**\n * @param tsconfigPath Path to tsconfig file relative to project root\n * @param sourceFilePaths Array of router source files relative to project root\n */\nexport const prepareOpenApiSpec = async ({\n\tlogLevel,\n\ttsconfigPath,\n\tsourceFilePaths,\n\tsourceFileDiscovery,\n\tincremental,\n\tprofiling = 'stats',\n}: Props): Promise<void> => {\n\tconst openApiManager = OpenApiManager.getInstance()\n\n\tif (openApiManager.isReady()) {\n\t\treturn\n\t}\n\n\tif (logLevel) {\n\t\tLogger.setLevel(logLevel)\n\t}\n\n\tLogger.info('Preparing OpenAPI spec')\n\n\tconst project = new Project({\n\t\ttsConfigFilePath: path.resolve(tsconfigPath),\n\t\tskipFileDependencyResolution: true,\n\t})\n\n\tconst { explicitRouters, discoveredRouterFiles, allSourceFiles } = (() => {\n\t\tconst sourceFilesToAdd = sourceFilePaths ?? []\n\t\tconst resolvedSourceFilePaths = sourceFilesToAdd.map((filepath) => path.resolve(filepath))\n\t\tconst sourceFiles = resolvedSourceFilePaths.map((filePath) => project.getSourceFileOrThrow(filePath))\n\t\tconst explicitRouters = sourceFiles.flatMap((file) => ({\n\t\t\tfileName: file.getFilePath(),\n\t\t\tsourceFile: file,\n\t\t\trouters: discoverRouters(file),\n\t\t}))\n\n\t\tconst { discoveredRouterFiles, discoveredSourceFiles } = (() => {\n\t\t\tif (sourceFileDiscovery === false) {\n\t\t\t\treturn { discoveredRouterFiles: [], discoveredSourceFiles: [] }\n\t\t\t}\n\n\t\t\tconst startTime = performance.now()\n\t\t\tconst files = discoverRouterFiles({\n\t\t\t\ttargetPath: typeof sourceFileDiscovery === 'object' ? sourceFileDiscovery.rootPath : '.',\n\t\t\t\ttsConfigPath: tsconfigPath,\n\t\t\t})\n\t\t\tif (profiling !== 'off') {\n\t\t\t\tLogger.info(`File discovery took ${Math.round(performance.now() - startTime)}ms`)\n\t\t\t}\n\t\t\treturn files\n\t\t})()\n\n\t\tconst allSourceFiles = sourceFiles.reduce(\n\t\t\t(acc, current) =>\n\t\t\t\tacc.some((r) => r.getFilePath() === current.getFilePath()) ? acc : acc.concat(current),\n\t\t\tdiscoveredSourceFiles,\n\t\t)\n\n\t\treturn { explicitRouters, discoveredRouterFiles, allSourceFiles }\n\t})()\n\n\tconst filesToAnalyze = explicitRouters.reduce(\n\t\t(acc, current) => (acc.some((r) => r.fileName === current.fileName) ? acc : acc.concat(current)),\n\t\tdiscoveredRouterFiles,\n\t)\n\n\tconst apiHeaders = allSourceFiles\n\t\t.flatMap((file) => analyzeSourceFileApiHeader(file))\n\t\t.filter((headers) => !!headers)\n\tif (apiHeaders.length > 0 && apiHeaders[0]) {\n\t\topenApiManager.setHeader(apiHeaders[0])\n\t}\n\n\tconst exposedModels = allSourceFiles.flatMap((file) => analyzeSourceFileExposedModels(file))\n\n\topenApiManager.setExposedModels(exposedModels)\n\n\tconst cachePath = (() => {\n\t\tif (typeof incremental === 'object' && incremental.cachePath) {\n\t\t\treturn incremental.cachePath\n\t\t}\n\t\treturn path.resolve(process.cwd(), 'node_modules', '.cache', 'moonflower')\n\t})()\n\tconst endpoints = await analyzeMultipleSourceFiles(filesToAnalyze, {\n\t\tincremental: incremental !== false,\n\t\tcachePath,\n\t\ttimestampCache: {},\n\t\tprofiling,\n\t\ttsconfigPath: path.resolve(tsconfigPath),\n\t})\n\n\topenApiManager.setStats({\n\t\tdiscoveredRouterFiles: discoveredRouterFiles.map((file) => ({\n\t\t\tpath: file.fileName,\n\t\t\trouters: file.routers.named.map((r) => ({\n\t\t\t\tname: r,\n\t\t\t\tendpoints: endpoints\n\t\t\t\t\t.filter((e) => e.sourceFilePath === file.fileName)\n\t\t\t\t\t.map((e) => `${e.method.toUpperCase()} ${e.path}`),\n\t\t\t})),\n\t\t})),\n\t\texplicitRouterFiles: explicitRouters.map((file) => ({\n\t\t\tpath: file.fileName,\n\t\t\trouters: file.routers.named.map((r) => ({\n\t\t\t\tname: r,\n\t\t\t\tendpoints: endpoints\n\t\t\t\t\t.filter((e) => e.sourceFilePath === file.fileName)\n\t\t\t\t\t.map((e) => `${e.method.toUpperCase()} ${e.path}`),\n\t\t\t})),\n\t\t})),\n\t})\n\n\topenApiManager.setEndpoints(endpoints)\n\topenApiManager.markAsReady()\n}\n\nexport const analyzeMultipleSourceFiles = async (\n\tfiles: DiscoveredSourceFile[],\n\tconfig: {\n\t\tincremental: boolean\n\t\tcachePath: string\n\t\ttimestampCache: TimestampCache\n\t\tprofiling?: 'stats' | 'off' | 'debug'\n\t\ttsconfigPath: string\n\t},\n\tfilterEndpointPaths?: string[],\n): Promise<EndpointData[]> => {\n\tconst profiling = config.profiling ?? 'stats'\n\tconst startTime = performance.now()\n\n\t// Separate cached files from those needing analysis\n\ttype CachedFile = { endpoints: EndpointData[]; fileName: string; timing: 0; endpointTimings: [] }\n\ttype UncachedFile = { file: DiscoveredSourceFile; timestamp: number }\n\n\tconst cached: CachedFile[] = []\n\tconst uncached: UncachedFile[] = []\n\n\tfor (const file of files) {\n\t\tconst timestamp = getSourceFileTimestamp(file.sourceFile, config.timestampCache)\n\t\tconst hit = config.incremental\n\t\t\t? SourceFileCache.getCachedResults(file.sourceFile, timestamp, config.cachePath)\n\t\t\t: null\n\t\tif (hit) {\n\t\t\tLogger.debug(`[${file.fileName}] Found cached results`)\n\t\t\tcached.push({ endpoints: hit.endpoints, fileName: file.fileName, timing: 0, endpointTimings: [] })\n\t\t} else {\n\t\t\tuncached.push({ file, timestamp })\n\t\t}\n\t}\n\n\tif (uncached.length === 0) {\n\t\tif (profiling !== 'off') {\n\t\t\tLogger.info(`Router analysis took ${Math.round(performance.now() - startTime)}ms`)\n\t\t}\n\t\treturn cached.flatMap((f) => f.endpoints)\n\t}\n\n\t// Build one task per uncached file. Each worker analyzes a whole file in a single pass, so it\n\t// pays the ts-morph Project cold-start (full TS program load + type-checker warmup) once and\n\t// reuses the warmed-up checker for every endpoint in that file.\n\ttype FileTask = { task: WorkerTask; fileName: string }\n\tconst allTasks: FileTask[] = uncached.map(({ file }) => ({\n\t\tfileName: file.fileName,\n\t\ttask: {\n\t\t\ttaskId: crypto.randomUUID(),\n\t\t\ttsconfigPath: config.tsconfigPath,\n\t\t\tsourceFilePath: file.sourceFile.getFilePath(),\n\t\t\trouterNames: file.routers.named,\n\t\t\tfilterEndpointPaths,\n\t\t},\n\t}))\n\n\t// Dispatch all tasks to the worker pool, capped at one worker per file.\n\tconst pool = new WorkerPool(resolveWorkerUrl(), allTasks.length)\n\n\ttype FileResult = {\n\t\tendpoints: EndpointData[]\n\t\tfileName: string\n\t\ttiming: number\n\t\tendpointTimings: EndpointTiming[]\n\t}\n\n\tlet results: WorkerResult[]\n\ttry {\n\t\tresults = await pool.runAll(allTasks.map((ft) => ft.task))\n\t} finally {\n\t\tpool.terminate()\n\t}\n\n\t// Each result maps 1:1 to a file task.\n\tconst byFile = new Map<string, FileResult>()\n\tfor (const { file } of uncached) {\n\t\tbyFile.set(file.fileName, { endpoints: [], fileName: file.fileName, timing: 0, endpointTimings: [] })\n\t}\n\n\tfor (let i = 0; i < results.length; i++) {\n\t\tconst result = results[i]\n\t\tconst fileName = allTasks[i].fileName\n\t\tconst fileResult = byFile.get(fileName)!\n\n\t\tif ('error' in result) {\n\t\t\tLogger.error(`[${fileName}] Worker error: ${result.error}`)\n\t\t\tcontinue\n\t\t}\n\n\t\tfileResult.endpoints = result.endpoints\n\t\tfileResult.endpointTimings = result.endpointTimings\n\t\tfileResult.timing = result.endpointTimings.reduce((sum, t) => sum + t.timing, 0)\n\t}\n\n\t// Write cache for each uncached file\n\tfor (const { file, timestamp } of uncached) {\n\t\tconst fileResult = byFile.get(file.fileName)!\n\t\tif (fileResult.endpoints.length > 0) {\n\t\t\tSourceFileCache.cacheResults(file.sourceFile, timestamp, config.cachePath, fileResult.endpoints)\n\t\t}\n\t}\n\n\tconst analyzedFiles = [...cached, ...Array.from(byFile.values())]\n\n\tif (profiling !== 'off') {\n\t\tLogger.info(`Router analysis took ${Math.round(performance.now() - startTime)}ms`)\n\t}\n\n\tif (profiling === 'stats') {\n\t\tanalyzedFiles\n\t\t\t.map((f) => ({ fileName: f.fileName, timeTaken: f.timing }))\n\t\t\t.sort((a, b) => b.timeTaken - a.timeTaken)\n\t\t\t.filter((t) => t.timeTaken > 500)\n\t\t\t.forEach((t) => {\n\t\t\t\tLogger.info(`- [${t.fileName}] Took ${Math.round(t.timeTaken)}ms to analyze`)\n\t\t\t})\n\t} else if (profiling === 'debug') {\n\t\tanalyzedFiles\n\t\t\t.map((f) => ({ fileName: f.fileName, timeTaken: f.timing, endpointTimings: f.endpointTimings }))\n\t\t\t.sort((a, b) => b.timeTaken - a.timeTaken)\n\t\t\t.forEach((t) => {\n\t\t\t\tLogger.info(`- [${t.fileName}] Took ${Math.round(t.timeTaken)}ms to analyze`)\n\t\t\t\tt.endpointTimings\n\t\t\t\t\t.sort((a, b) => b.timing - a.timing)\n\t\t\t\t\t.forEach((ep) => {\n\t\t\t\t\t\tLogger.info(` - ${ep.method} ${ep.path} (${Math.round(ep.timing)}ms)`)\n\t\t\t\t\t\tep.sectionTimings\n\t\t\t\t\t\t\t.filter((s) => s.timing >= 1)\n\t\t\t\t\t\t\t.sort((a, b) => b.timing - a.timing)\n\t\t\t\t\t\t\t.forEach((s) => {\n\t\t\t\t\t\t\t\tLogger.info(` - ${s.section}: ${Math.round(s.timing)}ms`)\n\t\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t})\n\t}\n\n\treturn analyzedFiles.flatMap((f) => f.endpoints)\n}\n\nexport const analyzeSourceFileWithCache = (\n\tfile: DiscoveredSourceFile,\n\tconfig: {\n\t\tincremental: boolean\n\t\tcachePath: string\n\t\ttimestampCache: TimestampCache\n\t\tprofiling?: 'stats' | 'off' | 'debug'\n\t},\n\tfilterEndpointPaths?: string[],\n): { endpoints: EndpointData[]; timing: number; endpointTimings: EndpointTiming[] } => {\n\tconst timestamp = getSourceFileTimestamp(file.sourceFile, config.timestampCache)\n\tconst cachedResults = SourceFileCache.getCachedResults(file.sourceFile, timestamp, config.cachePath)\n\n\tif (cachedResults) {\n\t\tLogger.debug(`[${file.fileName}] Found cached results`)\n\t\treturn { endpoints: cachedResults.endpoints, timing: 0, endpointTimings: [] }\n\t}\n\tLogger.debug(`[${file.fileName}] Analyzing...`)\n\n\tconst t1 = performance.now()\n\tconst { endpoints, endpointTimings } = analyzeSourceFileEndpoints(file, filterEndpointPaths)\n\tconst t2 = performance.now()\n\tLogger.debug(`[${file.fileName}] Analyzed in ${t2 - t1}ms`)\n\tSourceFileCache.cacheResults(file.sourceFile, timestamp, config.cachePath, endpoints)\n\treturn { endpoints, timing: t2 - t1, endpointTimings }\n}\n\nexport const analyzeSourceFileEndpoints = (\n\tfile: DiscoveredSourceFile,\n\tfilterEndpointPaths?: string[],\n): { endpoints: EndpointData[]; endpointTimings: EndpointTiming[] } => {\n\tconst endpoints: EndpointData[] = []\n\tconst endpointTimings: EndpointTiming[] = []\n\tconst operations = ['get', 'post', 'put', 'delete', 'del', 'patch']\n\tconst joinedOperations = operations.join('|')\n\n\tfile.routers.named.forEach((routerName) => {\n\t\tconst routerPattern = new RegExp(`${routerName}\\\\.(?:${joinedOperations})`)\n\t\tfile.sourceFile.forEachChild((node) => {\n\t\t\tconst nodeText = node.getText()\n\n\t\t\tif (routerPattern.test(nodeText)) {\n\t\t\t\tconst endpointPath = resolveEndpointPath(node) ?? ''\n\n\t\t\t\tif (filterEndpointPaths && !filterEndpointPaths.some((path) => endpointPath.includes(path))) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst t1 = performance.now()\n\t\t\t\tconst { endpoint, sectionTimings } = parseEndpoint(node, file.fileName)\n\t\t\t\tendpointTimings.push({\n\t\t\t\t\tmethod: endpoint.method,\n\t\t\t\t\tpath: endpoint.path,\n\t\t\t\t\ttiming: performance.now() - t1,\n\t\t\t\t\tsectionTimings,\n\t\t\t\t})\n\t\t\t\tendpoints.push(endpoint)\n\t\t\t}\n\t\t})\n\t})\n\n\treturn { endpoints, endpointTimings }\n}\n\nexport const analyzeSourceFileApiHeader = (sourceFile: SourceFile): ApiDocsHeader | null => {\n\tconst nameOfUseApiHeader = discoverImportedName({\n\t\tsourceFile,\n\t\toriginalName: 'useApiHeader',\n\t})\n\n\tif (!nameOfUseApiHeader) {\n\t\treturn null\n\t}\n\n\tconst node = sourceFile\n\t\t.forEachChildAsArray()\n\t\t.filter((node) => node.isKind(SyntaxKind.ExpressionStatement))\n\t\t.find((node) => nameOfUseApiHeader && node.getText().startsWith(nameOfUseApiHeader))\n\n\tif (!node) {\n\t\treturn null\n\t}\n\n\tconst targetNode = node.getFirstDescendantByKindOrThrow(SyntaxKind.ObjectLiteralExpression)\n\tconst values = getValuesOfObjectLiteral(targetNode)\n\n\tconst collapseObject = (v: string | string[] | typeof values): any => {\n\t\tif (typeof v === 'string') {\n\t\t\treturn v\n\t\t}\n\t\tif (Array.isArray(v) && v.every((value) => typeof value === 'string')) {\n\t\t\treturn v\n\t\t}\n\n\t\treturn v.reduce((acc, current) => {\n\t\t\tif (typeof current === 'string') {\n\t\t\t\treturn acc\n\t\t\t}\n\t\t\treturn {\n\t\t\t\t...acc,\n\t\t\t\t[current.identifier]: collapseObject(current.value as string[]),\n\t\t\t}\n\t\t}, {})\n\t}\n\treturn collapseObject(values)\n}\n\nexport const analyzeSourceFileExposedModels = (sourceFile: SourceFile): ExposedModelData[] => {\n\tconst models: ExposedModelData[] = []\n\n\tconst nameOfUseExposeApiModel = discoverImportedName({\n\t\tsourceFile,\n\t\toriginalName: 'useExposeApiModel',\n\t})\n\n\tconst nameOfUseExposeNamedApiModels = discoverImportedName({\n\t\tsourceFile,\n\t\toriginalName: 'useExposeNamedApiModels',\n\t})\n\n\tsourceFile\n\t\t.forEachChildAsArray()\n\t\t.filter((node) => node.isKind(SyntaxKind.ExpressionStatement))\n\t\t.map((node) => {\n\t\t\tif (nameOfUseExposeApiModel && node.getText().startsWith(nameOfUseExposeApiModel)) {\n\t\t\t\tconst callExpressionNode = node.getFirstChild()\n\t\t\t\tconst syntaxListChildren = callExpressionNode?.getChildrenOfKind(SyntaxKind.SyntaxList) || []\n\n\t\t\t\tconst firstChild = syntaxListChildren[0].getFirstChild()\n\t\t\t\tif (!firstChild) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tmodels.push(parseExposedModel(firstChild))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (nameOfUseExposeNamedApiModels && node.getText().startsWith(nameOfUseExposeNamedApiModels)) {\n\t\t\t\tconst callExpressionNode = node.getFirstChild()\n\t\t\t\tconst syntaxListChildren = callExpressionNode?.getChildrenOfKind(SyntaxKind.SyntaxList) || []\n\n\t\t\t\tconst firstChild = syntaxListChildren[0].getFirstChild()\n\t\t\t\tif (!firstChild) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst parsedModels = parseNamedExposedModels(firstChild)\n\t\t\t\tparsedModels.forEach((model) => models.push(model))\n\t\t\t}\n\t\t})\n\treturn models\n}\n"],"names":["resolveWorkerUrl","candidates","candidate","url","_documentCurrentScript","existsSync","fileURLToPath","prepareOpenApiSpec","logLevel","tsconfigPath","sourceFilePaths","sourceFileDiscovery","incremental","profiling","openApiManager","OpenApiManager","Logger","project","Project","path","explicitRouters","discoveredRouterFiles","allSourceFiles","sourceFiles","filepath","filePath","file","discoverRouters","discoveredSourceFiles","startTime","files","discoverRouterFiles","acc","current","r","filesToAnalyze","apiHeaders","analyzeSourceFileApiHeader","headers","exposedModels","analyzeSourceFileExposedModels","cachePath","endpoints","analyzeMultipleSourceFiles","e","config","filterEndpointPaths","cached","uncached","timestamp","getSourceFileTimestamp","hit","SourceFileCache","f","allTasks","crypto","pool","WorkerPool","results","ft","byFile","i","result","fileName","fileResult","sum","t","analyzedFiles","a","b","ep","s","sourceFile","nameOfUseApiHeader","discoverImportedName","node","SyntaxKind","targetNode","values","getValuesOfObjectLiteral","collapseObject","v","value","models","nameOfUseExposeApiModel","nameOfUseExposeNamedApiModels","firstChild","parseExposedModel","parseNamedExposedModels","model"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"analyzerModule.d.ts","sourceRoot":"","sources":["../../../src/openapi/analyzerModule/analyzerModule.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,UAAU,EAAc,MAAM,UAAU,CAAA;AAGjD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAE3C,OAAO,EACN,oBAAoB,EAEpB,MAAM,4DAA4D,CAAA;AAEnE,OAAO,EAAE,aAAa,EAAkB,MAAM,2BAA2B,CAAA;AACzE,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AACzD,OAAO,EAA0B,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAEjF,OAAO,EAAiB,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAK9D,KAAK,KAAK,GAAG;IACZ,QAAQ,CAAC,EAAE,UAAU,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IACrD,YAAY,EAAE,MAAM,CAAA;IACpB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;IAC1B,mBAAmB,CAAC,EAAE,OAAO,GAAG,mBAAmB,CAAA;IACnD,WAAW,CAAC,EACT,OAAO,GACP;QACA,SAAS,EAAE,MAAM,CAAA;KAChB,CAAA;IACJ,SAAS,CAAC,EAAE,OAAO,GAAG,KAAK,GAAG,OAAO,CAAA;CACrC,CAAA;AAED,KAAK,mBAAmB,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,KAAK,cAAc,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,aAAa,EAAE,CAAA;CAAE,CAAA;AAMvG,eAAO,MAAM,kBAAkB,8FAO5B,KAAK,KAAG,OAAO,CAAC,IAAI,CA0GtB,CAAA;AAED,eAAO,MAAM,0BAA0B,UAC/B,oBAAoB,EAAE,UACrB;IACP,WAAW,EAAE,OAAO,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,cAAc,EAAE,cAAc,CAAA;IAC9B,SAAS,CAAC,EAAE,OAAO,GAAG,KAAK,GAAG,OAAO,CAAA;IACrC,YAAY,EAAE,MAAM,CAAA;CACpB,wBACqB,MAAM,EAAE,KAC5B,OAAO,CAAC,YAAY,EAAE,CAyJxB,CAAA;AAED,eAAO,MAAM,0BAA0B,SAChC,oBAAoB,UAClB;IACP,WAAW,EAAE,OAAO,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,cAAc,EAAE,cAAc,CAAA;IAC9B,SAAS,CAAC,EAAE,OAAO,GAAG,KAAK,GAAG,OAAO,CAAA;CACrC,wBACqB,MAAM,EAAE,KAC5B;IAAE,SAAS,EAAE,YAAY,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,cAAc,EAAE,CAAA;CAgBhF,CAAA;AAED,eAAO,MAAM,0BAA0B,SAChC,oBAAoB,wBACJ,MAAM,EAAE,KAC5B;IAAE,SAAS,EAAE,YAAY,EAAE,CAAC;IAAC,eAAe,EAAE,cAAc,EAAE,CAAA;CAgChE,CAAA;AAED,eAAO,MAAM,0BAA0B,eAAgB,UAAU,KAAG,aAAa,GAAG,IAyCnF,CAAA;AAED,eAAO,MAAM,8BAA8B,eAAgB,UAAU,KAAG,gBAAgB,EA4CvF,CAAA"}
1
+ {"version":3,"file":"analyzerModule.d.ts","sourceRoot":"","sources":["../../../src/openapi/analyzerModule/analyzerModule.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,UAAU,EAAc,MAAM,UAAU,CAAA;AAGjD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAE3C,OAAO,EACN,oBAAoB,EAEpB,MAAM,4DAA4D,CAAA;AAEnE,OAAO,EAAE,aAAa,EAAkB,MAAM,2BAA2B,CAAA;AACzE,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AACzD,OAAO,EAA0B,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAEjF,OAAO,EAAiB,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAK9D,KAAK,KAAK,GAAG;IACZ,QAAQ,CAAC,EAAE,UAAU,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IACrD,YAAY,EAAE,MAAM,CAAA;IACpB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;IAC1B,mBAAmB,CAAC,EAAE,OAAO,GAAG,mBAAmB,CAAA;IACnD,WAAW,CAAC,EACT,OAAO,GACP;QACA,SAAS,EAAE,MAAM,CAAA;KAChB,CAAA;IACJ,SAAS,CAAC,EAAE,OAAO,GAAG,KAAK,GAAG,OAAO,CAAA;CACrC,CAAA;AAED,KAAK,mBAAmB,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,KAAK,cAAc,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,aAAa,EAAE,CAAA;CAAE,CAAA;AAMvG,eAAO,MAAM,kBAAkB,8FAO5B,KAAK,KAAG,OAAO,CAAC,IAAI,CA0GtB,CAAA;AAED,eAAO,MAAM,0BAA0B,UAC/B,oBAAoB,EAAE,UACrB;IACP,WAAW,EAAE,OAAO,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,cAAc,EAAE,cAAc,CAAA;IAC9B,SAAS,CAAC,EAAE,OAAO,GAAG,KAAK,GAAG,OAAO,CAAA;IACrC,YAAY,EAAE,MAAM,CAAA;CACpB,wBACqB,MAAM,EAAE,KAC5B,OAAO,CAAC,YAAY,EAAE,CA+HxB,CAAA;AAED,eAAO,MAAM,0BAA0B,SAChC,oBAAoB,UAClB;IACP,WAAW,EAAE,OAAO,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,cAAc,EAAE,cAAc,CAAA;IAC9B,SAAS,CAAC,EAAE,OAAO,GAAG,KAAK,GAAG,OAAO,CAAA;CACrC,wBACqB,MAAM,EAAE,KAC5B;IAAE,SAAS,EAAE,YAAY,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,cAAc,EAAE,CAAA;CAgBhF,CAAA;AAED,eAAO,MAAM,0BAA0B,SAChC,oBAAoB,wBACJ,MAAM,EAAE,KAC5B;IAAE,SAAS,EAAE,YAAY,EAAE,CAAC;IAAC,eAAe,EAAE,cAAc,EAAE,CAAA;CAgChE,CAAA;AAED,eAAO,MAAM,0BAA0B,eAAgB,UAAU,KAAG,aAAa,GAAG,IAyCnF,CAAA;AAED,eAAO,MAAM,8BAA8B,eAAgB,UAAU,KAAG,gBAAgB,EA4CvF,CAAA"}
@@ -1,194 +1,179 @@
1
- import w from "crypto";
2
- import { existsSync as O } from "fs";
3
- import * as P from "path";
4
- import { fileURLToPath as v } from "url";
5
- import { Project as j, SyntaxKind as x } from "ts-morph";
1
+ import $ from "crypto";
2
+ import { existsSync as v } from "fs";
3
+ import * as M from "path";
4
+ import { fileURLToPath as O } from "url";
5
+ import { Project as L, SyntaxKind as x } from "ts-morph";
6
6
  import { Logger as u } from "../../utils/logger.mjs";
7
- import { discoverImportedName as M } from "../discoveryModule/discoverImports/discoverImports.mjs";
8
- import { discoverRouterFiles as L } from "../discoveryModule/discoverRouterFiles/discoverRouterFiles.mjs";
7
+ import { discoverImportedName as E } from "../discoveryModule/discoverImports/discoverImports.mjs";
8
+ import { discoverRouterFiles as j } from "../discoveryModule/discoverRouterFiles/discoverRouterFiles.mjs";
9
9
  import { discoverRouters as z } from "../discoveryModule/discoverRouters/discoverRouters.mjs";
10
10
  import { OpenApiManager as U } from "../manager/OpenApiManager.mjs";
11
11
  import { getSourceFileTimestamp as b } from "./getSourceFileTimestamp.mjs";
12
12
  import { getValuesOfObjectLiteral as W } from "./nodeParsers.mjs";
13
- import { parseExposedModel as I, parseNamedExposedModels as K } from "./parseExposedModels.mjs";
14
- import { SourceFileCache as R } from "./sourceFileCache.mjs";
15
- import { WorkerPool as H } from "./workerPool.mjs";
13
+ import { parseExposedModel as K, parseNamedExposedModels as H } from "./parseExposedModels.mjs";
14
+ import { SourceFileCache as P } from "./sourceFileCache.mjs";
15
+ import { WorkerPool as I } from "./workerPool.mjs";
16
16
  function B() {
17
- const l = ["./analyzerWorker.mjs", "./analyzerWorker.test.mjs"];
18
- for (const s of l) {
19
- const d = new URL(s, import.meta.url);
20
- if (O(v(d)))
21
- return d;
17
+ const m = ["./analyzerWorker.mjs", "./analyzerWorker.test.mjs"];
18
+ for (const i of m) {
19
+ const p = new URL(i, import.meta.url);
20
+ if (v(O(p)))
21
+ return p;
22
22
  }
23
23
  throw new Error(
24
24
  "analyzerWorker.mjs not found. Run yarn build, or run tests via yarn test (which compiles the worker first)."
25
25
  );
26
26
  }
27
27
  const ae = async ({
28
- logLevel: l,
29
- tsconfigPath: s,
30
- sourceFilePaths: d,
31
- sourceFileDiscovery: m,
32
- incremental: r,
28
+ logLevel: m,
29
+ tsconfigPath: i,
30
+ sourceFilePaths: p,
31
+ sourceFileDiscovery: c,
32
+ incremental: n,
33
33
  profiling: f = "stats"
34
34
  }) => {
35
- const i = U.getInstance();
36
- if (i.isReady())
35
+ const t = U.getInstance();
36
+ if (t.isReady())
37
37
  return;
38
- l && u.setLevel(l), u.info("Preparing OpenAPI spec");
39
- const c = new j({
40
- tsConfigFilePath: P.resolve(s),
38
+ m && u.setLevel(m), u.info("Preparing OpenAPI spec");
39
+ const a = new L({
40
+ tsConfigFilePath: M.resolve(i),
41
41
  skipFileDependencyResolution: !0
42
- }), { explicitRouters: h, discoveredRouterFiles: g, allSourceFiles: y } = (() => {
43
- const a = (d ?? []).map((p) => P.resolve(p)).map((p) => c.getSourceFileOrThrow(p)), k = a.flatMap((p) => ({
44
- fileName: p.getFilePath(),
45
- sourceFile: p,
46
- routers: z(p)
42
+ }), { explicitRouters: h, discoveredRouterFiles: F, allSourceFiles: N } = (() => {
43
+ const l = (p ?? []).map((d) => M.resolve(d)).map((d) => a.getSourceFileOrThrow(d)), R = l.flatMap((d) => ({
44
+ fileName: d.getFilePath(),
45
+ sourceFile: d,
46
+ routers: z(d)
47
47
  })), { discoveredRouterFiles: A, discoveredSourceFiles: C } = (() => {
48
- if (m === !1)
48
+ if (c === !1)
49
49
  return { discoveredRouterFiles: [], discoveredSourceFiles: [] };
50
- const p = performance.now(), E = L({
51
- targetPath: typeof m == "object" ? m.rootPath : ".",
52
- tsConfigPath: s
50
+ const d = performance.now(), k = j({
51
+ targetPath: typeof c == "object" ? c.rootPath : ".",
52
+ tsConfigPath: i
53
53
  });
54
- return f !== "off" && u.info(`File discovery took ${Math.round(performance.now() - p)}ms`), E;
55
- })(), S = a.reduce(
56
- (p, E) => p.some(($) => $.getFilePath() === E.getFilePath()) ? p : p.concat(E),
54
+ return f !== "off" && u.info(`File discovery took ${Math.round(performance.now() - d)}ms`), k;
55
+ })(), S = l.reduce(
56
+ (d, k) => d.some((w) => w.getFilePath() === k.getFilePath()) ? d : d.concat(k),
57
57
  C
58
58
  );
59
- return { explicitRouters: k, discoveredRouterFiles: A, allSourceFiles: S };
60
- })(), N = h.reduce(
61
- (t, n) => t.some((a) => a.fileName === n.fileName) ? t : t.concat(n),
62
- g
63
- ), F = y.flatMap((t) => V(t)).filter((t) => !!t);
64
- F.length > 0 && F[0] && i.setHeader(F[0]);
65
- const T = y.flatMap((t) => _(t));
66
- i.setExposedModels(T);
67
- const e = typeof r == "object" && r.cachePath ? r.cachePath : P.resolve(process.cwd(), "node_modules", ".cache", "moonflower"), o = await D(N, {
68
- incremental: r !== !1,
69
- cachePath: e,
59
+ return { explicitRouters: R, discoveredRouterFiles: A, allSourceFiles: S };
60
+ })(), T = h.reduce(
61
+ (s, y) => s.some((l) => l.fileName === y.fileName) ? s : s.concat(y),
62
+ F
63
+ ), e = N.flatMap((s) => V(s)).filter((s) => !!s);
64
+ e.length > 0 && e[0] && t.setHeader(e[0]);
65
+ const o = N.flatMap((s) => _(s));
66
+ t.setExposedModels(o);
67
+ const r = typeof n == "object" && n.cachePath ? n.cachePath : M.resolve(process.cwd(), "node_modules", ".cache", "moonflower"), g = await D(T, {
68
+ incremental: n !== !1,
69
+ cachePath: r,
70
70
  timestampCache: {},
71
71
  profiling: f,
72
- tsconfigPath: P.resolve(s)
72
+ tsconfigPath: M.resolve(i)
73
73
  });
74
- i.setStats({
75
- discoveredRouterFiles: g.map((t) => ({
76
- path: t.fileName,
77
- routers: t.routers.named.map((n) => ({
78
- name: n,
79
- endpoints: o.filter((a) => a.sourceFilePath === t.fileName).map((a) => `${a.method.toUpperCase()} ${a.path}`)
74
+ t.setStats({
75
+ discoveredRouterFiles: F.map((s) => ({
76
+ path: s.fileName,
77
+ routers: s.routers.named.map((y) => ({
78
+ name: y,
79
+ endpoints: g.filter((l) => l.sourceFilePath === s.fileName).map((l) => `${l.method.toUpperCase()} ${l.path}`)
80
80
  }))
81
81
  })),
82
- explicitRouterFiles: h.map((t) => ({
83
- path: t.fileName,
84
- routers: t.routers.named.map((n) => ({
85
- name: n,
86
- endpoints: o.filter((a) => a.sourceFilePath === t.fileName).map((a) => `${a.method.toUpperCase()} ${a.path}`)
82
+ explicitRouterFiles: h.map((s) => ({
83
+ path: s.fileName,
84
+ routers: s.routers.named.map((y) => ({
85
+ name: y,
86
+ endpoints: g.filter((l) => l.sourceFilePath === s.fileName).map((l) => `${l.method.toUpperCase()} ${l.path}`)
87
87
  }))
88
88
  }))
89
- }), i.setEndpoints(o), i.markAsReady();
90
- }, D = async (l, s, d) => {
91
- const m = s.profiling ?? "stats", r = performance.now(), f = [], i = [];
92
- for (const e of l) {
93
- const o = b(e.sourceFile, s.timestampCache), t = s.incremental ? R.getCachedResults(e.sourceFile, o, s.cachePath) : null;
94
- t ? (u.debug(`[${e.fileName}] Found cached results`), f.push({ endpoints: t.endpoints, fileName: e.fileName, timing: 0, endpointTimings: [] })) : i.push({ file: e, timestamp: o });
89
+ }), t.setEndpoints(g), t.markAsReady();
90
+ }, D = async (m, i, p) => {
91
+ const c = i.profiling ?? "stats", n = performance.now(), f = [], t = [];
92
+ for (const e of m) {
93
+ const o = b(e.sourceFile, i.timestampCache), r = i.incremental ? P.getCachedResults(e.sourceFile, o, i.cachePath) : null;
94
+ r ? (u.debug(`[${e.fileName}] Found cached results`), f.push({ endpoints: r.endpoints, fileName: e.fileName, timing: 0, endpointTimings: [] })) : t.push({ file: e, timestamp: o });
95
95
  }
96
- if (i.length === 0)
97
- return m !== "off" && u.info(`Router analysis took ${Math.round(performance.now() - r)}ms`), f.flatMap((e) => e.endpoints);
98
- const h = ["get", "post", "put", "delete", "del", "patch"].join("|"), g = [];
99
- for (const { file: e } of i)
100
- for (const o of e.routers.named) {
101
- const t = new RegExp(`${o}\\.(?:${h})`);
102
- let n = 0;
103
- e.sourceFile.forEachChild((a) => {
104
- const k = a.getText();
105
- t.test(k) && (g.push({
106
- fileName: e.fileName,
107
- task: {
108
- taskId: w.randomUUID(),
109
- tsconfigPath: s.tsconfigPath,
110
- sourceFilePath: e.sourceFile.getFilePath(),
111
- routerName: o,
112
- endpointIndex: n
113
- }
114
- }), n++);
115
- });
96
+ if (t.length === 0)
97
+ return c !== "off" && u.info(`Router analysis took ${Math.round(performance.now() - n)}ms`), f.flatMap((e) => e.endpoints);
98
+ const a = t.map(({ file: e }) => ({
99
+ fileName: e.fileName,
100
+ task: {
101
+ taskId: $.randomUUID(),
102
+ tsconfigPath: i.tsconfigPath,
103
+ sourceFilePath: e.sourceFile.getFilePath(),
104
+ routerNames: e.routers.named,
105
+ filterEndpointPaths: p
116
106
  }
117
- const y = new H(B());
118
- let N;
107
+ })), h = new I(B(), a.length);
108
+ let F;
119
109
  try {
120
- N = await y.runAll(g.map((e) => e.task));
110
+ F = await h.runAll(a.map((e) => e.task));
121
111
  } finally {
122
- y.terminate();
112
+ h.terminate();
123
113
  }
124
- const F = /* @__PURE__ */ new Map();
125
- for (const { file: e } of i)
126
- F.set(e.fileName, { endpoints: [], fileName: e.fileName, timing: 0, endpointTimings: [] });
127
- for (let e = 0; e < N.length; e++) {
128
- const o = N[e], t = g[e].fileName, n = F.get(t);
114
+ const N = /* @__PURE__ */ new Map();
115
+ for (const { file: e } of t)
116
+ N.set(e.fileName, { endpoints: [], fileName: e.fileName, timing: 0, endpointTimings: [] });
117
+ for (let e = 0; e < F.length; e++) {
118
+ const o = F[e], r = a[e].fileName, g = N.get(r);
129
119
  if ("error" in o) {
130
- u.error(`[${t}] Worker error: ${o.error}`);
120
+ u.error(`[${r}] Worker error: ${o.error}`);
131
121
  continue;
132
122
  }
133
- n.endpoints.push(o.endpoint), n.timing += o.timing, n.endpointTimings.push({
134
- method: o.endpoint.method,
135
- path: o.endpoint.path,
136
- timing: o.timing,
137
- sectionTimings: o.sectionTimings
138
- });
123
+ g.endpoints = o.endpoints, g.endpointTimings = o.endpointTimings, g.timing = o.endpointTimings.reduce((s, y) => s + y.timing, 0);
139
124
  }
140
- for (const { file: e, timestamp: o } of i) {
141
- const t = F.get(e.fileName);
142
- t.endpoints.length > 0 && R.cacheResults(e.sourceFile, o, s.cachePath, t.endpoints);
125
+ for (const { file: e, timestamp: o } of t) {
126
+ const r = N.get(e.fileName);
127
+ r.endpoints.length > 0 && P.cacheResults(e.sourceFile, o, i.cachePath, r.endpoints);
143
128
  }
144
- const T = [...f, ...Array.from(F.values())];
145
- return m !== "off" && u.info(`Router analysis took ${Math.round(performance.now() - r)}ms`), m === "stats" ? T.map((e) => ({ fileName: e.fileName, timeTaken: e.timing })).sort((e, o) => o.timeTaken - e.timeTaken).filter((e) => e.timeTaken > 500).forEach((e) => {
129
+ const T = [...f, ...Array.from(N.values())];
130
+ return c !== "off" && u.info(`Router analysis took ${Math.round(performance.now() - n)}ms`), c === "stats" ? T.map((e) => ({ fileName: e.fileName, timeTaken: e.timing })).sort((e, o) => o.timeTaken - e.timeTaken).filter((e) => e.timeTaken > 500).forEach((e) => {
146
131
  u.info(`- [${e.fileName}] Took ${Math.round(e.timeTaken)}ms to analyze`);
147
- }) : m === "debug" && T.map((e) => ({ fileName: e.fileName, timeTaken: e.timing, endpointTimings: e.endpointTimings })).sort((e, o) => o.timeTaken - e.timeTaken).forEach((e) => {
148
- u.info(`- [${e.fileName}] Took ${Math.round(e.timeTaken)}ms to analyze`), e.endpointTimings.sort((o, t) => t.timing - o.timing).forEach((o) => {
149
- u.info(` - ${o.method} ${o.path} (${Math.round(o.timing)}ms)`), o.sectionTimings.filter((t) => t.timing >= 1).sort((t, n) => n.timing - t.timing).forEach((t) => {
150
- u.info(` - ${t.section}: ${Math.round(t.timing)}ms`);
132
+ }) : c === "debug" && T.map((e) => ({ fileName: e.fileName, timeTaken: e.timing, endpointTimings: e.endpointTimings })).sort((e, o) => o.timeTaken - e.timeTaken).forEach((e) => {
133
+ u.info(`- [${e.fileName}] Took ${Math.round(e.timeTaken)}ms to analyze`), e.endpointTimings.sort((o, r) => r.timing - o.timing).forEach((o) => {
134
+ u.info(` - ${o.method} ${o.path} (${Math.round(o.timing)}ms)`), o.sectionTimings.filter((r) => r.timing >= 1).sort((r, g) => g.timing - r.timing).forEach((r) => {
135
+ u.info(` - ${r.section}: ${Math.round(r.timing)}ms`);
151
136
  });
152
137
  });
153
138
  }), T.flatMap((e) => e.endpoints);
154
- }, V = (l) => {
155
- const s = M({
156
- sourceFile: l,
139
+ }, V = (m) => {
140
+ const i = E({
141
+ sourceFile: m,
157
142
  originalName: "useApiHeader"
158
143
  });
159
- if (!s)
144
+ if (!i)
160
145
  return null;
161
- const d = l.forEachChildAsArray().filter((i) => i.isKind(x.ExpressionStatement)).find((i) => s && i.getText().startsWith(s));
162
- if (!d)
146
+ const p = m.forEachChildAsArray().filter((t) => t.isKind(x.ExpressionStatement)).find((t) => i && t.getText().startsWith(i));
147
+ if (!p)
163
148
  return null;
164
- const m = d.getFirstDescendantByKindOrThrow(x.ObjectLiteralExpression), r = W(m), f = (i) => typeof i == "string" || Array.isArray(i) && i.every((c) => typeof c == "string") ? i : i.reduce((c, h) => typeof h == "string" ? c : {
165
- ...c,
149
+ const c = p.getFirstDescendantByKindOrThrow(x.ObjectLiteralExpression), n = W(c), f = (t) => typeof t == "string" || Array.isArray(t) && t.every((a) => typeof a == "string") ? t : t.reduce((a, h) => typeof h == "string" ? a : {
150
+ ...a,
166
151
  [h.identifier]: f(h.value)
167
152
  }, {});
168
- return f(r);
169
- }, _ = (l) => {
170
- const s = [], d = M({
171
- sourceFile: l,
153
+ return f(n);
154
+ }, _ = (m) => {
155
+ const i = [], p = E({
156
+ sourceFile: m,
172
157
  originalName: "useExposeApiModel"
173
- }), m = M({
174
- sourceFile: l,
158
+ }), c = E({
159
+ sourceFile: m,
175
160
  originalName: "useExposeNamedApiModels"
176
161
  });
177
- return l.forEachChildAsArray().filter((r) => r.isKind(x.ExpressionStatement)).map((r) => {
178
- if (d && r.getText().startsWith(d)) {
179
- const c = (r.getFirstChild()?.getChildrenOfKind(x.SyntaxList) || [])[0].getFirstChild();
180
- if (!c)
162
+ return m.forEachChildAsArray().filter((n) => n.isKind(x.ExpressionStatement)).map((n) => {
163
+ if (p && n.getText().startsWith(p)) {
164
+ const a = (n.getFirstChild()?.getChildrenOfKind(x.SyntaxList) || [])[0].getFirstChild();
165
+ if (!a)
181
166
  return;
182
- s.push(I(c));
167
+ i.push(K(a));
183
168
  return;
184
169
  }
185
- if (m && r.getText().startsWith(m)) {
186
- const c = (r.getFirstChild()?.getChildrenOfKind(x.SyntaxList) || [])[0].getFirstChild();
187
- if (!c)
170
+ if (c && n.getText().startsWith(c)) {
171
+ const a = (n.getFirstChild()?.getChildrenOfKind(x.SyntaxList) || [])[0].getFirstChild();
172
+ if (!a)
188
173
  return;
189
- K(c).forEach((g) => s.push(g));
174
+ H(a).forEach((F) => i.push(F));
190
175
  }
191
- }), s;
176
+ }), i;
192
177
  };
193
178
  export {
194
179
  D as analyzeMultipleSourceFiles,
@@ -1 +1 @@
1
- {"version":3,"file":"analyzerModule.mjs","sources":["../../../src/openapi/analyzerModule/analyzerModule.ts"],"sourcesContent":["import crypto from 'crypto'\nimport { existsSync } from 'fs'\nimport * as path from 'path'\nimport { fileURLToPath } from 'url'\n\nfunction resolveWorkerUrl(): URL {\n\tconst candidates = ['./analyzerWorker.mjs', './analyzerWorker.test.mjs']\n\tfor (const candidate of candidates) {\n\t\tconst url = new URL(candidate, import.meta.url)\n\t\tif (existsSync(fileURLToPath(url))) {\n\t\t\treturn url\n\t\t}\n\t}\n\tthrow new Error(\n\t\t'analyzerWorker.mjs not found. Run yarn build, or run tests via yarn test (which compiles the worker first).',\n\t)\n}\nimport { SourceFile, SyntaxKind } from 'ts-morph'\nimport { Project } from 'ts-morph'\n\nimport { Logger } from '../../utils/logger'\nimport { discoverImportedName } from '../discoveryModule/discoverImports/discoverImports'\nimport {\n\tDiscoveredSourceFile,\n\tdiscoverRouterFiles,\n} from '../discoveryModule/discoverRouterFiles/discoverRouterFiles'\nimport { discoverRouters } from '../discoveryModule/discoverRouters/discoverRouters'\nimport { ApiDocsHeader, OpenApiManager } from '../manager/OpenApiManager'\nimport { EndpointData, ExposedModelData } from '../types'\nimport { getSourceFileTimestamp, TimestampCache } from './getSourceFileTimestamp'\nimport { getValuesOfObjectLiteral, resolveEndpointPath } from './nodeParsers'\nimport { parseEndpoint, SectionTiming } from './parseEndpoint'\nimport { parseExposedModel, parseNamedExposedModels } from './parseExposedModels'\nimport { SourceFileCache } from './sourceFileCache'\nimport { WorkerPool, WorkerResult, WorkerTask } from './workerPool'\n\ntype Props = {\n\tlogLevel?: Parameters<(typeof Logger)['setLevel']>[0]\n\ttsconfigPath: string\n\tsourceFilePaths?: string[]\n\tsourceFileDiscovery?: boolean | FileDiscoveryConfig\n\tincremental?:\n\t\t| boolean\n\t\t| {\n\t\t\t\tcachePath: string\n\t\t }\n\tprofiling?: 'stats' | 'off' | 'debug'\n}\n\ntype FileDiscoveryConfig = {\n\trootPath: string\n}\n\ntype EndpointTiming = { method: string; path: string; timing: number; sectionTimings: SectionTiming[] }\n\n/**\n * @param tsconfigPath Path to tsconfig file relative to project root\n * @param sourceFilePaths Array of router source files relative to project root\n */\nexport const prepareOpenApiSpec = async ({\n\tlogLevel,\n\ttsconfigPath,\n\tsourceFilePaths,\n\tsourceFileDiscovery,\n\tincremental,\n\tprofiling = 'stats',\n}: Props): Promise<void> => {\n\tconst openApiManager = OpenApiManager.getInstance()\n\n\tif (openApiManager.isReady()) {\n\t\treturn\n\t}\n\n\tif (logLevel) {\n\t\tLogger.setLevel(logLevel)\n\t}\n\n\tLogger.info('Preparing OpenAPI spec')\n\n\tconst project = new Project({\n\t\ttsConfigFilePath: path.resolve(tsconfigPath),\n\t\tskipFileDependencyResolution: true,\n\t})\n\n\tconst { explicitRouters, discoveredRouterFiles, allSourceFiles } = (() => {\n\t\tconst sourceFilesToAdd = sourceFilePaths ?? []\n\t\tconst resolvedSourceFilePaths = sourceFilesToAdd.map((filepath) => path.resolve(filepath))\n\t\tconst sourceFiles = resolvedSourceFilePaths.map((filePath) => project.getSourceFileOrThrow(filePath))\n\t\tconst explicitRouters = sourceFiles.flatMap((file) => ({\n\t\t\tfileName: file.getFilePath(),\n\t\t\tsourceFile: file,\n\t\t\trouters: discoverRouters(file),\n\t\t}))\n\n\t\tconst { discoveredRouterFiles, discoveredSourceFiles } = (() => {\n\t\t\tif (sourceFileDiscovery === false) {\n\t\t\t\treturn { discoveredRouterFiles: [], discoveredSourceFiles: [] }\n\t\t\t}\n\n\t\t\tconst startTime = performance.now()\n\t\t\tconst files = discoverRouterFiles({\n\t\t\t\ttargetPath: typeof sourceFileDiscovery === 'object' ? sourceFileDiscovery.rootPath : '.',\n\t\t\t\ttsConfigPath: tsconfigPath,\n\t\t\t})\n\t\t\tif (profiling !== 'off') {\n\t\t\t\tLogger.info(`File discovery took ${Math.round(performance.now() - startTime)}ms`)\n\t\t\t}\n\t\t\treturn files\n\t\t})()\n\n\t\tconst allSourceFiles = sourceFiles.reduce(\n\t\t\t(acc, current) =>\n\t\t\t\tacc.some((r) => r.getFilePath() === current.getFilePath()) ? acc : acc.concat(current),\n\t\t\tdiscoveredSourceFiles,\n\t\t)\n\n\t\treturn { explicitRouters, discoveredRouterFiles, allSourceFiles }\n\t})()\n\n\tconst filesToAnalyze = explicitRouters.reduce(\n\t\t(acc, current) => (acc.some((r) => r.fileName === current.fileName) ? acc : acc.concat(current)),\n\t\tdiscoveredRouterFiles,\n\t)\n\n\tconst apiHeaders = allSourceFiles\n\t\t.flatMap((file) => analyzeSourceFileApiHeader(file))\n\t\t.filter((headers) => !!headers)\n\tif (apiHeaders.length > 0 && apiHeaders[0]) {\n\t\topenApiManager.setHeader(apiHeaders[0])\n\t}\n\n\tconst exposedModels = allSourceFiles.flatMap((file) => analyzeSourceFileExposedModels(file))\n\n\topenApiManager.setExposedModels(exposedModels)\n\n\tconst cachePath = (() => {\n\t\tif (typeof incremental === 'object' && incremental.cachePath) {\n\t\t\treturn incremental.cachePath\n\t\t}\n\t\treturn path.resolve(process.cwd(), 'node_modules', '.cache', 'moonflower')\n\t})()\n\tconst endpoints = await analyzeMultipleSourceFiles(filesToAnalyze, {\n\t\tincremental: incremental !== false,\n\t\tcachePath,\n\t\ttimestampCache: {},\n\t\tprofiling,\n\t\ttsconfigPath: path.resolve(tsconfigPath),\n\t})\n\n\topenApiManager.setStats({\n\t\tdiscoveredRouterFiles: discoveredRouterFiles.map((file) => ({\n\t\t\tpath: file.fileName,\n\t\t\trouters: file.routers.named.map((r) => ({\n\t\t\t\tname: r,\n\t\t\t\tendpoints: endpoints\n\t\t\t\t\t.filter((e) => e.sourceFilePath === file.fileName)\n\t\t\t\t\t.map((e) => `${e.method.toUpperCase()} ${e.path}`),\n\t\t\t})),\n\t\t})),\n\t\texplicitRouterFiles: explicitRouters.map((file) => ({\n\t\t\tpath: file.fileName,\n\t\t\trouters: file.routers.named.map((r) => ({\n\t\t\t\tname: r,\n\t\t\t\tendpoints: endpoints\n\t\t\t\t\t.filter((e) => e.sourceFilePath === file.fileName)\n\t\t\t\t\t.map((e) => `${e.method.toUpperCase()} ${e.path}`),\n\t\t\t})),\n\t\t})),\n\t})\n\n\topenApiManager.setEndpoints(endpoints)\n\topenApiManager.markAsReady()\n}\n\nexport const analyzeMultipleSourceFiles = async (\n\tfiles: DiscoveredSourceFile[],\n\tconfig: {\n\t\tincremental: boolean\n\t\tcachePath: string\n\t\ttimestampCache: TimestampCache\n\t\tprofiling?: 'stats' | 'off' | 'debug'\n\t\ttsconfigPath: string\n\t},\n\tfilterEndpointPaths?: string[],\n): Promise<EndpointData[]> => {\n\tconst profiling = config.profiling ?? 'stats'\n\tconst startTime = performance.now()\n\n\t// Separate cached files from those needing analysis\n\ttype CachedFile = { endpoints: EndpointData[]; fileName: string; timing: 0; endpointTimings: [] }\n\ttype UncachedFile = { file: DiscoveredSourceFile; timestamp: number }\n\n\tconst cached: CachedFile[] = []\n\tconst uncached: UncachedFile[] = []\n\n\tfor (const file of files) {\n\t\tconst timestamp = getSourceFileTimestamp(file.sourceFile, config.timestampCache)\n\t\tconst hit = config.incremental\n\t\t\t? SourceFileCache.getCachedResults(file.sourceFile, timestamp, config.cachePath)\n\t\t\t: null\n\t\tif (hit) {\n\t\t\tLogger.debug(`[${file.fileName}] Found cached results`)\n\t\t\tcached.push({ endpoints: hit.endpoints, fileName: file.fileName, timing: 0, endpointTimings: [] })\n\t\t} else {\n\t\t\tuncached.push({ file, timestamp })\n\t\t}\n\t}\n\n\tif (uncached.length === 0) {\n\t\tif (profiling !== 'off') {\n\t\t\tLogger.info(`Router analysis took ${Math.round(performance.now() - startTime)}ms`)\n\t\t}\n\t\treturn cached.flatMap((f) => f.endpoints)\n\t}\n\n\t// Build one task per endpoint across all uncached files\n\tconst OPERATIONS = ['get', 'post', 'put', 'delete', 'del', 'patch']\n\tconst operationsPattern = OPERATIONS.join('|')\n\n\ttype FileTask = { task: WorkerTask; fileName: string }\n\tconst allTasks: FileTask[] = []\n\n\tfor (const { file } of uncached) {\n\t\tfor (const routerName of file.routers.named) {\n\t\t\tconst routerPattern = new RegExp(`${routerName}\\\\.(?:${operationsPattern})`)\n\t\t\tlet endpointIndex = 0\n\t\t\tfile.sourceFile.forEachChild((node) => {\n\t\t\t\tconst nodeText = node.getText()\n\t\t\t\tif (routerPattern.test(nodeText)) {\n\t\t\t\t\tif (\n\t\t\t\t\t\t!filterEndpointPaths ||\n\t\t\t\t\t\tfilterEndpointPaths.some((p) => resolveEndpointPath(node)?.includes(p))\n\t\t\t\t\t) {\n\t\t\t\t\t\tallTasks.push({\n\t\t\t\t\t\t\tfileName: file.fileName,\n\t\t\t\t\t\t\ttask: {\n\t\t\t\t\t\t\t\ttaskId: crypto.randomUUID(),\n\t\t\t\t\t\t\t\ttsconfigPath: config.tsconfigPath,\n\t\t\t\t\t\t\t\tsourceFilePath: file.sourceFile.getFilePath(),\n\t\t\t\t\t\t\t\trouterName,\n\t\t\t\t\t\t\t\tendpointIndex,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t\tendpointIndex++\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t}\n\n\t// Dispatch all tasks to the worker pool\n\tconst pool = new WorkerPool(resolveWorkerUrl())\n\n\ttype EndpointTiming = { method: string; path: string; timing: number; sectionTimings: SectionTiming[] }\n\ttype FileResult = {\n\t\tendpoints: EndpointData[]\n\t\tfileName: string\n\t\ttiming: number\n\t\tendpointTimings: EndpointTiming[]\n\t}\n\n\tlet results: WorkerResult[]\n\ttry {\n\t\tresults = await pool.runAll(allTasks.map((ft) => ft.task))\n\t} finally {\n\t\tpool.terminate()\n\t}\n\n\t// Group results by file\n\tconst byFile = new Map<string, FileResult>()\n\tfor (const { file } of uncached) {\n\t\tbyFile.set(file.fileName, { endpoints: [], fileName: file.fileName, timing: 0, endpointTimings: [] })\n\t}\n\n\tfor (let i = 0; i < results.length; i++) {\n\t\tconst result = results[i]\n\t\tconst fileName = allTasks[i].fileName\n\t\tconst fileResult = byFile.get(fileName)!\n\n\t\tif ('error' in result) {\n\t\t\tLogger.error(`[${fileName}] Worker error: ${result.error}`)\n\t\t\tcontinue\n\t\t}\n\n\t\tfileResult.endpoints.push(result.endpoint)\n\t\tfileResult.timing += result.timing\n\t\tfileResult.endpointTimings.push({\n\t\t\tmethod: result.endpoint.method,\n\t\t\tpath: result.endpoint.path,\n\t\t\ttiming: result.timing,\n\t\t\tsectionTimings: result.sectionTimings,\n\t\t})\n\t}\n\n\t// Write cache for each uncached file\n\tfor (const { file, timestamp } of uncached) {\n\t\tconst fileResult = byFile.get(file.fileName)!\n\t\tif (fileResult.endpoints.length > 0) {\n\t\t\tSourceFileCache.cacheResults(file.sourceFile, timestamp, config.cachePath, fileResult.endpoints)\n\t\t}\n\t}\n\n\tconst analyzedFiles = [...cached, ...Array.from(byFile.values())]\n\n\tif (profiling !== 'off') {\n\t\tLogger.info(`Router analysis took ${Math.round(performance.now() - startTime)}ms`)\n\t}\n\n\tif (profiling === 'stats') {\n\t\tanalyzedFiles\n\t\t\t.map((f) => ({ fileName: f.fileName, timeTaken: f.timing }))\n\t\t\t.sort((a, b) => b.timeTaken - a.timeTaken)\n\t\t\t.filter((t) => t.timeTaken > 500)\n\t\t\t.forEach((t) => {\n\t\t\t\tLogger.info(`- [${t.fileName}] Took ${Math.round(t.timeTaken)}ms to analyze`)\n\t\t\t})\n\t} else if (profiling === 'debug') {\n\t\tanalyzedFiles\n\t\t\t.map((f) => ({ fileName: f.fileName, timeTaken: f.timing, endpointTimings: f.endpointTimings }))\n\t\t\t.sort((a, b) => b.timeTaken - a.timeTaken)\n\t\t\t.forEach((t) => {\n\t\t\t\tLogger.info(`- [${t.fileName}] Took ${Math.round(t.timeTaken)}ms to analyze`)\n\t\t\t\tt.endpointTimings\n\t\t\t\t\t.sort((a, b) => b.timing - a.timing)\n\t\t\t\t\t.forEach((ep) => {\n\t\t\t\t\t\tLogger.info(` - ${ep.method} ${ep.path} (${Math.round(ep.timing)}ms)`)\n\t\t\t\t\t\tep.sectionTimings\n\t\t\t\t\t\t\t.filter((s) => s.timing >= 1)\n\t\t\t\t\t\t\t.sort((a, b) => b.timing - a.timing)\n\t\t\t\t\t\t\t.forEach((s) => {\n\t\t\t\t\t\t\t\tLogger.info(` - ${s.section}: ${Math.round(s.timing)}ms`)\n\t\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t})\n\t}\n\n\treturn analyzedFiles.flatMap((f) => f.endpoints)\n}\n\nexport const analyzeSourceFileWithCache = (\n\tfile: DiscoveredSourceFile,\n\tconfig: {\n\t\tincremental: boolean\n\t\tcachePath: string\n\t\ttimestampCache: TimestampCache\n\t\tprofiling?: 'stats' | 'off' | 'debug'\n\t},\n\tfilterEndpointPaths?: string[],\n): { endpoints: EndpointData[]; timing: number; endpointTimings: EndpointTiming[] } => {\n\tconst timestamp = getSourceFileTimestamp(file.sourceFile, config.timestampCache)\n\tconst cachedResults = SourceFileCache.getCachedResults(file.sourceFile, timestamp, config.cachePath)\n\n\tif (cachedResults) {\n\t\tLogger.debug(`[${file.fileName}] Found cached results`)\n\t\treturn { endpoints: cachedResults.endpoints, timing: 0, endpointTimings: [] }\n\t}\n\tLogger.debug(`[${file.fileName}] Analyzing...`)\n\n\tconst t1 = performance.now()\n\tconst { endpoints, endpointTimings } = analyzeSourceFileEndpoints(file, filterEndpointPaths)\n\tconst t2 = performance.now()\n\tLogger.debug(`[${file.fileName}] Analyzed in ${t2 - t1}ms`)\n\tSourceFileCache.cacheResults(file.sourceFile, timestamp, config.cachePath, endpoints)\n\treturn { endpoints, timing: t2 - t1, endpointTimings }\n}\n\nexport const analyzeSourceFileEndpoints = (\n\tfile: DiscoveredSourceFile,\n\tfilterEndpointPaths?: string[],\n): { endpoints: EndpointData[]; endpointTimings: EndpointTiming[] } => {\n\tconst endpoints: EndpointData[] = []\n\tconst endpointTimings: EndpointTiming[] = []\n\tconst operations = ['get', 'post', 'put', 'delete', 'del', 'patch']\n\tconst joinedOperations = operations.join('|')\n\n\tfile.routers.named.forEach((routerName) => {\n\t\tconst routerPattern = new RegExp(`${routerName}\\\\.(?:${joinedOperations})`)\n\t\tfile.sourceFile.forEachChild((node) => {\n\t\t\tconst nodeText = node.getText()\n\n\t\t\tif (routerPattern.test(nodeText)) {\n\t\t\t\tconst endpointPath = resolveEndpointPath(node) ?? ''\n\n\t\t\t\tif (filterEndpointPaths && !filterEndpointPaths.some((path) => endpointPath.includes(path))) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst t1 = performance.now()\n\t\t\t\tconst { endpoint, sectionTimings } = parseEndpoint(node, file.fileName)\n\t\t\t\tendpointTimings.push({\n\t\t\t\t\tmethod: endpoint.method,\n\t\t\t\t\tpath: endpoint.path,\n\t\t\t\t\ttiming: performance.now() - t1,\n\t\t\t\t\tsectionTimings,\n\t\t\t\t})\n\t\t\t\tendpoints.push(endpoint)\n\t\t\t}\n\t\t})\n\t})\n\n\treturn { endpoints, endpointTimings }\n}\n\nexport const analyzeSourceFileApiHeader = (sourceFile: SourceFile): ApiDocsHeader | null => {\n\tconst nameOfUseApiHeader = discoverImportedName({\n\t\tsourceFile,\n\t\toriginalName: 'useApiHeader',\n\t})\n\n\tif (!nameOfUseApiHeader) {\n\t\treturn null\n\t}\n\n\tconst node = sourceFile\n\t\t.forEachChildAsArray()\n\t\t.filter((node) => node.isKind(SyntaxKind.ExpressionStatement))\n\t\t.find((node) => nameOfUseApiHeader && node.getText().startsWith(nameOfUseApiHeader))\n\n\tif (!node) {\n\t\treturn null\n\t}\n\n\tconst targetNode = node.getFirstDescendantByKindOrThrow(SyntaxKind.ObjectLiteralExpression)\n\tconst values = getValuesOfObjectLiteral(targetNode)\n\n\tconst collapseObject = (v: string | string[] | typeof values): any => {\n\t\tif (typeof v === 'string') {\n\t\t\treturn v\n\t\t}\n\t\tif (Array.isArray(v) && v.every((value) => typeof value === 'string')) {\n\t\t\treturn v\n\t\t}\n\n\t\treturn v.reduce((acc, current) => {\n\t\t\tif (typeof current === 'string') {\n\t\t\t\treturn acc\n\t\t\t}\n\t\t\treturn {\n\t\t\t\t...acc,\n\t\t\t\t[current.identifier]: collapseObject(current.value as string[]),\n\t\t\t}\n\t\t}, {})\n\t}\n\treturn collapseObject(values)\n}\n\nexport const analyzeSourceFileExposedModels = (sourceFile: SourceFile): ExposedModelData[] => {\n\tconst models: ExposedModelData[] = []\n\n\tconst nameOfUseExposeApiModel = discoverImportedName({\n\t\tsourceFile,\n\t\toriginalName: 'useExposeApiModel',\n\t})\n\n\tconst nameOfUseExposeNamedApiModels = discoverImportedName({\n\t\tsourceFile,\n\t\toriginalName: 'useExposeNamedApiModels',\n\t})\n\n\tsourceFile\n\t\t.forEachChildAsArray()\n\t\t.filter((node) => node.isKind(SyntaxKind.ExpressionStatement))\n\t\t.map((node) => {\n\t\t\tif (nameOfUseExposeApiModel && node.getText().startsWith(nameOfUseExposeApiModel)) {\n\t\t\t\tconst callExpressionNode = node.getFirstChild()\n\t\t\t\tconst syntaxListChildren = callExpressionNode?.getChildrenOfKind(SyntaxKind.SyntaxList) || []\n\n\t\t\t\tconst firstChild = syntaxListChildren[0].getFirstChild()\n\t\t\t\tif (!firstChild) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tmodels.push(parseExposedModel(firstChild))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (nameOfUseExposeNamedApiModels && node.getText().startsWith(nameOfUseExposeNamedApiModels)) {\n\t\t\t\tconst callExpressionNode = node.getFirstChild()\n\t\t\t\tconst syntaxListChildren = callExpressionNode?.getChildrenOfKind(SyntaxKind.SyntaxList) || []\n\n\t\t\t\tconst firstChild = syntaxListChildren[0].getFirstChild()\n\t\t\t\tif (!firstChild) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst parsedModels = parseNamedExposedModels(firstChild)\n\t\t\t\tparsedModels.forEach((model) => models.push(model))\n\t\t\t}\n\t\t})\n\treturn models\n}\n"],"names":["resolveWorkerUrl","candidates","candidate","url","existsSync","fileURLToPath","prepareOpenApiSpec","logLevel","tsconfigPath","sourceFilePaths","sourceFileDiscovery","incremental","profiling","openApiManager","OpenApiManager","Logger","project","Project","path","explicitRouters","discoveredRouterFiles","allSourceFiles","sourceFiles","filepath","filePath","file","discoverRouters","discoveredSourceFiles","startTime","files","discoverRouterFiles","acc","current","r","filesToAnalyze","apiHeaders","analyzeSourceFileApiHeader","headers","exposedModels","analyzeSourceFileExposedModels","cachePath","endpoints","analyzeMultipleSourceFiles","e","config","filterEndpointPaths","cached","uncached","timestamp","getSourceFileTimestamp","hit","SourceFileCache","f","operationsPattern","allTasks","routerName","routerPattern","endpointIndex","node","nodeText","crypto","pool","WorkerPool","results","ft","byFile","i","result","fileName","fileResult","analyzedFiles","a","b","t","ep","s","sourceFile","nameOfUseApiHeader","discoverImportedName","SyntaxKind","targetNode","values","getValuesOfObjectLiteral","collapseObject","v","value","models","nameOfUseExposeApiModel","nameOfUseExposeNamedApiModels","firstChild","parseExposedModel","parseNamedExposedModels","model"],"mappings":"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"}
1
+ {"version":3,"file":"analyzerModule.mjs","sources":["../../../src/openapi/analyzerModule/analyzerModule.ts"],"sourcesContent":["import crypto from 'crypto'\nimport { existsSync } from 'fs'\nimport * as path from 'path'\nimport { fileURLToPath } from 'url'\n\nfunction resolveWorkerUrl(): URL {\n\tconst candidates = ['./analyzerWorker.mjs', './analyzerWorker.test.mjs']\n\tfor (const candidate of candidates) {\n\t\tconst url = new URL(candidate, import.meta.url)\n\t\tif (existsSync(fileURLToPath(url))) {\n\t\t\treturn url\n\t\t}\n\t}\n\tthrow new Error(\n\t\t'analyzerWorker.mjs not found. Run yarn build, or run tests via yarn test (which compiles the worker first).',\n\t)\n}\nimport { SourceFile, SyntaxKind } from 'ts-morph'\nimport { Project } from 'ts-morph'\n\nimport { Logger } from '../../utils/logger'\nimport { discoverImportedName } from '../discoveryModule/discoverImports/discoverImports'\nimport {\n\tDiscoveredSourceFile,\n\tdiscoverRouterFiles,\n} from '../discoveryModule/discoverRouterFiles/discoverRouterFiles'\nimport { discoverRouters } from '../discoveryModule/discoverRouters/discoverRouters'\nimport { ApiDocsHeader, OpenApiManager } from '../manager/OpenApiManager'\nimport { EndpointData, ExposedModelData } from '../types'\nimport { getSourceFileTimestamp, TimestampCache } from './getSourceFileTimestamp'\nimport { getValuesOfObjectLiteral, resolveEndpointPath } from './nodeParsers'\nimport { parseEndpoint, SectionTiming } from './parseEndpoint'\nimport { parseExposedModel, parseNamedExposedModels } from './parseExposedModels'\nimport { SourceFileCache } from './sourceFileCache'\nimport { WorkerPool, WorkerResult, WorkerTask } from './workerPool'\n\ntype Props = {\n\tlogLevel?: Parameters<(typeof Logger)['setLevel']>[0]\n\ttsconfigPath: string\n\tsourceFilePaths?: string[]\n\tsourceFileDiscovery?: boolean | FileDiscoveryConfig\n\tincremental?:\n\t\t| boolean\n\t\t| {\n\t\t\t\tcachePath: string\n\t\t }\n\tprofiling?: 'stats' | 'off' | 'debug'\n}\n\ntype FileDiscoveryConfig = {\n\trootPath: string\n}\n\ntype EndpointTiming = { method: string; path: string; timing: number; sectionTimings: SectionTiming[] }\n\n/**\n * @param tsconfigPath Path to tsconfig file relative to project root\n * @param sourceFilePaths Array of router source files relative to project root\n */\nexport const prepareOpenApiSpec = async ({\n\tlogLevel,\n\ttsconfigPath,\n\tsourceFilePaths,\n\tsourceFileDiscovery,\n\tincremental,\n\tprofiling = 'stats',\n}: Props): Promise<void> => {\n\tconst openApiManager = OpenApiManager.getInstance()\n\n\tif (openApiManager.isReady()) {\n\t\treturn\n\t}\n\n\tif (logLevel) {\n\t\tLogger.setLevel(logLevel)\n\t}\n\n\tLogger.info('Preparing OpenAPI spec')\n\n\tconst project = new Project({\n\t\ttsConfigFilePath: path.resolve(tsconfigPath),\n\t\tskipFileDependencyResolution: true,\n\t})\n\n\tconst { explicitRouters, discoveredRouterFiles, allSourceFiles } = (() => {\n\t\tconst sourceFilesToAdd = sourceFilePaths ?? []\n\t\tconst resolvedSourceFilePaths = sourceFilesToAdd.map((filepath) => path.resolve(filepath))\n\t\tconst sourceFiles = resolvedSourceFilePaths.map((filePath) => project.getSourceFileOrThrow(filePath))\n\t\tconst explicitRouters = sourceFiles.flatMap((file) => ({\n\t\t\tfileName: file.getFilePath(),\n\t\t\tsourceFile: file,\n\t\t\trouters: discoverRouters(file),\n\t\t}))\n\n\t\tconst { discoveredRouterFiles, discoveredSourceFiles } = (() => {\n\t\t\tif (sourceFileDiscovery === false) {\n\t\t\t\treturn { discoveredRouterFiles: [], discoveredSourceFiles: [] }\n\t\t\t}\n\n\t\t\tconst startTime = performance.now()\n\t\t\tconst files = discoverRouterFiles({\n\t\t\t\ttargetPath: typeof sourceFileDiscovery === 'object' ? sourceFileDiscovery.rootPath : '.',\n\t\t\t\ttsConfigPath: tsconfigPath,\n\t\t\t})\n\t\t\tif (profiling !== 'off') {\n\t\t\t\tLogger.info(`File discovery took ${Math.round(performance.now() - startTime)}ms`)\n\t\t\t}\n\t\t\treturn files\n\t\t})()\n\n\t\tconst allSourceFiles = sourceFiles.reduce(\n\t\t\t(acc, current) =>\n\t\t\t\tacc.some((r) => r.getFilePath() === current.getFilePath()) ? acc : acc.concat(current),\n\t\t\tdiscoveredSourceFiles,\n\t\t)\n\n\t\treturn { explicitRouters, discoveredRouterFiles, allSourceFiles }\n\t})()\n\n\tconst filesToAnalyze = explicitRouters.reduce(\n\t\t(acc, current) => (acc.some((r) => r.fileName === current.fileName) ? acc : acc.concat(current)),\n\t\tdiscoveredRouterFiles,\n\t)\n\n\tconst apiHeaders = allSourceFiles\n\t\t.flatMap((file) => analyzeSourceFileApiHeader(file))\n\t\t.filter((headers) => !!headers)\n\tif (apiHeaders.length > 0 && apiHeaders[0]) {\n\t\topenApiManager.setHeader(apiHeaders[0])\n\t}\n\n\tconst exposedModels = allSourceFiles.flatMap((file) => analyzeSourceFileExposedModels(file))\n\n\topenApiManager.setExposedModels(exposedModels)\n\n\tconst cachePath = (() => {\n\t\tif (typeof incremental === 'object' && incremental.cachePath) {\n\t\t\treturn incremental.cachePath\n\t\t}\n\t\treturn path.resolve(process.cwd(), 'node_modules', '.cache', 'moonflower')\n\t})()\n\tconst endpoints = await analyzeMultipleSourceFiles(filesToAnalyze, {\n\t\tincremental: incremental !== false,\n\t\tcachePath,\n\t\ttimestampCache: {},\n\t\tprofiling,\n\t\ttsconfigPath: path.resolve(tsconfigPath),\n\t})\n\n\topenApiManager.setStats({\n\t\tdiscoveredRouterFiles: discoveredRouterFiles.map((file) => ({\n\t\t\tpath: file.fileName,\n\t\t\trouters: file.routers.named.map((r) => ({\n\t\t\t\tname: r,\n\t\t\t\tendpoints: endpoints\n\t\t\t\t\t.filter((e) => e.sourceFilePath === file.fileName)\n\t\t\t\t\t.map((e) => `${e.method.toUpperCase()} ${e.path}`),\n\t\t\t})),\n\t\t})),\n\t\texplicitRouterFiles: explicitRouters.map((file) => ({\n\t\t\tpath: file.fileName,\n\t\t\trouters: file.routers.named.map((r) => ({\n\t\t\t\tname: r,\n\t\t\t\tendpoints: endpoints\n\t\t\t\t\t.filter((e) => e.sourceFilePath === file.fileName)\n\t\t\t\t\t.map((e) => `${e.method.toUpperCase()} ${e.path}`),\n\t\t\t})),\n\t\t})),\n\t})\n\n\topenApiManager.setEndpoints(endpoints)\n\topenApiManager.markAsReady()\n}\n\nexport const analyzeMultipleSourceFiles = async (\n\tfiles: DiscoveredSourceFile[],\n\tconfig: {\n\t\tincremental: boolean\n\t\tcachePath: string\n\t\ttimestampCache: TimestampCache\n\t\tprofiling?: 'stats' | 'off' | 'debug'\n\t\ttsconfigPath: string\n\t},\n\tfilterEndpointPaths?: string[],\n): Promise<EndpointData[]> => {\n\tconst profiling = config.profiling ?? 'stats'\n\tconst startTime = performance.now()\n\n\t// Separate cached files from those needing analysis\n\ttype CachedFile = { endpoints: EndpointData[]; fileName: string; timing: 0; endpointTimings: [] }\n\ttype UncachedFile = { file: DiscoveredSourceFile; timestamp: number }\n\n\tconst cached: CachedFile[] = []\n\tconst uncached: UncachedFile[] = []\n\n\tfor (const file of files) {\n\t\tconst timestamp = getSourceFileTimestamp(file.sourceFile, config.timestampCache)\n\t\tconst hit = config.incremental\n\t\t\t? SourceFileCache.getCachedResults(file.sourceFile, timestamp, config.cachePath)\n\t\t\t: null\n\t\tif (hit) {\n\t\t\tLogger.debug(`[${file.fileName}] Found cached results`)\n\t\t\tcached.push({ endpoints: hit.endpoints, fileName: file.fileName, timing: 0, endpointTimings: [] })\n\t\t} else {\n\t\t\tuncached.push({ file, timestamp })\n\t\t}\n\t}\n\n\tif (uncached.length === 0) {\n\t\tif (profiling !== 'off') {\n\t\t\tLogger.info(`Router analysis took ${Math.round(performance.now() - startTime)}ms`)\n\t\t}\n\t\treturn cached.flatMap((f) => f.endpoints)\n\t}\n\n\t// Build one task per uncached file. Each worker analyzes a whole file in a single pass, so it\n\t// pays the ts-morph Project cold-start (full TS program load + type-checker warmup) once and\n\t// reuses the warmed-up checker for every endpoint in that file.\n\ttype FileTask = { task: WorkerTask; fileName: string }\n\tconst allTasks: FileTask[] = uncached.map(({ file }) => ({\n\t\tfileName: file.fileName,\n\t\ttask: {\n\t\t\ttaskId: crypto.randomUUID(),\n\t\t\ttsconfigPath: config.tsconfigPath,\n\t\t\tsourceFilePath: file.sourceFile.getFilePath(),\n\t\t\trouterNames: file.routers.named,\n\t\t\tfilterEndpointPaths,\n\t\t},\n\t}))\n\n\t// Dispatch all tasks to the worker pool, capped at one worker per file.\n\tconst pool = new WorkerPool(resolveWorkerUrl(), allTasks.length)\n\n\ttype FileResult = {\n\t\tendpoints: EndpointData[]\n\t\tfileName: string\n\t\ttiming: number\n\t\tendpointTimings: EndpointTiming[]\n\t}\n\n\tlet results: WorkerResult[]\n\ttry {\n\t\tresults = await pool.runAll(allTasks.map((ft) => ft.task))\n\t} finally {\n\t\tpool.terminate()\n\t}\n\n\t// Each result maps 1:1 to a file task.\n\tconst byFile = new Map<string, FileResult>()\n\tfor (const { file } of uncached) {\n\t\tbyFile.set(file.fileName, { endpoints: [], fileName: file.fileName, timing: 0, endpointTimings: [] })\n\t}\n\n\tfor (let i = 0; i < results.length; i++) {\n\t\tconst result = results[i]\n\t\tconst fileName = allTasks[i].fileName\n\t\tconst fileResult = byFile.get(fileName)!\n\n\t\tif ('error' in result) {\n\t\t\tLogger.error(`[${fileName}] Worker error: ${result.error}`)\n\t\t\tcontinue\n\t\t}\n\n\t\tfileResult.endpoints = result.endpoints\n\t\tfileResult.endpointTimings = result.endpointTimings\n\t\tfileResult.timing = result.endpointTimings.reduce((sum, t) => sum + t.timing, 0)\n\t}\n\n\t// Write cache for each uncached file\n\tfor (const { file, timestamp } of uncached) {\n\t\tconst fileResult = byFile.get(file.fileName)!\n\t\tif (fileResult.endpoints.length > 0) {\n\t\t\tSourceFileCache.cacheResults(file.sourceFile, timestamp, config.cachePath, fileResult.endpoints)\n\t\t}\n\t}\n\n\tconst analyzedFiles = [...cached, ...Array.from(byFile.values())]\n\n\tif (profiling !== 'off') {\n\t\tLogger.info(`Router analysis took ${Math.round(performance.now() - startTime)}ms`)\n\t}\n\n\tif (profiling === 'stats') {\n\t\tanalyzedFiles\n\t\t\t.map((f) => ({ fileName: f.fileName, timeTaken: f.timing }))\n\t\t\t.sort((a, b) => b.timeTaken - a.timeTaken)\n\t\t\t.filter((t) => t.timeTaken > 500)\n\t\t\t.forEach((t) => {\n\t\t\t\tLogger.info(`- [${t.fileName}] Took ${Math.round(t.timeTaken)}ms to analyze`)\n\t\t\t})\n\t} else if (profiling === 'debug') {\n\t\tanalyzedFiles\n\t\t\t.map((f) => ({ fileName: f.fileName, timeTaken: f.timing, endpointTimings: f.endpointTimings }))\n\t\t\t.sort((a, b) => b.timeTaken - a.timeTaken)\n\t\t\t.forEach((t) => {\n\t\t\t\tLogger.info(`- [${t.fileName}] Took ${Math.round(t.timeTaken)}ms to analyze`)\n\t\t\t\tt.endpointTimings\n\t\t\t\t\t.sort((a, b) => b.timing - a.timing)\n\t\t\t\t\t.forEach((ep) => {\n\t\t\t\t\t\tLogger.info(` - ${ep.method} ${ep.path} (${Math.round(ep.timing)}ms)`)\n\t\t\t\t\t\tep.sectionTimings\n\t\t\t\t\t\t\t.filter((s) => s.timing >= 1)\n\t\t\t\t\t\t\t.sort((a, b) => b.timing - a.timing)\n\t\t\t\t\t\t\t.forEach((s) => {\n\t\t\t\t\t\t\t\tLogger.info(` - ${s.section}: ${Math.round(s.timing)}ms`)\n\t\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t})\n\t}\n\n\treturn analyzedFiles.flatMap((f) => f.endpoints)\n}\n\nexport const analyzeSourceFileWithCache = (\n\tfile: DiscoveredSourceFile,\n\tconfig: {\n\t\tincremental: boolean\n\t\tcachePath: string\n\t\ttimestampCache: TimestampCache\n\t\tprofiling?: 'stats' | 'off' | 'debug'\n\t},\n\tfilterEndpointPaths?: string[],\n): { endpoints: EndpointData[]; timing: number; endpointTimings: EndpointTiming[] } => {\n\tconst timestamp = getSourceFileTimestamp(file.sourceFile, config.timestampCache)\n\tconst cachedResults = SourceFileCache.getCachedResults(file.sourceFile, timestamp, config.cachePath)\n\n\tif (cachedResults) {\n\t\tLogger.debug(`[${file.fileName}] Found cached results`)\n\t\treturn { endpoints: cachedResults.endpoints, timing: 0, endpointTimings: [] }\n\t}\n\tLogger.debug(`[${file.fileName}] Analyzing...`)\n\n\tconst t1 = performance.now()\n\tconst { endpoints, endpointTimings } = analyzeSourceFileEndpoints(file, filterEndpointPaths)\n\tconst t2 = performance.now()\n\tLogger.debug(`[${file.fileName}] Analyzed in ${t2 - t1}ms`)\n\tSourceFileCache.cacheResults(file.sourceFile, timestamp, config.cachePath, endpoints)\n\treturn { endpoints, timing: t2 - t1, endpointTimings }\n}\n\nexport const analyzeSourceFileEndpoints = (\n\tfile: DiscoveredSourceFile,\n\tfilterEndpointPaths?: string[],\n): { endpoints: EndpointData[]; endpointTimings: EndpointTiming[] } => {\n\tconst endpoints: EndpointData[] = []\n\tconst endpointTimings: EndpointTiming[] = []\n\tconst operations = ['get', 'post', 'put', 'delete', 'del', 'patch']\n\tconst joinedOperations = operations.join('|')\n\n\tfile.routers.named.forEach((routerName) => {\n\t\tconst routerPattern = new RegExp(`${routerName}\\\\.(?:${joinedOperations})`)\n\t\tfile.sourceFile.forEachChild((node) => {\n\t\t\tconst nodeText = node.getText()\n\n\t\t\tif (routerPattern.test(nodeText)) {\n\t\t\t\tconst endpointPath = resolveEndpointPath(node) ?? ''\n\n\t\t\t\tif (filterEndpointPaths && !filterEndpointPaths.some((path) => endpointPath.includes(path))) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst t1 = performance.now()\n\t\t\t\tconst { endpoint, sectionTimings } = parseEndpoint(node, file.fileName)\n\t\t\t\tendpointTimings.push({\n\t\t\t\t\tmethod: endpoint.method,\n\t\t\t\t\tpath: endpoint.path,\n\t\t\t\t\ttiming: performance.now() - t1,\n\t\t\t\t\tsectionTimings,\n\t\t\t\t})\n\t\t\t\tendpoints.push(endpoint)\n\t\t\t}\n\t\t})\n\t})\n\n\treturn { endpoints, endpointTimings }\n}\n\nexport const analyzeSourceFileApiHeader = (sourceFile: SourceFile): ApiDocsHeader | null => {\n\tconst nameOfUseApiHeader = discoverImportedName({\n\t\tsourceFile,\n\t\toriginalName: 'useApiHeader',\n\t})\n\n\tif (!nameOfUseApiHeader) {\n\t\treturn null\n\t}\n\n\tconst node = sourceFile\n\t\t.forEachChildAsArray()\n\t\t.filter((node) => node.isKind(SyntaxKind.ExpressionStatement))\n\t\t.find((node) => nameOfUseApiHeader && node.getText().startsWith(nameOfUseApiHeader))\n\n\tif (!node) {\n\t\treturn null\n\t}\n\n\tconst targetNode = node.getFirstDescendantByKindOrThrow(SyntaxKind.ObjectLiteralExpression)\n\tconst values = getValuesOfObjectLiteral(targetNode)\n\n\tconst collapseObject = (v: string | string[] | typeof values): any => {\n\t\tif (typeof v === 'string') {\n\t\t\treturn v\n\t\t}\n\t\tif (Array.isArray(v) && v.every((value) => typeof value === 'string')) {\n\t\t\treturn v\n\t\t}\n\n\t\treturn v.reduce((acc, current) => {\n\t\t\tif (typeof current === 'string') {\n\t\t\t\treturn acc\n\t\t\t}\n\t\t\treturn {\n\t\t\t\t...acc,\n\t\t\t\t[current.identifier]: collapseObject(current.value as string[]),\n\t\t\t}\n\t\t}, {})\n\t}\n\treturn collapseObject(values)\n}\n\nexport const analyzeSourceFileExposedModels = (sourceFile: SourceFile): ExposedModelData[] => {\n\tconst models: ExposedModelData[] = []\n\n\tconst nameOfUseExposeApiModel = discoverImportedName({\n\t\tsourceFile,\n\t\toriginalName: 'useExposeApiModel',\n\t})\n\n\tconst nameOfUseExposeNamedApiModels = discoverImportedName({\n\t\tsourceFile,\n\t\toriginalName: 'useExposeNamedApiModels',\n\t})\n\n\tsourceFile\n\t\t.forEachChildAsArray()\n\t\t.filter((node) => node.isKind(SyntaxKind.ExpressionStatement))\n\t\t.map((node) => {\n\t\t\tif (nameOfUseExposeApiModel && node.getText().startsWith(nameOfUseExposeApiModel)) {\n\t\t\t\tconst callExpressionNode = node.getFirstChild()\n\t\t\t\tconst syntaxListChildren = callExpressionNode?.getChildrenOfKind(SyntaxKind.SyntaxList) || []\n\n\t\t\t\tconst firstChild = syntaxListChildren[0].getFirstChild()\n\t\t\t\tif (!firstChild) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tmodels.push(parseExposedModel(firstChild))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (nameOfUseExposeNamedApiModels && node.getText().startsWith(nameOfUseExposeNamedApiModels)) {\n\t\t\t\tconst callExpressionNode = node.getFirstChild()\n\t\t\t\tconst syntaxListChildren = callExpressionNode?.getChildrenOfKind(SyntaxKind.SyntaxList) || []\n\n\t\t\t\tconst firstChild = syntaxListChildren[0].getFirstChild()\n\t\t\t\tif (!firstChild) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst parsedModels = parseNamedExposedModels(firstChild)\n\t\t\t\tparsedModels.forEach((model) => models.push(model))\n\t\t\t}\n\t\t})\n\treturn models\n}\n"],"names":["resolveWorkerUrl","candidates","candidate","url","existsSync","fileURLToPath","prepareOpenApiSpec","logLevel","tsconfigPath","sourceFilePaths","sourceFileDiscovery","incremental","profiling","openApiManager","OpenApiManager","Logger","project","Project","path","explicitRouters","discoveredRouterFiles","allSourceFiles","sourceFiles","filepath","filePath","file","discoverRouters","discoveredSourceFiles","startTime","files","discoverRouterFiles","acc","current","r","filesToAnalyze","apiHeaders","analyzeSourceFileApiHeader","headers","exposedModels","analyzeSourceFileExposedModels","cachePath","endpoints","analyzeMultipleSourceFiles","e","config","filterEndpointPaths","cached","uncached","timestamp","getSourceFileTimestamp","hit","SourceFileCache","f","allTasks","crypto","pool","WorkerPool","results","ft","byFile","i","result","fileName","fileResult","sum","t","analyzedFiles","a","b","ep","s","sourceFile","nameOfUseApiHeader","discoverImportedName","node","SyntaxKind","targetNode","values","getValuesOfObjectLiteral","collapseObject","v","value","models","nameOfUseExposeApiModel","nameOfUseExposeNamedApiModels","firstChild","parseExposedModel","parseNamedExposedModels","model"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use strict";const g=require("ts-morph"),i=require("worker_threads"),f=require("./parseEndpoint.cjs"),h=["get","post","put","delete","del","patch"],I=h.join("|");let s=null,u=null;function F(e){return(!s||u!==e)&&(s=new g.Project({tsConfigFilePath:e,skipFileDependencyResolution:!0}),u=e),s}i.parentPort.on("message",e=>{try{const r=F(e.tsconfigPath);let t=r.getSourceFile(e.sourceFilePath);t||(t=r.addSourceFileAtPath(e.sourceFilePath));const p=new RegExp(`${e.routerName}\\.(?:${I})`);let c=0,n;if(t.forEachChild(o=>{n||p.test(o.getText())&&(c===e.endpointIndex&&(n=o),c++)}),!n){const o={taskId:e.taskId,error:`Endpoint not found: routerName=${e.routerName} index=${e.endpointIndex} in ${e.sourceFilePath}`};i.parentPort.postMessage(o);return}const l=performance.now(),{endpoint:d,sectionTimings:a}=f.parseEndpoint(n,e.sourceFilePath),P={taskId:e.taskId,endpoint:d,sectionTimings:a,timing:performance.now()-l};i.parentPort.postMessage(P)}catch(r){const t={taskId:e.taskId,error:String(r)};i.parentPort.postMessage(t)}});
1
+ "use strict";const E=require("ts-morph"),s=require("worker_threads"),m=require("./nodeParsers.cjs"),F=require("./parseEndpoint.cjs"),T=["get","post","put","delete","del","patch"],I=T.join("|");let i=null,u=null;function j(e){return(!i||u!==e)&&(i=new E.Project({tsConfigFilePath:e,skipFileDependencyResolution:!0}),u=e),i}s.parentPort.on("message",e=>{try{const r=j(e.tsconfigPath);let t=r.getSourceFile(e.sourceFilePath);t||(t=r.addSourceFileAtPath(e.sourceFilePath));const c=[],p=[];e.routerNames.forEach(l=>{const d=new RegExp(`${l}\\.(?:${I})`);t.forEachChild(n=>{if(!d.test(n.getText()))return;if(e.filterEndpointPaths){const f=m.resolveEndpointPath(n)??"";if(!e.filterEndpointPaths.some(g=>f.includes(g)))return}const h=performance.now(),{endpoint:o,sectionTimings:P}=F.parseEndpoint(n,e.sourceFilePath);p.push({method:o.method,path:o.path,timing:performance.now()-h,sectionTimings:P}),c.push(o)})});const a={taskId:e.taskId,endpoints:c,endpointTimings:p};s.parentPort.postMessage(a)}catch(r){const t={taskId:e.taskId,error:String(r)};s.parentPort.postMessage(t)}});
2
2
  //# sourceMappingURL=analyzerWorker.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"analyzerWorker.cjs","sources":["../../../src/openapi/analyzerModule/analyzerWorker.ts"],"sourcesContent":["import { Node, Project, ts } from 'ts-morph'\nimport { parentPort } from 'worker_threads'\n\nimport { parseEndpoint } from './parseEndpoint'\nimport { WorkerResult, WorkerTask } from './workerPool'\n\nconst OPERATIONS = ['get', 'post', 'put', 'delete', 'del', 'patch']\nconst OPERATIONS_PATTERN = OPERATIONS.join('|')\n\nlet project: Project | null = null\nlet currentTsconfigPath: string | null = null\n\nfunction getProject(tsconfigPath: string): Project {\n\tif (!project || currentTsconfigPath !== tsconfigPath) {\n\t\tproject = new Project({\n\t\t\ttsConfigFilePath: tsconfigPath,\n\t\t\tskipFileDependencyResolution: true,\n\t\t})\n\t\tcurrentTsconfigPath = tsconfigPath\n\t}\n\treturn project\n}\n\nparentPort!.on('message', (task: WorkerTask) => {\n\ttry {\n\t\tconst proj = getProject(task.tsconfigPath)\n\n\t\tlet sourceFile = proj.getSourceFile(task.sourceFilePath)\n\t\tif (!sourceFile) {\n\t\t\tsourceFile = proj.addSourceFileAtPath(task.sourceFilePath)\n\t\t}\n\n\t\tconst routerPattern = new RegExp(`${task.routerName}\\\\.(?:${OPERATIONS_PATTERN})`)\n\t\tlet index = 0\n\t\tlet targetNode: Node<ts.Node> | undefined\n\n\t\tsourceFile.forEachChild((node) => {\n\t\t\tif (targetNode) return\n\t\t\tif (routerPattern.test(node.getText())) {\n\t\t\t\tif (index === task.endpointIndex) {\n\t\t\t\t\ttargetNode = node\n\t\t\t\t}\n\t\t\t\tindex++\n\t\t\t}\n\t\t})\n\n\t\tif (!targetNode) {\n\t\t\tconst result: WorkerResult = {\n\t\t\t\ttaskId: task.taskId,\n\t\t\t\terror: `Endpoint not found: routerName=${task.routerName} index=${task.endpointIndex} in ${task.sourceFilePath}`,\n\t\t\t}\n\t\t\tparentPort!.postMessage(result)\n\t\t\treturn\n\t\t}\n\n\t\tconst t1 = performance.now()\n\t\tconst { endpoint, sectionTimings } = parseEndpoint(targetNode, task.sourceFilePath)\n\n\t\tconst result: WorkerResult = {\n\t\t\ttaskId: task.taskId,\n\t\t\tendpoint,\n\t\t\tsectionTimings,\n\t\t\ttiming: performance.now() - t1,\n\t\t}\n\t\tparentPort!.postMessage(result)\n\t} catch (err) {\n\t\tconst result: WorkerResult = {\n\t\t\ttaskId: task.taskId,\n\t\t\terror: String(err),\n\t\t}\n\t\tparentPort!.postMessage(result)\n\t}\n})\n"],"names":["OPERATIONS","OPERATIONS_PATTERN","project","currentTsconfigPath","getProject","tsconfigPath","Project","parentPort","task","proj","sourceFile","routerPattern","index","targetNode","node","result","t1","endpoint","sectionTimings","parseEndpoint","err"],"mappings":"sGAMMA,EAAa,CAAC,MAAO,OAAQ,MAAO,SAAU,MAAO,OAAO,EAC5DC,EAAqBD,EAAW,KAAK,GAAG,EAE9C,IAAIE,EAA0B,KAC1BC,EAAqC,KAEzC,SAASC,EAAWC,EAA+B,CAC9C,OAAA,CAACH,GAAWC,IAAwBE,KACvCH,EAAU,IAAII,EAAAA,QAAQ,CACrB,iBAAkBD,EAClB,6BAA8B,EAAA,CAC9B,EACqBF,EAAAE,GAEhBH,CACR,CAEAK,EAAAA,WAAY,GAAG,UAAYC,GAAqB,CAC3C,GAAA,CACG,MAAAC,EAAOL,EAAWI,EAAK,YAAY,EAEzC,IAAIE,EAAaD,EAAK,cAAcD,EAAK,cAAc,EAClDE,IACSA,EAAAD,EAAK,oBAAoBD,EAAK,cAAc,GAGpD,MAAAG,EAAgB,IAAI,OAAO,GAAGH,EAAK,UAAU,SAASP,CAAkB,GAAG,EACjF,IAAIW,EAAQ,EACRC,EAYJ,GAVWH,EAAA,aAAcI,GAAS,CAC7BD,GACAF,EAAc,KAAKG,EAAK,QAAS,CAAA,IAChCF,IAAUJ,EAAK,gBACLK,EAAAC,GAEdF,IACD,CACA,EAEG,CAACC,EAAY,CAChB,MAAME,EAAuB,CAC5B,OAAQP,EAAK,OACb,MAAO,kCAAkCA,EAAK,UAAU,UAAUA,EAAK,aAAa,OAAOA,EAAK,cAAc,EAC/G,EACAD,EAAA,WAAY,YAAYQ,CAAM,EAC9B,MAAA,CAGK,MAAAC,EAAK,YAAY,IAAI,EACrB,CAAE,SAAAC,EAAU,eAAAC,GAAmBC,EAAc,cAAAN,EAAYL,EAAK,cAAc,EAE5EO,EAAuB,CAC5B,OAAQP,EAAK,OACb,SAAAS,EACA,eAAAC,EACA,OAAQ,YAAY,MAAQF,CAC7B,EACAT,EAAA,WAAY,YAAYQ,CAAM,QACtBK,EAAK,CACb,MAAML,EAAuB,CAC5B,OAAQP,EAAK,OACb,MAAO,OAAOY,CAAG,CAClB,EACAb,EAAA,WAAY,YAAYQ,CAAM,CAAA,CAEhC,CAAC"}
1
+ {"version":3,"file":"analyzerWorker.cjs","sources":["../../../src/openapi/analyzerModule/analyzerWorker.ts"],"sourcesContent":["import { Project } from 'ts-morph'\nimport { parentPort } from 'worker_threads'\n\nimport { EndpointData } from '../types'\nimport { resolveEndpointPath } from './nodeParsers'\nimport { parseEndpoint } from './parseEndpoint'\nimport { EndpointTiming, WorkerResult, WorkerTask } from './workerPool'\n\nconst OPERATIONS = ['get', 'post', 'put', 'delete', 'del', 'patch']\nconst OPERATIONS_PATTERN = OPERATIONS.join('|')\n\nlet project: Project | null = null\nlet currentTsconfigPath: string | null = null\n\nfunction getProject(tsconfigPath: string): Project {\n\tif (!project || currentTsconfigPath !== tsconfigPath) {\n\t\tproject = new Project({\n\t\t\ttsConfigFilePath: tsconfigPath,\n\t\t\tskipFileDependencyResolution: true,\n\t\t})\n\t\tcurrentTsconfigPath = tsconfigPath\n\t}\n\treturn project\n}\n\nparentPort!.on('message', (task: WorkerTask) => {\n\ttry {\n\t\tconst proj = getProject(task.tsconfigPath)\n\n\t\tlet sourceFile = proj.getSourceFile(task.sourceFilePath)\n\t\tif (!sourceFile) {\n\t\t\tsourceFile = proj.addSourceFileAtPath(task.sourceFilePath)\n\t\t}\n\n\t\tconst endpoints: EndpointData[] = []\n\t\tconst endpointTimings: EndpointTiming[] = []\n\n\t\ttask.routerNames.forEach((routerName) => {\n\t\t\tconst routerPattern = new RegExp(`${routerName}\\\\.(?:${OPERATIONS_PATTERN})`)\n\t\t\tsourceFile!.forEachChild((node) => {\n\t\t\t\tif (!routerPattern.test(node.getText())) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif (task.filterEndpointPaths) {\n\t\t\t\t\tconst endpointPath = resolveEndpointPath(node) ?? ''\n\t\t\t\t\tif (!task.filterEndpointPaths.some((p) => endpointPath.includes(p))) {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst t1 = performance.now()\n\t\t\t\tconst { endpoint, sectionTimings } = parseEndpoint(node, task.sourceFilePath)\n\t\t\t\tendpointTimings.push({\n\t\t\t\t\tmethod: endpoint.method,\n\t\t\t\t\tpath: endpoint.path,\n\t\t\t\t\ttiming: performance.now() - t1,\n\t\t\t\t\tsectionTimings,\n\t\t\t\t})\n\t\t\t\tendpoints.push(endpoint)\n\t\t\t})\n\t\t})\n\n\t\tconst result: WorkerResult = {\n\t\t\ttaskId: task.taskId,\n\t\t\tendpoints,\n\t\t\tendpointTimings,\n\t\t}\n\t\tparentPort!.postMessage(result)\n\t} catch (err) {\n\t\tconst result: WorkerResult = {\n\t\t\ttaskId: task.taskId,\n\t\t\terror: String(err),\n\t\t}\n\t\tparentPort!.postMessage(result)\n\t}\n})\n"],"names":["OPERATIONS","OPERATIONS_PATTERN","project","currentTsconfigPath","getProject","tsconfigPath","Project","parentPort","task","proj","sourceFile","endpoints","endpointTimings","routerName","routerPattern","node","endpointPath","resolveEndpointPath","p","t1","endpoint","sectionTimings","parseEndpoint","result","err"],"mappings":"qIAQMA,EAAa,CAAC,MAAO,OAAQ,MAAO,SAAU,MAAO,OAAO,EAC5DC,EAAqBD,EAAW,KAAK,GAAG,EAE9C,IAAIE,EAA0B,KAC1BC,EAAqC,KAEzC,SAASC,EAAWC,EAA+B,CAC9C,OAAA,CAACH,GAAWC,IAAwBE,KACvCH,EAAU,IAAII,EAAAA,QAAQ,CACrB,iBAAkBD,EAClB,6BAA8B,EAAA,CAC9B,EACqBF,EAAAE,GAEhBH,CACR,CAEAK,EAAAA,WAAY,GAAG,UAAYC,GAAqB,CAC3C,GAAA,CACG,MAAAC,EAAOL,EAAWI,EAAK,YAAY,EAEzC,IAAIE,EAAaD,EAAK,cAAcD,EAAK,cAAc,EAClDE,IACSA,EAAAD,EAAK,oBAAoBD,EAAK,cAAc,GAG1D,MAAMG,EAA4B,CAAC,EAC7BC,EAAoC,CAAC,EAEtCJ,EAAA,YAAY,QAASK,GAAe,CACxC,MAAMC,EAAgB,IAAI,OAAO,GAAGD,CAAU,SAASZ,CAAkB,GAAG,EAChES,EAAA,aAAcK,GAAS,CAClC,GAAI,CAACD,EAAc,KAAKC,EAAK,QAAS,CAAA,EACrC,OAGD,GAAIP,EAAK,oBAAqB,CACvB,MAAAQ,EAAeC,EAAAA,oBAAoBF,CAAI,GAAK,GAC9C,GAAA,CAACP,EAAK,oBAAoB,KAAMU,GAAMF,EAAa,SAASE,CAAC,CAAC,EACjE,MACD,CAGK,MAAAC,EAAK,YAAY,IAAI,EACrB,CAAE,SAAAC,EAAU,eAAAC,GAAmBC,EAAc,cAAAP,EAAMP,EAAK,cAAc,EAC5EI,EAAgB,KAAK,CACpB,OAAQQ,EAAS,OACjB,KAAMA,EAAS,KACf,OAAQ,YAAY,IAAA,EAAQD,EAC5B,eAAAE,CAAA,CACA,EACDV,EAAU,KAAKS,CAAQ,CAAA,CACvB,CAAA,CACD,EAED,MAAMG,EAAuB,CAC5B,OAAQf,EAAK,OACb,UAAAG,EACA,gBAAAC,CACD,EACAL,EAAA,WAAY,YAAYgB,CAAM,QACtBC,EAAK,CACb,MAAMD,EAAuB,CAC5B,OAAQf,EAAK,OACb,MAAO,OAAOgB,CAAG,CAClB,EACAjB,EAAA,WAAY,YAAYgB,CAAM,CAAA,CAEhC,CAAC"}
@@ -1,44 +1,52 @@
1
1
  import { Project as g } from "ts-morph";
2
2
  import { parentPort as i } from "worker_threads";
3
- import { parseEndpoint as m } from "./parseEndpoint.mjs";
4
- const P = ["get", "post", "put", "delete", "del", "patch"], h = P.join("|");
3
+ import { resolveEndpointPath as E } from "./nodeParsers.mjs";
4
+ import { parseEndpoint as F } from "./parseEndpoint.mjs";
5
+ const T = ["get", "post", "put", "delete", "del", "patch"], I = T.join("|");
5
6
  let s = null, l = null;
6
- function I(e) {
7
- return (!s || l !== e) && (s = new g({
8
- tsConfigFilePath: e,
7
+ function j(t) {
8
+ return (!s || l !== t) && (s = new g({
9
+ tsConfigFilePath: t,
9
10
  skipFileDependencyResolution: !0
10
- }), l = e), s;
11
+ }), l = t), s;
11
12
  }
12
- i.on("message", (e) => {
13
+ i.on("message", (t) => {
13
14
  try {
14
- const r = I(e.tsconfigPath);
15
- let t = r.getSourceFile(e.sourceFilePath);
16
- t || (t = r.addSourceFileAtPath(e.sourceFilePath));
17
- const u = new RegExp(`${e.routerName}\\.(?:${h})`);
18
- let c = 0, o;
19
- if (t.forEachChild((n) => {
20
- o || u.test(n.getText()) && (c === e.endpointIndex && (o = n), c++);
21
- }), !o) {
22
- const n = {
23
- taskId: e.taskId,
24
- error: `Endpoint not found: routerName=${e.routerName} index=${e.endpointIndex} in ${e.sourceFilePath}`
25
- };
26
- i.postMessage(n);
27
- return;
28
- }
29
- const p = performance.now(), { endpoint: d, sectionTimings: a } = m(o, e.sourceFilePath), f = {
30
- taskId: e.taskId,
31
- endpoint: d,
32
- sectionTimings: a,
33
- timing: performance.now() - p
15
+ const o = j(t.tsconfigPath);
16
+ let e = o.getSourceFile(t.sourceFilePath);
17
+ e || (e = o.addSourceFileAtPath(t.sourceFilePath));
18
+ const c = [], p = [];
19
+ t.routerNames.forEach((a) => {
20
+ const h = new RegExp(`${a}\\.(?:${I})`);
21
+ e.forEachChild((n) => {
22
+ if (!h.test(n.getText()))
23
+ return;
24
+ if (t.filterEndpointPaths) {
25
+ const f = E(n) ?? "";
26
+ if (!t.filterEndpointPaths.some((P) => f.includes(P)))
27
+ return;
28
+ }
29
+ const d = performance.now(), { endpoint: r, sectionTimings: m } = F(n, t.sourceFilePath);
30
+ p.push({
31
+ method: r.method,
32
+ path: r.path,
33
+ timing: performance.now() - d,
34
+ sectionTimings: m
35
+ }), c.push(r);
36
+ });
37
+ });
38
+ const u = {
39
+ taskId: t.taskId,
40
+ endpoints: c,
41
+ endpointTimings: p
34
42
  };
35
- i.postMessage(f);
36
- } catch (r) {
37
- const t = {
38
- taskId: e.taskId,
39
- error: String(r)
43
+ i.postMessage(u);
44
+ } catch (o) {
45
+ const e = {
46
+ taskId: t.taskId,
47
+ error: String(o)
40
48
  };
41
- i.postMessage(t);
49
+ i.postMessage(e);
42
50
  }
43
51
  });
44
52
  //# sourceMappingURL=analyzerWorker.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"analyzerWorker.mjs","sources":["../../../src/openapi/analyzerModule/analyzerWorker.ts"],"sourcesContent":["import { Node, Project, ts } from 'ts-morph'\nimport { parentPort } from 'worker_threads'\n\nimport { parseEndpoint } from './parseEndpoint'\nimport { WorkerResult, WorkerTask } from './workerPool'\n\nconst OPERATIONS = ['get', 'post', 'put', 'delete', 'del', 'patch']\nconst OPERATIONS_PATTERN = OPERATIONS.join('|')\n\nlet project: Project | null = null\nlet currentTsconfigPath: string | null = null\n\nfunction getProject(tsconfigPath: string): Project {\n\tif (!project || currentTsconfigPath !== tsconfigPath) {\n\t\tproject = new Project({\n\t\t\ttsConfigFilePath: tsconfigPath,\n\t\t\tskipFileDependencyResolution: true,\n\t\t})\n\t\tcurrentTsconfigPath = tsconfigPath\n\t}\n\treturn project\n}\n\nparentPort!.on('message', (task: WorkerTask) => {\n\ttry {\n\t\tconst proj = getProject(task.tsconfigPath)\n\n\t\tlet sourceFile = proj.getSourceFile(task.sourceFilePath)\n\t\tif (!sourceFile) {\n\t\t\tsourceFile = proj.addSourceFileAtPath(task.sourceFilePath)\n\t\t}\n\n\t\tconst routerPattern = new RegExp(`${task.routerName}\\\\.(?:${OPERATIONS_PATTERN})`)\n\t\tlet index = 0\n\t\tlet targetNode: Node<ts.Node> | undefined\n\n\t\tsourceFile.forEachChild((node) => {\n\t\t\tif (targetNode) return\n\t\t\tif (routerPattern.test(node.getText())) {\n\t\t\t\tif (index === task.endpointIndex) {\n\t\t\t\t\ttargetNode = node\n\t\t\t\t}\n\t\t\t\tindex++\n\t\t\t}\n\t\t})\n\n\t\tif (!targetNode) {\n\t\t\tconst result: WorkerResult = {\n\t\t\t\ttaskId: task.taskId,\n\t\t\t\terror: `Endpoint not found: routerName=${task.routerName} index=${task.endpointIndex} in ${task.sourceFilePath}`,\n\t\t\t}\n\t\t\tparentPort!.postMessage(result)\n\t\t\treturn\n\t\t}\n\n\t\tconst t1 = performance.now()\n\t\tconst { endpoint, sectionTimings } = parseEndpoint(targetNode, task.sourceFilePath)\n\n\t\tconst result: WorkerResult = {\n\t\t\ttaskId: task.taskId,\n\t\t\tendpoint,\n\t\t\tsectionTimings,\n\t\t\ttiming: performance.now() - t1,\n\t\t}\n\t\tparentPort!.postMessage(result)\n\t} catch (err) {\n\t\tconst result: WorkerResult = {\n\t\t\ttaskId: task.taskId,\n\t\t\terror: String(err),\n\t\t}\n\t\tparentPort!.postMessage(result)\n\t}\n})\n"],"names":["OPERATIONS","OPERATIONS_PATTERN","project","currentTsconfigPath","getProject","tsconfigPath","Project","parentPort","task","proj","sourceFile","routerPattern","index","targetNode","node","result","t1","endpoint","sectionTimings","parseEndpoint","err"],"mappings":";;;AAMA,MAAMA,IAAa,CAAC,OAAO,QAAQ,OAAO,UAAU,OAAO,OAAO,GAC5DC,IAAqBD,EAAW,KAAK,GAAG;AAE9C,IAAIE,IAA0B,MAC1BC,IAAqC;AAEzC,SAASC,EAAWC,GAA+B;AAC9C,UAAA,CAACH,KAAWC,MAAwBE,OACvCH,IAAU,IAAII,EAAQ;AAAA,IACrB,kBAAkBD;AAAA,IAClB,8BAA8B;AAAA,EAAA,CAC9B,GACqBF,IAAAE,IAEhBH;AACR;AAEAK,EAAY,GAAG,WAAW,CAACC,MAAqB;AAC3C,MAAA;AACG,UAAAC,IAAOL,EAAWI,EAAK,YAAY;AAEzC,QAAIE,IAAaD,EAAK,cAAcD,EAAK,cAAc;AACvD,IAAKE,MACSA,IAAAD,EAAK,oBAAoBD,EAAK,cAAc;AAGpD,UAAAG,IAAgB,IAAI,OAAO,GAAGH,EAAK,UAAU,SAASP,CAAkB,GAAG;AACjF,QAAIW,IAAQ,GACRC;AAYJ,QAVWH,EAAA,aAAa,CAACI,MAAS;AACjC,MAAID,KACAF,EAAc,KAAKG,EAAK,QAAS,CAAA,MAChCF,MAAUJ,EAAK,kBACLK,IAAAC,IAEdF;AAAA,IACD,CACA,GAEG,CAACC,GAAY;AAChB,YAAME,IAAuB;AAAA,QAC5B,QAAQP,EAAK;AAAA,QACb,OAAO,kCAAkCA,EAAK,UAAU,UAAUA,EAAK,aAAa,OAAOA,EAAK,cAAc;AAAA,MAC/G;AACA,MAAAD,EAAY,YAAYQ,CAAM;AAC9B;AAAA,IAAA;AAGK,UAAAC,IAAK,YAAY,IAAI,GACrB,EAAE,UAAAC,GAAU,gBAAAC,MAAmBC,EAAcN,GAAYL,EAAK,cAAc,GAE5EO,IAAuB;AAAA,MAC5B,QAAQP,EAAK;AAAA,MACb,UAAAS;AAAA,MACA,gBAAAC;AAAA,MACA,QAAQ,YAAY,QAAQF;AAAA,IAC7B;AACA,IAAAT,EAAY,YAAYQ,CAAM;AAAA,WACtBK,GAAK;AACb,UAAML,IAAuB;AAAA,MAC5B,QAAQP,EAAK;AAAA,MACb,OAAO,OAAOY,CAAG;AAAA,IAClB;AACA,IAAAb,EAAY,YAAYQ,CAAM;AAAA,EAAA;AAEhC,CAAC;"}
1
+ {"version":3,"file":"analyzerWorker.mjs","sources":["../../../src/openapi/analyzerModule/analyzerWorker.ts"],"sourcesContent":["import { Project } from 'ts-morph'\nimport { parentPort } from 'worker_threads'\n\nimport { EndpointData } from '../types'\nimport { resolveEndpointPath } from './nodeParsers'\nimport { parseEndpoint } from './parseEndpoint'\nimport { EndpointTiming, WorkerResult, WorkerTask } from './workerPool'\n\nconst OPERATIONS = ['get', 'post', 'put', 'delete', 'del', 'patch']\nconst OPERATIONS_PATTERN = OPERATIONS.join('|')\n\nlet project: Project | null = null\nlet currentTsconfigPath: string | null = null\n\nfunction getProject(tsconfigPath: string): Project {\n\tif (!project || currentTsconfigPath !== tsconfigPath) {\n\t\tproject = new Project({\n\t\t\ttsConfigFilePath: tsconfigPath,\n\t\t\tskipFileDependencyResolution: true,\n\t\t})\n\t\tcurrentTsconfigPath = tsconfigPath\n\t}\n\treturn project\n}\n\nparentPort!.on('message', (task: WorkerTask) => {\n\ttry {\n\t\tconst proj = getProject(task.tsconfigPath)\n\n\t\tlet sourceFile = proj.getSourceFile(task.sourceFilePath)\n\t\tif (!sourceFile) {\n\t\t\tsourceFile = proj.addSourceFileAtPath(task.sourceFilePath)\n\t\t}\n\n\t\tconst endpoints: EndpointData[] = []\n\t\tconst endpointTimings: EndpointTiming[] = []\n\n\t\ttask.routerNames.forEach((routerName) => {\n\t\t\tconst routerPattern = new RegExp(`${routerName}\\\\.(?:${OPERATIONS_PATTERN})`)\n\t\t\tsourceFile!.forEachChild((node) => {\n\t\t\t\tif (!routerPattern.test(node.getText())) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif (task.filterEndpointPaths) {\n\t\t\t\t\tconst endpointPath = resolveEndpointPath(node) ?? ''\n\t\t\t\t\tif (!task.filterEndpointPaths.some((p) => endpointPath.includes(p))) {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst t1 = performance.now()\n\t\t\t\tconst { endpoint, sectionTimings } = parseEndpoint(node, task.sourceFilePath)\n\t\t\t\tendpointTimings.push({\n\t\t\t\t\tmethod: endpoint.method,\n\t\t\t\t\tpath: endpoint.path,\n\t\t\t\t\ttiming: performance.now() - t1,\n\t\t\t\t\tsectionTimings,\n\t\t\t\t})\n\t\t\t\tendpoints.push(endpoint)\n\t\t\t})\n\t\t})\n\n\t\tconst result: WorkerResult = {\n\t\t\ttaskId: task.taskId,\n\t\t\tendpoints,\n\t\t\tendpointTimings,\n\t\t}\n\t\tparentPort!.postMessage(result)\n\t} catch (err) {\n\t\tconst result: WorkerResult = {\n\t\t\ttaskId: task.taskId,\n\t\t\terror: String(err),\n\t\t}\n\t\tparentPort!.postMessage(result)\n\t}\n})\n"],"names":["OPERATIONS","OPERATIONS_PATTERN","project","currentTsconfigPath","getProject","tsconfigPath","Project","parentPort","task","proj","sourceFile","endpoints","endpointTimings","routerName","routerPattern","node","endpointPath","resolveEndpointPath","p","t1","endpoint","sectionTimings","parseEndpoint","result","err"],"mappings":";;;;AAQA,MAAMA,IAAa,CAAC,OAAO,QAAQ,OAAO,UAAU,OAAO,OAAO,GAC5DC,IAAqBD,EAAW,KAAK,GAAG;AAE9C,IAAIE,IAA0B,MAC1BC,IAAqC;AAEzC,SAASC,EAAWC,GAA+B;AAC9C,UAAA,CAACH,KAAWC,MAAwBE,OACvCH,IAAU,IAAII,EAAQ;AAAA,IACrB,kBAAkBD;AAAA,IAClB,8BAA8B;AAAA,EAAA,CAC9B,GACqBF,IAAAE,IAEhBH;AACR;AAEAK,EAAY,GAAG,WAAW,CAACC,MAAqB;AAC3C,MAAA;AACG,UAAAC,IAAOL,EAAWI,EAAK,YAAY;AAEzC,QAAIE,IAAaD,EAAK,cAAcD,EAAK,cAAc;AACvD,IAAKE,MACSA,IAAAD,EAAK,oBAAoBD,EAAK,cAAc;AAG1D,UAAMG,IAA4B,CAAC,GAC7BC,IAAoC,CAAC;AAEtC,IAAAJ,EAAA,YAAY,QAAQ,CAACK,MAAe;AACxC,YAAMC,IAAgB,IAAI,OAAO,GAAGD,CAAU,SAASZ,CAAkB,GAAG;AAChE,MAAAS,EAAA,aAAa,CAACK,MAAS;AAClC,YAAI,CAACD,EAAc,KAAKC,EAAK,QAAS,CAAA;AACrC;AAGD,YAAIP,EAAK,qBAAqB;AACvB,gBAAAQ,IAAeC,EAAoBF,CAAI,KAAK;AAC9C,cAAA,CAACP,EAAK,oBAAoB,KAAK,CAACU,MAAMF,EAAa,SAASE,CAAC,CAAC;AACjE;AAAA,QACD;AAGK,cAAAC,IAAK,YAAY,IAAI,GACrB,EAAE,UAAAC,GAAU,gBAAAC,MAAmBC,EAAcP,GAAMP,EAAK,cAAc;AAC5E,QAAAI,EAAgB,KAAK;AAAA,UACpB,QAAQQ,EAAS;AAAA,UACjB,MAAMA,EAAS;AAAA,UACf,QAAQ,YAAY,IAAA,IAAQD;AAAA,UAC5B,gBAAAE;AAAA,QAAA,CACA,GACDV,EAAU,KAAKS,CAAQ;AAAA,MAAA,CACvB;AAAA,IAAA,CACD;AAED,UAAMG,IAAuB;AAAA,MAC5B,QAAQf,EAAK;AAAA,MACb,WAAAG;AAAA,MACA,iBAAAC;AAAA,IACD;AACA,IAAAL,EAAY,YAAYgB,CAAM;AAAA,WACtBC,GAAK;AACb,UAAMD,IAAuB;AAAA,MAC5B,QAAQf,EAAK;AAAA,MACb,OAAO,OAAOgB,CAAG;AAAA,IAClB;AACA,IAAAjB,EAAY,YAAYgB,CAAM;AAAA,EAAA;AAEhC,CAAC;"}
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("os"),h=require("worker_threads");class l{constructor(e){this.queue=[],this.pending=new Map;const t=Math.max(1,Math.min(o.cpus().length-1,8));this.workers=Array.from({length:t},()=>{const s=new h.Worker(e);return s.on("message",r=>{const i=this.pending.get(r.taskId);i&&(this.pending.delete(r.taskId),i(r)),this.idle.push(s),this.flush()}),s.on("error",r=>{for(const[i,n]of this.pending){n({taskId:i,error:String(r)}),this.pending.delete(i);break}this.idle.push(s),this.flush()}),s}),this.idle=[...this.workers]}run(e){return new Promise(t=>{if(this.idle.length>0){const s=this.idle.pop();this.pending.set(e.taskId,t),s.postMessage(e)}else this.queue.push({task:e,resolve:t})})}runAll(e){return Promise.all(e.map(t=>this.run(t)))}terminate(){this.workers.forEach(e=>e.terminate())}flush(){for(;this.queue.length>0&&this.idle.length>0;){const{task:e,resolve:t}=this.queue.shift(),s=this.idle.pop();this.pending.set(e.taskId,t),s.postMessage(e)}}}exports.WorkerPool=l;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const l=require("os"),u=require("worker_threads");class d{constructor(e,t){this.queue=[],this.pending=new Map;const s=Math.max(1,Math.min(l.cpus().length-1,8)),o=t===void 0?s:Math.max(1,Math.min(s,t));this.workers=Array.from({length:o},()=>{const i=new u.Worker(e);return i.on("message",r=>{const n=this.pending.get(r.taskId);n&&(this.pending.delete(r.taskId),n(r)),this.idle.push(i),this.flush()}),i.on("error",r=>{for(const[n,h]of this.pending){h({taskId:n,error:String(r)}),this.pending.delete(n);break}this.idle.push(i),this.flush()}),i}),this.idle=[...this.workers]}run(e){return new Promise(t=>{if(this.idle.length>0){const s=this.idle.pop();this.pending.set(e.taskId,t),s.postMessage(e)}else this.queue.push({task:e,resolve:t})})}runAll(e){return Promise.all(e.map(t=>this.run(t)))}terminate(){this.workers.forEach(e=>e.terminate())}flush(){for(;this.queue.length>0&&this.idle.length>0;){const{task:e,resolve:t}=this.queue.shift(),s=this.idle.pop();this.pending.set(e.taskId,t),s.postMessage(e)}}}exports.WorkerPool=d;
2
2
  //# sourceMappingURL=workerPool.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"workerPool.cjs","sources":["../../../src/openapi/analyzerModule/workerPool.ts"],"sourcesContent":["import os from 'os'\nimport { Worker } from 'worker_threads'\n\nimport { EndpointData } from '../types'\nimport { SectionTiming } from './parseEndpoint'\n\nexport type WorkerTask = {\n\ttaskId: string\n\ttsconfigPath: string\n\tsourceFilePath: string\n\trouterName: string\n\tendpointIndex: number\n}\n\nexport type WorkerResultSuccess = {\n\ttaskId: string\n\tendpoint: EndpointData\n\tsectionTimings: SectionTiming[]\n\ttiming: number\n}\n\nexport type WorkerResultError = {\n\ttaskId: string\n\terror: string\n}\n\nexport type WorkerResult = WorkerResultSuccess | WorkerResultError\n\nexport class WorkerPool {\n\tprivate workers: Worker[]\n\tprivate idle: Worker[]\n\tprivate queue: Array<{ task: WorkerTask; resolve: (r: WorkerResult) => void }> = []\n\tprivate pending = new Map<string, (r: WorkerResult) => void>()\n\n\tconstructor(workerUrl: URL) {\n\t\tconst size = Math.max(1, Math.min(os.cpus().length - 1, 8))\n\t\tthis.workers = Array.from({ length: size }, () => {\n\t\t\tconst worker = new Worker(workerUrl)\n\t\t\tworker.on('message', (result: WorkerResult) => {\n\t\t\t\tconst resolve = this.pending.get(result.taskId)\n\t\t\t\tif (resolve) {\n\t\t\t\t\tthis.pending.delete(result.taskId)\n\t\t\t\t\tresolve(result)\n\t\t\t\t}\n\t\t\t\tthis.idle.push(worker)\n\t\t\t\tthis.flush()\n\t\t\t})\n\t\t\tworker.on('error', (err) => {\n\t\t\t\t// Find any pending task for this worker and reject it\n\t\t\t\t// (worker crashed — shouldn't happen, but handle gracefully)\n\t\t\t\tfor (const [taskId, resolve] of this.pending) {\n\t\t\t\t\tresolve({ taskId, error: String(err) })\n\t\t\t\t\tthis.pending.delete(taskId)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tthis.idle.push(worker)\n\t\t\t\tthis.flush()\n\t\t\t})\n\t\t\treturn worker\n\t\t})\n\t\tthis.idle = [...this.workers]\n\t}\n\n\trun(task: WorkerTask): Promise<WorkerResult> {\n\t\treturn new Promise((resolve) => {\n\t\t\tif (this.idle.length > 0) {\n\t\t\t\tconst worker = this.idle.pop()!\n\t\t\t\tthis.pending.set(task.taskId, resolve)\n\t\t\t\tworker.postMessage(task)\n\t\t\t} else {\n\t\t\t\tthis.queue.push({ task, resolve })\n\t\t\t}\n\t\t})\n\t}\n\n\trunAll(tasks: WorkerTask[]): Promise<WorkerResult[]> {\n\t\treturn Promise.all(tasks.map((t) => this.run(t)))\n\t}\n\n\tterminate() {\n\t\tthis.workers.forEach((w) => w.terminate())\n\t}\n\n\tprivate flush() {\n\t\twhile (this.queue.length > 0 && this.idle.length > 0) {\n\t\t\tconst { task, resolve } = this.queue.shift()!\n\t\t\tconst worker = this.idle.pop()!\n\t\t\tthis.pending.set(task.taskId, resolve)\n\t\t\tworker.postMessage(task)\n\t\t}\n\t}\n}\n"],"names":["WorkerPool","workerUrl","size","os","worker","Worker","result","resolve","err","taskId","task","tasks","w"],"mappings":"kIA4BO,MAAMA,CAAW,CAMvB,YAAYC,EAAgB,CAH5B,KAAQ,MAAyE,CAAC,EAC1E,KAAA,YAAc,IAGrB,MAAMC,EAAO,KAAK,IAAI,EAAG,KAAK,IAAIC,EAAG,KAAA,EAAO,OAAS,EAAG,CAAC,CAAC,EAC1D,KAAK,QAAU,MAAM,KAAK,CAAE,OAAQD,CAAA,EAAQ,IAAM,CAC3C,MAAAE,EAAS,IAAIC,EAAA,OAAOJ,CAAS,EAC5B,OAAAG,EAAA,GAAG,UAAYE,GAAyB,CAC9C,MAAMC,EAAU,KAAK,QAAQ,IAAID,EAAO,MAAM,EAC1CC,IACE,KAAA,QAAQ,OAAOD,EAAO,MAAM,EACjCC,EAAQD,CAAM,GAEV,KAAA,KAAK,KAAKF,CAAM,EACrB,KAAK,MAAM,CAAA,CACX,EACMA,EAAA,GAAG,QAAUI,GAAQ,CAG3B,SAAW,CAACC,EAAQF,CAAO,IAAK,KAAK,QAAS,CAC7CA,EAAQ,CAAE,OAAAE,EAAQ,MAAO,OAAOD,CAAG,EAAG,EACjC,KAAA,QAAQ,OAAOC,CAAM,EAC1B,KAAA,CAEI,KAAA,KAAK,KAAKL,CAAM,EACrB,KAAK,MAAM,CAAA,CACX,EACMA,CAAA,CACP,EACD,KAAK,KAAO,CAAC,GAAG,KAAK,OAAO,CAAA,CAG7B,IAAIM,EAAyC,CACrC,OAAA,IAAI,QAASH,GAAY,CAC3B,GAAA,KAAK,KAAK,OAAS,EAAG,CACnB,MAAAH,EAAS,KAAK,KAAK,IAAI,EAC7B,KAAK,QAAQ,IAAIM,EAAK,OAAQH,CAAO,EACrCH,EAAO,YAAYM,CAAI,CAAA,MAEvB,KAAK,MAAM,KAAK,CAAE,KAAAA,EAAM,QAAAH,EAAS,CAClC,CACA,CAAA,CAGF,OAAOI,EAA8C,CAC7C,OAAA,QAAQ,IAAIA,EAAM,IAAK,GAAM,KAAK,IAAI,CAAC,CAAC,CAAC,CAAA,CAGjD,WAAY,CACX,KAAK,QAAQ,QAASC,GAAMA,EAAE,WAAW,CAAA,CAGlC,OAAQ,CACf,KAAO,KAAK,MAAM,OAAS,GAAK,KAAK,KAAK,OAAS,GAAG,CACrD,KAAM,CAAE,KAAAF,EAAM,QAAAH,CAAA,EAAY,KAAK,MAAM,MAAM,EACrCH,EAAS,KAAK,KAAK,IAAI,EAC7B,KAAK,QAAQ,IAAIM,EAAK,OAAQH,CAAO,EACrCH,EAAO,YAAYM,CAAI,CAAA,CACxB,CAEF"}
1
+ {"version":3,"file":"workerPool.cjs","sources":["../../../src/openapi/analyzerModule/workerPool.ts"],"sourcesContent":["import os from 'os'\nimport { Worker } from 'worker_threads'\n\nimport { EndpointData } from '../types'\nimport { SectionTiming } from './parseEndpoint'\n\nexport type WorkerTask = {\n\ttaskId: string\n\ttsconfigPath: string\n\tsourceFilePath: string\n\trouterNames: string[]\n\tfilterEndpointPaths?: string[]\n}\n\nexport type EndpointTiming = {\n\tmethod: string\n\tpath: string\n\ttiming: number\n\tsectionTimings: SectionTiming[]\n}\n\nexport type WorkerResultSuccess = {\n\ttaskId: string\n\tendpoints: EndpointData[]\n\tendpointTimings: EndpointTiming[]\n}\n\nexport type WorkerResultError = {\n\ttaskId: string\n\terror: string\n}\n\nexport type WorkerResult = WorkerResultSuccess | WorkerResultError\n\nexport class WorkerPool {\n\tprivate workers: Worker[]\n\tprivate idle: Worker[]\n\tprivate queue: Array<{ task: WorkerTask; resolve: (r: WorkerResult) => void }> = []\n\tprivate pending = new Map<string, (r: WorkerResult) => void>()\n\n\tconstructor(workerUrl: URL, maxWorkers?: number) {\n\t\tconst cpuBound = Math.max(1, Math.min(os.cpus().length - 1, 8))\n\t\t// Never spin up more workers than there are files to analyze: each extra worker would\n\t\t// just pay the ts-morph Project cold-start (full TS program load + type-checker warmup)\n\t\t// for nothing while contending for CPU with the workers that actually have work.\n\t\tconst size = maxWorkers === undefined ? cpuBound : Math.max(1, Math.min(cpuBound, maxWorkers))\n\t\tthis.workers = Array.from({ length: size }, () => {\n\t\t\tconst worker = new Worker(workerUrl)\n\t\t\tworker.on('message', (result: WorkerResult) => {\n\t\t\t\tconst resolve = this.pending.get(result.taskId)\n\t\t\t\tif (resolve) {\n\t\t\t\t\tthis.pending.delete(result.taskId)\n\t\t\t\t\tresolve(result)\n\t\t\t\t}\n\t\t\t\tthis.idle.push(worker)\n\t\t\t\tthis.flush()\n\t\t\t})\n\t\t\tworker.on('error', (err) => {\n\t\t\t\t// Find any pending task for this worker and reject it\n\t\t\t\t// (worker crashed — shouldn't happen, but handle gracefully)\n\t\t\t\tfor (const [taskId, resolve] of this.pending) {\n\t\t\t\t\tresolve({ taskId, error: String(err) })\n\t\t\t\t\tthis.pending.delete(taskId)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tthis.idle.push(worker)\n\t\t\t\tthis.flush()\n\t\t\t})\n\t\t\treturn worker\n\t\t})\n\t\tthis.idle = [...this.workers]\n\t}\n\n\trun(task: WorkerTask): Promise<WorkerResult> {\n\t\treturn new Promise((resolve) => {\n\t\t\tif (this.idle.length > 0) {\n\t\t\t\tconst worker = this.idle.pop()!\n\t\t\t\tthis.pending.set(task.taskId, resolve)\n\t\t\t\tworker.postMessage(task)\n\t\t\t} else {\n\t\t\t\tthis.queue.push({ task, resolve })\n\t\t\t}\n\t\t})\n\t}\n\n\trunAll(tasks: WorkerTask[]): Promise<WorkerResult[]> {\n\t\treturn Promise.all(tasks.map((t) => this.run(t)))\n\t}\n\n\tterminate() {\n\t\tthis.workers.forEach((w) => w.terminate())\n\t}\n\n\tprivate flush() {\n\t\twhile (this.queue.length > 0 && this.idle.length > 0) {\n\t\t\tconst { task, resolve } = this.queue.shift()!\n\t\t\tconst worker = this.idle.pop()!\n\t\t\tthis.pending.set(task.taskId, resolve)\n\t\t\tworker.postMessage(task)\n\t\t}\n\t}\n}\n"],"names":["WorkerPool","workerUrl","maxWorkers","cpuBound","os","size","worker","Worker","result","resolve","err","taskId","task","tasks","w"],"mappings":"kIAkCO,MAAMA,CAAW,CAMvB,YAAYC,EAAgBC,EAAqB,CAHjD,KAAQ,MAAyE,CAAC,EAC1E,KAAA,YAAc,IAGrB,MAAMC,EAAW,KAAK,IAAI,EAAG,KAAK,IAAIC,EAAG,KAAA,EAAO,OAAS,EAAG,CAAC,CAAC,EAIxDC,EAAOH,IAAe,OAAYC,EAAW,KAAK,IAAI,EAAG,KAAK,IAAIA,EAAUD,CAAU,CAAC,EAC7F,KAAK,QAAU,MAAM,KAAK,CAAE,OAAQG,CAAA,EAAQ,IAAM,CAC3C,MAAAC,EAAS,IAAIC,EAAA,OAAON,CAAS,EAC5B,OAAAK,EAAA,GAAG,UAAYE,GAAyB,CAC9C,MAAMC,EAAU,KAAK,QAAQ,IAAID,EAAO,MAAM,EAC1CC,IACE,KAAA,QAAQ,OAAOD,EAAO,MAAM,EACjCC,EAAQD,CAAM,GAEV,KAAA,KAAK,KAAKF,CAAM,EACrB,KAAK,MAAM,CAAA,CACX,EACMA,EAAA,GAAG,QAAUI,GAAQ,CAG3B,SAAW,CAACC,EAAQF,CAAO,IAAK,KAAK,QAAS,CAC7CA,EAAQ,CAAE,OAAAE,EAAQ,MAAO,OAAOD,CAAG,EAAG,EACjC,KAAA,QAAQ,OAAOC,CAAM,EAC1B,KAAA,CAEI,KAAA,KAAK,KAAKL,CAAM,EACrB,KAAK,MAAM,CAAA,CACX,EACMA,CAAA,CACP,EACD,KAAK,KAAO,CAAC,GAAG,KAAK,OAAO,CAAA,CAG7B,IAAIM,EAAyC,CACrC,OAAA,IAAI,QAASH,GAAY,CAC3B,GAAA,KAAK,KAAK,OAAS,EAAG,CACnB,MAAAH,EAAS,KAAK,KAAK,IAAI,EAC7B,KAAK,QAAQ,IAAIM,EAAK,OAAQH,CAAO,EACrCH,EAAO,YAAYM,CAAI,CAAA,MAEvB,KAAK,MAAM,KAAK,CAAE,KAAAA,EAAM,QAAAH,EAAS,CAClC,CACA,CAAA,CAGF,OAAOI,EAA8C,CAC7C,OAAA,QAAQ,IAAIA,EAAM,IAAK,GAAM,KAAK,IAAI,CAAC,CAAC,CAAC,CAAA,CAGjD,WAAY,CACX,KAAK,QAAQ,QAASC,GAAMA,EAAE,WAAW,CAAA,CAGlC,OAAQ,CACf,KAAO,KAAK,MAAM,OAAS,GAAK,KAAK,KAAK,OAAS,GAAG,CACrD,KAAM,CAAE,KAAAF,EAAM,QAAAH,CAAA,EAAY,KAAK,MAAM,MAAM,EACrCH,EAAS,KAAK,KAAK,IAAI,EAC7B,KAAK,QAAQ,IAAIM,EAAK,OAAQH,CAAO,EACrCH,EAAO,YAAYM,CAAI,CAAA,CACxB,CAEF"}
@@ -5,14 +5,19 @@ export type WorkerTask = {
5
5
  taskId: string;
6
6
  tsconfigPath: string;
7
7
  sourceFilePath: string;
8
- routerName: string;
9
- endpointIndex: number;
8
+ routerNames: string[];
9
+ filterEndpointPaths?: string[];
10
+ };
11
+ export type EndpointTiming = {
12
+ method: string;
13
+ path: string;
14
+ timing: number;
15
+ sectionTimings: SectionTiming[];
10
16
  };
11
17
  export type WorkerResultSuccess = {
12
18
  taskId: string;
13
- endpoint: EndpointData;
14
- sectionTimings: SectionTiming[];
15
- timing: number;
19
+ endpoints: EndpointData[];
20
+ endpointTimings: EndpointTiming[];
16
21
  };
17
22
  export type WorkerResultError = {
18
23
  taskId: string;
@@ -24,7 +29,7 @@ export declare class WorkerPool {
24
29
  private idle;
25
30
  private queue;
26
31
  private pending;
27
- constructor(workerUrl: URL);
32
+ constructor(workerUrl: URL, maxWorkers?: number);
28
33
  run(task: WorkerTask): Promise<WorkerResult>;
29
34
  runAll(tasks: WorkerTask[]): Promise<WorkerResult[]>;
30
35
  terminate(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"workerPool.d.ts","sourceRoot":"","sources":["../../../src/openapi/analyzerModule/workerPool.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAE/C,MAAM,MAAM,UAAU,GAAG;IACxB,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,MAAM,CAAA;IACpB,cAAc,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IACjC,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,YAAY,CAAA;IACtB,cAAc,EAAE,aAAa,EAAE,CAAA;IAC/B,MAAM,EAAE,MAAM,CAAA;CACd,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,YAAY,GAAG,mBAAmB,GAAG,iBAAiB,CAAA;AAElE,qBAAa,UAAU;IACtB,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,IAAI,CAAU;IACtB,OAAO,CAAC,KAAK,CAAsE;IACnF,OAAO,CAAC,OAAO,CAA+C;gBAElD,SAAS,EAAE,GAAG;IA6B1B,GAAG,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC;IAY5C,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAIpD,SAAS;IAIT,OAAO,CAAC,KAAK;CAQb"}
1
+ {"version":3,"file":"workerPool.d.ts","sourceRoot":"","sources":["../../../src/openapi/analyzerModule/workerPool.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAE/C,MAAM,MAAM,UAAU,GAAG;IACxB,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,MAAM,CAAA;IACpB,cAAc,EAAE,MAAM,CAAA;IACtB,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAA;CAC9B,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,EAAE,aAAa,EAAE,CAAA;CAC/B,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IACjC,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,YAAY,EAAE,CAAA;IACzB,eAAe,EAAE,cAAc,EAAE,CAAA;CACjC,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,YAAY,GAAG,mBAAmB,GAAG,iBAAiB,CAAA;AAElE,qBAAa,UAAU;IACtB,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,IAAI,CAAU;IACtB,OAAO,CAAC,KAAK,CAAsE;IACnF,OAAO,CAAC,OAAO,CAA+C;gBAElD,SAAS,EAAE,GAAG,EAAE,UAAU,CAAC,EAAE,MAAM;IAiC/C,GAAG,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC;IAY5C,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAIpD,SAAS;IAIT,OAAO,CAAC,KAAK;CAQb"}
@@ -1,46 +1,46 @@
1
- import o from "os";
2
- import { Worker as h } from "worker_threads";
3
- class u {
4
- constructor(e) {
1
+ import l from "os";
2
+ import { Worker as d } from "worker_threads";
3
+ class g {
4
+ constructor(e, s) {
5
5
  this.queue = [], this.pending = /* @__PURE__ */ new Map();
6
- const t = Math.max(1, Math.min(o.cpus().length - 1, 8));
7
- this.workers = Array.from({ length: t }, () => {
8
- const s = new h(e);
9
- return s.on("message", (i) => {
10
- const r = this.pending.get(i.taskId);
11
- r && (this.pending.delete(i.taskId), r(i)), this.idle.push(s), this.flush();
12
- }), s.on("error", (i) => {
13
- for (const [r, n] of this.pending) {
14
- n({ taskId: r, error: String(i) }), this.pending.delete(r);
6
+ const t = Math.max(1, Math.min(l.cpus().length - 1, 8)), o = s === void 0 ? t : Math.max(1, Math.min(t, s));
7
+ this.workers = Array.from({ length: o }, () => {
8
+ const i = new d(e);
9
+ return i.on("message", (r) => {
10
+ const n = this.pending.get(r.taskId);
11
+ n && (this.pending.delete(r.taskId), n(r)), this.idle.push(i), this.flush();
12
+ }), i.on("error", (r) => {
13
+ for (const [n, h] of this.pending) {
14
+ h({ taskId: n, error: String(r) }), this.pending.delete(n);
15
15
  break;
16
16
  }
17
- this.idle.push(s), this.flush();
18
- }), s;
17
+ this.idle.push(i), this.flush();
18
+ }), i;
19
19
  }), this.idle = [...this.workers];
20
20
  }
21
21
  run(e) {
22
- return new Promise((t) => {
22
+ return new Promise((s) => {
23
23
  if (this.idle.length > 0) {
24
- const s = this.idle.pop();
25
- this.pending.set(e.taskId, t), s.postMessage(e);
24
+ const t = this.idle.pop();
25
+ this.pending.set(e.taskId, s), t.postMessage(e);
26
26
  } else
27
- this.queue.push({ task: e, resolve: t });
27
+ this.queue.push({ task: e, resolve: s });
28
28
  });
29
29
  }
30
30
  runAll(e) {
31
- return Promise.all(e.map((t) => this.run(t)));
31
+ return Promise.all(e.map((s) => this.run(s)));
32
32
  }
33
33
  terminate() {
34
34
  this.workers.forEach((e) => e.terminate());
35
35
  }
36
36
  flush() {
37
37
  for (; this.queue.length > 0 && this.idle.length > 0; ) {
38
- const { task: e, resolve: t } = this.queue.shift(), s = this.idle.pop();
39
- this.pending.set(e.taskId, t), s.postMessage(e);
38
+ const { task: e, resolve: s } = this.queue.shift(), t = this.idle.pop();
39
+ this.pending.set(e.taskId, s), t.postMessage(e);
40
40
  }
41
41
  }
42
42
  }
43
43
  export {
44
- u as WorkerPool
44
+ g as WorkerPool
45
45
  };
46
46
  //# sourceMappingURL=workerPool.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"workerPool.mjs","sources":["../../../src/openapi/analyzerModule/workerPool.ts"],"sourcesContent":["import os from 'os'\nimport { Worker } from 'worker_threads'\n\nimport { EndpointData } from '../types'\nimport { SectionTiming } from './parseEndpoint'\n\nexport type WorkerTask = {\n\ttaskId: string\n\ttsconfigPath: string\n\tsourceFilePath: string\n\trouterName: string\n\tendpointIndex: number\n}\n\nexport type WorkerResultSuccess = {\n\ttaskId: string\n\tendpoint: EndpointData\n\tsectionTimings: SectionTiming[]\n\ttiming: number\n}\n\nexport type WorkerResultError = {\n\ttaskId: string\n\terror: string\n}\n\nexport type WorkerResult = WorkerResultSuccess | WorkerResultError\n\nexport class WorkerPool {\n\tprivate workers: Worker[]\n\tprivate idle: Worker[]\n\tprivate queue: Array<{ task: WorkerTask; resolve: (r: WorkerResult) => void }> = []\n\tprivate pending = new Map<string, (r: WorkerResult) => void>()\n\n\tconstructor(workerUrl: URL) {\n\t\tconst size = Math.max(1, Math.min(os.cpus().length - 1, 8))\n\t\tthis.workers = Array.from({ length: size }, () => {\n\t\t\tconst worker = new Worker(workerUrl)\n\t\t\tworker.on('message', (result: WorkerResult) => {\n\t\t\t\tconst resolve = this.pending.get(result.taskId)\n\t\t\t\tif (resolve) {\n\t\t\t\t\tthis.pending.delete(result.taskId)\n\t\t\t\t\tresolve(result)\n\t\t\t\t}\n\t\t\t\tthis.idle.push(worker)\n\t\t\t\tthis.flush()\n\t\t\t})\n\t\t\tworker.on('error', (err) => {\n\t\t\t\t// Find any pending task for this worker and reject it\n\t\t\t\t// (worker crashed — shouldn't happen, but handle gracefully)\n\t\t\t\tfor (const [taskId, resolve] of this.pending) {\n\t\t\t\t\tresolve({ taskId, error: String(err) })\n\t\t\t\t\tthis.pending.delete(taskId)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tthis.idle.push(worker)\n\t\t\t\tthis.flush()\n\t\t\t})\n\t\t\treturn worker\n\t\t})\n\t\tthis.idle = [...this.workers]\n\t}\n\n\trun(task: WorkerTask): Promise<WorkerResult> {\n\t\treturn new Promise((resolve) => {\n\t\t\tif (this.idle.length > 0) {\n\t\t\t\tconst worker = this.idle.pop()!\n\t\t\t\tthis.pending.set(task.taskId, resolve)\n\t\t\t\tworker.postMessage(task)\n\t\t\t} else {\n\t\t\t\tthis.queue.push({ task, resolve })\n\t\t\t}\n\t\t})\n\t}\n\n\trunAll(tasks: WorkerTask[]): Promise<WorkerResult[]> {\n\t\treturn Promise.all(tasks.map((t) => this.run(t)))\n\t}\n\n\tterminate() {\n\t\tthis.workers.forEach((w) => w.terminate())\n\t}\n\n\tprivate flush() {\n\t\twhile (this.queue.length > 0 && this.idle.length > 0) {\n\t\t\tconst { task, resolve } = this.queue.shift()!\n\t\t\tconst worker = this.idle.pop()!\n\t\t\tthis.pending.set(task.taskId, resolve)\n\t\t\tworker.postMessage(task)\n\t\t}\n\t}\n}\n"],"names":["WorkerPool","workerUrl","size","os","worker","Worker","result","resolve","err","taskId","task","tasks","w"],"mappings":";;AA4BO,MAAMA,EAAW;AAAA,EAMvB,YAAYC,GAAgB;AAH5B,SAAQ,QAAyE,CAAC,GAC1E,KAAA,8BAAc,IAAuC;AAG5D,UAAMC,IAAO,KAAK,IAAI,GAAG,KAAK,IAAIC,EAAG,KAAA,EAAO,SAAS,GAAG,CAAC,CAAC;AAC1D,SAAK,UAAU,MAAM,KAAK,EAAE,QAAQD,EAAA,GAAQ,MAAM;AAC3C,YAAAE,IAAS,IAAIC,EAAOJ,CAAS;AAC5B,aAAAG,EAAA,GAAG,WAAW,CAACE,MAAyB;AAC9C,cAAMC,IAAU,KAAK,QAAQ,IAAID,EAAO,MAAM;AAC9C,QAAIC,MACE,KAAA,QAAQ,OAAOD,EAAO,MAAM,GACjCC,EAAQD,CAAM,IAEV,KAAA,KAAK,KAAKF,CAAM,GACrB,KAAK,MAAM;AAAA,MAAA,CACX,GACMA,EAAA,GAAG,SAAS,CAACI,MAAQ;AAG3B,mBAAW,CAACC,GAAQF,CAAO,KAAK,KAAK,SAAS;AAC7C,UAAAA,EAAQ,EAAE,QAAAE,GAAQ,OAAO,OAAOD,CAAG,GAAG,GACjC,KAAA,QAAQ,OAAOC,CAAM;AAC1B;AAAA,QAAA;AAEI,aAAA,KAAK,KAAKL,CAAM,GACrB,KAAK,MAAM;AAAA,MAAA,CACX,GACMA;AAAA,IAAA,CACP,GACD,KAAK,OAAO,CAAC,GAAG,KAAK,OAAO;AAAA,EAAA;AAAA,EAG7B,IAAIM,GAAyC;AACrC,WAAA,IAAI,QAAQ,CAACH,MAAY;AAC3B,UAAA,KAAK,KAAK,SAAS,GAAG;AACnB,cAAAH,IAAS,KAAK,KAAK,IAAI;AAC7B,aAAK,QAAQ,IAAIM,EAAK,QAAQH,CAAO,GACrCH,EAAO,YAAYM,CAAI;AAAA,MAAA;AAEvB,aAAK,MAAM,KAAK,EAAE,MAAAA,GAAM,SAAAH,GAAS;AAAA,IAClC,CACA;AAAA,EAAA;AAAA,EAGF,OAAOI,GAA8C;AAC7C,WAAA,QAAQ,IAAIA,EAAM,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC;AAAA,EAAA;AAAA,EAGjD,YAAY;AACX,SAAK,QAAQ,QAAQ,CAACC,MAAMA,EAAE,WAAW;AAAA,EAAA;AAAA,EAGlC,QAAQ;AACf,WAAO,KAAK,MAAM,SAAS,KAAK,KAAK,KAAK,SAAS,KAAG;AACrD,YAAM,EAAE,MAAAF,GAAM,SAAAH,EAAA,IAAY,KAAK,MAAM,MAAM,GACrCH,IAAS,KAAK,KAAK,IAAI;AAC7B,WAAK,QAAQ,IAAIM,EAAK,QAAQH,CAAO,GACrCH,EAAO,YAAYM,CAAI;AAAA,IAAA;AAAA,EACxB;AAEF;"}
1
+ {"version":3,"file":"workerPool.mjs","sources":["../../../src/openapi/analyzerModule/workerPool.ts"],"sourcesContent":["import os from 'os'\nimport { Worker } from 'worker_threads'\n\nimport { EndpointData } from '../types'\nimport { SectionTiming } from './parseEndpoint'\n\nexport type WorkerTask = {\n\ttaskId: string\n\ttsconfigPath: string\n\tsourceFilePath: string\n\trouterNames: string[]\n\tfilterEndpointPaths?: string[]\n}\n\nexport type EndpointTiming = {\n\tmethod: string\n\tpath: string\n\ttiming: number\n\tsectionTimings: SectionTiming[]\n}\n\nexport type WorkerResultSuccess = {\n\ttaskId: string\n\tendpoints: EndpointData[]\n\tendpointTimings: EndpointTiming[]\n}\n\nexport type WorkerResultError = {\n\ttaskId: string\n\terror: string\n}\n\nexport type WorkerResult = WorkerResultSuccess | WorkerResultError\n\nexport class WorkerPool {\n\tprivate workers: Worker[]\n\tprivate idle: Worker[]\n\tprivate queue: Array<{ task: WorkerTask; resolve: (r: WorkerResult) => void }> = []\n\tprivate pending = new Map<string, (r: WorkerResult) => void>()\n\n\tconstructor(workerUrl: URL, maxWorkers?: number) {\n\t\tconst cpuBound = Math.max(1, Math.min(os.cpus().length - 1, 8))\n\t\t// Never spin up more workers than there are files to analyze: each extra worker would\n\t\t// just pay the ts-morph Project cold-start (full TS program load + type-checker warmup)\n\t\t// for nothing while contending for CPU with the workers that actually have work.\n\t\tconst size = maxWorkers === undefined ? cpuBound : Math.max(1, Math.min(cpuBound, maxWorkers))\n\t\tthis.workers = Array.from({ length: size }, () => {\n\t\t\tconst worker = new Worker(workerUrl)\n\t\t\tworker.on('message', (result: WorkerResult) => {\n\t\t\t\tconst resolve = this.pending.get(result.taskId)\n\t\t\t\tif (resolve) {\n\t\t\t\t\tthis.pending.delete(result.taskId)\n\t\t\t\t\tresolve(result)\n\t\t\t\t}\n\t\t\t\tthis.idle.push(worker)\n\t\t\t\tthis.flush()\n\t\t\t})\n\t\t\tworker.on('error', (err) => {\n\t\t\t\t// Find any pending task for this worker and reject it\n\t\t\t\t// (worker crashed — shouldn't happen, but handle gracefully)\n\t\t\t\tfor (const [taskId, resolve] of this.pending) {\n\t\t\t\t\tresolve({ taskId, error: String(err) })\n\t\t\t\t\tthis.pending.delete(taskId)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tthis.idle.push(worker)\n\t\t\t\tthis.flush()\n\t\t\t})\n\t\t\treturn worker\n\t\t})\n\t\tthis.idle = [...this.workers]\n\t}\n\n\trun(task: WorkerTask): Promise<WorkerResult> {\n\t\treturn new Promise((resolve) => {\n\t\t\tif (this.idle.length > 0) {\n\t\t\t\tconst worker = this.idle.pop()!\n\t\t\t\tthis.pending.set(task.taskId, resolve)\n\t\t\t\tworker.postMessage(task)\n\t\t\t} else {\n\t\t\t\tthis.queue.push({ task, resolve })\n\t\t\t}\n\t\t})\n\t}\n\n\trunAll(tasks: WorkerTask[]): Promise<WorkerResult[]> {\n\t\treturn Promise.all(tasks.map((t) => this.run(t)))\n\t}\n\n\tterminate() {\n\t\tthis.workers.forEach((w) => w.terminate())\n\t}\n\n\tprivate flush() {\n\t\twhile (this.queue.length > 0 && this.idle.length > 0) {\n\t\t\tconst { task, resolve } = this.queue.shift()!\n\t\t\tconst worker = this.idle.pop()!\n\t\t\tthis.pending.set(task.taskId, resolve)\n\t\t\tworker.postMessage(task)\n\t\t}\n\t}\n}\n"],"names":["WorkerPool","workerUrl","maxWorkers","cpuBound","os","size","worker","Worker","result","resolve","err","taskId","task","tasks","t","w"],"mappings":";;AAkCO,MAAMA,EAAW;AAAA,EAMvB,YAAYC,GAAgBC,GAAqB;AAHjD,SAAQ,QAAyE,CAAC,GAC1E,KAAA,8BAAc,IAAuC;AAG5D,UAAMC,IAAW,KAAK,IAAI,GAAG,KAAK,IAAIC,EAAG,KAAA,EAAO,SAAS,GAAG,CAAC,CAAC,GAIxDC,IAAOH,MAAe,SAAYC,IAAW,KAAK,IAAI,GAAG,KAAK,IAAIA,GAAUD,CAAU,CAAC;AAC7F,SAAK,UAAU,MAAM,KAAK,EAAE,QAAQG,EAAA,GAAQ,MAAM;AAC3C,YAAAC,IAAS,IAAIC,EAAON,CAAS;AAC5B,aAAAK,EAAA,GAAG,WAAW,CAACE,MAAyB;AAC9C,cAAMC,IAAU,KAAK,QAAQ,IAAID,EAAO,MAAM;AAC9C,QAAIC,MACE,KAAA,QAAQ,OAAOD,EAAO,MAAM,GACjCC,EAAQD,CAAM,IAEV,KAAA,KAAK,KAAKF,CAAM,GACrB,KAAK,MAAM;AAAA,MAAA,CACX,GACMA,EAAA,GAAG,SAAS,CAACI,MAAQ;AAG3B,mBAAW,CAACC,GAAQF,CAAO,KAAK,KAAK,SAAS;AAC7C,UAAAA,EAAQ,EAAE,QAAAE,GAAQ,OAAO,OAAOD,CAAG,GAAG,GACjC,KAAA,QAAQ,OAAOC,CAAM;AAC1B;AAAA,QAAA;AAEI,aAAA,KAAK,KAAKL,CAAM,GACrB,KAAK,MAAM;AAAA,MAAA,CACX,GACMA;AAAA,IAAA,CACP,GACD,KAAK,OAAO,CAAC,GAAG,KAAK,OAAO;AAAA,EAAA;AAAA,EAG7B,IAAIM,GAAyC;AACrC,WAAA,IAAI,QAAQ,CAACH,MAAY;AAC3B,UAAA,KAAK,KAAK,SAAS,GAAG;AACnB,cAAAH,IAAS,KAAK,KAAK,IAAI;AAC7B,aAAK,QAAQ,IAAIM,EAAK,QAAQH,CAAO,GACrCH,EAAO,YAAYM,CAAI;AAAA,MAAA;AAEvB,aAAK,MAAM,KAAK,EAAE,MAAAA,GAAM,SAAAH,GAAS;AAAA,IAClC,CACA;AAAA,EAAA;AAAA,EAGF,OAAOI,GAA8C;AAC7C,WAAA,QAAQ,IAAIA,EAAM,IAAI,CAACC,MAAM,KAAK,IAAIA,CAAC,CAAC,CAAC;AAAA,EAAA;AAAA,EAGjD,YAAY;AACX,SAAK,QAAQ,QAAQ,CAACC,MAAMA,EAAE,WAAW;AAAA,EAAA;AAAA,EAGlC,QAAQ;AACf,WAAO,KAAK,MAAM,SAAS,KAAK,KAAK,KAAK,SAAS,KAAG;AACrD,YAAM,EAAE,MAAAH,GAAM,SAAAH,EAAA,IAAY,KAAK,MAAM,MAAM,GACrCH,IAAS,KAAK,KAAK,IAAI;AAC7B,WAAK,QAAQ,IAAIM,EAAK,QAAQH,CAAO,GACrCH,EAAO,YAAYM,CAAI;AAAA,IAAA;AAAA,EACxB;AAEF;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "moonflower",
3
- "version": "1.5.0",
3
+ "version": "1.5.1",
4
4
  "description": "",
5
5
  "author": "tenebrie",
6
6
  "license": "MIT",
@@ -213,45 +213,24 @@ export const analyzeMultipleSourceFiles = async (
213
213
  return cached.flatMap((f) => f.endpoints)
214
214
  }
215
215
 
216
- // Build one task per endpoint across all uncached files
217
- const OPERATIONS = ['get', 'post', 'put', 'delete', 'del', 'patch']
218
- const operationsPattern = OPERATIONS.join('|')
219
-
216
+ // Build one task per uncached file. Each worker analyzes a whole file in a single pass, so it
217
+ // pays the ts-morph Project cold-start (full TS program load + type-checker warmup) once and
218
+ // reuses the warmed-up checker for every endpoint in that file.
220
219
  type FileTask = { task: WorkerTask; fileName: string }
221
- const allTasks: FileTask[] = []
220
+ const allTasks: FileTask[] = uncached.map(({ file }) => ({
221
+ fileName: file.fileName,
222
+ task: {
223
+ taskId: crypto.randomUUID(),
224
+ tsconfigPath: config.tsconfigPath,
225
+ sourceFilePath: file.sourceFile.getFilePath(),
226
+ routerNames: file.routers.named,
227
+ filterEndpointPaths,
228
+ },
229
+ }))
230
+
231
+ // Dispatch all tasks to the worker pool, capped at one worker per file.
232
+ const pool = new WorkerPool(resolveWorkerUrl(), allTasks.length)
222
233
 
223
- for (const { file } of uncached) {
224
- for (const routerName of file.routers.named) {
225
- const routerPattern = new RegExp(`${routerName}\\.(?:${operationsPattern})`)
226
- let endpointIndex = 0
227
- file.sourceFile.forEachChild((node) => {
228
- const nodeText = node.getText()
229
- if (routerPattern.test(nodeText)) {
230
- if (
231
- !filterEndpointPaths ||
232
- filterEndpointPaths.some((p) => resolveEndpointPath(node)?.includes(p))
233
- ) {
234
- allTasks.push({
235
- fileName: file.fileName,
236
- task: {
237
- taskId: crypto.randomUUID(),
238
- tsconfigPath: config.tsconfigPath,
239
- sourceFilePath: file.sourceFile.getFilePath(),
240
- routerName,
241
- endpointIndex,
242
- },
243
- })
244
- }
245
- endpointIndex++
246
- }
247
- })
248
- }
249
- }
250
-
251
- // Dispatch all tasks to the worker pool
252
- const pool = new WorkerPool(resolveWorkerUrl())
253
-
254
- type EndpointTiming = { method: string; path: string; timing: number; sectionTimings: SectionTiming[] }
255
234
  type FileResult = {
256
235
  endpoints: EndpointData[]
257
236
  fileName: string
@@ -266,7 +245,7 @@ export const analyzeMultipleSourceFiles = async (
266
245
  pool.terminate()
267
246
  }
268
247
 
269
- // Group results by file
248
+ // Each result maps 1:1 to a file task.
270
249
  const byFile = new Map<string, FileResult>()
271
250
  for (const { file } of uncached) {
272
251
  byFile.set(file.fileName, { endpoints: [], fileName: file.fileName, timing: 0, endpointTimings: [] })
@@ -282,14 +261,9 @@ export const analyzeMultipleSourceFiles = async (
282
261
  continue
283
262
  }
284
263
 
285
- fileResult.endpoints.push(result.endpoint)
286
- fileResult.timing += result.timing
287
- fileResult.endpointTimings.push({
288
- method: result.endpoint.method,
289
- path: result.endpoint.path,
290
- timing: result.timing,
291
- sectionTimings: result.sectionTimings,
292
- })
264
+ fileResult.endpoints = result.endpoints
265
+ fileResult.endpointTimings = result.endpointTimings
266
+ fileResult.timing = result.endpointTimings.reduce((sum, t) => sum + t.timing, 0)
293
267
  }
294
268
 
295
269
  // Write cache for each uncached file
@@ -1,8 +1,10 @@
1
- import { Node, Project, ts } from 'ts-morph'
1
+ import { Project } from 'ts-morph'
2
2
  import { parentPort } from 'worker_threads'
3
3
 
4
+ import { EndpointData } from '../types'
5
+ import { resolveEndpointPath } from './nodeParsers'
4
6
  import { parseEndpoint } from './parseEndpoint'
5
- import { WorkerResult, WorkerTask } from './workerPool'
7
+ import { EndpointTiming, WorkerResult, WorkerTask } from './workerPool'
6
8
 
7
9
  const OPERATIONS = ['get', 'post', 'put', 'delete', 'del', 'patch']
8
10
  const OPERATIONS_PATTERN = OPERATIONS.join('|')
@@ -30,37 +32,39 @@ parentPort!.on('message', (task: WorkerTask) => {
30
32
  sourceFile = proj.addSourceFileAtPath(task.sourceFilePath)
31
33
  }
32
34
 
33
- const routerPattern = new RegExp(`${task.routerName}\\.(?:${OPERATIONS_PATTERN})`)
34
- let index = 0
35
- let targetNode: Node<ts.Node> | undefined
35
+ const endpoints: EndpointData[] = []
36
+ const endpointTimings: EndpointTiming[] = []
36
37
 
37
- sourceFile.forEachChild((node) => {
38
- if (targetNode) return
39
- if (routerPattern.test(node.getText())) {
40
- if (index === task.endpointIndex) {
41
- targetNode = node
38
+ task.routerNames.forEach((routerName) => {
39
+ const routerPattern = new RegExp(`${routerName}\\.(?:${OPERATIONS_PATTERN})`)
40
+ sourceFile!.forEachChild((node) => {
41
+ if (!routerPattern.test(node.getText())) {
42
+ return
42
43
  }
43
- index++
44
- }
45
- })
46
44
 
47
- if (!targetNode) {
48
- const result: WorkerResult = {
49
- taskId: task.taskId,
50
- error: `Endpoint not found: routerName=${task.routerName} index=${task.endpointIndex} in ${task.sourceFilePath}`,
51
- }
52
- parentPort!.postMessage(result)
53
- return
54
- }
45
+ if (task.filterEndpointPaths) {
46
+ const endpointPath = resolveEndpointPath(node) ?? ''
47
+ if (!task.filterEndpointPaths.some((p) => endpointPath.includes(p))) {
48
+ return
49
+ }
50
+ }
55
51
 
56
- const t1 = performance.now()
57
- const { endpoint, sectionTimings } = parseEndpoint(targetNode, task.sourceFilePath)
52
+ const t1 = performance.now()
53
+ const { endpoint, sectionTimings } = parseEndpoint(node, task.sourceFilePath)
54
+ endpointTimings.push({
55
+ method: endpoint.method,
56
+ path: endpoint.path,
57
+ timing: performance.now() - t1,
58
+ sectionTimings,
59
+ })
60
+ endpoints.push(endpoint)
61
+ })
62
+ })
58
63
 
59
64
  const result: WorkerResult = {
60
65
  taskId: task.taskId,
61
- endpoint,
62
- sectionTimings,
63
- timing: performance.now() - t1,
66
+ endpoints,
67
+ endpointTimings,
64
68
  }
65
69
  parentPort!.postMessage(result)
66
70
  } catch (err) {
@@ -8,15 +8,21 @@ export type WorkerTask = {
8
8
  taskId: string
9
9
  tsconfigPath: string
10
10
  sourceFilePath: string
11
- routerName: string
12
- endpointIndex: number
11
+ routerNames: string[]
12
+ filterEndpointPaths?: string[]
13
+ }
14
+
15
+ export type EndpointTiming = {
16
+ method: string
17
+ path: string
18
+ timing: number
19
+ sectionTimings: SectionTiming[]
13
20
  }
14
21
 
15
22
  export type WorkerResultSuccess = {
16
23
  taskId: string
17
- endpoint: EndpointData
18
- sectionTimings: SectionTiming[]
19
- timing: number
24
+ endpoints: EndpointData[]
25
+ endpointTimings: EndpointTiming[]
20
26
  }
21
27
 
22
28
  export type WorkerResultError = {
@@ -32,8 +38,12 @@ export class WorkerPool {
32
38
  private queue: Array<{ task: WorkerTask; resolve: (r: WorkerResult) => void }> = []
33
39
  private pending = new Map<string, (r: WorkerResult) => void>()
34
40
 
35
- constructor(workerUrl: URL) {
36
- const size = Math.max(1, Math.min(os.cpus().length - 1, 8))
41
+ constructor(workerUrl: URL, maxWorkers?: number) {
42
+ const cpuBound = Math.max(1, Math.min(os.cpus().length - 1, 8))
43
+ // Never spin up more workers than there are files to analyze: each extra worker would
44
+ // just pay the ts-morph Project cold-start (full TS program load + type-checker warmup)
45
+ // for nothing while contending for CPU with the workers that actually have work.
46
+ const size = maxWorkers === undefined ? cpuBound : Math.max(1, Math.min(cpuBound, maxWorkers))
37
47
  this.workers = Array.from({ length: size }, () => {
38
48
  const worker = new Worker(workerUrl)
39
49
  worker.on('message', (result: WorkerResult) => {