api-tests-coverage 1.0.21 → 1.0.22

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.
Files changed (125) hide show
  1. package/dist/dashboard/dist/assets/_basePickBy-BKGHUeDJ.js +1 -0
  2. package/dist/dashboard/dist/assets/_baseUniq-CtA-DQF7.js +1 -0
  3. package/dist/dashboard/dist/assets/arc-CbXP3Mc9.js +1 -0
  4. package/dist/dashboard/dist/assets/architectureDiagram-VXUJARFQ-n7QxasMM.js +36 -0
  5. package/dist/dashboard/dist/assets/blockDiagram-VD42YOAC-MXnGwKRn.js +122 -0
  6. package/dist/dashboard/dist/assets/c4Diagram-YG6GDRKO-B3yZ5P9k.js +10 -0
  7. package/dist/dashboard/dist/assets/channel-C7QwX7U8.js +1 -0
  8. package/dist/dashboard/dist/assets/chunk-4BX2VUAB-Dw3El5KF.js +1 -0
  9. package/dist/dashboard/dist/assets/chunk-55IACEB6-T8Jf00IL.js +1 -0
  10. package/dist/dashboard/dist/assets/chunk-B4BG7PRW-DAJalKYJ.js +165 -0
  11. package/dist/dashboard/dist/assets/chunk-DI55MBZ5-p7o_KuDF.js +220 -0
  12. package/dist/dashboard/dist/assets/chunk-FMBD7UC4-B0wUAfQR.js +15 -0
  13. package/dist/dashboard/dist/assets/chunk-QN33PNHL-BTFwTEw8.js +1 -0
  14. package/dist/dashboard/dist/assets/chunk-QZHKN3VN-CNXHnjkC.js +1 -0
  15. package/dist/dashboard/dist/assets/chunk-TZMSLE5B-BIVywBYp.js +1 -0
  16. package/dist/dashboard/dist/assets/classDiagram-2ON5EDUG-Dkb-G0UK.js +1 -0
  17. package/dist/dashboard/dist/assets/classDiagram-v2-WZHVMYZB-Dkb-G0UK.js +1 -0
  18. package/dist/dashboard/dist/assets/clone-BSdXhKmH.js +1 -0
  19. package/dist/dashboard/dist/assets/cose-bilkent-S5V4N54A-DuALhY5a.js +1 -0
  20. package/dist/dashboard/dist/assets/cytoscape.esm-CyJtwmzi.js +331 -0
  21. package/dist/dashboard/dist/assets/dagre-6UL2VRFP-w6endfy9.js +4 -0
  22. package/dist/dashboard/dist/assets/diagram-PSM6KHXK-BDnUwPQC.js +24 -0
  23. package/dist/dashboard/dist/assets/diagram-QEK2KX5R-Cbb7ctAo.js +43 -0
  24. package/dist/dashboard/dist/assets/diagram-S2PKOQOG-76ascveh.js +24 -0
  25. package/dist/dashboard/dist/assets/erDiagram-Q2GNP2WA-BQQnVF0J.js +60 -0
  26. package/dist/dashboard/dist/assets/flowDiagram-NV44I4VS-dKukiSxD.js +162 -0
  27. package/dist/dashboard/dist/assets/ganttDiagram-JELNMOA3-BqnazZuZ.js +267 -0
  28. package/dist/dashboard/dist/assets/gitGraphDiagram-V2S2FVAM-BUFcnXCj.js +65 -0
  29. package/dist/dashboard/dist/assets/graph-CGQIvL3r.js +1 -0
  30. package/dist/dashboard/dist/assets/index-CadOHtae.css +1 -0
  31. package/dist/dashboard/dist/assets/index-DwqfA4mc.js +777 -0
  32. package/dist/dashboard/dist/assets/infoDiagram-HS3SLOUP-BdylFPLI.js +2 -0
  33. package/dist/dashboard/dist/assets/journeyDiagram-XKPGCS4Q-BzJTBkhp.js +139 -0
  34. package/dist/dashboard/dist/assets/kanban-definition-3W4ZIXB7-StKomgio.js +89 -0
  35. package/dist/dashboard/dist/assets/katex-O9d3_IXG.js +261 -0
  36. package/dist/dashboard/dist/assets/layout-BnsX73QS.js +1 -0
  37. package/dist/dashboard/dist/assets/mindmap-definition-VGOIOE7T-HqFRhVFX.js +68 -0
  38. package/dist/dashboard/dist/assets/pieDiagram-ADFJNKIX-2I2tFH0C.js +30 -0
  39. package/dist/dashboard/dist/assets/quadrantDiagram-AYHSOK5B-PlELkdBW.js +7 -0
  40. package/dist/dashboard/dist/assets/requirementDiagram-UZGBJVZJ-_9eLQNcZ.js +64 -0
  41. package/dist/dashboard/dist/assets/sankeyDiagram-TZEHDZUN-DHl1Ss7O.js +10 -0
  42. package/dist/dashboard/dist/assets/sequenceDiagram-WL72ISMW-B9p0m3Uf.js +145 -0
  43. package/dist/dashboard/dist/assets/stateDiagram-FKZM4ZOC--rpDODjT.js +1 -0
  44. package/dist/dashboard/dist/assets/stateDiagram-v2-4FDKWEC3-Ca9uGk46.js +1 -0
  45. package/dist/dashboard/dist/assets/timeline-definition-IT6M3QCI-BCHaGBHB.js +61 -0
  46. package/dist/dashboard/dist/assets/treemap-GDKQZRPO-CgiqDY8M.js +162 -0
  47. package/dist/dashboard/dist/assets/xychartDiagram-PRI3JC2R-BMvBBbeV.js +7 -0
  48. package/dist/dashboard/dist/index.html +14 -0
  49. package/dist/dashboard/dist/reports/business-coverage.json +201 -0
  50. package/dist/dashboard/dist/reports/coverage-intelligence.json +728 -0
  51. package/dist/dashboard/dist/reports/coverage-summary.json +995 -0
  52. package/dist/dashboard/dist/reports/endpoint-coverage.json +336 -0
  53. package/dist/dashboard/dist/reports/error-coverage.json +367 -0
  54. package/dist/dashboard/dist/reports/missing-tests-recommendations.json +285 -0
  55. package/dist/dashboard/dist/reports/risk-prioritization.json +312 -0
  56. package/dist/dashboard/dist/reports/security-coverage.json +299 -0
  57. package/dist/dashboard/dist/vite.svg +1 -0
  58. package/dist/src/generation/context-builder.d.ts +6 -0
  59. package/dist/src/generation/context-builder.d.ts.map +1 -0
  60. package/dist/src/generation/context-builder.js +202 -0
  61. package/dist/src/generation/engine.d.ts +40 -0
  62. package/dist/src/generation/engine.d.ts.map +1 -0
  63. package/dist/src/generation/engine.js +378 -0
  64. package/dist/src/generation/file-router.d.ts +7 -0
  65. package/dist/src/generation/file-router.d.ts.map +1 -0
  66. package/dist/src/generation/file-router.js +79 -0
  67. package/dist/src/generation/gap-extractor.d.ts +12 -0
  68. package/dist/src/generation/gap-extractor.d.ts.map +1 -0
  69. package/dist/src/generation/gap-extractor.js +281 -0
  70. package/dist/src/generation/template-renderer.d.ts +10 -0
  71. package/dist/src/generation/template-renderer.d.ts.map +1 -0
  72. package/dist/src/generation/template-renderer.js +526 -0
  73. package/dist/src/generation/types.d.ts +73 -0
  74. package/dist/src/generation/types.d.ts.map +1 -0
  75. package/dist/src/generation/types.js +2 -0
  76. package/dist/src/index.js +154 -0
  77. package/dist/src/pipeline/detectors/expressMiddlewareDetector.d.ts +21 -0
  78. package/dist/src/pipeline/detectors/expressMiddlewareDetector.d.ts.map +1 -0
  79. package/dist/src/pipeline/detectors/expressMiddlewareDetector.js +201 -0
  80. package/dist/src/pipeline/detectors/flaskBlueprintDetector.d.ts +23 -0
  81. package/dist/src/pipeline/detectors/flaskBlueprintDetector.d.ts.map +1 -0
  82. package/dist/src/pipeline/detectors/flaskBlueprintDetector.js +263 -0
  83. package/dist/src/pipeline/detectors/springDddDetector.d.ts +23 -0
  84. package/dist/src/pipeline/detectors/springDddDetector.d.ts.map +1 -0
  85. package/dist/src/pipeline/detectors/springDddDetector.js +237 -0
  86. package/dist/src/pipeline/detectors/types.d.ts +97 -0
  87. package/dist/src/pipeline/detectors/types.d.ts.map +1 -0
  88. package/dist/src/pipeline/detectors/types.js +15 -0
  89. package/dist/src/reporting.d.ts.map +1 -1
  90. package/dist/src/reporting.js +2 -1
  91. package/dist/src/streaming/detectors/eventBridgeDetector.d.ts +5 -0
  92. package/dist/src/streaming/detectors/eventBridgeDetector.d.ts.map +1 -0
  93. package/dist/src/streaming/detectors/eventBridgeDetector.js +82 -0
  94. package/dist/src/streaming/detectors/kafkaDetector.d.ts +5 -0
  95. package/dist/src/streaming/detectors/kafkaDetector.d.ts.map +1 -0
  96. package/dist/src/streaming/detectors/kafkaDetector.js +97 -0
  97. package/dist/src/streaming/detectors/natsDetector.d.ts +5 -0
  98. package/dist/src/streaming/detectors/natsDetector.d.ts.map +1 -0
  99. package/dist/src/streaming/detectors/natsDetector.js +96 -0
  100. package/dist/src/streaming/detectors/pubsubDetector.d.ts +5 -0
  101. package/dist/src/streaming/detectors/pubsubDetector.d.ts.map +1 -0
  102. package/dist/src/streaming/detectors/pubsubDetector.js +82 -0
  103. package/dist/src/streaming/detectors/rabbitmqDetector.d.ts +5 -0
  104. package/dist/src/streaming/detectors/rabbitmqDetector.d.ts.map +1 -0
  105. package/dist/src/streaming/detectors/rabbitmqDetector.js +103 -0
  106. package/dist/src/streaming/detectors/redisPubsubDetector.d.ts +5 -0
  107. package/dist/src/streaming/detectors/redisPubsubDetector.d.ts.map +1 -0
  108. package/dist/src/streaming/detectors/redisPubsubDetector.js +81 -0
  109. package/dist/src/streaming/detectors/snsDetector.d.ts +5 -0
  110. package/dist/src/streaming/detectors/snsDetector.d.ts.map +1 -0
  111. package/dist/src/streaming/detectors/snsDetector.js +81 -0
  112. package/dist/src/streaming/detectors/sqsDetector.d.ts +5 -0
  113. package/dist/src/streaming/detectors/sqsDetector.d.ts.map +1 -0
  114. package/dist/src/streaming/detectors/sqsDetector.js +81 -0
  115. package/dist/src/streaming/eventCoverage.d.ts +46 -0
  116. package/dist/src/streaming/eventCoverage.d.ts.map +1 -0
  117. package/dist/src/streaming/eventCoverage.js +270 -0
  118. package/dist/src/streaming/types.d.ts +34 -0
  119. package/dist/src/streaming/types.d.ts.map +1 -0
  120. package/dist/src/streaming/types.js +2 -0
  121. package/dist/src/summary/markdownRenderer.d.ts.map +1 -1
  122. package/dist/src/summary/markdownRenderer.js +1 -0
  123. package/dist/src/summary/summaryTypes.d.ts.map +1 -1
  124. package/dist/src/summary/summaryTypes.js +1 -0
  125. package/package.json +1 -1
@@ -0,0 +1,281 @@
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.buildSampleGaps = buildSampleGaps;
37
+ exports.extractGaps = extractGaps;
38
+ const fs = __importStar(require("node:fs"));
39
+ const path = __importStar(require("node:path"));
40
+ /**
41
+ * Builds 5 synthetic sample gaps for onboarding / dry-run purposes.
42
+ * Used when coverage-summary.json has zero gaps or does not exist.
43
+ */
44
+ function buildSampleGaps() {
45
+ return [
46
+ {
47
+ id: 'sample-gap-001',
48
+ type: 'endpoint:missing',
49
+ endpoint: { method: 'GET', path: '/api/v1/users' },
50
+ riskScore: 80,
51
+ sourceFile: undefined,
52
+ existingSimilarTests: [],
53
+ },
54
+ {
55
+ id: 'sample-gap-002',
56
+ type: 'parameter:missing',
57
+ endpoint: { method: 'POST', path: '/api/v1/orders' },
58
+ parameter: { name: 'customerId', in: 'body', required: true },
59
+ riskScore: 70,
60
+ sourceFile: undefined,
61
+ existingSimilarTests: [],
62
+ },
63
+ {
64
+ id: 'sample-gap-003',
65
+ type: 'business:missing',
66
+ businessRule: { id: 'BR-001', description: 'Order total must be positive' },
67
+ riskScore: 60,
68
+ sourceFile: undefined,
69
+ existingSimilarTests: [],
70
+ },
71
+ {
72
+ id: 'sample-gap-004',
73
+ type: 'error:missing-status',
74
+ endpoint: { method: 'DELETE', path: '/api/v1/items/{id}' },
75
+ riskScore: 65,
76
+ sourceFile: undefined,
77
+ existingSimilarTests: [],
78
+ },
79
+ {
80
+ id: 'sample-gap-005',
81
+ type: 'security:missing-auth',
82
+ endpoint: { method: 'PUT', path: '/api/v1/admin/settings' },
83
+ riskScore: 90,
84
+ sourceFile: undefined,
85
+ existingSimilarTests: [],
86
+ },
87
+ ];
88
+ }
89
+ /**
90
+ * Maps coverage summary type names to corresponding GapType values.
91
+ */
92
+ function mapTypeToGapType(coverageType, index) {
93
+ var _a;
94
+ const mapping = {
95
+ endpoint: 'endpoint:missing',
96
+ parameter: 'parameter:missing',
97
+ business: 'business:missing',
98
+ integration: 'integration:missing-step',
99
+ error: 'error:missing-status',
100
+ security: 'security:missing-auth',
101
+ performance: 'performance:missing-sla',
102
+ resilience: 'resilience:missing-retry',
103
+ };
104
+ return (_a = mapping[coverageType]) !== null && _a !== void 0 ? _a : 'endpoint:missing';
105
+ }
106
+ /**
107
+ * Reads reports/coverage-summary.json and extracts uncovered items as DetectedGap[].
108
+ * When there are zero gaps (perfect coverage), returns buildSampleGaps() instead of [].
109
+ */
110
+ function extractGaps(reportsDir) {
111
+ var _a, _b;
112
+ const summaryPath = path.join(reportsDir, 'coverage-summary.json');
113
+ if (!fs.existsSync(summaryPath)) {
114
+ return buildSampleGaps();
115
+ }
116
+ let summary;
117
+ try {
118
+ summary = JSON.parse(fs.readFileSync(summaryPath, 'utf-8'));
119
+ }
120
+ catch {
121
+ return buildSampleGaps();
122
+ }
123
+ const entries = (_a = summary.summary) !== null && _a !== void 0 ? _a : [];
124
+ const gaps = [];
125
+ let gapIndex = 0;
126
+ for (const entry of entries) {
127
+ const uncoveredCount = entry.totalItems - entry.coveredItems;
128
+ if (uncoveredCount <= 0)
129
+ continue;
130
+ const details = (_b = summary.details) === null || _b === void 0 ? void 0 : _b[entry.type];
131
+ const gapType = mapTypeToGapType(entry.type, gapIndex);
132
+ // Extract top gaps from details.topGaps if available
133
+ const topGaps = [];
134
+ if (details && typeof details === 'object' && 'topGaps' in details) {
135
+ const raw = details['topGaps'];
136
+ if (Array.isArray(raw)) {
137
+ for (const g of raw) {
138
+ if (typeof g === 'string')
139
+ topGaps.push(g);
140
+ }
141
+ }
142
+ }
143
+ // Extract uncovered items from details based on type
144
+ const uncoveredItems = extractUncoveredItems(entry.type, details);
145
+ if (uncoveredItems.length > 0) {
146
+ for (const item of uncoveredItems.slice(0, Math.max(uncoveredCount, 1))) {
147
+ gaps.push(buildGapFromItem(gapType, entry.type, item, gapIndex));
148
+ gapIndex++;
149
+ }
150
+ }
151
+ else {
152
+ // Fallback: create a single gap for the type
153
+ gaps.push({
154
+ id: `gap-${entry.type}-${gapIndex}`,
155
+ type: gapType,
156
+ riskScore: computeRiskScore(entry.coveragePercent),
157
+ existingSimilarTests: topGaps,
158
+ });
159
+ gapIndex++;
160
+ }
161
+ }
162
+ if (gaps.length === 0) {
163
+ return buildSampleGaps();
164
+ }
165
+ return gaps;
166
+ }
167
+ function computeRiskScore(coveragePercent) {
168
+ // Lower coverage → higher risk
169
+ return Math.round(Math.max(0, Math.min(100, 100 - coveragePercent)));
170
+ }
171
+ function extractUncoveredItems(coverageType, details) {
172
+ var _a, _b, _c, _d, _e;
173
+ if (!details || typeof details !== 'object')
174
+ return [];
175
+ const d = details;
176
+ switch (coverageType) {
177
+ case 'endpoint': {
178
+ const items = (_a = d['endpoints']) !== null && _a !== void 0 ? _a : d['items'];
179
+ if (Array.isArray(items)) {
180
+ return items.filter((i) => !i['covered'] && !i['isCovered']);
181
+ }
182
+ break;
183
+ }
184
+ case 'parameter': {
185
+ const params = (_b = d['parameters']) !== null && _b !== void 0 ? _b : d['items'];
186
+ if (Array.isArray(params)) {
187
+ return params.filter((p) => { var _a; return ((_a = p['ratio']) !== null && _a !== void 0 ? _a : 1) === 0; });
188
+ }
189
+ break;
190
+ }
191
+ case 'business': {
192
+ const rules = (_c = d['uncoveredRules']) !== null && _c !== void 0 ? _c : d['items'];
193
+ if (Array.isArray(rules)) {
194
+ return rules;
195
+ }
196
+ break;
197
+ }
198
+ case 'integration': {
199
+ const items = d['items'];
200
+ if (Array.isArray(items)) {
201
+ return items.filter((i) => !i['complete']);
202
+ }
203
+ break;
204
+ }
205
+ case 'error': {
206
+ const scenarios = (_d = d['uncoveredScenarios']) !== null && _d !== void 0 ? _d : d['items'];
207
+ if (Array.isArray(scenarios)) {
208
+ return scenarios;
209
+ }
210
+ break;
211
+ }
212
+ case 'security': {
213
+ const controls = (_e = d['uncoveredControls']) !== null && _e !== void 0 ? _e : d['items'];
214
+ if (Array.isArray(controls)) {
215
+ return controls;
216
+ }
217
+ break;
218
+ }
219
+ }
220
+ return [];
221
+ }
222
+ function buildGapFromItem(gapType, coverageType, item, index) {
223
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
224
+ const id = `gap-${coverageType}-${index}`;
225
+ const riskScore = computeRiskScore(0); // uncovered = high risk
226
+ switch (coverageType) {
227
+ case 'endpoint':
228
+ return {
229
+ id,
230
+ type: gapType,
231
+ endpoint: {
232
+ method: String((_a = item['method']) !== null && _a !== void 0 ? _a : 'GET').toUpperCase(),
233
+ path: String((_b = item['path']) !== null && _b !== void 0 ? _b : '/unknown'),
234
+ },
235
+ riskScore,
236
+ existingSimilarTests: [],
237
+ };
238
+ case 'parameter':
239
+ return {
240
+ id,
241
+ type: gapType,
242
+ parameter: {
243
+ name: String((_c = item['name']) !== null && _c !== void 0 ? _c : 'param'),
244
+ in: (_d = item['in']) !== null && _d !== void 0 ? _d : 'query',
245
+ required: Boolean((_e = item['required']) !== null && _e !== void 0 ? _e : false),
246
+ },
247
+ riskScore,
248
+ existingSimilarTests: [],
249
+ };
250
+ case 'business':
251
+ return {
252
+ id,
253
+ type: gapType,
254
+ businessRule: {
255
+ id: String((_f = item['id']) !== null && _f !== void 0 ? _f : id),
256
+ description: String((_g = item['description']) !== null && _g !== void 0 ? _g : 'Business rule'),
257
+ },
258
+ riskScore,
259
+ existingSimilarTests: [],
260
+ };
261
+ case 'integration':
262
+ return {
263
+ id,
264
+ type: gapType,
265
+ integrationFlow: {
266
+ id: String((_h = item['id']) !== null && _h !== void 0 ? _h : id),
267
+ name: String((_j = item['name']) !== null && _j !== void 0 ? _j : 'Flow'),
268
+ missingStep: String((_l = (_k = item['uncoveredSteps']) === null || _k === void 0 ? void 0 : _k[0]) !== null && _l !== void 0 ? _l : 'step'),
269
+ },
270
+ riskScore,
271
+ existingSimilarTests: [],
272
+ };
273
+ default:
274
+ return {
275
+ id,
276
+ type: gapType,
277
+ riskScore,
278
+ existingSimilarTests: [],
279
+ };
280
+ }
281
+ }
@@ -0,0 +1,10 @@
1
+ import type { DetectedGap, GenerationContext, GapType } from './types';
2
+ /** Common OWASP injection strings for security tests */
3
+ export declare const OWASP_INJECTION_STRINGS: string[];
4
+ /** Standard file header injected into every generated test file */
5
+ export declare function renderFileHeader(gapId: string, gapType: GapType): string;
6
+ /** Render test file content for a given gap and context */
7
+ export declare function renderTemplate(gap: DetectedGap, ctx: GenerationContext): string;
8
+ /** Returns the template name used for a given gap type */
9
+ export declare function getTemplateName(gap: DetectedGap, ctx: GenerationContext): string;
10
+ //# sourceMappingURL=template-renderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-renderer.d.ts","sourceRoot":"","sources":["../../../src/generation/template-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEvE,wDAAwD;AACxD,eAAO,MAAM,uBAAuB,EAAE,MAAM,EAW3C,CAAC;AAEF,mEAAmE;AACnE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,CAQxE;AA4eD,2DAA2D;AAC3D,wBAAgB,cAAc,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,iBAAiB,GAAG,MAAM,CAoC/E;AAED,0DAA0D;AAC1D,wBAAgB,eAAe,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,iBAAiB,GAAG,MAAM,CAEhF"}