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
package/shared/analyzer/util.js
CHANGED
|
@@ -1,30 +1,81 @@
|
|
|
1
1
|
// Small text utilities shared by the language analyzers.
|
|
2
|
-
|
|
3
2
|
// Blanks out comments while preserving string contents, offsets and line
|
|
4
3
|
// numbers (every matched character becomes a space except newlines), so
|
|
5
4
|
// positions reported afterwards still line up with the original source.
|
|
6
5
|
export function blankComments(source, { block = true, line = true, lineChar = '//' } = {}) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
6
|
+
let out = source;
|
|
7
|
+
if (block) {
|
|
8
|
+
out = out.replace(/\/\*[\s\S]*?\*\//g, (m) => m.replace(/[^\n]/g, ' '));
|
|
9
|
+
}
|
|
10
|
+
if (line && lineChar === '//') {
|
|
11
|
+
out = out.replace(/\/\/[^\n]*/g, (m) => ' '.repeat(m.length));
|
|
12
|
+
}
|
|
13
|
+
if (line && lineChar === '#') {
|
|
14
|
+
out = out.replace(/^[ \t]*#[^\n]*/gm, (m) => ' '.repeat(m.length));
|
|
15
|
+
}
|
|
16
|
+
return out;
|
|
17
|
+
}
|
|
18
|
+
// Keep only JavaScript/TypeScript code positions. Import regexes still read the
|
|
19
|
+
// original source for module strings, but use this mask to reject keywords
|
|
20
|
+
// inside comments and quoted examples. Newlines and offsets stay unchanged.
|
|
21
|
+
export function jsCodeMask(source) {
|
|
22
|
+
// Regex match indexes are UTF-16 offsets, so keep the same indexing even
|
|
23
|
+
// when source contains emoji or other astral characters.
|
|
24
|
+
const chars = String(source).split('');
|
|
25
|
+
const mask = chars.slice();
|
|
26
|
+
let state = 'code';
|
|
27
|
+
const blank = (i) => { if (chars[i] !== '\n')
|
|
28
|
+
mask[i] = ' '; };
|
|
29
|
+
for (let i = 0; i < chars.length; i++) {
|
|
30
|
+
const c = chars[i];
|
|
31
|
+
const next = chars[i + 1];
|
|
32
|
+
if (state === 'code') {
|
|
33
|
+
if (c === '/' && next === '/') {
|
|
34
|
+
blank(i);
|
|
35
|
+
blank(++i);
|
|
36
|
+
state = 'line';
|
|
37
|
+
}
|
|
38
|
+
else if (c === '/' && next === '*') {
|
|
39
|
+
blank(i);
|
|
40
|
+
blank(++i);
|
|
41
|
+
state = 'block';
|
|
42
|
+
}
|
|
43
|
+
else if (c === "'" || c === '"' || c === '`') {
|
|
44
|
+
blank(i);
|
|
45
|
+
state = c;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else if (state === 'line') {
|
|
49
|
+
if (c === '\n')
|
|
50
|
+
state = 'code';
|
|
51
|
+
else
|
|
52
|
+
blank(i);
|
|
53
|
+
}
|
|
54
|
+
else if (state === 'block') {
|
|
55
|
+
blank(i);
|
|
56
|
+
if (c === '*' && next === '/') {
|
|
57
|
+
blank(++i);
|
|
58
|
+
state = 'code';
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
blank(i);
|
|
63
|
+
if (c === '\\' && next !== undefined)
|
|
64
|
+
blank(++i);
|
|
65
|
+
else if (c === state)
|
|
66
|
+
state = 'code';
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return mask.join('');
|
|
18
70
|
}
|
|
19
|
-
|
|
20
71
|
export function lineOf(source, index) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
72
|
+
let line = 1;
|
|
73
|
+
for (let i = 0; i < index && i < source.length; i++) {
|
|
74
|
+
if (source.charCodeAt(i) === 10)
|
|
75
|
+
line++;
|
|
76
|
+
}
|
|
77
|
+
return line;
|
|
26
78
|
}
|
|
27
|
-
|
|
28
79
|
// The same answer as `lineOf`, for callers that need many of them from one
|
|
29
80
|
// source. `lineOf` walks from byte 0 every time, which is nothing once and
|
|
30
81
|
// quadratic when a 200KB file reports 300 imports, functions and classes — each
|
|
@@ -34,56 +85,58 @@ export function lineOf(source, index) {
|
|
|
34
85
|
// The table is built on first use rather than up front, so a file that reports
|
|
35
86
|
// no positions at all pays nothing for asking.
|
|
36
87
|
export function lineCounter(source) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
88
|
+
const s = String(source);
|
|
89
|
+
let starts = null;
|
|
90
|
+
return function lineAt(index) {
|
|
91
|
+
if (!starts) {
|
|
92
|
+
starts = [0];
|
|
93
|
+
for (let i = 0; i < s.length; i++) {
|
|
94
|
+
if (s.charCodeAt(i) === 10)
|
|
95
|
+
starts.push(i + 1);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// The line containing `index` is the last one that starts at or before it.
|
|
99
|
+
let lo = 0;
|
|
100
|
+
let hi = starts.length - 1;
|
|
101
|
+
while (lo < hi) {
|
|
102
|
+
const mid = (lo + hi + 1) >> 1;
|
|
103
|
+
if (starts[mid] <= index)
|
|
104
|
+
lo = mid;
|
|
105
|
+
else
|
|
106
|
+
hi = mid - 1;
|
|
107
|
+
}
|
|
108
|
+
return lo + 1;
|
|
109
|
+
};
|
|
57
110
|
}
|
|
58
|
-
|
|
59
111
|
// Returns the index just past the brace that closes the one at openIndex.
|
|
60
112
|
// Best-effort: a brace inside a string can throw the count off, which is an
|
|
61
113
|
// accepted trade of the no-parser approach.
|
|
62
114
|
export function braceBodyEnd(source, openIndex) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
115
|
+
let depth = 0;
|
|
116
|
+
for (let i = openIndex; i < source.length; i++) {
|
|
117
|
+
const c = source[i];
|
|
118
|
+
if (c === '{')
|
|
119
|
+
depth++;
|
|
120
|
+
else if (c === '}') {
|
|
121
|
+
depth--;
|
|
122
|
+
if (depth === 0)
|
|
123
|
+
return i + 1;
|
|
124
|
+
}
|
|
70
125
|
}
|
|
71
|
-
|
|
72
|
-
return source.length;
|
|
126
|
+
return source.length;
|
|
73
127
|
}
|
|
74
|
-
|
|
75
128
|
export function uniqueBy(items, key) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
129
|
+
const seen = new Set();
|
|
130
|
+
const out = [];
|
|
131
|
+
for (const item of items) {
|
|
132
|
+
const k = key(item);
|
|
133
|
+
if (seen.has(k))
|
|
134
|
+
continue;
|
|
135
|
+
seen.add(k);
|
|
136
|
+
out.push(item);
|
|
137
|
+
}
|
|
138
|
+
return out;
|
|
85
139
|
}
|
|
86
|
-
|
|
87
140
|
// Which of `names` is called inside `body`? Returns the distinct ones, in the
|
|
88
141
|
// order they first appear.
|
|
89
142
|
//
|
|
@@ -99,25 +152,26 @@ export function uniqueBy(items, key) {
|
|
|
99
152
|
// to `method`: this is a best-effort text scan, not a resolver, and a method
|
|
100
153
|
// name that matches a local function is more likely that function than not.
|
|
101
154
|
const CALL_TOKEN = /(?<![\w$])([A-Za-z_$][\w$]*)\s*\(/g;
|
|
102
|
-
|
|
103
155
|
export function callsWithin(body, names) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
156
|
+
const found = [];
|
|
157
|
+
const seen = new Set();
|
|
158
|
+
for (const m of String(body).matchAll(CALL_TOKEN)) {
|
|
159
|
+
const name = m[1];
|
|
160
|
+
if (seen.has(name) || !names.has(name))
|
|
161
|
+
continue;
|
|
162
|
+
seen.add(name);
|
|
163
|
+
found.push(name);
|
|
164
|
+
}
|
|
165
|
+
return found;
|
|
113
166
|
}
|
|
114
|
-
|
|
115
167
|
// The order the callee was declared in, first declaration winning — so the call
|
|
116
168
|
// list comes out in the same order the per-name loop produced it.
|
|
117
169
|
export function declarationOrder(fns) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
170
|
+
const order = new Map();
|
|
171
|
+
fns.forEach((f, i) => {
|
|
172
|
+
if (!order.has(f.name))
|
|
173
|
+
order.set(f.name, i);
|
|
174
|
+
});
|
|
175
|
+
return order;
|
|
123
176
|
}
|
|
177
|
+
//# sourceMappingURL=util.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/shared/analyzer/util.ts"],"names":[],"mappings":"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","sourcesContent":["// Small text utilities shared by the language analyzers.\n\n// Blanks out comments while preserving string contents, offsets and line\n// numbers (every matched character becomes a space except newlines), so\n// positions reported afterwards still line up with the original source.\nexport function blankComments(source, { block = true, line = true, lineChar = '//' }: Record<string, any> = {}) {\n let out = source;\n if (block) {\n out = out.replace(/\\/\\*[\\s\\S]*?\\*\\//g, (m) => m.replace(/[^\\n]/g, ' '));\n }\n if (line && lineChar === '//') {\n out = out.replace(/\\/\\/[^\\n]*/g, (m) => ' '.repeat(m.length));\n }\n if (line && lineChar === '#') {\n out = out.replace(/^[ \\t]*#[^\\n]*/gm, (m) => ' '.repeat(m.length));\n }\n return out;\n}\n\n// Keep only JavaScript/TypeScript code positions. Import regexes still read the\n// original source for module strings, but use this mask to reject keywords\n// inside comments and quoted examples. Newlines and offsets stay unchanged.\nexport function jsCodeMask(source) {\n // Regex match indexes are UTF-16 offsets, so keep the same indexing even\n // when source contains emoji or other astral characters.\n const chars = String(source).split('');\n const mask = chars.slice();\n let state = 'code';\n const blank = (i) => { if (chars[i] !== '\\n') mask[i] = ' '; };\n\n for (let i = 0; i < chars.length; i++) {\n const c = chars[i];\n const next = chars[i + 1];\n if (state === 'code') {\n if (c === '/' && next === '/') {\n blank(i); blank(++i); state = 'line';\n } else if (c === '/' && next === '*') {\n blank(i); blank(++i); state = 'block';\n } else if (c === \"'\" || c === '\"' || c === '`') {\n blank(i); state = c;\n }\n } else if (state === 'line') {\n if (c === '\\n') state = 'code';\n else blank(i);\n } else if (state === 'block') {\n blank(i);\n if (c === '*' && next === '/') { blank(++i); state = 'code'; }\n } else {\n blank(i);\n if (c === '\\\\' && next !== undefined) blank(++i);\n else if (c === state) state = 'code';\n }\n }\n return mask.join('');\n}\n\nexport function lineOf(source, index) {\n let line = 1;\n for (let i = 0; i < index && i < source.length; i++) {\n if (source.charCodeAt(i) === 10) line++;\n }\n return line;\n}\n\n// The same answer as `lineOf`, for callers that need many of them from one\n// source. `lineOf` walks from byte 0 every time, which is nothing once and\n// quadratic when a 200KB file reports 300 imports, functions and classes — each\n// scanning an average of half the file. This walks the source once, records\n// where every line starts, and binary-searches that table per lookup.\n//\n// The table is built on first use rather than up front, so a file that reports\n// no positions at all pays nothing for asking.\nexport function lineCounter(source) {\n const s = String(source);\n let starts = null;\n\n return function lineAt(index) {\n if (!starts) {\n starts = [0];\n for (let i = 0; i < s.length; i++) {\n if (s.charCodeAt(i) === 10) starts.push(i + 1);\n }\n }\n // The line containing `index` is the last one that starts at or before it.\n let lo = 0;\n let hi = starts.length - 1;\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1;\n if (starts[mid] <= index) lo = mid;\n else hi = mid - 1;\n }\n return lo + 1;\n };\n}\n\n// Returns the index just past the brace that closes the one at openIndex.\n// Best-effort: a brace inside a string can throw the count off, which is an\n// accepted trade of the no-parser approach.\nexport function braceBodyEnd(source, openIndex) {\n let depth = 0;\n for (let i = openIndex; i < source.length; i++) {\n const c = source[i];\n if (c === '{') depth++;\n else if (c === '}') {\n depth--;\n if (depth === 0) return i + 1;\n }\n }\n return source.length;\n}\n\nexport function uniqueBy(items, key) {\n const seen = new Set();\n const out = [];\n for (const item of items) {\n const k = key(item);\n if (seen.has(k)) continue;\n seen.add(k);\n out.push(item);\n }\n return out;\n}\n\n// Which of `names` is called inside `body`? Returns the distinct ones, in the\n// order they first appear.\n//\n// This replaced the obvious version — compile `\\bNAME\\s*\\(` per candidate and\n// test the body with it — which is O(functions²) in compiled regexes. A file\n// with 2,300 functions built five million of them, and profiling put two thirds\n// of the whole parse phase in that loop and the `escapeRe` feeding it. Reading\n// the body once and looking each token up in a Set does the same work in one\n// pass over the text.\n//\n// `$` counts as part of an identifier, as JavaScript has it, so `$foo(` is a\n// call to `$foo` and not to `foo`. A call written `obj.method(` counts as a call\n// to `method`: this is a best-effort text scan, not a resolver, and a method\n// name that matches a local function is more likely that function than not.\nconst CALL_TOKEN = /(?<![\\w$])([A-Za-z_$][\\w$]*)\\s*\\(/g;\n\nexport function callsWithin(body, names) {\n const found = [];\n const seen = new Set();\n for (const m of String(body).matchAll(CALL_TOKEN)) {\n const name = m[1];\n if (seen.has(name) || !names.has(name)) continue;\n seen.add(name);\n found.push(name);\n }\n return found;\n}\n\n// The order the callee was declared in, first declaration winning — so the call\n// list comes out in the same order the per-name loop produced it.\nexport function declarationOrder(fns) {\n const order = new Map();\n fns.forEach((f, i) => {\n if (!order.has(f.name)) order.set(f.name, i);\n });\n return order;\n}\n"]}
|
|
@@ -1,143 +1,148 @@
|
|
|
1
1
|
// GitHub Actions & CI/CD workflow parser.
|
|
2
2
|
// Pure isomorphic analyzer — parses YAML workflow files without external dependencies.
|
|
3
|
-
|
|
4
3
|
export async function analyzeWorkflows(source) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
4
|
+
const workflows = [];
|
|
5
|
+
const entries = await source.list('.github/workflows').catch(() => []);
|
|
6
|
+
const ymlFiles = entries.filter((e) => e.type === 'file' && /\.(ya?ml)$/i.test(e.name));
|
|
7
|
+
for (const entry of ymlFiles) {
|
|
8
|
+
const raw = await source.read(entry.path).catch(() => '');
|
|
9
|
+
if (!raw)
|
|
10
|
+
continue;
|
|
11
|
+
const parsed = parseWorkflowYaml(raw, entry.path, entry.name);
|
|
12
|
+
if (parsed)
|
|
13
|
+
workflows.push(parsed);
|
|
14
|
+
}
|
|
15
|
+
return workflows;
|
|
17
16
|
}
|
|
18
|
-
|
|
19
17
|
export function parseWorkflowYaml(text, filePath = '', fileName = '') {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (m) name = m[1].trim();
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Top-level on: ...
|
|
45
|
-
if (indent === 0 && /^on\s*:/i.test(trimmed)) {
|
|
46
|
-
currentSection = 'on';
|
|
47
|
-
inOnSection = true;
|
|
48
|
-
const rest = trimmed.replace(/^on\s*:\s*/i, '').trim();
|
|
49
|
-
if (rest) {
|
|
50
|
-
if (rest.startsWith('[') && rest.endsWith(']')) {
|
|
51
|
-
const list = rest.slice(1, -1).split(',').map((s) => s.trim().replace(/^['"]|['"]$/g, ''));
|
|
52
|
-
triggers.push(...list.filter(Boolean));
|
|
53
|
-
} else {
|
|
54
|
-
triggers.push(rest.replace(/^['"]|['"]$/g, ''));
|
|
18
|
+
const lines = text.split('\n');
|
|
19
|
+
let name = fileName.replace(/\.ya?ml$/i, '');
|
|
20
|
+
const triggers = [];
|
|
21
|
+
const jobs = [];
|
|
22
|
+
let currentSection = null;
|
|
23
|
+
let currentJob = null;
|
|
24
|
+
let currentStep = null;
|
|
25
|
+
let inOnSection = false;
|
|
26
|
+
for (let i = 0; i < lines.length; i++) {
|
|
27
|
+
const line = lines[i];
|
|
28
|
+
const trimmed = line.trim();
|
|
29
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
30
|
+
continue;
|
|
31
|
+
const indent = line.search(/\S/);
|
|
32
|
+
// Top-level name: ...
|
|
33
|
+
if (indent === 0 && /^name\s*:\s*(.+)$/i.test(trimmed)) {
|
|
34
|
+
const m = trimmed.match(/^name\s*:\s*['"]?([^'"]+)['"]?/i);
|
|
35
|
+
if (m)
|
|
36
|
+
name = m[1].trim();
|
|
37
|
+
continue;
|
|
55
38
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
39
|
+
// Top-level on: ...
|
|
40
|
+
if (indent === 0 && /^on\s*:/i.test(trimmed)) {
|
|
41
|
+
currentSection = 'on';
|
|
42
|
+
inOnSection = true;
|
|
43
|
+
const rest = trimmed.replace(/^on\s*:\s*/i, '').trim();
|
|
44
|
+
if (rest) {
|
|
45
|
+
if (rest.startsWith('[') && rest.endsWith(']')) {
|
|
46
|
+
const list = rest.slice(1, -1).split(',').map((s) => s.trim().replace(/^['"]|['"]$/g, ''));
|
|
47
|
+
triggers.push(...list.filter(Boolean));
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
triggers.push(rest.replace(/^['"]|['"]$/g, ''));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
// Top-level jobs: ...
|
|
56
|
+
if (indent === 0 && /^jobs\s*:/i.test(trimmed)) {
|
|
57
|
+
currentSection = 'jobs';
|
|
58
|
+
inOnSection = false;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (currentSection === 'on') {
|
|
62
|
+
if (indent === 0) {
|
|
63
|
+
currentSection = null;
|
|
64
|
+
inOnSection = false;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
const trigMatch = trimmed.match(/^([a-zA-Z0-9_-]+)\s*:/);
|
|
68
|
+
const listMatch = trimmed.match(/^-\s*([a-zA-Z0-9_-]+)/);
|
|
69
|
+
if (trigMatch && indent <= 4)
|
|
70
|
+
triggers.push(trigMatch[1]);
|
|
71
|
+
else if (listMatch && indent <= 4)
|
|
72
|
+
triggers.push(listMatch[1]);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (currentSection === 'jobs' && indent > 0) {
|
|
76
|
+
// Job declaration at indent 2 (or top under jobs)
|
|
77
|
+
if (indent === 2 && trimmed.endsWith(':')) {
|
|
78
|
+
const jobId = trimmed.replace(/:$/, '').trim();
|
|
79
|
+
currentJob = {
|
|
80
|
+
id: jobId,
|
|
81
|
+
name: jobId,
|
|
82
|
+
runsOn: 'ubuntu-latest',
|
|
83
|
+
needs: [],
|
|
84
|
+
steps: [],
|
|
85
|
+
};
|
|
86
|
+
jobs.push(currentJob);
|
|
87
|
+
currentStep = null;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (!currentJob)
|
|
91
|
+
continue;
|
|
92
|
+
// Inside a job:
|
|
93
|
+
if (/^name\s*:\s*(.+)$/i.test(trimmed) && indent <= 6 && !currentStep) {
|
|
94
|
+
const m = trimmed.match(/^name\s*:\s*['"]?([^'"]+)['"]?/i);
|
|
95
|
+
if (m)
|
|
96
|
+
currentJob.name = m[1].trim();
|
|
97
|
+
}
|
|
98
|
+
else if (/^runs-on\s*:\s*(.+)$/i.test(trimmed)) {
|
|
99
|
+
const m = trimmed.match(/^runs-on\s*:\s*['"]?([^'"]+)['"]?/i);
|
|
100
|
+
if (m)
|
|
101
|
+
currentJob.runsOn = m[1].trim();
|
|
102
|
+
}
|
|
103
|
+
else if (/^needs\s*:\s*(.+)$/i.test(trimmed)) {
|
|
104
|
+
const rest = trimmed.replace(/^needs\s*:\s*/i, '').trim();
|
|
105
|
+
if (rest.startsWith('[') && rest.endsWith(']')) {
|
|
106
|
+
const list = rest.slice(1, -1).split(',').map((s) => s.trim().replace(/^['"]|['"]$/g, ''));
|
|
107
|
+
currentJob.needs.push(...list.filter(Boolean));
|
|
108
|
+
}
|
|
109
|
+
else if (rest) {
|
|
110
|
+
currentJob.needs.push(rest.replace(/^['"]|['"]$/g, ''));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
else if (/^-\s*needs\s*:\s*(.+)$/i.test(trimmed)) {
|
|
114
|
+
const m = trimmed.match(/^-\s*needs\s*:\s*(.+)$/i);
|
|
115
|
+
if (m)
|
|
116
|
+
currentJob.needs.push(m[1].trim());
|
|
117
|
+
}
|
|
118
|
+
else if (trimmed.startsWith('-') && (indent >= 4 || trimmed.startsWith('- uses:') || trimmed.startsWith('- name:'))) {
|
|
119
|
+
// Step start
|
|
120
|
+
const stepNameMatch = trimmed.match(/^-\s*name\s*:\s*['"]?([^'"]+)['"]?/i);
|
|
121
|
+
const stepUsesMatch = trimmed.match(/^-\s*uses\s*:\s*['"]?([^'"]+)['"]?/i);
|
|
122
|
+
const stepRunMatch = trimmed.match(/^-\s*run\s*:\s*(.+)$/i);
|
|
123
|
+
currentStep = {
|
|
124
|
+
name: stepNameMatch ? stepNameMatch[1] : (stepUsesMatch ? stepUsesMatch[1] : (stepRunMatch ? stepRunMatch[1] : 'Step')),
|
|
125
|
+
uses: stepUsesMatch ? stepUsesMatch[1] : null,
|
|
126
|
+
run: stepRunMatch ? stepRunMatch[1] : null,
|
|
127
|
+
};
|
|
128
|
+
currentJob.steps.push(currentStep);
|
|
129
|
+
}
|
|
130
|
+
else if (currentStep) {
|
|
131
|
+
const stepUsesMatch = trimmed.match(/^uses\s*:\s*['"]?([^'"]+)['"]?/i);
|
|
132
|
+
const stepRunMatch = trimmed.match(/^run\s*:\s*(.+)$/i);
|
|
133
|
+
if (stepUsesMatch)
|
|
134
|
+
currentStep.uses = stepUsesMatch[1];
|
|
135
|
+
if (stepRunMatch)
|
|
136
|
+
currentStep.run = stepRunMatch[1];
|
|
137
|
+
}
|
|
111
138
|
}
|
|
112
|
-
} else if (/^-\s*needs\s*:\s*(.+)$/i.test(trimmed)) {
|
|
113
|
-
const m = trimmed.match(/^-\s*needs\s*:\s*(.+)$/i);
|
|
114
|
-
if (m) currentJob.needs.push(m[1].trim());
|
|
115
|
-
} else if (trimmed.startsWith('-') && (indent >= 4 || trimmed.startsWith('- uses:') || trimmed.startsWith('- name:'))) {
|
|
116
|
-
// Step start
|
|
117
|
-
const stepNameMatch = trimmed.match(/^-\s*name\s*:\s*['"]?([^'"]+)['"]?/i);
|
|
118
|
-
const stepUsesMatch = trimmed.match(/^-\s*uses\s*:\s*['"]?([^'"]+)['"]?/i);
|
|
119
|
-
const stepRunMatch = trimmed.match(/^-\s*run\s*:\s*(.+)$/i);
|
|
120
|
-
|
|
121
|
-
currentStep = {
|
|
122
|
-
name: stepNameMatch ? stepNameMatch[1] : (stepUsesMatch ? stepUsesMatch[1] : (stepRunMatch ? stepRunMatch[1] : 'Step')),
|
|
123
|
-
uses: stepUsesMatch ? stepUsesMatch[1] : null,
|
|
124
|
-
run: stepRunMatch ? stepRunMatch[1] : null,
|
|
125
|
-
};
|
|
126
|
-
currentJob.steps.push(currentStep);
|
|
127
|
-
} else if (currentStep) {
|
|
128
|
-
const stepUsesMatch = trimmed.match(/^uses\s*:\s*['"]?([^'"]+)['"]?/i);
|
|
129
|
-
const stepRunMatch = trimmed.match(/^run\s*:\s*(.+)$/i);
|
|
130
|
-
if (stepUsesMatch) currentStep.uses = stepUsesMatch[1];
|
|
131
|
-
if (stepRunMatch) currentStep.run = stepRunMatch[1];
|
|
132
|
-
}
|
|
133
139
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
jobs,
|
|
142
|
-
};
|
|
140
|
+
return {
|
|
141
|
+
file: filePath,
|
|
142
|
+
fileName,
|
|
143
|
+
name: name || fileName,
|
|
144
|
+
triggers: [...new Set(triggers.filter(Boolean))],
|
|
145
|
+
jobs,
|
|
146
|
+
};
|
|
143
147
|
}
|
|
148
|
+
//# sourceMappingURL=workflows.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflows.js","sourceRoot":"","sources":["../../src/shared/analyzer/workflows.ts"],"names":[],"mappings":"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","sourcesContent":["// GitHub Actions & CI/CD workflow parser.\n// Pure isomorphic analyzer — parses YAML workflow files without external dependencies.\n\nexport async function analyzeWorkflows(source) {\n const workflows = [];\n const entries = await source.list('.github/workflows').catch(() => []);\n const ymlFiles = entries.filter((e) => e.type === 'file' && /\\.(ya?ml)$/i.test(e.name));\n\n for (const entry of ymlFiles) {\n const raw = await source.read(entry.path).catch(() => '');\n if (!raw) continue;\n const parsed = parseWorkflowYaml(raw, entry.path, entry.name);\n if (parsed) workflows.push(parsed);\n }\n\n return workflows;\n}\n\nexport function parseWorkflowYaml(text, filePath = '', fileName = '') {\n const lines = text.split('\\n');\n let name = fileName.replace(/\\.ya?ml$/i, '');\n const triggers = [];\n const jobs = [];\n\n let currentSection = null;\n let currentJob = null;\n let currentStep = null;\n let inOnSection = false;\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n\n const indent = line.search(/\\S/);\n\n // Top-level name: ...\n if (indent === 0 && /^name\\s*:\\s*(.+)$/i.test(trimmed)) {\n const m = trimmed.match(/^name\\s*:\\s*['\"]?([^'\"]+)['\"]?/i);\n if (m) name = m[1].trim();\n continue;\n }\n\n // Top-level on: ...\n if (indent === 0 && /^on\\s*:/i.test(trimmed)) {\n currentSection = 'on';\n inOnSection = true;\n const rest = trimmed.replace(/^on\\s*:\\s*/i, '').trim();\n if (rest) {\n if (rest.startsWith('[') && rest.endsWith(']')) {\n const list = rest.slice(1, -1).split(',').map((s) => s.trim().replace(/^['\"]|['\"]$/g, ''));\n triggers.push(...list.filter(Boolean));\n } else {\n triggers.push(rest.replace(/^['\"]|['\"]$/g, ''));\n }\n }\n continue;\n }\n\n // Top-level jobs: ...\n if (indent === 0 && /^jobs\\s*:/i.test(trimmed)) {\n currentSection = 'jobs';\n inOnSection = false;\n continue;\n }\n\n if (currentSection === 'on') {\n if (indent === 0) {\n currentSection = null;\n inOnSection = false;\n } else {\n const trigMatch = trimmed.match(/^([a-zA-Z0-9_-]+)\\s*:/);\n const listMatch = trimmed.match(/^-\\s*([a-zA-Z0-9_-]+)/);\n if (trigMatch && indent <= 4) triggers.push(trigMatch[1]);\n else if (listMatch && indent <= 4) triggers.push(listMatch[1]);\n }\n }\n\n if (currentSection === 'jobs' && indent > 0) {\n // Job declaration at indent 2 (or top under jobs)\n if (indent === 2 && trimmed.endsWith(':')) {\n const jobId = trimmed.replace(/:$/, '').trim();\n currentJob = {\n id: jobId,\n name: jobId,\n runsOn: 'ubuntu-latest',\n needs: [],\n steps: [],\n };\n jobs.push(currentJob);\n currentStep = null;\n continue;\n }\n\n if (!currentJob) continue;\n\n // Inside a job:\n if (/^name\\s*:\\s*(.+)$/i.test(trimmed) && indent <= 6 && !currentStep) {\n const m = trimmed.match(/^name\\s*:\\s*['\"]?([^'\"]+)['\"]?/i);\n if (m) currentJob.name = m[1].trim();\n } else if (/^runs-on\\s*:\\s*(.+)$/i.test(trimmed)) {\n const m = trimmed.match(/^runs-on\\s*:\\s*['\"]?([^'\"]+)['\"]?/i);\n if (m) currentJob.runsOn = m[1].trim();\n } else if (/^needs\\s*:\\s*(.+)$/i.test(trimmed)) {\n const rest = trimmed.replace(/^needs\\s*:\\s*/i, '').trim();\n if (rest.startsWith('[') && rest.endsWith(']')) {\n const list = rest.slice(1, -1).split(',').map((s) => s.trim().replace(/^['\"]|['\"]$/g, ''));\n currentJob.needs.push(...list.filter(Boolean));\n } else if (rest) {\n currentJob.needs.push(rest.replace(/^['\"]|['\"]$/g, ''));\n }\n } else if (/^-\\s*needs\\s*:\\s*(.+)$/i.test(trimmed)) {\n const m = trimmed.match(/^-\\s*needs\\s*:\\s*(.+)$/i);\n if (m) currentJob.needs.push(m[1].trim());\n } else if (trimmed.startsWith('-') && (indent >= 4 || trimmed.startsWith('- uses:') || trimmed.startsWith('- name:'))) {\n // Step start\n const stepNameMatch = trimmed.match(/^-\\s*name\\s*:\\s*['\"]?([^'\"]+)['\"]?/i);\n const stepUsesMatch = trimmed.match(/^-\\s*uses\\s*:\\s*['\"]?([^'\"]+)['\"]?/i);\n const stepRunMatch = trimmed.match(/^-\\s*run\\s*:\\s*(.+)$/i);\n\n currentStep = {\n name: stepNameMatch ? stepNameMatch[1] : (stepUsesMatch ? stepUsesMatch[1] : (stepRunMatch ? stepRunMatch[1] : 'Step')),\n uses: stepUsesMatch ? stepUsesMatch[1] : null,\n run: stepRunMatch ? stepRunMatch[1] : null,\n };\n currentJob.steps.push(currentStep);\n } else if (currentStep) {\n const stepUsesMatch = trimmed.match(/^uses\\s*:\\s*['\"]?([^'\"]+)['\"]?/i);\n const stepRunMatch = trimmed.match(/^run\\s*:\\s*(.+)$/i);\n if (stepUsesMatch) currentStep.uses = stepUsesMatch[1];\n if (stepRunMatch) currentStep.run = stepRunMatch[1];\n }\n }\n }\n\n return {\n file: filePath,\n fileName,\n name: name || fileName,\n triggers: [...new Set(triggers.filter(Boolean))],\n jobs,\n };\n}\n"]}
|