lighthouse 13.4.0 → 13.4.1

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.
@@ -31,6 +31,8 @@ const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
31
31
  /** @typedef {LH.TraceEvent & {args: {feature: string, url?: string, lineNumber?: number, columnNumber?: number}}} DXFeatureEvent */
32
32
 
33
33
  class Baseline extends Audit {
34
+ static featureData = data;
35
+
34
36
  /**
35
37
  * @return {LH.Audit.Meta}
36
38
  */
@@ -44,6 +46,70 @@ class Baseline extends Audit {
44
46
  };
45
47
  }
46
48
 
49
+ /**
50
+ * Determines the baseline status and display string for a given feature ID.
51
+ * @param {string} featureId
52
+ * @param {{high: Record<string, string>, low: Record<string, string>, limited: string[]}} featureData
53
+ * @param {Date} currentDate
54
+ * @return {{displayStatus: string, baselineTier: 'high' | 'low' | 'limited'} | null}
55
+ */
56
+ static getFeatureStatus(featureId, featureData, currentDate) {
57
+ if (featureId in featureData.high) {
58
+ const highData = /** @type {Record<string, string>} */ (featureData.high);
59
+ return {
60
+ displayStatus: `Widely Available (${highData[featureId]})`,
61
+ baselineTier: 'high',
62
+ };
63
+ }
64
+
65
+ if (featureId in featureData.low) {
66
+ const lowData = /** @type {Record<string, string>} */ (featureData.low);
67
+ const widelyAvailableDate = new Date(lowData[featureId]);
68
+ widelyAvailableDate.setUTCMonth(widelyAvailableDate.getUTCMonth() + 30);
69
+ const widelyAvailableDateStr = widelyAvailableDate.toISOString().slice(0, 10);
70
+
71
+ if (widelyAvailableDate <= currentDate) {
72
+ return {
73
+ displayStatus: `Widely Available (${widelyAvailableDateStr})`,
74
+ baselineTier: 'high',
75
+ };
76
+ } else {
77
+ return {
78
+ displayStatus: `Newly Available (${lowData[featureId]})`,
79
+ baselineTier: 'low',
80
+ };
81
+ }
82
+ }
83
+
84
+ if (featureData.limited.includes(featureId)) {
85
+ return {
86
+ displayStatus: 'Limited Availability',
87
+ baselineTier: 'limited',
88
+ };
89
+ }
90
+
91
+ return null;
92
+ }
93
+
94
+ /**
95
+ * @param {string} featureId
96
+ * @param {{high: Record<string, string>, low: Record<string, string>, limited: string[]}} featureData
97
+ * @return {string|null}
98
+ */
99
+ static getLowDate(featureId, featureData) {
100
+ if (featureId in featureData.low) {
101
+ const lowData = /** @type {Record<string, string>} */ (featureData.low);
102
+ return lowData[featureId];
103
+ }
104
+ if (featureId in featureData.high) {
105
+ const highData = /** @type {Record<string, string>} */ (featureData.high);
106
+ const date = new Date(highData[featureId]);
107
+ date.setUTCMonth(date.getUTCMonth() - 30);
108
+ return date.toISOString().slice(0, 10);
109
+ }
110
+ return null;
111
+ }
112
+
47
113
  /**
48
114
  * @param {LH.Artifacts} artifacts
49
115
  * @return {Promise<LH.Audit.Product>}
@@ -54,15 +120,14 @@ class Baseline extends Audit {
54
120
  /** @type {Map<string, {featureId: string, source: LH.Audit.Details.SourceLocationValue | undefined}>} */
55
121
  const featuresMap = new Map();
56
122
 
57
- const dxEvents = /** @type {DXFeatureEvent[]} */ (
58
- (trace.traceEvents || []).filter(e => e.cat === 'blink.webdx_feature_usage' &&
59
- e.args?.feature)
60
- );
61
-
62
- for (const event of dxEvents) {
63
- const key = `${event.args.feature}`;
123
+ for (const e of trace.traceEvents || []) {
124
+ if (e.cat !== 'blink.webdx_feature_usage' || !e.args?.feature) {
125
+ continue;
126
+ }
127
+ const event = /** @type {DXFeatureEvent} */ (e);
64
128
 
65
- if (featuresMap.has(key)) continue;
129
+ const feature = /** @type {string} */ (event.args.feature);
130
+ if (featuresMap.has(feature)) continue;
66
131
 
67
132
  /** @type {LH.Audit.Details.SourceLocationValue | undefined} */
68
133
  let source;
@@ -74,14 +139,15 @@ class Baseline extends Audit {
74
139
  source = Audit.makeSourceLocation(event.args.url, line, column);
75
140
  }
76
141
 
77
- featuresMap.set(key, {
78
- featureId: event.args.feature,
142
+ featuresMap.set(feature, {
143
+ featureId: feature,
79
144
  source,
80
145
  });
81
146
  }
82
147
 
83
148
  const baselineFeatures = Array.from(featuresMap.values());
84
149
  const baselineStatus = [];
150
+ const currentDate = new Date;
85
151
 
86
152
  for (const feature of baselineFeatures) {
87
153
  if (!feature.featureId) {
@@ -90,20 +156,11 @@ class Baseline extends Audit {
90
156
 
91
157
  const featureId = feature.featureId;
92
158
 
93
- let displayStatus = 'Limited Availability';
94
- let baselineTier = 'limited';
95
-
96
- if (featureId in data.high) {
97
- const highData = /** @type {Record<string, string>} */ (data.high);
98
- displayStatus = `Widely Available (${highData[featureId]})`;
99
- baselineTier = 'high';
100
- } else if (featureId in data.low) {
101
- const lowData = /** @type {Record<string, string>} */ (data.low);
102
- displayStatus = `Newly Available (${lowData[featureId]})`;
103
- baselineTier = 'low';
104
- } else if (!data.limited.includes(featureId)) {
105
- continue;
106
- }
159
+ const status = Baseline.getFeatureStatus(featureId, Baseline.featureData, currentDate);
160
+ if (!status) continue;
161
+ const {displayStatus, baselineTier} = status;
162
+
163
+ const lowDate = Baseline.getLowDate(featureId, Baseline.featureData) || '';
107
164
 
108
165
  baselineStatus.push({
109
166
  featureId: {
@@ -117,11 +174,12 @@ class Baseline extends Audit {
117
174
  displayString: displayStatus,
118
175
  },
119
176
  source: feature.source,
177
+ lowDate,
120
178
  });
121
179
  }
122
180
 
123
181
  /** @type {LH.Audit.Details.Table['headings']} */
124
- const headings = [
182
+ const webFeatureHeadings = [
125
183
  {
126
184
  key: 'featureId',
127
185
  valueType: 'link',
@@ -139,43 +197,53 @@ class Baseline extends Audit {
139
197
  },
140
198
  ];
141
199
 
142
- /**
143
- * Determines the sorting rank of a baseline status.
144
- * @param {string} status The display status string.
145
- * @return {number} The numerical rank (1 is the highest priority).
146
- */
147
- const getStatusRank = (status) => {
148
- if (status.startsWith('Limited')) {
149
- return 1;
150
- }
151
- if (status.startsWith('Newly')) {
152
- return 2;
153
- }
154
- if (status.startsWith('Widely')) {
155
- return 3;
156
- }
157
- return 4;
200
+ /** @type {Record<string, number>} */
201
+ const TIER_RANKS = {
202
+ limited: 1,
203
+ low: 2,
204
+ high: 3,
158
205
  };
159
206
 
160
207
  const sortedStatuses = baselineStatus.sort((featureA, featureB) => {
161
- const rankA = getStatusRank(featureA.displayStatus.displayString);
162
- const rankB = getStatusRank(featureB.displayStatus.displayString);
208
+ const rankA = TIER_RANKS[featureA.displayStatus.status] || 4;
209
+ const rankB = TIER_RANKS[featureB.displayStatus.status] || 4;
163
210
 
164
211
  if (rankA !== rankB) {
165
212
  return rankA - rankB;
166
213
  }
167
214
 
168
- const hasSourceA = !!featureA.source;
169
- const hasSourceB = !!featureB.source;
215
+ return featureB.lowDate.localeCompare(featureA.lowDate);
216
+ });
217
+
218
+ const hasLimited = baselineStatus.some(item => item.displayStatus.status === 'limited');
170
219
 
171
- if (hasSourceA !== hasSourceB) {
172
- return hasSourceA ? -1 : 1;
220
+ /** @type {LH.Audit.Details.DebugData | undefined} */
221
+ let debugData;
222
+ if (!hasLimited && sortedStatuses.length > 0) {
223
+ const newestFeature = sortedStatuses[0];
224
+ const featureName = newestFeature.featureId.text;
225
+ const lowDate = newestFeature.lowDate;
226
+ if (lowDate) {
227
+ const year = lowDate.substring(0, 4);
228
+ debugData = {
229
+ type: 'debugdata',
230
+ newestFeatureId: featureName,
231
+ newestFeatureYear: year,
232
+ newestFeatureLowDate: lowDate,
233
+ };
173
234
  }
235
+ }
174
236
 
175
- return 0;
237
+ // Remove `lowDate` property from items before generating details table.
238
+ const tableItems = sortedStatuses.map(item => {
239
+ const {lowDate: _, ...rest} = item;
240
+ return rest;
176
241
  });
177
242
 
178
- const details = Audit.makeTableDetails(headings, sortedStatuses);
243
+ const details = Audit.makeTableDetails(webFeatureHeadings, tableItems);
244
+ if (debugData) {
245
+ details.debugData = debugData;
246
+ }
179
247
 
180
248
  return {
181
249
  score: 1,
@@ -12,6 +12,7 @@ export namespace UIStrings {
12
12
  let description: string;
13
13
  let displayValueHttpBadCode: string;
14
14
  let displayValueValidationError: string;
15
+ let explanationWithError: string;
15
16
  let explanation: string;
16
17
  }
17
18
  import { Audit } from '../audit.js';
@@ -50,8 +50,13 @@ const UIStrings = {
50
50
  =1 {1 error found}
51
51
  other {# errors found}
52
52
  }`,
53
+ /**
54
+ * @description Explanatory message stating that there was a failure in an audit caused by Lighthouse not being able to download the robots.txt file for the site. Note: "robots.txt" is a canonical filename and should not be translated.
55
+ * @example {Timed out fetching resource} error
56
+ * */
57
+ explanationWithError: 'Fetch of robots.txt failed: {error}',
53
58
  /** Explanatory message stating that there was a failure in an audit caused by Lighthouse not being able to download the robots.txt file for the site. Note: "robots.txt" is a canonical filename and should not be translated. */
54
- explanation: 'Lighthouse was unable to download a robots.txt file',
59
+ explanation: 'Fetch of robots.txt failed',
55
60
  };
56
61
 
57
62
  const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
@@ -200,24 +205,33 @@ class RobotsTxt extends Audit {
200
205
  const {
201
206
  status,
202
207
  content,
208
+ errorMessage,
203
209
  } = artifacts.RobotsTxt;
204
210
 
205
- if (!status) {
211
+ // Do specific error messages first.
212
+ if (status && status >= HTTP_SERVER_ERROR_CODE_LOW) {
206
213
  return {
207
214
  score: 0,
208
- explanation: str_(UIStrings.explanation),
215
+ displayValue: str_(UIStrings.displayValueHttpBadCode, {statusCode: status}),
216
+ };
217
+ } else if (status && status >= HTTP_CLIENT_ERROR_CODE_LOW || content === '') {
218
+ return {
219
+ score: 1,
220
+ notApplicable: true,
209
221
  };
210
222
  }
211
223
 
212
- if (status >= HTTP_SERVER_ERROR_CODE_LOW) {
224
+ if (errorMessage) {
213
225
  return {
214
226
  score: 0,
215
- displayValue: str_(UIStrings.displayValueHttpBadCode, {statusCode: status}),
227
+ explanation: str_(UIStrings.explanationWithError, {error: errorMessage}),
216
228
  };
217
- } else if (status >= HTTP_CLIENT_ERROR_CODE_LOW || content === '') {
229
+ }
230
+
231
+ if (!status) {
218
232
  return {
219
- score: 1,
220
- notApplicable: true,
233
+ score: 0,
234
+ explanation: str_(UIStrings.explanation),
221
235
  };
222
236
  }
223
237
 
@@ -11,8 +11,8 @@ const UIStrings = {
11
11
  /** Title of a Lighthouse audit that lists forms found in the page for WebMCP coverage. "WebMCP" stands for "Web Model Context Protocol" and should not be translated. */
12
12
  title: 'WebMCP form coverage',
13
13
  /** Description of a Lighthouse audit that lists forms found in the page and indicates whether they have WebMCP declarative tool annotations. This is displayed after a user expands the section to see more. No character length limits. "WebMCP" stands for "Web Model Context Protocol" and should not be translated. */
14
- description: 'Consider adding [WebMCP](http://goo.gle/webmcp-docs) annotations to the forms listed below. This helps AI ' +
15
- 'agents identify and interact with these forms more reliably.',
14
+ description: 'Consider adding [WebMCP](https://developer.chrome.com/docs/ai/webmcp) annotations to the forms listed below. ' +
15
+ 'This helps AI agents identify and interact with these forms more reliably.',
16
16
  /** [ICU Syntax] Label for the audit identifying the number of forms missing annotations. "WebMCP" stands for "Web Model Context Protocol" and should not be translated. */
17
17
  displayValue: `{itemCount, plural,
18
18
  =1 {1 form missing annotations}
@@ -35,7 +35,7 @@ class WebMcpFormCoverage extends Audit {
35
35
  title: str_(UIStrings.title),
36
36
  description: str_(UIStrings.description),
37
37
  requiredArtifacts: ['Inputs', 'WebMCP'],
38
- supportedModes: ['navigation', 'snapshot'],
38
+ supportedModes: ['navigation'],
39
39
  };
40
40
  }
41
41
 
@@ -15,7 +15,7 @@ const UIStrings = {
15
15
  /** Title of a Lighthouse audit that lists registered WebMCP tools. "WebMCP" stands for "Web Model Context Protocol" and should not be translated. */
16
16
  title: 'WebMCP tools registered',
17
17
  /** Description of a Lighthouse audit that lists registered WebMCP tools. This is displayed after a user expands the section to see more. No character length limits. "WebMCP" stands for "Web Model Context Protocol", neither should be translated. */
18
- description: 'Lists the [WebMCP tools](http://goo.gle/webmcp-docs) registered at the time of analysis.',
18
+ description: 'Lists the [WebMCP tools](https://developer.chrome.com/docs/ai/webmcp) registered at the time of analysis.',
19
19
  /** Label for a column in a data table; entries will be the name of a WebMCP tool. */
20
20
  columnTool: 'Tool name',
21
21
  /** Label for a column in a data table; entries will be the description of a WebMCP tool. */
@@ -45,7 +45,7 @@ class WebMCPRegisteredTools extends Audit {
45
45
  title: str_(UIStrings.title),
46
46
  description: str_(UIStrings.description),
47
47
  requiredArtifacts: ['WebMCP'],
48
- supportedModes: ['navigation', 'snapshot'],
48
+ supportedModes: ['navigation'],
49
49
  };
50
50
  }
51
51
 
@@ -13,9 +13,9 @@ const UIStrings = {
13
13
  /** Title of a Lighthouse audit that provides detail on WebMCP schema validity. This descriptive title is shown to users when there are schema validity issues. "WebMCP" stands for "Web Model Context Protocol" and should not be translated. */
14
14
  failureTitle: 'WebMCP schemas are invalid',
15
15
  /** Description of a Lighthouse audit that tells the user why they should ensure WebMCP schemas are valid. This is displayed after a user expands the section to see more. No character length limits. "WebMCP" stands for "Web Model Context Protocol" and should not be translated. */
16
- description: 'Valid [WebMCP schemas](http://goo.gle/webmcp-docs) are required for AI agents to ' +
17
- ' understand and interact with tools correctly. ' +
18
- 'Please fix any errors or warnings reported by the browser.',
16
+ description: 'Valid [WebMCP schemas](https://developer.chrome.com/docs/ai/webmcp) are required for AI agents to ' +
17
+ 'understand and interact with tools correctly. ' +
18
+ 'Please fix any errors or warnings reported by the browser.',
19
19
  /** Header of the table column which displays the element. */
20
20
  columnElement: 'Element',
21
21
  /** Header of the table column which displays the issue. */
@@ -48,7 +48,7 @@ class WebMcpSchemaValidity extends Audit {
48
48
  failureTitle: str_(UIStrings.failureTitle),
49
49
  description: str_(UIStrings.description),
50
50
  requiredArtifacts: ['WebMCP', 'WebMcpSchemaIssues'],
51
- supportedModes: ['navigation', 'snapshot'],
51
+ supportedModes: ['navigation'],
52
52
  };
53
53
  }
54
54
 
@@ -4,6 +4,7 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
+ import log from 'lighthouse-logger';
7
8
 
8
9
  import BaseGatherer from '../../base-gatherer.js';
9
10
 
@@ -21,7 +22,10 @@ class LlmsTxt extends BaseGatherer {
21
22
  const {finalDisplayedUrl} = passContext.baseArtifacts.URL;
22
23
  const llmUrl = new URL('/llms.txt', finalDisplayedUrl).href;
23
24
  return passContext.driver.fetcher.fetchResource(llmUrl)
24
- .catch(err => ({status: null, content: null, errorMessage: err.message}));
25
+ .catch(err => {
26
+ log.error('LlmsTxt', err);
27
+ return {status: null, content: null, errorMessage: err.message};
28
+ });
25
29
  }
26
30
  }
27
31
 
@@ -4,6 +4,8 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
+ import log from 'lighthouse-logger';
8
+
7
9
  import BaseGatherer from '../../base-gatherer.js';
8
10
 
9
11
  class RobotsTxt extends BaseGatherer {
@@ -20,7 +22,10 @@ class RobotsTxt extends BaseGatherer {
20
22
  const {finalDisplayedUrl} = passContext.baseArtifacts.URL;
21
23
  const robotsUrl = new URL('/robots.txt', finalDisplayedUrl).href;
22
24
  return passContext.driver.fetcher.fetchResource(robotsUrl)
23
- .catch(err => ({status: null, content: null, errorMessage: err.message}));
25
+ .catch(err => {
26
+ log.error('RobotsTxt', err);
27
+ return {status: null, content: null, errorMessage: err.message};
28
+ });
24
29
  }
25
30
  }
26
31
 
@@ -11,7 +11,7 @@ import {pageFunctions} from '../../lib/page-functions.js';
11
11
  class WebMcpSchemaIssues extends BaseGatherer {
12
12
  /** @type {LH.Gatherer.GathererMeta} */
13
13
  meta = {
14
- supportedModes: ['navigation', 'snapshot'],
14
+ supportedModes: ['navigation'],
15
15
  };
16
16
 
17
17
  constructor() {
@@ -25,7 +25,7 @@ import {pageFunctions} from '../../lib/page-functions.js';
25
25
  class WebMCP extends BaseGatherer {
26
26
  /** @type {LH.Gatherer.GathererMeta} */
27
27
  meta = {
28
- supportedModes: ['navigation', 'snapshot'],
28
+ supportedModes: ['navigation'],
29
29
  };
30
30
 
31
31
  constructor() {
@@ -135,8 +135,8 @@ class WebMCP extends BaseGatherer {
135
135
  */
136
136
  async getArtifact(context) {
137
137
  const isSupported = await context.driver.executionContext.evaluate(
138
- // @ts-expect-error - modelContext is not in types
139
- () => typeof navigator.modelContext !== 'undefined',
138
+ () => typeof (/** @type {any} */ (navigator)).modelContext !== 'undefined' ||
139
+ typeof (/** @type {any} */ (document)).modelContext !== 'undefined',
140
140
  {args: [], useIsolation: true}
141
141
  );
142
142
  if (!isSupported || !this._isSupported) {
@@ -77,7 +77,9 @@
77
77
  "canvas": "2015-07-29",
78
78
  "canvas-2d": "2015-07-29",
79
79
  "canvas-createconicgradient": "2023-04-11",
80
+ "canvas-reset": "2023-12-11",
80
81
  "canvas-roundrect": "2023-04-11",
82
+ "cap": "2023-12-11",
81
83
  "capture-stream-canvas": "2020-01-15",
82
84
  "caret-color": "2020-01-15",
83
85
  "cascade-layers": "2022-03-14",
@@ -119,8 +121,10 @@
119
121
  "contenteditable": "2015-07-29",
120
122
  "cookies": "2015-07-29",
121
123
  "cors": "2015-07-29",
124
+ "counter-set": "2023-12-11",
122
125
  "counter-style": "2023-09-18",
123
126
  "counters": "2015-07-29",
127
+ "createimagebitmap": "2023-12-11",
124
128
  "credential-management": "2020-01-15",
125
129
  "csp": "2016-08-02",
126
130
  "css-escape": "2020-01-15",
@@ -142,6 +146,7 @@
142
146
  "device-orientation-events": "2023-09-18",
143
147
  "dfn": "2015-07-29",
144
148
  "dialog": "2022-03-14",
149
+ "dir-pseudo": "2023-12-07",
145
150
  "dirname": "2023-08-01",
146
151
  "display": "2015-07-29",
147
152
  "display-flow-root": "2020-01-15",
@@ -165,6 +170,7 @@
165
170
  "error-cause": "2021-09-20",
166
171
  "events": "2015-07-29",
167
172
  "ex": "2015-07-29",
173
+ "exp-functions": "2023-12-07",
168
174
  "exponentiation": "2017-03-27",
169
175
  "ext-blend-minmax": "2018-04-30",
170
176
  "ext-color-buffer-float": "2021-09-20",
@@ -227,6 +233,7 @@
227
233
  "grid": "2017-10-17",
228
234
  "grid-animation": "2022-10-27",
229
235
  "hardware-concurrency": "2022-03-14",
236
+ "has": "2023-12-19",
230
237
  "hashbang-comments": "2020-03-24",
231
238
  "hashchange": "2015-07-29",
232
239
  "head": "2015-07-29",
@@ -313,12 +320,14 @@
313
320
  "lh": "2023-11-21",
314
321
  "line-break": "2020-07-28",
315
322
  "line-height": "2015-07-29",
323
+ "linear-easing": "2023-12-11",
316
324
  "link": "2015-07-29",
317
325
  "link-rel-preconnect": "2020-01-15",
318
326
  "link-rel-preload": "2021-01-26",
319
327
  "link-selectors": "2020-01-15",
320
328
  "list-elements": "2015-07-29",
321
329
  "list-style": "2015-07-29",
330
+ "loading-lazy": "2023-12-19",
322
331
  "localstorage": "2015-07-29",
323
332
  "location": "2015-07-29",
324
333
  "logical-assignments": "2020-09-16",
@@ -328,6 +337,7 @@
328
337
  "margin": "2015-07-29",
329
338
  "mark": "2015-07-29",
330
339
  "mask-type": "2020-01-15",
340
+ "masks": "2023-12-07",
331
341
  "matchmedia": "2015-07-29",
332
342
  "mathml": "2023-01-12",
333
343
  "media-capabilities": "2022-04-28",
@@ -357,6 +367,7 @@
357
367
  "nav": "2015-07-29",
358
368
  "navigation-timing": "2021-10-25",
359
369
  "navigator": "2015-07-29",
370
+ "nesting": "2023-12-11",
360
371
  "not": "2021-01-21",
361
372
  "notifications-apps": "2023-03-27",
362
373
  "nth-child": "2015-07-29",
@@ -414,6 +425,8 @@
414
425
  "prefers-color-scheme": "2020-01-15",
415
426
  "prefers-contrast": "2022-05-31",
416
427
  "prefers-reduced-motion": "2020-01-15",
428
+ "preloading-responsive-images": "2023-12-11",
429
+ "preserves-pitch": "2023-12-11",
417
430
  "print": "2023-06-06",
418
431
  "print-events": "2019-09-19",
419
432
  "progress": "2015-07-29",
@@ -451,6 +464,7 @@
451
464
  "screen": "2015-07-29",
452
465
  "screen-orientation": "2023-03-27",
453
466
  "script": "2015-07-29",
467
+ "scripting": "2023-12-07",
454
468
  "scroll-behavior": "2022-03-14",
455
469
  "scroll-elements": "2020-09-16",
456
470
  "scroll-into-view": "2020-01-15",
@@ -558,6 +572,7 @@
558
572
  "update": "2023-09-18",
559
573
  "upgrade-insecure-requests": "2018-04-30",
560
574
  "url": "2015-07-29",
575
+ "url-canparse": "2023-12-07",
561
576
  "user-action-pseudos": "2015-07-29",
562
577
  "user-activation": "2023-11-21",
563
578
  "user-agent-sniffing": "2016-09-20",
@@ -638,8 +653,6 @@
638
653
  "backdrop-filter": "2024-09-16",
639
654
  "baseline-shift": "2026-03-24",
640
655
  "canvas-2d-willreadfrequently": "2024-09-16",
641
- "canvas-reset": "2023-12-11",
642
- "cap": "2023-12-11",
643
656
  "check-visibility": "2024-03-05",
644
657
  "clipboard-supports": "2025-03-31",
645
658
  "composed-ranges": "2025-08-19",
@@ -648,24 +661,20 @@
648
661
  "contenteditable-plaintextonly": "2025-03-04",
649
662
  "contrast-color": "2026-04-10",
650
663
  "cookie-enabled": "2024-09-16",
651
- "counter-set": "2023-12-11",
652
- "createimagebitmap": "2023-12-11",
653
664
  "crisp-edges": "2026-05-07",
654
665
  "declarative-shadow-dom": "2024-02-20",
655
666
  "details-content": "2025-09-16",
656
667
  "details-name": "2024-09-03",
657
- "dir-pseudo": "2023-12-07",
658
668
  "document-caretpositionfrompoint": "2025-12-12",
659
669
  "event-timing": "2025-12-12",
660
- "exp-functions": "2023-12-07",
661
670
  "fetch-priority": "2024-10-29",
671
+ "field-sizing": "2026-06-16",
662
672
  "float16array": "2025-04-04",
663
673
  "font-family-math": "2026-03-24",
664
674
  "font-size-adjust": "2024-07-25",
665
675
  "gethtml": "2024-09-16",
666
676
  "getorinsert": "2026-02-14",
667
677
  "gradient-interpolation": "2024-06-11",
668
- "has": "2023-12-19",
669
678
  "highlight": "2026-03-24",
670
679
  "http3": "2024-09-16",
671
680
  "input-file-webkitdirectory": "2025-08-19",
@@ -680,20 +689,14 @@
680
689
  "json-raw": "2025-03-31",
681
690
  "largest-contentful-paint": "2025-12-12",
682
691
  "light-dark": "2024-05-13",
683
- "linear-easing": "2023-12-11",
684
692
  "link-rel-dns-prefetch": "2025-09-15",
685
- "loading-lazy": "2023-12-19",
686
- "masks": "2023-12-07",
687
693
  "math-sum-precise": "2026-04-10",
688
694
  "navigation": "2026-01-13",
689
- "nesting": "2023-12-11",
690
695
  "open-pseudo": "2026-05-11",
691
696
  "page-setup": "2024-12-11",
692
697
  "paint-order": "2024-03-22",
693
698
  "parse-html-unsafe": "2025-09-15",
694
699
  "popover": "2025-01-27",
695
- "preloading-responsive-images": "2023-12-11",
696
- "preserves-pitch": "2023-12-11",
697
700
  "print-color-adjust": "2025-05-01",
698
701
  "promise-try": "2025-01-07",
699
702
  "promise-withresolvers": "2024-03-05",
@@ -716,7 +719,6 @@
716
719
  "ruby-position": "2024-12-11",
717
720
  "scope": "2025-12-12",
718
721
  "screen-wake-lock": "2025-03-31",
719
- "scripting": "2023-12-07",
720
722
  "scroll-to-text-fragment": "2024-10-01",
721
723
  "scrollbar-color": "2025-12-12",
722
724
  "scrollbar-gutter": "2024-12-11",
@@ -740,7 +742,6 @@
740
742
  "transition-behavior": "2024-08-06",
741
743
  "trusted-types": "2026-02-24",
742
744
  "uint8array-base64-hex": "2025-09-05",
743
- "url-canparse": "2023-12-07",
744
745
  "urlpattern": "2025-09-15",
745
746
  "vertical-form-controls": "2024-04-18",
746
747
  "view-transition-class": "2025-10-14",
@@ -767,6 +768,7 @@
767
768
  "alternative-style-sheets",
768
769
  "ambient-light",
769
770
  "anchor-positioning",
771
+ "anchor-positioning-transforms",
770
772
  "app-file-handlers",
771
773
  "app-launch-handler",
772
774
  "app-migration",
@@ -891,7 +893,6 @@
891
893
  "fetch-metadata",
892
894
  "fetch-request-streams",
893
895
  "fetchlater",
894
- "field-sizing",
895
896
  "file-system-access",
896
897
  "filter-function",
897
898
  "fit-content-function",
@@ -919,6 +920,7 @@
919
920
  "gyroscope",
920
921
  "hanging-punctuation",
921
922
  "has-slotted",
923
+ "heading-offset",
922
924
  "heading-selectors",
923
925
  "hidden-until-found",
924
926
  "highlightsfrompoint",
@@ -980,6 +982,7 @@
980
982
  "meta-application-title",
981
983
  "meta-text-scale",
982
984
  "meta-theme-color",
985
+ "mixin",
983
986
  "move-before",
984
987
  "mutation-events",
985
988
  "navigation-precommit-handlers",
@@ -1069,6 +1072,7 @@
1069
1072
  "selection",
1070
1073
  "serial",
1071
1074
  "serializable-errors",
1075
+ "service-workers-static-routes",
1072
1076
  "share",
1073
1077
  "shared-storage",
1074
1078
  "shared-storage-locks",
@@ -1090,6 +1094,7 @@
1090
1094
  "summarizer",
1091
1095
  "supports-at-rule",
1092
1096
  "svg-discouraged",
1097
+ "switch-control",
1093
1098
  "table-discouraged",
1094
1099
  "target-within",
1095
1100
  "temporal",
@@ -1,3 +1,3 @@
1
1
  {
2
- "date": "2026-06-05"
2
+ "date": "2026-07-15"
3
3
  }