midline-agent 0.3.0 → 0.4.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/README.md +89 -4
- package/browser/package.json +8 -0
- package/dist/browser/client.d.ts +59 -0
- package/dist/browser/client.js +608 -0
- package/dist/browser/index.d.ts +34 -0
- package/dist/browser/index.js +65 -0
- package/dist/browser/instrument.d.ts +39 -0
- package/dist/browser/instrument.js +217 -0
- package/dist/browser/transport.d.ts +43 -0
- package/dist/browser/transport.js +168 -0
- package/dist/browser/types.d.ts +94 -0
- package/dist/browser/types.js +2 -0
- package/dist/browser/version.d.ts +2 -0
- package/dist/browser/version.js +5 -0
- package/dist/browser/vitals.d.ts +16 -0
- package/dist/browser/vitals.js +135 -0
- package/dist/cli.js +0 -0
- package/dist/esm/browser/client.js +601 -0
- package/dist/esm/browser/index.js +52 -0
- package/dist/esm/browser/instrument.js +210 -0
- package/dist/esm/browser/transport.js +164 -0
- package/dist/esm/browser/types.js +1 -0
- package/dist/esm/browser/version.js +2 -0
- package/dist/esm/browser/vitals.js +132 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/redact.js +224 -0
- package/dist/esm/types.js +1 -0
- package/dist/redact.d.ts +3 -0
- package/dist/redact.js +12 -6
- package/package.json +27 -4
- package/scripts/mark-esm.js +6 -0
- package/src/browser/client.ts +686 -0
- package/src/browser/index.ts +74 -0
- package/src/browser/instrument.ts +275 -0
- package/src/browser/transport.ts +184 -0
- package/src/browser/types.ts +105 -0
- package/src/browser/version.ts +2 -0
- package/src/browser/vitals.ts +149 -0
- package/src/redact.ts +12 -6
- package/test/browser.test.js +328 -0
- package/tsconfig.esm.json +14 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.observeVitals = observeVitals;
|
|
4
|
+
/** web.dev thresholds: at or under the first is good, over the second is poor. */
|
|
5
|
+
const THRESHOLDS = {
|
|
6
|
+
LCP: [2500, 4000],
|
|
7
|
+
INP: [200, 500],
|
|
8
|
+
CLS: [0.1, 0.25],
|
|
9
|
+
FCP: [1800, 3000],
|
|
10
|
+
TTFB: [800, 1800],
|
|
11
|
+
};
|
|
12
|
+
const rate = (name, value) => value <= THRESHOLDS[name][0] ? "good" : value <= THRESHOLDS[name][1] ? "needs-improvement" : "poor";
|
|
13
|
+
/**
|
|
14
|
+
* Core Web Vitals without a dependency, measured the way the web-vitals library
|
|
15
|
+
* does and reported once, when the page is first hidden (the last moment a
|
|
16
|
+
* report reliably gets out). Browsers that don't expose an entry type simply
|
|
17
|
+
* omit that metric: Safari has no LCP, INP or CLS.
|
|
18
|
+
*/
|
|
19
|
+
function observeVitals(win, onReport) {
|
|
20
|
+
const Observer = win.PerformanceObserver;
|
|
21
|
+
const supported = Observer?.supportedEntryTypes ?? [];
|
|
22
|
+
const perf = win.performance;
|
|
23
|
+
const doc = win.document;
|
|
24
|
+
const values = {};
|
|
25
|
+
const observers = [];
|
|
26
|
+
let activationStart = 0;
|
|
27
|
+
let navigationType;
|
|
28
|
+
try {
|
|
29
|
+
const navigation = perf?.getEntriesByType?.("navigation")?.[0];
|
|
30
|
+
if (navigation) {
|
|
31
|
+
activationStart = navigation.activationStart ?? 0;
|
|
32
|
+
navigationType = navigation.type;
|
|
33
|
+
if (typeof navigation.responseStart === "number" && navigation.responseStart > 0) {
|
|
34
|
+
values.TTFB = Math.max(0, navigation.responseStart - activationStart);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// no navigation timing
|
|
40
|
+
}
|
|
41
|
+
const observe = (type, handle, extra = {}) => {
|
|
42
|
+
if (!Observer || !supported.includes(type))
|
|
43
|
+
return;
|
|
44
|
+
try {
|
|
45
|
+
const observer = new Observer((list) => handle(list.getEntries()));
|
|
46
|
+
observer.observe({ type, buffered: true, ...extra });
|
|
47
|
+
observers.push({ observer, handle });
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// unsupported option in this browser
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
observe("paint", (entries) => {
|
|
54
|
+
for (const entry of entries) {
|
|
55
|
+
if (entry.name === "first-contentful-paint")
|
|
56
|
+
values.FCP = Math.max(0, entry.startTime - activationStart);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
observe("largest-contentful-paint", (entries) => {
|
|
60
|
+
const last = entries[entries.length - 1];
|
|
61
|
+
if (last)
|
|
62
|
+
values.LCP = Math.max(0, last.startTime - activationStart);
|
|
63
|
+
});
|
|
64
|
+
// CLS is the largest session window: shifts less than 1s apart, at most 5s long.
|
|
65
|
+
let windowValue = 0;
|
|
66
|
+
let windowStart = 0;
|
|
67
|
+
let windowLast = 0;
|
|
68
|
+
observe("layout-shift", (entries) => {
|
|
69
|
+
for (const entry of entries) {
|
|
70
|
+
if (entry.hadRecentInput || typeof entry.value !== "number")
|
|
71
|
+
continue;
|
|
72
|
+
if (windowValue && entry.startTime - windowLast < 1000 && entry.startTime - windowStart < 5000) {
|
|
73
|
+
windowValue += entry.value;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
windowValue = entry.value;
|
|
77
|
+
windowStart = entry.startTime;
|
|
78
|
+
}
|
|
79
|
+
windowLast = entry.startTime;
|
|
80
|
+
values.CLS = Math.max(values.CLS ?? 0, windowValue);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
// INP: the slowest interaction, or near the 98th percentile on busy pages.
|
|
84
|
+
const interactions = new Map();
|
|
85
|
+
const recordInteractions = (entries) => {
|
|
86
|
+
for (const entry of entries) {
|
|
87
|
+
if (!entry.interactionId)
|
|
88
|
+
continue;
|
|
89
|
+
interactions.set(entry.interactionId, Math.max(interactions.get(entry.interactionId) ?? 0, entry.duration));
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
observe("event", recordInteractions, { durationThreshold: 40 });
|
|
93
|
+
observe("first-input", recordInteractions);
|
|
94
|
+
let reported = false;
|
|
95
|
+
const report = () => {
|
|
96
|
+
if (reported)
|
|
97
|
+
return;
|
|
98
|
+
reported = true;
|
|
99
|
+
for (const { observer, handle } of observers) {
|
|
100
|
+
try {
|
|
101
|
+
handle(observer.takeRecords());
|
|
102
|
+
observer.disconnect();
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
// already disconnected
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (interactions.size) {
|
|
109
|
+
const durations = [...interactions.values()].sort((a, b) => b - a);
|
|
110
|
+
values.INP = durations[Math.min(durations.length - 1, Math.floor(durations.length / 50))];
|
|
111
|
+
}
|
|
112
|
+
const vitals = {};
|
|
113
|
+
for (const name of Object.keys(values)) {
|
|
114
|
+
const raw = values[name];
|
|
115
|
+
if (typeof raw !== "number" || !Number.isFinite(raw))
|
|
116
|
+
continue;
|
|
117
|
+
const value = name === "CLS" ? Math.round(raw * 10000) / 10000 : Math.round(raw);
|
|
118
|
+
vitals[name] = { value, rating: rate(name, value) };
|
|
119
|
+
}
|
|
120
|
+
if (Object.keys(vitals).length)
|
|
121
|
+
onReport({ vitals, navigationType });
|
|
122
|
+
};
|
|
123
|
+
const onVisibility = () => {
|
|
124
|
+
if (doc?.visibilityState === "hidden")
|
|
125
|
+
report();
|
|
126
|
+
};
|
|
127
|
+
doc?.addEventListener("visibilitychange", onVisibility, true);
|
|
128
|
+
win.addEventListener("pagehide", report, true);
|
|
129
|
+
return () => {
|
|
130
|
+
doc?.removeEventListener("visibilitychange", onVisibility, true);
|
|
131
|
+
win.removeEventListener("pagehide", report, true);
|
|
132
|
+
for (const { observer } of observers)
|
|
133
|
+
observer.disconnect();
|
|
134
|
+
};
|
|
135
|
+
}
|
package/dist/cli.js
CHANGED
|
File without changes
|