gitnexus 1.6.6-rc.73 → 1.6.6-rc.75
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/index.d.ts +6 -1
- package/dist/core/group/extractors/http-patterns/index.js +16 -1
- package/dist/core/group/extractors/http-patterns/java.js +44 -0
- package/dist/core/group/extractors/http-patterns/kotlin.d.ts +8 -0
- package/dist/core/group/extractors/http-patterns/kotlin.js +227 -0
- package/package.json +1 -1
|
@@ -4,12 +4,17 @@ export type { HttpDetection, HttpLanguagePlugin, HttpRole } from './types.js';
|
|
|
4
4
|
* Glob for files worth scanning for HTTP routes. Kept alongside the
|
|
5
5
|
* registry so adding a new language widens the glob in one edit.
|
|
6
6
|
*
|
|
7
|
+
* `.kt`/`.kts` are always present in the glob even when the optional
|
|
8
|
+
* `tree-sitter-kotlin` grammar isn't installed — `getPluginForFile`
|
|
9
|
+
* will return `undefined` for those files in that case, so the
|
|
10
|
+
* orchestrator simply skips them at scan time without erroring.
|
|
11
|
+
*
|
|
7
12
|
* `.vue` / `.svelte` files are intentionally omitted for the source-scan
|
|
8
13
|
* path — they need their own grammar-aware extraction and the existing
|
|
9
14
|
* regex fallback for them was never very accurate. The graph-assisted
|
|
10
15
|
* Strategy A still handles them via the ingestion pipeline.
|
|
11
16
|
*/
|
|
12
|
-
export declare const HTTP_SCAN_GLOB = "**/*.{ts,tsx,js,jsx,java,go,py,php}";
|
|
17
|
+
export declare const HTTP_SCAN_GLOB = "**/*.{ts,tsx,js,jsx,java,kt,kts,go,py,php}";
|
|
13
18
|
/**
|
|
14
19
|
* Return the HTTP plugin registered for the given file's extension,
|
|
15
20
|
* or `undefined` if the extension is not registered.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as path from 'node:path';
|
|
2
2
|
import { isBladeTemplateFilename } from '../../../../_shared/index.js';
|
|
3
3
|
import { JAVA_HTTP_PLUGIN } from './java.js';
|
|
4
|
+
import { KOTLIN_HTTP_PLUGIN } from './kotlin.js';
|
|
4
5
|
import { GO_HTTP_PLUGIN } from './go.js';
|
|
5
6
|
import { PYTHON_HTTP_PLUGIN } from './python.js';
|
|
6
7
|
import { PHP_HTTP_PLUGIN } from './php.js';
|
|
@@ -14,6 +15,11 @@ import { JAVASCRIPT_HTTP_PLUGIN, TYPESCRIPT_HTTP_PLUGIN, TSX_HTTP_PLUGIN } from
|
|
|
14
15
|
* new language, drop a `http-patterns/<lang>.ts` that exports a
|
|
15
16
|
* `HttpLanguagePlugin`, import it here and register the extension(s).
|
|
16
17
|
* No edits to `http-route-extractor.ts` are required.
|
|
18
|
+
*
|
|
19
|
+
* Optional grammar plugins (e.g. `kotlin.ts`, which depends on the
|
|
20
|
+
* optionalDependency `tree-sitter-kotlin`) export `null` when the
|
|
21
|
+
* native binding is unavailable; we skip registration in that case so
|
|
22
|
+
* a missing optional grammar never crashes the orchestrator.
|
|
17
23
|
*/
|
|
18
24
|
const REGISTRY = {
|
|
19
25
|
'.java': JAVA_HTTP_PLUGIN,
|
|
@@ -25,16 +31,25 @@ const REGISTRY = {
|
|
|
25
31
|
'.ts': TYPESCRIPT_HTTP_PLUGIN,
|
|
26
32
|
'.tsx': TSX_HTTP_PLUGIN,
|
|
27
33
|
};
|
|
34
|
+
if (KOTLIN_HTTP_PLUGIN) {
|
|
35
|
+
REGISTRY['.kt'] = KOTLIN_HTTP_PLUGIN;
|
|
36
|
+
REGISTRY['.kts'] = KOTLIN_HTTP_PLUGIN;
|
|
37
|
+
}
|
|
28
38
|
/**
|
|
29
39
|
* Glob for files worth scanning for HTTP routes. Kept alongside the
|
|
30
40
|
* registry so adding a new language widens the glob in one edit.
|
|
31
41
|
*
|
|
42
|
+
* `.kt`/`.kts` are always present in the glob even when the optional
|
|
43
|
+
* `tree-sitter-kotlin` grammar isn't installed — `getPluginForFile`
|
|
44
|
+
* will return `undefined` for those files in that case, so the
|
|
45
|
+
* orchestrator simply skips them at scan time without erroring.
|
|
46
|
+
*
|
|
32
47
|
* `.vue` / `.svelte` files are intentionally omitted for the source-scan
|
|
33
48
|
* path — they need their own grammar-aware extraction and the existing
|
|
34
49
|
* regex fallback for them was never very accurate. The graph-assisted
|
|
35
50
|
* Strategy A still handles them via the ingestion pipeline.
|
|
36
51
|
*/
|
|
37
|
-
export const HTTP_SCAN_GLOB = '**/*.{ts,tsx,js,jsx,java,go,py,php}';
|
|
52
|
+
export const HTTP_SCAN_GLOB = '**/*.{ts,tsx,js,jsx,java,kt,kts,go,py,php}';
|
|
38
53
|
/**
|
|
39
54
|
* Return the HTTP plugin registered for the given file's extension,
|
|
40
55
|
* or `undefined` if the extension is not registered.
|
|
@@ -20,6 +20,19 @@ const METHOD_ANNOTATION_TO_HTTP = {
|
|
|
20
20
|
PatchMapping: 'PATCH',
|
|
21
21
|
};
|
|
22
22
|
// ─── Provider: Spring class-level @RequestMapping prefix ──────────────
|
|
23
|
+
// Two patterns are needed because the AST shape differs depending on
|
|
24
|
+
// whether the annotation uses a positional argument or a named one:
|
|
25
|
+
// @RequestMapping("/api") → (annotation_argument_list (string_literal))
|
|
26
|
+
// @RequestMapping(path = "/api") → (annotation_argument_list (element_value_pair key:(identifier) value:(string_literal)))
|
|
27
|
+
// @RequestMapping(value = "/api") → same as above
|
|
28
|
+
//
|
|
29
|
+
// The named-argument pattern MUST constrain the `key` field to the route
|
|
30
|
+
// member names (`path`/`value`); without it, the query also captures
|
|
31
|
+
// non-route attributes such as `produces`, `consumes`, `headers`, `name`,
|
|
32
|
+
// `params` (their right-hand string literals would be mis-extracted as
|
|
33
|
+
// route prefixes — e.g. `produces = "application/json"` would corrupt
|
|
34
|
+
// every method route under that controller). The sibling
|
|
35
|
+
// `topic-patterns/java.ts` uses the same `key:` constraint approach.
|
|
23
36
|
const SPRING_CLASS_PREFIX_PATTERNS = compilePatterns({
|
|
24
37
|
name: 'java-spring-class-prefix',
|
|
25
38
|
language: Java,
|
|
@@ -34,9 +47,26 @@ const SPRING_CLASS_PREFIX_PATTERNS = compilePatterns({
|
|
|
34
47
|
arguments: (annotation_argument_list (string_literal) @prefix)))) @class
|
|
35
48
|
`,
|
|
36
49
|
},
|
|
50
|
+
{
|
|
51
|
+
meta: {},
|
|
52
|
+
query: `
|
|
53
|
+
(class_declaration
|
|
54
|
+
(modifiers
|
|
55
|
+
(annotation
|
|
56
|
+
name: (identifier) @ann (#eq? @ann "RequestMapping")
|
|
57
|
+
arguments: (annotation_argument_list
|
|
58
|
+
(element_value_pair
|
|
59
|
+
key: (identifier) @key (#match? @key "^(path|value)$")
|
|
60
|
+
value: (string_literal) @prefix))))) @class
|
|
61
|
+
`,
|
|
62
|
+
},
|
|
37
63
|
],
|
|
38
64
|
});
|
|
39
65
|
// ─── Provider: Spring @(Get|Post|...)Mapping method annotations ───────
|
|
66
|
+
// Same dual-pattern approach: positional vs named argument. The named
|
|
67
|
+
// pattern restricts the annotation member name to `path`/`value` to
|
|
68
|
+
// avoid capturing unrelated string-valued attributes
|
|
69
|
+
// (`produces`, `consumes`, `headers`, `name`, `params`, ...).
|
|
40
70
|
const SPRING_METHOD_ROUTE_PATTERNS = compilePatterns({
|
|
41
71
|
name: 'java-spring-method-route',
|
|
42
72
|
language: Java,
|
|
@@ -52,6 +82,20 @@ const SPRING_METHOD_ROUTE_PATTERNS = compilePatterns({
|
|
|
52
82
|
name: (identifier) @method_name) @method
|
|
53
83
|
`,
|
|
54
84
|
},
|
|
85
|
+
{
|
|
86
|
+
meta: {},
|
|
87
|
+
query: `
|
|
88
|
+
(method_declaration
|
|
89
|
+
(modifiers
|
|
90
|
+
(annotation
|
|
91
|
+
name: (identifier) @ann (#match? @ann "^(Get|Post|Put|Delete|Patch)Mapping$")
|
|
92
|
+
arguments: (annotation_argument_list
|
|
93
|
+
(element_value_pair
|
|
94
|
+
key: (identifier) @key (#match? @key "^(path|value)$")
|
|
95
|
+
value: (string_literal) @path))))
|
|
96
|
+
name: (identifier) @method_name) @method
|
|
97
|
+
`,
|
|
98
|
+
},
|
|
55
99
|
],
|
|
56
100
|
});
|
|
57
101
|
// ─── Consumer: Spring RestTemplate (object-named + method-named) ──────
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { HttpLanguagePlugin } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The exported plugin is `null` when tree-sitter-kotlin's native
|
|
4
|
+
* binding is unavailable. `http-patterns/index.ts` checks for null
|
|
5
|
+
* before registering `.kt`/`.kts` so missing optional grammars never
|
|
6
|
+
* crash the orchestrator.
|
|
7
|
+
*/
|
|
8
|
+
export declare const KOTLIN_HTTP_PLUGIN: HttpLanguagePlugin | null;
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import { compilePatterns, runCompiledPatterns, unquoteLiteral, } from '../tree-sitter-scanner.js';
|
|
3
|
+
/**
|
|
4
|
+
* Kotlin HTTP plugin (Spring providers).
|
|
5
|
+
*
|
|
6
|
+
* Mirrors the Java plugin for Spring `@RequestMapping` class prefixes
|
|
7
|
+
* and `@(Get|Post|...)Mapping` method annotations on Kotlin Spring
|
|
8
|
+
* Boot controllers. Both positional shorthand (`@GetMapping("/x")`)
|
|
9
|
+
* and named annotation arguments (`@GetMapping(value = "/x")` and
|
|
10
|
+
* `@GetMapping(path = "/x")`) are supported.
|
|
11
|
+
*
|
|
12
|
+
* Consumer detection (RestTemplate / WebClient / OkHttp) is intentionally
|
|
13
|
+
* out of scope for this plugin — Kotlin call-site ASTs are sufficiently
|
|
14
|
+
* different from Java's `method_invocation` shape that they warrant a
|
|
15
|
+
* separate, focused follow-up.
|
|
16
|
+
*
|
|
17
|
+
* tree-sitter-kotlin (fwcd) AST shapes used here:
|
|
18
|
+
* class_declaration
|
|
19
|
+
* modifiers
|
|
20
|
+
* annotation
|
|
21
|
+
* constructor_invocation
|
|
22
|
+
* user_type → type_identifier ← annotation name
|
|
23
|
+
* value_arguments
|
|
24
|
+
* value_argument
|
|
25
|
+
* (simple_identifier "=")? ← absent for positional, present for named
|
|
26
|
+
* string_literal
|
|
27
|
+
* type_identifier ← class name
|
|
28
|
+
*
|
|
29
|
+
* tree-sitter-kotlin is an optional npm dependency — when its native
|
|
30
|
+
* binding is unavailable the plugin gracefully exports `null` and
|
|
31
|
+
* `http-patterns/index.ts` skips registration for `.kt`/`.kts` files.
|
|
32
|
+
*/
|
|
33
|
+
const _require = createRequire(import.meta.url);
|
|
34
|
+
/** Loaded lazily; null when the grammar binding isn't installed. */
|
|
35
|
+
let Kotlin = null;
|
|
36
|
+
try {
|
|
37
|
+
Kotlin = _require('tree-sitter-kotlin');
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
Kotlin = null;
|
|
41
|
+
}
|
|
42
|
+
const METHOD_ANNOTATION_TO_HTTP = {
|
|
43
|
+
GetMapping: 'GET',
|
|
44
|
+
PostMapping: 'POST',
|
|
45
|
+
PutMapping: 'PUT',
|
|
46
|
+
DeleteMapping: 'DELETE',
|
|
47
|
+
PatchMapping: 'PATCH',
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Build the plugin only if the Kotlin grammar is available. Compiling
|
|
51
|
+
* the queries against a null grammar would throw at module load time
|
|
52
|
+
* and abort the whole http-route-extractor module.
|
|
53
|
+
*/
|
|
54
|
+
function buildKotlinPlugin(language) {
|
|
55
|
+
// ─── Provider: Spring class-level @RequestMapping prefix ──────────────
|
|
56
|
+
// Two patterns mirror the Java plugin's positional vs named split:
|
|
57
|
+
// @RequestMapping("/api") → value_argument has string_literal as its first named child
|
|
58
|
+
// @RequestMapping(path = "/api") → value_argument has [simple_identifier @key, string_literal]
|
|
59
|
+
// @RequestMapping(value = "/api") → same as above, with key="value"
|
|
60
|
+
//
|
|
61
|
+
// Tree-sitter-kotlin grammar (fwcd 0.3.8) does NOT have a separate
|
|
62
|
+
// node for named arguments — both positional and named forms share
|
|
63
|
+
// `value_argument`. The positional pattern uses the immediate-child
|
|
64
|
+
// anchor `.` so it only matches when the string_literal is the FIRST
|
|
65
|
+
// named child (i.e. no preceding simple_identifier "=" prefix). The
|
|
66
|
+
// named pattern explicitly captures the simple_identifier and uses
|
|
67
|
+
// `#match?` to restrict it to `path`/`value`, matching the same
|
|
68
|
+
// safety bar that the Java plugin enforces (see java.ts and the
|
|
69
|
+
// sibling topic-patterns/java.ts for the analogous constraint).
|
|
70
|
+
//
|
|
71
|
+
// Without the `key:` constraint the named query would also capture
|
|
72
|
+
// unrelated attributes like `produces`, `consumes`, `headers`,
|
|
73
|
+
// `name`, `params` — emitting bogus route contracts (a regression
|
|
74
|
+
// identical to the one Claude flagged on PR #1834 for Java).
|
|
75
|
+
const SPRING_CLASS_PREFIX_PATTERNS = compilePatterns({
|
|
76
|
+
name: 'kotlin-spring-class-prefix',
|
|
77
|
+
language,
|
|
78
|
+
patterns: [
|
|
79
|
+
{
|
|
80
|
+
meta: {},
|
|
81
|
+
query: `
|
|
82
|
+
(class_declaration
|
|
83
|
+
(modifiers
|
|
84
|
+
(annotation
|
|
85
|
+
(constructor_invocation
|
|
86
|
+
(user_type (type_identifier) @ann (#eq? @ann "RequestMapping"))
|
|
87
|
+
(value_arguments
|
|
88
|
+
(value_argument . (string_literal) @prefix)))))
|
|
89
|
+
(type_identifier) @cls) @class
|
|
90
|
+
`,
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
meta: {},
|
|
94
|
+
query: `
|
|
95
|
+
(class_declaration
|
|
96
|
+
(modifiers
|
|
97
|
+
(annotation
|
|
98
|
+
(constructor_invocation
|
|
99
|
+
(user_type (type_identifier) @ann (#eq? @ann "RequestMapping"))
|
|
100
|
+
(value_arguments
|
|
101
|
+
(value_argument
|
|
102
|
+
(simple_identifier) @key (#match? @key "^(path|value)$")
|
|
103
|
+
(string_literal) @prefix)))))
|
|
104
|
+
(type_identifier) @cls) @class
|
|
105
|
+
`,
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
});
|
|
109
|
+
// ─── Provider: Spring @(Get|Post|...)Mapping method annotations ───────
|
|
110
|
+
// Same dual-pattern positional/named approach. The Kotlin AST puts the
|
|
111
|
+
// function name (`simple_identifier`) outside the `modifiers` subtree,
|
|
112
|
+
// so we capture it from `function_declaration` directly.
|
|
113
|
+
const SPRING_METHOD_ROUTE_PATTERNS = compilePatterns({
|
|
114
|
+
name: 'kotlin-spring-method-route',
|
|
115
|
+
language,
|
|
116
|
+
patterns: [
|
|
117
|
+
{
|
|
118
|
+
meta: {},
|
|
119
|
+
query: `
|
|
120
|
+
(function_declaration
|
|
121
|
+
(modifiers
|
|
122
|
+
(annotation
|
|
123
|
+
(constructor_invocation
|
|
124
|
+
(user_type (type_identifier) @ann (#match? @ann "^(Get|Post|Put|Delete|Patch)Mapping$"))
|
|
125
|
+
(value_arguments
|
|
126
|
+
(value_argument . (string_literal) @path)))))
|
|
127
|
+
(simple_identifier) @method_name) @method
|
|
128
|
+
`,
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
meta: {},
|
|
132
|
+
query: `
|
|
133
|
+
(function_declaration
|
|
134
|
+
(modifiers
|
|
135
|
+
(annotation
|
|
136
|
+
(constructor_invocation
|
|
137
|
+
(user_type (type_identifier) @ann (#match? @ann "^(Get|Post|Put|Delete|Patch)Mapping$"))
|
|
138
|
+
(value_arguments
|
|
139
|
+
(value_argument
|
|
140
|
+
(simple_identifier) @key (#match? @key "^(path|value)$")
|
|
141
|
+
(string_literal) @path)))))
|
|
142
|
+
(simple_identifier) @method_name) @method
|
|
143
|
+
`,
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
});
|
|
147
|
+
/**
|
|
148
|
+
* Find the nearest enclosing class_declaration ancestor for a node, or
|
|
149
|
+
* null if the node is top-level. Mirrors the Java plugin's helper.
|
|
150
|
+
*/
|
|
151
|
+
function findEnclosingClass(node) {
|
|
152
|
+
let cur = node.parent;
|
|
153
|
+
while (cur) {
|
|
154
|
+
if (cur.type === 'class_declaration')
|
|
155
|
+
return cur;
|
|
156
|
+
cur = cur.parent;
|
|
157
|
+
}
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Join a class-level prefix and a method-level path. Identical
|
|
162
|
+
* semantics to the Java plugin: strip leading/trailing slashes on
|
|
163
|
+
* the prefix, strip leading slashes on the method path, ensure a
|
|
164
|
+
* single slash between them.
|
|
165
|
+
*/
|
|
166
|
+
function joinPath(prefix, methodPath) {
|
|
167
|
+
const cleanPrefix = prefix.replace(/^\/+/, '').replace(/\/+$/, '');
|
|
168
|
+
const cleanSub = methodPath.replace(/^\/+/, '');
|
|
169
|
+
if (!cleanPrefix)
|
|
170
|
+
return `/${cleanSub}`;
|
|
171
|
+
return `/${cleanPrefix}/${cleanSub}`;
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
name: 'kotlin-http',
|
|
175
|
+
language,
|
|
176
|
+
scan(tree) {
|
|
177
|
+
const out = [];
|
|
178
|
+
// ─── Class prefixes ─────────────────────────────────────────────
|
|
179
|
+
const prefixByClassId = new Map();
|
|
180
|
+
for (const match of runCompiledPatterns(SPRING_CLASS_PREFIX_PATTERNS, tree)) {
|
|
181
|
+
const prefixNode = match.captures.prefix;
|
|
182
|
+
const classNode = match.captures.class;
|
|
183
|
+
if (!prefixNode || !classNode)
|
|
184
|
+
continue;
|
|
185
|
+
const prefix = unquoteLiteral(prefixNode.text);
|
|
186
|
+
if (prefix !== null)
|
|
187
|
+
prefixByClassId.set(classNode.id, prefix);
|
|
188
|
+
}
|
|
189
|
+
// ─── Method routes ──────────────────────────────────────────────
|
|
190
|
+
for (const match of runCompiledPatterns(SPRING_METHOD_ROUTE_PATTERNS, tree)) {
|
|
191
|
+
const annNode = match.captures.ann;
|
|
192
|
+
const pathNode = match.captures.path;
|
|
193
|
+
const nameNode = match.captures.method_name;
|
|
194
|
+
const methodNode = match.captures.method;
|
|
195
|
+
if (!annNode || !pathNode || !methodNode)
|
|
196
|
+
continue;
|
|
197
|
+
const httpMethod = METHOD_ANNOTATION_TO_HTTP[annNode.text];
|
|
198
|
+
if (!httpMethod)
|
|
199
|
+
continue;
|
|
200
|
+
const rawPath = unquoteLiteral(pathNode.text);
|
|
201
|
+
if (rawPath === null)
|
|
202
|
+
continue;
|
|
203
|
+
const enclosingClass = findEnclosingClass(methodNode);
|
|
204
|
+
const prefix = enclosingClass ? (prefixByClassId.get(enclosingClass.id) ?? '') : '';
|
|
205
|
+
const fullPath = joinPath(prefix, rawPath);
|
|
206
|
+
out.push({
|
|
207
|
+
role: 'provider',
|
|
208
|
+
framework: 'spring',
|
|
209
|
+
method: httpMethod,
|
|
210
|
+
path: fullPath,
|
|
211
|
+
name: nameNode?.text ?? null,
|
|
212
|
+
confidence: 0.8,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
return out;
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* The exported plugin is `null` when tree-sitter-kotlin's native
|
|
221
|
+
* binding is unavailable. `http-patterns/index.ts` checks for null
|
|
222
|
+
* before registering `.kt`/`.kts` so missing optional grammars never
|
|
223
|
+
* crash the orchestrator.
|
|
224
|
+
*/
|
|
225
|
+
export const KOTLIN_HTTP_PLUGIN = Kotlin
|
|
226
|
+
? buildKotlinPlugin(Kotlin)
|
|
227
|
+
: null;
|
package/package.json
CHANGED