api-tests-coverage 1.0.16 → 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/index.html +1 -1
- package/dist/src/discovery/fileClassifier.d.ts.map +1 -1
- package/dist/src/discovery/fileClassifier.js +15 -16
- package/dist/src/discovery/projectDiscovery.d.ts.map +1 -1
- package/dist/src/discovery/projectDiscovery.js +4 -1
- 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.map +1 -1
- package/dist/src/languages/javascript/angularDetector.js +50 -17
- package/dist/src/languages/javascript/assertionResolver.js +6 -4
- package/dist/src/languages/javascript/hapiDetector.d.ts.map +1 -1
- package/dist/src/languages/javascript/hapiDetector.js +48 -5
- package/dist/src/languages/javascript/vueDetector.d.ts +2 -0
- package/dist/src/languages/javascript/vueDetector.d.ts.map +1 -1
- package/dist/src/languages/javascript/vueDetector.js +22 -0
- package/dist/src/languages/python/index.d.ts +1 -1
- package/dist/src/languages/python/index.d.ts.map +1 -1
- package/dist/src/languages/python/index.js +33 -3
- 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 +46 -2
- package/dist/src/pipeline/stages/ast/baseUrlComposer.d.ts.map +1 -1
- package/dist/src/pipeline/stages/ast/baseUrlComposer.js +18 -4
- package/dist/src/pipeline/stages/ast/crossFileResolver.js +29 -0
- 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 +3 -1
- package/dist/src/pipeline/stages/ast/optionalAuthUnifier.d.ts.map +1 -1
- package/dist/src/pipeline/stages/ast/optionalAuthUnifier.js +34 -14
- package/dist/src/pipeline/stages/ast/resolvers/angularInjectionResolver.d.ts.map +1 -1
- package/dist/src/pipeline/stages/ast/resolvers/angularInjectionResolver.js +22 -3
- package/dist/src/pipeline/stages/ast/resolvers/dddLayerResolver.d.ts.map +1 -1
- package/dist/src/pipeline/stages/ast/resolvers/dddLayerResolver.js +104 -28
- package/dist/src/pipeline/stages/ast/resolvers/mybatisResolver.d.ts.map +1 -1
- package/dist/src/pipeline/stages/ast/resolvers/mybatisResolver.js +56 -0
- package/dist/src/pipeline/stages/ast/resolvers/vuexActionResolver.d.ts.map +1 -1
- package/dist/src/pipeline/stages/ast/resolvers/vuexActionResolver.js +43 -18
- package/dist/src/pipeline/stages/ast/rulesEnforcer.d.ts.map +1 -1
- package/dist/src/pipeline/stages/ast/rulesEnforcer.js +336 -45
- 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 +36 -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
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>API Coverage Analyzer Dashboard</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-D3_88Gr5.js"></script>
|
|
9
9
|
<link rel="stylesheet" crossorigin href="/assets/index-D_begBP0.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fileClassifier.d.ts","sourceRoot":"","sources":["../../../src/discovery/fileClassifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,MAAM,MAAM,YAAY,GACpB,eAAe,GACf,cAAc,GACd,WAAW,GACX,eAAe,GACf,WAAW,GACX,aAAa,GACb,kBAAkB,GAClB,eAAe,GACf,UAAU,GACV,SAAS,CAAC;AAEd,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,YAAY,CAAC;IACvB,4DAA4D;IAC5D,kBAAkB,EAAE,OAAO,CAAC;CAC7B;AAED,4DAA4D;AAC5D,eAAO,MAAM,4BAA4B,EAAE,WAAW,CAAC,YAAY,CAGjE,CAAC;
|
|
1
|
+
{"version":3,"file":"fileClassifier.d.ts","sourceRoot":"","sources":["../../../src/discovery/fileClassifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,MAAM,MAAM,YAAY,GACpB,eAAe,GACf,cAAc,GACd,WAAW,GACX,eAAe,GACf,WAAW,GACX,aAAa,GACb,kBAAkB,GAClB,eAAe,GACf,UAAU,GACV,SAAS,CAAC;AAEd,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,YAAY,CAAC;IACvB,4DAA4D;IAC5D,kBAAkB,EAAE,OAAO,CAAC;CAC7B;AAED,4DAA4D;AAC5D,eAAO,MAAM,4BAA4B,EAAE,WAAW,CAAC,YAAY,CAGjE,CAAC;AA0EH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,CAkE7D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,cAAc,EAAE,CAEnE;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,cAAc,EAAE,CAEhF;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAapD"}
|
|
@@ -96,16 +96,25 @@ const CONFIG_BASENAMES = new Set([
|
|
|
96
96
|
'.eslintrc.yaml',
|
|
97
97
|
'.eslintrc.yml',
|
|
98
98
|
]);
|
|
99
|
-
/** Extensions mapped directly to a category */
|
|
100
|
-
const EXTENSION_MAP = {
|
|
101
|
-
'.feature': 'bdd_scenarios',
|
|
102
|
-
};
|
|
103
99
|
/** Glob-style path-segment patterns for security reports */
|
|
104
100
|
const SECURITY_REPORT_DIRS = ['zap', 'trivy', 'semgrep'];
|
|
105
101
|
/** Glob-style path-segment patterns for performance artifacts */
|
|
106
102
|
const PERFORMANCE_DIRS = ['jmeter', 'k6', 'gatling', 'locust'];
|
|
107
|
-
/**
|
|
108
|
-
const
|
|
103
|
+
/** Source code file extensions for service code classification */
|
|
104
|
+
const SERVICE_CODE_EXTS = new Set([
|
|
105
|
+
'.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs',
|
|
106
|
+
'.java', '.kt', '.kts',
|
|
107
|
+
'.py',
|
|
108
|
+
'.rb',
|
|
109
|
+
'.go',
|
|
110
|
+
'.cs',
|
|
111
|
+
'.cpp', '.cc', '.h',
|
|
112
|
+
'.rs',
|
|
113
|
+
'.php',
|
|
114
|
+
'.graphql', '.graphqls',
|
|
115
|
+
'.vue',
|
|
116
|
+
'.xml',
|
|
117
|
+
]);
|
|
109
118
|
// ─── Core classifier ─────────────────────────────────────────────────────────
|
|
110
119
|
/**
|
|
111
120
|
* Classify a single file path into a `FileCategory`.
|
|
@@ -173,16 +182,6 @@ function classifyFile(filePath) {
|
|
|
173
182
|
return make(filePath, 'test_code');
|
|
174
183
|
}
|
|
175
184
|
// 9. Service code by extension
|
|
176
|
-
const SERVICE_CODE_EXTS = new Set([
|
|
177
|
-
'.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs',
|
|
178
|
-
'.java', '.kt', '.kts',
|
|
179
|
-
'.py',
|
|
180
|
-
'.rb',
|
|
181
|
-
'.go',
|
|
182
|
-
'.cs',
|
|
183
|
-
'.cpp', '.cc', '.h',
|
|
184
|
-
'.rs',
|
|
185
|
-
]);
|
|
186
185
|
if (SERVICE_CODE_EXTS.has(ext)) {
|
|
187
186
|
return make(filePath, 'service_code');
|
|
188
187
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"projectDiscovery.d.ts","sourceRoot":"","sources":["../../../src/discovery/projectDiscovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,EAAgB,cAAc,EAAgB,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EAAuB,oBAAoB,EAAoB,MAAM,qBAAqB,CAAC;AAElG,YAAY,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAIlF,MAAM,MAAM,gBAAgB,GACxB,MAAM,GACN,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,YAAY,GACZ,YAAY,GACZ,IAAI,GACJ,QAAQ,CAAC;AAEb,MAAM,MAAM,iBAAiB,GAEzB,OAAO,GACP,QAAQ,GACR,aAAa,GAEb,QAAQ,GACR,UAAU,GAEV,OAAO,GACP,UAAU,GAEV,MAAM,GACN,OAAO,GACP,SAAS,GACT,YAAY,GAEZ,UAAU,GACV,SAAS,CAAC;AAEd,MAAM,WAAW,mBAAmB;IAClC,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,gDAAgD;IAChD,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,yCAAyC;IACzC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,oDAAoD;IACpD,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,qCAAqC;IACrC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,kCAAkC;IAClC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,uDAAuD;IACvD,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,iCAAiC;IACjC,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,gCAAgC;IAChC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,qCAAqC;IACrC,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAC9B,+BAA+B;IAC/B,UAAU,EAAE,iBAAiB,EAAE,CAAC;IAChC,0EAA0E;IAC1E,aAAa,EAAE,oBAAoB,EAAE,CAAC;IACtC,8EAA8E;IAC9E,eAAe,EAAE,MAAM,CAAC,MAAM,IAAI,CAAC,mBAAmB,EAAE,UAAU,GAAG,aAAa,GAAG,iBAAiB,GAAG,WAAW,GAAG,YAAY,GAAG,eAAe,CAAC,EAAE,UAAU,GAAG,YAAY,CAAC,CAAC;CACpL;AAED,MAAM,WAAW,gBAAgB;IAC/B,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,uDAAuD;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,uEAAuE;IACvE,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,2EAA2E;IAC3E,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;
|
|
1
|
+
{"version":3,"file":"projectDiscovery.d.ts","sourceRoot":"","sources":["../../../src/discovery/projectDiscovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,EAAgB,cAAc,EAAgB,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EAAuB,oBAAoB,EAAoB,MAAM,qBAAqB,CAAC;AAElG,YAAY,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAIlF,MAAM,MAAM,gBAAgB,GACxB,MAAM,GACN,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,YAAY,GACZ,YAAY,GACZ,IAAI,GACJ,QAAQ,CAAC;AAEb,MAAM,MAAM,iBAAiB,GAEzB,OAAO,GACP,QAAQ,GACR,aAAa,GAEb,QAAQ,GACR,UAAU,GAEV,OAAO,GACP,UAAU,GAEV,MAAM,GACN,OAAO,GACP,SAAS,GACT,YAAY,GAEZ,UAAU,GACV,SAAS,CAAC;AAEd,MAAM,WAAW,mBAAmB;IAClC,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,gDAAgD;IAChD,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,yCAAyC;IACzC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,oDAAoD;IACpD,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,qCAAqC;IACrC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,kCAAkC;IAClC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,uDAAuD;IACvD,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,iCAAiC;IACjC,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,gCAAgC;IAChC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,qCAAqC;IACrC,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAC9B,+BAA+B;IAC/B,UAAU,EAAE,iBAAiB,EAAE,CAAC;IAChC,0EAA0E;IAC1E,aAAa,EAAE,oBAAoB,EAAE,CAAC;IACtC,8EAA8E;IAC9E,eAAe,EAAE,MAAM,CAAC,MAAM,IAAI,CAAC,mBAAmB,EAAE,UAAU,GAAG,aAAa,GAAG,iBAAiB,GAAG,WAAW,GAAG,YAAY,GAAG,eAAe,CAAC,EAAE,UAAU,GAAG,YAAY,CAAC,CAAC;CACpL;AAED,MAAM,WAAW,gBAAgB;IAC/B,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,uDAAuD;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,uEAAuE;IACvE,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,2EAA2E;IAC3E,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;AAgHD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,GAAE,gBAAqB,GAAG,mBAAmB,CAkFnF"}
|
|
@@ -81,7 +81,6 @@ const BUILD_FILE_LANGUAGE_MAP = {
|
|
|
81
81
|
'pyproject.toml': ['python'],
|
|
82
82
|
'Gemfile': ['ruby'],
|
|
83
83
|
'go.mod': ['go'],
|
|
84
|
-
'*.csproj': ['csharp'],
|
|
85
84
|
};
|
|
86
85
|
const EXTENSION_LANGUAGE_MAP = {
|
|
87
86
|
'.java': 'java',
|
|
@@ -250,6 +249,10 @@ function detectLanguages(filePaths) {
|
|
|
250
249
|
if (byBuild) {
|
|
251
250
|
byBuild.forEach((lang) => found.add(lang));
|
|
252
251
|
}
|
|
252
|
+
// Check for .csproj files (basename varies, so match by extension)
|
|
253
|
+
if (basename.endsWith('.csproj')) {
|
|
254
|
+
found.add('csharp');
|
|
255
|
+
}
|
|
253
256
|
// Check extension mapping
|
|
254
257
|
const byExt = EXTENSION_LANGUAGE_MAP[ext];
|
|
255
258
|
if (byExt) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"semanticBuilder.d.ts","sourceRoot":"","sources":["../../../../src/languages/java/semanticBuilder.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAEV,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,OAAO,EACR,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAKL,KAAK,MAAM,EACZ,MAAM,2BAA2B,CAAC;AAOnC,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,GACX;IAAE,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CAAE,CAmErF;AAID,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,GACrC,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,
|
|
1
|
+
{"version":3,"file":"semanticBuilder.d.ts","sourceRoot":"","sources":["../../../../src/languages/java/semanticBuilder.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAEV,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,OAAO,EACR,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAKL,KAAK,MAAM,EACZ,MAAM,2BAA2B,CAAC;AAOnC,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,GACX;IAAE,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CAAE,CAmErF;AAID,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,GACrC,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAwC/B;AAID,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,EACtC,GAAG,EAAE,gBAAgB,EAAE,GACtB,IAAI,CA+EN;AAID,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,EAAE,CAyBvE;AAID,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,EAAE,CAa3E;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,CAa3D"}
|
|
@@ -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) => {
|
|
@@ -1 +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,
|
|
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"}
|
|
@@ -68,33 +68,66 @@ function detectAngularInjections(sourceText, filePath) {
|
|
|
68
68
|
const injections = [];
|
|
69
69
|
const lines = sourceText.split('\n');
|
|
70
70
|
let currentClass = '';
|
|
71
|
+
let currentConstHost = '';
|
|
72
|
+
let inConstructorParams = false;
|
|
71
73
|
for (let i = 0; i < lines.length; i++) {
|
|
72
74
|
const line = lines[i];
|
|
73
75
|
// Track current class
|
|
74
76
|
const classMatch = line.match(/class\s+(\w+)/);
|
|
75
77
|
if (classMatch)
|
|
76
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
|
+
}
|
|
77
95
|
// Constructor injection: constructor(private http: HttpClient)
|
|
78
|
-
|
|
79
|
-
if (
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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;
|
|
87
115
|
}
|
|
88
116
|
// Functional inject: inject(HttpClient)
|
|
89
117
|
const injectMatch = line.match(/(\w+)\s*=\s*inject\s*\(\s*(\w+)\s*\)/);
|
|
90
|
-
if (injectMatch
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
+
}
|
|
98
131
|
}
|
|
99
132
|
}
|
|
100
133
|
return injections;
|
|
@@ -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';
|
|
@@ -1 +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,
|
|
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"}
|
|
@@ -30,7 +30,7 @@ const BOOM_STATUS_MAP = {
|
|
|
30
30
|
* Detect HapiJS route definitions from source text.
|
|
31
31
|
*/
|
|
32
32
|
function detectHapiRoutes(sourceText, filePath) {
|
|
33
|
-
var _a, _b;
|
|
33
|
+
var _a, _b, _c, _d;
|
|
34
34
|
const routes = [];
|
|
35
35
|
const lines = sourceText.split('\n');
|
|
36
36
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -55,7 +55,7 @@ function detectHapiRoutes(sourceText, filePath) {
|
|
|
55
55
|
if (authSimple) {
|
|
56
56
|
auth = { strategy: authSimple[1], mode: 'required' };
|
|
57
57
|
}
|
|
58
|
-
// auth: { mode: 'try' } or auth: { mode: 'optional' }
|
|
58
|
+
// auth: { mode: 'try' } or auth: { mode: 'optional' } — single-line case
|
|
59
59
|
const authMode = nearLine.match(/(?:auth|mode)\s*:\s*[{]?\s*mode\s*:\s*['"](\w+)['"]/);
|
|
60
60
|
if (authMode) {
|
|
61
61
|
auth = {
|
|
@@ -63,6 +63,19 @@ function detectHapiRoutes(sourceText, filePath) {
|
|
|
63
63
|
mode: authMode[1] === 'try' || authMode[1] === 'optional' ? 'optional' : 'required',
|
|
64
64
|
};
|
|
65
65
|
}
|
|
66
|
+
else if (/auth\s*:\s*\{/.test(nearLine) || /auth\s*\{/.test(nearLine)) {
|
|
67
|
+
// Multi-line auth object: scan the next 5 lines for mode: 'try' / 'optional'
|
|
68
|
+
for (let m = j + 1; m < Math.min(j + 6, lines.length); m++) {
|
|
69
|
+
const modeMatch = lines[m].match(/mode\s*:\s*['"](\w+)['"]/);
|
|
70
|
+
if (modeMatch) {
|
|
71
|
+
auth = {
|
|
72
|
+
strategy: auth === null || auth === void 0 ? void 0 : auth.strategy,
|
|
73
|
+
mode: modeMatch[1] === 'try' || modeMatch[1] === 'optional' ? 'optional' : 'required',
|
|
74
|
+
};
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
66
79
|
// auth: false
|
|
67
80
|
if (/auth\s*:\s*false/.test(nearLine)) {
|
|
68
81
|
auth = undefined; // Public route
|
|
@@ -74,8 +87,9 @@ function detectHapiRoutes(sourceText, filePath) {
|
|
|
74
87
|
// Scan validation block
|
|
75
88
|
for (let k = j; k < Math.min(j + 15, lines.length); k++) {
|
|
76
89
|
const valLine = lines[k];
|
|
77
|
-
// query: { ... }
|
|
90
|
+
// query: { ... } — extract Joi fields from same line and subsequent lines
|
|
78
91
|
if (/query\s*:/.test(valLine)) {
|
|
92
|
+
// Same-line extraction
|
|
79
93
|
const paramMatch = valLine.match(/(\w+)\s*:\s*Joi\./g);
|
|
80
94
|
if (paramMatch) {
|
|
81
95
|
for (const pm of paramMatch) {
|
|
@@ -84,17 +98,46 @@ function detectHapiRoutes(sourceText, filePath) {
|
|
|
84
98
|
queryParams.push(name);
|
|
85
99
|
}
|
|
86
100
|
}
|
|
101
|
+
// Multi-line extraction: scan subsequent lines for fieldName: Joi. patterns
|
|
102
|
+
for (let q = k + 1; q < Math.min(k + 16, lines.length); q++) {
|
|
103
|
+
const subLine = lines[q];
|
|
104
|
+
if (/}\s*[),]/.test(subLine) || /]\s*,/.test(subLine))
|
|
105
|
+
break;
|
|
106
|
+
const fieldMatch = subLine.match(/(\w+)\s*:\s*Joi\./g);
|
|
107
|
+
if (fieldMatch) {
|
|
108
|
+
for (const fm of fieldMatch) {
|
|
109
|
+
const name = (_b = fm.match(/(\w+)\s*:/)) === null || _b === void 0 ? void 0 : _b[1];
|
|
110
|
+
if (name && !queryParams.includes(name))
|
|
111
|
+
queryParams.push(name);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
87
115
|
}
|
|
88
|
-
// payload: { ... }
|
|
116
|
+
// payload: { ... } — extract Joi fields from same line and subsequent lines
|
|
89
117
|
if (/payload\s*:/.test(valLine)) {
|
|
118
|
+
// Same-line extraction
|
|
90
119
|
const paramMatch = valLine.match(/(\w+)\s*:\s*Joi\./g);
|
|
91
120
|
if (paramMatch) {
|
|
92
121
|
for (const pm of paramMatch) {
|
|
93
|
-
const name = (
|
|
122
|
+
const name = (_c = pm.match(/(\w+)\s*:/)) === null || _c === void 0 ? void 0 : _c[1];
|
|
94
123
|
if (name)
|
|
95
124
|
payloadParams.push(name);
|
|
96
125
|
}
|
|
97
126
|
}
|
|
127
|
+
// Multi-line extraction: scan subsequent lines for fieldName: Joi. patterns
|
|
128
|
+
for (let q = k + 1; q < Math.min(k + 16, lines.length); q++) {
|
|
129
|
+
const subLine = lines[q];
|
|
130
|
+
if (/}\s*[),]/.test(subLine) || /]\s*,/.test(subLine))
|
|
131
|
+
break;
|
|
132
|
+
const fieldMatch = subLine.match(/(\w+)\s*:\s*Joi\./g);
|
|
133
|
+
if (fieldMatch) {
|
|
134
|
+
for (const fm of fieldMatch) {
|
|
135
|
+
const name = (_d = fm.match(/(\w+)\s*:/)) === null || _d === void 0 ? void 0 : _d[1];
|
|
136
|
+
if (name && !payloadParams.includes(name))
|
|
137
|
+
payloadParams.push(name);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
98
141
|
}
|
|
99
142
|
}
|
|
100
143
|
if (queryParams.length > 0 || payloadParams.length > 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vueDetector.d.ts","sourceRoot":"","sources":["../../../../src/languages/javascript/vueDetector.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,UAAU;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAO/F;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,EAAE,
|
|
1
|
+
{"version":3,"file":"vueDetector.d.ts","sourceRoot":"","sources":["../../../../src/languages/javascript/vueDetector.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,UAAU;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mGAAmG;IACnG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAO/F;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,EAAE,CAoEpF;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,YAAY,EAAE,CAkBzF"}
|
|
@@ -29,7 +29,20 @@ function detectAxiosBaseUrl(sourceText, filePath) {
|
|
|
29
29
|
function detectVuexActions(sourceText, filePath) {
|
|
30
30
|
const calls = [];
|
|
31
31
|
const lines = sourceText.split('\n');
|
|
32
|
+
// Pass 1: Build a map of constant name → string value.
|
|
33
|
+
// Matches patterns like:
|
|
34
|
+
// const FETCH_ARTICLES = 'fetchArticles'
|
|
35
|
+
// export const FETCH_ARTICLES = "fetchArticles"
|
|
36
|
+
const constantMap = new Map();
|
|
37
|
+
for (const line of lines) {
|
|
38
|
+
const constMatch = line.match(/(?:export\s+)?const\s+([A-Z_][A-Z0-9_]*)\s*=\s*['"]([^'"]+)['"]/);
|
|
39
|
+
if (constMatch) {
|
|
40
|
+
constantMap.set(constMatch[1], constMatch[2]);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// Pass 2: Detect action definitions and their API calls.
|
|
32
44
|
let currentAction = '';
|
|
45
|
+
let currentResolvedName;
|
|
33
46
|
for (let i = 0; i < lines.length; i++) {
|
|
34
47
|
const line = lines[i];
|
|
35
48
|
// Vuex action definition: [ACTION_NAME]({ commit }, payload) {
|
|
@@ -37,6 +50,13 @@ function detectVuexActions(sourceText, filePath) {
|
|
|
37
50
|
const actionMatch = line.match(/(?:\[(\w+)\]|(\w+))\s*\(\s*\{\s*(?:commit|dispatch|state|getters|rootState)/);
|
|
38
51
|
if (actionMatch) {
|
|
39
52
|
currentAction = actionMatch[1] || actionMatch[2] || '';
|
|
53
|
+
// If using bracket syntax with a constant, resolve to its string value
|
|
54
|
+
if (actionMatch[1] && constantMap.has(actionMatch[1])) {
|
|
55
|
+
currentResolvedName = constantMap.get(actionMatch[1]);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
currentResolvedName = undefined;
|
|
59
|
+
}
|
|
40
60
|
continue;
|
|
41
61
|
}
|
|
42
62
|
// ApiService.get/post/put/delete('resource')
|
|
@@ -44,6 +64,7 @@ function detectVuexActions(sourceText, filePath) {
|
|
|
44
64
|
if (apiServiceMatch) {
|
|
45
65
|
calls.push({
|
|
46
66
|
actionName: currentAction || undefined,
|
|
67
|
+
resolvedName: currentResolvedName,
|
|
47
68
|
method: apiServiceMatch[1].toUpperCase(),
|
|
48
69
|
urlPattern: apiServiceMatch[2],
|
|
49
70
|
sourceFile: filePath,
|
|
@@ -56,6 +77,7 @@ function detectVuexActions(sourceText, filePath) {
|
|
|
56
77
|
if (axiosMatch) {
|
|
57
78
|
calls.push({
|
|
58
79
|
actionName: currentAction || undefined,
|
|
80
|
+
resolvedName: currentResolvedName,
|
|
59
81
|
method: axiosMatch[1].toUpperCase(),
|
|
60
82
|
urlPattern: axiosMatch[2],
|
|
61
83
|
sourceFile: filePath,
|
|
@@ -10,7 +10,7 @@ export declare class PythonAnalyzer implements LanguageAnalyzer {
|
|
|
10
10
|
extractHttpInteractions(model: SemanticModel, _ctx: AnalysisContext): ResolvedHttpInteraction[];
|
|
11
11
|
extractAssertions(model: SemanticModel): SemanticAssertion[];
|
|
12
12
|
extractBusinessRuleRefs(model: SemanticModel): BusinessRuleRef[];
|
|
13
|
-
extractFlowRefs(
|
|
13
|
+
extractFlowRefs(model: SemanticModel): FlowRef[];
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
16
|
* Classify JWT/auth security from decorator information.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/languages/python/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EAIb,uBAAuB,EACvB,iBAAiB,EACjB,eAAe,EACf,OAAO,EACP,eAAe,EAEf,aAAa,EAEb,sBAAsB,EACvB,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/languages/python/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EAIb,uBAAuB,EACvB,iBAAiB,EACjB,eAAe,EACf,OAAO,EACP,eAAe,EAEf,aAAa,EAEb,sBAAsB,EACvB,MAAM,oBAAoB,CAAC;AAmC5B,qBAAa,cAAe,YAAW,gBAAgB;IACrD,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAY;IAEhD,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,gBAAgB;IAc1D,kBAAkB,CAAC,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,eAAe,GAAG,aAAa;IA4CtF,uBAAuB,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,eAAe,GAAG,uBAAuB,EAAE;IAwB/F,iBAAiB,CAAC,KAAK,EAAE,aAAa,GAAG,iBAAiB,EAAE;IAG5D,uBAAuB,CAAC,KAAK,EAAE,aAAa,GAAG,eAAe,EAAE;IAGhE,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,EAAE;CAqBjD;AA2WD;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,aAAa,EAAE,GAAG,sBAAsB,GAAG,SAAS,CAarG"}
|
|
@@ -8,6 +8,7 @@ exports.classifyFlaskSecurity = classifyFlaskSecurity;
|
|
|
8
8
|
const parserRegistry_1 = require("../../ast/parserRegistry");
|
|
9
9
|
const treeSitterUtils_1 = require("../shared/treeSitterUtils");
|
|
10
10
|
const resolvePaths_1 = require("../../coverage/deep-analysis/resolvePaths");
|
|
11
|
+
const testPatternDetector_1 = require("./testPatternDetector");
|
|
11
12
|
// ─── Parser ───────────────────────────────────────────────────────────────────
|
|
12
13
|
let cachedParser = undefined;
|
|
13
14
|
let parserLoaded = false;
|
|
@@ -45,7 +46,7 @@ class PythonAnalyzer {
|
|
|
45
46
|
}
|
|
46
47
|
}
|
|
47
48
|
buildSemanticModel(parsed, _context) {
|
|
48
|
-
var _a, _b;
|
|
49
|
+
var _a, _b, _c, _d;
|
|
49
50
|
const root = (_b = (_a = parsed.ast) === null || _a === void 0 ? void 0 : _a.rootNode) !== null && _b !== void 0 ? _b : parsed.ast;
|
|
50
51
|
if (!root)
|
|
51
52
|
return emptyModel(parsed.filePath);
|
|
@@ -56,6 +57,20 @@ class PythonAnalyzer {
|
|
|
56
57
|
// Feature 27: Flask/FastAPI pattern detection
|
|
57
58
|
const decoratorStacks = extractFlaskDecoratorStacks(root, parsed.filePath);
|
|
58
59
|
const routeRegistrations = extractFlaskRouteRegistrations(root, parsed.filePath);
|
|
60
|
+
// Feature 27: webtest API call detection
|
|
61
|
+
const sourceText = (_d = (_c = root.text) !== null && _c !== void 0 ? _c : parsed.content) !== null && _d !== void 0 ? _d : '';
|
|
62
|
+
const webtestCalls = (0, testPatternDetector_1.detectWebtestCalls)(sourceText, parsed.filePath);
|
|
63
|
+
if (webtestCalls.length > 0) {
|
|
64
|
+
const httpCalls = (0, testPatternDetector_1.webtestCallsToHttpCalls)(webtestCalls);
|
|
65
|
+
// Merge webtest HTTP calls into the functions map under a synthetic entry
|
|
66
|
+
const webtestFunc = {
|
|
67
|
+
name: '__webtest_calls__',
|
|
68
|
+
parameters: [],
|
|
69
|
+
bodyHttpCalls: httpCalls,
|
|
70
|
+
calledFunctions: [],
|
|
71
|
+
};
|
|
72
|
+
functions.set('__webtest_calls__', webtestFunc);
|
|
73
|
+
}
|
|
59
74
|
return {
|
|
60
75
|
filePath: parsed.filePath,
|
|
61
76
|
language: 'python',
|
|
@@ -103,8 +118,23 @@ class PythonAnalyzer {
|
|
|
103
118
|
extractBusinessRuleRefs(model) {
|
|
104
119
|
return model.businessRuleRefs;
|
|
105
120
|
}
|
|
106
|
-
extractFlowRefs(
|
|
107
|
-
|
|
121
|
+
extractFlowRefs(model) {
|
|
122
|
+
// Feature 27: Resolve pytest fixture chains
|
|
123
|
+
const refs = [...model.flowRefs];
|
|
124
|
+
// Use function parameter names to discover fixture dependencies
|
|
125
|
+
// In pytest, function parameters that aren't built-in fixtures are fixture references
|
|
126
|
+
const builtinFixtures = new Set([
|
|
127
|
+
'request', 'tmp_path', 'tmpdir', 'capsys', 'capfd', 'monkeypatch',
|
|
128
|
+
'pytestconfig', 'recwarn', 'caplog', 'cache', 'self',
|
|
129
|
+
]);
|
|
130
|
+
for (const [funcName, func] of model.functions) {
|
|
131
|
+
for (const param of func.parameters) {
|
|
132
|
+
if (param && !builtinFixtures.has(param)) {
|
|
133
|
+
refs.push({ flowId: `fixture:${param}`, source: 'tag' });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return refs;
|
|
108
138
|
}
|
|
109
139
|
}
|
|
110
140
|
exports.PythonAnalyzer = PythonAnalyzer;
|
|
@@ -65,6 +65,11 @@ export declare function hasPartialResolution(graph: CoverageKnowledgeGraph, path
|
|
|
65
65
|
/**
|
|
66
66
|
* Build a ConfidenceEvidence object from a coverage path in the graph.
|
|
67
67
|
* Inspects the nodes and their source stages to determine what evidence exists.
|
|
68
|
+
*
|
|
69
|
+
* Optional `iastConfirmed` and `dastConfirmed` flags allow the caller to
|
|
70
|
+
* inject runtime confirmation state that may not be discoverable by walking
|
|
71
|
+
* `pathNodeIds` alone (e.g. when IAST/DAST nodes are not directly on the
|
|
72
|
+
* endpoint→test path in the graph).
|
|
68
73
|
*/
|
|
69
|
-
export declare function buildConfidenceEvidence(graph: CoverageKnowledgeGraph, pathNodeIds: string[], isStaticOnlyMode: boolean): ConfidenceEvidence;
|
|
74
|
+
export declare function buildConfidenceEvidence(graph: CoverageKnowledgeGraph, pathNodeIds: string[], isStaticOnlyMode: boolean, iastConfirmed?: boolean, dastConfirmed?: boolean): ConfidenceEvidence;
|
|
70
75
|
//# sourceMappingURL=confidence.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"confidence.d.ts","sourceRoot":"","sources":["../../../src/pipeline/confidence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAa,MAAM,SAAS,CAAC;AACjF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAYtD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,kBAAkB,CAwBtF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,kBAAkB,EAC7B,QAAQ,EAAE,kBAAkB,GAC3B,kBAAkB,CAoBpB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,kBAAkB,CAGlF;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,kBAAkB,EAC3B,QAAQ,EAAE,kBAAkB,GAC3B,kBAAkB,CAIpB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,CAAC,EAAE,kBAAkB,EACrB,CAAC,EAAE,kBAAkB,GACpB,MAAM,CAER;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,kBAAkB,EAAE,GAAG,kBAAkB,CAS9E;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,sBAAsB,EAC7B,WAAW,EAAE,MAAM,EAAE,GACpB,OAAO,CAMT;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,sBAAsB,EAC7B,WAAW,EAAE,MAAM,EAAE,GACpB,OAAO,CAMT;AAED
|
|
1
|
+
{"version":3,"file":"confidence.d.ts","sourceRoot":"","sources":["../../../src/pipeline/confidence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAa,MAAM,SAAS,CAAC;AACjF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAYtD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,kBAAkB,CAwBtF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,kBAAkB,EAC7B,QAAQ,EAAE,kBAAkB,GAC3B,kBAAkB,CAoBpB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,kBAAkB,CAGlF;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,kBAAkB,EAC3B,QAAQ,EAAE,kBAAkB,GAC3B,kBAAkB,CAIpB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,CAAC,EAAE,kBAAkB,EACrB,CAAC,EAAE,kBAAkB,GACpB,MAAM,CAER;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,kBAAkB,EAAE,GAAG,kBAAkB,CAS9E;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,sBAAsB,EAC7B,WAAW,EAAE,MAAM,EAAE,GACpB,OAAO,CAMT;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,sBAAsB,EAC7B,WAAW,EAAE,MAAM,EAAE,GACpB,OAAO,CAMT;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,sBAAsB,EAC7B,WAAW,EAAE,MAAM,EAAE,EACrB,gBAAgB,EAAE,OAAO,EACzB,aAAa,CAAC,EAAE,OAAO,EACvB,aAAa,CAAC,EAAE,OAAO,GACtB,kBAAkB,CA6CpB"}
|