moonflower 1.5.2 → 1.5.3

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 q=require("crypto"),I=require("fs"),U=require("path"),K=require("url"),S=require("ts-morph"),g=require("../../utils/logger.cjs"),P=require("../discoveryModule/discoverImports/discoverImports.cjs"),_=require("../discoveryModule/discoverRouterFiles/discoverRouterFiles.cjs"),W=require("../discoveryModule/discoverRouters/discoverRouters.cjs"),H=require("../manager/OpenApiManager.cjs"),D=require("./getSourceFileTimestamp.cjs"),A=require("./nodeParsers.cjs"),B=require("./parseEndpoint.cjs"),R=require("./parseExposedModels.cjs"),k=require("./sourceFileCache.cjs"),V=require("./workerPool.cjs");var M=typeof document<"u"?document.currentScript:null;function Y(i){const n=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(i){for(const a in i)if(a!=="default"){const d=Object.getOwnPropertyDescriptor(i,a);Object.defineProperty(n,a,d.get?d:{enumerable:!0,get:()=>i[a]})}}return n.default=i,Object.freeze(n)}const E=Y(U);function G(){const i=["./analyzerWorker.mjs","./analyzerWorker.test.mjs"];for(const n of i){const a=new URL(n,typeof document>"u"?require("url").pathToFileURL(__filename).href:M&&M.tagName.toUpperCase()==="SCRIPT"&&M.src||new URL("openapi/analyzerModule/analyzerModule.cjs",document.baseURI).href);if(I.existsSync(K.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 J=2,Q=async({logLevel:i,tsconfigPath:n,sourceFilePaths:a,sourceFileDiscovery:d,incremental:p,profiling:f="stats"})=>{const t=H.OpenApiManager.getInstance();if(t.isReady())return;i&&g.Logger.setLevel(i),g.Logger.info("Preparing OpenAPI spec");const l=new S.Project({tsConfigFilePath:E.resolve(n),skipFileDependencyResolution:!0}),{explicitRouters:u,discoveredRouterFiles:e,allSourceFiles:r}=(()=>{const m=(a??[]).map(h=>E.resolve(h)).map(h=>l.getSourceFileOrThrow(h)),O=m.flatMap(h=>({fileName:h.getFilePath(),sourceFile:h,routers:W.discoverRouters(h)})),{discoveredRouterFiles:$,discoveredSourceFiles:b}=(()=>{if(d===!1)return{discoveredRouterFiles:[],discoveredSourceFiles:[]};const h=performance.now(),x=_.discoverRouterFiles({targetPath:typeof d=="object"?d.rootPath:".",tsConfigPath:n});return f!=="off"&&g.Logger.info(`File discovery took ${Math.round(performance.now()-h)}ms`),x})(),j=m.reduce((h,x)=>h.some(z=>z.getFilePath()===x.getFilePath())?h:h.concat(x),b);return{explicitRouters:O,discoveredRouterFiles:$,allSourceFiles:j}})(),o=u.reduce((c,F)=>c.some(m=>m.fileName===F.fileName)?c:c.concat(F),e),s=r.flatMap(c=>v(c)).filter(c=>!!c);s.length>0&&s[0]&&t.setHeader(s[0]);const y=r.flatMap(c=>w(c));t.setExposedModels(y);const T=typeof p=="object"&&p.cachePath?p.cachePath:E.resolve(process.cwd(),"node_modules",".cache","moonflower"),N=await C(o,{incremental:p!==!1,cachePath:T,timestampCache:{},profiling:f,tsconfigPath:E.resolve(n)});t.setStats({discoveredRouterFiles:e.map(c=>({path:c.fileName,routers:c.routers.named.map(F=>({name:F,endpoints:N.filter(m=>m.sourceFilePath===c.fileName).map(m=>`${m.method.toUpperCase()} ${m.path}`)}))})),explicitRouterFiles:u.map(c=>({path:c.fileName,routers:c.routers.named.map(F=>({name:F,endpoints:N.filter(m=>m.sourceFilePath===c.fileName).map(m=>`${m.method.toUpperCase()} ${m.path}`)}))}))}),t.setEndpoints(N),t.markAsReady()},C=async(i,n,a)=>{const d=n.profiling??"stats",p=performance.now(),f=[],t=[];for(const e of i){const r=D.getSourceFileTimestamp(e.sourceFile,n.timestampCache),o=n.incremental?k.SourceFileCache.getCachedResults(e.sourceFile,r,n.cachePath):null;o?(g.Logger.debug(`[${e.fileName}] Found cached results`),f.push({endpoints:o.endpoints,fileName:e.fileName,timing:0,endpointTimings:[]})):t.push({file:e,timestamp:r})}if(t.length===0)return d!=="off"&&g.Logger.info(`Router analysis took ${Math.round(performance.now()-p)}ms`),f.flatMap(e=>e.endpoints);const l=new Map;for(const{file:e}of t)l.set(e.fileName,{endpoints:[],fileName:e.fileName,timing:0,endpointTimings:[]});if(t.length<=J)for(const{file:e}of t){const{endpoints:r,endpointTimings:o}=L(e),s=l.get(e.fileName);s.endpoints=r,s.endpointTimings=o,s.timing=o.reduce((y,T)=>y+T.timing,0)}else{const e=t.map(({file:s})=>({fileName:s.fileName,task:{taskId:q.randomUUID(),tsconfigPath:n.tsconfigPath,sourceFilePath:s.sourceFile.getFilePath(),routerNames:s.routers.named,filterEndpointPaths:a}})),r=new V.WorkerPool(G(),e.length);let o;try{o=await r.runAll(e.map(s=>s.task))}finally{r.terminate()}for(let s=0;s<o.length;s++){const y=o[s],T=e[s].fileName,N=l.get(T);if("error"in y){g.Logger.error(`[${T}] Worker error: ${y.error}`);continue}N.endpoints=y.endpoints,N.endpointTimings=y.endpointTimings,N.timing=y.endpointTimings.reduce((c,F)=>c+F.timing,0)}}for(const{file:e,timestamp:r}of t){const o=l.get(e.fileName);o.endpoints.length>0&&k.SourceFileCache.cacheResults(e.sourceFile,r,n.cachePath,o.endpoints)}const u=[...f,...Array.from(l.values())];return d!=="off"&&g.Logger.info(`Router analysis took ${Math.round(performance.now()-p)}ms`),d==="stats"?u.map(e=>({fileName:e.fileName,timeTaken:e.timing})).sort((e,r)=>r.timeTaken-e.timeTaken).filter(e=>e.timeTaken>500).forEach(e=>{g.Logger.info(`- [${e.fileName}] Took ${Math.round(e.timeTaken)}ms to analyze`)}):d==="debug"&&u.map(e=>({fileName:e.fileName,timeTaken:e.timing,endpointTimings:e.endpointTimings})).sort((e,r)=>r.timeTaken-e.timeTaken).forEach(e=>{g.Logger.info(`- [${e.fileName}] Took ${Math.round(e.timeTaken)}ms to analyze`),e.endpointTimings.sort((r,o)=>o.timing-r.timing).forEach(r=>{g.Logger.info(` - ${r.method} ${r.path} (${Math.round(r.timing)}ms)`),r.sectionTimings.filter(o=>o.timing>=1).sort((o,s)=>s.timing-o.timing).forEach(o=>{g.Logger.info(` - ${o.section}: ${Math.round(o.timing)}ms`)})})}),u.flatMap(e=>e.endpoints)},L=(i,n)=>{const a=[],d=[],f=["get","post","put","delete","del","patch"].join("|");return i.routers.named.forEach(t=>{const l=new RegExp(`${t}\\.(?:${f})`);i.sourceFile.forEachChild(u=>{const e=u.getText();if(l.test(e)){A.resolveEndpointPath(u);const r=performance.now(),{endpoint:o,sectionTimings:s}=B.parseEndpoint(u,i.fileName);d.push({method:o.method,path:o.path,timing:performance.now()-r,sectionTimings:s}),a.push(o)}})}),{endpoints:a,endpointTimings:d}},v=i=>{const n=P.discoverImportedName({sourceFile:i,originalName:"useApiHeader"});if(!n)return null;const a=i.forEachChildAsArray().filter(t=>t.isKind(S.SyntaxKind.ExpressionStatement)).find(t=>n&&t.getText().startsWith(n));if(!a)return null;const d=a.getFirstDescendantByKindOrThrow(S.SyntaxKind.ObjectLiteralExpression),p=A.getValuesOfObjectLiteral(d),f=t=>typeof t=="string"||Array.isArray(t)&&t.every(l=>typeof l=="string")?t:t.reduce((l,u)=>typeof u=="string"?l:{...l,[u.identifier]:f(u.value)},{});return f(p)},w=i=>{const n=[],a=P.discoverImportedName({sourceFile:i,originalName:"useExposeApiModel"}),d=P.discoverImportedName({sourceFile:i,originalName:"useExposeNamedApiModels"});return i.forEachChildAsArray().filter(p=>p.isKind(S.SyntaxKind.ExpressionStatement)).map(p=>{if(a&&p.getText().startsWith(a)){const l=(p.getFirstChild()?.getChildrenOfKind(S.SyntaxKind.SyntaxList)||[])[0].getFirstChild();if(!l)return;n.push(R.parseExposedModel(l));return}if(d&&p.getText().startsWith(d)){const l=(p.getFirstChild()?.getChildrenOfKind(S.SyntaxKind.SyntaxList)||[])[0].getFirstChild();if(!l)return;R.parseNamedExposedModels(l).forEach(e=>n.push(e))}}),n};exports.analyzeMultipleSourceFiles=C;exports.analyzeSourceFileApiHeader=v;exports.analyzeSourceFileEndpoints=L;exports.analyzeSourceFileExposedModels=w;exports.prepareOpenApiSpec=Q;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const q=require("crypto"),I=require("fs"),U=require("path"),K=require("url"),S=require("ts-morph"),y=require("../../utils/logger.cjs"),R=require("../discoveryModule/discoverImports/discoverImports.cjs"),_=require("../discoveryModule/discoverRouterFiles/discoverRouterFiles.cjs"),W=require("../discoveryModule/discoverRouters/discoverRouters.cjs"),H=require("../manager/OpenApiManager.cjs"),D=require("./getSourceFileTimestamp.cjs"),w=require("./nodeParsers.cjs"),B=require("./parseEndpoint.cjs"),k=require("./parseExposedModels.cjs"),C=require("./sourceFileCache.cjs"),V=require("./workerPool.cjs");var P=typeof document<"u"?document.currentScript:null;function Y(r){const n=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(r){for(const a in r)if(a!=="default"){const l=Object.getOwnPropertyDescriptor(r,a);Object.defineProperty(n,a,l.get?l:{enumerable:!0,get:()=>r[a]})}}return n.default=r,Object.freeze(n)}const E=Y(U);function G(){const r=["./analyzerWorker.mjs","./analyzerWorker.test.mjs"];for(const n of r){const a=new URL(n,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(I.existsSync(K.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 J=2,Q=async({logLevel:r,tsconfigPath:n,sourceFilePaths:a,sourceFileDiscovery:l,incremental:d,profiling:m="stats"})=>{const t=H.OpenApiManager.getInstance();if(t.isReady())return;r&&y.Logger.setLevel(r),y.Logger.info("Preparing OpenAPI spec");const p=new S.Project({tsConfigFilePath:E.resolve(n),skipFileDependencyResolution:!0}),{explicitRouters:f,discoveredRouterFiles:g,allSourceFiles:T}=(()=>{const u=(a??[]).map(h=>E.resolve(h)).map(h=>p.getSourceFileOrThrow(h)),M=u.flatMap(h=>({fileName:h.getFilePath(),sourceFile:h,routers:W.discoverRouters(h)})),{discoveredRouterFiles:$,discoveredSourceFiles:b}=(()=>{if(l===!1)return{discoveredRouterFiles:[],discoveredSourceFiles:[]};const h=performance.now(),x=_.discoverRouterFiles({targetPath:typeof l=="object"?l.rootPath:".",tsConfigPath:n});return m!=="off"&&y.Logger.info(`File discovery took ${Math.round(performance.now()-h)}ms`),x})(),j=u.reduce((h,x)=>h.some(z=>z.getFilePath()===x.getFilePath())?h:h.concat(x),b);return{explicitRouters:M,discoveredRouterFiles:$,allSourceFiles:j}})(),e=f.reduce((i,N)=>i.some(u=>u.fileName===N.fileName)?i:i.concat(N),g),o=T.flatMap(i=>v(i)).filter(i=>!!i);o.length>0&&o[0]&&t.setHeader(o[0]);const s=T.flatMap(i=>O(i));t.setExposedModels(s);const c=typeof d=="object"&&d.cachePath?d.cachePath:E.resolve(process.cwd(),"node_modules",".cache","moonflower"),F=await A(e,{incremental:d!==!1,cachePath:c,timestampCache:{},profiling:m,tsconfigPath:E.resolve(n)});t.setStats({discoveredRouterFiles:g.map(i=>({path:i.fileName,routers:i.routers.named.map(N=>({name:N,endpoints:F.filter(u=>u.sourceFilePath===i.fileName).map(u=>`${u.method.toUpperCase()} ${u.path}`)}))})),explicitRouterFiles:f.map(i=>({path:i.fileName,routers:i.routers.named.map(N=>({name:N,endpoints:F.filter(u=>u.sourceFilePath===i.fileName).map(u=>`${u.method.toUpperCase()} ${u.path}`)}))}))}),t.setEndpoints(F),t.markAsReady()},A=async(r,n,a)=>{const l=n.profiling??"stats",d=performance.now(),m=[],t=[],p=performance.now(),f=new Map;for(const e of r){const o=D.getSourceFileTimestamp(e.sourceFile,n.timestampCache,f),s=n.incremental?C.SourceFileCache.getCachedResults(e.sourceFile,o,n.cachePath):null;s?(y.Logger.debug(`[${e.fileName}] Found cached results`),m.push({endpoints:s.endpoints,fileName:e.fileName,timing:0,endpointTimings:[]})):t.push({file:e,timestamp:o})}if(l!=="off"&&y.Logger.info(`Cache freshness check took ${Math.round(performance.now()-p)}ms`),t.length===0)return l!=="off"&&y.Logger.info(`Router analysis took ${Math.round(performance.now()-d)}ms`),m.flatMap(e=>e.endpoints);const g=new Map;for(const{file:e}of t)g.set(e.fileName,{endpoints:[],fileName:e.fileName,timing:0,endpointTimings:[]});if(t.length<=J)for(const{file:e}of t){const{endpoints:o,endpointTimings:s}=L(e),c=g.get(e.fileName);c.endpoints=o,c.endpointTimings=s,c.timing=s.reduce((F,i)=>F+i.timing,0)}else{const e=t.map(({file:c})=>({fileName:c.fileName,task:{taskId:q.randomUUID(),tsconfigPath:n.tsconfigPath,sourceFilePath:c.sourceFile.getFilePath(),routerNames:c.routers.named,filterEndpointPaths:a}})),o=new V.WorkerPool(G(),e.length);let s;try{s=await o.runAll(e.map(c=>c.task))}finally{o.terminate()}for(let c=0;c<s.length;c++){const F=s[c],i=e[c].fileName,N=g.get(i);if("error"in F){y.Logger.error(`[${i}] Worker error: ${F.error}`);continue}N.endpoints=F.endpoints,N.endpointTimings=F.endpointTimings,N.timing=F.endpointTimings.reduce((u,M)=>u+M.timing,0)}}for(const{file:e,timestamp:o}of t){const s=g.get(e.fileName);s.endpoints.length>0&&C.SourceFileCache.cacheResults(e.sourceFile,o,n.cachePath,s.endpoints)}const T=[...m,...Array.from(g.values())];return l!=="off"&&y.Logger.info(`Router analysis took ${Math.round(performance.now()-d)}ms`),l==="stats"?T.map(e=>({fileName:e.fileName,timeTaken:e.timing})).sort((e,o)=>o.timeTaken-e.timeTaken).filter(e=>e.timeTaken>500).forEach(e=>{y.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,o)=>o.timeTaken-e.timeTaken).forEach(e=>{y.Logger.info(`- [${e.fileName}] Took ${Math.round(e.timeTaken)}ms to analyze`),e.endpointTimings.sort((o,s)=>s.timing-o.timing).forEach(o=>{y.Logger.info(` - ${o.method} ${o.path} (${Math.round(o.timing)}ms)`),o.sectionTimings.filter(s=>s.timing>=1).sort((s,c)=>c.timing-s.timing).forEach(s=>{y.Logger.info(` - ${s.section}: ${Math.round(s.timing)}ms`)})})}),T.flatMap(e=>e.endpoints)},L=(r,n)=>{const a=[],l=[],m=["get","post","put","delete","del","patch"].join("|");return r.routers.named.forEach(t=>{const p=new RegExp(`${t}\\.(?:${m})`);r.sourceFile.forEachChild(f=>{const g=f.getText();if(p.test(g)){w.resolveEndpointPath(f);const T=performance.now(),{endpoint:e,sectionTimings:o}=B.parseEndpoint(f,r.fileName);l.push({method:e.method,path:e.path,timing:performance.now()-T,sectionTimings:o}),a.push(e)}})}),{endpoints:a,endpointTimings:l}},v=r=>{const n=R.discoverImportedName({sourceFile:r,originalName:"useApiHeader"});if(!n)return null;const a=r.forEachChildAsArray().filter(t=>t.isKind(S.SyntaxKind.ExpressionStatement)).find(t=>n&&t.getText().startsWith(n));if(!a)return null;const l=a.getFirstDescendantByKindOrThrow(S.SyntaxKind.ObjectLiteralExpression),d=w.getValuesOfObjectLiteral(l),m=t=>typeof t=="string"||Array.isArray(t)&&t.every(p=>typeof p=="string")?t:t.reduce((p,f)=>typeof f=="string"?p:{...p,[f.identifier]:m(f.value)},{});return m(d)},O=r=>{const n=[],a=R.discoverImportedName({sourceFile:r,originalName:"useExposeApiModel"}),l=R.discoverImportedName({sourceFile:r,originalName:"useExposeNamedApiModels"});return r.forEachChildAsArray().filter(d=>d.isKind(S.SyntaxKind.ExpressionStatement)).map(d=>{if(a&&d.getText().startsWith(a)){const p=(d.getFirstChild()?.getChildrenOfKind(S.SyntaxKind.SyntaxList)||[])[0].getFirstChild();if(!p)return;n.push(k.parseExposedModel(p));return}if(l&&d.getText().startsWith(l)){const p=(d.getFirstChild()?.getChildrenOfKind(S.SyntaxKind.SyntaxList)||[])[0].getFirstChild();if(!p)return;k.parseNamedExposedModels(p).forEach(g=>n.push(g))}}),n};exports.analyzeMultipleSourceFiles=A;exports.analyzeSourceFileApiHeader=v;exports.analyzeSourceFileEndpoints=L;exports.analyzeSourceFileExposedModels=O;exports.prepareOpenApiSpec=Q;
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 * Number of uncached files at or below which analysis runs inline on the (already-warm) main-thread\n * Project instead of fanning out to worker threads. Each worker pays a multi-second cold-start to\n * build its own Project, so parallelism only wins once there are several files to share that cost.\n */\nconst INLINE_ANALYSIS_FILE_THRESHOLD = 2\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\ttype FileResult = {\n\t\tendpoints: EndpointData[]\n\t\tfileName: string\n\t\ttiming: number\n\t\tendpointTimings: EndpointTiming[]\n\t}\n\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\t// The caller (prepareOpenApiSpec) already built and warmed a ts-morph Project on this thread, so\n\t// inline analysis runs against a hot type-checker. A worker, by contrast, must spawn a thread and\n\t// build its own Project from scratch — a multi-second cold-start. That cold-start only pays off\n\t// when there are enough uncached files that spreading the parse work across workers beats it; below\n\t// the threshold (e.g. the common single-file incremental rebuild), inline is strictly faster.\n\tif (uncached.length <= INLINE_ANALYSIS_FILE_THRESHOLD) {\n\t\tfor (const { file } of uncached) {\n\t\t\tconst { endpoints, endpointTimings } = analyzeSourceFileEndpoints(file, filterEndpointPaths)\n\t\t\tconst fileResult = byFile.get(file.fileName)!\n\t\t\tfileResult.endpoints = endpoints\n\t\t\tfileResult.endpointTimings = endpointTimings\n\t\t\tfileResult.timing = endpointTimings.reduce((sum, t) => sum + t.timing, 0)\n\t\t}\n\t} else {\n\t\t// One task per file: each worker analyzes a whole file in a single pass, paying its Project\n\t\t// cold-start once and reusing the warmed-up checker for every endpoint in that file. Cap the\n\t\t// pool at one worker per file so we never spin up a worker with nothing to do.\n\t\ttype FileTask = { task: WorkerTask; fileName: string }\n\t\tconst allTasks: FileTask[] = uncached.map(({ file }) => ({\n\t\t\tfileName: file.fileName,\n\t\t\ttask: {\n\t\t\t\ttaskId: crypto.randomUUID(),\n\t\t\t\ttsconfigPath: config.tsconfigPath,\n\t\t\t\tsourceFilePath: file.sourceFile.getFilePath(),\n\t\t\t\trouterNames: file.routers.named,\n\t\t\t\tfilterEndpointPaths,\n\t\t\t},\n\t\t}))\n\n\t\tconst pool = new WorkerPool(resolveWorkerUrl(), allTasks.length)\n\t\tlet results: WorkerResult[]\n\t\ttry {\n\t\t\tresults = await pool.runAll(allTasks.map((ft) => ft.task))\n\t\t} finally {\n\t\t\tpool.terminate()\n\t\t}\n\n\t\t// Each result maps 1:1 to a file task.\n\t\tfor (let i = 0; i < results.length; i++) {\n\t\t\tconst result = results[i]\n\t\t\tconst fileName = allTasks[i].fileName\n\t\t\tconst fileResult = byFile.get(fileName)!\n\n\t\t\tif ('error' in result) {\n\t\t\t\tLogger.error(`[${fileName}] Worker error: ${result.error}`)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tfileResult.endpoints = result.endpoints\n\t\t\tfileResult.endpointTimings = result.endpointTimings\n\t\t\tfileResult.timing = result.endpointTimings.reduce((sum, t) => sum + t.timing, 0)\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","INLINE_ANALYSIS_FILE_THRESHOLD","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","byFile","endpointTimings","analyzeSourceFileEndpoints","fileResult","sum","t","allTasks","crypto","pool","WorkerPool","results","ft","i","result","fileName","analyzedFiles","a","b","ep","s","joinedOperations","routerName","routerPattern","node","nodeText","resolveEndpointPath","t1","endpoint","sectionTimings","parseEndpoint","sourceFile","nameOfUseApiHeader","discoverImportedName","SyntaxKind","targetNode","values","getValuesOfObjectLiteral","collapseObject","v","value","models","nameOfUseExposeApiModel","nameOfUseExposeNamedApiModels","firstChild","parseExposedModel","parseNamedExposedModels","model"],"mappings":"u/BAKA,SAASA,GAAwB,CAC1B,MAAAC,EAAa,CAAC,uBAAwB,2BAA2B,EACvE,UAAWC,KAAaD,EAAY,CACnC,MAAME,EAAM,IAAI,IAAID,EAAW,OAAA,SAAA,IAAA,QAAA,KAAA,EAAA,cAAA,UAAA,EAAA,KAAAE,GAAAA,EAAA,QAAA,YAAA,IAAA,UAAAA,EAAA,KAAA,IAAA,IAAA,4CAAA,SAAA,OAAA,EAAA,IAAe,EAC9C,GAAIC,aAAWC,EAAAA,cAAcH,CAAG,CAAC,EACzB,OAAAA,CACR,CAED,MAAM,IAAI,MACT,6GACD,CACD,CA4CA,MAAMI,EAAiC,EAM1BC,EAAqB,MAAO,CACxC,SAAAC,EACA,aAAAC,EACA,gBAAAC,EACA,oBAAAC,EACA,YAAAC,EACA,UAAAC,EAAY,OACb,IAA4B,CACrB,MAAAC,EAAiBC,iBAAe,YAAY,EAE9C,GAAAD,EAAe,UAClB,OAGGN,GACHQ,EAAA,OAAO,SAASR,CAAQ,EAGzBQ,EAAA,OAAO,KAAK,wBAAwB,EAE9B,MAAAC,EAAU,IAAIC,UAAQ,CAC3B,iBAAkBC,EAAK,QAAQV,CAAY,EAC3C,6BAA8B,EAAA,CAC9B,EAEK,CAAE,gBAAAW,EAAiB,sBAAAC,EAAuB,eAAAC,CAAA,GAAoB,IAAM,CAGnE,MAAAC,GAFmBb,GAAmB,CAAC,GACI,IAAKc,GAAaL,EAAK,QAAQK,CAAQ,CAAC,EAC7C,IAAKC,GAAaR,EAAQ,qBAAqBQ,CAAQ,CAAC,EAC9FL,EAAkBG,EAAY,QAASG,IAAU,CACtD,SAAUA,EAAK,YAAY,EAC3B,WAAYA,EACZ,QAASC,kBAAgBD,CAAI,CAAA,EAC5B,EAEI,CAAE,sBAAAL,EAAuB,sBAAAO,CAAA,GAA2B,IAAM,CAC/D,GAAIjB,IAAwB,GAC3B,MAAO,CAAE,sBAAuB,GAAI,sBAAuB,CAAA,CAAG,EAGzD,MAAAkB,EAAY,YAAY,IAAI,EAC5BC,EAAQC,EAAAA,oBAAoB,CACjC,WAAY,OAAOpB,GAAwB,SAAWA,EAAoB,SAAW,IACrF,aAAcF,CAAA,CACd,EACD,OAAII,IAAc,OACVG,EAAAA,OAAA,KAAK,uBAAuB,KAAK,MAAM,YAAY,IAAI,EAAIa,CAAS,CAAC,IAAI,EAE1EC,CAAA,GACL,EAEGR,EAAiBC,EAAY,OAClC,CAACS,EAAKC,IACLD,EAAI,KAAME,GAAMA,EAAE,YAAY,IAAMD,EAAQ,YAAY,CAAC,EAAID,EAAMA,EAAI,OAAOC,CAAO,EACtFL,CACD,EAEA,MAAO,CAAE,gBAAAR,EAAiB,sBAAAC,EAAuB,eAAAC,CAAe,CAAA,GAC9D,EAEGa,EAAiBf,EAAgB,OACtC,CAACY,EAAKC,IAAaD,EAAI,KAAME,GAAMA,EAAE,WAAaD,EAAQ,QAAQ,EAAID,EAAMA,EAAI,OAAOC,CAAO,EAC9FZ,CACD,EAEMe,EAAad,EACjB,QAASI,GAASW,EAA2BX,CAAI,CAAC,EAClD,OAAQY,GAAY,CAAC,CAACA,CAAO,EAC3BF,EAAW,OAAS,GAAKA,EAAW,CAAC,GACzBtB,EAAA,UAAUsB,EAAW,CAAC,CAAC,EAGvC,MAAMG,EAAgBjB,EAAe,QAASI,GAASc,EAA+Bd,CAAI,CAAC,EAE3FZ,EAAe,iBAAiByB,CAAa,EAE7C,MAAME,EACD,OAAO7B,GAAgB,UAAYA,EAAY,UAC3CA,EAAY,UAEbO,EAAK,QAAQ,QAAQ,MAAO,eAAgB,SAAU,YAAY,EAEpEuB,EAAY,MAAMC,EAA2BR,EAAgB,CAClE,YAAavB,IAAgB,GAC7B,UAAA6B,EACA,eAAgB,CAAC,EACjB,UAAA5B,EACA,aAAcM,EAAK,QAAQV,CAAY,CAAA,CACvC,EAEDK,EAAe,SAAS,CACvB,sBAAuBO,EAAsB,IAAKK,IAAU,CAC3D,KAAMA,EAAK,SACX,QAASA,EAAK,QAAQ,MAAM,IAAKQ,IAAO,CACvC,KAAMA,EACN,UAAWQ,EACT,OAAQE,GAAMA,EAAE,iBAAmBlB,EAAK,QAAQ,EAChD,IAAKkB,GAAM,GAAGA,EAAE,OAAO,aAAa,IAAIA,EAAE,IAAI,EAAE,CAAA,EACjD,CAAA,EACD,EACF,oBAAqBxB,EAAgB,IAAKM,IAAU,CACnD,KAAMA,EAAK,SACX,QAASA,EAAK,QAAQ,MAAM,IAAKQ,IAAO,CACvC,KAAMA,EACN,UAAWQ,EACT,OAAQE,GAAMA,EAAE,iBAAmBlB,EAAK,QAAQ,EAChD,IAAKkB,GAAM,GAAGA,EAAE,OAAO,aAAa,IAAIA,EAAE,IAAI,EAAE,CAAA,EACjD,CAAA,EACD,CAAA,CACF,EAED9B,EAAe,aAAa4B,CAAS,EACrC5B,EAAe,YAAY,CAC5B,EAEa6B,EAA6B,MACzCb,EACAe,EAOAC,IAC6B,CACvB,MAAAjC,EAAYgC,EAAO,WAAa,QAChChB,EAAY,YAAY,IAAI,EAM5BkB,EAAuB,CAAC,EACxBC,EAA2B,CAAC,EAElC,UAAWtB,KAAQI,EAAO,CACzB,MAAMmB,EAAYC,EAAAA,uBAAuBxB,EAAK,WAAYmB,EAAO,cAAc,EACzEM,EAAMN,EAAO,YAChBO,EAAgB,gBAAA,iBAAiB1B,EAAK,WAAYuB,EAAWJ,EAAO,SAAS,EAC7E,KACCM,GACHnC,EAAA,OAAO,MAAM,IAAIU,EAAK,QAAQ,wBAAwB,EACtDqB,EAAO,KAAK,CAAE,UAAWI,EAAI,UAAW,SAAUzB,EAAK,SAAU,OAAQ,EAAG,gBAAiB,GAAI,GAEjGsB,EAAS,KAAK,CAAE,KAAAtB,EAAM,UAAAuB,CAAA,CAAW,CAClC,CAGG,GAAAD,EAAS,SAAW,EACvB,OAAInC,IAAc,OACVG,EAAAA,OAAA,KAAK,wBAAwB,KAAK,MAAM,YAAY,IAAI,EAAIa,CAAS,CAAC,IAAI,EAE3EkB,EAAO,QAASM,GAAMA,EAAE,SAAS,EAUnC,MAAAC,MAAa,IACR,SAAA,CAAE,KAAA5B,CAAK,IAAKsB,EACtBM,EAAO,IAAI5B,EAAK,SAAU,CAAE,UAAW,CAAC,EAAG,SAAUA,EAAK,SAAU,OAAQ,EAAG,gBAAiB,GAAI,EAQjG,GAAAsB,EAAS,QAAU1C,EACX,SAAA,CAAE,KAAAoB,CAAK,IAAKsB,EAAU,CAChC,KAAM,CAAE,UAAAN,EAAW,gBAAAa,GAAoBC,EAA2B9B,CAAyB,EACrF+B,EAAaH,EAAO,IAAI5B,EAAK,QAAQ,EAC3C+B,EAAW,UAAYf,EACvBe,EAAW,gBAAkBF,EAClBE,EAAA,OAASF,EAAgB,OAAO,CAACG,EAAKC,IAAMD,EAAMC,EAAE,OAAQ,CAAC,CAAA,KAEnE,CAKN,MAAMC,EAAuBZ,EAAS,IAAI,CAAC,CAAE,KAAAtB,MAAY,CACxD,SAAUA,EAAK,SACf,KAAM,CACL,OAAQmC,EAAO,WAAW,EAC1B,aAAchB,EAAO,aACrB,eAAgBnB,EAAK,WAAW,YAAY,EAC5C,YAAaA,EAAK,QAAQ,MAC1B,oBAAAoB,CAAA,CACD,EACC,EAEIgB,EAAO,IAAIC,EAAA,WAAWhE,EAAiB,EAAG6D,EAAS,MAAM,EAC3D,IAAAI,EACA,GAAA,CACOA,EAAA,MAAMF,EAAK,OAAOF,EAAS,IAAKK,GAAOA,EAAG,IAAI,CAAC,CAAA,QACxD,CACDH,EAAK,UAAU,CAAA,CAIhB,QAASI,EAAI,EAAGA,EAAIF,EAAQ,OAAQE,IAAK,CAClC,MAAAC,EAASH,EAAQE,CAAC,EAClBE,EAAWR,EAASM,CAAC,EAAE,SACvBT,EAAaH,EAAO,IAAIc,CAAQ,EAEtC,GAAI,UAAWD,EAAQ,CACtBnD,SAAO,MAAM,IAAIoD,CAAQ,mBAAmBD,EAAO,KAAK,EAAE,EAC1D,QAAA,CAGDV,EAAW,UAAYU,EAAO,UAC9BV,EAAW,gBAAkBU,EAAO,gBACzBV,EAAA,OAASU,EAAO,gBAAgB,OAAO,CAACT,EAAKC,IAAMD,EAAMC,EAAE,OAAQ,CAAC,CAAA,CAChF,CAID,SAAW,CAAE,KAAAjC,EAAM,UAAAuB,CAAU,IAAKD,EAAU,CAC3C,MAAMS,EAAaH,EAAO,IAAI5B,EAAK,QAAQ,EACvC+B,EAAW,UAAU,OAAS,GACjCL,kBAAgB,aAAa1B,EAAK,WAAYuB,EAAWJ,EAAO,UAAWY,EAAW,SAAS,CAChG,CAGK,MAAAY,EAAgB,CAAC,GAAGtB,EAAQ,GAAG,MAAM,KAAKO,EAAO,OAAO,CAAC,CAAC,EAEhE,OAAIzC,IAAc,OACVG,EAAAA,OAAA,KAAK,wBAAwB,KAAK,MAAM,YAAY,IAAI,EAAIa,CAAS,CAAC,IAAI,EAG9EhB,IAAc,QACjBwD,EACE,IAAKhB,IAAO,CAAE,SAAUA,EAAE,SAAU,UAAWA,EAAE,MAAS,EAAA,EAC1D,KAAK,CAACiB,EAAGC,IAAMA,EAAE,UAAYD,EAAE,SAAS,EACxC,OAAQX,GAAMA,EAAE,UAAY,GAAG,EAC/B,QAASA,GAAM,CACR3C,EAAAA,OAAA,KAAK,MAAM2C,EAAE,QAAQ,UAAU,KAAK,MAAMA,EAAE,SAAS,CAAC,eAAe,CAAA,CAC5E,EACQ9C,IAAc,SAEtBwD,EAAA,IAAKhB,IAAO,CAAE,SAAUA,EAAE,SAAU,UAAWA,EAAE,OAAQ,gBAAiBA,EAAE,iBAAkB,EAC9F,KAAK,CAACiB,EAAGC,IAAMA,EAAE,UAAYD,EAAE,SAAS,EACxC,QAASX,GAAM,CACR3C,EAAAA,OAAA,KAAK,MAAM2C,EAAE,QAAQ,UAAU,KAAK,MAAMA,EAAE,SAAS,CAAC,eAAe,EAC5EA,EAAE,gBACA,KAAK,CAACW,EAAGC,IAAMA,EAAE,OAASD,EAAE,MAAM,EAClC,QAASE,GAAO,CAChBxD,EAAA,OAAO,KAAK,OAAOwD,EAAG,MAAM,IAAIA,EAAG,IAAI,KAAK,KAAK,MAAMA,EAAG,MAAM,CAAC,KAAK,EACtEA,EAAG,eACD,OAAQC,GAAMA,EAAE,QAAU,CAAC,EAC3B,KAAK,CAACH,EAAGC,IAAMA,EAAE,OAASD,EAAE,MAAM,EAClC,QAASG,GAAM,CACRzD,EAAAA,OAAA,KAAK,SAASyD,EAAE,OAAO,KAAK,KAAK,MAAMA,EAAE,MAAM,CAAC,IAAI,CAAA,CAC3D,CAAA,CACF,CAAA,CACF,EAGIJ,EAAc,QAAShB,GAAMA,EAAE,SAAS,CAChD,EA6BaG,EAA6B,CACzC9B,EACAoB,IACsE,CACtE,MAAMJ,EAA4B,CAAC,EAC7Ba,EAAoC,CAAC,EAErCmB,EADa,CAAC,MAAO,OAAQ,MAAO,SAAU,MAAO,OAAO,EAC9B,KAAK,GAAG,EAE5C,OAAAhD,EAAK,QAAQ,MAAM,QAASiD,GAAe,CAC1C,MAAMC,EAAgB,IAAI,OAAO,GAAGD,CAAU,SAASD,CAAgB,GAAG,EACrEhD,EAAA,WAAW,aAAcmD,GAAS,CAChC,MAAAC,EAAWD,EAAK,QAAQ,EAE1B,GAAAD,EAAc,KAAKE,CAAQ,EAAG,CACZC,EAAoB,oBAAAF,CAAI,EAMvC,MAAAG,EAAK,YAAY,IAAI,EACrB,CAAE,SAAAC,EAAU,eAAAC,GAAmBC,EAAc,cAAAN,EAAMnD,EAAK,QAAQ,EACtE6B,EAAgB,KAAK,CACpB,OAAQ0B,EAAS,OACjB,KAAMA,EAAS,KACf,OAAQ,YAAY,IAAA,EAAQD,EAC5B,eAAAE,CAAA,CACA,EACDxC,EAAU,KAAKuC,CAAQ,CAAA,CACxB,CACA,CAAA,CACD,EAEM,CAAE,UAAAvC,EAAW,gBAAAa,CAAgB,CACrC,EAEalB,EAA8B+C,GAAiD,CAC3F,MAAMC,EAAqBC,EAAAA,qBAAqB,CAC/C,WAAAF,EACA,aAAc,cAAA,CACd,EAED,GAAI,CAACC,EACG,OAAA,KAGF,MAAAR,EAAOO,EACX,oBAAoB,EACpB,OAAQP,GAASA,EAAK,OAAOU,EAAW,WAAA,mBAAmB,CAAC,EAC5D,KAAMV,GAASQ,GAAsBR,EAAK,QAAQ,EAAE,WAAWQ,CAAkB,CAAC,EAEpF,GAAI,CAACR,EACG,OAAA,KAGR,MAAMW,EAAaX,EAAK,gCAAgCU,EAAAA,WAAW,uBAAuB,EACpFE,EAASC,2BAAyBF,CAAU,EAE5CG,EAAkBC,GACnB,OAAOA,GAAM,UAGb,MAAM,QAAQA,CAAC,GAAKA,EAAE,MAAOC,GAAU,OAAOA,GAAU,QAAQ,EAC5DD,EAGDA,EAAE,OAAO,CAAC5D,EAAKC,IACjB,OAAOA,GAAY,SACfD,EAED,CACN,GAAGA,EACH,CAACC,EAAQ,UAAU,EAAG0D,EAAe1D,EAAQ,KAAiB,CAC/D,EACE,EAAE,EAEN,OAAO0D,EAAeF,CAAM,CAC7B,EAEajD,EAAkC4C,GAA+C,CAC7F,MAAMU,EAA6B,CAAC,EAE9BC,EAA0BT,EAAAA,qBAAqB,CACpD,WAAAF,EACA,aAAc,mBAAA,CACd,EAEKY,EAAgCV,EAAAA,qBAAqB,CAC1D,WAAAF,EACA,aAAc,yBAAA,CACd,EAED,OAAAA,EACE,oBAAoB,EACpB,OAAQP,GAASA,EAAK,OAAOU,EAAAA,WAAW,mBAAmB,CAAC,EAC5D,IAAKV,GAAS,CACd,GAAIkB,GAA2BlB,EAAK,QAAU,EAAA,WAAWkB,CAAuB,EAAG,CAIlF,MAAME,GAHqBpB,EAAK,cAAc,GACC,kBAAkBU,EAAAA,WAAW,UAAU,GAAK,CAAC,GAEtD,CAAC,EAAE,cAAc,EACvD,GAAI,CAACU,EACJ,OAGMH,EAAA,KAAKI,oBAAkBD,CAAU,CAAC,EACzC,MAAA,CAGD,GAAID,GAAiCnB,EAAK,QAAU,EAAA,WAAWmB,CAA6B,EAAG,CAI9F,MAAMC,GAHqBpB,EAAK,cAAc,GACC,kBAAkBU,EAAAA,WAAW,UAAU,GAAK,CAAC,GAEtD,CAAC,EAAE,cAAc,EACvD,GAAI,CAACU,EACJ,OAGoBE,0BAAwBF,CAAU,EAC1C,QAASG,GAAUN,EAAO,KAAKM,CAAK,CAAC,CAAA,CACnD,CACA,EACKN,CACR"}
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, MtimeCache, 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 * Number of uncached files at or below which analysis runs inline on the (already-warm) main-thread\n * Project instead of fanning out to worker threads. Each worker pays a multi-second cold-start to\n * build its own Project, so parallelism only wins once there are several files to share that cost.\n */\nconst INLINE_ANALYSIS_FILE_THRESHOLD = 2\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\tconst freshnessCheckStart = performance.now()\n\tconst mtimeCache: MtimeCache = new Map()\n\tfor (const file of files) {\n\t\tconst timestamp = getSourceFileTimestamp(file.sourceFile, config.timestampCache, mtimeCache)\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\tif (profiling !== 'off') {\n\t\tLogger.info(`Cache freshness check took ${Math.round(performance.now() - freshnessCheckStart)}ms`)\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\ttype FileResult = {\n\t\tendpoints: EndpointData[]\n\t\tfileName: string\n\t\ttiming: number\n\t\tendpointTimings: EndpointTiming[]\n\t}\n\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\t// The caller (prepareOpenApiSpec) already built and warmed a ts-morph Project on this thread, so\n\t// inline analysis runs against a hot type-checker. A worker, by contrast, must spawn a thread and\n\t// build its own Project from scratch — a multi-second cold-start. That cold-start only pays off\n\t// when there are enough uncached files that spreading the parse work across workers beats it; below\n\t// the threshold (e.g. the common single-file incremental rebuild), inline is strictly faster.\n\tif (uncached.length <= INLINE_ANALYSIS_FILE_THRESHOLD) {\n\t\tfor (const { file } of uncached) {\n\t\t\tconst { endpoints, endpointTimings } = analyzeSourceFileEndpoints(file, filterEndpointPaths)\n\t\t\tconst fileResult = byFile.get(file.fileName)!\n\t\t\tfileResult.endpoints = endpoints\n\t\t\tfileResult.endpointTimings = endpointTimings\n\t\t\tfileResult.timing = endpointTimings.reduce((sum, t) => sum + t.timing, 0)\n\t\t}\n\t} else {\n\t\t// One task per file: each worker analyzes a whole file in a single pass, paying its Project\n\t\t// cold-start once and reusing the warmed-up checker for every endpoint in that file. Cap the\n\t\t// pool at one worker per file so we never spin up a worker with nothing to do.\n\t\ttype FileTask = { task: WorkerTask; fileName: string }\n\t\tconst allTasks: FileTask[] = uncached.map(({ file }) => ({\n\t\t\tfileName: file.fileName,\n\t\t\ttask: {\n\t\t\t\ttaskId: crypto.randomUUID(),\n\t\t\t\ttsconfigPath: config.tsconfigPath,\n\t\t\t\tsourceFilePath: file.sourceFile.getFilePath(),\n\t\t\t\trouterNames: file.routers.named,\n\t\t\t\tfilterEndpointPaths,\n\t\t\t},\n\t\t}))\n\n\t\tconst pool = new WorkerPool(resolveWorkerUrl(), allTasks.length)\n\t\tlet results: WorkerResult[]\n\t\ttry {\n\t\t\tresults = await pool.runAll(allTasks.map((ft) => ft.task))\n\t\t} finally {\n\t\t\tpool.terminate()\n\t\t}\n\n\t\t// Each result maps 1:1 to a file task.\n\t\tfor (let i = 0; i < results.length; i++) {\n\t\t\tconst result = results[i]\n\t\t\tconst fileName = allTasks[i].fileName\n\t\t\tconst fileResult = byFile.get(fileName)!\n\n\t\t\tif ('error' in result) {\n\t\t\t\tLogger.error(`[${fileName}] Worker error: ${result.error}`)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tfileResult.endpoints = result.endpoints\n\t\t\tfileResult.endpointTimings = result.endpointTimings\n\t\t\tfileResult.timing = result.endpointTimings.reduce((sum, t) => sum + t.timing, 0)\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","INLINE_ANALYSIS_FILE_THRESHOLD","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","freshnessCheckStart","mtimeCache","timestamp","getSourceFileTimestamp","hit","SourceFileCache","f","byFile","endpointTimings","analyzeSourceFileEndpoints","fileResult","sum","t","allTasks","crypto","pool","WorkerPool","results","ft","i","result","fileName","analyzedFiles","a","b","ep","joinedOperations","routerName","routerPattern","node","nodeText","resolveEndpointPath","t1","endpoint","sectionTimings","parseEndpoint","sourceFile","nameOfUseApiHeader","discoverImportedName","SyntaxKind","targetNode","values","getValuesOfObjectLiteral","collapseObject","v","value","models","nameOfUseExposeApiModel","nameOfUseExposeNamedApiModels","firstChild","parseExposedModel","parseNamedExposedModels","model"],"mappings":"u/BAKA,SAASA,GAAwB,CAC1B,MAAAC,EAAa,CAAC,uBAAwB,2BAA2B,EACvE,UAAWC,KAAaD,EAAY,CACnC,MAAME,EAAM,IAAI,IAAID,EAAW,OAAA,SAAA,IAAA,QAAA,KAAA,EAAA,cAAA,UAAA,EAAA,KAAAE,GAAAA,EAAA,QAAA,YAAA,IAAA,UAAAA,EAAA,KAAA,IAAA,IAAA,4CAAA,SAAA,OAAA,EAAA,IAAe,EAC9C,GAAIC,aAAWC,EAAAA,cAAcH,CAAG,CAAC,EACzB,OAAAA,CACR,CAED,MAAM,IAAI,MACT,6GACD,CACD,CA4CA,MAAMI,EAAiC,EAM1BC,EAAqB,MAAO,CACxC,SAAAC,EACA,aAAAC,EACA,gBAAAC,EACA,oBAAAC,EACA,YAAAC,EACA,UAAAC,EAAY,OACb,IAA4B,CACrB,MAAAC,EAAiBC,iBAAe,YAAY,EAE9C,GAAAD,EAAe,UAClB,OAGGN,GACHQ,EAAA,OAAO,SAASR,CAAQ,EAGzBQ,EAAA,OAAO,KAAK,wBAAwB,EAE9B,MAAAC,EAAU,IAAIC,UAAQ,CAC3B,iBAAkBC,EAAK,QAAQV,CAAY,EAC3C,6BAA8B,EAAA,CAC9B,EAEK,CAAE,gBAAAW,EAAiB,sBAAAC,EAAuB,eAAAC,CAAA,GAAoB,IAAM,CAGnE,MAAAC,GAFmBb,GAAmB,CAAC,GACI,IAAKc,GAAaL,EAAK,QAAQK,CAAQ,CAAC,EAC7C,IAAKC,GAAaR,EAAQ,qBAAqBQ,CAAQ,CAAC,EAC9FL,EAAkBG,EAAY,QAASG,IAAU,CACtD,SAAUA,EAAK,YAAY,EAC3B,WAAYA,EACZ,QAASC,kBAAgBD,CAAI,CAAA,EAC5B,EAEI,CAAE,sBAAAL,EAAuB,sBAAAO,CAAA,GAA2B,IAAM,CAC/D,GAAIjB,IAAwB,GAC3B,MAAO,CAAE,sBAAuB,GAAI,sBAAuB,CAAA,CAAG,EAGzD,MAAAkB,EAAY,YAAY,IAAI,EAC5BC,EAAQC,EAAAA,oBAAoB,CACjC,WAAY,OAAOpB,GAAwB,SAAWA,EAAoB,SAAW,IACrF,aAAcF,CAAA,CACd,EACD,OAAII,IAAc,OACVG,EAAAA,OAAA,KAAK,uBAAuB,KAAK,MAAM,YAAY,IAAI,EAAIa,CAAS,CAAC,IAAI,EAE1EC,CAAA,GACL,EAEGR,EAAiBC,EAAY,OAClC,CAACS,EAAKC,IACLD,EAAI,KAAME,GAAMA,EAAE,YAAY,IAAMD,EAAQ,YAAY,CAAC,EAAID,EAAMA,EAAI,OAAOC,CAAO,EACtFL,CACD,EAEA,MAAO,CAAE,gBAAAR,EAAiB,sBAAAC,EAAuB,eAAAC,CAAe,CAAA,GAC9D,EAEGa,EAAiBf,EAAgB,OACtC,CAACY,EAAKC,IAAaD,EAAI,KAAME,GAAMA,EAAE,WAAaD,EAAQ,QAAQ,EAAID,EAAMA,EAAI,OAAOC,CAAO,EAC9FZ,CACD,EAEMe,EAAad,EACjB,QAASI,GAASW,EAA2BX,CAAI,CAAC,EAClD,OAAQY,GAAY,CAAC,CAACA,CAAO,EAC3BF,EAAW,OAAS,GAAKA,EAAW,CAAC,GACzBtB,EAAA,UAAUsB,EAAW,CAAC,CAAC,EAGvC,MAAMG,EAAgBjB,EAAe,QAASI,GAASc,EAA+Bd,CAAI,CAAC,EAE3FZ,EAAe,iBAAiByB,CAAa,EAE7C,MAAME,EACD,OAAO7B,GAAgB,UAAYA,EAAY,UAC3CA,EAAY,UAEbO,EAAK,QAAQ,QAAQ,MAAO,eAAgB,SAAU,YAAY,EAEpEuB,EAAY,MAAMC,EAA2BR,EAAgB,CAClE,YAAavB,IAAgB,GAC7B,UAAA6B,EACA,eAAgB,CAAC,EACjB,UAAA5B,EACA,aAAcM,EAAK,QAAQV,CAAY,CAAA,CACvC,EAEDK,EAAe,SAAS,CACvB,sBAAuBO,EAAsB,IAAKK,IAAU,CAC3D,KAAMA,EAAK,SACX,QAASA,EAAK,QAAQ,MAAM,IAAKQ,IAAO,CACvC,KAAMA,EACN,UAAWQ,EACT,OAAQE,GAAMA,EAAE,iBAAmBlB,EAAK,QAAQ,EAChD,IAAKkB,GAAM,GAAGA,EAAE,OAAO,aAAa,IAAIA,EAAE,IAAI,EAAE,CAAA,EACjD,CAAA,EACD,EACF,oBAAqBxB,EAAgB,IAAKM,IAAU,CACnD,KAAMA,EAAK,SACX,QAASA,EAAK,QAAQ,MAAM,IAAKQ,IAAO,CACvC,KAAMA,EACN,UAAWQ,EACT,OAAQE,GAAMA,EAAE,iBAAmBlB,EAAK,QAAQ,EAChD,IAAKkB,GAAM,GAAGA,EAAE,OAAO,aAAa,IAAIA,EAAE,IAAI,EAAE,CAAA,EACjD,CAAA,EACD,CAAA,CACF,EAED9B,EAAe,aAAa4B,CAAS,EACrC5B,EAAe,YAAY,CAC5B,EAEa6B,EAA6B,MACzCb,EACAe,EAOAC,IAC6B,CACvB,MAAAjC,EAAYgC,EAAO,WAAa,QAChChB,EAAY,YAAY,IAAI,EAM5BkB,EAAuB,CAAC,EACxBC,EAA2B,CAAC,EAE5BC,EAAsB,YAAY,IAAI,EACtCC,MAA6B,IACnC,UAAWxB,KAAQI,EAAO,CACzB,MAAMqB,EAAYC,EAAAA,uBAAuB1B,EAAK,WAAYmB,EAAO,eAAgBK,CAAU,EACrFG,EAAMR,EAAO,YAChBS,EAAgB,gBAAA,iBAAiB5B,EAAK,WAAYyB,EAAWN,EAAO,SAAS,EAC7E,KACCQ,GACHrC,EAAA,OAAO,MAAM,IAAIU,EAAK,QAAQ,wBAAwB,EACtDqB,EAAO,KAAK,CAAE,UAAWM,EAAI,UAAW,SAAU3B,EAAK,SAAU,OAAQ,EAAG,gBAAiB,GAAI,GAEjGsB,EAAS,KAAK,CAAE,KAAAtB,EAAM,UAAAyB,CAAA,CAAW,CAClC,CAMG,GAJAtC,IAAc,OACVG,EAAAA,OAAA,KAAK,8BAA8B,KAAK,MAAM,YAAY,IAAI,EAAIiC,CAAmB,CAAC,IAAI,EAG9FD,EAAS,SAAW,EACvB,OAAInC,IAAc,OACVG,EAAAA,OAAA,KAAK,wBAAwB,KAAK,MAAM,YAAY,IAAI,EAAIa,CAAS,CAAC,IAAI,EAE3EkB,EAAO,QAASQ,GAAMA,EAAE,SAAS,EAUnC,MAAAC,MAAa,IACR,SAAA,CAAE,KAAA9B,CAAK,IAAKsB,EACtBQ,EAAO,IAAI9B,EAAK,SAAU,CAAE,UAAW,CAAC,EAAG,SAAUA,EAAK,SAAU,OAAQ,EAAG,gBAAiB,GAAI,EAQjG,GAAAsB,EAAS,QAAU1C,EACX,SAAA,CAAE,KAAAoB,CAAK,IAAKsB,EAAU,CAChC,KAAM,CAAE,UAAAN,EAAW,gBAAAe,GAAoBC,EAA2BhC,CAAyB,EACrFiC,EAAaH,EAAO,IAAI9B,EAAK,QAAQ,EAC3CiC,EAAW,UAAYjB,EACvBiB,EAAW,gBAAkBF,EAClBE,EAAA,OAASF,EAAgB,OAAO,CAACG,EAAKC,IAAMD,EAAMC,EAAE,OAAQ,CAAC,CAAA,KAEnE,CAKN,MAAMC,EAAuBd,EAAS,IAAI,CAAC,CAAE,KAAAtB,MAAY,CACxD,SAAUA,EAAK,SACf,KAAM,CACL,OAAQqC,EAAO,WAAW,EAC1B,aAAclB,EAAO,aACrB,eAAgBnB,EAAK,WAAW,YAAY,EAC5C,YAAaA,EAAK,QAAQ,MAC1B,oBAAAoB,CAAA,CACD,EACC,EAEIkB,EAAO,IAAIC,EAAA,WAAWlE,EAAiB,EAAG+D,EAAS,MAAM,EAC3D,IAAAI,EACA,GAAA,CACOA,EAAA,MAAMF,EAAK,OAAOF,EAAS,IAAKK,GAAOA,EAAG,IAAI,CAAC,CAAA,QACxD,CACDH,EAAK,UAAU,CAAA,CAIhB,QAASI,EAAI,EAAGA,EAAIF,EAAQ,OAAQE,IAAK,CAClC,MAAAC,EAASH,EAAQE,CAAC,EAClBE,EAAWR,EAASM,CAAC,EAAE,SACvBT,EAAaH,EAAO,IAAIc,CAAQ,EAEtC,GAAI,UAAWD,EAAQ,CACtBrD,SAAO,MAAM,IAAIsD,CAAQ,mBAAmBD,EAAO,KAAK,EAAE,EAC1D,QAAA,CAGDV,EAAW,UAAYU,EAAO,UAC9BV,EAAW,gBAAkBU,EAAO,gBACzBV,EAAA,OAASU,EAAO,gBAAgB,OAAO,CAACT,EAAKC,IAAMD,EAAMC,EAAE,OAAQ,CAAC,CAAA,CAChF,CAID,SAAW,CAAE,KAAAnC,EAAM,UAAAyB,CAAU,IAAKH,EAAU,CAC3C,MAAMW,EAAaH,EAAO,IAAI9B,EAAK,QAAQ,EACvCiC,EAAW,UAAU,OAAS,GACjCL,kBAAgB,aAAa5B,EAAK,WAAYyB,EAAWN,EAAO,UAAWc,EAAW,SAAS,CAChG,CAGK,MAAAY,EAAgB,CAAC,GAAGxB,EAAQ,GAAG,MAAM,KAAKS,EAAO,OAAO,CAAC,CAAC,EAEhE,OAAI3C,IAAc,OACVG,EAAAA,OAAA,KAAK,wBAAwB,KAAK,MAAM,YAAY,IAAI,EAAIa,CAAS,CAAC,IAAI,EAG9EhB,IAAc,QACjB0D,EACE,IAAKhB,IAAO,CAAE,SAAUA,EAAE,SAAU,UAAWA,EAAE,MAAS,EAAA,EAC1D,KAAK,CAACiB,EAAGC,IAAMA,EAAE,UAAYD,EAAE,SAAS,EACxC,OAAQX,GAAMA,EAAE,UAAY,GAAG,EAC/B,QAASA,GAAM,CACR7C,EAAAA,OAAA,KAAK,MAAM6C,EAAE,QAAQ,UAAU,KAAK,MAAMA,EAAE,SAAS,CAAC,eAAe,CAAA,CAC5E,EACQhD,IAAc,SAEtB0D,EAAA,IAAKhB,IAAO,CAAE,SAAUA,EAAE,SAAU,UAAWA,EAAE,OAAQ,gBAAiBA,EAAE,iBAAkB,EAC9F,KAAK,CAACiB,EAAGC,IAAMA,EAAE,UAAYD,EAAE,SAAS,EACxC,QAASX,GAAM,CACR7C,EAAAA,OAAA,KAAK,MAAM6C,EAAE,QAAQ,UAAU,KAAK,MAAMA,EAAE,SAAS,CAAC,eAAe,EAC5EA,EAAE,gBACA,KAAK,CAACW,EAAGC,IAAMA,EAAE,OAASD,EAAE,MAAM,EAClC,QAASE,GAAO,CAChB1D,EAAA,OAAO,KAAK,OAAO0D,EAAG,MAAM,IAAIA,EAAG,IAAI,KAAK,KAAK,MAAMA,EAAG,MAAM,CAAC,KAAK,EACtEA,EAAG,eACD,OAAQ,GAAM,EAAE,QAAU,CAAC,EAC3B,KAAK,CAACF,EAAGC,IAAMA,EAAE,OAASD,EAAE,MAAM,EAClC,QAAS,GAAM,CACRxD,EAAAA,OAAA,KAAK,SAAS,EAAE,OAAO,KAAK,KAAK,MAAM,EAAE,MAAM,CAAC,IAAI,CAAA,CAC3D,CAAA,CACF,CAAA,CACF,EAGIuD,EAAc,QAAShB,GAAMA,EAAE,SAAS,CAChD,EA6BaG,EAA6B,CACzChC,EACAoB,IACsE,CACtE,MAAMJ,EAA4B,CAAC,EAC7Be,EAAoC,CAAC,EAErCkB,EADa,CAAC,MAAO,OAAQ,MAAO,SAAU,MAAO,OAAO,EAC9B,KAAK,GAAG,EAE5C,OAAAjD,EAAK,QAAQ,MAAM,QAASkD,GAAe,CAC1C,MAAMC,EAAgB,IAAI,OAAO,GAAGD,CAAU,SAASD,CAAgB,GAAG,EACrEjD,EAAA,WAAW,aAAcoD,GAAS,CAChC,MAAAC,EAAWD,EAAK,QAAQ,EAE1B,GAAAD,EAAc,KAAKE,CAAQ,EAAG,CACZC,EAAoB,oBAAAF,CAAI,EAMvC,MAAAG,EAAK,YAAY,IAAI,EACrB,CAAE,SAAAC,EAAU,eAAAC,GAAmBC,EAAc,cAAAN,EAAMpD,EAAK,QAAQ,EACtE+B,EAAgB,KAAK,CACpB,OAAQyB,EAAS,OACjB,KAAMA,EAAS,KACf,OAAQ,YAAY,IAAA,EAAQD,EAC5B,eAAAE,CAAA,CACA,EACDzC,EAAU,KAAKwC,CAAQ,CAAA,CACxB,CACA,CAAA,CACD,EAEM,CAAE,UAAAxC,EAAW,gBAAAe,CAAgB,CACrC,EAEapB,EAA8BgD,GAAiD,CAC3F,MAAMC,EAAqBC,EAAAA,qBAAqB,CAC/C,WAAAF,EACA,aAAc,cAAA,CACd,EAED,GAAI,CAACC,EACG,OAAA,KAGF,MAAAR,EAAOO,EACX,oBAAoB,EACpB,OAAQP,GAASA,EAAK,OAAOU,EAAW,WAAA,mBAAmB,CAAC,EAC5D,KAAMV,GAASQ,GAAsBR,EAAK,QAAQ,EAAE,WAAWQ,CAAkB,CAAC,EAEpF,GAAI,CAACR,EACG,OAAA,KAGR,MAAMW,EAAaX,EAAK,gCAAgCU,EAAAA,WAAW,uBAAuB,EACpFE,EAASC,2BAAyBF,CAAU,EAE5CG,EAAkBC,GACnB,OAAOA,GAAM,UAGb,MAAM,QAAQA,CAAC,GAAKA,EAAE,MAAOC,GAAU,OAAOA,GAAU,QAAQ,EAC5DD,EAGDA,EAAE,OAAO,CAAC7D,EAAKC,IACjB,OAAOA,GAAY,SACfD,EAED,CACN,GAAGA,EACH,CAACC,EAAQ,UAAU,EAAG2D,EAAe3D,EAAQ,KAAiB,CAC/D,EACE,EAAE,EAEN,OAAO2D,EAAeF,CAAM,CAC7B,EAEalD,EAAkC6C,GAA+C,CAC7F,MAAMU,EAA6B,CAAC,EAE9BC,EAA0BT,EAAAA,qBAAqB,CACpD,WAAAF,EACA,aAAc,mBAAA,CACd,EAEKY,EAAgCV,EAAAA,qBAAqB,CAC1D,WAAAF,EACA,aAAc,yBAAA,CACd,EAED,OAAAA,EACE,oBAAoB,EACpB,OAAQP,GAASA,EAAK,OAAOU,EAAAA,WAAW,mBAAmB,CAAC,EAC5D,IAAKV,GAAS,CACd,GAAIkB,GAA2BlB,EAAK,QAAU,EAAA,WAAWkB,CAAuB,EAAG,CAIlF,MAAME,GAHqBpB,EAAK,cAAc,GACC,kBAAkBU,EAAAA,WAAW,UAAU,GAAK,CAAC,GAEtD,CAAC,EAAE,cAAc,EACvD,GAAI,CAACU,EACJ,OAGMH,EAAA,KAAKI,oBAAkBD,CAAU,CAAC,EACzC,MAAA,CAGD,GAAID,GAAiCnB,EAAK,QAAU,EAAA,WAAWmB,CAA6B,EAAG,CAI9F,MAAMC,GAHqBpB,EAAK,cAAc,GACC,kBAAkBU,EAAAA,WAAW,UAAU,GAAK,CAAC,GAEtD,CAAC,EAAE,cAAc,EACvD,GAAI,CAACU,EACJ,OAGoBE,0BAAwBF,CAAU,EAC1C,QAASG,GAAUN,EAAO,KAAKM,CAAK,CAAC,CAAA,CACnD,CACA,EACKN,CACR"}
@@ -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;AAavG,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,CA4IxB,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,EAAsC,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAE7F,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;AAavG,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,CAiJxB,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"}
@@ -3,8 +3,8 @@ import { existsSync as v } from "fs";
3
3
  import * as k from "path";
4
4
  import { fileURLToPath as O } from "url";
5
5
  import { Project as L, SyntaxKind as x } from "ts-morph";
6
- import { Logger as g } from "../../utils/logger.mjs";
7
- import { discoverImportedName as M } from "../discoveryModule/discoverImports/discoverImports.mjs";
6
+ import { Logger as F } from "../../utils/logger.mjs";
7
+ import { discoverImportedName as P } from "../discoveryModule/discoverImports/discoverImports.mjs";
8
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";
@@ -12,192 +12,192 @@ import { getSourceFileTimestamp as b } from "./getSourceFileTimestamp.mjs";
12
12
  import { getValuesOfObjectLiteral as I, resolveEndpointPath as W } from "./nodeParsers.mjs";
13
13
  import { parseEndpoint as H } from "./parseEndpoint.mjs";
14
14
  import { parseExposedModel as K, parseNamedExposedModels as _ } from "./parseExposedModels.mjs";
15
- import { SourceFileCache as P } from "./sourceFileCache.mjs";
15
+ import { SourceFileCache as R } from "./sourceFileCache.mjs";
16
16
  import { WorkerPool as D } from "./workerPool.mjs";
17
17
  function B() {
18
- const l = ["./analyzerWorker.mjs", "./analyzerWorker.test.mjs"];
19
- for (const n of l) {
20
- const p = new URL(n, import.meta.url);
21
- if (v(O(p)))
22
- return p;
18
+ const a = ["./analyzerWorker.mjs", "./analyzerWorker.test.mjs"];
19
+ for (const n of a) {
20
+ const d = new URL(n, import.meta.url);
21
+ if (v(O(d)))
22
+ return d;
23
23
  }
24
24
  throw new Error(
25
25
  "analyzerWorker.mjs not found. Run yarn build, or run tests via yarn test (which compiles the worker first)."
26
26
  );
27
27
  }
28
28
  const V = 2, de = async ({
29
- logLevel: l,
29
+ logLevel: a,
30
30
  tsconfigPath: n,
31
- sourceFilePaths: p,
32
- sourceFileDiscovery: c,
31
+ sourceFilePaths: d,
32
+ sourceFileDiscovery: l,
33
33
  incremental: m,
34
- profiling: u = "stats"
34
+ profiling: f = "stats"
35
35
  }) => {
36
36
  const t = U.getInstance();
37
37
  if (t.isReady())
38
38
  return;
39
- l && g.setLevel(l), g.info("Preparing OpenAPI spec");
40
- const r = new L({
39
+ a && F.setLevel(a), F.info("Preparing OpenAPI spec");
40
+ const c = new L({
41
41
  tsConfigFilePath: k.resolve(n),
42
42
  skipFileDependencyResolution: !0
43
- }), { explicitRouters: d, discoveredRouterFiles: e, allSourceFiles: i } = (() => {
44
- const f = (p ?? []).map((h) => k.resolve(h)).map((h) => r.getSourceFileOrThrow(h)), R = f.flatMap((h) => ({
43
+ }), { explicitRouters: u, discoveredRouterFiles: g, allSourceFiles: T } = (() => {
44
+ const p = (d ?? []).map((h) => k.resolve(h)).map((h) => c.getSourceFileOrThrow(h)), M = p.flatMap((h) => ({
45
45
  fileName: h.getFilePath(),
46
46
  sourceFile: h,
47
47
  routers: z(h)
48
- })), { discoveredRouterFiles: A, discoveredSourceFiles: S } = (() => {
49
- if (c === !1)
48
+ })), { discoveredRouterFiles: C, discoveredSourceFiles: S } = (() => {
49
+ if (l === !1)
50
50
  return { discoveredRouterFiles: [], discoveredSourceFiles: [] };
51
51
  const h = performance.now(), E = j({
52
- targetPath: typeof c == "object" ? c.rootPath : ".",
52
+ targetPath: typeof l == "object" ? l.rootPath : ".",
53
53
  tsConfigPath: n
54
54
  });
55
- return u !== "off" && g.info(`File discovery took ${Math.round(performance.now() - h)}ms`), E;
56
- })(), C = f.reduce(
57
- (h, E) => h.some((w) => w.getFilePath() === E.getFilePath()) ? h : h.concat(E),
55
+ return f !== "off" && F.info(`File discovery took ${Math.round(performance.now() - h)}ms`), E;
56
+ })(), w = p.reduce(
57
+ (h, E) => h.some((A) => A.getFilePath() === E.getFilePath()) ? h : h.concat(E),
58
58
  S
59
59
  );
60
- return { explicitRouters: R, discoveredRouterFiles: A, allSourceFiles: C };
61
- })(), o = d.reduce(
62
- (a, y) => a.some((f) => f.fileName === y.fileName) ? a : a.concat(y),
63
- e
64
- ), s = i.flatMap((a) => G(a)).filter((a) => !!a);
65
- s.length > 0 && s[0] && t.setHeader(s[0]);
66
- const F = i.flatMap((a) => J(a));
67
- t.setExposedModels(F);
68
- const T = typeof m == "object" && m.cachePath ? m.cachePath : k.resolve(process.cwd(), "node_modules", ".cache", "moonflower"), N = await Y(o, {
60
+ return { explicitRouters: M, discoveredRouterFiles: C, allSourceFiles: w };
61
+ })(), e = u.reduce(
62
+ (s, N) => s.some((p) => p.fileName === N.fileName) ? s : s.concat(N),
63
+ g
64
+ ), o = T.flatMap((s) => G(s)).filter((s) => !!s);
65
+ o.length > 0 && o[0] && t.setHeader(o[0]);
66
+ const i = T.flatMap((s) => J(s));
67
+ t.setExposedModels(i);
68
+ const r = typeof m == "object" && m.cachePath ? m.cachePath : k.resolve(process.cwd(), "node_modules", ".cache", "moonflower"), y = await Y(e, {
69
69
  incremental: m !== !1,
70
- cachePath: T,
70
+ cachePath: r,
71
71
  timestampCache: {},
72
- profiling: u,
72
+ profiling: f,
73
73
  tsconfigPath: k.resolve(n)
74
74
  });
75
75
  t.setStats({
76
- discoveredRouterFiles: e.map((a) => ({
77
- path: a.fileName,
78
- routers: a.routers.named.map((y) => ({
79
- name: y,
80
- endpoints: N.filter((f) => f.sourceFilePath === a.fileName).map((f) => `${f.method.toUpperCase()} ${f.path}`)
76
+ discoveredRouterFiles: g.map((s) => ({
77
+ path: s.fileName,
78
+ routers: s.routers.named.map((N) => ({
79
+ name: N,
80
+ endpoints: y.filter((p) => p.sourceFilePath === s.fileName).map((p) => `${p.method.toUpperCase()} ${p.path}`)
81
81
  }))
82
82
  })),
83
- explicitRouterFiles: d.map((a) => ({
84
- path: a.fileName,
85
- routers: a.routers.named.map((y) => ({
86
- name: y,
87
- endpoints: N.filter((f) => f.sourceFilePath === a.fileName).map((f) => `${f.method.toUpperCase()} ${f.path}`)
83
+ explicitRouterFiles: u.map((s) => ({
84
+ path: s.fileName,
85
+ routers: s.routers.named.map((N) => ({
86
+ name: N,
87
+ endpoints: y.filter((p) => p.sourceFilePath === s.fileName).map((p) => `${p.method.toUpperCase()} ${p.path}`)
88
88
  }))
89
89
  }))
90
- }), t.setEndpoints(N), t.markAsReady();
91
- }, Y = async (l, n, p) => {
92
- const c = n.profiling ?? "stats", m = performance.now(), u = [], t = [];
93
- for (const e of l) {
94
- const i = b(e.sourceFile, n.timestampCache), o = n.incremental ? P.getCachedResults(e.sourceFile, i, n.cachePath) : null;
95
- o ? (g.debug(`[${e.fileName}] Found cached results`), u.push({ endpoints: o.endpoints, fileName: e.fileName, timing: 0, endpointTimings: [] })) : t.push({ file: e, timestamp: i });
90
+ }), t.setEndpoints(y), t.markAsReady();
91
+ }, Y = async (a, n, d) => {
92
+ const l = n.profiling ?? "stats", m = performance.now(), f = [], t = [], c = performance.now(), u = /* @__PURE__ */ new Map();
93
+ for (const e of a) {
94
+ const o = b(e.sourceFile, n.timestampCache, u), i = n.incremental ? R.getCachedResults(e.sourceFile, o, n.cachePath) : null;
95
+ i ? (F.debug(`[${e.fileName}] Found cached results`), f.push({ endpoints: i.endpoints, fileName: e.fileName, timing: 0, endpointTimings: [] })) : t.push({ file: e, timestamp: o });
96
96
  }
97
- if (t.length === 0)
98
- return c !== "off" && g.info(`Router analysis took ${Math.round(performance.now() - m)}ms`), u.flatMap((e) => e.endpoints);
99
- const r = /* @__PURE__ */ new Map();
97
+ if (l !== "off" && F.info(`Cache freshness check took ${Math.round(performance.now() - c)}ms`), t.length === 0)
98
+ return l !== "off" && F.info(`Router analysis took ${Math.round(performance.now() - m)}ms`), f.flatMap((e) => e.endpoints);
99
+ const g = /* @__PURE__ */ new Map();
100
100
  for (const { file: e } of t)
101
- r.set(e.fileName, { endpoints: [], fileName: e.fileName, timing: 0, endpointTimings: [] });
101
+ g.set(e.fileName, { endpoints: [], fileName: e.fileName, timing: 0, endpointTimings: [] });
102
102
  if (t.length <= V)
103
103
  for (const { file: e } of t) {
104
- const { endpoints: i, endpointTimings: o } = q(e), s = r.get(e.fileName);
105
- s.endpoints = i, s.endpointTimings = o, s.timing = o.reduce((F, T) => F + T.timing, 0);
104
+ const { endpoints: o, endpointTimings: i } = q(e), r = g.get(e.fileName);
105
+ r.endpoints = o, r.endpointTimings = i, r.timing = i.reduce((y, s) => y + s.timing, 0);
106
106
  }
107
107
  else {
108
- const e = t.map(({ file: s }) => ({
109
- fileName: s.fileName,
108
+ const e = t.map(({ file: r }) => ({
109
+ fileName: r.fileName,
110
110
  task: {
111
111
  taskId: $.randomUUID(),
112
112
  tsconfigPath: n.tsconfigPath,
113
- sourceFilePath: s.sourceFile.getFilePath(),
114
- routerNames: s.routers.named,
115
- filterEndpointPaths: p
113
+ sourceFilePath: r.sourceFile.getFilePath(),
114
+ routerNames: r.routers.named,
115
+ filterEndpointPaths: d
116
116
  }
117
- })), i = new D(B(), e.length);
118
- let o;
117
+ })), o = new D(B(), e.length);
118
+ let i;
119
119
  try {
120
- o = await i.runAll(e.map((s) => s.task));
120
+ i = await o.runAll(e.map((r) => r.task));
121
121
  } finally {
122
- i.terminate();
122
+ o.terminate();
123
123
  }
124
- for (let s = 0; s < o.length; s++) {
125
- const F = o[s], T = e[s].fileName, N = r.get(T);
126
- if ("error" in F) {
127
- g.error(`[${T}] Worker error: ${F.error}`);
124
+ for (let r = 0; r < i.length; r++) {
125
+ const y = i[r], s = e[r].fileName, N = g.get(s);
126
+ if ("error" in y) {
127
+ F.error(`[${s}] Worker error: ${y.error}`);
128
128
  continue;
129
129
  }
130
- N.endpoints = F.endpoints, N.endpointTimings = F.endpointTimings, N.timing = F.endpointTimings.reduce((a, y) => a + y.timing, 0);
130
+ N.endpoints = y.endpoints, N.endpointTimings = y.endpointTimings, N.timing = y.endpointTimings.reduce((p, M) => p + M.timing, 0);
131
131
  }
132
132
  }
133
- for (const { file: e, timestamp: i } of t) {
134
- const o = r.get(e.fileName);
135
- o.endpoints.length > 0 && P.cacheResults(e.sourceFile, i, n.cachePath, o.endpoints);
133
+ for (const { file: e, timestamp: o } of t) {
134
+ const i = g.get(e.fileName);
135
+ i.endpoints.length > 0 && R.cacheResults(e.sourceFile, o, n.cachePath, i.endpoints);
136
136
  }
137
- const d = [...u, ...Array.from(r.values())];
138
- return c !== "off" && g.info(`Router analysis took ${Math.round(performance.now() - m)}ms`), c === "stats" ? d.map((e) => ({ fileName: e.fileName, timeTaken: e.timing })).sort((e, i) => i.timeTaken - e.timeTaken).filter((e) => e.timeTaken > 500).forEach((e) => {
139
- g.info(`- [${e.fileName}] Took ${Math.round(e.timeTaken)}ms to analyze`);
140
- }) : c === "debug" && d.map((e) => ({ fileName: e.fileName, timeTaken: e.timing, endpointTimings: e.endpointTimings })).sort((e, i) => i.timeTaken - e.timeTaken).forEach((e) => {
141
- g.info(`- [${e.fileName}] Took ${Math.round(e.timeTaken)}ms to analyze`), e.endpointTimings.sort((i, o) => o.timing - i.timing).forEach((i) => {
142
- g.info(` - ${i.method} ${i.path} (${Math.round(i.timing)}ms)`), i.sectionTimings.filter((o) => o.timing >= 1).sort((o, s) => s.timing - o.timing).forEach((o) => {
143
- g.info(` - ${o.section}: ${Math.round(o.timing)}ms`);
137
+ const T = [...f, ...Array.from(g.values())];
138
+ return l !== "off" && F.info(`Router analysis took ${Math.round(performance.now() - m)}ms`), l === "stats" ? T.map((e) => ({ fileName: e.fileName, timeTaken: e.timing })).sort((e, o) => o.timeTaken - e.timeTaken).filter((e) => e.timeTaken > 500).forEach((e) => {
139
+ F.info(`- [${e.fileName}] Took ${Math.round(e.timeTaken)}ms to analyze`);
140
+ }) : l === "debug" && T.map((e) => ({ fileName: e.fileName, timeTaken: e.timing, endpointTimings: e.endpointTimings })).sort((e, o) => o.timeTaken - e.timeTaken).forEach((e) => {
141
+ F.info(`- [${e.fileName}] Took ${Math.round(e.timeTaken)}ms to analyze`), e.endpointTimings.sort((o, i) => i.timing - o.timing).forEach((o) => {
142
+ F.info(` - ${o.method} ${o.path} (${Math.round(o.timing)}ms)`), o.sectionTimings.filter((i) => i.timing >= 1).sort((i, r) => r.timing - i.timing).forEach((i) => {
143
+ F.info(` - ${i.section}: ${Math.round(i.timing)}ms`);
144
144
  });
145
145
  });
146
- }), d.flatMap((e) => e.endpoints);
147
- }, q = (l, n) => {
148
- const p = [], c = [], u = ["get", "post", "put", "delete", "del", "patch"].join("|");
149
- return l.routers.named.forEach((t) => {
150
- const r = new RegExp(`${t}\\.(?:${u})`);
151
- l.sourceFile.forEachChild((d) => {
152
- const e = d.getText();
153
- if (r.test(e)) {
154
- W(d);
155
- const i = performance.now(), { endpoint: o, sectionTimings: s } = H(d, l.fileName);
156
- c.push({
157
- method: o.method,
158
- path: o.path,
159
- timing: performance.now() - i,
160
- sectionTimings: s
161
- }), p.push(o);
146
+ }), T.flatMap((e) => e.endpoints);
147
+ }, q = (a, n) => {
148
+ const d = [], l = [], f = ["get", "post", "put", "delete", "del", "patch"].join("|");
149
+ return a.routers.named.forEach((t) => {
150
+ const c = new RegExp(`${t}\\.(?:${f})`);
151
+ a.sourceFile.forEachChild((u) => {
152
+ const g = u.getText();
153
+ if (c.test(g)) {
154
+ W(u);
155
+ const T = performance.now(), { endpoint: e, sectionTimings: o } = H(u, a.fileName);
156
+ l.push({
157
+ method: e.method,
158
+ path: e.path,
159
+ timing: performance.now() - T,
160
+ sectionTimings: o
161
+ }), d.push(e);
162
162
  }
163
163
  });
164
- }), { endpoints: p, endpointTimings: c };
165
- }, G = (l) => {
166
- const n = M({
167
- sourceFile: l,
164
+ }), { endpoints: d, endpointTimings: l };
165
+ }, G = (a) => {
166
+ const n = P({
167
+ sourceFile: a,
168
168
  originalName: "useApiHeader"
169
169
  });
170
170
  if (!n)
171
171
  return null;
172
- const p = l.forEachChildAsArray().filter((t) => t.isKind(x.ExpressionStatement)).find((t) => n && t.getText().startsWith(n));
173
- if (!p)
172
+ const d = a.forEachChildAsArray().filter((t) => t.isKind(x.ExpressionStatement)).find((t) => n && t.getText().startsWith(n));
173
+ if (!d)
174
174
  return null;
175
- const c = p.getFirstDescendantByKindOrThrow(x.ObjectLiteralExpression), m = I(c), u = (t) => typeof t == "string" || Array.isArray(t) && t.every((r) => typeof r == "string") ? t : t.reduce((r, d) => typeof d == "string" ? r : {
176
- ...r,
177
- [d.identifier]: u(d.value)
175
+ const l = d.getFirstDescendantByKindOrThrow(x.ObjectLiteralExpression), m = I(l), f = (t) => typeof t == "string" || Array.isArray(t) && t.every((c) => typeof c == "string") ? t : t.reduce((c, u) => typeof u == "string" ? c : {
176
+ ...c,
177
+ [u.identifier]: f(u.value)
178
178
  }, {});
179
- return u(m);
180
- }, J = (l) => {
181
- const n = [], p = M({
182
- sourceFile: l,
179
+ return f(m);
180
+ }, J = (a) => {
181
+ const n = [], d = P({
182
+ sourceFile: a,
183
183
  originalName: "useExposeApiModel"
184
- }), c = M({
185
- sourceFile: l,
184
+ }), l = P({
185
+ sourceFile: a,
186
186
  originalName: "useExposeNamedApiModels"
187
187
  });
188
- return l.forEachChildAsArray().filter((m) => m.isKind(x.ExpressionStatement)).map((m) => {
189
- if (p && m.getText().startsWith(p)) {
190
- const r = (m.getFirstChild()?.getChildrenOfKind(x.SyntaxList) || [])[0].getFirstChild();
191
- if (!r)
188
+ return a.forEachChildAsArray().filter((m) => m.isKind(x.ExpressionStatement)).map((m) => {
189
+ if (d && m.getText().startsWith(d)) {
190
+ const c = (m.getFirstChild()?.getChildrenOfKind(x.SyntaxList) || [])[0].getFirstChild();
191
+ if (!c)
192
192
  return;
193
- n.push(K(r));
193
+ n.push(K(c));
194
194
  return;
195
195
  }
196
- if (c && m.getText().startsWith(c)) {
197
- const r = (m.getFirstChild()?.getChildrenOfKind(x.SyntaxList) || [])[0].getFirstChild();
198
- if (!r)
196
+ if (l && m.getText().startsWith(l)) {
197
+ const c = (m.getFirstChild()?.getChildrenOfKind(x.SyntaxList) || [])[0].getFirstChild();
198
+ if (!c)
199
199
  return;
200
- _(r).forEach((e) => n.push(e));
200
+ _(c).forEach((g) => n.push(g));
201
201
  }
202
202
  }), n;
203
203
  };
@@ -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 * Number of uncached files at or below which analysis runs inline on the (already-warm) main-thread\n * Project instead of fanning out to worker threads. Each worker pays a multi-second cold-start to\n * build its own Project, so parallelism only wins once there are several files to share that cost.\n */\nconst INLINE_ANALYSIS_FILE_THRESHOLD = 2\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\ttype FileResult = {\n\t\tendpoints: EndpointData[]\n\t\tfileName: string\n\t\ttiming: number\n\t\tendpointTimings: EndpointTiming[]\n\t}\n\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\t// The caller (prepareOpenApiSpec) already built and warmed a ts-morph Project on this thread, so\n\t// inline analysis runs against a hot type-checker. A worker, by contrast, must spawn a thread and\n\t// build its own Project from scratch — a multi-second cold-start. That cold-start only pays off\n\t// when there are enough uncached files that spreading the parse work across workers beats it; below\n\t// the threshold (e.g. the common single-file incremental rebuild), inline is strictly faster.\n\tif (uncached.length <= INLINE_ANALYSIS_FILE_THRESHOLD) {\n\t\tfor (const { file } of uncached) {\n\t\t\tconst { endpoints, endpointTimings } = analyzeSourceFileEndpoints(file, filterEndpointPaths)\n\t\t\tconst fileResult = byFile.get(file.fileName)!\n\t\t\tfileResult.endpoints = endpoints\n\t\t\tfileResult.endpointTimings = endpointTimings\n\t\t\tfileResult.timing = endpointTimings.reduce((sum, t) => sum + t.timing, 0)\n\t\t}\n\t} else {\n\t\t// One task per file: each worker analyzes a whole file in a single pass, paying its Project\n\t\t// cold-start once and reusing the warmed-up checker for every endpoint in that file. Cap the\n\t\t// pool at one worker per file so we never spin up a worker with nothing to do.\n\t\ttype FileTask = { task: WorkerTask; fileName: string }\n\t\tconst allTasks: FileTask[] = uncached.map(({ file }) => ({\n\t\t\tfileName: file.fileName,\n\t\t\ttask: {\n\t\t\t\ttaskId: crypto.randomUUID(),\n\t\t\t\ttsconfigPath: config.tsconfigPath,\n\t\t\t\tsourceFilePath: file.sourceFile.getFilePath(),\n\t\t\t\trouterNames: file.routers.named,\n\t\t\t\tfilterEndpointPaths,\n\t\t\t},\n\t\t}))\n\n\t\tconst pool = new WorkerPool(resolveWorkerUrl(), allTasks.length)\n\t\tlet results: WorkerResult[]\n\t\ttry {\n\t\t\tresults = await pool.runAll(allTasks.map((ft) => ft.task))\n\t\t} finally {\n\t\t\tpool.terminate()\n\t\t}\n\n\t\t// Each result maps 1:1 to a file task.\n\t\tfor (let i = 0; i < results.length; i++) {\n\t\t\tconst result = results[i]\n\t\t\tconst fileName = allTasks[i].fileName\n\t\t\tconst fileResult = byFile.get(fileName)!\n\n\t\t\tif ('error' in result) {\n\t\t\t\tLogger.error(`[${fileName}] Worker error: ${result.error}`)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tfileResult.endpoints = result.endpoints\n\t\t\tfileResult.endpointTimings = result.endpointTimings\n\t\t\tfileResult.timing = result.endpointTimings.reduce((sum, t) => sum + t.timing, 0)\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","INLINE_ANALYSIS_FILE_THRESHOLD","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","byFile","endpointTimings","analyzeSourceFileEndpoints","fileResult","sum","t","allTasks","crypto","pool","WorkerPool","results","ft","i","result","fileName","analyzedFiles","a","b","ep","s","joinedOperations","routerName","routerPattern","node","nodeText","resolveEndpointPath","t1","endpoint","sectionTimings","parseEndpoint","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, MtimeCache, 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 * Number of uncached files at or below which analysis runs inline on the (already-warm) main-thread\n * Project instead of fanning out to worker threads. Each worker pays a multi-second cold-start to\n * build its own Project, so parallelism only wins once there are several files to share that cost.\n */\nconst INLINE_ANALYSIS_FILE_THRESHOLD = 2\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\tconst freshnessCheckStart = performance.now()\n\tconst mtimeCache: MtimeCache = new Map()\n\tfor (const file of files) {\n\t\tconst timestamp = getSourceFileTimestamp(file.sourceFile, config.timestampCache, mtimeCache)\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\tif (profiling !== 'off') {\n\t\tLogger.info(`Cache freshness check took ${Math.round(performance.now() - freshnessCheckStart)}ms`)\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\ttype FileResult = {\n\t\tendpoints: EndpointData[]\n\t\tfileName: string\n\t\ttiming: number\n\t\tendpointTimings: EndpointTiming[]\n\t}\n\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\t// The caller (prepareOpenApiSpec) already built and warmed a ts-morph Project on this thread, so\n\t// inline analysis runs against a hot type-checker. A worker, by contrast, must spawn a thread and\n\t// build its own Project from scratch — a multi-second cold-start. That cold-start only pays off\n\t// when there are enough uncached files that spreading the parse work across workers beats it; below\n\t// the threshold (e.g. the common single-file incremental rebuild), inline is strictly faster.\n\tif (uncached.length <= INLINE_ANALYSIS_FILE_THRESHOLD) {\n\t\tfor (const { file } of uncached) {\n\t\t\tconst { endpoints, endpointTimings } = analyzeSourceFileEndpoints(file, filterEndpointPaths)\n\t\t\tconst fileResult = byFile.get(file.fileName)!\n\t\t\tfileResult.endpoints = endpoints\n\t\t\tfileResult.endpointTimings = endpointTimings\n\t\t\tfileResult.timing = endpointTimings.reduce((sum, t) => sum + t.timing, 0)\n\t\t}\n\t} else {\n\t\t// One task per file: each worker analyzes a whole file in a single pass, paying its Project\n\t\t// cold-start once and reusing the warmed-up checker for every endpoint in that file. Cap the\n\t\t// pool at one worker per file so we never spin up a worker with nothing to do.\n\t\ttype FileTask = { task: WorkerTask; fileName: string }\n\t\tconst allTasks: FileTask[] = uncached.map(({ file }) => ({\n\t\t\tfileName: file.fileName,\n\t\t\ttask: {\n\t\t\t\ttaskId: crypto.randomUUID(),\n\t\t\t\ttsconfigPath: config.tsconfigPath,\n\t\t\t\tsourceFilePath: file.sourceFile.getFilePath(),\n\t\t\t\trouterNames: file.routers.named,\n\t\t\t\tfilterEndpointPaths,\n\t\t\t},\n\t\t}))\n\n\t\tconst pool = new WorkerPool(resolveWorkerUrl(), allTasks.length)\n\t\tlet results: WorkerResult[]\n\t\ttry {\n\t\t\tresults = await pool.runAll(allTasks.map((ft) => ft.task))\n\t\t} finally {\n\t\t\tpool.terminate()\n\t\t}\n\n\t\t// Each result maps 1:1 to a file task.\n\t\tfor (let i = 0; i < results.length; i++) {\n\t\t\tconst result = results[i]\n\t\t\tconst fileName = allTasks[i].fileName\n\t\t\tconst fileResult = byFile.get(fileName)!\n\n\t\t\tif ('error' in result) {\n\t\t\t\tLogger.error(`[${fileName}] Worker error: ${result.error}`)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tfileResult.endpoints = result.endpoints\n\t\t\tfileResult.endpointTimings = result.endpointTimings\n\t\t\tfileResult.timing = result.endpointTimings.reduce((sum, t) => sum + t.timing, 0)\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","INLINE_ANALYSIS_FILE_THRESHOLD","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","freshnessCheckStart","mtimeCache","timestamp","getSourceFileTimestamp","hit","SourceFileCache","f","byFile","endpointTimings","analyzeSourceFileEndpoints","fileResult","sum","t","allTasks","crypto","pool","WorkerPool","results","ft","i","result","fileName","analyzedFiles","a","b","ep","s","joinedOperations","routerName","routerPattern","node","nodeText","resolveEndpointPath","t1","endpoint","sectionTimings","parseEndpoint","sourceFile","nameOfUseApiHeader","discoverImportedName","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";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("fs"),l=require("ts-morph"),c=require("../../utils/logger.cjs");function u(e,n){const o=p(e,n),r=o.map(s=>d.statSync(s.getFilePath()).mtimeMs),t=Math.max(...r),a=e.getFilePath().split("/").pop(),i=o.length;return c.Logger.debug(`[${a}] Found ${i} imports, latest touched at ${c.formatTimestamp(t)}.`),t}function p(e,n){const o=e.getFilePath().split("/").pop();if(!o)return[];const r=n[e.getFilePath()];if(r)return r.dependencies;n[e.getFilePath()]={dependencies:[]};try{const t=[e],a=e.getDescendantsOfKind(l.SyntaxKind.ImportDeclaration);for(const i of a){const s=i.getModuleSpecifierSourceFile();if(!s){c.Logger.debug(`[${o}] Could not resolve import ${i.getModuleSpecifierValue()}.`);continue}const g=p(s,n);t.push(...g)}return n[e.getFilePath()]={dependencies:t},t}catch(t){return c.Logger.warn(`[${o}] Caught an error while processing imports:`,t),n[e.getFilePath()]={dependencies:[]},[]}}exports.getSourceFileTimestamp=u;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const f=require("fs"),m=require("ts-morph"),a=require("../../utils/logger.cjs");function h(e,t,o=new Map){const r=u(e,t),n=r.map(d=>{const p=d.getFilePath(),g=o.get(p);if(g!==void 0)return g;const l=f.statSync(p).mtimeMs;return o.set(p,l),l}),i=Math.max(...n),s=e.getFilePath().split("/").pop(),c=r.length;return a.Logger.debug(`[${s}] Found ${c} imports, latest touched at ${a.formatTimestamp(i)}.`),i}function u(e,t){const o=e.getFilePath().split("/").pop();if(!o)return[];const r=t[e.getFilePath()];if(r)return r.dependencies;t[e.getFilePath()]={dependencies:[]};try{const n=[e],i=e.getDescendantsOfKind(m.SyntaxKind.ImportDeclaration);for(const s of i){const c=s.getModuleSpecifierSourceFile();if(!c){a.Logger.debug(`[${o}] Could not resolve import ${s.getModuleSpecifierValue()}.`);continue}const d=u(c,t);n.push(...d)}return t[e.getFilePath()]={dependencies:n},n}catch(n){return a.Logger.warn(`[${o}] Caught an error while processing imports:`,n),t[e.getFilePath()]={dependencies:[]},[]}}exports.getSourceFileTimestamp=h;
2
2
  //# sourceMappingURL=getSourceFileTimestamp.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"getSourceFileTimestamp.cjs","sources":["../../../src/openapi/analyzerModule/getSourceFileTimestamp.ts"],"sourcesContent":["import fs from 'fs'\nimport { SourceFile, SyntaxKind } from 'ts-morph'\n\nimport { formatTimestamp, Logger } from '../../utils/logger'\n\nexport type TimestampCache = Record<string, { dependencies: SourceFile[] }>\n\nexport function getSourceFileTimestamp(sourceFile: SourceFile, timestampCache: TimestampCache) {\n\tconst dependencies = getFileDependencies(sourceFile, timestampCache)\n\tconst timestamps = dependencies.map((dep) => {\n\t\tconst stat = fs.statSync(dep.getFilePath())\n\t\treturn stat.mtimeMs\n\t})\n\tconst latestTimestamp = Math.max(...timestamps)\n\n\tconst fileName = sourceFile.getFilePath().split('/').pop()\n\tconst depsCount = dependencies.length\n\tLogger.debug(\n\t\t`[${fileName}] Found ${depsCount} imports, latest touched at ${formatTimestamp(latestTimestamp)}.`,\n\t)\n\n\treturn latestTimestamp\n}\n\nfunction getFileDependencies(sourceFile: SourceFile, timestampCache: TimestampCache): SourceFile[] {\n\tconst fileName = sourceFile.getFilePath().split('/').pop()\n\tif (!fileName) {\n\t\treturn []\n\t}\n\n\tconst cacheHit = timestampCache[sourceFile.getFilePath()]\n\tif (cacheHit) {\n\t\treturn cacheHit.dependencies\n\t}\n\n\t// Initialize cache entry early to prevent circular dependencies\n\ttimestampCache[sourceFile.getFilePath()] = { dependencies: [] }\n\n\ttry {\n\t\tconst results = [sourceFile]\n\n\t\tconst importDeclarations = sourceFile.getDescendantsOfKind(SyntaxKind.ImportDeclaration)\n\n\t\tfor (const declaration of importDeclarations) {\n\t\t\tconst importedSourceFile = declaration.getModuleSpecifierSourceFile()\n\t\t\tif (!importedSourceFile) {\n\t\t\t\tLogger.debug(`[${fileName}] Could not resolve import ${declaration.getModuleSpecifierValue()}.`)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tconst deps = getFileDependencies(importedSourceFile, timestampCache)\n\t\t\tresults.push(...deps)\n\t\t}\n\n\t\ttimestampCache[sourceFile.getFilePath()] = { dependencies: results }\n\t\treturn results\n\t} catch (error) {\n\t\tLogger.warn(`[${fileName}] Caught an error while processing imports:`, error)\n\t\ttimestampCache[sourceFile.getFilePath()] = { dependencies: [] }\n\t\treturn []\n\t}\n}\n"],"names":["getSourceFileTimestamp","sourceFile","timestampCache","dependencies","getFileDependencies","timestamps","dep","fs","latestTimestamp","fileName","depsCount","Logger","formatTimestamp","cacheHit","results","importDeclarations","SyntaxKind","declaration","importedSourceFile","deps","error"],"mappings":"gKAOgB,SAAAA,EAAuBC,EAAwBC,EAAgC,CACxF,MAAAC,EAAeC,EAAoBH,EAAYC,CAAc,EAC7DG,EAAaF,EAAa,IAAKG,GACvBC,EAAG,SAASD,EAAI,aAAa,EAC9B,OACZ,EACKE,EAAkB,KAAK,IAAI,GAAGH,CAAU,EAExCI,EAAWR,EAAW,YAAA,EAAc,MAAM,GAAG,EAAE,IAAI,EACnDS,EAAYP,EAAa,OACxBQ,OAAAA,EAAAA,OAAA,MACN,IAAIF,CAAQ,WAAWC,CAAS,+BAA+BE,EAAA,gBAAgBJ,CAAe,CAAC,GAChG,EAEOA,CACR,CAEA,SAASJ,EAAoBH,EAAwBC,EAA8C,CAClG,MAAMO,EAAWR,EAAW,YAAA,EAAc,MAAM,GAAG,EAAE,IAAI,EACzD,GAAI,CAACQ,EACJ,MAAO,CAAC,EAGT,MAAMI,EAAWX,EAAeD,EAAW,YAAA,CAAa,EACxD,GAAIY,EACH,OAAOA,EAAS,aAIjBX,EAAeD,EAAW,YAAY,CAAC,EAAI,CAAE,aAAc,CAAA,CAAG,EAE1D,GAAA,CACG,MAAAa,EAAU,CAACb,CAAU,EAErBc,EAAqBd,EAAW,qBAAqBe,EAAAA,WAAW,iBAAiB,EAEvF,UAAWC,KAAeF,EAAoB,CACvC,MAAAG,EAAqBD,EAAY,6BAA6B,EACpE,GAAI,CAACC,EAAoB,CACxBP,SAAO,MAAM,IAAIF,CAAQ,8BAA8BQ,EAAY,yBAAyB,GAAG,EAC/F,QAAA,CAGK,MAAAE,EAAOf,EAAoBc,EAAoBhB,CAAc,EAC3DY,EAAA,KAAK,GAAGK,CAAI,CAAA,CAGrB,OAAAjB,EAAeD,EAAW,YAAY,CAAC,EAAI,CAAE,aAAca,CAAQ,EAC5DA,QACCM,EAAO,CACfT,OAAAA,EAAA,OAAO,KAAK,IAAIF,CAAQ,8CAA+CW,CAAK,EAC5ElB,EAAeD,EAAW,YAAY,CAAC,EAAI,CAAE,aAAc,CAAA,CAAG,EACvD,CAAC,CAAA,CAEV"}
1
+ {"version":3,"file":"getSourceFileTimestamp.cjs","sources":["../../../src/openapi/analyzerModule/getSourceFileTimestamp.ts"],"sourcesContent":["import fs from 'fs'\nimport { SourceFile, SyntaxKind } from 'ts-morph'\n\nimport { formatTimestamp, Logger } from '../../utils/logger'\n\nexport type TimestampCache = Record<string, { dependencies: SourceFile[] }>\n\n/**\n * Per-run cache of file mtimes keyed by absolute path. Router files share large parts of their\n * transitive import graphs (common types, utils, models), so without this the same dependency gets\n * `statSync`-ed once per importing router — tens of thousands of redundant syscalls across a project\n * with dozens of routers. mtimes don't change mid-run, so memoizing is safe.\n */\nexport type MtimeCache = Map<string, number>\n\nexport function getSourceFileTimestamp(\n\tsourceFile: SourceFile,\n\ttimestampCache: TimestampCache,\n\tmtimeCache: MtimeCache = new Map(),\n) {\n\tconst dependencies = getFileDependencies(sourceFile, timestampCache)\n\tconst timestamps = dependencies.map((dep) => {\n\t\tconst depPath = dep.getFilePath()\n\t\tconst cached = mtimeCache.get(depPath)\n\t\tif (cached !== undefined) {\n\t\t\treturn cached\n\t\t}\n\t\tconst mtime = fs.statSync(depPath).mtimeMs\n\t\tmtimeCache.set(depPath, mtime)\n\t\treturn mtime\n\t})\n\tconst latestTimestamp = Math.max(...timestamps)\n\n\tconst fileName = sourceFile.getFilePath().split('/').pop()\n\tconst depsCount = dependencies.length\n\tLogger.debug(\n\t\t`[${fileName}] Found ${depsCount} imports, latest touched at ${formatTimestamp(latestTimestamp)}.`,\n\t)\n\n\treturn latestTimestamp\n}\n\nfunction getFileDependencies(sourceFile: SourceFile, timestampCache: TimestampCache): SourceFile[] {\n\tconst fileName = sourceFile.getFilePath().split('/').pop()\n\tif (!fileName) {\n\t\treturn []\n\t}\n\n\tconst cacheHit = timestampCache[sourceFile.getFilePath()]\n\tif (cacheHit) {\n\t\treturn cacheHit.dependencies\n\t}\n\n\t// Initialize cache entry early to prevent circular dependencies\n\ttimestampCache[sourceFile.getFilePath()] = { dependencies: [] }\n\n\ttry {\n\t\tconst results = [sourceFile]\n\n\t\tconst importDeclarations = sourceFile.getDescendantsOfKind(SyntaxKind.ImportDeclaration)\n\n\t\tfor (const declaration of importDeclarations) {\n\t\t\tconst importedSourceFile = declaration.getModuleSpecifierSourceFile()\n\t\t\tif (!importedSourceFile) {\n\t\t\t\tLogger.debug(`[${fileName}] Could not resolve import ${declaration.getModuleSpecifierValue()}.`)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tconst deps = getFileDependencies(importedSourceFile, timestampCache)\n\t\t\tresults.push(...deps)\n\t\t}\n\n\t\ttimestampCache[sourceFile.getFilePath()] = { dependencies: results }\n\t\treturn results\n\t} catch (error) {\n\t\tLogger.warn(`[${fileName}] Caught an error while processing imports:`, error)\n\t\ttimestampCache[sourceFile.getFilePath()] = { dependencies: [] }\n\t\treturn []\n\t}\n}\n"],"names":["getSourceFileTimestamp","sourceFile","timestampCache","mtimeCache","dependencies","getFileDependencies","timestamps","dep","depPath","cached","mtime","fs","latestTimestamp","fileName","depsCount","Logger","formatTimestamp","cacheHit","results","importDeclarations","SyntaxKind","declaration","importedSourceFile","deps","error"],"mappings":"gKAeO,SAASA,EACfC,EACAC,EACAC,EAAyB,IAAI,IAC5B,CACK,MAAAC,EAAeC,EAAoBJ,EAAYC,CAAc,EAC7DI,EAAaF,EAAa,IAAKG,GAAQ,CACtC,MAAAC,EAAUD,EAAI,YAAY,EAC1BE,EAASN,EAAW,IAAIK,CAAO,EACrC,GAAIC,IAAW,OACP,OAAAA,EAER,MAAMC,EAAQC,EAAG,SAASH,CAAO,EAAE,QACxB,OAAAL,EAAA,IAAIK,EAASE,CAAK,EACtBA,CAAA,CACP,EACKE,EAAkB,KAAK,IAAI,GAAGN,CAAU,EAExCO,EAAWZ,EAAW,YAAA,EAAc,MAAM,GAAG,EAAE,IAAI,EACnDa,EAAYV,EAAa,OACxBW,OAAAA,EAAAA,OAAA,MACN,IAAIF,CAAQ,WAAWC,CAAS,+BAA+BE,EAAA,gBAAgBJ,CAAe,CAAC,GAChG,EAEOA,CACR,CAEA,SAASP,EAAoBJ,EAAwBC,EAA8C,CAClG,MAAMW,EAAWZ,EAAW,YAAA,EAAc,MAAM,GAAG,EAAE,IAAI,EACzD,GAAI,CAACY,EACJ,MAAO,CAAC,EAGT,MAAMI,EAAWf,EAAeD,EAAW,YAAA,CAAa,EACxD,GAAIgB,EACH,OAAOA,EAAS,aAIjBf,EAAeD,EAAW,YAAY,CAAC,EAAI,CAAE,aAAc,CAAA,CAAG,EAE1D,GAAA,CACG,MAAAiB,EAAU,CAACjB,CAAU,EAErBkB,EAAqBlB,EAAW,qBAAqBmB,EAAAA,WAAW,iBAAiB,EAEvF,UAAWC,KAAeF,EAAoB,CACvC,MAAAG,EAAqBD,EAAY,6BAA6B,EACpE,GAAI,CAACC,EAAoB,CACxBP,SAAO,MAAM,IAAIF,CAAQ,8BAA8BQ,EAAY,yBAAyB,GAAG,EAC/F,QAAA,CAGK,MAAAE,EAAOlB,EAAoBiB,EAAoBpB,CAAc,EAC3DgB,EAAA,KAAK,GAAGK,CAAI,CAAA,CAGrB,OAAArB,EAAeD,EAAW,YAAY,CAAC,EAAI,CAAE,aAAciB,CAAQ,EAC5DA,QACCM,EAAO,CACfT,OAAAA,EAAA,OAAO,KAAK,IAAIF,CAAQ,8CAA+CW,CAAK,EAC5EtB,EAAeD,EAAW,YAAY,CAAC,EAAI,CAAE,aAAc,CAAA,CAAG,EACvD,CAAC,CAAA,CAEV"}
@@ -3,5 +3,6 @@ import { SourceFile } from 'ts-morph';
3
3
  export type TimestampCache = Record<string, {
4
4
  dependencies: SourceFile[];
5
5
  }>;
6
- export declare function getSourceFileTimestamp(sourceFile: SourceFile, timestampCache: TimestampCache): number;
6
+ export type MtimeCache = Map<string, number>;
7
+ export declare function getSourceFileTimestamp(sourceFile: SourceFile, timestampCache: TimestampCache, mtimeCache?: MtimeCache): number;
7
8
  //# sourceMappingURL=getSourceFileTimestamp.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"getSourceFileTimestamp.d.ts","sourceRoot":"","sources":["../../../src/openapi/analyzerModule/getSourceFileTimestamp.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAc,MAAM,UAAU,CAAA;AAIjD,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE;IAAE,YAAY,EAAE,UAAU,EAAE,CAAA;CAAE,CAAC,CAAA;AAE3E,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,UAAU,EAAE,cAAc,EAAE,cAAc,UAe5F"}
1
+ {"version":3,"file":"getSourceFileTimestamp.d.ts","sourceRoot":"","sources":["../../../src/openapi/analyzerModule/getSourceFileTimestamp.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAc,MAAM,UAAU,CAAA;AAIjD,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE;IAAE,YAAY,EAAE,UAAU,EAAE,CAAA;CAAE,CAAC,CAAA;AAQ3E,MAAM,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAE5C,wBAAgB,sBAAsB,CACrC,UAAU,EAAE,UAAU,EACtB,cAAc,EAAE,cAAc,EAC9B,UAAU,GAAE,UAAsB,UAsBlC"}
@@ -1,37 +1,43 @@
1
- import l from "fs";
2
- import { SyntaxKind as g } from "ts-morph";
3
- import { Logger as p, formatTimestamp as m } from "../../utils/logger.mjs";
4
- function P(t, n) {
5
- const o = d(t, n), i = o.map((s) => l.statSync(s.getFilePath()).mtimeMs), e = Math.max(...i), a = t.getFilePath().split("/").pop(), r = o.length;
6
- return p.debug(
7
- `[${a}] Found ${r} imports, latest touched at ${m(e)}.`
8
- ), e;
1
+ import m from "fs";
2
+ import { SyntaxKind as u } from "ts-morph";
3
+ import { Logger as d, formatTimestamp as h } from "../../utils/logger.mjs";
4
+ function F(e, t, o = /* @__PURE__ */ new Map()) {
5
+ const i = f(e, t), n = i.map((a) => {
6
+ const c = a.getFilePath(), l = o.get(c);
7
+ if (l !== void 0)
8
+ return l;
9
+ const g = m.statSync(c).mtimeMs;
10
+ return o.set(c, g), g;
11
+ }), r = Math.max(...n), s = e.getFilePath().split("/").pop(), p = i.length;
12
+ return d.debug(
13
+ `[${s}] Found ${p} imports, latest touched at ${h(r)}.`
14
+ ), r;
9
15
  }
10
- function d(t, n) {
11
- const o = t.getFilePath().split("/").pop();
16
+ function f(e, t) {
17
+ const o = e.getFilePath().split("/").pop();
12
18
  if (!o)
13
19
  return [];
14
- const i = n[t.getFilePath()];
20
+ const i = t[e.getFilePath()];
15
21
  if (i)
16
22
  return i.dependencies;
17
- n[t.getFilePath()] = { dependencies: [] };
23
+ t[e.getFilePath()] = { dependencies: [] };
18
24
  try {
19
- const e = [t], a = t.getDescendantsOfKind(g.ImportDeclaration);
20
- for (const r of a) {
21
- const s = r.getModuleSpecifierSourceFile();
22
- if (!s) {
23
- p.debug(`[${o}] Could not resolve import ${r.getModuleSpecifierValue()}.`);
25
+ const n = [e], r = e.getDescendantsOfKind(u.ImportDeclaration);
26
+ for (const s of r) {
27
+ const p = s.getModuleSpecifierSourceFile();
28
+ if (!p) {
29
+ d.debug(`[${o}] Could not resolve import ${s.getModuleSpecifierValue()}.`);
24
30
  continue;
25
31
  }
26
- const c = d(s, n);
27
- e.push(...c);
32
+ const a = f(p, t);
33
+ n.push(...a);
28
34
  }
29
- return n[t.getFilePath()] = { dependencies: e }, e;
30
- } catch (e) {
31
- return p.warn(`[${o}] Caught an error while processing imports:`, e), n[t.getFilePath()] = { dependencies: [] }, [];
35
+ return t[e.getFilePath()] = { dependencies: n }, n;
36
+ } catch (n) {
37
+ return d.warn(`[${o}] Caught an error while processing imports:`, n), t[e.getFilePath()] = { dependencies: [] }, [];
32
38
  }
33
39
  }
34
40
  export {
35
- P as getSourceFileTimestamp
41
+ F as getSourceFileTimestamp
36
42
  };
37
43
  //# sourceMappingURL=getSourceFileTimestamp.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"getSourceFileTimestamp.mjs","sources":["../../../src/openapi/analyzerModule/getSourceFileTimestamp.ts"],"sourcesContent":["import fs from 'fs'\nimport { SourceFile, SyntaxKind } from 'ts-morph'\n\nimport { formatTimestamp, Logger } from '../../utils/logger'\n\nexport type TimestampCache = Record<string, { dependencies: SourceFile[] }>\n\nexport function getSourceFileTimestamp(sourceFile: SourceFile, timestampCache: TimestampCache) {\n\tconst dependencies = getFileDependencies(sourceFile, timestampCache)\n\tconst timestamps = dependencies.map((dep) => {\n\t\tconst stat = fs.statSync(dep.getFilePath())\n\t\treturn stat.mtimeMs\n\t})\n\tconst latestTimestamp = Math.max(...timestamps)\n\n\tconst fileName = sourceFile.getFilePath().split('/').pop()\n\tconst depsCount = dependencies.length\n\tLogger.debug(\n\t\t`[${fileName}] Found ${depsCount} imports, latest touched at ${formatTimestamp(latestTimestamp)}.`,\n\t)\n\n\treturn latestTimestamp\n}\n\nfunction getFileDependencies(sourceFile: SourceFile, timestampCache: TimestampCache): SourceFile[] {\n\tconst fileName = sourceFile.getFilePath().split('/').pop()\n\tif (!fileName) {\n\t\treturn []\n\t}\n\n\tconst cacheHit = timestampCache[sourceFile.getFilePath()]\n\tif (cacheHit) {\n\t\treturn cacheHit.dependencies\n\t}\n\n\t// Initialize cache entry early to prevent circular dependencies\n\ttimestampCache[sourceFile.getFilePath()] = { dependencies: [] }\n\n\ttry {\n\t\tconst results = [sourceFile]\n\n\t\tconst importDeclarations = sourceFile.getDescendantsOfKind(SyntaxKind.ImportDeclaration)\n\n\t\tfor (const declaration of importDeclarations) {\n\t\t\tconst importedSourceFile = declaration.getModuleSpecifierSourceFile()\n\t\t\tif (!importedSourceFile) {\n\t\t\t\tLogger.debug(`[${fileName}] Could not resolve import ${declaration.getModuleSpecifierValue()}.`)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tconst deps = getFileDependencies(importedSourceFile, timestampCache)\n\t\t\tresults.push(...deps)\n\t\t}\n\n\t\ttimestampCache[sourceFile.getFilePath()] = { dependencies: results }\n\t\treturn results\n\t} catch (error) {\n\t\tLogger.warn(`[${fileName}] Caught an error while processing imports:`, error)\n\t\ttimestampCache[sourceFile.getFilePath()] = { dependencies: [] }\n\t\treturn []\n\t}\n}\n"],"names":["getSourceFileTimestamp","sourceFile","timestampCache","dependencies","getFileDependencies","timestamps","dep","fs","latestTimestamp","fileName","depsCount","Logger","formatTimestamp","cacheHit","results","importDeclarations","SyntaxKind","declaration","importedSourceFile","deps","error"],"mappings":";;;AAOgB,SAAAA,EAAuBC,GAAwBC,GAAgC;AACxF,QAAAC,IAAeC,EAAoBH,GAAYC,CAAc,GAC7DG,IAAaF,EAAa,IAAI,CAACG,MACvBC,EAAG,SAASD,EAAI,aAAa,EAC9B,OACZ,GACKE,IAAkB,KAAK,IAAI,GAAGH,CAAU,GAExCI,IAAWR,EAAW,YAAA,EAAc,MAAM,GAAG,EAAE,IAAI,GACnDS,IAAYP,EAAa;AACxB,SAAAQ,EAAA;AAAA,IACN,IAAIF,CAAQ,WAAWC,CAAS,+BAA+BE,EAAgBJ,CAAe,CAAC;AAAA,EAChG,GAEOA;AACR;AAEA,SAASJ,EAAoBH,GAAwBC,GAA8C;AAClG,QAAMO,IAAWR,EAAW,YAAA,EAAc,MAAM,GAAG,EAAE,IAAI;AACzD,MAAI,CAACQ;AACJ,WAAO,CAAC;AAGT,QAAMI,IAAWX,EAAeD,EAAW,YAAA,CAAa;AACxD,MAAIY;AACH,WAAOA,EAAS;AAIjB,EAAAX,EAAeD,EAAW,YAAY,CAAC,IAAI,EAAE,cAAc,CAAA,EAAG;AAE1D,MAAA;AACG,UAAAa,IAAU,CAACb,CAAU,GAErBc,IAAqBd,EAAW,qBAAqBe,EAAW,iBAAiB;AAEvF,eAAWC,KAAeF,GAAoB;AACvC,YAAAG,IAAqBD,EAAY,6BAA6B;AACpE,UAAI,CAACC,GAAoB;AACxB,QAAAP,EAAO,MAAM,IAAIF,CAAQ,8BAA8BQ,EAAY,yBAAyB,GAAG;AAC/F;AAAA,MAAA;AAGK,YAAAE,IAAOf,EAAoBc,GAAoBhB,CAAc;AAC3D,MAAAY,EAAA,KAAK,GAAGK,CAAI;AAAA,IAAA;AAGrB,WAAAjB,EAAeD,EAAW,YAAY,CAAC,IAAI,EAAE,cAAca,EAAQ,GAC5DA;AAAA,WACCM,GAAO;AACf,WAAAT,EAAO,KAAK,IAAIF,CAAQ,+CAA+CW,CAAK,GAC5ElB,EAAeD,EAAW,YAAY,CAAC,IAAI,EAAE,cAAc,CAAA,EAAG,GACvD,CAAC;AAAA,EAAA;AAEV;"}
1
+ {"version":3,"file":"getSourceFileTimestamp.mjs","sources":["../../../src/openapi/analyzerModule/getSourceFileTimestamp.ts"],"sourcesContent":["import fs from 'fs'\nimport { SourceFile, SyntaxKind } from 'ts-morph'\n\nimport { formatTimestamp, Logger } from '../../utils/logger'\n\nexport type TimestampCache = Record<string, { dependencies: SourceFile[] }>\n\n/**\n * Per-run cache of file mtimes keyed by absolute path. Router files share large parts of their\n * transitive import graphs (common types, utils, models), so without this the same dependency gets\n * `statSync`-ed once per importing router — tens of thousands of redundant syscalls across a project\n * with dozens of routers. mtimes don't change mid-run, so memoizing is safe.\n */\nexport type MtimeCache = Map<string, number>\n\nexport function getSourceFileTimestamp(\n\tsourceFile: SourceFile,\n\ttimestampCache: TimestampCache,\n\tmtimeCache: MtimeCache = new Map(),\n) {\n\tconst dependencies = getFileDependencies(sourceFile, timestampCache)\n\tconst timestamps = dependencies.map((dep) => {\n\t\tconst depPath = dep.getFilePath()\n\t\tconst cached = mtimeCache.get(depPath)\n\t\tif (cached !== undefined) {\n\t\t\treturn cached\n\t\t}\n\t\tconst mtime = fs.statSync(depPath).mtimeMs\n\t\tmtimeCache.set(depPath, mtime)\n\t\treturn mtime\n\t})\n\tconst latestTimestamp = Math.max(...timestamps)\n\n\tconst fileName = sourceFile.getFilePath().split('/').pop()\n\tconst depsCount = dependencies.length\n\tLogger.debug(\n\t\t`[${fileName}] Found ${depsCount} imports, latest touched at ${formatTimestamp(latestTimestamp)}.`,\n\t)\n\n\treturn latestTimestamp\n}\n\nfunction getFileDependencies(sourceFile: SourceFile, timestampCache: TimestampCache): SourceFile[] {\n\tconst fileName = sourceFile.getFilePath().split('/').pop()\n\tif (!fileName) {\n\t\treturn []\n\t}\n\n\tconst cacheHit = timestampCache[sourceFile.getFilePath()]\n\tif (cacheHit) {\n\t\treturn cacheHit.dependencies\n\t}\n\n\t// Initialize cache entry early to prevent circular dependencies\n\ttimestampCache[sourceFile.getFilePath()] = { dependencies: [] }\n\n\ttry {\n\t\tconst results = [sourceFile]\n\n\t\tconst importDeclarations = sourceFile.getDescendantsOfKind(SyntaxKind.ImportDeclaration)\n\n\t\tfor (const declaration of importDeclarations) {\n\t\t\tconst importedSourceFile = declaration.getModuleSpecifierSourceFile()\n\t\t\tif (!importedSourceFile) {\n\t\t\t\tLogger.debug(`[${fileName}] Could not resolve import ${declaration.getModuleSpecifierValue()}.`)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tconst deps = getFileDependencies(importedSourceFile, timestampCache)\n\t\t\tresults.push(...deps)\n\t\t}\n\n\t\ttimestampCache[sourceFile.getFilePath()] = { dependencies: results }\n\t\treturn results\n\t} catch (error) {\n\t\tLogger.warn(`[${fileName}] Caught an error while processing imports:`, error)\n\t\ttimestampCache[sourceFile.getFilePath()] = { dependencies: [] }\n\t\treturn []\n\t}\n}\n"],"names":["getSourceFileTimestamp","sourceFile","timestampCache","mtimeCache","dependencies","getFileDependencies","timestamps","dep","depPath","cached","mtime","fs","latestTimestamp","fileName","depsCount","Logger","formatTimestamp","cacheHit","results","importDeclarations","SyntaxKind","declaration","importedSourceFile","deps","error"],"mappings":";;;AAeO,SAASA,EACfC,GACAC,GACAC,IAAyB,oBAAI,OAC5B;AACK,QAAAC,IAAeC,EAAoBJ,GAAYC,CAAc,GAC7DI,IAAaF,EAAa,IAAI,CAACG,MAAQ;AACtC,UAAAC,IAAUD,EAAI,YAAY,GAC1BE,IAASN,EAAW,IAAIK,CAAO;AACrC,QAAIC,MAAW;AACP,aAAAA;AAER,UAAMC,IAAQC,EAAG,SAASH,CAAO,EAAE;AACxB,WAAAL,EAAA,IAAIK,GAASE,CAAK,GACtBA;AAAA,EAAA,CACP,GACKE,IAAkB,KAAK,IAAI,GAAGN,CAAU,GAExCO,IAAWZ,EAAW,YAAA,EAAc,MAAM,GAAG,EAAE,IAAI,GACnDa,IAAYV,EAAa;AACxB,SAAAW,EAAA;AAAA,IACN,IAAIF,CAAQ,WAAWC,CAAS,+BAA+BE,EAAgBJ,CAAe,CAAC;AAAA,EAChG,GAEOA;AACR;AAEA,SAASP,EAAoBJ,GAAwBC,GAA8C;AAClG,QAAMW,IAAWZ,EAAW,YAAA,EAAc,MAAM,GAAG,EAAE,IAAI;AACzD,MAAI,CAACY;AACJ,WAAO,CAAC;AAGT,QAAMI,IAAWf,EAAeD,EAAW,YAAA,CAAa;AACxD,MAAIgB;AACH,WAAOA,EAAS;AAIjB,EAAAf,EAAeD,EAAW,YAAY,CAAC,IAAI,EAAE,cAAc,CAAA,EAAG;AAE1D,MAAA;AACG,UAAAiB,IAAU,CAACjB,CAAU,GAErBkB,IAAqBlB,EAAW,qBAAqBmB,EAAW,iBAAiB;AAEvF,eAAWC,KAAeF,GAAoB;AACvC,YAAAG,IAAqBD,EAAY,6BAA6B;AACpE,UAAI,CAACC,GAAoB;AACxB,QAAAP,EAAO,MAAM,IAAIF,CAAQ,8BAA8BQ,EAAY,yBAAyB,GAAG;AAC/F;AAAA,MAAA;AAGK,YAAAE,IAAOlB,EAAoBiB,GAAoBpB,CAAc;AAC3D,MAAAgB,EAAA,KAAK,GAAGK,CAAI;AAAA,IAAA;AAGrB,WAAArB,EAAeD,EAAW,YAAY,CAAC,IAAI,EAAE,cAAciB,EAAQ,GAC5DA;AAAA,WACCM,GAAO;AACf,WAAAT,EAAO,KAAK,IAAIF,CAAQ,+CAA+CW,CAAK,GAC5EtB,EAAeD,EAAW,YAAY,CAAC,IAAI,EAAE,cAAc,CAAA,EAAG,GACvD,CAAC;AAAA,EAAA;AAEV;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "moonflower",
3
- "version": "1.5.2",
3
+ "version": "1.5.3",
4
4
  "description": "",
5
5
  "author": "tenebrie",
6
6
  "license": "MIT",
@@ -27,7 +27,7 @@ import {
27
27
  import { discoverRouters } from '../discoveryModule/discoverRouters/discoverRouters'
28
28
  import { ApiDocsHeader, OpenApiManager } from '../manager/OpenApiManager'
29
29
  import { EndpointData, ExposedModelData } from '../types'
30
- import { getSourceFileTimestamp, TimestampCache } from './getSourceFileTimestamp'
30
+ import { getSourceFileTimestamp, MtimeCache, TimestampCache } from './getSourceFileTimestamp'
31
31
  import { getValuesOfObjectLiteral, resolveEndpointPath } from './nodeParsers'
32
32
  import { parseEndpoint, SectionTiming } from './parseEndpoint'
33
33
  import { parseExposedModel, parseNamedExposedModels } from './parseExposedModels'
@@ -200,8 +200,10 @@ export const analyzeMultipleSourceFiles = async (
200
200
  const cached: CachedFile[] = []
201
201
  const uncached: UncachedFile[] = []
202
202
 
203
+ const freshnessCheckStart = performance.now()
204
+ const mtimeCache: MtimeCache = new Map()
203
205
  for (const file of files) {
204
- const timestamp = getSourceFileTimestamp(file.sourceFile, config.timestampCache)
206
+ const timestamp = getSourceFileTimestamp(file.sourceFile, config.timestampCache, mtimeCache)
205
207
  const hit = config.incremental
206
208
  ? SourceFileCache.getCachedResults(file.sourceFile, timestamp, config.cachePath)
207
209
  : null
@@ -212,6 +214,9 @@ export const analyzeMultipleSourceFiles = async (
212
214
  uncached.push({ file, timestamp })
213
215
  }
214
216
  }
217
+ if (profiling !== 'off') {
218
+ Logger.info(`Cache freshness check took ${Math.round(performance.now() - freshnessCheckStart)}ms`)
219
+ }
215
220
 
216
221
  if (uncached.length === 0) {
217
222
  if (profiling !== 'off') {
@@ -5,11 +5,29 @@ import { formatTimestamp, Logger } from '../../utils/logger'
5
5
 
6
6
  export type TimestampCache = Record<string, { dependencies: SourceFile[] }>
7
7
 
8
- export function getSourceFileTimestamp(sourceFile: SourceFile, timestampCache: TimestampCache) {
8
+ /**
9
+ * Per-run cache of file mtimes keyed by absolute path. Router files share large parts of their
10
+ * transitive import graphs (common types, utils, models), so without this the same dependency gets
11
+ * `statSync`-ed once per importing router — tens of thousands of redundant syscalls across a project
12
+ * with dozens of routers. mtimes don't change mid-run, so memoizing is safe.
13
+ */
14
+ export type MtimeCache = Map<string, number>
15
+
16
+ export function getSourceFileTimestamp(
17
+ sourceFile: SourceFile,
18
+ timestampCache: TimestampCache,
19
+ mtimeCache: MtimeCache = new Map(),
20
+ ) {
9
21
  const dependencies = getFileDependencies(sourceFile, timestampCache)
10
22
  const timestamps = dependencies.map((dep) => {
11
- const stat = fs.statSync(dep.getFilePath())
12
- return stat.mtimeMs
23
+ const depPath = dep.getFilePath()
24
+ const cached = mtimeCache.get(depPath)
25
+ if (cached !== undefined) {
26
+ return cached
27
+ }
28
+ const mtime = fs.statSync(depPath).mtimeMs
29
+ mtimeCache.set(depPath, mtime)
30
+ return mtime
13
31
  })
14
32
  const latestTimestamp = Math.max(...timestamps)
15
33