api-tests-coverage 1.0.15 → 1.0.17
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/dashboard/dist/assets/_basePickBy-C2jmWITn.js +1 -0
- package/dist/dashboard/dist/assets/_baseUniq-DE6cyzJb.js +1 -0
- package/dist/dashboard/dist/assets/arc-B-Q4nGPT.js +1 -0
- package/dist/dashboard/dist/assets/architectureDiagram-VXUJARFQ-C_5dqWCI.js +36 -0
- package/dist/dashboard/dist/assets/blockDiagram-VD42YOAC-DbGIO6Kt.js +122 -0
- package/dist/dashboard/dist/assets/c4Diagram-YG6GDRKO-CAFpcejP.js +10 -0
- package/dist/dashboard/dist/assets/channel-Di9el3wE.js +1 -0
- package/dist/dashboard/dist/assets/chunk-4BX2VUAB-DY1boKsq.js +1 -0
- package/dist/dashboard/dist/assets/chunk-55IACEB6-BSL35gyW.js +1 -0
- package/dist/dashboard/dist/assets/chunk-B4BG7PRW-eTDXrKrv.js +165 -0
- package/dist/dashboard/dist/assets/chunk-DI55MBZ5-M-8I3jEy.js +220 -0
- package/dist/dashboard/dist/assets/chunk-FMBD7UC4-bSA0XiS0.js +15 -0
- package/dist/dashboard/dist/assets/chunk-QN33PNHL-BrOIYUBs.js +1 -0
- package/dist/dashboard/dist/assets/chunk-QZHKN3VN-CliaQGD4.js +1 -0
- package/dist/dashboard/dist/assets/chunk-TZMSLE5B-CyhcxGB1.js +1 -0
- package/dist/dashboard/dist/assets/classDiagram-2ON5EDUG-BkGN4Cpz.js +1 -0
- package/dist/dashboard/dist/assets/classDiagram-v2-WZHVMYZB-BkGN4Cpz.js +1 -0
- package/dist/dashboard/dist/assets/clone-Cvq8JuOb.js +1 -0
- package/dist/dashboard/dist/assets/cose-bilkent-S5V4N54A-BUkL7Wtq.js +1 -0
- package/dist/dashboard/dist/assets/dagre-6UL2VRFP-B8oEROJc.js +4 -0
- package/dist/dashboard/dist/assets/diagram-PSM6KHXK-5uki9Dw8.js +24 -0
- package/dist/dashboard/dist/assets/diagram-QEK2KX5R-BRNhmby2.js +43 -0
- package/dist/dashboard/dist/assets/diagram-S2PKOQOG-D-ku_X8U.js +24 -0
- package/dist/dashboard/dist/assets/erDiagram-Q2GNP2WA-DGl6gPe2.js +60 -0
- package/dist/dashboard/dist/assets/flowDiagram-NV44I4VS-Co89qYBD.js +162 -0
- package/dist/dashboard/dist/assets/ganttDiagram-JELNMOA3-2r3WpWQC.js +267 -0
- package/dist/dashboard/dist/assets/gitGraphDiagram-V2S2FVAM-CuJ5l3TK.js +65 -0
- package/dist/dashboard/dist/assets/graph-ZtgwAPQj.js +1 -0
- package/dist/dashboard/dist/assets/index-D3sRJga7.js +777 -0
- package/dist/dashboard/dist/assets/infoDiagram-HS3SLOUP-ujnMqVz3.js +2 -0
- package/dist/dashboard/dist/assets/journeyDiagram-XKPGCS4Q-DQzfeBIo.js +139 -0
- package/dist/dashboard/dist/assets/kanban-definition-3W4ZIXB7-ueIaoeks.js +89 -0
- package/dist/dashboard/dist/assets/layout-B1fTYUMj.js +1 -0
- package/dist/dashboard/dist/assets/mindmap-definition-VGOIOE7T-B7wYeLe1.js +68 -0
- package/dist/dashboard/dist/assets/pieDiagram-ADFJNKIX-Bf8vKEOf.js +30 -0
- package/dist/dashboard/dist/assets/quadrantDiagram-AYHSOK5B-CM8qiFLR.js +7 -0
- package/dist/dashboard/dist/assets/requirementDiagram-UZGBJVZJ-DPTtP4Ve.js +64 -0
- package/dist/dashboard/dist/assets/sankeyDiagram-TZEHDZUN-DEVTdH0h.js +10 -0
- package/dist/dashboard/dist/assets/sequenceDiagram-WL72ISMW-Bjr5wgXg.js +145 -0
- package/dist/dashboard/dist/assets/stateDiagram-FKZM4ZOC-DDrhZYly.js +1 -0
- package/dist/dashboard/dist/assets/stateDiagram-v2-4FDKWEC3-Im6pH8C-.js +1 -0
- package/dist/dashboard/dist/assets/timeline-definition-IT6M3QCI-DAT3r9va.js +61 -0
- package/dist/dashboard/dist/assets/treemap-GDKQZRPO-BlA8rg0m.js +162 -0
- package/dist/dashboard/dist/assets/xychartDiagram-PRI3JC2R-7aSkQtVu.js +7 -0
- package/dist/src/ast/astTypes.d.ts +86 -0
- package/dist/src/ast/astTypes.d.ts.map +1 -1
- package/dist/src/discovery/fileClassifier.d.ts.map +1 -1
- package/dist/src/discovery/fileClassifier.js +15 -16
- package/dist/src/discovery/frameworkDetector.d.ts +28 -0
- package/dist/src/discovery/frameworkDetector.d.ts.map +1 -0
- package/dist/src/discovery/frameworkDetector.js +189 -0
- package/dist/src/discovery/projectDiscovery.d.ts +5 -1
- package/dist/src/discovery/projectDiscovery.d.ts.map +1 -1
- package/dist/src/discovery/projectDiscovery.js +8 -1
- package/dist/src/inference/routeInference.d.ts.map +1 -1
- package/dist/src/inference/routeInference.js +224 -1
- package/dist/src/languages/java/graphqlSchemaParser.d.ts +65 -0
- package/dist/src/languages/java/graphqlSchemaParser.d.ts.map +1 -0
- package/dist/src/languages/java/graphqlSchemaParser.js +164 -0
- package/dist/src/languages/java/mybatisXmlParser.d.ts +52 -0
- package/dist/src/languages/java/mybatisXmlParser.d.ts.map +1 -0
- package/dist/src/languages/java/mybatisXmlParser.js +107 -0
- package/dist/src/languages/java/semanticBuilder.d.ts.map +1 -1
- package/dist/src/languages/java/semanticBuilder.js +69 -12
- package/dist/src/languages/javascript/angularDetector.d.ts +74 -0
- package/dist/src/languages/javascript/angularDetector.d.ts.map +1 -0
- package/dist/src/languages/javascript/angularDetector.js +227 -0
- package/dist/src/languages/javascript/assertionResolver.js +6 -4
- package/dist/src/languages/javascript/hapiDetector.d.ts +40 -0
- package/dist/src/languages/javascript/hapiDetector.d.ts.map +1 -0
- package/dist/src/languages/javascript/hapiDetector.js +174 -0
- package/dist/src/languages/javascript/mongooseDetector.d.ts +65 -0
- package/dist/src/languages/javascript/mongooseDetector.d.ts.map +1 -0
- package/dist/src/languages/javascript/mongooseDetector.js +237 -0
- package/dist/src/languages/javascript/vueDetector.d.ts +42 -0
- package/dist/src/languages/javascript/vueDetector.d.ts.map +1 -0
- package/dist/src/languages/javascript/vueDetector.js +109 -0
- package/dist/src/languages/python/index.d.ts +6 -2
- package/dist/src/languages/python/index.d.ts.map +1 -1
- package/dist/src/languages/python/index.js +200 -5
- package/dist/src/languages/python/testPatternDetector.d.ts +70 -0
- package/dist/src/languages/python/testPatternDetector.d.ts.map +1 -0
- package/dist/src/languages/python/testPatternDetector.js +201 -0
- package/dist/src/pipeline/confidence.d.ts +6 -1
- package/dist/src/pipeline/confidence.d.ts.map +1 -1
- package/dist/src/pipeline/confidence.js +8 -3
- package/dist/src/pipeline/graph.d.ts.map +1 -1
- package/dist/src/pipeline/graph.js +16 -4
- package/dist/src/pipeline/stages/ast/astStage.d.ts.map +1 -1
- package/dist/src/pipeline/stages/ast/astStage.js +51 -1
- package/dist/src/pipeline/stages/ast/baseUrlComposer.d.ts +44 -0
- package/dist/src/pipeline/stages/ast/baseUrlComposer.d.ts.map +1 -0
- package/dist/src/pipeline/stages/ast/baseUrlComposer.js +97 -0
- package/dist/src/pipeline/stages/ast/crossFileResolutionPass.d.ts +54 -0
- package/dist/src/pipeline/stages/ast/crossFileResolutionPass.d.ts.map +1 -0
- package/dist/src/pipeline/stages/ast/crossFileResolutionPass.js +88 -0
- package/dist/src/pipeline/stages/ast/crossFileResolver.d.ts.map +1 -1
- package/dist/src/pipeline/stages/ast/crossFileResolver.js +39 -1
- package/dist/src/pipeline/stages/ast/graphBuilder.d.ts.map +1 -1
- package/dist/src/pipeline/stages/ast/graphBuilder.js +81 -0
- package/dist/src/pipeline/stages/ast/optionalAuthUnifier.d.ts +41 -0
- package/dist/src/pipeline/stages/ast/optionalAuthUnifier.d.ts.map +1 -0
- package/dist/src/pipeline/stages/ast/optionalAuthUnifier.js +101 -0
- package/dist/src/pipeline/stages/ast/resolvers/angularInjectionResolver.d.ts +18 -0
- package/dist/src/pipeline/stages/ast/resolvers/angularInjectionResolver.d.ts.map +1 -0
- package/dist/src/pipeline/stages/ast/resolvers/angularInjectionResolver.js +96 -0
- package/dist/src/pipeline/stages/ast/resolvers/dddLayerResolver.d.ts +46 -0
- package/dist/src/pipeline/stages/ast/resolvers/dddLayerResolver.d.ts.map +1 -0
- package/dist/src/pipeline/stages/ast/resolvers/dddLayerResolver.js +314 -0
- package/dist/src/pipeline/stages/ast/resolvers/expressRouterResolver.d.ts +17 -0
- package/dist/src/pipeline/stages/ast/resolvers/expressRouterResolver.d.ts.map +1 -0
- package/dist/src/pipeline/stages/ast/resolvers/expressRouterResolver.js +65 -0
- package/dist/src/pipeline/stages/ast/resolvers/flaskBlueprintResolver.d.ts +17 -0
- package/dist/src/pipeline/stages/ast/resolvers/flaskBlueprintResolver.d.ts.map +1 -0
- package/dist/src/pipeline/stages/ast/resolvers/flaskBlueprintResolver.js +114 -0
- package/dist/src/pipeline/stages/ast/resolvers/mybatisResolver.d.ts +27 -0
- package/dist/src/pipeline/stages/ast/resolvers/mybatisResolver.d.ts.map +1 -0
- package/dist/src/pipeline/stages/ast/resolvers/mybatisResolver.js +130 -0
- package/dist/src/pipeline/stages/ast/resolvers/vuexActionResolver.d.ts +17 -0
- package/dist/src/pipeline/stages/ast/resolvers/vuexActionResolver.d.ts.map +1 -0
- package/dist/src/pipeline/stages/ast/resolvers/vuexActionResolver.js +80 -0
- package/dist/src/pipeline/stages/ast/rulesEnforcer.d.ts +24 -0
- package/dist/src/pipeline/stages/ast/rulesEnforcer.d.ts.map +1 -0
- package/dist/src/pipeline/stages/ast/rulesEnforcer.js +411 -0
- package/dist/src/pipeline/stages/ast/types.d.ts +114 -1
- package/dist/src/pipeline/stages/ast/types.d.ts.map +1 -1
- package/dist/src/pipeline/stages/merge/conflictDetector.d.ts +2 -0
- package/dist/src/pipeline/stages/merge/conflictDetector.d.ts.map +1 -1
- package/dist/src/pipeline/stages/merge/conflictDetector.js +54 -2
- package/dist/src/pipeline/stages/merge/coverageMappingBuilder.d.ts.map +1 -1
- package/dist/src/pipeline/stages/merge/coverageMappingBuilder.js +67 -3
- package/dist/src/pipeline/stages/tia/mockBoundaryDetector.d.ts.map +1 -1
- package/dist/src/pipeline/stages/tia/mockBoundaryDetector.js +8 -1
- package/dist/src/pipeline/stages/tia/parameterizedTestExpander.js +8 -4
- package/dist/src/pipeline/stages/tia/testLayerClassifier.d.ts.map +1 -1
- package/dist/src/pipeline/stages/tia/testLayerClassifier.js +41 -10
- package/dist/src/pipeline/types.d.ts +1 -1
- package/dist/src/pipeline/types.d.ts.map +1 -1
- package/package.json +3 -3
|
@@ -97,6 +97,8 @@ function extractJavaFunctions(root, constants) {
|
|
|
97
97
|
continue;
|
|
98
98
|
const annotations = extractAnnotationNames(method);
|
|
99
99
|
const block = (_e = (_d = method.childForFieldName) === null || _d === void 0 ? void 0 : _d.call(method, 'body')) !== null && _e !== void 0 ? _e : (0, treeSitterUtils_1.firstChildOfType)(method, 'block');
|
|
100
|
+
// Extract method parameters
|
|
101
|
+
const parameters = extractMethodParameters(method);
|
|
100
102
|
const bodyHttpCalls = [];
|
|
101
103
|
const calledFunctions = [];
|
|
102
104
|
let returnValue;
|
|
@@ -109,7 +111,7 @@ function extractJavaFunctions(root, constants) {
|
|
|
109
111
|
const cucumberPattern = extractCucumberPattern(annotations, method);
|
|
110
112
|
graph.set(name, {
|
|
111
113
|
name,
|
|
112
|
-
parameters
|
|
114
|
+
parameters,
|
|
113
115
|
bodyHttpCalls,
|
|
114
116
|
calledFunctions,
|
|
115
117
|
returnValue,
|
|
@@ -121,14 +123,44 @@ function extractJavaFunctions(root, constants) {
|
|
|
121
123
|
}
|
|
122
124
|
// ─── HTTP call extraction ─────────────────────────────────────────────────────
|
|
123
125
|
function extractJavaHttpCalls(block, constants, out) {
|
|
124
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
126
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
|
|
125
127
|
const methodInvocations = (0, treeSitterUtils_1.findNodes)(block, ['method_invocation']);
|
|
126
128
|
for (const invoc of methodInvocations) {
|
|
127
129
|
const methodName = (_e = (_c = (_b = (_a = invoc.childForFieldName) === null || _a === void 0 ? void 0 : _a.call(invoc, 'name')) === null || _b === void 0 ? void 0 : _b.text) !== null && _c !== void 0 ? _c : (_d = firstChildNamed(invoc, 'identifier')) === null || _d === void 0 ? void 0 : _d.text) !== null && _e !== void 0 ? _e : '';
|
|
128
130
|
const lowerMethod = methodName.toLowerCase();
|
|
131
|
+
// MockMvc: perform(get("/path")) — extract the inner HTTP method call
|
|
132
|
+
if (lowerMethod === 'perform') {
|
|
133
|
+
const argList = (_g = (_f = invoc.childForFieldName) === null || _f === void 0 ? void 0 : _f.call(invoc, 'arguments')) !== null && _g !== void 0 ? _g : (0, treeSitterUtils_1.firstChildOfType)(invoc, 'argument_list');
|
|
134
|
+
if (!argList)
|
|
135
|
+
continue;
|
|
136
|
+
// The argument to perform() is a method invocation like get("/path") or post("/path")
|
|
137
|
+
const innerInvocations = (0, treeSitterUtils_1.findNodes)(argList, ['method_invocation']);
|
|
138
|
+
for (const innerInvoc of innerInvocations) {
|
|
139
|
+
const innerMethodName = (_m = (_k = (_j = (_h = innerInvoc.childForFieldName) === null || _h === void 0 ? void 0 : _h.call(innerInvoc, 'name')) === null || _j === void 0 ? void 0 : _j.text) !== null && _k !== void 0 ? _k : (_l = firstChildNamed(innerInvoc, 'identifier')) === null || _l === void 0 ? void 0 : _l.text) !== null && _m !== void 0 ? _m : '';
|
|
140
|
+
const innerLower = innerMethodName.toLowerCase();
|
|
141
|
+
if (!HTTP_METHODS.has(innerLower))
|
|
142
|
+
continue;
|
|
143
|
+
const innerArgList = (_p = (_o = innerInvoc.childForFieldName) === null || _o === void 0 ? void 0 : _o.call(innerInvoc, 'arguments')) !== null && _p !== void 0 ? _p : (0, treeSitterUtils_1.firstChildOfType)(innerInvoc, 'argument_list');
|
|
144
|
+
if (!innerArgList)
|
|
145
|
+
continue;
|
|
146
|
+
const innerStringArg = findFirstStringInArgList(innerArgList, constants);
|
|
147
|
+
if (!innerStringArg)
|
|
148
|
+
continue;
|
|
149
|
+
const normalizedPath = innerStringArg.value.startsWith('/') ? (0, resolvePaths_1.normalizePathToTemplate)(innerStringArg.value) : undefined;
|
|
150
|
+
out.push({
|
|
151
|
+
method: innerLower.toUpperCase(),
|
|
152
|
+
rawPathArg: (_q = innerStringArg.varName) !== null && _q !== void 0 ? _q : innerStringArg.value,
|
|
153
|
+
resolvedPath: innerStringArg.value,
|
|
154
|
+
normalizedPath,
|
|
155
|
+
resolutionType: innerStringArg.isDirect ? 'direct' : 'constant',
|
|
156
|
+
confidence: innerStringArg.isDirect ? 'high' : 'medium',
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
129
161
|
if (!HTTP_METHODS.has(lowerMethod))
|
|
130
162
|
continue;
|
|
131
|
-
const argList = (
|
|
163
|
+
const argList = (_s = (_r = invoc.childForFieldName) === null || _r === void 0 ? void 0 : _r.call(invoc, 'arguments')) !== null && _s !== void 0 ? _s : (0, treeSitterUtils_1.firstChildOfType)(invoc, 'argument_list');
|
|
132
164
|
if (!argList)
|
|
133
165
|
continue;
|
|
134
166
|
// Extract the first string argument (path)
|
|
@@ -138,15 +170,6 @@ function extractJavaHttpCalls(block, constants, out) {
|
|
|
138
170
|
const { value: path, isDirect, varName } = firstStringArg;
|
|
139
171
|
// Try to detect the HTTP method from context
|
|
140
172
|
let httpMethod = lowerMethod.toUpperCase();
|
|
141
|
-
// RestAssured: .when().get("/path") or .given().get("/path")
|
|
142
|
-
// The method name IS the HTTP method in RestAssured style
|
|
143
|
-
if (!HTTP_METHODS.has(lowerMethod))
|
|
144
|
-
continue;
|
|
145
|
-
// MockMvc: perform(get("/path")) — perform wraps a method call
|
|
146
|
-
if (lowerMethod === 'perform') {
|
|
147
|
-
// The arg is another method invocation
|
|
148
|
-
continue; // Handled when we process the inner get/post
|
|
149
|
-
}
|
|
150
173
|
if (lowerMethod === 'request') {
|
|
151
174
|
// generic request(method, path) — try to get HTTP method from first arg
|
|
152
175
|
const stringArgs = findAllStringsInArgList(argList, constants);
|
|
@@ -224,6 +247,40 @@ function extractJavaFlowRefs(root) {
|
|
|
224
247
|
return refs;
|
|
225
248
|
}
|
|
226
249
|
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
250
|
+
/**
|
|
251
|
+
* Extract method parameters with their annotations (e.g. @RequestParam, @PathVariable, @RequestBody).
|
|
252
|
+
*/
|
|
253
|
+
function extractMethodParameters(method) {
|
|
254
|
+
var _a;
|
|
255
|
+
const params = [];
|
|
256
|
+
const formalParams = (0, treeSitterUtils_1.firstChildOfType)(method, 'formal_parameters');
|
|
257
|
+
if (!formalParams)
|
|
258
|
+
return params;
|
|
259
|
+
const paramNodes = (0, treeSitterUtils_1.findNodes)(formalParams, ['formal_parameter', 'spread_parameter']);
|
|
260
|
+
for (const paramNode of paramNodes) {
|
|
261
|
+
const paramAnnotations = [];
|
|
262
|
+
(0, treeSitterUtils_1.walkTree)(paramNode, (n) => {
|
|
263
|
+
var _a;
|
|
264
|
+
if (n.type === 'annotation' || n.type === 'marker_annotation') {
|
|
265
|
+
const nameNode = (0, treeSitterUtils_1.firstChildOfType)(n, 'identifier');
|
|
266
|
+
if (nameNode)
|
|
267
|
+
paramAnnotations.push('@' + ((_a = nameNode.text) !== null && _a !== void 0 ? _a : ''));
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
const nameNode = firstChildNamed(paramNode, 'identifier');
|
|
271
|
+
const name = (_a = nameNode === null || nameNode === void 0 ? void 0 : nameNode.text) !== null && _a !== void 0 ? _a : '';
|
|
272
|
+
if (!name)
|
|
273
|
+
continue;
|
|
274
|
+
// Include annotation prefix for annotated params (Spring @RequestParam, @PathVariable, etc.)
|
|
275
|
+
if (paramAnnotations.length > 0) {
|
|
276
|
+
params.push(`${paramAnnotations.join(' ')} ${name}`);
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
params.push(name);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return params;
|
|
283
|
+
}
|
|
227
284
|
function extractModifierTexts(node) {
|
|
228
285
|
const modifiers = [];
|
|
229
286
|
(0, treeSitterUtils_1.walkTree)(node, (n) => {
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Angular pattern detector (Feature 27, Sub-PR 7)
|
|
3
|
+
*
|
|
4
|
+
* Detects:
|
|
5
|
+
* 1. @Injectable services with HttpClient injection
|
|
6
|
+
* 2. this.http.get/post/put/delete/patch() API calls
|
|
7
|
+
* 3. inject(HttpClient) functional style
|
|
8
|
+
* 4. environment.api_url usage
|
|
9
|
+
* 5. CanActivateFn / CanActivateChildFn / CanDeactivateFn / ResolveFn guards
|
|
10
|
+
* 6. implements HttpInterceptor / HttpInterceptorFn interceptors
|
|
11
|
+
* 7. TestBed.configureTestingModule with HttpClientTestingModule
|
|
12
|
+
* 8. httpMock.expectOne(url) assertion nodes
|
|
13
|
+
* 9. signal<T>(), toSignal() patterns
|
|
14
|
+
*/
|
|
15
|
+
export interface AngularHttpCall {
|
|
16
|
+
method: string;
|
|
17
|
+
urlPattern: string;
|
|
18
|
+
usesEnvironmentUrl: boolean;
|
|
19
|
+
sourceFile: string;
|
|
20
|
+
line?: number;
|
|
21
|
+
}
|
|
22
|
+
export interface AngularInjection {
|
|
23
|
+
/** Class that receives the injection */
|
|
24
|
+
consumerClass: string;
|
|
25
|
+
/** Service class being injected */
|
|
26
|
+
serviceClass: string;
|
|
27
|
+
/** Injection style: constructor, inject function, decorator */
|
|
28
|
+
style: 'constructor' | 'inject-fn';
|
|
29
|
+
sourceFile: string;
|
|
30
|
+
line?: number;
|
|
31
|
+
}
|
|
32
|
+
export interface AngularGuard {
|
|
33
|
+
name: string;
|
|
34
|
+
type: 'functional' | 'class-based';
|
|
35
|
+
guardType: 'CanActivate' | 'CanActivateChild' | 'CanDeactivate' | 'Resolve' | 'CanMatch';
|
|
36
|
+
sourceFile: string;
|
|
37
|
+
line?: number;
|
|
38
|
+
}
|
|
39
|
+
export interface AngularInterceptor {
|
|
40
|
+
name: string;
|
|
41
|
+
type: 'class-based' | 'functional';
|
|
42
|
+
sourceFile: string;
|
|
43
|
+
line?: number;
|
|
44
|
+
}
|
|
45
|
+
export interface AngularTestSetup {
|
|
46
|
+
usesHttpClientTestingModule: boolean;
|
|
47
|
+
httpMockExpectations: string[];
|
|
48
|
+
sourceFile: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Detect Angular HttpClient API calls from source text.
|
|
52
|
+
*/
|
|
53
|
+
export declare function detectAngularHttpCalls(sourceText: string, filePath: string): AngularHttpCall[];
|
|
54
|
+
/**
|
|
55
|
+
* Detect Angular constructor injections and inject() calls.
|
|
56
|
+
*/
|
|
57
|
+
export declare function detectAngularInjections(sourceText: string, filePath: string): AngularInjection[];
|
|
58
|
+
/**
|
|
59
|
+
* Detect Angular guards (functional and class-based).
|
|
60
|
+
*/
|
|
61
|
+
export declare function detectAngularGuards(sourceText: string, filePath: string): AngularGuard[];
|
|
62
|
+
/**
|
|
63
|
+
* Detect Angular HTTP interceptors.
|
|
64
|
+
*/
|
|
65
|
+
export declare function detectAngularInterceptors(sourceText: string, filePath: string): AngularInterceptor[];
|
|
66
|
+
/**
|
|
67
|
+
* Detect Angular test setup patterns (HttpClientTestingModule, httpMock).
|
|
68
|
+
*/
|
|
69
|
+
export declare function detectAngularTestSetup(sourceText: string, filePath: string): AngularTestSetup;
|
|
70
|
+
/**
|
|
71
|
+
* Check if source text contains Angular HttpClient usage.
|
|
72
|
+
*/
|
|
73
|
+
export declare function hasAngularHttpClient(sourceText: string): boolean;
|
|
74
|
+
//# sourceMappingURL=angularDetector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"angularDetector.d.ts","sourceRoot":"","sources":["../../../../src/languages/javascript/angularDetector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,wCAAwC;IACxC,aAAa,EAAE,MAAM,CAAC;IACtB,mCAAmC;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,+DAA+D;IAC/D,KAAK,EAAE,aAAa,GAAG,WAAW,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,YAAY,GAAG,aAAa,CAAC;IACnC,SAAS,EAAE,aAAa,GAAG,kBAAkB,GAAG,eAAe,GAAG,SAAS,GAAG,UAAU,CAAC;IACzF,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,aAAa,GAAG,YAAY,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,2BAA2B,EAAE,OAAO,CAAC;IACrC,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,eAAe,EAAE,CAyC9F;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,gBAAgB,EAAE,CA2EhG;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,YAAY,EAAE,CAyCxF;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAgCpG;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAY7F;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAEhE"}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Angular pattern detector (Feature 27, Sub-PR 7)
|
|
4
|
+
*
|
|
5
|
+
* Detects:
|
|
6
|
+
* 1. @Injectable services with HttpClient injection
|
|
7
|
+
* 2. this.http.get/post/put/delete/patch() API calls
|
|
8
|
+
* 3. inject(HttpClient) functional style
|
|
9
|
+
* 4. environment.api_url usage
|
|
10
|
+
* 5. CanActivateFn / CanActivateChildFn / CanDeactivateFn / ResolveFn guards
|
|
11
|
+
* 6. implements HttpInterceptor / HttpInterceptorFn interceptors
|
|
12
|
+
* 7. TestBed.configureTestingModule with HttpClientTestingModule
|
|
13
|
+
* 8. httpMock.expectOne(url) assertion nodes
|
|
14
|
+
* 9. signal<T>(), toSignal() patterns
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.detectAngularHttpCalls = detectAngularHttpCalls;
|
|
18
|
+
exports.detectAngularInjections = detectAngularInjections;
|
|
19
|
+
exports.detectAngularGuards = detectAngularGuards;
|
|
20
|
+
exports.detectAngularInterceptors = detectAngularInterceptors;
|
|
21
|
+
exports.detectAngularTestSetup = detectAngularTestSetup;
|
|
22
|
+
exports.hasAngularHttpClient = hasAngularHttpClient;
|
|
23
|
+
/**
|
|
24
|
+
* Detect Angular HttpClient API calls from source text.
|
|
25
|
+
*/
|
|
26
|
+
function detectAngularHttpCalls(sourceText, filePath) {
|
|
27
|
+
const calls = [];
|
|
28
|
+
const lines = sourceText.split('\n');
|
|
29
|
+
// this.http.METHOD<Type>('url') or this.httpClient.METHOD('url')
|
|
30
|
+
const httpCallPattern = /this\.(?:http|httpClient)\.(get|post|put|patch|delete|head|options)(?:<[^>]+>)?\s*\(\s*[`'"]([^`'"]+)[`'"]/;
|
|
31
|
+
for (let i = 0; i < lines.length; i++) {
|
|
32
|
+
const line = lines[i];
|
|
33
|
+
const match = line.match(httpCallPattern);
|
|
34
|
+
if (match) {
|
|
35
|
+
const usesEnvironmentUrl = match[2].includes('environment') || match[2].includes('${');
|
|
36
|
+
calls.push({
|
|
37
|
+
method: match[1].toUpperCase(),
|
|
38
|
+
urlPattern: match[2],
|
|
39
|
+
usesEnvironmentUrl,
|
|
40
|
+
sourceFile: filePath,
|
|
41
|
+
line: i + 1,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// Also detect template literal calls: this.http.get(`${this.apiUrl}/articles`)
|
|
46
|
+
const templatePattern = /this\.(?:http|httpClient)\.(get|post|put|patch|delete)(?:<[^>]+>)?\s*\(\s*`([^`]+)`/g;
|
|
47
|
+
let tmatch;
|
|
48
|
+
while ((tmatch = templatePattern.exec(sourceText)) !== null) {
|
|
49
|
+
const url = tmatch[2];
|
|
50
|
+
// Avoid duplicates
|
|
51
|
+
if (!calls.some((c) => c.urlPattern === url)) {
|
|
52
|
+
const lineNum = sourceText.substring(0, tmatch.index).split('\n').length;
|
|
53
|
+
calls.push({
|
|
54
|
+
method: tmatch[1].toUpperCase(),
|
|
55
|
+
urlPattern: url,
|
|
56
|
+
usesEnvironmentUrl: url.includes('environment') || url.includes('${'),
|
|
57
|
+
sourceFile: filePath,
|
|
58
|
+
line: lineNum,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return calls;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Detect Angular constructor injections and inject() calls.
|
|
66
|
+
*/
|
|
67
|
+
function detectAngularInjections(sourceText, filePath) {
|
|
68
|
+
const injections = [];
|
|
69
|
+
const lines = sourceText.split('\n');
|
|
70
|
+
let currentClass = '';
|
|
71
|
+
let currentConstHost = '';
|
|
72
|
+
let inConstructorParams = false;
|
|
73
|
+
for (let i = 0; i < lines.length; i++) {
|
|
74
|
+
const line = lines[i];
|
|
75
|
+
// Track current class
|
|
76
|
+
const classMatch = line.match(/class\s+(\w+)/);
|
|
77
|
+
if (classMatch)
|
|
78
|
+
currentClass = classMatch[1];
|
|
79
|
+
// Track current const/export const assignment as potential host for inject() outside class.
|
|
80
|
+
// Only update when the line is NOT itself an inject() call (e.g. skip `const x = inject(Y)`).
|
|
81
|
+
if (!currentClass) {
|
|
82
|
+
const constHostMatch = line.match(/(?:export\s+)?(?:const|let)\s+(\w+)\s*(?::\s*\S+)?\s*=/);
|
|
83
|
+
if (constHostMatch && !line.match(/=\s*inject\s*\(/)) {
|
|
84
|
+
currentConstHost = constHostMatch[1];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Track constructor parameter block boundaries
|
|
88
|
+
if (/\bconstructor\s*\(/.test(line)) {
|
|
89
|
+
inConstructorParams = true;
|
|
90
|
+
}
|
|
91
|
+
if (inConstructorParams && line.includes(')')) {
|
|
92
|
+
// Process this line (it's still inside the constructor params), then close
|
|
93
|
+
// We'll close after checking for injections below
|
|
94
|
+
}
|
|
95
|
+
// Constructor injection: constructor(private http: HttpClient)
|
|
96
|
+
// Only match visibility-modified parameters when inside a constructor(...) block
|
|
97
|
+
if (inConstructorParams) {
|
|
98
|
+
const ctorParamPattern = /(?:private|protected|public|readonly)\s+(\w+)\s*:\s*(\w+)/g;
|
|
99
|
+
let ctorMatch;
|
|
100
|
+
while ((ctorMatch = ctorParamPattern.exec(line)) !== null) {
|
|
101
|
+
if (currentClass) {
|
|
102
|
+
injections.push({
|
|
103
|
+
consumerClass: currentClass,
|
|
104
|
+
serviceClass: ctorMatch[2],
|
|
105
|
+
style: 'constructor',
|
|
106
|
+
sourceFile: filePath,
|
|
107
|
+
line: i + 1,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// Close the constructor param block after processing (handles closing paren on same line)
|
|
113
|
+
if (inConstructorParams && line.includes(')')) {
|
|
114
|
+
inConstructorParams = false;
|
|
115
|
+
}
|
|
116
|
+
// Functional inject: inject(HttpClient)
|
|
117
|
+
const injectMatch = line.match(/(\w+)\s*=\s*inject\s*\(\s*(\w+)\s*\)/);
|
|
118
|
+
if (injectMatch) {
|
|
119
|
+
// Use the enclosing class, or fall back to the enclosing const/export const host
|
|
120
|
+
// (e.g. functional guards/interceptors: `export const authGuard: CanActivateFn = ...`)
|
|
121
|
+
const hostName = currentClass || currentConstHost;
|
|
122
|
+
if (hostName) {
|
|
123
|
+
injections.push({
|
|
124
|
+
consumerClass: hostName,
|
|
125
|
+
serviceClass: injectMatch[2],
|
|
126
|
+
style: 'inject-fn',
|
|
127
|
+
sourceFile: filePath,
|
|
128
|
+
line: i + 1,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return injections;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Detect Angular guards (functional and class-based).
|
|
137
|
+
*/
|
|
138
|
+
function detectAngularGuards(sourceText, filePath) {
|
|
139
|
+
var _a;
|
|
140
|
+
const guards = [];
|
|
141
|
+
const lines = sourceText.split('\n');
|
|
142
|
+
for (let i = 0; i < lines.length; i++) {
|
|
143
|
+
const line = lines[i];
|
|
144
|
+
// Functional guard: export const authGuard: CanActivateFn = ...
|
|
145
|
+
const functionalMatch = line.match(/(?:export\s+)?(?:const|let)\s+(\w+)\s*:\s*(CanActivateFn|CanActivateChildFn|CanDeactivateFn|ResolveFn|CanMatchFn)/);
|
|
146
|
+
if (functionalMatch) {
|
|
147
|
+
const guardTypeMap = {
|
|
148
|
+
CanActivateFn: 'CanActivate',
|
|
149
|
+
CanActivateChildFn: 'CanActivateChild',
|
|
150
|
+
CanDeactivateFn: 'CanDeactivate',
|
|
151
|
+
ResolveFn: 'Resolve',
|
|
152
|
+
CanMatchFn: 'CanMatch',
|
|
153
|
+
};
|
|
154
|
+
guards.push({
|
|
155
|
+
name: functionalMatch[1],
|
|
156
|
+
type: 'functional',
|
|
157
|
+
guardType: (_a = guardTypeMap[functionalMatch[2]]) !== null && _a !== void 0 ? _a : 'CanActivate',
|
|
158
|
+
sourceFile: filePath,
|
|
159
|
+
line: i + 1,
|
|
160
|
+
});
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
// Class-based guard: implements CanActivate
|
|
164
|
+
const classMatch = line.match(/class\s+(\w+).*implements\s+(?:.*,\s*)?(CanActivate|CanActivateChild|CanDeactivate|Resolve|CanMatch)/);
|
|
165
|
+
if (classMatch) {
|
|
166
|
+
guards.push({
|
|
167
|
+
name: classMatch[1],
|
|
168
|
+
type: 'class-based',
|
|
169
|
+
guardType: classMatch[2],
|
|
170
|
+
sourceFile: filePath,
|
|
171
|
+
line: i + 1,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return guards;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Detect Angular HTTP interceptors.
|
|
179
|
+
*/
|
|
180
|
+
function detectAngularInterceptors(sourceText, filePath) {
|
|
181
|
+
const interceptors = [];
|
|
182
|
+
const lines = sourceText.split('\n');
|
|
183
|
+
for (let i = 0; i < lines.length; i++) {
|
|
184
|
+
const line = lines[i];
|
|
185
|
+
// Class-based: implements HttpInterceptor
|
|
186
|
+
const classMatch = line.match(/class\s+(\w+).*implements\s+(?:.*,\s*)?HttpInterceptor/);
|
|
187
|
+
if (classMatch) {
|
|
188
|
+
interceptors.push({
|
|
189
|
+
name: classMatch[1],
|
|
190
|
+
type: 'class-based',
|
|
191
|
+
sourceFile: filePath,
|
|
192
|
+
line: i + 1,
|
|
193
|
+
});
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
// Functional: HttpInterceptorFn
|
|
197
|
+
const fnMatch = line.match(/(?:export\s+)?(?:const|let)\s+(\w+)\s*:\s*HttpInterceptorFn/);
|
|
198
|
+
if (fnMatch) {
|
|
199
|
+
interceptors.push({
|
|
200
|
+
name: fnMatch[1],
|
|
201
|
+
type: 'functional',
|
|
202
|
+
sourceFile: filePath,
|
|
203
|
+
line: i + 1,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return interceptors;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Detect Angular test setup patterns (HttpClientTestingModule, httpMock).
|
|
211
|
+
*/
|
|
212
|
+
function detectAngularTestSetup(sourceText, filePath) {
|
|
213
|
+
const usesHttpClientTestingModule = /HttpClientTestingModule|provideHttpClientTesting/.test(sourceText);
|
|
214
|
+
const httpMockExpectations = [];
|
|
215
|
+
const expectOnePattern = /httpMock\.expectOne\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
|
|
216
|
+
let match;
|
|
217
|
+
while ((match = expectOnePattern.exec(sourceText)) !== null) {
|
|
218
|
+
httpMockExpectations.push(match[1]);
|
|
219
|
+
}
|
|
220
|
+
return { usesHttpClientTestingModule, httpMockExpectations, sourceFile: filePath };
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Check if source text contains Angular HttpClient usage.
|
|
224
|
+
*/
|
|
225
|
+
function hasAngularHttpClient(sourceText) {
|
|
226
|
+
return /HttpClient|this\.http\.(get|post|put|patch|delete)/.test(sourceText);
|
|
227
|
+
}
|
|
@@ -66,8 +66,8 @@ function tryExtractAssertion(callNode) {
|
|
|
66
66
|
if (!subject)
|
|
67
67
|
return null;
|
|
68
68
|
const methodName = (_j = (_h = (_g = callee.property) === null || _g === void 0 ? void 0 : _g.name) === null || _h === void 0 ? void 0 : _h.toLowerCase()) !== null && _j !== void 0 ? _j : '';
|
|
69
|
-
const assertionType =
|
|
70
|
-
const
|
|
69
|
+
const { variable, assertionType: subjectType } = classifyExpectSubject(subject);
|
|
70
|
+
const assertionType = classifyAssertionMethod(methodName, subjectType);
|
|
71
71
|
return { assertionType, subjectVariable: variable, line: (_l = (_k = callNode.loc) === null || _k === void 0 ? void 0 : _k.start) === null || _l === void 0 ? void 0 : _l.line };
|
|
72
72
|
}
|
|
73
73
|
}
|
|
@@ -132,13 +132,15 @@ function classifyExpectSubject(subject) {
|
|
|
132
132
|
}
|
|
133
133
|
return { variable: undefined, assertionType: 'body-field' };
|
|
134
134
|
}
|
|
135
|
-
function classifyAssertionMethod(methodName) {
|
|
135
|
+
function classifyAssertionMethod(methodName, subjectType) {
|
|
136
136
|
if (methodName === 'tobe' ||
|
|
137
137
|
methodName === 'toequal' ||
|
|
138
138
|
methodName === 'tostrictequal' ||
|
|
139
139
|
methodName === 'tobetruthy' ||
|
|
140
140
|
methodName === 'tobefalsy') {
|
|
141
|
-
|
|
141
|
+
// These methods are ambiguous — use subject context to decide.
|
|
142
|
+
// Only classify as status-code when the subject involves status.
|
|
143
|
+
return subjectType === 'status-code' ? 'status-code' : 'body-field';
|
|
142
144
|
}
|
|
143
145
|
if (methodName === 'tohaveproperty' || methodName === 'tocontain' || methodName === 'tomatch') {
|
|
144
146
|
return 'body-field';
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HapiJS pattern detector (Feature 27, Sub-PR 8)
|
|
3
|
+
*
|
|
4
|
+
* Detects:
|
|
5
|
+
* 1. server.route({ method, path, options: { auth, validate, handler } })
|
|
6
|
+
* 2. Joi validator chains: Joi.string().required(), Joi.number().min().max().default()
|
|
7
|
+
* 3. auth: 'jwt' = required, auth: { mode: 'try' } = optional
|
|
8
|
+
* 4. Boom error responses: Boom.notFound(), Boom.unauthorized()
|
|
9
|
+
*/
|
|
10
|
+
export interface HapiRoute {
|
|
11
|
+
method: string;
|
|
12
|
+
path: string;
|
|
13
|
+
auth?: HapiAuth;
|
|
14
|
+
validation?: HapiValidation;
|
|
15
|
+
sourceFile: string;
|
|
16
|
+
line?: number;
|
|
17
|
+
}
|
|
18
|
+
export interface HapiAuth {
|
|
19
|
+
strategy?: string;
|
|
20
|
+
mode: 'required' | 'optional' | 'try';
|
|
21
|
+
}
|
|
22
|
+
export interface HapiValidation {
|
|
23
|
+
queryParams: string[];
|
|
24
|
+
payloadParams: string[];
|
|
25
|
+
}
|
|
26
|
+
export interface HapiBoomError {
|
|
27
|
+
errorType: string;
|
|
28
|
+
statusCode?: number;
|
|
29
|
+
sourceFile: string;
|
|
30
|
+
line?: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Detect HapiJS route definitions from source text.
|
|
34
|
+
*/
|
|
35
|
+
export declare function detectHapiRoutes(sourceText: string, filePath: string): HapiRoute[];
|
|
36
|
+
/**
|
|
37
|
+
* Detect Boom error responses.
|
|
38
|
+
*/
|
|
39
|
+
export declare function detectBoomErrors(sourceText: string, filePath: string): HapiBoomError[];
|
|
40
|
+
//# sourceMappingURL=hapiDetector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hapiDetector.d.ts","sourceRoot":"","sources":["../../../../src/languages/javascript/hapiDetector.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,UAAU,GAAG,UAAU,GAAG,KAAK,CAAC;CACvC;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAkBD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,CA6HlF;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,aAAa,EAAE,CAmBtF"}
|