@scenetest/scenes 0.1.0
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 +21 -0
- package/dist/__tests__/devices.test.d.ts +2 -0
- package/dist/__tests__/devices.test.d.ts.map +1 -0
- package/dist/__tests__/devices.test.js +117 -0
- package/dist/__tests__/devices.test.js.map +1 -0
- package/dist/__tests__/dsl.test.d.ts +2 -0
- package/dist/__tests__/dsl.test.d.ts.map +1 -0
- package/dist/__tests__/dsl.test.js +385 -0
- package/dist/__tests__/dsl.test.js.map +1 -0
- package/dist/__tests__/markdown-scene.test.d.ts +2 -0
- package/dist/__tests__/markdown-scene.test.d.ts.map +1 -0
- package/dist/__tests__/markdown-scene.test.js +508 -0
- package/dist/__tests__/markdown-scene.test.js.map +1 -0
- package/dist/__tests__/reactive.test.d.ts +2 -0
- package/dist/__tests__/reactive.test.d.ts.map +1 -0
- package/dist/__tests__/reactive.test.js +383 -0
- package/dist/__tests__/reactive.test.js.map +1 -0
- package/dist/__tests__/swarm.test.d.ts +2 -0
- package/dist/__tests__/swarm.test.d.ts.map +1 -0
- package/dist/__tests__/swarm.test.js +214 -0
- package/dist/__tests__/swarm.test.js.map +1 -0
- package/dist/actor.d.ts +104 -0
- package/dist/actor.d.ts.map +1 -0
- package/dist/actor.js +527 -0
- package/dist/actor.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +273 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +21 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +120 -0
- package/dist/config.js.map +1 -0
- package/dist/devices.d.ts +55 -0
- package/dist/devices.d.ts.map +1 -0
- package/dist/devices.js +167 -0
- package/dist/devices.js.map +1 -0
- package/dist/dsl.d.ts +99 -0
- package/dist/dsl.d.ts.map +1 -0
- package/dist/dsl.js +247 -0
- package/dist/dsl.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +9 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +27 -0
- package/dist/init.js.map +1 -0
- package/dist/loader.d.ts +2 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +10 -0
- package/dist/loader.js.map +1 -0
- package/dist/markdown-scene.d.ts +120 -0
- package/dist/markdown-scene.d.ts.map +1 -0
- package/dist/markdown-scene.js +452 -0
- package/dist/markdown-scene.js.map +1 -0
- package/dist/message-bus.d.ts +31 -0
- package/dist/message-bus.d.ts.map +1 -0
- package/dist/message-bus.js +74 -0
- package/dist/message-bus.js.map +1 -0
- package/dist/reactive.d.ts +267 -0
- package/dist/reactive.d.ts.map +1 -0
- package/dist/reactive.js +779 -0
- package/dist/reactive.js.map +1 -0
- package/dist/runner.d.ts +51 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +306 -0
- package/dist/runner.js.map +1 -0
- package/dist/scene.d.ts +40 -0
- package/dist/scene.d.ts.map +1 -0
- package/dist/scene.js +110 -0
- package/dist/scene.js.map +1 -0
- package/dist/selectors.d.ts +57 -0
- package/dist/selectors.d.ts.map +1 -0
- package/dist/selectors.js +193 -0
- package/dist/selectors.js.map +1 -0
- package/dist/swarm.d.ts +64 -0
- package/dist/swarm.d.ts.map +1 -0
- package/dist/swarm.js +306 -0
- package/dist/swarm.js.map +1 -0
- package/dist/team-manager.d.ts +120 -0
- package/dist/team-manager.d.ts.map +1 -0
- package/dist/team-manager.js +267 -0
- package/dist/team-manager.js.map +1 -0
- package/dist/types.d.ts +653 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global alias registry
|
|
3
|
+
*/
|
|
4
|
+
let globalAliases = {};
|
|
5
|
+
/**
|
|
6
|
+
* Set the global selector aliases
|
|
7
|
+
*/
|
|
8
|
+
export function setAliases(aliases) {
|
|
9
|
+
globalAliases = aliases;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Get the current aliases
|
|
13
|
+
*/
|
|
14
|
+
export function getAliases() {
|
|
15
|
+
return globalAliases;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Clear all aliases
|
|
19
|
+
*/
|
|
20
|
+
export function clearAliases() {
|
|
21
|
+
globalAliases = {};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Build CSS selector for a single token.
|
|
25
|
+
*
|
|
26
|
+
* Resolution order:
|
|
27
|
+
* 1. aria-label - Accessibility label (encouraged for interactive elements)
|
|
28
|
+
* 2. id - DOM id attribute
|
|
29
|
+
* 3. data-testid - Explicit test identifier
|
|
30
|
+
* 4. data-name - Custom name attribute
|
|
31
|
+
* 5. data-key - Key for list items
|
|
32
|
+
* 6. name - Form element name
|
|
33
|
+
*/
|
|
34
|
+
function buildTokenSelector(token) {
|
|
35
|
+
return [
|
|
36
|
+
`[aria-label="${token}"]`,
|
|
37
|
+
`[id="${token}"]`,
|
|
38
|
+
`[data-testid="${token}"]`,
|
|
39
|
+
`[data-name="${token}"]`,
|
|
40
|
+
`[data-key="${token}"]`,
|
|
41
|
+
`[name="${token}"]`,
|
|
42
|
+
].join(', ');
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Resolve a single token to a locator.
|
|
46
|
+
* Handles aliases (~) and explicit aria-labels (@).
|
|
47
|
+
*/
|
|
48
|
+
function resolveToken(base, token) {
|
|
49
|
+
// Check if it's an alias (starts with ~)
|
|
50
|
+
if (token.startsWith('~')) {
|
|
51
|
+
const aliasName = token.slice(1);
|
|
52
|
+
const aliasValue = globalAliases[aliasName];
|
|
53
|
+
if (!aliasValue) {
|
|
54
|
+
throw new Error(`Unknown alias: ${aliasName}`);
|
|
55
|
+
}
|
|
56
|
+
return base.locator(aliasValue);
|
|
57
|
+
}
|
|
58
|
+
// Check if it's explicitly an aria-label (starts with @)
|
|
59
|
+
if (token.startsWith('@')) {
|
|
60
|
+
const label = token.slice(1);
|
|
61
|
+
return base.locator(`[aria-label="${label}"]`);
|
|
62
|
+
}
|
|
63
|
+
// Default: try all attribute types
|
|
64
|
+
return base.locator(buildTokenSelector(token)).first();
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Resolve a space-separated selector string.
|
|
68
|
+
*
|
|
69
|
+
* Algorithm:
|
|
70
|
+
* 1. Split into tokens
|
|
71
|
+
* 2. For each token, find matching element
|
|
72
|
+
* 3. After matching, check if SAME element has data-key matching NEXT token
|
|
73
|
+
* - If yes: consume that token (it's a key match), continue with token after
|
|
74
|
+
* - If no: descend into children for next token
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts
|
|
78
|
+
* // Simple
|
|
79
|
+
* resolveSelector(page, 'button')
|
|
80
|
+
*
|
|
81
|
+
* // Nested
|
|
82
|
+
* resolveSelector(page, 'modal form submit-button')
|
|
83
|
+
*
|
|
84
|
+
* // With implicit key matching
|
|
85
|
+
* resolveSelector(page, 'playlist-row 12345 like-button')
|
|
86
|
+
* // Finds playlist-row, checks if it has data-key="12345",
|
|
87
|
+
* // if yes stays on same element, then finds like-button child
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
export function resolveSelector(base, selector) {
|
|
91
|
+
const tokens = selector.trim().split(/\s+/);
|
|
92
|
+
if (tokens.length === 0) {
|
|
93
|
+
throw new Error('Empty selector');
|
|
94
|
+
}
|
|
95
|
+
let locator = resolveToken(base, tokens[0]);
|
|
96
|
+
let i = 1;
|
|
97
|
+
while (i < tokens.length) {
|
|
98
|
+
const nextToken = tokens[i];
|
|
99
|
+
// Check if current element has data-key matching the next token
|
|
100
|
+
// This allows 'playlist-row 12345' to match a single element with both attributes
|
|
101
|
+
const withKey = locator.filter({ has: base.locator(`[data-key="${nextToken}"]`) });
|
|
102
|
+
// We need to check if the current locator itself has the data-key
|
|
103
|
+
// Use a more direct approach: check if locator[data-key=nextToken] exists
|
|
104
|
+
const sameElementWithKey = locator.locator(`xpath=self::*[@data-key="${nextToken}"]`);
|
|
105
|
+
// Try to resolve: does the current element have this data-key?
|
|
106
|
+
// We'll build a locator that checks, and use it in the chain
|
|
107
|
+
// Since we can't do async checks here, we use a filter approach
|
|
108
|
+
// Actually, let's use a simpler approach:
|
|
109
|
+
// Build a locator that tries both interpretations and takes the first match
|
|
110
|
+
// But that's complex. Let's just always check for key on same element first.
|
|
111
|
+
// Simpler: use locator chaining with 'or'
|
|
112
|
+
// Option 1: Same element has data-key=nextToken (consume token, stay on element)
|
|
113
|
+
// Option 2: Child matches nextToken (descend)
|
|
114
|
+
// We'll prioritize: if current element has data-key matching next token,
|
|
115
|
+
// treat it as a key match and skip to the token after
|
|
116
|
+
// This is done by checking self::*[@data-key=...]
|
|
117
|
+
// For now, use a practical approach: try to find child, but also support
|
|
118
|
+
// the key-on-same-element pattern by including data-key in the resolution
|
|
119
|
+
// Actually the cleanest way: always descend, but data-key IS in the attribute list
|
|
120
|
+
// So 'playlist-row 12345' will:
|
|
121
|
+
// 1. Find playlist-row (matches data-name or similar)
|
|
122
|
+
// 2. Look for '12345' in children - but wait, it might be on same element!
|
|
123
|
+
// The user's algorithm says: after matching token1, check if THAT SAME ELEMENT
|
|
124
|
+
// has data-key=token2. If yes, stay on same element and consume token2.
|
|
125
|
+
// We need to do this check. Let's restructure:
|
|
126
|
+
// Check if current locator's element has data-key = nextToken
|
|
127
|
+
// We can do this with a self:: xpath or by checking the attribute
|
|
128
|
+
// Build combined locator:
|
|
129
|
+
// Either: current element has data-key=nextToken (stay, consume)
|
|
130
|
+
// Or: find child matching nextToken (descend)
|
|
131
|
+
// Use Playwright's .or() to combine
|
|
132
|
+
const stayOnCurrent = locator.locator(`xpath=self::*[@data-key="${nextToken}"]`);
|
|
133
|
+
const descendToChild = locator.locator(buildTokenSelector(nextToken)).first();
|
|
134
|
+
// Prefer staying on current if it has the key, otherwise descend
|
|
135
|
+
locator = stayOnCurrent.or(descendToChild).first();
|
|
136
|
+
i++;
|
|
137
|
+
}
|
|
138
|
+
return locator;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Get debugging info about what a selector would match.
|
|
142
|
+
* Useful for the debug selector explorer.
|
|
143
|
+
*/
|
|
144
|
+
export async function explainSelector(page, selector) {
|
|
145
|
+
const locator = resolveSelector(page, selector);
|
|
146
|
+
const count = await locator.count();
|
|
147
|
+
const matches = [];
|
|
148
|
+
// Get info about matching elements
|
|
149
|
+
for (let i = 0; i < Math.min(count, 5); i++) {
|
|
150
|
+
const element = locator.nth(i);
|
|
151
|
+
const tag = await element.evaluate((el) => el.tagName.toLowerCase());
|
|
152
|
+
const text = (await element.textContent()) || '';
|
|
153
|
+
const attributes = await element.evaluate((el) => {
|
|
154
|
+
const attrs = {};
|
|
155
|
+
for (const attr of el.attributes) {
|
|
156
|
+
attrs[attr.name] = attr.value;
|
|
157
|
+
}
|
|
158
|
+
return attrs;
|
|
159
|
+
});
|
|
160
|
+
matches.push({ tag, attributes, text: text.slice(0, 100) });
|
|
161
|
+
}
|
|
162
|
+
// Generate suggestions if not found
|
|
163
|
+
const suggestions = [];
|
|
164
|
+
if (count === 0) {
|
|
165
|
+
const firstToken = selector.trim().split(/\s+/)[0];
|
|
166
|
+
// Look for similar elements
|
|
167
|
+
const similarByAriaLabel = await page.locator(`[aria-label*="${firstToken}"]`).all();
|
|
168
|
+
for (const el of similarByAriaLabel.slice(0, 3)) {
|
|
169
|
+
const label = await el.getAttribute('aria-label');
|
|
170
|
+
if (label)
|
|
171
|
+
suggestions.push(label);
|
|
172
|
+
}
|
|
173
|
+
const similarByTestId = await page.locator(`[data-testid*="${firstToken}"]`).all();
|
|
174
|
+
for (const el of similarByTestId.slice(0, 3)) {
|
|
175
|
+
const testid = await el.getAttribute('data-testid');
|
|
176
|
+
if (testid)
|
|
177
|
+
suggestions.push(testid);
|
|
178
|
+
}
|
|
179
|
+
const similarByName = await page.locator(`[data-name*="${firstToken}"]`).all();
|
|
180
|
+
for (const el of similarByName.slice(0, 3)) {
|
|
181
|
+
const name = await el.getAttribute('data-name');
|
|
182
|
+
if (name)
|
|
183
|
+
suggestions.push(name);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
found: count > 0,
|
|
188
|
+
count,
|
|
189
|
+
matches,
|
|
190
|
+
suggestions: [...new Set(suggestions)],
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=selectors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selectors.js","sourceRoot":"","sources":["../src/selectors.ts"],"names":[],"mappings":"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"}
|
package/dist/swarm.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { SwarmConfig, SwarmReport, RunReport, RegisteredScene } from './types.js';
|
|
2
|
+
import type { TeamManager } from './team-manager.js';
|
|
3
|
+
/**
|
|
4
|
+
* Resolve partial swarm config with defaults
|
|
5
|
+
*/
|
|
6
|
+
export declare function resolveSwarmConfig(config?: SwarmConfig): Required<SwarmConfig>;
|
|
7
|
+
/**
|
|
8
|
+
* Tracks failure history across runs for a scene.
|
|
9
|
+
* Used to determine whether swarm mode should be triggered.
|
|
10
|
+
*/
|
|
11
|
+
interface SceneFailureRecord {
|
|
12
|
+
/** Scene name */
|
|
13
|
+
name: string;
|
|
14
|
+
/** Recent run outcomes (true = passed, false = failed). Most recent last. */
|
|
15
|
+
history: boolean[];
|
|
16
|
+
/** Count of consecutive failures at the tail of history */
|
|
17
|
+
consecutiveFailures: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Evaluates whether swarm mode should be auto-triggered based on
|
|
21
|
+
* recent run history. Tracks failures across runs and checks whether
|
|
22
|
+
* any scene has exceeded the failure threshold within the window.
|
|
23
|
+
*/
|
|
24
|
+
export declare class SwarmTrigger {
|
|
25
|
+
private records;
|
|
26
|
+
private config;
|
|
27
|
+
constructor(config?: SwarmConfig);
|
|
28
|
+
/**
|
|
29
|
+
* Record the results of a run. Call this after each normal run completes.
|
|
30
|
+
*/
|
|
31
|
+
recordRun(report: RunReport): void;
|
|
32
|
+
/**
|
|
33
|
+
* Check whether swarm mode should be triggered.
|
|
34
|
+
* Returns the names of scenes that have exceeded the failure threshold.
|
|
35
|
+
*/
|
|
36
|
+
shouldTrigger(): string[];
|
|
37
|
+
/**
|
|
38
|
+
* Get the failure record for a scene
|
|
39
|
+
*/
|
|
40
|
+
getRecord(name: string): SceneFailureRecord | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Clear all tracking data
|
|
43
|
+
*/
|
|
44
|
+
clear(): void;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Run swarm mode: execute scenes across ALL teams with multiple repeats.
|
|
48
|
+
*
|
|
49
|
+
* This is the diagnostic mode that runs when failures persist beyond
|
|
50
|
+
* the threshold. It tests every scene against every team multiple times
|
|
51
|
+
* to classify failures as broken, flaky, or seed-data edge cases.
|
|
52
|
+
*
|
|
53
|
+
* @param scenes - Scenes to swarm (all registered, or just the triggering ones)
|
|
54
|
+
* @param teamManager - The team manager with all teams
|
|
55
|
+
* @param config - Swarm configuration
|
|
56
|
+
* @param timeout - Scene timeout
|
|
57
|
+
* @param actionTimeout - Per-action timeout
|
|
58
|
+
* @param warnAfter - Warn threshold
|
|
59
|
+
* @param baseUrl - Application base URL
|
|
60
|
+
* @param trigger - Whether this was auto-triggered or manually requested
|
|
61
|
+
*/
|
|
62
|
+
export declare function runSwarm(scenes: RegisteredScene[], teamManager: TeamManager, config: SwarmConfig | undefined, timeout: number, actionTimeout: number, warnAfter: number, baseUrl: string | undefined, trigger: 'auto' | 'manual'): Promise<SwarmReport>;
|
|
63
|
+
export {};
|
|
64
|
+
//# sourceMappingURL=swarm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"swarm.d.ts","sourceRoot":"","sources":["../src/swarm.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,WAAW,EAIX,SAAS,EAET,eAAe,EAChB,MAAM,YAAY,CAAA;AACnB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAcpD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,QAAQ,CAAC,WAAW,CAAC,CAE9E;AAID;;;GAGG;AACH,UAAU,kBAAkB;IAC1B,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,6EAA6E;IAC7E,OAAO,EAAE,OAAO,EAAE,CAAA;IAClB,2DAA2D;IAC3D,mBAAmB,EAAE,MAAM,CAAA;CAC5B;AAED;;;;GAIG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAwC;IACvD,OAAO,CAAC,MAAM,CAAuB;gBAEzB,MAAM,CAAC,EAAE,WAAW;IAIhC;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IA4BlC;;;OAGG;IACH,aAAa,IAAI,MAAM,EAAE;IAYzB;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAIvD;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd;AAID;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,eAAe,EAAE,EACzB,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,OAAO,EAAE,MAAM,GAAG,QAAQ,GACzB,OAAO,CAAC,WAAW,CAAC,CAiJtB"}
|
package/dist/swarm.js
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { runScene } from './scene.js';
|
|
2
|
+
/**
|
|
3
|
+
* Default swarm configuration values
|
|
4
|
+
*/
|
|
5
|
+
const swarmDefaults = {
|
|
6
|
+
failureThreshold: 5,
|
|
7
|
+
windowSize: 3,
|
|
8
|
+
concurrency: Infinity,
|
|
9
|
+
repeats: 3,
|
|
10
|
+
auto: true,
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Resolve partial swarm config with defaults
|
|
14
|
+
*/
|
|
15
|
+
export function resolveSwarmConfig(config) {
|
|
16
|
+
return { ...swarmDefaults, ...config };
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Evaluates whether swarm mode should be auto-triggered based on
|
|
20
|
+
* recent run history. Tracks failures across runs and checks whether
|
|
21
|
+
* any scene has exceeded the failure threshold within the window.
|
|
22
|
+
*/
|
|
23
|
+
export class SwarmTrigger {
|
|
24
|
+
records = new Map();
|
|
25
|
+
config;
|
|
26
|
+
constructor(config) {
|
|
27
|
+
this.config = resolveSwarmConfig(config);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Record the results of a run. Call this after each normal run completes.
|
|
31
|
+
*/
|
|
32
|
+
recordRun(report) {
|
|
33
|
+
for (const scene of report.scenes) {
|
|
34
|
+
const passed = scene.status === 'completed' && scene.assertions.every((a) => a.result);
|
|
35
|
+
let record = this.records.get(scene.name);
|
|
36
|
+
if (!record) {
|
|
37
|
+
record = { name: scene.name, history: [], consecutiveFailures: 0 };
|
|
38
|
+
this.records.set(scene.name, record);
|
|
39
|
+
}
|
|
40
|
+
record.history.push(passed);
|
|
41
|
+
// Trim history to window size
|
|
42
|
+
if (record.history.length > this.config.windowSize) {
|
|
43
|
+
record.history = record.history.slice(-this.config.windowSize);
|
|
44
|
+
}
|
|
45
|
+
// Count consecutive failures from the end
|
|
46
|
+
record.consecutiveFailures = 0;
|
|
47
|
+
for (let i = record.history.length - 1; i >= 0; i--) {
|
|
48
|
+
if (!record.history[i]) {
|
|
49
|
+
record.consecutiveFailures++;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Check whether swarm mode should be triggered.
|
|
59
|
+
* Returns the names of scenes that have exceeded the failure threshold.
|
|
60
|
+
*/
|
|
61
|
+
shouldTrigger() {
|
|
62
|
+
if (!this.config.auto)
|
|
63
|
+
return [];
|
|
64
|
+
const triggering = [];
|
|
65
|
+
for (const [name, record] of this.records) {
|
|
66
|
+
if (record.consecutiveFailures >= this.config.failureThreshold) {
|
|
67
|
+
triggering.push(name);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return triggering;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Get the failure record for a scene
|
|
74
|
+
*/
|
|
75
|
+
getRecord(name) {
|
|
76
|
+
return this.records.get(name);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Clear all tracking data
|
|
80
|
+
*/
|
|
81
|
+
clear() {
|
|
82
|
+
this.records.clear();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// ─── Swarm Execution ─────────────────────────────────────────────────
|
|
86
|
+
/**
|
|
87
|
+
* Run swarm mode: execute scenes across ALL teams with multiple repeats.
|
|
88
|
+
*
|
|
89
|
+
* This is the diagnostic mode that runs when failures persist beyond
|
|
90
|
+
* the threshold. It tests every scene against every team multiple times
|
|
91
|
+
* to classify failures as broken, flaky, or seed-data edge cases.
|
|
92
|
+
*
|
|
93
|
+
* @param scenes - Scenes to swarm (all registered, or just the triggering ones)
|
|
94
|
+
* @param teamManager - The team manager with all teams
|
|
95
|
+
* @param config - Swarm configuration
|
|
96
|
+
* @param timeout - Scene timeout
|
|
97
|
+
* @param actionTimeout - Per-action timeout
|
|
98
|
+
* @param warnAfter - Warn threshold
|
|
99
|
+
* @param baseUrl - Application base URL
|
|
100
|
+
* @param trigger - Whether this was auto-triggered or manually requested
|
|
101
|
+
*/
|
|
102
|
+
export async function runSwarm(scenes, teamManager, config, timeout, actionTimeout, warnAfter, baseUrl, trigger) {
|
|
103
|
+
const resolved = resolveSwarmConfig(config);
|
|
104
|
+
const totalTeams = teamManager.totalCount;
|
|
105
|
+
const concurrency = Math.min(resolved.concurrency, totalTeams);
|
|
106
|
+
console.log('\n' + '='.repeat(50));
|
|
107
|
+
console.log(' SWARM MODE');
|
|
108
|
+
console.log('='.repeat(50));
|
|
109
|
+
console.log(` Trigger: ${trigger}`);
|
|
110
|
+
console.log(` Scenes: ${scenes.length}`);
|
|
111
|
+
console.log(` Teams: ${totalTeams}`);
|
|
112
|
+
console.log(` Repeats: ${resolved.repeats}`);
|
|
113
|
+
console.log(` Concurrency: ${concurrency === Infinity ? 'unlimited' : concurrency}`);
|
|
114
|
+
console.log('='.repeat(50) + '\n');
|
|
115
|
+
const results = [];
|
|
116
|
+
for (const scene of scenes) {
|
|
117
|
+
console.log(` Swarming: ${scene.name}`);
|
|
118
|
+
const details = [];
|
|
119
|
+
const tasks = [];
|
|
120
|
+
for (let repeat = 0; repeat < resolved.repeats; repeat++) {
|
|
121
|
+
for (let teamIndex = 0; teamIndex < totalTeams; teamIndex++) {
|
|
122
|
+
tasks.push({ teamIndex, repeat });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// Execute with concurrency throttle
|
|
126
|
+
const semaphore = new Semaphore(concurrency);
|
|
127
|
+
const taskPromises = tasks.map(async (task) => {
|
|
128
|
+
await semaphore.acquire();
|
|
129
|
+
try {
|
|
130
|
+
// Wait for this specific team to be available
|
|
131
|
+
// (in swarm mode we bypass the normal acquire flow and
|
|
132
|
+
// coordinate at the task level)
|
|
133
|
+
while (true) {
|
|
134
|
+
const acquired = teamManager.acquire();
|
|
135
|
+
if (acquired === task.teamIndex)
|
|
136
|
+
break;
|
|
137
|
+
// We got a different team, release it
|
|
138
|
+
if (acquired !== null)
|
|
139
|
+
teamManager.release(acquired);
|
|
140
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
const session = await teamManager.createSession(task.teamIndex, actionTimeout, warnAfter, baseUrl);
|
|
144
|
+
try {
|
|
145
|
+
const report = await runScene(scene, session, timeout);
|
|
146
|
+
const detail = {
|
|
147
|
+
teamIndex: task.teamIndex,
|
|
148
|
+
repeat: task.repeat,
|
|
149
|
+
status: report.status,
|
|
150
|
+
duration: report.duration,
|
|
151
|
+
assertionsFailed: report.assertions.filter((a) => !a.result).length,
|
|
152
|
+
assertionsTotal: report.assertions.length,
|
|
153
|
+
error: report.error,
|
|
154
|
+
actors: {},
|
|
155
|
+
};
|
|
156
|
+
// Capture device info per actor
|
|
157
|
+
for (const [role] of session.getActors()) {
|
|
158
|
+
const device = session.getActorDevice(role);
|
|
159
|
+
detail.actors[role] = { device: device?.name };
|
|
160
|
+
}
|
|
161
|
+
details.push(detail);
|
|
162
|
+
const icon = report.status === 'completed' ? '.' : 'F';
|
|
163
|
+
process.stdout.write(icon);
|
|
164
|
+
}
|
|
165
|
+
finally {
|
|
166
|
+
await session.close();
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
finally {
|
|
170
|
+
teamManager.release(task.teamIndex);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
finally {
|
|
174
|
+
semaphore.release();
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
await Promise.all(taskPromises);
|
|
178
|
+
process.stdout.write('\n');
|
|
179
|
+
// Classify this scene
|
|
180
|
+
const passed = details.filter((d) => d.status === 'completed' && d.assertionsFailed === 0);
|
|
181
|
+
const failed = details.filter((d) => d.status !== 'completed' || d.assertionsFailed > 0);
|
|
182
|
+
const failingTeamSet = new Set(failed.map((d) => d.teamIndex));
|
|
183
|
+
const passingTeamSet = new Set(passed.map((d) => d.teamIndex));
|
|
184
|
+
const classification = classifyScene(details, totalTeams);
|
|
185
|
+
const result = {
|
|
186
|
+
name: scene.name,
|
|
187
|
+
file: scene.file,
|
|
188
|
+
runs: details.length,
|
|
189
|
+
passed: passed.length,
|
|
190
|
+
failed: failed.length,
|
|
191
|
+
failingTeams: [...failingTeamSet],
|
|
192
|
+
passingTeams: [...passingTeamSet],
|
|
193
|
+
classification,
|
|
194
|
+
details,
|
|
195
|
+
};
|
|
196
|
+
results.push(result);
|
|
197
|
+
console.log(` ${classificationLabel(classification)} (${passed.length}/${details.length} passed)`);
|
|
198
|
+
if (classification === 'seed-data-edge-case') {
|
|
199
|
+
console.log(` Failing teams: ${[...failingTeamSet].join(', ')}`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// Build summary
|
|
203
|
+
const summary = {
|
|
204
|
+
broken: results.filter((r) => r.classification === 'broken').length,
|
|
205
|
+
flaky: results.filter((r) => r.classification === 'flaky').length,
|
|
206
|
+
seedDataEdgeCase: results.filter((r) => r.classification === 'seed-data-edge-case').length,
|
|
207
|
+
healthy: results.filter((r) => r.classification === 'healthy').length,
|
|
208
|
+
};
|
|
209
|
+
console.log('\n' + '-'.repeat(50));
|
|
210
|
+
console.log(' Swarm Results');
|
|
211
|
+
console.log('-'.repeat(50));
|
|
212
|
+
if (summary.broken > 0)
|
|
213
|
+
console.log(` BROKEN: ${summary.broken} scene(s)`);
|
|
214
|
+
if (summary.flaky > 0)
|
|
215
|
+
console.log(` FLAKY: ${summary.flaky} scene(s)`);
|
|
216
|
+
if (summary.seedDataEdgeCase > 0)
|
|
217
|
+
console.log(` SEED DATA EDGE: ${summary.seedDataEdgeCase} scene(s)`);
|
|
218
|
+
if (summary.healthy > 0)
|
|
219
|
+
console.log(` HEALTHY: ${summary.healthy} scene(s)`);
|
|
220
|
+
console.log('-'.repeat(50) + '\n');
|
|
221
|
+
return { trigger, results, summary };
|
|
222
|
+
}
|
|
223
|
+
// ─── Classification ──────────────────────────────────────────────────
|
|
224
|
+
/**
|
|
225
|
+
* Classify a scene based on swarm run details.
|
|
226
|
+
*
|
|
227
|
+
* Logic:
|
|
228
|
+
* - If ALL runs failed → broken
|
|
229
|
+
* - If ALL runs passed → healthy
|
|
230
|
+
* - If failures are concentrated on specific teams (and other teams always pass)
|
|
231
|
+
* → seed-data-edge-case
|
|
232
|
+
* - Otherwise → flaky
|
|
233
|
+
*/
|
|
234
|
+
function classifyScene(details, totalTeams) {
|
|
235
|
+
const allFailed = details.every((d) => d.status !== 'completed' || d.assertionsFailed > 0);
|
|
236
|
+
const allPassed = details.every((d) => d.status === 'completed' && d.assertionsFailed === 0);
|
|
237
|
+
if (allPassed)
|
|
238
|
+
return 'healthy';
|
|
239
|
+
if (allFailed)
|
|
240
|
+
return 'broken';
|
|
241
|
+
// Check if failures are team-specific
|
|
242
|
+
// Group by team and check if some teams always fail while others always pass
|
|
243
|
+
const teamResults = new Map();
|
|
244
|
+
for (const detail of details) {
|
|
245
|
+
const entry = teamResults.get(detail.teamIndex) ?? { passed: 0, failed: 0 };
|
|
246
|
+
if (detail.status === 'completed' && detail.assertionsFailed === 0) {
|
|
247
|
+
entry.passed++;
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
entry.failed++;
|
|
251
|
+
}
|
|
252
|
+
teamResults.set(detail.teamIndex, entry);
|
|
253
|
+
}
|
|
254
|
+
// A team "always fails" if it never passed
|
|
255
|
+
// A team "always passes" if it never failed
|
|
256
|
+
const alwaysFailing = [...teamResults.entries()].filter(([, r]) => r.passed === 0);
|
|
257
|
+
const alwaysPassing = [...teamResults.entries()].filter(([, r]) => r.failed === 0);
|
|
258
|
+
// If there are teams that always fail AND teams that always pass,
|
|
259
|
+
// this looks like a seed-data edge case
|
|
260
|
+
if (alwaysFailing.length > 0 && alwaysPassing.length > 0) {
|
|
261
|
+
return 'seed-data-edge-case';
|
|
262
|
+
}
|
|
263
|
+
// Otherwise it's flaky — failures are spread across teams
|
|
264
|
+
return 'flaky';
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Human-readable label for a classification
|
|
268
|
+
*/
|
|
269
|
+
function classificationLabel(c) {
|
|
270
|
+
switch (c) {
|
|
271
|
+
case 'broken': return 'BROKEN - fails consistently';
|
|
272
|
+
case 'flaky': return 'FLAKY - intermittent failures';
|
|
273
|
+
case 'seed-data-edge-case': return 'SEED DATA EDGE CASE - team-specific failures';
|
|
274
|
+
case 'healthy': return 'HEALTHY - passes consistently';
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
// ─── Concurrency Control ─────────────────────────────────────────────
|
|
278
|
+
/**
|
|
279
|
+
* Simple counting semaphore for throttling concurrent work.
|
|
280
|
+
*/
|
|
281
|
+
class Semaphore {
|
|
282
|
+
count;
|
|
283
|
+
waiting = [];
|
|
284
|
+
constructor(max) {
|
|
285
|
+
this.count = max === Infinity ? Number.MAX_SAFE_INTEGER : max;
|
|
286
|
+
}
|
|
287
|
+
async acquire() {
|
|
288
|
+
if (this.count > 0) {
|
|
289
|
+
this.count--;
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
return new Promise((resolve) => {
|
|
293
|
+
this.waiting.push(resolve);
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
release() {
|
|
297
|
+
const next = this.waiting.shift();
|
|
298
|
+
if (next) {
|
|
299
|
+
next();
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
this.count++;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
//# sourceMappingURL=swarm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"swarm.js","sourceRoot":"","sources":["../src/swarm.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAErC;;GAEG;AACH,MAAM,aAAa,GAA0B;IAC3C,gBAAgB,EAAE,CAAC;IACnB,UAAU,EAAE,CAAC;IACb,WAAW,EAAE,QAAQ;IACrB,OAAO,EAAE,CAAC;IACV,IAAI,EAAE,IAAI;CACX,CAAA;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAoB;IACrD,OAAO,EAAE,GAAG,aAAa,EAAE,GAAG,MAAM,EAAE,CAAA;AACxC,CAAC;AAiBD;;;;GAIG;AACH,MAAM,OAAO,YAAY;IACf,OAAO,GAAG,IAAI,GAAG,EAA8B,CAAA;IAC/C,MAAM,CAAuB;IAErC,YAAY,MAAoB;QAC9B,IAAI,CAAC,MAAM,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAA;IAC1C,CAAC;IAED;;OAEG;IACH,SAAS,CAAC,MAAiB;QACzB,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClC,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,KAAK,WAAW,IAAI,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;YACtF,IAAI,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;YACzC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,mBAAmB,EAAE,CAAC,EAAE,CAAA;gBAClE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;YACtC,CAAC;YAED,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;YAE3B,8BAA8B;YAC9B,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;gBACnD,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;YAChE,CAAC;YAED,0CAA0C;YAC1C,MAAM,CAAC,mBAAmB,GAAG,CAAC,CAAA;YAC9B,KAAK,IAAI,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;gBACpD,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;oBACvB,MAAM,CAAC,mBAAmB,EAAE,CAAA;gBAC9B,CAAC;qBAAM,CAAC;oBACN,MAAK;gBACP,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,aAAa;QACX,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI;YAAE,OAAO,EAAE,CAAA;QAEhC,MAAM,UAAU,GAAa,EAAE,CAAA;QAC/B,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC1C,IAAI,MAAM,CAAC,mBAAmB,IAAI,IAAI,CAAC,MAAM,CAAC,gBAAgB,EAAE,CAAC;gBAC/D,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YACvB,CAAC;QACH,CAAC;QACD,OAAO,UAAU,CAAA;IACnB,CAAC;IAED;;OAEG;IACH,SAAS,CAAC,IAAY;QACpB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IAC/B,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAA;IACtB,CAAC;CACF;AAED,wEAAwE;AAExE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,MAAyB,EACzB,WAAwB,EACxB,MAA+B,EAC/B,OAAe,EACf,aAAqB,EACrB,SAAiB,EACjB,OAA2B,EAC3B,OAA0B;IAE1B,MAAM,QAAQ,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAA;IAC3C,MAAM,UAAU,GAAG,WAAW,CAAC,UAAU,CAAA;IACzC,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,WAAW,EAAE,UAAU,CAAC,CAAA;IAE9D,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAA;IAClC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;IAC3B,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAA;IAC3B,OAAO,CAAC,GAAG,CAAC,kBAAkB,OAAO,EAAE,CAAC,CAAA;IACxC,OAAO,CAAC,GAAG,CAAC,kBAAkB,MAAM,CAAC,MAAM,EAAE,CAAC,CAAA;IAC9C,OAAO,CAAC,GAAG,CAAC,kBAAkB,UAAU,EAAE,CAAC,CAAA;IAC3C,OAAO,CAAC,GAAG,CAAC,kBAAkB,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAA;IACjD,OAAO,CAAC,GAAG,CAAC,kBAAkB,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAA;IACrF,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAA;IAElC,MAAM,OAAO,GAAuB,EAAE,CAAA;IAEtC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,CAAC,IAAI,EAAE,CAAC,CAAA;QAExC,MAAM,OAAO,GAAqB,EAAE,CAAA;QAOpC,MAAM,KAAK,GAAgB,EAAE,CAAA;QAC7B,KAAK,IAAI,MAAM,GAAG,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC;YACzD,KAAK,IAAI,SAAS,GAAG,CAAC,EAAE,SAAS,GAAG,UAAU,EAAE,SAAS,EAAE,EAAE,CAAC;gBAC5D,KAAK,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAA;YACnC,CAAC;QACH,CAAC;QAED,oCAAoC;QACpC,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,WAAW,CAAC,CAAA;QAE5C,MAAM,YAAY,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;YAC5C,MAAM,SAAS,CAAC,OAAO,EAAE,CAAA;YACzB,IAAI,CAAC;gBACH,8CAA8C;gBAC9C,uDAAuD;gBACvD,iCAAiC;gBACjC,OAAO,IAAI,EAAE,CAAC;oBACZ,MAAM,QAAQ,GAAG,WAAW,CAAC,OAAO,EAAE,CAAA;oBACtC,IAAI,QAAQ,KAAK,IAAI,CAAC,SAAS;wBAAE,MAAK;oBACtC,sCAAsC;oBACtC,IAAI,QAAQ,KAAK,IAAI;wBAAE,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;oBACpD,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAA;gBAC7C,CAAC;gBAED,IAAI,CAAC;oBACH,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,aAAa,CAC7C,IAAI,CAAC,SAAS,EACd,aAAa,EACb,SAAS,EACT,OAAO,CACR,CAAA;oBAED,IAAI,CAAC;wBACH,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;wBAEtD,MAAM,MAAM,GAAmB;4BAC7B,SAAS,EAAE,IAAI,CAAC,SAAS;4BACzB,MAAM,EAAE,IAAI,CAAC,MAAM;4BACnB,MAAM,EAAE,MAAM,CAAC,MAAM;4BACrB,QAAQ,EAAE,MAAM,CAAC,QAAQ;4BACzB,gBAAgB,EAAE,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM;4BACnE,eAAe,EAAE,MAAM,CAAC,UAAU,CAAC,MAAM;4BACzC,KAAK,EAAE,MAAM,CAAC,KAAK;4BACnB,MAAM,EAAE,EAAE;yBACX,CAAA;wBAED,gCAAgC;wBAChC,KAAK,MAAM,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC;4BACzC,MAAM,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,CAAA;4BAC3C,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;wBAChD,CAAC;wBAED,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;wBAEpB,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAA;wBACtD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;oBAC5B,CAAC;4BAAS,CAAC;wBACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAA;oBACvB,CAAC;gBACH,CAAC;wBAAS,CAAC;oBACT,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;gBACrC,CAAC;YACH,CAAC;oBAAS,CAAC;gBACT,SAAS,CAAC,OAAO,EAAE,CAAA;YACrB,CAAC;QACH,CAAC,CAAC,CAAA;QAEF,MAAM,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;QAC/B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QAE1B,sBAAsB;QACtB,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,WAAW,IAAI,CAAC,CAAC,gBAAgB,KAAK,CAAC,CAAC,CAAA;QAC1F,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,WAAW,IAAI,CAAC,CAAC,gBAAgB,GAAG,CAAC,CAAC,CAAA;QAExF,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAA;QAC9D,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAA;QAE9D,MAAM,cAAc,GAAG,aAAa,CAAC,OAAO,EAAE,UAAU,CAAC,CAAA;QAEzD,MAAM,MAAM,GAAqB;YAC/B,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,IAAI,EAAE,OAAO,CAAC,MAAM;YACpB,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,YAAY,EAAE,CAAC,GAAG,cAAc,CAAC;YACjC,YAAY,EAAE,CAAC,GAAG,cAAc,CAAC;YACjC,cAAc;YACd,OAAO;SACR,CAAA;QAED,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAEpB,OAAO,CAAC,GAAG,CAAC,OAAO,mBAAmB,CAAC,cAAc,CAAC,KAAK,MAAM,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,UAAU,CAAC,CAAA;QAErG,IAAI,cAAc,KAAK,qBAAqB,EAAE,CAAC;YAC7C,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,GAAG,cAAc,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QACrE,CAAC;IACH,CAAC;IAED,gBAAgB;IAChB,MAAM,OAAO,GAAG;QACd,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,KAAK,QAAQ,CAAC,CAAC,MAAM;QACnE,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,KAAK,OAAO,CAAC,CAAC,MAAM;QACjE,gBAAgB,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,KAAK,qBAAqB,CAAC,CAAC,MAAM;QAC1F,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,MAAM;KACtE,CAAA;IAED,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAA;IAClC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAA;IAC9B,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAA;IAC3B,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB,OAAO,CAAC,MAAM,WAAW,CAAC,CAAA;IACpF,IAAI,OAAO,CAAC,KAAK,GAAG,CAAC;QAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB,OAAO,CAAC,KAAK,WAAW,CAAC,CAAA;IAClF,IAAI,OAAO,CAAC,gBAAgB,GAAG,CAAC;QAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB,OAAO,CAAC,gBAAgB,WAAW,CAAC,CAAA;IACxG,IAAI,OAAO,CAAC,OAAO,GAAG,CAAC;QAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB,OAAO,CAAC,OAAO,WAAW,CAAC,CAAA;IACtF,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAA;IAElC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAA;AACtC,CAAC;AAED,wEAAwE;AAExE;;;;;;;;;GASG;AACH,SAAS,aAAa,CAAC,OAAyB,EAAE,UAAkB;IAClE,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,WAAW,IAAI,CAAC,CAAC,gBAAgB,GAAG,CAAC,CAAC,CAAA;IAC1F,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,WAAW,IAAI,CAAC,CAAC,gBAAgB,KAAK,CAAC,CAAC,CAAA;IAE5F,IAAI,SAAS;QAAE,OAAO,SAAS,CAAA;IAC/B,IAAI,SAAS;QAAE,OAAO,QAAQ,CAAA;IAE9B,sCAAsC;IACtC,6EAA6E;IAC7E,MAAM,WAAW,GAAG,IAAI,GAAG,EAA8C,CAAA;IACzE,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAA;QAC3E,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW,IAAI,MAAM,CAAC,gBAAgB,KAAK,CAAC,EAAE,CAAC;YACnE,KAAK,CAAC,MAAM,EAAE,CAAA;QAChB,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,MAAM,EAAE,CAAA;QAChB,CAAC;QACD,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,CAAA;IAC1C,CAAC;IAED,2CAA2C;IAC3C,4CAA4C;IAC5C,MAAM,aAAa,GAAG,CAAC,GAAG,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,CAAA;IAClF,MAAM,aAAa,GAAG,CAAC,GAAG,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,CAAA;IAElF,kEAAkE;IAClE,wCAAwC;IACxC,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzD,OAAO,qBAAqB,CAAA;IAC9B,CAAC;IAED,0DAA0D;IAC1D,OAAO,OAAO,CAAA;AAChB,CAAC;AAED;;GAEG;AACH,SAAS,mBAAmB,CAAC,CAAsB;IACjD,QAAQ,CAAC,EAAE,CAAC;QACV,KAAK,QAAQ,CAAC,CAAC,OAAO,6BAA6B,CAAA;QACnD,KAAK,OAAO,CAAC,CAAC,OAAO,+BAA+B,CAAA;QACpD,KAAK,qBAAqB,CAAC,CAAC,OAAO,8CAA8C,CAAA;QACjF,KAAK,SAAS,CAAC,CAAC,OAAO,+BAA+B,CAAA;IACxD,CAAC;AACH,CAAC;AAED,wEAAwE;AAExE;;GAEG;AACH,MAAM,SAAS;IACL,KAAK,CAAQ;IACb,OAAO,GAAsB,EAAE,CAAA;IAEvC,YAAY,GAAW;QACrB,IAAI,CAAC,KAAK,GAAG,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,GAAG,CAAA;IAC/D,CAAC;IAED,KAAK,CAAC,OAAO;QACX,IAAI,IAAI,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC;YACnB,IAAI,CAAC,KAAK,EAAE,CAAA;YACZ,OAAM;QACR,CAAC;QACD,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YACnC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAC5B,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,OAAO;QACL,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAA;QACjC,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,EAAE,CAAA;QACR,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,KAAK,EAAE,CAAA;QACd,CAAC;IACH,CAAC;CACF"}
|