@backbencher/brain 0.0.0-stage → 0.1.1
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/CHANGELOG.md +298 -0
- package/EMBEDDINGS.md +355 -0
- package/LICENSE +21 -0
- package/README.md +415 -2
- package/bin/bb +13 -0
- package/dist/analysis-exclusions-7ZJDXOVZ.js +35 -0
- package/dist/brain-index-health-Q5APTJQL.js +17 -0
- package/dist/brain-relationship-enrichment-MYR2AM4F.js +558 -0
- package/dist/brain.sql-EIM4WS4D.js +29 -0
- package/dist/chunk-22V37YTK.js +1758 -0
- package/dist/chunk-2BSKD7O3.js +40 -0
- package/dist/chunk-2DE27DVC.js +1314 -0
- package/dist/chunk-2RRBMNBQ.js +322 -0
- package/dist/chunk-2WXYNLBA.js +29 -0
- package/dist/chunk-3CTFIJJB.js +17 -0
- package/dist/chunk-3ZTOHHSW.js +2104 -0
- package/dist/chunk-4H22TLOT.js +543 -0
- package/dist/chunk-5BVAH26D.js +340 -0
- package/dist/chunk-62DHLYW6.js +3427 -0
- package/dist/chunk-7AN7U5IZ.js +371 -0
- package/dist/chunk-7DRPNVIN.js +782 -0
- package/dist/chunk-7MBYT777.js +174 -0
- package/dist/chunk-AHDWGM7Q.js +236 -0
- package/dist/chunk-B5GEAAYF.js +818 -0
- package/dist/chunk-BEIZZ73B.js +1479 -0
- package/dist/chunk-BQ3OVQ3P.js +125 -0
- package/dist/chunk-D2Q4Y47A.js +53 -0
- package/dist/chunk-DIYAGQJL.js +34 -0
- package/dist/chunk-DWKXZ6IF.js +336 -0
- package/dist/chunk-DYM4FQEE.js +90 -0
- package/dist/chunk-ENW4BTBA.js +930 -0
- package/dist/chunk-EWMWK7WN.js +75 -0
- package/dist/chunk-EY5ZZUUA.js +626 -0
- package/dist/chunk-GBI7V7KY.js +69 -0
- package/dist/chunk-GEFFO6O2.js +152 -0
- package/dist/chunk-HJEWO5EV.js +247 -0
- package/dist/chunk-HQSPJDHI.js +613 -0
- package/dist/chunk-KAET47ZL.js +125 -0
- package/dist/chunk-KCDECRJY.js +440 -0
- package/dist/chunk-L5TDYKYK.js +76 -0
- package/dist/chunk-MLKGABMK.js +9 -0
- package/dist/chunk-NO3U3FJQ.js +201 -0
- package/dist/chunk-OWISBAOF.js +61 -0
- package/dist/chunk-QKTRECYS.js +1119 -0
- package/dist/chunk-QLK6ED4Q.js +235 -0
- package/dist/chunk-R2GW4F6R.js +19 -0
- package/dist/chunk-RCMTFSEF.js +87 -0
- package/dist/chunk-RM3PY6TG.js +376 -0
- package/dist/chunk-ROMM6KMC.js +433 -0
- package/dist/chunk-SGGCSHAX.js +12050 -0
- package/dist/chunk-SK6YVHFZ.js +211 -0
- package/dist/chunk-SM7RFQRO.js +214 -0
- package/dist/chunk-UGR4QIGS.js +67 -0
- package/dist/chunk-UQNQZXHY.js +711 -0
- package/dist/chunk-UUNHHX4M.js +13 -0
- package/dist/chunk-VL52PL2T.js +71 -0
- package/dist/chunk-WIG36E67.js +196 -0
- package/dist/chunk-XHK3WWGB.js +7436 -0
- package/dist/chunk-XTQM2IP3.js +2863 -0
- package/dist/chunk-ZE2LG76D.js +1296 -0
- package/dist/chunk-ZETTGPAW.js +6950 -0
- package/dist/cli/index.js +37 -0
- package/dist/config-HZAAYDP3.js +10 -0
- package/dist/content-search-7XIP555Q.js +593 -0
- package/dist/database-76RP44JG.js +9 -0
- package/dist/doctor-ANGETYMN.js +395 -0
- package/dist/edit-engine-CG72LDZD.js +51 -0
- package/dist/engine-BKEOGN7N.js +11 -0
- package/dist/engine-VIW6NSKO.js +18 -0
- package/dist/fingerprint-OBLIMZLP.js +33 -0
- package/dist/framework-intelligence-3LORTPOX.js +860 -0
- package/dist/freshness-3K6VU5OT.js +174 -0
- package/dist/graph-engine-HBSQNPTB.js +21 -0
- package/dist/graph_request-Q6NKB6N2.js +350 -0
- package/dist/impact_request-LYP672WE.js +68 -0
- package/dist/index-space-IUUN5WYV.js +15 -0
- package/dist/index-status-7OVGYSGW.js +34 -0
- package/dist/index.js +159 -0
- package/dist/instance-FO6QZRLH.js +19 -0
- package/dist/language-detection-DFCAU5DZ.js +11 -0
- package/dist/lsp/server.js +4301 -0
- package/dist/mcp/server.js +132 -0
- package/dist/onboard-DIQZV2NW.js +161 -0
- package/dist/override-companion-pass-XR7MHTP7.js +117 -0
- package/dist/parallel-indexer-FK2XKZXY.js +88 -0
- package/dist/pool-worker.js +1153 -0
- package/dist/project-health-6XPENVAA.js +94 -0
- package/dist/project-index-5SDPHITE.js +57 -0
- package/dist/project-registry-ZJ3DVUGO.js +355 -0
- package/dist/real-bash-analyzer-EJAU5LW5.js +2201 -0
- package/dist/real-c-analyzer-HBD3FNNH.js +5942 -0
- package/dist/real-clean-analyzer-4EEZLPV5.js +3470 -0
- package/dist/real-clojure-analyzer-Q55EBOO2.js +2818 -0
- package/dist/real-cpp-analyzer-IVVOD25R.js +4101 -0
- package/dist/real-csharp-analyzer-ZBNLNJZJ.js +3298 -0
- package/dist/real-css-analyzer-6XRQLUN7.js +15 -0
- package/dist/real-dart-analyzer-OLR72EFW.js +3944 -0
- package/dist/real-elixir-analyzer-MQWGAH2I.js +2192 -0
- package/dist/real-erlang-analyzer-7BVPQEHO.js +1731 -0
- package/dist/real-fsharp-analyzer-C2UPFNOA.js +2390 -0
- package/dist/real-go-analyzer-UKGI43YS.js +4126 -0
- package/dist/real-graphql-analyzer-VE2EEFII.js +1442 -0
- package/dist/real-groovy-analyzer-W26VACFW.js +2285 -0
- package/dist/real-haskell-analyzer-5CVVAJRX.js +2533 -0
- package/dist/real-hcl-analyzer-5ZT4U43N.js +725 -0
- package/dist/real-html-analyzer-JWNX3C6I.js +1759 -0
- package/dist/real-java-analyzer-GU42VBGQ.js +4677 -0
- package/dist/real-javascript-analyzer-QA4QOJ6W.js +14 -0
- package/dist/real-json-analyzer-MPA6PRXL.js +1640 -0
- package/dist/real-julia-analyzer-22CWYQMN.js +2085 -0
- package/dist/real-kotlin-analyzer-QDZJLXXU.js +3956 -0
- package/dist/real-lua-analyzer-G7NHLYKS.js +1957 -0
- package/dist/real-makefile-analyzer-OCY3GT5N.js +920 -0
- package/dist/real-markdown-analyzer-TEJK2JXJ.js +1215 -0
- package/dist/real-matlab-analyzer-Q5SWLN7Z.js +480 -0
- package/dist/real-nim-analyzer-YLXSK563.js +3603 -0
- package/dist/real-objective-c-analyzer-D23ACC2R.js +1518 -0
- package/dist/real-perl-analyzer-WWTRHYDT.js +1865 -0
- package/dist/real-php-analyzer-JNKJHTCL.js +9368 -0
- package/dist/real-powershell-analyzer-XRSNQERV.js +2915 -0
- package/dist/real-protobuf-analyzer-3OKUCZ7T.js +867 -0
- package/dist/real-python-analyzer-VXAFWI72.js +4880 -0
- package/dist/real-r-analyzer-ZGAIDVI4.js +2596 -0
- package/dist/real-ruby-analyzer-X2KTLVBH.js +2429 -0
- package/dist/real-rust-analyzer-OFFHPL2A.js +3359 -0
- package/dist/real-scala-analyzer-TSTW27J6.js +3293 -0
- package/dist/real-scss-analyzer-AVWYPSOQ.js +1515 -0
- package/dist/real-solidity-analyzer-UCI2KWUB.js +2608 -0
- package/dist/real-sql-analyzer-F77ZMQXR.js +3840 -0
- package/dist/real-svelte-analyzer-SF7NRUO5.js +2532 -0
- package/dist/real-swift-analyzer-MN5QT3QN.js +2311 -0
- package/dist/real-toml-analyzer-66L7BDCQ.js +929 -0
- package/dist/real-typescript-analyzer-6KEMVDWQ.js +14 -0
- package/dist/real-vb-analyzer-VICVPOP7.js +3145 -0
- package/dist/real-vba-analyzer-HXLTTHKM.js +466 -0
- package/dist/real-vue-analyzer-QXR56CFV.js +2680 -0
- package/dist/real-xml-analyzer-YL465ZJW.js +2405 -0
- package/dist/real-yaml-analyzer-SUBCBNSI.js +3528 -0
- package/dist/real-zig-analyzer-HIQLZMGY.js +2430 -0
- package/dist/regex-search-NJKHMZNO.js +239 -0
- package/dist/rules-ACHPB7QU.js +152 -0
- package/dist/scoped-path-MJYWJZQX.js +11 -0
- package/dist/setup-show-NT5ZHYNE.js +15 -0
- package/dist/setup-wizard-G5YYR5T6.js +24 -0
- package/dist/symbol-search-GZJIM2J2.js +16 -0
- package/dist/taint-KXUHHPIE.js +202 -0
- package/dist/taint-analyzer-N7YLZU3N.js +163 -0
- package/dist/tool-handlers-OJDC35DI.js +72 -0
- package/dist/tool-registry-BPEJNSZG.js +17 -0
- package/dist/tool-validation-Q57YMMBX.js +32 -0
- package/dist/type-inference-QMT2RMLV.js +13 -0
- package/dist/vector-binary-LMY4QGAN.js +19 -0
- package/dist/version-ABP2TUL2.js +9 -0
- package/dist/wasm-NMWO3WXY.js +16 -0
- package/docs/EMBEDDINGS.md +132 -0
- package/docs/README.md +178 -0
- package/docs/data/m10-baseline.json +602 -0
- package/docs/data/m10-benchmark-baseline.json +57 -0
- package/docs/data/m10-benchmark-last-run.json +127 -0
- package/docs/data/m10-final-audit.json +215 -0
- package/docs/data/m12-final-audit.json +138 -0
- package/docs/data/m12-surface-matrix.json +50 -0
- package/docs/data/public-tools.json +1902 -0
- package/docs/indexing/DEAD-ASSETS.md +88 -0
- package/docs/indexing/INDEXER-REVIEW.md +634 -0
- package/docs/indexing/PERFORMANCE-FINDINGS.md +106 -0
- package/docs/indexing/README.md +427 -0
- package/docs/indexing/discovery-matrix.md +41 -0
- package/docs/indexing/embeddings.md +325 -0
- package/docs/indexing/environment-variables.md +77 -0
- package/docs/indexing/performance.md +323 -0
- package/docs/indexing/resume.md +345 -0
- package/docs/indexing/troubleshooting.md +391 -0
- package/docs/man/bb.1 +190 -0
- package/docs/tools/bb_analyze.md +147 -0
- package/docs/tools/bb_annotate.md +110 -0
- package/docs/tools/bb_annotations.md +106 -0
- package/docs/tools/bb_ask.md +101 -0
- package/docs/tools/bb_cross_language.md +103 -0
- package/docs/tools/bb_doc.md +169 -0
- package/docs/tools/bb_edit.md +185 -0
- package/docs/tools/bb_graph.md +169 -0
- package/docs/tools/bb_health.md +156 -0
- package/docs/tools/bb_impact.md +117 -0
- package/docs/tools/bb_index.md +108 -0
- package/docs/tools/bb_migrate.md +135 -0
- package/docs/tools/bb_patterns.md +140 -0
- package/docs/tools/bb_search.md +150 -0
- package/docs/tools/bb_security.md +100 -0
- package/docs/tools/bb_visualize.md +184 -0
- package/grammars/README.md +57 -0
- package/grammars/THIRD-PARTY-NOTICES.md +50 -0
- package/grammars/tree-sitter-c/LICENSE +21 -0
- package/grammars/tree-sitter-clojure/LICENSE.md +21 -0
- package/grammars/tree-sitter-cpp/LICENSE +21 -0
- package/grammars/tree-sitter-csharp/LICENSE +21 -0
- package/grammars/tree-sitter-css/LICENSE +21 -0
- package/grammars/tree-sitter-css/README.md +21 -0
- package/grammars/tree-sitter-dart/LICENSE +20 -0
- package/grammars/tree-sitter-dart-upstream/LICENSE +20 -0
- package/grammars/tree-sitter-dart-upstream/README.md +18 -0
- package/grammars/tree-sitter-elixir/LICENSE +176 -0
- package/grammars/tree-sitter-elixir/README.md +11 -0
- package/grammars/tree-sitter-fsharp/LICENSE +21 -0
- package/grammars/tree-sitter-fsharp/README.md +45 -0
- package/grammars/tree-sitter-graphql/LICENSE +21 -0
- package/grammars/tree-sitter-graphql/README.md +106 -0
- package/grammars/tree-sitter-groovy/LICENSE +21 -0
- package/grammars/tree-sitter-groovy/README.md +4 -0
- package/grammars/tree-sitter-haskell/LICENSE +21 -0
- package/grammars/tree-sitter-haskell/README.md +394 -0
- package/grammars/tree-sitter-hcl/CHANGELOG.md +102 -0
- package/grammars/tree-sitter-hcl/LICENSE +201 -0
- package/grammars/tree-sitter-hcl/README.md +39 -0
- package/grammars/tree-sitter-html/LICENSE +21 -0
- package/grammars/tree-sitter-html/README.md +21 -0
- package/grammars/tree-sitter-julia/LICENSE +21 -0
- package/grammars/tree-sitter-julia/README.md +24 -0
- package/grammars/tree-sitter-kotlin/LICENSE +9 -0
- package/grammars/tree-sitter-lua/LICENSE.md +21 -0
- package/grammars/tree-sitter-lua/README.md +29 -0
- package/grammars/tree-sitter-make/LICENSE +21 -0
- package/grammars/tree-sitter-matlab/LICENSE +19 -0
- package/grammars/tree-sitter-matlab/README.md +99 -0
- package/grammars/tree-sitter-nim/LICENSE +373 -0
- package/grammars/tree-sitter-nim/README.md +46 -0
- package/grammars/tree-sitter-objc/LICENSE +21 -0
- package/grammars/tree-sitter-objc/README.md +20 -0
- package/grammars/tree-sitter-perl/LICENSE +7 -0
- package/grammars/tree-sitter-perl/README.md +100 -0
- package/grammars/tree-sitter-powershell/LICENSE +21 -0
- package/grammars/tree-sitter-powershell/README.md +7 -0
- package/grammars/tree-sitter-proto/LICENSE +21 -0
- package/grammars/tree-sitter-proto/README.md +105 -0
- package/grammars/tree-sitter-r/LICENSE +9 -0
- package/grammars/tree-sitter-r/README.md +66 -0
- package/grammars/tree-sitter-ruby/LICENSE +22 -0
- package/grammars/tree-sitter-scss/LICENSE +21 -0
- package/grammars/tree-sitter-scss/README.md +17 -0
- package/grammars/tree-sitter-solidity/LICENSE +7 -0
- package/grammars/tree-sitter-sql/LICENSE +21 -0
- package/grammars/tree-sitter-typescript/LICENSE +21 -0
- package/grammars/tree-sitter-vba/LICENSE +21 -0
- package/grammars/tree-sitter-vba/README.md +738 -0
- package/grammars/tree-sitter-vbnet/LICENSE +21 -0
- package/grammars/tree-sitter-vbnet/README.md +322 -0
- package/grammars/tree-sitter-xml/LICENSE +19 -0
- package/grammars/tree-sitter-yaml/LICENSE +20 -0
- package/grammars/tree-sitter-yaml/README.md +21 -0
- package/package.json +184 -4
- package/wasm/dart.wasm +0 -0
- package/wasm/tree-sitter-bash.wasm +0 -0
- package/wasm/tree-sitter-c.wasm +0 -0
- package/wasm/tree-sitter-c_sharp.wasm +0 -0
- package/wasm/tree-sitter-clean.wasm +0 -0
- package/wasm/tree-sitter-clojure.wasm +0 -0
- package/wasm/tree-sitter-core_schema.wasm +0 -0
- package/wasm/tree-sitter-cpp.wasm +0 -0
- package/wasm/tree-sitter-css.wasm +0 -0
- package/wasm/tree-sitter-dart.wasm +0 -0
- package/wasm/tree-sitter-elisp.wasm +0 -0
- package/wasm/tree-sitter-elixir.wasm +0 -0
- package/wasm/tree-sitter-elm.wasm +0 -0
- package/wasm/tree-sitter-embedded_template.wasm +0 -0
- package/wasm/tree-sitter-erlang.wasm +0 -0
- package/wasm/tree-sitter-fsharp.wasm +0 -0
- package/wasm/tree-sitter-fsharp_signature.wasm +0 -0
- package/wasm/tree-sitter-go.wasm +0 -0
- package/wasm/tree-sitter-graphql.wasm +0 -0
- package/wasm/tree-sitter-groovy.wasm +0 -0
- package/wasm/tree-sitter-haskell.wasm +0 -0
- package/wasm/tree-sitter-hcl.wasm +0 -0
- package/wasm/tree-sitter-html.wasm +0 -0
- package/wasm/tree-sitter-java.wasm +0 -0
- package/wasm/tree-sitter-javascript.wasm +0 -0
- package/wasm/tree-sitter-json.wasm +0 -0
- package/wasm/tree-sitter-json_schema.wasm +0 -0
- package/wasm/tree-sitter-julia.wasm +0 -0
- package/wasm/tree-sitter-kotlin.wasm +0 -0
- package/wasm/tree-sitter-lua.wasm +0 -0
- package/wasm/tree-sitter-make.wasm +0 -0
- package/wasm/tree-sitter-markdown.wasm +0 -0
- package/wasm/tree-sitter-markdown_inline.wasm +0 -0
- package/wasm/tree-sitter-matlab.wasm +0 -0
- package/wasm/tree-sitter-nim.wasm +0 -0
- package/wasm/tree-sitter-objc.wasm +0 -0
- package/wasm/tree-sitter-ocaml.wasm +0 -0
- package/wasm/tree-sitter-perl.wasm +0 -0
- package/wasm/tree-sitter-php.wasm +0 -0
- package/wasm/tree-sitter-php_only.wasm +0 -0
- package/wasm/tree-sitter-powershell.wasm +0 -0
- package/wasm/tree-sitter-protobuf.wasm +0 -0
- package/wasm/tree-sitter-python.wasm +0 -0
- package/wasm/tree-sitter-ql.wasm +0 -0
- package/wasm/tree-sitter-r.wasm +0 -0
- package/wasm/tree-sitter-rescript.wasm +0 -0
- package/wasm/tree-sitter-ruby.wasm +0 -0
- package/wasm/tree-sitter-rust.wasm +0 -0
- package/wasm/tree-sitter-scala.wasm +0 -0
- package/wasm/tree-sitter-scss.wasm +0 -0
- package/wasm/tree-sitter-solidity.wasm +0 -0
- package/wasm/tree-sitter-sql.wasm +0 -0
- package/wasm/tree-sitter-svelte.wasm +0 -0
- package/wasm/tree-sitter-swift.wasm +0 -0
- package/wasm/tree-sitter-systemrdl.wasm +0 -0
- package/wasm/tree-sitter-terraform.wasm +0 -0
- package/wasm/tree-sitter-tlaplus.wasm +0 -0
- package/wasm/tree-sitter-toml.wasm +0 -0
- package/wasm/tree-sitter-tsx.wasm +0 -0
- package/wasm/tree-sitter-typescript.wasm +0 -0
- package/wasm/tree-sitter-vba.wasm +0 -0
- package/wasm/tree-sitter-vbnet.wasm +0 -0
- package/wasm/tree-sitter-vue.wasm +0 -0
- package/wasm/tree-sitter-xml.wasm +0 -0
- package/wasm/tree-sitter-yaml.wasm +0 -0
- package/wasm/tree-sitter-zig.wasm +0 -0
- package/wasm/tree-sitter.wasm +0 -0
- package/wasm/tsx.wasm +0 -0
- package/wasm/typescript.wasm +0 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readSymbolSnippet
|
|
3
|
+
} from "./chunk-3CTFIJJB.js";
|
|
4
|
+
import {
|
|
5
|
+
checkResultsFreshness
|
|
6
|
+
} from "./chunk-QLK6ED4Q.js";
|
|
7
|
+
import {
|
|
8
|
+
BrainDatabase
|
|
9
|
+
} from "./chunk-QKTRECYS.js";
|
|
10
|
+
import "./chunk-2BSKD7O3.js";
|
|
11
|
+
import {
|
|
12
|
+
analysis_exclusions_exports
|
|
13
|
+
} from "./chunk-SM7RFQRO.js";
|
|
14
|
+
import "./chunk-L5TDYKYK.js";
|
|
15
|
+
import "./chunk-ROMM6KMC.js";
|
|
16
|
+
import "./chunk-MLKGABMK.js";
|
|
17
|
+
|
|
18
|
+
// src/intelligence/search/regex-scan-worker.ts
|
|
19
|
+
import { Worker, parentPort, workerData } from "worker_threads";
|
|
20
|
+
function matchNames(request) {
|
|
21
|
+
const expression = new RegExp(request.pattern, request.caseSensitive ? "" : "i");
|
|
22
|
+
const matched = [];
|
|
23
|
+
const names = request.names;
|
|
24
|
+
for (let i = 0; i < names.length; i++) {
|
|
25
|
+
if (expression.test(names[i])) matched.push(i);
|
|
26
|
+
}
|
|
27
|
+
return matched;
|
|
28
|
+
}
|
|
29
|
+
if (parentPort && workerData && workerData.__regexScanWorker) {
|
|
30
|
+
const request = workerData.request;
|
|
31
|
+
try {
|
|
32
|
+
const matchedIndexes = matchNames(request);
|
|
33
|
+
parentPort.postMessage({ ok: true, matchedIndexes, scanned: request.names.length });
|
|
34
|
+
} catch (error) {
|
|
35
|
+
parentPort.postMessage({ ok: false, error: error?.message ?? String(error) });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function scanWithDeadline(request, timeoutMs) {
|
|
39
|
+
return new Promise((resolve) => {
|
|
40
|
+
let settled = false;
|
|
41
|
+
let worker = null;
|
|
42
|
+
let budgetTimer = null;
|
|
43
|
+
const timers = [];
|
|
44
|
+
const finish = (outcome) => {
|
|
45
|
+
if (settled) return;
|
|
46
|
+
settled = true;
|
|
47
|
+
for (const timer of timers) clearTimeout(timer);
|
|
48
|
+
try {
|
|
49
|
+
worker?.terminate();
|
|
50
|
+
} catch {
|
|
51
|
+
}
|
|
52
|
+
resolve(outcome);
|
|
53
|
+
};
|
|
54
|
+
const runInline = () => {
|
|
55
|
+
if (settled) return;
|
|
56
|
+
try {
|
|
57
|
+
const matchedIndexes = matchNames(request);
|
|
58
|
+
finish({ matchedIndexes, scanned: request.names.length, timedOut: false, inline: true });
|
|
59
|
+
} catch {
|
|
60
|
+
finish({ matchedIndexes: [], scanned: 0, timedOut: false, inline: true });
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
try {
|
|
64
|
+
worker = new Worker(new URL(import.meta.url), {
|
|
65
|
+
workerData: { __regexScanWorker: true, request },
|
|
66
|
+
// A catastrophic regex can exhaust the stack, and an uncaught exception
|
|
67
|
+
// in a worker must not take the server with it.
|
|
68
|
+
stdout: true,
|
|
69
|
+
stderr: true
|
|
70
|
+
});
|
|
71
|
+
} catch {
|
|
72
|
+
runInline();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const startBudget = () => {
|
|
76
|
+
if (settled || budgetTimer !== null) return;
|
|
77
|
+
budgetTimer = setTimeout(
|
|
78
|
+
() => finish({ matchedIndexes: [], scanned: 0, timedOut: true, inline: false }),
|
|
79
|
+
timeoutMs
|
|
80
|
+
);
|
|
81
|
+
timers.push(budgetTimer);
|
|
82
|
+
};
|
|
83
|
+
worker.on("online", startBudget);
|
|
84
|
+
worker.on("message", (message) => {
|
|
85
|
+
if (message?.ok) finish({ matchedIndexes: message.matchedIndexes, scanned: message.scanned, timedOut: false, inline: false });
|
|
86
|
+
else runInline();
|
|
87
|
+
});
|
|
88
|
+
worker.on("error", () => runInline());
|
|
89
|
+
worker.on("exit", () => {
|
|
90
|
+
if (!settled) runInline();
|
|
91
|
+
});
|
|
92
|
+
timers.push(
|
|
93
|
+
setTimeout(() => {
|
|
94
|
+
if (!settled && budgetTimer === null) runInline();
|
|
95
|
+
}, Math.max(1e3, timeoutMs))
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// src/intelligence/search/regex-search.ts
|
|
101
|
+
var REGEX_SCAN_CAP = 2e4;
|
|
102
|
+
var REGEX_TIME_BUDGET_MS = 2e3;
|
|
103
|
+
var PROJECTION_MAX = 5e4;
|
|
104
|
+
var InvalidRegexError = class extends Error {
|
|
105
|
+
constructor(pattern, cause) {
|
|
106
|
+
super('invalid regex "' + pattern + '": ' + cause);
|
|
107
|
+
this.name = "InvalidRegexError";
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
function compileRegex(pattern, caseSensitive) {
|
|
111
|
+
try {
|
|
112
|
+
return new RegExp(pattern, caseSensitive ? "" : "i");
|
|
113
|
+
} catch (error) {
|
|
114
|
+
throw new InvalidRegexError(pattern, error instanceof Error ? error.message : String(error));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function compilePatternForTests(pattern, caseSensitive) {
|
|
118
|
+
return compileRegex(pattern, caseSensitive);
|
|
119
|
+
}
|
|
120
|
+
function normalizePageSize(value) {
|
|
121
|
+
if (value === void 0 || value === null) return 100;
|
|
122
|
+
const n = Math.trunc(Number(value));
|
|
123
|
+
if (!Number.isFinite(n) || n < 0) return 100;
|
|
124
|
+
return n;
|
|
125
|
+
}
|
|
126
|
+
function normalizeOffset(value) {
|
|
127
|
+
if (value === void 0 || value === null) return 0;
|
|
128
|
+
const n = Math.trunc(Number(value));
|
|
129
|
+
if (!Number.isFinite(n) || n < 0) return 0;
|
|
130
|
+
return n;
|
|
131
|
+
}
|
|
132
|
+
function compareCodeUnits(a, b) {
|
|
133
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
134
|
+
}
|
|
135
|
+
async function searchRegex(projectId, rawPattern, options = {}) {
|
|
136
|
+
const pattern = String(rawPattern ?? "").trim();
|
|
137
|
+
const maxResults = normalizePageSize(options.maxResults);
|
|
138
|
+
const offset = normalizeOffset(options.offset);
|
|
139
|
+
if (!pattern) {
|
|
140
|
+
const empty = [];
|
|
141
|
+
empty.searchMode = "regex";
|
|
142
|
+
empty.staleCount = 0;
|
|
143
|
+
empty.freshCount = 0;
|
|
144
|
+
empty.totalMatches = 0;
|
|
145
|
+
return empty;
|
|
146
|
+
}
|
|
147
|
+
const expression = compileRegex(pattern, options.caseSensitive === true);
|
|
148
|
+
const raw = await BrainDatabase.getRawInstance(projectId, { readonly: true });
|
|
149
|
+
const params = [projectId];
|
|
150
|
+
let filters = "";
|
|
151
|
+
if (options.languages?.length) {
|
|
152
|
+
filters += " AND s.file_id IN (SELECT id FROM brain_file WHERE project_id = ? AND language COLLATE NOCASE IN (" + options.languages.map(() => "?").join(",") + "))";
|
|
153
|
+
params.push(projectId, ...options.languages);
|
|
154
|
+
}
|
|
155
|
+
if (options.symbolKind) {
|
|
156
|
+
filters += " AND s.symbol_type COLLATE NOCASE = ?";
|
|
157
|
+
params.push(options.symbolKind);
|
|
158
|
+
}
|
|
159
|
+
const workerExtraParams = [...params.slice(1)];
|
|
160
|
+
const candidates = raw.prepare(
|
|
161
|
+
"SELECT s.id AS id, s.name AS name FROM brain_symbol s WHERE s.project_id = ? AND COALESCE(s.line_start, 0) > 0 AND s.name NOT LIKE '/%/%'" + filters + " LIMIT ?"
|
|
162
|
+
).all(...params, REGEX_SCAN_CAP + 1);
|
|
163
|
+
const rowCapReached = candidates.length > REGEX_SCAN_CAP;
|
|
164
|
+
const rows = rowCapReached ? candidates.slice(0, REGEX_SCAN_CAP) : candidates;
|
|
165
|
+
const scan = await scanWithDeadline(
|
|
166
|
+
{ pattern, caseSensitive: options.caseSensitive === true, names: rows.map((row) => row.name) },
|
|
167
|
+
REGEX_TIME_BUDGET_MS
|
|
168
|
+
);
|
|
169
|
+
const timeBudgetReached = scan.timedOut;
|
|
170
|
+
const unisolated = scan.inline;
|
|
171
|
+
const matchedIds = scan.matchedIndexes.map((index) => rows[index].id);
|
|
172
|
+
const matched = [];
|
|
173
|
+
const PROJECTION_CHUNK = 500;
|
|
174
|
+
const projectionCapReached = matchedIds.length > PROJECTION_MAX;
|
|
175
|
+
const projectable = projectionCapReached ? matchedIds.slice(0, PROJECTION_MAX) : matchedIds;
|
|
176
|
+
const matchedRowCount = matchedIds.length;
|
|
177
|
+
for (let start = 0; start < projectable.length; start += PROJECTION_CHUNK) {
|
|
178
|
+
const chunk = projectable.slice(start, start + PROJECTION_CHUNK);
|
|
179
|
+
const rows2 = raw.prepare(
|
|
180
|
+
"SELECT s.id, s.file_id AS fileId, s.name, s.symbol_type AS symbolType, COALESCE(s.line_start, 0) AS line, COALESCE(s.line_end, 0) AS lineEnd, COALESCE(s.complexity, 0) AS complexity, f.relative_path AS relativePath, f.path AS filePath, COALESCE(f.language, 'unknown') AS language FROM brain_symbol s JOIN brain_file f ON f.id = s.file_id WHERE s.id IN (" + chunk.map(() => "?").join(",") + ")"
|
|
181
|
+
).all(...chunk);
|
|
182
|
+
for (const row of rows2) {
|
|
183
|
+
if (analysis_exclusions_exports.isHardNoisePath(row.relativePath) || analysis_exclusions_exports.isNoiseSymbolName(row.name)) continue;
|
|
184
|
+
matched.push(row);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
matched.sort(
|
|
188
|
+
(a, b) => (analysis_exclusions_exports.isTestPath(a.relativePath) ? 1 : 0) - (analysis_exclusions_exports.isTestPath(b.relativePath) ? 1 : 0) || b.complexity - a.complexity || compareCodeUnits(a.relativePath, b.relativePath) || compareCodeUnits(a.name, b.name)
|
|
189
|
+
);
|
|
190
|
+
const page = matched.slice(offset, offset + maxResults);
|
|
191
|
+
const results = [];
|
|
192
|
+
results.searchMode = "regex";
|
|
193
|
+
results.truncated = rowCapReached || timeBudgetReached || projectionCapReached;
|
|
194
|
+
const reasons = [];
|
|
195
|
+
if (rowCapReached) reasons.push("scan-cap");
|
|
196
|
+
if (timeBudgetReached) reasons.push("time-budget");
|
|
197
|
+
if (projectionCapReached) reasons.push("projection-cap");
|
|
198
|
+
if (unisolated) reasons.push("unisolated-scan");
|
|
199
|
+
if (reasons.length) results.truncationReason = reasons.join(",");
|
|
200
|
+
results.totalMatches = matchedRowCount;
|
|
201
|
+
results.rankedMatches = matched.length;
|
|
202
|
+
if (timeBudgetReached) results.scannedRows = rows.length;
|
|
203
|
+
for (const row of page) {
|
|
204
|
+
results.push({
|
|
205
|
+
file: row.relativePath,
|
|
206
|
+
fileId: row.fileId,
|
|
207
|
+
line: row.line,
|
|
208
|
+
column: 0,
|
|
209
|
+
context: row.symbolType + ": " + row.name,
|
|
210
|
+
symbolName: row.name,
|
|
211
|
+
symbolType: row.symbolType,
|
|
212
|
+
language: row.language,
|
|
213
|
+
matchType: "symbol",
|
|
214
|
+
snippet: options.includeContent ? await readSymbolSnippet(row.filePath, row.line, row.lineEnd) || row.name : row.name,
|
|
215
|
+
complexity: row.complexity,
|
|
216
|
+
_relevance: 1
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
try {
|
|
220
|
+
const freshness = checkResultsFreshness(raw, results.map((result) => ({ fileId: result.fileId })));
|
|
221
|
+
let staleCount = 0;
|
|
222
|
+
for (const result of results) {
|
|
223
|
+
if (["stale", "missing"].includes(freshness.get(result.fileId) ?? "")) {
|
|
224
|
+
result.stale = true;
|
|
225
|
+
staleCount++;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
results.staleCount = staleCount;
|
|
229
|
+
results.freshCount = results.length - staleCount;
|
|
230
|
+
} catch {
|
|
231
|
+
results.freshnessError = true;
|
|
232
|
+
}
|
|
233
|
+
return results;
|
|
234
|
+
}
|
|
235
|
+
export {
|
|
236
|
+
InvalidRegexError,
|
|
237
|
+
compilePatternForTests,
|
|
238
|
+
searchRegex
|
|
239
|
+
};
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BrainDatabase
|
|
3
|
+
} from "./chunk-QKTRECYS.js";
|
|
4
|
+
import "./chunk-2BSKD7O3.js";
|
|
5
|
+
import {
|
|
6
|
+
nonCodeLanguageSQL
|
|
7
|
+
} from "./chunk-SM7RFQRO.js";
|
|
8
|
+
import "./chunk-L5TDYKYK.js";
|
|
9
|
+
import "./chunk-ROMM6KMC.js";
|
|
10
|
+
import "./chunk-MLKGABMK.js";
|
|
11
|
+
|
|
12
|
+
// src/intelligence/patterns/rules.ts
|
|
13
|
+
var COMPLEXITY_THRESHOLD = 15;
|
|
14
|
+
var PARAMETER_THRESHOLD = 7;
|
|
15
|
+
var GOD_CLASS_MEMBER_THRESHOLD = 10;
|
|
16
|
+
async function detectIndexRules(projectId, options) {
|
|
17
|
+
const findings = [];
|
|
18
|
+
const raw = await BrainDatabase.getRawInstance(projectId, { readonly: true });
|
|
19
|
+
const budget = Math.max(0, options.maxResults);
|
|
20
|
+
const nonCode = nonCodeLanguageSQL("f");
|
|
21
|
+
const nonCodeClause = ` AND ${nonCode.sql}`;
|
|
22
|
+
if (options.includeAntiPatterns) {
|
|
23
|
+
const complex = raw.prepare(
|
|
24
|
+
"SELECT s.name, s.complexity, f.relative_path AS file, COALESCE(s.line_start, 0) AS line FROM brain_symbol s JOIN brain_file f ON f.id = s.file_id WHERE s.project_id = ? AND s.complexity IS NOT NULL AND s.complexity >= ?" + nonCodeClause + " ORDER BY s.complexity DESC, s.id"
|
|
25
|
+
).all(projectId, COMPLEXITY_THRESHOLD, ...nonCode.params);
|
|
26
|
+
let shown = 0;
|
|
27
|
+
for (const row of complex) {
|
|
28
|
+
const confidence = Math.min(0.9, 0.5 + row.complexity / 100);
|
|
29
|
+
if (confidence < options.confidenceThreshold) continue;
|
|
30
|
+
if (shown >= budget) {
|
|
31
|
+
findings.push({
|
|
32
|
+
type: "high-complexity-function",
|
|
33
|
+
confidence,
|
|
34
|
+
location: { file: row.file, line: row.line, symbol: row.name },
|
|
35
|
+
description: `Function '${row.name}' has complexity ${row.complexity} (threshold ${COMPLEXITY_THRESHOLD}).`,
|
|
36
|
+
severity: "warning",
|
|
37
|
+
suggestion: "Split the function into smaller units and reduce nesting depth.",
|
|
38
|
+
evidence: [{ type: "indexed-complexity", description: `complexity=${row.complexity} (from brain_symbol.complexity)` }],
|
|
39
|
+
truncatedByDisplayLimit: true
|
|
40
|
+
});
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
shown++;
|
|
44
|
+
findings.push({
|
|
45
|
+
type: "high-complexity-function",
|
|
46
|
+
confidence,
|
|
47
|
+
location: { file: row.file, line: row.line, symbol: row.name },
|
|
48
|
+
description: `Function '${row.name}' has complexity ${row.complexity} (threshold ${COMPLEXITY_THRESHOLD}).`,
|
|
49
|
+
severity: "warning",
|
|
50
|
+
suggestion: "Split the function into smaller units and reduce nesting depth.",
|
|
51
|
+
evidence: [{ type: "indexed-complexity", description: `complexity=${row.complexity} (from brain_symbol.complexity)` }]
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
const paramRows = raw.prepare(
|
|
55
|
+
"SELECT s.name, s.parameters, f.relative_path AS file, COALESCE(s.line_start, 0) AS line, json_array_length(s.parameters) AS paramCount FROM brain_symbol s JOIN brain_file f ON f.id = s.file_id WHERE s.project_id = ? AND s.parameters IS NOT NULL AND s.parameters != '[]' AND json_valid(s.parameters) AND length(s.parameters) >= 17 AND json_array_length(s.parameters) > ?" + nonCodeClause + " ORDER BY s.id"
|
|
56
|
+
).all(projectId, PARAMETER_THRESHOLD, ...nonCode.params);
|
|
57
|
+
let shownParams = 0;
|
|
58
|
+
for (const row of paramRows) {
|
|
59
|
+
const confidence = 0.7;
|
|
60
|
+
if (confidence < options.confidenceThreshold) continue;
|
|
61
|
+
const params = JSON.parse(row.parameters);
|
|
62
|
+
if (shownParams >= budget) {
|
|
63
|
+
findings.push({
|
|
64
|
+
type: "long-parameter-list",
|
|
65
|
+
confidence,
|
|
66
|
+
location: { file: row.file, line: row.line, symbol: row.name },
|
|
67
|
+
description: `Function '${row.name}' has ${row.paramCount} parameters (threshold ${PARAMETER_THRESHOLD}).`,
|
|
68
|
+
severity: "warning",
|
|
69
|
+
suggestion: "Group related parameters into a single options/configuration object.",
|
|
70
|
+
evidence: [{ type: "indexed-parameters", description: `parameters=${params.slice(0, 12).join(", ")}` }],
|
|
71
|
+
truncatedByDisplayLimit: true
|
|
72
|
+
});
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
shownParams++;
|
|
76
|
+
findings.push({
|
|
77
|
+
type: "long-parameter-list",
|
|
78
|
+
confidence,
|
|
79
|
+
location: { file: row.file, line: row.line, symbol: row.name },
|
|
80
|
+
description: `Function '${row.name}' has ${row.paramCount} parameters (threshold ${PARAMETER_THRESHOLD}).`,
|
|
81
|
+
severity: "warning",
|
|
82
|
+
suggestion: "Group related parameters into a single options/configuration object.",
|
|
83
|
+
evidence: [{ type: "indexed-parameters", description: `parameters=${params.slice(0, 12).join(", ")}` }]
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
const godClasses = raw.prepare(
|
|
87
|
+
"SELECT s.id, s.name, f.relative_path AS file, COALESCE(s.line_start, 0) AS line, (SELECT COUNT(*) FROM brain_symbol m WHERE m.owner_symbol_id = s.id) AS members FROM brain_symbol s JOIN brain_file f ON f.id = s.file_id WHERE s.project_id = ? AND s.symbol_type IN ('class','interface','struct','trait','enum') AND (SELECT COUNT(*) FROM brain_symbol m WHERE m.owner_symbol_id = s.id) > ?" + nonCodeClause + " ORDER BY members DESC, s.id"
|
|
88
|
+
).all(projectId, GOD_CLASS_MEMBER_THRESHOLD, ...nonCode.params);
|
|
89
|
+
let shownGod = 0;
|
|
90
|
+
for (const row of godClasses) {
|
|
91
|
+
const confidence = Math.min(0.9, 0.5 + row.members / 40);
|
|
92
|
+
if (confidence < options.confidenceThreshold) continue;
|
|
93
|
+
const finding = {
|
|
94
|
+
type: "god-class",
|
|
95
|
+
confidence,
|
|
96
|
+
location: { file: row.file, line: row.line, symbol: row.name },
|
|
97
|
+
description: `Type '${row.name}' has ${row.members} direct members (threshold ${GOD_CLASS_MEMBER_THRESHOLD}).`,
|
|
98
|
+
severity: "warning",
|
|
99
|
+
suggestion: "Split the type by responsibility; extract cohesive subsets into focused types.",
|
|
100
|
+
evidence: [{ type: "indexed-members", description: `direct members=${row.members} (via owner_symbol_id)` }]
|
|
101
|
+
};
|
|
102
|
+
if (shownGod >= budget) {
|
|
103
|
+
finding.truncatedByDisplayLimit = true;
|
|
104
|
+
findings.push(finding);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
shownGod++;
|
|
108
|
+
findings.push(finding);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (options.includeFramework) {
|
|
112
|
+
const frameworks = raw.prepare(
|
|
113
|
+
"SELECT framework_type, COUNT(*) c FROM brain_file WHERE project_id = ? AND framework_type IS NOT NULL AND framework_type NOT IN ('', 'unknown') GROUP BY framework_type ORDER BY c DESC, framework_type"
|
|
114
|
+
).all(projectId);
|
|
115
|
+
let shownFw = 0;
|
|
116
|
+
for (const fw of frameworks) {
|
|
117
|
+
const confidence = 0.6;
|
|
118
|
+
if (confidence < options.confidenceThreshold) continue;
|
|
119
|
+
if (shownFw >= budget && budget > 0 && frameworks.length > budget) {
|
|
120
|
+
findings.push({
|
|
121
|
+
type: "framework-detected",
|
|
122
|
+
confidence: 0.6,
|
|
123
|
+
location: { file: "", line: 0 },
|
|
124
|
+
description: `Framework '${fw.framework_type}' detected in ${fw.c} indexed file(s).`,
|
|
125
|
+
severity: "info",
|
|
126
|
+
suggestion: "Framework usage is static index evidence; verify actual runtime wiring before drawing conclusions.",
|
|
127
|
+
evidence: [],
|
|
128
|
+
truncatedByDisplayLimit: true
|
|
129
|
+
});
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
shownFw++;
|
|
133
|
+
const files = raw.prepare(
|
|
134
|
+
"SELECT relative_path FROM brain_file WHERE project_id = ? AND framework_type = ? LIMIT 10"
|
|
135
|
+
).all(projectId, fw.framework_type);
|
|
136
|
+
const first = files[0];
|
|
137
|
+
findings.push({
|
|
138
|
+
type: "framework-detected",
|
|
139
|
+
confidence: 0.6,
|
|
140
|
+
location: { file: first?.relative_path ?? "", line: 0 },
|
|
141
|
+
description: `Framework '${fw.framework_type}' detected in ${fw.c} indexed file(s).`,
|
|
142
|
+
severity: "info",
|
|
143
|
+
suggestion: "Framework usage is static index evidence; verify actual runtime wiring before drawing conclusions.",
|
|
144
|
+
evidence: files.slice(0, 5).map((f) => ({ type: "indexed-file", description: f.relative_path }))
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return findings;
|
|
149
|
+
}
|
|
150
|
+
export {
|
|
151
|
+
detectIndexRules
|
|
152
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
describeEmbeddingConfig,
|
|
3
|
+
renderSetupShow
|
|
4
|
+
} from "./chunk-DWKXZ6IF.js";
|
|
5
|
+
import "./chunk-2RRBMNBQ.js";
|
|
6
|
+
import "./chunk-B5GEAAYF.js";
|
|
7
|
+
import "./chunk-7AN7U5IZ.js";
|
|
8
|
+
import "./chunk-UGR4QIGS.js";
|
|
9
|
+
import "./chunk-BQ3OVQ3P.js";
|
|
10
|
+
import "./chunk-2BSKD7O3.js";
|
|
11
|
+
import "./chunk-MLKGABMK.js";
|
|
12
|
+
export {
|
|
13
|
+
describeEmbeddingConfig,
|
|
14
|
+
renderSetupShow
|
|
15
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PROVIDERS,
|
|
3
|
+
fingerprintKey,
|
|
4
|
+
fixConfigMode,
|
|
5
|
+
hasEmbeddingConfig,
|
|
6
|
+
mapValidationError,
|
|
7
|
+
maybeRunFirstRunSetup,
|
|
8
|
+
runSetupWizard,
|
|
9
|
+
saveEmbeddingConfig,
|
|
10
|
+
validateKey
|
|
11
|
+
} from "./chunk-2RRBMNBQ.js";
|
|
12
|
+
import "./chunk-2BSKD7O3.js";
|
|
13
|
+
import "./chunk-MLKGABMK.js";
|
|
14
|
+
export {
|
|
15
|
+
PROVIDERS,
|
|
16
|
+
fingerprintKey,
|
|
17
|
+
fixConfigMode,
|
|
18
|
+
hasEmbeddingConfig,
|
|
19
|
+
mapValidationError,
|
|
20
|
+
maybeRunFirstRunSetup,
|
|
21
|
+
runSetupWizard,
|
|
22
|
+
saveEmbeddingConfig,
|
|
23
|
+
validateKey
|
|
24
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
searchSymbols,
|
|
3
|
+
symbolSearchPhases
|
|
4
|
+
} from "./chunk-AHDWGM7Q.js";
|
|
5
|
+
import "./chunk-3CTFIJJB.js";
|
|
6
|
+
import "./chunk-QLK6ED4Q.js";
|
|
7
|
+
import "./chunk-QKTRECYS.js";
|
|
8
|
+
import "./chunk-2BSKD7O3.js";
|
|
9
|
+
import "./chunk-SM7RFQRO.js";
|
|
10
|
+
import "./chunk-L5TDYKYK.js";
|
|
11
|
+
import "./chunk-ROMM6KMC.js";
|
|
12
|
+
import "./chunk-MLKGABMK.js";
|
|
13
|
+
export {
|
|
14
|
+
searchSymbols,
|
|
15
|
+
symbolSearchPhases
|
|
16
|
+
};
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import {
|
|
2
|
+
makeProvenance
|
|
3
|
+
} from "./chunk-UUNHHX4M.js";
|
|
4
|
+
import {
|
|
5
|
+
BrainDatabase
|
|
6
|
+
} from "./chunk-QKTRECYS.js";
|
|
7
|
+
import "./chunk-2BSKD7O3.js";
|
|
8
|
+
import "./chunk-ROMM6KMC.js";
|
|
9
|
+
import "./chunk-MLKGABMK.js";
|
|
10
|
+
|
|
11
|
+
// src/intelligence/security/taint.ts
|
|
12
|
+
var ENGINE = "taint@intelligence";
|
|
13
|
+
var VULN_ALIASES = {
|
|
14
|
+
sqli: "sql_injection",
|
|
15
|
+
sql: "sql_injection",
|
|
16
|
+
"sql-injection": "sql_injection",
|
|
17
|
+
cmd_injection: "command_injection",
|
|
18
|
+
cmdi: "command_injection",
|
|
19
|
+
rce: "code_injection",
|
|
20
|
+
xss_reflected: "xss",
|
|
21
|
+
xss_stored: "xss",
|
|
22
|
+
path_traversal: "path_traversal",
|
|
23
|
+
lfi: "path_traversal",
|
|
24
|
+
pii: "pii_leak"
|
|
25
|
+
};
|
|
26
|
+
var VULN_LABELS = {
|
|
27
|
+
sql_injection: "SQL injection",
|
|
28
|
+
command_injection: "Command injection",
|
|
29
|
+
code_injection: "Code injection",
|
|
30
|
+
xss: "Cross-site scripting (XSS)",
|
|
31
|
+
path_traversal: "Path traversal",
|
|
32
|
+
ssrf: "Server-side request forgery (SSRF)",
|
|
33
|
+
open_redirect: "Open redirect",
|
|
34
|
+
weak_crypto: "Weak cryptography",
|
|
35
|
+
insecure_deserialization: "Insecure deserialization",
|
|
36
|
+
pii_leak: "PII leak"
|
|
37
|
+
};
|
|
38
|
+
function normalizeType(t) {
|
|
39
|
+
const key = (t ?? "unknown").toLowerCase();
|
|
40
|
+
return VULN_ALIASES[key] ?? key;
|
|
41
|
+
}
|
|
42
|
+
var SEVERITIES = ["critical", "high", "medium", "low", "unknown"];
|
|
43
|
+
function normalizeSeverity(value) {
|
|
44
|
+
const key = typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
45
|
+
return SEVERITIES.includes(key) ? key : "unknown";
|
|
46
|
+
}
|
|
47
|
+
function severityRank(s) {
|
|
48
|
+
return { critical: 0, high: 1, medium: 2, low: 3, unknown: 4 }[s];
|
|
49
|
+
}
|
|
50
|
+
function parseFlowPath(value) {
|
|
51
|
+
if (Array.isArray(value)) return value.filter(Boolean).map(String);
|
|
52
|
+
if (typeof value !== "string" || !value) return [];
|
|
53
|
+
const trimmed = value.trim();
|
|
54
|
+
try {
|
|
55
|
+
const parsed = JSON.parse(trimmed);
|
|
56
|
+
if (Array.isArray(parsed)) return parsed.filter(Boolean).map(String);
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
if (trimmed.includes("|||")) return trimmed.split("|||").filter(Boolean);
|
|
60
|
+
return trimmed ? [trimmed] : [];
|
|
61
|
+
}
|
|
62
|
+
async function analyzeTaintFlows(projectId, options = {}) {
|
|
63
|
+
const raw = await BrainDatabase.getRawInstance(projectId, { readonly: true });
|
|
64
|
+
const minConfidence = options.minConfidence ?? 0;
|
|
65
|
+
const includeMitigated = options.includeMitigated ?? true;
|
|
66
|
+
const includeUnresolved = options.includeUnresolved ?? true;
|
|
67
|
+
const maxResults = options.maxResults ?? 500;
|
|
68
|
+
const rows = raw.prepare(
|
|
69
|
+
"SELECT t.id, t.vulnerability_type, t.severity, t.source_type, t.sink_type, t.is_mitigated, t.flow_path, t.cross_language_boundaries, t.source_symbol_id, t.sink_symbol_id, src.name AS src_name, src.line_start AS src_line, srcf.relative_path AS src_file, srcf.language AS src_lang, snk.name AS snk_name, snk.line_start AS snk_line, snkf.relative_path AS snk_file, snkf.language AS snk_lang FROM brain_taint_flow t LEFT JOIN brain_symbol src ON src.id = t.source_symbol_id LEFT JOIN brain_file srcf ON srcf.id = src.file_id LEFT JOIN brain_symbol snk ON snk.id = t.sink_symbol_id LEFT JOIN brain_file snkf ON snkf.id = snk.file_id WHERE t.project_id = ? ORDER BY t.id"
|
|
70
|
+
).all(projectId);
|
|
71
|
+
const pathCache = new Array(rows.length);
|
|
72
|
+
const neededIds = /* @__PURE__ */ new Set();
|
|
73
|
+
for (let i = 0; i < rows.length; i++) {
|
|
74
|
+
const r = rows[i];
|
|
75
|
+
if (r.source_symbol_id) neededIds.add(r.source_symbol_id);
|
|
76
|
+
if (r.sink_symbol_id) neededIds.add(r.sink_symbol_id);
|
|
77
|
+
const path = parseFlowPath(r.flow_path);
|
|
78
|
+
pathCache[i] = path;
|
|
79
|
+
for (const id of path) neededIds.add(id);
|
|
80
|
+
}
|
|
81
|
+
const symById = /* @__PURE__ */ new Map();
|
|
82
|
+
const ids = [...neededIds];
|
|
83
|
+
const CHUNK = 500;
|
|
84
|
+
for (let i = 0; i < ids.length; i += CHUNK) {
|
|
85
|
+
const chunk = ids.slice(i, i + CHUNK);
|
|
86
|
+
const stmt = "SELECT s.id, s.project_id, s.name, s.line_start AS line, f.relative_path AS file FROM brain_symbol s LEFT JOIN brain_file f ON f.id = s.file_id WHERE s.id IN (" + chunk.map(() => "?").join(",") + ")";
|
|
87
|
+
for (const s of raw.prepare(stmt).all(...chunk)) {
|
|
88
|
+
if (s.project_id !== projectId) continue;
|
|
89
|
+
symById.set(s.id, { name: s.name, file: s.file, line: s.line });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const resolveNode = (symbolId) => {
|
|
93
|
+
if (!symbolId) return { symbolId: null, name: null, file: null, line: null };
|
|
94
|
+
const meta = symById.get(symbolId);
|
|
95
|
+
if (meta) return { symbolId, name: meta.name, file: meta.file, line: meta.line };
|
|
96
|
+
const parts = symbolId.split(":");
|
|
97
|
+
return { symbolId, name: parts.length >= 3 ? parts[parts.length - 2] : symbolId, file: null, line: null };
|
|
98
|
+
};
|
|
99
|
+
const findingByKey = /* @__PURE__ */ new Map();
|
|
100
|
+
let scannedFlows = 0;
|
|
101
|
+
for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
|
|
102
|
+
const row = rows[rowIndex];
|
|
103
|
+
scannedFlows++;
|
|
104
|
+
const vulnerabilityType = normalizeType(row.vulnerability_type);
|
|
105
|
+
const severity = normalizeSeverity(row.severity);
|
|
106
|
+
const source = resolveNode(row.source_symbol_id);
|
|
107
|
+
const sink = resolveNode(row.sink_symbol_id);
|
|
108
|
+
const path = (pathCache[rowIndex] ?? []).map(resolveNode);
|
|
109
|
+
const mitigated = !!row.is_mitigated;
|
|
110
|
+
const sourceResolved = !!row.source_symbol_id && !!source.file;
|
|
111
|
+
const sinkResolved = !!row.sink_symbol_id && !!sink.file;
|
|
112
|
+
const state = !sinkResolved ? "source_only" : !sourceResolved ? "unresolved" : mitigated ? "mitigated" : "confirmed";
|
|
113
|
+
if (!includeMitigated && state === "mitigated") continue;
|
|
114
|
+
if (!includeUnresolved && (state === "unresolved" || state === "source_only")) continue;
|
|
115
|
+
const confidence = state === "confirmed" ? 0.7 : state === "mitigated" ? 0.3 : 0.4;
|
|
116
|
+
if (confidence < minConfidence) continue;
|
|
117
|
+
const file = sink.file ?? source.file;
|
|
118
|
+
const line = sink.line ?? source.line;
|
|
119
|
+
const symbol = sink.name ?? source.name;
|
|
120
|
+
const language = row.snk_lang ?? row.src_lang ?? null;
|
|
121
|
+
const key = `${vulnerabilityType}|${row.sink_symbol_id ?? ""}|${row.sink_type ?? ""}|${file ?? ""}:${line ?? ""}|${state}`;
|
|
122
|
+
const existing = findingByKey.get(key);
|
|
123
|
+
if (existing) {
|
|
124
|
+
if (path.length > existing.path.length) existing.path = path;
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
findingByKey.set(key, {
|
|
128
|
+
id: row.id,
|
|
129
|
+
vulnerabilityType,
|
|
130
|
+
label: VULN_LABELS[vulnerabilityType] ?? vulnerabilityType,
|
|
131
|
+
severity,
|
|
132
|
+
confidence,
|
|
133
|
+
state,
|
|
134
|
+
file,
|
|
135
|
+
line,
|
|
136
|
+
symbol,
|
|
137
|
+
language,
|
|
138
|
+
sourceType: row.source_type ?? null,
|
|
139
|
+
sinkType: row.sink_type ?? null,
|
|
140
|
+
source,
|
|
141
|
+
sink,
|
|
142
|
+
path,
|
|
143
|
+
crossLanguage: (row.cross_language_boundaries ?? 0) > 0,
|
|
144
|
+
mitigated,
|
|
145
|
+
evidence: [
|
|
146
|
+
{ kind: "taint_flow", description: `${row.source_type ?? "source"} -> ${row.sink_type ?? "sink"} taint flow (${state})` }
|
|
147
|
+
],
|
|
148
|
+
limitations: [
|
|
149
|
+
"Flows are indexer heuristics; parameterized/escaped sinks may still be reported.",
|
|
150
|
+
state === "source_only" ? "Source-only observation: no confirmed sink flow." : "",
|
|
151
|
+
state === "unresolved" ? "One endpoint could not be resolved to an indexed symbol." : "",
|
|
152
|
+
state === "mitigated" && (!sourceResolved || !sinkResolved) ? "Mitigation flag is not trusted until both endpoints resolve." : ""
|
|
153
|
+
].filter(Boolean)
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
let findings = Array.from(findingByKey.values());
|
|
157
|
+
findings.sort(
|
|
158
|
+
(a, b) => severityRank(a.severity) - severityRank(b.severity) || b.confidence - a.confidence || (a.file ?? "").localeCompare(b.file ?? "") || a.id.localeCompare(b.id)
|
|
159
|
+
);
|
|
160
|
+
const deduplicatedFlows = findings.length;
|
|
161
|
+
const truncatedFindings = Math.max(0, deduplicatedFlows - maxResults);
|
|
162
|
+
findings = findings.slice(0, maxResults);
|
|
163
|
+
const bySeverity = {};
|
|
164
|
+
const byType = {};
|
|
165
|
+
const byState = { confirmed: 0, mitigated: 0, unresolved: 0, source_only: 0 };
|
|
166
|
+
let crossLanguage = 0;
|
|
167
|
+
for (const f of findings) {
|
|
168
|
+
bySeverity[f.severity] = (bySeverity[f.severity] ?? 0) + 1;
|
|
169
|
+
byType[f.vulnerabilityType] = (byType[f.vulnerabilityType] ?? 0) + 1;
|
|
170
|
+
byState[f.state]++;
|
|
171
|
+
if (f.crossLanguage) crossLanguage++;
|
|
172
|
+
}
|
|
173
|
+
const fileCounts = /* @__PURE__ */ new Map();
|
|
174
|
+
const symCounts = /* @__PURE__ */ new Map();
|
|
175
|
+
const worse = (a, b) => severityRank(a) <= severityRank(b) ? a : b;
|
|
176
|
+
for (const f of findings) {
|
|
177
|
+
if (f.file) {
|
|
178
|
+
const cur = fileCounts.get(f.file) ?? { n: 0, sev: "unknown" };
|
|
179
|
+
fileCounts.set(f.file, { n: cur.n + 1, sev: worse(cur.sev, f.severity) });
|
|
180
|
+
}
|
|
181
|
+
if (f.symbol) {
|
|
182
|
+
const key = f.file ? `${f.file}::${f.symbol}` : `::${f.symbol}`;
|
|
183
|
+
const cur = symCounts.get(key) ?? { n: 0, sev: "unknown", name: f.symbol };
|
|
184
|
+
symCounts.set(key, { n: cur.n + 1, sev: worse(cur.sev, f.severity), name: cur.name });
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
const hotspots = [
|
|
188
|
+
...Array.from(fileCounts.entries()).map(([key, v]) => ({ key, kind: "file", findings: v.n, maxSeverity: v.sev })),
|
|
189
|
+
// The KEY is the qualified form (that is the identity); `name` carries the
|
|
190
|
+
// bare symbol for a reader who wants to search on it.
|
|
191
|
+
...Array.from(symCounts.entries()).map(([key, v]) => ({ key, kind: "symbol", name: v.name, findings: v.n, maxSeverity: v.sev }))
|
|
192
|
+
].sort((a, b) => b.findings - a.findings || severityRank(a.maxSeverity) - severityRank(b.maxSeverity)).slice(0, 20);
|
|
193
|
+
return {
|
|
194
|
+
findings,
|
|
195
|
+
summary: { total: findings.length, bySeverity, byType, byState, crossLanguage, scannedFlows, deduplicatedFlows, truncatedFindings },
|
|
196
|
+
hotspots,
|
|
197
|
+
provenance: makeProvenance({ evidenceClass: "static_finding", engine: ENGINE, engineVersion: "m10.02", projectId, source: "brain_taint_flow", limitations: ["Static taint flows can contain false positives and false negatives and are not runtime exploit proofs."], truncated: truncatedFindings > 0, unknowns: truncatedFindings > 0 ? [`${truncatedFindings} deduplicated finding(s) omitted by the maxResults cap (${maxResults}); summary counts describe the returned window, deduplicatedFlows is the pre-cap census`] : [] })
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
export {
|
|
201
|
+
analyzeTaintFlows
|
|
202
|
+
};
|