apify-test-tools 0.10.1-beta.4 → 0.10.1-beta.6
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/.claude-plugin/marketplace.json +12 -0
- package/.github/workflows/public_platform-tests-claude-investigate-and-fix.yaml +15 -8
- package/CHANGELOG.md +2 -0
- package/README.md +6 -7
- package/dist/test/unit/bin/build-from-local.test.js +6 -15
- package/dist/test/unit/bin/build-from-local.test.js.map +1 -1
- package/dist/test/unit/bin/git-command.test.js +12 -42
- package/dist/test/unit/bin/git-command.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/platform-tests-investigate-and-fix/.claude-plugin/plugin.json +4 -0
- package/platform-tests-investigate-and-fix/skills/measure-flakiness/SKILL.md +28 -0
- package/test/unit/bin/build-from-local.test.ts +9 -18
- package/test/unit/bin/git-command.test.ts +14 -55
|
@@ -39,16 +39,17 @@ on:
|
|
|
39
39
|
type: string
|
|
40
40
|
default: sonnet
|
|
41
41
|
description: Claude model to use for investigation and fix
|
|
42
|
+
max_flakiness_runs:
|
|
43
|
+
required: false
|
|
44
|
+
type: number
|
|
45
|
+
default: 200
|
|
46
|
+
description: The most times Claude may re-run the failing Actor run to measure how often the failure happens, when that isn't obvious. Claude decides how many of them it needs.
|
|
42
47
|
investigate_prompt:
|
|
43
48
|
required: false
|
|
44
49
|
type: string
|
|
45
50
|
default: |
|
|
46
51
|
Investigate the failing E2E platform test and open a GitHub issue documenting your findings. Start by reviewing the test logs, error messages, and recent workflow run history to determine the root cause.
|
|
47
52
|
|
|
48
|
-
The failure will typically fall into one of these categories:
|
|
49
|
-
- Flaky test: The test is non-deterministic or depends on timing/external state and fails intermittently. Check past workflow runs to see if this test has failed before.
|
|
50
|
-
- Real bug: Genuine broken functionality — either a pre-existing issue or an environment/dependency change outside the codebase (e.g. a website changed its structure, an external API changed behavior).
|
|
51
|
-
|
|
52
53
|
Use available tools to check previous workflow runs, git history, and existing GitHub issues to gather context and avoid creating duplicate issues. For deeper context,
|
|
53
54
|
use tools to investigate the run on Apify platform, review its logs, datasets, key-value stores.
|
|
54
55
|
|
|
@@ -56,9 +57,12 @@ on:
|
|
|
56
57
|
- Debug snapshots: often stored as a field on dataset items (e.g. `_debugSnapshots`) or as records in a key-value store, keyed so they can be matched to the log line for the failing request.
|
|
57
58
|
- Any other debug artifacts the Actor produced (extra logs, error/state fields on output items) that narrow down the failing request.
|
|
58
59
|
|
|
60
|
+
Before you create a new issue, check whether the `platform-tests-investigate-and-fix:measure-flakiness` skill applies, and follow it if it does.
|
|
61
|
+
|
|
59
62
|
When you create the issue, write it like an incident postmortem: a short summary and impact up top for anyone skimming, full technical detail below for whoever fixes it. Don't water down the engineering detail to make the top readable.
|
|
60
63
|
- Title: short and clean, describing the actual problem in plain terms — no dates, run numbers, or other noise. Prefix it with a conventional-commit-style type, e.g. "fix: blocking issue on detail endpoint".
|
|
61
64
|
- Body, section 1 ("## Summary"): one line stating what's broken plus its impact, in plain language a PM can understand at a glance — no jargon, internal field names, or stack traces (e.g. "Scraping product detail pages fails when a listing has no reviews, affecting N% of runs").
|
|
65
|
+
- If you measured flakiness, a "## Flakiness" section right after the Summary, as the `measure-flakiness` skill describes.
|
|
62
66
|
- A `---` divider, then section 2 ("## Details"): the engineering bug report — root cause, relevant log lines, links to the failing run, and any other internals an engineer needs. If a single item/request failed among otherwise-good ones, show or link that specific one (e.g. the dataset item index/id, or its matching debug snapshot) rather than just describing the run as failed.
|
|
63
67
|
|
|
64
68
|
If you create a new issue, you MUST write its issue number (just the number, nothing else) to $GITHUB_WORKSPACE/issue-number.txt as the very last thing you do. If you skip creating one because a duplicate already exists, do not create the file.
|
|
@@ -67,9 +71,9 @@ on:
|
|
|
67
71
|
ANTHROPIC_API_KEY:
|
|
68
72
|
required: true
|
|
69
73
|
description: Claude API key
|
|
70
|
-
|
|
74
|
+
TESTER_APIFY_TOKEN:
|
|
71
75
|
required: true
|
|
72
|
-
description: Apify token
|
|
76
|
+
description: Apify token for investigating the failing run — reading it, its log and its storages — and re-running it to measure how often the failure happens.
|
|
73
77
|
NPM_TOKEN:
|
|
74
78
|
required: false
|
|
75
79
|
description: Read-only token for installing private npm packages before Claude runs.
|
|
@@ -92,7 +96,8 @@ jobs:
|
|
|
92
96
|
id: claude
|
|
93
97
|
uses: anthropics/claude-code-action@8251c103ac8c1d761882c86aba1412c7f583c844 # v1.0.213
|
|
94
98
|
env:
|
|
95
|
-
|
|
99
|
+
TESTER_APIFY_TOKEN: ${{ secrets.TESTER_APIFY_TOKEN }}
|
|
100
|
+
MAX_FLAKINESS_RUNS: ${{ inputs.max_flakiness_runs }}
|
|
96
101
|
with:
|
|
97
102
|
prompt: |
|
|
98
103
|
The failing E2E platform test ran in workflow run ${{ github.run_id }} of this repo.
|
|
@@ -100,13 +105,15 @@ jobs:
|
|
|
100
105
|
${{ inputs.investigate_prompt }}
|
|
101
106
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
102
107
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
108
|
+
plugin_marketplaces: https://github.com/apify/apify-test-tools.git
|
|
109
|
+
plugins: platform-tests-investigate-and-fix@apify-test-tools
|
|
103
110
|
# The `tools` query parameter below selects which tools the Apify MCP server
|
|
104
111
|
# exposes: reading an Actor run — its status, exit code, storage ids and log — and
|
|
105
112
|
# reading that run's dataset items and its key-value store keys and records, e.g.
|
|
106
113
|
# INPUT, OUTPUT and error snapshots. WARN: the server adds `abort-actor-run` on top
|
|
107
114
|
# even when not listed here, see https://github.com/apify/apify-mcp-server
|
|
108
115
|
claude_args: |
|
|
109
|
-
--mcp-config '{"mcpServers":{"apify":{"type":"http","url":"https://mcp.apify.com/?tools=get-actor-run,get-actor-log,get-dataset-list,get-dataset,get-dataset-items,get-key-value-store-list,get-key-value-store,get-key-value-store-keys,get-key-value-store-record","headers":{"Authorization":"Bearer ${
|
|
116
|
+
--mcp-config '{"mcpServers":{"apify":{"type":"http","url":"https://mcp.apify.com/?tools=get-actor-run,get-actor-run-log,get-dataset-list,get-dataset,get-dataset-items,get-key-value-store-list,get-key-value-store,get-key-value-store-keys,get-key-value-store-record","headers":{"Authorization":"Bearer ${TESTER_APIFY_TOKEN}"}}}}'
|
|
110
117
|
--model ${{ inputs.model }}
|
|
111
118
|
--dangerously-skip-permissions
|
|
112
119
|
|
package/CHANGELOG.md
CHANGED
|
@@ -9,12 +9,14 @@ All notable changes to this project will be documented in this file.
|
|
|
9
9
|
|
|
10
10
|
- **relative-path:** Path safety and manipulation lib ([#154](https://github.com/apify/apify-test-tools/pull/154)) ([c151a1c](https://github.com/apify/apify-test-tools/commit/c151a1c2755fef98b335d48a3461f5042b90def9)) by [@JuanGalilea](https://github.com/JuanGalilea)
|
|
11
11
|
- **logger:** Add logger and ban console statements ([#170](https://github.com/apify/apify-test-tools/pull/170)) ([b295d36](https://github.com/apify/apify-test-tools/commit/b295d363276c92fc6c30dd34d7a90acef5d380bf)) by [@JuanGalilea](https://github.com/JuanGalilea), closes [#156](https://github.com/apify/apify-test-tools/issues/156)
|
|
12
|
+
- Give claude investigator a skill to test flakiness ([#184](https://github.com/apify/apify-test-tools/pull/184)) ([b490298](https://github.com/apify/apify-test-tools/commit/b4902987dc2ae55882463ff8223e1e9f25b8b497)) by [@foxt451](https://github.com/foxt451), closes [#162](https://github.com/apify/apify-test-tools/issues/162)
|
|
12
13
|
|
|
13
14
|
### 🐛 Bug Fixes
|
|
14
15
|
|
|
15
16
|
- **internal-workflows:** Wait out npm's async publish before holding the major tag ([#160](https://github.com/apify/apify-test-tools/pull/160)) ([78434f0](https://github.com/apify/apify-test-tools/commit/78434f03d288ecbd383d55d56a1610f06ded298f)) by [@metalwarrior665](https://github.com/metalwarrior665)
|
|
16
17
|
- Skip builds and tests when the net diff is empty ([#171](https://github.com/apify/apify-test-tools/pull/171)) ([874d70c](https://github.com/apify/apify-test-tools/commit/874d70ca5506f7ec742a1808c9bf6c670f08552c)) by [@artogahr](https://github.com/artogahr)
|
|
17
18
|
- **workflows:** Add npm install step to Claude fix workflow ([#161](https://github.com/apify/apify-test-tools/pull/161)) ([d8884e0](https://github.com/apify/apify-test-tools/commit/d8884e07f778de14c8d9d7f0ec9e0a1672c224ad)) by [@foxt451](https://github.com/foxt451), closes [#39](https://github.com/apify/apify-test-tools/issues/39)
|
|
19
|
+
- **worktree:** Support worktree usage in tests ([#188](https://github.com/apify/apify-test-tools/pull/188)) ([ffc7e7b](https://github.com/apify/apify-test-tools/commit/ffc7e7bf1466af00eed671137feff0cbda712003)) by [@JuanGalilea](https://github.com/JuanGalilea)
|
|
18
20
|
|
|
19
21
|
|
|
20
22
|
<!-- git-cliff-unreleased-end -->
|
package/README.md
CHANGED
|
@@ -258,13 +258,12 @@ it at a branch only to test a prompt change.
|
|
|
258
258
|
Callers pass `secrets: inherit`. The workflows do not turn every inherited secret into job-wide
|
|
259
259
|
environment variables, so a secret is only visible to the step that needs it:
|
|
260
260
|
|
|
261
|
-
| Secret | Reaches
|
|
262
|
-
| ------------------------------------------------------------------------- |
|
|
263
|
-
| `NPM_TOKEN` | dependency install steps only, as both `NPM_TOKEN` and `NODE_AUTH_TOKEN`. Use a read-only token: npm granular tokens can be read-only, classic automation tokens can publish.
|
|
264
|
-
| the Actor tokens named by `tokenEnvVar` in `apify-test-tools.config.json` | the `build`, `release`, and `delete-old-builds` steps only
|
|
265
|
-
| `TESTER_APIFY_TOKEN` | the vitest step
|
|
266
|
-
| `SLACK_TOKEN_TESTS_BOT` / `SLACK_TOKEN_RELEASES_BOT` | the reporting and release steps only
|
|
267
|
-
| `TESTER_APIFY_TOKEN_READ_ONLY` | the Claude investigation step only, as the Apify MCP server's bearer token. Required by `public_platform-tests-claude-investigate-and-fix`. Use a read-only token: it reads the failing run, its log and its storages. |
|
|
261
|
+
| Secret | Reaches |
|
|
262
|
+
| ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
263
|
+
| `NPM_TOKEN` | dependency install steps only, as both `NPM_TOKEN` and `NODE_AUTH_TOKEN`. Use a read-only token: npm granular tokens can be read-only, classic automation tokens can publish. |
|
|
264
|
+
| the Actor tokens named by `tokenEnvVar` in `apify-test-tools.config.json` | the `build`, `release`, and `delete-old-builds` steps only |
|
|
265
|
+
| `TESTER_APIFY_TOKEN` | the vitest step, and the Claude investigation step: the Apify MCP server's bearer token for reading the failing run, its log and its storages, and Claude's token for re-running it to measure how often the failure happens |
|
|
266
|
+
| `SLACK_TOKEN_TESTS_BOT` / `SLACK_TOKEN_RELEASES_BOT` | the reporting and release steps only |
|
|
268
267
|
|
|
269
268
|
The Actor tokens are the one set that cannot be listed in the workflow, because each Actor names its
|
|
270
269
|
own token via `tokenEnvVar` in `apify-test-tools.config.json`. Those steps pass
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { spawnSync } from 'node:child_process';
|
|
2
1
|
import fs from 'node:fs/promises';
|
|
3
2
|
import os from 'node:os';
|
|
4
3
|
import path from 'node:path';
|
|
@@ -9,17 +8,7 @@ import { collectNonIgnoredFiles, collectSourceFiles, flattenMonorepoContext, rew
|
|
|
9
8
|
// tsconfig (used for the real build and for eslint's type-aware linting) has no such restriction.
|
|
10
9
|
} from '../../../bin/build-from-local.js';
|
|
11
10
|
import * as Utils from '../../../bin/utils.js';
|
|
12
|
-
// Defaults to the real spawnSync so `git init`/`git ls-files` calls made by the code under test
|
|
13
|
-
// (and by test setup below) actually run — individual tests override this via mockReturnValue
|
|
14
|
-
// where they need to fake git's output, and vi.restoreAllMocks() reverts back to this passthrough.
|
|
15
|
-
vi.mock('node:child_process', async (importOriginal) => {
|
|
16
|
-
const actual = await importOriginal();
|
|
17
|
-
return { ...actual, spawnSync: vi.fn(actual.spawnSync) };
|
|
18
|
-
});
|
|
19
11
|
const mkTempDir = async (prefix) => fs.mkdtemp(path.join(os.tmpdir(), prefix));
|
|
20
|
-
const initGitRepo = (dir) => {
|
|
21
|
-
spawnSync('git', ['init', '-q'], { cwd: dir });
|
|
22
|
-
};
|
|
23
12
|
describe('build-from-local helpers', () => {
|
|
24
13
|
const tempDirs = [];
|
|
25
14
|
afterEach(async () => {
|
|
@@ -30,13 +19,11 @@ describe('build-from-local helpers', () => {
|
|
|
30
19
|
it('drops secret-pattern files and gitignored files, keeps everything else', async () => {
|
|
31
20
|
const rootDir = await mkTempDir('apify-test-tools-collect-');
|
|
32
21
|
tempDirs.push(rootDir);
|
|
33
|
-
initGitRepo(rootDir);
|
|
34
22
|
await fs.writeFile(path.join(rootDir, 'main.js'), 'console.log(1)');
|
|
35
23
|
await fs.writeFile(path.join(rootDir, '.env'), 'SECRET=1');
|
|
36
24
|
await fs.mkdir(path.join(rootDir, 'sub'));
|
|
37
25
|
await fs.writeFile(path.join(rootDir, 'sub', 'ignored.log'), 'log');
|
|
38
|
-
|
|
39
|
-
// proves the secret-pattern backstop applies independently of .gitignore.
|
|
26
|
+
vi.spyOn(Utils, 'listRepoFilePaths').mockResolvedValue(['main.js', '.env', 'sub/ignored.log']);
|
|
40
27
|
vi.spyOn(Utils, 'getGitignoredPaths').mockImplementation(async (relativePaths) => new Set(relativePaths.filter((p) => p.endsWith('.log'))));
|
|
41
28
|
const result = await collectNonIgnoredFiles(rootDir, rootDir);
|
|
42
29
|
expect(result).toStrictEqual([path.join(rootDir, 'main.js')]);
|
|
@@ -100,7 +87,6 @@ describe('build-from-local helpers', () => {
|
|
|
100
87
|
it("always collects the actor's own .actor/actor.json for a monorepo actor, since flattenMonorepoContext depends on it", async () => {
|
|
101
88
|
const repoRoot = await mkTempDir('apify-test-tools-collect-source-');
|
|
102
89
|
tempDirs.push(repoRoot);
|
|
103
|
-
initGitRepo(repoRoot);
|
|
104
90
|
const originalCwd = process.cwd();
|
|
105
91
|
// We simulate the working directory being the repo root, since collectSourceFiles uses relative paths to the repo root.
|
|
106
92
|
process.chdir(repoRoot);
|
|
@@ -110,6 +96,11 @@ describe('build-from-local helpers', () => {
|
|
|
110
96
|
await fs.mkdir(path.join(actorDir, '.actor'), { recursive: true });
|
|
111
97
|
await fs.writeFile(path.join(actorDir, '.actor', 'actor.json'), JSON.stringify({ actorSpecification: 1, name: 'actor', dockerContextDir: '../../..' }));
|
|
112
98
|
await fs.writeFile(path.join(cwd, 'package.json'), '{}');
|
|
99
|
+
vi.spyOn(Utils, 'listRepoFilePaths').mockResolvedValue([
|
|
100
|
+
'actors/owner_actor/.actor/actor.json',
|
|
101
|
+
'package.json',
|
|
102
|
+
]);
|
|
103
|
+
vi.spyOn(Utils, 'getGitignoredPaths').mockResolvedValue(new Set(['actors/owner_actor/.actor/actor.json']));
|
|
113
104
|
const sourceFiles = await collectSourceFiles('owner/actor', actorDir);
|
|
114
105
|
expect(sourceFiles.map((file) => file.name)).toContain('.actor/actor.json');
|
|
115
106
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-from-local.test.js","sourceRoot":"","sources":["../../../../test/unit/bin/build-from-local.test.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"build-from-local.test.js","sourceRoot":"","sources":["../../../../test/unit/bin/build-from-local.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,54 +1,24 @@
|
|
|
1
|
-
import os from 'node:os';
|
|
2
|
-
import path from 'node:path';
|
|
3
1
|
import { describe, expect, it } from 'vitest';
|
|
4
|
-
import {
|
|
2
|
+
import { GitCommandError, runGitCommand } from '../../../bin/utils.js';
|
|
5
3
|
describe('runGitCommand', () => {
|
|
6
|
-
it('returns trimmed Git output', async () => {
|
|
7
|
-
await expect(runGitCommand(['
|
|
8
|
-
cwd: process.cwd(),
|
|
9
|
-
operation: 'check whether the current directory is in a Git repository',
|
|
10
|
-
})).resolves.toBe('true');
|
|
4
|
+
it('returns trimmed Git output without requiring a repository', async () => {
|
|
5
|
+
await expect(runGitCommand(['--version'])).resolves.toMatch(/^git version /);
|
|
11
6
|
});
|
|
12
|
-
it('
|
|
13
|
-
const error = await runGitCommand(['
|
|
14
|
-
|
|
15
|
-
operation: 'verify a commit',
|
|
7
|
+
it('wraps a Git failure with the command and operation', async () => {
|
|
8
|
+
const error = await runGitCommand(['--definitely-invalid-option'], {
|
|
9
|
+
operation: 'check error handling',
|
|
16
10
|
}).catch((failure) => failure);
|
|
17
11
|
expect(error).toBeInstanceOf(GitCommandError);
|
|
18
12
|
if (error instanceof GitCommandError) {
|
|
19
|
-
expect(error.exitCode).toBe(
|
|
20
|
-
expect(error.
|
|
21
|
-
expect(error.message).toContain('
|
|
22
|
-
expect(error.
|
|
13
|
+
expect(error.exitCode).not.toBe(0);
|
|
14
|
+
expect(error.message).toContain('Failed to check error handling.\n');
|
|
15
|
+
expect(error.message).toContain('Command: git "--definitely-invalid-option"\n');
|
|
16
|
+
expect(error.gitError).toContain('--definitely-invalid-option');
|
|
23
17
|
expect(error.cause).toBeInstanceOf(Error);
|
|
24
18
|
}
|
|
25
19
|
});
|
|
26
|
-
it('passes arguments literally
|
|
27
|
-
await expect(runGitCommand(['
|
|
28
|
-
operation: 'verify a commit reference',
|
|
29
|
-
})).rejects.toThrow('Command: git "rev-parse" "--verify" "HEAD; echo injected"\nGit error:');
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
describe('listRepoFilePaths', () => {
|
|
33
|
-
it('lists files under a subdirectory relative to the repository root', async () => {
|
|
34
|
-
const repoRoot = process.cwd();
|
|
35
|
-
await expect(listRepoFilePaths(repoRoot, path.join(repoRoot, 'bin'))).resolves.toContain('bin/utils.ts');
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
describe('getGitignoredPaths', () => {
|
|
39
|
-
it('returns an empty set for no paths', async () => {
|
|
40
|
-
await expect(getGitignoredPaths([])).resolves.toStrictEqual(new Set());
|
|
41
|
-
});
|
|
42
|
-
it('returns only paths ignored by the repository rules', async () => {
|
|
43
|
-
await expect(getGitignoredPaths(['node_modules/example.js', 'bin/utils.ts', '.DS_Store'], process.cwd())).resolves.toStrictEqual(new Set(['node_modules/example.js', '.DS_Store']));
|
|
44
|
-
});
|
|
45
|
-
it('returns an empty set when Git exits with code 1', async () => {
|
|
46
|
-
await expect(getGitignoredPaths(['bin/utils.ts'], process.cwd())).resolves.toStrictEqual(new Set());
|
|
47
|
-
});
|
|
48
|
-
it('rejects unexpected Git errors', async () => {
|
|
49
|
-
await expect(getGitignoredPaths(['bin/utils.ts'], os.tmpdir())).rejects.toThrow('Failed to check which repository paths are ignored.\n' +
|
|
50
|
-
'Command: git "check-ignore" "--" "bin/utils.ts"\n' +
|
|
51
|
-
'Git error: fatal: not a git repository');
|
|
20
|
+
it('passes arguments literally instead of running them through a shell', async () => {
|
|
21
|
+
await expect(runGitCommand(['--version; echo injected'])).rejects.toThrow('Command: git "--version; echo injected"\nGit error:');
|
|
52
22
|
});
|
|
53
23
|
});
|
|
54
24
|
//# sourceMappingURL=git-command.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-command.test.js","sourceRoot":"","sources":["../../../../test/unit/bin/git-command.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"git-command.test.js","sourceRoot":"","sources":["../../../../test/unit/bin/git-command.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEvE,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC3B,EAAE,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;QACvE,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IACjF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QAChE,MAAM,KAAK,GAAY,MAAM,aAAa,CAAC,CAAC,6BAA6B,CAAC,EAAE;YACxE,SAAS,EAAE,sBAAsB;SACpC,CAAC,CAAC,KAAK,CAAC,CAAC,OAAgB,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC;QAExC,MAAM,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,eAAe,CAAC,CAAC;QAC9C,IAAI,KAAK,YAAY,eAAe,EAAE,CAAC;YACnC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACnC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,mCAAmC,CAAC,CAAC;YACrE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,8CAA8C,CAAC,CAAC;YAChF,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,6BAA6B,CAAC,CAAC;YAChE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QAC9C,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oEAAoE,EAAE,KAAK,IAAI,EAAE;QAChF,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CACrE,qDAAqD,CACxD,CAAC;IACN,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
|