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,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Critical Path Length extractor.
|
|
3
|
+
*
|
|
4
|
+
* Computes metrics from the critical path chain derived in the Network IR:
|
|
5
|
+
* total chain length (duration sum), request counts, depth, and longest
|
|
6
|
+
* single request.
|
|
7
|
+
*/
|
|
8
|
+
/** Safely treat any non-finite number as 0. */
|
|
9
|
+
function safe(v) {
|
|
10
|
+
return Number.isFinite(v) && v >= 0 ? v : 0;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Determine whether a request is considered "render-blocking".
|
|
14
|
+
*
|
|
15
|
+
* - Document & Stylesheet requests are always blocking.
|
|
16
|
+
* - Scripts at VeryHigh priority are considered blocking (render-blocking JS).
|
|
17
|
+
* - Everything else (images, fonts, XHR, fetch, etc.) is non-blocking.
|
|
18
|
+
*/
|
|
19
|
+
function isBlocking(req) {
|
|
20
|
+
if (req.failed)
|
|
21
|
+
return false;
|
|
22
|
+
if (req.resourceType === 'Document')
|
|
23
|
+
return true;
|
|
24
|
+
if (req.resourceType === 'Stylesheet')
|
|
25
|
+
return true;
|
|
26
|
+
if (req.resourceType === 'Script' && req.priority === 'VeryHigh')
|
|
27
|
+
return true;
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Walk the critical path tree recursively and aggregate metrics.
|
|
32
|
+
*/
|
|
33
|
+
function walkTree(node, reqMap) {
|
|
34
|
+
let totalChainLength = 0;
|
|
35
|
+
let blockingCount = 0;
|
|
36
|
+
let nonBlockingCount = 0;
|
|
37
|
+
let longestSingleRequest = 0;
|
|
38
|
+
let requestCount = 0;
|
|
39
|
+
let anyMatched = false;
|
|
40
|
+
const req = reqMap.get(node.url);
|
|
41
|
+
if (req) {
|
|
42
|
+
anyMatched = true;
|
|
43
|
+
const dur = safe(req.duration);
|
|
44
|
+
totalChainLength += dur;
|
|
45
|
+
longestSingleRequest = Math.max(longestSingleRequest, dur);
|
|
46
|
+
requestCount++;
|
|
47
|
+
if (isBlocking(req)) {
|
|
48
|
+
blockingCount++;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
nonBlockingCount++;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
for (const child of node.children ?? []) {
|
|
55
|
+
const childResult = walkTree(child, reqMap);
|
|
56
|
+
totalChainLength += childResult.totalChainLength;
|
|
57
|
+
blockingCount += childResult.blockingCount;
|
|
58
|
+
nonBlockingCount += childResult.nonBlockingCount;
|
|
59
|
+
longestSingleRequest = Math.max(longestSingleRequest, childResult.longestSingleRequest);
|
|
60
|
+
requestCount += childResult.requestCount;
|
|
61
|
+
anyMatched = anyMatched || childResult.anyMatched;
|
|
62
|
+
}
|
|
63
|
+
return { totalChainLength, blockingCount, nonBlockingCount, longestSingleRequest, requestCount, anyMatched };
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Extract critical path metrics from an IRBundle.
|
|
67
|
+
*
|
|
68
|
+
* Returns undefined when the critical path info is empty or none of its
|
|
69
|
+
* tree URLs can be matched to a network request.
|
|
70
|
+
*/
|
|
71
|
+
export function extractCriticalPath(ir) {
|
|
72
|
+
const cpInfo = ir.network.summary.criticalPath;
|
|
73
|
+
if (!cpInfo || !cpInfo.tree || !cpInfo.tree.url) {
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
// Build a map for O(1) request lookups by URL
|
|
77
|
+
const reqMap = new Map();
|
|
78
|
+
for (const req of ir.network.requests) {
|
|
79
|
+
reqMap.set(req.url, req);
|
|
80
|
+
}
|
|
81
|
+
const result = walkTree(cpInfo.tree, reqMap);
|
|
82
|
+
if (!result.anyMatched) {
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
totalChainLength: result.totalChainLength,
|
|
87
|
+
requestCount: result.requestCount,
|
|
88
|
+
blockingCount: result.blockingCount,
|
|
89
|
+
nonBlockingCount: result.nonBlockingCount,
|
|
90
|
+
deepestChainDepth: cpInfo.depth,
|
|
91
|
+
longestSingleRequest: result.longestSingleRequest,
|
|
92
|
+
urlsOnLongestPath: cpInfo.urlsOnLongestPath?.length ? cpInfo.urlsOnLongestPath : undefined,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=critical-path.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"critical-path.js","sourceRoot":"","sources":["../../src/extract/critical-path.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,+CAA+C;AAC/C,SAAS,IAAI,CAAC,CAAS;IACrB,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC9C,CAAC;AAED;;;;;;GAMG;AACH,SAAS,UAAU,CAAC,GAAsB;IACxC,IAAI,GAAG,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC7B,IAAI,GAAG,CAAC,YAAY,KAAK,UAAU;QAAE,OAAO,IAAI,CAAC;IACjD,IAAI,GAAG,CAAC,YAAY,KAAK,YAAY;QAAE,OAAO,IAAI,CAAC;IACnD,IAAI,GAAG,CAAC,YAAY,KAAK,QAAQ,IAAI,GAAG,CAAC,QAAQ,KAAK,UAAU;QAAE,OAAO,IAAI,CAAC;IAC9E,OAAO,KAAK,CAAC;AACf,CAAC;AAcD;;GAEG;AACH,SAAS,QAAQ,CACf,IAA0B,EAC1B,MAAsC;IAEtC,IAAI,gBAAgB,GAAG,CAAC,CAAC;IACzB,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,IAAI,gBAAgB,GAAG,CAAC,CAAC;IACzB,IAAI,oBAAoB,GAAG,CAAC,CAAC;IAC7B,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,IAAI,UAAU,GAAG,KAAK,CAAC;IAEvB,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,GAAG,EAAE,CAAC;QACR,UAAU,GAAG,IAAI,CAAC;QAClB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC/B,gBAAgB,IAAI,GAAG,CAAC;QACxB,oBAAoB,GAAG,IAAI,CAAC,GAAG,CAAC,oBAAoB,EAAE,GAAG,CAAC,CAAC;QAC3D,YAAY,EAAE,CAAC;QACf,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACpB,aAAa,EAAE,CAAC;QAClB,CAAC;aAAM,CAAC;YACN,gBAAgB,EAAE,CAAC;QACrB,CAAC;IACH,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;QACxC,MAAM,WAAW,GAAG,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAC5C,gBAAgB,IAAI,WAAW,CAAC,gBAAgB,CAAC;QACjD,aAAa,IAAI,WAAW,CAAC,aAAa,CAAC;QAC3C,gBAAgB,IAAI,WAAW,CAAC,gBAAgB,CAAC;QACjD,oBAAoB,GAAG,IAAI,CAAC,GAAG,CAAC,oBAAoB,EAAE,WAAW,CAAC,oBAAoB,CAAC,CAAC;QACxF,YAAY,IAAI,WAAW,CAAC,YAAY,CAAC;QACzC,UAAU,GAAG,UAAU,IAAI,WAAW,CAAC,UAAU,CAAC;IACpD,CAAC;IAED,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,YAAY,EAAE,UAAU,EAAE,CAAC;AAC/G,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,EAAY;IAC9C,MAAM,MAAM,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC;IAC/C,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;QAChD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,8CAA8C;IAC9C,MAAM,MAAM,GAAG,IAAI,GAAG,EAA6B,CAAC;IACpD,KAAK,MAAM,GAAG,IAAI,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;QACtC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC3B,CAAC;IAED,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAE7C,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;QACvB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO;QACL,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;QACzC,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,aAAa,EAAE,MAAM,CAAC,aAAa;QACnC,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;QACzC,iBAAiB,EAAE,MAAM,CAAC,KAAK;QAC/B,oBAAoB,EAAE,MAAM,CAAC,oBAAoB;QACjD,iBAAiB,EAAE,MAAM,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,SAAS;KAC3F,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feature Extraction — entry point.
|
|
3
|
+
*
|
|
4
|
+
* extract() принимает IRBundle, запускает все extractor-функции и собирает
|
|
5
|
+
* FeatureSet. Каждая фича опциональна (graceful degradation при нехватке данных).
|
|
6
|
+
*/
|
|
7
|
+
import type { IRBundle } from '../normalize/types.js';
|
|
8
|
+
import type { FeatureSet } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Извлечь все диагностические сигналы из IRBundle.
|
|
11
|
+
*
|
|
12
|
+
* Чистая функция: одинаковый IRBundle → одинаковый FeatureSet.
|
|
13
|
+
* Возвращает FeatureSet со всеми опциональными полями.
|
|
14
|
+
*/
|
|
15
|
+
export declare function extract(ir: IRBundle): FeatureSet;
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/extract/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAS7C;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,EAAE,EAAE,QAAQ,GAAG,UAAU,CAWhD"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feature Extraction — entry point.
|
|
3
|
+
*
|
|
4
|
+
* extract() принимает IRBundle, запускает все extractor-функции и собирает
|
|
5
|
+
* FeatureSet. Каждая фича опциональна (graceful degradation при нехватке данных).
|
|
6
|
+
*/
|
|
7
|
+
import { extractLCPBreakdown } from './lcp-breakdown.js';
|
|
8
|
+
import { extractCriticalPath } from './critical-path.js';
|
|
9
|
+
import { extractMainThreadBlocking } from './main-thread.js';
|
|
10
|
+
import { extractJSHotspots } from './js-hotspots.js';
|
|
11
|
+
import { extractLayoutShifts } from './layout-shifts.js';
|
|
12
|
+
import { extractThirdPartyOverhead } from './third-party.js';
|
|
13
|
+
import { extractRenderBlocking } from './render-blocking.js';
|
|
14
|
+
/**
|
|
15
|
+
* Извлечь все диагностические сигналы из IRBundle.
|
|
16
|
+
*
|
|
17
|
+
* Чистая функция: одинаковый IRBundle → одинаковый FeatureSet.
|
|
18
|
+
* Возвращает FeatureSet со всеми опциональными полями.
|
|
19
|
+
*/
|
|
20
|
+
export function extract(ir) {
|
|
21
|
+
return {
|
|
22
|
+
url: ir.meta.url || undefined,
|
|
23
|
+
lcpBreakdown: extractLCPBreakdown(ir),
|
|
24
|
+
criticalPath: extractCriticalPath(ir),
|
|
25
|
+
mainThreadBlocking: extractMainThreadBlocking(ir),
|
|
26
|
+
jsHotspots: extractJSHotspots(ir),
|
|
27
|
+
layoutShifts: extractLayoutShifts(ir),
|
|
28
|
+
thirdPartyOverhead: extractThirdPartyOverhead(ir),
|
|
29
|
+
renderBlocking: extractRenderBlocking(ir),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/extract/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE7D;;;;;GAKG;AACH,MAAM,UAAU,OAAO,CAAC,EAAY;IAClC,OAAO;QACL,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,IAAI,SAAS;QAC7B,YAAY,EAAE,mBAAmB,CAAC,EAAE,CAAC;QACrC,YAAY,EAAE,mBAAmB,CAAC,EAAE,CAAC;QACrC,kBAAkB,EAAE,yBAAyB,CAAC,EAAE,CAAC;QACjD,UAAU,EAAE,iBAAiB,CAAC,EAAE,CAAC;QACjC,YAAY,EAAE,mBAAmB,CAAC,EAAE,CAAC;QACrC,kBAAkB,EAAE,yBAAyB,CAAC,EAAE,CAAC;QACjD,cAAc,EAAE,qBAAqB,CAAC,EAAE,CAAC;KAC1C,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JS Hotspots extractor.
|
|
3
|
+
*
|
|
4
|
+
* Identifies JS execution hotspots from runtime IR data:
|
|
5
|
+
* hydration cost, long tasks, and execution context count.
|
|
6
|
+
*/
|
|
7
|
+
import type { IRBundle } from '../normalize/types.js';
|
|
8
|
+
import type { JSHotspots } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Extract JS hotspot metrics from an IRBundle.
|
|
11
|
+
*
|
|
12
|
+
* Never returns undefined — defaults to zero when runtime data is absent.
|
|
13
|
+
*/
|
|
14
|
+
export declare function extractJSHotspots(ir: IRBundle): JSHotspots;
|
|
15
|
+
//# sourceMappingURL=js-hotspots.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"js-hotspots.d.ts","sourceRoot":"","sources":["../../src/extract/js-hotspots.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,QAAQ,GAAG,UAAU,CAU1D"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JS Hotspots extractor.
|
|
3
|
+
*
|
|
4
|
+
* Identifies JS execution hotspots from runtime IR data:
|
|
5
|
+
* hydration cost, long tasks, and execution context count.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Extract JS hotspot metrics from an IRBundle.
|
|
9
|
+
*
|
|
10
|
+
* Never returns undefined — defaults to zero when runtime data is absent.
|
|
11
|
+
*/
|
|
12
|
+
export function extractJSHotspots(ir) {
|
|
13
|
+
const { runtime } = ir;
|
|
14
|
+
return {
|
|
15
|
+
bootupTime: runtime.hydrationCost?.bootupTime ?? 0,
|
|
16
|
+
evaluatedScripts: runtime.hydrationCost?.evaluatedScripts ?? 0,
|
|
17
|
+
longTaskCount: runtime.eventLoopStats?.longTasks ?? 0,
|
|
18
|
+
maxBlockingDuration: runtime.eventLoopStats?.maxBlockingDuration ?? 0,
|
|
19
|
+
contextCount: runtime.executionContexts.length,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=js-hotspots.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"js-hotspots.js","sourceRoot":"","sources":["../../src/extract/js-hotspots.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,EAAY;IAC5C,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;IAEvB,OAAO;QACL,UAAU,EAAE,OAAO,CAAC,aAAa,EAAE,UAAU,IAAI,CAAC;QAClD,gBAAgB,EAAE,OAAO,CAAC,aAAa,EAAE,gBAAgB,IAAI,CAAC;QAC9D,aAAa,EAAE,OAAO,CAAC,cAAc,EAAE,SAAS,IAAI,CAAC;QACrD,mBAAmB,EAAE,OAAO,CAAC,cAAc,EAAE,mBAAmB,IAAI,CAAC;QACrE,YAAY,EAAE,OAAO,CAAC,iBAAiB,CAAC,MAAM;KAC/C,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layout Shifts extractor.
|
|
3
|
+
*
|
|
4
|
+
* Computes layout instability metrics from DOM IR layout shift candidates
|
|
5
|
+
* and the CLS value from Core Web Vitals.
|
|
6
|
+
*
|
|
7
|
+
* clusterScore — композитная оценка 0-1, основанная на CLS и
|
|
8
|
+
* структурных признаках нестабильности layout (сложные поддеревья,
|
|
9
|
+
* глубокое вложение).
|
|
10
|
+
*/
|
|
11
|
+
import type { IRBundle } from '../normalize/types.js';
|
|
12
|
+
import type { LayoutShifts } from './types.js';
|
|
13
|
+
/**
|
|
14
|
+
* Extract layout shift metrics from an IRBundle.
|
|
15
|
+
*
|
|
16
|
+
* Never returns undefined — defaults to zero when DOM/Layout data is absent.
|
|
17
|
+
*/
|
|
18
|
+
export declare function extractLayoutShifts(ir: IRBundle): LayoutShifts;
|
|
19
|
+
//# sourceMappingURL=layout-shifts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout-shifts.d.ts","sourceRoot":"","sources":["../../src/extract/layout-shifts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAK/C;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,QAAQ,GAAG,YAAY,CAiC9D"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layout Shifts extractor.
|
|
3
|
+
*
|
|
4
|
+
* Computes layout instability metrics from DOM IR layout shift candidates
|
|
5
|
+
* and the CLS value from Core Web Vitals.
|
|
6
|
+
*
|
|
7
|
+
* clusterScore — композитная оценка 0-1, основанная на CLS и
|
|
8
|
+
* структурных признаках нестабильности layout (сложные поддеревья,
|
|
9
|
+
* глубокое вложение).
|
|
10
|
+
*/
|
|
11
|
+
/** Categories that contribute to layout shift risk when over-represented. */
|
|
12
|
+
const HIGH_RISK_TAGS = new Set(['img', 'video', 'iframe', 'canvas', 'svg']);
|
|
13
|
+
/**
|
|
14
|
+
* Extract layout shift metrics from an IRBundle.
|
|
15
|
+
*
|
|
16
|
+
* Never returns undefined — defaults to zero when DOM/Layout data is absent.
|
|
17
|
+
*/
|
|
18
|
+
export function extractLayoutShifts(ir) {
|
|
19
|
+
const { layoutShiftCandidates } = ir.dom;
|
|
20
|
+
const cls = ir.performance.coreWebVitals.cls;
|
|
21
|
+
const highComplexitySubtreeCount = layoutShiftCandidates?.highComplexitySubtrees ?? 0;
|
|
22
|
+
const deepNesting = (layoutShiftCandidates?.deepNesting ?? 0) > 0;
|
|
23
|
+
// clusterScore: heuristic weighted score combining CLS and structural risk
|
|
24
|
+
const clsScore = cls !== undefined && Number.isFinite(cls) ? Math.min(1, cls * 50) : 0;
|
|
25
|
+
const complexityScore = Math.min(1, highComplexitySubtreeCount / 10);
|
|
26
|
+
const nestingScore = deepNesting ? 0.3 : 0;
|
|
27
|
+
// Tag risk: look at tag distribution — too many img/video/iframe in small DOM
|
|
28
|
+
let tagRisk = 0;
|
|
29
|
+
const tags = ir.dom.tagDistribution;
|
|
30
|
+
if (tags.length > 0) {
|
|
31
|
+
const totalTags = tags.reduce((sum, t) => sum + t.count, 0);
|
|
32
|
+
if (totalTags > 0) {
|
|
33
|
+
const highRiskCount = tags
|
|
34
|
+
.filter((t) => HIGH_RISK_TAGS.has(t.tag))
|
|
35
|
+
.reduce((sum, t) => sum + t.count, 0);
|
|
36
|
+
tagRisk = Math.min(1, highRiskCount / totalTags);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const clusterScore = Math.min(1, clsScore * 0.5 + complexityScore * 0.25 + nestingScore * 0.15 + tagRisk * 0.1);
|
|
40
|
+
return {
|
|
41
|
+
cls: cls !== undefined && Number.isFinite(cls) ? cls : undefined,
|
|
42
|
+
highComplexitySubtreeCount,
|
|
43
|
+
deepNesting,
|
|
44
|
+
clusterScore,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=layout-shifts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout-shifts.js","sourceRoot":"","sources":["../../src/extract/layout-shifts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,6EAA6E;AAC7E,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC;AAE5E;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,EAAY;IAC9C,MAAM,EAAE,qBAAqB,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC;IACzC,MAAM,GAAG,GAAG,EAAE,CAAC,WAAW,CAAC,aAAa,CAAC,GAAG,CAAC;IAE7C,MAAM,0BAA0B,GAAG,qBAAqB,EAAE,sBAAsB,IAAI,CAAC,CAAC;IACtF,MAAM,WAAW,GAAG,CAAC,qBAAqB,EAAE,WAAW,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IAElE,2EAA2E;IAC3E,MAAM,QAAQ,GAAG,GAAG,KAAK,SAAS,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACvF,MAAM,eAAe,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,0BAA0B,GAAG,EAAE,CAAC,CAAC;IACrE,MAAM,YAAY,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAE3C,8EAA8E;IAC9E,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,MAAM,IAAI,GAAG,EAAE,CAAC,GAAG,CAAC,eAAe,CAAC;IACpC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpB,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAC5D,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;YAClB,MAAM,aAAa,GAAG,IAAI;iBACvB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;iBACxC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;YACxC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IAED,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,GAAG,GAAG,GAAG,eAAe,GAAG,IAAI,GAAG,YAAY,GAAG,IAAI,GAAG,OAAO,GAAG,GAAG,CAAC,CAAC;IAEhH,OAAO;QACL,GAAG,EAAE,GAAG,KAAK,SAAS,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS;QAChE,0BAA0B;QAC1B,WAAW;QACX,YAAY;KACb,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LCP (Largest Contentful Paint) Breakdown extractor.
|
|
3
|
+
*
|
|
4
|
+
* Splits LCP time into 4 subparts per Chrome spec:
|
|
5
|
+
* TTFB → Resource Load Delay → Resource Load Time → Element Render Delay
|
|
6
|
+
*/
|
|
7
|
+
import type { IRBundle } from '../normalize/types.js';
|
|
8
|
+
import type { LCPBreakdown } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Extract LCP breakdown from an IRBundle.
|
|
11
|
+
*
|
|
12
|
+
* Uses a fallback chain for the total LCP value:
|
|
13
|
+
* 1. Trace LCP from coreWebVitals.lcp
|
|
14
|
+
* 2. Lighthouse LCP from lighthouse.lcpNumericValue
|
|
15
|
+
*
|
|
16
|
+
* Returns undefined if neither source has LCP data.
|
|
17
|
+
*/
|
|
18
|
+
export declare function extractLCPBreakdown(ir: IRBundle): LCPBreakdown | undefined;
|
|
19
|
+
//# sourceMappingURL=lcp-breakdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lcp-breakdown.d.ts","sourceRoot":"","sources":["../../src/extract/lcp-breakdown.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAqB,MAAM,uBAAuB,CAAC;AACzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAoD/C;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,QAAQ,GAAG,YAAY,GAAG,SAAS,CAwD1E"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LCP (Largest Contentful Paint) Breakdown extractor.
|
|
3
|
+
*
|
|
4
|
+
* Splits LCP time into 4 subparts per Chrome spec:
|
|
5
|
+
* TTFB → Resource Load Delay → Resource Load Time → Element Render Delay
|
|
6
|
+
*/
|
|
7
|
+
/** Safely clamp a number to 0 if it's negative, NaN, or Infinity. */
|
|
8
|
+
function clamp(value) {
|
|
9
|
+
return Number.isFinite(value) && value >= 0 ? value : 0;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Find the Document request (first page navigation) from the network log.
|
|
13
|
+
*/
|
|
14
|
+
function findDocumentRequest(requests) {
|
|
15
|
+
return requests.find((r) => r.resourceType === 'Document');
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Find the most likely LCP resource among network requests.
|
|
19
|
+
* Chrome LCP is typically the largest image (by bytes) or a text node.
|
|
20
|
+
* Returns undefined for text-only LCP.
|
|
21
|
+
*/
|
|
22
|
+
function findLcpResource(requests) {
|
|
23
|
+
const images = requests.filter((r) => r.resourceType === 'Image' && !r.failed);
|
|
24
|
+
if (images.length === 0)
|
|
25
|
+
return undefined;
|
|
26
|
+
return images.reduce((a, b) => (a.bytes >= b.bytes ? a : b));
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Extract TTFB from the network document request or fallback to Lighthouse.
|
|
30
|
+
*
|
|
31
|
+
* Primary: sum of DNS + Connect + SSL + Wait from the Document request's timing.
|
|
32
|
+
* Fallback: Lighthouse audit 'server-response-time' numericValue.
|
|
33
|
+
* Last resort: 0.
|
|
34
|
+
*/
|
|
35
|
+
function extractTTFB(ir) {
|
|
36
|
+
// Primary: Document request timing
|
|
37
|
+
const doc = findDocumentRequest(ir.network.requests);
|
|
38
|
+
if (doc?.timing) {
|
|
39
|
+
const { dns = 0, connect = 0, ssl = 0, wait = 0 } = doc.timing;
|
|
40
|
+
const ttfb = dns + connect + ssl + wait;
|
|
41
|
+
if (Number.isFinite(ttfb))
|
|
42
|
+
return Math.max(0, ttfb);
|
|
43
|
+
}
|
|
44
|
+
// Fallback: Lighthouse server-response-time audit
|
|
45
|
+
const lhAudit = ir.lighthouse.failedAudits.find((a) => a.id === 'server-response-time');
|
|
46
|
+
if (lhAudit?.numericValue !== undefined && Number.isFinite(lhAudit.numericValue)) {
|
|
47
|
+
return Math.max(0, lhAudit.numericValue);
|
|
48
|
+
}
|
|
49
|
+
return 0;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Extract LCP breakdown from an IRBundle.
|
|
53
|
+
*
|
|
54
|
+
* Uses a fallback chain for the total LCP value:
|
|
55
|
+
* 1. Trace LCP from coreWebVitals.lcp
|
|
56
|
+
* 2. Lighthouse LCP from lighthouse.lcpNumericValue
|
|
57
|
+
*
|
|
58
|
+
* Returns undefined if neither source has LCP data.
|
|
59
|
+
*/
|
|
60
|
+
export function extractLCPBreakdown(ir) {
|
|
61
|
+
// Fallback chain: trace → Lighthouse
|
|
62
|
+
const totalLCP = ir.performance.coreWebVitals.lcp ?? ir.lighthouse.lcpNumericValue;
|
|
63
|
+
if (totalLCP === undefined || !Number.isFinite(totalLCP)) {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
// Determine source
|
|
67
|
+
const hasTrace = ir.performance.coreWebVitals.lcp !== undefined;
|
|
68
|
+
const hasLighthouse = ir.lighthouse.lcpNumericValue !== undefined;
|
|
69
|
+
const source = hasTrace && hasLighthouse ? 'mixed' :
|
|
70
|
+
hasLighthouse ? 'lighthouse' : 'trace';
|
|
71
|
+
const ttfb = extractTTFB(ir);
|
|
72
|
+
const lcpResource = findLcpResource(ir.network.requests);
|
|
73
|
+
// Build lcpElement if Lighthouse provides selector info
|
|
74
|
+
const lcpElement = ir.lighthouse.lcpElementSelector
|
|
75
|
+
? {
|
|
76
|
+
selector: ir.lighthouse.lcpElementSelector,
|
|
77
|
+
snippet: ir.lighthouse.lcpElementSnippet,
|
|
78
|
+
nodeLabel: ir.lighthouse.lcpElementNodeLabel,
|
|
79
|
+
}
|
|
80
|
+
: undefined;
|
|
81
|
+
// Text-only LCP: no image resource to load
|
|
82
|
+
if (!lcpResource) {
|
|
83
|
+
return {
|
|
84
|
+
ttfb,
|
|
85
|
+
resourceLoadDelay: 0,
|
|
86
|
+
resourceLoadTime: 0,
|
|
87
|
+
elementRenderDelay: clamp(totalLCP - ttfb),
|
|
88
|
+
totalLCP,
|
|
89
|
+
lcpElementUrl: undefined,
|
|
90
|
+
lcpResourceType: undefined,
|
|
91
|
+
lcpElement,
|
|
92
|
+
source,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
const resourceLoadDelay = clamp(lcpResource.startTime - ttfb);
|
|
96
|
+
const resourceLoadTime = clamp(lcpResource.duration);
|
|
97
|
+
const elementRenderDelay = clamp(totalLCP - ttfb - resourceLoadDelay - resourceLoadTime);
|
|
98
|
+
return {
|
|
99
|
+
ttfb,
|
|
100
|
+
resourceLoadDelay,
|
|
101
|
+
resourceLoadTime,
|
|
102
|
+
elementRenderDelay,
|
|
103
|
+
totalLCP,
|
|
104
|
+
lcpElementUrl: lcpResource.url,
|
|
105
|
+
lcpResourceType: lcpResource.resourceType,
|
|
106
|
+
lcpElement,
|
|
107
|
+
source,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=lcp-breakdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lcp-breakdown.js","sourceRoot":"","sources":["../../src/extract/lcp-breakdown.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main Thread Blocking Score extractor.
|
|
3
|
+
*
|
|
4
|
+
* Computes a blocking score from thread activity and main thread busyness.
|
|
5
|
+
* Combines trace-level thread activity (by category) with the global
|
|
6
|
+
* mainThreadBusyness ratio to produce a 0-1 blocking score.
|
|
7
|
+
*/
|
|
8
|
+
import type { IRBundle } from '../normalize/types.js';
|
|
9
|
+
import type { MainThreadBlocking } from './types.js';
|
|
10
|
+
/**
|
|
11
|
+
* Extract main thread blocking metrics from an IRBundle.
|
|
12
|
+
*
|
|
13
|
+
* Returns undefined when traceSummary data is missing (zero duration).
|
|
14
|
+
*/
|
|
15
|
+
export declare function extractMainThreadBlocking(ir: IRBundle): MainThreadBlocking | undefined;
|
|
16
|
+
//# sourceMappingURL=main-thread.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main-thread.d.ts","sourceRoot":"","sources":["../../src/extract/main-thread.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,QAAQ,GAAG,kBAAkB,GAAG,SAAS,CAiCtF"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main Thread Blocking Score extractor.
|
|
3
|
+
*
|
|
4
|
+
* Computes a blocking score from thread activity and main thread busyness.
|
|
5
|
+
* Combines trace-level thread activity (by category) with the global
|
|
6
|
+
* mainThreadBusyness ratio to produce a 0-1 blocking score.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Extract main thread blocking metrics from an IRBundle.
|
|
10
|
+
*
|
|
11
|
+
* Returns undefined when traceSummary data is missing (zero duration).
|
|
12
|
+
*/
|
|
13
|
+
export function extractMainThreadBlocking(ir) {
|
|
14
|
+
const { traceSummary } = ir.performance;
|
|
15
|
+
const totalDuration = traceSummary.totalDuration;
|
|
16
|
+
if (!totalDuration || totalDuration <= 0) {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
const busyMs = traceSummary.threadActivity.totalMs;
|
|
20
|
+
// idleMs = totalDuration - busyMs can produce absurd values (e.g. 210M ms ≈ 58h)
|
|
21
|
+
// when totalDuration (trace event timestamps, wall-clock anchored) and busyMs
|
|
22
|
+
// (CDP Tracing durations, monotonic clock) come from different clock domains.
|
|
23
|
+
// Clamp idleMs to a sane maximum so clock-domain mismatch doesn't pollute the output.
|
|
24
|
+
const IDLE_MS_MAX = 120_000; // 2 minutes — reasonable upper bound for idle time
|
|
25
|
+
const idleMs = Math.min(IDLE_MS_MAX, Math.max(0, totalDuration - busyMs));
|
|
26
|
+
const blockingRatio = totalDuration > 0 ? Math.min(1, Math.max(0, busyMs / totalDuration)) : 0;
|
|
27
|
+
// blockingScore blends mainThreadBusyness with the raw busy ratio
|
|
28
|
+
// to smooth out edge cases where one source is more reliable
|
|
29
|
+
const busynessScore = ir.performance.mainThreadBusyness;
|
|
30
|
+
const blockingScore = Number.isFinite(busynessScore)
|
|
31
|
+
? Math.min(1, Math.max(0, (busynessScore + blockingRatio) / 2))
|
|
32
|
+
: blockingRatio;
|
|
33
|
+
return {
|
|
34
|
+
blockingScore,
|
|
35
|
+
busyMs,
|
|
36
|
+
idleMs,
|
|
37
|
+
blockingRatio,
|
|
38
|
+
categories: { ...traceSummary.threadActivity.byCategory },
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=main-thread.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main-thread.js","sourceRoot":"","sources":["../../src/extract/main-thread.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CAAC,EAAY;IACpD,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC;IACxC,MAAM,aAAa,GAAG,YAAY,CAAC,aAAa,CAAC;IAEjD,IAAI,CAAC,aAAa,IAAI,aAAa,IAAI,CAAC,EAAE,CAAC;QACzC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,MAAM,GAAG,YAAY,CAAC,cAAc,CAAC,OAAO,CAAC;IAEnD,iFAAiF;IACjF,8EAA8E;IAC9E,8EAA8E;IAC9E,sFAAsF;IACtF,MAAM,WAAW,GAAG,OAAO,CAAC,CAAC,mDAAmD;IAChF,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC,CAAC,CAAC;IAE1E,MAAM,aAAa,GAAG,aAAa,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAE/F,kEAAkE;IAClE,6DAA6D;IAC7D,MAAM,aAAa,GAAG,EAAE,CAAC,WAAW,CAAC,kBAAkB,CAAC;IACxD,MAAM,aAAa,GAAG,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC;QAClD,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,aAAa,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;QAC/D,CAAC,CAAC,aAAa,CAAC;IAElB,OAAO;QACL,aAAa;QACb,MAAM;QACN,MAAM;QACN,aAAa;QACb,UAAU,EAAE,EAAE,GAAG,YAAY,CAAC,cAAc,CAAC,UAAU,EAAE;KAC1D,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render-Blocking Score extractor.
|
|
3
|
+
*
|
|
4
|
+
* Identifies render-blocking resources (stylesheets, render-blocking scripts)
|
|
5
|
+
* and computes a weighted score combining count, bytes, and Lighthouse
|
|
6
|
+
* numeric value when available.
|
|
7
|
+
*/
|
|
8
|
+
import type { IRBundle } from '../normalize/types.js';
|
|
9
|
+
import type { RenderBlockingScore } from './types.js';
|
|
10
|
+
/**
|
|
11
|
+
* Extract render-blocking metrics from an IRBundle.
|
|
12
|
+
*
|
|
13
|
+
* Returns undefined when there are no network requests.
|
|
14
|
+
*/
|
|
15
|
+
export declare function extractRenderBlocking(ir: IRBundle): RenderBlockingScore | undefined;
|
|
16
|
+
//# sourceMappingURL=render-blocking.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-blocking.d.ts","sourceRoot":"","sources":["../../src/extract/render-blocking.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAqB,MAAM,uBAAuB,CAAC;AACzE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AA0BtD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,QAAQ,GAAG,mBAAmB,GAAG,SAAS,CAgDnF"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render-Blocking Score extractor.
|
|
3
|
+
*
|
|
4
|
+
* Identifies render-blocking resources (stylesheets, render-blocking scripts)
|
|
5
|
+
* and computes a weighted score combining count, bytes, and Lighthouse
|
|
6
|
+
* numeric value when available.
|
|
7
|
+
*/
|
|
8
|
+
/** Tags considered render-blocking by default. */
|
|
9
|
+
const BLOCKING_TYPES = new Set(['Stylesheet', 'Document']);
|
|
10
|
+
/**
|
|
11
|
+
* Check whether a request is render-blocking.
|
|
12
|
+
*
|
|
13
|
+
* - Stylesheet and Document are always blocking.
|
|
14
|
+
* - Scripts at VeryHigh priority are considered blocking.
|
|
15
|
+
* - Font preloads and other early-critical resources could be added here.
|
|
16
|
+
*/
|
|
17
|
+
function isRenderBlocking(req) {
|
|
18
|
+
if (req.failed)
|
|
19
|
+
return false;
|
|
20
|
+
if (BLOCKING_TYPES.has(req.resourceType))
|
|
21
|
+
return true;
|
|
22
|
+
if (req.resourceType === 'Script' && req.priority === 'VeryHigh')
|
|
23
|
+
return true;
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Clamp a number to non-negative, returning 0 for NaN/Infinity.
|
|
28
|
+
*/
|
|
29
|
+
function safe(v) {
|
|
30
|
+
return Number.isFinite(v) && v >= 0 ? v : 0;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Extract render-blocking metrics from an IRBundle.
|
|
34
|
+
*
|
|
35
|
+
* Returns undefined when there are no network requests.
|
|
36
|
+
*/
|
|
37
|
+
export function extractRenderBlocking(ir) {
|
|
38
|
+
const requests = ir.network.requests;
|
|
39
|
+
if (requests.length === 0)
|
|
40
|
+
return undefined;
|
|
41
|
+
let blockingRequestCount = 0;
|
|
42
|
+
let blockingBytes = 0;
|
|
43
|
+
let blockingDuration = 0;
|
|
44
|
+
for (const req of requests) {
|
|
45
|
+
if (isRenderBlocking(req)) {
|
|
46
|
+
blockingRequestCount++;
|
|
47
|
+
blockingBytes += safe(req.bytes);
|
|
48
|
+
blockingDuration += safe(req.duration);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Lighthouse render-blocking-resources audit
|
|
52
|
+
const lhAudit = ir.lighthouse.failedAudits.find((a) => a.id === 'render-blocking-resources');
|
|
53
|
+
const lhRenderBlockingMs = lhAudit?.numericValue !== undefined && Number.isFinite(lhAudit.numericValue)
|
|
54
|
+
? lhAudit.numericValue
|
|
55
|
+
: undefined;
|
|
56
|
+
// Score: 0 = lots of blocking, 1 = no blocking
|
|
57
|
+
// Blend count (up to 20 resources = max penalty) and Lighthouse value
|
|
58
|
+
const countScore = Math.max(0, 1 - blockingRequestCount / 20);
|
|
59
|
+
const lhScore = lhAudit !== undefined && lhAudit.score !== undefined && Number.isFinite(lhAudit.score)
|
|
60
|
+
? lhAudit.score
|
|
61
|
+
: undefined;
|
|
62
|
+
const renderBlockingScore = lhScore !== undefined
|
|
63
|
+
? (countScore + lhScore) / 2
|
|
64
|
+
: countScore;
|
|
65
|
+
// Use Lighthouse insight data for URL-level details
|
|
66
|
+
const lighthouseResources = ir.lighthouse.renderBlockingResources;
|
|
67
|
+
const totalWastedMs = lighthouseResources
|
|
68
|
+
?.reduce((sum, r) => sum + (r.wastedMs ?? 0), 0);
|
|
69
|
+
return {
|
|
70
|
+
blockingRequestCount,
|
|
71
|
+
blockingBytes,
|
|
72
|
+
blockingDuration,
|
|
73
|
+
renderBlockingScore,
|
|
74
|
+
lhRenderBlockingMs,
|
|
75
|
+
resources: lighthouseResources,
|
|
76
|
+
totalWastedMs: lighthouseResources ? totalWastedMs : undefined,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=render-blocking.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-blocking.js","sourceRoot":"","sources":["../../src/extract/render-blocking.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Third-Party Overhead extractor.
|
|
3
|
+
*
|
|
4
|
+
* Identifies and categorises requests to third-party origins and computes
|
|
5
|
+
* aggregate metrics: count, bytes, duration, and ratio vs first-party.
|
|
6
|
+
*/
|
|
7
|
+
import type { IRBundle } from '../normalize/types.js';
|
|
8
|
+
import type { ThirdPartyOverhead } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Extract third-party overhead metrics from an IRBundle.
|
|
11
|
+
*
|
|
12
|
+
* Returns undefined when there are no network requests.
|
|
13
|
+
*/
|
|
14
|
+
export declare function extractThirdPartyOverhead(ir: IRBundle): ThirdPartyOverhead | undefined;
|
|
15
|
+
//# sourceMappingURL=third-party.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"third-party.d.ts","sourceRoot":"","sources":["../../src/extract/third-party.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAqB,MAAM,uBAAuB,CAAC;AACzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AA8DrD;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,QAAQ,GAAG,kBAAkB,GAAG,SAAS,CA6DtF"}
|