gitnexus 1.6.0 → 1.6.2-rc.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 (145) hide show
  1. package/README.md +73 -0
  2. package/dist/cli/analyze.js +50 -3
  3. package/dist/core/group/extractors/fs-utils.d.ts +10 -0
  4. package/dist/core/group/extractors/fs-utils.js +24 -0
  5. package/dist/core/group/extractors/grpc-extractor.d.ts +17 -8
  6. package/dist/core/group/extractors/grpc-extractor.js +328 -191
  7. package/dist/core/group/extractors/grpc-patterns/go.d.ts +2 -0
  8. package/dist/core/group/extractors/grpc-patterns/go.js +97 -0
  9. package/dist/core/group/extractors/grpc-patterns/index.d.ts +19 -0
  10. package/dist/core/group/extractors/grpc-patterns/index.js +46 -0
  11. package/dist/core/group/extractors/grpc-patterns/java.d.ts +2 -0
  12. package/dist/core/group/extractors/grpc-patterns/java.js +173 -0
  13. package/dist/core/group/extractors/grpc-patterns/node.d.ts +4 -0
  14. package/dist/core/group/extractors/grpc-patterns/node.js +290 -0
  15. package/dist/core/group/extractors/grpc-patterns/proto.d.ts +9 -0
  16. package/dist/core/group/extractors/grpc-patterns/proto.js +134 -0
  17. package/dist/core/group/extractors/grpc-patterns/python.d.ts +2 -0
  18. package/dist/core/group/extractors/grpc-patterns/python.js +67 -0
  19. package/dist/core/group/extractors/grpc-patterns/types.d.ts +50 -0
  20. package/dist/core/group/extractors/grpc-patterns/types.js +1 -0
  21. package/dist/core/group/extractors/http-patterns/go.d.ts +2 -0
  22. package/dist/core/group/extractors/http-patterns/go.js +215 -0
  23. package/dist/core/group/extractors/http-patterns/index.d.ts +17 -0
  24. package/dist/core/group/extractors/http-patterns/index.js +44 -0
  25. package/dist/core/group/extractors/http-patterns/java.d.ts +2 -0
  26. package/dist/core/group/extractors/http-patterns/java.js +253 -0
  27. package/dist/core/group/extractors/http-patterns/node.d.ts +4 -0
  28. package/dist/core/group/extractors/http-patterns/node.js +354 -0
  29. package/dist/core/group/extractors/http-patterns/php.d.ts +2 -0
  30. package/dist/core/group/extractors/http-patterns/php.js +70 -0
  31. package/dist/core/group/extractors/http-patterns/python.d.ts +2 -0
  32. package/dist/core/group/extractors/http-patterns/python.js +133 -0
  33. package/dist/core/group/extractors/http-patterns/types.d.ts +61 -0
  34. package/dist/core/group/extractors/http-patterns/types.js +1 -0
  35. package/dist/core/group/extractors/http-route-extractor.d.ts +10 -13
  36. package/dist/core/group/extractors/http-route-extractor.js +231 -238
  37. package/dist/core/group/extractors/manifest-extractor.d.ts +54 -0
  38. package/dist/core/group/extractors/manifest-extractor.js +277 -0
  39. package/dist/core/group/extractors/topic-extractor.d.ts +0 -1
  40. package/dist/core/group/extractors/topic-extractor.js +55 -192
  41. package/dist/core/group/extractors/topic-patterns/go.d.ts +2 -0
  42. package/dist/core/group/extractors/topic-patterns/go.js +120 -0
  43. package/dist/core/group/extractors/topic-patterns/index.d.ts +14 -0
  44. package/dist/core/group/extractors/topic-patterns/index.js +38 -0
  45. package/dist/core/group/extractors/topic-patterns/java.d.ts +2 -0
  46. package/dist/core/group/extractors/topic-patterns/java.js +80 -0
  47. package/dist/core/group/extractors/topic-patterns/node.d.ts +4 -0
  48. package/dist/core/group/extractors/topic-patterns/node.js +155 -0
  49. package/dist/core/group/extractors/topic-patterns/python.d.ts +2 -0
  50. package/dist/core/group/extractors/topic-patterns/python.js +116 -0
  51. package/dist/core/group/extractors/topic-patterns/types.d.ts +25 -0
  52. package/dist/core/group/extractors/topic-patterns/types.js +10 -0
  53. package/dist/core/group/extractors/tree-sitter-scanner.d.ts +113 -0
  54. package/dist/core/group/extractors/tree-sitter-scanner.js +94 -0
  55. package/dist/core/ingestion/binding-accumulator.d.ts +22 -17
  56. package/dist/core/ingestion/binding-accumulator.js +29 -25
  57. package/dist/core/ingestion/cobol-processor.d.ts +1 -1
  58. package/dist/core/ingestion/import-processor.js +1 -1
  59. package/dist/core/ingestion/language-config.js +1 -1
  60. package/dist/core/ingestion/language-provider.d.ts +32 -5
  61. package/dist/core/ingestion/languages/c-cpp.js +2 -2
  62. package/dist/core/ingestion/languages/dart.d.ts +1 -1
  63. package/dist/core/ingestion/languages/dart.js +2 -2
  64. package/dist/core/ingestion/languages/go.d.ts +1 -1
  65. package/dist/core/ingestion/languages/go.js +2 -2
  66. package/dist/core/ingestion/languages/ruby.js +16 -1
  67. package/dist/core/ingestion/languages/swift.d.ts +1 -1
  68. package/dist/core/ingestion/languages/swift.js +2 -2
  69. package/dist/core/ingestion/markdown-processor.d.ts +1 -1
  70. package/dist/core/ingestion/method-extractors/configs/jvm.js +1 -0
  71. package/dist/core/ingestion/method-extractors/configs/ruby.js +1 -0
  72. package/dist/core/ingestion/method-extractors/generic.d.ts +6 -0
  73. package/dist/core/ingestion/method-extractors/generic.js +48 -4
  74. package/dist/core/ingestion/method-types.d.ts +4 -0
  75. package/dist/core/ingestion/model/resolve.js +103 -48
  76. package/dist/core/ingestion/model/semantic-model.d.ts +1 -1
  77. package/dist/core/ingestion/model/semantic-model.js +1 -1
  78. package/dist/core/ingestion/model/symbol-table.d.ts +7 -7
  79. package/dist/core/ingestion/model/symbol-table.js +7 -7
  80. package/dist/core/ingestion/mro-processor.d.ts +1 -1
  81. package/dist/core/ingestion/mro-processor.js +1 -1
  82. package/dist/core/ingestion/parsing-processor.js +54 -42
  83. package/dist/core/ingestion/pipeline-phases/cobol.d.ts +16 -0
  84. package/dist/core/ingestion/pipeline-phases/cobol.js +45 -0
  85. package/dist/core/ingestion/pipeline-phases/communities.d.ts +16 -0
  86. package/dist/core/ingestion/pipeline-phases/communities.js +62 -0
  87. package/dist/core/ingestion/pipeline-phases/cross-file-impl.d.ts +17 -0
  88. package/dist/core/ingestion/pipeline-phases/cross-file-impl.js +156 -0
  89. package/dist/core/ingestion/pipeline-phases/cross-file.d.ts +37 -0
  90. package/dist/core/ingestion/pipeline-phases/cross-file.js +63 -0
  91. package/dist/core/ingestion/pipeline-phases/index.d.ts +21 -0
  92. package/dist/core/ingestion/pipeline-phases/index.js +22 -0
  93. package/dist/core/ingestion/pipeline-phases/markdown.d.ts +17 -0
  94. package/dist/core/ingestion/pipeline-phases/markdown.js +33 -0
  95. package/dist/core/ingestion/pipeline-phases/mro.d.ts +18 -0
  96. package/dist/core/ingestion/pipeline-phases/mro.js +36 -0
  97. package/dist/core/ingestion/pipeline-phases/orm-extraction.d.ts +22 -0
  98. package/dist/core/ingestion/pipeline-phases/orm-extraction.js +92 -0
  99. package/dist/core/ingestion/pipeline-phases/orm.d.ts +15 -0
  100. package/dist/core/ingestion/pipeline-phases/orm.js +74 -0
  101. package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +47 -0
  102. package/dist/core/ingestion/pipeline-phases/parse-impl.js +437 -0
  103. package/dist/core/ingestion/pipeline-phases/parse.d.ts +49 -0
  104. package/dist/core/ingestion/pipeline-phases/parse.js +33 -0
  105. package/dist/core/ingestion/pipeline-phases/processes.d.ts +16 -0
  106. package/dist/core/ingestion/pipeline-phases/processes.js +143 -0
  107. package/dist/core/ingestion/pipeline-phases/routes.d.ts +21 -0
  108. package/dist/core/ingestion/pipeline-phases/routes.js +243 -0
  109. package/dist/core/ingestion/pipeline-phases/runner.d.ts +22 -0
  110. package/dist/core/ingestion/pipeline-phases/runner.js +203 -0
  111. package/dist/core/ingestion/pipeline-phases/scan.d.ts +21 -0
  112. package/dist/core/ingestion/pipeline-phases/scan.js +46 -0
  113. package/dist/core/ingestion/pipeline-phases/structure.d.ts +27 -0
  114. package/dist/core/ingestion/pipeline-phases/structure.js +35 -0
  115. package/dist/core/ingestion/pipeline-phases/tools.d.ts +20 -0
  116. package/dist/core/ingestion/pipeline-phases/tools.js +79 -0
  117. package/dist/core/ingestion/pipeline-phases/types.d.ts +79 -0
  118. package/dist/core/ingestion/pipeline-phases/types.js +37 -0
  119. package/dist/core/ingestion/pipeline-phases/wildcard-synthesis.d.ts +70 -0
  120. package/dist/core/ingestion/pipeline-phases/wildcard-synthesis.js +312 -0
  121. package/dist/core/ingestion/pipeline.d.ts +16 -10
  122. package/dist/core/ingestion/pipeline.js +66 -1534
  123. package/dist/core/ingestion/process-processor.js +1 -1
  124. package/dist/core/ingestion/tree-sitter-queries.d.ts +2 -2
  125. package/dist/core/ingestion/tree-sitter-queries.js +69 -0
  126. package/dist/core/ingestion/utils/ast-helpers.d.ts +1 -3
  127. package/dist/core/ingestion/utils/ast-helpers.js +48 -21
  128. package/dist/core/ingestion/utils/env.d.ts +10 -0
  129. package/dist/core/ingestion/utils/env.js +10 -0
  130. package/dist/core/ingestion/utils/graph-sort.d.ts +58 -0
  131. package/dist/core/ingestion/utils/graph-sort.js +100 -0
  132. package/dist/core/ingestion/workers/parse-worker.js +12 -8
  133. package/dist/core/lbug/lbug-adapter.d.ts +28 -0
  134. package/dist/core/lbug/lbug-adapter.js +162 -57
  135. package/package.json +3 -3
  136. package/vendor/tree-sitter-proto/binding.gyp +30 -0
  137. package/vendor/tree-sitter-proto/bindings/node/binding.cc +20 -0
  138. package/vendor/tree-sitter-proto/bindings/node/index.d.ts +28 -0
  139. package/vendor/tree-sitter-proto/bindings/node/index.js +7 -0
  140. package/vendor/tree-sitter-proto/package.json +18 -0
  141. package/vendor/tree-sitter-proto/src/node-types.json +1145 -0
  142. package/vendor/tree-sitter-proto/src/parser.c +10149 -0
  143. package/vendor/tree-sitter-proto/src/tree_sitter/alloc.h +54 -0
  144. package/vendor/tree-sitter-proto/src/tree_sitter/array.h +291 -0
  145. package/vendor/tree-sitter-proto/src/tree_sitter/parser.h +266 -0
@@ -0,0 +1,67 @@
1
+ import Python from 'tree-sitter-python';
2
+ import { compilePatterns, runCompiledPatterns, } from '../tree-sitter-scanner.js';
3
+ /**
4
+ * Python gRPC plugin. Detects:
5
+ * - Provider: `add_XxxServicer_to_server(...)` calls (bare identifier
6
+ * or qualified attribute form `auth_pb2_grpc.add_XxxServicer_to_server`)
7
+ * - Consumer: `XxxStub(channel)` calls (bare or `auth_pb2_grpc.XxxStub`)
8
+ */
9
+ const ADD_SERVICER_RE = /^add_(\w+)Servicer_to_server$/;
10
+ const STUB_RE = /^(\w+)Stub$/;
11
+ /** Reserved names that would produce garbage service names. */
12
+ const STUB_IGNORE = new Set(['Mock', 'Test', 'Fake', 'Stub']);
13
+ // Any call whose target is either a bare identifier or an attribute
14
+ // access (`obj.method`). The plugin filters the function name in JS.
15
+ const CALL_PATTERNS = compilePatterns({
16
+ name: 'python-grpc-call',
17
+ language: Python,
18
+ patterns: [
19
+ {
20
+ meta: {},
21
+ query: `
22
+ (call
23
+ function: [
24
+ (identifier) @fn
25
+ (attribute attribute: (identifier) @fn)
26
+ ])
27
+ `,
28
+ },
29
+ ],
30
+ });
31
+ export const PYTHON_GRPC_PLUGIN = {
32
+ name: 'python-grpc',
33
+ language: Python,
34
+ scan(tree) {
35
+ const out = [];
36
+ for (const match of runCompiledPatterns(CALL_PATTERNS, tree)) {
37
+ const fnNode = match.captures.fn;
38
+ if (!fnNode)
39
+ continue;
40
+ const fnText = fnNode.text;
41
+ const addServicer = ADD_SERVICER_RE.exec(fnText);
42
+ if (addServicer) {
43
+ out.push({
44
+ role: 'provider',
45
+ serviceName: addServicer[1],
46
+ symbolName: fnText,
47
+ source: 'python_servicer',
48
+ confidenceWithProto: 0.8,
49
+ confidenceWithoutProto: 0.65,
50
+ });
51
+ continue;
52
+ }
53
+ const stubMatch = STUB_RE.exec(fnText);
54
+ if (stubMatch && !STUB_IGNORE.has(stubMatch[1])) {
55
+ out.push({
56
+ role: 'consumer',
57
+ serviceName: stubMatch[1],
58
+ symbolName: fnText,
59
+ source: 'python_stub',
60
+ confidenceWithProto: 0.75,
61
+ confidenceWithoutProto: 0.55,
62
+ });
63
+ }
64
+ }
65
+ return out;
66
+ },
67
+ };
@@ -0,0 +1,50 @@
1
+ import type Parser from 'tree-sitter';
2
+ /**
3
+ * Shared types for the grpc-extractor language plugins.
4
+ *
5
+ * Each plugin lives in its own file (java.ts, go.ts, ...) and owns the
6
+ * tree-sitter grammar import + query sources. The top-level
7
+ * `grpc-extractor.ts` orchestrator only knows about this type module
8
+ * and the plugin registry (`./index.ts`). It MUST NOT import any
9
+ * grammar or query text directly.
10
+ */
11
+ export type GrpcRole = 'provider' | 'consumer';
12
+ /**
13
+ * One raw gRPC detection produced by a plugin's `scan()` function. The
14
+ * orchestrator uses the proto map to resolve the full package-qualified
15
+ * contract id and choose a confidence based on whether the proto was
16
+ * found.
17
+ *
18
+ * Most patterns produce service-level detections; `TS @GrpcMethod` is
19
+ * the only pattern that captures an explicit `methodName`, producing
20
+ * a method-level contract (`grpc::pkg.Service/Method`).
21
+ */
22
+ export interface GrpcDetection {
23
+ role: GrpcRole;
24
+ /** Short service name, e.g. `"AuthService"`. */
25
+ serviceName: string;
26
+ /** Symbol name emitted into the contract's symbolRef. */
27
+ symbolName: string;
28
+ /** Metadata source label (goes into `meta.source`). */
29
+ source: string;
30
+ /** Explicit method name; set only by TS `@GrpcMethod`. */
31
+ methodName?: string;
32
+ /** Confidence when the proto map resolves the service. */
33
+ confidenceWithProto: number;
34
+ /** Confidence when the proto map has no entry. */
35
+ confidenceWithoutProto: number;
36
+ }
37
+ /**
38
+ * One language-scoped gRPC plugin. Plugins own the tree-sitter grammar
39
+ * and a `scan(tree)` function that returns zero or more
40
+ * `GrpcDetection`s. The plugin is free to run multiple compiled query
41
+ * bundles and walk the AST to cross-reference captures.
42
+ *
43
+ * `language` is typed `unknown` for the same reason as in
44
+ * `tree-sitter-scanner.ts`.
45
+ */
46
+ export interface GrpcLanguagePlugin {
47
+ name: string;
48
+ language: unknown;
49
+ scan(tree: Parser.Tree): GrpcDetection[];
50
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import type { HttpLanguagePlugin } from './types.js';
2
+ export declare const GO_HTTP_PLUGIN: HttpLanguagePlugin;
@@ -0,0 +1,215 @@
1
+ import Go from 'tree-sitter-go';
2
+ import { compilePatterns, runCompiledPatterns, unquoteLiteral, } from '../tree-sitter-scanner.js';
3
+ /**
4
+ * Go HTTP plugin. Handles:
5
+ * - gin / echo / chi framework routing — `r.GET("/path", handler)`
6
+ * - net/http stdlib — `http.HandleFunc("/path", handler)`
7
+ * - net/http consumer — `http.Get(...)`, `http.NewRequest("METHOD", ...)`
8
+ * - resty consumer — `client.R().Delete("/path")`
9
+ */
10
+ // ─── Provider: framework routing ──────────────────────────────────────
11
+ // Matches `\w+\.GET(...)` etc. (gin, echo, chi all share this shape).
12
+ // Captures the HTTP method (field name), path literal, and handler
13
+ // identifier passed as the second argument.
14
+ const FRAMEWORK_ROUTE_PATTERNS = compilePatterns({
15
+ name: 'go-framework-route',
16
+ language: Go,
17
+ patterns: [
18
+ {
19
+ meta: {},
20
+ query: `
21
+ (call_expression
22
+ function: (selector_expression
23
+ field: (field_identifier) @http_method (#match? @http_method "^(GET|POST|PUT|DELETE|PATCH)$"))
24
+ arguments: (argument_list
25
+ (interpreted_string_literal) @path
26
+ (identifier) @handler))
27
+ `,
28
+ },
29
+ ],
30
+ });
31
+ // ─── Provider: net/http `http.HandleFunc("/p", handler)` ─────────────
32
+ const HANDLE_FUNC_PATTERNS = compilePatterns({
33
+ name: 'go-handle-func',
34
+ language: Go,
35
+ patterns: [
36
+ {
37
+ meta: {},
38
+ query: `
39
+ (call_expression
40
+ function: (selector_expression
41
+ operand: (identifier) @pkg (#eq? @pkg "http")
42
+ field: (field_identifier) @fn (#eq? @fn "HandleFunc"))
43
+ arguments: (argument_list
44
+ (interpreted_string_literal) @path
45
+ (identifier) @handler))
46
+ `,
47
+ },
48
+ ],
49
+ });
50
+ // ─── Consumer: net/http stdlib Get / Post / Head ─────────────────────
51
+ const HTTP_CLIENT_METHOD_TO_HTTP = {
52
+ Get: 'GET',
53
+ Post: 'POST',
54
+ Head: 'GET', // HEAD has no body semantics we care about — treat as GET for contract matching
55
+ };
56
+ const HTTP_CLIENT_PATTERNS = compilePatterns({
57
+ name: 'go-http-client',
58
+ language: Go,
59
+ patterns: [
60
+ {
61
+ meta: {},
62
+ query: `
63
+ (call_expression
64
+ function: (selector_expression
65
+ operand: (identifier) @pkg (#eq? @pkg "http")
66
+ field: (field_identifier) @fn (#match? @fn "^(Get|Post|Head)$"))
67
+ arguments: (argument_list . (interpreted_string_literal) @path))
68
+ `,
69
+ },
70
+ ],
71
+ });
72
+ // ─── Consumer: net/http `http.NewRequest("METHOD", "/path", ...)` ────
73
+ const NEW_REQUEST_PATTERNS = compilePatterns({
74
+ name: 'go-new-request',
75
+ language: Go,
76
+ patterns: [
77
+ {
78
+ meta: {},
79
+ query: `
80
+ (call_expression
81
+ function: (selector_expression
82
+ operand: (identifier) @pkg (#eq? @pkg "http")
83
+ field: (field_identifier) @fn (#eq? @fn "NewRequest"))
84
+ arguments: (argument_list
85
+ .
86
+ (interpreted_string_literal) @http_method
87
+ (interpreted_string_literal) @path))
88
+ `,
89
+ },
90
+ ],
91
+ });
92
+ // ─── Consumer: resty `client.R().Delete("/path")` ─────────────────────
93
+ // Matches any chained call whose receiver is `something.R()` and whose
94
+ // method name is an HTTP verb. This is how go-resty's fluent API looks.
95
+ const RESTY_PATTERNS = compilePatterns({
96
+ name: 'go-resty',
97
+ language: Go,
98
+ patterns: [
99
+ {
100
+ meta: {},
101
+ query: `
102
+ (call_expression
103
+ function: (selector_expression
104
+ operand: (call_expression
105
+ function: (selector_expression
106
+ field: (field_identifier) @r (#eq? @r "R")))
107
+ field: (field_identifier) @http_method (#match? @http_method "^(Get|Post|Put|Delete|Patch)$"))
108
+ arguments: (argument_list . (interpreted_string_literal) @path))
109
+ `,
110
+ },
111
+ ],
112
+ });
113
+ export const GO_HTTP_PLUGIN = {
114
+ name: 'go-http',
115
+ language: Go,
116
+ scan(tree) {
117
+ const out = [];
118
+ // Framework providers: r.GET/POST/... with handler identifier
119
+ for (const match of runCompiledPatterns(FRAMEWORK_ROUTE_PATTERNS, tree)) {
120
+ const methodNode = match.captures.http_method;
121
+ const pathNode = match.captures.path;
122
+ const handlerNode = match.captures.handler;
123
+ if (!methodNode || !pathNode)
124
+ continue;
125
+ const path = unquoteLiteral(pathNode.text);
126
+ if (path === null)
127
+ continue;
128
+ out.push({
129
+ role: 'provider',
130
+ framework: 'go-framework',
131
+ method: methodNode.text.toUpperCase(),
132
+ path,
133
+ name: handlerNode?.text ?? null,
134
+ confidence: 0.8,
135
+ });
136
+ }
137
+ // net/http HandleFunc: default method GET
138
+ for (const match of runCompiledPatterns(HANDLE_FUNC_PATTERNS, tree)) {
139
+ const pathNode = match.captures.path;
140
+ const handlerNode = match.captures.handler;
141
+ if (!pathNode)
142
+ continue;
143
+ const path = unquoteLiteral(pathNode.text);
144
+ if (path === null)
145
+ continue;
146
+ out.push({
147
+ role: 'provider',
148
+ framework: 'go-stdlib',
149
+ method: 'GET',
150
+ path,
151
+ name: handlerNode?.text ?? null,
152
+ confidence: 0.8,
153
+ });
154
+ }
155
+ // net/http client: http.Get/Post/Head
156
+ for (const match of runCompiledPatterns(HTTP_CLIENT_PATTERNS, tree)) {
157
+ const fnNode = match.captures.fn;
158
+ const pathNode = match.captures.path;
159
+ if (!fnNode || !pathNode)
160
+ continue;
161
+ const httpMethod = HTTP_CLIENT_METHOD_TO_HTTP[fnNode.text];
162
+ if (!httpMethod)
163
+ continue;
164
+ const path = unquoteLiteral(pathNode.text);
165
+ if (path === null)
166
+ continue;
167
+ out.push({
168
+ role: 'consumer',
169
+ framework: 'go-stdlib',
170
+ method: httpMethod,
171
+ path,
172
+ name: null,
173
+ confidence: 0.7,
174
+ });
175
+ }
176
+ // net/http NewRequest
177
+ for (const match of runCompiledPatterns(NEW_REQUEST_PATTERNS, tree)) {
178
+ const methodNode = match.captures.http_method;
179
+ const pathNode = match.captures.path;
180
+ if (!methodNode || !pathNode)
181
+ continue;
182
+ const method = unquoteLiteral(methodNode.text);
183
+ const path = unquoteLiteral(pathNode.text);
184
+ if (method === null || path === null)
185
+ continue;
186
+ out.push({
187
+ role: 'consumer',
188
+ framework: 'go-stdlib',
189
+ method: method.toUpperCase(),
190
+ path,
191
+ name: null,
192
+ confidence: 0.7,
193
+ });
194
+ }
195
+ // resty
196
+ for (const match of runCompiledPatterns(RESTY_PATTERNS, tree)) {
197
+ const methodNode = match.captures.http_method;
198
+ const pathNode = match.captures.path;
199
+ if (!methodNode || !pathNode)
200
+ continue;
201
+ const path = unquoteLiteral(pathNode.text);
202
+ if (path === null)
203
+ continue;
204
+ out.push({
205
+ role: 'consumer',
206
+ framework: 'go-resty',
207
+ method: methodNode.text.toUpperCase(),
208
+ path,
209
+ name: null,
210
+ confidence: 0.7,
211
+ });
212
+ }
213
+ return out;
214
+ },
215
+ };
@@ -0,0 +1,17 @@
1
+ import type { HttpLanguagePlugin } from './types.js';
2
+ export type { HttpDetection, HttpLanguagePlugin, HttpRole } from './types.js';
3
+ /**
4
+ * Glob for files worth scanning for HTTP routes. Kept alongside the
5
+ * registry so adding a new language widens the glob in one edit.
6
+ *
7
+ * `.vue` / `.svelte` files are intentionally omitted for the source-scan
8
+ * path — they need their own grammar-aware extraction and the existing
9
+ * regex fallback for them was never very accurate. The graph-assisted
10
+ * Strategy A still handles them via the ingestion pipeline.
11
+ */
12
+ export declare const HTTP_SCAN_GLOB = "**/*.{ts,tsx,js,jsx,java,go,py,php}";
13
+ /**
14
+ * Return the HTTP plugin registered for the given file's extension,
15
+ * or `undefined` if the extension is not registered.
16
+ */
17
+ export declare function getPluginForFile(rel: string): HttpLanguagePlugin | undefined;
@@ -0,0 +1,44 @@
1
+ import * as path from 'node:path';
2
+ import { JAVA_HTTP_PLUGIN } from './java.js';
3
+ import { GO_HTTP_PLUGIN } from './go.js';
4
+ import { PYTHON_HTTP_PLUGIN } from './python.js';
5
+ import { PHP_HTTP_PLUGIN } from './php.js';
6
+ import { JAVASCRIPT_HTTP_PLUGIN, TYPESCRIPT_HTTP_PLUGIN, TSX_HTTP_PLUGIN } from './node.js';
7
+ /**
8
+ * File-extension → HTTP language plugin registry. The top-level
9
+ * orchestrator (`http-route-extractor.ts`) looks up the plugin for each
10
+ * file it visits and delegates the tree-sitter scanning to the plugin.
11
+ *
12
+ * Keys are lowercase extensions including the leading dot. To add a
13
+ * new language, drop a `http-patterns/<lang>.ts` that exports a
14
+ * `HttpLanguagePlugin`, import it here and register the extension(s).
15
+ * No edits to `http-route-extractor.ts` are required.
16
+ */
17
+ const REGISTRY = {
18
+ '.java': JAVA_HTTP_PLUGIN,
19
+ '.go': GO_HTTP_PLUGIN,
20
+ '.py': PYTHON_HTTP_PLUGIN,
21
+ '.php': PHP_HTTP_PLUGIN,
22
+ '.js': JAVASCRIPT_HTTP_PLUGIN,
23
+ '.jsx': JAVASCRIPT_HTTP_PLUGIN,
24
+ '.ts': TYPESCRIPT_HTTP_PLUGIN,
25
+ '.tsx': TSX_HTTP_PLUGIN,
26
+ };
27
+ /**
28
+ * Glob for files worth scanning for HTTP routes. Kept alongside the
29
+ * registry so adding a new language widens the glob in one edit.
30
+ *
31
+ * `.vue` / `.svelte` files are intentionally omitted for the source-scan
32
+ * path — they need their own grammar-aware extraction and the existing
33
+ * regex fallback for them was never very accurate. The graph-assisted
34
+ * Strategy A still handles them via the ingestion pipeline.
35
+ */
36
+ export const HTTP_SCAN_GLOB = '**/*.{ts,tsx,js,jsx,java,go,py,php}';
37
+ /**
38
+ * Return the HTTP plugin registered for the given file's extension,
39
+ * or `undefined` if the extension is not registered.
40
+ */
41
+ export function getPluginForFile(rel) {
42
+ const ext = path.extname(rel).toLowerCase();
43
+ return REGISTRY[ext];
44
+ }
@@ -0,0 +1,2 @@
1
+ import type { HttpLanguagePlugin } from './types.js';
2
+ export declare const JAVA_HTTP_PLUGIN: HttpLanguagePlugin;
@@ -0,0 +1,253 @@
1
+ import Java from 'tree-sitter-java';
2
+ import { compilePatterns, runCompiledPatterns, unquoteLiteral, } from '../tree-sitter-scanner.js';
3
+ /**
4
+ * Java HTTP plugin. Handles:
5
+ * - Spring `@RequestMapping` class prefixes + `@(Get|Post|...)Mapping` method annotations
6
+ * - Spring `RestTemplate.getForObject/...`, `WebClient.method(HttpMethod.X, ...)`
7
+ * - OkHttp `new Request.Builder().url("...")`
8
+ *
9
+ * The plugin runs two pattern bundles: one to collect class-level
10
+ * `@RequestMapping` prefixes keyed by the enclosing class node, and a
11
+ * second to match method-level annotations. The `scan` function walks
12
+ * up from each matched annotation to find its enclosing class and
13
+ * combines the prefix with the method path.
14
+ */
15
+ const METHOD_ANNOTATION_TO_HTTP = {
16
+ GetMapping: 'GET',
17
+ PostMapping: 'POST',
18
+ PutMapping: 'PUT',
19
+ DeleteMapping: 'DELETE',
20
+ PatchMapping: 'PATCH',
21
+ };
22
+ // ─── Provider: Spring class-level @RequestMapping prefix ──────────────
23
+ const SPRING_CLASS_PREFIX_PATTERNS = compilePatterns({
24
+ name: 'java-spring-class-prefix',
25
+ language: Java,
26
+ patterns: [
27
+ {
28
+ meta: {},
29
+ query: `
30
+ (class_declaration
31
+ (modifiers
32
+ (annotation
33
+ name: (identifier) @ann (#eq? @ann "RequestMapping")
34
+ arguments: (annotation_argument_list (string_literal) @prefix)))) @class
35
+ `,
36
+ },
37
+ ],
38
+ });
39
+ // ─── Provider: Spring @(Get|Post|...)Mapping method annotations ───────
40
+ const SPRING_METHOD_ROUTE_PATTERNS = compilePatterns({
41
+ name: 'java-spring-method-route',
42
+ language: Java,
43
+ patterns: [
44
+ {
45
+ meta: {},
46
+ query: `
47
+ (method_declaration
48
+ (modifiers
49
+ (annotation
50
+ name: (identifier) @ann (#match? @ann "^(Get|Post|Put|Delete|Patch)Mapping$")
51
+ arguments: (annotation_argument_list (string_literal) @path)))
52
+ name: (identifier) @method_name) @method
53
+ `,
54
+ },
55
+ ],
56
+ });
57
+ // ─── Consumer: Spring RestTemplate (object-named + method-named) ──────
58
+ // RestTemplate.getForObject / getForEntity → GET
59
+ // RestTemplate.postForObject / postForEntity → POST
60
+ // RestTemplate.put → PUT
61
+ // RestTemplate.delete → DELETE
62
+ // RestTemplate.patchForObject → PATCH
63
+ const REST_TEMPLATE_TO_HTTP = {
64
+ getForObject: 'GET',
65
+ getForEntity: 'GET',
66
+ postForObject: 'POST',
67
+ postForEntity: 'POST',
68
+ put: 'PUT',
69
+ delete: 'DELETE',
70
+ patchForObject: 'PATCH',
71
+ };
72
+ const REST_TEMPLATE_PATTERNS = compilePatterns({
73
+ name: 'java-rest-template',
74
+ language: Java,
75
+ patterns: [
76
+ {
77
+ meta: { framework: 'spring-rest-template' },
78
+ query: `
79
+ (method_invocation
80
+ object: (identifier) @obj (#eq? @obj "restTemplate")
81
+ name: (identifier) @method
82
+ arguments: (argument_list . (string_literal) @path))
83
+ `,
84
+ },
85
+ ],
86
+ });
87
+ // ─── Consumer: Spring WebClient — webClient.method(HttpMethod.X, "path") ─
88
+ const WEB_CLIENT_PATTERNS = compilePatterns({
89
+ name: 'java-web-client',
90
+ language: Java,
91
+ patterns: [
92
+ {
93
+ meta: {},
94
+ query: `
95
+ (method_invocation
96
+ object: (identifier) @obj (#eq? @obj "webClient")
97
+ name: (identifier) @method (#eq? @method "method")
98
+ arguments: (argument_list
99
+ (field_access
100
+ object: (identifier) @httpMethodCls (#eq? @httpMethodCls "HttpMethod")
101
+ field: (identifier) @http_method)
102
+ (string_literal) @path))
103
+ `,
104
+ },
105
+ ],
106
+ });
107
+ // ─── Consumer: OkHttp `new Request.Builder().url("path")` ─────────────
108
+ // Note: `Request.Builder` is a `scoped_type_identifier` whose text includes
109
+ // the dot, so `#eq?` against the literal string matches cleanly (no need
110
+ // to escape a regex dot).
111
+ const OK_HTTP_PATTERNS = compilePatterns({
112
+ name: 'java-okhttp',
113
+ language: Java,
114
+ patterns: [
115
+ {
116
+ meta: {},
117
+ query: `
118
+ (method_invocation
119
+ object: (object_creation_expression
120
+ type: (scoped_type_identifier) @type (#eq? @type "Request.Builder"))
121
+ name: (identifier) @method (#eq? @method "url")
122
+ arguments: (argument_list . (string_literal) @path))
123
+ `,
124
+ },
125
+ ],
126
+ });
127
+ /**
128
+ * Find the nearest enclosing class_declaration ancestor for a node, or
129
+ * null if the node is top-level. Tree-sitter's SyntaxNode.parent walks
130
+ * one level at a time.
131
+ */
132
+ function findEnclosingClass(node) {
133
+ let cur = node.parent;
134
+ while (cur) {
135
+ if (cur.type === 'class_declaration')
136
+ return cur;
137
+ cur = cur.parent;
138
+ }
139
+ return null;
140
+ }
141
+ /**
142
+ * Join a class-level prefix and a method-level path into a single URL
143
+ * path. Mirrors the semantics of the original regex implementation:
144
+ * strip trailing slashes on the prefix, then ensure a single slash
145
+ * between prefix and method path.
146
+ */
147
+ function joinPath(prefix, methodPath) {
148
+ const cleanPrefix = prefix.replace(/^\/+/, '').replace(/\/+$/, '');
149
+ const cleanSub = methodPath.replace(/^\/+/, '');
150
+ if (!cleanPrefix)
151
+ return `/${cleanSub}`;
152
+ return `/${cleanPrefix}/${cleanSub}`;
153
+ }
154
+ export const JAVA_HTTP_PLUGIN = {
155
+ name: 'java-http',
156
+ language: Java,
157
+ scan(tree) {
158
+ const out = [];
159
+ // ─── Providers: Spring class prefix + method annotations ────────
160
+ const prefixByClassId = new Map();
161
+ for (const match of runCompiledPatterns(SPRING_CLASS_PREFIX_PATTERNS, tree)) {
162
+ const prefixNode = match.captures.prefix;
163
+ const classNode = match.captures.class;
164
+ if (!prefixNode || !classNode)
165
+ continue;
166
+ const prefix = unquoteLiteral(prefixNode.text);
167
+ if (prefix !== null)
168
+ prefixByClassId.set(classNode.id, prefix);
169
+ }
170
+ for (const match of runCompiledPatterns(SPRING_METHOD_ROUTE_PATTERNS, tree)) {
171
+ const annNode = match.captures.ann;
172
+ const pathNode = match.captures.path;
173
+ const nameNode = match.captures.method_name;
174
+ const methodNode = match.captures.method;
175
+ if (!annNode || !pathNode || !methodNode)
176
+ continue;
177
+ const httpMethod = METHOD_ANNOTATION_TO_HTTP[annNode.text];
178
+ if (!httpMethod)
179
+ continue;
180
+ const rawPath = unquoteLiteral(pathNode.text);
181
+ if (rawPath === null)
182
+ continue;
183
+ const enclosingClass = findEnclosingClass(methodNode);
184
+ const prefix = enclosingClass ? (prefixByClassId.get(enclosingClass.id) ?? '') : '';
185
+ const fullPath = joinPath(prefix, rawPath);
186
+ out.push({
187
+ role: 'provider',
188
+ framework: 'spring',
189
+ method: httpMethod,
190
+ path: fullPath,
191
+ name: nameNode?.text ?? null,
192
+ confidence: 0.8,
193
+ });
194
+ }
195
+ // ─── Consumers: RestTemplate ────────────────────────────────────
196
+ for (const match of runCompiledPatterns(REST_TEMPLATE_PATTERNS, tree)) {
197
+ const methodNode = match.captures.method;
198
+ const pathNode = match.captures.path;
199
+ if (!methodNode || !pathNode)
200
+ continue;
201
+ const httpMethod = REST_TEMPLATE_TO_HTTP[methodNode.text];
202
+ if (!httpMethod)
203
+ continue;
204
+ const path = unquoteLiteral(pathNode.text);
205
+ if (path === null)
206
+ continue;
207
+ out.push({
208
+ role: 'consumer',
209
+ framework: 'spring-rest-template',
210
+ method: httpMethod,
211
+ path,
212
+ name: null,
213
+ confidence: 0.7,
214
+ });
215
+ }
216
+ // ─── Consumers: WebClient.method(HttpMethod.X, "path") ──────────
217
+ for (const match of runCompiledPatterns(WEB_CLIENT_PATTERNS, tree)) {
218
+ const httpMethodNode = match.captures.http_method;
219
+ const pathNode = match.captures.path;
220
+ if (!httpMethodNode || !pathNode)
221
+ continue;
222
+ const path = unquoteLiteral(pathNode.text);
223
+ if (path === null)
224
+ continue;
225
+ out.push({
226
+ role: 'consumer',
227
+ framework: 'spring-web-client',
228
+ method: httpMethodNode.text.toUpperCase(),
229
+ path,
230
+ name: null,
231
+ confidence: 0.7,
232
+ });
233
+ }
234
+ // ─── Consumers: OkHttp Request.Builder().url("path") ────────────
235
+ for (const match of runCompiledPatterns(OK_HTTP_PATTERNS, tree)) {
236
+ const pathNode = match.captures.path;
237
+ if (!pathNode)
238
+ continue;
239
+ const path = unquoteLiteral(pathNode.text);
240
+ if (path === null)
241
+ continue;
242
+ out.push({
243
+ role: 'consumer',
244
+ framework: 'okhttp',
245
+ method: 'GET',
246
+ path,
247
+ name: null,
248
+ confidence: 0.7,
249
+ });
250
+ }
251
+ return out;
252
+ },
253
+ };
@@ -0,0 +1,4 @@
1
+ import type { HttpLanguagePlugin } from './types.js';
2
+ export declare const JAVASCRIPT_HTTP_PLUGIN: HttpLanguagePlugin;
3
+ export declare const TYPESCRIPT_HTTP_PLUGIN: HttpLanguagePlugin;
4
+ export declare const TSX_HTTP_PLUGIN: HttpLanguagePlugin;