gitnexus 1.6.9-rc.2 → 1.6.9-rc.3
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 +223 -195
- package/dist/core/group/extractors/http-patterns/kotlin.js +575 -81
- package/dist/core/group/extractors/http-patterns/spring-consumer-shared.d.ts +137 -0
- package/dist/core/group/extractors/http-patterns/spring-consumer-shared.js +230 -0
- package/package.json +1 -1
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared, language-agnostic primitives for Spring / OpenFeign / Spring-HTTP-Interface
|
|
3
|
+
* HTTP *consumer* extraction, used by BOTH the Java (`java.ts`) and Kotlin
|
|
4
|
+
* (`kotlin.ts`) group-layer HTTP plugins so the two cannot drift apart.
|
|
5
|
+
*
|
|
6
|
+
* These are pure value maps + string helpers — no tree-sitter AST knowledge.
|
|
7
|
+
* Each language plugin keeps its own grammar-specific queries and walkers and
|
|
8
|
+
* funnels the extracted (verb, path, prefix) facts through these helpers, so a
|
|
9
|
+
* polyglot repo emits byte-identical contract IDs from `.java` and `.kt`.
|
|
10
|
+
*
|
|
11
|
+
* The provider-side annotation→verb map (`METHOD_ANNOTATION_TO_HTTP`),
|
|
12
|
+
* `isRouteMemberKey`, and `findEnclosingClass` live in the lower
|
|
13
|
+
* `ingestion/route-extractors/spring-shared.ts` (shared with the ingestion
|
|
14
|
+
* route extractor). This module is the consumer-side counterpart and lives in
|
|
15
|
+
* the group layer beside the plugins that use it.
|
|
16
|
+
*/
|
|
17
|
+
import type { HttpFileDetections } from './types.js';
|
|
18
|
+
/**
|
|
19
|
+
* RestTemplate method-name → HTTP verb. Source-scan only: the receiver must be
|
|
20
|
+
* named exactly `restTemplate` (the per-language query enforces that).
|
|
21
|
+
*/
|
|
22
|
+
export declare const REST_TEMPLATE_TO_HTTP: Record<string, string>;
|
|
23
|
+
/**
|
|
24
|
+
* Reactive WebClient short-form verb helper → HTTP verb
|
|
25
|
+
* (`webClient.get().uri("/x")`, `.post()`, ...). HEAD/OPTIONS/TRACE are
|
|
26
|
+
* intentionally excluded for symmetry across the plugins.
|
|
27
|
+
*/
|
|
28
|
+
export declare const WEB_CLIENT_SHORT_TO_HTTP: Record<string, string>;
|
|
29
|
+
/**
|
|
30
|
+
* Accepted HTTP verbs for the WebClient long form
|
|
31
|
+
* `webClient.method(HttpMethod.X).uri("/y")`. The verb is captured as the
|
|
32
|
+
* literal `HttpMethod.X` field name (`GET`, `POST`, …); HEAD/OPTIONS/TRACE are
|
|
33
|
+
* intentionally excluded for symmetry with the short form
|
|
34
|
+
* (`WEB_CLIENT_SHORT_TO_HTTP`). Shared so the Java and Kotlin long-form scans
|
|
35
|
+
* gate verbs identically.
|
|
36
|
+
*/
|
|
37
|
+
export declare const WEB_CLIENT_LONG_VERB_RE: RegExp;
|
|
38
|
+
/**
|
|
39
|
+
* Spring 6 declarative HTTP Interface shortcut annotation → HTTP verb.
|
|
40
|
+
*
|
|
41
|
+
* `@GetExchange`/`@PostExchange`/… on a service interface proxied by
|
|
42
|
+
* `HttpServiceProxyFactory` (over RestClient / WebClient / RestTemplate)
|
|
43
|
+
* describe an OUTBOUND call — i.e. a CONSUMER, the modern analogue of an
|
|
44
|
+
* OpenFeign `@(Get|Post|...)Mapping` interface method. The path lives in the
|
|
45
|
+
* annotation's `url` (or `value`) attribute, or positionally.
|
|
46
|
+
*
|
|
47
|
+
* The base `@HttpExchange(method = "GET", url = "...")` form carries its verb
|
|
48
|
+
* in an attribute rather than the annotation name; the shortcut annotations
|
|
49
|
+
* above are the overwhelmingly common case and the only ones mapped here.
|
|
50
|
+
*/
|
|
51
|
+
export declare const EXCHANGE_ANNOTATION_TO_HTTP: Record<string, string>;
|
|
52
|
+
/**
|
|
53
|
+
* Accumulate a route prefix (de-duped) under a class/interface declaration node
|
|
54
|
+
* id. A Spring route attribute is `String[]`; a multi-element array (`["/a","/b"]`,
|
|
55
|
+
* `arrayOf("/a","/b")`, `{"/a","/b"}`) yields one query match per element, so
|
|
56
|
+
* prefixes accumulate rather than overwrite. Shared by the Java and Kotlin plugins
|
|
57
|
+
* so both build their prefix maps identically.
|
|
58
|
+
*/
|
|
59
|
+
export declare const pushPrefix: (map: Map<number, string[]>, id: number, prefix: string) => void;
|
|
60
|
+
/** Framework tags emitted on consumer detections (stable contract metadata). */
|
|
61
|
+
export declare const OPENFEIGN_FRAMEWORK = "openfeign";
|
|
62
|
+
export declare const HTTP_INTERFACE_FRAMEWORK = "spring-http-interface";
|
|
63
|
+
/** Consumer-detection confidences, shared so `.java` and `.kt` agree. */
|
|
64
|
+
export declare const FEIGN_CONFIDENCE = 0.7;
|
|
65
|
+
export declare const REQUEST_LINE_CONFIDENCE = 0.75;
|
|
66
|
+
export declare const EXCHANGE_CONFIDENCE = 0.75;
|
|
67
|
+
/**
|
|
68
|
+
* OpenFeign's native `@RequestLine("METHOD /path[?query]")` packs an HTTP
|
|
69
|
+
* method and path in a single string literal — see
|
|
70
|
+
* https://github.com/OpenFeign/feign#interface-annotations. This regex splits
|
|
71
|
+
* the verb from the path of that literal.
|
|
72
|
+
*/
|
|
73
|
+
export declare const REQUEST_LINE_VERB_RE: RegExp;
|
|
74
|
+
/**
|
|
75
|
+
* Parse a Feign `@RequestLine` value into a method + path pair. The query
|
|
76
|
+
* portion is dropped because contract IDs are method+path only (consistent
|
|
77
|
+
* with how RestTemplate/WebClient consumers drop inline query strings).
|
|
78
|
+
*
|
|
79
|
+
* Returns null if the value is not a recognized HTTP verb followed by a path
|
|
80
|
+
* beginning with `/`.
|
|
81
|
+
*/
|
|
82
|
+
export declare function parseRequestLine(raw: string): {
|
|
83
|
+
method: string;
|
|
84
|
+
path: string;
|
|
85
|
+
} | null;
|
|
86
|
+
/**
|
|
87
|
+
* Join a class/interface-level prefix and a method-level path into a single
|
|
88
|
+
* URL path: strip leading/trailing slashes on the prefix and leading slashes
|
|
89
|
+
* on the method path, then ensure exactly one slash between them.
|
|
90
|
+
*/
|
|
91
|
+
export declare function joinPath(prefix: string, methodPath: string): string;
|
|
92
|
+
/**
|
|
93
|
+
* Join a controller's own class prefix with a route inherited from an interface
|
|
94
|
+
* (interface-based controllers, #1743). The inherited path already has the
|
|
95
|
+
* interface's own class prefix (`inheritedOwnerPrefix`) baked in; when the
|
|
96
|
+
* controller repeats that same prefix we must NOT prepend it twice (#2057).
|
|
97
|
+
* Shared by both plugins' `scanProject` so Java and Kotlin agree.
|
|
98
|
+
*/
|
|
99
|
+
export declare function joinInheritedSpringPath(controllerPrefix: string, inheritedPath: string, inheritedOwnerPrefix?: string): string;
|
|
100
|
+
/**
|
|
101
|
+
* Language-agnostic view of a Spring class/interface that each plugin's
|
|
102
|
+
* grammar-specific collector produces. The interface-based-controller
|
|
103
|
+
* inheritance algorithm (`scanSpringInheritanceProject`) operates only on this
|
|
104
|
+
* shape, so the Java and Kotlin plugins share one algorithm and cannot drift.
|
|
105
|
+
*
|
|
106
|
+
* `methods[].routes` carry only `{ method, path }` — the interface's own class
|
|
107
|
+
* prefix is applied *inside* `scanSpringInheritanceProject` (it is not part of
|
|
108
|
+
* the collector's output).
|
|
109
|
+
*/
|
|
110
|
+
export interface SharedSpringType {
|
|
111
|
+
filePath: string;
|
|
112
|
+
kind: 'class' | 'interface';
|
|
113
|
+
name: string;
|
|
114
|
+
/** Class-level `@RequestMapping` prefixes — one per array element. */
|
|
115
|
+
classPrefixes: string[];
|
|
116
|
+
implementedInterfaces: string[];
|
|
117
|
+
isController: boolean;
|
|
118
|
+
methods: Array<{
|
|
119
|
+
name: string;
|
|
120
|
+
routes: Array<{
|
|
121
|
+
method: string;
|
|
122
|
+
path: string;
|
|
123
|
+
}>;
|
|
124
|
+
}>;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Resolve interface-based-controller provider routes (#1743): a concrete
|
|
128
|
+
* `@RestController`/`@Controller` class inherits the `@(Get|...)Mapping` routes
|
|
129
|
+
* declared on the interface it implements. Shared by the Java and Kotlin plugins
|
|
130
|
+
* so both emit byte-identical provider contracts.
|
|
131
|
+
*
|
|
132
|
+
* An interface name that resolves to two distinct interfaces is ambiguous and
|
|
133
|
+
* its routes are dropped (the `null` marker). The controller's own class
|
|
134
|
+
* prefix(es) cross-product the inherited routes; `joinInheritedSpringPath`
|
|
135
|
+
* avoids doubling a prefix the interface already baked in (#2057).
|
|
136
|
+
*/
|
|
137
|
+
export declare function scanSpringInheritanceProject(types: SharedSpringType[]): HttpFileDetections[];
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared, language-agnostic primitives for Spring / OpenFeign / Spring-HTTP-Interface
|
|
3
|
+
* HTTP *consumer* extraction, used by BOTH the Java (`java.ts`) and Kotlin
|
|
4
|
+
* (`kotlin.ts`) group-layer HTTP plugins so the two cannot drift apart.
|
|
5
|
+
*
|
|
6
|
+
* These are pure value maps + string helpers — no tree-sitter AST knowledge.
|
|
7
|
+
* Each language plugin keeps its own grammar-specific queries and walkers and
|
|
8
|
+
* funnels the extracted (verb, path, prefix) facts through these helpers, so a
|
|
9
|
+
* polyglot repo emits byte-identical contract IDs from `.java` and `.kt`.
|
|
10
|
+
*
|
|
11
|
+
* The provider-side annotation→verb map (`METHOD_ANNOTATION_TO_HTTP`),
|
|
12
|
+
* `isRouteMemberKey`, and `findEnclosingClass` live in the lower
|
|
13
|
+
* `ingestion/route-extractors/spring-shared.ts` (shared with the ingestion
|
|
14
|
+
* route extractor). This module is the consumer-side counterpart and lives in
|
|
15
|
+
* the group layer beside the plugins that use it.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* RestTemplate method-name → HTTP verb. Source-scan only: the receiver must be
|
|
19
|
+
* named exactly `restTemplate` (the per-language query enforces that).
|
|
20
|
+
*/
|
|
21
|
+
export const REST_TEMPLATE_TO_HTTP = {
|
|
22
|
+
getForObject: 'GET',
|
|
23
|
+
getForEntity: 'GET',
|
|
24
|
+
postForObject: 'POST',
|
|
25
|
+
postForEntity: 'POST',
|
|
26
|
+
put: 'PUT',
|
|
27
|
+
delete: 'DELETE',
|
|
28
|
+
patchForObject: 'PATCH',
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Reactive WebClient short-form verb helper → HTTP verb
|
|
32
|
+
* (`webClient.get().uri("/x")`, `.post()`, ...). HEAD/OPTIONS/TRACE are
|
|
33
|
+
* intentionally excluded for symmetry across the plugins.
|
|
34
|
+
*/
|
|
35
|
+
export const WEB_CLIENT_SHORT_TO_HTTP = {
|
|
36
|
+
get: 'GET',
|
|
37
|
+
post: 'POST',
|
|
38
|
+
put: 'PUT',
|
|
39
|
+
delete: 'DELETE',
|
|
40
|
+
patch: 'PATCH',
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Accepted HTTP verbs for the WebClient long form
|
|
44
|
+
* `webClient.method(HttpMethod.X).uri("/y")`. The verb is captured as the
|
|
45
|
+
* literal `HttpMethod.X` field name (`GET`, `POST`, …); HEAD/OPTIONS/TRACE are
|
|
46
|
+
* intentionally excluded for symmetry with the short form
|
|
47
|
+
* (`WEB_CLIENT_SHORT_TO_HTTP`). Shared so the Java and Kotlin long-form scans
|
|
48
|
+
* gate verbs identically.
|
|
49
|
+
*/
|
|
50
|
+
export const WEB_CLIENT_LONG_VERB_RE = /^(GET|POST|PUT|DELETE|PATCH)$/;
|
|
51
|
+
/**
|
|
52
|
+
* Spring 6 declarative HTTP Interface shortcut annotation → HTTP verb.
|
|
53
|
+
*
|
|
54
|
+
* `@GetExchange`/`@PostExchange`/… on a service interface proxied by
|
|
55
|
+
* `HttpServiceProxyFactory` (over RestClient / WebClient / RestTemplate)
|
|
56
|
+
* describe an OUTBOUND call — i.e. a CONSUMER, the modern analogue of an
|
|
57
|
+
* OpenFeign `@(Get|Post|...)Mapping` interface method. The path lives in the
|
|
58
|
+
* annotation's `url` (or `value`) attribute, or positionally.
|
|
59
|
+
*
|
|
60
|
+
* The base `@HttpExchange(method = "GET", url = "...")` form carries its verb
|
|
61
|
+
* in an attribute rather than the annotation name; the shortcut annotations
|
|
62
|
+
* above are the overwhelmingly common case and the only ones mapped here.
|
|
63
|
+
*/
|
|
64
|
+
export const EXCHANGE_ANNOTATION_TO_HTTP = {
|
|
65
|
+
GetExchange: 'GET',
|
|
66
|
+
PostExchange: 'POST',
|
|
67
|
+
PutExchange: 'PUT',
|
|
68
|
+
DeleteExchange: 'DELETE',
|
|
69
|
+
PatchExchange: 'PATCH',
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Accumulate a route prefix (de-duped) under a class/interface declaration node
|
|
73
|
+
* id. A Spring route attribute is `String[]`; a multi-element array (`["/a","/b"]`,
|
|
74
|
+
* `arrayOf("/a","/b")`, `{"/a","/b"}`) yields one query match per element, so
|
|
75
|
+
* prefixes accumulate rather than overwrite. Shared by the Java and Kotlin plugins
|
|
76
|
+
* so both build their prefix maps identically.
|
|
77
|
+
*/
|
|
78
|
+
export const pushPrefix = (map, id, prefix) => {
|
|
79
|
+
const arr = map.get(id) ?? [];
|
|
80
|
+
if (!arr.includes(prefix))
|
|
81
|
+
arr.push(prefix);
|
|
82
|
+
map.set(id, arr);
|
|
83
|
+
};
|
|
84
|
+
/** Framework tags emitted on consumer detections (stable contract metadata). */
|
|
85
|
+
export const OPENFEIGN_FRAMEWORK = 'openfeign';
|
|
86
|
+
export const HTTP_INTERFACE_FRAMEWORK = 'spring-http-interface';
|
|
87
|
+
/** Consumer-detection confidences, shared so `.java` and `.kt` agree. */
|
|
88
|
+
export const FEIGN_CONFIDENCE = 0.7;
|
|
89
|
+
export const REQUEST_LINE_CONFIDENCE = 0.75;
|
|
90
|
+
export const EXCHANGE_CONFIDENCE = 0.75;
|
|
91
|
+
/**
|
|
92
|
+
* OpenFeign's native `@RequestLine("METHOD /path[?query]")` packs an HTTP
|
|
93
|
+
* method and path in a single string literal — see
|
|
94
|
+
* https://github.com/OpenFeign/feign#interface-annotations. This regex splits
|
|
95
|
+
* the verb from the path of that literal.
|
|
96
|
+
*/
|
|
97
|
+
export const REQUEST_LINE_VERB_RE = /^\s*(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)\s+(\S.*?)\s*$/i;
|
|
98
|
+
/**
|
|
99
|
+
* Parse a Feign `@RequestLine` value into a method + path pair. The query
|
|
100
|
+
* portion is dropped because contract IDs are method+path only (consistent
|
|
101
|
+
* with how RestTemplate/WebClient consumers drop inline query strings).
|
|
102
|
+
*
|
|
103
|
+
* Returns null if the value is not a recognized HTTP verb followed by a path
|
|
104
|
+
* beginning with `/`.
|
|
105
|
+
*/
|
|
106
|
+
export function parseRequestLine(raw) {
|
|
107
|
+
const match = REQUEST_LINE_VERB_RE.exec(raw);
|
|
108
|
+
if (!match)
|
|
109
|
+
return null;
|
|
110
|
+
const [, verb, rest] = match;
|
|
111
|
+
if (typeof verb !== 'string' || typeof rest !== 'string')
|
|
112
|
+
return null;
|
|
113
|
+
const queryIdx = rest.indexOf('?');
|
|
114
|
+
const pathOnly = (queryIdx >= 0 ? rest.slice(0, queryIdx) : rest).trim();
|
|
115
|
+
if (!pathOnly.startsWith('/'))
|
|
116
|
+
return null;
|
|
117
|
+
return { method: verb.toUpperCase(), path: pathOnly };
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Join a class/interface-level prefix and a method-level path into a single
|
|
121
|
+
* URL path: strip leading/trailing slashes on the prefix and leading slashes
|
|
122
|
+
* on the method path, then ensure exactly one slash between them.
|
|
123
|
+
*/
|
|
124
|
+
export function joinPath(prefix, methodPath) {
|
|
125
|
+
const cleanPrefix = prefix.replace(/^\/+/, '').replace(/\/+$/, '');
|
|
126
|
+
const cleanSub = methodPath.replace(/^\/+/, '');
|
|
127
|
+
if (!cleanPrefix)
|
|
128
|
+
return `/${cleanSub}`;
|
|
129
|
+
return `/${cleanPrefix}/${cleanSub}`;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Join a controller's own class prefix with a route inherited from an interface
|
|
133
|
+
* (interface-based controllers, #1743). The inherited path already has the
|
|
134
|
+
* interface's own class prefix (`inheritedOwnerPrefix`) baked in; when the
|
|
135
|
+
* controller repeats that same prefix we must NOT prepend it twice (#2057).
|
|
136
|
+
* Shared by both plugins' `scanProject` so Java and Kotlin agree.
|
|
137
|
+
*/
|
|
138
|
+
export function joinInheritedSpringPath(controllerPrefix, inheritedPath, inheritedOwnerPrefix = '') {
|
|
139
|
+
const joined = joinPath(controllerPrefix, inheritedPath);
|
|
140
|
+
const cleanPrefix = controllerPrefix.replace(/^\/+/, '').replace(/\/+$/, '');
|
|
141
|
+
const cleanOwnerPrefix = inheritedOwnerPrefix.replace(/^\/+/, '').replace(/\/+$/, '');
|
|
142
|
+
const cleanInherited = inheritedPath.replace(/^\/+/, '');
|
|
143
|
+
if (!cleanPrefix)
|
|
144
|
+
return joined;
|
|
145
|
+
if (cleanPrefix === cleanOwnerPrefix &&
|
|
146
|
+
(cleanInherited === cleanPrefix || cleanInherited.startsWith(`${cleanPrefix}/`))) {
|
|
147
|
+
return `/${cleanInherited}`;
|
|
148
|
+
}
|
|
149
|
+
return joined;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Resolve interface-based-controller provider routes (#1743): a concrete
|
|
153
|
+
* `@RestController`/`@Controller` class inherits the `@(Get|...)Mapping` routes
|
|
154
|
+
* declared on the interface it implements. Shared by the Java and Kotlin plugins
|
|
155
|
+
* so both emit byte-identical provider contracts.
|
|
156
|
+
*
|
|
157
|
+
* An interface name that resolves to two distinct interfaces is ambiguous and
|
|
158
|
+
* its routes are dropped (the `null` marker). The controller's own class
|
|
159
|
+
* prefix(es) cross-product the inherited routes; `joinInheritedSpringPath`
|
|
160
|
+
* avoids doubling a prefix the interface already baked in (#2057).
|
|
161
|
+
*/
|
|
162
|
+
export function scanSpringInheritanceProject(types) {
|
|
163
|
+
const interfaceRoutes = new Map();
|
|
164
|
+
for (const type of types) {
|
|
165
|
+
if (type.kind !== 'interface')
|
|
166
|
+
continue;
|
|
167
|
+
if (interfaceRoutes.has(type.name)) {
|
|
168
|
+
interfaceRoutes.set(type.name, null);
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
const prefixes = type.classPrefixes.length ? type.classPrefixes : [''];
|
|
172
|
+
const methodMap = new Map();
|
|
173
|
+
for (const method of type.methods) {
|
|
174
|
+
// Cross-product the interface's class prefixes with each method route, so a
|
|
175
|
+
// multi-element `@RequestMapping(["/a","/b"])` interface yields N bindings.
|
|
176
|
+
const routes = method.routes.flatMap((route) => prefixes.map((prefix) => ({
|
|
177
|
+
method: route.method,
|
|
178
|
+
path: prefix ? joinPath(prefix, route.path) : route.path,
|
|
179
|
+
ownerPrefix: prefix,
|
|
180
|
+
})));
|
|
181
|
+
if (routes.length > 0)
|
|
182
|
+
methodMap.set(method.name, routes);
|
|
183
|
+
}
|
|
184
|
+
interfaceRoutes.set(type.name, methodMap);
|
|
185
|
+
}
|
|
186
|
+
const detectionsByFile = new Map();
|
|
187
|
+
for (const type of types) {
|
|
188
|
+
if (type.kind !== 'class' || !type.isController)
|
|
189
|
+
continue;
|
|
190
|
+
// Cross-product the controller's own class prefixes with each inherited
|
|
191
|
+
// route; `['']` keeps the common no-prefix controller emitting the
|
|
192
|
+
// interface path unchanged.
|
|
193
|
+
const controllerPrefixes = type.classPrefixes.length ? type.classPrefixes : [''];
|
|
194
|
+
for (const method of type.methods) {
|
|
195
|
+
if (method.routes.length > 0)
|
|
196
|
+
continue; // own @*Mapping → already a provider via scan()
|
|
197
|
+
const inherited = type.implementedInterfaces.flatMap((iface) => {
|
|
198
|
+
const routeMap = interfaceRoutes.get(iface);
|
|
199
|
+
if (!routeMap)
|
|
200
|
+
return [];
|
|
201
|
+
const routes = routeMap.get(method.name) ?? [];
|
|
202
|
+
return routes.flatMap((route) => controllerPrefixes.map((controllerPrefix) => ({
|
|
203
|
+
method: route.method,
|
|
204
|
+
path: joinInheritedSpringPath(controllerPrefix, route.path, route.ownerPrefix),
|
|
205
|
+
})));
|
|
206
|
+
});
|
|
207
|
+
const seen = new Set();
|
|
208
|
+
for (const route of inherited) {
|
|
209
|
+
const key = `${route.method} ${route.path}`;
|
|
210
|
+
if (seen.has(key))
|
|
211
|
+
continue;
|
|
212
|
+
seen.add(key);
|
|
213
|
+
const detections = detectionsByFile.get(type.filePath) ?? [];
|
|
214
|
+
detections.push({
|
|
215
|
+
role: 'provider',
|
|
216
|
+
framework: 'spring',
|
|
217
|
+
method: route.method,
|
|
218
|
+
path: route.path,
|
|
219
|
+
name: method.name,
|
|
220
|
+
confidence: 0.8,
|
|
221
|
+
});
|
|
222
|
+
detectionsByFile.set(type.filePath, detections);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return [...detectionsByFile.entries()].map(([filePath, detections]) => ({
|
|
227
|
+
filePath,
|
|
228
|
+
detections,
|
|
229
|
+
}));
|
|
230
|
+
}
|
package/package.json
CHANGED