codebase-onboarder 0.5.0 → 0.7.0

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.
Files changed (448) hide show
  1. package/README.md +196 -47
  2. package/bin/build-if-source.js +23 -0
  3. package/cli/commands.d.ts +26 -0
  4. package/cli/commands.js +836 -811
  5. package/cli/commands.js.map +1 -0
  6. package/cli/explorer/advanced.d.ts +14 -0
  7. package/cli/explorer/advanced.js +449 -0
  8. package/cli/explorer/advanced.js.map +1 -0
  9. package/cli/explorer/app.d.ts +1 -0
  10. package/cli/explorer/app.js +396 -229
  11. package/cli/explorer/app.js.map +1 -0
  12. package/cli/explorer/assistant.d.ts +9 -0
  13. package/cli/explorer/assistant.js +58 -0
  14. package/cli/explorer/assistant.js.map +1 -0
  15. package/cli/explorer/commands.d.ts +17 -0
  16. package/cli/explorer/commands.js +400 -162
  17. package/cli/explorer/commands.js.map +1 -0
  18. package/cli/explorer/featureViews.d.ts +124 -0
  19. package/cli/explorer/featureViews.js +179 -0
  20. package/cli/explorer/featureViews.js.map +1 -0
  21. package/cli/explorer/github.d.ts +21 -0
  22. package/cli/explorer/github.js +73 -0
  23. package/cli/explorer/github.js.map +1 -0
  24. package/cli/explorer/graphs.d.ts +7 -0
  25. package/cli/explorer/graphs.js +173 -0
  26. package/cli/explorer/graphs.js.map +1 -0
  27. package/cli/explorer/picker.d.ts +22 -0
  28. package/cli/explorer/picker.js +150 -0
  29. package/cli/explorer/picker.js.map +1 -0
  30. package/cli/explorer/report.d.ts +85 -0
  31. package/cli/explorer/report.js +60 -0
  32. package/cli/explorer/report.js.map +1 -0
  33. package/cli/explorer/session.d.ts +449 -0
  34. package/cli/explorer/session.js +184 -95
  35. package/cli/explorer/session.js.map +1 -0
  36. package/cli/explorer/views.d.ts +19 -0
  37. package/cli/explorer/views.js +496 -412
  38. package/cli/explorer/views.js.map +1 -0
  39. package/cli/explorer/wrap.d.ts +1 -0
  40. package/cli/explorer/wrap.js +33 -0
  41. package/cli/explorer/wrap.js.map +1 -0
  42. package/cli/main.d.ts +1 -0
  43. package/cli/main.js +235 -138
  44. package/cli/main.js.map +1 -0
  45. package/cli/prompt.d.ts +5 -0
  46. package/cli/prompt.js +84 -80
  47. package/cli/prompt.js.map +1 -0
  48. package/cli/scanOptions.d.ts +2 -0
  49. package/cli/scanOptions.js +11 -0
  50. package/cli/scanOptions.js.map +1 -0
  51. package/cli/ui.d.ts +33 -0
  52. package/cli/ui.js +55 -35
  53. package/cli/ui.js.map +1 -0
  54. package/cli/wizard.d.ts +187 -0
  55. package/cli/wizard.js +304 -284
  56. package/cli/wizard.js.map +1 -0
  57. package/package.json +15 -4
  58. package/public/app.d.ts +1 -0
  59. package/public/app.js +1057 -1009
  60. package/public/app.js.map +1 -0
  61. package/public/browser.d.ts +68 -0
  62. package/public/browser.js +2 -0
  63. package/public/browser.js.map +1 -0
  64. package/public/index.html +30 -13
  65. package/public/js/about.d.ts +6 -0
  66. package/public/js/about.js +165 -172
  67. package/public/js/about.js.map +1 -0
  68. package/public/js/aiDraft.d.ts +3 -0
  69. package/public/js/aiDraft.js +116 -124
  70. package/public/js/aiDraft.js.map +1 -0
  71. package/public/js/analysisPanel.d.ts +7 -0
  72. package/public/js/analysisPanel.js +166 -166
  73. package/public/js/analysisPanel.js.map +1 -0
  74. package/public/js/analysisReport.d.ts +38 -0
  75. package/public/js/analysisReport.js +105 -110
  76. package/public/js/analysisReport.js.map +1 -0
  77. package/public/js/api.d.ts +26 -0
  78. package/public/js/api.js +176 -167
  79. package/public/js/api.js.map +1 -0
  80. package/public/js/atlas.d.ts +6 -0
  81. package/public/js/atlas.js +50 -58
  82. package/public/js/atlas.js.map +1 -0
  83. package/public/js/blameView.d.ts +2 -0
  84. package/public/js/blameView.js +36 -37
  85. package/public/js/blameView.js.map +1 -0
  86. package/public/js/codeTab.d.ts +3 -0
  87. package/public/js/codeTab.js +175 -180
  88. package/public/js/codeTab.js.map +1 -0
  89. package/public/js/codeViewer.d.ts +5 -0
  90. package/public/js/codeViewer.js +162 -150
  91. package/public/js/codeViewer.js.map +1 -0
  92. package/public/js/components/FileChip.d.ts +9 -0
  93. package/public/js/components/FileChip.js +45 -49
  94. package/public/js/components/FileChip.js.map +1 -0
  95. package/public/js/components/MetricSparkline.d.ts +7 -0
  96. package/public/js/components/MetricSparkline.js +35 -44
  97. package/public/js/components/MetricSparkline.js.map +1 -0
  98. package/public/js/components/RiskBadge.d.ts +9 -0
  99. package/public/js/components/RiskBadge.js +55 -63
  100. package/public/js/components/RiskBadge.js.map +1 -0
  101. package/public/js/deepAnalysisView.d.ts +99 -0
  102. package/public/js/deepAnalysisView.js +282 -302
  103. package/public/js/deepAnalysisView.js.map +1 -0
  104. package/public/js/diagramPane.d.ts +8 -0
  105. package/public/js/diagramPane.js +133 -135
  106. package/public/js/diagramPane.js.map +1 -0
  107. package/public/js/diffView.d.ts +6 -0
  108. package/public/js/diffView.js +222 -209
  109. package/public/js/diffView.js.map +1 -0
  110. package/public/js/docsView.d.ts +2 -0
  111. package/public/js/docsView.js +441 -442
  112. package/public/js/docsView.js.map +1 -0
  113. package/public/js/dom.d.ts +189 -0
  114. package/public/js/dom.js +5 -0
  115. package/public/js/dom.js.map +1 -0
  116. package/public/js/fileSourceBrowser.d.ts +5 -0
  117. package/public/js/fileSourceBrowser.js +33 -41
  118. package/public/js/fileSourceBrowser.js.map +1 -0
  119. package/public/js/flameGraph.d.ts +5 -0
  120. package/public/js/flameGraph.js +62 -72
  121. package/public/js/flameGraph.js.map +1 -0
  122. package/public/js/forceGraph.d.ts +10 -0
  123. package/public/js/forceGraph.js +581 -556
  124. package/public/js/forceGraph.js.map +1 -0
  125. package/public/js/heatmap.d.ts +4 -0
  126. package/public/js/heatmap.js +142 -164
  127. package/public/js/heatmap.js.map +1 -0
  128. package/public/js/highlight.d.ts +2 -0
  129. package/public/js/highlight.js +116 -107
  130. package/public/js/highlight.js.map +1 -0
  131. package/public/js/html.d.ts +2 -0
  132. package/public/js/html.js +14 -44
  133. package/public/js/html.js.map +1 -0
  134. package/public/js/insightsView.d.ts +5 -0
  135. package/public/js/insightsView.js +93 -111
  136. package/public/js/insightsView.js.map +1 -0
  137. package/public/js/inspector.d.ts +37 -0
  138. package/public/js/inspector.js +416 -445
  139. package/public/js/inspector.js.map +1 -0
  140. package/public/js/llm.d.ts +113 -0
  141. package/public/js/llm.js +233 -272
  142. package/public/js/llm.js.map +1 -0
  143. package/public/js/mapView.d.ts +3 -0
  144. package/public/js/mapView.js +121 -120
  145. package/public/js/mapView.js.map +1 -0
  146. package/public/js/markdown.d.ts +3 -0
  147. package/public/js/markdown.js +84 -91
  148. package/public/js/markdown.js.map +1 -0
  149. package/public/js/mindmap.d.ts +32 -0
  150. package/public/js/mindmap.js +244 -259
  151. package/public/js/mindmap.js.map +1 -0
  152. package/public/js/mobilePanels.d.ts +5 -0
  153. package/public/js/mobilePanels.js +50 -0
  154. package/public/js/mobilePanels.js.map +1 -0
  155. package/public/js/repoFiles.d.ts +12 -0
  156. package/public/js/repoFiles.js +28 -27
  157. package/public/js/repoFiles.js.map +1 -0
  158. package/public/js/reviewView.d.ts +28 -0
  159. package/public/js/reviewView.js +256 -0
  160. package/public/js/reviewView.js.map +1 -0
  161. package/public/js/sbomView.d.ts +3 -0
  162. package/public/js/sbomView.js +59 -67
  163. package/public/js/sbomView.js.map +1 -0
  164. package/public/js/scanCache.d.ts +16 -0
  165. package/public/js/scanCache.js +67 -65
  166. package/public/js/scanCache.js.map +1 -0
  167. package/public/js/search.d.ts +7 -0
  168. package/public/js/search.js +868 -800
  169. package/public/js/search.js.map +1 -0
  170. package/public/js/searchLocal.d.ts +42 -0
  171. package/public/js/searchLocal.js +87 -0
  172. package/public/js/searchLocal.js.map +1 -0
  173. package/public/js/serverSettings.d.ts +5 -0
  174. package/public/js/serverSettings.js +153 -164
  175. package/public/js/serverSettings.js.map +1 -0
  176. package/public/js/state.d.ts +798 -0
  177. package/public/js/state.js +114 -122
  178. package/public/js/state.js.map +1 -0
  179. package/public/js/tour.d.ts +6 -0
  180. package/public/js/tour.js +5 -7
  181. package/public/js/tour.js.map +1 -0
  182. package/public/js/transitions.d.ts +2 -0
  183. package/public/js/transitions.js +9 -10
  184. package/public/js/transitions.js.map +1 -0
  185. package/public/js/tree.d.ts +12 -0
  186. package/public/js/tree.js +157 -154
  187. package/public/js/tree.js.map +1 -0
  188. package/public/js/workflowsView.d.ts +3 -0
  189. package/public/js/workflowsView.js +15 -22
  190. package/public/js/workflowsView.js.map +1 -0
  191. package/public/search.css +495 -0
  192. package/public/styles.css +163 -346
  193. package/public/workbench.css +166 -0
  194. package/server/apiAuth.d.ts +3 -0
  195. package/server/apiAuth.js +18 -20
  196. package/server/apiAuth.js.map +1 -0
  197. package/server/apiDiff.d.ts +2 -0
  198. package/server/apiDiff.js +20 -18
  199. package/server/apiDiff.js.map +1 -0
  200. package/server/apiDocs.d.ts +2 -0
  201. package/server/apiDocs.js +38 -40
  202. package/server/apiDocs.js.map +1 -0
  203. package/server/apiFile.d.ts +1 -0
  204. package/server/apiFile.js +14 -17
  205. package/server/apiFile.js.map +1 -0
  206. package/server/apiGitBlame.d.ts +2 -0
  207. package/server/apiGitBlame.js +50 -51
  208. package/server/apiGitBlame.js.map +1 -0
  209. package/server/apiMcp.d.ts +4 -0
  210. package/server/apiMcp.js +64 -66
  211. package/server/apiMcp.js.map +1 -0
  212. package/server/apiReview.d.ts +2 -0
  213. package/server/apiReview.js +45 -0
  214. package/server/apiReview.js.map +1 -0
  215. package/server/apiScan.d.ts +5 -0
  216. package/server/apiScan.js +90 -71
  217. package/server/apiScan.js.map +1 -0
  218. package/server/apiSearch.d.ts +33 -0
  219. package/server/apiSearch.js +184 -200
  220. package/server/apiSearch.js.map +1 -0
  221. package/server/apiSettings.d.ts +4 -0
  222. package/server/apiSettings.js +84 -85
  223. package/server/apiSettings.js.map +1 -0
  224. package/server/apiTools.d.ts +3 -0
  225. package/server/apiTools.js +55 -56
  226. package/server/apiTools.js.map +1 -0
  227. package/server/auth.d.ts +5 -0
  228. package/server/auth.js +54 -51
  229. package/server/auth.js.map +1 -0
  230. package/server/config.d.ts +42 -0
  231. package/server/config.js +189 -194
  232. package/server/config.js.map +1 -0
  233. package/server/daemon.d.ts +12 -0
  234. package/server/daemon.js +124 -114
  235. package/server/daemon.js.map +1 -0
  236. package/server/fileSourceNode.d.ts +2 -0
  237. package/server/fileSourceNode.js +34 -36
  238. package/server/fileSourceNode.js.map +1 -0
  239. package/server/gitClone.d.ts +9 -0
  240. package/server/gitClone.js +59 -61
  241. package/server/gitClone.js.map +1 -0
  242. package/server/gitDiff.d.ts +36 -0
  243. package/server/gitDiff.js +271 -169
  244. package/server/gitDiff.js.map +1 -0
  245. package/server/gitHistory.d.ts +12 -0
  246. package/server/gitHistory.js +80 -80
  247. package/server/gitHistory.js.map +1 -0
  248. package/server/gitRemote.d.ts +12 -0
  249. package/server/gitRemote.js +60 -0
  250. package/server/gitRemote.js.map +1 -0
  251. package/server/htmlText.d.ts +4 -0
  252. package/server/htmlText.js +16 -23
  253. package/server/htmlText.js.map +1 -0
  254. package/server/http.d.ts +5 -0
  255. package/server/http.js +27 -30
  256. package/server/http.js.map +1 -0
  257. package/server/httpGuards.d.ts +2 -0
  258. package/server/httpGuards.js +47 -43
  259. package/server/httpGuards.js.map +1 -0
  260. package/server/https.d.ts +34 -0
  261. package/server/https.js +121 -111
  262. package/server/https.js.map +1 -0
  263. package/server/index.d.ts +40 -0
  264. package/server/index.js +139 -147
  265. package/server/index.js.map +1 -0
  266. package/server/layout.d.ts +26 -0
  267. package/server/layout.js +59 -37
  268. package/server/layout.js.map +1 -0
  269. package/server/llmProxy.d.ts +3 -0
  270. package/server/llmProxy.js +137 -135
  271. package/server/llmProxy.js.map +1 -0
  272. package/server/logger.d.ts +38 -0
  273. package/server/logger.js +198 -211
  274. package/server/logger.js.map +1 -0
  275. package/server/mcp/analysis.d.ts +29 -0
  276. package/server/mcp/analysis.js +99 -120
  277. package/server/mcp/analysis.js.map +1 -0
  278. package/server/mcp/http.d.ts +9 -0
  279. package/server/mcp/http.js +128 -137
  280. package/server/mcp/http.js.map +1 -0
  281. package/server/mcp/runner.d.ts +68 -0
  282. package/server/mcp/runner.js +208 -218
  283. package/server/mcp/runner.js.map +1 -0
  284. package/server/mcp/server.d.ts +29 -0
  285. package/server/mcp/server.js +166 -179
  286. package/server/mcp/server.js.map +1 -0
  287. package/server/mcp/standalone.d.ts +1 -0
  288. package/server/mcp/standalone.js +8 -10
  289. package/server/mcp/standalone.js.map +1 -0
  290. package/server/mcp/tools.d.ts +2020 -0
  291. package/server/mcp/tools.js +523 -574
  292. package/server/mcp/tools.js.map +1 -0
  293. package/server/paths.d.ts +3 -0
  294. package/server/paths.js +17 -15
  295. package/server/paths.js.map +1 -0
  296. package/server/pidfile.d.ts +9 -0
  297. package/server/pidfile.js +43 -45
  298. package/server/pidfile.js.map +1 -0
  299. package/server/review.d.ts +5 -0
  300. package/server/review.js +35 -0
  301. package/server/review.js.map +1 -0
  302. package/server/router.d.ts +33 -0
  303. package/server/router.js +191 -197
  304. package/server/router.js.map +1 -0
  305. package/server/scanRequest.d.ts +3 -0
  306. package/server/scanRequest.js +36 -0
  307. package/server/scanRequest.js.map +1 -0
  308. package/server/searchIndex.d.ts +17 -0
  309. package/server/searchIndex.js +83 -80
  310. package/server/searchIndex.js.map +1 -0
  311. package/server/sessions.d.ts +14 -0
  312. package/server/sessions.js +62 -70
  313. package/server/sessions.js.map +1 -0
  314. package/server/startup.d.ts +118 -0
  315. package/server/startup.js +143 -148
  316. package/server/startup.js.map +1 -0
  317. package/server/static.d.ts +6 -0
  318. package/server/static.js +28 -31
  319. package/server/static.js.map +1 -0
  320. package/server/tools/install.d.ts +4 -0
  321. package/server/tools/install.js +176 -185
  322. package/server/tools/install.js.map +1 -0
  323. package/server/tools/parse.d.ts +8 -0
  324. package/server/tools/parse.js +112 -114
  325. package/server/tools/parse.js.map +1 -0
  326. package/server/tools/platform.d.ts +9 -0
  327. package/server/tools/platform.js +49 -47
  328. package/server/tools/platform.js.map +1 -0
  329. package/server/tools/registry.d.ts +137 -0
  330. package/server/tools/registry.js +173 -172
  331. package/server/tools/registry.js.map +1 -0
  332. package/server/tools/scan.d.ts +34 -0
  333. package/server/tools/scan.js +89 -98
  334. package/server/tools/scan.js.map +1 -0
  335. package/server/tools.d.ts +34 -0
  336. package/server/tools.js +94 -107
  337. package/server/tools.js.map +1 -0
  338. package/server/tunnel.d.ts +29 -0
  339. package/server/tunnel.js +61 -66
  340. package/server/tunnel.js.map +1 -0
  341. package/shared/analyzer/docs.d.ts +16 -0
  342. package/shared/analyzer/docs.js +99 -93
  343. package/shared/analyzer/docs.js.map +1 -0
  344. package/shared/analyzer/explainLocal.d.ts +4 -0
  345. package/shared/analyzer/explainLocal.js +122 -142
  346. package/shared/analyzer/explainLocal.js.map +1 -0
  347. package/shared/analyzer/github.d.ts +37 -0
  348. package/shared/analyzer/github.js +79 -0
  349. package/shared/analyzer/github.js.map +1 -0
  350. package/shared/analyzer/graph.d.ts +41 -0
  351. package/shared/analyzer/graph.js +393 -400
  352. package/shared/analyzer/graph.js.map +1 -0
  353. package/shared/analyzer/health.d.ts +33 -0
  354. package/shared/analyzer/health.js +197 -193
  355. package/shared/analyzer/health.js.map +1 -0
  356. package/shared/analyzer/history.d.ts +29 -0
  357. package/shared/analyzer/history.js +110 -111
  358. package/shared/analyzer/history.js.map +1 -0
  359. package/shared/analyzer/jsonConfig.d.ts +2 -0
  360. package/shared/analyzer/jsonConfig.js +61 -0
  361. package/shared/analyzer/jsonConfig.js.map +1 -0
  362. package/shared/analyzer/languages/csharp.d.ts +26 -0
  363. package/shared/analyzer/languages/csharp.js +83 -88
  364. package/shared/analyzer/languages/csharp.js.map +1 -0
  365. package/shared/analyzer/languages/generic.d.ts +23 -0
  366. package/shared/analyzer/languages/generic.js +94 -105
  367. package/shared/analyzer/languages/generic.js.map +1 -0
  368. package/shared/analyzer/languages/go.d.ts +24 -0
  369. package/shared/analyzer/languages/go.js +45 -54
  370. package/shared/analyzer/languages/go.js.map +1 -0
  371. package/shared/analyzer/languages/index.d.ts +6 -0
  372. package/shared/analyzer/languages/index.js +19 -20
  373. package/shared/analyzer/languages/index.js.map +1 -0
  374. package/shared/analyzer/languages/java.d.ts +20 -0
  375. package/shared/analyzer/languages/java.js +73 -78
  376. package/shared/analyzer/languages/java.js.map +1 -0
  377. package/shared/analyzer/languages/javascript.d.ts +12 -0
  378. package/shared/analyzer/languages/javascript.js +258 -209
  379. package/shared/analyzer/languages/javascript.js.map +1 -0
  380. package/shared/analyzer/languages/python.d.ts +23 -0
  381. package/shared/analyzer/languages/python.js +92 -100
  382. package/shared/analyzer/languages/python.js.map +1 -0
  383. package/shared/analyzer/languages/rust.d.ts +17 -0
  384. package/shared/analyzer/languages/rust.js +96 -98
  385. package/shared/analyzer/languages/rust.js.map +1 -0
  386. package/shared/analyzer/languages/typescript.d.ts +11 -0
  387. package/shared/analyzer/languages/typescript.js +117 -116
  388. package/shared/analyzer/languages/typescript.js.map +1 -0
  389. package/shared/analyzer/licenses.d.ts +18 -0
  390. package/shared/analyzer/licenses.js +130 -133
  391. package/shared/analyzer/licenses.js.map +1 -0
  392. package/shared/analyzer/metrics.d.ts +20 -0
  393. package/shared/analyzer/metrics.js +112 -104
  394. package/shared/analyzer/metrics.js.map +1 -0
  395. package/shared/analyzer/pathUtil.d.ts +10 -0
  396. package/shared/analyzer/pathUtil.js +29 -35
  397. package/shared/analyzer/pathUtil.js.map +1 -0
  398. package/shared/analyzer/patterns.d.ts +11 -0
  399. package/shared/analyzer/patterns.js +231 -253
  400. package/shared/analyzer/patterns.js.map +1 -0
  401. package/shared/analyzer/scan.d.ts +62 -0
  402. package/shared/analyzer/scan.js +451 -442
  403. package/shared/analyzer/scan.js.map +1 -0
  404. package/shared/analyzer/scanControl.d.ts +11 -0
  405. package/shared/analyzer/scanControl.js +62 -0
  406. package/shared/analyzer/scanControl.js.map +1 -0
  407. package/shared/analyzer/security.d.ts +18 -0
  408. package/shared/analyzer/security.js +159 -159
  409. package/shared/analyzer/security.js.map +1 -0
  410. package/shared/analyzer/services.d.ts +5 -0
  411. package/shared/analyzer/services.js +141 -138
  412. package/shared/analyzer/services.js.map +1 -0
  413. package/shared/analyzer/stack.d.ts +6 -0
  414. package/shared/analyzer/stack.js +134 -141
  415. package/shared/analyzer/stack.js.map +1 -0
  416. package/shared/analyzer/tour.d.ts +2 -0
  417. package/shared/analyzer/tour.js +45 -53
  418. package/shared/analyzer/tour.js.map +1 -0
  419. package/shared/analyzer/util.d.ts +8 -0
  420. package/shared/analyzer/util.js +130 -76
  421. package/shared/analyzer/util.js.map +1 -0
  422. package/shared/analyzer/workflows.d.ts +8 -0
  423. package/shared/analyzer/workflows.js +139 -134
  424. package/shared/analyzer/workflows.js.map +1 -0
  425. package/shared/contracts.d.ts +250 -0
  426. package/shared/contracts.js +2 -0
  427. package/shared/contracts.js.map +1 -0
  428. package/shared/diagram/aiFacts.d.ts +35 -0
  429. package/shared/diagram/aiFacts.js +102 -105
  430. package/shared/diagram/aiFacts.js.map +1 -0
  431. package/shared/diagram/aiMermaid.d.ts +5 -0
  432. package/shared/diagram/aiMermaid.js +46 -45
  433. package/shared/diagram/aiMermaid.js.map +1 -0
  434. package/shared/diagram/atlas.d.ts +16 -0
  435. package/shared/diagram/atlas.js +51 -60
  436. package/shared/diagram/atlas.js.map +1 -0
  437. package/shared/diagram/mermaid.d.ts +43 -0
  438. package/shared/diagram/mermaid.js +348 -370
  439. package/shared/diagram/mermaid.js.map +1 -0
  440. package/shared/review/contracts.d.ts +95 -0
  441. package/shared/review/contracts.js +2 -0
  442. package/shared/review/contracts.js.map +1 -0
  443. package/shared/review/review.d.ts +10 -0
  444. package/shared/review/review.js +184 -0
  445. package/shared/review/review.js.map +1 -0
  446. package/shared/search/query.d.ts +32 -0
  447. package/shared/search/query.js +395 -388
  448. package/shared/search/query.js.map +1 -0
@@ -1,3 +1,6 @@
1
+ import { parseJsonConfig } from './jsonConfig.js';
2
+ import { DEFAULT_LIMITS, normalizeScanOptions, throwIfAborted, abortable, utf8ByteLength } from './scanControl.js';
3
+ export { DEFAULT_LIMITS } from './scanControl.js';
1
4
  // The scanner: walks a FileSource, dispatches per-language analyzers,
2
5
  // resolves imports into edges, and hands back one big plain-data result.
3
6
  //
@@ -5,500 +8,506 @@
5
8
  // list(dir) -> [{ name, path, type: 'file'|'dir' }]
6
9
  // read(path) -> string
7
10
  // Paths are repo-relative, POSIX style ('' is the root).
8
-
9
11
  import { languageFor } from './languages/index.js';
10
12
  import { dirOf, baseName, extOf } from './pathUtil.js';
11
13
  import { codeStats, complexityOf } from './metrics.js';
12
14
  import { analyzeSecurityFile } from './security.js';
13
15
  import { analyzeLicenses } from './licenses.js';
14
16
  import { analyzeWorkflows } from './workflows.js';
15
-
17
+ import { detectManifest } from './services.js';
16
18
  const SKIP_DIRS = new Set([
17
- 'node_modules', '.git', '.hg', '.svn', 'dist', 'build', 'out', '.next',
18
- '.nuxt', 'target', 'vendor', '__pycache__', '.venv', 'venv', 'env',
19
- '.idea', '.vscode', 'coverage', '.cache', '.turbo', 'tmp', 'temp',
20
- 'DerivedData', 'Pods', '.gradle', 'bin', 'obj',
19
+ 'node_modules', '.git', '.hg', '.svn', 'dist', 'build', 'out', '.next',
20
+ '.nuxt', 'target', 'vendor', '__pycache__', '.venv', 'venv', 'env',
21
+ '.idea', '.vscode', 'coverage', '.cache', '.turbo', 'tmp', 'temp',
22
+ 'DerivedData', 'Pods', '.gradle', 'bin', 'obj',
21
23
  ]);
22
-
23
24
  const SKIP_FILES = new Set([
24
- 'package-lock.json', 'yarn.lock', 'pnpm-lock.yaml', 'composer.lock',
25
- 'Gemfile.lock', 'Cargo.lock', 'poetry.lock', '.DS_Store',
25
+ 'package-lock.json', 'yarn.lock', 'pnpm-lock.yaml', 'composer.lock',
26
+ 'Gemfile.lock', 'Cargo.lock', 'poetry.lock', '.DS_Store',
26
27
  ]);
27
-
28
28
  const SKIP_EXTS = new Set([
29
- '.png', '.jpg', '.jpeg', '.gif', '.webp', '.ico', '.icns', '.svg',
30
- '.woff', '.woff2', '.ttf', '.otf', '.eot', '.mp4', '.mov', '.mp3',
31
- '.wav', '.zip', '.gz', '.tar', '.rar', '.7z', '.pdf', '.jar', '.war',
32
- '.class', '.o', '.a', '.so', '.dylib', '.dll', '.exe', '.pyc', '.wasm',
33
- '.min.js', '.min.css', '.map', '.lock', '.bin', '.db', '.sqlite',
29
+ '.png', '.jpg', '.jpeg', '.gif', '.webp', '.ico', '.icns', '.svg',
30
+ '.woff', '.woff2', '.ttf', '.otf', '.eot', '.mp4', '.mov', '.mp3',
31
+ '.wav', '.zip', '.gz', '.tar', '.rar', '.7z', '.pdf', '.jar', '.war',
32
+ '.class', '.o', '.a', '.so', '.dylib', '.dll', '.exe', '.pyc', '.wasm',
33
+ '.min.js', '.min.css', '.map', '.lock', '.bin', '.db', '.sqlite',
34
34
  ]);
35
-
36
- export const DEFAULT_LIMITS = { maxFiles: 4000, maxFileSize: 200 * 1024 };
37
-
38
35
  function shouldSkipFile(name) {
39
- if (SKIP_FILES.has(name)) return true;
40
- if (SKIP_EXTS.has(extOf(name))) return true;
41
- // extOf() only sees the final suffix, so name the common bundle shapes here.
42
- return /\.min\.(js|css)$/i.test(name);
36
+ if (SKIP_FILES.has(name))
37
+ return true;
38
+ if (SKIP_EXTS.has(extOf(name)))
39
+ return true;
40
+ // extOf() only sees the final suffix, so name the common bundle shapes here.
41
+ return /\.min\.(js|css)$/i.test(name);
43
42
  }
44
-
45
43
  export async function scanRepo(source, options = {}) {
46
- const limits = { ...DEFAULT_LIMITS, ...options };
47
- const onProgress = options.onProgress || (() => {});
48
- const startedAt = Date.now();
49
-
50
- const ignore = await buildIgnoreMatcher(source);
51
-
52
- // Every way a file or directory can drop out of the scan, counted apart.
53
- // One integer for all of them hid the difference between "we ignored a
54
- // lockfile" and "we could not read your source" — and only one of those is
55
- // something the reader needs to know about.
56
- const skips = {
57
- ignored: 0, // matched a .gitignore rule
58
- vendorDir: 0, // node_modules, dist, .git, any dotfolder
59
- notSource: 0, // images, lockfiles, minified bundles
60
- listFailed: 0, // the directory would not list
61
- notCode: 0, // no analyzer for this extension (README.md, .yml, …)
62
- readFailed: 0, // the file would not read
63
- tooLarge: 0, // over maxFileSize
64
- analyzeFailed: 0, // the language analyzer threw
65
- };
66
-
67
- // ---- Walk --------------------------------------------------------------
68
- const allFiles = [];
69
- let truncated = null;
70
- const queue = [''];
71
- walk: while (queue.length) {
72
- const dir = queue.shift();
73
- let entries;
74
- try {
75
- entries = await source.list(dir);
76
- } catch {
77
- skips.listFailed++;
78
- continue;
79
- }
80
- for (const entry of entries) {
81
- if (allFiles.length >= limits.maxFiles) {
82
- // Stop the entire walk, not just this directory. A plain `break` here
83
- // left every already-queued directory to be listed anyway, so the cap
84
- // was both slower and less predictable than it looked.
85
- truncated = { atFiles: allFiles.length, dirsQueued: queue.length };
86
- break walk;
87
- }
88
- if (ignore(entry.path)) {
89
- skips.ignored++;
90
- continue;
91
- }
92
- if (entry.type === 'dir') {
93
- if (SKIP_DIRS.has(entry.name) || entry.name.startsWith('.')) {
94
- skips.vendorDir++;
95
- continue;
44
+ const limits = normalizeScanOptions(options);
45
+ const signal = options.signal;
46
+ throwIfAborted(signal);
47
+ const onProgress = options.onProgress || ((..._args) => { });
48
+ const startedAt = Date.now();
49
+ const ignore = await abortable(buildIgnoreMatcher(source), signal);
50
+ // Every way a file or directory can drop out of the scan, counted apart.
51
+ // One integer for all of them hid the difference between "we ignored a
52
+ // lockfile" and "we could not read your source" — and only one of those is
53
+ // something the reader needs to know about.
54
+ const skips = {
55
+ ignored: 0, // matched a .gitignore rule
56
+ vendorDir: 0, // node_modules, dist, .git, any dotfolder
57
+ notSource: 0, // images, lockfiles, minified bundles
58
+ listFailed: 0, // the directory would not list
59
+ notCode: 0, // no analyzer for this extension (README.md, .yml, …)
60
+ readFailed: 0, // the file would not read
61
+ tooLarge: 0, // over maxFileSize
62
+ analyzeFailed: 0, // the language analyzer threw
63
+ };
64
+ // ---- Walk --------------------------------------------------------------
65
+ const allFiles = [];
66
+ let truncated = null;
67
+ const queue = [''];
68
+ walk: while (queue.length) {
69
+ throwIfAborted(signal);
70
+ const dir = queue.shift();
71
+ let entries;
72
+ try {
73
+ entries = await abortable(source.list(dir), signal);
96
74
  }
97
- queue.push(entry.path);
98
- } else {
99
- if (shouldSkipFile(entry.name)) {
100
- skips.notSource++;
101
- continue;
75
+ catch {
76
+ throwIfAborted(signal);
77
+ skips.listFailed++;
78
+ continue;
102
79
  }
103
- allFiles.push(entry.path);
104
- }
80
+ for (const entry of entries) {
81
+ if (allFiles.length >= limits.maxFiles) {
82
+ // Stop the entire walk, not just this directory. A plain `break` here
83
+ // left every already-queued directory to be listed anyway, so the cap
84
+ // was both slower and less predictable than it looked.
85
+ truncated = { atFiles: allFiles.length, dirsQueued: queue.length };
86
+ break walk;
87
+ }
88
+ if (ignore(entry.path)) {
89
+ skips.ignored++;
90
+ continue;
91
+ }
92
+ if (entry.type === 'dir') {
93
+ if (SKIP_DIRS.has(entry.name) || entry.name.startsWith('.')) {
94
+ skips.vendorDir++;
95
+ continue;
96
+ }
97
+ queue.push(entry.path);
98
+ }
99
+ else {
100
+ if (shouldSkipFile(entry.name)) {
101
+ skips.notSource++;
102
+ continue;
103
+ }
104
+ allFiles.push(entry.path);
105
+ }
106
+ }
107
+ onProgress({ phase: 'walk', found: allFiles.length });
105
108
  }
106
- onProgress({ phase: 'walk', found: allFiles.length });
107
- }
108
- allFiles.sort();
109
-
110
- const fileSet = new Set(allFiles);
111
- const dirSet = new Set(['']);
112
- for (const p of allFiles) {
113
- let d = dirOf(p);
114
- while (true) {
115
- if (dirSet.has(d)) break;
116
- dirSet.add(d);
117
- d = dirOf(d);
109
+ allFiles.sort();
110
+ const fileSet = new Set(allFiles);
111
+ const dirSet = new Set(['']);
112
+ for (const p of allFiles) {
113
+ let d = dirOf(p);
114
+ while (true) {
115
+ if (dirSet.has(d))
116
+ break;
117
+ dirSet.add(d);
118
+ d = dirOf(d);
119
+ }
118
120
  }
119
- }
120
- const has = (p) => fileSet.has(p);
121
- const hasDir = (d) => dirSet.has(d);
122
- const nameIndex = new Map();
123
- for (const p of allFiles) {
124
- const n = baseName(p);
125
- if (!nameIndex.has(n)) nameIndex.set(n, p);
126
- }
127
- const findByName = (n) => nameIndex.get(n) || null;
128
-
129
- // Find a directory whose path *ends with* a run of segments. This is what C#
130
- // needs and Java does not: a C# file's folder mirrors the namespace, but the
131
- // root namespace (the company or project name) is normally not a directory,
132
- // so `namespace Acme.Services` lives in `src/Services/`. Matching on a path
133
- // suffix is the only thing that finds it — stripping the whole namespace, the
134
- // Java approach, matches nothing and silently drops every import.
135
- //
136
- // Keyed by the last three segments, which is enough to tell
137
- // `Acme/Models/Entities` apart from a bare `Entities` elsewhere while staying
138
- // O(1) per lookup. A collision at the shortest key keeps the first match, and
139
- // callers query longest-first, so the most specific answer is tried first.
140
- const dirByTail = new Map();
141
- for (const d of dirSet) {
142
- if (!d) continue;
143
- const segments = d.split('/');
144
- for (let take = 1; take <= 3 && take <= segments.length; take += 1) {
145
- const key = segments.slice(segments.length - take).join('/');
146
- if (!dirByTail.has(key)) dirByTail.set(key, d);
121
+ const has = (p) => fileSet.has(p);
122
+ const hasDir = (d) => dirSet.has(d);
123
+ const nameIndex = new Map();
124
+ for (const p of allFiles) {
125
+ const n = baseName(p);
126
+ if (!nameIndex.has(n))
127
+ nameIndex.set(n, p);
147
128
  }
148
- }
149
- const findDirEndingWith = (suffix) => (suffix ? dirByTail.get(suffix) || null : null);
150
-
151
- const context = {
152
- hasDir,
153
- findDirEndingWith,
154
- findByName,
155
- modulePath: await readModulePath(source),
156
- tsPaths: await readTsConfigPaths(source),
157
- };
158
-
159
- // ---- Parse ---------------------------------------------------------------
160
- // Reads run a window ahead of the analysis; the analysis stays strictly in
161
- // path order. Waiting for one `read` to land before starting the next was the
162
- // largest wall-clock cost of a scan and the one with the least to show for
163
- // it — every adapter behind `FileSource` is I/O, and neither `node:fs` nor the
164
- // File System Access API cares how many requests are outstanding.
165
- //
166
- // Order matters more than it looks: `files`, `edges` and every skip count
167
- // have to come out identical no matter how the reads interleave, or two scans
168
- // of one repo would disagree. So the window only prefetches — the loop below
169
- // consumes it one file at a time, in the order `allFiles` was sorted into.
170
- //
171
- // The language is resolved here and kept. The resolve phase needs the same
172
- // answer per file and used to ask `languageFor` for it a second time.
173
- const files = [];
174
- const langOf = new Map();
175
- let parsed = 0;
176
- const codeFiles = [];
177
- for (const path of allFiles) {
178
- const lang = languageFor(path);
179
- if (!lang) {
180
- skips.notCode++;
181
- continue;
129
+ const findByName = (n) => nameIndex.get(n) || null;
130
+ // Find a directory whose path *ends with* a run of segments. This is what C#
131
+ // needs and Java does not: a C# file's folder mirrors the namespace, but the
132
+ // root namespace (the company or project name) is normally not a directory,
133
+ // so `namespace Acme.Services` lives in `src/Services/`. Matching on a path
134
+ // suffix is the only thing that finds it — stripping the whole namespace, the
135
+ // Java approach, matches nothing and silently drops every import.
136
+ //
137
+ // Keyed by the last three segments, which is enough to tell
138
+ // `Acme/Models/Entities` apart from a bare `Entities` elsewhere while staying
139
+ // O(1) per lookup. A collision at the shortest key keeps the first match, and
140
+ // callers query longest-first, so the most specific answer is tried first.
141
+ const dirByTail = new Map();
142
+ for (const d of dirSet) {
143
+ if (!d)
144
+ continue;
145
+ const segments = d.split('/');
146
+ for (let take = 1; take <= 3 && take <= segments.length; take += 1) {
147
+ const key = segments.slice(segments.length - take).join('/');
148
+ if (!dirByTail.has(key))
149
+ dirByTail.set(key, d);
150
+ }
182
151
  }
183
- langOf.set(path, lang);
184
- codeFiles.push(path);
185
- }
186
-
187
- for await (const { path, text, failed } of readAhead(source, codeFiles)) {
188
- if (failed) {
189
- skips.readFailed++;
190
- continue;
152
+ const findDirEndingWith = (suffix) => (suffix ? dirByTail.get(suffix) || null : null);
153
+ const context = {
154
+ hasDir,
155
+ findDirEndingWith,
156
+ findByName,
157
+ modulePath: await abortable(readModulePath(source), signal),
158
+ tsPaths: await abortable(readTsConfigPaths(source), signal),
159
+ };
160
+ // ---- Parse ---------------------------------------------------------------
161
+ // Reads run a window ahead of the analysis; the analysis stays strictly in
162
+ // path order. Waiting for one `read` to land before starting the next was the
163
+ // largest wall-clock cost of a scan and the one with the least to show for
164
+ // it — every adapter behind `FileSource` is I/O, and neither `node:fs` nor the
165
+ // File System Access API cares how many requests are outstanding.
166
+ //
167
+ // Order matters more than it looks: `files`, `edges` and every skip count
168
+ // have to come out identical no matter how the reads interleave, or two scans
169
+ // of one repo would disagree. So the window only prefetches — the loop below
170
+ // consumes it one file at a time, in the order `allFiles` was sorted into.
171
+ //
172
+ // The language is resolved here and kept. The resolve phase needs the same
173
+ // answer per file and used to ask `languageFor` for it a second time.
174
+ const files = [];
175
+ const langOf = new Map();
176
+ let parsed = 0;
177
+ const codeFiles = [];
178
+ for (const path of allFiles) {
179
+ const lang = languageFor(path);
180
+ if (!lang) {
181
+ skips.notCode++;
182
+ continue;
183
+ }
184
+ langOf.set(path, lang);
185
+ codeFiles.push(path);
191
186
  }
192
- if (text.length > limits.maxFileSize) {
193
- skips.tooLarge++;
194
- continue;
187
+ for await (const { path, text, failed } of readAhead(source, codeFiles, limits.readConcurrency, signal)) {
188
+ throwIfAborted(signal);
189
+ if (failed) {
190
+ skips.readFailed++;
191
+ continue;
192
+ }
193
+ const size = utf8ByteLength(text);
194
+ if (size > limits.maxFileSize) {
195
+ skips.tooLarge++;
196
+ continue;
197
+ }
198
+ const lang = langOf.get(path);
199
+ let analysis;
200
+ try {
201
+ analysis = lang.analyze(text, path);
202
+ }
203
+ catch {
204
+ skips.analyzeFailed++;
205
+ continue;
206
+ }
207
+ const cs = codeStats(text, lang.id);
208
+ const findings = analyzeSecurityFile(text, lang.id);
209
+ const docRatio = cs.code + cs.comment > 0 ? Math.round((cs.comment / (cs.code + cs.comment)) * 100) : 0;
210
+ files.push({
211
+ path,
212
+ dir: dirOf(path),
213
+ name: baseName(path),
214
+ ext: extOf(path),
215
+ lang: lang.id,
216
+ size,
217
+ loc: cs.code,
218
+ comment: cs.comment,
219
+ blank: cs.blank,
220
+ lines: cs.lines,
221
+ docRatio,
222
+ complexity: complexityOf(text, lang.id),
223
+ findings: findings.length ? findings : undefined,
224
+ ...analysis,
225
+ });
226
+ parsed++;
227
+ if (parsed % 25 === 0)
228
+ onProgress({ phase: 'parse', done: parsed, total: codeFiles.length });
195
229
  }
196
- const lang = langOf.get(path);
197
- let analysis;
198
- try {
199
- analysis = lang.analyze(text, path);
200
- } catch {
201
- skips.analyzeFailed++;
202
- continue;
230
+ onProgress({ phase: 'parse', done: parsed, total: codeFiles.length });
231
+ // ---- Resolve imports into edges -------------------------------------------
232
+ const edges = [];
233
+ const edgeKeys = new Map(); // key -> edge
234
+ const externals = new Map(); // package name -> Set of files
235
+ // What share of the imports we found did we manage to place? A regex-based
236
+ // analyzer owes its reader this number more than any other: it is the
237
+ // engine's own estimate of how much of the graph is missing.
238
+ const tally = { total: 0, internal: 0, external: 0, unresolved: 0 };
239
+ const unresolvedSpecs = new Map(); // spec -> { count, from }
240
+ // A resolver may answer with a *directory* instead of a file: Go's import is a
241
+ // package, and a C# `using Acme.Models;` names a namespace whose classes we
242
+ // cannot know from the import alone. Every file in that directory is a
243
+ // target. Indexed once here rather than scanned per import — a Go repo with
244
+ // 1,000 files and 8,000 imports was doing eight million comparisons for it.
245
+ //
246
+ // Language-neutral on purpose: this used to be `goFilesByDir`, built only from
247
+ // Go files, which meant no other language could use the mechanism that already
248
+ // existed for exactly this problem.
249
+ const filesByDir = new Map();
250
+ for (const f of files) {
251
+ const bucket = filesByDir.get(f.dir);
252
+ if (bucket)
253
+ bucket.push(f.path);
254
+ else
255
+ filesByDir.set(f.dir, [f.path]);
203
256
  }
204
- const cs = codeStats(text, lang.id);
205
- const findings = analyzeSecurityFile(text, lang.id);
206
- const docRatio = cs.code + cs.comment > 0 ? Math.round((cs.comment / (cs.code + cs.comment)) * 100) : 0;
207
- files.push({
208
- path,
209
- dir: dirOf(path),
210
- name: baseName(path),
211
- ext: extOf(path),
212
- lang: lang.id,
213
- size: text.length,
214
- loc: cs.code,
215
- comment: cs.comment,
216
- blank: cs.blank,
217
- lines: cs.lines,
218
- docRatio,
219
- complexity: complexityOf(text, lang.id),
220
- findings: findings.length ? findings : undefined,
221
- ...analysis,
222
- });
223
- parsed++;
224
- if (parsed % 25 === 0) onProgress({ phase: 'parse', done: parsed });
225
- }
226
- onProgress({ phase: 'parse', done: parsed });
227
- // ---- Resolve imports into edges -------------------------------------------
228
- const edges = [];
229
- const edgeKeys = new Map(); // key -> edge
230
- const externals = new Map(); // package name -> Set of files
231
-
232
- // What share of the imports we found did we manage to place? A regex-based
233
- // analyzer owes its reader this number more than any other: it is the
234
- // engine's own estimate of how much of the graph is missing.
235
- const tally = { total: 0, internal: 0, external: 0, unresolved: 0 };
236
- const unresolvedSpecs = new Map(); // spec -> { count, from }
237
-
238
- // A resolver may answer with a *directory* instead of a file: Go's import is a
239
- // package, and a C# `using Acme.Models;` names a namespace whose classes we
240
- // cannot know from the import alone. Every file in that directory is a
241
- // target. Indexed once here rather than scanned per import — a Go repo with
242
- // 1,000 files and 8,000 imports was doing eight million comparisons for it.
243
- //
244
- // Language-neutral on purpose: this used to be `goFilesByDir`, built only from
245
- // Go files, which meant no other language could use the mechanism that already
246
- // existed for exactly this problem.
247
- const filesByDir = new Map();
248
- for (const f of files) {
249
- const bucket = filesByDir.get(f.dir);
250
- if (bucket) bucket.push(f.path);
251
- else filesByDir.set(f.dir, [f.path]);
252
- }
253
-
254
- for (const file of files) {
255
- const lang = langOf.get(file.path);
256
- for (const imp of file.imports) {
257
- tally.total++;
258
- const res = lang.resolveImport(imp.spec, file.path, has, context, imp);
259
- if (res.path) {
260
- tally.internal++;
261
- addEdge(edges, edgeKeys, file.path, res.path, 'imports', imp.symbols);
262
- } else if (res.packageDir) {
263
- tally.internal++;
264
- // A directory import means "everything in there". addEdge de-duplicates,
265
- // so a package with fifty files yields fifty honest edges rather than
266
- // one edge standing in for all of them.
267
- for (const target of filesByDir.get(res.packageDir) || []) {
268
- addEdge(edges, edgeKeys, file.path, target, 'imports', imp.symbols);
257
+ let resolved = 0;
258
+ for (const file of files) {
259
+ throwIfAborted(signal);
260
+ if (resolved++ % 25 === 0)
261
+ onProgress({ phase: 'resolve', done: resolved, total: files.length });
262
+ const lang = langOf.get(file.path);
263
+ for (const imp of file.imports) {
264
+ tally.total++;
265
+ const res = lang.resolveImport(imp.spec, file.path, has, context, imp);
266
+ if (res.path) {
267
+ tally.internal++;
268
+ addEdge(edges, edgeKeys, file.path, res.path, 'imports', imp.symbols);
269
+ }
270
+ else if (res.packageDir) {
271
+ tally.internal++;
272
+ // A directory import means "everything in there". addEdge de-duplicates,
273
+ // so a package with fifty files yields fifty honest edges rather than
274
+ // one edge standing in for all of them.
275
+ for (const target of filesByDir.get(res.packageDir) || []) {
276
+ addEdge(edges, edgeKeys, file.path, target, 'imports', imp.symbols);
277
+ }
278
+ }
279
+ else if (res.external) {
280
+ tally.external++;
281
+ if (!externals.has(res.external))
282
+ externals.set(res.external, new Set());
283
+ externals.get(res.external).add(file.path);
284
+ }
285
+ else {
286
+ // Every resolver ends with `return { unresolved: spec }`. This branch
287
+ // is where that used to disappear for want of an `else` — the import
288
+ // was counted nowhere and the graph simply came up short in silence.
289
+ tally.unresolved++;
290
+ const spec = res.unresolved || imp.spec;
291
+ const seen = unresolvedSpecs.get(spec);
292
+ if (seen)
293
+ seen.count++;
294
+ else
295
+ unresolvedSpecs.set(spec, { spec, count: 1, from: file.path });
296
+ }
269
297
  }
270
- } else if (res.external) {
271
- tally.external++;
272
- if (!externals.has(res.external)) externals.set(res.external, new Set());
273
- externals.get(res.external).add(file.path);
274
- } else {
275
- // Every resolver ends with `return { unresolved: spec }`. This branch
276
- // is where that used to disappear for want of an `else` — the import
277
- // was counted nowhere and the graph simply came up short in silence.
278
- tally.unresolved++;
279
- const spec = res.unresolved || imp.spec;
280
- const seen = unresolvedSpecs.get(spec);
281
- if (seen) seen.count++;
282
- else unresolvedSpecs.set(spec, { spec, count: 1, from: file.path });
283
- }
284
298
  }
285
- }
286
-
287
- // Resolved-or-correctly-external over everything we saw. An external package
288
- // counts as a success: the resolver knew what it was looking at.
289
- const confidence = tally.total
290
- ? Math.round(((tally.internal + tally.external) / tally.total) * 100)
291
- : 100;
292
- const worstUnresolved = [...unresolvedSpecs.values()]
293
- .sort((a, b) => b.count - a.count || a.spec.localeCompare(b.spec))
294
- .slice(0, 12);
295
-
296
- const externalList = [...externals.entries()]
297
- .map(([name, usedBySet]) => ({ name, usedBy: [...usedBySet].sort() }))
298
- .sort((a, b) => b.usedBy.length - a.usedBy.length || a.name.localeCompare(b.name));
299
-
300
- // Folder rollups with full size/LOC/documentation breakdown
301
- const filesPerDir = new Map();
302
- const folderData = new Map();
303
- for (const p of allFiles) {
304
- const d = dirOf(p);
305
- filesPerDir.set(d, (filesPerDir.get(d) || 0) + 1);
306
- }
307
- for (const path of dirSet) {
308
- if (!path) continue;
309
- folderData.set(path, {
310
- path,
311
- name: baseName(path),
312
- depth: path.split('/').length,
313
- fileCount: filesPerDir.get(path) || 0,
314
- loc: 0,
315
- comment: 0,
316
- blank: 0,
317
- size: 0,
318
- langs: {},
319
- hasReadme: false,
320
- });
321
- }
322
- for (const p of allFiles) {
323
- const d = dirOf(p);
324
- const bn = baseName(p).toLowerCase();
325
- if (bn.startsWith('readme.') || bn === 'readme') {
326
- const entry = folderData.get(d);
327
- if (entry) entry.hasReadme = true;
299
+ // Resolved-or-correctly-external over everything we saw. An external package
300
+ // counts as a success: the resolver knew what it was looking at.
301
+ const confidence = tally.total
302
+ ? Math.round(((tally.internal + tally.external) / tally.total) * 100)
303
+ : 100;
304
+ const worstUnresolved = [...unresolvedSpecs.values()]
305
+ .sort((a, b) => b.count - a.count || a.spec.localeCompare(b.spec))
306
+ .slice(0, 12);
307
+ const externalList = [...externals.entries()]
308
+ .map(([name, usedBySet]) => ({ name, usedBy: [...usedBySet].sort() }))
309
+ .sort((a, b) => b.usedBy.length - a.usedBy.length || a.name.localeCompare(b.name));
310
+ // Folder rollups with full size/LOC/documentation breakdown
311
+ const filesPerDir = new Map();
312
+ const folderData = new Map();
313
+ for (const p of allFiles) {
314
+ const d = dirOf(p);
315
+ filesPerDir.set(d, (filesPerDir.get(d) || 0) + 1);
316
+ }
317
+ for (const path of dirSet) {
318
+ if (!path)
319
+ continue;
320
+ folderData.set(path, {
321
+ path,
322
+ name: baseName(path),
323
+ depth: path.split('/').length,
324
+ fileCount: filesPerDir.get(path) || 0,
325
+ loc: 0,
326
+ comment: 0,
327
+ blank: 0,
328
+ size: 0,
329
+ langs: {},
330
+ hasReadme: false,
331
+ });
332
+ }
333
+ for (const p of allFiles) {
334
+ const d = dirOf(p);
335
+ const bn = baseName(p).toLowerCase();
336
+ if (bn.startsWith('readme.') || bn === 'readme') {
337
+ const entry = folderData.get(d);
338
+ if (entry)
339
+ entry.hasReadme = true;
340
+ }
328
341
  }
329
- }
330
- for (const f of files) {
331
- const entry = folderData.get(f.dir);
332
- if (entry) {
333
- entry.loc += f.loc || 0;
334
- entry.comment += f.comment || 0;
335
- entry.blank += f.blank || 0;
336
- entry.size += f.size || 0;
337
- entry.langs[f.lang] = (entry.langs[f.lang] || 0) + (f.loc || 0);
342
+ for (const f of files) {
343
+ const entry = folderData.get(f.dir);
344
+ if (entry) {
345
+ entry.loc += f.loc || 0;
346
+ entry.comment += f.comment || 0;
347
+ entry.blank += f.blank || 0;
348
+ entry.size += f.size || 0;
349
+ entry.langs[f.lang] = (entry.langs[f.lang] || 0) + (f.loc || 0);
350
+ }
338
351
  }
339
- }
340
- const folders = [...folderData.values()]
341
- .map((f) => ({
342
- ...f,
343
- docRatio: f.loc + f.comment > 0 ? Math.round((f.comment / (f.loc + f.comment)) * 100) : 0,
352
+ const folders = [...folderData.values()]
353
+ .map((f) => ({
354
+ ...f,
355
+ docRatio: f.loc + f.comment > 0 ? Math.round((f.comment / (f.loc + f.comment)) * 100) : 0,
344
356
  }))
345
- .sort((a, b) => a.path.localeCompare(b.path));
346
-
347
- const skippedTotal = Object.values(skips).reduce((a, b) => a + b, 0);
348
-
349
- const stats = {
350
- filesTotal: allFiles.length,
351
- filesParsed: parsed,
352
- skipped: skippedTotal,
353
- skips,
354
- truncated,
355
- edgeCount: edges.length,
356
- imports: {
357
- total: tally.total,
358
- internal: tally.internal,
359
- external: tally.external,
360
- unresolved: tally.unresolved,
361
- confidence,
362
- worst: worstUnresolved,
363
- },
364
- tookMs: Date.now() - startedAt,
365
- languages: countBy(files, (f) => f.lang),
366
- languageLoc: files.reduce((acc, f) => {
367
- acc[f.lang] = (acc[f.lang] || 0) + (f.loc || 0);
368
- return acc;
369
- }, {}),
370
- };
371
-
372
- const [workflows, licenseReport] = await Promise.all([
373
- analyzeWorkflows(source).catch(() => []),
374
- analyzeLicenses(source, context.manifest || {}).catch(() => null),
375
- ]);
376
-
377
- return {
378
- root: source.root || '(unknown)',
379
- name: source.name || baseName(source.root || '') || 'repo',
380
- scannedAt: new Date().toISOString(),
381
- stats,
382
- allFiles,
383
- files,
384
- edges,
385
- externals: externalList,
386
- folders,
387
- workflows: workflows || [],
388
- sbom: licenseReport?.sbom || [],
389
- license: licenseReport?.projectLicense || { id: 'UNKNOWN', name: 'Unknown' },
390
- licenseReport: licenseReport || undefined,
391
- };
357
+ .sort((a, b) => a.path.localeCompare(b.path));
358
+ const skippedTotal = Object.values(skips).reduce((a, b) => a + b, 0);
359
+ const stats = {
360
+ filesTotal: allFiles.length,
361
+ filesParsed: parsed,
362
+ skipped: skippedTotal,
363
+ skips,
364
+ truncated,
365
+ edgeCount: edges.length,
366
+ imports: {
367
+ total: tally.total,
368
+ internal: tally.internal,
369
+ external: tally.external,
370
+ unresolved: tally.unresolved,
371
+ confidence,
372
+ worst: worstUnresolved,
373
+ },
374
+ tookMs: Date.now() - startedAt,
375
+ languages: countBy(files, (f) => f.lang),
376
+ languageLoc: files.reduce((acc, f) => {
377
+ acc[f.lang] = (acc[f.lang] || 0) + (f.loc || 0);
378
+ return acc;
379
+ }, {}),
380
+ };
381
+ const [workflows, licenseReport] = await abortable(Promise.all([
382
+ analyzeWorkflows(source).catch(() => []),
383
+ detectManifest(source).then((manifest) => analyzeLicenses(source, manifest)).catch(() => null),
384
+ ]), signal);
385
+ throwIfAborted(signal);
386
+ onProgress({ phase: 'complete', files: parsed, edges: edges.length });
387
+ const result = {
388
+ root: source.root || '(unknown)',
389
+ name: source.name || baseName(source.root || '') || 'repo',
390
+ scannedAt: new Date().toISOString(),
391
+ stats,
392
+ allFiles,
393
+ files,
394
+ edges,
395
+ externals: externalList,
396
+ folders,
397
+ workflows: workflows || [],
398
+ sbom: licenseReport?.sbom || [],
399
+ license: licenseReport?.projectLicense || { id: 'UNKNOWN', name: 'Unknown' },
400
+ licenseReport: licenseReport || undefined,
401
+ };
402
+ return result;
392
403
  }
393
-
394
404
  function addEdge(edges, edgeKeys, from, to, kind, symbols = []) {
395
- if (from === to) return;
396
- const key = from + '->' + to;
397
- const existing = edgeKeys.get(key);
398
- if (existing) {
399
- if (symbols && symbols.length) {
400
- existing.symbols = Array.from(new Set([...(existing.symbols || []), ...symbols]));
405
+ if (from === to)
406
+ return;
407
+ const key = from + '->' + to;
408
+ const existing = edgeKeys.get(key);
409
+ if (existing) {
410
+ if (symbols && symbols.length) {
411
+ existing.symbols = Array.from(new Set([...(existing.symbols || []), ...symbols]));
412
+ }
413
+ return;
401
414
  }
402
- return;
403
- }
404
- const edge = { from, to, kind, symbols: symbols ? symbols.slice() : [] };
405
- edgeKeys.set(key, edge);
406
- edges.push(edge);
415
+ const edge = { from, to, kind, symbols: symbols ? symbols.slice() : [] };
416
+ edgeKeys.set(key, edge);
417
+ edges.push(edge);
407
418
  }
408
-
409
419
  // How many reads may be in flight at once. Small on purpose: a file is read
410
420
  // whole before its size can be checked (a `FileSource` has no `stat`), so this
411
421
  // is also a multiplier on peak memory. Eight is enough to keep a disk or the
412
422
  // browser's file layer busy without holding a heap of large files at once.
413
423
  const READ_AHEAD = 8;
414
-
415
424
  // Yields `{ path, text, failed }` for each path, in the order given, while
416
425
  // keeping up to READ_AHEAD reads in flight. Failures are yielded rather than
417
426
  // thrown so the caller can count them and keep going — one unreadable file
418
427
  // should not end a scan.
419
- async function* readAhead(source, paths, width = READ_AHEAD) {
420
- const read = (path) =>
421
- Promise.resolve()
422
- .then(() => source.read(path))
423
- .then((text) => ({ path, text, failed: false }), () => ({ path, text: '', failed: true }));
424
-
425
- const inFlight = [];
426
- let next = 0;
427
- while (next < paths.length && inFlight.length < width) inFlight.push(read(paths[next++]));
428
-
429
- while (inFlight.length) {
430
- // shift() before the await: the slot is refilled immediately, so the next
431
- // read starts while this file is being analyzed rather than after it.
432
- const settled = inFlight.shift();
433
- if (next < paths.length) inFlight.push(read(paths[next++]));
434
- yield await settled;
435
- }
428
+ async function* readAhead(source, paths, width = READ_AHEAD, signal) {
429
+ const read = (path) => Promise.resolve()
430
+ .then(() => { throwIfAborted(signal); return source.read(path); })
431
+ .then((text) => ({ path, text, failed: false }), () => ({ path, text: '', failed: true }));
432
+ const inFlight = [];
433
+ let next = 0;
434
+ while (next < paths.length && inFlight.length < width)
435
+ inFlight.push(read(paths[next++]));
436
+ while (inFlight.length) {
437
+ throwIfAborted(signal);
438
+ // Refill only after this read settles: refilling before awaiting it made
439
+ // the configured window run one extra read. The next read still starts
440
+ // before this file is analyzed, while the total stays within the limit.
441
+ const settled = await abortable(inFlight.shift(), signal);
442
+ throwIfAborted(signal);
443
+ if (next < paths.length)
444
+ inFlight.push(read(paths[next++]));
445
+ yield settled;
446
+ }
436
447
  }
437
-
438
448
  function countBy(items, fn) {
439
- const out = {};
440
- for (const item of items) {
441
- const k = fn(item);
442
- out[k] = (out[k] || 0) + 1;
443
- }
444
- return out;
449
+ const out = {};
450
+ for (const item of items) {
451
+ const k = fn(item);
452
+ out[k] = (out[k] || 0) + 1;
453
+ }
454
+ return out;
445
455
  }
446
-
447
456
  async function readModulePath(source) {
448
- try {
449
- const text = await source.read('go.mod');
450
- const m = text.match(/^module\s+(\S+)/m);
451
- return m ? m[1] : '';
452
- } catch {
453
- return '';
454
- }
457
+ try {
458
+ const text = await source.read('go.mod');
459
+ const m = text.match(/^module\s+(\S+)/m);
460
+ return m ? m[1] : '';
461
+ }
462
+ catch {
463
+ return '';
464
+ }
455
465
  }
456
-
457
466
  // Simplified .gitignore: plain names, dir/, *.ext, /anchored. No negation —
458
467
  // documented limitation.
459
468
  async function buildIgnoreMatcher(source) {
460
- let text = '';
461
- try {
462
- text = await source.read('.gitignore');
463
- } catch {
464
- return () => false;
465
- }
466
- const rules = [];
467
- for (let line of text.split('\n')) {
468
- line = line.trim();
469
- if (!line || line.startsWith('#') || line.startsWith('!')) continue;
470
- const anchored = line.startsWith('/');
471
- const pattern = line.replace(/^\//, '').replace(/\/$/, '');
472
- if (!pattern) continue;
473
- const re = new RegExp(
474
- (anchored ? '^' : '(^|/)') +
475
- pattern.split(/(\*\*|\*)/).map((part) =>
476
- part === '**' ? '.*' : part === '*' ? '[^/]*' : escapeRe(part)
477
- ).join('') +
478
- '($|/)'
479
- );
480
- rules.push({ re });
481
- }
482
- return (path) => rules.some((r) => r.re.test(path));
469
+ let text = '';
470
+ try {
471
+ text = await source.read('.gitignore');
472
+ }
473
+ catch {
474
+ return () => false;
475
+ }
476
+ const rules = [];
477
+ for (let line of text.split('\n')) {
478
+ line = line.trim();
479
+ if (!line || line.startsWith('#') || line.startsWith('!'))
480
+ continue;
481
+ const anchored = line.startsWith('/');
482
+ const pattern = line.replace(/^\//, '').replace(/\/$/, '');
483
+ if (!pattern)
484
+ continue;
485
+ const re = new RegExp((anchored ? '^' : '(^|/)') +
486
+ pattern.split(/(\*\*|\*)/).map((part) => part === '**' ? '.*' : part === '*' ? '[^/]*' : escapeRe(part)).join('') +
487
+ '($|/)');
488
+ rules.push({ re });
489
+ }
490
+ return (path) => rules.some((r) => r.re.test(path));
483
491
  }
484
-
485
492
  function escapeRe(s) {
486
- return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
493
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
487
494
  }
488
-
489
495
  async function readTsConfigPaths(source) {
490
- for (const name of ['tsconfig.json', 'jsconfig.json']) {
491
- try {
492
- const text = await source.read(name);
493
- const clean = text.replace(/\/\*[\s\S]*?\*\/|\/\/[^\n]*/g, '');
494
- const json = JSON.parse(clean);
495
- const compilerOptions = json.compilerOptions || {};
496
- const baseUrl = compilerOptions.baseUrl ? compilerOptions.baseUrl.replace(/^\.?\//, '').replace(/\/$/, '') : '';
497
- const paths = compilerOptions.paths || {};
498
- if (Object.keys(paths).length || baseUrl) {
499
- return { baseUrl, paths };
500
- }
501
- } catch {}
502
- }
503
- return null;
496
+ for (const name of ['tsconfig.json', 'jsconfig.json']) {
497
+ try {
498
+ const text = await source.read(name);
499
+ const json = parseJsonConfig(text);
500
+ const compilerOptions = json.compilerOptions || {};
501
+ const baseUrl = compilerOptions.baseUrl ? compilerOptions.baseUrl.replace(/^\.?\//, '').replace(/\/$/, '') : '';
502
+ const paths = compilerOptions.paths || {};
503
+ const sourceRoot = compilerOptions.rootDir?.replace(/^\.?\//, '').replace(/\/$/, '');
504
+ const outputRoot = compilerOptions.outDir?.replace(/^\.?\//, '').replace(/\/$/, '').replace(/^\.$/, '');
505
+ if (Object.keys(paths).length || baseUrl || sourceRoot) {
506
+ return { baseUrl, paths, sourceRoot, outputRoot };
507
+ }
508
+ }
509
+ catch { }
510
+ }
511
+ return null;
504
512
  }
513
+ //# sourceMappingURL=scan.js.map