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.
- package/README.md +10 -0
- package/dist/_shared/graph/types.d.ts +1 -1
- package/dist/_shared/graph/types.d.ts.map +1 -1
- package/dist/_shared/index.d.ts +1 -0
- package/dist/_shared/index.d.ts.map +1 -1
- package/dist/_shared/language-detection.d.ts.map +1 -1
- package/dist/_shared/language-detection.js +2 -0
- package/dist/_shared/language-detection.js.map +1 -1
- package/dist/_shared/languages.d.ts +1 -0
- package/dist/_shared/languages.d.ts.map +1 -1
- package/dist/_shared/languages.js +1 -0
- package/dist/_shared/languages.js.map +1 -1
- package/dist/_shared/lbug/schema-constants.d.ts +1 -1
- package/dist/_shared/lbug/schema-constants.d.ts.map +1 -1
- package/dist/_shared/lbug/schema-constants.js +3 -1
- package/dist/_shared/lbug/schema-constants.js.map +1 -1
- package/dist/_shared/mro-strategy.d.ts +19 -0
- package/dist/_shared/mro-strategy.d.ts.map +1 -0
- package/dist/_shared/mro-strategy.js +2 -0
- package/dist/_shared/mro-strategy.js.map +1 -0
- package/dist/cli/ai-context.d.ts +1 -0
- package/dist/cli/ai-context.js +28 -4
- package/dist/cli/analyze.d.ts +2 -0
- package/dist/cli/analyze.js +30 -4
- package/dist/cli/group.d.ts +2 -0
- package/dist/cli/group.js +233 -0
- package/dist/cli/index.js +3 -0
- package/dist/cli/serve.js +4 -1
- package/dist/cli/setup.js +34 -3
- package/dist/config/ignore-service.js +8 -3
- package/dist/core/augmentation/engine.js +1 -1
- package/dist/core/git-staleness.d.ts +13 -0
- package/dist/core/git-staleness.js +29 -0
- package/dist/core/group/bridge-db.d.ts +82 -0
- package/dist/core/group/bridge-db.js +460 -0
- package/dist/core/group/bridge-schema.d.ts +27 -0
- package/dist/core/group/bridge-schema.js +55 -0
- package/dist/core/group/config-parser.d.ts +3 -0
- package/dist/core/group/config-parser.js +83 -0
- package/dist/core/group/contract-extractor.d.ts +7 -0
- package/dist/core/group/contract-extractor.js +1 -0
- package/dist/core/group/extractors/fs-utils.d.ts +10 -0
- package/dist/core/group/extractors/fs-utils.js +24 -0
- package/dist/core/group/extractors/grpc-extractor.d.ts +25 -0
- package/dist/core/group/extractors/grpc-extractor.js +386 -0
- package/dist/core/group/extractors/grpc-patterns/go.d.ts +2 -0
- package/dist/core/group/extractors/grpc-patterns/go.js +97 -0
- package/dist/core/group/extractors/grpc-patterns/index.d.ts +19 -0
- package/dist/core/group/extractors/grpc-patterns/index.js +46 -0
- package/dist/core/group/extractors/grpc-patterns/java.d.ts +2 -0
- package/dist/core/group/extractors/grpc-patterns/java.js +173 -0
- package/dist/core/group/extractors/grpc-patterns/node.d.ts +4 -0
- package/dist/core/group/extractors/grpc-patterns/node.js +290 -0
- package/dist/core/group/extractors/grpc-patterns/proto.d.ts +9 -0
- package/dist/core/group/extractors/grpc-patterns/proto.js +134 -0
- package/dist/core/group/extractors/grpc-patterns/python.d.ts +2 -0
- package/dist/core/group/extractors/grpc-patterns/python.js +67 -0
- package/dist/core/group/extractors/grpc-patterns/types.d.ts +50 -0
- package/dist/core/group/extractors/grpc-patterns/types.js +1 -0
- package/dist/core/group/extractors/http-patterns/go.d.ts +2 -0
- package/dist/core/group/extractors/http-patterns/go.js +215 -0
- package/dist/core/group/extractors/http-patterns/index.d.ts +17 -0
- package/dist/core/group/extractors/http-patterns/index.js +44 -0
- package/dist/core/group/extractors/http-patterns/java.d.ts +2 -0
- package/dist/core/group/extractors/http-patterns/java.js +253 -0
- package/dist/core/group/extractors/http-patterns/node.d.ts +4 -0
- package/dist/core/group/extractors/http-patterns/node.js +354 -0
- package/dist/core/group/extractors/http-patterns/php.d.ts +2 -0
- package/dist/core/group/extractors/http-patterns/php.js +70 -0
- package/dist/core/group/extractors/http-patterns/python.d.ts +2 -0
- package/dist/core/group/extractors/http-patterns/python.js +133 -0
- package/dist/core/group/extractors/http-patterns/types.d.ts +61 -0
- package/dist/core/group/extractors/http-patterns/types.js +1 -0
- package/dist/core/group/extractors/http-route-extractor.d.ts +21 -0
- package/dist/core/group/extractors/http-route-extractor.js +391 -0
- package/dist/core/group/extractors/manifest-extractor.d.ts +54 -0
- package/dist/core/group/extractors/manifest-extractor.js +235 -0
- package/dist/core/group/extractors/topic-extractor.d.ts +8 -0
- package/dist/core/group/extractors/topic-extractor.js +97 -0
- package/dist/core/group/extractors/topic-patterns/go.d.ts +2 -0
- package/dist/core/group/extractors/topic-patterns/go.js +120 -0
- package/dist/core/group/extractors/topic-patterns/index.d.ts +14 -0
- package/dist/core/group/extractors/topic-patterns/index.js +38 -0
- package/dist/core/group/extractors/topic-patterns/java.d.ts +2 -0
- package/dist/core/group/extractors/topic-patterns/java.js +80 -0
- package/dist/core/group/extractors/topic-patterns/node.d.ts +4 -0
- package/dist/core/group/extractors/topic-patterns/node.js +155 -0
- package/dist/core/group/extractors/topic-patterns/python.d.ts +2 -0
- package/dist/core/group/extractors/topic-patterns/python.js +116 -0
- package/dist/core/group/extractors/topic-patterns/types.d.ts +25 -0
- package/dist/core/group/extractors/topic-patterns/types.js +10 -0
- package/dist/core/group/extractors/tree-sitter-scanner.d.ts +113 -0
- package/dist/core/group/extractors/tree-sitter-scanner.js +94 -0
- package/dist/core/group/matching.d.ts +13 -0
- package/dist/core/group/matching.js +198 -0
- package/dist/core/group/normalization.d.ts +3 -0
- package/dist/core/group/normalization.js +115 -0
- package/dist/core/group/service-boundary-detector.d.ts +8 -0
- package/dist/core/group/service-boundary-detector.js +155 -0
- package/dist/core/group/service.d.ts +46 -0
- package/dist/core/group/service.js +160 -0
- package/dist/core/group/storage.d.ts +9 -0
- package/dist/core/group/storage.js +91 -0
- package/dist/core/group/sync.d.ts +21 -0
- package/dist/core/group/sync.js +148 -0
- package/dist/core/group/types.d.ts +130 -0
- package/dist/core/group/types.js +1 -0
- package/dist/core/ingestion/binding-accumulator.d.ts +212 -0
- package/dist/core/ingestion/binding-accumulator.js +336 -0
- package/dist/core/ingestion/call-processor.d.ts +155 -24
- package/dist/core/ingestion/call-processor.js +1129 -247
- package/dist/core/ingestion/class-extractors/generic.d.ts +2 -0
- package/dist/core/ingestion/class-extractors/generic.js +135 -0
- package/dist/core/ingestion/class-types.d.ts +34 -0
- package/dist/core/ingestion/class-types.js +1 -0
- package/dist/core/ingestion/cobol-processor.d.ts +1 -1
- package/dist/core/ingestion/entry-point-scoring.d.ts +1 -0
- package/dist/core/ingestion/entry-point-scoring.js +1 -0
- package/dist/core/ingestion/field-types.d.ts +2 -2
- package/dist/core/ingestion/filesystem-walker.js +8 -0
- package/dist/core/ingestion/framework-detection.d.ts +1 -0
- package/dist/core/ingestion/framework-detection.js +1 -0
- package/dist/core/ingestion/heritage-processor.d.ts +8 -15
- package/dist/core/ingestion/heritage-processor.js +15 -28
- package/dist/core/ingestion/import-processor.d.ts +1 -11
- package/dist/core/ingestion/import-processor.js +1 -13
- package/dist/core/ingestion/import-resolvers/utils.js +1 -0
- package/dist/core/ingestion/import-resolvers/vue.d.ts +8 -0
- package/dist/core/ingestion/import-resolvers/vue.js +9 -0
- package/dist/core/ingestion/language-config.js +1 -1
- package/dist/core/ingestion/language-provider.d.ts +14 -3
- package/dist/core/ingestion/languages/c-cpp.js +168 -1
- package/dist/core/ingestion/languages/csharp.js +20 -0
- package/dist/core/ingestion/languages/dart.js +26 -4
- package/dist/core/ingestion/languages/go.js +22 -0
- package/dist/core/ingestion/languages/index.d.ts +1 -0
- package/dist/core/ingestion/languages/index.js +2 -0
- package/dist/core/ingestion/languages/java.js +17 -0
- package/dist/core/ingestion/languages/kotlin.js +24 -1
- package/dist/core/ingestion/languages/php.js +23 -11
- package/dist/core/ingestion/languages/python.js +9 -0
- package/dist/core/ingestion/languages/ruby.js +43 -0
- package/dist/core/ingestion/languages/rust.js +38 -0
- package/dist/core/ingestion/languages/swift.js +31 -0
- package/dist/core/ingestion/languages/typescript.d.ts +1 -0
- package/dist/core/ingestion/languages/typescript.js +52 -3
- package/dist/core/ingestion/languages/vue.d.ts +13 -0
- package/dist/core/ingestion/languages/vue.js +81 -0
- package/dist/core/ingestion/markdown-processor.d.ts +1 -1
- package/dist/core/ingestion/method-extractors/configs/c-cpp.d.ts +3 -0
- package/dist/core/ingestion/method-extractors/configs/c-cpp.js +387 -0
- package/dist/core/ingestion/method-extractors/configs/csharp.js +5 -1
- package/dist/core/ingestion/method-extractors/configs/dart.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/dart.js +376 -0
- package/dist/core/ingestion/method-extractors/configs/go.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/go.js +176 -0
- package/dist/core/ingestion/method-extractors/configs/jvm.js +14 -4
- package/dist/core/ingestion/method-extractors/configs/php.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/php.js +304 -0
- package/dist/core/ingestion/method-extractors/configs/python.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/python.js +309 -0
- package/dist/core/ingestion/method-extractors/configs/ruby.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/ruby.js +286 -0
- package/dist/core/ingestion/method-extractors/configs/rust.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/rust.js +195 -0
- package/dist/core/ingestion/method-extractors/configs/swift.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/swift.js +277 -0
- package/dist/core/ingestion/method-extractors/configs/typescript-javascript.js +85 -8
- package/dist/core/ingestion/method-extractors/generic.d.ts +6 -0
- package/dist/core/ingestion/method-extractors/generic.js +84 -17
- package/dist/core/ingestion/method-types.d.ts +29 -0
- package/dist/core/ingestion/model/field-registry.d.ts +18 -0
- package/dist/core/ingestion/model/field-registry.js +22 -0
- package/dist/core/ingestion/model/heritage-map.d.ts +70 -0
- package/dist/core/ingestion/model/heritage-map.js +159 -0
- package/dist/core/ingestion/model/index.d.ts +20 -0
- package/dist/core/ingestion/model/index.js +41 -0
- package/dist/core/ingestion/model/method-registry.d.ts +62 -0
- package/dist/core/ingestion/model/method-registry.js +130 -0
- package/dist/core/ingestion/model/registration-table.d.ts +139 -0
- package/dist/core/ingestion/model/registration-table.js +224 -0
- package/dist/core/ingestion/model/resolution-context.d.ts +93 -0
- package/dist/core/ingestion/model/resolution-context.js +337 -0
- package/dist/core/ingestion/model/resolve.d.ts +56 -0
- package/dist/core/ingestion/model/resolve.js +297 -0
- package/dist/core/ingestion/model/semantic-model.d.ts +86 -0
- package/dist/core/ingestion/model/semantic-model.js +120 -0
- package/dist/core/ingestion/model/symbol-table.d.ts +222 -0
- package/dist/core/ingestion/model/symbol-table.js +206 -0
- package/dist/core/ingestion/model/type-registry.d.ts +39 -0
- package/dist/core/ingestion/model/type-registry.js +62 -0
- package/dist/core/ingestion/mro-processor.d.ts +5 -4
- package/dist/core/ingestion/mro-processor.js +311 -107
- package/dist/core/ingestion/parsing-processor.d.ts +5 -4
- package/dist/core/ingestion/parsing-processor.js +224 -87
- package/dist/core/ingestion/pipeline-phases/cobol.d.ts +16 -0
- package/dist/core/ingestion/pipeline-phases/cobol.js +45 -0
- package/dist/core/ingestion/pipeline-phases/communities.d.ts +16 -0
- package/dist/core/ingestion/pipeline-phases/communities.js +62 -0
- package/dist/core/ingestion/pipeline-phases/cross-file-impl.d.ts +17 -0
- package/dist/core/ingestion/pipeline-phases/cross-file-impl.js +156 -0
- package/dist/core/ingestion/pipeline-phases/cross-file.d.ts +37 -0
- package/dist/core/ingestion/pipeline-phases/cross-file.js +63 -0
- package/dist/core/ingestion/pipeline-phases/index.d.ts +21 -0
- package/dist/core/ingestion/pipeline-phases/index.js +22 -0
- package/dist/core/ingestion/pipeline-phases/markdown.d.ts +17 -0
- package/dist/core/ingestion/pipeline-phases/markdown.js +33 -0
- package/dist/core/ingestion/pipeline-phases/mro.d.ts +18 -0
- package/dist/core/ingestion/pipeline-phases/mro.js +36 -0
- package/dist/core/ingestion/pipeline-phases/orm-extraction.d.ts +22 -0
- package/dist/core/ingestion/pipeline-phases/orm-extraction.js +92 -0
- package/dist/core/ingestion/pipeline-phases/orm.d.ts +15 -0
- package/dist/core/ingestion/pipeline-phases/orm.js +74 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +47 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +437 -0
- package/dist/core/ingestion/pipeline-phases/parse.d.ts +49 -0
- package/dist/core/ingestion/pipeline-phases/parse.js +33 -0
- package/dist/core/ingestion/pipeline-phases/processes.d.ts +16 -0
- package/dist/core/ingestion/pipeline-phases/processes.js +143 -0
- package/dist/core/ingestion/pipeline-phases/routes.d.ts +21 -0
- package/dist/core/ingestion/pipeline-phases/routes.js +243 -0
- package/dist/core/ingestion/pipeline-phases/runner.d.ts +22 -0
- package/dist/core/ingestion/pipeline-phases/runner.js +203 -0
- package/dist/core/ingestion/pipeline-phases/scan.d.ts +21 -0
- package/dist/core/ingestion/pipeline-phases/scan.js +46 -0
- package/dist/core/ingestion/pipeline-phases/structure.d.ts +27 -0
- package/dist/core/ingestion/pipeline-phases/structure.js +35 -0
- package/dist/core/ingestion/pipeline-phases/tools.d.ts +20 -0
- package/dist/core/ingestion/pipeline-phases/tools.js +79 -0
- package/dist/core/ingestion/pipeline-phases/types.d.ts +79 -0
- package/dist/core/ingestion/pipeline-phases/types.js +37 -0
- package/dist/core/ingestion/pipeline-phases/wildcard-synthesis.d.ts +35 -0
- package/dist/core/ingestion/pipeline-phases/wildcard-synthesis.js +174 -0
- package/dist/core/ingestion/pipeline.d.ts +18 -10
- package/dist/core/ingestion/pipeline.js +66 -1410
- package/dist/core/ingestion/process-processor.js +1 -1
- package/dist/core/ingestion/tree-sitter-queries.d.ts +5 -5
- package/dist/core/ingestion/tree-sitter-queries.js +90 -0
- package/dist/core/ingestion/type-env.d.ts +15 -2
- package/dist/core/ingestion/type-env.js +163 -102
- package/dist/core/ingestion/type-extractors/csharp.js +17 -0
- package/dist/core/ingestion/type-extractors/jvm.js +11 -0
- package/dist/core/ingestion/type-extractors/php.js +0 -55
- package/dist/core/ingestion/type-extractors/ruby.js +0 -32
- package/dist/core/ingestion/type-extractors/swift.js +13 -0
- package/dist/core/ingestion/type-extractors/types.d.ts +8 -8
- package/dist/core/ingestion/type-extractors/typescript.js +66 -69
- package/dist/core/ingestion/utils/ast-helpers.d.ts +32 -44
- package/dist/core/ingestion/utils/ast-helpers.js +157 -573
- package/dist/core/ingestion/utils/env.d.ts +10 -0
- package/dist/core/ingestion/utils/env.js +10 -0
- package/dist/core/ingestion/utils/graph-sort.d.ts +58 -0
- package/dist/core/ingestion/utils/graph-sort.js +100 -0
- package/dist/core/ingestion/utils/method-props.d.ts +32 -0
- package/dist/core/ingestion/utils/method-props.js +147 -0
- package/dist/core/ingestion/vue-sfc-extractor.d.ts +44 -0
- package/dist/core/ingestion/vue-sfc-extractor.js +94 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +31 -19
- package/dist/core/ingestion/workers/parse-worker.js +469 -200
- package/dist/core/lbug/lbug-adapter.d.ts +6 -0
- package/dist/core/lbug/lbug-adapter.js +134 -27
- package/dist/core/lbug/pool-adapter.d.ts +76 -0
- package/dist/core/lbug/pool-adapter.js +522 -0
- package/dist/core/run-analyze.d.ts +2 -0
- package/dist/core/run-analyze.js +1 -1
- package/dist/core/search/bm25-index.js +1 -1
- package/dist/core/tree-sitter/parser-loader.js +1 -0
- package/dist/core/wiki/graph-queries.js +1 -1
- package/dist/mcp/core/embedder.js +6 -5
- package/dist/mcp/core/lbug-adapter.d.ts +3 -63
- package/dist/mcp/core/lbug-adapter.js +3 -484
- package/dist/mcp/local/local-backend.d.ts +31 -2
- package/dist/mcp/local/local-backend.js +255 -46
- package/dist/mcp/resources.js +5 -4
- package/dist/mcp/staleness.d.ts +3 -13
- package/dist/mcp/staleness.js +2 -31
- package/dist/mcp/tools.js +80 -4
- package/dist/server/analyze-job.d.ts +2 -0
- package/dist/server/analyze-job.js +4 -0
- package/dist/server/api.d.ts +20 -1
- package/dist/server/api.js +306 -71
- package/dist/server/git-clone.d.ts +2 -1
- package/dist/server/git-clone.js +98 -5
- package/dist/storage/git.d.ts +13 -0
- package/dist/storage/git.js +25 -0
- package/dist/storage/repo-manager.js +1 -1
- package/package.json +9 -3
- package/scripts/patch-tree-sitter-swift.cjs +78 -0
- package/vendor/tree-sitter-proto/binding.gyp +30 -0
- package/vendor/tree-sitter-proto/bindings/node/binding.cc +20 -0
- package/vendor/tree-sitter-proto/bindings/node/index.d.ts +28 -0
- package/vendor/tree-sitter-proto/bindings/node/index.js +7 -0
- package/vendor/tree-sitter-proto/package.json +18 -0
- package/vendor/tree-sitter-proto/src/node-types.json +1145 -0
- package/vendor/tree-sitter-proto/src/parser.c +10149 -0
- package/vendor/tree-sitter-proto/src/tree_sitter/alloc.h +54 -0
- package/vendor/tree-sitter-proto/src/tree_sitter/array.h +291 -0
- package/vendor/tree-sitter-proto/src/tree_sitter/parser.h +266 -0
- package/dist/core/ingestion/named-binding-processor.d.ts +0 -18
- package/dist/core/ingestion/named-binding-processor.js +0 -42
- package/dist/core/ingestion/resolution-context.d.ts +0 -58
- package/dist/core/ingestion/resolution-context.js +0 -135
- package/dist/core/ingestion/symbol-table.d.ts +0 -79
- package/dist/core/ingestion/symbol-table.js +0 -115
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonicalize an HTTP path for matching against Route.name in the graph.
|
|
3
|
+
* Mirrors core/ingestion/pipeline.ts ensureSlash semantics:
|
|
4
|
+
* - Ensures a leading slash.
|
|
5
|
+
* - Strips trailing slashes (except the root "/").
|
|
6
|
+
* - Normalizes consecutive slashes.
|
|
7
|
+
* - Does NOT lowercase (route matching is case-sensitive).
|
|
8
|
+
*/
|
|
9
|
+
function normalizeRoutePath(raw) {
|
|
10
|
+
const trimmed = raw.trim();
|
|
11
|
+
if (!trimmed)
|
|
12
|
+
return '/';
|
|
13
|
+
const withLeading = trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
|
|
14
|
+
const collapsed = withLeading.replace(/\/+/g, '/');
|
|
15
|
+
if (collapsed === '/')
|
|
16
|
+
return '/';
|
|
17
|
+
return collapsed.replace(/\/+$/, '');
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Stable synthetic symbolUid for a manifest-declared contract whose target
|
|
21
|
+
* symbol could not be resolved against the per-repo graph (resolveSymbol
|
|
22
|
+
* returned null). Two reasons we don't leave the uid empty:
|
|
23
|
+
*
|
|
24
|
+
* 1. The bridge stores Contract nodes keyed in part by symbolUid; an empty
|
|
25
|
+
* uid means downstream Cypher queries that anchor on `provider.symbolUid`
|
|
26
|
+
* can't tell two different unresolved manifest contracts apart.
|
|
27
|
+
* 2. The cross-impact bridge query in cross-impact.ts joins local impact
|
|
28
|
+
* results to bridge contracts via `WHERE provider.symbolUid IN $localUids`.
|
|
29
|
+
* If the local impact engine produces a deterministic identifier for the
|
|
30
|
+
* unresolved target, it must agree with the value the bridge stored. A
|
|
31
|
+
* synthetic uid keyed off (repo, contractId) is the only thing both sides
|
|
32
|
+
* can derive without knowing about each other.
|
|
33
|
+
*
|
|
34
|
+
* Format: `manifest::<repo>::<contractId>`. Stable across syncs, scoped to a
|
|
35
|
+
* single repo within a group, and never collides with real indexer uids
|
|
36
|
+
* (which never start with `manifest::`).
|
|
37
|
+
*/
|
|
38
|
+
export function manifestSymbolUid(repo, contractId) {
|
|
39
|
+
return `manifest::${repo}::${contractId}`;
|
|
40
|
+
}
|
|
41
|
+
export class ManifestExtractor {
|
|
42
|
+
async extractFromManifest(links, dbExecutors) {
|
|
43
|
+
const contracts = [];
|
|
44
|
+
const crossLinks = [];
|
|
45
|
+
for (const link of links) {
|
|
46
|
+
const contractId = this.buildContractId(link.type, link.contract);
|
|
47
|
+
const providerRepo = link.role === 'provider' ? link.from : link.to;
|
|
48
|
+
const consumerRepo = link.role === 'provider' ? link.to : link.from;
|
|
49
|
+
const providerSymbol = await this.resolveSymbol(providerRepo, link, dbExecutors);
|
|
50
|
+
const consumerSymbol = await this.resolveSymbol(consumerRepo, link, dbExecutors);
|
|
51
|
+
const providerRef = providerSymbol || { filePath: '', name: link.contract };
|
|
52
|
+
const consumerRef = consumerSymbol || { filePath: '', name: link.contract };
|
|
53
|
+
// When the resolver finds a real graph symbol we keep its uid, otherwise
|
|
54
|
+
// fall back to the deterministic synthetic uid (see manifestSymbolUid).
|
|
55
|
+
const providerUid = providerSymbol?.uid || manifestSymbolUid(providerRepo, contractId);
|
|
56
|
+
const consumerUid = consumerSymbol?.uid || manifestSymbolUid(consumerRepo, contractId);
|
|
57
|
+
contracts.push({
|
|
58
|
+
contractId,
|
|
59
|
+
type: link.type,
|
|
60
|
+
role: 'provider',
|
|
61
|
+
symbolUid: providerUid,
|
|
62
|
+
symbolRef: providerRef,
|
|
63
|
+
symbolName: link.contract,
|
|
64
|
+
confidence: 1.0,
|
|
65
|
+
meta: { source: 'manifest' },
|
|
66
|
+
repo: providerRepo,
|
|
67
|
+
});
|
|
68
|
+
contracts.push({
|
|
69
|
+
contractId,
|
|
70
|
+
type: link.type,
|
|
71
|
+
role: 'consumer',
|
|
72
|
+
symbolUid: consumerUid,
|
|
73
|
+
symbolRef: consumerRef,
|
|
74
|
+
symbolName: link.contract,
|
|
75
|
+
confidence: 1.0,
|
|
76
|
+
meta: { source: 'manifest' },
|
|
77
|
+
repo: consumerRepo,
|
|
78
|
+
});
|
|
79
|
+
crossLinks.push({
|
|
80
|
+
from: { repo: consumerRepo, symbolUid: consumerUid, symbolRef: consumerRef },
|
|
81
|
+
to: { repo: providerRepo, symbolUid: providerUid, symbolRef: providerRef },
|
|
82
|
+
type: link.type,
|
|
83
|
+
contractId,
|
|
84
|
+
matchType: 'manifest',
|
|
85
|
+
confidence: 1.0,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return { contracts, crossLinks };
|
|
89
|
+
}
|
|
90
|
+
async resolveSymbol(repoPathKey, link, dbExecutors) {
|
|
91
|
+
const executor = dbExecutors?.get(repoPathKey);
|
|
92
|
+
if (!executor)
|
|
93
|
+
return null;
|
|
94
|
+
// NOTE: All lookups use EXACT equality on the relevant name field and
|
|
95
|
+
// deterministic ORDER BY before LIMIT 1. Previous versions used CONTAINS
|
|
96
|
+
// for fuzzy matching (plus an unconditional ".proto" fallback for gRPC)
|
|
97
|
+
// which produced silent false positives: e.g. manifest "/orders" would
|
|
98
|
+
// match "/suborders", and a gRPC manifest entry in a repo with any
|
|
99
|
+
// .proto file would attach to a random proto symbol.
|
|
100
|
+
//
|
|
101
|
+
// If resolveSymbol returns null, the extractor falls back to a
|
|
102
|
+
// deterministic synthetic uid via `manifestSymbolUid(repo, contractId)`
|
|
103
|
+
// (see the function's docstring for why synthetic rather than empty).
|
|
104
|
+
// Cross-impact still works: the bridge query joins on the synthetic
|
|
105
|
+
// uid, and the local impact engine derives the same uid for the
|
|
106
|
+
// unresolved symbol — name-based hints are the additional safety net.
|
|
107
|
+
try {
|
|
108
|
+
let rows;
|
|
109
|
+
if (link.type === 'http') {
|
|
110
|
+
// Route.name is the canonicalized URL path (see
|
|
111
|
+
// core/ingestion/pipeline.ts ensureSlash + generateId('Route', ...)).
|
|
112
|
+
// Normalize the manifest contract the same way so a user-written
|
|
113
|
+
// "/api/orders" matches "api/orders" in the graph.
|
|
114
|
+
const normalized = normalizeRoutePath(link.contract);
|
|
115
|
+
rows = await executor(`MATCH (handler)-[r:CodeRelation {type: 'HANDLES_ROUTE'}]->(route:Route)
|
|
116
|
+
WHERE route.name = $normalized
|
|
117
|
+
RETURN handler.id AS uid, handler.name AS name, handler.filePath AS filePath
|
|
118
|
+
ORDER BY handler.filePath ASC
|
|
119
|
+
LIMIT 1`, { normalized });
|
|
120
|
+
}
|
|
121
|
+
else if (link.type === 'topic') {
|
|
122
|
+
// Topic names aren't a first-class NodeLabel in the graph —
|
|
123
|
+
// topics are referenced by function/method symbols (Kafka
|
|
124
|
+
// listeners, publishers). Restrict to symbol-like labels to
|
|
125
|
+
// avoid cross-matching Files/Variables/Imports that happen to
|
|
126
|
+
// share the topic name.
|
|
127
|
+
rows = await executor(`MATCH (n:Function|Method|Class|Interface) WHERE n.name = $contract
|
|
128
|
+
RETURN n.id AS uid, n.name AS name, n.filePath AS filePath
|
|
129
|
+
ORDER BY n.filePath ASC
|
|
130
|
+
LIMIT 1`, { contract: link.contract });
|
|
131
|
+
}
|
|
132
|
+
else if (link.type === 'grpc') {
|
|
133
|
+
// Contract is "Service/Method" or just "Service" (or package.Service
|
|
134
|
+
// variants). Prefer matching by method name when present, otherwise
|
|
135
|
+
// by service name. NO .proto path fallback — that's guaranteed to
|
|
136
|
+
// return a wrong symbol in any repo with more than one proto file.
|
|
137
|
+
// Label filters scope lookups: methods → Function|Method, services
|
|
138
|
+
// → Class|Interface (no label match = no silent wrong hits on
|
|
139
|
+
// File/Variable nodes that happen to share the name).
|
|
140
|
+
const parts = link.contract.split('/');
|
|
141
|
+
const serviceName = parts[0]?.trim() ?? '';
|
|
142
|
+
const methodName = parts[1]?.trim() ?? '';
|
|
143
|
+
if (methodName) {
|
|
144
|
+
rows = await executor(`MATCH (n:Function|Method) WHERE n.name = $methodName
|
|
145
|
+
RETURN n.id AS uid, n.name AS name, n.filePath AS filePath
|
|
146
|
+
ORDER BY n.filePath ASC
|
|
147
|
+
LIMIT 1`, { methodName });
|
|
148
|
+
}
|
|
149
|
+
else if (serviceName) {
|
|
150
|
+
rows = await executor(`MATCH (n:Class|Interface) WHERE n.name = $serviceName
|
|
151
|
+
RETURN n.id AS uid, n.name AS name, n.filePath AS filePath
|
|
152
|
+
ORDER BY n.filePath ASC
|
|
153
|
+
LIMIT 1`, { serviceName });
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
rows = [];
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
else if (link.type === 'lib') {
|
|
160
|
+
// Only exact match on the symbol's name. Previous fallback to
|
|
161
|
+
// CONTAINS on n.filePath would promote "react" to "react-native"
|
|
162
|
+
// or "@types/react" — silent wrong attribution. Restrict to
|
|
163
|
+
// package-level labels so we don't return arbitrary symbols
|
|
164
|
+
// named after a library.
|
|
165
|
+
rows = await executor(`MATCH (n:Package|Module) WHERE n.name = $contract
|
|
166
|
+
RETURN n.id AS uid, n.name AS name, n.filePath AS filePath
|
|
167
|
+
ORDER BY n.filePath ASC
|
|
168
|
+
LIMIT 1`, { contract: link.contract });
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
if (rows.length > 0) {
|
|
174
|
+
return {
|
|
175
|
+
filePath: rows[0].filePath,
|
|
176
|
+
name: rows[0].name,
|
|
177
|
+
uid: String(rows[0].uid ?? ''),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
catch (err) {
|
|
182
|
+
// Log but don't throw: a broken graph query in one repo shouldn't
|
|
183
|
+
// fail the whole manifest extraction. Unresolved contracts still
|
|
184
|
+
// get a synthetic symbolUid below, so cross-impact can proceed.
|
|
185
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
186
|
+
console.warn(`[manifest-extractor] resolveSymbol failed for ${link.type}:${link.contract} ` +
|
|
187
|
+
`in ${repoPathKey}: ${message}`);
|
|
188
|
+
}
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Build a canonical contract id for a manifest link.
|
|
193
|
+
*
|
|
194
|
+
* HTTP is the only type with two valid forms:
|
|
195
|
+
* - Explicit method: `"GET::/api/orders"` → `"http::GET::/api/orders"`
|
|
196
|
+
* (matches exactly against `HttpRouteExtractor` provider/consumer
|
|
197
|
+
* contracts, which are also keyed by `http::<METHOD>::<path>`).
|
|
198
|
+
* - Method-agnostic: `"/api/orders"` → `"http::*::/api/orders"`
|
|
199
|
+
* — the `*` is a wildcard and is intended to match any concrete
|
|
200
|
+
* HTTP method on that path. Wildcard-aware matching is the
|
|
201
|
+
* responsibility of the sync / cross-impact layer (see #793);
|
|
202
|
+
* downstream code should treat `http::*::<path>` as matching
|
|
203
|
+
* every `http::<METHOD>::<path>` for the same path.
|
|
204
|
+
*
|
|
205
|
+
* Recommend the explicit-method form in group.yaml whenever the
|
|
206
|
+
* manifest author knows the method — it round-trips through exact
|
|
207
|
+
* equality matching without requiring wildcard logic downstream.
|
|
208
|
+
*
|
|
209
|
+
* NOTE on exhaustiveness: the switch covers every current
|
|
210
|
+
* `ContractType` variant and falls through to a `never` assertion so
|
|
211
|
+
* TypeScript fails the build if a new variant is added without a
|
|
212
|
+
* corresponding case.
|
|
213
|
+
*/
|
|
214
|
+
buildContractId(type, contract) {
|
|
215
|
+
switch (type) {
|
|
216
|
+
case 'http': {
|
|
217
|
+
if (/^[A-Za-z]+::/.test(contract))
|
|
218
|
+
return `http::${contract}`;
|
|
219
|
+
return `http::*::${contract}`;
|
|
220
|
+
}
|
|
221
|
+
case 'grpc':
|
|
222
|
+
return `grpc::${contract}`;
|
|
223
|
+
case 'topic':
|
|
224
|
+
return `topic::${contract}`;
|
|
225
|
+
case 'lib':
|
|
226
|
+
return `lib::${contract}`;
|
|
227
|
+
case 'custom':
|
|
228
|
+
return `custom::${contract}`;
|
|
229
|
+
default: {
|
|
230
|
+
const _exhaustive = type;
|
|
231
|
+
throw new Error(`Unhandled ContractType: ${String(_exhaustive)}`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ContractExtractor, CypherExecutor } from '../contract-extractor.js';
|
|
2
|
+
import type { ExtractedContract, RepoHandle } from '../types.js';
|
|
3
|
+
export declare class TopicExtractor implements ContractExtractor {
|
|
4
|
+
type: "topic";
|
|
5
|
+
canExtract(_repo: RepoHandle): Promise<boolean>;
|
|
6
|
+
extract(_dbExecutor: CypherExecutor | null, repoPath: string, _repo: RepoHandle): Promise<ExtractedContract[]>;
|
|
7
|
+
private dedupe;
|
|
8
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { glob } from 'glob';
|
|
2
|
+
import Parser from 'tree-sitter';
|
|
3
|
+
import { readSafe } from './fs-utils.js';
|
|
4
|
+
import { scanFile, unquoteLiteral } from './tree-sitter-scanner.js';
|
|
5
|
+
import { TOPIC_SCAN_GLOB, getProviderForFile, } from './topic-patterns/index.js';
|
|
6
|
+
/**
|
|
7
|
+
* Language-agnostic orchestrator for topic (message broker) contract
|
|
8
|
+
* extraction. All grammar-specific knowledge lives in `topic-patterns/*`
|
|
9
|
+
* — this file must not import any tree-sitter grammar directly.
|
|
10
|
+
*
|
|
11
|
+
* Flow per file:
|
|
12
|
+
* 1. `getProviderForFile(rel)` → compiled plugin (or `undefined` if the
|
|
13
|
+
* file's extension isn't registered, in which case we skip it).
|
|
14
|
+
* 2. `scanFile(parser, provider, content)` → list of `{meta, valueText}`
|
|
15
|
+
* pairs, one per matched literal.
|
|
16
|
+
* 3. `unquoteLiteral(valueText)` → the raw topic string.
|
|
17
|
+
* 4. `makeContract(topic, meta, relPath)` → `ExtractedContract`.
|
|
18
|
+
*
|
|
19
|
+
* Adding a new language is a one-file edit in `topic-patterns/index.ts`.
|
|
20
|
+
*/
|
|
21
|
+
function makeContract(topicName, meta, filePath) {
|
|
22
|
+
return {
|
|
23
|
+
contractId: `topic::${topicName}`,
|
|
24
|
+
type: 'topic',
|
|
25
|
+
role: meta.role,
|
|
26
|
+
symbolUid: '',
|
|
27
|
+
symbolRef: { filePath: filePath.replace(/\\/g, '/'), name: meta.symbolName },
|
|
28
|
+
symbolName: meta.symbolName,
|
|
29
|
+
confidence: meta.confidence,
|
|
30
|
+
meta: {
|
|
31
|
+
broker: meta.broker,
|
|
32
|
+
topicName,
|
|
33
|
+
extractionStrategy: 'tree_sitter',
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export class TopicExtractor {
|
|
38
|
+
type = 'topic';
|
|
39
|
+
async canExtract(_repo) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
async extract(_dbExecutor, repoPath, _repo) {
|
|
43
|
+
const files = await glob(TOPIC_SCAN_GLOB, {
|
|
44
|
+
cwd: repoPath,
|
|
45
|
+
ignore: [
|
|
46
|
+
'**/node_modules/**',
|
|
47
|
+
'**/.git/**',
|
|
48
|
+
'**/vendor/**',
|
|
49
|
+
'**/dist/**',
|
|
50
|
+
'**/build/**',
|
|
51
|
+
// Language-level test file conventions. Go test files
|
|
52
|
+
// `*_test.go` live next to source; other languages either use
|
|
53
|
+
// separate test directories (Python's `tests/`, Java's
|
|
54
|
+
// `src/test/`) or are already covered by the dist/build ignores.
|
|
55
|
+
// Pushed to the glob level so the orchestrator stays
|
|
56
|
+
// language-agnostic.
|
|
57
|
+
'**/*_test.go',
|
|
58
|
+
],
|
|
59
|
+
nodir: true,
|
|
60
|
+
});
|
|
61
|
+
// One parser reused across files; the scanner calls `setLanguage` per
|
|
62
|
+
// file based on which plugin the registry returns.
|
|
63
|
+
const parser = new Parser();
|
|
64
|
+
const out = [];
|
|
65
|
+
for (const rel of files) {
|
|
66
|
+
const provider = getProviderForFile(rel);
|
|
67
|
+
if (!provider)
|
|
68
|
+
continue;
|
|
69
|
+
const content = readSafe(repoPath, rel);
|
|
70
|
+
if (!content)
|
|
71
|
+
continue;
|
|
72
|
+
const matches = scanFile(parser, provider, content);
|
|
73
|
+
for (const match of matches) {
|
|
74
|
+
const valueNode = match.captures.value;
|
|
75
|
+
if (!valueNode)
|
|
76
|
+
continue;
|
|
77
|
+
const topicName = unquoteLiteral(valueNode.text);
|
|
78
|
+
if (!topicName)
|
|
79
|
+
continue;
|
|
80
|
+
out.push(makeContract(topicName, match.meta, rel));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return this.dedupe(out);
|
|
84
|
+
}
|
|
85
|
+
dedupe(items) {
|
|
86
|
+
const seen = new Set();
|
|
87
|
+
const out = [];
|
|
88
|
+
for (const c of items) {
|
|
89
|
+
const k = `${c.contractId}|${c.role}|${c.symbolRef.filePath}`;
|
|
90
|
+
if (seen.has(k))
|
|
91
|
+
continue;
|
|
92
|
+
seen.add(k);
|
|
93
|
+
out.push(c);
|
|
94
|
+
}
|
|
95
|
+
return out;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import Go from 'tree-sitter-go';
|
|
2
|
+
import { compilePatterns } from '../tree-sitter-scanner.js';
|
|
3
|
+
/**
|
|
4
|
+
* Go topic extraction patterns.
|
|
5
|
+
*
|
|
6
|
+
* Detects Sarama, segmentio/kafka-go and nats.go producer/consumer APIs:
|
|
7
|
+
* - `X.ConsumePartition("topic", ...)`
|
|
8
|
+
* - `sarama.ProducerMessage{Topic: "xxx"}`
|
|
9
|
+
* - `kafka.Writer{Topic: "xxx"}` / `kafka.WriterConfig{Topic: ...}`
|
|
10
|
+
* - `kafka.Reader{Topic: "xxx"}` / `kafka.ReaderConfig{Topic: ...}`
|
|
11
|
+
* - `nc.Subscribe("topic", ...)` / `js.Subscribe("topic", ...)`
|
|
12
|
+
* - `nc.Publish("topic", ...)` / `js.Publish("topic", ...)`
|
|
13
|
+
*
|
|
14
|
+
* Every query MUST bind `@value` to the topic literal node.
|
|
15
|
+
*/
|
|
16
|
+
const GO_TOPIC_SPEC = {
|
|
17
|
+
name: 'go-topic',
|
|
18
|
+
language: Go,
|
|
19
|
+
patterns: [
|
|
20
|
+
{
|
|
21
|
+
meta: {
|
|
22
|
+
role: 'consumer',
|
|
23
|
+
broker: 'kafka',
|
|
24
|
+
confidence: 0.7,
|
|
25
|
+
symbolName: 'ConsumePartition',
|
|
26
|
+
},
|
|
27
|
+
query: `
|
|
28
|
+
(call_expression
|
|
29
|
+
function: (selector_expression
|
|
30
|
+
field: (field_identifier) @method (#eq? @method "ConsumePartition"))
|
|
31
|
+
arguments: (argument_list . (interpreted_string_literal) @value))
|
|
32
|
+
`,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
meta: {
|
|
36
|
+
role: 'provider',
|
|
37
|
+
broker: 'kafka',
|
|
38
|
+
confidence: 0.75,
|
|
39
|
+
symbolName: 'sarama.ProducerMessage',
|
|
40
|
+
},
|
|
41
|
+
query: `
|
|
42
|
+
(composite_literal
|
|
43
|
+
type: (qualified_type
|
|
44
|
+
package: (package_identifier) @pkg (#eq? @pkg "sarama")
|
|
45
|
+
name: (type_identifier) @ty (#eq? @ty "ProducerMessage"))
|
|
46
|
+
body: (literal_value
|
|
47
|
+
(keyed_element
|
|
48
|
+
(literal_element (identifier) @field (#eq? @field "Topic"))
|
|
49
|
+
(literal_element (interpreted_string_literal) @value))))
|
|
50
|
+
`,
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
meta: {
|
|
54
|
+
role: 'provider',
|
|
55
|
+
broker: 'kafka',
|
|
56
|
+
confidence: 0.75,
|
|
57
|
+
symbolName: 'kafka.Writer',
|
|
58
|
+
},
|
|
59
|
+
query: `
|
|
60
|
+
(composite_literal
|
|
61
|
+
type: (qualified_type
|
|
62
|
+
package: (package_identifier) @pkg (#eq? @pkg "kafka")
|
|
63
|
+
name: (type_identifier) @ty (#match? @ty "^(Writer|WriterConfig)$"))
|
|
64
|
+
body: (literal_value
|
|
65
|
+
(keyed_element
|
|
66
|
+
(literal_element (identifier) @field (#eq? @field "Topic"))
|
|
67
|
+
(literal_element (interpreted_string_literal) @value))))
|
|
68
|
+
`,
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
meta: {
|
|
72
|
+
role: 'consumer',
|
|
73
|
+
broker: 'kafka',
|
|
74
|
+
confidence: 0.75,
|
|
75
|
+
symbolName: 'kafka.Reader',
|
|
76
|
+
},
|
|
77
|
+
query: `
|
|
78
|
+
(composite_literal
|
|
79
|
+
type: (qualified_type
|
|
80
|
+
package: (package_identifier) @pkg (#eq? @pkg "kafka")
|
|
81
|
+
name: (type_identifier) @ty (#match? @ty "^(Reader|ReaderConfig)$"))
|
|
82
|
+
body: (literal_value
|
|
83
|
+
(keyed_element
|
|
84
|
+
(literal_element (identifier) @field (#eq? @field "Topic"))
|
|
85
|
+
(literal_element (interpreted_string_literal) @value))))
|
|
86
|
+
`,
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
meta: {
|
|
90
|
+
role: 'consumer',
|
|
91
|
+
broker: 'nats',
|
|
92
|
+
confidence: 0.8,
|
|
93
|
+
symbolName: 'nc.Subscribe',
|
|
94
|
+
},
|
|
95
|
+
query: `
|
|
96
|
+
(call_expression
|
|
97
|
+
function: (selector_expression
|
|
98
|
+
operand: (identifier) @obj (#match? @obj "^(nc|js)$")
|
|
99
|
+
field: (field_identifier) @method (#match? @method "^[Ss]ubscribe$"))
|
|
100
|
+
arguments: (argument_list . (interpreted_string_literal) @value))
|
|
101
|
+
`,
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
meta: {
|
|
105
|
+
role: 'provider',
|
|
106
|
+
broker: 'nats',
|
|
107
|
+
confidence: 0.8,
|
|
108
|
+
symbolName: 'nc.Publish',
|
|
109
|
+
},
|
|
110
|
+
query: `
|
|
111
|
+
(call_expression
|
|
112
|
+
function: (selector_expression
|
|
113
|
+
operand: (identifier) @obj (#match? @obj "^(nc|js)$")
|
|
114
|
+
field: (field_identifier) @method (#match? @method "^[Pp]ublish$"))
|
|
115
|
+
arguments: (argument_list . (interpreted_string_literal) @value))
|
|
116
|
+
`,
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
};
|
|
120
|
+
export const GO_TOPIC_PROVIDER = compilePatterns(GO_TOPIC_SPEC);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CompiledPatterns } from '../tree-sitter-scanner.js';
|
|
2
|
+
import type { TopicMeta } from './types.js';
|
|
3
|
+
export type { TopicMeta, Broker } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Glob pattern for files worth scanning. Kept here so adding a new
|
|
6
|
+
* language to the registry also widens the glob automatically via a
|
|
7
|
+
* single edit.
|
|
8
|
+
*/
|
|
9
|
+
export declare const TOPIC_SCAN_GLOB = "**/*.{ts,tsx,js,jsx,java,go,py}";
|
|
10
|
+
/**
|
|
11
|
+
* Return the compiled provider registered for the given file's
|
|
12
|
+
* extension, or `undefined` if the extension is not registered.
|
|
13
|
+
*/
|
|
14
|
+
export declare function getProviderForFile(rel: string): CompiledPatterns<TopicMeta> | undefined;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
import { JAVA_TOPIC_PROVIDER } from './java.js';
|
|
3
|
+
import { GO_TOPIC_PROVIDER } from './go.js';
|
|
4
|
+
import { PYTHON_TOPIC_PROVIDER } from './python.js';
|
|
5
|
+
import { JAVASCRIPT_TOPIC_PROVIDER, TYPESCRIPT_TOPIC_PROVIDER, TSX_TOPIC_PROVIDER, } from './node.js';
|
|
6
|
+
/**
|
|
7
|
+
* File-extension → compiled-plugin registry for topic extraction. The
|
|
8
|
+
* top-level orchestrator (`topic-extractor.ts`) looks up the plugin for
|
|
9
|
+
* each file it visits and delegates the scanning to `tree-sitter-scanner`.
|
|
10
|
+
*
|
|
11
|
+
* Keys are lowercase extensions including the leading dot. To add a new
|
|
12
|
+
* language, drop a `topic-patterns/<lang>.ts` that exports a compiled
|
|
13
|
+
* provider, import it here and register the extension(s). No edits to
|
|
14
|
+
* `topic-extractor.ts` are required.
|
|
15
|
+
*/
|
|
16
|
+
const REGISTRY = {
|
|
17
|
+
'.java': JAVA_TOPIC_PROVIDER,
|
|
18
|
+
'.go': GO_TOPIC_PROVIDER,
|
|
19
|
+
'.py': PYTHON_TOPIC_PROVIDER,
|
|
20
|
+
'.js': JAVASCRIPT_TOPIC_PROVIDER,
|
|
21
|
+
'.jsx': JAVASCRIPT_TOPIC_PROVIDER,
|
|
22
|
+
'.ts': TYPESCRIPT_TOPIC_PROVIDER,
|
|
23
|
+
'.tsx': TSX_TOPIC_PROVIDER,
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Glob pattern for files worth scanning. Kept here so adding a new
|
|
27
|
+
* language to the registry also widens the glob automatically via a
|
|
28
|
+
* single edit.
|
|
29
|
+
*/
|
|
30
|
+
export const TOPIC_SCAN_GLOB = '**/*.{ts,tsx,js,jsx,java,go,py}';
|
|
31
|
+
/**
|
|
32
|
+
* Return the compiled provider registered for the given file's
|
|
33
|
+
* extension, or `undefined` if the extension is not registered.
|
|
34
|
+
*/
|
|
35
|
+
export function getProviderForFile(rel) {
|
|
36
|
+
const ext = path.extname(rel).toLowerCase();
|
|
37
|
+
return REGISTRY[ext];
|
|
38
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import Java from 'tree-sitter-java';
|
|
2
|
+
import { compilePatterns } from '../tree-sitter-scanner.js';
|
|
3
|
+
/**
|
|
4
|
+
* Java topic extraction patterns.
|
|
5
|
+
*
|
|
6
|
+
* Detects Kafka and RabbitMQ (Spring conventions) producer/consumer APIs:
|
|
7
|
+
* - `@KafkaListener(topics = "xxx")`
|
|
8
|
+
* - `@RabbitListener(queues = "xxx")`
|
|
9
|
+
* - `kafkaTemplate.send("xxx", ...)`
|
|
10
|
+
* - `rabbitTemplate.convertAndSend("xxx", ...)`
|
|
11
|
+
*
|
|
12
|
+
* Every query MUST bind `@value` to the topic literal node.
|
|
13
|
+
*/
|
|
14
|
+
const JAVA_TOPIC_SPEC = {
|
|
15
|
+
name: 'java-topic',
|
|
16
|
+
language: Java,
|
|
17
|
+
patterns: [
|
|
18
|
+
{
|
|
19
|
+
meta: {
|
|
20
|
+
role: 'consumer',
|
|
21
|
+
broker: 'kafka',
|
|
22
|
+
confidence: 0.8,
|
|
23
|
+
symbolName: 'kafkaListener',
|
|
24
|
+
},
|
|
25
|
+
query: `
|
|
26
|
+
(annotation
|
|
27
|
+
name: (identifier) @name (#eq? @name "KafkaListener")
|
|
28
|
+
arguments: (annotation_argument_list
|
|
29
|
+
(element_value_pair
|
|
30
|
+
key: (identifier) @key (#eq? @key "topics")
|
|
31
|
+
value: (string_literal) @value)))
|
|
32
|
+
`,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
meta: {
|
|
36
|
+
role: 'consumer',
|
|
37
|
+
broker: 'rabbitmq',
|
|
38
|
+
confidence: 0.8,
|
|
39
|
+
symbolName: 'rabbitListener',
|
|
40
|
+
},
|
|
41
|
+
query: `
|
|
42
|
+
(annotation
|
|
43
|
+
name: (identifier) @name (#eq? @name "RabbitListener")
|
|
44
|
+
arguments: (annotation_argument_list
|
|
45
|
+
(element_value_pair
|
|
46
|
+
key: (identifier) @key (#eq? @key "queues")
|
|
47
|
+
value: (string_literal) @value)))
|
|
48
|
+
`,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
meta: {
|
|
52
|
+
role: 'provider',
|
|
53
|
+
broker: 'kafka',
|
|
54
|
+
confidence: 0.8,
|
|
55
|
+
symbolName: 'kafkaTemplate.send',
|
|
56
|
+
},
|
|
57
|
+
query: `
|
|
58
|
+
(method_invocation
|
|
59
|
+
object: (identifier) @obj (#eq? @obj "kafkaTemplate")
|
|
60
|
+
name: (identifier) @method (#eq? @method "send")
|
|
61
|
+
arguments: (argument_list . (string_literal) @value))
|
|
62
|
+
`,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
meta: {
|
|
66
|
+
role: 'provider',
|
|
67
|
+
broker: 'rabbitmq',
|
|
68
|
+
confidence: 0.8,
|
|
69
|
+
symbolName: 'rabbitTemplate.convertAndSend',
|
|
70
|
+
},
|
|
71
|
+
query: `
|
|
72
|
+
(method_invocation
|
|
73
|
+
object: (identifier) @obj (#eq? @obj "rabbitTemplate")
|
|
74
|
+
name: (identifier) @method (#eq? @method "convertAndSend")
|
|
75
|
+
arguments: (argument_list . (string_literal) @value))
|
|
76
|
+
`,
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
};
|
|
80
|
+
export const JAVA_TOPIC_PROVIDER = compilePatterns(JAVA_TOPIC_SPEC);
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { TopicMeta } from './types.js';
|
|
2
|
+
export declare const JAVASCRIPT_TOPIC_PROVIDER: import("../tree-sitter-scanner.js").CompiledPatterns<TopicMeta>;
|
|
3
|
+
export declare const TYPESCRIPT_TOPIC_PROVIDER: import("../tree-sitter-scanner.js").CompiledPatterns<TopicMeta>;
|
|
4
|
+
export declare const TSX_TOPIC_PROVIDER: import("../tree-sitter-scanner.js").CompiledPatterns<TopicMeta>;
|