-
Notifications
You must be signed in to change notification settings - Fork 85
Expand file tree
/
Copy pathTTFB-Resources.js
More file actions
115 lines (103 loc) · 4.01 KB
/
TTFB-Resources.js
File metadata and controls
115 lines (103 loc) · 4.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// Measure TTFB for all resources with sorting and summary
// https://webperf-snippets.nucliweb.net
(() => {
new PerformanceObserver((entryList) => {
const entries = entryList.getEntries();
const resourcesData = entries
.filter((entry) => entry.responseStart > 0)
.map((entry) => {
const url = new URL(entry.name);
const isThirdParty = url.hostname !== location.hostname;
return {
ttfb: entry.responseStart,
duration: entry.duration,
type: entry.initiatorType,
thirdParty: isThirdParty,
resource: entry.name.length > 70 ? "..." + entry.name.slice(-67) : entry.name,
fullUrl: entry.name,
};
})
.sort((a, b) => b.ttfb - a.ttfb);
if (resourcesData.length === 0) {
console.log("%c⚠️ No resources with TTFB data available", "color: #f59e0b;");
console.log("Resources may be cached or missing Timing-Allow-Origin header.");
return;
}
// Summary statistics
const ttfbValues = resourcesData.map((r) => r.ttfb);
const avgTtfb = ttfbValues.reduce((a, b) => a + b, 0) / ttfbValues.length;
const maxTtfb = Math.max(...ttfbValues);
const minTtfb = Math.min(...ttfbValues);
const thirdPartyCount = resourcesData.filter((r) => r.thirdParty).length;
const slowResources = resourcesData.filter((r) => r.ttfb > 500).length;
console.group(`%c📊 Resource TTFB Analysis (${resourcesData.length} resources)`, "font-weight: bold; font-size: 14px;");
// Summary
console.log("");
console.log("%cSummary:", "font-weight: bold;");
console.log(` Average TTFB: ${avgTtfb.toFixed(0)}ms`);
console.log(` Fastest: ${minTtfb.toFixed(0)}ms | Slowest: ${maxTtfb.toFixed(0)}ms`);
console.log(` Third-party resources: ${thirdPartyCount}`);
if (slowResources > 0) {
console.log(`%c ⚠️ Slow resources (>500ms): ${slowResources}`, "color: #f59e0b;");
}
// Table (sorted by TTFB, slowest first)
console.log("");
console.log("%cResources (sorted by TTFB, slowest first):", "font-weight: bold;");
const tableData = resourcesData.slice(0, 25).map((resource) => ({
"TTFB (ms)": resource.ttfb.toFixed(0),
"Duration (ms)": resource.duration.toFixed(0),
Type: resource.type,
"3rd Party": resource.thirdParty ? "Yes" : "",
Resource: resource.resource,
}));
console.table(tableData);
if (resourcesData.length > 25) {
console.log(`... and ${resourcesData.length - 25} more resources`);
}
// Slowest resources highlight
const slowest = resourcesData.slice(0, 5);
if (slowest[0].ttfb > 500) {
console.log("");
console.log("%c🐌 Slowest resources:", "color: #ef4444; font-weight: bold;");
slowest.forEach((r, i) => {
const marker = r.thirdParty ? " [3rd party]" : "";
console.log(` ${i + 1}. ${r.ttfb.toFixed(0)}ms - ${r.type}${marker}: ${r.resource}`);
});
}
console.groupEnd();
}).observe({
type: "resource",
buffered: true,
});
// Synchronous return for agent
const resourcesSync = performance.getEntriesByType("resource")
.filter((entry) => entry.responseStart > 0)
.map((entry) => {
const url = new URL(entry.name);
return {
url: entry.name,
ttfbMs: Math.round(entry.responseStart),
durationMs: Math.round(entry.duration),
type: entry.initiatorType,
isThirdParty: url.hostname !== location.hostname,
};
})
.sort((a, b) => b.ttfbMs - a.ttfbMs);
if (resourcesSync.length === 0) {
return { script: "TTFB-Resources", status: "error", error: "No resources with TTFB data available" };
}
const ttfbVals = resourcesSync.map((r) => r.ttfbMs);
return {
script: "TTFB-Resources",
status: "ok",
count: resourcesSync.length,
details: {
avgTtfbMs: Math.round(ttfbVals.reduce((a, b) => a + b, 0) / ttfbVals.length),
maxTtfbMs: Math.max(...ttfbVals),
minTtfbMs: Math.min(...ttfbVals),
thirdPartyCount: resourcesSync.filter((r) => r.isThirdParty).length,
slowCount: resourcesSync.filter((r) => r.ttfbMs > 500).length,
},
items: resourcesSync,
};
})();