@elastic/synthetics 1.0.0-beta.8 → 1.0.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.
Files changed (220) hide show
  1. package/README.md +5 -4
  2. package/dist/cli.js +139 -165
  3. package/dist/cli.js.map +1 -1
  4. package/dist/common_types.d.ts +158 -34
  5. package/dist/common_types.d.ts.map +1 -1
  6. package/dist/config.d.ts +1 -0
  7. package/dist/config.d.ts.map +1 -1
  8. package/dist/config.js +7 -6
  9. package/dist/config.js.map +1 -1
  10. package/dist/core/browser-service.d.ts +1 -1
  11. package/dist/core/browser-service.d.ts.map +1 -1
  12. package/dist/core/browser-service.js +7 -6
  13. package/dist/core/browser-service.js.map +1 -1
  14. package/dist/core/expect.d.ts +25 -25
  15. package/dist/core/expect.d.ts.map +1 -1
  16. package/dist/core/expect.js.map +1 -1
  17. package/dist/core/gatherer.d.ts +5 -8
  18. package/dist/core/gatherer.d.ts.map +1 -1
  19. package/dist/core/gatherer.js +75 -17
  20. package/dist/core/gatherer.js.map +1 -1
  21. package/dist/core/index.d.ts +5 -1
  22. package/dist/core/index.d.ts.map +1 -1
  23. package/dist/core/index.js +23 -11
  24. package/dist/core/index.js.map +1 -1
  25. package/dist/core/logger.d.ts +0 -1
  26. package/dist/core/logger.d.ts.map +1 -1
  27. package/dist/core/logger.js +10 -17
  28. package/dist/core/logger.js.map +1 -1
  29. package/dist/core/runner.d.ts +12 -63
  30. package/dist/core/runner.d.ts.map +1 -1
  31. package/dist/core/runner.js +159 -92
  32. package/dist/core/runner.js.map +1 -1
  33. package/dist/core/transform.d.ts +37 -0
  34. package/dist/core/transform.d.ts.map +1 -0
  35. package/dist/core/transform.js +148 -0
  36. package/dist/core/transform.js.map +1 -0
  37. package/dist/dsl/index.js +5 -1
  38. package/dist/dsl/index.js.map +1 -1
  39. package/dist/dsl/journey.d.ts +9 -7
  40. package/dist/dsl/journey.d.ts.map +1 -1
  41. package/dist/dsl/journey.js +32 -39
  42. package/dist/dsl/journey.js.map +1 -1
  43. package/dist/dsl/monitor.d.ts +93 -0
  44. package/dist/dsl/monitor.d.ts.map +1 -0
  45. package/dist/dsl/monitor.js +110 -0
  46. package/dist/dsl/monitor.js.map +1 -0
  47. package/dist/dsl/step.d.ts +3 -2
  48. package/dist/dsl/step.d.ts.map +1 -1
  49. package/dist/dsl/step.js +6 -1
  50. package/dist/dsl/step.js.map +1 -1
  51. package/dist/formatter/javascript.d.ts +115 -0
  52. package/dist/formatter/javascript.d.ts.map +1 -0
  53. package/dist/formatter/javascript.js +331 -0
  54. package/dist/formatter/javascript.js.map +1 -0
  55. package/dist/generator/index.d.ts +24 -0
  56. package/dist/generator/index.d.ts.map +1 -0
  57. package/dist/generator/index.js +241 -0
  58. package/dist/generator/index.js.map +1 -0
  59. package/dist/{reporters/reporter.js → generator/utils.d.ts} +5 -3
  60. package/dist/generator/utils.d.ts.map +1 -0
  61. package/dist/generator/utils.js +84 -0
  62. package/dist/generator/utils.js.map +1 -0
  63. package/dist/helpers.d.ts +36 -9
  64. package/dist/helpers.d.ts.map +1 -1
  65. package/dist/helpers.js +170 -24
  66. package/dist/helpers.js.map +1 -1
  67. package/dist/index.d.ts +5 -7
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +5 -26
  70. package/dist/index.js.map +1 -1
  71. package/dist/loader.d.ts +34 -0
  72. package/dist/loader.d.ts.map +1 -0
  73. package/dist/loader.js +153 -0
  74. package/dist/loader.js.map +1 -0
  75. package/dist/locations/index.d.ts +45 -0
  76. package/dist/locations/index.d.ts.map +1 -0
  77. package/dist/locations/index.js +83 -0
  78. package/dist/locations/index.js.map +1 -0
  79. package/dist/{reporters/reporter.d.ts → locations/public-locations.d.ts} +12 -8
  80. package/dist/locations/public-locations.d.ts.map +1 -0
  81. package/dist/locations/public-locations.js +41 -0
  82. package/dist/locations/public-locations.js.map +1 -0
  83. package/dist/options.d.ts +42 -0
  84. package/dist/options.d.ts.map +1 -0
  85. package/dist/options.js +146 -0
  86. package/dist/options.js.map +1 -0
  87. package/dist/plugins/browser-console.d.ts +5 -4
  88. package/dist/plugins/browser-console.d.ts.map +1 -1
  89. package/dist/plugins/browser-console.js +47 -24
  90. package/dist/plugins/browser-console.js.map +1 -1
  91. package/dist/plugins/index.js +5 -1
  92. package/dist/plugins/index.js.map +1 -1
  93. package/dist/plugins/network.d.ts +24 -31
  94. package/dist/plugins/network.d.ts.map +1 -1
  95. package/dist/plugins/network.js +226 -229
  96. package/dist/plugins/network.js.map +1 -1
  97. package/dist/plugins/performance.d.ts +4 -19
  98. package/dist/plugins/performance.d.ts.map +1 -1
  99. package/dist/plugins/performance.js +6 -5
  100. package/dist/plugins/performance.js.map +1 -1
  101. package/dist/plugins/plugin-manager.d.ts +9 -5
  102. package/dist/plugins/plugin-manager.d.ts.map +1 -1
  103. package/dist/plugins/plugin-manager.js +46 -30
  104. package/dist/plugins/plugin-manager.js.map +1 -1
  105. package/dist/plugins/tracing.d.ts +6 -6
  106. package/dist/plugins/tracing.d.ts.map +1 -1
  107. package/dist/plugins/tracing.js +13 -7
  108. package/dist/plugins/tracing.js.map +1 -1
  109. package/dist/push/bundler.d.ts +35 -0
  110. package/dist/push/bundler.d.ts.map +1 -0
  111. package/dist/push/bundler.js +131 -0
  112. package/dist/push/bundler.js.map +1 -0
  113. package/dist/push/index.d.ts +10 -0
  114. package/dist/push/index.d.ts.map +1 -0
  115. package/dist/push/index.js +235 -0
  116. package/dist/push/index.js.map +1 -0
  117. package/dist/push/kibana_api.d.ts +56 -0
  118. package/dist/push/kibana_api.d.ts.map +1 -0
  119. package/dist/push/kibana_api.js +135 -0
  120. package/dist/push/kibana_api.js.map +1 -0
  121. package/dist/push/monitor.d.ts +55 -0
  122. package/dist/push/monitor.d.ts.map +1 -0
  123. package/dist/push/monitor.js +268 -0
  124. package/dist/push/monitor.js.map +1 -0
  125. package/dist/{parse_args.d.ts → push/plugin.d.ts} +9 -6
  126. package/dist/push/plugin.d.ts.map +1 -0
  127. package/dist/push/plugin.js +66 -0
  128. package/dist/push/plugin.js.map +1 -0
  129. package/dist/push/request.d.ts +45 -0
  130. package/dist/push/request.d.ts.map +1 -0
  131. package/dist/push/request.js +109 -0
  132. package/dist/push/request.js.map +1 -0
  133. package/dist/push/utils.d.ts +31 -0
  134. package/dist/push/utils.d.ts.map +1 -0
  135. package/dist/push/utils.js +64 -0
  136. package/dist/push/utils.js.map +1 -0
  137. package/dist/reporters/base.d.ts +14 -6
  138. package/dist/reporters/base.d.ts.map +1 -1
  139. package/dist/reporters/base.js +58 -50
  140. package/dist/reporters/base.js.map +1 -1
  141. package/dist/reporters/index.d.ts +18 -8
  142. package/dist/reporters/index.d.ts.map +1 -1
  143. package/dist/reporters/index.js.map +1 -1
  144. package/dist/reporters/json.d.ts +17 -62
  145. package/dist/reporters/json.d.ts.map +1 -1
  146. package/dist/reporters/json.js +147 -177
  147. package/dist/reporters/json.js.map +1 -1
  148. package/dist/reporters/junit.d.ts +7 -1
  149. package/dist/reporters/junit.d.ts.map +1 -1
  150. package/dist/reporters/junit.js +90 -95
  151. package/dist/reporters/junit.js.map +1 -1
  152. package/dist/sdk/lh-trace-processor.d.ts +35 -9
  153. package/dist/sdk/lh-trace-processor.d.ts.map +1 -1
  154. package/dist/sdk/lh-trace-processor.js +114 -53
  155. package/dist/sdk/lh-trace-processor.js.map +1 -1
  156. package/dist/sdk/trace-metrics.d.ts +4 -4
  157. package/dist/sdk/trace-metrics.d.ts.map +1 -1
  158. package/dist/sdk/trace-metrics.js +3 -4
  159. package/dist/sdk/trace-metrics.js.map +1 -1
  160. package/dist/sdk/trace-processor.d.ts +11 -3
  161. package/dist/sdk/trace-processor.d.ts.map +1 -1
  162. package/dist/sdk/trace-processor.js +17 -9
  163. package/dist/sdk/trace-processor.js.map +1 -1
  164. package/package.json +38 -30
  165. package/src/cli.ts +223 -158
  166. package/src/common_types.ts +191 -34
  167. package/src/config.ts +1 -1
  168. package/src/core/expect.ts +58 -24
  169. package/src/core/gatherer.ts +92 -25
  170. package/src/core/index.ts +36 -15
  171. package/src/core/logger.ts +8 -12
  172. package/src/core/runner.ts +158 -148
  173. package/src/core/transform.ts +160 -0
  174. package/src/dsl/journey.ts +41 -21
  175. package/src/dsl/monitor.ts +152 -0
  176. package/src/dsl/step.ts +9 -2
  177. package/src/formatter/javascript.ts +414 -0
  178. package/src/generator/index.ts +308 -0
  179. package/src/generator/utils.ts +84 -0
  180. package/src/helpers.ts +182 -16
  181. package/src/index.ts +21 -22
  182. package/src/loader.ts +171 -0
  183. package/src/locations/index.ts +95 -0
  184. package/src/{reporters/reporter.ts → locations/public-locations.ts} +12 -9
  185. package/src/options.ts +175 -0
  186. package/src/plugins/browser-console.ts +33 -8
  187. package/src/plugins/network.ts +266 -284
  188. package/src/plugins/performance.ts +6 -22
  189. package/src/plugins/plugin-manager.ts +49 -31
  190. package/src/plugins/tracing.ts +11 -9
  191. package/src/push/bundler.ts +112 -0
  192. package/src/push/index.ts +291 -0
  193. package/src/push/kibana_api.ts +191 -0
  194. package/src/push/monitor.ts +307 -0
  195. package/src/push/plugin.ts +66 -0
  196. package/src/push/request.ts +141 -0
  197. package/src/push/utils.ts +77 -0
  198. package/src/reporters/base.ts +63 -52
  199. package/src/reporters/index.ts +26 -3
  200. package/src/reporters/json.ts +181 -201
  201. package/src/reporters/junit.ts +98 -93
  202. package/src/sdk/lh-trace-processor.ts +132 -68
  203. package/src/sdk/trace-metrics.ts +8 -4
  204. package/src/sdk/trace-processor.ts +28 -13
  205. package/templates/.github/workflows/run-synthetics.yml +27 -0
  206. package/templates/README.md +21 -0
  207. package/templates/journeys/advanced-example-helpers.ts +54 -0
  208. package/templates/journeys/advanced-example.journey.ts +43 -0
  209. package/templates/journeys/example.journey.ts +18 -0
  210. package/templates/lightweight/heartbeat.yml +9 -0
  211. package/templates/synthetics.config.ts +35 -0
  212. package/CHANGELOG.md +0 -181
  213. package/NOTICE.txt +0 -221
  214. package/dist/parse_args.d.ts.map +0 -1
  215. package/dist/parse_args.js +0 -98
  216. package/dist/parse_args.js.map +0 -1
  217. package/dist/reporters/reporter.d.ts.map +0 -1
  218. package/dist/reporters/reporter.js.map +0 -1
  219. package/dist/tsconfig.tsbuildinfo +0 -1895
  220. package/src/parse_args.ts +0 -146
@@ -23,8 +23,14 @@
23
23
  *
24
24
  */
25
25
 
26
- import { mkdirSync, writeFileSync } from 'fs';
26
+ import { mkdir, writeFile } from 'fs/promises';
27
27
  import { dirname } from 'path';
28
+ import {
29
+ JourneyEndResult,
30
+ JourneyStartResult,
31
+ StepEndResult,
32
+ } from '../common_types';
33
+ import { Journey, Step } from '../dsl';
28
34
  import { formatError, indent, now } from '../helpers';
29
35
  import BaseReporter from './base';
30
36
 
@@ -51,104 +57,103 @@ export default class JUnitReporter extends BaseReporter {
51
57
  private totalTests = 0;
52
58
  private totalFailures = 0;
53
59
  private totalSkipped = 0;
60
+ #journeyMap = new Map<string, XMLEntry>();
54
61
 
55
- _registerListeners() {
56
- const journeyMap = new Map<string, XMLEntry>();
62
+ override onJourneyStart(journey: Journey, {}: JourneyStartResult) {
63
+ if (!this.#journeyMap.has(journey.name)) {
64
+ const entry = {
65
+ name: 'testsuite',
66
+ attributes: {
67
+ name: journey.name,
68
+ tests: 0,
69
+ failures: 0,
70
+ skipped: 0,
71
+ errors: 0,
72
+ },
73
+ children: [],
74
+ };
75
+ this.#journeyMap.set(journey.name, entry);
76
+ }
77
+ }
57
78
 
58
- this.runner.on('journey:start', ({ journey }) => {
59
- if (!journeyMap.has(journey.name)) {
60
- const entry = {
61
- name: 'testsuite',
62
- attributes: {
63
- name: journey.name,
64
- tests: 0,
65
- failures: 0,
66
- skipped: 0,
67
- errors: 0,
68
- },
69
- children: [],
70
- };
71
- journeyMap.set(journey.name, entry);
72
- }
73
- });
79
+ override onStepEnd(
80
+ journey: Journey,
81
+ step: Step,
82
+ { status, error, start, end }: StepEndResult
83
+ ) {
84
+ if (!this.#journeyMap.has(journey.name)) {
85
+ return;
86
+ }
87
+ const entry = this.#journeyMap.get(journey.name);
88
+ const caseEntry = {
89
+ name: 'testcase',
90
+ attributes: {
91
+ name: step.name,
92
+ classname: journey.name + ' ' + step.name,
93
+ time: end - start,
94
+ },
95
+ children: [],
96
+ };
74
97
 
75
- this.runner.on(
76
- 'step:end',
77
- ({ journey, step, status, error, start, end }) => {
78
- if (!journeyMap.has(journey.name)) {
79
- return;
80
- }
81
- const entry = journeyMap.get(journey.name);
82
- const caseEntry = {
83
- name: 'testcase',
84
- attributes: {
85
- name: step.name,
86
- classname: journey.name + ' ' + step.name,
87
- time: end - start,
88
- },
89
- children: [],
90
- };
98
+ entry.attributes.tests++;
99
+ if (status === 'failed') {
100
+ const { name, message, stack } = formatError(error);
101
+ caseEntry.children.push({
102
+ name: 'failure',
103
+ attributes: {
104
+ message,
105
+ type: name,
106
+ },
107
+ text: stack,
108
+ });
109
+ entry.attributes.failures++;
110
+ } else if (status === 'skipped') {
111
+ caseEntry.children.push({
112
+ name: 'skipped',
113
+ attributes: {
114
+ message: 'previous step failed',
115
+ },
116
+ });
117
+ entry.attributes.skipped++;
118
+ }
119
+ entry.children.push(caseEntry);
120
+ }
91
121
 
92
- entry.attributes.tests++;
93
- if (status === 'failed') {
94
- const { name, message, stack } = formatError(error);
95
- caseEntry.children.push({
96
- name: 'failure',
97
- attributes: {
98
- message,
99
- type: name,
100
- },
101
- text: stack,
102
- });
103
- entry.attributes.failures++;
104
- } else if (status === 'skipped') {
105
- caseEntry.children.push({
106
- name: 'skipped',
107
- attributes: {
108
- message: 'previous step failed',
109
- },
110
- });
111
- entry.attributes.skipped++;
112
- }
113
- entry.children.push(caseEntry);
114
- }
115
- );
122
+ override onJourneyEnd(journey: Journey, {}: JourneyEndResult) {
123
+ if (!this.#journeyMap.has(journey.name)) {
124
+ return;
125
+ }
126
+ const { attributes } = this.#journeyMap.get(journey.name);
127
+ this.totalTests += attributes.tests;
128
+ this.totalFailures += attributes.failures;
129
+ this.totalSkipped += attributes.skipped;
130
+ }
116
131
 
117
- this.runner.on('journey:end', ({ journey }) => {
118
- if (!journeyMap.has(journey.name)) {
119
- return;
120
- }
121
- const { attributes } = journeyMap.get(journey.name);
122
- this.totalTests += attributes.tests;
123
- this.totalFailures += attributes.failures;
124
- this.totalSkipped += attributes.skipped;
125
- });
132
+ override async onEnd() {
133
+ const root: XMLEntry = {
134
+ name: 'testsuites',
135
+ attributes: {
136
+ name: '',
137
+ tests: this.totalTests,
138
+ failures: this.totalFailures,
139
+ skipped: this.totalSkipped,
140
+ errors: 0,
141
+ time: parseInt(String(now())) / 1000,
142
+ },
143
+ children: [...this.#journeyMap.values()],
144
+ };
126
145
 
127
- this.runner.on('end', () => {
128
- const root: XMLEntry = {
129
- name: 'testsuites',
130
- attributes: {
131
- name: '',
132
- tests: this.totalTests,
133
- failures: this.totalFailures,
134
- skipped: this.totalSkipped,
135
- errors: 0,
136
- time: parseInt(String(now())) / 1000,
137
- },
138
- children: [...journeyMap.values()],
139
- };
140
- const output = serializeEntries(root).join('\n');
141
- /**
142
- * write the xml output to a file if specified via env flag
143
- */
144
- const fileName = process.env['SYNTHETICS_JUNIT_FILE'];
145
- if (fileName) {
146
- mkdirSync(dirname(fileName), { recursive: true });
147
- writeFileSync(fileName, output);
148
- } else {
149
- this.write(output);
150
- }
151
- });
146
+ const output = serializeEntries(root).join('\n');
147
+ /**
148
+ * write the xml output to a file if specified via env flag
149
+ */
150
+ const fileName = process.env['SYNTHETICS_JUNIT_FILE'];
151
+ if (fileName) {
152
+ await mkdir(dirname(fileName), { recursive: true });
153
+ await writeFile(fileName, output);
154
+ } else {
155
+ this.write(output);
156
+ }
152
157
  }
153
158
  }
154
159
 
@@ -16,10 +16,8 @@
16
16
  * 4. Return all those items in one handy bundle.
17
17
  */
18
18
 
19
- /** @typedef {Omit<LH.Artifacts.TraceTimes, 'firstContentfulPaint'|'firstContentfulPaintAllFrames'> & {firstContentfulPaint?: number, firstContentfulPaintAllFrames?: number}} TraceTimesWithoutFCP */
20
- /** @typedef {Omit<TraceTimesWithoutFCP, 'traceEnd'>} TraceTimesWithoutFCPAndTraceEnd */
21
- /** @typedef {Omit<LH.Artifacts.TraceOfTab, 'firstContentfulPaintEvt'|'firstContentfulPaintAllFramesEvt'|'timings'|'timestamps'> & {timings: TraceTimesWithoutFCP, timestamps: TraceTimesWithoutFCP, firstContentfulPaintEvt?: LH.Artifacts.TraceOfTab['firstContentfulPaintEvt'], firstContentfulPaintAllFramesEvt?: LH.Artifacts.TraceOfTab['largestContentfulPaintAllFramesEvt']}} TraceOfTabWithoutFCP */
22
- /** @typedef {'lastNavigationStart'|'firstResourceSendRequest'} TimeOriginDeterminationMethod */
19
+ /** @typedef {Omit<LH.Artifacts.NavigationTraceTimes, 'firstContentfulPaintAllFrames'|'traceEnd'>} TraceNavigationTimesForFrame */
20
+ /** @typedef {'lastNavigationStart'|'firstResourceSendRequest'|'lighthouseMarker'|'auto'} TimeOriginDeterminationMethod */
23
21
  /** @typedef {Omit<LH.TraceEvent, 'name'|'args'> & {name: 'FrameCommittedInBrowser', args: {data: {frame: string, url: string, parent?: string}}}} FrameCommittedEvent */
24
22
  /** @typedef {Omit<LH.TraceEvent, 'name'|'args'> & {name: 'largestContentfulPaint::Invalidate'|'largestContentfulPaint::Candidate', args: {data?: {size?: number}, frame: string}}} LCPEvent */
25
23
  /** @typedef {Omit<LH.TraceEvent, 'name'|'args'> & {name: 'largestContentfulPaint::Candidate', args: {data: {size: number}, frame: string}}} LCPCandidateEvent */
@@ -42,6 +40,10 @@ const SCHEDULABLE_TASK_TITLE_ALT3 =
42
40
  'TaskQueueManager::ProcessTaskFromWorkQueue';
43
41
 
44
42
  class TraceProcessor {
43
+ static get TIMESPAN_MARKER_ID() {
44
+ return '__lighthouseTimespanStart__';
45
+ }
46
+
45
47
  /**
46
48
  * @return {Error}
47
49
  */
@@ -63,6 +65,20 @@ class TraceProcessor {
63
65
  return new Error('No tracingStartedInBrowser event found');
64
66
  }
65
67
 
68
+ /**
69
+ * @return {Error}
70
+ */
71
+ static createNoFirstContentfulPaintError() {
72
+ return new Error('No FirstContentfulPaint event found');
73
+ }
74
+
75
+ /**
76
+ * @return {Error}
77
+ */
78
+ static createNoLighthouseMarkerError() {
79
+ return new Error('No Lighthouse timespan marker event found');
80
+ }
81
+
66
82
  /**
67
83
  * Returns true if the event is a navigation start event of a document whose URL seems valid.
68
84
  *
@@ -101,16 +117,16 @@ class TraceProcessor {
101
117
  traceEvents
102
118
  ) {
103
119
  /*
104
- * We have two different sets of indices going on here.
105
-
106
- * 1. There's the index for an element of `traceEvents`, referred to here as an `ArrayIndex`.
107
- * `timestampSortedIndices` is an array of `ArrayIndex` elements.
108
- * 2. There's the index for an element of `timestampSortedIndices`, referred to here as a `TsIndex`.
109
- * A `TsIndex` is therefore an index to an element which is itself an index.
110
- *
111
- * These two helper functions help resolve this layer of indirection.
112
- * Our final return value is an array of `ArrayIndex` in their final sort order.
113
- */
120
+ * We have two different sets of indices going on here.
121
+
122
+ * 1. There's the index for an element of `traceEvents`, referred to here as an `ArrayIndex`.
123
+ * `timestampSortedIndices` is an array of `ArrayIndex` elements.
124
+ * 2. There's the index for an element of `timestampSortedIndices`, referred to here as a `TsIndex`.
125
+ * A `TsIndex` is therefore an index to an element which is itself an index.
126
+ *
127
+ * These two helper functions help resolve this layer of indirection.
128
+ * Our final return value is an array of `ArrayIndex` in their final sort order.
129
+ */
114
130
  /** @param {number} i */
115
131
  const lookupArrayIndexByTsIndex = i => timestampSortedIndices[i];
116
132
  /** @param {number} i */
@@ -406,25 +422,21 @@ class TraceProcessor {
406
422
  }
407
423
 
408
424
  /**
409
- * Provides the top level events on the main thread with timestamps in ms relative to navigation
425
+ * Provides the top level events on the main thread with timestamps in ms relative to timeOrigin.
410
426
  * start.
411
- * @param {LH.Artifacts.TraceOfTab} tabTrace
427
+ * @param {LH.Artifacts.ProcessedTrace} trace
412
428
  * @param {number=} startTime Optional start time (in ms relative to timeOrigin) of range of interest. Defaults to 0.
413
429
  * @param {number=} endTime Optional end time (in ms relative to timeOrigin) of range of interest. Defaults to trace end.
414
430
  * @return {Array<ToplevelEvent>}
415
431
  */
416
- static getMainThreadTopLevelEvents(
417
- tabTrace,
418
- startTime = 0,
419
- endTime = Infinity
420
- ) {
432
+ static getMainThreadTopLevelEvents(trace, startTime = 0, endTime = Infinity) {
421
433
  const topLevelEvents = [];
422
434
  // note: mainThreadEvents is already sorted by event start
423
- for (const event of tabTrace.mainThreadEvents) {
435
+ for (const event of trace.mainThreadEvents) {
424
436
  if (!this.isScheduleableTask(event) || !event.dur) continue;
425
437
 
426
- const start = (event.ts - tabTrace.timeOriginEvt.ts) / 1000;
427
- const end = (event.ts + event.dur - tabTrace.timeOriginEvt.ts) / 1000;
438
+ const start = (event.ts - trace.timeOriginEvt.ts) / 1000;
439
+ const end = (event.ts + event.dur - trace.timeOriginEvt.ts) / 1000;
428
440
  if (start > endTime || end < startTime) continue;
429
441
 
430
442
  topLevelEvents.push({
@@ -651,11 +663,10 @@ class TraceProcessor {
651
663
  * in milliseconds since the time origin in addition to the standard microsecond monotonic timestamps.
652
664
  * @param {LH.Trace} trace
653
665
  * @param {{timeOriginDeterminationMethod?: TimeOriginDeterminationMethod}} [options]
654
- * @return {TraceOfTabWithoutFCP}
666
+ * @return {LH.Artifacts.ProcessedTrace}
655
667
  */
656
- static computeTraceOfTab(trace, options) {
657
- const { timeOriginDeterminationMethod = 'lastNavigationStart' } =
658
- options || {};
668
+ static processTrace(trace, options) {
669
+ const { timeOriginDeterminationMethod = 'auto' } = options || {};
659
670
 
660
671
  // Parse the trace for our key events and sort them by timestamp. Note: sort
661
672
  // *must* be stable to keep events correctly nested.
@@ -722,22 +733,6 @@ class TraceProcessor {
722
733
  timeOriginDeterminationMethod
723
734
  );
724
735
 
725
- // Compute the key frame timings for the main frame.
726
- const frameTimings = this.computeKeyTimingsForFrame(frameEvents, {
727
- timeOriginEvt,
728
- });
729
-
730
- // Compute FCP for all frames.
731
- const fcpAllFramesEvt = frameTreeEvents.find(
732
- e => e.name === 'firstContentfulPaint' && e.ts > timeOriginEvt.ts
733
- );
734
-
735
- // Compute LCP for all frames.
736
- const lcpAllFramesEvt = this.computeValidLCPAllFrames(
737
- frameTreeEvents,
738
- timeOriginEvt
739
- ).lcp;
740
-
741
736
  // Subset all trace events to just our tab's process (incl threads other than main)
742
737
  // stable-sort events to keep them correctly nested.
743
738
  const processEvents = TraceProcessor.filteredTraceSort(
@@ -752,46 +747,89 @@ class TraceProcessor {
752
747
  // Ensure our traceEnd reflects all page activity.
753
748
  const traceEnd = this.computeTraceEnd(trace.traceEvents, timeOriginEvt);
754
749
 
755
- /** @param {number|undefined} ts */
756
- const maybeGetTiming = ts =>
757
- ts === undefined ? undefined : (ts - timeOriginEvt.ts) / 1000;
758
750
  // This could be much more concise with object spread, but the consensus is that explicitness is
759
751
  // preferred over brevity here.
760
752
  return {
761
753
  frames,
762
754
  mainThreadEvents,
755
+ frameEvents,
763
756
  frameTreeEvents,
764
757
  processEvents,
765
758
  mainFrameIds,
759
+ timeOriginEvt,
766
760
  timings: {
767
- timeOrigin: frameTimings.timings.timeOrigin,
761
+ timeOrigin: 0,
762
+ traceEnd: traceEnd.timing,
763
+ },
764
+ timestamps: {
765
+ timeOrigin: timeOriginEvt.ts,
766
+ traceEnd: traceEnd.timestamp,
767
+ },
768
+ };
769
+ }
770
+
771
+ /**
772
+ * Finds key navigation trace events and computes timings of events in milliseconds since the time
773
+ * origin in addition to the standard microsecond monotonic timestamps.
774
+ * @param {LH.Artifacts.ProcessedTrace} processedTrace
775
+ * @return {LH.Artifacts.ProcessedNavigation}
776
+ */
777
+ static processNavigation(processedTrace) {
778
+ const { frameEvents, frameTreeEvents, timeOriginEvt, timings, timestamps } =
779
+ processedTrace;
780
+
781
+ // Compute the key frame timings for the main frame.
782
+ const frameTimings = this.computeNavigationTimingsForFrame(frameEvents, {
783
+ timeOriginEvt,
784
+ });
785
+
786
+ // Compute FCP for all frames.
787
+ const fcpAllFramesEvt = frameTreeEvents.find(
788
+ e => e.name === 'firstContentfulPaint' && e.ts > timeOriginEvt.ts
789
+ );
790
+
791
+ if (!fcpAllFramesEvt) {
792
+ throw this.createNoFirstContentfulPaintError();
793
+ }
794
+
795
+ // Compute LCP for all frames.
796
+ const lcpAllFramesEvt = this.computeValidLCPAllFrames(
797
+ frameTreeEvents,
798
+ timeOriginEvt
799
+ ).lcp;
800
+
801
+ /** @param {number} ts */
802
+ const getTiming = ts => (ts - timeOriginEvt.ts) / 1000;
803
+ /** @param {number=} ts */
804
+ const maybeGetTiming = ts => (ts === undefined ? undefined : getTiming(ts));
805
+
806
+ return {
807
+ timings: {
808
+ timeOrigin: timings.timeOrigin,
768
809
  firstPaint: frameTimings.timings.firstPaint,
769
810
  firstContentfulPaint: frameTimings.timings.firstContentfulPaint,
770
- firstContentfulPaintAllFrames: maybeGetTiming(
771
- fcpAllFramesEvt && fcpAllFramesEvt.ts
772
- ),
811
+ firstContentfulPaintAllFrames: getTiming(fcpAllFramesEvt.ts),
773
812
  firstMeaningfulPaint: frameTimings.timings.firstMeaningfulPaint,
774
813
  largestContentfulPaint: frameTimings.timings.largestContentfulPaint,
775
814
  largestContentfulPaintAllFrames: maybeGetTiming(
776
815
  lcpAllFramesEvt && lcpAllFramesEvt.ts
777
816
  ),
778
- traceEnd: traceEnd.timing,
779
817
  load: frameTimings.timings.load,
780
818
  domContentLoaded: frameTimings.timings.domContentLoaded,
819
+ traceEnd: timings.traceEnd,
781
820
  },
782
821
  timestamps: {
783
- timeOrigin: frameTimings.timestamps.timeOrigin,
822
+ timeOrigin: timestamps.timeOrigin,
784
823
  firstPaint: frameTimings.timestamps.firstPaint,
785
824
  firstContentfulPaint: frameTimings.timestamps.firstContentfulPaint,
786
- firstContentfulPaintAllFrames: fcpAllFramesEvt && fcpAllFramesEvt.ts,
825
+ firstContentfulPaintAllFrames: fcpAllFramesEvt.ts,
787
826
  firstMeaningfulPaint: frameTimings.timestamps.firstMeaningfulPaint,
788
827
  largestContentfulPaint: frameTimings.timestamps.largestContentfulPaint,
789
828
  largestContentfulPaintAllFrames: lcpAllFramesEvt && lcpAllFramesEvt.ts,
790
- traceEnd: traceEnd.timestamp,
791
829
  load: frameTimings.timestamps.load,
792
830
  domContentLoaded: frameTimings.timestamps.domContentLoaded,
831
+ traceEnd: timestamps.traceEnd,
793
832
  },
794
- timeOriginEvt: frameTimings.timeOriginEvt,
795
833
  firstPaintEvt: frameTimings.firstPaintEvt,
796
834
  firstContentfulPaintEvt: frameTimings.firstContentfulPaintEvt,
797
835
  firstContentfulPaintAllFramesEvt: fcpAllFramesEvt,
@@ -843,6 +881,21 @@ class TraceProcessor {
843
881
  * @return {LH.TraceEvent}
844
882
  */
845
883
  static computeTimeOrigin(traceEventSubsets, method) {
884
+ const lastNavigationStart = () => {
885
+ // Our time origin will be the last frame navigation in the trace
886
+ const frameEvents = traceEventSubsets.frameEvents;
887
+ return frameEvents.filter(this._isNavigationStartOfInterest).pop();
888
+ };
889
+
890
+ const lighthouseMarker = () => {
891
+ const frameEvents = traceEventSubsets.keyEvents;
892
+ return frameEvents.find(
893
+ evt =>
894
+ evt.name === 'clock_sync' &&
895
+ evt.args.sync_id === TraceProcessor.TIMESPAN_MARKER_ID
896
+ );
897
+ };
898
+
846
899
  switch (method) {
847
900
  case 'firstResourceSendRequest': {
848
901
  // Our time origin will be the timestamp of the first request that's sent in the frame.
@@ -855,14 +908,20 @@ class TraceProcessor {
855
908
  return fetchStart;
856
909
  }
857
910
  case 'lastNavigationStart': {
858
- // Our time origin will be the last frame navigation in the trace
859
- const frameEvents = traceEventSubsets.frameEvents;
860
- const navigationStart = frameEvents
861
- .filter(this._isNavigationStartOfInterest)
862
- .pop();
911
+ const navigationStart = lastNavigationStart();
863
912
  if (!navigationStart) throw this.createNoNavstartError();
864
913
  return navigationStart;
865
914
  }
915
+ case 'lighthouseMarker': {
916
+ const marker = lighthouseMarker();
917
+ if (!marker) throw this.createNoLighthouseMarkerError();
918
+ return marker;
919
+ }
920
+ case 'auto': {
921
+ const marker = lighthouseMarker() || lastNavigationStart();
922
+ if (!marker) throw this.createNoNavstartError();
923
+ return marker;
924
+ }
866
925
  }
867
926
  }
868
927
 
@@ -872,7 +931,7 @@ class TraceProcessor {
872
931
  * @param {Array<LH.TraceEvent>} frameEvents
873
932
  * @param {{timeOriginEvt: LH.TraceEvent}} options
874
933
  */
875
- static computeKeyTimingsForFrame(frameEvents, options) {
934
+ static computeNavigationTimingsForFrame(frameEvents, options) {
876
935
  const { timeOriginEvt } = options;
877
936
 
878
937
  // Find our first paint of this frame
@@ -885,6 +944,10 @@ class TraceProcessor {
885
944
  e => e.name === 'firstContentfulPaint' && e.ts > timeOriginEvt.ts
886
945
  );
887
946
 
947
+ if (!firstContentfulPaint) {
948
+ throw this.createNoFirstContentfulPaintError();
949
+ }
950
+
888
951
  // fMP will follow at/after the FP
889
952
  let firstMeaningfulPaint = frameEvents.find(
890
953
  e => e.name === 'firstMeaningfulPaint' && e.ts > timeOriginEvt.ts
@@ -918,25 +981,26 @@ class TraceProcessor {
918
981
 
919
982
  /** @param {{ts: number}=} event */
920
983
  const getTimestamp = event => event && event.ts;
921
- /** @type {TraceTimesWithoutFCPAndTraceEnd} */
984
+ /** @type {TraceNavigationTimesForFrame} */
922
985
  const timestamps = {
923
986
  timeOrigin: timeOriginEvt.ts,
924
987
  firstPaint: getTimestamp(firstPaint),
925
- firstContentfulPaint: getTimestamp(firstContentfulPaint),
988
+ firstContentfulPaint: firstContentfulPaint.ts,
926
989
  firstMeaningfulPaint: getTimestamp(firstMeaningfulPaint),
927
990
  largestContentfulPaint: getTimestamp(lcpResult.lcp),
928
991
  load: getTimestamp(load),
929
992
  domContentLoaded: getTimestamp(domContentLoaded),
930
993
  };
931
994
 
995
+ /** @param {number} ts */
996
+ const getTiming = ts => (ts - timeOriginEvt.ts) / 1000;
932
997
  /** @param {number=} ts */
933
- const maybeGetTiming = ts =>
934
- ts === undefined ? undefined : (ts - timeOriginEvt.ts) / 1000;
935
- /** @type {TraceTimesWithoutFCPAndTraceEnd} */
998
+ const maybeGetTiming = ts => (ts === undefined ? undefined : getTiming(ts));
999
+ /** @type {TraceNavigationTimesForFrame} */
936
1000
  const timings = {
937
1001
  timeOrigin: 0,
938
1002
  firstPaint: maybeGetTiming(timestamps.firstPaint),
939
- firstContentfulPaint: maybeGetTiming(timestamps.firstContentfulPaint),
1003
+ firstContentfulPaint: getTiming(timestamps.firstContentfulPaint),
940
1004
  firstMeaningfulPaint: maybeGetTiming(timestamps.firstMeaningfulPaint),
941
1005
  largestContentfulPaint: maybeGetTiming(timestamps.largestContentfulPaint),
942
1006
  load: maybeGetTiming(timestamps.load),
@@ -24,10 +24,14 @@
24
24
  */
25
25
 
26
26
  import { Filmstrip, TraceOutput } from '../common_types';
27
- import type { LHTrace, TraceEvent } from './trace-processor';
27
+ import type {
28
+ LHProcessedNavigation,
29
+ LHProcessedTrace,
30
+ TraceEvent,
31
+ } from './trace-processor';
28
32
 
29
33
  export class UserTimings {
30
- static compute(trace: LHTrace) {
34
+ static compute(trace: LHProcessedTrace) {
31
35
  const { processEvents } = trace;
32
36
  const measuresMap = new Map();
33
37
  const userTimings: Array<TraceOutput> = [];
@@ -96,7 +100,7 @@ export class ExperienceMetrics {
96
100
  };
97
101
  }
98
102
 
99
- static compute(trace: LHTrace) {
103
+ static compute(trace: LHProcessedNavigation) {
100
104
  const traces: Array<TraceOutput> = [];
101
105
  const { timestamps, timings, lcpInvalidated } = trace;
102
106
 
@@ -226,7 +230,7 @@ export class CumulativeLayoutShift {
226
230
  return maxScore;
227
231
  }
228
232
 
229
- static compute(trace: LHTrace) {
233
+ static compute(trace: LHProcessedTrace) {
230
234
  const layoutShiftEvents = this.getLayoutShiftEvents(trace.frameTreeEvents);
231
235
  const traces: Array<TraceOutput> = layoutShiftEvents.map(event => {
232
236
  return {
@@ -23,13 +23,14 @@
23
23
  *
24
24
  */
25
25
 
26
- import { PerfMetrics } from '../common_types';
27
26
  import LighthouseTraceProcessor from './lh-trace-processor';
28
27
  import {
29
28
  ExperienceMetrics,
30
29
  CumulativeLayoutShift,
31
30
  UserTimings,
32
31
  } from './trace-metrics';
32
+ import { PerfMetrics } from '../common_types';
33
+ import { log } from '../core/logger';
33
34
 
34
35
  const ACCEPTABLE_NAVIGATION_URL_REGEX = /^(file|https?):/;
35
36
 
@@ -81,16 +82,22 @@ type LHTraceTime = {
81
82
  traceEnd: number;
82
83
  };
83
84
 
84
- export type LHTrace = {
85
+ export type LHProcessedNavigation = {
85
86
  domContentLoadedEvt: TraceEvent;
86
87
  firstContentfulPaintEvt: TraceEvent;
87
88
  largestContentfulPaintEvt: TraceEvent;
88
89
  loadEvt: TraceEvent;
89
- timeOriginEvt: TraceEvent;
90
+ timestamps: LHTraceTime;
91
+ timings: LHTraceTime;
90
92
  lcpInvalidated: boolean;
93
+ };
94
+
95
+ export type LHProcessedTrace = {
91
96
  processEvents: Array<TraceEvent>;
97
+ frameEvents: Array<TraceEvent>;
92
98
  mainThreadEvents: Array<TraceEvent>;
93
99
  frameTreeEvents: Array<TraceEvent>;
100
+ timeOriginEvt: TraceEvent;
94
101
  timestamps: Partial<LHTraceTime>;
95
102
  timings: Partial<LHTraceTime>;
96
103
  };
@@ -104,7 +111,7 @@ export type LHTrace = {
104
111
  * metrics based on multiple navigations instead of a single navigation.
105
112
  */
106
113
  export class TraceProcessor extends LighthouseTraceProcessor {
107
- static _isNavigationStartOfInterest(event) {
114
+ static override _isNavigationStartOfInterest(event) {
108
115
  return (
109
116
  event.name === 'navigationStart' &&
110
117
  (!event.args.data ||
@@ -118,15 +125,23 @@ export class TraceProcessor extends LighthouseTraceProcessor {
118
125
  const options = {
119
126
  timeOriginDeterminationMethod: 'lastNavigationStart',
120
127
  };
121
- const trace = super.computeTraceOfTab({ traceEvents }, options);
122
- const userTiming = UserTimings.compute(trace);
123
- const { traces: expTraces, metrics } = ExperienceMetrics.compute(trace);
124
- const { cls, traces: layoutTraces } = CumulativeLayoutShift.compute(trace);
125
- const perfMetrics: Partial<PerfMetrics> = { cls, ...metrics };
128
+ try {
129
+ const processedTrace = this.processTrace({ traceEvents }, options);
130
+ const processedNavigation = this.processNavigation(processedTrace);
131
+ const userTiming = UserTimings.compute(processedTrace);
132
+ const { traces: expTraces, metrics } =
133
+ ExperienceMetrics.compute(processedNavigation);
134
+ const { cls, traces: layoutTraces } =
135
+ CumulativeLayoutShift.compute(processedTrace);
136
+ const perfMetrics: Partial<PerfMetrics> = { cls, ...metrics };
126
137
 
127
- return {
128
- traces: userTiming.concat(expTraces, layoutTraces),
129
- metrics: perfMetrics,
130
- };
138
+ return {
139
+ traces: userTiming.concat(expTraces, layoutTraces),
140
+ metrics: perfMetrics,
141
+ };
142
+ } catch (e) {
143
+ log(e);
144
+ return {};
145
+ }
131
146
  }
132
147
  }