gitnexus 1.6.6-rc.81 → 1.6.6-rc.83
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/dist/core/group/extractors/http-patterns/java.js +246 -12
- package/dist/core/group/extractors/http-patterns/python.js +345 -6
- package/dist/core/group/extractors/http-patterns/types.d.ts +33 -1
- package/dist/core/group/extractors/http-route-extractor.js +39 -10
- package/dist/core/ingestion/parsing-processor.d.ts +4 -0
- package/dist/core/ingestion/parsing-processor.js +15 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +152 -0
- package/dist/core/ingestion/pipeline-phases/routes.js +2 -2
- package/dist/core/ingestion/route-extractors/fastapi-router-bindings.d.ts +134 -0
- package/dist/core/ingestion/route-extractors/fastapi-router-bindings.js +208 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +26 -0
- package/dist/core/ingestion/workers/parse-worker.js +36 -0
- package/package.json +1 -1
|
@@ -3,8 +3,11 @@ import { compilePatterns, runCompiledPatterns, unquoteLiteral, } from '../tree-s
|
|
|
3
3
|
/**
|
|
4
4
|
* Java HTTP plugin. Handles:
|
|
5
5
|
* - Spring `@RequestMapping` class prefixes + `@(Get|Post|...)Mapping` method annotations
|
|
6
|
-
* - Spring `RestTemplate.getForObject/...`, `
|
|
6
|
+
* - Spring `RestTemplate.getForObject/...`, `exchange(...)`
|
|
7
|
+
* - Spring `WebClient.method(HttpMethod.X, ...)`, `WebClient.get().uri(...)`
|
|
7
8
|
* - OkHttp `new Request.Builder().url("...")`
|
|
9
|
+
* - OpenFeign interfaces with Spring MVC method annotations
|
|
10
|
+
* - Java / Apache HttpClient literal request construction
|
|
8
11
|
*
|
|
9
12
|
* The plugin runs two pattern bundles: one to collect class-level
|
|
10
13
|
* `@RequestMapping` prefixes keyed by the enclosing class node, and a
|
|
@@ -62,6 +65,52 @@ const SPRING_CLASS_PREFIX_PATTERNS = compilePatterns({
|
|
|
62
65
|
},
|
|
63
66
|
],
|
|
64
67
|
});
|
|
68
|
+
// ─── Consumer: OpenFeign interface-level prefixes ───────────────────
|
|
69
|
+
// Feign's `name`/`value` attributes identify a service, not an HTTP path,
|
|
70
|
+
// so only `path` is used as a URL prefix. `@RequestMapping` on a Feign
|
|
71
|
+
// interface is also common and does carry a path prefix.
|
|
72
|
+
const FEIGN_INTERFACE_PREFIX_PATTERNS = compilePatterns({
|
|
73
|
+
name: 'java-feign-interface-prefix',
|
|
74
|
+
language: Java,
|
|
75
|
+
patterns: [
|
|
76
|
+
{
|
|
77
|
+
meta: {},
|
|
78
|
+
query: `
|
|
79
|
+
(interface_declaration
|
|
80
|
+
(modifiers
|
|
81
|
+
(annotation
|
|
82
|
+
name: (identifier) @ann (#eq? @ann "FeignClient")
|
|
83
|
+
arguments: (annotation_argument_list
|
|
84
|
+
(element_value_pair
|
|
85
|
+
key: (identifier) @key (#eq? @key "path")
|
|
86
|
+
value: (string_literal) @prefix))))) @interface
|
|
87
|
+
`,
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
meta: {},
|
|
91
|
+
query: `
|
|
92
|
+
(interface_declaration
|
|
93
|
+
(modifiers
|
|
94
|
+
(annotation
|
|
95
|
+
name: (identifier) @ann (#eq? @ann "RequestMapping")
|
|
96
|
+
arguments: (annotation_argument_list (string_literal) @prefix)))) @interface
|
|
97
|
+
`,
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
meta: {},
|
|
101
|
+
query: `
|
|
102
|
+
(interface_declaration
|
|
103
|
+
(modifiers
|
|
104
|
+
(annotation
|
|
105
|
+
name: (identifier) @ann (#eq? @ann "RequestMapping")
|
|
106
|
+
arguments: (annotation_argument_list
|
|
107
|
+
(element_value_pair
|
|
108
|
+
key: (identifier) @key (#match? @key "^(path|value)$")
|
|
109
|
+
value: (string_literal) @prefix))))) @interface
|
|
110
|
+
`,
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
});
|
|
65
114
|
// ─── Provider: Spring @(Get|Post|...)Mapping method annotations ───────
|
|
66
115
|
// Same dual-pattern approach: positional vs named argument. The named
|
|
67
116
|
// pattern restricts the annotation member name to `path`/`value` to
|
|
@@ -104,6 +153,8 @@ const SPRING_METHOD_ROUTE_PATTERNS = compilePatterns({
|
|
|
104
153
|
// RestTemplate.put → PUT
|
|
105
154
|
// RestTemplate.delete → DELETE
|
|
106
155
|
// RestTemplate.patchForObject → PATCH
|
|
156
|
+
// Source-scan only: receiver must be named exactly `restTemplate`.
|
|
157
|
+
// Fields, `this.restTemplate`, aliases, and other injection names are deferred.
|
|
107
158
|
const REST_TEMPLATE_TO_HTTP = {
|
|
108
159
|
getForObject: 'GET',
|
|
109
160
|
getForEntity: 'GET',
|
|
@@ -128,22 +179,46 @@ const REST_TEMPLATE_PATTERNS = compilePatterns({
|
|
|
128
179
|
},
|
|
129
180
|
],
|
|
130
181
|
});
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
name: 'java-web-client',
|
|
182
|
+
const REST_TEMPLATE_EXCHANGE_PATTERNS = compilePatterns({
|
|
183
|
+
name: 'java-rest-template-exchange',
|
|
134
184
|
language: Java,
|
|
135
185
|
patterns: [
|
|
136
186
|
{
|
|
137
|
-
meta: {},
|
|
187
|
+
meta: { framework: 'spring-rest-template' },
|
|
138
188
|
query: `
|
|
139
189
|
(method_invocation
|
|
140
|
-
object: (identifier) @obj (#eq? @obj "
|
|
141
|
-
name: (identifier) @method (#eq? @method "
|
|
190
|
+
object: (identifier) @obj (#eq? @obj "restTemplate")
|
|
191
|
+
name: (identifier) @method (#eq? @method "exchange")
|
|
142
192
|
arguments: (argument_list
|
|
193
|
+
. (string_literal) @path
|
|
143
194
|
(field_access
|
|
144
195
|
object: (identifier) @httpMethodCls (#eq? @httpMethodCls "HttpMethod")
|
|
145
|
-
field: (identifier) @http_method)
|
|
146
|
-
|
|
196
|
+
field: (identifier) @http_method)))
|
|
197
|
+
`,
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
});
|
|
201
|
+
const WEB_CLIENT_SHORT_TO_HTTP = {
|
|
202
|
+
get: 'GET',
|
|
203
|
+
post: 'POST',
|
|
204
|
+
put: 'PUT',
|
|
205
|
+
delete: 'DELETE',
|
|
206
|
+
patch: 'PATCH',
|
|
207
|
+
};
|
|
208
|
+
const WEB_CLIENT_SHORT_FORM_PATTERNS = compilePatterns({
|
|
209
|
+
name: 'java-web-client-short-form',
|
|
210
|
+
language: Java,
|
|
211
|
+
patterns: [
|
|
212
|
+
{
|
|
213
|
+
meta: {},
|
|
214
|
+
query: `
|
|
215
|
+
(method_invocation
|
|
216
|
+
object: (method_invocation
|
|
217
|
+
object: (identifier) @obj (#eq? @obj "webClient")
|
|
218
|
+
name: (identifier) @verb (#match? @verb "^(get|post|put|delete|patch)$")
|
|
219
|
+
arguments: (argument_list))
|
|
220
|
+
name: (identifier) @uri_method (#eq? @uri_method "uri")
|
|
221
|
+
arguments: (argument_list . (string_literal) @path))
|
|
147
222
|
`,
|
|
148
223
|
},
|
|
149
224
|
],
|
|
@@ -168,6 +243,51 @@ const OK_HTTP_PATTERNS = compilePatterns({
|
|
|
168
243
|
},
|
|
169
244
|
],
|
|
170
245
|
});
|
|
246
|
+
const JAVA_HTTP_CLIENT_PATTERNS = compilePatterns({
|
|
247
|
+
name: 'java-http-client',
|
|
248
|
+
language: Java,
|
|
249
|
+
patterns: [
|
|
250
|
+
{
|
|
251
|
+
meta: {},
|
|
252
|
+
query: `
|
|
253
|
+
(method_invocation
|
|
254
|
+
object: (method_invocation
|
|
255
|
+
object: (method_invocation
|
|
256
|
+
object: (identifier) @builderCls (#eq? @builderCls "HttpRequest")
|
|
257
|
+
name: (identifier) @newBuilder (#eq? @newBuilder "newBuilder")
|
|
258
|
+
arguments: (argument_list))
|
|
259
|
+
name: (identifier) @uri_method (#eq? @uri_method "uri")
|
|
260
|
+
arguments: (argument_list
|
|
261
|
+
(method_invocation
|
|
262
|
+
object: (identifier) @uriCls (#eq? @uriCls "URI")
|
|
263
|
+
name: (identifier) @create (#eq? @create "create")
|
|
264
|
+
arguments: (argument_list . (string_literal) @path))))
|
|
265
|
+
name: (identifier) @http_method (#match? @http_method "^(GET|POST|PUT|DELETE)$"))
|
|
266
|
+
`,
|
|
267
|
+
},
|
|
268
|
+
],
|
|
269
|
+
});
|
|
270
|
+
const APACHE_HTTP_CLIENT_TO_HTTP = {
|
|
271
|
+
HttpGet: 'GET',
|
|
272
|
+
HttpPost: 'POST',
|
|
273
|
+
HttpPut: 'PUT',
|
|
274
|
+
HttpDelete: 'DELETE',
|
|
275
|
+
HttpPatch: 'PATCH',
|
|
276
|
+
};
|
|
277
|
+
const APACHE_HTTP_CLIENT_PATTERNS = compilePatterns({
|
|
278
|
+
name: 'java-apache-http-client',
|
|
279
|
+
language: Java,
|
|
280
|
+
patterns: [
|
|
281
|
+
{
|
|
282
|
+
meta: {},
|
|
283
|
+
query: `
|
|
284
|
+
(object_creation_expression
|
|
285
|
+
type: (type_identifier) @type (#match? @type "^Http(Get|Post|Put|Delete|Patch)$")
|
|
286
|
+
arguments: (argument_list . (string_literal) @path))
|
|
287
|
+
`,
|
|
288
|
+
},
|
|
289
|
+
],
|
|
290
|
+
});
|
|
171
291
|
/**
|
|
172
292
|
* Find the nearest enclosing class_declaration ancestor for a node, or
|
|
173
293
|
* null if the node is top-level. Tree-sitter's SyntaxNode.parent walks
|
|
@@ -182,6 +302,32 @@ function findEnclosingClass(node) {
|
|
|
182
302
|
}
|
|
183
303
|
return null;
|
|
184
304
|
}
|
|
305
|
+
function findEnclosingInterface(node) {
|
|
306
|
+
let cur = node.parent;
|
|
307
|
+
while (cur) {
|
|
308
|
+
if (cur.type === 'interface_declaration')
|
|
309
|
+
return cur;
|
|
310
|
+
cur = cur.parent;
|
|
311
|
+
}
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
function hasAnnotation(node, annotationName) {
|
|
315
|
+
for (const child of node.namedChildren) {
|
|
316
|
+
if (child.type !== 'modifiers')
|
|
317
|
+
continue;
|
|
318
|
+
for (const modifier of child.namedChildren) {
|
|
319
|
+
if (modifier.type !== 'annotation')
|
|
320
|
+
continue;
|
|
321
|
+
const nameNode = modifier.childForFieldName('name');
|
|
322
|
+
if (!nameNode)
|
|
323
|
+
continue;
|
|
324
|
+
const simpleName = nameNode.text.split('.').pop();
|
|
325
|
+
if (nameNode.text === annotationName || simpleName === annotationName)
|
|
326
|
+
return true;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
185
331
|
/**
|
|
186
332
|
* Join a class-level prefix and a method-level path into a single URL
|
|
187
333
|
* path. Mirrors the semantics of the original regex implementation:
|
|
@@ -211,6 +357,16 @@ export const JAVA_HTTP_PLUGIN = {
|
|
|
211
357
|
if (prefix !== null)
|
|
212
358
|
prefixByClassId.set(classNode.id, prefix);
|
|
213
359
|
}
|
|
360
|
+
const feignPrefixByInterfaceId = new Map();
|
|
361
|
+
for (const match of runCompiledPatterns(FEIGN_INTERFACE_PREFIX_PATTERNS, tree)) {
|
|
362
|
+
const prefixNode = match.captures.prefix;
|
|
363
|
+
const interfaceNode = match.captures.interface;
|
|
364
|
+
if (!prefixNode || !interfaceNode)
|
|
365
|
+
continue;
|
|
366
|
+
const prefix = unquoteLiteral(prefixNode.text);
|
|
367
|
+
if (prefix !== null && !feignPrefixByInterfaceId.has(interfaceNode.id))
|
|
368
|
+
feignPrefixByInterfaceId.set(interfaceNode.id, prefix);
|
|
369
|
+
}
|
|
214
370
|
for (const match of runCompiledPatterns(SPRING_METHOD_ROUTE_PATTERNS, tree)) {
|
|
215
371
|
const annNode = match.captures.ann;
|
|
216
372
|
const pathNode = match.captures.path;
|
|
@@ -224,6 +380,20 @@ export const JAVA_HTTP_PLUGIN = {
|
|
|
224
380
|
const rawPath = unquoteLiteral(pathNode.text);
|
|
225
381
|
if (rawPath === null)
|
|
226
382
|
continue;
|
|
383
|
+
const enclosingInterface = findEnclosingInterface(methodNode);
|
|
384
|
+
if (enclosingInterface && hasAnnotation(enclosingInterface, 'FeignClient')) {
|
|
385
|
+
const prefix = feignPrefixByInterfaceId.get(enclosingInterface.id) ?? '';
|
|
386
|
+
const fullPath = joinPath(prefix, rawPath);
|
|
387
|
+
out.push({
|
|
388
|
+
role: 'consumer',
|
|
389
|
+
framework: 'openfeign',
|
|
390
|
+
method: httpMethod,
|
|
391
|
+
path: fullPath,
|
|
392
|
+
name: nameNode?.text ?? null,
|
|
393
|
+
confidence: 0.7,
|
|
394
|
+
});
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
227
397
|
const enclosingClass = findEnclosingClass(methodNode);
|
|
228
398
|
const prefix = enclosingClass ? (prefixByClassId.get(enclosingClass.id) ?? '') : '';
|
|
229
399
|
const fullPath = joinPath(prefix, rawPath);
|
|
@@ -257,8 +427,7 @@ export const JAVA_HTTP_PLUGIN = {
|
|
|
257
427
|
confidence: 0.7,
|
|
258
428
|
});
|
|
259
429
|
}
|
|
260
|
-
|
|
261
|
-
for (const match of runCompiledPatterns(WEB_CLIENT_PATTERNS, tree)) {
|
|
430
|
+
for (const match of runCompiledPatterns(REST_TEMPLATE_EXCHANGE_PATTERNS, tree)) {
|
|
262
431
|
const httpMethodNode = match.captures.http_method;
|
|
263
432
|
const pathNode = match.captures.path;
|
|
264
433
|
if (!httpMethodNode || !pathNode)
|
|
@@ -268,13 +437,37 @@ export const JAVA_HTTP_PLUGIN = {
|
|
|
268
437
|
continue;
|
|
269
438
|
out.push({
|
|
270
439
|
role: 'consumer',
|
|
271
|
-
framework: 'spring-
|
|
440
|
+
framework: 'spring-rest-template',
|
|
272
441
|
method: httpMethodNode.text.toUpperCase(),
|
|
273
442
|
path,
|
|
274
443
|
name: null,
|
|
275
444
|
confidence: 0.7,
|
|
276
445
|
});
|
|
277
446
|
}
|
|
447
|
+
// ─── Consumers: WebClient.get().uri("path") short form ─────────
|
|
448
|
+
// Source-scan only: receiver must be named exactly `webClient`.
|
|
449
|
+
// The real long-form chain `webClient.method(HttpMethod.X).uri("/x")`
|
|
450
|
+
// needs multi-hop chain analysis and is intentionally deferred.
|
|
451
|
+
for (const match of runCompiledPatterns(WEB_CLIENT_SHORT_FORM_PATTERNS, tree)) {
|
|
452
|
+
const verbNode = match.captures.verb;
|
|
453
|
+
const pathNode = match.captures.path;
|
|
454
|
+
if (!verbNode || !pathNode)
|
|
455
|
+
continue;
|
|
456
|
+
const httpMethod = WEB_CLIENT_SHORT_TO_HTTP[verbNode.text];
|
|
457
|
+
if (!httpMethod)
|
|
458
|
+
continue;
|
|
459
|
+
const path = unquoteLiteral(pathNode.text);
|
|
460
|
+
if (path === null)
|
|
461
|
+
continue;
|
|
462
|
+
out.push({
|
|
463
|
+
role: 'consumer',
|
|
464
|
+
framework: 'spring-web-client',
|
|
465
|
+
method: httpMethod,
|
|
466
|
+
path,
|
|
467
|
+
name: null,
|
|
468
|
+
confidence: 0.7,
|
|
469
|
+
});
|
|
470
|
+
}
|
|
278
471
|
// ─── Consumers: OkHttp Request.Builder().url("path") ────────────
|
|
279
472
|
for (const match of runCompiledPatterns(OK_HTTP_PATTERNS, tree)) {
|
|
280
473
|
const pathNode = match.captures.path;
|
|
@@ -292,6 +485,47 @@ export const JAVA_HTTP_PLUGIN = {
|
|
|
292
485
|
confidence: 0.7,
|
|
293
486
|
});
|
|
294
487
|
}
|
|
488
|
+
// ─── Consumers: Java HttpClient request builder ─────────────────
|
|
489
|
+
// Java's builder exposes GET/POST/PUT/DELETE helpers. PATCH uses
|
|
490
|
+
// `.method("PATCH", body)`, which is intentionally deferred.
|
|
491
|
+
for (const match of runCompiledPatterns(JAVA_HTTP_CLIENT_PATTERNS, tree)) {
|
|
492
|
+
const httpMethodNode = match.captures.http_method;
|
|
493
|
+
const pathNode = match.captures.path;
|
|
494
|
+
if (!httpMethodNode || !pathNode)
|
|
495
|
+
continue;
|
|
496
|
+
const path = unquoteLiteral(pathNode.text);
|
|
497
|
+
if (path === null)
|
|
498
|
+
continue;
|
|
499
|
+
out.push({
|
|
500
|
+
role: 'consumer',
|
|
501
|
+
framework: 'java-http-client',
|
|
502
|
+
method: httpMethodNode.text.toUpperCase(),
|
|
503
|
+
path,
|
|
504
|
+
name: null,
|
|
505
|
+
confidence: 0.65,
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
// ─── Consumers: Apache HttpClient request constructors ──────────
|
|
509
|
+
for (const match of runCompiledPatterns(APACHE_HTTP_CLIENT_PATTERNS, tree)) {
|
|
510
|
+
const typeNode = match.captures.type;
|
|
511
|
+
const pathNode = match.captures.path;
|
|
512
|
+
if (!typeNode || !pathNode)
|
|
513
|
+
continue;
|
|
514
|
+
const httpMethod = APACHE_HTTP_CLIENT_TO_HTTP[typeNode.text];
|
|
515
|
+
if (!httpMethod)
|
|
516
|
+
continue;
|
|
517
|
+
const path = unquoteLiteral(pathNode.text);
|
|
518
|
+
if (path === null)
|
|
519
|
+
continue;
|
|
520
|
+
out.push({
|
|
521
|
+
role: 'consumer',
|
|
522
|
+
framework: 'apache-http-client',
|
|
523
|
+
method: httpMethod,
|
|
524
|
+
path,
|
|
525
|
+
name: null,
|
|
526
|
+
confidence: 0.65,
|
|
527
|
+
});
|
|
528
|
+
}
|
|
295
529
|
return out;
|
|
296
530
|
},
|
|
297
531
|
};
|