perfgraph 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/README.md +280 -0
- package/dist/causal/builder.d.ts +34 -0
- package/dist/causal/builder.d.ts.map +1 -0
- package/dist/causal/builder.js +132 -0
- package/dist/causal/builder.js.map +1 -0
- package/dist/causal/index.d.ts +11 -0
- package/dist/causal/index.d.ts.map +1 -0
- package/dist/causal/index.js +10 -0
- package/dist/causal/index.js.map +1 -0
- package/dist/causal/rules/js.d.ts +49 -0
- package/dist/causal/rules/js.d.ts.map +1 -0
- package/dist/causal/rules/js.js +209 -0
- package/dist/causal/rules/js.js.map +1 -0
- package/dist/causal/rules/layout.d.ts +39 -0
- package/dist/causal/rules/layout.d.ts.map +1 -0
- package/dist/causal/rules/layout.js +169 -0
- package/dist/causal/rules/layout.js.map +1 -0
- package/dist/causal/rules/lcp.d.ts +49 -0
- package/dist/causal/rules/lcp.d.ts.map +1 -0
- package/dist/causal/rules/lcp.js +250 -0
- package/dist/causal/rules/lcp.js.map +1 -0
- package/dist/causal/rules/network.d.ts +39 -0
- package/dist/causal/rules/network.d.ts.map +1 -0
- package/dist/causal/rules/network.js +140 -0
- package/dist/causal/rules/network.js.map +1 -0
- package/dist/causal/types.d.ts +142 -0
- package/dist/causal/types.d.ts.map +1 -0
- package/dist/causal/types.js +88 -0
- package/dist/causal/types.js.map +1 -0
- package/dist/cli/analyze.d.ts +33 -0
- package/dist/cli/analyze.d.ts.map +1 -0
- package/dist/cli/analyze.js +137 -0
- package/dist/cli/analyze.js.map +1 -0
- package/dist/cli/collect.d.ts +28 -0
- package/dist/cli/collect.d.ts.map +1 -0
- package/dist/cli/collect.js +125 -0
- package/dist/cli/collect.js.map +1 -0
- package/dist/cli/extract.d.ts +31 -0
- package/dist/cli/extract.d.ts.map +1 -0
- package/dist/cli/extract.js +135 -0
- package/dist/cli/extract.js.map +1 -0
- package/dist/cli/mcp.d.ts +14 -0
- package/dist/cli/mcp.d.ts.map +1 -0
- package/dist/cli/mcp.js +53 -0
- package/dist/cli/mcp.js.map +1 -0
- package/dist/cli/normalize.d.ts +58 -0
- package/dist/cli/normalize.d.ts.map +1 -0
- package/dist/cli/normalize.js +179 -0
- package/dist/cli/normalize.js.map +1 -0
- package/dist/cli/report.d.ts +33 -0
- package/dist/cli/report.d.ts.map +1 -0
- package/dist/cli/report.js +137 -0
- package/dist/cli/report.js.map +1 -0
- package/dist/cli/run.d.ts +49 -0
- package/dist/cli/run.d.ts.map +1 -0
- package/dist/cli/run.js +243 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/collect/buffer-monitor.d.ts +25 -0
- package/dist/collect/buffer-monitor.d.ts.map +1 -0
- package/dist/collect/buffer-monitor.js +69 -0
- package/dist/collect/buffer-monitor.js.map +1 -0
- package/dist/collect/console.d.ts +25 -0
- package/dist/collect/console.d.ts.map +1 -0
- package/dist/collect/console.js +82 -0
- package/dist/collect/console.js.map +1 -0
- package/dist/collect/coverage.d.ts +26 -0
- package/dist/collect/coverage.d.ts.map +1 -0
- package/dist/collect/coverage.js +99 -0
- package/dist/collect/coverage.js.map +1 -0
- package/dist/collect/dom.d.ts +37 -0
- package/dist/collect/dom.d.ts.map +1 -0
- package/dist/collect/dom.js +118 -0
- package/dist/collect/dom.js.map +1 -0
- package/dist/collect/lighthouse.d.ts +41 -0
- package/dist/collect/lighthouse.d.ts.map +1 -0
- package/dist/collect/lighthouse.js +111 -0
- package/dist/collect/lighthouse.js.map +1 -0
- package/dist/collect/network.d.ts +32 -0
- package/dist/collect/network.d.ts.map +1 -0
- package/dist/collect/network.js +236 -0
- package/dist/collect/network.js.map +1 -0
- package/dist/collect/orchestrator.d.ts +23 -0
- package/dist/collect/orchestrator.d.ts.map +1 -0
- package/dist/collect/orchestrator.js +350 -0
- package/dist/collect/orchestrator.js.map +1 -0
- package/dist/collect/performance.d.ts +28 -0
- package/dist/collect/performance.d.ts.map +1 -0
- package/dist/collect/performance.js +108 -0
- package/dist/collect/performance.js.map +1 -0
- package/dist/collect/runtime.d.ts +26 -0
- package/dist/collect/runtime.d.ts.map +1 -0
- package/dist/collect/runtime.js +81 -0
- package/dist/collect/runtime.js.map +1 -0
- package/dist/collect/session.d.ts +58 -0
- package/dist/collect/session.d.ts.map +1 -0
- package/dist/collect/session.js +162 -0
- package/dist/collect/session.js.map +1 -0
- package/dist/collect/trace.d.ts +40 -0
- package/dist/collect/trace.d.ts.map +1 -0
- package/dist/collect/trace.js +127 -0
- package/dist/collect/trace.js.map +1 -0
- package/dist/collect/types.d.ts +317 -0
- package/dist/collect/types.d.ts.map +1 -0
- package/dist/collect/types.js +12 -0
- package/dist/collect/types.js.map +1 -0
- package/dist/distill/insights.d.ts +67 -0
- package/dist/distill/insights.d.ts.map +1 -0
- package/dist/distill/insights.js +317 -0
- package/dist/distill/insights.js.map +1 -0
- package/dist/distill/summaries.d.ts +71 -0
- package/dist/distill/summaries.d.ts.map +1 -0
- package/dist/distill/summaries.js +165 -0
- package/dist/distill/summaries.js.map +1 -0
- package/dist/extract/critical-path.d.ts +17 -0
- package/dist/extract/critical-path.d.ts.map +1 -0
- package/dist/extract/critical-path.js +95 -0
- package/dist/extract/critical-path.js.map +1 -0
- package/dist/extract/index.d.ts +16 -0
- package/dist/extract/index.d.ts.map +1 -0
- package/dist/extract/index.js +32 -0
- package/dist/extract/index.js.map +1 -0
- package/dist/extract/js-hotspots.d.ts +15 -0
- package/dist/extract/js-hotspots.d.ts.map +1 -0
- package/dist/extract/js-hotspots.js +22 -0
- package/dist/extract/js-hotspots.js.map +1 -0
- package/dist/extract/layout-shifts.d.ts +19 -0
- package/dist/extract/layout-shifts.d.ts.map +1 -0
- package/dist/extract/layout-shifts.js +47 -0
- package/dist/extract/layout-shifts.js.map +1 -0
- package/dist/extract/lcp-breakdown.d.ts +19 -0
- package/dist/extract/lcp-breakdown.d.ts.map +1 -0
- package/dist/extract/lcp-breakdown.js +110 -0
- package/dist/extract/lcp-breakdown.js.map +1 -0
- package/dist/extract/main-thread.d.ts +16 -0
- package/dist/extract/main-thread.d.ts.map +1 -0
- package/dist/extract/main-thread.js +41 -0
- package/dist/extract/main-thread.js.map +1 -0
- package/dist/extract/render-blocking.d.ts +16 -0
- package/dist/extract/render-blocking.d.ts.map +1 -0
- package/dist/extract/render-blocking.js +79 -0
- package/dist/extract/render-blocking.js.map +1 -0
- package/dist/extract/third-party.d.ts +15 -0
- package/dist/extract/third-party.d.ts.map +1 -0
- package/dist/extract/third-party.js +127 -0
- package/dist/extract/third-party.js.map +1 -0
- package/dist/extract/types.d.ts +183 -0
- package/dist/extract/types.d.ts.map +1 -0
- package/dist/extract/types.js +118 -0
- package/dist/extract/types.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +188 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/handlers.d.ts +72 -0
- package/dist/mcp/handlers.d.ts.map +1 -0
- package/dist/mcp/handlers.js +285 -0
- package/dist/mcp/handlers.js.map +1 -0
- package/dist/mcp/progress.d.ts +37 -0
- package/dist/mcp/progress.d.ts.map +1 -0
- package/dist/mcp/progress.js +51 -0
- package/dist/mcp/progress.js.map +1 -0
- package/dist/mcp/prompts.d.ts +57 -0
- package/dist/mcp/prompts.d.ts.map +1 -0
- package/dist/mcp/prompts.js +225 -0
- package/dist/mcp/prompts.js.map +1 -0
- package/dist/mcp/resources.d.ts +48 -0
- package/dist/mcp/resources.d.ts.map +1 -0
- package/dist/mcp/resources.js +149 -0
- package/dist/mcp/resources.js.map +1 -0
- package/dist/mcp/server.d.ts +34 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +452 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/types.d.ts +47 -0
- package/dist/mcp/types.d.ts.map +1 -0
- package/dist/mcp/types.js +67 -0
- package/dist/mcp/types.js.map +1 -0
- package/dist/normalize/clock.d.ts +40 -0
- package/dist/normalize/clock.d.ts.map +1 -0
- package/dist/normalize/clock.js +96 -0
- package/dist/normalize/clock.js.map +1 -0
- package/dist/normalize/dom-ir.d.ts +16 -0
- package/dist/normalize/dom-ir.d.ts.map +1 -0
- package/dist/normalize/dom-ir.js +48 -0
- package/dist/normalize/dom-ir.js.map +1 -0
- package/dist/normalize/index.d.ts +23 -0
- package/dist/normalize/index.d.ts.map +1 -0
- package/dist/normalize/index.js +118 -0
- package/dist/normalize/index.js.map +1 -0
- package/dist/normalize/lighthouse-insights.d.ts +27 -0
- package/dist/normalize/lighthouse-insights.d.ts.map +1 -0
- package/dist/normalize/lighthouse-insights.js +318 -0
- package/dist/normalize/lighthouse-insights.js.map +1 -0
- package/dist/normalize/lighthouse-ir.d.ts +25 -0
- package/dist/normalize/lighthouse-ir.d.ts.map +1 -0
- package/dist/normalize/lighthouse-ir.js +151 -0
- package/dist/normalize/lighthouse-ir.js.map +1 -0
- package/dist/normalize/network-ir.d.ts +23 -0
- package/dist/normalize/network-ir.d.ts.map +1 -0
- package/dist/normalize/network-ir.js +180 -0
- package/dist/normalize/network-ir.js.map +1 -0
- package/dist/normalize/runtime-ir.d.ts +18 -0
- package/dist/normalize/runtime-ir.d.ts.map +1 -0
- package/dist/normalize/runtime-ir.js +114 -0
- package/dist/normalize/runtime-ir.js.map +1 -0
- package/dist/normalize/trace-ir.d.ts +20 -0
- package/dist/normalize/trace-ir.d.ts.map +1 -0
- package/dist/normalize/trace-ir.js +224 -0
- package/dist/normalize/trace-ir.js.map +1 -0
- package/dist/normalize/types.d.ts +536 -0
- package/dist/normalize/types.d.ts.map +1 -0
- package/dist/normalize/types.js +242 -0
- package/dist/normalize/types.js.map +1 -0
- package/dist/output/manifest.d.ts +44 -0
- package/dist/output/manifest.d.ts.map +1 -0
- package/dist/output/manifest.js +232 -0
- package/dist/output/manifest.js.map +1 -0
- package/dist/output/toon.d.ts +19 -0
- package/dist/output/toon.d.ts.map +1 -0
- package/dist/output/toon.js +22 -0
- package/dist/output/toon.js.map +1 -0
- package/dist/output/writer.d.ts +31 -0
- package/dist/output/writer.d.ts.map +1 -0
- package/dist/output/writer.js +51 -0
- package/dist/output/writer.js.map +1 -0
- package/dist/report/analyzer.d.ts +29 -0
- package/dist/report/analyzer.d.ts.map +1 -0
- package/dist/report/analyzer.js +330 -0
- package/dist/report/analyzer.js.map +1 -0
- package/dist/report/index.d.ts +13 -0
- package/dist/report/index.d.ts.map +1 -0
- package/dist/report/index.js +12 -0
- package/dist/report/index.js.map +1 -0
- package/dist/report/markdown.d.ts +21 -0
- package/dist/report/markdown.d.ts.map +1 -0
- package/dist/report/markdown.js +182 -0
- package/dist/report/markdown.js.map +1 -0
- package/dist/report/remediations.d.ts +42 -0
- package/dist/report/remediations.d.ts.map +1 -0
- package/dist/report/remediations.js +179 -0
- package/dist/report/remediations.js.map +1 -0
- package/dist/report/scorer.d.ts +35 -0
- package/dist/report/scorer.d.ts.map +1 -0
- package/dist/report/scorer.js +72 -0
- package/dist/report/scorer.js.map +1 -0
- package/dist/report/types.d.ts +201 -0
- package/dist/report/types.d.ts.map +1 -0
- package/dist/report/types.js +149 -0
- package/dist/report/types.js.map +1 -0
- package/dist/shared/fs-utils.d.ts +12 -0
- package/dist/shared/fs-utils.d.ts.map +1 -0
- package/dist/shared/fs-utils.js +44 -0
- package/dist/shared/fs-utils.js.map +1 -0
- package/dist/shared/types.d.ts +69 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +18 -0
- package/dist/shared/types.js.map +1 -0
- package/dist/shared/utils.d.ts +6 -0
- package/dist/shared/utils.d.ts.map +1 -0
- package/dist/shared/utils.js +9 -0
- package/dist/shared/utils.js.map +1 -0
- package/package.json +67 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
# PerfGraph
|
|
2
|
+
|
|
3
|
+
[![npm][badge-version]][npm] [![license][badge-license]][license]
|
|
4
|
+
[![npm downloads][badge-downloads]][npm] [![github stars][badge-stars]][repo]
|
|
5
|
+
|
|
6
|
+
> Real browser metrics → causal degradation graph → actionable report. Built for AI agents, useful for humans.
|
|
7
|
+
|
|
8
|
+
PerfGraph launches a headless Chromium browser, captures performance data via Chrome DevTools Protocol, runs it through a 5-stage analysis pipeline, and spits out a structured JSON report with issues sorted by severity, causal chains, and prioritized fixes.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npx perfgraph run --url https://example.com --pretty
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Why
|
|
15
|
+
|
|
16
|
+
Lighthouse gives you a score. PerfGraph tells you _why_ it's bad and what to fix first.
|
|
17
|
+
|
|
18
|
+
Instead of digging through a 10k-line trace.json or a wall of Lighthouse audits, you get a focused report with root causes linked to impact. The output is designed to be read by AI agents (or you) without a decoder ring.
|
|
19
|
+
|
|
20
|
+
## Pipeline
|
|
21
|
+
|
|
22
|
+

|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
collect → normalize → extract → analyze → report
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
| Step | What it does |
|
|
29
|
+
| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
30
|
+
| **Collect** | Launches headless Chromium, captures network, trace, performance API, JS coverage, console logs, DOM snapshot, and Lighthouse audit |
|
|
31
|
+
| **Normalize** | Validates raw data through Zod schemas into a unified Intermediate Representation (IRBundle). All timestamps normalized, data cleaned |
|
|
32
|
+
| **Extract** | Computes 7 diagnostic feature sets from the IR: LCP breakdown, critical network chain, main-thread blocking time, JS hotspots, layout shifts, third-party overhead, render-blocking score |
|
|
33
|
+
| **Analyze** | Runs 30+ causal rules against the features, builds a directed graph where edges represent known causal relationships with confidence levels (strong/medium/weak) |
|
|
34
|
+
| **Report** | Produces a self-contained JSON report with issues sorted by severity, causal chains from root cause to user impact, and prioritized recommendations with expected impact estimates |
|
|
35
|
+
|
|
36
|
+
## Install
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install -g perfgraph
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Or skip the install:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npx perfgraph --help
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Requirements:** Node.js ≥ 22, Chromium (Playwright installs it automatically on first run).
|
|
49
|
+
|
|
50
|
+
## Quick start
|
|
51
|
+
|
|
52
|
+
Full pipeline, one command:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
perfgraph run --url https://example.com --pretty
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Step by step:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# 1. Collect data
|
|
62
|
+
perfgraph collect --url https://example.com --output ./results
|
|
63
|
+
|
|
64
|
+
# 2. Normalize → Extract → Report
|
|
65
|
+
perfgraph normalize ./results/perfgraph_example_20260609_120000 --output ir.json
|
|
66
|
+
perfgraph extract ir.json --output features.json
|
|
67
|
+
perfgraph report features.json --output report.json --pretty
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Commands
|
|
71
|
+
|
|
72
|
+
### `perfgraph run`
|
|
73
|
+
|
|
74
|
+
Full pipeline in one shot.
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
--url <url> Required. Target URL to analyze.
|
|
78
|
+
--output <dir> Output directory (default: ./perfgraph-output).
|
|
79
|
+
--runs <n> Number of collection runs (default: 1).
|
|
80
|
+
--pretty Pretty-print the final report JSON.
|
|
81
|
+
--device <name> Mobile emulation (e.g. "iPhone 13").
|
|
82
|
+
--no-lighthouse Skip Lighthouse collection.
|
|
83
|
+
--no-coverage Skip JS/CSS coverage.
|
|
84
|
+
--no-console Skip console log capture.
|
|
85
|
+
--no-dom Skip DOM snapshot.
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### `perfgraph collect`
|
|
89
|
+
|
|
90
|
+
Captures performance data from a URL. See `run` options — same flags apply.
|
|
91
|
+
|
|
92
|
+
### `perfgraph normalize <input>`
|
|
93
|
+
|
|
94
|
+
Converts raw collected data into a validated IRBundle. Accepts a run directory or a parent directory (auto-detects latest run).
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
--output, -o <file> Write to file (default: stdout).
|
|
98
|
+
--pretty Pretty-print JSON.
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### `perfgraph extract <ir-file>`
|
|
102
|
+
|
|
103
|
+
Computes diagnostic features from a normalized IR bundle.
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
--input, -i <file> Path to IR JSON (alternative to positional).
|
|
107
|
+
--output, -o <file> Write to file (default: stdout).
|
|
108
|
+
--pretty Pretty-print JSON.
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### `perfgraph analyze <features-file>`
|
|
112
|
+
|
|
113
|
+
Applies causal rules and builds a degradation graph.
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
--input, -i <file> Path to FeatureSet JSON (alternative to positional).
|
|
117
|
+
--output, -o <file> Write to file (default: stdout).
|
|
118
|
+
--pretty Pretty-print JSON.
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### `perfgraph report <features-file>`
|
|
122
|
+
|
|
123
|
+
Generates the final performance report. Accepts a FeatureSet JSON (from `extract`) — runs the causal engine internally, no need to call `analyze` separately.
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
--input, -i <file> Path to FeatureSet JSON (alternative to positional).
|
|
127
|
+
--output, -o <file> Write to file (default: stdout).
|
|
128
|
+
--pretty Pretty-print JSON.
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### `perfgraph mcp`
|
|
132
|
+
|
|
133
|
+
Starts an MCP stdio server for AI agent integration. No flags. See [AGENTS.md](AGENTS.md) for details.
|
|
134
|
+
|
|
135
|
+
## Report format
|
|
136
|
+
|
|
137
|
+
The report is a single JSON file. Key sections:
|
|
138
|
+
|
|
139
|
+
```jsonc
|
|
140
|
+
{
|
|
141
|
+
"meta": {
|
|
142
|
+
"url": "https://example.com",
|
|
143
|
+
"analyzedAt": "2026-06-09T19:15:19.000Z",
|
|
144
|
+
"reportVersion": "1.0.0",
|
|
145
|
+
"featureCount": 7,
|
|
146
|
+
"graphNodeCount": 24,
|
|
147
|
+
"graphEdgeCount": 31,
|
|
148
|
+
"ruleCount": 32,
|
|
149
|
+
},
|
|
150
|
+
"summary": {
|
|
151
|
+
"score": "moderate", // "good" | "moderate" | "poor"
|
|
152
|
+
"criticalIssues": 2,
|
|
153
|
+
"warnings": 5,
|
|
154
|
+
"infos": 3,
|
|
155
|
+
"topIssues": [
|
|
156
|
+
{
|
|
157
|
+
"id": "js-long-task",
|
|
158
|
+
"label": "Long task",
|
|
159
|
+
"severity": "critical",
|
|
160
|
+
"confidence": "strong",
|
|
161
|
+
},
|
|
162
|
+
],
|
|
163
|
+
},
|
|
164
|
+
"issues": [
|
|
165
|
+
{
|
|
166
|
+
"id": "lcp-slow",
|
|
167
|
+
"label": "LCP exceeds 2.5s threshold",
|
|
168
|
+
"severity": "critical",
|
|
169
|
+
"value": 4320,
|
|
170
|
+
"unit": "ms",
|
|
171
|
+
"threshold": 2500,
|
|
172
|
+
"confidence": "strong",
|
|
173
|
+
"remediation": "Optimize largest contentful paint element...",
|
|
174
|
+
"chainId": "lcp:3",
|
|
175
|
+
},
|
|
176
|
+
],
|
|
177
|
+
"chains": [
|
|
178
|
+
{
|
|
179
|
+
"id": "lcp:3",
|
|
180
|
+
"rootCause": "LCP > 2.5s",
|
|
181
|
+
"impact": "Poor user experience",
|
|
182
|
+
"path": [
|
|
183
|
+
"TTFB delayed by server response",
|
|
184
|
+
"Render-blocking stylesheets",
|
|
185
|
+
"LCP element render delay",
|
|
186
|
+
],
|
|
187
|
+
"length": 3,
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
"recommendations": [
|
|
191
|
+
{
|
|
192
|
+
"priority": "critical",
|
|
193
|
+
"category": "LCP",
|
|
194
|
+
"title": "Optimize Largest Contentful Paint",
|
|
195
|
+
"action": "Inline critical styles, defer non-critical CSS",
|
|
196
|
+
"expectedImpact": "Reduces LCP by ~40%",
|
|
197
|
+
"relatedIssues": ["lcp-slow"],
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
"features": {
|
|
201
|
+
/* raw extracted features for cross-referencing */
|
|
202
|
+
},
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## What it detects
|
|
207
|
+
|
|
208
|
+
| Category | Issues |
|
|
209
|
+
| --------------- | ----------------------------------------------------------------------------------- |
|
|
210
|
+
| **LCP** | Slow LCP, high TTFB, render-blocking resources, LCP resource delay chains |
|
|
211
|
+
| **JavaScript** | Long tasks, high TBT, heavy execution, unused code, main-thread bottlenecks |
|
|
212
|
+
| **Network** | Deep request chains, bandwidth bottlenecks, waterfall depth, critical path analysis |
|
|
213
|
+
| **Layout** | Layout shifts (CLS), large DOM size, forced reflows |
|
|
214
|
+
| **Third-party** | Third-party script overhead, tracking pixels, embedded widget impact |
|
|
215
|
+
|
|
216
|
+
## Architecture
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
src/
|
|
220
|
+
├── index.ts CLI entry — lazy-loads commands
|
|
221
|
+
├── collect/ CDP data collection via Playwright
|
|
222
|
+
│ ├── browser.ts Browser launcher
|
|
223
|
+
│ ├── collector.ts Orchestrator
|
|
224
|
+
│ ├── coverage.ts JS/CSS coverage
|
|
225
|
+
│ ├── network.ts Network request capture
|
|
226
|
+
│ ├── performance.ts Performance API metrics
|
|
227
|
+
│ ├── runtime.ts Runtime metadata
|
|
228
|
+
│ ├── dom.ts DOM snapshot
|
|
229
|
+
│ └── lighthouse.ts Lighthouse audit
|
|
230
|
+
├── normalize/ Data normalization & IR validation
|
|
231
|
+
├── extract/ 7 diagnostic feature extractors
|
|
232
|
+
├── causal/ Causal rule engine (30+ rules)
|
|
233
|
+
│ ├── builder.ts Graph construction
|
|
234
|
+
│ ├── rules/ Individual causal rules by category
|
|
235
|
+
│ └── types.ts Graph data types
|
|
236
|
+
├── report/ Report generation & scoring
|
|
237
|
+
│ ├── analyzer.ts Report builder
|
|
238
|
+
│ ├── scorer.ts Score computation
|
|
239
|
+
│ ├── remediations.ts Remediation templates
|
|
240
|
+
│ └── types.ts Report schema
|
|
241
|
+
├── distill/ Agent-optimized summary layer (insights.json)
|
|
242
|
+
├── mcp/ MCP stdio server
|
|
243
|
+
├── cli/ CLI command handlers
|
|
244
|
+
└── shared/ Shared utilities & types
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Development
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
npm install
|
|
251
|
+
npm run build # compile TypeScript
|
|
252
|
+
npm run typecheck # check types only
|
|
253
|
+
npm test # run tests
|
|
254
|
+
npm run test:watch # tests in watch mode
|
|
255
|
+
npm run dev # tsx watch — no build step
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## Tech
|
|
259
|
+
|
|
260
|
+
| Thing | What |
|
|
261
|
+
| ------------------ | --------------------------------------------- |
|
|
262
|
+
| Runtime | Node.js ≥ 22 |
|
|
263
|
+
| Language | TypeScript (strict, noUncheckedIndexedAccess) |
|
|
264
|
+
| Browser | Playwright (Chromium) |
|
|
265
|
+
| Validation | Zod at every data boundary |
|
|
266
|
+
| Graph engine | @dagrejs/graphlib |
|
|
267
|
+
| Performance audits | Lighthouse |
|
|
268
|
+
| Testing | Vitest |
|
|
269
|
+
|
|
270
|
+
## License
|
|
271
|
+
|
|
272
|
+
MIT
|
|
273
|
+
|
|
274
|
+
[badge-version]: https://img.shields.io/npm/v/perfgraph.svg
|
|
275
|
+
[badge-license]: https://img.shields.io/npm/l/perfgraph.svg
|
|
276
|
+
[badge-downloads]: https://img.shields.io/npm/dm/perfgraph.svg
|
|
277
|
+
[badge-stars]: https://img.shields.io/github/stars/Be1zebub/PerfGraph.svg?style=flat&logo=github
|
|
278
|
+
[npm]: https://www.npmjs.com/package/perfgraph
|
|
279
|
+
[license]: https://github.com/Be1zebub/PerfGraph/blob/master/LICENSE
|
|
280
|
+
[repo]: https://github.com/Be1zebub/PerfGraph
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Causal Graph Builder — deterministic rules engine.
|
|
3
|
+
*
|
|
4
|
+
* Takes a FeatureSet, evaluates all known causal rules, builds a DAG,
|
|
5
|
+
* validates acyclicity, and returns a typed CausalGraph.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
import type { FeatureSet } from '../extract/types.js';
|
|
10
|
+
import type { CausalGraph, CausalNode, CausalEdge } from './types.js';
|
|
11
|
+
export interface CausalRule {
|
|
12
|
+
/** Unique rule identifier (e.g. "lcp-ttfb-chain") */
|
|
13
|
+
id: string;
|
|
14
|
+
/** Human-readable rule label */
|
|
15
|
+
label: string;
|
|
16
|
+
/** Check whether this rule applies to the given FeatureSet */
|
|
17
|
+
applies: (features: FeatureSet) => boolean;
|
|
18
|
+
/** Build nodes and edges for this causal chain */
|
|
19
|
+
build: (features: FeatureSet) => {
|
|
20
|
+
nodes: CausalNode[];
|
|
21
|
+
edges: CausalEdge[];
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Build a deterministic CausalGraph from a FeatureSet.
|
|
26
|
+
*
|
|
27
|
+
* This is a pure function: same FeatureSet → same CausalGraph (given the same rules).
|
|
28
|
+
*
|
|
29
|
+
* @param features - The FeatureSet from Phase 3 extraction
|
|
30
|
+
* @returns A typed CausalGraph with nodes, edges, and metadata
|
|
31
|
+
* @throws If the graph contains cycles after all rules are applied
|
|
32
|
+
*/
|
|
33
|
+
export declare function buildCausalGraph(features: FeatureSet): CausalGraph;
|
|
34
|
+
//# sourceMappingURL=builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../src/causal/builder.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EACV,WAAW,EACX,UAAU,EACV,UAAU,EACX,MAAM,YAAY,CAAC;AAWpB,MAAM,WAAW,UAAU;IACzB,qDAAqD;IACrD,EAAE,EAAE,MAAM,CAAC;IACX,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,8DAA8D;IAC9D,OAAO,EAAE,CAAC,QAAQ,EAAE,UAAU,KAAK,OAAO,CAAC;IAC3C,kDAAkD;IAClD,KAAK,EAAE,CAAC,QAAQ,EAAE,UAAU,KAAK;QAAE,KAAK,EAAE,UAAU,EAAE,CAAC;QAAC,KAAK,EAAE,UAAU,EAAE,CAAA;KAAE,CAAC;CAC/E;AAkCD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,UAAU,GAAG,WAAW,CA8FlE"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Causal Graph Builder — deterministic rules engine.
|
|
3
|
+
*
|
|
4
|
+
* Takes a FeatureSet, evaluates all known causal rules, builds a DAG,
|
|
5
|
+
* validates acyclicity, and returns a typed CausalGraph.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
import { Graph, alg } from '@dagrejs/graphlib';
|
|
10
|
+
import { CausalGraphSchema } from './types.js';
|
|
11
|
+
import { lcpRules } from './rules/lcp.js';
|
|
12
|
+
import { networkRules } from './rules/network.js';
|
|
13
|
+
import { jsRules } from './rules/js.js';
|
|
14
|
+
import { layoutRules } from './rules/layout.js';
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// All registered rules
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// IMPORTANT: order matters — rules evaluated first get priority for
|
|
19
|
+
// node/edge IDs if there are overlaps (unlikely, but possible).
|
|
20
|
+
const ALL_RULES = [
|
|
21
|
+
...lcpRules,
|
|
22
|
+
...networkRules,
|
|
23
|
+
...jsRules,
|
|
24
|
+
...layoutRules,
|
|
25
|
+
];
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// Node deduplication: merge nodes with the same id
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
function mergeNodes(existing, incoming) {
|
|
30
|
+
return {
|
|
31
|
+
...existing,
|
|
32
|
+
// Prefer the non-undefined value
|
|
33
|
+
severity: incoming.severity ?? existing.severity,
|
|
34
|
+
value: incoming.value ?? existing.value,
|
|
35
|
+
unit: incoming.unit ?? existing.unit,
|
|
36
|
+
threshold: incoming.threshold ?? existing.threshold,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
// Build the causal graph from a FeatureSet
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
/**
|
|
43
|
+
* Build a deterministic CausalGraph from a FeatureSet.
|
|
44
|
+
*
|
|
45
|
+
* This is a pure function: same FeatureSet → same CausalGraph (given the same rules).
|
|
46
|
+
*
|
|
47
|
+
* @param features - The FeatureSet from Phase 3 extraction
|
|
48
|
+
* @returns A typed CausalGraph with nodes, edges, and metadata
|
|
49
|
+
* @throws If the graph contains cycles after all rules are applied
|
|
50
|
+
*/
|
|
51
|
+
export function buildCausalGraph(features) {
|
|
52
|
+
const g = new Graph({ directed: true });
|
|
53
|
+
const nodeMap = new Map();
|
|
54
|
+
// Track which rules were applicable
|
|
55
|
+
let applicableCount = 0;
|
|
56
|
+
for (const rule of ALL_RULES) {
|
|
57
|
+
if (!rule.applies(features))
|
|
58
|
+
continue;
|
|
59
|
+
applicableCount++;
|
|
60
|
+
const { nodes, edges } = rule.build(features);
|
|
61
|
+
// Add nodes (deduplicate by id)
|
|
62
|
+
for (const node of nodes) {
|
|
63
|
+
const existing = nodeMap.get(node.id);
|
|
64
|
+
if (existing) {
|
|
65
|
+
nodeMap.set(node.id, mergeNodes(existing, node));
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
nodeMap.set(node.id, node);
|
|
69
|
+
}
|
|
70
|
+
g.setNode(node.id, nodeMap.get(node.id));
|
|
71
|
+
}
|
|
72
|
+
// Add edges
|
|
73
|
+
for (const edge of edges) {
|
|
74
|
+
// Ensure both endpoints exist in the graph
|
|
75
|
+
if (!nodeMap.has(edge.source)) {
|
|
76
|
+
const fallback = {
|
|
77
|
+
id: edge.source,
|
|
78
|
+
label: edge.source,
|
|
79
|
+
type: 'bottleneck',
|
|
80
|
+
};
|
|
81
|
+
nodeMap.set(edge.source, fallback);
|
|
82
|
+
g.setNode(edge.source, fallback);
|
|
83
|
+
}
|
|
84
|
+
if (!nodeMap.has(edge.target)) {
|
|
85
|
+
const fallback = {
|
|
86
|
+
id: edge.target,
|
|
87
|
+
label: edge.target,
|
|
88
|
+
type: 'impact',
|
|
89
|
+
};
|
|
90
|
+
nodeMap.set(edge.target, fallback);
|
|
91
|
+
g.setNode(edge.target, fallback);
|
|
92
|
+
}
|
|
93
|
+
g.setEdge(edge.source, edge.target, edge);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// -----------------------------------------------------------------------
|
|
97
|
+
// Validation: check for cycles
|
|
98
|
+
// -----------------------------------------------------------------------
|
|
99
|
+
const cycles = alg.findCycles(g);
|
|
100
|
+
const hasCycle = cycles.length > 0;
|
|
101
|
+
if (hasCycle) {
|
|
102
|
+
const cycleDescriptions = cycles
|
|
103
|
+
.map((cycle) => cycle.join(' → '))
|
|
104
|
+
.join('; ');
|
|
105
|
+
throw new Error(`Causal graph contains cycle(s): ${cycleDescriptions}. ` +
|
|
106
|
+
`Cycle-free DAG is required for deterministic analysis.`);
|
|
107
|
+
}
|
|
108
|
+
// Topological sort (will throw if cycles remain)
|
|
109
|
+
const sortedIds = alg.topsort(g);
|
|
110
|
+
// -----------------------------------------------------------------------
|
|
111
|
+
// Build output — sorted by topological order
|
|
112
|
+
// -----------------------------------------------------------------------
|
|
113
|
+
const sortedNodes = sortedIds
|
|
114
|
+
.map((id) => g.node(id))
|
|
115
|
+
.filter((n) => n != null);
|
|
116
|
+
const allEdges = g.edges().map((e) => g.edge(e.v, e.w));
|
|
117
|
+
const output = {
|
|
118
|
+
nodes: sortedNodes,
|
|
119
|
+
edges: allEdges,
|
|
120
|
+
metadata: {
|
|
121
|
+
featureCount: applicableCount,
|
|
122
|
+
ruleCount: ALL_RULES.length,
|
|
123
|
+
totalNodes: sortedNodes.length,
|
|
124
|
+
totalEdges: allEdges.length,
|
|
125
|
+
hasCycle: false,
|
|
126
|
+
targetUrl: features.url ?? undefined,
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
// Final schema validation
|
|
130
|
+
return CausalGraphSchema.parse(output);
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builder.js","sourceRoot":"","sources":["../../src/causal/builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Causal Graph Builder — public API.
|
|
3
|
+
*
|
|
4
|
+
* Entry point for Phase 4: takes FeatureSet → returns CausalGraph.
|
|
5
|
+
*
|
|
6
|
+
* @packageDocumentation
|
|
7
|
+
*/
|
|
8
|
+
export { buildCausalGraph } from './builder.js';
|
|
9
|
+
export type { CausalRule } from './builder.js';
|
|
10
|
+
export { CausalNodeSchema, CausalEdgeSchema, CausalGraphSchema, type CausalNode, type CausalEdge, type CausalGraph, type CausalGraphMetadata, type Confidence, type Severity, } from './types.js';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/causal/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,KAAK,UAAU,EACf,KAAK,QAAQ,GACd,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Causal Graph Builder — public API.
|
|
3
|
+
*
|
|
4
|
+
* Entry point for Phase 4: takes FeatureSet → returns CausalGraph.
|
|
5
|
+
*
|
|
6
|
+
* @packageDocumentation
|
|
7
|
+
*/
|
|
8
|
+
export { buildCausalGraph } from './builder.js';
|
|
9
|
+
export { CausalNodeSchema, CausalEdgeSchema, CausalGraphSchema, } from './types.js';
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/causal/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,GAOlB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JavaScript / Main Thread causal rules.
|
|
3
|
+
*
|
|
4
|
+
* Chains covered:
|
|
5
|
+
* 1. Long main thread blocking → high TBT → delayed INP
|
|
6
|
+
* 2. Excessive JS → long parse time → main thread contention
|
|
7
|
+
* 3. JS execution hotspots → long tasks → blocking
|
|
8
|
+
*
|
|
9
|
+
* @packageDocumentation
|
|
10
|
+
*/
|
|
11
|
+
import type { FeatureSet } from '../../extract/types.js';
|
|
12
|
+
import type { CausalNode, CausalEdge } from '../types.js';
|
|
13
|
+
export declare const tbtInpChain: {
|
|
14
|
+
id: string;
|
|
15
|
+
label: string;
|
|
16
|
+
applies(features: FeatureSet): boolean;
|
|
17
|
+
build(features: FeatureSet): {
|
|
18
|
+
nodes: CausalNode[];
|
|
19
|
+
edges: CausalEdge[];
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
export declare const jsHotspotsChain: {
|
|
23
|
+
id: string;
|
|
24
|
+
label: string;
|
|
25
|
+
applies(features: FeatureSet): boolean;
|
|
26
|
+
build(features: FeatureSet): {
|
|
27
|
+
nodes: CausalNode[];
|
|
28
|
+
edges: CausalEdge[];
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
export declare const excessiveScriptsChain: {
|
|
32
|
+
id: string;
|
|
33
|
+
label: string;
|
|
34
|
+
applies(features: FeatureSet): boolean;
|
|
35
|
+
build(features: FeatureSet): {
|
|
36
|
+
nodes: CausalNode[];
|
|
37
|
+
edges: CausalEdge[];
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
export declare const jsRules: {
|
|
41
|
+
id: string;
|
|
42
|
+
label: string;
|
|
43
|
+
applies(features: FeatureSet): boolean;
|
|
44
|
+
build(features: FeatureSet): {
|
|
45
|
+
nodes: CausalNode[];
|
|
46
|
+
edges: CausalEdge[];
|
|
47
|
+
};
|
|
48
|
+
}[];
|
|
49
|
+
//# sourceMappingURL=js.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"js.d.ts","sourceRoot":"","sources":["../../../src/causal/rules/js.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EAAc,UAAU,EAAE,UAAU,EAAY,MAAM,aAAa,CAAC;AAiChF,eAAO,MAAM,WAAW;;;sBAIJ,UAAU,GAAG,OAAO;oBAKtB,UAAU,GAAG;QAAE,KAAK,EAAE,UAAU,EAAE,CAAC;QAAC,KAAK,EAAE,UAAU,EAAE,CAAA;KAAE;CAwD1E,CAAC;AAMF,eAAO,MAAM,eAAe;;;sBAIR,UAAU,GAAG,OAAO;oBAKtB,UAAU,GAAG;QAAE,KAAK,EAAE,UAAU,EAAE,CAAC;QAAC,KAAK,EAAE,UAAU,EAAE,CAAA;KAAE;CAuD1E,CAAC;AAMF,eAAO,MAAM,qBAAqB;;;sBAId,UAAU,GAAG,OAAO;oBAKtB,UAAU,GAAG;QAAE,KAAK,EAAE,UAAU,EAAE,CAAC;QAAC,KAAK,EAAE,UAAU,EAAE,CAAA;KAAE;CAqC1E,CAAC;AAMF,eAAO,MAAM,OAAO;;;sBA7LA,UAAU,GAAG,OAAO;oBAKtB,UAAU,GAAG;QAAE,KAAK,EAAE,UAAU,EAAE,CAAC;QAAC,KAAK,EAAE,UAAU,EAAE,CAAA;KAAE;GA4L1E,CAAC"}
|