gitnexus 1.6.10-rc.99 → 1.6.10

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 (775) hide show
  1. package/README.md +177 -18
  2. package/dist/_shared/graph/types.d.ts +21 -2
  3. package/dist/_shared/graph/types.d.ts.map +1 -1
  4. package/dist/_shared/index.d.ts +3 -3
  5. package/dist/_shared/index.d.ts.map +1 -1
  6. package/dist/_shared/index.js +1 -1
  7. package/dist/_shared/index.js.map +1 -1
  8. package/dist/_shared/integrations/circuit-breaker.d.ts.map +1 -1
  9. package/dist/_shared/integrations/circuit-breaker.js.map +1 -1
  10. package/dist/_shared/integrations/resilient-fetch.d.ts +16 -0
  11. package/dist/_shared/integrations/resilient-fetch.d.ts.map +1 -1
  12. package/dist/_shared/integrations/resilient-fetch.js +19 -7
  13. package/dist/_shared/integrations/resilient-fetch.js.map +1 -1
  14. package/dist/_shared/language-detection.d.ts.map +1 -1
  15. package/dist/_shared/lbug/schema-constants.d.ts +2 -2
  16. package/dist/_shared/lbug/schema-constants.d.ts.map +1 -1
  17. package/dist/_shared/lbug/schema-constants.js +3 -0
  18. package/dist/_shared/lbug/schema-constants.js.map +1 -1
  19. package/dist/_shared/scope-resolution/evidence-weights.d.ts.map +1 -1
  20. package/dist/_shared/scope-resolution/finalize-algorithm.d.ts +5 -0
  21. package/dist/_shared/scope-resolution/finalize-algorithm.d.ts.map +1 -1
  22. package/dist/_shared/scope-resolution/finalize-algorithm.js +315 -36
  23. package/dist/_shared/scope-resolution/finalize-algorithm.js.map +1 -1
  24. package/dist/_shared/scope-resolution/reference-site.d.ts +115 -0
  25. package/dist/_shared/scope-resolution/reference-site.d.ts.map +1 -1
  26. package/dist/_shared/scope-resolution/registries/field-registry.js.map +1 -1
  27. package/dist/_shared/scope-resolution/registries/lookup-core.d.ts.map +1 -1
  28. package/dist/_shared/scope-resolution/registries/lookup-core.js +58 -2
  29. package/dist/_shared/scope-resolution/registries/lookup-core.js.map +1 -1
  30. package/dist/_shared/scope-resolution/registries/method-registry.js.map +1 -1
  31. package/dist/_shared/scope-resolution/scope-tree.d.ts.map +1 -1
  32. package/dist/_shared/scope-resolution/scope-tree.js.map +1 -1
  33. package/dist/_shared/scope-resolution/symbol-definition.d.ts +47 -0
  34. package/dist/_shared/scope-resolution/symbol-definition.d.ts.map +1 -1
  35. package/dist/_shared/scope-resolution/types.d.ts +278 -3
  36. package/dist/_shared/scope-resolution/types.d.ts.map +1 -1
  37. package/dist/cli/ai-context.d.ts +3 -2
  38. package/dist/cli/ai-context.js +71 -16
  39. package/dist/cli/analyze-config.d.ts +1 -1
  40. package/dist/cli/analyze-options.d.ts +131 -0
  41. package/dist/cli/analyze-options.js +1 -0
  42. package/dist/cli/analyze.d.ts +21 -113
  43. package/dist/cli/analyze.js +172 -29
  44. package/dist/cli/cli-message.d.ts +1 -1
  45. package/dist/cli/detect-changes-format.js +19 -2
  46. package/dist/cli/doctor.d.ts +12 -0
  47. package/dist/cli/doctor.js +32 -5
  48. package/dist/cli/editor-targets.js +6 -0
  49. package/dist/cli/eval-server.js +2 -1
  50. package/dist/cli/format-symbol.d.ts +15 -0
  51. package/dist/cli/format-symbol.js +17 -0
  52. package/dist/cli/generated-skill.d.ts +17 -0
  53. package/dist/cli/generated-skill.js +1 -0
  54. package/dist/cli/group.js +229 -21
  55. package/dist/cli/help-i18n.js +1 -2
  56. package/dist/cli/i18n/en.d.ts +11 -9
  57. package/dist/cli/i18n/en.js +13 -9
  58. package/dist/cli/i18n/resources.d.ts +15 -11
  59. package/dist/cli/i18n/zh-CN.d.ts +4 -2
  60. package/dist/cli/i18n/zh-CN.js +11 -9
  61. package/dist/cli/index.js +8 -4
  62. package/dist/cli/resolve-invocation.d.ts +7 -3
  63. package/dist/cli/resolve-invocation.js +7 -3
  64. package/dist/cli/setup.js +4 -3
  65. package/dist/cli/skill-gen.d.ts +1 -6
  66. package/dist/cli/tool.d.ts +1 -0
  67. package/dist/cli/tool.js +74 -17
  68. package/dist/cli/wiki.js +53 -10
  69. package/dist/config/ignore-service.d.ts +2 -0
  70. package/dist/config/ignore-service.js +54 -13
  71. package/dist/core/analyzer-identity.d.ts +80 -0
  72. package/dist/core/analyzer-identity.js +364 -26
  73. package/dist/core/augmentation/engine.js +86 -71
  74. package/dist/core/embedding-checkpoint.d.ts +143 -0
  75. package/dist/core/embedding-checkpoint.js +221 -0
  76. package/dist/core/embedding-count.d.ts +61 -0
  77. package/dist/core/embedding-count.js +69 -0
  78. package/dist/core/embeddings/ast-utils.js +9 -1
  79. package/dist/core/embeddings/embedding-pipeline.d.ts +8 -0
  80. package/dist/core/embeddings/embedding-pipeline.js +253 -14
  81. package/dist/core/embeddings/http-client.d.ts +5 -0
  82. package/dist/core/embeddings/http-client.js +142 -27
  83. package/dist/core/graph/graph.js +3 -0
  84. package/dist/core/graph/import-cycles.d.ts +194 -3
  85. package/dist/core/graph/import-cycles.js +522 -73
  86. package/dist/core/graph/types.d.ts +18 -0
  87. package/dist/core/group/bridge-db.d.ts +204 -1
  88. package/dist/core/group/bridge-db.js +401 -28
  89. package/dist/core/group/completeness.d.ts +130 -0
  90. package/dist/core/group/completeness.js +83 -0
  91. package/dist/core/group/config-parser.js +0 -5
  92. package/dist/core/group/cross-impact.d.ts +46 -1
  93. package/dist/core/group/cross-impact.js +220 -19
  94. package/dist/core/group/cross-trace.d.ts +32 -11
  95. package/dist/core/group/cross-trace.js +114 -20
  96. package/dist/core/group/extractors/http-patterns/java.js +278 -22
  97. package/dist/core/group/extractors/http-patterns/node.js +332 -216
  98. package/dist/core/group/extractors/http-patterns/types.d.ts +8 -0
  99. package/dist/core/group/extractors/http-route-extractor.d.ts +1 -0
  100. package/dist/core/group/extractors/http-route-extractor.js +122 -48
  101. package/dist/core/group/extractors/include-extractor.js +20 -2
  102. package/dist/core/group/extractors/manifest-extractor.d.ts +1 -1
  103. package/dist/core/group/extractors/manifest-extractor.js +7 -7
  104. package/dist/core/group/extractors/python-workspace-extractor.js +5 -2
  105. package/dist/core/group/group-lock.d.ts +31 -0
  106. package/dist/core/group/group-lock.js +166 -0
  107. package/dist/core/group/service.js +297 -16
  108. package/dist/core/group/storage.d.ts +3 -0
  109. package/dist/core/group/storage.js +7 -39
  110. package/dist/core/group/sync.d.ts +49 -3
  111. package/dist/core/group/sync.js +0 -0
  112. package/dist/core/group/types.d.ts +183 -8
  113. package/dist/core/group/types.js +28 -1
  114. package/dist/core/incremental/subgraph-extract.d.ts +9 -6
  115. package/dist/core/incremental/subgraph-extract.js +33 -10
  116. package/dist/core/index-freshness.d.ts +77 -2
  117. package/dist/core/index-freshness.js +109 -2
  118. package/dist/core/ingestion/call-processor.d.ts +13 -1
  119. package/dist/core/ingestion/call-processor.js +110 -3
  120. package/dist/core/ingestion/cfg/callee-cell-format.d.ts +42 -0
  121. package/dist/core/ingestion/cfg/callee-cell-format.js +42 -0
  122. package/dist/core/ingestion/cfg/emit.d.ts +8 -23
  123. package/dist/core/ingestion/cfg/emit.js +9 -23
  124. package/dist/core/ingestion/class-extractors/configs/go.js +10 -5
  125. package/dist/core/ingestion/cluster-enricher.js +8 -4
  126. package/dist/core/ingestion/community-processor.d.ts +21 -3
  127. package/dist/core/ingestion/community-processor.js +81 -82
  128. package/dist/core/ingestion/di-extractors/index.d.ts +9 -33
  129. package/dist/core/ingestion/di-extractors/spring.d.ts +1 -1
  130. package/dist/core/ingestion/di-extractors/spring.js +8 -1
  131. package/dist/core/ingestion/di-extractors/types.d.ts +60 -0
  132. package/dist/core/ingestion/di-extractors/types.js +15 -0
  133. package/dist/core/ingestion/filesystem-walker.js +30 -14
  134. package/dist/core/ingestion/finalize-orchestrator.js +1 -0
  135. package/dist/core/ingestion/frameworks/spring/analysis-features.d.ts +6 -0
  136. package/dist/core/ingestion/frameworks/spring/analysis-features.js +35 -1
  137. package/dist/core/ingestion/frameworks/spring/annotation-arguments.d.ts +13 -0
  138. package/dist/core/ingestion/frameworks/spring/annotation-arguments.js +227 -0
  139. package/dist/core/ingestion/frameworks/spring/aop-candidates.d.ts +15 -0
  140. package/dist/core/ingestion/frameworks/spring/aop-candidates.js +85 -0
  141. package/dist/core/ingestion/frameworks/spring/aop.d.ts +118 -0
  142. package/dist/core/ingestion/frameworks/spring/aop.js +476 -0
  143. package/dist/core/ingestion/frameworks/spring/auto-configuration.d.ts +8 -0
  144. package/dist/core/ingestion/frameworks/spring/auto-configuration.js +16 -0
  145. package/dist/core/ingestion/frameworks/spring/bean-catalog.js +1 -0
  146. package/dist/core/ingestion/frameworks/spring/bean-factories.d.ts +33 -0
  147. package/dist/core/ingestion/frameworks/spring/bean-factories.js +65 -0
  148. package/dist/core/ingestion/frameworks/spring/conditionals.d.ts +25 -0
  149. package/dist/core/ingestion/frameworks/spring/conditionals.js +319 -0
  150. package/dist/core/ingestion/frameworks/spring/di-metadata.d.ts +3 -0
  151. package/dist/core/ingestion/frameworks/spring/di-metadata.js +162 -13
  152. package/dist/core/ingestion/frameworks/spring/non-http-handlers.d.ts +28 -0
  153. package/dist/core/ingestion/frameworks/spring/non-http-handlers.js +152 -0
  154. package/dist/core/ingestion/frameworks/spring/resource-injection.d.ts +5 -0
  155. package/dist/core/ingestion/frameworks/spring/resource-injection.js +91 -0
  156. package/dist/core/ingestion/import-resolvers/configs/csharp.js +5 -1
  157. package/dist/core/ingestion/import-resolvers/configs/swift.js +13 -0
  158. package/dist/core/ingestion/import-resolvers/csharp.d.ts +9 -1
  159. package/dist/core/ingestion/import-resolvers/csharp.js +234 -27
  160. package/dist/core/ingestion/import-resolvers/go.d.ts +11 -1
  161. package/dist/core/ingestion/import-resolvers/go.js +44 -17
  162. package/dist/core/ingestion/import-resolvers/jvm.d.ts +2 -2
  163. package/dist/core/ingestion/import-resolvers/node-workspace-packages.d.ts +90 -0
  164. package/dist/core/ingestion/import-resolvers/node-workspace-packages.js +429 -0
  165. package/dist/core/ingestion/import-resolvers/package-dir-index.d.ts +124 -0
  166. package/dist/core/ingestion/import-resolvers/package-dir-index.js +223 -0
  167. package/dist/core/ingestion/import-resolvers/pass-cache.d.ts +50 -0
  168. package/dist/core/ingestion/import-resolvers/pass-cache.js +60 -0
  169. package/dist/core/ingestion/import-resolvers/per-file-set.d.ts +71 -0
  170. package/dist/core/ingestion/import-resolvers/per-file-set.js +11 -0
  171. package/dist/core/ingestion/import-resolvers/php.d.ts +1 -1
  172. package/dist/core/ingestion/import-resolvers/php.js +65 -19
  173. package/dist/core/ingestion/import-resolvers/python-file-index.d.ts +201 -0
  174. package/dist/core/ingestion/import-resolvers/python-file-index.js +302 -0
  175. package/dist/core/ingestion/import-resolvers/python.js +25 -6
  176. package/dist/core/ingestion/import-resolvers/ruby.d.ts +1 -1
  177. package/dist/core/ingestion/import-resolvers/rust.d.ts +2 -2
  178. package/dist/core/ingestion/import-resolvers/standard.d.ts +1 -1
  179. package/dist/core/ingestion/import-resolvers/types.d.ts +1 -12
  180. package/dist/core/ingestion/import-resolvers/utils.d.ts +65 -6
  181. package/dist/core/ingestion/import-resolvers/utils.js +215 -36
  182. package/dist/core/ingestion/import-resolvers/workspace-file-index.d.ts +72 -0
  183. package/dist/core/ingestion/import-resolvers/workspace-file-index.js +58 -0
  184. package/dist/core/ingestion/language-config.d.ts +29 -1
  185. package/dist/core/ingestion/language-config.js +110 -35
  186. package/dist/core/ingestion/language-provider.d.ts +209 -7
  187. package/dist/core/ingestion/language-provider.js +37 -0
  188. package/dist/core/ingestion/languages/c/captures.js +7 -0
  189. package/dist/core/ingestion/languages/c/import-target.js +12 -16
  190. package/dist/core/ingestion/languages/c/scope-resolver.js +20 -19
  191. package/dist/core/ingestion/languages/c/static-linkage.js +12 -16
  192. package/dist/core/ingestion/languages/c-cpp.js +13 -0
  193. package/dist/core/ingestion/languages/cobol/scope-resolver.js +27 -20
  194. package/dist/core/ingestion/languages/cobol.js +13 -0
  195. package/dist/core/ingestion/languages/cpp/adl.d.ts +2 -2
  196. package/dist/core/ingestion/languages/cpp/adl.js +67 -44
  197. package/dist/core/ingestion/languages/cpp/captures.js +14 -2
  198. package/dist/core/ingestion/languages/cpp/file-local-linkage.js +11 -15
  199. package/dist/core/ingestion/languages/cpp/inline-namespaces.d.ts +28 -8
  200. package/dist/core/ingestion/languages/cpp/inline-namespaces.js +242 -68
  201. package/dist/core/ingestion/languages/cpp/interpret.d.ts +2 -0
  202. package/dist/core/ingestion/languages/cpp/interpret.js +126 -6
  203. package/dist/core/ingestion/languages/cpp/query.js +106 -0
  204. package/dist/core/ingestion/languages/cpp/scope-resolver.js +48 -20
  205. package/dist/core/ingestion/languages/csharp/accessor-unwrap.d.ts +2 -15
  206. package/dist/core/ingestion/languages/csharp/accessor-unwrap.js +18 -10
  207. package/dist/core/ingestion/languages/csharp/captures.js +19 -0
  208. package/dist/core/ingestion/languages/csharp/import-target.js +46 -72
  209. package/dist/core/ingestion/languages/csharp/namespace-siblings.js +5 -1
  210. package/dist/core/ingestion/languages/csharp/query.js +25 -5
  211. package/dist/core/ingestion/languages/csharp/scope-resolver.js +75 -2
  212. package/dist/core/ingestion/languages/dart/captures.d.ts +10 -0
  213. package/dist/core/ingestion/languages/dart/captures.js +598 -16
  214. package/dist/core/ingestion/languages/dart/import-target.js +32 -9
  215. package/dist/core/ingestion/languages/dart/index.d.ts +1 -1
  216. package/dist/core/ingestion/languages/dart/index.js +1 -1
  217. package/dist/core/ingestion/languages/dart/query.js +98 -1
  218. package/dist/core/ingestion/languages/dart/scope-resolver.js +14 -3
  219. package/dist/core/ingestion/languages/dart/simple-hooks.js +9 -0
  220. package/dist/core/ingestion/languages/dart.js +6 -1
  221. package/dist/core/ingestion/languages/go/captures.js +108 -3
  222. package/dist/core/ingestion/languages/go/generic-type-parameters.d.ts +31 -0
  223. package/dist/core/ingestion/languages/go/generic-type-parameters.js +140 -0
  224. package/dist/core/ingestion/languages/go/import-target.d.ts +3 -10
  225. package/dist/core/ingestion/languages/go/import-target.js +34 -30
  226. package/dist/core/ingestion/languages/go/index.d.ts +1 -1
  227. package/dist/core/ingestion/languages/go/interface-impls.d.ts +9 -1
  228. package/dist/core/ingestion/languages/go/interface-impls.js +693 -87
  229. package/dist/core/ingestion/languages/go/interpret.d.ts +2 -0
  230. package/dist/core/ingestion/languages/go/interpret.js +22 -8
  231. package/dist/core/ingestion/languages/go/method-owners.js +31 -12
  232. package/dist/core/ingestion/languages/go/package-clause.d.ts +46 -0
  233. package/dist/core/ingestion/languages/go/package-clause.js +75 -0
  234. package/dist/core/ingestion/languages/go/package-siblings.js +7 -11
  235. package/dist/core/ingestion/languages/go/query.js +56 -3
  236. package/dist/core/ingestion/languages/go/scope-resolver.js +39 -0
  237. package/dist/core/ingestion/languages/go.js +9 -0
  238. package/dist/core/ingestion/languages/java/analysis-features.d.ts +4 -0
  239. package/dist/core/ingestion/languages/java/analysis-features.js +12 -0
  240. package/dist/core/ingestion/languages/java/capture-side-channel.d.ts +12 -0
  241. package/dist/core/ingestion/languages/java/capture-side-channel.js +48 -0
  242. package/dist/core/ingestion/languages/java/captures.js +77 -32
  243. package/dist/core/ingestion/languages/java/import-target.d.ts +49 -9
  244. package/dist/core/ingestion/languages/java/import-target.js +68 -88
  245. package/dist/core/ingestion/languages/java/interpret.js +3 -2
  246. package/dist/core/ingestion/languages/java/module-resolution.d.ts +62 -0
  247. package/dist/core/ingestion/languages/java/module-resolution.js +142 -0
  248. package/dist/core/ingestion/languages/java/query.js +15 -3
  249. package/dist/core/ingestion/languages/java/record-components.d.ts +14 -0
  250. package/dist/core/ingestion/languages/java/record-components.js +185 -0
  251. package/dist/core/ingestion/languages/java/scope-resolver.js +10 -2
  252. package/dist/core/ingestion/languages/java/spring-aop.d.ts +12 -0
  253. package/dist/core/ingestion/languages/java/spring-aop.js +49 -0
  254. package/dist/core/ingestion/languages/java/spring-conditionals.d.ts +11 -0
  255. package/dist/core/ingestion/languages/java/spring-conditionals.js +48 -0
  256. package/dist/core/ingestion/languages/java/spring-di.d.ts +4 -1
  257. package/dist/core/ingestion/languages/java/spring-di.js +41 -9
  258. package/dist/core/ingestion/languages/java/spring-non-http-handlers.d.ts +7 -0
  259. package/dist/core/ingestion/languages/java/spring-non-http-handlers.js +32 -0
  260. package/dist/core/ingestion/languages/java.js +94 -3
  261. package/dist/core/ingestion/languages/javascript/captures.d.ts +6 -1
  262. package/dist/core/ingestion/languages/javascript/captures.js +262 -4
  263. package/dist/core/ingestion/languages/javascript/import-target.d.ts +17 -21
  264. package/dist/core/ingestion/languages/javascript/import-target.js +24 -41
  265. package/dist/core/ingestion/languages/javascript/index.d.ts +54 -5
  266. package/dist/core/ingestion/languages/javascript/index.js +54 -5
  267. package/dist/core/ingestion/languages/javascript/query.d.ts +1 -0
  268. package/dist/core/ingestion/languages/javascript/query.js +211 -5
  269. package/dist/core/ingestion/languages/javascript/scope-resolver.js +12 -0
  270. package/dist/core/ingestion/languages/javascript/simple-hooks.js +2 -1
  271. package/dist/core/ingestion/languages/jvm/package-siblings.js +77 -9
  272. package/dist/core/ingestion/languages/kotlin/capture-side-channel.d.ts +15 -0
  273. package/dist/core/ingestion/languages/kotlin/capture-side-channel.js +48 -0
  274. package/dist/core/ingestion/languages/kotlin/captures.js +43 -1
  275. package/dist/core/ingestion/languages/kotlin/import-target.d.ts +8 -1
  276. package/dist/core/ingestion/languages/kotlin/import-target.js +20 -123
  277. package/dist/core/ingestion/languages/kotlin/index.d.ts +1 -0
  278. package/dist/core/ingestion/languages/kotlin/index.js +1 -0
  279. package/dist/core/ingestion/languages/kotlin/module-resolution.d.ts +22 -0
  280. package/dist/core/ingestion/languages/kotlin/module-resolution.js +108 -0
  281. package/dist/core/ingestion/languages/kotlin/query.js +28 -2
  282. package/dist/core/ingestion/languages/kotlin/scope-resolver.js +23 -2
  283. package/dist/core/ingestion/languages/kotlin/spring-aop.d.ts +12 -0
  284. package/dist/core/ingestion/languages/kotlin/spring-aop.js +64 -0
  285. package/dist/core/ingestion/languages/kotlin/spring-conditionals.d.ts +12 -0
  286. package/dist/core/ingestion/languages/kotlin/spring-conditionals.js +49 -0
  287. package/dist/core/ingestion/languages/kotlin/spring-di.d.ts +2 -0
  288. package/dist/core/ingestion/languages/kotlin/spring-di.js +60 -11
  289. package/dist/core/ingestion/languages/kotlin/spring-non-http-handlers.d.ts +10 -0
  290. package/dist/core/ingestion/languages/kotlin/spring-non-http-handlers.js +41 -0
  291. package/dist/core/ingestion/languages/php/captures.d.ts +5 -0
  292. package/dist/core/ingestion/languages/php/captures.js +319 -51
  293. package/dist/core/ingestion/languages/php/import-target.d.ts +3 -4
  294. package/dist/core/ingestion/languages/php/import-target.js +192 -62
  295. package/dist/core/ingestion/languages/php/query.js +31 -0
  296. package/dist/core/ingestion/languages/python/arity-metadata.d.ts +1 -0
  297. package/dist/core/ingestion/languages/python/arity-metadata.js +1 -0
  298. package/dist/core/ingestion/languages/python/captures.js +29 -0
  299. package/dist/core/ingestion/languages/python/import-decomposer.js +22 -1
  300. package/dist/core/ingestion/languages/python/import-target.d.ts +42 -1
  301. package/dist/core/ingestion/languages/python/import-target.js +277 -91
  302. package/dist/core/ingestion/languages/python/index.d.ts +1 -1
  303. package/dist/core/ingestion/languages/python/index.js +1 -1
  304. package/dist/core/ingestion/languages/python/interpret.d.ts +52 -0
  305. package/dist/core/ingestion/languages/python/interpret.js +249 -7
  306. package/dist/core/ingestion/languages/python/query.js +9 -0
  307. package/dist/core/ingestion/languages/python/receiver-binding.d.ts +8 -6
  308. package/dist/core/ingestion/languages/python/receiver-binding.js +108 -12
  309. package/dist/core/ingestion/languages/python/scope-resolver.js +27 -4
  310. package/dist/core/ingestion/languages/python/simple-hooks.js +2 -8
  311. package/dist/core/ingestion/languages/python.js +13 -0
  312. package/dist/core/ingestion/languages/ruby/captures.js +158 -0
  313. package/dist/core/ingestion/languages/ruby/import-target.js +7 -5
  314. package/dist/core/ingestion/languages/ruby/query.js +93 -0
  315. package/dist/core/ingestion/languages/ruby/scope-resolver.js +2 -0
  316. package/dist/core/ingestion/languages/ruby/simple-hooks.d.ts +1 -1
  317. package/dist/core/ingestion/languages/ruby/simple-hooks.js +22 -1
  318. package/dist/core/ingestion/languages/rust/captures.js +39 -1
  319. package/dist/core/ingestion/languages/rust/method-owners.js +11 -1
  320. package/dist/core/ingestion/languages/rust/module-path.d.ts +126 -0
  321. package/dist/core/ingestion/languages/rust/module-path.js +214 -0
  322. package/dist/core/ingestion/languages/rust/qualified-call.d.ts +41 -0
  323. package/dist/core/ingestion/languages/rust/qualified-call.js +481 -0
  324. package/dist/core/ingestion/languages/rust/query.js +39 -6
  325. package/dist/core/ingestion/languages/rust/scope-resolver.js +21 -2
  326. package/dist/core/ingestion/languages/rust.js +17 -0
  327. package/dist/core/ingestion/languages/swift/captures.js +87 -3
  328. package/dist/core/ingestion/languages/swift/conditional-directive-preprocess.d.ts +48 -0
  329. package/dist/core/ingestion/languages/swift/conditional-directive-preprocess.js +253 -0
  330. package/dist/core/ingestion/languages/swift/import-target.js +4 -9
  331. package/dist/core/ingestion/languages/swift/query.js +17 -0
  332. package/dist/core/ingestion/languages/swift/receiver-binding.d.ts +8 -0
  333. package/dist/core/ingestion/languages/swift/receiver-binding.js +6 -2
  334. package/dist/core/ingestion/languages/swift.js +2 -0
  335. package/dist/core/ingestion/languages/typescript/captures.d.ts +90 -1
  336. package/dist/core/ingestion/languages/typescript/captures.js +267 -5
  337. package/dist/core/ingestion/languages/typescript/cjs-export-assignment.d.ts +134 -0
  338. package/dist/core/ingestion/languages/typescript/cjs-export-assignment.js +523 -0
  339. package/dist/core/ingestion/languages/typescript/cjs-module-exports.d.ts +54 -0
  340. package/dist/core/ingestion/languages/typescript/cjs-module-exports.js +191 -0
  341. package/dist/core/ingestion/languages/typescript/convex-endpoint-metadata.d.ts +7 -0
  342. package/dist/core/ingestion/languages/typescript/convex-endpoint-metadata.js +92 -0
  343. package/dist/core/ingestion/languages/typescript/file-candidates.d.ts +29 -0
  344. package/dist/core/ingestion/languages/typescript/file-candidates.js +70 -0
  345. package/dist/core/ingestion/languages/typescript/import-decomposer.d.ts +28 -1
  346. package/dist/core/ingestion/languages/typescript/import-decomposer.js +125 -10
  347. package/dist/core/ingestion/languages/typescript/import-target.d.ts +25 -37
  348. package/dist/core/ingestion/languages/typescript/import-target.js +24 -26
  349. package/dist/core/ingestion/languages/typescript/interpret.d.ts +21 -1
  350. package/dist/core/ingestion/languages/typescript/interpret.js +45 -5
  351. package/dist/core/ingestion/languages/typescript/module-resolution.d.ts +48 -0
  352. package/dist/core/ingestion/languages/typescript/module-resolution.js +171 -0
  353. package/dist/core/ingestion/languages/typescript/query.d.ts +1 -0
  354. package/dist/core/ingestion/languages/typescript/query.js +303 -14
  355. package/dist/core/ingestion/languages/typescript/receiver-binding.d.ts +17 -0
  356. package/dist/core/ingestion/languages/typescript/receiver-binding.js +27 -0
  357. package/dist/core/ingestion/languages/typescript/scope-resolver.js +27 -40
  358. package/dist/core/ingestion/languages/typescript/simple-hooks.d.ts +0 -10
  359. package/dist/core/ingestion/languages/typescript/simple-hooks.js +22 -22
  360. package/dist/core/ingestion/languages/typescript/tsconfig.d.ts +59 -0
  361. package/dist/core/ingestion/languages/typescript/tsconfig.js +292 -0
  362. package/dist/core/ingestion/languages/typescript.js +93 -2
  363. package/dist/core/ingestion/languages/vue/import-target.d.ts +6 -22
  364. package/dist/core/ingestion/languages/vue/import-target.js +11 -47
  365. package/dist/core/ingestion/languages/vue/scope-resolver.js +4 -2
  366. package/dist/core/ingestion/local-symbol-pruner.js +11 -11
  367. package/dist/core/ingestion/method-extractors/configs/csharp.js +1 -0
  368. package/dist/core/ingestion/method-extractors/generic.js +1 -0
  369. package/dist/core/ingestion/method-types.d.ts +16 -0
  370. package/dist/core/ingestion/pipeline-phases/di.d.ts +1 -1
  371. package/dist/core/ingestion/pipeline-phases/di.js +136 -21
  372. package/dist/core/ingestion/pipeline-phases/index.d.ts +2 -0
  373. package/dist/core/ingestion/pipeline-phases/index.js +2 -0
  374. package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +15 -0
  375. package/dist/core/ingestion/pipeline-phases/parse-impl.js +154 -4
  376. package/dist/core/ingestion/pipeline-phases/parse.js +5 -0
  377. package/dist/core/ingestion/pipeline-phases/processes.d.ts +3 -2
  378. package/dist/core/ingestion/pipeline-phases/processes.js +91 -6
  379. package/dist/core/ingestion/pipeline-phases/routes.js +116 -25
  380. package/dist/core/ingestion/pipeline-phases/spring-aop.d.ts +34 -0
  381. package/dist/core/ingestion/pipeline-phases/spring-aop.js +439 -0
  382. package/dist/core/ingestion/pipeline-phases/spring-auto-configuration.d.ts +34 -0
  383. package/dist/core/ingestion/pipeline-phases/spring-auto-configuration.js +255 -0
  384. package/dist/core/ingestion/pipeline-phases/types.d.ts +7 -0
  385. package/dist/core/ingestion/pipeline.d.ts +26 -2
  386. package/dist/core/ingestion/pipeline.js +60 -11
  387. package/dist/core/ingestion/process-processor.d.ts +109 -2
  388. package/dist/core/ingestion/process-processor.js +464 -52
  389. package/dist/core/ingestion/resolve-references.d.ts +6 -1
  390. package/dist/core/ingestion/resolve-references.js +66 -6
  391. package/dist/core/ingestion/route-extractors/constant-resolver.d.ts +8 -2
  392. package/dist/core/ingestion/route-extractors/constant-resolver.js +11 -7
  393. package/dist/core/ingestion/route-extractors/data-route-table.d.ts +40 -0
  394. package/dist/core/ingestion/route-extractors/data-route-table.js +876 -0
  395. package/dist/core/ingestion/route-extractors/dispatch-guard.d.ts +96 -0
  396. package/dist/core/ingestion/route-extractors/dispatch-guard.js +1184 -0
  397. package/dist/core/ingestion/route-extractors/java-const-resolver.d.ts +114 -0
  398. package/dist/core/ingestion/route-extractors/java-const-resolver.js +569 -0
  399. package/dist/core/ingestion/route-extractors/js-const-resolver.d.ts +192 -0
  400. package/dist/core/ingestion/route-extractors/js-const-resolver.js +1033 -0
  401. package/dist/core/ingestion/route-extractors/nest.d.ts +53 -0
  402. package/dist/core/ingestion/route-extractors/nest.js +523 -0
  403. package/dist/core/ingestion/route-extractors/spring-shared.d.ts +11 -0
  404. package/dist/core/ingestion/route-extractors/spring-shared.js +89 -0
  405. package/dist/core/ingestion/route-extractors/spring.js +214 -29
  406. package/dist/core/ingestion/scope-extractor-bridge.js +9 -1
  407. package/dist/core/ingestion/scope-extractor.d.ts +22 -7
  408. package/dist/core/ingestion/scope-extractor.js +309 -15
  409. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +311 -21
  410. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.js +16 -5
  411. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.d.ts +12 -0
  412. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.js +47 -0
  413. package/dist/core/ingestion/scope-resolution/graph-bridge/ids.d.ts +20 -15
  414. package/dist/core/ingestion/scope-resolution/graph-bridge/ids.js +280 -42
  415. package/dist/core/ingestion/scope-resolution/graph-bridge/imports-to-edges.d.ts +123 -0
  416. package/dist/core/ingestion/scope-resolution/graph-bridge/imports-to-edges.js +171 -17
  417. package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.d.ts +58 -1
  418. package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.js +137 -34
  419. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.d.ts +30 -1
  420. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +39 -1
  421. package/dist/core/ingestion/scope-resolution/passes/callable-value-flow.d.ts +18 -1
  422. package/dist/core/ingestion/scope-resolution/passes/callable-value-flow.js +113 -18
  423. package/dist/core/ingestion/scope-resolution/passes/compound-receiver.d.ts +107 -5
  424. package/dist/core/ingestion/scope-resolution/passes/compound-receiver.js +590 -33
  425. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +3 -0
  426. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +101 -41
  427. package/dist/core/ingestion/scope-resolution/passes/imported-value-refs.d.ts +34 -0
  428. package/dist/core/ingestion/scope-resolution/passes/imported-value-refs.js +62 -0
  429. package/dist/core/ingestion/scope-resolution/passes/property-dispatch.d.ts +5 -1
  430. package/dist/core/ingestion/scope-resolution/passes/property-dispatch.js +8 -1
  431. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +105 -5
  432. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +931 -92
  433. package/dist/core/ingestion/scope-resolution/passes/return-shape-members.d.ts +58 -0
  434. package/dist/core/ingestion/scope-resolution/passes/return-shape-members.js +458 -0
  435. package/dist/core/ingestion/scope-resolution/passes/unique-name-properties.d.ts +180 -0
  436. package/dist/core/ingestion/scope-resolution/passes/unique-name-properties.js +418 -0
  437. package/dist/core/ingestion/scope-resolution/pipeline/phase.d.ts +29 -0
  438. package/dist/core/ingestion/scope-resolution/pipeline/phase.js +57 -0
  439. package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +55 -1
  440. package/dist/core/ingestion/scope-resolution/pipeline/run.js +241 -19
  441. package/dist/core/ingestion/scope-resolution/resolution-outcome.d.ts +112 -2
  442. package/dist/core/ingestion/scope-resolution/resolution-outcome.js +38 -1
  443. package/dist/core/ingestion/scope-resolution/scope/namespace-targets.d.ts +25 -3
  444. package/dist/core/ingestion/scope-resolution/scope/namespace-targets.js +36 -16
  445. package/dist/core/ingestion/scope-resolution/scope/walkers.d.ts +210 -9
  446. package/dist/core/ingestion/scope-resolution/scope/walkers.js +905 -22
  447. package/dist/core/ingestion/scope-resolution/summary-maps.d.ts +28 -0
  448. package/dist/core/ingestion/scope-resolution/summary-maps.js +50 -0
  449. package/dist/core/ingestion/scope-resolution/undecided-satisfaction.d.ts +55 -0
  450. package/dist/core/ingestion/scope-resolution/undecided-satisfaction.js +37 -0
  451. package/dist/core/ingestion/scope-resolution/unresolved-receivers.d.ts +109 -0
  452. package/dist/core/ingestion/scope-resolution/unresolved-receivers.js +204 -0
  453. package/dist/core/ingestion/scope-resolution/utils/definition-id.d.ts +13 -0
  454. package/dist/core/ingestion/scope-resolution/utils/definition-id.js +19 -0
  455. package/dist/core/ingestion/scope-resolution/utils/generic-instantiation.d.ts +171 -0
  456. package/dist/core/ingestion/scope-resolution/utils/generic-instantiation.js +218 -0
  457. package/dist/core/ingestion/scope-resolution/workspace-index-types.d.ts +34 -0
  458. package/dist/core/ingestion/scope-resolution/workspace-index-types.js +15 -0
  459. package/dist/core/ingestion/scope-resolution/workspace-index.d.ts +7 -20
  460. package/dist/core/ingestion/tree-sitter-queries.d.ts +11 -11
  461. package/dist/core/ingestion/tree-sitter-queries.js +678 -4
  462. package/dist/core/ingestion/type-env.js +11 -7
  463. package/dist/core/ingestion/type-extractors/shared.d.ts +17 -0
  464. package/dist/core/ingestion/type-extractors/shared.js +16 -0
  465. package/dist/core/ingestion/type-extractors/types.d.ts +12 -0
  466. package/dist/core/ingestion/type-extractors/typescript.d.ts +20 -0
  467. package/dist/core/ingestion/type-extractors/typescript.js +26 -0
  468. package/dist/core/ingestion/utils/ast-helpers.d.ts +297 -15
  469. package/dist/core/ingestion/utils/ast-helpers.js +578 -46
  470. package/dist/core/ingestion/utils/call-analysis.d.ts +27 -22
  471. package/dist/core/ingestion/utils/call-analysis.js +202 -7
  472. package/dist/core/ingestion/utils/callable-flow-captures.d.ts +59 -0
  473. package/dist/core/ingestion/utils/callable-flow-captures.js +51 -1
  474. package/dist/core/ingestion/utils/callable-labels.d.ts +25 -0
  475. package/dist/core/ingestion/utils/callable-labels.js +31 -0
  476. package/dist/core/ingestion/utils/effective-ram.d.ts +40 -0
  477. package/dist/core/ingestion/utils/effective-ram.js +58 -0
  478. package/dist/core/ingestion/utils/line-base.d.ts +14 -0
  479. package/dist/core/ingestion/utils/line-base.js +14 -0
  480. package/dist/core/ingestion/utils/method-props.d.ts +27 -4
  481. package/dist/core/ingestion/utils/method-props.js +29 -4
  482. package/dist/core/ingestion/utils/receiver-chain-captures.d.ts +31 -0
  483. package/dist/core/ingestion/utils/receiver-chain-captures.js +57 -0
  484. package/dist/core/ingestion/utils/receiver-chain-codec.d.ts +89 -0
  485. package/dist/core/ingestion/utils/receiver-chain-codec.js +182 -0
  486. package/dist/core/ingestion/utils/scope-tree-walk.d.ts +22 -0
  487. package/dist/core/ingestion/utils/scope-tree-walk.js +33 -0
  488. package/dist/core/ingestion/utils/template-arguments.d.ts +96 -0
  489. package/dist/core/ingestion/utils/template-arguments.js +196 -0
  490. package/dist/core/ingestion/utils/type-parameters.d.ts +49 -0
  491. package/dist/core/ingestion/utils/type-parameters.js +206 -0
  492. package/dist/core/ingestion/workers/callable-id.d.ts +80 -0
  493. package/dist/core/ingestion/workers/callable-id.js +125 -0
  494. package/dist/core/ingestion/workers/parse-worker.d.ts +14 -1
  495. package/dist/core/ingestion/workers/parse-worker.js +493 -54
  496. package/dist/core/ingestion/workers/worker-pool.d.ts +20 -27
  497. package/dist/core/ingestion/workers/worker-pool.js +91 -3
  498. package/dist/core/lbug/csv-generator.d.ts +27 -0
  499. package/dist/core/lbug/csv-generator.js +72 -17
  500. package/dist/core/lbug/extension-load-error.js +13 -2
  501. package/dist/core/lbug/extension-loader.d.ts +29 -0
  502. package/dist/core/lbug/extension-loader.js +31 -6
  503. package/dist/core/lbug/graph-emit-sink.d.ts +264 -0
  504. package/dist/core/lbug/graph-emit-sink.js +581 -0
  505. package/dist/core/lbug/lbug-adapter.d.ts +290 -11
  506. package/dist/core/lbug/lbug-adapter.js +652 -69
  507. package/dist/core/lbug/native-check.d.ts +21 -0
  508. package/dist/core/lbug/native-check.js +248 -32
  509. package/dist/core/lbug/pdg-emit-sink.d.ts +13 -4
  510. package/dist/core/lbug/pdg-emit-sink.js +21 -111
  511. package/dist/core/lbug/pool-adapter.d.ts +9 -0
  512. package/dist/core/lbug/pool-adapter.js +164 -14
  513. package/dist/core/lbug/query-batch.d.ts +75 -0
  514. package/dist/core/lbug/query-batch.js +102 -0
  515. package/dist/core/lbug/rel-pair-routing.d.ts +160 -2
  516. package/dist/core/lbug/rel-pair-routing.js +228 -10
  517. package/dist/core/lbug/schema.d.ts +163 -4
  518. package/dist/core/lbug/schema.js +404 -159
  519. package/dist/core/lbug/sync-csv-writer.d.ts +47 -0
  520. package/dist/core/lbug/sync-csv-writer.js +108 -0
  521. package/dist/core/run-analyze.d.ts +147 -0
  522. package/dist/core/run-analyze.js +1215 -179
  523. package/dist/core/search/bm25-index.d.ts +11 -0
  524. package/dist/core/search/bm25-index.js +60 -26
  525. package/dist/core/search/fts-indexes.d.ts +150 -9
  526. package/dist/core/search/fts-indexes.js +241 -23
  527. package/dist/core/wiki/graph-queries.js +145 -112
  528. package/dist/core/wiki/llm-client.d.ts +29 -3
  529. package/dist/core/wiki/llm-client.js +47 -12
  530. package/dist/core/wiki/prompts.d.ts +19 -0
  531. package/dist/core/wiki/prompts.js +20 -1
  532. package/dist/lib/utils.d.ts +113 -0
  533. package/dist/lib/utils.js +185 -0
  534. package/dist/mcp/local/aop-metadata.d.ts +77 -0
  535. package/dist/mcp/local/aop-metadata.js +227 -0
  536. package/dist/mcp/local/bean-metadata.d.ts +2 -1
  537. package/dist/mcp/local/bean-metadata.js +22 -1
  538. package/dist/mcp/local/convex-metadata.d.ts +8 -0
  539. package/dist/mcp/local/convex-metadata.js +45 -0
  540. package/dist/mcp/local/local-backend.d.ts +174 -11
  541. package/dist/mcp/local/local-backend.js +1678 -331
  542. package/dist/mcp/local/path-predicate.d.ts +19 -0
  543. package/dist/mcp/local/path-predicate.js +21 -0
  544. package/dist/mcp/local/pdg-impact.d.ts +132 -10
  545. package/dist/mcp/local/pdg-impact.js +391 -98
  546. package/dist/mcp/repository-policy.d.ts +1 -0
  547. package/dist/mcp/repository-policy.js +5 -0
  548. package/dist/mcp/resources.js +26 -4
  549. package/dist/mcp/server.js +23 -13
  550. package/dist/mcp/tools.d.ts +8 -0
  551. package/dist/mcp/tools.js +76 -23
  552. package/dist/server/analyze-job.d.ts +37 -2
  553. package/dist/server/analyze-job.js +13 -2
  554. package/dist/server/analyze-launch.d.ts +1 -1
  555. package/dist/server/analyze-launch.js +79 -10
  556. package/dist/server/analyze-worker-core.d.ts +1 -1
  557. package/dist/server/analyze-worker-core.js +10 -1
  558. package/dist/server/analyze-worker-ipc.d.ts +1 -1
  559. package/dist/server/analyze-worker-ipc.js +4 -0
  560. package/dist/server/analyze-worker-protocol.d.ts +59 -0
  561. package/dist/server/analyze-worker-protocol.js +1 -0
  562. package/dist/server/analyze-worker.d.ts +1 -17
  563. package/dist/server/api.d.ts +2 -11
  564. package/dist/server/api.js +164 -163
  565. package/dist/server/embed-run-outcome.d.ts +89 -0
  566. package/dist/server/embed-run-outcome.js +88 -0
  567. package/dist/server/git-clone.js +6 -2
  568. package/dist/server/middleware.d.ts +116 -9
  569. package/dist/server/middleware.js +291 -22
  570. package/dist/server/sse-progress.d.ts +41 -0
  571. package/dist/server/sse-progress.js +125 -0
  572. package/dist/storage/branch-index.d.ts +11 -6
  573. package/dist/storage/branch-index.js +15 -7
  574. package/dist/storage/file-hash.js +2 -2
  575. package/dist/storage/fs-atomic.d.ts +25 -0
  576. package/dist/storage/fs-atomic.js +42 -0
  577. package/dist/storage/git.d.ts +131 -0
  578. package/dist/storage/git.js +236 -2
  579. package/dist/storage/index-lock.d.ts +102 -0
  580. package/dist/storage/index-lock.js +660 -0
  581. package/dist/storage/parse-cache.js +588 -6
  582. package/dist/storage/parsedfile-store.js +76 -6
  583. package/dist/storage/repo-manager.d.ts +54 -441
  584. package/dist/storage/repo-manager.js +312 -221
  585. package/dist/storage/repo-meta.d.ts +530 -0
  586. package/dist/storage/repo-meta.js +100 -0
  587. package/dist/types/pipeline.d.ts +37 -0
  588. package/hooks/antigravity/gitnexus-antigravity-hook.cjs +1 -1
  589. package/hooks/claude/gitnexus-hook.cjs +1 -1
  590. package/hooks/claude/resolve-analyze-cmd.cjs +80 -13
  591. package/package.json +11 -2
  592. package/scripts/cross-platform-shard.ts +153 -0
  593. package/scripts/cross-platform-tests.ts +122 -0
  594. package/scripts/run-cross-platform.ts +16 -5
  595. package/skills/gitnexus-cli.md +3 -3
  596. package/skills/gitnexus-debugging.md +37 -7
  597. package/skills/gitnexus-exploring.md +26 -4
  598. package/skills/gitnexus-impact-analysis.md +71 -10
  599. package/skills/gitnexus-plan/README.md +11 -6
  600. package/skills/gitnexus-plan/references/evidence-provenance.md +49 -12
  601. package/skills/gitnexus-plan/scripts/evidence-provenance.mjs +654 -372
  602. package/skills/gitnexus-refactoring.md +48 -5
  603. package/skills/gitnexus-review/SKILL.md +12 -6
  604. package/skills/gitnexus-review/ci-personas/ci-adversarial-lens.md +1 -1
  605. package/skills/gitnexus-review/ci-personas/ci-blast-radius-lens.md +1 -1
  606. package/skills/gitnexus-review/ci-personas/ci-correctness-lens.md +1 -1
  607. package/skills/gitnexus-review/ci-personas/ci-coverage-lens.md +1 -1
  608. package/skills/gitnexus-review/ci-personas/ci-critic-lens.md +1 -1
  609. package/skills/gitnexus-review/ci-personas/ci-security-lens.md +1 -1
  610. package/skills/gitnexus-taint-analysis.md +13 -7
  611. package/skills/gitnexus-work/SKILL.md +4 -1
  612. package/skills/gitnexus-work/references/evidence-provenance.md +49 -12
  613. package/skills/gitnexus-work/scripts/evidence-provenance.mjs +654 -372
  614. package/vendor/leiden/utils.cjs +28 -1
  615. package/web/assets/ProcessFlowModal-Badh2Pbg.js +24 -0
  616. package/web/assets/{__vite-browser-external-bMKyYQkO.js → __vite-browser-external-COxE8F0G.js} +1 -1
  617. package/web/assets/abnfDiagram-QNN2D4NM-Cq2djWWe.js +1 -0
  618. package/web/assets/agent-Dr4l5EOp.js +662 -0
  619. package/web/assets/architecture-TIHT7OUA-V6N4J3SY-fTePvLoe.js +1 -0
  620. package/web/assets/architectureDiagram-OT7VKSJ7-mKEXxkeF.js +36 -0
  621. package/web/assets/{blockDiagram-7IZFK4PR-DBefcjof.js → blockDiagram-54WLDVWM-Dg_hrRWE.js} +4 -4
  622. package/web/assets/{c4Diagram-Y2BXMSZH-EjWnjEJl.js → c4Diagram-X2LASM7V-CnhoT4aV.js} +2 -2
  623. package/web/assets/chunk-224SPVON-B8gxtXpy.js +1 -0
  624. package/web/assets/chunk-2AEHWXPW-CV4r6-1I.js +127 -0
  625. package/web/assets/chunk-2SREHG4O-Dip42xOq.js +1 -0
  626. package/web/assets/chunk-4RFN2BYJ-DHHWuC-n.js +1 -0
  627. package/web/assets/chunk-66DQ2XMT-CM0xjFAN.js +1 -0
  628. package/web/assets/chunk-6BELYETK-ChsYtfHW.js +1 -0
  629. package/web/assets/{chunk-LII3EMHJ-BL6lKJ_P.js → chunk-6L755F7B-CMhLBxi2.js} +1 -1
  630. package/web/assets/{chunk-QA3QBVWF-DVXPOIxe.js → chunk-7CWYLC5S-DRgGkkpF.js} +1 -1
  631. package/web/assets/{chunk-ZXARS5L4-C3v-4Fsw.js → chunk-A7VWPJGB-B4O3oQdQ.js} +1 -1
  632. package/web/assets/chunk-BI6VK774-D0oaWk8V.js +1 -0
  633. package/web/assets/chunk-D6VWDJW2-BxcBdZ9c.js +2 -0
  634. package/web/assets/chunk-DMV4VAQV-Bn6WXDSX.js +4 -0
  635. package/web/assets/chunk-E5QJAATJ-dh-5CtIV.js +2 -0
  636. package/web/assets/chunk-EQFTRU2I-1ix7TzW9.js +2 -0
  637. package/web/assets/chunk-FWYVLQTC-BgDItRzJ.js +1 -0
  638. package/web/assets/chunk-HNC4WDU7-DoU3KnA7.js +1 -0
  639. package/web/assets/chunk-L5GCOVLC-CE2w8Nm_.js +161 -0
  640. package/web/assets/chunk-LAHSG3N2-Bz4hcn0N.js +321 -0
  641. package/web/assets/chunk-LORZLZWG-B57uGw2N.js +88 -0
  642. package/web/assets/chunk-NLANEA3F-XpH_NPA5.js +62 -0
  643. package/web/assets/chunk-NV3KIAZN-5yvcpMiu.js +1 -0
  644. package/web/assets/chunk-OIYT25JQ-CQ6KOZGX.js +1 -0
  645. package/web/assets/chunk-PE7DX7ZZ-CCvRVj43.js +10 -0
  646. package/web/assets/chunk-Q67WD55A-BWCH2SVz.js +156 -0
  647. package/web/assets/chunk-RRFMTAIC-CMN0wcYA.js +1 -0
  648. package/web/assets/{chunk-RLI5ZMPA-CkXZ0y0U.js → chunk-SATU7PGQ-B1zhgOvc.js} +1 -1
  649. package/web/assets/chunk-STOV2HOB-GnExfzIX.js +1 -0
  650. package/web/assets/{chunk-INKRHTLW-Ckl3_KkO.js → chunk-SZD42YQK-ijyKKVr1.js} +21 -21
  651. package/web/assets/{chunk-LCXTWHL2-C162wE5z.js → chunk-TFPGFQPA-Dj6a-0pC.js} +3 -3
  652. package/web/assets/chunk-TGVD4F4B-BMc31Gj2.js +2 -0
  653. package/web/assets/chunk-TZUXEDM2-B7p9AcTw.js +1 -0
  654. package/web/assets/chunk-UAT7B5JY-DW-A_fpI.js +1 -0
  655. package/web/assets/{chunk-6764PJDD-C7xxk063.js → chunk-VE5CLXGZ-C1vB7wpY.js} +1 -1
  656. package/web/assets/{chunk-RG4AUYOV-ZNgyLFq6.js → chunk-VJFVEZTB-CX66F2ep.js} +3 -3
  657. package/web/assets/chunk-VY5UBI4V-yxPQAWxw.js +1 -0
  658. package/web/assets/chunk-WJBAP47W-BckNkuEN.js +1 -0
  659. package/web/assets/chunk-XNMVGMAZ-Cg_LnPtj.js +1 -0
  660. package/web/assets/classDiagram-VPOSVPXH-BjOiXA_C.js +1 -0
  661. package/web/assets/classDiagram-v2-5VFNUXQA-BjOiXA_C.js +1 -0
  662. package/web/assets/{cose-bilkent-UX7MHV2Q-BmnZ7Mvj.js → cose-bilkent-RZA4XIPR-BHpK4Crp.js} +1 -1
  663. package/web/assets/cynefin-VYW2F7L2-2VAHYKR6-DAsF6GZv.js +1 -0
  664. package/web/assets/cynefinDiagram-TR7LZUCG-C3Iar9lZ.js +62 -0
  665. package/web/assets/dagre-K64A6Z3X-CUwi79mA.js +4 -0
  666. package/web/assets/diagram-2TLGJV2N-C2F5Nfym.js +30 -0
  667. package/web/assets/diagram-JCD7PQ7C-DC5-9FRr.js +24 -0
  668. package/web/assets/{diagram-HNR7UZ2L-DVRltWbL.js → diagram-KFBZAVDW-Dfmf_4ti.js} +3 -3
  669. package/web/assets/diagram-L7DVE3PN-GIRyVUtt.js +41 -0
  670. package/web/assets/diagram-YP3FGXHI-CyNFYtkd.js +24 -0
  671. package/web/assets/ebnfDiagram-DRHRQJWO-D8_CI4ZF.js +1 -0
  672. package/web/assets/erDiagram-SZLJLQMB-CohQjznC.js +85 -0
  673. package/web/assets/error-D9MtuG9s.js +1 -0
  674. package/web/assets/eventmodeling-45OFAUF4-AOUJWSLX-BvTc6iQi.js +1 -0
  675. package/web/assets/flowDiagram-BWE6NHOH-B6RnEvD9.js +1 -0
  676. package/web/assets/ganttDiagram-2QTCJSNI-Cks_mEcF.js +292 -0
  677. package/web/assets/gitGraph-TEB2WS4Q-U5VWYIBC-Dy7CCSZ5.js +1 -0
  678. package/web/assets/gitGraphDiagram-VVZAVV5G-CG41cIVc.js +106 -0
  679. package/web/assets/{index-D7mTFcvQ.js → index-2zdvEdzg.js} +51 -51
  680. package/web/assets/index-CJDpyH-n.css +2 -0
  681. package/web/assets/info-DKCQHKI2-DX6KST54-BLJg1ikX.js +1 -0
  682. package/web/assets/infoDiagram-FY4PZKRE-BJ3vPVwW.js +2 -0
  683. package/web/assets/{ishikawaDiagram-BNXS4ZKH-XpjJRU9h.js → ishikawaDiagram-4JDSWWDM-DmqWtjkl.js} +2 -2
  684. package/web/assets/{journeyDiagram-M6C3CM3L-C6wjooas.js → journeyDiagram-QUS3FAKT-XAzVzrQW.js} +3 -3
  685. package/web/assets/{kanban-definition-75IXJCU3-5_UVY79g.js → kanban-definition-D4L7SQKK-YurRnmYn.js} +6 -6
  686. package/web/assets/katex-C5OPUE3Q-Bnbavtmu.js +257 -0
  687. package/web/assets/{mindmap-definition-2TDM6QVE-8xDLzk2e.js → mindmap-definition-66QP5TD6-ChrWWnil.js} +5 -5
  688. package/web/assets/node.browser-U-KyT5AW.js +1 -0
  689. package/web/assets/packet-7NZHBO7P-5WG45QPS-KUxcuvaA.js +1 -0
  690. package/web/assets/pegDiagram-YKIO5C6U-QGeQLEq2.js +1 -0
  691. package/web/assets/pie-RZYD4A2V-2OCZ2VTD-DPs08kJY.js +1 -0
  692. package/web/assets/pieDiagram-YPY3TKG5-BuT3MYzm.js +39 -0
  693. package/web/assets/{quadrantDiagram-VICAPDV7-DR54jOeR.js → quadrantDiagram-VO5QLCXA-B7098wSc.js} +3 -3
  694. package/web/assets/radar-I7S5WNFK-EPLSA35B-w5F0K3a7.js +1 -0
  695. package/web/assets/railroad-3IZDKUUU-PWFQV6Y6-Bu668yqY.js +1 -0
  696. package/web/assets/railroad-abnf-AHOZXSZD-JYYJX3BS-B6ghpETS.js +1 -0
  697. package/web/assets/railroad-ebnf-EBAXGLYW-RBZ4ONAP-D-XmHh3M.js +1 -0
  698. package/web/assets/railroad-peg-LSFZ7HO6-KHNPXGPG-mhkrhzEn.js +1 -0
  699. package/web/assets/railroadDiagram-M77LTMSB-AXu4vxk_.js +1 -0
  700. package/web/assets/{requirementDiagram-JXO7QTGE-PEkg7sKC.js → requirementDiagram-RASO5HTO-B-l8InpK.js} +4 -4
  701. package/web/assets/{sankeyDiagram-URQDO5SZ-DDWEtjcs.js → sankeyDiagram-F32LOJ6L-Kd7zjJgX.js} +3 -3
  702. package/web/assets/{sequenceDiagram-VS2MUI6T-Bv-uwANx.js → sequenceDiagram-URATNSBD-BlcdY9DS.js} +7 -7
  703. package/web/assets/sizeCapture-5GORAGP4-BEqQzsxs.js +1 -0
  704. package/web/assets/src-C5sx_gMp.js +1 -0
  705. package/web/assets/stateDiagram-DODFXNG5-DTdHc-fF.js +1 -0
  706. package/web/assets/stateDiagram-v2-JNMM5JRA-DioNLDDo.js +1 -0
  707. package/web/assets/swimlanes-OSWOYLSK-CA0ofdwY.js +2 -0
  708. package/web/assets/swimlanesDiagram-RHGX2RDE-PkwUdckC.js +8 -0
  709. package/web/assets/{timeline-definition-O6YCAMPW-Qmsuo7qS.js → timeline-definition-WJXWRH4I-DUfp1R3E.js} +3 -3
  710. package/web/assets/treeView-QDETBFTQ-EHYTSQNO-C5lpBgPm.js +1 -0
  711. package/web/assets/treemap-6X3UGDF4-CCWCF7EK-BB0ZyO-B.js +1 -0
  712. package/web/assets/vennDiagram-K3VCMQLI-Bj-FN-XS.js +34 -0
  713. package/web/assets/wardley-OPB4EBWU-6OWERDVT-CEA3Nq0j.js +1 -0
  714. package/web/assets/wardleyDiagram-XK6FUAWV-CVV0dHFr.js +78 -0
  715. package/web/assets/xychartDiagram-5VRUVLCW-CK_rbSWE.js +7 -0
  716. package/web/index.html +16 -15
  717. package/dist/core/ingestion/languages/python/index-stats.d.ts +0 -18
  718. package/dist/core/ingestion/languages/python/index-stats.js +0 -25
  719. package/web/assets/ProcessFlowModal-CwkpVkv-.js +0 -22
  720. package/web/assets/agent-Ds-lkZjX.js +0 -626
  721. package/web/assets/architecture-7EHR7CIX-6QZW5X65-BVWFvYz2.js +0 -1
  722. package/web/assets/architectureDiagram-UL44E2DR-CmG3uPWf.js +0 -36
  723. package/web/assets/chunk-2T2R6R2M-Bb6Sy3_j.js +0 -4
  724. package/web/assets/chunk-2UTLFMKG-CXlhF3Pt.js +0 -1
  725. package/web/assets/chunk-3SSMPTDK-rPsRM1x0.js +0 -321
  726. package/web/assets/chunk-4R4BOZG6-Be_F0JoX.js +0 -159
  727. package/web/assets/chunk-67TQ5CYL-CA7OhIEf.js +0 -124
  728. package/web/assets/chunk-7J6CGLKN-BlKwgXKs.js +0 -10
  729. package/web/assets/chunk-C62D2QBJ-3gdL8EKc.js +0 -1
  730. package/web/assets/chunk-CEXFNPSA-CjwtMDkc.js +0 -1
  731. package/web/assets/chunk-J5EP6P6S-L19oz55R.js +0 -1
  732. package/web/assets/chunk-KGFNY3KK-BZeP-55u.js +0 -62
  733. package/web/assets/chunk-KGYTTC2M-zm6n242I.js +0 -1
  734. package/web/assets/chunk-KNLZD3CH-BGlP6X9R.js +0 -1
  735. package/web/assets/chunk-LRIF4GLE-n59lhprL.js +0 -1
  736. package/web/assets/chunk-RERM46MO-B5dAdWpP.js +0 -1
  737. package/web/assets/chunk-RKZBBQEN-MyIQbGgD.js +0 -1
  738. package/web/assets/chunk-T2UQINTJ-BEyXhZWe.js +0 -1
  739. package/web/assets/chunk-UP6H54XL-BG-n7qDp.js +0 -1
  740. package/web/assets/chunk-UXSXWOXI-CW1_r5cS.js +0 -1
  741. package/web/assets/chunk-UY5QBCOK-ByqTVLvh.js +0 -1
  742. package/web/assets/chunk-VU6ZFW4Y-BLeasdkU.js +0 -2
  743. package/web/assets/classDiagram-KGZ6W3CR-olpO3fMe.js +0 -1
  744. package/web/assets/classDiagram-v2-72OJOZXJ-olpO3fMe.js +0 -1
  745. package/web/assets/dagre-ND4H6XIP-CY4iIF7v.js +0 -4
  746. package/web/assets/diagram-3NCE3AQN-CO6QpzEE.js +0 -43
  747. package/web/assets/diagram-GF46GFSD-B2atbzmU.js +0 -24
  748. package/web/assets/diagram-QXG6HAR7-DR6AHD2h.js +0 -24
  749. package/web/assets/diagram-WEQXMOUZ-DkpoMwTL.js +0 -10
  750. package/web/assets/erDiagram-L5TCEMPS-n8g_90bc.js +0 -85
  751. package/web/assets/eventmodeling-FCH6USID-MREXMVOE-CgFBM7e-.js +0 -1
  752. package/web/assets/flowDiagram-H6V6AXG4-SGMDiCkB.js +0 -162
  753. package/web/assets/ganttDiagram-JCBTUEKG-Cb3UBqRS.js +0 -292
  754. package/web/assets/gitGraph-WXDBUCRP-R675I2BI-DCJv7SII.js +0 -1
  755. package/web/assets/gitGraphDiagram-S2ZK5IYY-Bs9CR7tq.js +0 -106
  756. package/web/assets/index-CX_fADmQ.css +0 -2
  757. package/web/assets/info-J43DQDTF-KCYPFFUO-BfNy-9Nd.js +0 -1
  758. package/web/assets/infoDiagram-3YFTVSEB-T368on4D.js +0 -2
  759. package/web/assets/katex-K3KEBU37-qRQBy-EZ.js +0 -261
  760. package/web/assets/node-Ct_zna8U.js +0 -8
  761. package/web/assets/packet-YPE3B663-LP52Z2RK-D4qkeG9k.js +0 -1
  762. package/web/assets/pie-LRSECV5Y-TCRJHUBD-B_yMKyQa.js +0 -1
  763. package/web/assets/pieDiagram-CU6KROY3-Bv4LpAbr.js +0 -30
  764. package/web/assets/radar-GUYGQ44K-RDLRG3WG-DeD6yN_0.js +0 -1
  765. package/web/assets/sdk-CzHHH8tD.js +0 -37
  766. package/web/assets/src-Cv1eWg0o.js +0 -1
  767. package/web/assets/stateDiagram-7D4R322I-DVNjmkEq.js +0 -1
  768. package/web/assets/stateDiagram-v2-36443NZ5-CyN4Fh7_.js +0 -1
  769. package/web/assets/treeView-BLDUP644-QA4HXRO3-CDuSYnd4.js +0 -1
  770. package/web/assets/treemap-LRROVOQU-LLAWBHMP-imazucyJ.js +0 -1
  771. package/web/assets/vennDiagram-MWXL3ELB-BD1iK1SX.js +0 -34
  772. package/web/assets/wardley-L42UT6IY-5TKZOOLJ-DqrT9tB8.js +0 -1
  773. package/web/assets/wardleyDiagram-CUQ6CDDI-D6TzLVob.js +0 -78
  774. package/web/assets/xychartDiagram-N2JHSOCM-CoZ_Tits.js +0 -7
  775. /package/web/assets/{chunk-7W6UQGC5-pyKGssV3.js → chunk-LIEV3EAG-pyKGssV3.js} +0 -0
@@ -0,0 +1,1184 @@
1
+ /**
2
+ * Hand-rolled dispatch-guard route extractor (JavaScript / TypeScript).
3
+ *
4
+ * Every route extractor before this one recognises a route because a FRAMEWORK
5
+ * declares it: a decorator, a `Route::get()` call, a filesystem convention. A
6
+ * server written against raw `node:http` declares its routes the only way the
7
+ * language offers — by COMPARING the request path to a literal:
8
+ *
9
+ * if (req.method === 'GET' && pathname === '/api/live/portfolio') { … }
10
+ *
11
+ * That is a route definition in every sense that matters to this graph: it has a
12
+ * path, a verb, and a handler. GitNexus simply had no rule that could see it, so
13
+ * `route_map` answered "No routes found in this project" for a repo with
14
+ * seventeen route modules and 113 such comparisons — the same confident-empty
15
+ * failure this whole change set is about, one tool wide.
16
+ *
17
+ * PRECISION OVER RECALL, deliberately. A missed route is a coverage limit; an
18
+ * invented route is a false fact, and `route_map` presents its output as fact.
19
+ * So every rule here requires the comparison to be against something that is
20
+ * demonstrably a request path, and anything that cannot be converted cleanly is
21
+ * dropped rather than guessed at. Specifically NOT extracted:
22
+ *
23
+ * - `pathname.startsWith('/api/')` — a namespace test ("do I own this?"),
24
+ * not a route. Minting `/api` would claim a route nobody serves.
25
+ * - a bare `pathname === '/'` with no verb — far more often a normalisation
26
+ * branch (`pathname === '/' ? '/index.html' : pathname`) than a route. With
27
+ * a verb alongside it the intent is unambiguous, so that form IS extracted.
28
+ * - any regex whose body is not a literal path plus single-segment wildcards.
29
+ *
30
+ * One consequence worth stating rather than discovering: a single-page app that
31
+ * branches on `location.pathname === '/settings'` mints a Route too. That is
32
+ * intentional — it is the same claim a Next.js filesystem route makes, that this
33
+ * file serves this path — and it keeps the rule from needing to guess whether a
34
+ * comparison is "backend enough". It does mean `route_map` on a SPA reports
35
+ * client routes alongside API ones, distinguishable by their `source`.
36
+ *
37
+ * @module route-extractors/dispatch-guard
38
+ */
39
+ /** Provenance stamped on the Route node, in place of `decorator-<name>`. */
40
+ export const DISPATCH_GUARD_SOURCE = 'dispatch-guard-route';
41
+ const HTTP_VERBS = new Set([
42
+ 'GET',
43
+ 'POST',
44
+ 'PUT',
45
+ 'PATCH',
46
+ 'DELETE',
47
+ 'HEAD',
48
+ 'OPTIONS',
49
+ ]);
50
+ const EQUALITY_OPERATORS = new Set(['===', '==']);
51
+ /**
52
+ * Expressions that denote the request path. Kept deliberately narrow: this is
53
+ * the predicate standing between "a string comparison" and "a route", so a loose
54
+ * match here is how invented routes would get in. `path` alone is excluded — in
55
+ * Node it is overwhelmingly the `node:path` module or a filesystem path.
56
+ */
57
+ const PATH_IDENTIFIERS = new Set([
58
+ 'pathname',
59
+ 'pathName',
60
+ 'urlPath',
61
+ 'routePath',
62
+ 'reqPath',
63
+ 'requestPath',
64
+ ]);
65
+ /** `req.url` / `request.url` — the raw form, before a URL parse. */
66
+ const RAW_URL_RECEIVERS = new Set(['req', 'request']);
67
+ /**
68
+ * Cheap pre-filter, so this costs nothing on the overwhelming majority of files.
69
+ *
70
+ * Sound by construction rather than by luck: every rule below reaches a route
71
+ * only through {@link isPathExpression}, which returns true only for one of the
72
+ * {@link PATH_IDENTIFIERS} or for a member access whose property is `pathname` /
73
+ * `url`. A file whose source contains none of those substrings cannot produce a
74
+ * route, so skipping the walk cannot change the output. Keep this alternation in
75
+ * step with those two predicates — widening one without the other would silently
76
+ * re-introduce the empty answer this module exists to remove.
77
+ */
78
+ const PATH_TOKEN_HINT = /pathname|pathName|urlPath|routePath|reqPath|requestPath|\.\s*url\b/;
79
+ const FUNCTION_NODE_TYPES = new Set([
80
+ 'function_declaration',
81
+ 'generator_function_declaration',
82
+ 'function_expression',
83
+ 'generator_function',
84
+ 'arrow_function',
85
+ 'method_definition',
86
+ ]);
87
+ /**
88
+ * A string literal that could be a URL path: leading slash, no whitespace, and
89
+ * no scheme. The character class is permissive about what a path may CONTAIN
90
+ * (`{id}`, `:id`, `%20`, `.json` are all legitimate) because the leading slash
91
+ * plus a path-denoting operand already carries the discrimination.
92
+ */
93
+ function isPathLiteral(value) {
94
+ if (!value.startsWith('/'))
95
+ return false;
96
+ if (value.includes('://'))
97
+ return false;
98
+ return /^\/[\w\-./{}:$*%~@]*$/.test(value);
99
+ }
100
+ /** Follow `a = b = 'literal'` chains, with a cap so a cycle cannot hang. */
101
+ const MAX_CONSTANT_HOPS = 4;
102
+ function buildConstantMap(root) {
103
+ const direct = new Map(); // name -> literal
104
+ const alias = new Map(); // name -> other name
105
+ const ambiguous = new Set();
106
+ const record = (map, name, value) => {
107
+ const existing = map.get(name);
108
+ if (existing !== undefined && existing !== value)
109
+ ambiguous.add(name);
110
+ else
111
+ map.set(name, value);
112
+ };
113
+ const visit = (node) => {
114
+ if (node.type === 'variable_declarator') {
115
+ const name = node.childForFieldName('name');
116
+ const value = unparenthesize(node.childForFieldName('value'));
117
+ if (name !== null && name.type === 'identifier' && value !== null) {
118
+ if (value.type === 'string' || value.type === 'template_string') {
119
+ const raw = plainLiteralValue(value);
120
+ if (raw !== null)
121
+ record(direct, name.text, raw);
122
+ }
123
+ else if (value.type === 'identifier') {
124
+ record(alias, name.text, value.text);
125
+ }
126
+ }
127
+ }
128
+ for (const child of node.namedChildren)
129
+ visit(child);
130
+ };
131
+ visit(root);
132
+ const resolved = new Map();
133
+ for (const name of [...direct.keys(), ...alias.keys()]) {
134
+ if (ambiguous.has(name))
135
+ continue;
136
+ let current = name;
137
+ for (let hop = 0; hop < MAX_CONSTANT_HOPS; hop++) {
138
+ if (ambiguous.has(current))
139
+ break;
140
+ const literal = direct.get(current);
141
+ if (literal !== undefined) {
142
+ resolved.set(name, literal);
143
+ break;
144
+ }
145
+ const next = alias.get(current);
146
+ if (next === undefined)
147
+ break;
148
+ current = next;
149
+ }
150
+ }
151
+ return resolved;
152
+ }
153
+ /** Unquote a plain string / substitution-free template literal. */
154
+ function plainLiteralValue(node) {
155
+ if (node.type !== 'string' && node.type !== 'template_string')
156
+ return null;
157
+ if (node.type === 'template_string' &&
158
+ node.namedChildren.some((c) => c.type !== 'string_fragment')) {
159
+ return null;
160
+ }
161
+ const text = node.text;
162
+ if (text.length < 2)
163
+ return null;
164
+ return text.slice(1, -1);
165
+ }
166
+ /**
167
+ * The string this expression denotes, folding same-file constants where it can.
168
+ *
169
+ * Handles a plain literal, a template string whose substitutions all resolve to
170
+ * known constants, and `+` concatenation of those. Returns `null` the moment any
171
+ * part is unknown — a partially-folded path would be a wrong route, and a route
172
+ * that is missing is the cheaper of the two failures.
173
+ */
174
+ function literalValue(node, constants = new Map()) {
175
+ const plain = plainLiteralValue(node);
176
+ if (plain !== null)
177
+ return plain;
178
+ if (node.type === 'identifier')
179
+ return constants.get(node.text) ?? null;
180
+ if (node.type === 'template_string') {
181
+ let out = '';
182
+ for (const child of node.namedChildren) {
183
+ if (child.type === 'string_fragment') {
184
+ out += child.text;
185
+ continue;
186
+ }
187
+ if (child.type !== 'template_substitution')
188
+ return null;
189
+ const inner = unparenthesize(child.namedChildren[0] ?? null);
190
+ if (inner === null)
191
+ return null;
192
+ const value = literalValue(inner, constants);
193
+ if (value === null)
194
+ return null;
195
+ out += value;
196
+ }
197
+ return out;
198
+ }
199
+ if (node.type === 'binary_expression' && node.childForFieldName('operator')?.text === '+') {
200
+ const left = unparenthesize(node.childForFieldName('left'));
201
+ const right = unparenthesize(node.childForFieldName('right'));
202
+ if (left === null || right === null)
203
+ return null;
204
+ const leftValue = literalValue(left, constants);
205
+ const rightValue = literalValue(right, constants);
206
+ if (leftValue === null || rightValue === null)
207
+ return null;
208
+ return leftValue + rightValue;
209
+ }
210
+ return null;
211
+ }
212
+ /**
213
+ * Does this expression denote the request path? Accepts a bare identifier from
214
+ * {@link PATH_IDENTIFIERS}, any member access ending in `.pathname`, and the raw
215
+ * `req.url` / `request.url` forms.
216
+ */
217
+ function isPathExpression(node) {
218
+ if (node.type === 'identifier')
219
+ return PATH_IDENTIFIERS.has(node.text);
220
+ if (node.type === 'member_expression') {
221
+ const property = node.childForFieldName('property');
222
+ if (property === null)
223
+ return false;
224
+ if (PATH_IDENTIFIERS.has(property.text))
225
+ return true;
226
+ if (property.text === 'url') {
227
+ const object = node.childForFieldName('object');
228
+ return object !== null && RAW_URL_RECEIVERS.has(object.text);
229
+ }
230
+ return false;
231
+ }
232
+ return false;
233
+ }
234
+ /** A logical `!`. `-` and `~` are unary too and are not negation. */
235
+ function isNegation(node) {
236
+ return node.type === 'unary_expression' && node.childForFieldName('operator')?.text === '!';
237
+ }
238
+ /**
239
+ * Is this comparison reached only when it is FALSE?
240
+ *
241
+ * The module already refuses to inherit a verb from an `if` whose `else` branch
242
+ * holds the comparison, for the reason stated in `governingVerb`: the branch runs
243
+ * precisely when the condition did NOT hold, so attributing it is backwards.
244
+ * `!` is the same fact written as an operator, and it was not handled — a stated
245
+ * invariant with half an implementation, which is worse than an absent one
246
+ * because the doc comment reads as though it were covered.
247
+ *
248
+ * Measured before fixing. `if (!(pathname === '/api/admin'))` INVENTED
249
+ * `/api/admin`; `if (!(req.method === 'GET') && pathname === '/api/x')` emitted
250
+ * `GET /api/x`, the one verb the branch guarantees the request does not have.
251
+ *
252
+ * PARITY, not presence: `!!x` is `x`, and a rule keyed on "is there a `!` above
253
+ * me" would refuse a positive condition.
254
+ *
255
+ * The walk stops at the FUNCTION boundary and nowhere else. An earlier draft
256
+ * also broke at `statement_block`, reasoning that `if (!cond) { … }` must not
257
+ * negate a comparison written in its body — true, but already guaranteed by the
258
+ * tree shape: the `!` lives in the if's CONDITION, which is a sibling of the
259
+ * block, never an ancestor of anything inside it. So that break could only ever
260
+ * fire where a `!` genuinely IS an ancestor across a block, i.e. an IIFE — which
261
+ * the function-boundary stop catches first. Unreachable, and unreachable in the
262
+ * UNSAFE direction: stopping early under-counts negations, and an under-count
263
+ * reads a negated guard as positive and invents the route. Removed rather than
264
+ * kept for symmetry.
265
+ */
266
+ function isNegatedContext(node) {
267
+ let negations = 0;
268
+ let current = node;
269
+ let parent = current.parent;
270
+ while (parent !== null && !FUNCTION_NODE_TYPES.has(parent.type)) {
271
+ if (isNegation(parent))
272
+ negations += 1;
273
+ current = parent;
274
+ parent = current.parent;
275
+ }
276
+ return negations % 2 === 1;
277
+ }
278
+ /** Strip redundant parentheses, which the grammar keeps as real nodes. */
279
+ function unparenthesize(node) {
280
+ let current = node;
281
+ while (current !== null && current.type === 'parenthesized_expression') {
282
+ current = current.namedChildren[0] ?? null;
283
+ }
284
+ return current;
285
+ }
286
+ /** Does this expression denote the request METHOD (`req.method`, `method`)? */
287
+ function isMethodExpression(node) {
288
+ if (node.type === 'identifier')
289
+ return node.text === 'method' || node.text === 'httpMethod';
290
+ if (node.type === 'member_expression') {
291
+ const property = node.childForFieldName('property');
292
+ return property !== null && (property.text === 'method' || property.text === 'httpMethod');
293
+ }
294
+ return false;
295
+ }
296
+ /**
297
+ * The HTTP verb an equality comparison asserts, if it is one — `req.method ===
298
+ * 'GET'` → `GET`. Case-normalised, so `'get'` works too.
299
+ */
300
+ function verbFromComparison(node) {
301
+ if (node.type !== 'binary_expression')
302
+ return null;
303
+ const operator = node.childForFieldName('operator')?.text ?? '';
304
+ if (!EQUALITY_OPERATORS.has(operator))
305
+ return null;
306
+ const left = node.childForFieldName('left');
307
+ const right = node.childForFieldName('right');
308
+ if (left === null || right === null)
309
+ return null;
310
+ for (const [expr, literal] of [
311
+ [left, right],
312
+ [right, left],
313
+ ]) {
314
+ if (!isMethodExpression(expr))
315
+ continue;
316
+ const value = literalValue(literal);
317
+ if (value === null)
318
+ continue;
319
+ const verb = value.toUpperCase();
320
+ if (HTTP_VERBS.has(verb))
321
+ return verb;
322
+ }
323
+ return null;
324
+ }
325
+ /**
326
+ * Find the verbs that govern a path comparison, by walking outward.
327
+ *
328
+ * Two idioms, both common and both handled:
329
+ * `if (req.method === 'GET' && pathname === '/x')` — a sibling in the same
330
+ * condition; and
331
+ * `if (req.method === 'GET') { if (pathname === '/x') … }` — an enclosing
332
+ * guard.
333
+ *
334
+ * The walk stops at the function boundary, and REFUSES to inherit a verb from an
335
+ * `if` whose `else` branch we are standing in: in
336
+ * `if (req.method === 'POST') {…} else if (pathname === '/x')` the path
337
+ * comparison is reached precisely when the method is NOT POST, so attributing
338
+ * POST to it would be exactly backwards.
339
+ *
340
+ * Returns a LIST because one guard can serve several methods:
341
+ * `if ((req.method === 'GET' || req.method === 'POST') && pathname === '/x')` is
342
+ * two routes, and returning the first verb reported it as GET-only — a route
343
+ * that silently loses its other methods reads as a narrower contract than the
344
+ * code implements. Empty means "no verb is guaranteed", which stays verb-less.
345
+ */
346
+ function governingVerbs(comparison) {
347
+ let current = comparison;
348
+ let parent = current.parent;
349
+ while (parent !== null && !FUNCTION_NODE_TYPES.has(parent.type)) {
350
+ if (parent.type === 'binary_expression' &&
351
+ parent.childForFieldName('operator')?.text === '&&') {
352
+ const sibling = parent.childForFieldName('left')?.id === current.id
353
+ ? parent.childForFieldName('right')
354
+ : parent.childForFieldName('left');
355
+ const verbs = sibling === null ? [] : findVerbsInSubtree(sibling);
356
+ if (verbs.length > 0)
357
+ return verbs;
358
+ }
359
+ if (parent.type === 'if_statement') {
360
+ const alternative = parent.childForFieldName('alternative');
361
+ const inElseBranch = alternative !== null && alternative.id === current.id;
362
+ const condition = parent.childForFieldName('condition');
363
+ // A comparison inside the condition itself is handled by the `&&` rule
364
+ // above; here we only inherit from an ENCLOSING if we are governed by.
365
+ if (!inElseBranch && condition !== null && condition.id !== current.id) {
366
+ const verbs = findVerbsInSubtree(condition);
367
+ if (verbs.length > 0)
368
+ return verbs;
369
+ }
370
+ }
371
+ current = parent;
372
+ parent = current.parent;
373
+ }
374
+ return [];
375
+ }
376
+ /**
377
+ * The verb a subtree GUARANTEES when it evaluates truthy.
378
+ *
379
+ * `negated` counts whether an odd number of `!` stands between the question and
380
+ * this node. It is PARITY, the same rule `isNegatedContext` states — and the
381
+ * rule the previous presence-based check contradicted: it returned null at the
382
+ * first `!` it saw, so `!!(req.method === 'GET')` lost a verb the source states
383
+ * outright. A stated invariant with half an implementation, in the same module
384
+ * that had already been fixed for exactly that once.
385
+ *
386
+ * A verb reached at odd parity is the verb the branch EXCLUDES, so it yields
387
+ * nothing — the route survives, verb-less, which is the honest answer: this
388
+ * branch does not say which method it serves. Siblings are still searched,
389
+ * because excluding one verb says nothing about the next.
390
+ */
391
+ function findVerbsInSubtree(node, negated = false) {
392
+ if (isNegation(node)) {
393
+ const operand = node.childForFieldName('argument');
394
+ return operand === null ? [] : findVerbsInSubtree(operand, !negated);
395
+ }
396
+ // A ternary SELECTS between its arms, so a verb inside one is not reached
397
+ // merely because the whole is truthy — see `verbsFromTernary`.
398
+ if (node.type === 'ternary_expression')
399
+ return verbsFromTernary(node, negated);
400
+ if (isDisjunction(node))
401
+ return verbsFromDisjunction(node, negated);
402
+ const direct = verbFromComparison(node);
403
+ if (direct !== null)
404
+ return negated ? [] : [direct];
405
+ // Generic descent keeps FIRST-match rather than unioning across children: an
406
+ // arbitrary node says nothing about how its children combine, and two verbs
407
+ // found under one are far more likely to be unrelated than alternatives. The
408
+ // one construct that genuinely means "either of these" is `||`, handled above.
409
+ for (const child of node.namedChildren) {
410
+ const found = findVerbsInSubtree(child, negated);
411
+ if (found.length > 0)
412
+ return found;
413
+ }
414
+ return [];
415
+ }
416
+ /** A logical `||`. */
417
+ function isDisjunction(node) {
418
+ return node.type === 'binary_expression' && node.childForFieldName('operator')?.text === '||';
419
+ }
420
+ /**
421
+ * The verbs a disjunction guarantees — ALL of them, or none.
422
+ *
423
+ * `req.method === 'GET' || req.method === 'POST'` is the multi-method guard, and
424
+ * every operand names a verb, so the guard serves exactly those two.
425
+ *
426
+ * `req.method === 'GET' || isAdmin` is not: the branch is reached for ANY method
427
+ * when `isAdmin` holds, so the honest answer is no verb at all. Reporting `GET`
428
+ * — which is what taking the first match did — presents a route open to every
429
+ * method as one restricted to a single method, and this module's whole bar is
430
+ * that a wrong answer costs more than a missing one.
431
+ *
432
+ * So: every operand must yield at least one verb, or the whole disjunction
433
+ * yields none. At odd parity `!(A || B)` is `!A && !B`, which excludes verbs
434
+ * rather than offering them, so nothing is guaranteed either.
435
+ */
436
+ function verbsFromDisjunction(node, negated) {
437
+ if (negated)
438
+ return [];
439
+ const operands = [node.childForFieldName('left'), node.childForFieldName('right')];
440
+ const collected = [];
441
+ for (const operand of operands) {
442
+ if (operand === null)
443
+ return [];
444
+ const verbs = findVerbsInSubtree(operand, false);
445
+ if (verbs.length === 0)
446
+ return [];
447
+ for (const verb of verbs)
448
+ if (!collected.includes(verb))
449
+ collected.push(verb);
450
+ }
451
+ return collected;
452
+ }
453
+ /**
454
+ * The verbs BOTH operands of a conjunction guarantee — their INTERSECTION.
455
+ *
456
+ * `A && B` is reached only when each side holds, so the methods it serves are
457
+ * the methods they agree on. Taking the first non-empty side instead — which is
458
+ * what {@link verbsFromTernary} did — reports one operand's set unintersected:
459
+ * `(GET || POST) ? (POST || PUT) : false` emitted GET and POST where only POST
460
+ * can reach the body, so the GET route was invented outright.
461
+ *
462
+ * An EMPTY side is "this operand names no method", not "this operand admits
463
+ * none", so it yields to the other rather than annihilating it — that is the
464
+ * `isAdmin && req.method === 'POST'` shape, and it is the whole reason the
465
+ * fallthrough existed. An empty INTERSECTION of two non-empty sides is the
466
+ * opposite: two conflicting method assertions, a guard nothing can satisfy. No
467
+ * verb is honest there, and the route survives verb-less, which is this
468
+ * module's stated direction for "cannot prove it".
469
+ */
470
+ function intersectVerbs(a, b) {
471
+ if (a.length === 0)
472
+ return b;
473
+ if (b.length === 0)
474
+ return a;
475
+ return a.filter((verb) => b.includes(verb));
476
+ }
477
+ /**
478
+ * The verb a ternary guarantees — which is one only when an arm is a boolean
479
+ * literal, because that is what collapses the selection into a conjunction:
480
+ *
481
+ * c ? A : false ≡ c && A both hold, so INTERSECT both
482
+ * c ? false : B ≡ !c && B c must NOT hold, so search it at flipped parity
483
+ * c ? true : B ≡ c || B a disjunction guarantees neither operand
484
+ * c ? A : true ≡ !c || A likewise
485
+ *
486
+ * With two non-literal arms the verb is chosen by a condition whose value is
487
+ * unknown, so the ternary guarantees nothing.
488
+ *
489
+ * The two conjunctions intersect rather than take the first side that names a
490
+ * verb — see {@link intersectVerbs} for the route that mistake invented. The
491
+ * `||` rule below is the mirror image and already had it right: a disjunction
492
+ * UNIONS its operands, all-or-nothing.
493
+ *
494
+ * Measured before fixing: `(req.method === 'GET' ? false : true) && pathname ===
495
+ * '/api/i'` emitted `GET /api/i` — the one method that branch guarantees the
496
+ * request does NOT have, the same inversion `!` produced before `d4dcba8c`. The
497
+ * three shapes that were already right stay right; refusing every ternary would
498
+ * have been safe but would have dropped them.
499
+ *
500
+ * At odd parity every conjunction above becomes a disjunction (De Morgan) and
501
+ * guarantees nothing, so a negated ternary yields no verb. `!(c ? false : true)`
502
+ * is really `c` and could be read, but it needs BOTH arms folded as literals to
503
+ * see that, and no such condition has been observed in a real dispatcher.
504
+ * Declining is the safe direction: a missing verb, not an inverted one.
505
+ */
506
+ function verbsFromTernary(node, negated) {
507
+ if (negated)
508
+ return [];
509
+ const condition = unparenthesize(node.childForFieldName('condition'));
510
+ const consequence = unparenthesize(node.childForFieldName('consequence'));
511
+ const alternative = unparenthesize(node.childForFieldName('alternative'));
512
+ if (condition === null || consequence === null || alternative === null)
513
+ return [];
514
+ if (alternative.type === 'false') {
515
+ return intersectVerbs(findVerbsInSubtree(condition, false), findVerbsInSubtree(consequence, false));
516
+ }
517
+ if (consequence.type === 'false') {
518
+ return intersectVerbs(findVerbsInSubtree(condition, true), findVerbsInSubtree(alternative, false));
519
+ }
520
+ return [];
521
+ }
522
+ /**
523
+ * The name of the function containing this comparison — the route's handler.
524
+ *
525
+ * Covers the declared forms and the two anonymous ones that carry a name from
526
+ * their binding site: `const handle = (req) => …` and the object-literal method
527
+ * shorthand (`{ async handle(req, res) {…} }`), which is how the reporting
528
+ * repo's route modules are written.
529
+ */
530
+ function enclosingHandlerName(node) {
531
+ const fn = enclosingFunction(node);
532
+ if (fn === null)
533
+ return undefined;
534
+ const own = fn.childForFieldName('name');
535
+ if (own !== null)
536
+ return own.text;
537
+ const parent = fn.parent;
538
+ if (parent === null)
539
+ return undefined;
540
+ if (parent.type === 'variable_declarator' || parent.type === 'pair') {
541
+ const bound = parent.childForFieldName('name') ?? parent.childForFieldName('key');
542
+ return bound?.text;
543
+ }
544
+ if (parent.type === 'assignment_expression') {
545
+ const left = parent.childForFieldName('left');
546
+ if (left === null)
547
+ return undefined;
548
+ return left.type === 'member_expression'
549
+ ? (left.childForFieldName('property')?.text ?? undefined)
550
+ : left.text;
551
+ }
552
+ return undefined;
553
+ }
554
+ /**
555
+ * The function this node sits in, or `null` at module scope.
556
+ *
557
+ * ONE traversal, three readers: the handler name above, the scope half of a
558
+ * match-binding key, and the chain an assignment can rebind. They have to agree
559
+ * on where a function begins or "the same name in the same function" stops
560
+ * meaning one thing, so they share the walk rather than each re-deriving it.
561
+ */
562
+ function enclosingFunction(node) {
563
+ let current = node.parent;
564
+ while (current !== null) {
565
+ if (FUNCTION_NODE_TYPES.has(current.type))
566
+ return current;
567
+ current = current.parent;
568
+ }
569
+ return null;
570
+ }
571
+ /** Names bound outside every function share this scope. */
572
+ const MODULE_SCOPE_ID = -1;
573
+ /** The scope half of a binding key: the id of the function this node lives in. */
574
+ function enclosingScopeId(node) {
575
+ return enclosingFunction(node)?.id ?? MODULE_SCOPE_ID;
576
+ }
577
+ /**
578
+ * Every scope an assignment written here could be rebinding — its own function,
579
+ * then outward. `m = x` inside a callback rebinds the `m` of whichever enclosing
580
+ * function declared it, and this module does not resolve which, so an assignment
581
+ * is taken to reach all of them.
582
+ */
583
+ function enclosingScopeIds(node) {
584
+ const ids = [];
585
+ for (let fn = enclosingFunction(node); fn !== null; fn = enclosingFunction(fn))
586
+ ids.push(fn.id);
587
+ ids.push(MODULE_SCOPE_ID);
588
+ return ids;
589
+ }
590
+ /**
591
+ * A binding key: the function a name is bound in, plus the name.
592
+ *
593
+ * The scope id is a number and NUL cannot appear in an identifier, so the two
594
+ * halves cannot run together into a collision. Written as the `\u0000` ESCAPE,
595
+ * never a raw NUL byte: a literal NUL makes the source a binary file to git,
596
+ * grep and every other line-oriented tool.
597
+ */
598
+ function bindingKey(scopeId, name) {
599
+ return `${scopeId}\u0000${name}`;
600
+ }
601
+ /**
602
+ * Every name a binding pattern introduces — `m`, `{ m }`, `{ a: m }`, `[m]`,
603
+ * `...m`, `m = fallback`.
604
+ *
605
+ * Destructuring is here because it SHADOWS: `{ const { m } = req.body }` in a
606
+ * block below a real `const m = pathname.match(…)` binds a different `m` in the
607
+ * same function scope, and a shadow this module cannot see is a shadow it would
608
+ * mint a route from. Over-collecting a name only ever refuses one, so the
609
+ * recursion is deliberately blunt about the shapes it does not name.
610
+ */
611
+ function patternNames(pattern, out = []) {
612
+ if (pattern.type === 'identifier' || pattern.type === 'shorthand_property_identifier_pattern') {
613
+ out.push(pattern.text);
614
+ return out;
615
+ }
616
+ if (pattern.type === 'pair_pattern' || pattern.type === 'assignment_pattern') {
617
+ const bound = pattern.childForFieldName('value') ?? pattern.childForFieldName('left');
618
+ if (bound !== null)
619
+ patternNames(bound, out);
620
+ return out;
621
+ }
622
+ for (const child of pattern.namedChildren)
623
+ patternNames(child, out);
624
+ return out;
625
+ }
626
+ /**
627
+ * A single URL segment, capturing or not: `[^/]+`, `[^\/]*`, `([^/]+)`.
628
+ *
629
+ * The CAPTURING form is the one real dispatchers write, and it was the one form
630
+ * this converter refused. `(` fell through to the metacharacter bail below, so
631
+ * `^\/api\/research-runs\/([^/]+)$` translated to nothing — while the
632
+ * non-capturing twin translated fine, which is why every test for this rule
633
+ * passed. The tests were written against the implementation instead of against
634
+ * the corpus, and the reporting repo does not contain a single non-capturing
635
+ * path wildcard: a dispatcher captures the segment because it needs the id.
636
+ *
637
+ * The alternatives are balanced on purpose — `([^/]+` unclosed is not a segment,
638
+ * and matching it would leave a stray `)` to be read as a literal.
639
+ */
640
+ const SEGMENT_WILDCARD = /^(?:\(\[\^\\?\/\][+*]\)|\[\^\\?\/\][+*])/;
641
+ /**
642
+ * Convert an anchored regex used as a path test into a route path, or `null` if
643
+ * any part of it is not cleanly representable.
644
+ *
645
+ * `^\/api\/research-runs\/([^/]+)$` → `/api/research-runs/{param1}`
646
+ *
647
+ * Only single-segment wildcards are recognised — see {@link SEGMENT_WILDCARD}.
648
+ * Anything else — an optional group, an alternation, a bare `.*` — bails,
649
+ * because a route path is a claim about what the server serves and a
650
+ * mistranslated pattern is a wrong one. A capture group around anything OTHER
651
+ * than a segment wildcard still bails: `(.+)` spans slashes, so it is not one
652
+ * segment and cannot be one `{param}`.
653
+ */
654
+ export function regexToRoutePath(source) {
655
+ if (!source.startsWith('^') || !source.endsWith('$'))
656
+ return null;
657
+ const body = source.slice(1, -1);
658
+ if (body.length === 0)
659
+ return null;
660
+ let out = '';
661
+ let i = 0;
662
+ let paramIndex = 0;
663
+ while (i < body.length) {
664
+ const rest = body.slice(i);
665
+ const wildcard = SEGMENT_WILDCARD.exec(rest);
666
+ if (wildcard !== null) {
667
+ paramIndex += 1;
668
+ out += `{param${paramIndex}}`;
669
+ i += wildcard[0].length;
670
+ continue;
671
+ }
672
+ const char = body[i] ?? '';
673
+ if (char === '\\') {
674
+ const escaped = body[i + 1];
675
+ if (escaped === undefined)
676
+ return null;
677
+ // Only escapes of literal path punctuation are meaningful here; an escape
678
+ // class (`\d`, `\w`, `\s`) is a pattern, not a literal.
679
+ if (/[A-Za-z0-9]/.test(escaped))
680
+ return null;
681
+ out += escaped;
682
+ i += 2;
683
+ continue;
684
+ }
685
+ if ('[](){}|+*?^$.'.includes(char))
686
+ return null;
687
+ out += char;
688
+ i += 1;
689
+ }
690
+ return out.startsWith('/') ? out : null;
691
+ }
692
+ /**
693
+ * Extract routes declared by path-comparison dispatch from one JS/TS file.
694
+ *
695
+ * Returns the same {@link ExtractedDecoratorRoute} transport every AST-level
696
+ * route extractor returns — a route is a route once it has a path, a verb and a
697
+ * handler, and reusing the transport means the routes phase, the `(method, url)`
698
+ * dedup and the handler-symbol resolution all apply unchanged. `source`
699
+ * distinguishes the provenance, which is the part that actually differs: a
700
+ * decorator route is DECLARED, a dispatch-guard route is INFERRED from a
701
+ * comparison.
702
+ */
703
+ export function extractDispatchGuardRoutes(tree, filePath, lineOffset = 0) {
704
+ // Every JS/TS file in every repo reaches this hook, so the walk is gated on a
705
+ // substring test first — see PATH_TOKEN_HINT for why skipping is sound.
706
+ if (!PATH_TOKEN_HINT.test(tree.rootNode.text))
707
+ return [];
708
+ const found = [];
709
+ const constants = buildConstantMap(tree.rootNode);
710
+ const regexes = buildRegexConstantMap(tree.rootNode);
711
+ const matches = { bindings: new Map(), declarations: new Map() };
712
+ // Declarations and assignments are noted on the SAME walk that records the
713
+ // bindings, and every emission happens after it, so a shadow or a rebinding
714
+ // written below the match still refuses the name it would have poisoned.
715
+ const visit = (node) => {
716
+ if (node.type === 'binary_expression')
717
+ collectFromComparison(node, found, constants);
718
+ else if (node.type === 'call_expression')
719
+ collectFromRegexDispatch(node, found, regexes, matches);
720
+ else if (node.type === 'switch_statement')
721
+ collectFromSwitch(node, found, constants);
722
+ else if (node.type === 'variable_declarator')
723
+ noteDeclaration(node, matches);
724
+ else if (node.type === 'assignment_expression' ||
725
+ node.type === 'augmented_assignment_expression')
726
+ noteReassignment(node, matches);
727
+ for (const child of node.namedChildren)
728
+ visit(child);
729
+ };
730
+ visit(tree.rootNode);
731
+ collectFromMatchBindings(tree.rootNode, matches.bindings, found);
732
+ return dedupeWithinFile(found).map((route) => ({
733
+ filePath,
734
+ routePath: route.url,
735
+ httpMethod: route.verb ?? '',
736
+ decoratorName: DISPATCH_GUARD_SOURCE,
737
+ source: DISPATCH_GUARD_SOURCE,
738
+ lineNumber: route.line + lineOffset,
739
+ ...(route.handlerName ? { handlerName: route.handlerName } : {}),
740
+ }));
741
+ }
742
+ function collectFromComparison(node, out, constants) {
743
+ // Reached only when the comparison is FALSE — claiming the path would be
744
+ // exactly backwards. See `isNegatedContext`.
745
+ if (isNegatedContext(node))
746
+ return;
747
+ const operator = node.childForFieldName('operator')?.text ?? '';
748
+ if (!EQUALITY_OPERATORS.has(operator))
749
+ return;
750
+ const left = node.childForFieldName('left');
751
+ const right = node.childForFieldName('right');
752
+ if (left === null || right === null)
753
+ return;
754
+ for (const [expr, literal] of [
755
+ [left, right],
756
+ [right, left],
757
+ ]) {
758
+ if (!isPathExpression(expr))
759
+ continue;
760
+ const value = literalValue(literal, constants);
761
+ if (value === null || !isPathLiteral(value))
762
+ continue;
763
+ const verbs = governingVerbs(node);
764
+ // A bare `/` is only a route when a verb says so — see the module header.
765
+ if (value === '/' && verbs.length === 0)
766
+ continue;
767
+ pushPerVerb(out, verbs, {
768
+ url: value,
769
+ handlerName: enclosingHandlerName(node),
770
+ line: node.startPosition.row + 1,
771
+ });
772
+ return;
773
+ }
774
+ }
775
+ /**
776
+ * Emit one route per governing verb, or a single verb-less route when the guard
777
+ * guarantees none. A multi-method guard is genuinely several routes: they share
778
+ * a path and a handler but not a method, and `(method, url)` is the key every
779
+ * downstream consumer dedups and looks up on.
780
+ */
781
+ function pushPerVerb(out, verbs, route) {
782
+ if (verbs.length === 0) {
783
+ out.push({ ...route, verb: null });
784
+ return;
785
+ }
786
+ for (const verb of verbs)
787
+ out.push({ ...route, verb });
788
+ }
789
+ /**
790
+ * `switch (pathname) { case '/api/health': … }` — the other way to write the
791
+ * same dispatch, and the reason this module is not a rule about `if`. The
792
+ * discriminant carries the path signal for every arm at once, so each
793
+ * string-literal case is a route with no further evidence needed.
794
+ *
795
+ * Not reported by anyone; included because it is the same shape wearing
796
+ * different syntax, and waiting for a bug report per shape is how a graph stays
797
+ * permanently one idiom behind the code it indexes.
798
+ */
799
+ function collectFromSwitch(node, out, constants) {
800
+ // The grammar wraps a switch discriminant in `parenthesized_expression`,
801
+ // unlike a comparison operand.
802
+ const discriminant = unparenthesize(node.childForFieldName('value'));
803
+ if (discriminant === null || !isPathExpression(discriminant))
804
+ return;
805
+ const body = node.childForFieldName('body');
806
+ if (body === null)
807
+ return;
808
+ // The verbs governing the whole switch, if any (`if (req.method === 'GET')
809
+ // switch (pathname) { … }`). Read once — every arm shares them.
810
+ const verbs = governingVerbs(node);
811
+ for (const arm of body.namedChildren) {
812
+ if (arm.type !== 'switch_case')
813
+ continue;
814
+ const caseValue = arm.childForFieldName('value');
815
+ if (caseValue === null)
816
+ continue;
817
+ const value = literalValue(caseValue, constants);
818
+ if (value === null || !isPathLiteral(value))
819
+ continue;
820
+ if (value === '/' && verbs.length === 0)
821
+ continue;
822
+ pushPerVerb(out, verbs, {
823
+ url: value,
824
+ handlerName: enclosingHandlerName(arm),
825
+ line: arm.startPosition.row + 1,
826
+ });
827
+ }
828
+ }
829
+ /**
830
+ * Count a declarator against its key, and refuse the key once a second one
831
+ * binds it.
832
+ *
833
+ * Refusing rather than ordering costs the real route in
834
+ * `const m = pathname.match(RE); { const m = other() }` — the honest GET is
835
+ * dropped alongside the shadow that would have fabricated a DELETE. That is the
836
+ * cheaper failure by this module's own bar, and it is the same trade
837
+ * {@link buildConstantMap} makes for a name declared twice.
838
+ */
839
+ function noteDeclaration(node, matches) {
840
+ const name = node.childForFieldName('name');
841
+ if (name === null)
842
+ return;
843
+ const scopeId = enclosingScopeId(node);
844
+ for (const bound of patternNames(name)) {
845
+ const key = bindingKey(scopeId, bound);
846
+ const count = (matches.declarations.get(key) ?? 0) + 1;
847
+ matches.declarations.set(key, count);
848
+ if (count > 1)
849
+ matches.bindings.set(key, null);
850
+ }
851
+ }
852
+ /**
853
+ * Refuse a name that is ASSIGNED anywhere it could reach.
854
+ *
855
+ * `let m = pathname.match(RE); m = fallback()` leaves `m` holding something this
856
+ * walk never saw, and the declaration alone is no longer evidence of what the
857
+ * later `if (m)` tests. Poisoning pre-emptively — before the binding is even
858
+ * recorded — is what makes the order of the two statements not matter.
859
+ *
860
+ * Only a REBINDING counts. `m.index = 0` and `m[1] = x` assign THROUGH the name
861
+ * and leave it bound to the same match, so refusing on them would drop routes
862
+ * for writes that change nothing this module reads.
863
+ */
864
+ const REBINDABLE_TARGETS = new Set([
865
+ 'identifier',
866
+ 'array_pattern',
867
+ 'object_pattern',
868
+ ]);
869
+ function noteReassignment(node, matches) {
870
+ const left = node.childForFieldName('left');
871
+ if (left === null || !REBINDABLE_TARGETS.has(left.type))
872
+ return;
873
+ const names = patternNames(left);
874
+ if (names.length === 0)
875
+ return;
876
+ for (const scopeId of enclosingScopeIds(node)) {
877
+ for (const name of names)
878
+ matches.bindings.set(bindingKey(scopeId, name), null);
879
+ }
880
+ }
881
+ /**
882
+ * Same-file `const NAME = /re/` bindings, so a regex named once and used by name
883
+ * still yields its route.
884
+ *
885
+ * Verbatim from the reporting repo: `positionReplayRoutes.js` declares
886
+ * `const POSITION_REPLAY_RE = /^\/api\/live\/positions\/([^/]+)\/replay$/` at
887
+ * module scope and then uses it BOTH ways — `POSITION_REPLAY_RE.test(pathname)`
888
+ * and `pathname.match(POSITION_REPLAY_RE)`. Keying only on inline literals
889
+ * loses the whole file.
890
+ *
891
+ * Ambiguity is refused the same way {@link buildConstantMap} refuses it: a name
892
+ * bound twice to different patterns is dropped rather than resolved to the
893
+ * first, because a half-right regex is a wrong route.
894
+ *
895
+ * That refusal only ever SAW regex literals, which left the two rebindings that
896
+ * matter walking straight past it. `let RE = /^\/api\/re\/([^/]+)$/` followed by
897
+ * `RE = buildDynamic(req)` still minted the literal's route, and so did a
898
+ * `const RE = new RegExp(userPrefix + '/x')` twin in another function — the map
899
+ * is flat, so a same-named binding anywhere in the file is exactly the ambiguity
900
+ * the doc claims to refuse. A name bound to ANYTHING that is not a regex
901
+ * literal, or assigned at all, is now dropped.
902
+ */
903
+ function buildRegexConstantMap(root) {
904
+ const patterns = new Map();
905
+ const ambiguous = new Set();
906
+ const visit = (node) => {
907
+ if (node.type === 'variable_declarator') {
908
+ const name = node.childForFieldName('name');
909
+ const value = unparenthesize(node.childForFieldName('value'));
910
+ if (name !== null && name.type === 'identifier') {
911
+ const pattern = value !== null && value.type === 'regex'
912
+ ? (value.childForFieldName('pattern')?.text ?? null)
913
+ : null;
914
+ if (pattern === null)
915
+ ambiguous.add(name.text);
916
+ else {
917
+ const existing = patterns.get(name.text);
918
+ if (existing !== undefined && existing !== pattern)
919
+ ambiguous.add(name.text);
920
+ else
921
+ patterns.set(name.text, pattern);
922
+ }
923
+ }
924
+ }
925
+ if (node.type === 'assignment_expression' || node.type === 'augmented_assignment_expression') {
926
+ const left = node.childForFieldName('left');
927
+ if (left !== null && left.type === 'identifier')
928
+ ambiguous.add(left.text);
929
+ }
930
+ for (const child of node.namedChildren)
931
+ visit(child);
932
+ };
933
+ visit(root);
934
+ for (const name of ambiguous)
935
+ patterns.delete(name);
936
+ return patterns;
937
+ }
938
+ /** The regex pattern this expression denotes — inline literal or named const. */
939
+ function regexPatternOf(node, regexes) {
940
+ const expr = unparenthesize(node);
941
+ if (expr === null)
942
+ return null;
943
+ if (expr.type === 'regex')
944
+ return expr.childForFieldName('pattern')?.text ?? null;
945
+ if (expr.type === 'identifier')
946
+ return regexes.get(expr.text) ?? null;
947
+ return null;
948
+ }
949
+ /**
950
+ * A route declared by matching the path against an anchored regex.
951
+ *
952
+ * Two spellings of the same test, with the operands swapped:
953
+ *
954
+ * if (RE.test(pathname)) — receiver is the regex
955
+ * const m = pathname.match(RE) — receiver is the path
956
+ *
957
+ * Only `.test` was read, which is why 28 of the reporting repo's 75 routes still
958
+ * named the shared route table as their handler rather than the module that
959
+ * actually serves them: their modules dispatch with `.match`.
960
+ *
961
+ * `.match` differs in one way that matters. Its result is USED — it carries the
962
+ * captured segments — so it is almost always BOUND, and the verb then lives in a
963
+ * later `if` rather than around the call:
964
+ *
965
+ * const runMatch = pathname.match(/^\/api\/research-runs\/([^/]+)$/)
966
+ * if (req.method === 'GET' && runMatch) { … }
967
+ *
968
+ * Reading the verb off the CALL would report every one of those verb-less. So a
969
+ * bound match is recorded rather than emitted, and {@link collectFromMatchBindings}
970
+ * emits it where the binding is actually tested. An unbound match is a plain
971
+ * predicate and is emitted here, exactly like `.test`.
972
+ */
973
+ function collectFromRegexDispatch(node, out, regexes, matches) {
974
+ const callee = node.childForFieldName('function');
975
+ if (callee === null || callee.type !== 'member_expression')
976
+ return;
977
+ const method = callee.childForFieldName('property')?.text;
978
+ if (method !== 'test' && method !== 'match')
979
+ return;
980
+ const receiver = callee.childForFieldName('object');
981
+ const argument = node.childForFieldName('arguments')?.namedChildren[0] ?? null;
982
+ if (receiver === null || argument === null)
983
+ return;
984
+ // `RE.test(pathname)` vs `pathname.match(RE)` — the regex and the path swap
985
+ // sides with the method, so each spelling is checked in its own orientation
986
+ // rather than accepting any pairing.
987
+ const pattern = method === 'test'
988
+ ? isPathExpression(argument)
989
+ ? regexPatternOf(receiver, regexes)
990
+ : null
991
+ : isPathExpression(receiver)
992
+ ? regexPatternOf(argument, regexes)
993
+ : null;
994
+ if (pattern === null)
995
+ return;
996
+ const url = regexToRoutePath(pattern);
997
+ if (url === null)
998
+ return;
999
+ const boundName = boundDeclaratorName(node);
1000
+ if (boundName !== null) {
1001
+ // Keyed by the function this name is bound in — see MatchBindingState for
1002
+ // the routes the bare name invented. Already-refused keys stay refused, and
1003
+ // a key bound twice to DIFFERENT routes is poisoned rather than resolved to
1004
+ // the first.
1005
+ const key = bindingKey(enclosingScopeId(node), boundName);
1006
+ const existing = matches.bindings.get(key);
1007
+ if (existing !== undefined && (existing === null || existing.url !== url)) {
1008
+ matches.bindings.set(key, null);
1009
+ return;
1010
+ }
1011
+ matches.bindings.set(key, {
1012
+ name: boundName,
1013
+ url,
1014
+ line: node.startPosition.row + 1,
1015
+ handlerName: enclosingHandlerName(node),
1016
+ });
1017
+ return;
1018
+ }
1019
+ // Unbound: the call IS the predicate, so its own context carries the verb.
1020
+ if (isNegatedContext(node))
1021
+ return;
1022
+ pushPerVerb(out, governingVerbs(node), {
1023
+ url,
1024
+ handlerName: enclosingHandlerName(node),
1025
+ line: node.startPosition.row + 1,
1026
+ });
1027
+ }
1028
+ /** The name this call's result is bound to by `const NAME = <call>`, if any. */
1029
+ function boundDeclaratorName(call) {
1030
+ const parent = call.parent;
1031
+ if (parent === null || parent.type !== 'variable_declarator')
1032
+ return null;
1033
+ if (parent.childForFieldName('value')?.id !== call.id)
1034
+ return null;
1035
+ const name = parent.childForFieldName('name');
1036
+ return name !== null && name.type === 'identifier' ? name.text : null;
1037
+ }
1038
+ /**
1039
+ * Emit a route wherever a recorded match binding is TESTED.
1040
+ *
1041
+ * The binding's declaration proves a path; the test site proves the method, and
1042
+ * one binding can be tested more than once. A reference counts only in a
1043
+ * truthiness position — see {@link isTruthinessPosition} — which is what
1044
+ * separates `if (m && …)` from `m[1]`, a read of the captured segment that says
1045
+ * nothing about dispatch and would otherwise mint a duplicate route per capture
1046
+ * group used.
1047
+ *
1048
+ * A binding that is never tested still emits ONE verb-less route: the code did
1049
+ * compute an anchored match against the request path, which is the same evidence
1050
+ * an unbound `.test` carries, and dropping it would trade a known path for
1051
+ * nothing.
1052
+ *
1053
+ * The DECLARATION's own name identifier needs no special case: its parent is a
1054
+ * `variable_declarator`, which is not a truthiness position, so the same
1055
+ * predicate that rejects `m[1]` rejects it. An explicit skip was written here
1056
+ * first and removed once it proved unreachable — it read as though the
1057
+ * declaration were a hazard, which sends the next reader looking for one.
1058
+ *
1059
+ * A use counts only against a binding in ITS OWN function. `tested` is keyed the
1060
+ * same way, and that half matters as much as the emission: keyed by bare name, a
1061
+ * same-named local in another handler marked the name tested and SUPPRESSED the
1062
+ * real binding's own verb-less route from the tail loop below — so the honest
1063
+ * route was not merely joined by a fabricated one, it was replaced by it, down
1064
+ * to reporting the wrong handler and the wrong line.
1065
+ */
1066
+ function collectFromMatchBindings(root, matchBindings, out) {
1067
+ // Resolving a scope costs a walk to the function boundary, and this visits
1068
+ // every identifier in the file. Names that no live binding uses are rejected
1069
+ // on a set lookup first, so files without a bound match pay nothing.
1070
+ const liveNames = new Set();
1071
+ for (const binding of matchBindings.values())
1072
+ if (binding !== null)
1073
+ liveNames.add(binding.name);
1074
+ if (liveNames.size === 0)
1075
+ return;
1076
+ const tested = new Set();
1077
+ const visit = (node) => {
1078
+ if (node.type === 'identifier' && liveNames.has(node.text)) {
1079
+ const key = bindingKey(enclosingScopeId(node), node.text);
1080
+ const binding = matchBindings.get(key);
1081
+ if (binding !== undefined &&
1082
+ binding !== null &&
1083
+ isTruthinessPosition(node) &&
1084
+ !isNegatedContext(node)) {
1085
+ tested.add(key);
1086
+ pushPerVerb(out, governingVerbs(node), {
1087
+ url: binding.url,
1088
+ handlerName: enclosingHandlerName(node) ?? binding.handlerName,
1089
+ line: node.startPosition.row + 1,
1090
+ });
1091
+ }
1092
+ }
1093
+ for (const child of node.namedChildren)
1094
+ visit(child);
1095
+ };
1096
+ visit(root);
1097
+ for (const [key, binding] of matchBindings) {
1098
+ if (binding === null || tested.has(key))
1099
+ continue;
1100
+ out.push({
1101
+ url: binding.url,
1102
+ verb: null,
1103
+ handlerName: binding.handlerName,
1104
+ line: binding.line,
1105
+ });
1106
+ }
1107
+ }
1108
+ /**
1109
+ * Is this reference read for its TRUTH — an operand of `&&`/`||`, or the whole
1110
+ * condition of an `if`?
1111
+ *
1112
+ * Deliberately narrow. `runMatch[1]` (a `subscript_expression` parent) reads a
1113
+ * captured segment, and `validate(runMatch)` passes it along; neither asserts
1114
+ * that the request took this route, and counting them would emit one duplicate
1115
+ * route per use of the captured id. Parentheses are transparent, so
1116
+ * `if ((runMatch))` and `if (verb && (runMatch))` both count.
1117
+ */
1118
+ function isTruthinessPosition(node) {
1119
+ let current = node;
1120
+ let parent = current.parent;
1121
+ while (parent !== null && parent.type === 'parenthesized_expression') {
1122
+ current = parent;
1123
+ parent = current.parent;
1124
+ }
1125
+ if (parent === null)
1126
+ return false;
1127
+ if (parent.type === 'binary_expression') {
1128
+ const operator = parent.childForFieldName('operator')?.text;
1129
+ return operator === '&&' || operator === '||';
1130
+ }
1131
+ if (parent.type === 'if_statement') {
1132
+ return parent.childForFieldName('condition')?.id === current.id;
1133
+ }
1134
+ return false;
1135
+ }
1136
+ /**
1137
+ * Collapse duplicate `(url, verb)` findings within one file, keeping the first —
1138
+ * matching the routes phase's own first-writer-wins. The same comparison can
1139
+ * legitimately appear more than once (an early-return guard and the branch that
1140
+ * serves it), and each occurrence is the same route.
1141
+ *
1142
+ * The verb-less/verb-qualified reconciliation is deliberately NOT here — see
1143
+ * {@link reconcileDispatchGuardRoutes}, which needs the whole repo to do it.
1144
+ */
1145
+ function dedupeWithinFile(routes) {
1146
+ const seen = new Set();
1147
+ const out = [];
1148
+ for (const route of routes) {
1149
+ const key = `${route.verb ?? ''} ${route.url}`;
1150
+ if (seen.has(key))
1151
+ continue;
1152
+ seen.add(key);
1153
+ out.push(route);
1154
+ }
1155
+ return out;
1156
+ }
1157
+ /**
1158
+ * Drop a dispatch-guard route whose URL is claimed WITH a verb somewhere in the
1159
+ * repository.
1160
+ *
1161
+ * The idiom that makes this necessary is the split route table: one module lists
1162
+ * every path it recognises (`isKnownApiPath`, or a `match(method, pathname)`
1163
+ * that ORs them all) so the dispatcher can 404 early, and separate modules
1164
+ * handle each path by verb. Both are path comparisons and both are real, but
1165
+ * only the second is a route in the sense `route_map` reports — the first is a
1166
+ * membership test.
1167
+ *
1168
+ * Left alone this doubles the map: measured on the reporting repo, 94 routes of
1169
+ * which 34 were the table's verb-less shadow of a route already listed with its
1170
+ * verb and its true handler. Reconciling per-FILE cannot see it, because the
1171
+ * table and the handlers are different files; only the whole registry can.
1172
+ *
1173
+ * Applies to dispatch-guard routes only. A framework route with no verb is
1174
+ * method-agnostic BY DECLARATION (a Django function view, a Laravel resource),
1175
+ * which is a fact rather than a weaker observation, and must not be dropped.
1176
+ */
1177
+ export function reconcileDispatchGuardRoutes(routes) {
1178
+ const verbedUrls = new Set(routes
1179
+ .filter((r) => r.source === DISPATCH_GUARD_SOURCE && r.httpMethod !== '')
1180
+ .map((r) => r.routePath));
1181
+ if (verbedUrls.size === 0)
1182
+ return [...routes];
1183
+ return routes.filter((r) => !(r.source === DISPATCH_GUARD_SOURCE && r.httpMethod === '' && verbedUrls.has(r.routePath)));
1184
+ }