codebase-onboarder 0.6.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 +174 -46
  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 +321 -329
  8. package/cli/explorer/advanced.js.map +1 -0
  9. package/cli/explorer/app.d.ts +1 -0
  10. package/cli/explorer/app.js +384 -313
  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 +368 -302
  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 +42 -44
  23. package/cli/explorer/github.js.map +1 -0
  24. package/cli/explorer/graphs.d.ts +7 -0
  25. package/cli/explorer/graphs.js +90 -95
  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 +143 -144
  35. package/cli/explorer/session.js.map +1 -0
  36. package/cli/explorer/views.d.ts +19 -0
  37. package/cli/explorer/views.js +465 -482
  38. package/cli/explorer/views.js.map +1 -0
  39. package/cli/explorer/wrap.d.ts +1 -0
  40. package/cli/explorer/wrap.js +28 -44
  41. package/cli/explorer/wrap.js.map +1 -0
  42. package/cli/main.d.ts +1 -0
  43. package/cli/main.js +214 -161
  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 +35 -44
  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 +152 -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 +37 -38
  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 -64
  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 +41 -43
  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,62 +1,55 @@
1
1
  // Turns scan results into Mermaid flowchart source. Kept separate from the
2
2
  // UI so the exact same diagrams can be produced in Node (tests, exports)
3
3
  // and in the browser.
4
-
5
4
  import { baseName, dirOf, topFolderOf } from '../analyzer/pathUtil.js';
6
5
  import { factIndex, scanIndex } from '../analyzer/graph.js';
7
-
8
6
  // ---- theming ---------------------------------------------------------------
9
7
  // Every diagram embeds its own init directive, so exported SVGs carry the
10
8
  // theme too. The app flips `currentTheme`; Node tests can do the same.
11
-
12
9
  let currentTheme = 'light';
13
-
14
10
  export function setDiagramTheme(theme) {
15
- currentTheme = theme === 'dark' ? 'dark' : 'light';
11
+ currentTheme = theme === 'dark' ? 'dark' : 'light';
16
12
  }
17
-
18
13
  export function getDiagramTheme() {
19
- return currentTheme;
14
+ return currentTheme;
20
15
  }
21
-
22
16
  const PALETTES = {
23
- light: {
24
- background: '#f7f4ed', node: '#fffdf7', border: '#3d3a33', text: '#2a2721',
25
- line: '#8a8577', cluster: '#efe9dc66', clusterBorder: '#8a8577',
26
- entry: 'fill:#dce8d8,stroke:#2e5d43,stroke-width:2px,color:#1d3a2a',
27
- hub: 'fill:#f3e3c8,stroke:#8a5a1d,stroke-width:2px,color:#5c3a10',
28
- cycle: 'fill:#f3d4c8,stroke:#a4442a,stroke-width:2px,color:#6e2c17',
29
- ghost: 'fill:#efe9dc,stroke:#b5ae9c,stroke-dasharray:4 3,color:#8a8577',
30
- self: 'fill:#dce8d8,stroke:#2e5d43,stroke-width:3px,color:#1d3a2a',
31
- ext: 'fill:#efe9dc,stroke:#b5ae9c,stroke-dasharray:4 3,color:#8a8577',
32
- blank: 'fill:transparent,stroke:transparent,color:transparent',
33
- note: 'fill:transparent,stroke:transparent,color:#8a8577',
34
- r0: 'fill:#dce8d8,stroke:#2e5d43,stroke-width:1.5px,color:#1d3a2a',
35
- r1: 'fill:#fffdf7,stroke:#8a8577,stroke-width:1px,color:#2a2721',
36
- r2: 'fill:#f3e3c8,stroke:#8a5a1d,stroke-width:1.5px,color:#5c3a10',
37
- r3: 'fill:#f3d4c8,stroke:#a4442a,stroke-width:2px,color:#6e2c17',
38
- },
39
- dark: {
40
- background: '#211e19', node: '#2b2721', border: '#b8b0a0', text: '#ece7db',
41
- line: '#8a8577', cluster: '#2b272166', clusterBorder: '#6b6558',
42
- entry: 'fill:#243d2c,stroke:#8fbd9f,stroke-width:2px,color:#cfe6d6',
43
- hub: 'fill:#463617,stroke:#d9a75f,stroke-width:2px,color:#f0d9ae',
44
- cycle: 'fill:#472418,stroke:#d97b5a,stroke-width:2px,color:#f3c4ae',
45
- ghost: 'fill:#2b2721,stroke:#6b6558,stroke-dasharray:4 3,color:#9b9484',
46
- self: 'fill:#243d2c,stroke:#8fbd9f,stroke-width:3px,color:#cfe6d6',
47
- ext: 'fill:#2b2721,stroke:#6b6558,stroke-dasharray:4 3,color:#9b9484',
48
- blank: 'fill:transparent,stroke:transparent,color:transparent',
49
- note: 'fill:transparent,stroke:transparent,color:#9b9484',
50
- r0: 'fill:#243d2c,stroke:#8fbd9f,stroke-width:1.5px,color:#cfe6d6',
51
- r1: 'fill:#2b2721,stroke:#8a8577,stroke-width:1px,color:#ece7db',
52
- r2: 'fill:#463617,stroke:#d9a75f,stroke-width:1.5px,color:#f0d9ae',
53
- r3: 'fill:#472418,stroke:#d97b5a,stroke-width:2px,color:#f3c4ae',
54
- },
17
+ light: {
18
+ background: '#f7f4ed', node: '#fffdf7', border: '#3d3a33', text: '#2a2721',
19
+ line: '#8a8577', cluster: '#efe9dc66', clusterBorder: '#8a8577',
20
+ entry: 'fill:#dce8d8,stroke:#2e5d43,stroke-width:2px,color:#1d3a2a',
21
+ hub: 'fill:#f3e3c8,stroke:#8a5a1d,stroke-width:2px,color:#5c3a10',
22
+ cycle: 'fill:#f3d4c8,stroke:#a4442a,stroke-width:2px,color:#6e2c17',
23
+ ghost: 'fill:#efe9dc,stroke:#b5ae9c,stroke-dasharray:4 3,color:#8a8577',
24
+ self: 'fill:#dce8d8,stroke:#2e5d43,stroke-width:3px,color:#1d3a2a',
25
+ ext: 'fill:#efe9dc,stroke:#b5ae9c,stroke-dasharray:4 3,color:#8a8577',
26
+ blank: 'fill:transparent,stroke:transparent,color:transparent',
27
+ note: 'fill:transparent,stroke:transparent,color:#8a8577',
28
+ r0: 'fill:#dce8d8,stroke:#2e5d43,stroke-width:1.5px,color:#1d3a2a',
29
+ r1: 'fill:#fffdf7,stroke:#8a8577,stroke-width:1px,color:#2a2721',
30
+ r2: 'fill:#f3e3c8,stroke:#8a5a1d,stroke-width:1.5px,color:#5c3a10',
31
+ r3: 'fill:#f3d4c8,stroke:#a4442a,stroke-width:2px,color:#6e2c17',
32
+ },
33
+ dark: {
34
+ background: '#211e19', node: '#2b2721', border: '#b8b0a0', text: '#ece7db',
35
+ line: '#8a8577', cluster: '#2b272166', clusterBorder: '#6b6558',
36
+ entry: 'fill:#243d2c,stroke:#8fbd9f,stroke-width:2px,color:#cfe6d6',
37
+ hub: 'fill:#463617,stroke:#d9a75f,stroke-width:2px,color:#f0d9ae',
38
+ cycle: 'fill:#472418,stroke:#d97b5a,stroke-width:2px,color:#f3c4ae',
39
+ ghost: 'fill:#2b2721,stroke:#6b6558,stroke-dasharray:4 3,color:#9b9484',
40
+ self: 'fill:#243d2c,stroke:#8fbd9f,stroke-width:3px,color:#cfe6d6',
41
+ ext: 'fill:#2b2721,stroke:#6b6558,stroke-dasharray:4 3,color:#9b9484',
42
+ blank: 'fill:transparent,stroke:transparent,color:transparent',
43
+ note: 'fill:transparent,stroke:transparent,color:#9b9484',
44
+ r0: 'fill:#243d2c,stroke:#8fbd9f,stroke-width:1.5px,color:#cfe6d6',
45
+ r1: 'fill:#2b2721,stroke:#8a8577,stroke-width:1px,color:#ece7db',
46
+ r2: 'fill:#463617,stroke:#d9a75f,stroke-width:1.5px,color:#f0d9ae',
47
+ r3: 'fill:#472418,stroke:#d97b5a,stroke-width:2px,color:#f3c4ae',
48
+ },
55
49
  };
56
-
57
50
  function themeBlock() {
58
- const p = PALETTES[currentTheme];
59
- return `%%{init: {
51
+ const p = PALETTES[currentTheme];
52
+ return `%%{init: {
60
53
  'theme': 'base',
61
54
  'themeVariables': {
62
55
  'background': '${p.background}',
@@ -75,352 +68,334 @@ function themeBlock() {
75
68
  'flowchart': { 'curve': 'basis', 'htmlLabels': false, 'nodeSpacing': 42, 'rankSpacing': 52 }
76
69
  }}%%`;
77
70
  }
78
-
79
71
  // AI-generated diagrams get our theme injected after the fact, so a model's
80
72
  // sketch is indistinguishable from the hand-drawn ones.
81
73
  export function diagramThemeBlock() {
82
- return themeBlock();
74
+ return themeBlock();
83
75
  }
84
-
85
76
  function classDefLines() {
86
- const p = PALETTES[currentTheme];
87
- return [
88
- ` classDef entry ${p.entry}`,
89
- ` classDef hub ${p.hub}`,
90
- ` classDef cycle ${p.cycle}`,
91
- ` classDef ghost ${p.ghost}`,
92
- ` classDef self ${p.self}`,
93
- ` classDef ext ${p.ext}`,
94
- ` classDef blank ${p.blank}`,
95
- ` classDef note ${p.note}`,
96
- ` classDef r0 ${p.r0}`,
97
- ` classDef r1 ${p.r1}`,
98
- ` classDef r2 ${p.r2}`,
99
- ` classDef r3 ${p.r3}`,
100
- ];
77
+ const p = PALETTES[currentTheme];
78
+ return [
79
+ ` classDef entry ${p.entry}`,
80
+ ` classDef hub ${p.hub}`,
81
+ ` classDef cycle ${p.cycle}`,
82
+ ` classDef ghost ${p.ghost}`,
83
+ ` classDef self ${p.self}`,
84
+ ` classDef ext ${p.ext}`,
85
+ ` classDef blank ${p.blank}`,
86
+ ` classDef note ${p.note}`,
87
+ ` classDef r0 ${p.r0}`,
88
+ ` classDef r1 ${p.r1}`,
89
+ ` classDef r2 ${p.r2}`,
90
+ ` classDef r3 ${p.r3}`,
91
+ ];
101
92
  }
102
-
103
93
  const MAX_LABEL = 34;
104
-
105
94
  export function overviewDiagram(scan, facts) {
106
- const lines = [themeBlock(), 'flowchart LR'];
107
-
108
- // Nodes: top-level folders, sized by parsed file count.
109
- const folderStats = new Map();
110
- for (const f of scan.files) {
111
- const top = topFolderOf(f.path);
112
- folderStats.set(top, (folderStats.get(top) || 0) + 1);
113
- }
114
- const entryFolders = new Set(facts.entries.map((p) => topFolderOf(p)));
115
-
116
- const ids = new Map();
117
- let n = 0;
118
- const sorted = [...folderStats.entries()].sort((a, b) => b[1] - a[1]);
119
- for (const [folder, count] of sorted) {
120
- const id = 'f' + n++;
121
- ids.set(folder, id);
122
- const isEntry = entryFolders.has(folder);
123
- const label = esc(folder === '(root)' ? '(repo root)' : folder + '/') + '\\n' + count + (count === 1 ? ' file' : ' files');
124
- if (isEntry) {
125
- lines.push(` ${id}(["${label}"]):::entry`);
126
- } else {
127
- lines.push(` ${id}["${label}"]`);
95
+ const lines = [themeBlock(), 'flowchart LR'];
96
+ // Nodes: top-level folders, sized by parsed file count.
97
+ const folderStats = new Map();
98
+ for (const f of scan.files) {
99
+ const top = topFolderOf(f.path);
100
+ folderStats.set(top, (folderStats.get(top) || 0) + 1);
101
+ }
102
+ const entryFolders = new Set(facts.entries.map((p) => topFolderOf(p)));
103
+ const ids = new Map();
104
+ let n = 0;
105
+ const sorted = [...folderStats.entries()].sort((a, b) => b[1] - a[1]);
106
+ for (const [folder, count] of sorted) {
107
+ const id = 'f' + n++;
108
+ ids.set(folder, id);
109
+ const isEntry = entryFolders.has(folder);
110
+ const label = esc(folder === '(root)' ? '(repo root)' : folder + '/') + '\\n' + count + (count === 1 ? ' file' : ' files');
111
+ if (isEntry) {
112
+ lines.push(` ${id}(["${label}"]):::entry`);
113
+ }
114
+ else {
115
+ lines.push(` ${id}["${label}"]`);
116
+ }
128
117
  }
129
- }
130
-
131
- for (const fe of facts.folderEdges) {
132
- const a = ids.get(fe.from);
133
- const b = ids.get(fe.to);
134
- if (a === undefined || b === undefined) continue;
135
- lines.push(` ${a} -->|${fe.count}| ${b}`);
136
- }
137
-
138
- lines.push(...classDefLines());
139
-
140
- const nodes = {};
141
- for (const [folder, id] of ids) {
142
- nodes[id] = { kind: 'folder', path: folder === '(root)' ? '' : folder };
143
- }
144
- return { source: lines.join('\n'), nodes };
118
+ for (const fe of facts.folderEdges) {
119
+ const a = ids.get(fe.from);
120
+ const b = ids.get(fe.to);
121
+ if (a === undefined || b === undefined)
122
+ continue;
123
+ lines.push(` ${a} -->|${fe.count}| ${b}`);
124
+ }
125
+ lines.push(...classDefLines());
126
+ const nodes = {};
127
+ for (const [folder, id] of ids) {
128
+ nodes[id] = { kind: 'folder', path: folder === '(root)' ? '' : folder };
129
+ }
130
+ return { source: lines.join('\n'), nodes };
145
131
  }
146
-
147
132
  export function folderDiagram(scan, facts, folder, opts = {}) {
148
- const include = opts.include || (() => true);
149
- const here = scanIndex(scan).filesIn(folder).filter((f) => include(f.path));
150
- if (!here.length) return emptyDiagram('Nothing matches the filter in this folder.');
151
-
152
- const lines = [themeBlock(), 'flowchart LR'];
153
- const localSet = new Set(here.map((f) => f.path));
154
- const ids = idMapFor(here.map((f) => f.path));
155
- const fx = factIndex(facts);
156
-
157
- for (const f of here) {
158
- const id = ids.get(f.path);
159
- const fin = facts.fanIn[f.path] || 0;
160
- const isEntry = fx.entries.has(f.path);
161
- const inCycle = fx.inCycle.has(f.path);
162
- const cls = isEntry ? ':::entry' : inCycle ? ':::cycle' : fin >= 5 ? ':::hub' : '';
163
- lines.push(` ${id}["${esc(shortName(f.name))}"]${cls}`);
164
- }
165
-
166
- // Edges between files in this folder; edges to elsewhere get ghost nodes.
167
- const ghosts = new Map(); // outside path -> id
168
- let g = 0;
169
- for (const e of scan.edges) {
170
- const fromIn = localSet.has(e.from);
171
- const toIn = localSet.has(e.to);
172
- if (fromIn && toIn) {
173
- lines.push(` ${ids.get(e.from)} --> ${ids.get(e.to)}`);
174
- } else if (fromIn && !toIn && include(e.to)) {
175
- const gid = ghostId(ghosts, e.to, () => 'g' + g++);
176
- lines.push(` ${ids.get(e.from)} -.-> ${gid}`);
177
- } else if (!fromIn && toIn && include(e.from)) {
178
- const gid = ghostId(ghosts, e.from, () => 'g' + g++);
179
- lines.push(` ${gid} -.-> ${ids.get(e.to)}`);
133
+ const include = opts.include || (() => true);
134
+ const here = scanIndex(scan).filesIn(folder).filter((f) => include(f.path));
135
+ if (!here.length)
136
+ return emptyDiagram('Nothing matches the filter in this folder.');
137
+ const lines = [themeBlock(), 'flowchart LR'];
138
+ const localSet = new Set(here.map((f) => f.path));
139
+ const ids = idMapFor(here.map((f) => f.path));
140
+ const fx = factIndex(facts);
141
+ for (const f of here) {
142
+ const id = ids.get(f.path);
143
+ const fin = facts.fanIn[f.path] || 0;
144
+ const isEntry = fx.entries.has(f.path);
145
+ const inCycle = fx.inCycle.has(f.path);
146
+ const cls = isEntry ? ':::entry' : inCycle ? ':::cycle' : fin >= 5 ? ':::hub' : '';
147
+ lines.push(` ${id}["${esc(shortName(f.name))}"]${cls}`);
148
+ }
149
+ // Edges between files in this folder; edges to elsewhere get ghost nodes.
150
+ const ghosts = new Map(); // outside path -> id
151
+ let g = 0;
152
+ for (const e of scan.edges) {
153
+ const fromIn = localSet.has(e.from);
154
+ const toIn = localSet.has(e.to);
155
+ if (fromIn && toIn) {
156
+ lines.push(` ${ids.get(e.from)} --> ${ids.get(e.to)}`);
157
+ }
158
+ else if (fromIn && !toIn && include(e.to)) {
159
+ const gid = ghostId(ghosts, e.to, () => 'g' + g++);
160
+ lines.push(` ${ids.get(e.from)} -.-> ${gid}`);
161
+ }
162
+ else if (!fromIn && toIn && include(e.from)) {
163
+ const gid = ghostId(ghosts, e.from, () => 'g' + g++);
164
+ lines.push(` ${gid} -.-> ${ids.get(e.to)}`);
165
+ }
180
166
  }
181
- }
182
- for (const [p, gid] of ghosts) {
183
- lines.push(` ${gid}["${esc(shortName(baseName(p)))}\\n${esc(dirOf(p) || '(root)')}"]:::ghost`);
184
- }
185
-
186
- lines.push(...classDefLines());
187
-
188
- const nodes = {};
189
- for (const [p, id] of ids) nodes[id] = { kind: 'file', path: p };
190
- for (const [p, id] of ghosts) nodes[id] = { kind: 'file', path: p };
191
- return { source: lines.join('\n'), nodes };
167
+ for (const [p, gid] of ghosts) {
168
+ lines.push(` ${gid}["${esc(shortName(baseName(p)))}\\n${esc(dirOf(p) || '(root)')}"]:::ghost`);
169
+ }
170
+ lines.push(...classDefLines());
171
+ const nodes = {};
172
+ for (const [p, id] of ids)
173
+ nodes[id] = { kind: 'file', path: p };
174
+ for (const [p, id] of ghosts)
175
+ nodes[id] = { kind: 'file', path: p };
176
+ return { source: lines.join('\n'), nodes };
192
177
  }
193
178
  export function fileDetailDiagram(scan, facts, path) {
194
- const file = scanIndex(scan).fileAt(path);
195
- if (!file) return emptyDiagram('File not found in the scan.');
196
-
197
- const lines = [themeBlock(), 'flowchart LR'];
198
- const importers = (facts.importers[path] || []).slice(0, 8);
199
- const imports = (facts.importsOf[path] || []).slice(0, 8);
200
- const ext = scan.externals.filter((x) => x.usedBy.includes(path)).slice(0, 5);
201
-
202
- lines.push(` subgraph up["imported by"]`);
203
- importers.forEach((p, i) => lines.push(` up${i}["${esc(shortName(baseName(p)))}"]`));
204
- if (!importers.length) lines.push(` upnone[" "]:::blank`);
205
- lines.push(' end');
206
-
207
- lines.push(` self["${esc(shortName(file.name))}"]:::self`);
208
-
209
- lines.push(` subgraph down["imports"]`);
210
- imports.forEach((p, i) => lines.push(` dn${i}["${esc(shortName(baseName(p)))}"]`));
211
- ext.forEach((x, i) => lines.push(` dx${i}["${esc(x.name)}"]:::ext`));
212
- if (!imports.length && !ext.length) lines.push(` dnnone[" "]:::blank`);
213
- lines.push(' end');
214
-
215
- importers.forEach((p, i) => lines.push(` up${i} --> self`));
216
- imports.forEach((p, i) => lines.push(` self --> dn${i}`));
217
- ext.forEach((x, i) => lines.push(` self -.-> dx${i}`));
218
-
219
- lines.push(...classDefLines());
220
-
221
- const nodes = { self: { kind: 'file', path } };
222
- importers.forEach((p, i) => (nodes['up' + i] = { kind: 'file', path: p }));
223
- imports.forEach((p, i) => (nodes['dn' + i] = { kind: 'file', path: p }));
224
- return { source: lines.join('\n'), nodes };
179
+ const file = scanIndex(scan).fileAt(path);
180
+ if (!file)
181
+ return emptyDiagram('File not found in the scan.');
182
+ const lines = [themeBlock(), 'flowchart LR'];
183
+ const importers = (facts.importers[path] || []).slice(0, 8);
184
+ const imports = (facts.importsOf[path] || []).slice(0, 8);
185
+ const ext = scan.externals.filter((x) => x.usedBy.includes(path)).slice(0, 5);
186
+ lines.push(` subgraph up["imported by"]`);
187
+ importers.forEach((p, i) => lines.push(` up${i}["${esc(shortName(baseName(p)))}"]`));
188
+ if (!importers.length)
189
+ lines.push(` upnone[" "]:::blank`);
190
+ lines.push(' end');
191
+ lines.push(` self["${esc(shortName(file.name))}"]:::self`);
192
+ lines.push(` subgraph down["imports"]`);
193
+ imports.forEach((p, i) => lines.push(` dn${i}["${esc(shortName(baseName(p)))}"]`));
194
+ ext.forEach((x, i) => lines.push(` dx${i}["${esc(x.name)}"]:::ext`));
195
+ if (!imports.length && !ext.length)
196
+ lines.push(` dnnone[" "]:::blank`);
197
+ lines.push(' end');
198
+ importers.forEach((p, i) => lines.push(` up${i} --> self`));
199
+ imports.forEach((p, i) => lines.push(` self --> dn${i}`));
200
+ ext.forEach((x, i) => lines.push(` self -.-> dx${i}`));
201
+ lines.push(...classDefLines());
202
+ const nodes = { self: { kind: 'file', path } };
203
+ importers.forEach((p, i) => (nodes['up' + i] = { kind: 'file', path: p }));
204
+ imports.forEach((p, i) => (nodes['dn' + i] = { kind: 'file', path: p }));
205
+ return { source: lines.join('\n'), nodes };
225
206
  }
226
-
227
207
  export function callsDiagram(file) {
228
- if (!file?.calls?.length) return emptyDiagram('No internal call links we can see.');
229
- const lines = [themeBlock(), 'flowchart LR'];
230
- const names = new Set();
231
- for (const c of file.calls) {
232
- names.add(c.from);
233
- names.add(c.to);
234
- }
235
- const ids = idMapFor([...names]);
236
- for (const name of names) {
237
- lines.push(` ${ids.get(name)}["${esc(name)}()"]`);
238
- }
239
- for (const c of file.calls) {
240
- lines.push(` ${ids.get(c.from)} --> ${ids.get(c.to)}`);
241
- }
242
- return { source: lines.join('\n'), nodes: {} };
208
+ if (!file?.calls?.length)
209
+ return emptyDiagram('No internal call links we can see.');
210
+ const lines = [themeBlock(), 'flowchart LR'];
211
+ const names = new Set();
212
+ for (const c of file.calls) {
213
+ names.add(c.from);
214
+ names.add(c.to);
215
+ }
216
+ const ids = idMapFor([...names]);
217
+ for (const name of names) {
218
+ lines.push(` ${ids.get(name)}["${esc(name)}()"]`);
219
+ }
220
+ for (const c of file.calls) {
221
+ lines.push(` ${ids.get(c.from)} --> ${ids.get(c.to)}`);
222
+ }
223
+ return { source: lines.join('\n'), nodes: {} };
243
224
  }
244
-
245
225
  export function servicesDiagram(scan, manifest) {
246
- if (!manifest?.services?.length) return emptyDiagram('No services detected.');
247
- const lines = [themeBlock(), 'flowchart LR'];
248
- manifest.services.forEach((s, i) => {
249
- const bits = [esc(s.name)];
250
- if (s.image) bits.push(esc(s.image));
251
- if (s.ports?.length) bits.push(esc(s.ports.join(', ')));
252
- if (s.command) bits.push(esc(shortName(s.command)));
253
- lines.push(` svc${i}(["${bits.join('\\n')}"])`);
254
- });
255
- // Link each service to the folder it builds from, when we can see one.
256
- manifest.services.forEach((s, i) => {
257
- if (!s.build) return;
258
- const dir = s.build.replace(/^\.\/?/, '').replace(/\/$/, '');
259
- if (!dir || scan.folders.some((f) => f.path === dir)) {
260
- lines.push(` svc${i} -.->|"${esc(dir || '.')}"| root${i}["${esc(dir || '(repo root)')}"]:::ghost`);
261
- }
262
- });
263
- lines.push(...classDefLines());
264
- return { source: lines.join('\n'), nodes: {} };
226
+ if (!manifest?.services?.length)
227
+ return emptyDiagram('No services detected.');
228
+ const lines = [themeBlock(), 'flowchart LR'];
229
+ manifest.services.forEach((s, i) => {
230
+ const bits = [esc(s.name)];
231
+ if (s.image)
232
+ bits.push(esc(s.image));
233
+ if (s.ports?.length)
234
+ bits.push(esc(s.ports.join(', ')));
235
+ if (s.command)
236
+ bits.push(esc(shortName(s.command)));
237
+ lines.push(` svc${i}(["${bits.join('\\n')}"])`);
238
+ });
239
+ // Link each service to the folder it builds from, when we can see one.
240
+ manifest.services.forEach((s, i) => {
241
+ if (!s.build)
242
+ return;
243
+ const dir = s.build.replace(/^\.\/?/, '').replace(/\/$/, '');
244
+ if (!dir || scan.folders.some((f) => f.path === dir)) {
245
+ lines.push(` svc${i} -.->|"${esc(dir || '.')}"| root${i}["${esc(dir || '(repo root)')}"]:::ghost`);
246
+ }
247
+ });
248
+ lines.push(...classDefLines());
249
+ return { source: lines.join('\n'), nodes: {} };
265
250
  }
266
-
267
251
  export function emptyDiagram(note) {
268
- const source = [themeBlock(), 'flowchart LR', ` empty["${esc(note)}"]:::note`, ...classDefLines()].join('\n');
269
- return { source, nodes: {} };
252
+ const source = [themeBlock(), 'flowchart LR', ` empty["${esc(note)}"]:::note`, ...classDefLines()].join('\n');
253
+ return { source, nodes: {} };
270
254
  }
271
-
272
255
  // The Patterns view: every file placed on its import depth — the longest
273
256
  // chain from an entry point — so the architecture's strata are visible at a
274
257
  // glance. Edges flow downward; anything horizontal or upward earns a look.
275
258
  export function layersDiagram(scan, facts, layersInfo, opts = {}) {
276
- const include = opts.include || (() => true);
277
- const { layers, unreachable } = layersInfo;
278
- const MAX_PER_LAYER = 10;
279
- const MAX_EDGES = 70;
280
-
281
- const lines = [themeBlock(), 'flowchart TD'];
282
- const ids = new Map();
283
- const nodes = {};
284
- let n = 0;
285
-
286
- const fx = factIndex(facts);
287
- const clsFor = (p) =>
288
- fx.entries.has(p) ? ':::entry'
289
- : fx.inCycle.has(p) ? ':::cycle'
290
- : (facts.fanIn[p] || 0) >= 5 ? ':::hub'
291
- : '';
292
-
293
- layers.forEach((files, i) => {
294
- const shown = files.filter(include);
295
- if (!shown.length) return;
296
- const label = i === 0 ? 'the way in' : 'depth ' + i;
297
- lines.push(` subgraph L${i}["${label}"]`);
298
- for (const p of shown.slice(0, MAX_PER_LAYER)) {
299
- const id = 'n' + n++;
300
- ids.set(p, id);
301
- nodes[id] = { kind: 'file', path: p };
302
- lines.push(` ${id}["${esc(shortName(baseName(p)))}"]${clsFor(p)}`);
303
- }
304
- if (shown.length > MAX_PER_LAYER) {
305
- lines.push(` moreL${i}["+ ${shown.length - MAX_PER_LAYER} more"]:::ghost`);
306
- }
307
- lines.push(' end');
308
- });
309
-
310
- const stray = unreachable.filter(include).slice(0, 6);
311
- if (stray.length) {
312
- lines.push(` subgraph LU["off the path"]`);
313
- for (const p of stray) {
314
- const id = 'n' + n++;
315
- ids.set(p, id);
316
- nodes[id] = { kind: 'file', path: p };
317
- lines.push(` ${id}["${esc(shortName(baseName(p)))}"]:::ghost`);
259
+ const include = opts.include || (() => true);
260
+ const { layers, unreachable } = layersInfo;
261
+ const MAX_PER_LAYER = 10;
262
+ const MAX_EDGES = 70;
263
+ const lines = [themeBlock(), 'flowchart TD'];
264
+ const ids = new Map();
265
+ const nodes = {};
266
+ let n = 0;
267
+ const fx = factIndex(facts);
268
+ const clsFor = (p) => fx.entries.has(p) ? ':::entry'
269
+ : fx.inCycle.has(p) ? ':::cycle'
270
+ : (facts.fanIn[p] || 0) >= 5 ? ':::hub'
271
+ : '';
272
+ layers.forEach((files, i) => {
273
+ const shown = files.filter(include);
274
+ if (!shown.length)
275
+ return;
276
+ const label = i === 0 ? 'the way in' : 'depth ' + i;
277
+ lines.push(` subgraph L${i}["${label}"]`);
278
+ for (const p of shown.slice(0, MAX_PER_LAYER)) {
279
+ const id = 'n' + n++;
280
+ ids.set(p, id);
281
+ nodes[id] = { kind: 'file', path: p };
282
+ lines.push(` ${id}["${esc(shortName(baseName(p)))}"]${clsFor(p)}`);
283
+ }
284
+ if (shown.length > MAX_PER_LAYER) {
285
+ lines.push(` moreL${i}["+ ${shown.length - MAX_PER_LAYER} more"]:::ghost`);
286
+ }
287
+ lines.push(' end');
288
+ });
289
+ const stray = unreachable.filter(include).slice(0, 6);
290
+ if (stray.length) {
291
+ lines.push(` subgraph LU["off the path"]`);
292
+ for (const p of stray) {
293
+ const id = 'n' + n++;
294
+ ids.set(p, id);
295
+ nodes[id] = { kind: 'file', path: p };
296
+ lines.push(` ${id}["${esc(shortName(baseName(p)))}"]:::ghost`);
297
+ }
298
+ if (unreachable.length > stray.length) {
299
+ lines.push(` moreLU["+ ${unreachable.length - stray.length} more"]:::ghost`);
300
+ }
301
+ lines.push(' end');
318
302
  }
319
- if (unreachable.length > stray.length) {
320
- lines.push(` moreLU["+ ${unreachable.length - stray.length} more"]:::ghost`);
303
+ let edgeCount = 0;
304
+ for (const e of scan.edges) {
305
+ if (edgeCount >= MAX_EDGES)
306
+ break;
307
+ if (!ids.has(e.from) || !ids.has(e.to))
308
+ continue;
309
+ lines.push(` ${ids.get(e.from)} --> ${ids.get(e.to)}`);
310
+ edgeCount++;
321
311
  }
322
- lines.push(' end');
323
- }
324
-
325
- let edgeCount = 0;
326
- for (const e of scan.edges) {
327
- if (edgeCount >= MAX_EDGES) break;
328
- if (!ids.has(e.from) || !ids.has(e.to)) continue;
329
- lines.push(` ${ids.get(e.from)} --> ${ids.get(e.to)}`);
330
- edgeCount++;
331
- }
332
-
333
- lines.push(...classDefLines());
334
- return { source: lines.join('\n'), nodes };
312
+ lines.push(...classDefLines());
313
+ return { source: lines.join('\n'), nodes };
335
314
  }
336
-
337
315
  // ---- helpers ---------------------------------------------------------------
338
-
339
316
  function idMapFor(paths) {
340
- const map = new Map();
341
- paths.forEach((p, i) => map.set(p, 'n' + i));
342
- return map;
317
+ const map = new Map();
318
+ paths.forEach((p, i) => map.set(p, 'n' + i));
319
+ return map;
343
320
  }
344
-
345
321
  function ghostId(ghosts, path, next) {
346
- if (!ghosts.has(path)) ghosts.set(path, next());
347
- return ghosts.get(path);
322
+ if (!ghosts.has(path))
323
+ ghosts.set(path, next());
324
+ return ghosts.get(path);
348
325
  }
349
-
350
326
  // Mermaid labels break on a handful of characters; keep them out.
351
327
  function esc(s) {
352
- return String(s)
353
- .replace(/["<>|{}[\]()]/g, '')
354
- .replace(/&/g, 'and')
355
- .slice(0, 80);
328
+ return String(s)
329
+ .replace(/["<>|{}[\]()]/g, '')
330
+ .replace(/&/g, 'and')
331
+ .slice(0, 80);
356
332
  }
357
-
358
333
  function shortName(name) {
359
- return name.length > MAX_LABEL ? name.slice(0, MAX_LABEL - 1) + '…' : name;
334
+ return name.length > MAX_LABEL ? name.slice(0, MAX_LABEL - 1) + '…' : name;
360
335
  }
361
-
362
336
  // The Health view: the riskiest files as a heat map, colored by risk band and
363
337
  // wired with their import arrows, so the dangerous neighborhood reads at a
364
338
  // glance. Nodes stay clickable into the deep-dive.
365
339
  export function healthDiagram(scan, facts, health, opts = {}) {
366
- const include = opts.include || (() => true);
367
- const MAX = 26;
368
- const top = health.perFile.filter((f) => include(f.path)).slice(0, MAX);
369
- const lines = [themeBlock(), 'flowchart TB', ...classDefLines()];
370
- const ids = new Map();
371
- const nodes = {};
372
- let n = 0;
373
- const band = (r) => (r >= 70 ? 'r3' : r >= 50 ? 'r2' : r >= 30 ? 'r1' : 'r0');
374
-
375
- for (const f of top) {
376
- const id = 'h' + n++;
377
- ids.set(f.path, id);
378
- nodes[id] = { kind: 'file', path: f.path };
379
- lines.push(` ${id}["${esc(shortName(baseName(f.path)))} · ${f.risk}"]:::${band(f.risk)}`);
380
- }
381
-
382
- const set = new Set(top.map((f) => f.path));
383
- let drawn = 0;
384
- for (const e of scan.edges) {
385
- if (drawn >= 60) break;
386
-
387
- if (!set.has(e.from) || !set.has(e.to)) continue;
388
- lines.push(` ${ids.get(e.from)} --> ${ids.get(e.to)}`);
389
- drawn++;
390
- }
391
- if (!top.length) lines.push(' empty["Nothing stands out — this repo is in good shape."]:::note');
392
- return { source: lines.join('\n'), nodes };
340
+ const include = opts.include || (() => true);
341
+ const MAX = 26;
342
+ const top = health.perFile.filter((f) => include(f.path)).slice(0, MAX);
343
+ const lines = [themeBlock(), 'flowchart TB', ...classDefLines()];
344
+ const ids = new Map();
345
+ const nodes = {};
346
+ let n = 0;
347
+ const band = (r) => (r >= 70 ? 'r3' : r >= 50 ? 'r2' : r >= 30 ? 'r1' : 'r0');
348
+ for (const f of top) {
349
+ const id = 'h' + n++;
350
+ ids.set(f.path, id);
351
+ nodes[id] = { kind: 'file', path: f.path };
352
+ lines.push(` ${id}["${esc(shortName(baseName(f.path)))} · ${f.risk}"]:::${band(f.risk)}`);
353
+ }
354
+ const set = new Set(top.map((f) => f.path));
355
+ let drawn = 0;
356
+ for (const e of scan.edges) {
357
+ if (drawn >= 60)
358
+ break;
359
+ if (!set.has(e.from) || !set.has(e.to))
360
+ continue;
361
+ lines.push(` ${ids.get(e.from)} --> ${ids.get(e.to)}`);
362
+ drawn++;
363
+ }
364
+ if (!top.length)
365
+ lines.push(' empty["Nothing stands out — this repo is in good shape."]:::note');
366
+ return { source: lines.join('\n'), nodes };
393
367
  }
394
-
395
368
  // The Security view: files carrying rule-engine findings, colored by their
396
369
  // worst severity (critical red → info green), wired with their import arrows.
397
370
  export function securityDiagram(scan, facts, security, opts = {}) {
398
- const include = opts.include || (() => true);
399
- const MAX = 26;
400
- const sevClass = { critical: 'r3', high: 'r2', medium: 'r1', low: 'r0', info: 'r0' };
401
- const top = security.files.filter((f) => include(f.path)).slice(0, MAX);
402
- const lines = [themeBlock(), 'flowchart TB', ...classDefLines()];
403
- const ids = new Map();
404
- const nodes = {};
405
- let n = 0;
406
- for (const f of top) {
407
- const id = 's' + n++;
408
- ids.set(f.path, id);
409
- nodes[id] = { kind: 'file', path: f.path };
410
- lines.push(` ${id}["${esc(shortName(baseName(f.path)))} · ${f.count}"]:::${sevClass[f.worst]}`);
411
- }
412
- const set = new Set(top.map((f) => f.path));
413
- let drawn = 0;
414
- for (const e of scan.edges) {
415
- if (drawn >= 60) break;
416
- if (!set.has(e.from) || !set.has(e.to)) continue;
417
- lines.push(` ${ids.get(e.from)} --> ${ids.get(e.to)}`);
418
- drawn++;
419
- }
420
- if (!top.length) lines.push(' clean["No findings — the rule engine found nothing to flag."]:::r0');
421
- return { source: lines.join('\n'), nodes };
371
+ const include = opts.include || (() => true);
372
+ const MAX = 26;
373
+ const sevClass = { critical: 'r3', high: 'r2', medium: 'r1', low: 'r0', info: 'r0' };
374
+ const top = security.files.filter((f) => include(f.path)).slice(0, MAX);
375
+ const lines = [themeBlock(), 'flowchart TB', ...classDefLines()];
376
+ const ids = new Map();
377
+ const nodes = {};
378
+ let n = 0;
379
+ for (const f of top) {
380
+ const id = 's' + n++;
381
+ ids.set(f.path, id);
382
+ nodes[id] = { kind: 'file', path: f.path };
383
+ lines.push(` ${id}["${esc(shortName(baseName(f.path)))} · ${f.count}"]:::${sevClass[f.worst]}`);
384
+ }
385
+ const set = new Set(top.map((f) => f.path));
386
+ let drawn = 0;
387
+ for (const e of scan.edges) {
388
+ if (drawn >= 60)
389
+ break;
390
+ if (!set.has(e.from) || !set.has(e.to))
391
+ continue;
392
+ lines.push(` ${ids.get(e.from)} --> ${ids.get(e.to)}`);
393
+ drawn++;
394
+ }
395
+ if (!top.length)
396
+ lines.push(' clean["No findings — the rule engine found nothing to flag."]:::r0');
397
+ return { source: lines.join('\n'), nodes };
422
398
  }
423
-
424
399
  // The History view: the hotspot cross-tab — complexity × churn — as a heat
425
400
  // map. A file high here is both hard to change and changed constantly, which
426
401
  // is where a newcomer's caution (and a maintainer's tests) belong. Import
@@ -428,32 +403,35 @@ export function securityDiagram(scan, facts, security, opts = {}) {
428
403
  // No history (browser pick, tarball, not a checkout) says why instead of
429
404
  // drawing an empty chart.
430
405
  export function historyDiagram(scan, facts, history, opts = {}) {
431
- if (!history?.available) {
432
- return emptyDiagram(history?.reason || 'No history available.');
433
- }
434
- const include = opts.include || (() => true);
435
- const MAX = 26;
436
- const band = (s) => (s >= 70 ? 'r3' : s >= 50 ? 'r2' : s >= 30 ? 'r1' : 'r0');
437
- const top = history.perFile.filter((f) => include(f.path)).slice(0, MAX);
438
- const lines = [themeBlock(), 'flowchart TB', ...classDefLines()];
439
- const ids = new Map();
440
- const nodes = {};
441
- let n = 0;
442
- for (const f of top) {
443
- const id = 't' + n++;
444
- ids.set(f.path, id);
445
- nodes[id] = { kind: 'file', path: f.path };
446
- lines.push(` ${id}["${esc(shortName(baseName(f.path)))} · ${f.hotspot}"]:::${band(f.hotspot)}`);
447
- }
448
- const set = new Set(top.map((f) => f.path));
449
- let drawn = 0;
450
- for (const e of scan.edges) {
451
- if (drawn >= 60) break;
452
- if (!set.has(e.from) || !set.has(e.to)) continue;
453
- lines.push(` ${ids.get(e.from)} --> ${ids.get(e.to)}`);
454
- drawn++;
455
- }
456
- if (!top.length) lines.push(' quiet["No file in this repo has been touched in the recent window."]:::note');
457
- return { source: lines.join('\n'), nodes };
406
+ if (!history?.available) {
407
+ return emptyDiagram(history?.reason || 'No history available.');
408
+ }
409
+ const include = opts.include || (() => true);
410
+ const MAX = 26;
411
+ const band = (s) => (s >= 70 ? 'r3' : s >= 50 ? 'r2' : s >= 30 ? 'r1' : 'r0');
412
+ const top = history.perFile.filter((f) => include(f.path)).slice(0, MAX);
413
+ const lines = [themeBlock(), 'flowchart TB', ...classDefLines()];
414
+ const ids = new Map();
415
+ const nodes = {};
416
+ let n = 0;
417
+ for (const f of top) {
418
+ const id = 't' + n++;
419
+ ids.set(f.path, id);
420
+ nodes[id] = { kind: 'file', path: f.path };
421
+ lines.push(` ${id}["${esc(shortName(baseName(f.path)))} · ${f.hotspot}"]:::${band(f.hotspot)}`);
422
+ }
423
+ const set = new Set(top.map((f) => f.path));
424
+ let drawn = 0;
425
+ for (const e of scan.edges) {
426
+ if (drawn >= 60)
427
+ break;
428
+ if (!set.has(e.from) || !set.has(e.to))
429
+ continue;
430
+ lines.push(` ${ids.get(e.from)} --> ${ids.get(e.to)}`);
431
+ drawn++;
432
+ }
433
+ if (!top.length)
434
+ lines.push(' quiet["No file in this repo has been touched in the recent window."]:::note');
435
+ return { source: lines.join('\n'), nodes };
458
436
  }
459
-
437
+ //# sourceMappingURL=mermaid.js.map