@e2edev/e2e 0.13.0 → 0.14.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/dist/agent/act.js +4 -1
- package/dist/agent/act.js.map +1 -1
- package/dist/agent/action-dispatcher.d.ts +3 -3
- package/dist/agent/action-dispatcher.js +3 -3
- package/dist/agent/error.d.ts.map +1 -1
- package/dist/agent/error.js +1 -0
- package/dist/agent/error.js.map +1 -1
- package/dist/agent/executor.d.ts +3 -3
- package/dist/agent/phases.js +1 -1
- package/dist/agent/phases.js.map +1 -1
- package/dist/agent/primitives.d.ts.map +1 -1
- package/dist/agent/primitives.js +3 -2
- package/dist/agent/primitives.js.map +1 -1
- package/dist/agent/secrets.d.ts.map +1 -1
- package/dist/agent/secrets.js +13 -9
- package/dist/agent/secrets.js.map +1 -1
- package/dist/agent/step-accounting.d.ts +12 -1
- package/dist/agent/step-accounting.d.ts.map +1 -1
- package/dist/agent/step-accounting.js +18 -3
- package/dist/agent/step-accounting.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +5 -8
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init/engine-versions.json +1 -1
- package/dist/cli/init/engines.d.ts +19 -9
- package/dist/cli/init/engines.d.ts.map +1 -1
- package/dist/cli/init/engines.js +24 -11
- package/dist/cli/init/engines.js.map +1 -1
- package/dist/cli/init/scaffold.d.ts.map +1 -1
- package/dist/cli/init/scaffold.js +1 -3
- package/dist/cli/init/scaffold.js.map +1 -1
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +1 -0
- package/dist/cli/init.js.map +1 -1
- package/dist/config/diagnose.d.ts.map +1 -1
- package/dist/config/diagnose.js +2 -0
- package/dist/config/diagnose.js.map +1 -1
- package/dist/config/resolve.d.ts +11 -2
- package/dist/config/resolve.d.ts.map +1 -1
- package/dist/config/resolve.js +43 -18
- package/dist/config/resolve.js.map +1 -1
- package/dist/config/secrets.d.ts +89 -0
- package/dist/config/secrets.d.ts.map +1 -0
- package/dist/config/secrets.js +67 -0
- package/dist/config/secrets.js.map +1 -0
- package/dist/engine/contract.d.ts +8 -0
- package/dist/engine/contract.d.ts.map +1 -1
- package/dist/engine/contract.js.map +1 -1
- package/dist/explore/body.js +1 -1
- package/dist/explore/body.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/locator/engine.d.ts.map +1 -1
- package/dist/locator/engine.js +1 -0
- package/dist/locator/engine.js.map +1 -1
- package/dist/mcp/session.d.ts +1 -1
- package/dist/mcp/session.d.ts.map +1 -1
- package/dist/mcp/session.js +8 -4
- package/dist/mcp/session.js.map +1 -1
- package/dist/report/builtin.d.ts +14 -0
- package/dist/report/builtin.d.ts.map +1 -0
- package/dist/report/builtin.js +22 -0
- package/dist/report/builtin.js.map +1 -0
- package/dist/report/explore-text.d.ts +17 -0
- package/dist/report/explore-text.d.ts.map +1 -0
- package/dist/report/explore-text.js +37 -0
- package/dist/report/explore-text.js.map +1 -0
- package/dist/report/format.d.ts +2 -0
- package/dist/report/format.d.ts.map +1 -1
- package/dist/report/format.js +1 -1
- package/dist/report/format.js.map +1 -1
- package/dist/report/junit.d.ts +2 -4
- package/dist/report/junit.d.ts.map +1 -1
- package/dist/report/junit.js +7 -40
- package/dist/report/junit.js.map +1 -1
- package/dist/report/list-explore.d.ts +1 -1
- package/dist/report/list-explore.d.ts.map +1 -1
- package/dist/report/list-explore.js +5 -25
- package/dist/report/list-explore.js.map +1 -1
- package/dist/report/markdown.d.ts +39 -0
- package/dist/report/markdown.d.ts.map +1 -0
- package/dist/report/markdown.js +426 -0
- package/dist/report/markdown.js.map +1 -0
- package/dist/report/outcome.d.ts +29 -0
- package/dist/report/outcome.d.ts.map +1 -0
- package/dist/report/outcome.js +36 -0
- package/dist/report/outcome.js.map +1 -0
- package/dist/report/write.d.ts +10 -1
- package/dist/report/write.d.ts.map +1 -1
- package/dist/report/write.js +22 -1
- package/dist/report/write.js.map +1 -1
- package/dist/run/execute.js +1 -1
- package/dist/run/execute.js.map +1 -1
- package/dist/run/fixtures.d.ts.map +1 -1
- package/dist/run/fixtures.js +22 -7
- package/dist/run/fixtures.js.map +1 -1
- package/dist/run/runner.d.ts.map +1 -1
- package/dist/run/runner.js +5 -7
- package/dist/run/runner.js.map +1 -1
- package/dist/run/secrecy.d.ts +3 -3
- package/dist/run/secrecy.d.ts.map +1 -1
- package/dist/run/secrecy.js +4 -4
- package/dist/run/secrecy.js.map +1 -1
- package/dist/run/standalone.js +6 -6
- package/dist/run/standalone.js.map +1 -1
- package/dist/run/worker/entry.js +2 -2
- package/dist/run/worker/entry.js.map +1 -1
- package/dist/secrets.d.ts +21 -0
- package/dist/secrets.d.ts.map +1 -0
- package/dist/secrets.js +66 -0
- package/dist/secrets.js.map +1 -0
- package/dist/types.d.ts +21 -37
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/skills/e2e/SKILL.md +39 -21
- package/skills/e2e/references/agent.md +55 -14
- package/skills/e2e/references/debugging.md +2 -1
- package/skills/e2e/references/mcp.md +1 -1
- package/skills/e2e/references/running.md +11 -4
- package/skills/e2e/references/setup.md +17 -4
- package/skills/e2e/references/writing-tests.md +20 -11
- package/dist/credentials.d.ts +0 -13
- package/dist/credentials.d.ts.map +0 -1
- package/dist/credentials.js +0 -51
- package/dist/credentials.js.map +0 -1
|
@@ -14,6 +14,10 @@
|
|
|
14
14
|
(npm's `ERESOLVE`); upgrade `playwright` within the range.
|
|
15
15
|
Missing browsers download when the engine first boots. In CI install them
|
|
16
16
|
up front: `npx playwright install chromium --with-deps`.
|
|
17
|
+
- For mobile tests, `@e2edev/agent-device` plus `agent-device` (`0.21.x`),
|
|
18
|
+
likewise a peer dependency: a project that already drives devices with the
|
|
19
|
+
agent-device CLI keeps its version. The range pins the tested minor because
|
|
20
|
+
agent-device minors break; it moves with each engine release.
|
|
17
21
|
|
|
18
22
|
## Scaffold
|
|
19
23
|
|
|
@@ -73,7 +77,7 @@ export default {
|
|
|
73
77
|
}),
|
|
74
78
|
},
|
|
75
79
|
],
|
|
76
|
-
//
|
|
80
|
+
// The model behind every agent.* step: an AI SDK instance; gateway() from 'ai' reads AI_GATEWAY_API_KEY.
|
|
77
81
|
agents: {
|
|
78
82
|
default: createAgent({
|
|
79
83
|
model: gateway('openai/gpt-5.6-luna'),
|
|
@@ -95,10 +99,11 @@ export default {
|
|
|
95
99
|
| `assertionTimeout` | `5000` | `expect` polling window. |
|
|
96
100
|
| `retries` | `0`, `1` in CI | 0 to 10. |
|
|
97
101
|
| `workers` | half the cores, `1` in CI | Test files run in parallel across workers, at most the `workers` the engine declares per target (a device target: one per device). |
|
|
98
|
-
| `reporters` | `['list']` | `list`, `json`, `junit`, and reporter objects (`{ name, onEvent?, onRunFinished? }`) that receive the finished run. `json` excludes `list`; `--reporter` keeps the objects. |
|
|
102
|
+
| `reporters` | `['list']` | `list`, `json`, `junit`, `markdown`, and reporter objects (`{ name, onEvent?, onRunFinished? }`) that receive the finished run. `json` excludes `list`; `--reporter` keeps the objects. |
|
|
99
103
|
| `cache` | `'read-write'`, `'read-only'` in CI | The trace cache for `agent.act`; `'off'` disables it. |
|
|
100
104
|
| `agents` | `{ default: built-in }` | Agents by name. `default` is what tests run with; `e2e run --agent <name>` runs with another. Each entry is `createAgent(...)`, an options block `{ model, context, maxSteps, maxModelCalls, vision, providerOptions }`, or a custom `StepExecutor`. `model` is an AI SDK instance; without one anywhere, acquiring `agent` is `MODEL_UNAVAILABLE`. |
|
|
101
105
|
| `credentials` | `{}` | Named `{ username, password, allowedOrigins? }` entries; `password` may be a function returning the value. |
|
|
106
|
+
| `secrets` | `{}` | Named values the model never sees (API keys, tokens): a string, a function returning the value, or `{ value, allowedOrigins? }`. A name cannot also be a credential. |
|
|
102
107
|
| `screen.testIdAttribute` | `'data-testid'` | Attribute read by `getByTestId`. |
|
|
103
108
|
| `artifacts` | `['screenshot', 'trace']` | Kinds to keep (`screenshot`, `trace`, and the opt-in `video`), or `{ kinds, store, video }`; `video: { retain: 'on-failure' }` keeps only the recordings of attempts that did not pass. |
|
|
104
109
|
| `projectId` | the package name | Report and cache identity. |
|
|
@@ -206,6 +211,7 @@ or read the address from the environment:
|
|
|
206
211
|
| --- | --- |
|
|
207
212
|
| `AI_GATEWAY_API_KEY`, `OPENROUTER_API_KEY`, `OPENAI_API_KEY`, ... | Read by the provider package the config constructs the model with (`gateway()` from `ai`, `openrouter()`, `openai()`), not by the runner. The runner reads no model variable; the model is always an AI SDK instance in the config. |
|
|
208
213
|
| `E2E_USER_<NAME>_USERNAME`, `E2E_USER_<NAME>_PASSWORD` | Override `credentials.<name>`. `<NAME>` is the credential name uppercased, other characters as `_`. |
|
|
214
|
+
| `E2E_SECRET_<NAME>` | Overrides the value of `secrets.<name>`, same uppercasing rule. |
|
|
209
215
|
| `CI` | Turns on CI defaults: `retries: 1`, `workers: 1`, `test.only` rejected, cache read-only, `reuseExisting` ignored. |
|
|
210
216
|
| `E2E_TELEMETRY_DISABLED`, `DO_NOT_TRACK` | Turn anonymous usage telemetry off; `e2e telemetry disable` saves the same choice. `E2E_TELEMETRY_DEBUG=1` prints each event instead of sending it. |
|
|
211
217
|
|
|
@@ -240,6 +246,11 @@ export default {
|
|
|
240
246
|
single `device` runs one worker whatever `workers` says; a list,
|
|
241
247
|
`device: ['iPhone 17', 'iPhone 17 Pro']`, is an explicit pool. Devices boot
|
|
242
248
|
in `prepare`, before the run's clock starts.
|
|
249
|
+
- A test's steps never wait for the screen to settle; `expect` verifies the
|
|
250
|
+
outcome. Only a control that appeared or moved with the previous action
|
|
251
|
+
waits out the `transition` budget (default 500 ms) before it is acted on.
|
|
252
|
+
Agent actions settle for `settle` ms (default 150) before the agent
|
|
253
|
+
observes again; `settle: false` skips that wait.
|
|
243
254
|
- Cancelled device commands keep running; the next attempt waits for them.
|
|
244
255
|
Raw screenshot files are removed when capture finishes, including after
|
|
245
256
|
cancellation.
|
|
@@ -260,5 +271,7 @@ export default {
|
|
|
260
271
|
- `npx e2e run tests/example.e2e.ts` passes against the app.
|
|
261
272
|
- `package.json` has a script such as `"test:e2e": "e2e run"`.
|
|
262
273
|
- `.gitignore` lists the `.e2e/` outputs (init adds them). Committing
|
|
263
|
-
`.e2e/cache/` is opt-in: remove that line
|
|
264
|
-
|
|
274
|
+
`.e2e/cache/` is opt-in: remove that line so CI and teammates replay
|
|
275
|
+
`agent.act` steps instead of re-running the model.
|
|
276
|
+
- CI runs the whole suite on pull requests, agent steps included; see
|
|
277
|
+
`running`.
|
|
@@ -12,28 +12,31 @@ test.describe('todos', { tags: ['todos'] }, () => {
|
|
|
12
12
|
await app.open('/todos');
|
|
13
13
|
});
|
|
14
14
|
|
|
15
|
-
test('adds and completes a todo', async ({ screen, web }) => {
|
|
16
|
-
await
|
|
17
|
-
await screen.getByRole('button', { name: 'Add' }).tap();
|
|
18
|
-
|
|
15
|
+
test('adds and completes a todo', async ({ agent, screen, web }) => {
|
|
16
|
+
await agent.act('add a todo named {title}', { params: { title: 'Write the release notes' } });
|
|
19
17
|
await expect(screen.getByRole('listitem')).toHaveCount(1);
|
|
20
18
|
await expect(screen.getByRole('status', { name: 'Remaining' })).toHaveText('1 remaining');
|
|
21
19
|
|
|
22
|
-
await
|
|
20
|
+
await agent.act('mark the todo as done');
|
|
23
21
|
await expect(screen.getByRole('status', { name: 'Remaining' })).toHaveText('0 remaining');
|
|
24
22
|
await expect(web).toHaveURL('/todos');
|
|
25
23
|
});
|
|
26
24
|
|
|
27
25
|
test('ignores an empty submission', async ({ screen }) => {
|
|
26
|
+
// An exact interaction: the empty submit is the point of the test.
|
|
28
27
|
await screen.getByRole('button', { name: 'Add' }).tap();
|
|
29
28
|
await expect(screen.getByRole('listitem')).toHaveCount(0);
|
|
30
29
|
});
|
|
31
30
|
});
|
|
32
31
|
```
|
|
33
32
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
The agent does the flow; `expect` pins what must be true after each goal,
|
|
34
|
+
and that check is what lets the trace cache replay the step on later runs.
|
|
35
|
+
`screen` actions are for exact interactions and values, like the empty
|
|
36
|
+
submit above or a sign-in form. Files match the config `tests` glob, default
|
|
37
|
+
`tests/**/*.e2e.ts`. Every test starts from clean state: a fresh browser
|
|
38
|
+
context and no page open, so a test calls `app.open()` first (here in
|
|
39
|
+
`beforeEach`).
|
|
37
40
|
|
|
38
41
|
## Registration
|
|
39
42
|
|
|
@@ -237,7 +240,12 @@ credentials: {
|
|
|
237
240
|
|
|
238
241
|
`E2E_USER_ADMIN_USERNAME` and `E2E_USER_ADMIN_PASSWORD` override either
|
|
239
242
|
field per run. `credentials.user('admin').password` is a `Secret` with no
|
|
240
|
-
plaintext accessor; only `fill()` and `agent.act` params accept it.
|
|
243
|
+
plaintext accessor; only `fill()` and `agent.act` params accept it. Any other
|
|
244
|
+
sensitive value (an API key, a token) is a `secrets` entry,
|
|
245
|
+
`secrets: { 'stripe-key': process.env.STRIPE_KEY ?? '' }`, overridable with
|
|
246
|
+
`E2E_SECRET_STRIPE_KEY`; `secrets.get('stripe-key')` is the same kind of
|
|
247
|
+
handle and fills any editable input, with the value redacted by name
|
|
248
|
+
everywhere the runner writes. Once a
|
|
241
249
|
secret is filled, model pixels and assertion screenshots are withheld for
|
|
242
250
|
the rest of that session, including later tests sharing a serial session.
|
|
243
251
|
`app.screenshot()` fails with `POLICY_DENIED` before capture. Sign in inside
|
|
@@ -282,8 +290,9 @@ expect(response.status).toBe(201);
|
|
|
282
290
|
Otherwise tests are independent and may run on different workers.
|
|
283
291
|
- Tag by area and by cost (`smoke`, `billing`, `agent`) and run subsets with
|
|
284
292
|
`--tag`.
|
|
285
|
-
-
|
|
286
|
-
|
|
293
|
+
- Drive flows with `agent.act` and pin each outcome with `expect`. The
|
|
294
|
+
calls on this page are for exact values and exact checks; the `agent`
|
|
295
|
+
topic covers the steps that do the work.
|
|
287
296
|
|
|
288
297
|
## Mistakes to avoid
|
|
289
298
|
|
package/dist/credentials.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/** Opaque credential handles. */
|
|
2
|
-
import type { ResolvedCredential } from './config/resolve.ts';
|
|
3
|
-
import type { Credentials } from './types.ts';
|
|
4
|
-
/** Installed by the runner once config resolution completes. */
|
|
5
|
-
export declare function setCredentialRegistry(map: ReadonlyMap<string, ResolvedCredential> | undefined): void;
|
|
6
|
-
/**
|
|
7
|
-
* Removes `map` from the slot if it is still the installed registry. A host
|
|
8
|
-
* that closes one attempt while opening the next must not wipe what the new
|
|
9
|
-
* attempt just installed.
|
|
10
|
-
*/
|
|
11
|
-
export declare function releaseCredentialRegistry(map: ReadonlyMap<string, ResolvedCredential>): void;
|
|
12
|
-
export declare const credentials: Credentials;
|
|
13
|
-
//# sourceMappingURL=credentials.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAAA,iCAAiC;AAKjC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,KAAK,EAAc,WAAW,EAAU,MAAM,YAAY,CAAC;AAKlE,gEAAgE;AAChE,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,WAAW,CAAC,MAAM,EAAE,kBAAkB,CAAC,GAAG,SAAS,GAAG,IAAI,CAGpG;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,WAAW,CAAC,MAAM,EAAE,kBAAkB,CAAC,GAAG,IAAI,CAE5F;AAcD,eAAO,MAAM,WAAW,EAAE,WAuBzB,CAAC"}
|
package/dist/credentials.js
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/** Opaque credential handles. */
|
|
2
|
-
import { credentialBrand, secretBrand } from './internal/brands.js';
|
|
3
|
-
import { ConfigurationError } from './internal/errors.js';
|
|
4
|
-
import { realmSlot } from './internal/realm-slot.js';
|
|
5
|
-
/** Global slot so test modules in an isolated realm reach the runner's registry. */
|
|
6
|
-
const credentialsSlot = realmSlot('e2e.credentials.v1');
|
|
7
|
-
/** Installed by the runner once config resolution completes. */
|
|
8
|
-
export function setCredentialRegistry(map) {
|
|
9
|
-
if (map === undefined)
|
|
10
|
-
credentialsSlot.delete(globalThis);
|
|
11
|
-
else
|
|
12
|
-
credentialsSlot.set(globalThis, map);
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Removes `map` from the slot if it is still the installed registry. A host
|
|
16
|
-
* that closes one attempt while opening the next must not wipe what the new
|
|
17
|
-
* attempt just installed.
|
|
18
|
-
*/
|
|
19
|
-
export function releaseCredentialRegistry(map) {
|
|
20
|
-
if (credentialsSlot.get(globalThis) === map)
|
|
21
|
-
credentialsSlot.delete(globalThis);
|
|
22
|
-
}
|
|
23
|
-
function getRegistry() {
|
|
24
|
-
return credentialsSlot.get(globalThis);
|
|
25
|
-
}
|
|
26
|
-
function makeSecret(name) {
|
|
27
|
-
return Object.freeze({
|
|
28
|
-
name,
|
|
29
|
-
purpose: 'password',
|
|
30
|
-
[secretBrand]: true,
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
export const credentials = {
|
|
34
|
-
user(name) {
|
|
35
|
-
const registry = getRegistry();
|
|
36
|
-
if (registry === undefined) {
|
|
37
|
-
throw new ConfigurationError('AUTH_CREDENTIAL_UNAVAILABLE', 'credentials.user() is only available while the e2e runner is active');
|
|
38
|
-
}
|
|
39
|
-
const resolved = registry.get(name);
|
|
40
|
-
if (resolved === undefined) {
|
|
41
|
-
throw new ConfigurationError('AUTH_CREDENTIAL_UNAVAILABLE', `credential "${name}" is not configured; add it to config.credentials or E2E_USER_* variables`);
|
|
42
|
-
}
|
|
43
|
-
return Object.freeze({
|
|
44
|
-
name,
|
|
45
|
-
username: resolved.username,
|
|
46
|
-
password: makeSecret(name),
|
|
47
|
-
[credentialBrand]: true,
|
|
48
|
-
});
|
|
49
|
-
},
|
|
50
|
-
};
|
|
51
|
-
//# sourceMappingURL=credentials.js.map
|
package/dist/credentials.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAAA,iCAAiC;AAEjC,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAIrD,oFAAoF;AACpF,MAAM,eAAe,GAAG,SAAS,CAA0C,oBAAoB,CAAC,CAAC;AAEjG,gEAAgE;AAChE,MAAM,UAAU,qBAAqB,CAAC,GAAwD;IAC5F,IAAI,GAAG,KAAK,SAAS;QAAE,eAAe,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;;QACrD,eAAe,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;AAC5C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CAAC,GAA4C;IACpF,IAAI,eAAe,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,GAAG;QAAE,eAAe,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;AAClF,CAAC;AAED,SAAS,WAAW;IAClB,OAAO,eAAe,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;AACzC,CAAC;AAED,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,IAAI;QACJ,OAAO,EAAE,UAAmB;QAC5B,CAAC,WAAW,CAAC,EAAE,IAAa;KAC7B,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAgB;IACtC,IAAI,CAAC,IAAY;QACf,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;QAC/B,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,MAAM,IAAI,kBAAkB,CAC1B,6BAA6B,EAC7B,qEAAqE,CACtE,CAAC;QACJ,CAAC;QACD,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,MAAM,IAAI,kBAAkB,CAC1B,6BAA6B,EAC7B,eAAe,IAAI,2EAA2E,CAC/F,CAAC;QACJ,CAAC;QACD,OAAO,MAAM,CAAC,MAAM,CAAC;YACnB,IAAI;YACJ,QAAQ,EAAE,QAAQ,CAAC,QAAQ;YAC3B,QAAQ,EAAE,UAAU,CAAC,IAAI,CAAC;YAC1B,CAAC,eAAe,CAAC,EAAE,IAAa;SACjC,CAAC,CAAC;IACL,CAAC;CACF,CAAC"}
|