moonflower 1.5.3 → 1.5.4

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"),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;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const I=require("crypto"),U=require("fs"),W=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"),H=require("../discoveryModule/discoverRouters/discoverRouters.cjs"),D=require("../manager/OpenApiManager.cjs"),B=require("./getSourceFileTimestamp.cjs"),A=require("./nodeParsers.cjs"),V=require("./parseEndpoint.cjs"),C=require("./parseExposedModels.cjs"),w=require("./sourceFileCache.cjs"),Y=require("./workerPool.cjs");var P=typeof document<"u"?document.currentScript:null;function G(r){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(r){for(const l in r)if(l!=="default"){const c=Object.getOwnPropertyDescriptor(r,l);Object.defineProperty(t,l,c.get?c:{enumerable:!0,get:()=>r[l]})}}return t.default=r,Object.freeze(t)}const M=G(W);function J(){const r=["./analyzerWorker.mjs","./analyzerWorker.test.mjs"];for(const t of r){const l=new URL(t,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(U.existsSync(K.fileURLToPath(l)))return l}throw new Error("analyzerWorker.mjs not found. Run yarn build, or run tests via yarn test (which compiles the worker first).")}const Q=2,X=async({logLevel:r,tsconfigPath:t,sourceFilePaths:l,sourceFileDiscovery:c,incremental:p,profiling:m="stats",workers:o=!1})=>{const a=D.OpenApiManager.getInstance();if(a.isReady())return;r&&y.Logger.setLevel(r),y.Logger.info("Preparing OpenAPI spec");const h=new S.Project({tsConfigFilePath:M.resolve(t),skipFileDependencyResolution:!0}),{explicitRouters:g,discoveredRouterFiles:x,allSourceFiles:T}=(()=>{const u=(l??[]).map(f=>M.resolve(f)).map(f=>h.getSourceFileOrThrow(f)),k=u.flatMap(f=>({fileName:f.getFilePath(),sourceFile:f,routers:H.discoverRouters(f)})),{discoveredRouterFiles:b,discoveredSourceFiles:j}=(()=>{if(c===!1)return{discoveredRouterFiles:[],discoveredSourceFiles:[]};const f=performance.now(),E=_.discoverRouterFiles({targetPath:typeof c=="object"?c.rootPath:".",tsConfigPath:t});return m!=="off"&&y.Logger.info(`File discovery took ${Math.round(performance.now()-f)}ms`),E})(),z=u.reduce((f,E)=>f.some(q=>q.getFilePath()===E.getFilePath())?f:f.concat(E),j);return{explicitRouters:k,discoveredRouterFiles:b,allSourceFiles:z}})(),e=g.reduce((i,N)=>i.some(u=>u.fileName===N.fileName)?i:i.concat(N),x),n=T.flatMap(i=>O(i)).filter(i=>!!i);n.length>0&&n[0]&&a.setHeader(n[0]);const s=T.flatMap(i=>$(i));a.setExposedModels(s);const d=typeof p=="object"&&p.cachePath?p.cachePath:M.resolve(process.cwd(),"node_modules",".cache","moonflower"),F=await L(e,{incremental:p!==!1,cachePath:d,timestampCache:{},profiling:m,tsconfigPath:M.resolve(t),workers:o});a.setStats({discoveredRouterFiles:x.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: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}`)}))}))}),a.setEndpoints(F),a.markAsReady()},L=async(r,t,l)=>{const c=t.profiling??"stats",p=performance.now(),m=[],o=[],a=performance.now(),h=new Map;for(const e of r){const n=B.getSourceFileTimestamp(e.sourceFile,t.timestampCache,h),s=t.incremental?w.SourceFileCache.getCachedResults(e.sourceFile,n,t.cachePath):null;s?(y.Logger.debug(`[${e.fileName}] Found cached results`),m.push({endpoints:s.endpoints,fileName:e.fileName,timing:0,endpointTimings:[]})):o.push({file:e,timestamp:n})}if(c!=="off"&&y.Logger.info(`Cache freshness check took ${Math.round(performance.now()-a)}ms`),o.length===0)return c!=="off"&&y.Logger.info(`Router analysis took ${Math.round(performance.now()-p)}ms`),m.flatMap(e=>e.endpoints);const g=new Map;for(const{file:e}of o)g.set(e.fileName,{endpoints:[],fileName:e.fileName,timing:0,endpointTimings:[]});if(!(t.workers!==!1)||o.length<=Q)for(const{file:e}of o){const{endpoints:n,endpointTimings:s}=v(e),d=g.get(e.fileName);d.endpoints=n,d.endpointTimings=s,d.timing=s.reduce((F,i)=>F+i.timing,0)}else{const e=o.map(({file:d})=>({fileName:d.fileName,task:{taskId:I.randomUUID(),tsconfigPath:t.tsconfigPath,sourceFilePath:d.sourceFile.getFilePath(),routerNames:d.routers.named,filterEndpointPaths:l}})),n=new Y.WorkerPool(J(),e.length);let s;try{s=await n.runAll(e.map(d=>d.task))}finally{n.terminate()}for(let d=0;d<s.length;d++){const F=s[d],i=e[d].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,k)=>u+k.timing,0)}}for(const{file:e,timestamp:n}of o){const s=g.get(e.fileName);s.endpoints.length>0&&w.SourceFileCache.cacheResults(e.sourceFile,n,t.cachePath,s.endpoints)}const T=[...m,...Array.from(g.values())];return c!=="off"&&y.Logger.info(`Router analysis took ${Math.round(performance.now()-p)}ms`),c==="stats"?T.map(e=>({fileName:e.fileName,timeTaken:e.timing})).sort((e,n)=>n.timeTaken-e.timeTaken).filter(e=>e.timeTaken>500).forEach(e=>{y.Logger.info(`- [${e.fileName}] Took ${Math.round(e.timeTaken)}ms to analyze`)}):c==="debug"&&T.map(e=>({fileName:e.fileName,timeTaken:e.timing,endpointTimings:e.endpointTimings})).sort((e,n)=>n.timeTaken-e.timeTaken).forEach(e=>{y.Logger.info(`- [${e.fileName}] Took ${Math.round(e.timeTaken)}ms to analyze`),e.endpointTimings.sort((n,s)=>s.timing-n.timing).forEach(n=>{y.Logger.info(` - ${n.method} ${n.path} (${Math.round(n.timing)}ms)`),n.sectionTimings.filter(s=>s.timing>=1).sort((s,d)=>d.timing-s.timing).forEach(s=>{y.Logger.info(` - ${s.section}: ${Math.round(s.timing)}ms`)})})}),T.flatMap(e=>e.endpoints)},v=(r,t)=>{const l=[],c=[],m=["get","post","put","delete","del","patch"].join("|");return r.routers.named.forEach(o=>{const a=new RegExp(`${o}\\.(?:${m})`);r.sourceFile.forEachChild(h=>{const g=h.getText();if(a.test(g)){A.resolveEndpointPath(h);const x=performance.now(),{endpoint:T,sectionTimings:e}=V.parseEndpoint(h,r.fileName);c.push({method:T.method,path:T.path,timing:performance.now()-x,sectionTimings:e}),l.push(T)}})}),{endpoints:l,endpointTimings:c}},O=r=>{const t=R.discoverImportedName({sourceFile:r,originalName:"useApiHeader"});if(!t)return null;const l=r.forEachChildAsArray().filter(o=>o.isKind(S.SyntaxKind.ExpressionStatement)).find(o=>t&&o.getText().startsWith(t));if(!l)return null;const c=l.getFirstDescendantByKindOrThrow(S.SyntaxKind.ObjectLiteralExpression),p=A.getValuesOfObjectLiteral(c),m=o=>typeof o=="string"||Array.isArray(o)&&o.every(a=>typeof a=="string")?o:o.reduce((a,h)=>typeof h=="string"?a:{...a,[h.identifier]:m(h.value)},{});return m(p)},$=r=>{const t=[],l=R.discoverImportedName({sourceFile:r,originalName:"useExposeApiModel"}),c=R.discoverImportedName({sourceFile:r,originalName:"useExposeNamedApiModels"});return r.forEachChildAsArray().filter(p=>p.isKind(S.SyntaxKind.ExpressionStatement)).map(p=>{if(l&&p.getText().startsWith(l)){const a=(p.getFirstChild()?.getChildrenOfKind(S.SyntaxKind.SyntaxList)||[])[0].getFirstChild();if(!a)return;t.push(C.parseExposedModel(a));return}if(c&&p.getText().startsWith(c)){const a=(p.getFirstChild()?.getChildrenOfKind(S.SyntaxKind.SyntaxList)||[])[0].getFirstChild();if(!a)return;C.parseNamedExposedModels(a).forEach(g=>t.push(g))}}),t};exports.analyzeMultipleSourceFiles=L;exports.analyzeSourceFileApiHeader=O;exports.analyzeSourceFileEndpoints=v;exports.analyzeSourceFileExposedModels=$;exports.prepareOpenApiSpec=X;
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, 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
+ {"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\tworkers?: boolean\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\tworkers = false,\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\tworkers,\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\tworkers?: boolean\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\tconst useWorkers = config.workers !== false\n\tif (!useWorkers || 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\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","workers","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":"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"}
@@ -15,6 +15,7 @@ type Props = {
15
15
  cachePath: string;
16
16
  };
17
17
  profiling?: 'stats' | 'off' | 'debug';
18
+ workers?: boolean;
18
19
  };
19
20
  type FileDiscoveryConfig = {
20
21
  rootPath: string;
@@ -25,13 +26,14 @@ type EndpointTiming = {
25
26
  timing: number;
26
27
  sectionTimings: SectionTiming[];
27
28
  };
28
- export declare const prepareOpenApiSpec: ({ logLevel, tsconfigPath, sourceFilePaths, sourceFileDiscovery, incremental, profiling, }: Props) => Promise<void>;
29
+ export declare const prepareOpenApiSpec: ({ logLevel, tsconfigPath, sourceFilePaths, sourceFileDiscovery, incremental, profiling, workers, }: Props) => Promise<void>;
29
30
  export declare const analyzeMultipleSourceFiles: (files: DiscoveredSourceFile[], config: {
30
31
  incremental: boolean;
31
32
  cachePath: string;
32
33
  timestampCache: TimestampCache;
33
34
  profiling?: "stats" | "off" | "debug";
34
35
  tsconfigPath: string;
36
+ workers?: boolean;
35
37
  }, filterEndpointPaths?: string[]) => Promise<EndpointData[]>;
36
38
  export declare const analyzeSourceFileWithCache: (file: DiscoveredSourceFile, config: {
37
39
  incremental: boolean;
@@ -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,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"}
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;IACrC,OAAO,CAAC,EAAE,OAAO,CAAA;CACjB,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,uGAQ5B,KAAK,KAAG,OAAO,CAAC,IAAI,CA2GtB,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;IACpB,OAAO,CAAC,EAAE,OAAO,CAAA;CACjB,wBACqB,MAAM,EAAE,KAC5B,OAAO,CAAC,YAAY,EAAE,CA0IxB,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,211 +1,213 @@
1
- import $ from "crypto";
2
- import { existsSync as v } from "fs";
3
- import * as k from "path";
4
- import { fileURLToPath as O } from "url";
5
- import { Project as L, SyntaxKind as x } from "ts-morph";
1
+ import v from "crypto";
2
+ import { existsSync as O } from "fs";
3
+ import * as M from "path";
4
+ import { fileURLToPath as L } from "url";
5
+ import { Project as j, SyntaxKind as E } from "ts-morph";
6
6
  import { Logger as F } from "../../utils/logger.mjs";
7
- import { discoverImportedName as P } from "../discoveryModule/discoverImports/discoverImports.mjs";
8
- import { discoverRouterFiles as j } from "../discoveryModule/discoverRouterFiles/discoverRouterFiles.mjs";
9
- import { discoverRouters as z } from "../discoveryModule/discoverRouters/discoverRouters.mjs";
7
+ import { discoverImportedName as R } from "../discoveryModule/discoverImports/discoverImports.mjs";
8
+ import { discoverRouterFiles as z } from "../discoveryModule/discoverRouterFiles/discoverRouterFiles.mjs";
9
+ import { discoverRouters as W } from "../discoveryModule/discoverRouters/discoverRouters.mjs";
10
10
  import { OpenApiManager as U } from "../manager/OpenApiManager.mjs";
11
11
  import { getSourceFileTimestamp as b } from "./getSourceFileTimestamp.mjs";
12
- import { getValuesOfObjectLiteral as I, resolveEndpointPath as W } from "./nodeParsers.mjs";
13
- import { parseEndpoint as H } from "./parseEndpoint.mjs";
14
- import { parseExposedModel as K, parseNamedExposedModels as _ } from "./parseExposedModels.mjs";
15
- import { SourceFileCache as R } from "./sourceFileCache.mjs";
16
- import { WorkerPool as D } from "./workerPool.mjs";
17
- function B() {
18
- const a = ["./analyzerWorker.mjs", "./analyzerWorker.test.mjs"];
19
- for (const n of a) {
12
+ import { getValuesOfObjectLiteral as I, resolveEndpointPath as H } from "./nodeParsers.mjs";
13
+ import { parseEndpoint as K } from "./parseEndpoint.mjs";
14
+ import { parseExposedModel as _, parseNamedExposedModels as D } from "./parseExposedModels.mjs";
15
+ import { SourceFileCache as w } from "./sourceFileCache.mjs";
16
+ import { WorkerPool as B } from "./workerPool.mjs";
17
+ function V() {
18
+ const l = ["./analyzerWorker.mjs", "./analyzerWorker.test.mjs"];
19
+ for (const n of l) {
20
20
  const d = new URL(n, import.meta.url);
21
- if (v(O(d)))
21
+ if (O(L(d)))
22
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
- const V = 2, de = async ({
29
- logLevel: a,
28
+ const Y = 2, fe = async ({
29
+ logLevel: l,
30
30
  tsconfigPath: n,
31
31
  sourceFilePaths: d,
32
- sourceFileDiscovery: l,
33
- incremental: m,
34
- profiling: f = "stats"
32
+ sourceFileDiscovery: m,
33
+ incremental: c,
34
+ profiling: f = "stats",
35
+ workers: t = !1
35
36
  }) => {
36
- const t = U.getInstance();
37
- if (t.isReady())
37
+ const r = U.getInstance();
38
+ if (r.isReady())
38
39
  return;
39
- a && F.setLevel(a), F.info("Preparing OpenAPI spec");
40
- const c = new L({
41
- tsConfigFilePath: k.resolve(n),
40
+ l && F.setLevel(l), F.info("Preparing OpenAPI spec");
41
+ const h = new j({
42
+ tsConfigFilePath: M.resolve(n),
42
43
  skipFileDependencyResolution: !0
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
- fileName: h.getFilePath(),
46
- sourceFile: h,
47
- routers: z(h)
44
+ }), { explicitRouters: g, discoveredRouterFiles: x, allSourceFiles: T } = (() => {
45
+ const p = (d ?? []).map((u) => M.resolve(u)).map((u) => h.getSourceFileOrThrow(u)), P = p.flatMap((u) => ({
46
+ fileName: u.getFilePath(),
47
+ sourceFile: u,
48
+ routers: W(u)
48
49
  })), { discoveredRouterFiles: C, discoveredSourceFiles: S } = (() => {
49
- if (l === !1)
50
+ if (m === !1)
50
51
  return { discoveredRouterFiles: [], discoveredSourceFiles: [] };
51
- const h = performance.now(), E = j({
52
- targetPath: typeof l == "object" ? l.rootPath : ".",
52
+ const u = performance.now(), k = z({
53
+ targetPath: typeof m == "object" ? m.rootPath : ".",
53
54
  tsConfigPath: n
54
55
  });
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),
56
+ return f !== "off" && F.info(`File discovery took ${Math.round(performance.now() - u)}ms`), k;
57
+ })(), A = p.reduce(
58
+ (u, k) => u.some(($) => $.getFilePath() === k.getFilePath()) ? u : u.concat(k),
58
59
  S
59
60
  );
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
- incremental: m !== !1,
70
- cachePath: r,
61
+ return { explicitRouters: P, discoveredRouterFiles: C, allSourceFiles: A };
62
+ })(), e = g.reduce(
63
+ (i, N) => i.some((p) => p.fileName === N.fileName) ? i : i.concat(N),
64
+ x
65
+ ), o = T.flatMap((i) => J(i)).filter((i) => !!i);
66
+ o.length > 0 && o[0] && r.setHeader(o[0]);
67
+ const s = T.flatMap((i) => Q(i));
68
+ r.setExposedModels(s);
69
+ const a = typeof c == "object" && c.cachePath ? c.cachePath : M.resolve(process.cwd(), "node_modules", ".cache", "moonflower"), y = await q(e, {
70
+ incremental: c !== !1,
71
+ cachePath: a,
71
72
  timestampCache: {},
72
73
  profiling: f,
73
- tsconfigPath: k.resolve(n)
74
+ tsconfigPath: M.resolve(n),
75
+ workers: t
74
76
  });
75
- t.setStats({
76
- discoveredRouterFiles: g.map((s) => ({
77
- path: s.fileName,
78
- routers: s.routers.named.map((N) => ({
77
+ r.setStats({
78
+ discoveredRouterFiles: x.map((i) => ({
79
+ path: i.fileName,
80
+ routers: i.routers.named.map((N) => ({
79
81
  name: N,
80
- endpoints: y.filter((p) => p.sourceFilePath === s.fileName).map((p) => `${p.method.toUpperCase()} ${p.path}`)
82
+ endpoints: y.filter((p) => p.sourceFilePath === i.fileName).map((p) => `${p.method.toUpperCase()} ${p.path}`)
81
83
  }))
82
84
  })),
83
- explicitRouterFiles: u.map((s) => ({
84
- path: s.fileName,
85
- routers: s.routers.named.map((N) => ({
85
+ explicitRouterFiles: g.map((i) => ({
86
+ path: i.fileName,
87
+ routers: i.routers.named.map((N) => ({
86
88
  name: N,
87
- endpoints: y.filter((p) => p.sourceFilePath === s.fileName).map((p) => `${p.method.toUpperCase()} ${p.path}`)
89
+ endpoints: y.filter((p) => p.sourceFilePath === i.fileName).map((p) => `${p.method.toUpperCase()} ${p.path}`)
88
90
  }))
89
91
  }))
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 });
92
+ }), r.setEndpoints(y), r.markAsReady();
93
+ }, q = async (l, n, d) => {
94
+ const m = n.profiling ?? "stats", c = performance.now(), f = [], t = [], r = performance.now(), h = /* @__PURE__ */ new Map();
95
+ for (const e of l) {
96
+ const o = b(e.sourceFile, n.timestampCache, h), s = n.incremental ? w.getCachedResults(e.sourceFile, o, n.cachePath) : null;
97
+ s ? (F.debug(`[${e.fileName}] Found cached results`), f.push({ endpoints: s.endpoints, fileName: e.fileName, timing: 0, endpointTimings: [] })) : t.push({ file: e, timestamp: o });
96
98
  }
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
+ if (m !== "off" && F.info(`Cache freshness check took ${Math.round(performance.now() - r)}ms`), t.length === 0)
100
+ return m !== "off" && F.info(`Router analysis took ${Math.round(performance.now() - c)}ms`), f.flatMap((e) => e.endpoints);
99
101
  const g = /* @__PURE__ */ new Map();
100
102
  for (const { file: e } of t)
101
103
  g.set(e.fileName, { endpoints: [], fileName: e.fileName, timing: 0, endpointTimings: [] });
102
- if (t.length <= V)
104
+ if (!(n.workers !== !1) || t.length <= Y)
103
105
  for (const { file: e } of t) {
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
+ const { endpoints: o, endpointTimings: s } = G(e), a = g.get(e.fileName);
107
+ a.endpoints = o, a.endpointTimings = s, a.timing = s.reduce((y, i) => y + i.timing, 0);
106
108
  }
107
109
  else {
108
- const e = t.map(({ file: r }) => ({
109
- fileName: r.fileName,
110
+ const e = t.map(({ file: a }) => ({
111
+ fileName: a.fileName,
110
112
  task: {
111
- taskId: $.randomUUID(),
113
+ taskId: v.randomUUID(),
112
114
  tsconfigPath: n.tsconfigPath,
113
- sourceFilePath: r.sourceFile.getFilePath(),
114
- routerNames: r.routers.named,
115
+ sourceFilePath: a.sourceFile.getFilePath(),
116
+ routerNames: a.routers.named,
115
117
  filterEndpointPaths: d
116
118
  }
117
- })), o = new D(B(), e.length);
118
- let i;
119
+ })), o = new B(V(), e.length);
120
+ let s;
119
121
  try {
120
- i = await o.runAll(e.map((r) => r.task));
122
+ s = await o.runAll(e.map((a) => a.task));
121
123
  } finally {
122
124
  o.terminate();
123
125
  }
124
- for (let r = 0; r < i.length; r++) {
125
- const y = i[r], s = e[r].fileName, N = g.get(s);
126
+ for (let a = 0; a < s.length; a++) {
127
+ const y = s[a], i = e[a].fileName, N = g.get(i);
126
128
  if ("error" in y) {
127
- F.error(`[${s}] Worker error: ${y.error}`);
129
+ F.error(`[${i}] Worker error: ${y.error}`);
128
130
  continue;
129
131
  }
130
- N.endpoints = y.endpoints, N.endpointTimings = y.endpointTimings, N.timing = y.endpointTimings.reduce((p, M) => p + M.timing, 0);
132
+ N.endpoints = y.endpoints, N.endpointTimings = y.endpointTimings, N.timing = y.endpointTimings.reduce((p, P) => p + P.timing, 0);
131
133
  }
132
134
  }
133
135
  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
+ const s = g.get(e.fileName);
137
+ s.endpoints.length > 0 && w.cacheResults(e.sourceFile, o, n.cachePath, s.endpoints);
136
138
  }
137
139
  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) => {
140
+ return m !== "off" && F.info(`Router analysis took ${Math.round(performance.now() - c)}ms`), m === "stats" ? T.map((e) => ({ fileName: e.fileName, timeTaken: e.timing })).sort((e, o) => o.timeTaken - e.timeTaken).filter((e) => e.timeTaken > 500).forEach((e) => {
139
141
  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`);
142
+ }) : m === "debug" && T.map((e) => ({ fileName: e.fileName, timeTaken: e.timing, endpointTimings: e.endpointTimings })).sort((e, o) => o.timeTaken - e.timeTaken).forEach((e) => {
143
+ F.info(`- [${e.fileName}] Took ${Math.round(e.timeTaken)}ms to analyze`), e.endpointTimings.sort((o, s) => s.timing - o.timing).forEach((o) => {
144
+ F.info(` - ${o.method} ${o.path} (${Math.round(o.timing)}ms)`), o.sectionTimings.filter((s) => s.timing >= 1).sort((s, a) => a.timing - s.timing).forEach((s) => {
145
+ F.info(` - ${s.section}: ${Math.round(s.timing)}ms`);
144
146
  });
145
147
  });
146
148
  }), 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);
149
+ }, G = (l, n) => {
150
+ const d = [], m = [], f = ["get", "post", "put", "delete", "del", "patch"].join("|");
151
+ return l.routers.named.forEach((t) => {
152
+ const r = new RegExp(`${t}\\.(?:${f})`);
153
+ l.sourceFile.forEachChild((h) => {
154
+ const g = h.getText();
155
+ if (r.test(g)) {
156
+ H(h);
157
+ const x = performance.now(), { endpoint: T, sectionTimings: e } = K(h, l.fileName);
158
+ m.push({
159
+ method: T.method,
160
+ path: T.path,
161
+ timing: performance.now() - x,
162
+ sectionTimings: e
163
+ }), d.push(T);
162
164
  }
163
165
  });
164
- }), { endpoints: d, endpointTimings: l };
165
- }, G = (a) => {
166
- const n = P({
167
- sourceFile: a,
166
+ }), { endpoints: d, endpointTimings: m };
167
+ }, J = (l) => {
168
+ const n = R({
169
+ sourceFile: l,
168
170
  originalName: "useApiHeader"
169
171
  });
170
172
  if (!n)
171
173
  return null;
172
- const d = a.forEachChildAsArray().filter((t) => t.isKind(x.ExpressionStatement)).find((t) => n && t.getText().startsWith(n));
174
+ const d = l.forEachChildAsArray().filter((t) => t.isKind(E.ExpressionStatement)).find((t) => n && t.getText().startsWith(n));
173
175
  if (!d)
174
176
  return null;
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)
177
+ const m = d.getFirstDescendantByKindOrThrow(E.ObjectLiteralExpression), c = I(m), f = (t) => typeof t == "string" || Array.isArray(t) && t.every((r) => typeof r == "string") ? t : t.reduce((r, h) => typeof h == "string" ? r : {
178
+ ...r,
179
+ [h.identifier]: f(h.value)
178
180
  }, {});
179
- return f(m);
180
- }, J = (a) => {
181
- const n = [], d = P({
182
- sourceFile: a,
181
+ return f(c);
182
+ }, Q = (l) => {
183
+ const n = [], d = R({
184
+ sourceFile: l,
183
185
  originalName: "useExposeApiModel"
184
- }), l = P({
185
- sourceFile: a,
186
+ }), m = R({
187
+ sourceFile: l,
186
188
  originalName: "useExposeNamedApiModels"
187
189
  });
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)
190
+ return l.forEachChildAsArray().filter((c) => c.isKind(E.ExpressionStatement)).map((c) => {
191
+ if (d && c.getText().startsWith(d)) {
192
+ const r = (c.getFirstChild()?.getChildrenOfKind(E.SyntaxList) || [])[0].getFirstChild();
193
+ if (!r)
192
194
  return;
193
- n.push(K(c));
195
+ n.push(_(r));
194
196
  return;
195
197
  }
196
- if (l && m.getText().startsWith(l)) {
197
- const c = (m.getFirstChild()?.getChildrenOfKind(x.SyntaxList) || [])[0].getFirstChild();
198
- if (!c)
198
+ if (m && c.getText().startsWith(m)) {
199
+ const r = (c.getFirstChild()?.getChildrenOfKind(E.SyntaxList) || [])[0].getFirstChild();
200
+ if (!r)
199
201
  return;
200
- _(c).forEach((g) => n.push(g));
202
+ D(r).forEach((g) => n.push(g));
201
203
  }
202
204
  }), n;
203
205
  };
204
206
  export {
205
- Y as analyzeMultipleSourceFiles,
206
- G as analyzeSourceFileApiHeader,
207
- q as analyzeSourceFileEndpoints,
208
- J as analyzeSourceFileExposedModels,
209
- de as prepareOpenApiSpec
207
+ q as analyzeMultipleSourceFiles,
208
+ J as analyzeSourceFileApiHeader,
209
+ G as analyzeSourceFileEndpoints,
210
+ Q as analyzeSourceFileExposedModels,
211
+ fe as prepareOpenApiSpec
210
212
  };
211
213
  //# sourceMappingURL=analyzerModule.mjs.map
@@ -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, 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
+ {"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\tworkers?: boolean\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\tworkers = false,\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\tworkers,\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\tworkers?: boolean\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\tconst useWorkers = config.workers !== false\n\tif (!useWorkers || 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\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","workers","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":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "moonflower",
3
- "version": "1.5.3",
3
+ "version": "1.5.4",
4
4
  "description": "",
5
5
  "author": "tenebrie",
6
6
  "license": "MIT",
@@ -45,6 +45,7 @@ type Props = {
45
45
  cachePath: string
46
46
  }
47
47
  profiling?: 'stats' | 'off' | 'debug'
48
+ workers?: boolean
48
49
  }
49
50
 
50
51
  type FileDiscoveryConfig = {
@@ -71,6 +72,7 @@ export const prepareOpenApiSpec = async ({
71
72
  sourceFileDiscovery,
72
73
  incremental,
73
74
  profiling = 'stats',
75
+ workers = false,
74
76
  }: Props): Promise<void> => {
75
77
  const openApiManager = OpenApiManager.getInstance()
76
78
 
@@ -152,6 +154,7 @@ export const prepareOpenApiSpec = async ({
152
154
  timestampCache: {},
153
155
  profiling,
154
156
  tsconfigPath: path.resolve(tsconfigPath),
157
+ workers,
155
158
  })
156
159
 
157
160
  openApiManager.setStats({
@@ -187,6 +190,7 @@ export const analyzeMultipleSourceFiles = async (
187
190
  timestampCache: TimestampCache
188
191
  profiling?: 'stats' | 'off' | 'debug'
189
192
  tsconfigPath: string
193
+ workers?: boolean
190
194
  },
191
195
  filterEndpointPaths?: string[],
192
196
  ): Promise<EndpointData[]> => {
@@ -237,12 +241,8 @@ export const analyzeMultipleSourceFiles = async (
237
241
  byFile.set(file.fileName, { endpoints: [], fileName: file.fileName, timing: 0, endpointTimings: [] })
238
242
  }
239
243
 
240
- // The caller (prepareOpenApiSpec) already built and warmed a ts-morph Project on this thread, so
241
- // inline analysis runs against a hot type-checker. A worker, by contrast, must spawn a thread and
242
- // build its own Project from scratch — a multi-second cold-start. That cold-start only pays off
243
- // when there are enough uncached files that spreading the parse work across workers beats it; below
244
- // the threshold (e.g. the common single-file incremental rebuild), inline is strictly faster.
245
- if (uncached.length <= INLINE_ANALYSIS_FILE_THRESHOLD) {
244
+ const useWorkers = config.workers !== false
245
+ if (!useWorkers || uncached.length <= INLINE_ANALYSIS_FILE_THRESHOLD) {
246
246
  for (const { file } of uncached) {
247
247
  const { endpoints, endpointTimings } = analyzeSourceFileEndpoints(file, filterEndpointPaths)
248
248
  const fileResult = byFile.get(file.fileName)!
@@ -251,9 +251,6 @@ export const analyzeMultipleSourceFiles = async (
251
251
  fileResult.timing = endpointTimings.reduce((sum, t) => sum + t.timing, 0)
252
252
  }
253
253
  } else {
254
- // One task per file: each worker analyzes a whole file in a single pass, paying its Project
255
- // cold-start once and reusing the warmed-up checker for every endpoint in that file. Cap the
256
- // pool at one worker per file so we never spin up a worker with nothing to do.
257
254
  type FileTask = { task: WorkerTask; fileName: string }
258
255
  const allTasks: FileTask[] = uncached.map(({ file }) => ({
259
256
  fileName: file.fileName,