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.
- package/README.md +174 -46
- package/bin/build-if-source.js +23 -0
- package/cli/commands.d.ts +26 -0
- package/cli/commands.js +836 -811
- package/cli/commands.js.map +1 -0
- package/cli/explorer/advanced.d.ts +14 -0
- package/cli/explorer/advanced.js +321 -329
- package/cli/explorer/advanced.js.map +1 -0
- package/cli/explorer/app.d.ts +1 -0
- package/cli/explorer/app.js +384 -313
- package/cli/explorer/app.js.map +1 -0
- package/cli/explorer/assistant.d.ts +9 -0
- package/cli/explorer/assistant.js +58 -0
- package/cli/explorer/assistant.js.map +1 -0
- package/cli/explorer/commands.d.ts +17 -0
- package/cli/explorer/commands.js +368 -302
- package/cli/explorer/commands.js.map +1 -0
- package/cli/explorer/featureViews.d.ts +124 -0
- package/cli/explorer/featureViews.js +179 -0
- package/cli/explorer/featureViews.js.map +1 -0
- package/cli/explorer/github.d.ts +21 -0
- package/cli/explorer/github.js +42 -44
- package/cli/explorer/github.js.map +1 -0
- package/cli/explorer/graphs.d.ts +7 -0
- package/cli/explorer/graphs.js +90 -95
- package/cli/explorer/graphs.js.map +1 -0
- package/cli/explorer/picker.d.ts +22 -0
- package/cli/explorer/picker.js +150 -0
- package/cli/explorer/picker.js.map +1 -0
- package/cli/explorer/report.d.ts +85 -0
- package/cli/explorer/report.js +60 -0
- package/cli/explorer/report.js.map +1 -0
- package/cli/explorer/session.d.ts +449 -0
- package/cli/explorer/session.js +143 -144
- package/cli/explorer/session.js.map +1 -0
- package/cli/explorer/views.d.ts +19 -0
- package/cli/explorer/views.js +465 -482
- package/cli/explorer/views.js.map +1 -0
- package/cli/explorer/wrap.d.ts +1 -0
- package/cli/explorer/wrap.js +28 -44
- package/cli/explorer/wrap.js.map +1 -0
- package/cli/main.d.ts +1 -0
- package/cli/main.js +214 -161
- package/cli/main.js.map +1 -0
- package/cli/prompt.d.ts +5 -0
- package/cli/prompt.js +84 -80
- package/cli/prompt.js.map +1 -0
- package/cli/scanOptions.d.ts +2 -0
- package/cli/scanOptions.js +11 -0
- package/cli/scanOptions.js.map +1 -0
- package/cli/ui.d.ts +33 -0
- package/cli/ui.js +35 -44
- package/cli/ui.js.map +1 -0
- package/cli/wizard.d.ts +187 -0
- package/cli/wizard.js +304 -284
- package/cli/wizard.js.map +1 -0
- package/package.json +15 -4
- package/public/app.d.ts +1 -0
- package/public/app.js +1057 -1009
- package/public/app.js.map +1 -0
- package/public/browser.d.ts +68 -0
- package/public/browser.js +2 -0
- package/public/browser.js.map +1 -0
- package/public/index.html +30 -13
- package/public/js/about.d.ts +6 -0
- package/public/js/about.js +152 -172
- package/public/js/about.js.map +1 -0
- package/public/js/aiDraft.d.ts +3 -0
- package/public/js/aiDraft.js +116 -124
- package/public/js/aiDraft.js.map +1 -0
- package/public/js/analysisPanel.d.ts +7 -0
- package/public/js/analysisPanel.js +166 -166
- package/public/js/analysisPanel.js.map +1 -0
- package/public/js/analysisReport.d.ts +38 -0
- package/public/js/analysisReport.js +105 -110
- package/public/js/analysisReport.js.map +1 -0
- package/public/js/api.d.ts +26 -0
- package/public/js/api.js +176 -167
- package/public/js/api.js.map +1 -0
- package/public/js/atlas.d.ts +6 -0
- package/public/js/atlas.js +50 -58
- package/public/js/atlas.js.map +1 -0
- package/public/js/blameView.d.ts +2 -0
- package/public/js/blameView.js +36 -37
- package/public/js/blameView.js.map +1 -0
- package/public/js/codeTab.d.ts +3 -0
- package/public/js/codeTab.js +175 -180
- package/public/js/codeTab.js.map +1 -0
- package/public/js/codeViewer.d.ts +5 -0
- package/public/js/codeViewer.js +162 -150
- package/public/js/codeViewer.js.map +1 -0
- package/public/js/components/FileChip.d.ts +9 -0
- package/public/js/components/FileChip.js +45 -49
- package/public/js/components/FileChip.js.map +1 -0
- package/public/js/components/MetricSparkline.d.ts +7 -0
- package/public/js/components/MetricSparkline.js +35 -44
- package/public/js/components/MetricSparkline.js.map +1 -0
- package/public/js/components/RiskBadge.d.ts +9 -0
- package/public/js/components/RiskBadge.js +55 -63
- package/public/js/components/RiskBadge.js.map +1 -0
- package/public/js/deepAnalysisView.d.ts +99 -0
- package/public/js/deepAnalysisView.js +282 -302
- package/public/js/deepAnalysisView.js.map +1 -0
- package/public/js/diagramPane.d.ts +8 -0
- package/public/js/diagramPane.js +133 -135
- package/public/js/diagramPane.js.map +1 -0
- package/public/js/diffView.d.ts +6 -0
- package/public/js/diffView.js +222 -209
- package/public/js/diffView.js.map +1 -0
- package/public/js/docsView.d.ts +2 -0
- package/public/js/docsView.js +441 -442
- package/public/js/docsView.js.map +1 -0
- package/public/js/dom.d.ts +189 -0
- package/public/js/dom.js +5 -0
- package/public/js/dom.js.map +1 -0
- package/public/js/fileSourceBrowser.d.ts +5 -0
- package/public/js/fileSourceBrowser.js +33 -41
- package/public/js/fileSourceBrowser.js.map +1 -0
- package/public/js/flameGraph.d.ts +5 -0
- package/public/js/flameGraph.js +62 -72
- package/public/js/flameGraph.js.map +1 -0
- package/public/js/forceGraph.d.ts +10 -0
- package/public/js/forceGraph.js +581 -556
- package/public/js/forceGraph.js.map +1 -0
- package/public/js/heatmap.d.ts +4 -0
- package/public/js/heatmap.js +142 -164
- package/public/js/heatmap.js.map +1 -0
- package/public/js/highlight.d.ts +2 -0
- package/public/js/highlight.js +116 -107
- package/public/js/highlight.js.map +1 -0
- package/public/js/html.d.ts +2 -0
- package/public/js/html.js +14 -44
- package/public/js/html.js.map +1 -0
- package/public/js/insightsView.d.ts +5 -0
- package/public/js/insightsView.js +93 -111
- package/public/js/insightsView.js.map +1 -0
- package/public/js/inspector.d.ts +37 -0
- package/public/js/inspector.js +416 -445
- package/public/js/inspector.js.map +1 -0
- package/public/js/llm.d.ts +113 -0
- package/public/js/llm.js +233 -272
- package/public/js/llm.js.map +1 -0
- package/public/js/mapView.d.ts +3 -0
- package/public/js/mapView.js +121 -120
- package/public/js/mapView.js.map +1 -0
- package/public/js/markdown.d.ts +3 -0
- package/public/js/markdown.js +84 -91
- package/public/js/markdown.js.map +1 -0
- package/public/js/mindmap.d.ts +32 -0
- package/public/js/mindmap.js +244 -259
- package/public/js/mindmap.js.map +1 -0
- package/public/js/mobilePanels.d.ts +5 -0
- package/public/js/mobilePanels.js +50 -0
- package/public/js/mobilePanels.js.map +1 -0
- package/public/js/repoFiles.d.ts +12 -0
- package/public/js/repoFiles.js +28 -27
- package/public/js/repoFiles.js.map +1 -0
- package/public/js/reviewView.d.ts +28 -0
- package/public/js/reviewView.js +256 -0
- package/public/js/reviewView.js.map +1 -0
- package/public/js/sbomView.d.ts +3 -0
- package/public/js/sbomView.js +59 -67
- package/public/js/sbomView.js.map +1 -0
- package/public/js/scanCache.d.ts +16 -0
- package/public/js/scanCache.js +67 -65
- package/public/js/scanCache.js.map +1 -0
- package/public/js/search.d.ts +7 -0
- package/public/js/search.js +868 -800
- package/public/js/search.js.map +1 -0
- package/public/js/searchLocal.d.ts +42 -0
- package/public/js/searchLocal.js +87 -0
- package/public/js/searchLocal.js.map +1 -0
- package/public/js/serverSettings.d.ts +5 -0
- package/public/js/serverSettings.js +153 -164
- package/public/js/serverSettings.js.map +1 -0
- package/public/js/state.d.ts +798 -0
- package/public/js/state.js +114 -122
- package/public/js/state.js.map +1 -0
- package/public/js/tour.d.ts +6 -0
- package/public/js/tour.js +5 -7
- package/public/js/tour.js.map +1 -0
- package/public/js/transitions.d.ts +2 -0
- package/public/js/transitions.js +9 -10
- package/public/js/transitions.js.map +1 -0
- package/public/js/tree.d.ts +12 -0
- package/public/js/tree.js +157 -154
- package/public/js/tree.js.map +1 -0
- package/public/js/workflowsView.d.ts +3 -0
- package/public/js/workflowsView.js +15 -22
- package/public/js/workflowsView.js.map +1 -0
- package/public/search.css +495 -0
- package/public/styles.css +163 -346
- package/public/workbench.css +166 -0
- package/server/apiAuth.d.ts +3 -0
- package/server/apiAuth.js +18 -20
- package/server/apiAuth.js.map +1 -0
- package/server/apiDiff.d.ts +2 -0
- package/server/apiDiff.js +20 -18
- package/server/apiDiff.js.map +1 -0
- package/server/apiDocs.d.ts +2 -0
- package/server/apiDocs.js +38 -40
- package/server/apiDocs.js.map +1 -0
- package/server/apiFile.d.ts +1 -0
- package/server/apiFile.js +14 -17
- package/server/apiFile.js.map +1 -0
- package/server/apiGitBlame.d.ts +2 -0
- package/server/apiGitBlame.js +50 -51
- package/server/apiGitBlame.js.map +1 -0
- package/server/apiMcp.d.ts +4 -0
- package/server/apiMcp.js +64 -66
- package/server/apiMcp.js.map +1 -0
- package/server/apiReview.d.ts +2 -0
- package/server/apiReview.js +45 -0
- package/server/apiReview.js.map +1 -0
- package/server/apiScan.d.ts +5 -0
- package/server/apiScan.js +90 -71
- package/server/apiScan.js.map +1 -0
- package/server/apiSearch.d.ts +33 -0
- package/server/apiSearch.js +184 -200
- package/server/apiSearch.js.map +1 -0
- package/server/apiSettings.d.ts +4 -0
- package/server/apiSettings.js +84 -85
- package/server/apiSettings.js.map +1 -0
- package/server/apiTools.d.ts +3 -0
- package/server/apiTools.js +55 -56
- package/server/apiTools.js.map +1 -0
- package/server/auth.d.ts +5 -0
- package/server/auth.js +54 -51
- package/server/auth.js.map +1 -0
- package/server/config.d.ts +42 -0
- package/server/config.js +189 -194
- package/server/config.js.map +1 -0
- package/server/daemon.d.ts +12 -0
- package/server/daemon.js +124 -114
- package/server/daemon.js.map +1 -0
- package/server/fileSourceNode.d.ts +2 -0
- package/server/fileSourceNode.js +34 -36
- package/server/fileSourceNode.js.map +1 -0
- package/server/gitClone.d.ts +9 -0
- package/server/gitClone.js +59 -61
- package/server/gitClone.js.map +1 -0
- package/server/gitDiff.d.ts +36 -0
- package/server/gitDiff.js +271 -169
- package/server/gitDiff.js.map +1 -0
- package/server/gitHistory.d.ts +12 -0
- package/server/gitHistory.js +80 -80
- package/server/gitHistory.js.map +1 -0
- package/server/gitRemote.d.ts +12 -0
- package/server/gitRemote.js +37 -38
- package/server/gitRemote.js.map +1 -0
- package/server/htmlText.d.ts +4 -0
- package/server/htmlText.js +16 -23
- package/server/htmlText.js.map +1 -0
- package/server/http.d.ts +5 -0
- package/server/http.js +27 -30
- package/server/http.js.map +1 -0
- package/server/httpGuards.d.ts +2 -0
- package/server/httpGuards.js +47 -43
- package/server/httpGuards.js.map +1 -0
- package/server/https.d.ts +34 -0
- package/server/https.js +121 -111
- package/server/https.js.map +1 -0
- package/server/index.d.ts +40 -0
- package/server/index.js +139 -147
- package/server/index.js.map +1 -0
- package/server/layout.d.ts +26 -0
- package/server/layout.js +59 -64
- package/server/layout.js.map +1 -0
- package/server/llmProxy.d.ts +3 -0
- package/server/llmProxy.js +137 -135
- package/server/llmProxy.js.map +1 -0
- package/server/logger.d.ts +38 -0
- package/server/logger.js +198 -211
- package/server/logger.js.map +1 -0
- package/server/mcp/analysis.d.ts +29 -0
- package/server/mcp/analysis.js +99 -120
- package/server/mcp/analysis.js.map +1 -0
- package/server/mcp/http.d.ts +9 -0
- package/server/mcp/http.js +128 -137
- package/server/mcp/http.js.map +1 -0
- package/server/mcp/runner.d.ts +68 -0
- package/server/mcp/runner.js +208 -218
- package/server/mcp/runner.js.map +1 -0
- package/server/mcp/server.d.ts +29 -0
- package/server/mcp/server.js +166 -179
- package/server/mcp/server.js.map +1 -0
- package/server/mcp/standalone.d.ts +1 -0
- package/server/mcp/standalone.js +8 -10
- package/server/mcp/standalone.js.map +1 -0
- package/server/mcp/tools.d.ts +2020 -0
- package/server/mcp/tools.js +523 -574
- package/server/mcp/tools.js.map +1 -0
- package/server/paths.d.ts +3 -0
- package/server/paths.js +17 -15
- package/server/paths.js.map +1 -0
- package/server/pidfile.d.ts +9 -0
- package/server/pidfile.js +43 -45
- package/server/pidfile.js.map +1 -0
- package/server/review.d.ts +5 -0
- package/server/review.js +35 -0
- package/server/review.js.map +1 -0
- package/server/router.d.ts +33 -0
- package/server/router.js +191 -197
- package/server/router.js.map +1 -0
- package/server/scanRequest.d.ts +3 -0
- package/server/scanRequest.js +36 -0
- package/server/scanRequest.js.map +1 -0
- package/server/searchIndex.d.ts +17 -0
- package/server/searchIndex.js +83 -80
- package/server/searchIndex.js.map +1 -0
- package/server/sessions.d.ts +14 -0
- package/server/sessions.js +62 -70
- package/server/sessions.js.map +1 -0
- package/server/startup.d.ts +118 -0
- package/server/startup.js +143 -148
- package/server/startup.js.map +1 -0
- package/server/static.d.ts +6 -0
- package/server/static.js +28 -31
- package/server/static.js.map +1 -0
- package/server/tools/install.d.ts +4 -0
- package/server/tools/install.js +176 -185
- package/server/tools/install.js.map +1 -0
- package/server/tools/parse.d.ts +8 -0
- package/server/tools/parse.js +112 -114
- package/server/tools/parse.js.map +1 -0
- package/server/tools/platform.d.ts +9 -0
- package/server/tools/platform.js +49 -47
- package/server/tools/platform.js.map +1 -0
- package/server/tools/registry.d.ts +137 -0
- package/server/tools/registry.js +173 -172
- package/server/tools/registry.js.map +1 -0
- package/server/tools/scan.d.ts +34 -0
- package/server/tools/scan.js +89 -98
- package/server/tools/scan.js.map +1 -0
- package/server/tools.d.ts +34 -0
- package/server/tools.js +94 -107
- package/server/tools.js.map +1 -0
- package/server/tunnel.d.ts +29 -0
- package/server/tunnel.js +61 -66
- package/server/tunnel.js.map +1 -0
- package/shared/analyzer/docs.d.ts +16 -0
- package/shared/analyzer/docs.js +99 -93
- package/shared/analyzer/docs.js.map +1 -0
- package/shared/analyzer/explainLocal.d.ts +4 -0
- package/shared/analyzer/explainLocal.js +122 -142
- package/shared/analyzer/explainLocal.js.map +1 -0
- package/shared/analyzer/github.d.ts +37 -0
- package/shared/analyzer/github.js +41 -43
- package/shared/analyzer/github.js.map +1 -0
- package/shared/analyzer/graph.d.ts +41 -0
- package/shared/analyzer/graph.js +393 -400
- package/shared/analyzer/graph.js.map +1 -0
- package/shared/analyzer/health.d.ts +33 -0
- package/shared/analyzer/health.js +197 -193
- package/shared/analyzer/health.js.map +1 -0
- package/shared/analyzer/history.d.ts +29 -0
- package/shared/analyzer/history.js +110 -111
- package/shared/analyzer/history.js.map +1 -0
- package/shared/analyzer/jsonConfig.d.ts +2 -0
- package/shared/analyzer/jsonConfig.js +61 -0
- package/shared/analyzer/jsonConfig.js.map +1 -0
- package/shared/analyzer/languages/csharp.d.ts +26 -0
- package/shared/analyzer/languages/csharp.js +83 -88
- package/shared/analyzer/languages/csharp.js.map +1 -0
- package/shared/analyzer/languages/generic.d.ts +23 -0
- package/shared/analyzer/languages/generic.js +94 -105
- package/shared/analyzer/languages/generic.js.map +1 -0
- package/shared/analyzer/languages/go.d.ts +24 -0
- package/shared/analyzer/languages/go.js +45 -54
- package/shared/analyzer/languages/go.js.map +1 -0
- package/shared/analyzer/languages/index.d.ts +6 -0
- package/shared/analyzer/languages/index.js +19 -20
- package/shared/analyzer/languages/index.js.map +1 -0
- package/shared/analyzer/languages/java.d.ts +20 -0
- package/shared/analyzer/languages/java.js +73 -78
- package/shared/analyzer/languages/java.js.map +1 -0
- package/shared/analyzer/languages/javascript.d.ts +12 -0
- package/shared/analyzer/languages/javascript.js +258 -209
- package/shared/analyzer/languages/javascript.js.map +1 -0
- package/shared/analyzer/languages/python.d.ts +23 -0
- package/shared/analyzer/languages/python.js +92 -100
- package/shared/analyzer/languages/python.js.map +1 -0
- package/shared/analyzer/languages/rust.d.ts +17 -0
- package/shared/analyzer/languages/rust.js +96 -98
- package/shared/analyzer/languages/rust.js.map +1 -0
- package/shared/analyzer/languages/typescript.d.ts +11 -0
- package/shared/analyzer/languages/typescript.js +117 -116
- package/shared/analyzer/languages/typescript.js.map +1 -0
- package/shared/analyzer/licenses.d.ts +18 -0
- package/shared/analyzer/licenses.js +130 -133
- package/shared/analyzer/licenses.js.map +1 -0
- package/shared/analyzer/metrics.d.ts +20 -0
- package/shared/analyzer/metrics.js +112 -104
- package/shared/analyzer/metrics.js.map +1 -0
- package/shared/analyzer/pathUtil.d.ts +10 -0
- package/shared/analyzer/pathUtil.js +29 -35
- package/shared/analyzer/pathUtil.js.map +1 -0
- package/shared/analyzer/patterns.d.ts +11 -0
- package/shared/analyzer/patterns.js +231 -253
- package/shared/analyzer/patterns.js.map +1 -0
- package/shared/analyzer/scan.d.ts +62 -0
- package/shared/analyzer/scan.js +451 -442
- package/shared/analyzer/scan.js.map +1 -0
- package/shared/analyzer/scanControl.d.ts +11 -0
- package/shared/analyzer/scanControl.js +62 -0
- package/shared/analyzer/scanControl.js.map +1 -0
- package/shared/analyzer/security.d.ts +18 -0
- package/shared/analyzer/security.js +159 -159
- package/shared/analyzer/security.js.map +1 -0
- package/shared/analyzer/services.d.ts +5 -0
- package/shared/analyzer/services.js +141 -138
- package/shared/analyzer/services.js.map +1 -0
- package/shared/analyzer/stack.d.ts +6 -0
- package/shared/analyzer/stack.js +134 -141
- package/shared/analyzer/stack.js.map +1 -0
- package/shared/analyzer/tour.d.ts +2 -0
- package/shared/analyzer/tour.js +45 -53
- package/shared/analyzer/tour.js.map +1 -0
- package/shared/analyzer/util.d.ts +8 -0
- package/shared/analyzer/util.js +130 -76
- package/shared/analyzer/util.js.map +1 -0
- package/shared/analyzer/workflows.d.ts +8 -0
- package/shared/analyzer/workflows.js +139 -134
- package/shared/analyzer/workflows.js.map +1 -0
- package/shared/contracts.d.ts +250 -0
- package/shared/contracts.js +2 -0
- package/shared/contracts.js.map +1 -0
- package/shared/diagram/aiFacts.d.ts +35 -0
- package/shared/diagram/aiFacts.js +102 -105
- package/shared/diagram/aiFacts.js.map +1 -0
- package/shared/diagram/aiMermaid.d.ts +5 -0
- package/shared/diagram/aiMermaid.js +46 -45
- package/shared/diagram/aiMermaid.js.map +1 -0
- package/shared/diagram/atlas.d.ts +16 -0
- package/shared/diagram/atlas.js +51 -60
- package/shared/diagram/atlas.js.map +1 -0
- package/shared/diagram/mermaid.d.ts +43 -0
- package/shared/diagram/mermaid.js +348 -370
- package/shared/diagram/mermaid.js.map +1 -0
- package/shared/review/contracts.d.ts +95 -0
- package/shared/review/contracts.js +2 -0
- package/shared/review/contracts.js.map +1 -0
- package/shared/review/review.d.ts +10 -0
- package/shared/review/review.js +184 -0
- package/shared/review/review.js.map +1 -0
- package/shared/search/query.d.ts +32 -0
- package/shared/search/query.js +395 -388
- 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
|
-
|
|
11
|
+
currentTheme = theme === 'dark' ? 'dark' : 'light';
|
|
16
12
|
}
|
|
17
|
-
|
|
18
13
|
export function getDiagramTheme() {
|
|
19
|
-
|
|
14
|
+
return currentTheme;
|
|
20
15
|
}
|
|
21
|
-
|
|
22
16
|
const PALETTES = {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
59
|
-
|
|
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
|
-
|
|
74
|
+
return themeBlock();
|
|
83
75
|
}
|
|
84
|
-
|
|
85
76
|
function classDefLines() {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
}
|
|
242
|
-
|
|
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
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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
|
-
|
|
269
|
-
|
|
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
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
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
|
-
|
|
320
|
-
|
|
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(
|
|
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
|
-
|
|
341
|
-
|
|
342
|
-
|
|
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
|
-
|
|
347
|
-
|
|
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
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
328
|
+
return String(s)
|
|
329
|
+
.replace(/["<>|{}[\]()]/g, '')
|
|
330
|
+
.replace(/&/g, 'and')
|
|
331
|
+
.slice(0, 80);
|
|
356
332
|
}
|
|
357
|
-
|
|
358
333
|
function shortName(name) {
|
|
359
|
-
|
|
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
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
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
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
|
|
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
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
|
|
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
|