api-tests-coverage 1.0.21 → 1.0.23
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-BKGHUeDJ.js +1 -0
- package/dist/dashboard/dist/assets/_basePickBy-BamgEusj.js +1 -0
- package/dist/dashboard/dist/assets/_baseUniq-BBhq12Ja.js +1 -0
- package/dist/dashboard/dist/assets/_baseUniq-CtA-DQF7.js +1 -0
- package/dist/dashboard/dist/assets/arc-CbXP3Mc9.js +1 -0
- package/dist/dashboard/dist/assets/arc-Dh-qL1ea.js +1 -0
- package/dist/dashboard/dist/assets/architectureDiagram-VXUJARFQ-BxQ_anmt.js +36 -0
- package/dist/dashboard/dist/assets/architectureDiagram-VXUJARFQ-n7QxasMM.js +36 -0
- package/dist/dashboard/dist/assets/blockDiagram-VD42YOAC-Krm3lc7z.js +122 -0
- package/dist/dashboard/dist/assets/blockDiagram-VD42YOAC-MXnGwKRn.js +122 -0
- package/dist/dashboard/dist/assets/c4Diagram-YG6GDRKO-B3yZ5P9k.js +10 -0
- package/dist/dashboard/dist/assets/c4Diagram-YG6GDRKO-Cfd4OeWg.js +10 -0
- package/dist/dashboard/dist/assets/channel-C7QwX7U8.js +1 -0
- package/dist/dashboard/dist/assets/channel-a4t2URTe.js +1 -0
- package/dist/dashboard/dist/assets/chunk-4BX2VUAB-Dw3El5KF.js +1 -0
- package/dist/dashboard/dist/assets/chunk-4BX2VUAB-ljBQ5lHA.js +1 -0
- package/dist/dashboard/dist/assets/chunk-55IACEB6-Cikrdc3Q.js +1 -0
- package/dist/dashboard/dist/assets/chunk-55IACEB6-T8Jf00IL.js +1 -0
- package/dist/dashboard/dist/assets/chunk-B4BG7PRW-DAJalKYJ.js +165 -0
- package/dist/dashboard/dist/assets/chunk-B4BG7PRW-dtHgbkmj.js +165 -0
- package/dist/dashboard/dist/assets/chunk-DI55MBZ5-Cv3hm2Ke.js +220 -0
- package/dist/dashboard/dist/assets/chunk-DI55MBZ5-p7o_KuDF.js +220 -0
- package/dist/dashboard/dist/assets/chunk-FMBD7UC4-B0wUAfQR.js +15 -0
- package/dist/dashboard/dist/assets/chunk-FMBD7UC4-Ds1_OqKH.js +15 -0
- package/dist/dashboard/dist/assets/chunk-QN33PNHL-B6zkzIAo.js +1 -0
- package/dist/dashboard/dist/assets/chunk-QN33PNHL-BTFwTEw8.js +1 -0
- package/dist/dashboard/dist/assets/chunk-QZHKN3VN-C7xuA6tl.js +1 -0
- package/dist/dashboard/dist/assets/chunk-QZHKN3VN-CNXHnjkC.js +1 -0
- package/dist/dashboard/dist/assets/chunk-TZMSLE5B-BIVywBYp.js +1 -0
- package/dist/dashboard/dist/assets/chunk-TZMSLE5B-D_ea_wdP.js +1 -0
- package/dist/dashboard/dist/assets/classDiagram-2ON5EDUG-B6SxXE6T.js +1 -0
- package/dist/dashboard/dist/assets/classDiagram-2ON5EDUG-Dkb-G0UK.js +1 -0
- package/dist/dashboard/dist/assets/classDiagram-v2-WZHVMYZB-B6SxXE6T.js +1 -0
- package/dist/dashboard/dist/assets/classDiagram-v2-WZHVMYZB-Dkb-G0UK.js +1 -0
- package/dist/dashboard/dist/assets/clone-BSdXhKmH.js +1 -0
- package/dist/dashboard/dist/assets/clone-kcKg1tUH.js +1 -0
- package/dist/dashboard/dist/assets/cose-bilkent-S5V4N54A-DasyAK5c.js +1 -0
- package/dist/dashboard/dist/assets/cose-bilkent-S5V4N54A-DuALhY5a.js +1 -0
- package/dist/dashboard/dist/assets/cytoscape.esm-CyJtwmzi.js +331 -0
- package/dist/dashboard/dist/assets/dagre-6UL2VRFP-m-5bs635.js +4 -0
- package/dist/dashboard/dist/assets/dagre-6UL2VRFP-w6endfy9.js +4 -0
- package/dist/dashboard/dist/assets/diagram-PSM6KHXK-BDnUwPQC.js +24 -0
- package/dist/dashboard/dist/assets/diagram-PSM6KHXK-CYFwwEdy.js +24 -0
- package/dist/dashboard/dist/assets/diagram-QEK2KX5R-Cbb7ctAo.js +43 -0
- package/dist/dashboard/dist/assets/diagram-QEK2KX5R-m4Fda1GA.js +43 -0
- package/dist/dashboard/dist/assets/diagram-S2PKOQOG-76ascveh.js +24 -0
- package/dist/dashboard/dist/assets/diagram-S2PKOQOG-BDVk4AKU.js +24 -0
- package/dist/dashboard/dist/assets/erDiagram-Q2GNP2WA-3-jAbxQ6.js +60 -0
- package/dist/dashboard/dist/assets/erDiagram-Q2GNP2WA-BQQnVF0J.js +60 -0
- package/dist/dashboard/dist/assets/flowDiagram-NV44I4VS-Cfv1hkQB.js +162 -0
- package/dist/dashboard/dist/assets/flowDiagram-NV44I4VS-dKukiSxD.js +162 -0
- package/dist/dashboard/dist/assets/ganttDiagram-JELNMOA3-B7GZPGck.js +267 -0
- package/dist/dashboard/dist/assets/ganttDiagram-JELNMOA3-BqnazZuZ.js +267 -0
- package/dist/dashboard/dist/assets/gitGraphDiagram-V2S2FVAM-BUFcnXCj.js +65 -0
- package/dist/dashboard/dist/assets/gitGraphDiagram-V2S2FVAM-K8X-_4av.js +65 -0
- package/dist/dashboard/dist/assets/graph-CGQIvL3r.js +1 -0
- package/dist/dashboard/dist/assets/graph-CfuGK9GG.js +1 -0
- package/dist/dashboard/dist/assets/index-CadOHtae.css +1 -0
- package/dist/dashboard/dist/assets/index-DS-KIxwV.js +777 -0
- package/dist/dashboard/dist/assets/index-DwqfA4mc.js +777 -0
- package/dist/dashboard/dist/assets/infoDiagram-HS3SLOUP-BdylFPLI.js +2 -0
- package/dist/dashboard/dist/assets/infoDiagram-HS3SLOUP-CaIaIUhT.js +2 -0
- package/dist/dashboard/dist/assets/journeyDiagram-XKPGCS4Q-BzJTBkhp.js +139 -0
- package/dist/dashboard/dist/assets/journeyDiagram-XKPGCS4Q-D6dwPswq.js +139 -0
- package/dist/dashboard/dist/assets/kanban-definition-3W4ZIXB7-CERyhhrH.js +89 -0
- package/dist/dashboard/dist/assets/kanban-definition-3W4ZIXB7-StKomgio.js +89 -0
- package/dist/dashboard/dist/assets/katex-O9d3_IXG.js +261 -0
- package/dist/dashboard/dist/assets/layout-BnsX73QS.js +1 -0
- package/dist/dashboard/dist/assets/layout-v7cCi3Fl.js +1 -0
- package/dist/dashboard/dist/assets/mindmap-definition-VGOIOE7T-BvNtTz8N.js +68 -0
- package/dist/dashboard/dist/assets/mindmap-definition-VGOIOE7T-HqFRhVFX.js +68 -0
- package/dist/dashboard/dist/assets/pieDiagram-ADFJNKIX-2I2tFH0C.js +30 -0
- package/dist/dashboard/dist/assets/pieDiagram-ADFJNKIX-Cjg80C_b.js +30 -0
- package/dist/dashboard/dist/assets/quadrantDiagram-AYHSOK5B-PlELkdBW.js +7 -0
- package/dist/dashboard/dist/assets/quadrantDiagram-AYHSOK5B-YtFFUYGD.js +7 -0
- package/dist/dashboard/dist/assets/requirementDiagram-UZGBJVZJ-DLV2LTE5.js +64 -0
- package/dist/dashboard/dist/assets/requirementDiagram-UZGBJVZJ-_9eLQNcZ.js +64 -0
- package/dist/dashboard/dist/assets/sankeyDiagram-TZEHDZUN-C6_Urrii.js +10 -0
- package/dist/dashboard/dist/assets/sankeyDiagram-TZEHDZUN-DHl1Ss7O.js +10 -0
- package/dist/dashboard/dist/assets/sequenceDiagram-WL72ISMW-B9p0m3Uf.js +145 -0
- package/dist/dashboard/dist/assets/sequenceDiagram-WL72ISMW-D33UwAtz.js +145 -0
- package/dist/dashboard/dist/assets/stateDiagram-FKZM4ZOC--rpDODjT.js +1 -0
- package/dist/dashboard/dist/assets/stateDiagram-FKZM4ZOC-DSp83t9D.js +1 -0
- package/dist/dashboard/dist/assets/stateDiagram-v2-4FDKWEC3-Ca9uGk46.js +1 -0
- package/dist/dashboard/dist/assets/stateDiagram-v2-4FDKWEC3-OTWrEpQO.js +1 -0
- package/dist/dashboard/dist/assets/timeline-definition-IT6M3QCI-BCHaGBHB.js +61 -0
- package/dist/dashboard/dist/assets/timeline-definition-IT6M3QCI-D5Bb3Jj7.js +61 -0
- package/dist/dashboard/dist/assets/treemap-GDKQZRPO-CKbkkwye.js +162 -0
- package/dist/dashboard/dist/assets/treemap-GDKQZRPO-CgiqDY8M.js +162 -0
- package/dist/dashboard/dist/assets/xychartDiagram-PRI3JC2R-BMvBBbeV.js +7 -0
- package/dist/dashboard/dist/assets/xychartDiagram-PRI3JC2R-C_Tlzchx.js +7 -0
- package/dist/dashboard/dist/index.html +14 -0
- package/dist/dashboard/dist/reports/business-coverage.json +201 -0
- package/dist/dashboard/dist/reports/coverage-intelligence.json +728 -0
- package/dist/dashboard/dist/reports/coverage-summary.json +995 -0
- package/dist/dashboard/dist/reports/endpoint-coverage.json +336 -0
- package/dist/dashboard/dist/reports/error-coverage.json +367 -0
- package/dist/dashboard/dist/reports/missing-tests-recommendations.json +285 -0
- package/dist/dashboard/dist/reports/risk-prioritization.json +312 -0
- package/dist/dashboard/dist/reports/security-coverage.json +299 -0
- package/dist/dashboard/dist/vite.svg +1 -0
- package/dist/src/generation/context-builder.d.ts +6 -0
- package/dist/src/generation/context-builder.d.ts.map +1 -0
- package/dist/src/generation/context-builder.js +202 -0
- package/dist/src/generation/engine.d.ts +40 -0
- package/dist/src/generation/engine.d.ts.map +1 -0
- package/dist/src/generation/engine.js +378 -0
- package/dist/src/generation/file-router.d.ts +7 -0
- package/dist/src/generation/file-router.d.ts.map +1 -0
- package/dist/src/generation/file-router.js +79 -0
- package/dist/src/generation/gap-extractor.d.ts +12 -0
- package/dist/src/generation/gap-extractor.d.ts.map +1 -0
- package/dist/src/generation/gap-extractor.js +281 -0
- package/dist/src/generation/template-renderer.d.ts +10 -0
- package/dist/src/generation/template-renderer.d.ts.map +1 -0
- package/dist/src/generation/template-renderer.js +526 -0
- package/dist/src/generation/types.d.ts +73 -0
- package/dist/src/generation/types.d.ts.map +1 -0
- package/dist/src/generation/types.js +2 -0
- package/dist/src/index.js +154 -0
- package/dist/src/pipeline/detectors/expressMiddlewareDetector.d.ts +21 -0
- package/dist/src/pipeline/detectors/expressMiddlewareDetector.d.ts.map +1 -0
- package/dist/src/pipeline/detectors/expressMiddlewareDetector.js +201 -0
- package/dist/src/pipeline/detectors/flaskBlueprintDetector.d.ts +23 -0
- package/dist/src/pipeline/detectors/flaskBlueprintDetector.d.ts.map +1 -0
- package/dist/src/pipeline/detectors/flaskBlueprintDetector.js +263 -0
- package/dist/src/pipeline/detectors/springDddDetector.d.ts +23 -0
- package/dist/src/pipeline/detectors/springDddDetector.d.ts.map +1 -0
- package/dist/src/pipeline/detectors/springDddDetector.js +237 -0
- package/dist/src/pipeline/detectors/types.d.ts +97 -0
- package/dist/src/pipeline/detectors/types.d.ts.map +1 -0
- package/dist/src/pipeline/detectors/types.js +15 -0
- package/dist/src/reporting.d.ts.map +1 -1
- package/dist/src/reporting.js +2 -1
- package/dist/src/streaming/detectors/eventBridgeDetector.d.ts +5 -0
- package/dist/src/streaming/detectors/eventBridgeDetector.d.ts.map +1 -0
- package/dist/src/streaming/detectors/eventBridgeDetector.js +82 -0
- package/dist/src/streaming/detectors/kafkaDetector.d.ts +5 -0
- package/dist/src/streaming/detectors/kafkaDetector.d.ts.map +1 -0
- package/dist/src/streaming/detectors/kafkaDetector.js +97 -0
- package/dist/src/streaming/detectors/natsDetector.d.ts +5 -0
- package/dist/src/streaming/detectors/natsDetector.d.ts.map +1 -0
- package/dist/src/streaming/detectors/natsDetector.js +96 -0
- package/dist/src/streaming/detectors/pubsubDetector.d.ts +5 -0
- package/dist/src/streaming/detectors/pubsubDetector.d.ts.map +1 -0
- package/dist/src/streaming/detectors/pubsubDetector.js +82 -0
- package/dist/src/streaming/detectors/rabbitmqDetector.d.ts +5 -0
- package/dist/src/streaming/detectors/rabbitmqDetector.d.ts.map +1 -0
- package/dist/src/streaming/detectors/rabbitmqDetector.js +103 -0
- package/dist/src/streaming/detectors/redisPubsubDetector.d.ts +5 -0
- package/dist/src/streaming/detectors/redisPubsubDetector.d.ts.map +1 -0
- package/dist/src/streaming/detectors/redisPubsubDetector.js +81 -0
- package/dist/src/streaming/detectors/snsDetector.d.ts +5 -0
- package/dist/src/streaming/detectors/snsDetector.d.ts.map +1 -0
- package/dist/src/streaming/detectors/snsDetector.js +81 -0
- package/dist/src/streaming/detectors/sqsDetector.d.ts +5 -0
- package/dist/src/streaming/detectors/sqsDetector.d.ts.map +1 -0
- package/dist/src/streaming/detectors/sqsDetector.js +81 -0
- package/dist/src/streaming/eventCoverage.d.ts +46 -0
- package/dist/src/streaming/eventCoverage.d.ts.map +1 -0
- package/dist/src/streaming/eventCoverage.js +270 -0
- package/dist/src/streaming/types.d.ts +34 -0
- package/dist/src/streaming/types.d.ts.map +1 -0
- package/dist/src/streaming/types.js +2 -0
- package/dist/src/summary/markdownRenderer.d.ts.map +1 -1
- package/dist/src/summary/markdownRenderer.js +1 -0
- package/dist/src/summary/summaryTypes.d.ts.map +1 -1
- package/dist/src/summary/summaryTypes.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
{
|
|
2
|
+
"total": 27,
|
|
3
|
+
"covered": 7,
|
|
4
|
+
"percentage": 23.08,
|
|
5
|
+
"scanFindings": 0,
|
|
6
|
+
"categorySummary": {
|
|
7
|
+
"authentication": {
|
|
8
|
+
"total": 1,
|
|
9
|
+
"covered": 1
|
|
10
|
+
},
|
|
11
|
+
"authorization": {
|
|
12
|
+
"total": 13,
|
|
13
|
+
"covered": 0
|
|
14
|
+
},
|
|
15
|
+
"input-validation": {
|
|
16
|
+
"total": 11,
|
|
17
|
+
"covered": 5
|
|
18
|
+
},
|
|
19
|
+
"cryptography": {
|
|
20
|
+
"total": 1,
|
|
21
|
+
"covered": 0
|
|
22
|
+
},
|
|
23
|
+
"session-management": {
|
|
24
|
+
"total": 0,
|
|
25
|
+
"covered": 0
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"controls": [
|
|
29
|
+
{
|
|
30
|
+
"id": "authentication:bearerAuth",
|
|
31
|
+
"category": "authentication",
|
|
32
|
+
"description": "Authentication via security scheme \"bearerAuth\" (http/bearer)",
|
|
33
|
+
"covered": true,
|
|
34
|
+
"matchedTests": [
|
|
35
|
+
"returns 401 when no token",
|
|
36
|
+
"returns 401 when no token provided",
|
|
37
|
+
"returns 401 for invalid token",
|
|
38
|
+
"returns 401 without auth"
|
|
39
|
+
],
|
|
40
|
+
"coveredByScanReport": false
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"id": "cryptography:https",
|
|
44
|
+
"category": "cryptography",
|
|
45
|
+
"description": "API servers include non-HTTPS URLs – cryptographic transport security may be missing",
|
|
46
|
+
"covered": false,
|
|
47
|
+
"matchedTests": [],
|
|
48
|
+
"coveredByScanReport": false
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"id": "authorization:get:/wallets",
|
|
52
|
+
"category": "authorization",
|
|
53
|
+
"description": "Authorization check for GET /wallets",
|
|
54
|
+
"endpoint": "GET /wallets",
|
|
55
|
+
"covered": false,
|
|
56
|
+
"matchedTests": [],
|
|
57
|
+
"coveredByScanReport": false
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"id": "authorization:post:/wallets",
|
|
61
|
+
"category": "authorization",
|
|
62
|
+
"description": "Authorization check for POST /wallets",
|
|
63
|
+
"endpoint": "POST /wallets",
|
|
64
|
+
"covered": false,
|
|
65
|
+
"matchedTests": [],
|
|
66
|
+
"coveredByScanReport": false
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"id": "input-validation:post:/wallets",
|
|
70
|
+
"category": "input-validation",
|
|
71
|
+
"description": "Input validation for POST /wallets",
|
|
72
|
+
"endpoint": "POST /wallets",
|
|
73
|
+
"covered": true,
|
|
74
|
+
"matchedTests": [
|
|
75
|
+
"returns 400 when currency is missing",
|
|
76
|
+
"returns 400 for unsupported currency",
|
|
77
|
+
"returns 422 for insufficient funds",
|
|
78
|
+
"returns 401 for invalid token",
|
|
79
|
+
"returns 400 for invalid currency",
|
|
80
|
+
"returns 422 with insufficient funds"
|
|
81
|
+
],
|
|
82
|
+
"coveredByScanReport": false
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"id": "authorization:get:/wallets/{id}",
|
|
86
|
+
"category": "authorization",
|
|
87
|
+
"description": "Authorization check for GET /wallets/{id}",
|
|
88
|
+
"endpoint": "GET /wallets/{id}",
|
|
89
|
+
"covered": false,
|
|
90
|
+
"matchedTests": [],
|
|
91
|
+
"coveredByScanReport": false
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"id": "input-validation:get:/wallets/{id}",
|
|
95
|
+
"category": "input-validation",
|
|
96
|
+
"description": "Input validation for GET /wallets/{id}",
|
|
97
|
+
"endpoint": "GET /wallets/{id}",
|
|
98
|
+
"covered": true,
|
|
99
|
+
"matchedTests": [
|
|
100
|
+
"returns 422 for insufficient funds",
|
|
101
|
+
"returns 400 for invalid currency",
|
|
102
|
+
"returns 422 with insufficient funds"
|
|
103
|
+
],
|
|
104
|
+
"coveredByScanReport": false
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"id": "authorization:delete:/wallets/{id}",
|
|
108
|
+
"category": "authorization",
|
|
109
|
+
"description": "Authorization check for DELETE /wallets/{id}",
|
|
110
|
+
"endpoint": "DELETE /wallets/{id}",
|
|
111
|
+
"covered": false,
|
|
112
|
+
"matchedTests": [],
|
|
113
|
+
"coveredByScanReport": false
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"id": "input-validation:delete:/wallets/{id}",
|
|
117
|
+
"category": "input-validation",
|
|
118
|
+
"description": "Input validation for DELETE /wallets/{id}",
|
|
119
|
+
"endpoint": "DELETE /wallets/{id}",
|
|
120
|
+
"covered": true,
|
|
121
|
+
"matchedTests": [
|
|
122
|
+
"returns 422 for insufficient funds",
|
|
123
|
+
"returns 400 for invalid currency",
|
|
124
|
+
"returns 422 with insufficient funds"
|
|
125
|
+
],
|
|
126
|
+
"coveredByScanReport": false
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"id": "authorization:patch:/wallets/{id}/freeze",
|
|
130
|
+
"category": "authorization",
|
|
131
|
+
"description": "Authorization check for PATCH /wallets/{id}/freeze",
|
|
132
|
+
"endpoint": "PATCH /wallets/{id}/freeze",
|
|
133
|
+
"covered": false,
|
|
134
|
+
"matchedTests": [],
|
|
135
|
+
"coveredByScanReport": false
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"id": "input-validation:patch:/wallets/{id}/freeze",
|
|
139
|
+
"category": "input-validation",
|
|
140
|
+
"description": "Input validation for PATCH /wallets/{id}/freeze",
|
|
141
|
+
"endpoint": "PATCH /wallets/{id}/freeze",
|
|
142
|
+
"covered": false,
|
|
143
|
+
"matchedTests": [],
|
|
144
|
+
"coveredByScanReport": false
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"id": "authorization:patch:/wallets/{id}/unfreeze",
|
|
148
|
+
"category": "authorization",
|
|
149
|
+
"description": "Authorization check for PATCH /wallets/{id}/unfreeze",
|
|
150
|
+
"endpoint": "PATCH /wallets/{id}/unfreeze",
|
|
151
|
+
"covered": false,
|
|
152
|
+
"matchedTests": [],
|
|
153
|
+
"coveredByScanReport": false
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"id": "input-validation:patch:/wallets/{id}/unfreeze",
|
|
157
|
+
"category": "input-validation",
|
|
158
|
+
"description": "Input validation for PATCH /wallets/{id}/unfreeze",
|
|
159
|
+
"endpoint": "PATCH /wallets/{id}/unfreeze",
|
|
160
|
+
"covered": false,
|
|
161
|
+
"matchedTests": [],
|
|
162
|
+
"coveredByScanReport": false
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
"id": "authorization:post:/wallets/{id}/fund",
|
|
166
|
+
"category": "authorization",
|
|
167
|
+
"description": "Authorization check for POST /wallets/{id}/fund",
|
|
168
|
+
"endpoint": "POST /wallets/{id}/fund",
|
|
169
|
+
"covered": false,
|
|
170
|
+
"matchedTests": [],
|
|
171
|
+
"coveredByScanReport": false
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"id": "input-validation:post:/wallets/{id}/fund",
|
|
175
|
+
"category": "input-validation",
|
|
176
|
+
"description": "Input validation for POST /wallets/{id}/fund",
|
|
177
|
+
"endpoint": "POST /wallets/{id}/fund",
|
|
178
|
+
"covered": false,
|
|
179
|
+
"matchedTests": [],
|
|
180
|
+
"coveredByScanReport": false
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"id": "authorization:post:/wallets/{id}/debit",
|
|
184
|
+
"category": "authorization",
|
|
185
|
+
"description": "Authorization check for POST /wallets/{id}/debit",
|
|
186
|
+
"endpoint": "POST /wallets/{id}/debit",
|
|
187
|
+
"covered": false,
|
|
188
|
+
"matchedTests": [],
|
|
189
|
+
"coveredByScanReport": false
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"id": "input-validation:post:/wallets/{id}/debit",
|
|
193
|
+
"category": "input-validation",
|
|
194
|
+
"description": "Input validation for POST /wallets/{id}/debit",
|
|
195
|
+
"endpoint": "POST /wallets/{id}/debit",
|
|
196
|
+
"covered": false,
|
|
197
|
+
"matchedTests": [],
|
|
198
|
+
"coveredByScanReport": false
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"id": "authorization:post:/wallets/{id}/transfer",
|
|
202
|
+
"category": "authorization",
|
|
203
|
+
"description": "Authorization check for POST /wallets/{id}/transfer",
|
|
204
|
+
"endpoint": "POST /wallets/{id}/transfer",
|
|
205
|
+
"covered": false,
|
|
206
|
+
"matchedTests": [],
|
|
207
|
+
"coveredByScanReport": false
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
"id": "input-validation:post:/wallets/{id}/transfer",
|
|
211
|
+
"category": "input-validation",
|
|
212
|
+
"description": "Input validation for POST /wallets/{id}/transfer",
|
|
213
|
+
"endpoint": "POST /wallets/{id}/transfer",
|
|
214
|
+
"covered": false,
|
|
215
|
+
"matchedTests": [],
|
|
216
|
+
"coveredByScanReport": false
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
"id": "authorization:post:/payments",
|
|
220
|
+
"category": "authorization",
|
|
221
|
+
"description": "Authorization check for POST /payments",
|
|
222
|
+
"endpoint": "POST /payments",
|
|
223
|
+
"covered": false,
|
|
224
|
+
"matchedTests": [],
|
|
225
|
+
"coveredByScanReport": false
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
"id": "input-validation:post:/payments",
|
|
229
|
+
"category": "input-validation",
|
|
230
|
+
"description": "Input validation for POST /payments",
|
|
231
|
+
"endpoint": "POST /payments",
|
|
232
|
+
"covered": true,
|
|
233
|
+
"matchedTests": [
|
|
234
|
+
"returns 400 for missing required fields",
|
|
235
|
+
"returns 422 for invalid (non-existent) wallet",
|
|
236
|
+
"returns 422 when refunding a non-completed payment"
|
|
237
|
+
],
|
|
238
|
+
"coveredByScanReport": false
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
"id": "authorization:get:/payments/{id}",
|
|
242
|
+
"category": "authorization",
|
|
243
|
+
"description": "Authorization check for GET /payments/{id}",
|
|
244
|
+
"endpoint": "GET /payments/{id}",
|
|
245
|
+
"covered": false,
|
|
246
|
+
"matchedTests": [],
|
|
247
|
+
"coveredByScanReport": false
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
"id": "input-validation:get:/payments/{id}",
|
|
251
|
+
"category": "input-validation",
|
|
252
|
+
"description": "Input validation for GET /payments/{id}",
|
|
253
|
+
"endpoint": "GET /payments/{id}",
|
|
254
|
+
"covered": true,
|
|
255
|
+
"matchedTests": [
|
|
256
|
+
"returns 422 when refunding a non-completed payment"
|
|
257
|
+
],
|
|
258
|
+
"coveredByScanReport": false
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
"id": "authorization:post:/payments/{id}/refund",
|
|
262
|
+
"category": "authorization",
|
|
263
|
+
"description": "Authorization check for POST /payments/{id}/refund",
|
|
264
|
+
"endpoint": "POST /payments/{id}/refund",
|
|
265
|
+
"covered": false,
|
|
266
|
+
"matchedTests": [],
|
|
267
|
+
"coveredByScanReport": false
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
"id": "input-validation:post:/payments/{id}/refund",
|
|
271
|
+
"category": "input-validation",
|
|
272
|
+
"description": "Input validation for POST /payments/{id}/refund",
|
|
273
|
+
"endpoint": "POST /payments/{id}/refund",
|
|
274
|
+
"covered": false,
|
|
275
|
+
"matchedTests": [],
|
|
276
|
+
"coveredByScanReport": false
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
"id": "authorization:get:/transactions",
|
|
280
|
+
"category": "authorization",
|
|
281
|
+
"description": "Authorization check for GET /transactions",
|
|
282
|
+
"endpoint": "GET /transactions",
|
|
283
|
+
"covered": false,
|
|
284
|
+
"matchedTests": [],
|
|
285
|
+
"coveredByScanReport": false
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
"id": "authentication:JWT",
|
|
289
|
+
"category": "authentication",
|
|
290
|
+
"description": "JWT token signature and expiry validation",
|
|
291
|
+
"covered": true,
|
|
292
|
+
"matchedTests": [
|
|
293
|
+
"returns 401 for expired JWT",
|
|
294
|
+
"returns 401 for invalid JWT signature"
|
|
295
|
+
],
|
|
296
|
+
"coveredByScanReport": false
|
|
297
|
+
}
|
|
298
|
+
]
|
|
299
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { GenerationContext, DetectedGap } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Assembles a GenerationContext from scan results and project detection.
|
|
4
|
+
*/
|
|
5
|
+
export declare function buildContext(projectRoot: string, gaps: DetectedGap[], overrides?: Partial<Pick<GenerationContext, 'language' | 'framework' | 'baseUrl' | 'authHeader'>>): GenerationContext;
|
|
6
|
+
//# sourceMappingURL=context-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-builder.d.ts","sourceRoot":"","sources":["../../../src/generation/context-builder.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAuB,WAAW,EAAE,MAAM,SAAS,CAAC;AA6InF;;GAEG;AACH,wBAAgB,YAAY,CAC1B,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,WAAW,EAAE,EACnB,SAAS,GAAE,OAAO,CAAC,IAAI,CAAC,iBAAiB,EAAE,UAAU,GAAG,WAAW,GAAG,SAAS,GAAG,YAAY,CAAC,CAAM,GACpG,iBAAiB,CAgBnB"}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.buildContext = buildContext;
|
|
37
|
+
const fs = __importStar(require("node:fs"));
|
|
38
|
+
const path = __importStar(require("node:path"));
|
|
39
|
+
/**
|
|
40
|
+
* Detects the primary programming language from file extensions found in the project.
|
|
41
|
+
*/
|
|
42
|
+
function detectLanguage(projectRoot) {
|
|
43
|
+
const counters = {
|
|
44
|
+
typescript: 0,
|
|
45
|
+
javascript: 0,
|
|
46
|
+
python: 0,
|
|
47
|
+
java: 0,
|
|
48
|
+
kotlin: 0,
|
|
49
|
+
};
|
|
50
|
+
const extMap = {
|
|
51
|
+
'.ts': 'typescript',
|
|
52
|
+
'.tsx': 'typescript',
|
|
53
|
+
'.js': 'javascript',
|
|
54
|
+
'.jsx': 'javascript',
|
|
55
|
+
'.mjs': 'javascript',
|
|
56
|
+
'.cjs': 'javascript',
|
|
57
|
+
'.py': 'python',
|
|
58
|
+
'.java': 'java',
|
|
59
|
+
'.kt': 'kotlin',
|
|
60
|
+
'.kts': 'kotlin',
|
|
61
|
+
};
|
|
62
|
+
function walk(dir, depth) {
|
|
63
|
+
if (depth > 4)
|
|
64
|
+
return;
|
|
65
|
+
let entries;
|
|
66
|
+
try {
|
|
67
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
for (const entry of entries) {
|
|
73
|
+
if (entry.name.startsWith('.') || entry.name === 'node_modules' || entry.name === 'dist') {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const fullPath = path.join(dir, entry.name);
|
|
77
|
+
if (entry.isDirectory()) {
|
|
78
|
+
walk(fullPath, depth + 1);
|
|
79
|
+
}
|
|
80
|
+
else if (entry.isFile()) {
|
|
81
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
82
|
+
const lang = extMap[ext];
|
|
83
|
+
if (lang)
|
|
84
|
+
counters[lang]++;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
walk(projectRoot, 0);
|
|
89
|
+
const sorted = Object.entries(counters).sort((a, b) => b[1] - a[1]);
|
|
90
|
+
return sorted[0][1] > 0 ? sorted[0][0] : 'typescript';
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Detects the primary test framework from project configuration files.
|
|
94
|
+
*/
|
|
95
|
+
function detectFramework(projectRoot) {
|
|
96
|
+
var _a, _b;
|
|
97
|
+
// Check package.json for jest/cypress/playwright
|
|
98
|
+
const pkgPath = path.join(projectRoot, 'package.json');
|
|
99
|
+
if (fs.existsSync(pkgPath)) {
|
|
100
|
+
try {
|
|
101
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
102
|
+
const allDeps = {
|
|
103
|
+
...((_a = pkg['dependencies']) !== null && _a !== void 0 ? _a : {}),
|
|
104
|
+
...((_b = pkg['devDependencies']) !== null && _b !== void 0 ? _b : {}),
|
|
105
|
+
};
|
|
106
|
+
if ('playwright' in allDeps || '@playwright/test' in allDeps)
|
|
107
|
+
return 'playwright';
|
|
108
|
+
if ('cypress' in allDeps)
|
|
109
|
+
return 'cypress';
|
|
110
|
+
if ('jest' in allDeps || 'ts-jest' in allDeps)
|
|
111
|
+
return 'jest';
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// ignore parse errors
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// Check for pytest (requirements.txt or setup.py/pyproject.toml)
|
|
118
|
+
const requirementsPath = path.join(projectRoot, 'requirements.txt');
|
|
119
|
+
if (fs.existsSync(requirementsPath)) {
|
|
120
|
+
try {
|
|
121
|
+
const content = fs.readFileSync(requirementsPath, 'utf-8').toLowerCase();
|
|
122
|
+
if (content.includes('pytest'))
|
|
123
|
+
return 'pytest';
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// ignore
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const pyprojectPath = path.join(projectRoot, 'pyproject.toml');
|
|
130
|
+
if (fs.existsSync(pyprojectPath)) {
|
|
131
|
+
try {
|
|
132
|
+
const content = fs.readFileSync(pyprojectPath, 'utf-8').toLowerCase();
|
|
133
|
+
if (content.includes('pytest'))
|
|
134
|
+
return 'pytest';
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
// ignore
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// Check for JUnit5 (build.gradle or pom.xml)
|
|
141
|
+
const buildGradlePath = path.join(projectRoot, 'build.gradle');
|
|
142
|
+
const buildGradleKtsPath = path.join(projectRoot, 'build.gradle.kts');
|
|
143
|
+
for (const gradlePath of [buildGradlePath, buildGradleKtsPath]) {
|
|
144
|
+
if (fs.existsSync(gradlePath)) {
|
|
145
|
+
try {
|
|
146
|
+
const content = fs.readFileSync(gradlePath, 'utf-8').toLowerCase();
|
|
147
|
+
if (content.includes('junit') || content.includes('junit-jupiter'))
|
|
148
|
+
return 'junit5';
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
// ignore
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const pomPath = path.join(projectRoot, 'pom.xml');
|
|
156
|
+
if (fs.existsSync(pomPath)) {
|
|
157
|
+
try {
|
|
158
|
+
const content = fs.readFileSync(pomPath, 'utf-8').toLowerCase();
|
|
159
|
+
if (content.includes('junit-jupiter') || content.includes('junit5'))
|
|
160
|
+
return 'junit5';
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
// ignore
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return 'jest';
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Maps Framework to its corresponding test framework name.
|
|
170
|
+
*/
|
|
171
|
+
function resolveTestFramework(framework) {
|
|
172
|
+
switch (framework) {
|
|
173
|
+
case 'jest':
|
|
174
|
+
case 'cypress':
|
|
175
|
+
case 'playwright':
|
|
176
|
+
return 'jest';
|
|
177
|
+
case 'pytest':
|
|
178
|
+
return 'pytest';
|
|
179
|
+
case 'junit5':
|
|
180
|
+
return 'junit5';
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Assembles a GenerationContext from scan results and project detection.
|
|
185
|
+
*/
|
|
186
|
+
function buildContext(projectRoot, gaps, overrides = {}) {
|
|
187
|
+
var _a, _b, _c;
|
|
188
|
+
const language = (_a = overrides.language) !== null && _a !== void 0 ? _a : detectLanguage(projectRoot);
|
|
189
|
+
const framework = (_b = overrides.framework) !== null && _b !== void 0 ? _b : detectFramework(projectRoot);
|
|
190
|
+
const testFramework = resolveTestFramework(framework);
|
|
191
|
+
const baseUrl = (_c = overrides.baseUrl) !== null && _c !== void 0 ? _c : 'http://localhost:3000';
|
|
192
|
+
const authHeader = overrides.authHeader;
|
|
193
|
+
return {
|
|
194
|
+
projectRoot,
|
|
195
|
+
language,
|
|
196
|
+
framework,
|
|
197
|
+
testFramework,
|
|
198
|
+
baseUrl,
|
|
199
|
+
authHeader,
|
|
200
|
+
gaps,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Generation Engine (Feature F-TGE)
|
|
3
|
+
*
|
|
4
|
+
* Entry point: TestGenerationEngine
|
|
5
|
+
* CLI commands: generate-tests, export-ai-flows, score-tests
|
|
6
|
+
*
|
|
7
|
+
* Every generated file starts with: // AUTO-GENERATED by api-test-coverage-analyzer
|
|
8
|
+
* expect(response.status) ALWAYS before expect(response.body)
|
|
9
|
+
* NEVER use toBeTruthy() or toBeDefined() — use specific matchers
|
|
10
|
+
*/
|
|
11
|
+
import type { GeneratedFile, TestQualityReport, AiReadyFlowsManifest } from './types';
|
|
12
|
+
export declare class TestGenerationEngine {
|
|
13
|
+
private projectRoot;
|
|
14
|
+
private reportsDir;
|
|
15
|
+
constructor(projectRoot: string, reportsDir?: string);
|
|
16
|
+
/**
|
|
17
|
+
* Generate test files for all detected coverage gaps.
|
|
18
|
+
* In dry-run mode, files are not written to disk.
|
|
19
|
+
*/
|
|
20
|
+
generateTests(options?: {
|
|
21
|
+
dryRun?: boolean;
|
|
22
|
+
outDir?: string;
|
|
23
|
+
baseUrl?: string;
|
|
24
|
+
authHeader?: string;
|
|
25
|
+
}): GeneratedFile[];
|
|
26
|
+
/**
|
|
27
|
+
* Score existing test files for quality.
|
|
28
|
+
* Dimensions: statusAssertion, bodyAssertion, errorPath, noGenericMatchers, descriptiveName.
|
|
29
|
+
*/
|
|
30
|
+
scoreTests(testsGlob: string): TestQualityReport[];
|
|
31
|
+
/**
|
|
32
|
+
* Export AI-ready flows: structured prompts for Copilot / LLM consumption.
|
|
33
|
+
* RULE-AI05: prompt ≤ 800 tokens (~3200 chars)
|
|
34
|
+
*/
|
|
35
|
+
exportAiFlows(options?: {
|
|
36
|
+
sourceGlob?: string;
|
|
37
|
+
testsGlob?: string;
|
|
38
|
+
}): AiReadyFlowsManifest;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../src/generation/engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,KAAK,EAGV,aAAa,EACb,iBAAiB,EAEjB,oBAAoB,EAErB,MAAM,SAAS,CAAC;AAQjB,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,UAAU,CAAS;gBAEf,WAAW,EAAE,MAAM,EAAE,UAAU,GAAE,MAAkB;IAK/D;;;OAGG;IACH,aAAa,CAAC,OAAO,GAAE;QACrB,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,MAAM,CAAC;KAChB,GAAG,aAAa,EAAE;IAmDxB;;;OAGG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,iBAAiB,EAAE;IAoBlD;;;OAGG;IACH,aAAa,CAAC,OAAO,GAAE;QACrB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC;KACf,GAAG,oBAAoB;CAuD9B"}
|