gitnexus 1.6.9-rc.13 → 1.6.9-rc.14

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.
@@ -576,13 +576,16 @@ export const JAVA_HTTP_PLUGIN = {
576
576
  language: Java,
577
577
  // routeCoverage intentionally LEFT at the default 'partial' (#2138 Part 2).
578
578
  // The graph provider set is a strict *subset* of this scan()'s provider set —
579
- // ingestion does NOT emit a Route node for (1) array-form `@GetMapping({...})`,
580
- // (2) interface-inherited Spring routes, or (3) the 2nd verb of a same-URL
581
- // GET+POST pair (Route nodes are URL-keyed). Declaring 'complete' here would
582
- // let the parse-skip drop those group-only providers. Java flips to 'complete'
583
- // only once ingestion provider extraction matches this scan (a follow-up:
584
- // array-form query branch + interface-inheritance emission + per-verb Route
585
- // identity). `hasConsumerSignals` below is kept ready for that flip.
579
+ // ingestion does NOT emit a Route node for (1) a method-level array route
580
+ // nested under a class-level array-form `@RequestMapping` (ingestion suppresses
581
+ // it rather than drop the prefix; bare/scalar-prefixed array methods ARE now
582
+ // emitted see #2280), (2) interface-inherited Spring routes, or (3) the 2nd
583
+ // verb of a same-URL GET+POST pair (Route nodes are URL-keyed). Declaring
584
+ // 'complete' here would let the parse-skip drop those group-only providers.
585
+ // Java flips to 'complete' only once ingestion provider extraction matches this
586
+ // scan (a follow-up: class-level array-form prefix support + interface-
587
+ // inheritance emission + per-verb Route identity — tracked in #2280).
588
+ // `hasConsumerSignals` below is kept ready for that flip.
586
589
  // Consumer signals this plugin's scan() can detect: RestTemplate / WebClient /
587
590
  // OkHttp / Java-HttpClient / Apache-HttpClient call sites, OpenFeign
588
591
  // (`@FeignClient` + `@RequestLine`) interfaces, and Spring 6 HTTP Interface
@@ -31,6 +31,18 @@ import { METHOD_ANNOTATION_TO_HTTP, isRouteMemberKey, findEnclosingClass, unquot
31
31
  * @node → enclosing declaration (class_declaration | method_declaration)
32
32
  * @value → the string-literal argument
33
33
  * @key → the named-argument member key (absent for positional form)
34
+ *
35
+ * Method-level routes accept both the bare string form `@GetMapping("/x")` and
36
+ * the array form `@GetMapping({"/a","/b"})` (positional or `path =`/`value =`):
37
+ * a multi-element array yields one match per element, so the Phase 2 loop emits
38
+ * one route per path with no special-casing. This mirrors the group-layer
39
+ * `java.ts` query so the two Spring extractors stay in parity (#2138 follow-up;
40
+ * the divergence here was the root of the #2265 array-form gap). The class-level
41
+ * `@RequestMapping` branches also match the array form, but only to *detect* it:
42
+ * an array-form class prefix can't be resolved to a single string, so Phase 2
43
+ * suppresses that class's method-level array routes rather than emit them with a
44
+ * dropped prefix (a wrong route). Full class-array cross-product support is left
45
+ * to a follow-up (#2280).
34
46
  */
35
47
  const ROUTE_ANNOTATION_QUERY = new Parser.Query(Java, `
36
48
  [
@@ -38,7 +50,9 @@ const ROUTE_ANNOTATION_QUERY = new Parser.Query(Java, `
38
50
  (modifiers
39
51
  (annotation
40
52
  name: (identifier) @ann
41
- arguments: (annotation_argument_list (string_literal) @value)))) @node
53
+ arguments: (annotation_argument_list
54
+ [(string_literal) @value
55
+ (element_value_array_initializer (string_literal) @value)])))) @node
42
56
  (class_declaration
43
57
  (modifiers
44
58
  (annotation
@@ -46,12 +60,15 @@ const ROUTE_ANNOTATION_QUERY = new Parser.Query(Java, `
46
60
  arguments: (annotation_argument_list
47
61
  (element_value_pair
48
62
  key: (identifier) @key
49
- value: (string_literal) @value))))) @node
63
+ value: [(string_literal) @value
64
+ (element_value_array_initializer (string_literal) @value)]))))) @node
50
65
  (method_declaration
51
66
  (modifiers
52
67
  (annotation
53
68
  name: (identifier) @ann
54
- arguments: (annotation_argument_list (string_literal) @value)))) @node
69
+ arguments: (annotation_argument_list
70
+ [(string_literal) @value
71
+ (element_value_array_initializer (string_literal) @value)])))) @node
55
72
  (method_declaration
56
73
  (modifiers
57
74
  (annotation
@@ -59,7 +76,8 @@ const ROUTE_ANNOTATION_QUERY = new Parser.Query(Java, `
59
76
  arguments: (annotation_argument_list
60
77
  (element_value_pair
61
78
  key: (identifier) @key
62
- value: (string_literal) @value))))) @node
79
+ value: [(string_literal) @value
80
+ (element_value_array_initializer (string_literal) @value)]))))) @node
63
81
  ]
64
82
  `);
65
83
  /**
@@ -76,8 +94,15 @@ const ROUTE_ANNOTATION_QUERY = new Parser.Query(Java, `
76
94
  */
77
95
  export function extractSpringRoutes(tree, filePath, lineOffset = 0) {
78
96
  const matches = ROUTE_ANNOTATION_QUERY.matches(tree.rootNode);
79
- // Phase 1: collect class-level @RequestMapping prefixes keyed by node id
97
+ // Phase 1: collect class-level @RequestMapping prefixes keyed by node id.
98
+ // A scalar prefix (`@RequestMapping("/base")`) is stored in prefixByClassId.
99
+ // A class whose @RequestMapping uses the array form (`@RequestMapping({...})`)
100
+ // is instead recorded in classesWithArrayPrefix: there is no single prefix to
101
+ // store, and Phase 2 uses this to suppress that class's method-level array
102
+ // routes rather than emit them unprefixed (a wrong route — see #2280). Full
103
+ // class-array cross-product support is out of scope here.
80
104
  const prefixByClassId = new Map();
105
+ const classesWithArrayPrefix = new Set();
81
106
  for (const match of matches) {
82
107
  const caps = {};
83
108
  for (const { name, node } of match.captures) {
@@ -92,6 +117,10 @@ export function extractSpringRoutes(tree, filePath, lineOffset = 0) {
92
117
  if (node.type === 'class_declaration' && annNode.text === 'RequestMapping') {
93
118
  if (!isRouteMemberKey(keyNode))
94
119
  continue;
120
+ if (valueNode.parent?.type === 'element_value_array_initializer') {
121
+ classesWithArrayPrefix.add(node.id);
122
+ continue;
123
+ }
95
124
  const prefix = unquoteSpringLiteral(valueNode.text);
96
125
  if (prefix !== null)
97
126
  prefixByClassId.set(node.id, prefix);
@@ -122,6 +151,18 @@ export function extractSpringRoutes(tree, filePath, lineOffset = 0) {
122
151
  if (routePath === null)
123
152
  continue;
124
153
  const enclosingClass = findEnclosingClass(node);
154
+ // Suppress a method-level *array-form* route nested under a class-level
155
+ // array-form @RequestMapping. The class prefix is one of several values that
156
+ // cannot be resolved to a single string here, so emitting the route would
157
+ // drop the prefix and yield a wrong unprefixed Route (a false signal, worse
158
+ // than a missing one). Skipping keeps ingestion a strict subset of the group
159
+ // scan — safe under routeCoverage:'partial'. Full class-array cross-product
160
+ // support is tracked in #2280. (Scalar method paths under an array class
161
+ // prefix are left unchanged: that pre-existing divergence is out of scope.)
162
+ const isArrayElement = valueNode.parent?.type === 'element_value_array_initializer';
163
+ if (isArrayElement && enclosingClass && classesWithArrayPrefix.has(enclosingClass.id)) {
164
+ continue;
165
+ }
125
166
  const classPrefix = enclosingClass ? (prefixByClassId.get(enclosingClass.id) ?? '') : '';
126
167
  // `node` is the annotated `method_declaration`; its name field is the
127
168
  // handler method name (resolved to a symbol UID later by the routes phase).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.6.9-rc.13",
3
+ "version": "1.6.9-rc.14",
4
4
  "description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
5
5
  "author": "Abhigyan Patwari",
6
6
  "license": "PolyForm-Noncommercial-1.0.0",