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,27 @@
1
+ /**
2
+ * Bridge LadybugDB schema for cross-repo Contract Registry.
3
+ * Separate from per-repo schema in lbug/schema.ts.
4
+ */
5
+ /**
6
+ * Version of the bridge.lbug schema below. `openBridgeDbReadOnly` compares
7
+ * this against `meta.json`'s version field and returns `null` on mismatch,
8
+ * which trips the caller into either the JSON fallback path or a fresh
9
+ * `group sync` that rebuilds `bridge.lbug` from scratch.
10
+ *
11
+ * Migration contract for contributors bumping this constant:
12
+ * 1. Bump the number (e.g. `1` → `2`).
13
+ * 2. Update the DDL below to match the new schema.
14
+ * 3. DO NOT attempt an online migration in this file — the version gate
15
+ * is intentionally a "discard and re-sync" strategy for V1. An old
16
+ * bridge.lbug whose version doesn't match is treated as opaque and
17
+ * rebuilt by the next `group sync`.
18
+ * 4. If online migration becomes necessary (e.g. when groups accumulate
19
+ * large amounts of embedding data), add a migration path as a
20
+ * separate `bridge-migrations.ts` module rather than bloating this
21
+ * file — keep schema and migration concerns separate.
22
+ */
23
+ export declare const BRIDGE_SCHEMA_VERSION = 1;
24
+ export declare const CONTRACT_SCHEMA = "\nCREATE NODE TABLE Contract (\n id STRING,\n contractId STRING,\n type STRING,\n role STRING,\n repo STRING,\n service STRING DEFAULT '',\n symbolUid STRING DEFAULT '',\n filePath STRING DEFAULT '',\n symbolName STRING DEFAULT '',\n confidence DOUBLE DEFAULT 0.0,\n meta STRING DEFAULT '{}',\n PRIMARY KEY (id)\n)";
25
+ export declare const REPO_SNAPSHOT_SCHEMA = "\nCREATE NODE TABLE RepoSnapshot (\n id STRING,\n indexedAt STRING DEFAULT '',\n lastCommit STRING DEFAULT '',\n PRIMARY KEY (id)\n)";
26
+ export declare const CONTRACT_LINK_SCHEMA = "\nCREATE REL TABLE ContractLink (\n FROM Contract TO Contract,\n matchType STRING,\n confidence DOUBLE,\n contractId STRING,\n fromRepo STRING,\n toRepo STRING\n)";
27
+ export declare const BRIDGE_SCHEMA_QUERIES: string[];
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Bridge LadybugDB schema for cross-repo Contract Registry.
3
+ * Separate from per-repo schema in lbug/schema.ts.
4
+ */
5
+ /**
6
+ * Version of the bridge.lbug schema below. `openBridgeDbReadOnly` compares
7
+ * this against `meta.json`'s version field and returns `null` on mismatch,
8
+ * which trips the caller into either the JSON fallback path or a fresh
9
+ * `group sync` that rebuilds `bridge.lbug` from scratch.
10
+ *
11
+ * Migration contract for contributors bumping this constant:
12
+ * 1. Bump the number (e.g. `1` → `2`).
13
+ * 2. Update the DDL below to match the new schema.
14
+ * 3. DO NOT attempt an online migration in this file — the version gate
15
+ * is intentionally a "discard and re-sync" strategy for V1. An old
16
+ * bridge.lbug whose version doesn't match is treated as opaque and
17
+ * rebuilt by the next `group sync`.
18
+ * 4. If online migration becomes necessary (e.g. when groups accumulate
19
+ * large amounts of embedding data), add a migration path as a
20
+ * separate `bridge-migrations.ts` module rather than bloating this
21
+ * file — keep schema and migration concerns separate.
22
+ */
23
+ export const BRIDGE_SCHEMA_VERSION = 1;
24
+ export const CONTRACT_SCHEMA = `
25
+ CREATE NODE TABLE Contract (
26
+ id STRING,
27
+ contractId STRING,
28
+ type STRING,
29
+ role STRING,
30
+ repo STRING,
31
+ service STRING DEFAULT '',
32
+ symbolUid STRING DEFAULT '',
33
+ filePath STRING DEFAULT '',
34
+ symbolName STRING DEFAULT '',
35
+ confidence DOUBLE DEFAULT 0.0,
36
+ meta STRING DEFAULT '{}',
37
+ PRIMARY KEY (id)
38
+ )`;
39
+ export const REPO_SNAPSHOT_SCHEMA = `
40
+ CREATE NODE TABLE RepoSnapshot (
41
+ id STRING,
42
+ indexedAt STRING DEFAULT '',
43
+ lastCommit STRING DEFAULT '',
44
+ PRIMARY KEY (id)
45
+ )`;
46
+ export const CONTRACT_LINK_SCHEMA = `
47
+ CREATE REL TABLE ContractLink (
48
+ FROM Contract TO Contract,
49
+ matchType STRING,
50
+ confidence DOUBLE,
51
+ contractId STRING,
52
+ fromRepo STRING,
53
+ toRepo STRING
54
+ )`;
55
+ export const BRIDGE_SCHEMA_QUERIES = [CONTRACT_SCHEMA, REPO_SNAPSHOT_SCHEMA, CONTRACT_LINK_SCHEMA];
@@ -0,0 +1,3 @@
1
+ import type { GroupConfig } from './types.js';
2
+ export declare function parseGroupConfig(yamlContent: string): GroupConfig;
3
+ export declare function loadGroupConfig(groupDir: string): Promise<GroupConfig>;
@@ -0,0 +1,83 @@
1
+ import { createRequire } from 'node:module';
2
+ const _require = createRequire(import.meta.url);
3
+ const yaml = _require('js-yaml');
4
+ const VALID_CONTRACT_TYPES = ['http', 'grpc', 'topic', 'lib', 'custom'];
5
+ const VALID_ROLES = ['provider', 'consumer'];
6
+ const DEFAULT_DETECT = {
7
+ http: true,
8
+ grpc: true,
9
+ topics: true,
10
+ shared_libs: true,
11
+ embedding_fallback: true,
12
+ };
13
+ const DEFAULT_MATCHING = {
14
+ bm25_threshold: 0.7,
15
+ embedding_threshold: 0.65,
16
+ max_candidates_per_step: 3,
17
+ };
18
+ export function parseGroupConfig(yamlContent) {
19
+ const raw = yaml.load(yamlContent, { schema: yaml.JSON_SCHEMA });
20
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
21
+ throw new Error('Invalid YAML: expected an object');
22
+ }
23
+ if (raw.version === undefined)
24
+ throw new Error('version is required in group.yaml');
25
+ if (raw.version !== 1) {
26
+ throw new Error(`Unsupported group.yaml version: ${raw.version}. Expected 1.`);
27
+ }
28
+ if (!raw.name || typeof raw.name !== 'string')
29
+ throw new Error('name is required in group.yaml');
30
+ if (!raw.repos || typeof raw.repos !== 'object' || Array.isArray(raw.repos)) {
31
+ throw new Error('repos is required in group.yaml (must be a mapping)');
32
+ }
33
+ const repos = raw.repos;
34
+ const repoPaths = new Set(Object.keys(repos));
35
+ const rawLinks = raw.links || [];
36
+ const links = rawLinks.map((l, i) => {
37
+ const link = l;
38
+ if (!link.from || !repoPaths.has(link.from)) {
39
+ throw new Error(`links[${i}].from "${link.from}" does not match any repo path in group`);
40
+ }
41
+ if (!link.to || !repoPaths.has(link.to)) {
42
+ throw new Error(`links[${i}].to "${link.to}" does not match any repo path in group`);
43
+ }
44
+ if (!VALID_CONTRACT_TYPES.includes(link.type)) {
45
+ throw new Error(`links[${i}].type "${link.type}" is invalid. Expected: ${VALID_CONTRACT_TYPES.join(', ')}`);
46
+ }
47
+ if (!VALID_ROLES.includes(link.role)) {
48
+ throw new Error(`links[${i}].role "${link.role}" is invalid. Expected: provider | consumer`);
49
+ }
50
+ if (link.contract === undefined ||
51
+ link.contract === null ||
52
+ String(link.contract).trim() === '') {
53
+ throw new Error(`links[${i}].contract is required`);
54
+ }
55
+ return {
56
+ from: link.from,
57
+ to: link.to,
58
+ type: link.type,
59
+ contract: String(link.contract),
60
+ role: link.role,
61
+ };
62
+ });
63
+ const detect = { ...DEFAULT_DETECT, ...(raw.detect || {}) };
64
+ const matching = { ...DEFAULT_MATCHING, ...(raw.matching || {}) };
65
+ const packages = raw.packages || {};
66
+ return {
67
+ version: 1,
68
+ name: raw.name,
69
+ description: raw.description || '',
70
+ repos,
71
+ links,
72
+ packages,
73
+ detect,
74
+ matching,
75
+ };
76
+ }
77
+ export async function loadGroupConfig(groupDir) {
78
+ const fsp = await import('node:fs/promises');
79
+ const path = await import('node:path');
80
+ const yamlPath = path.join(groupDir, 'group.yaml');
81
+ const content = await fsp.readFile(yamlPath, 'utf-8');
82
+ return parseGroupConfig(content);
83
+ }
@@ -0,0 +1,7 @@
1
+ import type { ContractType, ExtractedContract, RepoHandle } from './types.js';
2
+ export interface ContractExtractor {
3
+ type: ContractType;
4
+ canExtract(repo: RepoHandle): Promise<boolean>;
5
+ extract(dbExecutor: CypherExecutor | null, repoPath: string, repo: RepoHandle): Promise<ExtractedContract[]>;
6
+ }
7
+ export type CypherExecutor = (query: string, params?: Record<string, unknown>) => Promise<Record<string, unknown>[]>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Safely read a file inside a repo, rejecting any path that escapes
3
+ * `repoPath` via `..` traversal or absolute segments. Returns `null` if
4
+ * the path is outside the repo or the file can't be read.
5
+ *
6
+ * Used by every source-scan extractor under this directory. Kept as a
7
+ * single shared implementation so the path-traversal guard (security-
8
+ * sensitive) lives in exactly one place.
9
+ */
10
+ export declare function readSafe(repoPath: string, rel: string): string | null;
@@ -0,0 +1,24 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ /**
4
+ * Safely read a file inside a repo, rejecting any path that escapes
5
+ * `repoPath` via `..` traversal or absolute segments. Returns `null` if
6
+ * the path is outside the repo or the file can't be read.
7
+ *
8
+ * Used by every source-scan extractor under this directory. Kept as a
9
+ * single shared implementation so the path-traversal guard (security-
10
+ * sensitive) lives in exactly one place.
11
+ */
12
+ export function readSafe(repoPath, rel) {
13
+ const abs = path.resolve(repoPath, rel);
14
+ const base = path.resolve(repoPath);
15
+ const relToBase = path.relative(base, abs);
16
+ if (relToBase.startsWith('..') || path.isAbsolute(relToBase))
17
+ return null;
18
+ try {
19
+ return fs.readFileSync(abs, 'utf-8');
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ }
@@ -0,0 +1,25 @@
1
+ import type { ContractExtractor, CypherExecutor } from '../contract-extractor.js';
2
+ import type { ExtractedContract, RepoHandle } from '../types.js';
3
+ export interface ProtoServiceInfo {
4
+ package: string;
5
+ serviceName: string;
6
+ methods: string[];
7
+ protoPath: string;
8
+ }
9
+ export declare function buildProtoMap(repoPath: string): Promise<Map<string, ProtoServiceInfo[]>>;
10
+ export declare function resolveProtoConflict(_serviceName: string, sourceFilePath: string, candidates: ProtoServiceInfo[]): ProtoServiceInfo | null;
11
+ export declare function serviceContractId(pkg: string, serviceName: string): string;
12
+ export declare class GrpcExtractor implements ContractExtractor {
13
+ type: "grpc";
14
+ canExtract(_repo: RepoHandle): Promise<boolean>;
15
+ extract(_dbExecutor: CypherExecutor | null, repoPath: string, _repo: RepoHandle): Promise<ExtractedContract[]>;
16
+ /**
17
+ * Convert a plugin `GrpcDetection` into a concrete `ExtractedContract`
18
+ * by resolving the short service name against the proto map, building
19
+ * either a service-level (`grpc::pkg.Svc/*`) or method-level
20
+ * (`grpc::pkg.Svc/Method`) contract id, and selecting confidence
21
+ * based on whether the proto map had an entry.
22
+ */
23
+ private detectionToContract;
24
+ private dedupe;
25
+ }
@@ -0,0 +1,386 @@
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 { GRPC_SCAN_GLOB, getPluginForFile, hasProtoPlugin, } from './grpc-patterns/index.js';
6
+ /**
7
+ * Language-agnostic orchestrator for gRPC (provider + consumer) contract
8
+ * extraction.
9
+ *
10
+ * Two parts:
11
+ *
12
+ * 1. **`.proto` parsing** — tree-sitter when `tree-sitter-proto` is
13
+ * installed (optionalDependency vendored in `vendor/tree-sitter-proto/`),
14
+ * via the `.proto` entry in `grpc-patterns/` and `hasProtoPlugin`.
15
+ * When the grammar isn't available (platform incompatibility, native
16
+ * build failure) the orchestrator falls back to the in-process
17
+ * string-sanitizing parser defined below (`stripProtoCommentsAndStrings`
18
+ * + `extractServiceBlocks`). The fallback preserves offsets so any
19
+ * downstream regex scans run against a sanitized copy without
20
+ * affecting line numbers of the original.
21
+ *
22
+ * 2. **Source-scan providers / consumers** — delegated to per-language
23
+ * plugins in `./grpc-patterns/`. The orchestrator imports NO
24
+ * tree-sitter grammars or query strings — each plugin owns its own.
25
+ */
26
+ // ─── .proto fallback parser (used only when tree-sitter-proto is absent) ───
27
+ function contractId(pkg, service, method) {
28
+ const prefix = pkg ? `${pkg}.${service}` : service;
29
+ return `grpc::${prefix}/${method}`;
30
+ }
31
+ function serviceOnlyContractId(serviceName) {
32
+ return `grpc::${serviceName}/*`;
33
+ }
34
+ /**
35
+ * Replace all .proto comments and string literals with spaces, preserving the
36
+ * original length and character offsets of the input. This lets downstream
37
+ * regex / brace-depth parsers run on a "sanitized" copy without having to
38
+ * understand proto syntax, while any RegExp.exec/index-based lookups that
39
+ * were already positional against `content` continue to work against the
40
+ * original string.
41
+ *
42
+ * Supported comment forms: `// line comment`, `/* block comment * /`.
43
+ * Supported strings: double-quoted ("…") and single-quoted ('…') with `\`
44
+ * escape handling. Raw/unterminated strings are not supported — we stop
45
+ * on a line break for line-style comments and on EOF for unterminated
46
+ * strings/blocks, which matches how most real proto files parse.
47
+ */
48
+ function stripProtoCommentsAndStrings(content) {
49
+ const out = new Array(content.length);
50
+ let i = 0;
51
+ while (i < content.length) {
52
+ const ch = content[i];
53
+ const next = content[i + 1];
54
+ // Line comment: // ... \n
55
+ if (ch === '/' && next === '/') {
56
+ out[i] = ' ';
57
+ out[i + 1] = ' ';
58
+ i += 2;
59
+ while (i < content.length && content[i] !== '\n') {
60
+ out[i] = content[i] === '\r' ? '\r' : ' ';
61
+ i++;
62
+ }
63
+ continue;
64
+ }
65
+ // Block comment: /* ... */
66
+ if (ch === '/' && next === '*') {
67
+ out[i] = ' ';
68
+ out[i + 1] = ' ';
69
+ i += 2;
70
+ while (i < content.length) {
71
+ if (content[i] === '*' && content[i + 1] === '/') {
72
+ out[i] = ' ';
73
+ out[i + 1] = ' ';
74
+ i += 2;
75
+ break;
76
+ }
77
+ // Preserve newlines so line numbers stay stable for downstream code.
78
+ out[i] = content[i] === '\n' || content[i] === '\r' ? content[i] : ' ';
79
+ i++;
80
+ }
81
+ continue;
82
+ }
83
+ // String literal: "..." or '...'
84
+ if (ch === '"' || ch === "'") {
85
+ const quote = ch;
86
+ out[i] = ' '; // replace opening quote
87
+ i++;
88
+ while (i < content.length) {
89
+ const c = content[i];
90
+ if (c === '\\' && i + 1 < content.length) {
91
+ // Skip escaped pair (e.g. \" \n \\)
92
+ out[i] = ' ';
93
+ out[i + 1] = ' ';
94
+ i += 2;
95
+ continue;
96
+ }
97
+ if (c === quote) {
98
+ out[i] = ' ';
99
+ i++;
100
+ break;
101
+ }
102
+ // Preserve newlines; proto technically disallows unescaped newlines
103
+ // inside strings, but real files occasionally have them.
104
+ out[i] = c === '\n' || c === '\r' ? c : ' ';
105
+ i++;
106
+ }
107
+ continue;
108
+ }
109
+ out[i] = ch;
110
+ i++;
111
+ }
112
+ return out.join('');
113
+ }
114
+ function extractServiceBlocks(content) {
115
+ const results = [];
116
+ // Sanitize comments and string literals so braces inside them don't
117
+ // throw off the depth counter. The sanitized copy has the same length
118
+ // and offsets as the original, so we use it ONLY to scan for service
119
+ // headers and braces; the service body we return is sliced from the
120
+ // ORIGINAL content to preserve exact source text for downstream use.
121
+ const sanitized = stripProtoCommentsAndStrings(content);
122
+ const headerRe = /service\s+(\w+)\s*\{/g;
123
+ let headerMatch;
124
+ while ((headerMatch = headerRe.exec(sanitized)) !== null) {
125
+ const serviceName = headerMatch[1];
126
+ const bodyStart = headerMatch.index + headerMatch[0].length;
127
+ let depth = 1;
128
+ let pos = bodyStart;
129
+ while (pos < sanitized.length && depth > 0) {
130
+ const ch = sanitized[pos];
131
+ if (ch === '{')
132
+ depth++;
133
+ else if (ch === '}')
134
+ depth--;
135
+ pos++;
136
+ }
137
+ // If EOF before depth returns to 0, skip incomplete service
138
+ if (depth !== 0)
139
+ continue;
140
+ // body is between opening { (consumed by regex) and closing } (pos is one past it)
141
+ const body = content.slice(bodyStart, pos - 1);
142
+ results.push({ name: serviceName, body });
143
+ }
144
+ return results;
145
+ }
146
+ function makeContract(cid, role, filePath, symbolName, confidence, meta) {
147
+ return {
148
+ contractId: cid,
149
+ type: 'grpc',
150
+ role,
151
+ symbolUid: '',
152
+ symbolRef: { filePath: filePath.replace(/\\/g, '/'), name: symbolName },
153
+ symbolName,
154
+ confidence,
155
+ meta: { ...meta, extractionStrategy: 'source_scan' },
156
+ };
157
+ }
158
+ function normalizeProtoPath(rel) {
159
+ return rel.replace(/\\/g, '/');
160
+ }
161
+ function extractProtoImports(content) {
162
+ const imports = [];
163
+ const re = /^\s*import\s+"([^"]+)"\s*;/gm;
164
+ let match;
165
+ while ((match = re.exec(content)) !== null) {
166
+ imports.push(match[1]);
167
+ }
168
+ return imports;
169
+ }
170
+ function longestSharedSegmentRun(aPath, bPath) {
171
+ const a = aPath.split('/').filter(Boolean);
172
+ const b = bPath.split('/').filter(Boolean);
173
+ let best = 0;
174
+ for (let i = 0; i < a.length; i++) {
175
+ for (let j = 0; j < b.length; j++) {
176
+ let run = 0;
177
+ while (a[i + run] && b[j + run] && a[i + run] === b[j + run]) {
178
+ run++;
179
+ }
180
+ if (run > best)
181
+ best = run;
182
+ }
183
+ }
184
+ return best;
185
+ }
186
+ async function buildProtoContext(repoPath) {
187
+ const servicesByName = new Map();
188
+ const protoFiles = await glob('**/*.proto', {
189
+ cwd: repoPath,
190
+ absolute: false,
191
+ nodir: true,
192
+ ignore: ['**/node_modules/**', '**/.git/**', '**/vendor/**'],
193
+ });
194
+ const contents = new Map();
195
+ for (const rel of protoFiles) {
196
+ const content = readSafe(repoPath, rel);
197
+ if (!content)
198
+ continue;
199
+ contents.set(normalizeProtoPath(rel), content);
200
+ }
201
+ const packagesByProto = new Map();
202
+ const resolvePackage = (protoPath, seen = new Set()) => {
203
+ if (packagesByProto.has(protoPath))
204
+ return packagesByProto.get(protoPath) ?? '';
205
+ if (seen.has(protoPath))
206
+ return '';
207
+ const content = contents.get(protoPath);
208
+ if (!content)
209
+ return '';
210
+ seen.add(protoPath);
211
+ const pkgMatch = content.match(/^\s*package\s+([\w.]+)\s*;/m);
212
+ if (pkgMatch?.[1]) {
213
+ packagesByProto.set(protoPath, pkgMatch[1]);
214
+ return pkgMatch[1];
215
+ }
216
+ for (const importPath of extractProtoImports(content)) {
217
+ const normalizedImport = normalizeProtoPath(importPath);
218
+ const candidates = [
219
+ normalizeProtoPath(path.posix.normalize(path.posix.join(path.posix.dirname(protoPath), normalizedImport))),
220
+ normalizedImport,
221
+ ];
222
+ for (const candidate of candidates) {
223
+ if (!contents.has(candidate))
224
+ continue;
225
+ const inheritedPackage = resolvePackage(candidate, seen);
226
+ if (inheritedPackage) {
227
+ packagesByProto.set(protoPath, inheritedPackage);
228
+ return inheritedPackage;
229
+ }
230
+ }
231
+ }
232
+ packagesByProto.set(protoPath, '');
233
+ return '';
234
+ };
235
+ for (const rel of protoFiles) {
236
+ const normalizedRel = normalizeProtoPath(rel);
237
+ const content = contents.get(normalizedRel);
238
+ if (!content)
239
+ continue;
240
+ const pkg = resolvePackage(normalizedRel);
241
+ const serviceBlocks = extractServiceBlocks(content);
242
+ for (const block of serviceBlocks) {
243
+ const rpcRe = /rpc\s+(\w+)\s*\(/g;
244
+ const methods = [];
245
+ let m;
246
+ while ((m = rpcRe.exec(block.body)) !== null) {
247
+ methods.push(m[1]);
248
+ }
249
+ const info = {
250
+ package: pkg,
251
+ serviceName: block.name,
252
+ methods,
253
+ protoPath: normalizedRel,
254
+ };
255
+ const existing = servicesByName.get(block.name) ?? [];
256
+ existing.push(info);
257
+ servicesByName.set(block.name, existing);
258
+ }
259
+ }
260
+ return { packagesByProto, servicesByName };
261
+ }
262
+ export async function buildProtoMap(repoPath) {
263
+ const { servicesByName } = await buildProtoContext(repoPath);
264
+ return servicesByName;
265
+ }
266
+ export function resolveProtoConflict(_serviceName, sourceFilePath, candidates) {
267
+ if (candidates.length === 0)
268
+ return null;
269
+ if (candidates.length === 1)
270
+ return candidates[0];
271
+ const sourceDir = normalizeProtoPath(path.dirname(sourceFilePath));
272
+ let best = candidates[0];
273
+ let bestScore = -1;
274
+ for (const c of candidates) {
275
+ const protoDir = normalizeProtoPath(path.dirname(c.protoPath));
276
+ const sharedRun = longestSharedSegmentRun(sourceDir, protoDir);
277
+ if (sharedRun > bestScore) {
278
+ bestScore = sharedRun;
279
+ best = c;
280
+ }
281
+ }
282
+ return best;
283
+ }
284
+ export function serviceContractId(pkg, serviceName) {
285
+ const prefix = pkg ? `${pkg}.${serviceName}` : serviceName;
286
+ return `grpc::${prefix}/*`;
287
+ }
288
+ // ─── Orchestrator ────────────────────────────────────────────────────
289
+ export class GrpcExtractor {
290
+ type = 'grpc';
291
+ async canExtract(_repo) {
292
+ return true;
293
+ }
294
+ async extract(_dbExecutor, repoPath, _repo) {
295
+ const out = [];
296
+ const protoContext = await buildProtoContext(repoPath);
297
+ const protoMap = protoContext.servicesByName;
298
+ // ─── Proto files — definitive provider source ─────────────────
299
+ // When tree-sitter-proto is available, .proto files are handled by
300
+ // the plugin loop below (they're in GRPC_SCAN_GLOB). Otherwise
301
+ // emit provider contracts directly from the proto map that
302
+ // `buildProtoContext` already built — no second glob / parse pass.
303
+ if (!hasProtoPlugin) {
304
+ for (const infos of protoMap.values()) {
305
+ for (const info of infos) {
306
+ for (const methodName of info.methods) {
307
+ const cid = contractId(info.package, info.serviceName, methodName);
308
+ out.push(makeContract(cid, 'provider', info.protoPath, `${info.serviceName}.${methodName}`, 0.85, {
309
+ package: info.package,
310
+ service: info.serviceName,
311
+ method: methodName,
312
+ source: 'proto',
313
+ }));
314
+ }
315
+ }
316
+ }
317
+ }
318
+ // ─── Source files (+ .proto when plugin available) ────────────
319
+ const sourceFiles = await glob(GRPC_SCAN_GLOB, {
320
+ cwd: repoPath,
321
+ ignore: ['**/node_modules/**', '**/.git/**', '**/vendor/**', '**/dist/**', '**/build/**'],
322
+ nodir: true,
323
+ });
324
+ const parser = new Parser();
325
+ for (const rel of sourceFiles) {
326
+ const plugin = getPluginForFile(rel);
327
+ if (!plugin)
328
+ continue;
329
+ const content = readSafe(repoPath, rel);
330
+ if (!content)
331
+ continue;
332
+ let detections = [];
333
+ try {
334
+ parser.setLanguage(plugin.language);
335
+ const tree = parser.parse(content);
336
+ detections = plugin.scan(tree);
337
+ }
338
+ catch {
339
+ continue;
340
+ }
341
+ for (const d of detections) {
342
+ out.push(this.detectionToContract(d, rel, protoMap));
343
+ }
344
+ }
345
+ return this.dedupe(out);
346
+ }
347
+ /**
348
+ * Convert a plugin `GrpcDetection` into a concrete `ExtractedContract`
349
+ * by resolving the short service name against the proto map, building
350
+ * either a service-level (`grpc::pkg.Svc/*`) or method-level
351
+ * (`grpc::pkg.Svc/Method`) contract id, and selecting confidence
352
+ * based on whether the proto map had an entry.
353
+ */
354
+ detectionToContract(d, filePath, protoMap) {
355
+ const candidates = protoMap.get(d.serviceName);
356
+ const proto = resolveProtoConflict(d.serviceName, filePath, candidates ?? []);
357
+ const pkg = proto?.package ?? '';
358
+ const cid = d.methodName
359
+ ? contractId(pkg, d.serviceName, d.methodName)
360
+ : proto
361
+ ? serviceContractId(pkg, d.serviceName)
362
+ : serviceOnlyContractId(d.serviceName);
363
+ const confidence = proto ? d.confidenceWithProto : d.confidenceWithoutProto;
364
+ const meta = {
365
+ service: d.serviceName,
366
+ source: d.source,
367
+ };
368
+ if (d.methodName)
369
+ meta.method = d.methodName;
370
+ return makeContract(cid, d.role, filePath, d.symbolName, confidence, meta);
371
+ }
372
+ dedupe(items) {
373
+ const byKey = new Map();
374
+ for (const c of items) {
375
+ const k = `${c.contractId}|${c.role}|${c.symbolRef.filePath}`;
376
+ const existing = byKey.get(k);
377
+ if (!existing ||
378
+ c.confidence > existing.confidence ||
379
+ (c.confidence === existing.confidence &&
380
+ String(c.meta.source) < String(existing.meta.source))) {
381
+ byKey.set(k, c);
382
+ }
383
+ }
384
+ return Array.from(byKey.values());
385
+ }
386
+ }
@@ -0,0 +1,2 @@
1
+ import type { GrpcLanguagePlugin } from './types.js';
2
+ export declare const GO_GRPC_PLUGIN: GrpcLanguagePlugin;