gitnexus 1.6.8-rc.9 → 1.6.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (430) hide show
  1. package/README.md +3 -3
  2. package/dist/_shared/graph/types.d.ts +31 -1
  3. package/dist/_shared/graph/types.d.ts.map +1 -1
  4. package/dist/_shared/language-detection.d.ts.map +1 -1
  5. package/dist/_shared/language-detection.js +11 -1
  6. package/dist/_shared/language-detection.js.map +1 -1
  7. package/dist/_shared/lbug/schema-constants.d.ts +1 -1
  8. package/dist/_shared/lbug/schema-constants.d.ts.map +1 -1
  9. package/dist/_shared/lbug/schema-constants.js +6 -0
  10. package/dist/_shared/lbug/schema-constants.js.map +1 -1
  11. package/dist/_shared/scope-resolution/parsed-file.d.ts +21 -0
  12. package/dist/_shared/scope-resolution/parsed-file.d.ts.map +1 -1
  13. package/dist/_shared/scope-resolution/symbol-definition.d.ts +6 -0
  14. package/dist/_shared/scope-resolution/symbol-definition.d.ts.map +1 -1
  15. package/dist/cli/ai-context.d.ts +31 -1
  16. package/dist/cli/ai-context.js +18 -16
  17. package/dist/cli/analyze-config.js +11 -0
  18. package/dist/cli/analyze.d.ts +14 -0
  19. package/dist/cli/analyze.js +98 -0
  20. package/dist/cli/embedding-dims.d.ts +33 -0
  21. package/dist/cli/embedding-dims.js +39 -0
  22. package/dist/cli/eval-server.d.ts +8 -0
  23. package/dist/cli/eval-server.js +242 -2
  24. package/dist/cli/help-i18n.js +19 -0
  25. package/dist/cli/i18n/en.d.ts +15 -1
  26. package/dist/cli/i18n/en.js +15 -1
  27. package/dist/cli/i18n/resources.d.ts +29 -1
  28. package/dist/cli/i18n/zh-CN.d.ts +14 -0
  29. package/dist/cli/i18n/zh-CN.js +15 -1
  30. package/dist/cli/index.js +89 -1
  31. package/dist/cli/mcp.d.ts +6 -1
  32. package/dist/cli/mcp.js +26 -1
  33. package/dist/cli/optional-grammars.d.ts +6 -8
  34. package/dist/cli/optional-grammars.js +8 -11
  35. package/dist/cli/setup.d.ts +3 -1
  36. package/dist/cli/setup.js +67 -17
  37. package/dist/cli/skill-gen.js +2 -1
  38. package/dist/cli/tool.d.ts +19 -1
  39. package/dist/cli/tool.js +96 -3
  40. package/dist/core/embeddings/hf-env.d.ts +0 -66
  41. package/dist/core/embeddings/http-client.d.ts +7 -0
  42. package/dist/core/embeddings/http-client.js +5 -3
  43. package/dist/core/embeddings/server-mapping.d.ts +1 -1
  44. package/dist/core/embeddings/server-mapping.js +7 -3
  45. package/dist/core/graph/import-cycles.d.ts +10 -0
  46. package/dist/core/graph/import-cycles.js +103 -0
  47. package/dist/core/group/cross-impact.d.ts +2 -1
  48. package/dist/core/group/cross-impact.js +5 -1
  49. package/dist/core/group/extractors/grpc-patterns/proto.js +10 -6
  50. package/dist/core/group/extractors/http-patterns/kotlin.js +7 -7
  51. package/dist/core/group/extractors/http-route-extractor.d.ts +24 -0
  52. package/dist/core/group/extractors/http-route-extractor.js +22 -4
  53. package/dist/core/group/extractors/include-extractor.js +22 -13
  54. package/dist/core/group/sync.d.ts +27 -1
  55. package/dist/core/group/sync.js +0 -0
  56. package/dist/core/incremental/subgraph-extract.js +18 -1
  57. package/dist/core/ingestion/cfg/cfg-builder.d.ts +114 -0
  58. package/dist/core/ingestion/cfg/cfg-builder.js +196 -0
  59. package/dist/core/ingestion/cfg/collect.d.ts +51 -0
  60. package/dist/core/ingestion/cfg/collect.js +77 -0
  61. package/dist/core/ingestion/cfg/control-dependence.d.ts +58 -0
  62. package/dist/core/ingestion/cfg/control-dependence.js +182 -0
  63. package/dist/core/ingestion/cfg/control-flow-context.d.ts +106 -0
  64. package/dist/core/ingestion/cfg/control-flow-context.js +124 -0
  65. package/dist/core/ingestion/cfg/emit.d.ts +293 -0
  66. package/dist/core/ingestion/cfg/emit.js +646 -0
  67. package/dist/core/ingestion/cfg/post-dominators.d.ts +79 -0
  68. package/dist/core/ingestion/cfg/post-dominators.js +176 -0
  69. package/dist/core/ingestion/cfg/reaching-def-reason-codec.d.ts +106 -0
  70. package/dist/core/ingestion/cfg/reaching-def-reason-codec.js +133 -0
  71. package/dist/core/ingestion/cfg/reaching-defs-graph.d.ts +18 -0
  72. package/dist/core/ingestion/cfg/reaching-defs-graph.js +312 -0
  73. package/dist/core/ingestion/cfg/reaching-defs.d.ts +162 -0
  74. package/dist/core/ingestion/cfg/reaching-defs.js +785 -0
  75. package/dist/core/ingestion/cfg/synthetic-escape.d.ts +124 -0
  76. package/dist/core/ingestion/cfg/synthetic-escape.js +300 -0
  77. package/dist/core/ingestion/cfg/traversal-result.d.ts +20 -0
  78. package/dist/core/ingestion/cfg/traversal-result.js +2 -0
  79. package/dist/core/ingestion/cfg/types.d.ts +274 -0
  80. package/dist/core/ingestion/cfg/types.js +13 -0
  81. package/dist/core/ingestion/cfg/visitors/c-cpp-harvest.d.ts +111 -0
  82. package/dist/core/ingestion/cfg/visitors/c-cpp-harvest.js +541 -0
  83. package/dist/core/ingestion/cfg/visitors/c-cpp.d.ts +74 -0
  84. package/dist/core/ingestion/cfg/visitors/c-cpp.js +584 -0
  85. package/dist/core/ingestion/cfg/visitors/call-site-harvest.d.ts +195 -0
  86. package/dist/core/ingestion/cfg/visitors/call-site-harvest.js +350 -0
  87. package/dist/core/ingestion/cfg/visitors/csharp-harvest.d.ts +124 -0
  88. package/dist/core/ingestion/cfg/visitors/csharp-harvest.js +589 -0
  89. package/dist/core/ingestion/cfg/visitors/csharp.d.ts +87 -0
  90. package/dist/core/ingestion/cfg/visitors/csharp.js +867 -0
  91. package/dist/core/ingestion/cfg/visitors/dart-harvest.d.ts +278 -0
  92. package/dist/core/ingestion/cfg/visitors/dart-harvest.js +870 -0
  93. package/dist/core/ingestion/cfg/visitors/dart.d.ts +119 -0
  94. package/dist/core/ingestion/cfg/visitors/dart.js +836 -0
  95. package/dist/core/ingestion/cfg/visitors/go-harvest.d.ts +160 -0
  96. package/dist/core/ingestion/cfg/visitors/go-harvest.js +621 -0
  97. package/dist/core/ingestion/cfg/visitors/go.d.ts +108 -0
  98. package/dist/core/ingestion/cfg/visitors/go.js +638 -0
  99. package/dist/core/ingestion/cfg/visitors/java-harvest.d.ts +108 -0
  100. package/dist/core/ingestion/cfg/visitors/java-harvest.js +513 -0
  101. package/dist/core/ingestion/cfg/visitors/java.d.ts +101 -0
  102. package/dist/core/ingestion/cfg/visitors/java.js +812 -0
  103. package/dist/core/ingestion/cfg/visitors/kotlin-harvest.d.ts +253 -0
  104. package/dist/core/ingestion/cfg/visitors/kotlin-harvest.js +719 -0
  105. package/dist/core/ingestion/cfg/visitors/kotlin.d.ts +121 -0
  106. package/dist/core/ingestion/cfg/visitors/kotlin.js +809 -0
  107. package/dist/core/ingestion/cfg/visitors/php-harvest.d.ts +172 -0
  108. package/dist/core/ingestion/cfg/visitors/php-harvest.js +626 -0
  109. package/dist/core/ingestion/cfg/visitors/php.d.ts +96 -0
  110. package/dist/core/ingestion/cfg/visitors/php.js +721 -0
  111. package/dist/core/ingestion/cfg/visitors/python-harvest.d.ts +212 -0
  112. package/dist/core/ingestion/cfg/visitors/python-harvest.js +739 -0
  113. package/dist/core/ingestion/cfg/visitors/python.d.ts +103 -0
  114. package/dist/core/ingestion/cfg/visitors/python.js +558 -0
  115. package/dist/core/ingestion/cfg/visitors/ruby-harvest.d.ts +223 -0
  116. package/dist/core/ingestion/cfg/visitors/ruby-harvest.js +587 -0
  117. package/dist/core/ingestion/cfg/visitors/ruby.d.ts +112 -0
  118. package/dist/core/ingestion/cfg/visitors/ruby.js +756 -0
  119. package/dist/core/ingestion/cfg/visitors/rust-harvest.d.ts +324 -0
  120. package/dist/core/ingestion/cfg/visitors/rust-harvest.js +873 -0
  121. package/dist/core/ingestion/cfg/visitors/rust.d.ts +95 -0
  122. package/dist/core/ingestion/cfg/visitors/rust.js +558 -0
  123. package/dist/core/ingestion/cfg/visitors/scope-tree-harvest.d.ts +87 -0
  124. package/dist/core/ingestion/cfg/visitors/scope-tree-harvest.js +116 -0
  125. package/dist/core/ingestion/cfg/visitors/swift-harvest.d.ts +255 -0
  126. package/dist/core/ingestion/cfg/visitors/swift-harvest.js +679 -0
  127. package/dist/core/ingestion/cfg/visitors/swift.d.ts +116 -0
  128. package/dist/core/ingestion/cfg/visitors/swift.js +787 -0
  129. package/dist/core/ingestion/cfg/visitors/typescript-harvest.d.ts +149 -0
  130. package/dist/core/ingestion/cfg/visitors/typescript-harvest.js +1056 -0
  131. package/dist/core/ingestion/cfg/visitors/typescript.d.ts +66 -0
  132. package/dist/core/ingestion/cfg/visitors/typescript.js +583 -0
  133. package/dist/core/ingestion/import-resolvers/utils.js +2 -0
  134. package/dist/core/ingestion/language-provider.d.ts +11 -0
  135. package/dist/core/ingestion/languages/c-cpp.js +6 -1
  136. package/dist/core/ingestion/languages/cobol.js +4 -0
  137. package/dist/core/ingestion/languages/cpp/arity-metadata.js +41 -2
  138. package/dist/core/ingestion/languages/cpp/captures.js +50 -1
  139. package/dist/core/ingestion/languages/cpp/conversion-rank.d.ts +2 -0
  140. package/dist/core/ingestion/languages/cpp/conversion-rank.js +89 -0
  141. package/dist/core/ingestion/languages/cpp/header-scan.d.ts +1 -1
  142. package/dist/core/ingestion/languages/cpp/header-scan.js +2 -2
  143. package/dist/core/ingestion/languages/cpp/inline-namespaces.js +7 -2
  144. package/dist/core/ingestion/languages/cpp/member-lookup.js +2 -1
  145. package/dist/core/ingestion/languages/cpp/query.js +23 -0
  146. package/dist/core/ingestion/languages/cpp/scope-resolver.js +2 -1
  147. package/dist/core/ingestion/languages/csharp.js +2 -0
  148. package/dist/core/ingestion/languages/dart.js +2 -0
  149. package/dist/core/ingestion/languages/go.js +2 -0
  150. package/dist/core/ingestion/languages/java.js +3 -0
  151. package/dist/core/ingestion/languages/kotlin/query.js +3 -2
  152. package/dist/core/ingestion/languages/kotlin.js +3 -0
  153. package/dist/core/ingestion/languages/php.js +2 -0
  154. package/dist/core/ingestion/languages/python.js +2 -0
  155. package/dist/core/ingestion/languages/ruby.js +2 -0
  156. package/dist/core/ingestion/languages/rust.js +2 -0
  157. package/dist/core/ingestion/languages/swift.js +2 -0
  158. package/dist/core/ingestion/languages/typescript.js +5 -0
  159. package/dist/core/ingestion/languages/vue.js +6 -0
  160. package/dist/core/ingestion/method-extractors/configs/c-cpp.js +6 -13
  161. package/dist/core/ingestion/method-extractors/generic.js +1 -0
  162. package/dist/core/ingestion/method-types.d.ts +2 -0
  163. package/dist/core/ingestion/model/symbol-table.d.ts +1 -0
  164. package/dist/core/ingestion/model/symbol-table.js +1 -0
  165. package/dist/core/ingestion/mro-processor.js +109 -0
  166. package/dist/core/ingestion/parsing-processor.js +23 -0
  167. package/dist/core/ingestion/pipeline-phases/call-summaries.d.ts +26 -0
  168. package/dist/core/ingestion/pipeline-phases/call-summaries.js +46 -0
  169. package/dist/core/ingestion/pipeline-phases/index.d.ts +2 -0
  170. package/dist/core/ingestion/pipeline-phases/index.js +2 -0
  171. package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +0 -21
  172. package/dist/core/ingestion/pipeline-phases/parse-impl.js +18 -1
  173. package/dist/core/ingestion/pipeline-phases/routes.d.ts +10 -0
  174. package/dist/core/ingestion/pipeline-phases/routes.js +30 -1
  175. package/dist/core/ingestion/pipeline-phases/taint-summaries.d.ts +30 -0
  176. package/dist/core/ingestion/pipeline-phases/taint-summaries.js +81 -0
  177. package/dist/core/ingestion/pipeline.d.ts +92 -10
  178. package/dist/core/ingestion/pipeline.js +11 -2
  179. package/dist/core/ingestion/scope-extractor.js +12 -2
  180. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +9 -0
  181. package/dist/core/ingestion/scope-resolution/graph-bridge/callee-id-sink.d.ts +75 -0
  182. package/dist/core/ingestion/scope-resolution/graph-bridge/callee-id-sink.js +67 -0
  183. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.d.ts +12 -2
  184. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.js +14 -2
  185. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.d.ts +6 -1
  186. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +13 -1
  187. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +9 -1
  188. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +28 -2
  189. package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.d.ts +4 -0
  190. package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.js +9 -0
  191. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +6 -1
  192. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +174 -40
  193. package/dist/core/ingestion/scope-resolution/pipeline/phase.d.ts +24 -0
  194. package/dist/core/ingestion/scope-resolution/pipeline/phase.js +276 -190
  195. package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +43 -3
  196. package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +75 -0
  197. package/dist/core/ingestion/scope-resolution/pipeline/run.js +349 -1
  198. package/dist/core/ingestion/scope-resolution/resolution-outcome.d.ts +1 -1
  199. package/dist/core/ingestion/taint/call-summary-codec.d.ts +75 -0
  200. package/dist/core/ingestion/taint/call-summary-codec.js +128 -0
  201. package/dist/core/ingestion/taint/call-summary-emit.d.ts +43 -0
  202. package/dist/core/ingestion/taint/call-summary-emit.js +70 -0
  203. package/dist/core/ingestion/taint/call-summary-harvest.d.ts +77 -0
  204. package/dist/core/ingestion/taint/call-summary-harvest.js +179 -0
  205. package/dist/core/ingestion/taint/call-summary-model.d.ts +47 -0
  206. package/dist/core/ingestion/taint/call-summary-model.js +25 -0
  207. package/dist/core/ingestion/taint/emit.d.ts +125 -0
  208. package/dist/core/ingestion/taint/emit.js +208 -0
  209. package/dist/core/ingestion/taint/interproc-emit.d.ts +50 -0
  210. package/dist/core/ingestion/taint/interproc-emit.js +88 -0
  211. package/dist/core/ingestion/taint/interproc-solver.d.ts +110 -0
  212. package/dist/core/ingestion/taint/interproc-solver.js +312 -0
  213. package/dist/core/ingestion/taint/match.d.ts +153 -0
  214. package/dist/core/ingestion/taint/match.js +278 -0
  215. package/dist/core/ingestion/taint/path-codec.d.ts +134 -0
  216. package/dist/core/ingestion/taint/path-codec.js +190 -0
  217. package/dist/core/ingestion/taint/propagate.d.ts +216 -0
  218. package/dist/core/ingestion/taint/propagate.js +659 -0
  219. package/dist/core/ingestion/taint/site-safety.d.ts +29 -0
  220. package/dist/core/ingestion/taint/site-safety.js +98 -0
  221. package/dist/core/ingestion/taint/source-sink-config.d.ts +107 -23
  222. package/dist/core/ingestion/taint/source-sink-config.js +35 -12
  223. package/dist/core/ingestion/taint/source-sink-registry.d.ts +6 -4
  224. package/dist/core/ingestion/taint/source-sink-registry.js +6 -4
  225. package/dist/core/ingestion/taint/summary-harvest-driver.d.ts +80 -0
  226. package/dist/core/ingestion/taint/summary-harvest-driver.js +179 -0
  227. package/dist/core/ingestion/taint/summary-harvest.d.ts +89 -0
  228. package/dist/core/ingestion/taint/summary-harvest.js +537 -0
  229. package/dist/core/ingestion/taint/summary-model.d.ts +200 -0
  230. package/dist/core/ingestion/taint/summary-model.js +86 -0
  231. package/dist/core/ingestion/taint/typescript-model.d.ts +38 -0
  232. package/dist/core/ingestion/taint/typescript-model.js +102 -0
  233. package/dist/core/ingestion/type-extractors/c-cpp.js +21 -0
  234. package/dist/core/ingestion/utils/env.d.ts +7 -0
  235. package/dist/core/ingestion/utils/env.js +12 -0
  236. package/dist/core/ingestion/utils/method-props.js +1 -0
  237. package/dist/core/ingestion/workers/parse-worker.d.ts +13 -0
  238. package/dist/core/ingestion/workers/parse-worker.js +76 -17
  239. package/dist/core/ingestion/workers/worker-pool.d.ts +9 -40
  240. package/dist/core/ingestion/workers/worker-pool.js +15 -2
  241. package/dist/core/lbug/csv-generator.d.ts +51 -3
  242. package/dist/core/lbug/csv-generator.js +369 -255
  243. package/dist/core/lbug/lbug-adapter.d.ts +58 -18
  244. package/dist/core/lbug/lbug-adapter.js +346 -84
  245. package/dist/core/lbug/lbug-config.d.ts +19 -0
  246. package/dist/core/lbug/lbug-config.js +29 -0
  247. package/dist/core/lbug/pdg-emit-sink.d.ts +127 -0
  248. package/dist/core/lbug/pdg-emit-sink.js +358 -0
  249. package/dist/core/lbug/pool-adapter.d.ts +35 -0
  250. package/dist/core/lbug/pool-adapter.js +90 -1
  251. package/dist/core/lbug/rel-pair-routing.d.ts +54 -0
  252. package/dist/core/lbug/rel-pair-routing.js +140 -0
  253. package/dist/core/lbug/schema.d.ts +2 -2
  254. package/dist/core/lbug/schema.js +3 -0
  255. package/dist/core/logger.d.ts +0 -5
  256. package/dist/core/run-analyze.d.ts +96 -0
  257. package/dist/core/run-analyze.js +207 -3
  258. package/dist/core/tree-sitter/parser-loader.js +5 -4
  259. package/dist/core/tree-sitter/safe-parse.d.ts +0 -5
  260. package/dist/core/tree-sitter/vendored-grammars.d.ts +39 -0
  261. package/dist/core/tree-sitter/vendored-grammars.js +57 -0
  262. package/dist/core/wiki/generator.js +3 -1
  263. package/dist/core/wiki/graph-queries.d.ts +6 -0
  264. package/dist/core/wiki/graph-queries.js +9 -1
  265. package/dist/mcp/http-transport.d.ts +134 -0
  266. package/dist/mcp/http-transport.js +503 -0
  267. package/dist/mcp/local/local-backend.d.ts +110 -24
  268. package/dist/mcp/local/local-backend.js +1225 -19
  269. package/dist/mcp/local/pdg-impact.d.ts +384 -0
  270. package/dist/mcp/local/pdg-impact.js +1972 -0
  271. package/dist/mcp/resources.js +13 -1
  272. package/dist/mcp/tools.d.ts +12 -0
  273. package/dist/mcp/tools.js +214 -7
  274. package/dist/server/analyze-launch.d.ts +29 -0
  275. package/dist/server/analyze-launch.js +137 -0
  276. package/dist/server/analyze-upload.d.ts +33 -0
  277. package/dist/server/analyze-upload.js +123 -0
  278. package/dist/server/analyze-worker.d.ts +17 -1
  279. package/dist/server/api.d.ts +17 -0
  280. package/dist/server/api.js +132 -168
  281. package/dist/server/git-clone.d.ts +43 -2
  282. package/dist/server/git-clone.js +211 -16
  283. package/dist/server/mcp-http.d.ts +5 -6
  284. package/dist/server/mcp-http.js +9 -82
  285. package/dist/server/middleware.d.ts +40 -0
  286. package/dist/server/middleware.js +92 -0
  287. package/dist/server/private-ip.d.ts +1 -0
  288. package/dist/server/private-ip.js +15 -0
  289. package/dist/server/upload-ingest.d.ts +56 -0
  290. package/dist/server/upload-ingest.js +276 -0
  291. package/dist/server/upload-paths.d.ts +37 -0
  292. package/dist/server/upload-paths.js +57 -0
  293. package/dist/server/upload-sweep.d.ts +20 -0
  294. package/dist/server/upload-sweep.js +57 -0
  295. package/dist/storage/git.js +5 -8
  296. package/dist/storage/parse-cache.d.ts +22 -1
  297. package/dist/storage/parse-cache.js +41 -10
  298. package/dist/storage/repo-manager.d.ts +121 -10
  299. package/dist/storage/repo-manager.js +43 -5
  300. package/dist/types/pipeline.d.ts +9 -0
  301. package/hooks/antigravity/gitnexus-antigravity-hook.cjs +110 -10
  302. package/hooks/claude/gitnexus-hook.cjs +110 -13
  303. package/hooks/claude/hook-db-lock-probe.cjs +513 -26
  304. package/package.json +8 -2
  305. package/scripts/assert-publish-grammar-coverage.cjs +31 -0
  306. package/scripts/build-tree-sitter-grammars.cjs +16 -10
  307. package/scripts/cross-platform-tests.ts +1 -0
  308. package/skills/gitnexus-debugging.md +16 -4
  309. package/skills/gitnexus-exploring.md +3 -3
  310. package/skills/gitnexus-guide.md +33 -0
  311. package/skills/gitnexus-pdg-query.md +89 -0
  312. package/skills/gitnexus-refactoring.md +1 -1
  313. package/skills/gitnexus-taint-analysis.md +178 -0
  314. package/vendor/tree-sitter-c/package.json +1 -1
  315. package/vendor/tree-sitter-dart/package.json +1 -1
  316. package/vendor/tree-sitter-kotlin/package.json +1 -1
  317. package/vendor/tree-sitter-proto/package.json +1 -1
  318. package/vendor/tree-sitter-swift/package.json +1 -1
  319. package/web/assets/{agent-CKPMqImC.js → agent-D3Xd7zZE.js} +123 -123
  320. package/web/assets/architecture-7EHR7CIX-6QZW5X65-DxJw65fT.js +1 -0
  321. package/web/assets/architectureDiagram-UL44E2DR-ChH9xW-N.js +36 -0
  322. package/web/assets/{blockDiagram-7IZFK4PR-B4niSLvR.js → blockDiagram-7IZFK4PR-BkWhpzWB.js} +1 -1
  323. package/web/assets/{c4Diagram-Y2BXMSZH-DIzEXalR.js → c4Diagram-Y2BXMSZH-DtBvpzWD.js} +1 -1
  324. package/web/assets/{chunk-3SSMPTDK-BzGeeheI.js → chunk-3SSMPTDK-B4_etUhr.js} +2 -2
  325. package/web/assets/{chunk-5IMINLNL-CnTLsXIV.js → chunk-5IMINLNL-DOdTeQri.js} +1 -1
  326. package/web/assets/{chunk-5VCL7Z4A-w1BCx98Z.js → chunk-5VCL7Z4A-CQ-2dYWj.js} +1 -1
  327. package/web/assets/chunk-6764PJDD-DpIS7hxD.js +1 -0
  328. package/web/assets/{chunk-67TQ5CYL-CChC2cAv.js → chunk-67TQ5CYL-4sNPJU1q.js} +3 -3
  329. package/web/assets/{chunk-7W6UQGC5-CgFyfZ6k.js → chunk-7W6UQGC5-KyEG0HQg.js} +1 -1
  330. package/web/assets/{chunk-AZZRMDJM-5E9ARdDQ.js → chunk-AZZRMDJM-y-xJBuMz.js} +1 -1
  331. package/web/assets/{chunk-INKRHTLW-ELpfuk9h.js → chunk-INKRHTLW-Nu4ri9P-.js} +1 -1
  332. package/web/assets/{chunk-JQRUD6KW-Dcq_Qnyy.js → chunk-JQRUD6KW-Dbva2Z17.js} +1 -1
  333. package/web/assets/{chunk-KGFNY3KK-W-VR57xb.js → chunk-KGFNY3KK-JGMJvKCK.js} +1 -1
  334. package/web/assets/chunk-KGYTTC2M-CWC_c3H9.js +161 -0
  335. package/web/assets/{chunk-KNLZD3CH-BuZBOcso.js → chunk-KNLZD3CH-BGlP6X9R.js} +1 -1
  336. package/web/assets/{chunk-KRXBNO2N-DH2NPM7I.js → chunk-KRXBNO2N-BW5o0KUN.js} +1 -1
  337. package/web/assets/{chunk-LCXTWHL2-Dfmux4m1.js → chunk-LCXTWHL2-zoomRtWX.js} +1 -1
  338. package/web/assets/{chunk-LII3EMHJ-3X33tCpU.js → chunk-LII3EMHJ-D-jm-dLa.js} +1 -1
  339. package/web/assets/{chunk-QA3QBVWF-D5Ha3GgB.js → chunk-QA3QBVWF-BgRW3SXd.js} +1 -1
  340. package/web/assets/{chunk-RG4AUYOV-CAkzcoRj.js → chunk-RG4AUYOV-DD0B2N2B.js} +1 -1
  341. package/web/assets/{chunk-T2UQINTJ-kGYG7ppP.js → chunk-T2UQINTJ-DSBqkzH7.js} +1 -1
  342. package/web/assets/chunk-T5OCTHI4-DdZvN-9i.js +1 -0
  343. package/web/assets/{chunk-UY5QBCOK-Du-c56gs.js → chunk-UY5QBCOK-DyF0vyGd.js} +1 -1
  344. package/web/assets/{chunk-W44A43WB-lrCpuupA.js → chunk-W44A43WB-JqMLIpR0.js} +1 -1
  345. package/web/assets/{chunk-ZXARS5L4-CSZRiyOf.js → chunk-ZXARS5L4-CxN8oiwI.js} +1 -1
  346. package/web/assets/classDiagram-KGZ6W3CR-DJm9BgF-.js +1 -0
  347. package/web/assets/classDiagram-v2-72OJOZXJ-DJm9BgF-.js +1 -0
  348. package/web/assets/{context-builder-_HS0v2ma.js → context-builder-BHiFUA8O.js} +10 -2
  349. package/web/assets/{cose-bilkent-UX7MHV2Q-BHv2bbtl.js → cose-bilkent-UX7MHV2Q-D71wNYRJ.js} +1 -1
  350. package/web/assets/dagre-ND4H6XIP-BIkxmhcu.js +4 -0
  351. package/web/assets/diagram-3NCE3AQN-BTa6iH0T.js +43 -0
  352. package/web/assets/diagram-GF46GFSD-DZDV0vBj.js +24 -0
  353. package/web/assets/{diagram-HNR7UZ2L-fUWreZzx.js → diagram-HNR7UZ2L-DFCfd5LI.js} +2 -2
  354. package/web/assets/diagram-QXG6HAR7-BzM--Jj0.js +24 -0
  355. package/web/assets/diagram-WEQXMOUZ-DFFwz0k4.js +10 -0
  356. package/web/assets/{erDiagram-L5TCEMPS-QN2eEP1e.js → erDiagram-L5TCEMPS-CfHMJJqo.js} +1 -1
  357. package/web/assets/eventmodeling-FCH6USID-MREXMVOE-BQm9QzEa.js +1 -0
  358. package/web/assets/{flowDiagram-H6V6AXG4-CXbXImlN.js → flowDiagram-H6V6AXG4-Be805QiJ.js} +3 -3
  359. package/web/assets/{ganttDiagram-JCBTUEKG-BHQssTz6.js → ganttDiagram-JCBTUEKG-D3iL2Aet.js} +1 -1
  360. package/web/assets/gitGraph-WXDBUCRP-R675I2BI-Cr_Bm2Nb.js +1 -0
  361. package/web/assets/gitGraphDiagram-S2ZK5IYY-CPIDgxGm.js +106 -0
  362. package/web/assets/index-BvYev1UJ.js +635 -0
  363. package/web/assets/index-_lgn7hs5.css +2 -0
  364. package/web/assets/info-J43DQDTF-KCYPFFUO-Cs4p2oyk.js +1 -0
  365. package/web/assets/infoDiagram-3YFTVSEB-Y7BKSoke.js +2 -0
  366. package/web/assets/{ishikawaDiagram-BNXS4ZKH-6LKOvBfp.js → ishikawaDiagram-BNXS4ZKH-CT4SHFuI.js} +1 -1
  367. package/web/assets/{journeyDiagram-M6C3CM3L-DNPYCpvq.js → journeyDiagram-M6C3CM3L-CvArszLo.js} +1 -1
  368. package/web/assets/{kanban-definition-75IXJCU3-DgDi9oJT.js → kanban-definition-75IXJCU3-C6yDxr7N.js} +3 -3
  369. package/web/assets/{katex-K3KEBU37-CrVLz8l7.js → katex-K3KEBU37-CTc5BslQ.js} +1 -1
  370. package/web/assets/{mindmap-definition-2TDM6QVE-CwR5sBB-.js → mindmap-definition-2TDM6QVE-BOc2WEne.js} +1 -1
  371. package/web/assets/packet-YPE3B663-LP52Z2RK-CKCrztD2.js +1 -0
  372. package/web/assets/pie-LRSECV5Y-TCRJHUBD-BB7pHqoD.js +1 -0
  373. package/web/assets/{pieDiagram-CU6KROY3-By8g6f6B.js → pieDiagram-CU6KROY3-Cvc0f_Pd.js} +1 -1
  374. package/web/assets/{quadrantDiagram-VICAPDV7-bAm6Hi_5.js → quadrantDiagram-VICAPDV7-yXSZ2lLz.js} +1 -1
  375. package/web/assets/radar-GUYGQ44K-RDLRG3WG-DhoTOcuK.js +1 -0
  376. package/web/assets/{requirementDiagram-JXO7QTGE-7oDcJ1_J.js → requirementDiagram-JXO7QTGE-BRcZvHs4.js} +1 -1
  377. package/web/assets/{sankeyDiagram-URQDO5SZ-LFJz53u5.js → sankeyDiagram-URQDO5SZ-1qEjlFIB.js} +1 -1
  378. package/web/assets/{sequenceDiagram-VS2MUI6T-FySeKCUy.js → sequenceDiagram-VS2MUI6T-PcSCuvrB.js} +3 -3
  379. package/web/assets/stateDiagram-7D4R322I-D6qYSV1E.js +1 -0
  380. package/web/assets/stateDiagram-v2-36443NZ5-Bg-ImOSX.js +1 -0
  381. package/web/assets/{timeline-definition-O6YCAMPW-Ds2CnVZK.js → timeline-definition-O6YCAMPW-QWkh01xw.js} +1 -1
  382. package/web/assets/treeView-BLDUP644-QA4HXRO3-ODehyGKL.js +1 -0
  383. package/web/assets/treemap-LRROVOQU-LLAWBHMP-BfpgC-7o.js +1 -0
  384. package/web/assets/{vennDiagram-MWXL3ELB-DUIEwXWp.js → vennDiagram-MWXL3ELB-ak4DfwuM.js} +3 -3
  385. package/web/assets/wardley-L42UT6IY-5TKZOOLJ-CNeL6VHE.js +1 -0
  386. package/web/assets/{wardleyDiagram-CUQ6CDDI-DEiFPQih.js → wardleyDiagram-CUQ6CDDI-CTzR4Loq.js} +1 -1
  387. package/web/assets/{xychartDiagram-N2JHSOCM-BUbayhST.js → xychartDiagram-N2JHSOCM-DA1BEaQz.js} +1 -1
  388. package/web/index.html +13 -15
  389. package/scripts/materialize-vendor-grammars.cjs +0 -97
  390. package/web/assets/architecture-7EHR7CIX-6QZW5X65-BETj2x4g.js +0 -1
  391. package/web/assets/architectureDiagram-UL44E2DR-D1EXI0zA.js +0 -36
  392. package/web/assets/chunk-2T2R6R2M-n6s9JZqv.js +0 -4
  393. package/web/assets/chunk-2UTLFMKG-CMBB1TMN.js +0 -1
  394. package/web/assets/chunk-4R4BOZG6-mKhL59ul.js +0 -159
  395. package/web/assets/chunk-6764PJDD-ChwMM2z4.js +0 -1
  396. package/web/assets/chunk-7J6CGLKN-DT-b53FT.js +0 -10
  397. package/web/assets/chunk-C62D2QBJ-CDAWj26E.js +0 -1
  398. package/web/assets/chunk-CEXFNPSA-D68Tk6ls.js +0 -1
  399. package/web/assets/chunk-CilyBKbf.js +0 -1
  400. package/web/assets/chunk-J5EP6P6S-DxWW0yvu.js +0 -1
  401. package/web/assets/chunk-KGYTTC2M-C6PHeuay.js +0 -1
  402. package/web/assets/chunk-RERM46MO-DQNbXtfw.js +0 -1
  403. package/web/assets/chunk-RKZBBQEN-BmTPLSyv.js +0 -1
  404. package/web/assets/chunk-RLI5ZMPA-DExu2DOK.js +0 -1
  405. package/web/assets/chunk-T5OCTHI4--9wWpVws.js +0 -1
  406. package/web/assets/chunk-UP6H54XL-DsKdC6jC.js +0 -1
  407. package/web/assets/chunk-UXSXWOXI-DR81EqLr.js +0 -1
  408. package/web/assets/classDiagram-KGZ6W3CR-Bv93af_b.js +0 -1
  409. package/web/assets/classDiagram-v2-72OJOZXJ-CLfEqHUa.js +0 -1
  410. package/web/assets/dagre-ND4H6XIP-DsIr9MOv.js +0 -4
  411. package/web/assets/diagram-3NCE3AQN-BqAtKUpW.js +0 -43
  412. package/web/assets/diagram-GF46GFSD-CaBG6n6o.js +0 -24
  413. package/web/assets/diagram-QXG6HAR7-CZ-O3rcV.js +0 -24
  414. package/web/assets/diagram-WEQXMOUZ-P4lSL4GH.js +0 -10
  415. package/web/assets/eventmodeling-FCH6USID-MREXMVOE-B3myHUst.js +0 -1
  416. package/web/assets/gitGraph-WXDBUCRP-R675I2BI-DF3rtf4A.js +0 -1
  417. package/web/assets/gitGraphDiagram-S2ZK5IYY-CCwVLT__.js +0 -106
  418. package/web/assets/index-BKWA-m7o.css +0 -2
  419. package/web/assets/index-CG6q8eTs.js +0 -626
  420. package/web/assets/info-J43DQDTF-KCYPFFUO-R4XV_akJ.js +0 -1
  421. package/web/assets/infoDiagram-3YFTVSEB-DmLICZx1.js +0 -2
  422. package/web/assets/packet-YPE3B663-LP52Z2RK-CgQPdNZa.js +0 -1
  423. package/web/assets/pie-LRSECV5Y-TCRJHUBD-BzWOwc_c.js +0 -1
  424. package/web/assets/radar-GUYGQ44K-RDLRG3WG-B7KG0FN7.js +0 -1
  425. package/web/assets/stateDiagram-7D4R322I-CB2nABwH.js +0 -1
  426. package/web/assets/stateDiagram-v2-36443NZ5-COBGd2RL.js +0 -1
  427. package/web/assets/treeView-BLDUP644-QA4HXRO3-CRSxEwLO.js +0 -1
  428. package/web/assets/treemap-LRROVOQU-LLAWBHMP-a0PfKoVn.js +0 -1
  429. package/web/assets/wardley-L42UT6IY-5TKZOOLJ-BKQ6e5mC.js +0 -1
  430. /package/web/assets/{chunk-AQ6EADP3-CCFje6lL.js → chunk-AQ6EADP3-CZhslHi-.js} +0 -0
@@ -0,0 +1,66 @@
1
+ /**
2
+ * TS/JS CfgVisitor (issue #2081, M1).
3
+ *
4
+ * Walks a TypeScript/JavaScript function's tree-sitter AST and drives the
5
+ * language-agnostic {@link CfgBuilder} to produce a serializable
6
+ * {@link FunctionCfg}. TS and JS share a grammar family (tree-sitter-typescript
7
+ * reuses tree-sitter-javascript's statement nodes), so one visitor covers both.
8
+ *
9
+ * Design — a `visit_<node_type>` dispatch over the statement taxonomy. The
10
+ * classic CFG hazards (R10) are handled explicitly:
11
+ * - loops allocate a dedicated **loop-exit** block so `break` has a concrete
12
+ * target before the loop's successor is known; `continue` targets the
13
+ * header/increment; the back-edge closes the loop.
14
+ * - `switch` cases fall through naturally: a case body that does not `break`
15
+ * yields non-empty `exits`, which we wire to the next case as `fallthrough`;
16
+ * a case that `break`s wires to the switch exit (via {@link ControlFlowContext})
17
+ * and yields no fall-out.
18
+ * - `try/catch/finally` routes both normal completion AND a `throw` in the try
19
+ * through `finally` (the finally block post-dominates the try/catch); a
20
+ * `throw` with no catch propagates through finally to the enclosing handler.
21
+ * - EARLY EXITS THROUGH FINALLY (#2082 M2 U2, closes the M1 soundness gap): a
22
+ * `break`/`continue`/`return` whose jump CROSSES a `finally` is re-routed to
23
+ * the finally entry (keeping its bare jump kind), and the finally's exits
24
+ * gain a `finally-return`/`finally-break`/`finally-continue` completion edge
25
+ * to the resumed target. Threading is TARGET-RELATIVE via finalizer frames
26
+ * interleaved on the {@link ControlFlowContext} stack: only the finallys
27
+ * lexically between the jump and its target thread (a `break` whose loop is
28
+ * wholly inside the try keeps its direct edge — re-routing it would let a
29
+ * finally redefinition falsely kill in-loop defs for reaching-defs). Nested
30
+ * finallys chain inner→outer; finally-as-shared-join conflates exit paths
31
+ * (sound over-approximation; duplication-per-exit-path was rejected). An
32
+ * empty/comment-only finally pushes no frame — jumps keep direct edges.
33
+ * - labeled `break`/`continue` resolve against the labeled construct's frame:
34
+ * loops/switches carry their full label LIST (`outer: inner: for` resolves
35
+ * both), and a labeled NON-loop statement (`blk: { … break blk; … }`) gets
36
+ * a break-target frame whose target is a synthesized join after the body —
37
+ * the M1 route-to-EXIT fallback removed the real continuation and falsely
38
+ * killed defs for reaching-defs (tri-review P1).
39
+ *
40
+ * Known limitations:
41
+ * - A jump whose label STILL fails to resolve (malformed source) keeps the
42
+ * conservative route-to-EXIT + thread-all-finallys fallback in
43
+ * visitBreak/visitContinue — single-exit preserved, no finally bypassed,
44
+ * but the continuation path is approximate.
45
+ * - Exceptional flow stays the sound over-approximation: EVERY protected-region
46
+ * block edges to the handler (an exception may fire mid-block), which
47
+ * over-supplies reaching-defs facts into `catch` — extra facts, never false
48
+ * kills. Per-leader throw precision is deliberately deferred (M3 decides).
49
+ * - Def/use harvest scope (#2082 M2, see typescript-harvest.ts for the full
50
+ * v1 semantics table): member/property writes are not scalar defs; nested
51
+ * function bodies are opaque in BOTH directions (writes to and reads of
52
+ * captured outer variables are invisible — callback flows are M4 territory);
53
+ * `case x:` test uses attach to the switch dispatch block (sound
54
+ * over-approximation of in-order case evaluation).
55
+ *
56
+ * Block/edge accounting and reachability are pinned in
57
+ * `test/unit/cfg/cfg-builder.test.ts` (core) and
58
+ * `test/unit/cfg/typescript-visitor.test.ts` (this visitor, per hazard).
59
+ */
60
+ import type { SyntaxNode } from '../../utils/ast-helpers.js';
61
+ import type { CfgVisitor } from '../types.js';
62
+ /** TS/JS node types that own a CFG-bearing function body. */
63
+ declare const TS_FUNCTION_TYPES: Set<string>;
64
+ /** The TS/JS CFG visitor (shared by TypeScript and JavaScript). */
65
+ export declare function createTypeScriptCfgVisitor(): CfgVisitor<SyntaxNode>;
66
+ export { TS_FUNCTION_TYPES };
@@ -0,0 +1,583 @@
1
+ import { CfgBuilder } from '../cfg-builder.js';
2
+ import { ControlFlowContext, drainFinalizerPending, wireJumpThroughFinalizers, } from '../control-flow-context.js';
3
+ import { TsHarvester } from './typescript-harvest.js';
4
+ /** TS/JS node types that own a CFG-bearing function body. */
5
+ const TS_FUNCTION_TYPES = new Set([
6
+ 'function_declaration',
7
+ 'function_expression',
8
+ 'arrow_function',
9
+ 'method_definition',
10
+ 'generator_function_declaration',
11
+ 'generator_function',
12
+ ]);
13
+ /** Statement node types that break a basic block (everything else coalesces). */
14
+ const CONTROL_FLOW_TYPES = new Set([
15
+ 'if_statement',
16
+ 'while_statement',
17
+ 'do_statement',
18
+ 'for_statement',
19
+ 'for_in_statement',
20
+ 'switch_statement',
21
+ 'try_statement',
22
+ 'return_statement',
23
+ 'break_statement',
24
+ 'continue_statement',
25
+ 'throw_statement',
26
+ 'labeled_statement',
27
+ 'statement_block',
28
+ ]);
29
+ const LOOP_OR_SWITCH_TYPES = new Set([
30
+ 'while_statement',
31
+ 'do_statement',
32
+ 'for_statement',
33
+ 'for_in_statement',
34
+ 'switch_statement',
35
+ ]);
36
+ const startLineOf = (n) => n.startPosition.row + 1;
37
+ const endLineOf = (n) => n.endPosition.row + 1;
38
+ /**
39
+ * Per-function walk state. One instance is created per function so the
40
+ * {@link ControlFlowContext}, exception-handler stack, and pending label are
41
+ * scoped to that function and never leak across functions.
42
+ */
43
+ class TsCfgWalk {
44
+ builder;
45
+ harvest;
46
+ cfc = new ControlFlowContext();
47
+ /** Stack of exception-handler entry blocks (catch/finally) a `throw` jumps to. */
48
+ handlers = [];
49
+ /** Labels awaiting the construct they precede (`outer: inner: for` = both). */
50
+ pendingLabels = [];
51
+ constructor(builder,
52
+ /** Def/use fact extractor (#2082 M2 U1) — phase-2 only; its scope tree is
53
+ * already complete, so any walk order resolves names correctly. */
54
+ harvest) {
55
+ this.builder = builder;
56
+ this.harvest = harvest;
57
+ }
58
+ /** Statements of a block node, ignoring comments. */
59
+ statementsOf(block) {
60
+ return block.namedChildren.filter((c) => c.type !== 'comment');
61
+ }
62
+ /** The `body` block of a node (field, or the first statement_block child). */
63
+ bodyBlockOf(node) {
64
+ return (node.childForFieldName('body') ?? node.namedChildren.find((c) => c.type === 'statement_block'));
65
+ }
66
+ /** Visit a body that may be a `statement_block` or a single statement. */
67
+ visitBody(node) {
68
+ return this.builder.withNesting(() => {
69
+ if (!node)
70
+ return null;
71
+ if (node.type === 'statement_block')
72
+ return this.visitSeq(this.statementsOf(node));
73
+ return this.visitStmt(node);
74
+ });
75
+ }
76
+ /** Wire a sequence of statements, coalescing straight-line runs into blocks. */
77
+ visitSeq(stmts) {
78
+ return this.builder.withNesting(() => {
79
+ let entry;
80
+ let dangling = [];
81
+ let openSimple;
82
+ for (const stmt of stmts) {
83
+ if (CONTROL_FLOW_TYPES.has(stmt.type)) {
84
+ openSimple = undefined; // close any open straight-line block
85
+ const res = this.visitStmt(stmt);
86
+ if (res === null)
87
+ continue; // transparent (empty nested block)
88
+ if (entry === undefined)
89
+ entry = res.entry;
90
+ else
91
+ this.builder.connect(dangling, res.entry, 'seq');
92
+ dangling = [...res.exits];
93
+ }
94
+ else {
95
+ // Simple statement — coalesce into the current straight-line block.
96
+ if (openSimple === undefined) {
97
+ const idx = this.builder.newBlock(startLineOf(stmt), endLineOf(stmt), stmt.text, 'normal', this.harvest.facts(stmt));
98
+ if (entry === undefined)
99
+ entry = idx;
100
+ else
101
+ this.builder.connect(dangling, idx, 'seq');
102
+ openSimple = idx;
103
+ dangling = [idx];
104
+ }
105
+ else {
106
+ this.builder.extendBlock(openSimple, endLineOf(stmt), stmt.text, this.harvest.facts(stmt));
107
+ }
108
+ }
109
+ }
110
+ if (entry === undefined)
111
+ return null;
112
+ return { entry, exits: dangling };
113
+ });
114
+ }
115
+ /** Dispatch one statement to its handler. Non-null except for empty blocks. */
116
+ visitStmt(stmt) {
117
+ switch (stmt.type) {
118
+ case 'if_statement':
119
+ return this.visitIf(stmt);
120
+ case 'while_statement':
121
+ return this.visitWhile(stmt);
122
+ case 'do_statement':
123
+ return this.visitDoWhile(stmt);
124
+ case 'for_statement':
125
+ return this.visitFor(stmt);
126
+ case 'for_in_statement':
127
+ return this.visitForIn(stmt);
128
+ case 'switch_statement':
129
+ return this.visitSwitch(stmt);
130
+ case 'try_statement':
131
+ return this.visitTry(stmt);
132
+ case 'return_statement':
133
+ return this.visitReturn(stmt);
134
+ case 'throw_statement':
135
+ return this.visitThrow(stmt);
136
+ case 'break_statement':
137
+ return this.visitBreak(stmt);
138
+ case 'continue_statement':
139
+ return this.visitContinue(stmt);
140
+ case 'labeled_statement':
141
+ return this.visitLabeled(stmt);
142
+ case 'statement_block':
143
+ return this.visitSeq(this.statementsOf(stmt));
144
+ default:
145
+ return this.visitSimple(stmt);
146
+ }
147
+ }
148
+ visitSimple(stmt) {
149
+ const idx = this.builder.newBlock(startLineOf(stmt), endLineOf(stmt), stmt.text, 'normal', this.harvest.facts(stmt));
150
+ return { entry: idx, exits: [idx] };
151
+ }
152
+ visitReturn(stmt) {
153
+ // Harvest the argument expression's uses — `return x` blocks live in this
154
+ // dedicated handler, not visitSeq, and were a silently-missed site once.
155
+ const idx = this.builder.newBlock(startLineOf(stmt), endLineOf(stmt), stmt.text, 'normal', this.harvest.facts(stmt));
156
+ // A return crosses EVERY active finally before reaching EXIT.
157
+ wireJumpThroughFinalizers(this.builder, idx, this.cfc.finalizersForReturn(), this.builder.exitIndex, 'return');
158
+ return { entry: idx, exits: [] };
159
+ }
160
+ visitThrow(stmt) {
161
+ const idx = this.builder.newBlock(startLineOf(stmt), endLineOf(stmt), stmt.text, 'normal', this.harvest.facts(stmt));
162
+ this.builder.edge(idx, this.currentHandler(), 'throw');
163
+ return { entry: idx, exits: [] };
164
+ }
165
+ visitBreak(stmt) {
166
+ const idx = this.builder.newBlock(startLineOf(stmt), endLineOf(stmt), stmt.text);
167
+ const res = this.cfc.resolveBreak(this.labelOf(stmt));
168
+ // An unresolved target — a label this visitor doesn't model (a stacked
169
+ // outer label like `outer: inner: for`, or a labeled non-loop block) —
170
+ // would otherwise leave this block with NO out-edge, stranding it and
171
+ // breaking the single-exit invariant a downstream post-dominator / PDG pass
172
+ // relies on. Conservatively route an unresolved jump to the function EXIT
173
+ // ("escapes the function") and thread ALL active finallys — a superset of
174
+ // the truly-crossed set (the real target is somewhere in the function, so
175
+ // execution provably runs every finally between the jump and wherever it
176
+ // lands... up to the ones the conservative EXIT routing over-includes).
177
+ // Sound for dataflow either way: extra paths, never a bypassed finally.
178
+ const { target, finalizers } = res ?? {
179
+ target: this.builder.exitIndex,
180
+ finalizers: this.cfc.finalizersForReturn(),
181
+ };
182
+ wireJumpThroughFinalizers(this.builder, idx, finalizers, target, 'break');
183
+ return { entry: idx, exits: [] };
184
+ }
185
+ visitContinue(stmt) {
186
+ const idx = this.builder.newBlock(startLineOf(stmt), endLineOf(stmt), stmt.text);
187
+ const res = this.cfc.resolveContinue(this.labelOf(stmt));
188
+ // See visitBreak: an unresolved label routes to EXIT (threading all
189
+ // active finallys) to preserve single-exit without bypassing a finally.
190
+ const { target, finalizers } = res ?? {
191
+ target: this.builder.exitIndex,
192
+ finalizers: this.cfc.finalizersForReturn(),
193
+ };
194
+ wireJumpThroughFinalizers(this.builder, idx, finalizers, target, 'continue');
195
+ return { entry: idx, exits: [] };
196
+ }
197
+ visitLabeled(stmt) {
198
+ const body = stmt.childForFieldName('body') ?? stmt.namedChildren[stmt.namedChildren.length - 1];
199
+ const label = this.labelOf(stmt);
200
+ if (body && (LOOP_OR_SWITCH_TYPES.has(body.type) || body.type === 'labeled_statement')) {
201
+ // Loop/switch consumes the accumulated labels via takeLabels(); a nested
202
+ // labeled_statement keeps accumulating (`outer: inner: for` → both
203
+ // labels land on the loop frame).
204
+ if (label)
205
+ this.pendingLabels.push(label);
206
+ const res = this.visitStmt(body);
207
+ this.pendingLabels = []; // clear leftovers if the construct didn't consume
208
+ return res;
209
+ }
210
+ // Labeled NON-loop statement (`blk: { … break blk; … }`): break-to-label
211
+ // targets a synthesized join after the body. Routing it to EXIT instead
212
+ // (the M1 behavior) removed the real continuation and falsely killed
213
+ // every def live at the jump for post-construct uses (tri-review P1).
214
+ const labels = [...this.pendingLabels, ...(label ? [label] : [])];
215
+ this.pendingLabels = [];
216
+ const join = this.builder.newBlock(endLineOf(stmt), endLineOf(stmt), '');
217
+ this.cfc.pushLabeledBlock(join, labels);
218
+ const res = this.visitBody(body);
219
+ this.cfc.pop();
220
+ if (res)
221
+ this.builder.connect(res.exits, join, 'seq');
222
+ return { entry: res?.entry ?? join, exits: [join] };
223
+ }
224
+ visitIf(stmt) {
225
+ const cond = stmt.childForFieldName('condition') ?? stmt;
226
+ const condBlock = this.builder.newBlock(startLineOf(stmt), endLineOf(cond), cond.text, 'normal', this.harvest.facts(cond));
227
+ const exits = [];
228
+ const thenRes = this.visitBody(stmt.childForFieldName('consequence'));
229
+ if (thenRes) {
230
+ this.builder.edge(condBlock, thenRes.entry, 'cond-true');
231
+ exits.push(...thenRes.exits);
232
+ }
233
+ else {
234
+ exits.push(condBlock); // empty then — true path falls through
235
+ }
236
+ const elseNode = this.elseBodyOf(stmt);
237
+ if (elseNode) {
238
+ const elseRes = this.visitBody(elseNode);
239
+ if (elseRes) {
240
+ this.builder.edge(condBlock, elseRes.entry, 'cond-false');
241
+ exits.push(...elseRes.exits);
242
+ }
243
+ else {
244
+ exits.push(condBlock); // empty else block
245
+ }
246
+ }
247
+ else {
248
+ exits.push(condBlock); // no else — false path falls through to the join
249
+ }
250
+ return { entry: condBlock, exits: [...new Set(exits)] };
251
+ }
252
+ /** The else body node (unwraps an `else_clause` wrapper if present). */
253
+ elseBodyOf(ifStmt) {
254
+ const alt = ifStmt.childForFieldName('alternative');
255
+ if (!alt)
256
+ return undefined;
257
+ if (alt.type === 'else_clause') {
258
+ return alt.childForFieldName('body') ?? alt.namedChildren[0];
259
+ }
260
+ return alt;
261
+ }
262
+ visitWhile(stmt) {
263
+ const labels = this.takeLabels();
264
+ const cond = stmt.childForFieldName('condition') ?? stmt;
265
+ const header = this.builder.newBlock(startLineOf(stmt), endLineOf(cond), cond.text, 'normal', this.harvest.facts(cond));
266
+ const loopExit = this.builder.newBlock(endLineOf(stmt), endLineOf(stmt), '');
267
+ this.cfc.pushLoop(header, loopExit, labels);
268
+ const body = this.visitBody(this.bodyBlockOf(stmt));
269
+ this.cfc.pop();
270
+ if (body) {
271
+ this.builder.edge(header, body.entry, 'cond-true');
272
+ this.builder.connect(body.exits, header, 'loop-back');
273
+ }
274
+ else {
275
+ this.builder.edge(header, header, 'loop-back'); // empty body re-tests
276
+ }
277
+ this.builder.edge(header, loopExit, 'cond-false');
278
+ return { entry: header, exits: [loopExit] };
279
+ }
280
+ visitDoWhile(stmt) {
281
+ const labels = this.takeLabels();
282
+ const cond = stmt.childForFieldName('condition') ?? stmt;
283
+ const condBlock = this.builder.newBlock(startLineOf(cond), endLineOf(cond), cond.text, 'normal', this.harvest.facts(cond));
284
+ const loopExit = this.builder.newBlock(endLineOf(stmt), endLineOf(stmt), '');
285
+ this.cfc.pushLoop(condBlock, loopExit, labels);
286
+ const body = this.visitBody(this.bodyBlockOf(stmt));
287
+ this.cfc.pop();
288
+ const backTarget = body ? body.entry : condBlock;
289
+ if (body)
290
+ this.builder.connect(body.exits, condBlock, 'seq');
291
+ this.builder.edge(condBlock, backTarget, 'loop-back'); // cond true → run body again
292
+ this.builder.edge(condBlock, loopExit, 'cond-false');
293
+ return { entry: backTarget, exits: [loopExit] };
294
+ }
295
+ visitFor(stmt) {
296
+ const labels = this.takeLabels();
297
+ const init = stmt.childForFieldName('initializer');
298
+ const cond = stmt.childForFieldName('condition');
299
+ const incr = stmt.childForFieldName('increment');
300
+ const header = this.builder.newBlock(startLineOf(stmt), cond ? endLineOf(cond) : startLineOf(stmt), cond ? cond.text : 'for(;;)', 'normal', cond ? this.harvest.facts(cond) : undefined);
301
+ const loopExit = this.builder.newBlock(endLineOf(stmt), endLineOf(stmt), '');
302
+ let incrBlock = header;
303
+ if (incr) {
304
+ incrBlock = this.builder.newBlock(startLineOf(incr), endLineOf(incr), incr.text, 'normal', this.harvest.facts(incr));
305
+ this.builder.edge(incrBlock, header, 'loop-back');
306
+ }
307
+ this.cfc.pushLoop(incrBlock, loopExit, labels);
308
+ const body = this.visitBody(this.bodyBlockOf(stmt));
309
+ this.cfc.pop();
310
+ if (body) {
311
+ this.builder.edge(header, body.entry, 'cond-true');
312
+ // With no increment clause the body's exits ARE the back-edge — carry
313
+ // the loop-back kind on them (mirroring visitWhile/visitForIn) instead
314
+ // of a phantom header→header self-loop that models a path which never
315
+ // executes the body. With an increment, the body falls through to the
316
+ // increment (`seq`) and the increment carries the loop-back (:338).
317
+ this.builder.connect(body.exits, incrBlock, incr ? 'seq' : 'loop-back');
318
+ }
319
+ else {
320
+ this.builder.edge(header, incrBlock, 'cond-true');
321
+ // Empty body with no increment: the header genuinely re-tests itself.
322
+ if (!incr)
323
+ this.builder.edge(header, header, 'loop-back');
324
+ }
325
+ this.builder.edge(header, loopExit, 'cond-false');
326
+ let entry = header;
327
+ if (init) {
328
+ const initBlock = this.builder.newBlock(startLineOf(init), endLineOf(init), init.text, 'normal', this.harvest.facts(init));
329
+ this.builder.edge(initBlock, header, 'seq');
330
+ entry = initBlock;
331
+ }
332
+ return { entry, exits: [loopExit] };
333
+ }
334
+ visitForIn(stmt) {
335
+ const labels = this.takeLabels();
336
+ // Header text is SYNTHESIZED, so facts come from the left/right AST nodes
337
+ // directly (the loop variable is a def, the iterated expression a use).
338
+ const header = this.builder.newBlock(startLineOf(stmt), startLineOf(stmt), this.forInHeaderText(stmt), 'normal', this.harvest.forInHeadFacts(stmt));
339
+ const loopExit = this.builder.newBlock(endLineOf(stmt), endLineOf(stmt), '');
340
+ this.cfc.pushLoop(header, loopExit, labels);
341
+ const body = this.visitBody(this.bodyBlockOf(stmt));
342
+ this.cfc.pop();
343
+ if (body) {
344
+ this.builder.edge(header, body.entry, 'cond-true');
345
+ this.builder.connect(body.exits, header, 'loop-back');
346
+ }
347
+ else {
348
+ this.builder.edge(header, header, 'loop-back');
349
+ }
350
+ this.builder.edge(header, loopExit, 'cond-false');
351
+ return { entry: header, exits: [loopExit] };
352
+ }
353
+ forInHeaderText(stmt) {
354
+ const left = stmt.childForFieldName('left')?.text ?? '';
355
+ const right = stmt.childForFieldName('right')?.text ?? '';
356
+ return left || right ? `for(${left} … ${right})` : 'for(… in/of …)';
357
+ }
358
+ visitSwitch(stmt) {
359
+ const labels = this.takeLabels();
360
+ const value = stmt.childForFieldName('value') ?? stmt;
361
+ const dispatch = this.builder.newBlock(startLineOf(stmt), endLineOf(value), value.text, 'normal', this.harvest.facts(value));
362
+ const switchExit = this.builder.newBlock(endLineOf(stmt), endLineOf(stmt), '');
363
+ this.cfc.pushSwitch(switchExit, labels);
364
+ const body = stmt.childForFieldName('body');
365
+ const cases = body
366
+ ? body.namedChildren.filter((c) => c.type === 'switch_case' || c.type === 'switch_default')
367
+ : [];
368
+ // `case x:` test expressions live in no block (caseStatements filters the
369
+ // value node out) — harvest their uses onto the dispatch block, one record
370
+ // per case in source order (a sound over-approximation of JS's in-order
371
+ // case evaluation). Conditionally: a later case test only evaluates when
372
+ // earlier cases didn't match, so any def inside one is a may-def — as a
373
+ // must-def on the always-executed dispatch block it would falsely kill
374
+ // prior defs for earlier-matching arms (tri-review).
375
+ for (const c of cases) {
376
+ const caseValue = c.childForFieldName('value');
377
+ if (caseValue)
378
+ this.builder.attachFacts(dispatch, this.harvest.factsConditional(caseValue));
379
+ }
380
+ const caseResults = cases.map((c) => this.visitSeq(this.caseStatements(c)));
381
+ const hasDefault = cases.some((c) => c.type === 'switch_default');
382
+ // entryOf[i] = block a dispatch/fallthrough INTO case i lands on (empty
383
+ // cases are transparent — they resolve to the next case, or the exit).
384
+ const entryOf = new Array(cases.length);
385
+ let after = switchExit;
386
+ for (let i = cases.length - 1; i >= 0; i--) {
387
+ entryOf[i] = caseResults[i]?.entry ?? after;
388
+ after = entryOf[i];
389
+ }
390
+ for (let i = 0; i < cases.length; i++) {
391
+ this.builder.edge(dispatch, entryOf[i], 'switch-case');
392
+ }
393
+ if (!hasDefault)
394
+ this.builder.edge(dispatch, switchExit, 'switch-case'); // no-match path
395
+ for (let i = 0; i < cases.length; i++) {
396
+ const res = caseResults[i];
397
+ if (!res)
398
+ continue;
399
+ const fallTarget = i + 1 < cases.length ? entryOf[i + 1] : switchExit;
400
+ this.builder.connect(res.exits, fallTarget, 'fallthrough');
401
+ }
402
+ this.cfc.pop();
403
+ return { entry: dispatch, exits: [switchExit] };
404
+ }
405
+ caseStatements(caseNode) {
406
+ const value = caseNode.childForFieldName('value');
407
+ return caseNode.namedChildren.filter((c) => c.id !== value?.id && c.type !== 'comment');
408
+ }
409
+ visitTry(stmt) {
410
+ const bodyNode = stmt.childForFieldName('body');
411
+ // Single pass over named children — tree-sitter's `namedChildren` getter
412
+ // allocates a fresh array on every access, so avoid the double `.find`.
413
+ let catchClause;
414
+ let finallyClause;
415
+ for (let i = 0; i < stmt.namedChildCount; i++) {
416
+ const c = stmt.namedChild(i);
417
+ if (c?.type === 'catch_clause')
418
+ catchClause = c;
419
+ else if (c?.type === 'finally_clause')
420
+ finallyClause = c;
421
+ }
422
+ // Build finally first so its entry is known as both a normal join and a
423
+ // handler target. The finally body runs in the OUTER handler context — and
424
+ // OUTSIDE this try's finalizer frame: a return inside the finally must not
425
+ // thread itself (it threads only outer finallys, matching JS semantics).
426
+ const finallyRes = finallyClause
427
+ ? this.visitSeq(this.statementsOf(this.bodyBlockOf(finallyClause)))
428
+ : null;
429
+ // Finalizer frame for early-exit threading (#2082 M2 U2): active while the
430
+ // catch and protected bodies are walked, so a crossing `return`/`break`/
431
+ // `continue` inside either routes through the finally. An empty/comment-only
432
+ // finally (`finallyRes` null — the #2099-F2 empty-catch bug shape) pushes
433
+ // NO frame: it can define nothing, so jumps soundly keep direct edges.
434
+ const finFrame = finallyRes ? this.cfc.pushFinalizer(finallyRes.entry) : null;
435
+ // A throw inside catch propagates to finally (if any), else the outer handler.
436
+ let catchRes = null;
437
+ if (catchClause) {
438
+ if (finallyRes)
439
+ this.handlers.push(finallyRes.entry);
440
+ catchRes = this.visitSeq(this.statementsOf(this.bodyBlockOf(catchClause)));
441
+ if (finallyRes)
442
+ this.handlers.pop();
443
+ if (catchRes === null) {
444
+ // Empty (or comment-only) catch body — `catch {}`. The clause still
445
+ // CATCHES: handler semantics key off the syntactic clause, not the
446
+ // traversal result. Treating it as "no catch" sent the swallowed
447
+ // exception to the outer handler/EXIT and left post-try code
448
+ // unreachable when the body always throws — a hard false-negative
449
+ // for downstream taint. Synthesize one empty block spanning the
450
+ // clause (entry == sole exit) so exception flow lands in it and
451
+ // rejoins the normal continuation. Created BEFORE the protected
452
+ // region is walked, so it never receives a spurious throw edge.
453
+ const idx = this.builder.newBlock(startLineOf(catchClause), endLineOf(catchClause), '');
454
+ catchRes = { entry: idx, exits: [idx] };
455
+ }
456
+ // `catch (e)` has no header block — the param def gets its OWN
457
+ // facts-only block in front of the body entry. It must NOT be prepended
458
+ // into the body's entry block: when the catch body STARTS with a loop,
459
+ // that entry is the loop HEADER, re-entered on every iteration — the
460
+ // param def would re-gen there and falsely KILL loop-carried
461
+ // redefinitions of the param (`catch (e) { while (c) { e = fix(e); }
462
+ // sink(e); }` would lose the fix→sink fact, a taint false negative).
463
+ // The param block becomes the handler entry, which is also semantically
464
+ // right: the binding happens exactly once, on handler entry.
465
+ const paramFacts = this.harvest.catchParamFacts(catchClause);
466
+ if (paramFacts) {
467
+ const paramBlock = this.builder.newBlock(startLineOf(catchClause), startLineOf(catchClause), '', 'normal', paramFacts);
468
+ this.builder.edge(paramBlock, catchRes.entry, 'seq');
469
+ catchRes = { entry: paramBlock, exits: catchRes.exits };
470
+ }
471
+ }
472
+ // Handler for the try body: catch if present, else finally, else outer.
473
+ const tryHandler = catchRes?.entry ?? finallyRes?.entry ?? this.currentHandler();
474
+ const protectedStart = this.builder.blockCount;
475
+ this.handlers.push(tryHandler);
476
+ const bodyRes = bodyNode ? this.visitSeq(this.statementsOf(bodyNode)) : null;
477
+ this.handlers.pop();
478
+ // Conservative exceptional edges: ANY block in the protected region may raise
479
+ // to the handler — not just an explicit `throw`, and not just the body ENTRY.
480
+ // Edging every block created during the try-body walk keeps exception flow
481
+ // sound when the body BRANCHES: an `if` / nested-try / post-branch block whose
482
+ // interior blocks would otherwise have no path to the handler — i.e. a taint
483
+ // false-negative into `catch` for the downstream PDG analysis. The
484
+ // per-function edge cap bounds the count; explicit `throw`s add their own
485
+ // (idempotent) edge to the same handler.
486
+ if (catchClause || finallyClause) {
487
+ for (let b = protectedStart; b < this.builder.blockCount; b++) {
488
+ this.builder.edge(b, tryHandler, 'throw');
489
+ }
490
+ }
491
+ // The finalizer frame closes once the protected/catch walks are done; any
492
+ // jumps that crossed it left their completion legs on `pending`, wired
493
+ // here from the finally's exits (see drainFinalizerPending for the
494
+ // finally-override semantics of an always-jumping finally).
495
+ if (finFrame && finallyRes) {
496
+ this.cfc.pop();
497
+ drainFinalizerPending(this.builder, finFrame, finallyRes.exits);
498
+ }
499
+ const exits = [];
500
+ if (finallyRes) {
501
+ // Normal completion of try AND catch both flow through finally.
502
+ if (bodyRes)
503
+ this.builder.connect(bodyRes.exits, finallyRes.entry, 'seq');
504
+ if (catchRes)
505
+ this.builder.connect(catchRes.exits, finallyRes.entry, 'seq');
506
+ exits.push(...finallyRes.exits);
507
+ // No catch → an exception re-propagates out after finally runs.
508
+ if (!catchRes)
509
+ this.builder.connect(finallyRes.exits, this.currentHandler(), 'throw');
510
+ }
511
+ else {
512
+ if (bodyRes)
513
+ exits.push(...bodyRes.exits);
514
+ if (catchRes)
515
+ exits.push(...catchRes.exits);
516
+ }
517
+ const entry = bodyRes?.entry ?? finallyRes?.entry ?? catchRes?.entry;
518
+ if (entry === undefined)
519
+ return null;
520
+ return { entry, exits: [...new Set(exits)] };
521
+ }
522
+ /** Nearest enclosing exception handler, or the function EXIT. */
523
+ currentHandler() {
524
+ return this.handlers.length ? this.handlers[this.handlers.length - 1] : this.builder.exitIndex;
525
+ }
526
+ /** Consume the labels awaiting the loop/switch this call is building. */
527
+ takeLabels() {
528
+ const labels = this.pendingLabels;
529
+ this.pendingLabels = [];
530
+ return labels;
531
+ }
532
+ labelOf(stmt) {
533
+ const id = stmt.childForFieldName('label') ??
534
+ stmt.namedChildren.find((c) => c.type === 'statement_identifier');
535
+ return id?.text;
536
+ }
537
+ }
538
+ /** Build the CFG for one TS/JS function node (or `undefined` if not a function). */
539
+ function buildFunctionCfg(fnNode, filePath) {
540
+ if (!TS_FUNCTION_TYPES.has(fnNode.type))
541
+ return undefined;
542
+ const startLine = startLineOf(fnNode);
543
+ const endLine = endLineOf(fnNode);
544
+ const startColumn = fnNode.startPosition.column;
545
+ const builder = new CfgBuilder(filePath, startLine, endLine, startColumn);
546
+ const body = fnNode.childForFieldName('body');
547
+ if (!body)
548
+ return undefined; // overload signature / abstract method — no body
549
+ // Phase-1 declaration pre-scan (#2082 M2 U1) — must complete before any
550
+ // facts are extracted; the CFG walk below is not source-order.
551
+ const harvest = new TsHarvester(fnNode);
552
+ // Parameters define at ENTRY (facts only — never touch the entry block's
553
+ // text or span: bench fingerprints and CFG snapshots include block text).
554
+ const paramFacts = harvest.paramFacts();
555
+ if (paramFacts)
556
+ builder.attachFacts(builder.entryIndex, paramFacts);
557
+ if (body.type !== 'statement_block') {
558
+ // Expression-bodied arrow: `() => expr` — one block whose value is returned.
559
+ // Lives outside the walk class, so it harvests explicitly.
560
+ const blk = builder.newBlock(startLineOf(body), endLineOf(body), body.text, 'normal', harvest.facts(body));
561
+ builder.edge(builder.entryIndex, blk, 'seq');
562
+ builder.edge(blk, builder.exitIndex, 'return');
563
+ return builder.finish(harvest.table());
564
+ }
565
+ const walk = new TsCfgWalk(builder, harvest);
566
+ const res = walk.visitSeq(body.namedChildren.filter((c) => c.type !== 'comment'));
567
+ if (!res) {
568
+ builder.edge(builder.entryIndex, builder.exitIndex, 'seq'); // empty body
569
+ return builder.finish(harvest.table());
570
+ }
571
+ builder.edge(builder.entryIndex, res.entry, 'seq');
572
+ builder.connect(res.exits, builder.exitIndex, 'seq'); // normal fall-off → EXIT
573
+ return builder.finish(harvest.table());
574
+ }
575
+ /** Whether a node is a TS/JS function this visitor builds a CFG for. */
576
+ function isFunction(node) {
577
+ return TS_FUNCTION_TYPES.has(node.type);
578
+ }
579
+ /** The TS/JS CFG visitor (shared by TypeScript and JavaScript). */
580
+ export function createTypeScriptCfgVisitor() {
581
+ return { buildFunctionCfg, isFunction };
582
+ }
583
+ export { TS_FUNCTION_TYPES };
@@ -35,6 +35,8 @@ export const EXTENSIONS = [
35
35
  '.cxx',
36
36
  '.hxx',
37
37
  '.hh',
38
+ '.cu',
39
+ '.cuh',
38
40
  // C#
39
41
  '.cs',
40
42
  // Go