lighthouse 12.2.2 → 12.2.3-dev.20241203

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.
Files changed (63) hide show
  1. package/cli/test/smokehouse/core-tests.js +4 -0
  2. package/core/audits/byte-efficiency/render-blocking-resources.js +1 -1
  3. package/core/audits/has-hsts.d.ts +44 -0
  4. package/core/audits/has-hsts.js +208 -0
  5. package/core/computed/metrics/cumulative-layout-shift.js +4 -4
  6. package/core/computed/metrics/lantern-metric.js +4 -2
  7. package/core/computed/navigation-insights.d.ts +1 -1
  8. package/core/computed/network-analysis.js +13 -1
  9. package/core/computed/trace-engine-result.d.ts +4 -0
  10. package/core/computed/trace-engine-result.js +30 -4
  11. package/core/config/default-config.js +2 -0
  12. package/core/config/lr-mobile-config.js +1 -1
  13. package/core/lib/trace-engine.d.ts +1 -1
  14. package/core/lib/trace-engine.js +1 -1
  15. package/package.json +2 -2
  16. package/shared/localization/locales/ar-XB.json +60 -0
  17. package/shared/localization/locales/ar.json +60 -0
  18. package/shared/localization/locales/bg.json +60 -0
  19. package/shared/localization/locales/ca.json +60 -0
  20. package/shared/localization/locales/cs.json +60 -0
  21. package/shared/localization/locales/da.json +60 -0
  22. package/shared/localization/locales/de.json +60 -0
  23. package/shared/localization/locales/el.json +60 -0
  24. package/shared/localization/locales/en-GB.json +60 -0
  25. package/shared/localization/locales/en-US.json +136 -40
  26. package/shared/localization/locales/en-XL.json +136 -40
  27. package/shared/localization/locales/es-419.json +60 -0
  28. package/shared/localization/locales/es.json +60 -0
  29. package/shared/localization/locales/fi.json +60 -0
  30. package/shared/localization/locales/fil.json +60 -0
  31. package/shared/localization/locales/fr.json +60 -0
  32. package/shared/localization/locales/he.json +60 -0
  33. package/shared/localization/locales/hi.json +60 -0
  34. package/shared/localization/locales/hr.json +60 -0
  35. package/shared/localization/locales/hu.json +60 -0
  36. package/shared/localization/locales/id.json +60 -0
  37. package/shared/localization/locales/it.json +60 -0
  38. package/shared/localization/locales/ja.json +60 -0
  39. package/shared/localization/locales/ko.json +60 -0
  40. package/shared/localization/locales/lt.json +60 -0
  41. package/shared/localization/locales/lv.json +60 -0
  42. package/shared/localization/locales/nl.json +60 -0
  43. package/shared/localization/locales/no.json +60 -0
  44. package/shared/localization/locales/pl.json +60 -0
  45. package/shared/localization/locales/pt-PT.json +60 -0
  46. package/shared/localization/locales/pt.json +60 -0
  47. package/shared/localization/locales/ro.json +60 -0
  48. package/shared/localization/locales/ru.json +60 -0
  49. package/shared/localization/locales/sk.json +60 -0
  50. package/shared/localization/locales/sl.json +60 -0
  51. package/shared/localization/locales/sr-Latn.json +60 -0
  52. package/shared/localization/locales/sr.json +60 -0
  53. package/shared/localization/locales/sv.json +60 -0
  54. package/shared/localization/locales/ta.json +60 -0
  55. package/shared/localization/locales/te.json +60 -0
  56. package/shared/localization/locales/th.json +60 -0
  57. package/shared/localization/locales/tr.json +60 -0
  58. package/shared/localization/locales/uk.json +60 -0
  59. package/shared/localization/locales/vi.json +60 -0
  60. package/shared/localization/locales/zh-HK.json +60 -0
  61. package/shared/localization/locales/zh-TW.json +60 -0
  62. package/shared/localization/locales/zh.json +60 -0
  63. package/types/artifacts.d.ts +2 -2
@@ -21,6 +21,8 @@ import fpsMaxPassive from './test-definitions/fps-max-passive.js';
21
21
  import fpsScaled from './test-definitions/fps-scaled.js';
22
22
  import fpsOverflowX from './test-definitions/fps-overflow-x.js';
23
23
  import issuesMixedContent from './test-definitions/issues-mixed-content.js';
24
+ import hstsFullyPresent from './test-definitions/hsts-fully-present.js';
25
+ import hstsMissingDirectives from './test-definitions/hsts-missing-directives.js';
24
26
  import lanternFetch from './test-definitions/lantern-fetch.js';
25
27
  import lanternIdleCallbackLong from './test-definitions/lantern-idle-callback-long.js';
26
28
  import lanternIdleCallbackShort from './test-definitions/lantern-idle-callback-short.js';
@@ -79,6 +81,8 @@ const smokeTests = [
79
81
  fpsOverflowX,
80
82
  fpsScaled,
81
83
  issuesMixedContent,
84
+ hstsFullyPresent,
85
+ hstsMissingDirectives,
82
86
  lanternFetch,
83
87
  lanternIdleCallbackLong,
84
88
  lanternIdleCallbackShort,
@@ -130,7 +130,7 @@ class RenderBlockingResources extends Audit {
130
130
  const wastedCssBytes = await RenderBlockingResources.computeWastedCSSBytes(artifacts, context);
131
131
  const navInsights = await NavigationInsights.request(trace, context);
132
132
 
133
- const renderBlocking = navInsights.RenderBlocking;
133
+ const renderBlocking = navInsights.model.RenderBlocking;
134
134
  if (renderBlocking instanceof Error) throw renderBlocking;
135
135
 
136
136
  /** @type {LH.Audit.Context['settings']} */
@@ -0,0 +1,44 @@
1
+ export default HasHsts;
2
+ declare class HasHsts extends Audit {
3
+ /**
4
+ * @param {LH.Artifacts} artifacts
5
+ * @param {LH.Audit.Context} context
6
+ * @return {Promise<string[]>}
7
+ */
8
+ static getRawHsts(artifacts: LH.Artifacts, context: LH.Audit.Context): Promise<string[]>;
9
+ /**
10
+ * @param {string} hstsDirective
11
+ * @param {LH.IcuMessage | string} findingDescription
12
+ * @param {LH.IcuMessage=} severity
13
+ * @return {LH.Audit.Details.TableItem}
14
+ */
15
+ static findingToTableItem(hstsDirective: string, findingDescription: LH.IcuMessage | string, severity?: LH.IcuMessage | undefined): LH.Audit.Details.TableItem;
16
+ /**
17
+ * @param {string[]} hstsHeaders
18
+ * @return {{score: number, results: LH.Audit.Details.TableItem[]}}
19
+ */
20
+ static constructResults(hstsHeaders: string[]): {
21
+ score: number;
22
+ results: LH.Audit.Details.TableItem[];
23
+ };
24
+ /**
25
+ * @param {LH.Artifacts} artifacts
26
+ * @param {LH.Audit.Context} context
27
+ * @return {Promise<LH.Audit.Product>}
28
+ */
29
+ static audit(artifacts: LH.Artifacts, context: LH.Audit.Context): Promise<LH.Audit.Product>;
30
+ }
31
+ export namespace UIStrings {
32
+ let title: string;
33
+ let description: string;
34
+ let noHsts: string;
35
+ let noPreload: string;
36
+ let noSubdomain: string;
37
+ let noMaxAge: string;
38
+ let lowMaxAge: string;
39
+ let invalidSyntax: string;
40
+ let columnDirective: string;
41
+ let columnSeverity: string;
42
+ }
43
+ import { Audit } from './audit.js';
44
+ //# sourceMappingURL=has-hsts.d.ts.map
@@ -0,0 +1,208 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2024 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import {Audit} from './audit.js';
8
+ import {MainResource} from '../computed/main-resource.js';
9
+ import * as i18n from '../lib/i18n/i18n.js';
10
+
11
+ const UIStrings = {
12
+ /** Title of a Lighthouse audit that evaluates the security of a page's HSTS header. "HSTS" stands for "HTTP Strict Transport Security". */
13
+ title: 'Use a strong HSTS policy',
14
+ /** Description of a Lighthouse audit that evaluates the security of a page's HSTS header. This is displayed after a user expands the section to see more. No character length limits. The last sentence starting with 'Learn' becomes link text to additional documentation. "HSTS" stands for "HTTP Strict Transport Security". */
15
+ description: 'Deployment of the HSTS header significantly ' +
16
+ 'reduces the risk of downgrading HTTP connections and eavesdropping attacks. ' +
17
+ 'A rollout in stages, starting with a low max-age is recommended. ' +
18
+ '[Learn more about using a strong HSTS policy.](https://developer.chrome.com/docs/lighthouse/best-practices/has-hsts)',
19
+ /** Summary text for the results of a Lighthouse audit that evaluates the HSTS header. This is displayed if no HSTS header is deployed. "HSTS" stands for "HTTP Strict Transport Security". */
20
+ noHsts: 'No HSTS header found',
21
+ /** Summary text for the results of a Lighthouse audit that evaluates the HSTS header. This is displayed if the preload directive is missing. "HSTS" stands for "HTTP Strict Transport Security". */
22
+ noPreload: 'No `preload` directive found',
23
+ /** Summary text for the results of a Lighthouse audit that evaluates the HSTS header. This is displayed if the includeSubDomains directive is missing. "HSTS" stands for "HTTP Strict Transport Security". */
24
+ noSubdomain: 'No `includeSubDomains` directive found',
25
+ /** Summary text for the results of a Lighthouse audit that evaluates the HSTS header. This is displayed if the max-age directive is missing. "HSTS" stands for "HTTP Strict Transport Security". */
26
+ noMaxAge: 'No `max-age` directive',
27
+ /** Summary text for the results of a Lighthouse audit that evaluates the HSTS header. This is displayed if the provided duration for the max-age directive is too low. "HSTS" stands for "HTTP Strict Transport Security". */
28
+ lowMaxAge: '`max-age` is too low',
29
+ /** Table item value calling out the presence of a syntax error. */
30
+ invalidSyntax: 'Invalid syntax',
31
+ /** Label for a column in a data table; entries will be a directive of the HSTS header. "HSTS" stands for "HTTP Strict Transport Security". */
32
+ columnDirective: 'Directive',
33
+ /** Label for a column in a data table; entries will be the severity of an issue with the HSTS header. "HSTS" stands for "HTTP Strict Transport Security". */
34
+ columnSeverity: 'Severity',
35
+ };
36
+
37
+ const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
38
+
39
+ class HasHsts extends Audit {
40
+ /**
41
+ * @return {LH.Audit.Meta}
42
+ */
43
+ static get meta() {
44
+ return {
45
+ id: 'has-hsts',
46
+ scoreDisplayMode: Audit.SCORING_MODES.INFORMATIVE,
47
+ title: str_(UIStrings.title),
48
+ description: str_(UIStrings.description),
49
+ requiredArtifacts: ['devtoolsLogs', 'URL'],
50
+ supportedModes: ['navigation'],
51
+ };
52
+ }
53
+
54
+
55
+ /**
56
+ * @param {LH.Artifacts} artifacts
57
+ * @param {LH.Audit.Context} context
58
+ * @return {Promise<string[]>}
59
+ */
60
+ static async getRawHsts(artifacts, context) {
61
+ const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
62
+ const mainResource =
63
+ await MainResource.request({devtoolsLog, URL: artifacts.URL}, context);
64
+
65
+ let hstsHeaders =
66
+ mainResource.responseHeaders
67
+ .filter(h => {
68
+ return h.name.toLowerCase() === 'strict-transport-security';
69
+ })
70
+ .flatMap(h => h.value.split(';'));
71
+
72
+ // Sanitize the header value / directives.
73
+ hstsHeaders = hstsHeaders.map(v => v.toLowerCase().replace(/\s/g, ''));
74
+
75
+ return hstsHeaders;
76
+ }
77
+
78
+ /**
79
+ * @param {string} hstsDirective
80
+ * @param {LH.IcuMessage | string} findingDescription
81
+ * @param {LH.IcuMessage=} severity
82
+ * @return {LH.Audit.Details.TableItem}
83
+ */
84
+ static findingToTableItem(hstsDirective, findingDescription, severity) {
85
+ return {
86
+ directive: hstsDirective,
87
+ description: findingDescription,
88
+ severity,
89
+ };
90
+ }
91
+
92
+ /**
93
+ * @param {string[]} hstsHeaders
94
+ * @return {{score: number, results: LH.Audit.Details.TableItem[]}}
95
+ */
96
+ static constructResults(hstsHeaders) {
97
+ const rawHsts = [...hstsHeaders];
98
+ const allowedDirectives = ['max-age', 'includesubdomains', 'preload'];
99
+ const violations = [];
100
+ const warnings = [];
101
+ const syntax = [];
102
+
103
+ if (!rawHsts.length) {
104
+ return {
105
+ score: 0,
106
+ results: [{
107
+ severity: str_(i18n.UIStrings.itemSeverityHigh),
108
+ description: str_(UIStrings.noHsts),
109
+ directive: undefined,
110
+ }],
111
+ };
112
+ }
113
+
114
+ // No max-age is a violation and renders the HSTS header useless.
115
+ if (!hstsHeaders.toString().includes('max-age')) {
116
+ violations.push({
117
+ severity: str_(i18n.UIStrings.itemSeverityHigh),
118
+ description: str_(UIStrings.noMaxAge),
119
+ directive: 'max-age',
120
+ });
121
+ }
122
+
123
+ if (!hstsHeaders.toString().includes('includesubdomains')) {
124
+ // No includeSubdomains might be even wanted. But would be preferred.
125
+ warnings.push({
126
+ severity: str_(i18n.UIStrings.itemSeverityMedium),
127
+ description: str_(UIStrings.noSubdomain),
128
+ directive: 'includeSubDomains',
129
+ });
130
+ }
131
+
132
+ if (!hstsHeaders.toString().includes('preload')) {
133
+ // No preload might be even wanted. But would be preferred.
134
+ warnings.push({
135
+ severity: str_(i18n.UIStrings.itemSeverityMedium),
136
+ description: str_(UIStrings.noPreload),
137
+ directive: 'preload',
138
+ });
139
+ }
140
+
141
+ for (const actualDirective of hstsHeaders) {
142
+ // We recommend 2y max-age. But if it's lower than 1y, it's a violation.
143
+ if (actualDirective.includes('max-age') &&
144
+ parseInt(actualDirective.split('=')[1], 10) < 31536000) {
145
+ violations.push({
146
+ severity: str_(i18n.UIStrings.itemSeverityHigh),
147
+ description: str_(UIStrings.lowMaxAge),
148
+ directive: 'max-age',
149
+ });
150
+ }
151
+
152
+ // If there is a directive that's not an official HSTS directive.
153
+ if (!allowedDirectives.includes(actualDirective) &&
154
+ !actualDirective.includes('max-age')) {
155
+ syntax.push({
156
+ severity: str_(i18n.UIStrings.itemSeverityLow),
157
+ description: str_(UIStrings.invalidSyntax),
158
+ directive: actualDirective,
159
+ });
160
+ }
161
+ }
162
+
163
+ const results = [
164
+ ...violations.map(
165
+ f => this.findingToTableItem(
166
+ f.directive, f.description,
167
+ str_(i18n.UIStrings.itemSeverityHigh))),
168
+ ...warnings.map(
169
+ f => this.findingToTableItem(
170
+ f.directive, f.description,
171
+ str_(i18n.UIStrings.itemSeverityMedium))),
172
+ ...syntax.map(
173
+ f => this.findingToTableItem(
174
+ f.directive, f.description,
175
+ str_(i18n.UIStrings.itemSeverityLow))),
176
+ ];
177
+ return {score: violations.length || syntax.length ? 0 : 1, results};
178
+ }
179
+
180
+ /**
181
+ * @param {LH.Artifacts} artifacts
182
+ * @param {LH.Audit.Context} context
183
+ * @return {Promise<LH.Audit.Product>}
184
+ */
185
+ static async audit(artifacts, context) {
186
+ const hstsHeaders = await this.getRawHsts(artifacts, context);
187
+ const {score, results} = this.constructResults(hstsHeaders);
188
+
189
+ /** @type {LH.Audit.Details.Table['headings']} */
190
+ const headings = [
191
+ /* eslint-disable max-len */
192
+ {key: 'description', valueType: 'text', subItemsHeading: {key: 'description'}, label: str_(i18n.UIStrings.columnDescription)},
193
+ {key: 'directive', valueType: 'code', subItemsHeading: {key: 'directive'}, label: str_(UIStrings.columnDirective)},
194
+ {key: 'severity', valueType: 'text', subItemsHeading: {key: 'severity'}, label: str_(UIStrings.columnSeverity)},
195
+ /* eslint-enable max-len */
196
+ ];
197
+ const details = Audit.makeTableDetails(headings, results);
198
+
199
+ return {
200
+ score,
201
+ notApplicable: !results.length,
202
+ details,
203
+ };
204
+ }
205
+ }
206
+
207
+ export default HasHsts;
208
+ export {UIStrings};
@@ -160,13 +160,13 @@ class CumulativeLayoutShift {
160
160
  Screenshots: TraceEngine.TraceHandlers.Screenshots,
161
161
  });
162
162
  // eslint-disable-next-line max-len
163
- await processor.parse(/** @type {import('@paulirish/trace_engine').Types.TraceEvents.TraceEventData[]} */ (
163
+ await processor.parse(/** @type {import('@paulirish/trace_engine').Types.Events.Event[]} */ (
164
164
  events
165
- ));
166
- if (!processor.traceParsedData) {
165
+ ), {});
166
+ if (!processor.parsedTrace) {
167
167
  throw new Error('null trace engine result');
168
168
  }
169
- return processor.traceParsedData.LayoutShifts.sessionMaxScore;
169
+ return processor.parsedTrace.LayoutShifts.sessionMaxScore;
170
170
  };
171
171
  const cumulativeLayoutShift = await run(allFrameShiftEvents.map(e => e.event));
172
172
  const cumulativeLayoutShiftMainFrame = await run(mainFrameShiftEvents.map(e => e.event));
@@ -38,8 +38,10 @@ async function getComputationDataParamsFromTrace(data, context) {
38
38
 
39
39
  const graph = await PageDependencyGraph.request({...data, fromTrace: true}, context);
40
40
  const traceEngineResult = await TraceEngineResult.request(data, context);
41
- const processedNavigation =
42
- Lantern.TraceEngineComputationData.createProcessedNavigation(traceEngineResult.data);
41
+ const frameId = traceEngineResult.data.Meta.mainFrameId;
42
+ const navigationId = traceEngineResult.data.Meta.mainFrameNavigations[0].args.data.navigationId;
43
+ const processedNavigation = Lantern.TraceEngineComputationData.createProcessedNavigation(
44
+ traceEngineResult.data, frameId, navigationId);
43
45
  const simulator = data.simulator || (await LoadSimulator.request(data, context));
44
46
 
45
47
  return {simulator, graph, processedNavigation};
@@ -11,6 +11,6 @@ declare class NavigationInsights {
11
11
  * @param {LH.Trace} trace
12
12
  * @param {LH.Artifacts.ComputedContext} context
13
13
  */
14
- static compute_(trace: LH.Trace, context: LH.Artifacts.ComputedContext): Promise<import("@paulirish/trace_engine/models/trace/insights/types.js").NavigationInsightData>;
14
+ static compute_(trace: LH.Trace, context: LH.Artifacts.ComputedContext): Promise<import("@paulirish/trace_engine/models/trace/insights/types.js").InsightSet>;
15
15
  }
16
16
  //# sourceMappingURL=navigation-insights.d.ts.map
@@ -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 * as Lantern from '../lib/lantern/lantern.js';
8
10
  import {makeComputedArtifact} from './computed-artifact.js';
9
11
  import {NetworkRecords} from './network-records.js';
@@ -16,7 +18,17 @@ class NetworkAnalysis {
16
18
  */
17
19
  static async compute_(devtoolsLog, context) {
18
20
  const records = await NetworkRecords.request(devtoolsLog, context);
19
- return Lantern.Core.NetworkAnalyzer.analyze(records);
21
+ const analysis = Lantern.Core.NetworkAnalyzer.analyze(records);
22
+ if (!analysis) {
23
+ log.error('NetworkAnalysis', 'Network analysis failed due to lack of transfer data');
24
+ return {
25
+ throughput: 0,
26
+ rtt: Number.POSITIVE_INFINITY,
27
+ additionalRttByOrigin: new Map(),
28
+ serverResponseTimeByOrigin: new Map(),
29
+ };
30
+ }
31
+ return analysis;
20
32
  }
21
33
  }
22
34
 
@@ -13,6 +13,10 @@ declare class TraceEngineResult {
13
13
  * @return {Promise<LH.Artifacts.TraceEngineResult>}
14
14
  */
15
15
  static runTraceEngine(traceEvents: LH.TraceEvent[]): Promise<LH.Artifacts.TraceEngineResult>;
16
+ /**
17
+ * @param {import('@paulirish/trace_engine/models/trace/insights/types.js').TraceInsightSets} insightSets
18
+ */
19
+ static localizeInsights(insightSets: import("@paulirish/trace_engine/models/trace/insights/types.js").TraceInsightSets): void;
16
20
  /**
17
21
  * @param {{trace: LH.Trace}} data
18
22
  * @param {LH.Artifacts.ComputedContext} context
@@ -4,6 +4,7 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
+ import * as i18n from '../lib/i18n/i18n.js';
7
8
  import * as TraceEngine from '../lib/trace-engine.js';
8
9
  import {makeComputedArtifact} from './computed-artifact.js';
9
10
  import {CumulativeLayoutShift} from './metrics/cumulative-layout-shift.js';
@@ -30,12 +31,37 @@ class TraceEngineResult {
30
31
  const processor = new TraceEngine.TraceProcessor(traceHandlers);
31
32
 
32
33
  // eslint-disable-next-line max-len
33
- await processor.parse(/** @type {import('@paulirish/trace_engine').Types.TraceEvents.TraceEventData[]} */ (
34
+ await processor.parse(/** @type {import('@paulirish/trace_engine').Types.Events.Event[]} */ (
34
35
  traceEvents
35
- ));
36
- if (!processor.traceParsedData) throw new Error('No data');
36
+ ), {});
37
+ if (!processor.parsedTrace) throw new Error('No data');
37
38
  if (!processor.insights) throw new Error('No insights');
38
- return {data: processor.traceParsedData, insights: processor.insights};
39
+ this.localizeInsights(processor.insights);
40
+ return {data: processor.parsedTrace, insights: processor.insights};
41
+ }
42
+
43
+ /**
44
+ * @param {import('@paulirish/trace_engine/models/trace/insights/types.js').TraceInsightSets} insightSets
45
+ */
46
+ static localizeInsights(insightSets) {
47
+ for (const insightSet of insightSets.values()) {
48
+ for (const [name, model] of Object.entries(insightSet.model)) {
49
+ if (model instanceof Error) {
50
+ continue;
51
+ }
52
+
53
+ const key = `node_modules/@paulirish/trace_engine/models/trace/insights/${name}.js`;
54
+ const str_ = i18n.createIcuMessageFn(key, {
55
+ title: model.title,
56
+ description: model.description,
57
+ });
58
+
59
+ // @ts-expect-error coerce to string, should be fine
60
+ model.title = str_(model.title);
61
+ // @ts-expect-error coerce to string, should be fine
62
+ model.description = str_(model.description);
63
+ }
64
+ }
39
65
  }
40
66
 
41
67
  /**
@@ -192,6 +192,7 @@ const defaultConfig = {
192
192
  'valid-source-maps',
193
193
  'prioritize-lcp-image',
194
194
  'csp-xss',
195
+ 'has-hsts',
195
196
  'script-treemap-data',
196
197
  'accessibility/accesskeys',
197
198
  'accessibility/aria-allowed-attr',
@@ -541,6 +542,7 @@ const defaultConfig = {
541
542
  {id: 'geolocation-on-start', weight: 1, group: 'best-practices-trust-safety'},
542
543
  {id: 'notification-on-start', weight: 1, group: 'best-practices-trust-safety'},
543
544
  {id: 'csp-xss', weight: 0, group: 'best-practices-trust-safety'},
545
+ {id: 'has-hsts', weight: 0, group: 'best-practices-trust-safety'},
544
546
  // User Experience
545
547
  {id: 'paste-preventing-inputs', weight: 3, group: 'best-practices-ux'},
546
548
  {id: 'image-aspect-ratio', weight: 1, group: 'best-practices-ux'},
@@ -13,7 +13,7 @@ const config = {
13
13
  throttling: {
14
14
  // Determined using PSI CPU benchmark median and
15
15
  // https://lighthouse-cpu-throttling-calculator.vercel.app/
16
- cpuSlowdownMultiplier: 1.5,
16
+ cpuSlowdownMultiplier: 1.2,
17
17
  },
18
18
  skipAudits: [
19
19
  // Skip the h2 audit so it doesn't lie to us. See https://github.com/GoogleChrome/lighthouse/issues/6539
@@ -1,4 +1,4 @@
1
- export type SyntheticLayoutShift = import("@paulirish/trace_engine").Types.TraceEvents.SyntheticLayoutShift;
1
+ export type SyntheticLayoutShift = import("@paulirish/trace_engine").Types.Events.SyntheticLayoutShift;
2
2
  export type SaneSyntheticLayoutShift = SyntheticLayoutShift & {
3
3
  args: {
4
4
  data: NonNullable<SyntheticLayoutShift["args"]["data"]>;
@@ -2,7 +2,7 @@ import * as TraceEngine from '@paulirish/trace_engine';
2
2
 
3
3
  import {polyfillDOMRect} from './polyfill-dom-rect.js';
4
4
 
5
- /** @typedef {import('@paulirish/trace_engine').Types.TraceEvents.SyntheticLayoutShift} SyntheticLayoutShift */
5
+ /** @typedef {import('@paulirish/trace_engine').Types.Events.SyntheticLayoutShift} SyntheticLayoutShift */
6
6
  /** @typedef {SyntheticLayoutShift & {args: {data: NonNullable<SyntheticLayoutShift['args']['data']>}}} SaneSyntheticLayoutShift */
7
7
 
8
8
  polyfillDOMRect();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "lighthouse",
3
3
  "type": "module",
4
- "version": "12.2.2",
4
+ "version": "12.2.3-dev.20241203",
5
5
  "description": "Automated auditing, performance metrics, and best practices for the web.",
6
6
  "main": "./core/index.js",
7
7
  "bin": {
@@ -181,7 +181,7 @@
181
181
  "webtreemap-cdt": "^3.2.1"
182
182
  },
183
183
  "dependencies": {
184
- "@paulirish/trace_engine": "0.0.32",
184
+ "@paulirish/trace_engine": "0.0.39",
185
185
  "@sentry/node": "^7.0.0",
186
186
  "axe-core": "^4.10.2",
187
187
  "chrome-launcher": "^1.1.2",
@@ -2621,6 +2621,66 @@
2621
2621
  "flow-report/src/i18n/ui-strings.js | title": {
2622
2622
  "message": "‏‮Lighthouse‬‏ ‏‮User‬‏ ‏‮Flow‬‏ ‏‮Report‬‏"
2623
2623
  },
2624
+ "node_modules/@paulirish/trace_engine/models/trace/insights/CLSCulprits.js | description": {
2625
+ "message": "تحدث تغييرات التصميم عندما تتحرك العناصر بدون أي تدخُّل من المستخدم. [تحقَّق من أسباب تغييرات التصميم](https://web.dev/articles/optimize-cls)، مثل إضافة عناصر أو إزالتها أو تغيير خطوطها أثناء تحميل الصفحة."
2626
+ },
2627
+ "node_modules/@paulirish/trace_engine/models/trace/insights/CLSCulprits.js | title": {
2628
+ "message": "أسباب تغييرات التصميم"
2629
+ },
2630
+ "node_modules/@paulirish/trace_engine/models/trace/insights/DocumentLatency.js | description": {
2631
+ "message": "طلب الشبكة الأول هو الأهم. يمكنك تقليل وقت الاستجابة للطلب عن طريق تجنُّب عمليات إعادة التوجيه وضمان استجابة الخادم بسرعة وتفعيل ميزة ضغط النص."
2632
+ },
2633
+ "node_modules/@paulirish/trace_engine/models/trace/insights/DocumentLatency.js | title": {
2634
+ "message": "وقت الاستجابة لطلب المستند"
2635
+ },
2636
+ "node_modules/@paulirish/trace_engine/models/trace/insights/FontDisplay.js | description": {
2637
+ "message": "يمكنك ضبط [font-display](https://developer.chrome.com/blog/font-display) على swap أو optional لضمان ظهور النص بشكل ثابت. ويمكن تحسين swap بشكل أكبر للتخفيف من تغييرات التصميم باستخدام [إجراءات تجاوز مقاييس الخطوط](https://developer.chrome.com/blog/font-fallbacks)."
2638
+ },
2639
+ "node_modules/@paulirish/trace_engine/models/trace/insights/FontDisplay.js | title": {
2640
+ "message": "عرض الخط"
2641
+ },
2642
+ "node_modules/@paulirish/trace_engine/models/trace/insights/InteractionToNextPaint.js | description": {
2643
+ "message": "ابدأ بالتحقيق في المرحلة الأطول. [يمكن تقليل التأخيرات](https://web.dev/articles/optimize-inp#optimize_interactions). ولتقليل مدة المعالجة، [حسِّن تكاليف سلسلة التعليمات الرئيسية](https://web.dev/articles/optimize-long-tasks)، والتي تكون في الغالب بيانات JavaScript."
2644
+ },
2645
+ "node_modules/@paulirish/trace_engine/models/trace/insights/InteractionToNextPaint.js | title": {
2646
+ "message": "مدى استجابة الصفحة لتفاعلات المستخدم (INP) حسب المرحلة"
2647
+ },
2648
+ "node_modules/@paulirish/trace_engine/models/trace/insights/LCPDiscovery.js | description": {
2649
+ "message": "يمكنك تحسين مقياس سرعة عرض أكبر محتوى مرئي (LCP) من خلال جعل صورة مقياس LCP [قابلة للاكتشاف](https://web.dev/articles/optimize-lcp#1_eliminate_resource_load_delay) من HTML على الفور، و[تجنُّب التحميل الكسول](https://web.dev/articles/lcp-lazy-loading)."
2650
+ },
2651
+ "node_modules/@paulirish/trace_engine/models/trace/insights/LCPDiscovery.js | title": {
2652
+ "message": "الاطّلاع على طلبات LCP"
2653
+ },
2654
+ "node_modules/@paulirish/trace_engine/models/trace/insights/LCPPhases.js | description": {
2655
+ "message": "لكل [مرحلة استراتيجيات تحسين محدَّدة](https://web.dev/articles/optimize-lcp#lcp-breakdown). ومن الأفضل أن يتم قضاء معظم وقت سرعة عرض أكبر محتوى مرئي (LCP) في تحميل الموارد، وليس في التأخيرات."
2656
+ },
2657
+ "node_modules/@paulirish/trace_engine/models/trace/insights/LCPPhases.js | title": {
2658
+ "message": "سرعة عرض أكبر محتوى مرئي (LCP) حسب المرحلة"
2659
+ },
2660
+ "node_modules/@paulirish/trace_engine/models/trace/insights/RenderBlocking.js | description": {
2661
+ "message": "تؤدي الطلبات إلى حظر العرض الأوّلي للصفحة، ما قد يُبطئ سرعة عرض أكبر محتوى مرئي (LCP). ويمكن [تأجيل هذه الطلبات](https://web.dev/learn/performance/understanding-the-critical-path#render-blocking_resources/) أو تضمينها لإخراجها من المسار الحرج."
2662
+ },
2663
+ "node_modules/@paulirish/trace_engine/models/trace/insights/RenderBlocking.js | title": {
2664
+ "message": "طلبات حظر العرض"
2665
+ },
2666
+ "node_modules/@paulirish/trace_engine/models/trace/insights/SlowCSSSelector.js | description": {
2667
+ "message": "إذا ظلت تكاليف ميزة \"إعادة احتساب النمط\" مرتفعة، يمكن أن يؤدي تحسين أداة الاختيار إلى خفضها. يمكنك [تحسين أدوات الاختيار](https://developer.chrome.com/docs/devtools/performance/selector-stats) من خلال زيادة الوقت المنقضي وزيادة النسبة المئوية للمسار البطيء. وسيؤدي استخدام أدوات الاختيار الأبسط وعدد أدوات الاختيار الأقل ونموذج DOM الأصغر ونموذج DOM الأقل تعقيدًا إلى تقليل تكاليف المطابقة."
2668
+ },
2669
+ "node_modules/@paulirish/trace_engine/models/trace/insights/SlowCSSSelector.js | title": {
2670
+ "message": "تكاليف أداة اختيار لغة CSS"
2671
+ },
2672
+ "node_modules/@paulirish/trace_engine/models/trace/insights/ThirdParties.js | description": {
2673
+ "message": "يمكن أن يؤثر الرمز البرمجي التابع لجهة خارجية بشكل كبير في أداء التحميل. [يمكنك تقليل تحميل الرموز البرمجية التابعة لجهات خارجية وتأجيله](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/loading-third-party-javascript/)، ومنح الأولوية لمحتوى صفحتك."
2674
+ },
2675
+ "node_modules/@paulirish/trace_engine/models/trace/insights/ThirdParties.js | title": {
2676
+ "message": "الجهات الخارجية"
2677
+ },
2678
+ "node_modules/@paulirish/trace_engine/models/trace/insights/Viewport.js | description": {
2679
+ "message": "إنّ إطار عرض الصفحة غير محسّن للأجهزة الجوّالة، لذا قد يتم [تأخير التفاعلات التي تتم من خلال النقر بمقدار 300 ملي ثانية على الأكثر](https://developer.chrome.com/blog/300ms-tap-delay-gone-away/)."
2680
+ },
2681
+ "node_modules/@paulirish/trace_engine/models/trace/insights/Viewport.js | title": {
2682
+ "message": "إطار العرض غير محسَّن للأجهزة الجوّالة"
2683
+ },
2624
2684
  "node_modules/lighthouse-stack-packs/packs/amp.js | efficient-animated-content": {
2625
2685
  "message": "‏‮For‬‏ ‏‮animated‬‏ ‏‮content‬‏, ‏‮use‬‏ [`amp-anim`](https://amp.dev/documentation/components/amp-anim/) ‏‮to‬‏ ‏‮minimize‬‏ ‏‮CPU‬‏ ‏‮usage‬‏ ‏‮when‬‏ ‏‮the‬‏ ‏‮content‬‏ ‏‮is‬‏ ‏‮offscreen‬‏."
2626
2686
  },
@@ -2621,6 +2621,66 @@
2621
2621
  "flow-report/src/i18n/ui-strings.js | title": {
2622
2622
  "message": "تقرير تدفق المستخدمين في أداة Lighthouse"
2623
2623
  },
2624
+ "node_modules/@paulirish/trace_engine/models/trace/insights/CLSCulprits.js | description": {
2625
+ "message": "تحدث تغييرات التصميم عندما تتحرك العناصر بدون أي تدخُّل من المستخدم. [تحقَّق من أسباب تغييرات التصميم](https://web.dev/articles/optimize-cls)، مثل إضافة عناصر أو إزالتها أو تغيير خطوطها أثناء تحميل الصفحة."
2626
+ },
2627
+ "node_modules/@paulirish/trace_engine/models/trace/insights/CLSCulprits.js | title": {
2628
+ "message": "أسباب تغييرات التصميم"
2629
+ },
2630
+ "node_modules/@paulirish/trace_engine/models/trace/insights/DocumentLatency.js | description": {
2631
+ "message": "طلب الشبكة الأول هو الأهم. يمكنك تقليل وقت الاستجابة للطلب عن طريق تجنُّب عمليات إعادة التوجيه وضمان استجابة الخادم بسرعة وتفعيل ميزة ضغط النص."
2632
+ },
2633
+ "node_modules/@paulirish/trace_engine/models/trace/insights/DocumentLatency.js | title": {
2634
+ "message": "وقت الاستجابة لطلب المستند"
2635
+ },
2636
+ "node_modules/@paulirish/trace_engine/models/trace/insights/FontDisplay.js | description": {
2637
+ "message": "يمكنك ضبط [font-display](https://developer.chrome.com/blog/font-display) على swap أو optional لضمان ظهور النص بشكل ثابت. ويمكن تحسين swap بشكل أكبر للتخفيف من تغييرات التصميم باستخدام [إجراءات تجاوز مقاييس الخطوط](https://developer.chrome.com/blog/font-fallbacks)."
2638
+ },
2639
+ "node_modules/@paulirish/trace_engine/models/trace/insights/FontDisplay.js | title": {
2640
+ "message": "عرض الخط"
2641
+ },
2642
+ "node_modules/@paulirish/trace_engine/models/trace/insights/InteractionToNextPaint.js | description": {
2643
+ "message": "ابدأ بالتحقيق في المرحلة الأطول. [يمكن تقليل التأخيرات](https://web.dev/articles/optimize-inp#optimize_interactions). ولتقليل مدة المعالجة، [حسِّن تكاليف سلسلة التعليمات الرئيسية](https://web.dev/articles/optimize-long-tasks)، والتي تكون في الغالب بيانات JavaScript."
2644
+ },
2645
+ "node_modules/@paulirish/trace_engine/models/trace/insights/InteractionToNextPaint.js | title": {
2646
+ "message": "مدى استجابة الصفحة لتفاعلات المستخدم (INP) حسب المرحلة"
2647
+ },
2648
+ "node_modules/@paulirish/trace_engine/models/trace/insights/LCPDiscovery.js | description": {
2649
+ "message": "يمكنك تحسين مقياس سرعة عرض أكبر محتوى مرئي (LCP) من خلال جعل صورة مقياس LCP [قابلة للاكتشاف](https://web.dev/articles/optimize-lcp#1_eliminate_resource_load_delay) من HTML على الفور، و[تجنُّب التحميل الكسول](https://web.dev/articles/lcp-lazy-loading)."
2650
+ },
2651
+ "node_modules/@paulirish/trace_engine/models/trace/insights/LCPDiscovery.js | title": {
2652
+ "message": "الاطّلاع على طلبات LCP"
2653
+ },
2654
+ "node_modules/@paulirish/trace_engine/models/trace/insights/LCPPhases.js | description": {
2655
+ "message": "لكل [مرحلة استراتيجيات تحسين محدَّدة](https://web.dev/articles/optimize-lcp#lcp-breakdown). ومن الأفضل أن يتم قضاء معظم وقت سرعة عرض أكبر محتوى مرئي (LCP) في تحميل الموارد، وليس في التأخيرات."
2656
+ },
2657
+ "node_modules/@paulirish/trace_engine/models/trace/insights/LCPPhases.js | title": {
2658
+ "message": "سرعة عرض أكبر محتوى مرئي (LCP) حسب المرحلة"
2659
+ },
2660
+ "node_modules/@paulirish/trace_engine/models/trace/insights/RenderBlocking.js | description": {
2661
+ "message": "تؤدي الطلبات إلى حظر العرض الأوّلي للصفحة، ما قد يُبطئ سرعة عرض أكبر محتوى مرئي (LCP). ويمكن [تأجيل هذه الطلبات](https://web.dev/learn/performance/understanding-the-critical-path#render-blocking_resources/) أو تضمينها لإخراجها من المسار الحرج."
2662
+ },
2663
+ "node_modules/@paulirish/trace_engine/models/trace/insights/RenderBlocking.js | title": {
2664
+ "message": "طلبات حظر العرض"
2665
+ },
2666
+ "node_modules/@paulirish/trace_engine/models/trace/insights/SlowCSSSelector.js | description": {
2667
+ "message": "إذا ظلت تكاليف ميزة \"إعادة احتساب النمط\" مرتفعة، يمكن أن يؤدي تحسين أداة الاختيار إلى خفضها. يمكنك [تحسين أدوات الاختيار](https://developer.chrome.com/docs/devtools/performance/selector-stats) من خلال زيادة الوقت المنقضي وزيادة النسبة المئوية للمسار البطيء. وسيؤدي استخدام أدوات الاختيار الأبسط وعدد أدوات الاختيار الأقل ونموذج DOM الأصغر ونموذج DOM الأقل تعقيدًا إلى تقليل تكاليف المطابقة."
2668
+ },
2669
+ "node_modules/@paulirish/trace_engine/models/trace/insights/SlowCSSSelector.js | title": {
2670
+ "message": "تكاليف أداة اختيار لغة CSS"
2671
+ },
2672
+ "node_modules/@paulirish/trace_engine/models/trace/insights/ThirdParties.js | description": {
2673
+ "message": "يمكن أن يؤثر الرمز البرمجي التابع لجهة خارجية بشكل كبير في أداء التحميل. [يمكنك تقليل تحميل الرموز البرمجية التابعة لجهات خارجية وتأجيله](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/loading-third-party-javascript/)، ومنح الأولوية لمحتوى صفحتك."
2674
+ },
2675
+ "node_modules/@paulirish/trace_engine/models/trace/insights/ThirdParties.js | title": {
2676
+ "message": "الجهات الخارجية"
2677
+ },
2678
+ "node_modules/@paulirish/trace_engine/models/trace/insights/Viewport.js | description": {
2679
+ "message": "إنّ إطار عرض الصفحة غير محسّن للأجهزة الجوّالة، لذا قد يتم [تأخير التفاعلات التي تتم من خلال النقر بمقدار 300 ملي ثانية على الأكثر](https://developer.chrome.com/blog/300ms-tap-delay-gone-away/)."
2680
+ },
2681
+ "node_modules/@paulirish/trace_engine/models/trace/insights/Viewport.js | title": {
2682
+ "message": "إطار العرض غير محسَّن للأجهزة الجوّالة"
2683
+ },
2624
2684
  "node_modules/lighthouse-stack-packs/packs/amp.js | efficient-animated-content": {
2625
2685
  "message": "بالنسبة إلى المحتوى الذي يتضمن صورًا متحركة، يمكنك استخدام [`amp-anim`](https://amp.dev/documentation/components/amp-anim/) لتقليل استخدام وحدة المعالجة المركزية (CPU) عندما يكون المحتوى خارج الشاشة."
2626
2686
  },