@jsenv/core 28.0.2 → 28.1.2

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 (55) hide show
  1. package/dist/controllable_child_process.mjs +1 -2
  2. package/dist/controllable_worker_thread.mjs +1 -2
  3. package/dist/js/autoreload.js +25 -9
  4. package/dist/js/execute_using_dynamic_import.js +804 -1
  5. package/dist/js/script_type_module_supervisor.js +122 -0
  6. package/dist/js/supervisor.js +915 -0
  7. package/dist/main.js +432 -492
  8. package/package.json +13 -13
  9. package/readme.md +1 -1
  10. package/src/build/inject_global_version_mappings.js +3 -3
  11. package/src/dev/start_dev_server.js +2 -2
  12. package/src/execute/execute.js +1 -1
  13. package/src/execute/run.js +26 -38
  14. package/src/execute/runtimes/browsers/from_playwright.js +51 -77
  15. package/src/execute/runtimes/node/node_child_process.js +36 -36
  16. package/src/execute/runtimes/node/node_worker_thread.js +36 -36
  17. package/src/omega/kitchen.js +12 -9
  18. package/src/omega/omega_server.js +2 -2
  19. package/src/omega/server/file_service.js +2 -2
  20. package/src/omega/url_graph/url_info_transformations.js +8 -1
  21. package/src/plugins/autoreload/client/reload.js +20 -7
  22. package/src/plugins/autoreload/jsenv_plugin_autoreload_client.js +4 -4
  23. package/src/plugins/import_meta_hot/html_hot_dependencies.js +2 -2
  24. package/src/plugins/importmap/jsenv_plugin_importmap.js +5 -3
  25. package/src/plugins/inject_globals/inject_globals.js +3 -3
  26. package/src/plugins/inline/jsenv_plugin_data_urls.js +1 -1
  27. package/src/plugins/inline/jsenv_plugin_html_inline_content.js +10 -5
  28. package/src/plugins/plugins.js +5 -5
  29. package/src/plugins/server_events/jsenv_plugin_server_events_client_injection.js +4 -4
  30. package/src/plugins/supervisor/client/script_type_module_supervisor.js +99 -0
  31. package/src/plugins/supervisor/client/supervisor.js +915 -0
  32. package/src/plugins/{html_supervisor/jsenv_plugin_html_supervisor.js → supervisor/jsenv_plugin_supervisor.js} +128 -102
  33. package/src/plugins/toolbar/client/execution/toolbar_execution.js +1 -1
  34. package/src/plugins/toolbar/jsenv_plugin_toolbar.js +4 -4
  35. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic.js +7 -5
  36. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic_html.js +5 -4
  37. package/src/plugins/transpilation/babel/jsenv_plugin_babel.js +13 -7
  38. package/src/plugins/transpilation/babel/new_stylesheet/babel_plugin_new_stylesheet_as_jsenv_import.js +6 -4
  39. package/src/plugins/transpilation/jsenv_plugin_transpilation.js +4 -2
  40. package/src/plugins/url_analysis/html/html_urls.js +11 -10
  41. package/src/test/coverage/babel_plugin_instrument.js +1 -35
  42. package/src/test/coverage/empty_coverage_factory.js +1 -1
  43. package/src/test/execute_plan.js +7 -3
  44. package/src/test/execute_test_plan.js +2 -1
  45. package/src/test/logs_file_execution.js +49 -8
  46. package/dist/js/html_supervisor_installer.js +0 -1091
  47. package/dist/js/html_supervisor_setup.js +0 -89
  48. package/dist/js/uneval.js +0 -804
  49. package/src/plugins/html_supervisor/client/error_formatter.js +0 -426
  50. package/src/plugins/html_supervisor/client/error_in_notification.js +0 -21
  51. package/src/plugins/html_supervisor/client/error_overlay.js +0 -191
  52. package/src/plugins/html_supervisor/client/html_supervisor_installer.js +0 -315
  53. package/src/plugins/html_supervisor/client/html_supervisor_setup.js +0 -89
  54. package/src/plugins/html_supervisor/client/perf_browser.js +0 -17
  55. package/src/plugins/html_supervisor/client/uneval_exception.js +0 -8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/core",
3
- "version": "28.0.2",
3
+ "version": "28.1.2",
4
4
  "description": "Tool to develop, test and build js projects",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -66,14 +66,14 @@
66
66
  "@c88/v8-coverage": "0.1.1",
67
67
  "@financial-times/polyfill-useragent-normaliser": "2.0.1",
68
68
  "@jsenv/abort": "4.2.3",
69
- "@jsenv/ast": "1.1.3",
69
+ "@jsenv/ast": "1.2.0",
70
70
  "@jsenv/babel-plugins": "1.0.6",
71
- "@jsenv/filesystem": "4.1.1",
71
+ "@jsenv/filesystem": "4.1.2",
72
72
  "@jsenv/importmap": "1.2.1",
73
73
  "@jsenv/integrity": "0.0.1",
74
- "@jsenv/log": "3.1.0",
74
+ "@jsenv/log": "3.2.0",
75
75
  "@jsenv/node-esm-resolution": "0.1.0",
76
- "@jsenv/server": "14.1.0",
76
+ "@jsenv/server": "14.1.1",
77
77
  "@jsenv/sourcemap": "1.0.4",
78
78
  "@jsenv/uneval": "1.6.0",
79
79
  "@jsenv/url-meta": "7.0.0",
@@ -87,9 +87,9 @@
87
87
  "istanbul-lib-instrument": "5.2.0",
88
88
  "istanbul-lib-report": "3.0.0",
89
89
  "istanbul-reports": "3.1.5",
90
- "launch-editor": "2.4.0",
90
+ "launch-editor": "2.5.0",
91
91
  "pidtree": "0.6.0",
92
- "rollup": "2.77.0",
92
+ "rollup": "2.77.2",
93
93
  "string-width": "5.1.2",
94
94
  "strip-ansi": "7.0.1",
95
95
  "terser": "5.14.2",
@@ -97,19 +97,19 @@
97
97
  "wrap-ansi": "8.0.1"
98
98
  },
99
99
  "devDependencies": {
100
- "@babel/eslint-parser": "7.18.2",
100
+ "@babel/eslint-parser": "7.18.9",
101
101
  "@babel/plugin-syntax-import-assertions": "7.18.6",
102
- "@jsenv/assert": "2.6.0",
102
+ "@jsenv/assert": "2.7.0",
103
103
  "@jsenv/eslint-config": "16.2.1",
104
104
  "@jsenv/file-size-impact": "13.0.1",
105
- "@jsenv/https-local": "2.1.0",
105
+ "@jsenv/https-local": "3.0.1",
106
106
  "@jsenv/package-workspace": "0.4.1",
107
107
  "@jsenv/performance-impact": "3.0.1",
108
- "eslint": "8.20.0",
109
- "eslint-plugin-html": "7.0.0",
108
+ "eslint": "8.21.0",
109
+ "eslint-plugin-html": "7.1.0",
110
110
  "eslint-plugin-import": "2.26.0",
111
111
  "eslint-plugin-react": "7.30.1",
112
- "playwright": "1.24.1",
112
+ "playwright": "1.24.2",
113
113
  "prettier": "2.7.1"
114
114
  }
115
115
  }
package/readme.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # @jsenv/core [![npm package](https://img.shields.io/npm/v/@jsenv/core.svg?logo=npm&label=package)](https://www.npmjs.com/package/@jsenv/core)
2
2
 
3
3
  Jsenv was first created to write tests that could be executed in different runtimes.
4
- It has naturally evolved to cover the core needs of a JavaScript project: developement, testing and building for production.
4
+ It has naturally evolved to cover the core needs of a JavaScript project: developement, testing and building for production.
5
5
 
6
6
  - :exploding_head: Execute HTML files as tests
7
7
  - :sparkles: A single tool for the whole developer experience
@@ -61,12 +61,12 @@ const injectors = {
61
61
  injectScriptNodeAsEarlyAsPossible(
62
62
  htmlAst,
63
63
  createHtmlNode({
64
- "tagName": "script",
65
- "textContent": generateClientCodeForVersionMappings(versionMappings, {
64
+ tagName: "script",
65
+ textContent: generateClientCodeForVersionMappings(versionMappings, {
66
66
  globalName: "window",
67
67
  }),
68
- "injected-by": "jsenv:versioning",
69
68
  }),
69
+ "jsenv:versioning",
70
70
  )
71
71
  return {
72
72
  content: stringifyHtmlAst(htmlAst),
@@ -48,7 +48,7 @@ export const startDevServer = async ({
48
48
  runtimeCompat = defaultRuntimeCompat,
49
49
  plugins = [],
50
50
  urlAnalysis = {},
51
- htmlSupervisor = true,
51
+ supervisor = true,
52
52
  nodeEsmResolution,
53
53
  fileSystemMagicResolution,
54
54
  transpilation,
@@ -160,7 +160,7 @@ export const startDevServer = async ({
160
160
 
161
161
  plugins,
162
162
  urlAnalysis,
163
- htmlSupervisor,
163
+ supervisor,
164
164
  nodeEsmResolution,
165
165
  fileSystemMagicResolution,
166
166
  transpilation,
@@ -94,7 +94,7 @@ export const execute = async ({
94
94
  ```
95
95
  But it feels like a hack.
96
96
  */
97
- throw result.error
97
+ throw result.errors[result.errors.length - 1]
98
98
  }
99
99
  return result
100
100
  } finally {
@@ -16,7 +16,11 @@ export const run = async ({
16
16
  runtime,
17
17
  runtimeParams,
18
18
  }) => {
19
- const result = {}
19
+ const result = {
20
+ status: "pending",
21
+ errors: [],
22
+ namespace: null,
23
+ }
20
24
  const callbacks = []
21
25
 
22
26
  const onConsoleRef = { current: () => {} }
@@ -25,6 +29,7 @@ export const run = async ({
25
29
 
26
30
  const runOperation = Abort.startOperation()
27
31
  runOperation.addAbortSignal(signal)
32
+ let timeoutAbortSource
28
33
  if (
29
34
  // ideally we would rather log than the timeout is ignored
30
35
  // when keepRunning is true
@@ -32,24 +37,8 @@ export const run = async ({
32
37
  typeof allocatedMs === "number" &&
33
38
  allocatedMs !== Infinity
34
39
  ) {
35
- const timeoutAbortSource = runOperation.timeout(allocatedMs)
36
- callbacks.push(() => {
37
- if (
38
- result.status === "errored" &&
39
- Abort.isAbortError(result.error) &&
40
- timeoutAbortSource.signal.aborted
41
- ) {
42
- result.status = "timedout"
43
- delete result.error
44
- }
45
- })
40
+ timeoutAbortSource = runOperation.timeout(allocatedMs)
46
41
  }
47
- callbacks.push(() => {
48
- if (result.status === "errored" && Abort.isAbortError(result.error)) {
49
- result.status = "aborted"
50
- delete result.error
51
- }
52
- })
53
42
  const consoleCalls = []
54
43
  onConsoleRef.current = ({ type, text }) => {
55
44
  if (mirrorConsole) {
@@ -64,9 +53,7 @@ export const run = async ({
64
53
  }
65
54
  }
66
55
  if (collectConsole) {
67
- callbacks.push(() => {
68
- result.consoleCalls = consoleCalls
69
- })
56
+ result.consoleCalls = consoleCalls
70
57
  }
71
58
 
72
59
  // we do not keep coverage in memory, it can grow very big
@@ -133,29 +120,30 @@ export const run = async ({
133
120
  if (winner.name === "aborted") {
134
121
  runOperation.throwIfAborted()
135
122
  }
136
-
137
- const { status, namespace, error, performance } = winner.data
123
+ const { status, namespace, errors, performance } = winner.data
138
124
  result.status = status
139
- if (status === "errored") {
140
- result.error = error
141
- } else {
142
- result.namespace = namespace
143
- }
125
+ result.errors.push(...errors)
126
+ result.namespace = namespace
144
127
  if (collectPerformance) {
145
128
  result.performance = performance
146
129
  }
147
- callbacks.forEach((callback) => {
148
- callback()
149
- })
150
- return result
151
130
  } catch (e) {
152
- result.status = "errored"
153
- result.error = e
154
- callbacks.forEach((callback) => {
155
- callback()
156
- })
157
- return result
131
+ if (Abort.isAbortError(e)) {
132
+ if (timeoutAbortSource && timeoutAbortSource.signal.aborted) {
133
+ result.status = "timedout"
134
+ } else {
135
+ result.status = "aborted"
136
+ }
137
+ } else {
138
+ result.status = "errored"
139
+ result.errors.push(e)
140
+ }
158
141
  } finally {
159
142
  await runOperation.end()
160
143
  }
144
+
145
+ callbacks.forEach((callback) => {
146
+ callback()
147
+ })
148
+ return result
161
149
  }
@@ -1,4 +1,3 @@
1
- import { Script } from "node:vm"
2
1
  import { writeFileSync } from "node:fs"
3
2
 
4
3
  import { createDetailedMessage } from "@jsenv/log"
@@ -10,7 +9,6 @@ import {
10
9
  } from "@jsenv/abort"
11
10
  import { moveUrl } from "@jsenv/urls"
12
11
  import { memoize } from "@jsenv/utils/src/memoize/memoize.js"
13
- import { escapeRegexpSpecialChars } from "@jsenv/utils/src/string/escape_regexp_special_chars.js"
14
12
 
15
13
  import { filterV8Coverage } from "@jsenv/core/src/test/coverage/v8_coverage.js"
16
14
  import { composeTwoFileByFileIstanbulCoverages } from "@jsenv/core/src/test/coverage/istanbul_coverage_composition.js"
@@ -121,7 +119,11 @@ export const createRuntimeFromPlaywright = ({
121
119
  }
122
120
  }
123
121
 
124
- const result = {}
122
+ const result = {
123
+ status: "pending",
124
+ namespace: null,
125
+ errors: [],
126
+ }
125
127
  const callbacks = []
126
128
  if (coverageEnabled) {
127
129
  if (
@@ -251,18 +253,18 @@ export const createRuntimeFromPlaywright = ({
251
253
  })
252
254
  },
253
255
  // https://github.com/GoogleChrome/puppeteer/blob/v1.4.0/docs/api.md#event-pageerror
254
- pageerror: (cb) => {
255
- return registerEvent({
256
- object: page,
257
- eventType: "pageerror",
258
- callback: (error) => {
259
- if (ignoreErrorHook(error)) {
260
- return
261
- }
262
- cb(transformErrorHook(error))
263
- },
264
- })
265
- },
256
+ // pageerror: () => {
257
+ // return registerEvent({
258
+ // object: page,
259
+ // eventType: "pageerror",
260
+ // callback: (error) => {
261
+ // if (ignoreErrorHook(error)) {
262
+ // return
263
+ // }
264
+ // result.errors.push(transformErrorHook(error))
265
+ // },
266
+ // })
267
+ // },
266
268
  closed: (cb) => {
267
269
  // https://github.com/GoogleChrome/puppeteer/blob/v1.4.0/docs/api.md#event-disconnected
268
270
  if (isBrowserDedicatedToExecution) {
@@ -301,32 +303,14 @@ export const createRuntimeFromPlaywright = ({
301
303
  /* eslint-disable no-undef */
302
304
  /* istanbul ignore next */
303
305
  () => {
304
- if (!window.__html_supervisor__) {
305
- throw new Error(`window.__html_supervisor__ not found`)
306
+ if (!window.__supervisor__) {
307
+ throw new Error(`window.__supervisor__ not found`)
306
308
  }
307
- return window.__html_supervisor__.getScriptExecutionResults()
309
+ return window.__supervisor__.getDocumentExecutionResult()
308
310
  },
309
311
  /* eslint-enable no-undef */
310
312
  )
311
- const { status, scriptExecutionResults } = returnValue
312
- if (status === "errored") {
313
- const { exceptionSource } = returnValue
314
- const error = evalException(exceptionSource, {
315
- rootDirectoryUrl,
316
- devServerOrigin,
317
- transformErrorHook,
318
- })
319
- cb({
320
- status: "errored",
321
- error,
322
- namespace: scriptExecutionResults,
323
- })
324
- } else {
325
- cb({
326
- status: "completed",
327
- namespace: scriptExecutionResults,
328
- })
329
- }
313
+ cb(returnValue)
330
314
  } catch (e) {
331
315
  reject(e)
332
316
  }
@@ -336,39 +320,46 @@ export const createRuntimeFromPlaywright = ({
336
320
  )
337
321
  })
338
322
 
339
- const getResult = async () => {
323
+ const writeResult = async () => {
340
324
  const winner = await winnerPromise
341
325
  if (winner.name === "aborted") {
342
- return {
343
- status: "aborted",
344
- }
326
+ result.status = "aborted"
327
+ return
345
328
  }
346
- if (winner.name === "error" || winner.name === "pageerror") {
347
- const error = winner.data
348
- return {
349
- status: "errored",
350
- error,
351
- }
329
+ if (winner.name === "error") {
330
+ let error = winner.data
331
+ result.status = "errored"
332
+ result.errors.push(error)
333
+ return
352
334
  }
353
335
  if (winner.name === "closed") {
354
- return {
355
- status: "errored",
356
- error: isBrowserDedicatedToExecution
336
+ result.status = "errored"
337
+ result.errors.push(
338
+ isBrowserDedicatedToExecution
357
339
  ? new Error(`browser disconnected during execution`)
358
340
  : new Error(`page closed during execution`),
359
- }
341
+ )
342
+ return
360
343
  }
361
- return winner.data
344
+ // winner.name = 'response'
345
+ const { executionResults } = winner.data
346
+ result.status = "completed"
347
+ result.namespace = executionResults
348
+ Object.keys(executionResults).forEach((key) => {
349
+ const executionResult = executionResults[key]
350
+ if (executionResult.status === "errored") {
351
+ result.status = "errored"
352
+ result.errors.push({
353
+ ...executionResult.exception,
354
+ stack: executionResult.exception.text,
355
+ })
356
+ }
357
+ })
358
+ return
362
359
  }
363
360
 
364
361
  try {
365
- const { status, error, namespace, performance } = await getResult()
366
- result.status = status
367
- if (status === "errored") {
368
- result.error = error
369
- } else {
370
- result.namespace = namespace
371
- }
362
+ await writeResult()
372
363
  if (collectPerformance) {
373
364
  result.performance = performance
374
365
  }
@@ -378,7 +369,7 @@ export const createRuntimeFromPlaywright = ({
378
369
  }, Promise.resolve())
379
370
  } catch (e) {
380
371
  result.status = "errored"
381
- result.error = e
372
+ result.errors = [e]
382
373
  }
383
374
  if (keepRunning) {
384
375
  stopSignal.notify = cleanup
@@ -533,20 +524,3 @@ const registerEvent = ({ object, eventType, callback }) => {
533
524
  object.removeListener(eventType, callback)
534
525
  }
535
526
  }
536
-
537
- const evalException = (
538
- exceptionSource,
539
- { rootDirectoryUrl, devServerOrigin, transformErrorHook },
540
- ) => {
541
- const script = new Script(exceptionSource, { filename: "" })
542
- const error = script.runInThisContext()
543
- if (error && error instanceof Error) {
544
- const remoteRootRegexp = new RegExp(
545
- escapeRegexpSpecialChars(`${devServerOrigin}/`),
546
- "g",
547
- )
548
- error.stack = error.stack.replace(remoteRootRegexp, rootDirectoryUrl)
549
- error.message = error.message.replace(remoteRootRegexp, rootDirectoryUrl)
550
- }
551
- return transformErrorHook(error)
552
- }
@@ -185,7 +185,13 @@ nodeChildProcess.run = async ({
185
185
  resolve,
186
186
  )
187
187
  })
188
- const getResult = async () => {
188
+ const result = {
189
+ status: "executing",
190
+ errors: [],
191
+ namespace: null,
192
+ }
193
+
194
+ const writeResult = async () => {
189
195
  actionOperation.throwIfAborted()
190
196
  await childProcessReadyPromise
191
197
  actionOperation.throwIfAborted()
@@ -207,28 +213,27 @@ nodeChildProcess.run = async ({
207
213
  })
208
214
  const winner = await winnerPromise
209
215
  if (winner.name === "aborted") {
210
- return {
211
- status: "aborted",
212
- }
216
+ result.status = "aborted"
217
+ return
213
218
  }
214
219
  if (winner.name === "error") {
215
220
  const error = winner.data
216
221
  removeOutputListener()
217
- return {
218
- status: "errored",
219
- error,
220
- }
222
+ result.status = "errored"
223
+ result.errors.push(error)
224
+ return
221
225
  }
222
226
  if (winner.name === "exit") {
223
227
  const { code } = winner.data
224
228
  await cleanup("process exit")
225
229
  if (code === 12) {
226
- return {
227
- status: "errored",
228
- error: new Error(
230
+ result.status = "errored"
231
+ result.errors.push(
232
+ new Error(
229
233
  `node process exited with 12 (the forked child process wanted to use a non-available port for debug)`,
230
234
  ),
231
- }
235
+ )
236
+ return
232
237
  }
233
238
  if (
234
239
  code === null ||
@@ -237,41 +242,36 @@ nodeChildProcess.run = async ({
237
242
  code === EXIT_CODES.SIGTERM ||
238
243
  code === EXIT_CODES.SIGABORT
239
244
  ) {
240
- return {
241
- status: "errored",
242
- error: new Error(`node process exited during execution`),
243
- }
245
+ result.status = "errored"
246
+ result.errors.push(new Error(`node process exited during execution`))
247
+ return
244
248
  }
245
249
  // process.exit(1) in child process or process.exitCode = 1 + process.exit()
246
250
  // means there was an error even if we don't know exactly what.
247
- return {
248
- status: "errored",
249
- error: new Error(
250
- `node process exited with code ${code} during execution`,
251
- ),
252
- }
251
+ result.status = "errored"
252
+ result.errors.push(
253
+ new Error(`node process exited with code ${code} during execution`),
254
+ )
255
+ return
253
256
  }
254
257
  const { status, value } = winner.data
255
258
  if (status === "action-failed") {
256
- return {
257
- status: "errored",
258
- error: value,
259
- }
260
- }
261
- return {
262
- status: "completed",
263
- ...value,
259
+ result.status = "errored"
260
+ result.errors.push(value)
261
+ return
264
262
  }
263
+ const { namespace, performance, coverage } = value
264
+ result.status = "completed"
265
+ result.namespace = namespace
266
+ result.performance = performance
267
+ result.coverage = coverage
265
268
  }
266
269
 
267
- let result
268
270
  try {
269
- result = await getResult()
271
+ await writeResult()
270
272
  } catch (e) {
271
- result = {
272
- status: "errored",
273
- error: e,
274
- }
273
+ result.status = "errored"
274
+ result.errors.push(e)
275
275
  }
276
276
  if (keepRunning) {
277
277
  stopSignal.notify = stop
@@ -137,7 +137,13 @@ nodeWorkerThread.run = async ({
137
137
  )
138
138
  })
139
139
 
140
- const getResult = async () => {
140
+ const result = {
141
+ status: "executing",
142
+ errors: [],
143
+ namespace: null,
144
+ }
145
+
146
+ const writeResult = async () => {
141
147
  actionOperation.throwIfAborted()
142
148
  await workerThreadReadyPromise
143
149
  actionOperation.throwIfAborted()
@@ -159,28 +165,27 @@ nodeWorkerThread.run = async ({
159
165
  })
160
166
  const winner = await winnerPromise
161
167
  if (winner.name === "aborted") {
162
- return {
163
- status: "aborted",
164
- }
168
+ result.status = "aborted"
169
+ return
165
170
  }
166
171
  if (winner.name === "error") {
167
172
  const error = winner.data
168
173
  removeOutputListener()
169
- return {
170
- status: "errored",
171
- error,
172
- }
174
+ result.status = "errored"
175
+ result.errors.push(error)
176
+ return
173
177
  }
174
178
  if (winner.name === "exit") {
175
179
  const { code } = winner.data
176
180
  await cleanup("process exit")
177
181
  if (code === 12) {
178
- return {
179
- status: "errored",
180
- error: new Error(
182
+ result.status = "errored"
183
+ result.errors.push(
184
+ new Error(
181
185
  `node process exited with 12 (the forked child process wanted to use a non-available port for debug)`,
182
186
  ),
183
- }
187
+ )
188
+ return
184
189
  }
185
190
  if (
186
191
  code === null ||
@@ -189,44 +194,39 @@ nodeWorkerThread.run = async ({
189
194
  code === EXIT_CODES.SIGTERM ||
190
195
  code === EXIT_CODES.SIGABORT
191
196
  ) {
192
- return {
193
- status: "errored",
194
- error: new Error(`node worker thread exited during execution`),
195
- }
197
+ result.status = "errored"
198
+ result.errors.push(
199
+ new Error(`node worker thread exited during execution`),
200
+ )
201
+ return
196
202
  }
197
203
  // process.exit(1) in child process or process.exitCode = 1 + process.exit()
198
204
  // means there was an error even if we don't know exactly what.
199
- return {
200
- status: "errored",
201
- error: new Error(
205
+ result.status = "errored"
206
+ result.errors.push(
207
+ new Error(
202
208
  `node worker thread exited with code ${code} during execution`,
203
209
  ),
204
- }
210
+ )
205
211
  }
206
212
  const { status, value } = winner.data
207
213
  if (status === "action-failed") {
208
- return {
209
- status: "errored",
210
- error: value,
211
- }
214
+ result.status = "errored"
215
+ result.errors.push(value)
216
+ return
212
217
  }
213
218
  const { namespace, performance, coverage } = value
214
- return {
215
- status: "completed",
216
- namespace,
217
- performance,
218
- coverage,
219
- }
219
+ result.status = "completed"
220
+ result.namespace = namespace
221
+ result.performance = performance
222
+ result.coverage = coverage
220
223
  }
221
224
 
222
- let result
223
225
  try {
224
- result = await getResult()
226
+ await writeResult()
225
227
  } catch (e) {
226
- result = {
227
- status: "errored",
228
- error: e,
229
- }
228
+ result.status = "errored"
229
+ result.errors.push(e)
230
230
  }
231
231
 
232
232
  if (keepRunning) {