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
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collector orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* Coordinates the full collection lifecycle for a single run:
|
|
5
|
+
* 1. Validate URL
|
|
6
|
+
* 2. Launch browser + create CDP session
|
|
7
|
+
* 3. Run each active collector in the correct order
|
|
8
|
+
* 4. Write results to the output directory
|
|
9
|
+
* 5. Clean up browser
|
|
10
|
+
* 6. Run Lighthouse audit (separate browser, after CDP session closes)
|
|
11
|
+
*
|
|
12
|
+
* Designed for multi-run support: `run()` wraps `runOnce()`.
|
|
13
|
+
*/
|
|
14
|
+
import ow from 'ow';
|
|
15
|
+
import { DEFAULT_RUN_OPTIONS } from '../shared/types.js';
|
|
16
|
+
import { launchBrowserServer, closeBrowser } from './session.js';
|
|
17
|
+
import { TraceCollector } from './trace.js';
|
|
18
|
+
import { NetworkCollector } from './network.js';
|
|
19
|
+
import { PerformanceCollector } from './performance.js';
|
|
20
|
+
import { RuntimeCollector } from './runtime.js';
|
|
21
|
+
import { CoverageCollector } from './coverage.js';
|
|
22
|
+
import { ConsoleCollector } from './console.js';
|
|
23
|
+
import { DOMCollector } from './dom.js';
|
|
24
|
+
import { LighthouseCollector } from './lighthouse.js';
|
|
25
|
+
import { createOutputDir, writeJsonFile, writeWarnings } from '../output/writer.js';
|
|
26
|
+
/** Collectors that participate in the CDP session lifecycle */
|
|
27
|
+
const CDP_COLLECTORS = [
|
|
28
|
+
{
|
|
29
|
+
label: 'network',
|
|
30
|
+
create: () => new NetworkCollector(),
|
|
31
|
+
bundleKey: 'network',
|
|
32
|
+
filename: 'network.json',
|
|
33
|
+
disableFlag: 'noNetwork',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
label: 'performance',
|
|
37
|
+
create: () => new PerformanceCollector(),
|
|
38
|
+
bundleKey: 'performance',
|
|
39
|
+
filename: 'performance.json',
|
|
40
|
+
disableFlag: 'noPerformance',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
label: 'trace',
|
|
44
|
+
create: () => new TraceCollector(),
|
|
45
|
+
bundleKey: 'trace',
|
|
46
|
+
filename: 'trace.json',
|
|
47
|
+
disableFlag: undefined, // always enabled
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
label: 'runtime',
|
|
51
|
+
create: () => new RuntimeCollector(),
|
|
52
|
+
bundleKey: 'runtime',
|
|
53
|
+
filename: 'runtime.json',
|
|
54
|
+
disableFlag: 'noRuntime',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
label: 'coverage',
|
|
58
|
+
create: () => new CoverageCollector(),
|
|
59
|
+
bundleKey: 'coverage',
|
|
60
|
+
filename: 'coverage.json',
|
|
61
|
+
disableFlag: 'noCoverage',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
label: 'console',
|
|
65
|
+
create: () => new ConsoleCollector(),
|
|
66
|
+
bundleKey: 'consoleEntries',
|
|
67
|
+
filename: 'console.json',
|
|
68
|
+
disableFlag: 'noConsole',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
label: 'dom',
|
|
72
|
+
create: ({ page }) => new DOMCollector(page),
|
|
73
|
+
bundleKey: 'dom',
|
|
74
|
+
filename: 'dom.json',
|
|
75
|
+
disableFlag: 'noDom',
|
|
76
|
+
},
|
|
77
|
+
];
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
// Helpers
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
/**
|
|
82
|
+
* Check whether a collector is enabled according to run options.
|
|
83
|
+
* Unless the --no-* flag is explicitly set, the collector is active.
|
|
84
|
+
*/
|
|
85
|
+
function isEnabled(def, options) {
|
|
86
|
+
return def.disableFlag === undefined || !options[def.disableFlag];
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Filter enabled collectors from the definition list.
|
|
90
|
+
*/
|
|
91
|
+
function enabledCollectors(defs, options) {
|
|
92
|
+
return defs.filter((d) => isEnabled(d, options));
|
|
93
|
+
}
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
// LCP wait
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
/**
|
|
98
|
+
* Maximum time to wait for LargestContentfulPaint before stopping trace collection.
|
|
99
|
+
* If LCP doesn't fire within this window, a warning is recorded but collection
|
|
100
|
+
* proceeds with whatever data was captured.
|
|
101
|
+
*/
|
|
102
|
+
const LCP_TIMEOUT_MS = 5_000;
|
|
103
|
+
/**
|
|
104
|
+
* Wait for LargestContentfulPaint to fire on the page.
|
|
105
|
+
*
|
|
106
|
+
* Uses a PerformanceObserver for 'largest-contentful-paint' with `buffered: true`
|
|
107
|
+
* so that entries which already fired before the observer was created are still
|
|
108
|
+
* captured. If the observer API is unavailable or the timeout expires, returns
|
|
109
|
+
* false so the caller can record a warning but still proceed.
|
|
110
|
+
*
|
|
111
|
+
* @param page - Playwright Page to evaluate the observer in
|
|
112
|
+
* @param timeoutMs - Maximum time to wait in milliseconds
|
|
113
|
+
* @returns true if LCP was detected, false otherwise
|
|
114
|
+
*/
|
|
115
|
+
async function waitForLCP(page, timeoutMs) {
|
|
116
|
+
try {
|
|
117
|
+
return await page.evaluate((timeout) => {
|
|
118
|
+
return new Promise((resolve) => {
|
|
119
|
+
let resolved = false;
|
|
120
|
+
try {
|
|
121
|
+
const observer = new PerformanceObserver((list) => {
|
|
122
|
+
const entries = list.getEntries();
|
|
123
|
+
if (entries.length > 0) {
|
|
124
|
+
observer.disconnect();
|
|
125
|
+
resolved = true;
|
|
126
|
+
resolve(true);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
observer.observe({ type: 'largest-contentful-paint', buffered: true });
|
|
130
|
+
setTimeout(() => {
|
|
131
|
+
if (!resolved) {
|
|
132
|
+
resolve(false);
|
|
133
|
+
}
|
|
134
|
+
}, timeout);
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
// PerformanceObserver or LCP type not supported by this browser
|
|
138
|
+
resolve(false);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}, timeoutMs);
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// page.evaluate failed (CSP restrictions, page crash, etc.)
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// ---------------------------------------------------------------------------
|
|
149
|
+
// runOnce
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
/**
|
|
152
|
+
* Execute a single collection run against the given URL.
|
|
153
|
+
*
|
|
154
|
+
* @param url - Target URL to collect data from
|
|
155
|
+
* @param options - Collection options
|
|
156
|
+
* @param runIndex - 1-based run index (for multi-run scenarios)
|
|
157
|
+
* @returns RunResult with output path and status
|
|
158
|
+
*/
|
|
159
|
+
async function runOnce(url, options, runIndex) {
|
|
160
|
+
ow(url, 'url', ow.string.nonEmpty);
|
|
161
|
+
ow(options, 'options', ow.object);
|
|
162
|
+
ow(runIndex, 'runIndex', ow.number.integer.positive);
|
|
163
|
+
const bundle = {};
|
|
164
|
+
const warnings = [];
|
|
165
|
+
const files = [];
|
|
166
|
+
// --- Phase 1: Create output directory ---
|
|
167
|
+
const baseDir = options.output;
|
|
168
|
+
const outputDir = await createOutputDir(baseDir, url);
|
|
169
|
+
// --- Phase 2: Launch browser + CDP session ---
|
|
170
|
+
console.error(`[${runIndex}] Launching browser...`);
|
|
171
|
+
const { browser, page, cdp, cdpPort } = await launchBrowserServer(30_000, options.mobile);
|
|
172
|
+
try {
|
|
173
|
+
// --- Phase 3: Determine which CDP collectors to run ---
|
|
174
|
+
const activeDefs = enabledCollectors(CDP_COLLECTORS, options);
|
|
175
|
+
if (activeDefs.length === 0) {
|
|
176
|
+
console.error(`[${runIndex}] No CDP collectors enabled — skipping navigation`);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
// Start all active collectors (pre-navigation)
|
|
180
|
+
const running = [];
|
|
181
|
+
for (const def of activeDefs) {
|
|
182
|
+
console.error(`[${runIndex}] Starting ${def.label} collection...`);
|
|
183
|
+
const collector = def.create({ page });
|
|
184
|
+
await collector.start(cdp, options);
|
|
185
|
+
running.push({ def, collector });
|
|
186
|
+
}
|
|
187
|
+
// --- Phase 4: Navigate to target URL ---
|
|
188
|
+
console.error(`[${runIndex}] Navigating to ${url}...`);
|
|
189
|
+
await page.goto(url, { waitUntil: 'networkidle', timeout: 60_000 });
|
|
190
|
+
// --- Phase 4b: Wait for LCP before stopping collectors ---
|
|
191
|
+
// This ensures the trace (and other collectors) capture the LargestContentfulPaint
|
|
192
|
+
// event, which may fire after networkidle (e.g. large hero images, late fonts).
|
|
193
|
+
console.error(`[${runIndex}] Waiting for LargestContentfulPaint...`);
|
|
194
|
+
const lcpReady = await waitForLCP(page, LCP_TIMEOUT_MS);
|
|
195
|
+
if (lcpReady) {
|
|
196
|
+
console.error(`[${runIndex}] LargestContentfulPaint captured`);
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
const lcpWarning = 'LCP event not detected within timeout — trace may lack LargestContentfulPaint event data';
|
|
200
|
+
warnings.push(lcpWarning);
|
|
201
|
+
console.error(`[${runIndex}] ${lcpWarning}`);
|
|
202
|
+
}
|
|
203
|
+
// --- Phase 5: Stop all collectors (post-navigation) and write results ---
|
|
204
|
+
for (const { def, collector } of running) {
|
|
205
|
+
console.error(`[${runIndex}] Stopping ${def.label} collection...`);
|
|
206
|
+
const result = await collector.stop();
|
|
207
|
+
if (result.ok) {
|
|
208
|
+
// Assign collector data to bundle (all data fields are `unknown`)
|
|
209
|
+
bundle[def.bundleKey] = result.data;
|
|
210
|
+
// Extract warnings if the data has a warnings field
|
|
211
|
+
const data = result.data;
|
|
212
|
+
if (data &&
|
|
213
|
+
typeof data === 'object' &&
|
|
214
|
+
'warnings' in data &&
|
|
215
|
+
Array.isArray(data.warnings)) {
|
|
216
|
+
warnings.push(...data.warnings);
|
|
217
|
+
}
|
|
218
|
+
const filePath = await writeJsonFile(outputDir, def.filename, result.data);
|
|
219
|
+
files.push(filePath);
|
|
220
|
+
console.error(`[${runIndex}] ${def.label}: OK — ${filePath}`);
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
warnings.push(`${def.label} collector failed: ${result.error}`);
|
|
224
|
+
console.error(`[${runIndex}] ${def.label}: FAILED — ${result.error}`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
// --- Phase 6: Write warnings ---
|
|
229
|
+
bundle.warnings = warnings;
|
|
230
|
+
if (warnings.length > 0) {
|
|
231
|
+
const warningsPath = await writeWarnings(outputDir, warnings);
|
|
232
|
+
files.push(warningsPath);
|
|
233
|
+
}
|
|
234
|
+
// --- Phase 7: Run Lighthouse in the SAME browser (Option C) ---
|
|
235
|
+
// Browser stays alive from Phase 2. Lighthouse opens its own tab via CDP port,
|
|
236
|
+
// does a fresh navigation, runs audits, and closes the tab — all in one Chrome.
|
|
237
|
+
// This eliminates metric drift between CDP trace and Lighthouse.
|
|
238
|
+
if (!options.noLighthouse) {
|
|
239
|
+
console.error(`[${runIndex}] Starting Lighthouse audit (same browser, port ${cdpPort})...`);
|
|
240
|
+
const lighthouseCollector = new LighthouseCollector();
|
|
241
|
+
const lhResult = await lighthouseCollector.runCollection(url, cdpPort);
|
|
242
|
+
if (lhResult.ok) {
|
|
243
|
+
bundle.lighthouse = lhResult.data;
|
|
244
|
+
const lhPath = await writeJsonFile(outputDir, 'lighthouse.json', lhResult.data);
|
|
245
|
+
files.push(lhPath);
|
|
246
|
+
const lhData = lhResult.data;
|
|
247
|
+
if (lhData &&
|
|
248
|
+
typeof lhData === 'object' &&
|
|
249
|
+
'warnings' in lhData &&
|
|
250
|
+
Array.isArray(lhData.warnings)) {
|
|
251
|
+
warnings.push(...lhData.warnings);
|
|
252
|
+
}
|
|
253
|
+
console.error(`[${runIndex}] Lighthouse: OK — ${lhPath}`);
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
warnings.push(`Lighthouse collector failed: ${lhResult.error}`);
|
|
257
|
+
console.error(`[${runIndex}] Lighthouse: FAILED — ${lhResult.error}`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
finally {
|
|
262
|
+
// --- Phase 8: Always clean up the browser ---
|
|
263
|
+
await closeBrowser(browser);
|
|
264
|
+
}
|
|
265
|
+
return {
|
|
266
|
+
index: runIndex,
|
|
267
|
+
outputDir,
|
|
268
|
+
warningCount: warnings.length,
|
|
269
|
+
files,
|
|
270
|
+
success: true,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Run one or more collection runs against the given URL.
|
|
275
|
+
*
|
|
276
|
+
* @param url - Target URL to collect data from
|
|
277
|
+
* @param partialOptions - Partial options (merged with defaults)
|
|
278
|
+
* @returns Array of RunResult — one per run
|
|
279
|
+
*/
|
|
280
|
+
export async function run(url, partialOptions) {
|
|
281
|
+
ow(url, 'url', ow.string.nonEmpty);
|
|
282
|
+
// Validate URL format early (T-01-01 mitigation)
|
|
283
|
+
try {
|
|
284
|
+
new URL(url);
|
|
285
|
+
}
|
|
286
|
+
catch {
|
|
287
|
+
throw new Error(`Invalid URL: "${url}". Provide a valid URL including protocol (e.g. https://example.com).`);
|
|
288
|
+
}
|
|
289
|
+
const options = { ...DEFAULT_RUN_OPTIONS, ...partialOptions, url };
|
|
290
|
+
const results = [];
|
|
291
|
+
console.error(`PerfGraph collection starting — ${options.runs} run(s) for ${url}`);
|
|
292
|
+
console.error(`Output directory: ${options.output}`);
|
|
293
|
+
console.error('');
|
|
294
|
+
for (let i = 1; i <= options.runs; i++) {
|
|
295
|
+
const runLabel = options.runs > 1 ? `Run ${i}/${options.runs}` : 'Run';
|
|
296
|
+
console.error(`--- ${runLabel} ---`);
|
|
297
|
+
try {
|
|
298
|
+
const result = await runOnce(url, options, i);
|
|
299
|
+
results.push(result);
|
|
300
|
+
console.error(`[${i}] Completed — ${result.success ? 'OK' : 'FAILED'}`);
|
|
301
|
+
console.error(`[${i}] Output: ${result.outputDir}`);
|
|
302
|
+
if (result.warningCount > 0) {
|
|
303
|
+
console.error(`[${i}] Warnings: ${result.warningCount}`);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
catch (error) {
|
|
307
|
+
console.error(`[${i}] Fatal error: ${error}`);
|
|
308
|
+
results.push({
|
|
309
|
+
index: i,
|
|
310
|
+
outputDir: '',
|
|
311
|
+
warningCount: 0,
|
|
312
|
+
files: [],
|
|
313
|
+
success: false,
|
|
314
|
+
error: String(error),
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
console.error('');
|
|
318
|
+
}
|
|
319
|
+
// Write multi-run summary if needed
|
|
320
|
+
if (options.runs > 1) {
|
|
321
|
+
const summaryDir = results.find((r) => r.outputDir)?.outputDir;
|
|
322
|
+
if (summaryDir) {
|
|
323
|
+
const parentDir = summaryDir.substring(0, summaryDir.lastIndexOf('\\'));
|
|
324
|
+
const summary = {
|
|
325
|
+
url,
|
|
326
|
+
runs: options.runs,
|
|
327
|
+
completedAt: new Date().toISOString(),
|
|
328
|
+
results: results.map((r) => ({
|
|
329
|
+
run: r.index,
|
|
330
|
+
outputDir: r.outputDir,
|
|
331
|
+
success: r.success,
|
|
332
|
+
warningCount: r.warningCount,
|
|
333
|
+
error: r.error ?? null,
|
|
334
|
+
files: r.files,
|
|
335
|
+
})),
|
|
336
|
+
summary: {
|
|
337
|
+
successCount: results.filter((r) => r.success).length,
|
|
338
|
+
failureCount: results.filter((r) => !r.success).length,
|
|
339
|
+
totalWarnings: results.reduce((sum, r) => sum + r.warningCount, 0),
|
|
340
|
+
},
|
|
341
|
+
};
|
|
342
|
+
const summaryPath = await writeJsonFile(options.output, 'runs-summary.json', summary);
|
|
343
|
+
console.error(`Summary: ${summaryPath}`);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
const successCount = results.filter((r) => r.success).length;
|
|
347
|
+
console.error(`Collection complete — ${successCount}/${options.runs} runs successful`);
|
|
348
|
+
return results;
|
|
349
|
+
}
|
|
350
|
+
//# sourceMappingURL=orchestrator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../../src/collect/orchestrator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PerformanceCollector — captures browser performance metrics via the CDP Performance domain.
|
|
3
|
+
*
|
|
4
|
+
* Uses:
|
|
5
|
+
* - Performance.enable to receive periodic Performance.metrics events.
|
|
6
|
+
* - Performance.getMetrics after page load to capture the final metrics snapshot.
|
|
7
|
+
*
|
|
8
|
+
* Standard metrics include Timestamp, Documents, Frames, JSEventListeners, Nodes,
|
|
9
|
+
* LayoutCount, RecalcStyleCount, LayoutDuration, RecalcStyleDuration,
|
|
10
|
+
* ScriptDuration, TaskDuration, JSHeapUsedSize, JSHeapTotalSize,
|
|
11
|
+
* FirstMeaningfulPaint, DomContentLoaded, and NavigationStart.
|
|
12
|
+
*/
|
|
13
|
+
import { type CDPSession } from 'playwright';
|
|
14
|
+
import type { Collector, CollectorResult } from './types.js';
|
|
15
|
+
import type { PerformanceRawData, PerformanceMetric } from './types.js';
|
|
16
|
+
import type { CollectorRunOptions } from '../shared/types.js';
|
|
17
|
+
export declare class PerformanceCollector implements Collector {
|
|
18
|
+
readonly name = "performance";
|
|
19
|
+
private session;
|
|
20
|
+
/** Accumulated timeline metric snapshots */
|
|
21
|
+
private timelineSnapshots;
|
|
22
|
+
private collected;
|
|
23
|
+
start(session: CDPSession, _options?: CollectorRunOptions): Promise<void>;
|
|
24
|
+
stop(): Promise<CollectorResult<PerformanceRawData>>;
|
|
25
|
+
/** Return timeline metric snapshots collected during the session (useful for tests) */
|
|
26
|
+
getTimelineSnapshots(): PerformanceMetric[][];
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=performance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"performance.d.ts","sourceRoot":"","sources":["../../src/collect/performance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7D,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAuC9D,qBAAa,oBAAqB,YAAW,SAAS;IACpD,QAAQ,CAAC,IAAI,iBAAiB;IAE9B,OAAO,CAAC,OAAO,CAA2B;IAC1C,4CAA4C;IAC5C,OAAO,CAAC,iBAAiB,CAA6B;IACtD,OAAO,CAAC,SAAS,CAAS;IAEpB,KAAK,CAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBzE,IAAI,IAAI,OAAO,CAAC,eAAe,CAAC,kBAAkB,CAAC,CAAC;IAiD1D,uFAAuF;IACvF,oBAAoB,IAAI,iBAAiB,EAAE,EAAE;CAG9C"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PerformanceCollector — captures browser performance metrics via the CDP Performance domain.
|
|
3
|
+
*
|
|
4
|
+
* Uses:
|
|
5
|
+
* - Performance.enable to receive periodic Performance.metrics events.
|
|
6
|
+
* - Performance.getMetrics after page load to capture the final metrics snapshot.
|
|
7
|
+
*
|
|
8
|
+
* Standard metrics include Timestamp, Documents, Frames, JSEventListeners, Nodes,
|
|
9
|
+
* LayoutCount, RecalcStyleCount, LayoutDuration, RecalcStyleDuration,
|
|
10
|
+
* ScriptDuration, TaskDuration, JSHeapUsedSize, JSHeapTotalSize,
|
|
11
|
+
* FirstMeaningfulPaint, DomContentLoaded, and NavigationStart.
|
|
12
|
+
*/
|
|
13
|
+
import {} from 'playwright';
|
|
14
|
+
/**
|
|
15
|
+
* Validate the collected performance metrics are reasonable.
|
|
16
|
+
*
|
|
17
|
+
* @param metrics - Array of collected performance metrics
|
|
18
|
+
* @returns Array of warning strings
|
|
19
|
+
*/
|
|
20
|
+
function validatePerformanceMetrics(metrics) {
|
|
21
|
+
const warnings = [];
|
|
22
|
+
if (metrics.length === 0) {
|
|
23
|
+
warnings.push('No performance metrics were collected.');
|
|
24
|
+
return warnings;
|
|
25
|
+
}
|
|
26
|
+
// Check for expected standard metrics
|
|
27
|
+
const expectedMetrics = [
|
|
28
|
+
'Timestamp',
|
|
29
|
+
'Documents',
|
|
30
|
+
'Frames',
|
|
31
|
+
'JSEventListeners',
|
|
32
|
+
'Nodes',
|
|
33
|
+
'JSHeapUsedSize',
|
|
34
|
+
'JSHeapTotalSize',
|
|
35
|
+
];
|
|
36
|
+
const metricNames = new Set(metrics.map((m) => m.name));
|
|
37
|
+
const missing = expectedMetrics.filter((name) => !metricNames.has(name));
|
|
38
|
+
if (missing.length > 0) {
|
|
39
|
+
warnings.push(`Performance metrics may be incomplete — missing expected metrics: ${missing.join(', ')}`);
|
|
40
|
+
}
|
|
41
|
+
return warnings;
|
|
42
|
+
}
|
|
43
|
+
export class PerformanceCollector {
|
|
44
|
+
name = 'performance';
|
|
45
|
+
session = null;
|
|
46
|
+
/** Accumulated timeline metric snapshots */
|
|
47
|
+
timelineSnapshots = [];
|
|
48
|
+
collected = false;
|
|
49
|
+
async start(session, _options) {
|
|
50
|
+
this.session = session;
|
|
51
|
+
this.timelineSnapshots = [];
|
|
52
|
+
this.collected = false;
|
|
53
|
+
// Listen for periodic Performance.metrics events
|
|
54
|
+
session.on('Performance.metrics', ((event) => {
|
|
55
|
+
if (Array.isArray(event.metrics) && event.metrics.length > 0) {
|
|
56
|
+
this.timelineSnapshots.push(event.metrics);
|
|
57
|
+
}
|
|
58
|
+
}));
|
|
59
|
+
// Enable the Performance domain to receive metrics events
|
|
60
|
+
await session.send('Performance.enable');
|
|
61
|
+
}
|
|
62
|
+
async stop() {
|
|
63
|
+
this.collected = true;
|
|
64
|
+
if (!this.session) {
|
|
65
|
+
return { ok: false, error: 'PerformanceCollector: session not initialized' };
|
|
66
|
+
}
|
|
67
|
+
const warnings = [];
|
|
68
|
+
try {
|
|
69
|
+
// Query final metrics snapshot
|
|
70
|
+
const result = await this.session.send('Performance.getMetrics');
|
|
71
|
+
const metrics = result.metrics;
|
|
72
|
+
const timestamp = Date.now();
|
|
73
|
+
// Disable the Performance domain
|
|
74
|
+
try {
|
|
75
|
+
await this.session.send('Performance.disable');
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Non-critical — domain may already be disabled
|
|
79
|
+
}
|
|
80
|
+
// Run completeness validation
|
|
81
|
+
const validationWarnings = validatePerformanceMetrics(metrics);
|
|
82
|
+
warnings.push(...validationWarnings);
|
|
83
|
+
// Check for timeline data availability
|
|
84
|
+
if (this.timelineSnapshots.length > 0) {
|
|
85
|
+
warnings.push(`${this.timelineSnapshots.length} timeline metric snapshot(s) were collected during the session (data available for delta analysis).`);
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
ok: true,
|
|
89
|
+
data: {
|
|
90
|
+
metrics,
|
|
91
|
+
timestamp,
|
|
92
|
+
warnings,
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
return {
|
|
98
|
+
ok: false,
|
|
99
|
+
error: `PerformanceCollector: failed to get metrics — ${String(error)}`,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/** Return timeline metric snapshots collected during the session (useful for tests) */
|
|
104
|
+
getTimelineSnapshots() {
|
|
105
|
+
return [...this.timelineSnapshots];
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=performance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"performance.js","sourceRoot":"","sources":["../../src/collect/performance.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RuntimeCollector — captures JavaScript runtime context and stats via CDP.
|
|
3
|
+
*
|
|
4
|
+
* Enables the Runtime domain to track execution context creation (frames,
|
|
5
|
+
* workers, extensions) and evaluates runtime metrics (JS heap, DOM node
|
|
6
|
+
* count) on stop().
|
|
7
|
+
*/
|
|
8
|
+
import { type CDPSession } from 'playwright';
|
|
9
|
+
import type { Collector, CollectorResult, RuntimeRawData } from './types.js';
|
|
10
|
+
import type { CollectorRunOptions } from '../shared/types.js';
|
|
11
|
+
export declare class RuntimeCollector implements Collector {
|
|
12
|
+
readonly name = "runtime";
|
|
13
|
+
private session;
|
|
14
|
+
private contexts;
|
|
15
|
+
/**
|
|
16
|
+
* Start collecting runtime data.
|
|
17
|
+
* Enables the Runtime CDP domain and subscribes to executionContextCreated.
|
|
18
|
+
*/
|
|
19
|
+
start(session: CDPSession, _options?: CollectorRunOptions): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Stop collecting and return runtime data.
|
|
22
|
+
* Evaluates inline JavaScript to capture runtime stats.
|
|
23
|
+
*/
|
|
24
|
+
stop(): Promise<CollectorResult<RuntimeRawData>>;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/collect/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC7E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAE9D,qBAAa,gBAAiB,YAAW,SAAS;IAChD,QAAQ,CAAC,IAAI,aAAa;IAE1B,OAAO,CAAC,OAAO,CAA2B;IAC1C,OAAO,CAAC,QAAQ,CAAkC;IAElD;;;OAGG;IACG,KAAK,CAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB/E;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,eAAe,CAAC,cAAc,CAAC,CAAC;CAiDvD"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RuntimeCollector — captures JavaScript runtime context and stats via CDP.
|
|
3
|
+
*
|
|
4
|
+
* Enables the Runtime domain to track execution context creation (frames,
|
|
5
|
+
* workers, extensions) and evaluates runtime metrics (JS heap, DOM node
|
|
6
|
+
* count) on stop().
|
|
7
|
+
*/
|
|
8
|
+
import {} from 'playwright';
|
|
9
|
+
export class RuntimeCollector {
|
|
10
|
+
name = 'runtime';
|
|
11
|
+
session = null;
|
|
12
|
+
contexts = [];
|
|
13
|
+
/**
|
|
14
|
+
* Start collecting runtime data.
|
|
15
|
+
* Enables the Runtime CDP domain and subscribes to executionContextCreated.
|
|
16
|
+
*/
|
|
17
|
+
async start(session, _options) {
|
|
18
|
+
this.session = session;
|
|
19
|
+
this.contexts = [];
|
|
20
|
+
// Enable Runtime domain so we receive execution context events
|
|
21
|
+
await session.send('Runtime.enable');
|
|
22
|
+
// Track all execution contexts created during the session
|
|
23
|
+
session.on('Runtime.executionContextCreated', (event) => {
|
|
24
|
+
if (event.context) {
|
|
25
|
+
this.contexts.push({
|
|
26
|
+
id: event.context.id,
|
|
27
|
+
origin: event.context.origin,
|
|
28
|
+
name: event.context.name,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Stop collecting and return runtime data.
|
|
35
|
+
* Evaluates inline JavaScript to capture runtime stats.
|
|
36
|
+
*/
|
|
37
|
+
async stop() {
|
|
38
|
+
if (!this.session) {
|
|
39
|
+
return { ok: false, error: 'RuntimeCollector: session not initialised' };
|
|
40
|
+
}
|
|
41
|
+
let stats = undefined;
|
|
42
|
+
try {
|
|
43
|
+
// Evaluate runtime stats within the page context
|
|
44
|
+
const result = await this.session.send('Runtime.evaluate', {
|
|
45
|
+
expression: `({
|
|
46
|
+
jsHeapSize: performance?.memory?.usedJSHeapSize ?? null,
|
|
47
|
+
domNodeCount: document?.querySelectorAll('*')?.length ?? null,
|
|
48
|
+
documentUrl: document?.URL ?? ''
|
|
49
|
+
})`,
|
|
50
|
+
returnByValue: true,
|
|
51
|
+
});
|
|
52
|
+
const value = result?.result?.value;
|
|
53
|
+
if (value) {
|
|
54
|
+
stats = {
|
|
55
|
+
jsHeapSize: value.jsHeapSize ?? 0,
|
|
56
|
+
domNodeCount: value.domNodeCount ?? 0,
|
|
57
|
+
documentUrl: value.documentUrl ?? '',
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
// Stats are non-critical — return contexts even if evaluation fails
|
|
63
|
+
return {
|
|
64
|
+
ok: true,
|
|
65
|
+
data: {
|
|
66
|
+
contexts: this.contexts,
|
|
67
|
+
warnings: [`Failed to evaluate runtime stats: ${String(error)}`],
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
ok: true,
|
|
73
|
+
data: {
|
|
74
|
+
contexts: this.contexts,
|
|
75
|
+
stats,
|
|
76
|
+
warnings: [],
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/collect/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAmB,MAAM,YAAY,CAAC;AAI7C,MAAM,OAAO,gBAAgB;IAClB,IAAI,GAAG,SAAS,CAAC;IAElB,OAAO,GAAsB,IAAI,CAAC;IAClC,QAAQ,GAA+B,EAAE,CAAC;IAElD;;;OAGG;IACH,KAAK,CAAC,KAAK,CAAC,OAAmB,EAAE,QAA8B;QAC7D,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;QAEnB,+DAA+D;QAC/D,MAAM,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAErC,0DAA0D;QAC1D,OAAO,CAAC,EAAE,CAAC,iCAAiC,EAAE,CAAC,KAAgE,EAAE,EAAE;YACjH,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;gBAClB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;oBACjB,EAAE,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE;oBACpB,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM;oBAC5B,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI;iBACzB,CAAC,CAAC;YACL,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,IAAI;QACR,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,2CAA2C,EAAE,CAAC;QAC3E,CAAC;QAED,IAAI,KAAK,GAA4B,SAAS,CAAC;QAE/C,IAAI,CAAC;YACH,iDAAiD;YACjD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,kBAAkB,EAAE;gBACzD,UAAU,EAAE;;;;WAIT;gBACH,aAAa,EAAE,IAAI;aACpB,CAAC,CAAC;YAEH,MAAM,KAAK,GAAG,MAAM,EAAE,MAAM,EAAE,KAEjB,CAAC;YAEd,IAAI,KAAK,EAAE,CAAC;gBACV,KAAK,GAAG;oBACN,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,CAAC;oBACjC,YAAY,EAAE,KAAK,CAAC,YAAY,IAAI,CAAC;oBACrC,WAAW,EAAE,KAAK,CAAC,WAAW,IAAI,EAAE;iBACrC,CAAC;YACJ,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,oEAAoE;YACpE,OAAO;gBACL,EAAE,EAAE,IAAI;gBACR,IAAI,EAAE;oBACJ,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,QAAQ,EAAE,CAAC,qCAAqC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;iBACjE;aACF,CAAC;QACJ,CAAC;QAED,OAAO;YACL,EAAE,EAAE,IAAI;YACR,IAAI,EAAE;gBACJ,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,KAAK;gBACL,QAAQ,EAAE,EAAE;aACb;SACF,CAAC;IACJ,CAAC;CACF"}
|