browsertime 27.0.1 → 27.0.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Browsertime changelog (we do [semantic versioning](https://semver.org))
2
2
 
3
+
4
+ ## 27.0.3 - 2026-05-04
5
+ ### Fixed
6
+ * Fix so INP only is calculated on interactions with an id [#2456](https://github.com/sitespeedio/browsertime/pull/2456).
7
+
8
+ ## 27.0.2 - 2026-05-02
9
+
10
+ ### Fixed
11
+ * SPA scripts no longer drop a HAR page when Chrome's soft-navigation `PerformanceObserver` entry doesn't fire. [#2438](https://github.com/sitespeedio/browsertime/pull/2438) gated HAR-page creation on Chrome detecting all three soft-navigation criteria (interaction + URL change + visible paint within a short window), but real SPAs like Grafana sometimes paint too late and Chrome never emits the entry — chrome-har then sees only `Network.*` events for that measurement and emits zero pages, breaking downstream per-iteration page indexing in sitespeed.io. When the measurement was started without a URL (`commands.measure.start(alias)` form) and no soft-nav entry was detected, fall back to a synthetic page anchored at the current `location.href` so each `measure.start` produces exactly one HAR page.
12
+
3
13
  ## 27.0.1 - 2026-05-02
4
14
 
5
15
  ### Fixed
@@ -106,6 +106,13 @@
106
106
  const longestInteractionList = [];
107
107
  const longestInteractionMap = {};
108
108
  for (let entry of entries) {
109
+ // web-vitals only counts entries the browser tagged as part of a
110
+ // discrete interaction; bare events like a `pointerover` fired by
111
+ // a stationary OS cursor get interactionId === 0 and must be
112
+ // skipped. Without this guard automated runs report a synthetic
113
+ // INP whenever the cursor happens to be over content as it paints.
114
+ // See https://github.com/GoogleChrome/web-vitals/blob/main/src/lib/interactions.ts
115
+ if (!entry.interactionId) continue;
109
116
  var minLongestInteraction =
110
117
  longestInteractionList[longestInteractionList.length - 1];
111
118
  var existingInteraction = longestInteractionMap[entry.interactionId];
@@ -9,6 +9,13 @@
9
9
  const longestInteractionList = [];
10
10
  const longestInteractionMap = {};
11
11
  for (let entry of entries) {
12
+ // web-vitals only counts entries the browser tagged as part of a
13
+ // discrete interaction; bare events like a `pointerover` fired by
14
+ // a stationary OS cursor get interactionId === 0 and must be
15
+ // skipped. Without this guard automated runs report a synthetic
16
+ // INP whenever the cursor happens to be over content as it paints.
17
+ // See https://github.com/GoogleChrome/web-vitals/blob/main/src/lib/interactions.ts
18
+ if (!entry.interactionId) continue;
12
19
  var minLongestInteraction =
13
20
  longestInteractionList[longestInteractionList.length - 1];
14
21
  var existingInteraction = longestInteractionMap[entry.interactionId];
package/lib/chrome/har.js CHANGED
@@ -23,10 +23,17 @@ export async function getHar(
23
23
  const logs = await runner.getLogs(Type.PERFORMANCE);
24
24
  const messages = logs.map(entry => JSON.parse(entry.message).message);
25
25
 
26
- // Inject a SoftNavigation.detected event so chrome-har renames the
27
- // current HAR page with the soft navigation URL. The PerformanceObserver
28
- // API is the only reliable source that confirms all three soft navigation
29
- // criteria (user interaction + URL change + visible paint).
26
+ // Inject a SoftNavigation.detected event so chrome-har emits/renames a
27
+ // HAR page for this measurement. Chrome's PerformanceObserver
28
+ // soft-navigation entry is the trustworthy signal (user interaction +
29
+ // URL change + visible paint), but it doesn't always fire — Grafana and
30
+ // similar SPAs can defer their post-route paint past Chrome's window —
31
+ // so when this measurement was started without a URL (the
32
+ // `commands.measure.start(alias)` form, signalling "the next page is a
33
+ // soft-navigation I'm about to drive") we fall back to the current
34
+ // location.href. Without the fallback, chrome-har silently emits zero
35
+ // pages for that measurement and downstream consumers (sitespeed.io's
36
+ // per-iteration page indexing) drift out of alignment.
30
37
  try {
31
38
  const softNavEntries = await runner.runScript(
32
39
  `const supported = PerformanceObserver.supportedEntryTypes;
@@ -44,6 +51,17 @@ export async function getHar(
44
51
  method: 'SoftNavigation.detected',
45
52
  params: { url: entry.url, startTime: entry.startTime }
46
53
  });
54
+ } else if (!result.url) {
55
+ const fallbackUrl = await runner.runScript(
56
+ 'return window.location.href;',
57
+ 'GET_URL_FOR_HAR_FALLBACK'
58
+ );
59
+ if (fallbackUrl) {
60
+ messages.push({
61
+ method: 'SoftNavigation.detected',
62
+ params: { url: fallbackUrl, startTime: 0 }
63
+ });
64
+ }
47
65
  }
48
66
  } catch {
49
67
  // Soft navigations not supported, ignore
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "browsertime",
3
3
  "description": "Get performance metrics from your web page using Browsertime.",
4
- "version": "27.0.1",
4
+ "version": "27.0.3",
5
5
  "bin": "./bin/browsertime.js",
6
6
  "type": "module",
7
7
  "types": "./types/scripting.d.ts",