gitnexus 1.5.3 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (304) hide show
  1. package/README.md +10 -0
  2. package/dist/_shared/graph/types.d.ts +1 -1
  3. package/dist/_shared/graph/types.d.ts.map +1 -1
  4. package/dist/_shared/index.d.ts +1 -0
  5. package/dist/_shared/index.d.ts.map +1 -1
  6. package/dist/_shared/language-detection.d.ts.map +1 -1
  7. package/dist/_shared/language-detection.js +2 -0
  8. package/dist/_shared/language-detection.js.map +1 -1
  9. package/dist/_shared/languages.d.ts +1 -0
  10. package/dist/_shared/languages.d.ts.map +1 -1
  11. package/dist/_shared/languages.js +1 -0
  12. package/dist/_shared/languages.js.map +1 -1
  13. package/dist/_shared/lbug/schema-constants.d.ts +1 -1
  14. package/dist/_shared/lbug/schema-constants.d.ts.map +1 -1
  15. package/dist/_shared/lbug/schema-constants.js +3 -1
  16. package/dist/_shared/lbug/schema-constants.js.map +1 -1
  17. package/dist/_shared/mro-strategy.d.ts +19 -0
  18. package/dist/_shared/mro-strategy.d.ts.map +1 -0
  19. package/dist/_shared/mro-strategy.js +2 -0
  20. package/dist/_shared/mro-strategy.js.map +1 -0
  21. package/dist/cli/ai-context.d.ts +1 -0
  22. package/dist/cli/ai-context.js +28 -4
  23. package/dist/cli/analyze.d.ts +2 -0
  24. package/dist/cli/analyze.js +30 -4
  25. package/dist/cli/group.d.ts +2 -0
  26. package/dist/cli/group.js +233 -0
  27. package/dist/cli/index.js +3 -0
  28. package/dist/cli/serve.js +4 -1
  29. package/dist/cli/setup.js +34 -3
  30. package/dist/config/ignore-service.js +8 -3
  31. package/dist/core/augmentation/engine.js +1 -1
  32. package/dist/core/git-staleness.d.ts +13 -0
  33. package/dist/core/git-staleness.js +29 -0
  34. package/dist/core/group/bridge-db.d.ts +82 -0
  35. package/dist/core/group/bridge-db.js +460 -0
  36. package/dist/core/group/bridge-schema.d.ts +27 -0
  37. package/dist/core/group/bridge-schema.js +55 -0
  38. package/dist/core/group/config-parser.d.ts +3 -0
  39. package/dist/core/group/config-parser.js +83 -0
  40. package/dist/core/group/contract-extractor.d.ts +7 -0
  41. package/dist/core/group/contract-extractor.js +1 -0
  42. package/dist/core/group/extractors/fs-utils.d.ts +10 -0
  43. package/dist/core/group/extractors/fs-utils.js +24 -0
  44. package/dist/core/group/extractors/grpc-extractor.d.ts +25 -0
  45. package/dist/core/group/extractors/grpc-extractor.js +386 -0
  46. package/dist/core/group/extractors/grpc-patterns/go.d.ts +2 -0
  47. package/dist/core/group/extractors/grpc-patterns/go.js +97 -0
  48. package/dist/core/group/extractors/grpc-patterns/index.d.ts +19 -0
  49. package/dist/core/group/extractors/grpc-patterns/index.js +46 -0
  50. package/dist/core/group/extractors/grpc-patterns/java.d.ts +2 -0
  51. package/dist/core/group/extractors/grpc-patterns/java.js +173 -0
  52. package/dist/core/group/extractors/grpc-patterns/node.d.ts +4 -0
  53. package/dist/core/group/extractors/grpc-patterns/node.js +290 -0
  54. package/dist/core/group/extractors/grpc-patterns/proto.d.ts +9 -0
  55. package/dist/core/group/extractors/grpc-patterns/proto.js +134 -0
  56. package/dist/core/group/extractors/grpc-patterns/python.d.ts +2 -0
  57. package/dist/core/group/extractors/grpc-patterns/python.js +67 -0
  58. package/dist/core/group/extractors/grpc-patterns/types.d.ts +50 -0
  59. package/dist/core/group/extractors/grpc-patterns/types.js +1 -0
  60. package/dist/core/group/extractors/http-patterns/go.d.ts +2 -0
  61. package/dist/core/group/extractors/http-patterns/go.js +215 -0
  62. package/dist/core/group/extractors/http-patterns/index.d.ts +17 -0
  63. package/dist/core/group/extractors/http-patterns/index.js +44 -0
  64. package/dist/core/group/extractors/http-patterns/java.d.ts +2 -0
  65. package/dist/core/group/extractors/http-patterns/java.js +253 -0
  66. package/dist/core/group/extractors/http-patterns/node.d.ts +4 -0
  67. package/dist/core/group/extractors/http-patterns/node.js +354 -0
  68. package/dist/core/group/extractors/http-patterns/php.d.ts +2 -0
  69. package/dist/core/group/extractors/http-patterns/php.js +70 -0
  70. package/dist/core/group/extractors/http-patterns/python.d.ts +2 -0
  71. package/dist/core/group/extractors/http-patterns/python.js +133 -0
  72. package/dist/core/group/extractors/http-patterns/types.d.ts +61 -0
  73. package/dist/core/group/extractors/http-patterns/types.js +1 -0
  74. package/dist/core/group/extractors/http-route-extractor.d.ts +21 -0
  75. package/dist/core/group/extractors/http-route-extractor.js +391 -0
  76. package/dist/core/group/extractors/manifest-extractor.d.ts +54 -0
  77. package/dist/core/group/extractors/manifest-extractor.js +235 -0
  78. package/dist/core/group/extractors/topic-extractor.d.ts +8 -0
  79. package/dist/core/group/extractors/topic-extractor.js +97 -0
  80. package/dist/core/group/extractors/topic-patterns/go.d.ts +2 -0
  81. package/dist/core/group/extractors/topic-patterns/go.js +120 -0
  82. package/dist/core/group/extractors/topic-patterns/index.d.ts +14 -0
  83. package/dist/core/group/extractors/topic-patterns/index.js +38 -0
  84. package/dist/core/group/extractors/topic-patterns/java.d.ts +2 -0
  85. package/dist/core/group/extractors/topic-patterns/java.js +80 -0
  86. package/dist/core/group/extractors/topic-patterns/node.d.ts +4 -0
  87. package/dist/core/group/extractors/topic-patterns/node.js +155 -0
  88. package/dist/core/group/extractors/topic-patterns/python.d.ts +2 -0
  89. package/dist/core/group/extractors/topic-patterns/python.js +116 -0
  90. package/dist/core/group/extractors/topic-patterns/types.d.ts +25 -0
  91. package/dist/core/group/extractors/topic-patterns/types.js +10 -0
  92. package/dist/core/group/extractors/tree-sitter-scanner.d.ts +113 -0
  93. package/dist/core/group/extractors/tree-sitter-scanner.js +94 -0
  94. package/dist/core/group/matching.d.ts +13 -0
  95. package/dist/core/group/matching.js +198 -0
  96. package/dist/core/group/normalization.d.ts +3 -0
  97. package/dist/core/group/normalization.js +115 -0
  98. package/dist/core/group/service-boundary-detector.d.ts +8 -0
  99. package/dist/core/group/service-boundary-detector.js +155 -0
  100. package/dist/core/group/service.d.ts +46 -0
  101. package/dist/core/group/service.js +160 -0
  102. package/dist/core/group/storage.d.ts +9 -0
  103. package/dist/core/group/storage.js +91 -0
  104. package/dist/core/group/sync.d.ts +21 -0
  105. package/dist/core/group/sync.js +148 -0
  106. package/dist/core/group/types.d.ts +130 -0
  107. package/dist/core/group/types.js +1 -0
  108. package/dist/core/ingestion/binding-accumulator.d.ts +212 -0
  109. package/dist/core/ingestion/binding-accumulator.js +336 -0
  110. package/dist/core/ingestion/call-processor.d.ts +155 -24
  111. package/dist/core/ingestion/call-processor.js +1129 -247
  112. package/dist/core/ingestion/class-extractors/generic.d.ts +2 -0
  113. package/dist/core/ingestion/class-extractors/generic.js +135 -0
  114. package/dist/core/ingestion/class-types.d.ts +34 -0
  115. package/dist/core/ingestion/class-types.js +1 -0
  116. package/dist/core/ingestion/cobol-processor.d.ts +1 -1
  117. package/dist/core/ingestion/entry-point-scoring.d.ts +1 -0
  118. package/dist/core/ingestion/entry-point-scoring.js +1 -0
  119. package/dist/core/ingestion/field-types.d.ts +2 -2
  120. package/dist/core/ingestion/filesystem-walker.js +8 -0
  121. package/dist/core/ingestion/framework-detection.d.ts +1 -0
  122. package/dist/core/ingestion/framework-detection.js +1 -0
  123. package/dist/core/ingestion/heritage-processor.d.ts +8 -15
  124. package/dist/core/ingestion/heritage-processor.js +15 -28
  125. package/dist/core/ingestion/import-processor.d.ts +1 -11
  126. package/dist/core/ingestion/import-processor.js +1 -13
  127. package/dist/core/ingestion/import-resolvers/utils.js +1 -0
  128. package/dist/core/ingestion/import-resolvers/vue.d.ts +8 -0
  129. package/dist/core/ingestion/import-resolvers/vue.js +9 -0
  130. package/dist/core/ingestion/language-config.js +1 -1
  131. package/dist/core/ingestion/language-provider.d.ts +14 -3
  132. package/dist/core/ingestion/languages/c-cpp.js +168 -1
  133. package/dist/core/ingestion/languages/csharp.js +20 -0
  134. package/dist/core/ingestion/languages/dart.js +26 -4
  135. package/dist/core/ingestion/languages/go.js +22 -0
  136. package/dist/core/ingestion/languages/index.d.ts +1 -0
  137. package/dist/core/ingestion/languages/index.js +2 -0
  138. package/dist/core/ingestion/languages/java.js +17 -0
  139. package/dist/core/ingestion/languages/kotlin.js +24 -1
  140. package/dist/core/ingestion/languages/php.js +23 -11
  141. package/dist/core/ingestion/languages/python.js +9 -0
  142. package/dist/core/ingestion/languages/ruby.js +43 -0
  143. package/dist/core/ingestion/languages/rust.js +38 -0
  144. package/dist/core/ingestion/languages/swift.js +31 -0
  145. package/dist/core/ingestion/languages/typescript.d.ts +1 -0
  146. package/dist/core/ingestion/languages/typescript.js +52 -3
  147. package/dist/core/ingestion/languages/vue.d.ts +13 -0
  148. package/dist/core/ingestion/languages/vue.js +81 -0
  149. package/dist/core/ingestion/markdown-processor.d.ts +1 -1
  150. package/dist/core/ingestion/method-extractors/configs/c-cpp.d.ts +3 -0
  151. package/dist/core/ingestion/method-extractors/configs/c-cpp.js +387 -0
  152. package/dist/core/ingestion/method-extractors/configs/csharp.js +5 -1
  153. package/dist/core/ingestion/method-extractors/configs/dart.d.ts +2 -0
  154. package/dist/core/ingestion/method-extractors/configs/dart.js +376 -0
  155. package/dist/core/ingestion/method-extractors/configs/go.d.ts +2 -0
  156. package/dist/core/ingestion/method-extractors/configs/go.js +176 -0
  157. package/dist/core/ingestion/method-extractors/configs/jvm.js +14 -4
  158. package/dist/core/ingestion/method-extractors/configs/php.d.ts +2 -0
  159. package/dist/core/ingestion/method-extractors/configs/php.js +304 -0
  160. package/dist/core/ingestion/method-extractors/configs/python.d.ts +2 -0
  161. package/dist/core/ingestion/method-extractors/configs/python.js +309 -0
  162. package/dist/core/ingestion/method-extractors/configs/ruby.d.ts +2 -0
  163. package/dist/core/ingestion/method-extractors/configs/ruby.js +286 -0
  164. package/dist/core/ingestion/method-extractors/configs/rust.d.ts +2 -0
  165. package/dist/core/ingestion/method-extractors/configs/rust.js +195 -0
  166. package/dist/core/ingestion/method-extractors/configs/swift.d.ts +2 -0
  167. package/dist/core/ingestion/method-extractors/configs/swift.js +277 -0
  168. package/dist/core/ingestion/method-extractors/configs/typescript-javascript.js +85 -8
  169. package/dist/core/ingestion/method-extractors/generic.d.ts +6 -0
  170. package/dist/core/ingestion/method-extractors/generic.js +84 -17
  171. package/dist/core/ingestion/method-types.d.ts +29 -0
  172. package/dist/core/ingestion/model/field-registry.d.ts +18 -0
  173. package/dist/core/ingestion/model/field-registry.js +22 -0
  174. package/dist/core/ingestion/model/heritage-map.d.ts +70 -0
  175. package/dist/core/ingestion/model/heritage-map.js +159 -0
  176. package/dist/core/ingestion/model/index.d.ts +20 -0
  177. package/dist/core/ingestion/model/index.js +41 -0
  178. package/dist/core/ingestion/model/method-registry.d.ts +62 -0
  179. package/dist/core/ingestion/model/method-registry.js +130 -0
  180. package/dist/core/ingestion/model/registration-table.d.ts +139 -0
  181. package/dist/core/ingestion/model/registration-table.js +224 -0
  182. package/dist/core/ingestion/model/resolution-context.d.ts +93 -0
  183. package/dist/core/ingestion/model/resolution-context.js +337 -0
  184. package/dist/core/ingestion/model/resolve.d.ts +56 -0
  185. package/dist/core/ingestion/model/resolve.js +297 -0
  186. package/dist/core/ingestion/model/semantic-model.d.ts +86 -0
  187. package/dist/core/ingestion/model/semantic-model.js +120 -0
  188. package/dist/core/ingestion/model/symbol-table.d.ts +222 -0
  189. package/dist/core/ingestion/model/symbol-table.js +206 -0
  190. package/dist/core/ingestion/model/type-registry.d.ts +39 -0
  191. package/dist/core/ingestion/model/type-registry.js +62 -0
  192. package/dist/core/ingestion/mro-processor.d.ts +5 -4
  193. package/dist/core/ingestion/mro-processor.js +311 -107
  194. package/dist/core/ingestion/parsing-processor.d.ts +5 -4
  195. package/dist/core/ingestion/parsing-processor.js +224 -87
  196. package/dist/core/ingestion/pipeline-phases/cobol.d.ts +16 -0
  197. package/dist/core/ingestion/pipeline-phases/cobol.js +45 -0
  198. package/dist/core/ingestion/pipeline-phases/communities.d.ts +16 -0
  199. package/dist/core/ingestion/pipeline-phases/communities.js +62 -0
  200. package/dist/core/ingestion/pipeline-phases/cross-file-impl.d.ts +17 -0
  201. package/dist/core/ingestion/pipeline-phases/cross-file-impl.js +156 -0
  202. package/dist/core/ingestion/pipeline-phases/cross-file.d.ts +37 -0
  203. package/dist/core/ingestion/pipeline-phases/cross-file.js +63 -0
  204. package/dist/core/ingestion/pipeline-phases/index.d.ts +21 -0
  205. package/dist/core/ingestion/pipeline-phases/index.js +22 -0
  206. package/dist/core/ingestion/pipeline-phases/markdown.d.ts +17 -0
  207. package/dist/core/ingestion/pipeline-phases/markdown.js +33 -0
  208. package/dist/core/ingestion/pipeline-phases/mro.d.ts +18 -0
  209. package/dist/core/ingestion/pipeline-phases/mro.js +36 -0
  210. package/dist/core/ingestion/pipeline-phases/orm-extraction.d.ts +22 -0
  211. package/dist/core/ingestion/pipeline-phases/orm-extraction.js +92 -0
  212. package/dist/core/ingestion/pipeline-phases/orm.d.ts +15 -0
  213. package/dist/core/ingestion/pipeline-phases/orm.js +74 -0
  214. package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +47 -0
  215. package/dist/core/ingestion/pipeline-phases/parse-impl.js +437 -0
  216. package/dist/core/ingestion/pipeline-phases/parse.d.ts +49 -0
  217. package/dist/core/ingestion/pipeline-phases/parse.js +33 -0
  218. package/dist/core/ingestion/pipeline-phases/processes.d.ts +16 -0
  219. package/dist/core/ingestion/pipeline-phases/processes.js +143 -0
  220. package/dist/core/ingestion/pipeline-phases/routes.d.ts +21 -0
  221. package/dist/core/ingestion/pipeline-phases/routes.js +243 -0
  222. package/dist/core/ingestion/pipeline-phases/runner.d.ts +22 -0
  223. package/dist/core/ingestion/pipeline-phases/runner.js +203 -0
  224. package/dist/core/ingestion/pipeline-phases/scan.d.ts +21 -0
  225. package/dist/core/ingestion/pipeline-phases/scan.js +46 -0
  226. package/dist/core/ingestion/pipeline-phases/structure.d.ts +27 -0
  227. package/dist/core/ingestion/pipeline-phases/structure.js +35 -0
  228. package/dist/core/ingestion/pipeline-phases/tools.d.ts +20 -0
  229. package/dist/core/ingestion/pipeline-phases/tools.js +79 -0
  230. package/dist/core/ingestion/pipeline-phases/types.d.ts +79 -0
  231. package/dist/core/ingestion/pipeline-phases/types.js +37 -0
  232. package/dist/core/ingestion/pipeline-phases/wildcard-synthesis.d.ts +35 -0
  233. package/dist/core/ingestion/pipeline-phases/wildcard-synthesis.js +174 -0
  234. package/dist/core/ingestion/pipeline.d.ts +18 -10
  235. package/dist/core/ingestion/pipeline.js +66 -1410
  236. package/dist/core/ingestion/process-processor.js +1 -1
  237. package/dist/core/ingestion/tree-sitter-queries.d.ts +5 -5
  238. package/dist/core/ingestion/tree-sitter-queries.js +90 -0
  239. package/dist/core/ingestion/type-env.d.ts +15 -2
  240. package/dist/core/ingestion/type-env.js +163 -102
  241. package/dist/core/ingestion/type-extractors/csharp.js +17 -0
  242. package/dist/core/ingestion/type-extractors/jvm.js +11 -0
  243. package/dist/core/ingestion/type-extractors/php.js +0 -55
  244. package/dist/core/ingestion/type-extractors/ruby.js +0 -32
  245. package/dist/core/ingestion/type-extractors/swift.js +13 -0
  246. package/dist/core/ingestion/type-extractors/types.d.ts +8 -8
  247. package/dist/core/ingestion/type-extractors/typescript.js +66 -69
  248. package/dist/core/ingestion/utils/ast-helpers.d.ts +32 -44
  249. package/dist/core/ingestion/utils/ast-helpers.js +157 -573
  250. package/dist/core/ingestion/utils/env.d.ts +10 -0
  251. package/dist/core/ingestion/utils/env.js +10 -0
  252. package/dist/core/ingestion/utils/graph-sort.d.ts +58 -0
  253. package/dist/core/ingestion/utils/graph-sort.js +100 -0
  254. package/dist/core/ingestion/utils/method-props.d.ts +32 -0
  255. package/dist/core/ingestion/utils/method-props.js +147 -0
  256. package/dist/core/ingestion/vue-sfc-extractor.d.ts +44 -0
  257. package/dist/core/ingestion/vue-sfc-extractor.js +94 -0
  258. package/dist/core/ingestion/workers/parse-worker.d.ts +31 -19
  259. package/dist/core/ingestion/workers/parse-worker.js +469 -200
  260. package/dist/core/lbug/lbug-adapter.d.ts +6 -0
  261. package/dist/core/lbug/lbug-adapter.js +134 -27
  262. package/dist/core/lbug/pool-adapter.d.ts +76 -0
  263. package/dist/core/lbug/pool-adapter.js +522 -0
  264. package/dist/core/run-analyze.d.ts +2 -0
  265. package/dist/core/run-analyze.js +1 -1
  266. package/dist/core/search/bm25-index.js +1 -1
  267. package/dist/core/tree-sitter/parser-loader.js +1 -0
  268. package/dist/core/wiki/graph-queries.js +1 -1
  269. package/dist/mcp/core/embedder.js +6 -5
  270. package/dist/mcp/core/lbug-adapter.d.ts +3 -63
  271. package/dist/mcp/core/lbug-adapter.js +3 -484
  272. package/dist/mcp/local/local-backend.d.ts +31 -2
  273. package/dist/mcp/local/local-backend.js +255 -46
  274. package/dist/mcp/resources.js +5 -4
  275. package/dist/mcp/staleness.d.ts +3 -13
  276. package/dist/mcp/staleness.js +2 -31
  277. package/dist/mcp/tools.js +80 -4
  278. package/dist/server/analyze-job.d.ts +2 -0
  279. package/dist/server/analyze-job.js +4 -0
  280. package/dist/server/api.d.ts +20 -1
  281. package/dist/server/api.js +306 -71
  282. package/dist/server/git-clone.d.ts +2 -1
  283. package/dist/server/git-clone.js +98 -5
  284. package/dist/storage/git.d.ts +13 -0
  285. package/dist/storage/git.js +25 -0
  286. package/dist/storage/repo-manager.js +1 -1
  287. package/package.json +9 -3
  288. package/scripts/patch-tree-sitter-swift.cjs +78 -0
  289. package/vendor/tree-sitter-proto/binding.gyp +30 -0
  290. package/vendor/tree-sitter-proto/bindings/node/binding.cc +20 -0
  291. package/vendor/tree-sitter-proto/bindings/node/index.d.ts +28 -0
  292. package/vendor/tree-sitter-proto/bindings/node/index.js +7 -0
  293. package/vendor/tree-sitter-proto/package.json +18 -0
  294. package/vendor/tree-sitter-proto/src/node-types.json +1145 -0
  295. package/vendor/tree-sitter-proto/src/parser.c +10149 -0
  296. package/vendor/tree-sitter-proto/src/tree_sitter/alloc.h +54 -0
  297. package/vendor/tree-sitter-proto/src/tree_sitter/array.h +291 -0
  298. package/vendor/tree-sitter-proto/src/tree_sitter/parser.h +266 -0
  299. package/dist/core/ingestion/named-binding-processor.d.ts +0 -18
  300. package/dist/core/ingestion/named-binding-processor.js +0 -42
  301. package/dist/core/ingestion/resolution-context.d.ts +0 -58
  302. package/dist/core/ingestion/resolution-context.js +0 -135
  303. package/dist/core/ingestion/symbol-table.d.ts +0 -79
  304. package/dist/core/ingestion/symbol-table.js +0 -115
@@ -0,0 +1,133 @@
1
+ import Python from 'tree-sitter-python';
2
+ import { compilePatterns, runCompiledPatterns, unquoteLiteral, } from '../tree-sitter-scanner.js';
3
+ /**
4
+ * Python HTTP plugin. Handles:
5
+ * - FastAPI `@app.get("/path")` provider decorators
6
+ * - `requests.get/post/...("url")` consumer calls
7
+ * - Generic `requests.request("METHOD", "url")` consumer calls
8
+ */
9
+ const FASTAPI_VERBS = {
10
+ get: 'GET',
11
+ post: 'POST',
12
+ put: 'PUT',
13
+ delete: 'DELETE',
14
+ patch: 'PATCH',
15
+ };
16
+ // ─── Provider: FastAPI @app.get/... ──────────────────────────────────
17
+ const FASTAPI_PATTERNS = compilePatterns({
18
+ name: 'python-fastapi',
19
+ language: Python,
20
+ patterns: [
21
+ {
22
+ meta: {},
23
+ query: `
24
+ (decorator
25
+ (call
26
+ function: (attribute
27
+ object: (identifier) @obj (#eq? @obj "app")
28
+ attribute: (identifier) @method (#match? @method "^(get|post|put|delete|patch)$"))
29
+ arguments: (argument_list . (string) @path)))
30
+ `,
31
+ },
32
+ ],
33
+ });
34
+ // ─── Consumer: requests.get/post/... ──────────────────────────────────
35
+ const REQUESTS_VERB_PATTERNS = compilePatterns({
36
+ name: 'python-requests-verb',
37
+ language: Python,
38
+ patterns: [
39
+ {
40
+ meta: {},
41
+ query: `
42
+ (call
43
+ function: (attribute
44
+ object: (identifier) @obj (#eq? @obj "requests")
45
+ attribute: (identifier) @method (#match? @method "^(get|post|put|delete|patch)$"))
46
+ arguments: (argument_list . (string) @path))
47
+ `,
48
+ },
49
+ ],
50
+ });
51
+ // ─── Consumer: requests.request("METHOD", "url") ─────────────────────
52
+ const REQUESTS_GENERIC_PATTERNS = compilePatterns({
53
+ name: 'python-requests-generic',
54
+ language: Python,
55
+ patterns: [
56
+ {
57
+ meta: {},
58
+ query: `
59
+ (call
60
+ function: (attribute
61
+ object: (identifier) @obj (#eq? @obj "requests")
62
+ attribute: (identifier) @method (#eq? @method "request"))
63
+ arguments: (argument_list . (string) @http_method (string) @path))
64
+ `,
65
+ },
66
+ ],
67
+ });
68
+ export const PYTHON_HTTP_PLUGIN = {
69
+ name: 'python-http',
70
+ language: Python,
71
+ scan(tree) {
72
+ const out = [];
73
+ // Providers: FastAPI
74
+ for (const match of runCompiledPatterns(FASTAPI_PATTERNS, tree)) {
75
+ const methodNode = match.captures.method;
76
+ const pathNode = match.captures.path;
77
+ if (!methodNode || !pathNode)
78
+ continue;
79
+ const httpMethod = FASTAPI_VERBS[methodNode.text];
80
+ if (!httpMethod)
81
+ continue;
82
+ const path = unquoteLiteral(pathNode.text);
83
+ if (path === null)
84
+ continue;
85
+ out.push({
86
+ role: 'provider',
87
+ framework: 'fastapi',
88
+ method: httpMethod,
89
+ path,
90
+ name: null,
91
+ confidence: 0.8,
92
+ });
93
+ }
94
+ // Consumers: requests.<verb>
95
+ for (const match of runCompiledPatterns(REQUESTS_VERB_PATTERNS, tree)) {
96
+ const methodNode = match.captures.method;
97
+ const pathNode = match.captures.path;
98
+ if (!methodNode || !pathNode)
99
+ continue;
100
+ const path = unquoteLiteral(pathNode.text);
101
+ if (path === null)
102
+ continue;
103
+ out.push({
104
+ role: 'consumer',
105
+ framework: 'python-requests',
106
+ method: methodNode.text.toUpperCase(),
107
+ path,
108
+ name: null,
109
+ confidence: 0.7,
110
+ });
111
+ }
112
+ // Consumers: requests.request("METHOD", "url")
113
+ for (const match of runCompiledPatterns(REQUESTS_GENERIC_PATTERNS, tree)) {
114
+ const methodNode = match.captures.http_method;
115
+ const pathNode = match.captures.path;
116
+ if (!methodNode || !pathNode)
117
+ continue;
118
+ const methodRaw = unquoteLiteral(methodNode.text);
119
+ const path = unquoteLiteral(pathNode.text);
120
+ if (methodRaw === null || path === null)
121
+ continue;
122
+ out.push({
123
+ role: 'consumer',
124
+ framework: 'python-requests',
125
+ method: methodRaw.toUpperCase(),
126
+ path,
127
+ name: null,
128
+ confidence: 0.7,
129
+ });
130
+ }
131
+ return out;
132
+ },
133
+ };
@@ -0,0 +1,61 @@
1
+ import type Parser from 'tree-sitter';
2
+ /**
3
+ * Shared types for the http-route-extractor language plugins.
4
+ *
5
+ * Each plugin lives in its own file (java.ts, node.ts, ...) and owns
6
+ * the tree-sitter grammar import + queries. The top-level
7
+ * `http-route-extractor.ts` orchestrator only knows about this type
8
+ * module and the plugin registry (`./index.ts`). It MUST NOT import
9
+ * any grammar or query text directly — language-specific knowledge
10
+ * belongs in the plugins.
11
+ */
12
+ export type HttpRole = 'provider' | 'consumer';
13
+ /**
14
+ * One raw HTTP detection produced by a plugin's `scan()` function. The
15
+ * orchestrator converts this into a full `ExtractedContract` by running
16
+ * path normalization and building the contract id.
17
+ *
18
+ * `path` is the raw literal string as it appeared in source (with
19
+ * `${...}` template placeholders still in place); the orchestrator
20
+ * runs the appropriate normalizer for provider vs. consumer paths.
21
+ */
22
+ export interface HttpDetection {
23
+ role: HttpRole;
24
+ /** Short framework label, e.g. `'spring'`, `'nest'`, `'express'`. */
25
+ framework: string;
26
+ /** HTTP method in upper case (`'GET'`, `'POST'`, ...). */
27
+ method: string;
28
+ /** Raw path literal as seen in source (template placeholders intact). */
29
+ path: string;
30
+ /**
31
+ * Symbol name of the handler (for providers) or calling function
32
+ * (for consumers) when the plugin can determine it structurally.
33
+ * Null when no good candidate is available.
34
+ */
35
+ name: string | null;
36
+ /** Confidence in (0, 1]. Source-scan plugins typically use 0.7–0.8. */
37
+ confidence: number;
38
+ }
39
+ /**
40
+ * One language-scoped HTTP plugin. The plugin owns the tree-sitter
41
+ * grammar and the `scan` function that translates a parsed tree into
42
+ * zero or more `HttpDetection`s. Plugins are free to run multiple
43
+ * compiled pattern bundles internally (see the shared scanner's
44
+ * `runCompiledPatterns` helper).
45
+ *
46
+ * `language` is typed as `unknown` for the same reason as
47
+ * `LanguagePatterns.language` in `tree-sitter-scanner.ts` — the
48
+ * grammar modules export different shapes.
49
+ */
50
+ export interface HttpLanguagePlugin {
51
+ /** Human-readable plugin name for diagnostics. */
52
+ name: string;
53
+ /** tree-sitter grammar object (passed to the shared parser). */
54
+ language: unknown;
55
+ /**
56
+ * Scan a parsed tree and return zero or more HTTP detections. Plugins
57
+ * must not throw — they should swallow per-match errors so a single
58
+ * malformed construct does not abort the whole file.
59
+ */
60
+ scan(tree: Parser.Tree): HttpDetection[];
61
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,21 @@
1
+ import type { ContractExtractor, CypherExecutor } from '../contract-extractor.js';
2
+ import type { ExtractedContract, RepoHandle } from '../types.js';
3
+ /**
4
+ * Canonicalize a provider-side HTTP path for contract-id generation:
5
+ * - strip query string
6
+ * - lower-case
7
+ * - drop trailing slash
8
+ * - collapse `:id`, `{id}`, `[id]` path params into a single `{param}`
9
+ */
10
+ export declare function normalizeHttpPath(p: string): string;
11
+ export declare class HttpRouteExtractor implements ContractExtractor {
12
+ type: "http";
13
+ canExtract(_repo: RepoHandle): Promise<boolean>;
14
+ extract(dbExecutor: CypherExecutor | null, repoPath: string, _repo: RepoHandle): Promise<ExtractedContract[]>;
15
+ private scanFiles;
16
+ private extractProvidersGraph;
17
+ private extractProvidersSourceScan;
18
+ private extractConsumersGraph;
19
+ private extractConsumersSourceScan;
20
+ private dedupeContracts;
21
+ }
@@ -0,0 +1,391 @@
1
+ import * as path from 'node:path';
2
+ import { glob } from 'glob';
3
+ import Parser from 'tree-sitter';
4
+ import { readSafe } from './fs-utils.js';
5
+ import { getPluginForFile, HTTP_SCAN_GLOB } from './http-patterns/index.js';
6
+ /**
7
+ * Language-agnostic orchestrator for HTTP route (provider + consumer)
8
+ * contract extraction. Two strategies, in order of preference per role:
9
+ *
10
+ * 1. **Graph-assisted (Strategy A)** — if a per-repo LadybugDB executor
11
+ * is available, read `HANDLES_ROUTE` / `FETCHES` Cypher edges that
12
+ * the ingestion pipeline already produced via tree-sitter. This is
13
+ * the preferred path because the graph has richer symbol metadata
14
+ * (real uids, class/method structure, etc.).
15
+ *
16
+ * 2. **Source-scan fallback (Strategy B)** — parse files directly with
17
+ * the per-language plugin registry in `./http-patterns/`. Used when
18
+ * the graph has no routes/fetches for this repo (e.g. a repo that
19
+ * hasn't been indexed yet, or whose indexer doesn't know the
20
+ * framework). Each plugin owns its tree-sitter grammar and query
21
+ * sources — this orchestrator imports NO grammars or query strings.
22
+ *
23
+ * Adding a new language for Strategy B is a one-file edit in
24
+ * `http-patterns/index.ts`: register a new `HttpLanguagePlugin` and
25
+ * widen `HTTP_SCAN_GLOB` if needed.
26
+ */
27
+ // ─── Graph-assisted queries ──────────────────────────────────────────
28
+ const HANDLES_ROUTE_QUERY = `
29
+ MATCH (handlerFile:File)-[r:CodeRelation {type: 'HANDLES_ROUTE'}]->(route:Route)
30
+ RETURN handlerFile.id AS fileId, handlerFile.filePath AS filePath,
31
+ route.name AS routePath, route.id AS routeId,
32
+ route.responseKeys AS responseKeys,
33
+ r.reason AS routeSource`;
34
+ const FETCHES_QUERY = `
35
+ MATCH (callerFile:File)-[r:CodeRelation {type: 'FETCHES'}]->(route:Route)
36
+ RETURN callerFile.id AS fileId, callerFile.filePath AS filePath,
37
+ route.name AS routePath, route.id AS routeId,
38
+ r.reason AS fetchReason`;
39
+ const CONTAINS_QUERY = `
40
+ MATCH (file:File {id: $fileId})<-[:CodeRelation {type: 'CONTAINS'}]-(sym)
41
+ WHERE sym.startLine IS NOT NULL
42
+ RETURN sym.id AS uid, sym.name AS name, sym.filePath AS filePath, labels(sym) AS labels
43
+ ORDER BY sym.startLine`;
44
+ // ─── Path normalization (shared between provider / consumer paths) ──
45
+ /**
46
+ * Canonicalize a provider-side HTTP path for contract-id generation:
47
+ * - strip query string
48
+ * - lower-case
49
+ * - drop trailing slash
50
+ * - collapse `:id`, `{id}`, `[id]` path params into a single `{param}`
51
+ */
52
+ export function normalizeHttpPath(p) {
53
+ let s = p.trim().split('?')[0].toLowerCase().replace(/\/+$/, '');
54
+ s = s.replace(/:\w+/g, '{param}');
55
+ s = s.replace(/\{[^}]+\}/g, '{param}');
56
+ s = s.replace(/\[[^\]]+\]/g, '{param}');
57
+ // Preserve root: after stripping trailing slashes, the root "/"
58
+ // collapses to "" which would produce malformed contract ids like
59
+ // `http::GET::`. Restore a single slash for the root case.
60
+ return s === '' ? '/' : s;
61
+ }
62
+ /**
63
+ * Consumer-side normalization is more aggressive:
64
+ * - template literals (`${x}`) → `{param}`
65
+ * - strip protocol + host if the URL is absolute
66
+ * - numeric segments → `{param}` (so `/api/orders/42` → `/api/orders/{param}`)
67
+ */
68
+ function normalizeConsumerPath(url) {
69
+ const templated = url.replace(/\$\{[^}]+\}/g, '{param}').trim();
70
+ let pathOnly = templated;
71
+ if (/^https?:\/\//i.test(templated)) {
72
+ try {
73
+ pathOnly = new URL(templated).pathname;
74
+ }
75
+ catch {
76
+ pathOnly = templated.replace(/^https?:\/\/[^/]+/i, '');
77
+ }
78
+ }
79
+ const normalized = normalizeHttpPath(pathOnly || '/');
80
+ const segments = normalized
81
+ .split('/')
82
+ .filter(Boolean)
83
+ .map((segment) => (/^\d+$/.test(segment) ? '{param}' : segment));
84
+ return `/${segments.join('/')}`.replace(/\/+$/, '') || '/';
85
+ }
86
+ function contractIdFor(method, pathNorm) {
87
+ return `http::${method.toUpperCase()}::${pathNorm}`;
88
+ }
89
+ // ─── Graph row helpers ───────────────────────────────────────────────
90
+ function methodFromRouteReason(reason) {
91
+ const r = reason || '';
92
+ if (/GetMapping|decorator-Get/i.test(r))
93
+ return 'GET';
94
+ if (/PostMapping|decorator-Post/i.test(r))
95
+ return 'POST';
96
+ if (/PutMapping|decorator-Put/i.test(r))
97
+ return 'PUT';
98
+ if (/DeleteMapping|decorator-Delete/i.test(r))
99
+ return 'DELETE';
100
+ if (/PatchMapping|decorator-Patch/i.test(r))
101
+ return 'PATCH';
102
+ return null;
103
+ }
104
+ function pickSymbolUid(rows, preferredName) {
105
+ const norm = (x) => String(x ?? '');
106
+ const labeled = rows.filter((r) => {
107
+ const labels = r.labels ?? r[3];
108
+ const s = JSON.stringify(labels);
109
+ return s.includes('Method') || s.includes('Function');
110
+ });
111
+ const pool = labeled.length > 0 ? labeled : rows;
112
+ if (preferredName) {
113
+ const hit = pool.find((r) => norm(r.name ?? r[1]) === preferredName);
114
+ if (hit) {
115
+ return {
116
+ uid: norm(hit.uid ?? hit[0]),
117
+ name: norm(hit.name ?? hit[1]),
118
+ filePath: norm(hit.filePath ?? hit[2]),
119
+ };
120
+ }
121
+ }
122
+ const first = pool[0] || rows[0];
123
+ return {
124
+ uid: norm(first?.uid ?? first?.[0]),
125
+ name: norm(first?.name ?? first?.[1]),
126
+ filePath: norm(first?.filePath ?? first?.[2]),
127
+ };
128
+ }
129
+ // ─── Orchestrator ────────────────────────────────────────────────────
130
+ export class HttpRouteExtractor {
131
+ type = 'http';
132
+ async canExtract(_repo) {
133
+ return true;
134
+ }
135
+ async extract(dbExecutor, repoPath, _repo) {
136
+ // Parse each file at most once and reuse the plugin results across
137
+ // both graph-assisted enrichment and source-scan emission.
138
+ const parser = new Parser();
139
+ const cachedDetections = new Map();
140
+ const getDetections = (rel) => {
141
+ const cached = cachedDetections.get(rel);
142
+ if (cached)
143
+ return cached;
144
+ const plugin = getPluginForFile(rel);
145
+ if (!plugin) {
146
+ cachedDetections.set(rel, []);
147
+ return [];
148
+ }
149
+ const content = readSafe(repoPath, rel);
150
+ if (!content) {
151
+ cachedDetections.set(rel, []);
152
+ return [];
153
+ }
154
+ try {
155
+ parser.setLanguage(plugin.language);
156
+ const tree = parser.parse(content);
157
+ const detections = plugin.scan(tree);
158
+ cachedDetections.set(rel, detections);
159
+ return detections;
160
+ }
161
+ catch {
162
+ cachedDetections.set(rel, []);
163
+ return [];
164
+ }
165
+ };
166
+ // Glob the source-scan file list at most once per extract() —
167
+ // both provider and consumer fallback paths share the same list.
168
+ let scannedFiles = null;
169
+ const getScannedFiles = async () => {
170
+ if (scannedFiles)
171
+ return scannedFiles;
172
+ scannedFiles = await this.scanFiles(repoPath);
173
+ return scannedFiles;
174
+ };
175
+ const graphProviders = dbExecutor != null ? await this.extractProvidersGraph(dbExecutor, getDetections) : [];
176
+ const providers = graphProviders.length > 0
177
+ ? graphProviders
178
+ : this.extractProvidersSourceScan(await getScannedFiles(), getDetections);
179
+ const graphConsumers = dbExecutor != null ? await this.extractConsumersGraph(dbExecutor, getDetections) : [];
180
+ const consumers = graphConsumers.length > 0
181
+ ? graphConsumers
182
+ : this.extractConsumersSourceScan(await getScannedFiles(), getDetections);
183
+ return [...providers, ...consumers];
184
+ }
185
+ async scanFiles(repoPath) {
186
+ return glob(HTTP_SCAN_GLOB, {
187
+ cwd: repoPath,
188
+ ignore: ['**/node_modules/**', '**/.git/**', '**/dist/**', '**/build/**', '**/vendor/**'],
189
+ nodir: true,
190
+ });
191
+ }
192
+ // ─── Graph-assisted providers ──────────────────────────────────────
193
+ async extractProvidersGraph(db, getDetections) {
194
+ const out = [];
195
+ let rows;
196
+ try {
197
+ rows = await db(HANDLES_ROUTE_QUERY);
198
+ }
199
+ catch {
200
+ return [];
201
+ }
202
+ for (const row of rows) {
203
+ const filePath = String(row.filePath ?? '');
204
+ const routePath = String(row.routePath ?? '');
205
+ const routeSource = String(row.routeSource ?? row.routeReason ?? '');
206
+ let method = methodFromRouteReason(routeSource);
207
+ // Look up handler name (and backfill method if missing) from the
208
+ // plugin's scan of the handler file. This replaces the old
209
+ // regex-based `inferMethodFromFileScan` and `pickJavaHandlerName`
210
+ // helpers — tree-sitter gives both pieces of information
211
+ // structurally. Always run the lookup: even when method is set by
212
+ // `methodFromRouteReason`, we still need the handler name.
213
+ const detections = filePath ? getDetections(filePath) : [];
214
+ const providerDetections = detections.filter((d) => d.role === 'provider');
215
+ let handlerName = null;
216
+ const normalizedRoute = normalizeHttpPath(routePath);
217
+ const match = providerDetections.find((d) => normalizeHttpPath(d.path) === normalizedRoute);
218
+ if (match) {
219
+ if (!method)
220
+ method = match.method;
221
+ handlerName = match.name;
222
+ }
223
+ if (!method)
224
+ method = 'GET';
225
+ const pathNorm = normalizeHttpPath(routePath);
226
+ const cid = contractIdFor(method, pathNorm);
227
+ let symbolUid = '';
228
+ let symbolName = path.basename(filePath) || 'handler';
229
+ let symPath = filePath;
230
+ const fileId = row.fileId ?? row[0];
231
+ if (fileId) {
232
+ try {
233
+ const syms = await db(CONTAINS_QUERY, { fileId });
234
+ if (syms.length > 0) {
235
+ const picked = pickSymbolUid(syms, handlerName);
236
+ symbolUid = picked.uid;
237
+ symbolName = picked.name;
238
+ symPath = picked.filePath || filePath;
239
+ }
240
+ }
241
+ catch {
242
+ /* ignore */
243
+ }
244
+ }
245
+ out.push({
246
+ contractId: cid,
247
+ type: 'http',
248
+ role: 'provider',
249
+ symbolUid,
250
+ symbolRef: { filePath: symPath, name: symbolName },
251
+ symbolName,
252
+ confidence: 0.9,
253
+ meta: {
254
+ method,
255
+ path: pathNorm,
256
+ pathSegments: pathNorm.split('/').filter(Boolean),
257
+ extractionStrategy: 'graph_assisted',
258
+ routeSource,
259
+ },
260
+ });
261
+ }
262
+ return out;
263
+ }
264
+ // ─── Source-scan providers ─────────────────────────────────────────
265
+ extractProvidersSourceScan(files, getDetections) {
266
+ const out = [];
267
+ for (const rel of files) {
268
+ const detections = getDetections(rel);
269
+ for (const d of detections) {
270
+ if (d.role !== 'provider')
271
+ continue;
272
+ const pathNorm = normalizeHttpPath(d.path);
273
+ out.push({
274
+ contractId: contractIdFor(d.method, pathNorm),
275
+ type: 'http',
276
+ role: 'provider',
277
+ symbolUid: '',
278
+ symbolRef: { filePath: rel, name: d.name ?? 'handler' },
279
+ symbolName: d.name ?? 'handler',
280
+ confidence: d.confidence,
281
+ meta: {
282
+ method: d.method,
283
+ path: pathNorm,
284
+ pathSegments: pathNorm.split('/').filter(Boolean),
285
+ extractionStrategy: 'source_scan',
286
+ framework: d.framework,
287
+ },
288
+ });
289
+ }
290
+ }
291
+ return this.dedupeContracts(out);
292
+ }
293
+ // ─── Graph-assisted consumers ──────────────────────────────────────
294
+ async extractConsumersGraph(db, getDetections) {
295
+ const out = [];
296
+ let rows;
297
+ try {
298
+ rows = await db(FETCHES_QUERY);
299
+ }
300
+ catch {
301
+ return [];
302
+ }
303
+ for (const row of rows) {
304
+ const filePath = String(row.filePath ?? '');
305
+ const routePath = String(row.routePath ?? '');
306
+ const pathNorm = normalizeHttpPath(routePath);
307
+ let method = 'GET';
308
+ // Prefer the plugin's detected method if we can find a matching
309
+ // fetch/axios call in the same file.
310
+ const detections = filePath ? getDetections(filePath) : [];
311
+ const inferred = detections.find((d) => d.role === 'consumer' && normalizeConsumerPath(d.path) === pathNorm);
312
+ if (inferred)
313
+ method = inferred.method;
314
+ const cid = contractIdFor(method, pathNorm);
315
+ let symbolUid = '';
316
+ let symbolName = 'fetch';
317
+ let symPath = filePath;
318
+ const fileId = row.fileId ?? row[0];
319
+ if (fileId) {
320
+ try {
321
+ const syms = await db(CONTAINS_QUERY, { fileId });
322
+ if (syms.length > 0) {
323
+ const picked = pickSymbolUid(syms, null);
324
+ symbolUid = picked.uid;
325
+ symbolName = picked.name;
326
+ symPath = picked.filePath || filePath;
327
+ }
328
+ }
329
+ catch {
330
+ /* ignore */
331
+ }
332
+ }
333
+ out.push({
334
+ contractId: cid,
335
+ type: 'http',
336
+ role: 'consumer',
337
+ symbolUid,
338
+ symbolRef: { filePath: symPath, name: symbolName },
339
+ symbolName,
340
+ confidence: 0.9,
341
+ meta: {
342
+ method,
343
+ path: pathNorm,
344
+ extractionStrategy: 'graph_assisted',
345
+ fetchReason: String(row.fetchReason ?? ''),
346
+ },
347
+ });
348
+ }
349
+ return out;
350
+ }
351
+ // ─── Source-scan consumers ─────────────────────────────────────────
352
+ extractConsumersSourceScan(files, getDetections) {
353
+ const out = [];
354
+ for (const rel of files) {
355
+ const detections = getDetections(rel);
356
+ for (const d of detections) {
357
+ if (d.role !== 'consumer')
358
+ continue;
359
+ const pathNorm = normalizeConsumerPath(d.path);
360
+ out.push({
361
+ contractId: contractIdFor(d.method, pathNorm),
362
+ type: 'http',
363
+ role: 'consumer',
364
+ symbolUid: '',
365
+ symbolRef: { filePath: rel, name: 'fetch' },
366
+ symbolName: 'fetch',
367
+ confidence: d.confidence,
368
+ meta: {
369
+ method: d.method,
370
+ path: pathNorm,
371
+ extractionStrategy: 'source_scan',
372
+ framework: d.framework,
373
+ },
374
+ });
375
+ }
376
+ }
377
+ return this.dedupeContracts(out);
378
+ }
379
+ dedupeContracts(items) {
380
+ const seen = new Set();
381
+ const out = [];
382
+ for (const c of items) {
383
+ const k = `${c.contractId}|${c.symbolRef.filePath}|${c.symbolRef.name}`;
384
+ if (seen.has(k))
385
+ continue;
386
+ seen.add(k);
387
+ out.push(c);
388
+ }
389
+ return out;
390
+ }
391
+ }
@@ -0,0 +1,54 @@
1
+ import type { CrossLink, GroupManifestLink, StoredContract } from '../types.js';
2
+ import type { CypherExecutor } from '../contract-extractor.js';
3
+ export interface ManifestExtractResult {
4
+ contracts: StoredContract[];
5
+ crossLinks: CrossLink[];
6
+ }
7
+ /**
8
+ * Stable synthetic symbolUid for a manifest-declared contract whose target
9
+ * symbol could not be resolved against the per-repo graph (resolveSymbol
10
+ * returned null). Two reasons we don't leave the uid empty:
11
+ *
12
+ * 1. The bridge stores Contract nodes keyed in part by symbolUid; an empty
13
+ * uid means downstream Cypher queries that anchor on `provider.symbolUid`
14
+ * can't tell two different unresolved manifest contracts apart.
15
+ * 2. The cross-impact bridge query in cross-impact.ts joins local impact
16
+ * results to bridge contracts via `WHERE provider.symbolUid IN $localUids`.
17
+ * If the local impact engine produces a deterministic identifier for the
18
+ * unresolved target, it must agree with the value the bridge stored. A
19
+ * synthetic uid keyed off (repo, contractId) is the only thing both sides
20
+ * can derive without knowing about each other.
21
+ *
22
+ * Format: `manifest::<repo>::<contractId>`. Stable across syncs, scoped to a
23
+ * single repo within a group, and never collides with real indexer uids
24
+ * (which never start with `manifest::`).
25
+ */
26
+ export declare function manifestSymbolUid(repo: string, contractId: string): string;
27
+ export declare class ManifestExtractor {
28
+ extractFromManifest(links: GroupManifestLink[], dbExecutors?: Map<string, CypherExecutor>): Promise<ManifestExtractResult>;
29
+ private resolveSymbol;
30
+ /**
31
+ * Build a canonical contract id for a manifest link.
32
+ *
33
+ * HTTP is the only type with two valid forms:
34
+ * - Explicit method: `"GET::/api/orders"` → `"http::GET::/api/orders"`
35
+ * (matches exactly against `HttpRouteExtractor` provider/consumer
36
+ * contracts, which are also keyed by `http::<METHOD>::<path>`).
37
+ * - Method-agnostic: `"/api/orders"` → `"http::*::/api/orders"`
38
+ * — the `*` is a wildcard and is intended to match any concrete
39
+ * HTTP method on that path. Wildcard-aware matching is the
40
+ * responsibility of the sync / cross-impact layer (see #793);
41
+ * downstream code should treat `http::*::<path>` as matching
42
+ * every `http::<METHOD>::<path>` for the same path.
43
+ *
44
+ * Recommend the explicit-method form in group.yaml whenever the
45
+ * manifest author knows the method — it round-trips through exact
46
+ * equality matching without requiring wildcard logic downstream.
47
+ *
48
+ * NOTE on exhaustiveness: the switch covers every current
49
+ * `ContractType` variant and falls through to a `never` assertion so
50
+ * TypeScript fails the build if a new variant is added without a
51
+ * corresponding case.
52
+ */
53
+ private buildContractId;
54
+ }