@yasserkhanorg/e2e-agents 0.3.2
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/LICENSE +168 -0
- package/README.md +620 -0
- package/dist/agent/analysis.d.ts +62 -0
- package/dist/agent/analysis.d.ts.map +1 -0
- package/dist/agent/analysis.js +292 -0
- package/dist/agent/blast_radius.d.ts +4 -0
- package/dist/agent/blast_radius.d.ts.map +1 -0
- package/dist/agent/blast_radius.js +37 -0
- package/dist/agent/cache_utils.d.ts +38 -0
- package/dist/agent/cache_utils.d.ts.map +1 -0
- package/dist/agent/cache_utils.js +67 -0
- package/dist/agent/config.d.ts +148 -0
- package/dist/agent/config.d.ts.map +1 -0
- package/dist/agent/config.js +640 -0
- package/dist/agent/dependency_graph.d.ts +14 -0
- package/dist/agent/dependency_graph.d.ts.map +1 -0
- package/dist/agent/dependency_graph.js +227 -0
- package/dist/agent/feedback.d.ts +55 -0
- package/dist/agent/feedback.d.ts.map +1 -0
- package/dist/agent/feedback.js +257 -0
- package/dist/agent/flags.d.ts +23 -0
- package/dist/agent/flags.d.ts.map +1 -0
- package/dist/agent/flags.js +171 -0
- package/dist/agent/flow_catalog.d.ts +25 -0
- package/dist/agent/flow_catalog.d.ts.map +1 -0
- package/dist/agent/flow_catalog.js +106 -0
- package/dist/agent/flow_mapping.d.ts +10 -0
- package/dist/agent/flow_mapping.d.ts.map +1 -0
- package/dist/agent/flow_mapping.js +84 -0
- package/dist/agent/framework.d.ts +13 -0
- package/dist/agent/framework.d.ts.map +1 -0
- package/dist/agent/framework.js +149 -0
- package/dist/agent/gap_suggestions.d.ts +14 -0
- package/dist/agent/gap_suggestions.d.ts.map +1 -0
- package/dist/agent/gap_suggestions.js +101 -0
- package/dist/agent/generator.d.ts +10 -0
- package/dist/agent/generator.d.ts.map +1 -0
- package/dist/agent/generator.js +115 -0
- package/dist/agent/git.d.ts +11 -0
- package/dist/agent/git.d.ts.map +1 -0
- package/dist/agent/git.js +90 -0
- package/dist/agent/handoff.d.ts +22 -0
- package/dist/agent/handoff.d.ts.map +1 -0
- package/dist/agent/handoff.js +180 -0
- package/dist/agent/impact-analyzer.d.ts +114 -0
- package/dist/agent/impact-analyzer.d.ts.map +1 -0
- package/dist/agent/impact-analyzer.js +557 -0
- package/dist/agent/index.d.ts +21 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +38 -0
- package/dist/agent/model-router.d.ts +57 -0
- package/dist/agent/model-router.d.ts.map +1 -0
- package/dist/agent/model-router.js +154 -0
- package/dist/agent/operational_insights.d.ts +41 -0
- package/dist/agent/operational_insights.d.ts.map +1 -0
- package/dist/agent/operational_insights.js +126 -0
- package/dist/agent/pipeline.d.ts +23 -0
- package/dist/agent/pipeline.d.ts.map +1 -0
- package/dist/agent/pipeline.js +609 -0
- package/dist/agent/plan.d.ts +91 -0
- package/dist/agent/plan.d.ts.map +1 -0
- package/dist/agent/plan.js +331 -0
- package/dist/agent/playwright_report.d.ts +8 -0
- package/dist/agent/playwright_report.d.ts.map +1 -0
- package/dist/agent/playwright_report.js +126 -0
- package/dist/agent/report-generator.d.ts +24 -0
- package/dist/agent/report-generator.d.ts.map +1 -0
- package/dist/agent/report-generator.js +250 -0
- package/dist/agent/report.d.ts +81 -0
- package/dist/agent/report.d.ts.map +1 -0
- package/dist/agent/report.js +147 -0
- package/dist/agent/runner.d.ts +7 -0
- package/dist/agent/runner.d.ts.map +1 -0
- package/dist/agent/runner.js +576 -0
- package/dist/agent/selectors.d.ts +10 -0
- package/dist/agent/selectors.d.ts.map +1 -0
- package/dist/agent/selectors.js +75 -0
- package/dist/agent/spec-bridge.d.ts +101 -0
- package/dist/agent/spec-bridge.d.ts.map +1 -0
- package/dist/agent/spec-bridge.js +273 -0
- package/dist/agent/spec-builder.d.ts +102 -0
- package/dist/agent/spec-builder.d.ts.map +1 -0
- package/dist/agent/spec-builder.js +273 -0
- package/dist/agent/subsystem_risk.d.ts +23 -0
- package/dist/agent/subsystem_risk.d.ts.map +1 -0
- package/dist/agent/subsystem_risk.js +207 -0
- package/dist/agent/telemetry.d.ts +84 -0
- package/dist/agent/telemetry.d.ts.map +1 -0
- package/dist/agent/telemetry.js +220 -0
- package/dist/agent/test_path.d.ts +2 -0
- package/dist/agent/test_path.d.ts.map +1 -0
- package/dist/agent/test_path.js +23 -0
- package/dist/agent/tests.d.ts +18 -0
- package/dist/agent/tests.d.ts.map +1 -0
- package/dist/agent/tests.js +106 -0
- package/dist/agent/traceability.d.ts +22 -0
- package/dist/agent/traceability.d.ts.map +1 -0
- package/dist/agent/traceability.js +183 -0
- package/dist/agent/traceability_capture.d.ts +18 -0
- package/dist/agent/traceability_capture.d.ts.map +1 -0
- package/dist/agent/traceability_capture.js +313 -0
- package/dist/agent/traceability_ingest.d.ts +21 -0
- package/dist/agent/traceability_ingest.d.ts.map +1 -0
- package/dist/agent/traceability_ingest.js +237 -0
- package/dist/agent/utils.d.ts +13 -0
- package/dist/agent/utils.d.ts.map +1 -0
- package/dist/agent/utils.js +152 -0
- package/dist/agent/validators/selector-validator.d.ts +74 -0
- package/dist/agent/validators/selector-validator.d.ts.map +1 -0
- package/dist/agent/validators/selector-validator.js +165 -0
- package/dist/anthropic_provider.d.ts +65 -0
- package/dist/anthropic_provider.d.ts.map +1 -0
- package/dist/anthropic_provider.js +332 -0
- package/dist/api.d.ts +48 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +113 -0
- package/dist/base_provider.d.ts +53 -0
- package/dist/base_provider.d.ts.map +1 -0
- package/dist/base_provider.js +81 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +843 -0
- package/dist/custom_provider.d.ts +20 -0
- package/dist/custom_provider.d.ts.map +1 -0
- package/dist/custom_provider.js +276 -0
- package/dist/e2e-test-gen/index.d.ts +51 -0
- package/dist/e2e-test-gen/index.d.ts.map +1 -0
- package/dist/e2e-test-gen/index.js +57 -0
- package/dist/e2e-test-gen/spec_parser.d.ts +142 -0
- package/dist/e2e-test-gen/spec_parser.d.ts.map +1 -0
- package/dist/e2e-test-gen/spec_parser.js +786 -0
- package/dist/e2e-test-gen/types.d.ts +185 -0
- package/dist/e2e-test-gen/types.d.ts.map +1 -0
- package/dist/e2e-test-gen/types.js +4 -0
- package/dist/esm/agent/analysis.js +287 -0
- package/dist/esm/agent/blast_radius.js +34 -0
- package/dist/esm/agent/cache_utils.js +63 -0
- package/dist/esm/agent/config.js +637 -0
- package/dist/esm/agent/dependency_graph.js +224 -0
- package/dist/esm/agent/feedback.js +253 -0
- package/dist/esm/agent/flags.js +160 -0
- package/dist/esm/agent/flow_catalog.js +103 -0
- package/dist/esm/agent/flow_mapping.js +81 -0
- package/dist/esm/agent/framework.js +145 -0
- package/dist/esm/agent/gap_suggestions.js +98 -0
- package/dist/esm/agent/generator.js +112 -0
- package/dist/esm/agent/git.js +87 -0
- package/dist/esm/agent/handoff.js +177 -0
- package/dist/esm/agent/impact-analyzer.js +548 -0
- package/dist/esm/agent/index.js +22 -0
- package/dist/esm/agent/model-router.js +150 -0
- package/dist/esm/agent/operational_insights.js +123 -0
- package/dist/esm/agent/pipeline.js +605 -0
- package/dist/esm/agent/plan.js +324 -0
- package/dist/esm/agent/playwright_report.js +123 -0
- package/dist/esm/agent/report-generator.js +247 -0
- package/dist/esm/agent/report.js +144 -0
- package/dist/esm/agent/runner.js +572 -0
- package/dist/esm/agent/selectors.js +71 -0
- package/dist/esm/agent/spec-bridge.js +267 -0
- package/dist/esm/agent/spec-builder.js +267 -0
- package/dist/esm/agent/subsystem_risk.js +204 -0
- package/dist/esm/agent/telemetry.js +216 -0
- package/dist/esm/agent/test_path.js +20 -0
- package/dist/esm/agent/tests.js +101 -0
- package/dist/esm/agent/traceability.js +180 -0
- package/dist/esm/agent/traceability_capture.js +310 -0
- package/dist/esm/agent/traceability_ingest.js +234 -0
- package/dist/esm/agent/utils.js +138 -0
- package/dist/esm/agent/validators/selector-validator.js +160 -0
- package/dist/esm/anthropic_provider.js +324 -0
- package/dist/esm/api.js +105 -0
- package/dist/esm/base_provider.js +77 -0
- package/dist/esm/cli.js +841 -0
- package/dist/esm/custom_provider.js +272 -0
- package/dist/esm/e2e-test-gen/index.js +50 -0
- package/dist/esm/e2e-test-gen/spec_parser.js +782 -0
- package/dist/esm/e2e-test-gen/types.js +3 -0
- package/dist/esm/index.js +16 -0
- package/dist/esm/logger.js +89 -0
- package/dist/esm/mcp-server.js +465 -0
- package/dist/esm/ollama_provider.js +300 -0
- package/dist/esm/openai_provider.js +242 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/plan-and-test-constants.js +126 -0
- package/dist/esm/provider_factory.js +336 -0
- package/dist/esm/provider_interface.js +23 -0
- package/dist/esm/provider_utils.js +96 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -0
- package/dist/logger.d.ts +23 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +93 -0
- package/dist/mcp-server.d.ts +35 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +469 -0
- package/dist/ollama_provider.d.ts +65 -0
- package/dist/ollama_provider.d.ts.map +1 -0
- package/dist/ollama_provider.js +308 -0
- package/dist/openai_provider.d.ts +23 -0
- package/dist/openai_provider.d.ts.map +1 -0
- package/dist/openai_provider.js +250 -0
- package/dist/plan-and-test-constants.d.ts +110 -0
- package/dist/plan-and-test-constants.d.ts.map +1 -0
- package/dist/plan-and-test-constants.js +132 -0
- package/dist/provider_factory.d.ts +99 -0
- package/dist/provider_factory.d.ts.map +1 -0
- package/dist/provider_factory.js +341 -0
- package/dist/provider_interface.d.ts +358 -0
- package/dist/provider_interface.d.ts.map +1 -0
- package/dist/provider_interface.js +28 -0
- package/dist/provider_utils.d.ts +39 -0
- package/dist/provider_utils.d.ts.map +1 -0
- package/dist/provider_utils.js +103 -0
- package/package.json +101 -0
- package/schemas/gap.schema.json +18 -0
- package/schemas/impact.schema.json +418 -0
- package/schemas/plan.schema.json +285 -0
- package/schemas/subsystem-risk-map.schema.json +62 -0
- package/schemas/traceability-input.schema.json +122 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
3
|
+
// See LICENSE.txt for license information.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.expandByDependencyGraph = expandByDependencyGraph;
|
|
6
|
+
const glob_1 = require("glob");
|
|
7
|
+
const path_1 = require("path");
|
|
8
|
+
const utils_js_1 = require("./utils.js");
|
|
9
|
+
const IMPORT_REGEXES = [
|
|
10
|
+
/import\s+[\s\S]*?\s+from\s+['"]([^'"]+)['"]/g,
|
|
11
|
+
/import\s+['"]([^'"]+)['"]/g,
|
|
12
|
+
/export\s+[\s\S]*?\s+from\s+['"]([^'"]+)['"]/g,
|
|
13
|
+
/require\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
14
|
+
/import\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
15
|
+
];
|
|
16
|
+
const RESOLVABLE_EXTENSIONS = ['ts', 'tsx', 'js', 'jsx', 'mjs', 'cjs'];
|
|
17
|
+
function extractImportSpecifiers(content) {
|
|
18
|
+
const imports = [];
|
|
19
|
+
for (const regex of IMPORT_REGEXES) {
|
|
20
|
+
regex.lastIndex = 0;
|
|
21
|
+
let match = regex.exec(content);
|
|
22
|
+
while (match) {
|
|
23
|
+
const specifier = match[1];
|
|
24
|
+
if (specifier) {
|
|
25
|
+
imports.push(specifier);
|
|
26
|
+
}
|
|
27
|
+
match = regex.exec(content);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return Array.from(new Set(imports));
|
|
31
|
+
}
|
|
32
|
+
function listCandidateFiles(appRoot, cfg) {
|
|
33
|
+
const files = new Set();
|
|
34
|
+
for (const pattern of cfg.filePatterns) {
|
|
35
|
+
const matches = (0, glob_1.globSync)(pattern, {
|
|
36
|
+
cwd: appRoot,
|
|
37
|
+
nodir: true,
|
|
38
|
+
ignore: cfg.excludePatterns,
|
|
39
|
+
});
|
|
40
|
+
for (const match of matches) {
|
|
41
|
+
files.add((0, utils_js_1.normalizePath)(match));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return Array.from(files);
|
|
45
|
+
}
|
|
46
|
+
function resolveWithCandidates(candidateBase, fileSet) {
|
|
47
|
+
if (fileSet.has(candidateBase)) {
|
|
48
|
+
return candidateBase;
|
|
49
|
+
}
|
|
50
|
+
for (const ext of RESOLVABLE_EXTENSIONS) {
|
|
51
|
+
const withExt = `${candidateBase}.${ext}`;
|
|
52
|
+
if (fileSet.has(withExt)) {
|
|
53
|
+
return withExt;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
for (const ext of RESOLVABLE_EXTENSIONS) {
|
|
57
|
+
const indexPath = `${candidateBase}/index.${ext}`;
|
|
58
|
+
if (fileSet.has(indexPath)) {
|
|
59
|
+
return indexPath;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
function expandAliasTarget(pattern, target, specifier) {
|
|
65
|
+
const normalizedPattern = (0, utils_js_1.normalizePath)(pattern);
|
|
66
|
+
const normalizedTarget = (0, utils_js_1.normalizePath)(target);
|
|
67
|
+
const normalizedSpecifier = (0, utils_js_1.normalizePath)(specifier);
|
|
68
|
+
if (normalizedPattern.endsWith('/*')) {
|
|
69
|
+
const prefix = normalizedPattern.slice(0, -2);
|
|
70
|
+
if (normalizedSpecifier === prefix || normalizedSpecifier.startsWith(`${prefix}/`)) {
|
|
71
|
+
const suffix = normalizedSpecifier === prefix ? '' : normalizedSpecifier.slice(prefix.length + 1);
|
|
72
|
+
if (normalizedTarget.endsWith('/*')) {
|
|
73
|
+
const targetPrefix = normalizedTarget.slice(0, -2);
|
|
74
|
+
return suffix ? (0, utils_js_1.normalizePath)(`${targetPrefix}/${suffix}`) : (0, utils_js_1.normalizePath)(targetPrefix);
|
|
75
|
+
}
|
|
76
|
+
return (0, utils_js_1.normalizePath)(normalizedTarget);
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
if (normalizedPattern === normalizedSpecifier) {
|
|
81
|
+
if (normalizedTarget.endsWith('/*')) {
|
|
82
|
+
return (0, utils_js_1.normalizePath)(normalizedTarget.slice(0, -2));
|
|
83
|
+
}
|
|
84
|
+
return (0, utils_js_1.normalizePath)(normalizedTarget);
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
function resolvePathAliasImport(specifier, fileSet, cfg) {
|
|
89
|
+
for (const [pattern, targets] of Object.entries(cfg.pathAliases)) {
|
|
90
|
+
for (const target of targets) {
|
|
91
|
+
const aliasPath = expandAliasTarget(pattern, target, specifier);
|
|
92
|
+
if (!aliasPath) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const resolved = resolveWithCandidates(aliasPath, fileSet);
|
|
96
|
+
if (resolved) {
|
|
97
|
+
return resolved;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
function resolveImport(fromFile, specifier, fileSet, cfg) {
|
|
104
|
+
if (specifier.startsWith('.')) {
|
|
105
|
+
const fromDir = (0, path_1.dirname)(fromFile);
|
|
106
|
+
const relativeCandidate = (0, utils_js_1.normalizePath)((0, path_1.normalize)((0, path_1.join)(fromDir, specifier)));
|
|
107
|
+
return resolveWithCandidates(relativeCandidate, fileSet);
|
|
108
|
+
}
|
|
109
|
+
const aliasResolved = resolvePathAliasImport(specifier, fileSet, cfg);
|
|
110
|
+
if (aliasResolved) {
|
|
111
|
+
return aliasResolved;
|
|
112
|
+
}
|
|
113
|
+
for (const root of cfg.aliasRoots) {
|
|
114
|
+
const rootedCandidate = (0, utils_js_1.normalizePath)((0, path_1.normalize)((0, path_1.join)(root, specifier)));
|
|
115
|
+
const resolved = resolveWithCandidates(rootedCandidate, fileSet);
|
|
116
|
+
if (resolved) {
|
|
117
|
+
return resolved;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const directCandidate = (0, utils_js_1.normalizePath)(specifier);
|
|
121
|
+
return resolveWithCandidates(directCandidate, fileSet);
|
|
122
|
+
}
|
|
123
|
+
function expandByDependencyGraph(appRoot, changedFiles, cfg) {
|
|
124
|
+
const warnings = [];
|
|
125
|
+
if (!cfg.enabled) {
|
|
126
|
+
return {
|
|
127
|
+
source: 'static-dependency-graph',
|
|
128
|
+
seedFiles: [],
|
|
129
|
+
impactedFiles: [],
|
|
130
|
+
expandedFiles: [],
|
|
131
|
+
analyzedFiles: 0,
|
|
132
|
+
analyzedEdges: 0,
|
|
133
|
+
maxDepth: 0,
|
|
134
|
+
truncated: false,
|
|
135
|
+
warnings,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
const candidates = listCandidateFiles(appRoot, cfg);
|
|
139
|
+
const fileSet = new Set(candidates);
|
|
140
|
+
if (candidates.length === 0) {
|
|
141
|
+
warnings.push('Dependency graph found no candidate source files.');
|
|
142
|
+
}
|
|
143
|
+
const reverse = new Map();
|
|
144
|
+
let analyzedEdges = 0;
|
|
145
|
+
for (const file of candidates) {
|
|
146
|
+
const fullPath = (0, path_1.join)(appRoot, file);
|
|
147
|
+
const content = (0, utils_js_1.safeReadTextFile)(fullPath);
|
|
148
|
+
if (!content) {
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
const imports = extractImportSpecifiers(content);
|
|
152
|
+
for (const specifier of imports) {
|
|
153
|
+
const resolved = resolveImport(file, specifier, fileSet, cfg);
|
|
154
|
+
if (!resolved) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (!reverse.has(resolved)) {
|
|
158
|
+
reverse.set(resolved, new Set());
|
|
159
|
+
}
|
|
160
|
+
reverse.get(resolved)?.add(file);
|
|
161
|
+
analyzedEdges += 1;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const seeds = Array.from(new Set(changedFiles
|
|
165
|
+
.map((file) => (0, utils_js_1.normalizePath)(file))
|
|
166
|
+
.filter((file) => fileSet.has(file))));
|
|
167
|
+
if (seeds.length === 0) {
|
|
168
|
+
warnings.push('No changed files were found in dependency graph candidates.');
|
|
169
|
+
return {
|
|
170
|
+
source: 'static-dependency-graph',
|
|
171
|
+
seedFiles: [],
|
|
172
|
+
impactedFiles: [],
|
|
173
|
+
expandedFiles: [],
|
|
174
|
+
analyzedFiles: candidates.length,
|
|
175
|
+
analyzedEdges,
|
|
176
|
+
maxDepth: cfg.maxDepth,
|
|
177
|
+
truncated: false,
|
|
178
|
+
warnings,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
const impacted = new Set(seeds);
|
|
182
|
+
const queue = seeds.map((file) => ({ file, depth: 0 }));
|
|
183
|
+
let truncated = false;
|
|
184
|
+
while (queue.length > 0) {
|
|
185
|
+
const next = queue.shift();
|
|
186
|
+
if (!next) {
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
if (next.depth >= cfg.maxDepth) {
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
const dependents = reverse.get(next.file);
|
|
193
|
+
if (!dependents) {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
for (const dependent of dependents) {
|
|
197
|
+
if (impacted.has(dependent)) {
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (impacted.size - seeds.length >= cfg.maxExpandedFiles) {
|
|
201
|
+
truncated = true;
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
impacted.add(dependent);
|
|
205
|
+
queue.push({ file: dependent, depth: next.depth + 1 });
|
|
206
|
+
}
|
|
207
|
+
if (truncated) {
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
const impactedFiles = Array.from(impacted);
|
|
212
|
+
const expandedFiles = impactedFiles.filter((file) => !seeds.includes(file));
|
|
213
|
+
if (truncated) {
|
|
214
|
+
warnings.push(`Dependency expansion was truncated at maxExpandedFiles=${cfg.maxExpandedFiles}.`);
|
|
215
|
+
}
|
|
216
|
+
return {
|
|
217
|
+
source: 'static-dependency-graph',
|
|
218
|
+
seedFiles: seeds,
|
|
219
|
+
impactedFiles,
|
|
220
|
+
expandedFiles,
|
|
221
|
+
analyzedFiles: candidates.length,
|
|
222
|
+
analyzedEdges,
|
|
223
|
+
maxDepth: cfg.maxDepth,
|
|
224
|
+
truncated,
|
|
225
|
+
warnings,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export interface RecommendationFeedbackEntry {
|
|
2
|
+
timestamp: string;
|
|
3
|
+
runSet: 'smoke' | 'targeted' | 'full';
|
|
4
|
+
recommendedTests: string[];
|
|
5
|
+
executedTests: string[];
|
|
6
|
+
failedTests: string[];
|
|
7
|
+
escapedFailures?: string[];
|
|
8
|
+
}
|
|
9
|
+
export interface CalibrationSummary {
|
|
10
|
+
schemaVersion: '1.1.0';
|
|
11
|
+
generatedAt: string;
|
|
12
|
+
samples: number;
|
|
13
|
+
overall: {
|
|
14
|
+
precision: number;
|
|
15
|
+
recall: number;
|
|
16
|
+
falseNegativeRate: number;
|
|
17
|
+
};
|
|
18
|
+
recent7d: {
|
|
19
|
+
precision: number;
|
|
20
|
+
recall: number;
|
|
21
|
+
falseNegativeRate: number;
|
|
22
|
+
samples: number;
|
|
23
|
+
};
|
|
24
|
+
recent30d: {
|
|
25
|
+
precision: number;
|
|
26
|
+
recall: number;
|
|
27
|
+
falseNegativeRate: number;
|
|
28
|
+
samples: number;
|
|
29
|
+
};
|
|
30
|
+
bySubsystem: Record<string, {
|
|
31
|
+
precision: number;
|
|
32
|
+
recall: number;
|
|
33
|
+
falseNegativeRate: number;
|
|
34
|
+
samples: number;
|
|
35
|
+
recent7d: {
|
|
36
|
+
precision: number;
|
|
37
|
+
recall: number;
|
|
38
|
+
falseNegativeRate: number;
|
|
39
|
+
samples: number;
|
|
40
|
+
};
|
|
41
|
+
recent30d: {
|
|
42
|
+
precision: number;
|
|
43
|
+
recall: number;
|
|
44
|
+
falseNegativeRate: number;
|
|
45
|
+
samples: number;
|
|
46
|
+
};
|
|
47
|
+
}>;
|
|
48
|
+
}
|
|
49
|
+
export declare function appendFeedbackAndRecompute(appRoot: string, input: RecommendationFeedbackEntry): {
|
|
50
|
+
feedbackPath: string;
|
|
51
|
+
calibrationPath: string;
|
|
52
|
+
calibration: CalibrationSummary;
|
|
53
|
+
};
|
|
54
|
+
export declare function readCalibration(appRoot: string): CalibrationSummary | null;
|
|
55
|
+
//# sourceMappingURL=feedback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feedback.d.ts","sourceRoot":"","sources":["../../src/agent/feedback.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,2BAA2B;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,OAAO,GAAG,UAAU,GAAG,MAAM,CAAC;IACtC,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,kBAAkB;IAC/B,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE;QACL,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;QACf,iBAAiB,EAAE,MAAM,CAAC;KAC7B,CAAC;IACF,QAAQ,EAAE;QACN,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;QACf,iBAAiB,EAAE,MAAM,CAAC;QAC1B,OAAO,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,SAAS,EAAE;QACP,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;QACf,iBAAiB,EAAE,MAAM,CAAC;QAC1B,OAAO,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,WAAW,EAAE,MAAM,CACnB,MAAM,EACN;QACI,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;QACf,iBAAiB,EAAE,MAAM,CAAC;QAC1B,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE;YACN,SAAS,EAAE,MAAM,CAAC;YAClB,MAAM,EAAE,MAAM,CAAC;YACf,iBAAiB,EAAE,MAAM,CAAC;YAC1B,OAAO,EAAE,MAAM,CAAC;SACnB,CAAC;QACF,SAAS,EAAE;YACP,SAAS,EAAE,MAAM,CAAC;YAClB,MAAM,EAAE,MAAM,CAAC;YACf,iBAAiB,EAAE,MAAM,CAAC;YAC1B,OAAO,EAAE,MAAM,CAAC;SACnB,CAAC;KACL,CACA,CAAC;CACL;AAkSD,wBAAgB,0BAA0B,CACtC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,2BAA2B,GACnC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,kBAAkB,CAAA;CAAC,CAwBlF;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CAE1E"}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
3
|
+
// See LICENSE.txt for license information.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.appendFeedbackAndRecompute = appendFeedbackAndRecompute;
|
|
6
|
+
exports.readCalibration = readCalibration;
|
|
7
|
+
const fs_1 = require("fs");
|
|
8
|
+
const path_1 = require("path");
|
|
9
|
+
const test_path_js_1 = require("./test_path.js");
|
|
10
|
+
function readJson(path) {
|
|
11
|
+
if (!(0, fs_1.existsSync)(path)) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
return JSON.parse((0, fs_1.readFileSync)(path, 'utf-8'));
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function normalizeTestName(test) {
|
|
22
|
+
return test.replace(/ \(flags:.*\)$/, '').trim();
|
|
23
|
+
}
|
|
24
|
+
function asSet(values) {
|
|
25
|
+
return new Set(values.map(normalizeTestName).filter(Boolean));
|
|
26
|
+
}
|
|
27
|
+
function ratio(numerator, denominator) {
|
|
28
|
+
if (denominator <= 0) {
|
|
29
|
+
return 0;
|
|
30
|
+
}
|
|
31
|
+
return Number((numerator / denominator).toFixed(4));
|
|
32
|
+
}
|
|
33
|
+
function subsystemForTest(test) {
|
|
34
|
+
return (0, test_path_js_1.inferSubsystemFromTestPath)(test);
|
|
35
|
+
}
|
|
36
|
+
function parseTimestamp(value) {
|
|
37
|
+
const time = Date.parse(value);
|
|
38
|
+
if (Number.isNaN(time)) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
return time;
|
|
42
|
+
}
|
|
43
|
+
function filterRecent(entries, days) {
|
|
44
|
+
const cutoff = Date.now() - days * 24 * 60 * 60 * 1000;
|
|
45
|
+
return entries.filter((entry) => {
|
|
46
|
+
const time = parseTimestamp(entry.timestamp);
|
|
47
|
+
return time !== null && time >= cutoff;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function aggregateMetrics(entries) {
|
|
51
|
+
let truePositives = 0;
|
|
52
|
+
let recommendedTotal = 0;
|
|
53
|
+
let failuresTotal = 0;
|
|
54
|
+
let escapedTotal = 0;
|
|
55
|
+
for (const entry of entries) {
|
|
56
|
+
const recommended = asSet(entry.recommendedTests || []);
|
|
57
|
+
const failed = asSet(entry.failedTests || []);
|
|
58
|
+
const escaped = asSet(entry.escapedFailures || []);
|
|
59
|
+
const tp = Array.from(recommended).filter((test) => failed.has(test)).length;
|
|
60
|
+
truePositives += tp;
|
|
61
|
+
recommendedTotal += recommended.size;
|
|
62
|
+
failuresTotal += failed.size;
|
|
63
|
+
escapedTotal += escaped.size;
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
precision: ratio(truePositives, recommendedTotal),
|
|
67
|
+
recall: ratio(truePositives, failuresTotal),
|
|
68
|
+
falseNegativeRate: ratio(escapedTotal, failuresTotal + escapedTotal),
|
|
69
|
+
samples: entries.length,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function aggregate(entries) {
|
|
73
|
+
const subsystemAcc = new Map();
|
|
74
|
+
for (const entry of entries) {
|
|
75
|
+
const recommended = asSet(entry.recommendedTests || []);
|
|
76
|
+
const failed = asSet(entry.failedTests || []);
|
|
77
|
+
const escaped = asSet(entry.escapedFailures || []);
|
|
78
|
+
const allSubsystems = new Set([
|
|
79
|
+
...Array.from(recommended).map(subsystemForTest),
|
|
80
|
+
...Array.from(failed).map(subsystemForTest),
|
|
81
|
+
...Array.from(escaped).map(subsystemForTest),
|
|
82
|
+
]);
|
|
83
|
+
for (const subsystem of allSubsystems) {
|
|
84
|
+
const bucket = subsystemAcc.get(subsystem) || { entries: [] };
|
|
85
|
+
bucket.entries.push({
|
|
86
|
+
...entry,
|
|
87
|
+
recommendedTests: Array.from(recommended).filter((test) => subsystemForTest(test) === subsystem),
|
|
88
|
+
executedTests: (entry.executedTests || []).filter((test) => subsystemForTest(test) === subsystem),
|
|
89
|
+
failedTests: Array.from(failed).filter((test) => subsystemForTest(test) === subsystem),
|
|
90
|
+
escapedFailures: Array.from(escaped).filter((test) => subsystemForTest(test) === subsystem),
|
|
91
|
+
});
|
|
92
|
+
subsystemAcc.set(subsystem, bucket);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const bySubsystem = {};
|
|
96
|
+
for (const [subsystem, bucket] of subsystemAcc.entries()) {
|
|
97
|
+
const all = aggregateMetrics(bucket.entries);
|
|
98
|
+
const recent7d = aggregateMetrics(filterRecent(bucket.entries, 7));
|
|
99
|
+
const recent30d = aggregateMetrics(filterRecent(bucket.entries, 30));
|
|
100
|
+
bySubsystem[subsystem] = {
|
|
101
|
+
precision: all.precision,
|
|
102
|
+
recall: all.recall,
|
|
103
|
+
falseNegativeRate: all.falseNegativeRate,
|
|
104
|
+
samples: all.samples,
|
|
105
|
+
recent7d,
|
|
106
|
+
recent30d,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
const overall = aggregateMetrics(entries);
|
|
110
|
+
const recent7d = aggregateMetrics(filterRecent(entries, 7));
|
|
111
|
+
const recent30d = aggregateMetrics(filterRecent(entries, 30));
|
|
112
|
+
return {
|
|
113
|
+
schemaVersion: '1.1.0',
|
|
114
|
+
generatedAt: new Date().toISOString(),
|
|
115
|
+
samples: entries.length,
|
|
116
|
+
overall,
|
|
117
|
+
recent7d,
|
|
118
|
+
recent30d,
|
|
119
|
+
bySubsystem,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function rateFor(runs, failed) {
|
|
123
|
+
return runs > 0 ? Number((failed / runs).toFixed(4)) : 0;
|
|
124
|
+
}
|
|
125
|
+
function trendFor(rate7d, rate30d) {
|
|
126
|
+
if (rate7d - rate30d >= 0.08) {
|
|
127
|
+
return 'up';
|
|
128
|
+
}
|
|
129
|
+
if (rate30d - rate7d >= 0.08) {
|
|
130
|
+
return 'down';
|
|
131
|
+
}
|
|
132
|
+
return 'stable';
|
|
133
|
+
}
|
|
134
|
+
function loadOwners(appRoot) {
|
|
135
|
+
const path = (0, path_1.join)(appRoot, '.e2e-ai-agents', 'subsystem-owners.json');
|
|
136
|
+
const manifest = readJson(path);
|
|
137
|
+
if (!manifest) {
|
|
138
|
+
return {};
|
|
139
|
+
}
|
|
140
|
+
if (manifest.ownersBySubsystem) {
|
|
141
|
+
return manifest.ownersBySubsystem;
|
|
142
|
+
}
|
|
143
|
+
if (manifest.subsystems) {
|
|
144
|
+
return manifest.subsystems;
|
|
145
|
+
}
|
|
146
|
+
return {};
|
|
147
|
+
}
|
|
148
|
+
function daysSince(value) {
|
|
149
|
+
if (!value) {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
const time = parseTimestamp(value);
|
|
153
|
+
if (time === null) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
return (Date.now() - time) / (24 * 60 * 60 * 1000);
|
|
157
|
+
}
|
|
158
|
+
function aggregateFlaky(entries, appRoot) {
|
|
159
|
+
const acc = new Map();
|
|
160
|
+
const cutoff7 = Date.now() - 7 * 24 * 60 * 60 * 1000;
|
|
161
|
+
const cutoff30 = Date.now() - 30 * 24 * 60 * 60 * 1000;
|
|
162
|
+
const ownersBySubsystem = loadOwners(appRoot);
|
|
163
|
+
for (const entry of entries) {
|
|
164
|
+
const executed = asSet(entry.executedTests || []);
|
|
165
|
+
const failed = asSet(entry.failedTests || []);
|
|
166
|
+
const time = parseTimestamp(entry.timestamp);
|
|
167
|
+
for (const test of executed) {
|
|
168
|
+
const bucket = acc.get(test) || {
|
|
169
|
+
runs: 0,
|
|
170
|
+
failed: 0,
|
|
171
|
+
runs7d: 0,
|
|
172
|
+
failed7d: 0,
|
|
173
|
+
runs30d: 0,
|
|
174
|
+
failed30d: 0,
|
|
175
|
+
lastFailureAt: undefined,
|
|
176
|
+
};
|
|
177
|
+
bucket.runs += 1;
|
|
178
|
+
if (time !== null && time >= cutoff7) {
|
|
179
|
+
bucket.runs7d += 1;
|
|
180
|
+
}
|
|
181
|
+
if (time !== null && time >= cutoff30) {
|
|
182
|
+
bucket.runs30d += 1;
|
|
183
|
+
}
|
|
184
|
+
if (failed.has(test)) {
|
|
185
|
+
bucket.failed += 1;
|
|
186
|
+
bucket.lastFailureAt = entry.timestamp;
|
|
187
|
+
if (time !== null && time >= cutoff7) {
|
|
188
|
+
bucket.failed7d += 1;
|
|
189
|
+
}
|
|
190
|
+
if (time !== null && time >= cutoff30) {
|
|
191
|
+
bucket.failed30d += 1;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
acc.set(test, bucket);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
const tests = Array.from(acc.entries())
|
|
198
|
+
.map(([test, bucket]) => {
|
|
199
|
+
const flakeRate = rateFor(bucket.runs, bucket.failed);
|
|
200
|
+
const flakeRate7d = rateFor(bucket.runs7d, bucket.failed7d);
|
|
201
|
+
const flakeRate30d = rateFor(bucket.runs30d, bucket.failed30d);
|
|
202
|
+
const trend = trendFor(flakeRate7d, flakeRate30d);
|
|
203
|
+
const quarantine = (bucket.runs30d >= 5 && flakeRate30d >= 0.35) || (bucket.runs >= 8 && flakeRate >= 0.4);
|
|
204
|
+
const daysFromLastFailure = daysSince(bucket.lastFailureAt);
|
|
205
|
+
const quarantineState = quarantine
|
|
206
|
+
? (daysFromLastFailure !== null && daysFromLastFailure >= 14 && flakeRate7d <= 0.05 ? 'retire-candidate' : 'active')
|
|
207
|
+
: 'none';
|
|
208
|
+
const subsystem = subsystemForTest(test);
|
|
209
|
+
const owners = ownersBySubsystem[subsystem] || [];
|
|
210
|
+
return {
|
|
211
|
+
test,
|
|
212
|
+
subsystem,
|
|
213
|
+
owners,
|
|
214
|
+
flakeRate,
|
|
215
|
+
flakeRate7d,
|
|
216
|
+
flakeRate30d,
|
|
217
|
+
trend,
|
|
218
|
+
quarantine,
|
|
219
|
+
quarantineState,
|
|
220
|
+
lastFailureAt: bucket.lastFailureAt,
|
|
221
|
+
samples: bucket.runs,
|
|
222
|
+
samples7d: bucket.runs7d,
|
|
223
|
+
samples30d: bucket.runs30d,
|
|
224
|
+
};
|
|
225
|
+
})
|
|
226
|
+
.filter((entry) => entry.flakeRate > 0)
|
|
227
|
+
.sort((a, b) => (b.flakeRate30d || b.flakeRate) - (a.flakeRate30d || a.flakeRate));
|
|
228
|
+
return {
|
|
229
|
+
schemaVersion: '1.1.0',
|
|
230
|
+
generatedAt: new Date().toISOString(),
|
|
231
|
+
tests,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
function appendFeedbackAndRecompute(appRoot, input) {
|
|
235
|
+
const baseDir = (0, path_1.join)(appRoot, '.e2e-ai-agents');
|
|
236
|
+
(0, fs_1.mkdirSync)(baseDir, { recursive: true });
|
|
237
|
+
const feedbackPath = (0, path_1.join)(baseDir, 'feedback.json');
|
|
238
|
+
const existing = readJson(feedbackPath) || { schemaVersion: '1.0.0', entries: [] };
|
|
239
|
+
existing.entries.push({
|
|
240
|
+
...input,
|
|
241
|
+
recommendedTests: input.recommendedTests || [],
|
|
242
|
+
executedTests: input.executedTests || [],
|
|
243
|
+
failedTests: input.failedTests || [],
|
|
244
|
+
escapedFailures: input.escapedFailures || [],
|
|
245
|
+
});
|
|
246
|
+
(0, fs_1.writeFileSync)(feedbackPath, JSON.stringify(existing, null, 2), 'utf-8');
|
|
247
|
+
const calibration = aggregate(existing.entries);
|
|
248
|
+
const calibrationPath = (0, path_1.join)(baseDir, 'calibration.json');
|
|
249
|
+
(0, fs_1.writeFileSync)(calibrationPath, JSON.stringify(calibration, null, 2), 'utf-8');
|
|
250
|
+
const flaky = aggregateFlaky(existing.entries, appRoot);
|
|
251
|
+
const flakyPath = (0, path_1.join)(baseDir, 'flaky-tests.json');
|
|
252
|
+
(0, fs_1.writeFileSync)(flakyPath, JSON.stringify(flaky, null, 2), 'utf-8');
|
|
253
|
+
return { feedbackPath, calibrationPath, calibration };
|
|
254
|
+
}
|
|
255
|
+
function readCalibration(appRoot) {
|
|
256
|
+
return readJson((0, path_1.join)(appRoot, '.e2e-ai-agents', 'calibration.json'));
|
|
257
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { AgentConfig, AudienceRole, FlagState } from './config.js';
|
|
2
|
+
export type FlagSource = 'featureFlag' | 'configFlag' | 'testGate';
|
|
3
|
+
export interface FlagHit {
|
|
4
|
+
name: string;
|
|
5
|
+
source: FlagSource;
|
|
6
|
+
defaultState: FlagState;
|
|
7
|
+
}
|
|
8
|
+
export interface BlastRadius {
|
|
9
|
+
audience: AudienceRole[];
|
|
10
|
+
flags: FlagHit[];
|
|
11
|
+
summary: string;
|
|
12
|
+
scoreDelta: number;
|
|
13
|
+
}
|
|
14
|
+
export declare function normalizeRole(role: string): AudienceRole | null;
|
|
15
|
+
export declare function normalizeRoles(roles: string[], fallback: AudienceRole[]): AudienceRole[];
|
|
16
|
+
export declare function normalizeFlagSource(source?: string): FlagSource;
|
|
17
|
+
export declare function normalizeFlagState(value: string | undefined, fallback: FlagState): FlagState;
|
|
18
|
+
export declare function mergeFlags(flags: FlagHit[], defaultState: FlagState): FlagHit[];
|
|
19
|
+
export declare function extractFlagHits(content: string | null, config: AgentConfig): FlagHit[];
|
|
20
|
+
export declare function inferAudienceFromPath(relativePath: string, config: AgentConfig): AudienceRole[];
|
|
21
|
+
export declare function formatFlags(flags: FlagHit[]): string;
|
|
22
|
+
export declare function computeBlastRadius(audience: AudienceRole[], flags: FlagHit[], config: AgentConfig): BlastRadius;
|
|
23
|
+
//# sourceMappingURL=flags.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flags.d.ts","sourceRoot":"","sources":["../../src/agent/flags.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,WAAW,EAAE,YAAY,EAAE,SAAS,EAAC,MAAM,aAAa,CAAC;AAEtE,MAAM,MAAM,UAAU,GAAG,aAAa,GAAG,YAAY,GAAG,UAAU,CAAC;AAEnE,MAAM,WAAW,OAAO;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,CAAC;IACnB,YAAY,EAAE,SAAS,CAAC;CAC3B;AAED,MAAM,WAAW,WAAW;IACxB,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACtB;AAkCD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAG/D;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,YAAY,EAAE,CAOxF;AAED,wBAAgB,mBAAmB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,UAAU,CAe/D;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,SAAS,GAAG,SAAS,CAS5F;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,YAAY,EAAE,SAAS,GAAG,OAAO,EAAE,CAY/E;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,EAAE,CAiCtF;AAED,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,YAAY,EAAE,CAa/F;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,MAAM,CAKpD;AAED,wBAAgB,kBAAkB,CAC9B,QAAQ,EAAE,YAAY,EAAE,EACxB,KAAK,EAAE,OAAO,EAAE,EAChB,MAAM,EAAE,WAAW,GACpB,WAAW,CAqCb"}
|