@jsenv/core 23.1.3 → 23.2.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 (82) hide show
  1. package/dist/jsenv_browser_system.js +36 -99
  2. package/dist/jsenv_browser_system.js.map +12 -21
  3. package/dist/jsenv_compile_proxy.js +18 -82
  4. package/dist/jsenv_compile_proxy.js.map +11 -21
  5. package/dist/jsenv_exploring_index.js +127 -274
  6. package/dist/jsenv_exploring_index.js.map +76 -90
  7. package/dist/jsenv_exploring_redirector.js +21 -89
  8. package/dist/jsenv_exploring_redirector.js.map +13 -25
  9. package/dist/jsenv_toolbar.js +81 -149
  10. package/dist/jsenv_toolbar.js.map +50 -61
  11. package/dist/jsenv_toolbar_injector.js +185 -231
  12. package/dist/jsenv_toolbar_injector.js.map +30 -42
  13. package/package.json +8 -9
  14. package/src/abort/abortable.js +172 -0
  15. package/src/abort/callback_list.js +64 -0
  16. package/src/abort/callback_race.js +34 -0
  17. package/src/abort/cleaner.js +22 -0
  18. package/src/abort/main.js +32 -0
  19. package/src/abort/process_teardown_events.js +59 -0
  20. package/src/buildProject.js +132 -123
  21. package/src/execute.js +108 -107
  22. package/src/executeTestPlan.js +107 -125
  23. package/src/importUsingChildProcess.js +2 -1
  24. package/src/internal/browser-launcher/executeHtmlFile.js +33 -12
  25. package/src/internal/browser-utils/fetch-browser.js +4 -29
  26. package/src/internal/browser-utils/fetchUsingXHR.js +5 -7
  27. package/src/internal/building/buildUsingRollup.js +60 -24
  28. package/src/internal/building/createJsenvRollupPlugin.js +13 -31
  29. package/src/internal/building/ressource_builder.js +3 -6
  30. package/src/internal/building/sourcemap_loader.js +4 -5
  31. package/src/internal/building/url_fetcher.js +2 -5
  32. package/src/internal/building/url_loader.js +3 -6
  33. package/src/internal/compiling/compileFile.js +1 -2
  34. package/src/internal/compiling/createCompiledFileService.js +8 -9
  35. package/src/internal/compiling/startCompileServer.js +74 -135
  36. package/src/internal/executing/coverage/generateCoverageJsonFile.js +20 -3
  37. package/src/internal/executing/coverage/relativeUrlToEmptyCoverage.js +19 -30
  38. package/src/internal/executing/coverage/reportToCoverage.js +44 -24
  39. package/src/internal/executing/coverage/v8CoverageFromNodeV8Directory.js +2 -15
  40. package/src/internal/executing/createSummaryLog.js +50 -37
  41. package/src/internal/executing/executeConcurrently.js +89 -47
  42. package/src/internal/executing/executePlan.js +33 -7
  43. package/src/internal/executing/executionLogs.js +25 -28
  44. package/src/internal/executing/execution_colors.js +15 -0
  45. package/src/internal/executing/generateExecutionSteps.js +3 -2
  46. package/src/internal/executing/launchAndExecute.js +217 -261
  47. package/src/internal/exploring/fetchExploringJson.js +3 -4
  48. package/src/internal/fetchUrl.js +6 -2
  49. package/src/internal/logs/log_style.js +16 -28
  50. package/src/internal/logs/msAsDuration.js +1 -1
  51. package/src/internal/node-launcher/createChildProcessOptions.js +4 -5
  52. package/src/internal/node-launcher/createControllableNodeProcess.js +117 -229
  53. package/src/internal/node-launcher/kill_process_tree.js +76 -0
  54. package/src/internal/node-launcher/nodeControllableFile.mjs +16 -10
  55. package/src/internal/{promise_track_race.js → promise_race.js} +2 -2
  56. package/src/internal/runtime/s.js +25 -24
  57. package/src/internal/toolbar/toolbar.html +157 -61
  58. package/src/internal/toolbar/toolbar.injector.js +8 -0
  59. package/src/internal/toolbar/util/animation.js +3 -7
  60. package/src/internal/toolbar/util/fetching.js +1 -30
  61. package/src/jsenvServiceWorkerFinalizer.js +1 -2
  62. package/src/launchBrowser.js +131 -127
  63. package/src/launchNode.js +29 -17
  64. package/src/playwright_browser_versions.js +3 -3
  65. package/src/requireUsingChildProcess.js +2 -1
  66. package/src/signal/signal.js +65 -0
  67. package/src/startExploring.js +70 -72
  68. package/src/internal/executeJsenvAsyncFunction.js +0 -34
  69. package/src/internal/toolbar/animation/toolbar-movie-icon.svg +0 -15
  70. package/src/internal/toolbar/compilation/flask.svg +0 -7
  71. package/src/internal/toolbar/compilation/info.svg +0 -9
  72. package/src/internal/toolbar/compilation/loupe.svg +0 -11
  73. package/src/internal/toolbar/compilation/toolbar_compilation.svg +0 -11
  74. package/src/internal/toolbar/eventsource/toolbar-power-icon.svg +0 -10
  75. package/src/internal/toolbar/eventsource/toolbar-power-off-icon.svg +0 -10
  76. package/src/internal/toolbar/responsive/toolbar-dots-icon.svg +0 -10
  77. package/src/internal/toolbar/settings/toolbar-settings-icon.svg +0 -9
  78. package/src/internal/toolbar/theme/toolbar-palette-icon.svg +0 -10
  79. package/src/internal/toolbar/toolbar-cross-icon.svg +0 -10
  80. package/src/internal/toolbar/toolbar-loading-icon.svg +0 -102
  81. package/src/internal/toolbar/toolbar-notif-icon.svg +0 -9
  82. package/src/internal/trackRessources.js +0 -23
@@ -1,4 +1,3 @@
1
- import { createCancellationToken } from "@jsenv/cancellation"
2
1
  import { findFreePort } from "@jsenv/server"
3
2
  import { createDetailedMessage } from "@jsenv/logger"
4
3
 
@@ -14,7 +13,7 @@ const AVAILABLE_DEBUG_MODE = [
14
13
  ]
15
14
 
16
15
  export const createChildProcessOptions = async ({
17
- cancellationToken = createCancellationToken(),
16
+ signal = new AbortController().signal,
18
17
  // https://code.visualstudio.com/docs/nodejs/nodejs-debugging#_automatically-attach-debugger-to-nodejs-subprocesses
19
18
  processExecArgv = process.execArgv,
20
19
  processDebugPort = process.debugPort,
@@ -38,7 +37,7 @@ export const createChildProcessOptions = async ({
38
37
  const childProcessOptions = processOptionsFromExecArgv(processExecArgv)
39
38
 
40
39
  await mutateDebuggingOptions(childProcessOptions, {
41
- cancellationToken,
40
+ signal,
42
41
  processDebugPort,
43
42
  debugMode,
44
43
  debugPort,
@@ -52,7 +51,7 @@ const mutateDebuggingOptions = async (
52
51
  childProcessOptions,
53
52
  {
54
53
  // ensure multiline
55
- cancellationToken,
54
+ signal,
56
55
  processDebugPort,
57
56
  debugMode,
58
57
  debugPort,
@@ -92,7 +91,7 @@ const mutateDebuggingOptions = async (
92
91
  // support assigning a child spawned without a specific port
93
92
  const childDebugPortOptionValue =
94
93
  debugPort === 0
95
- ? await findFreePort(processDebugPort + 37, { cancellationToken })
94
+ ? await findFreePort(processDebugPort + 37, { signal })
96
95
  : debugPort
97
96
  // replace child debug port
98
97
  if (parentDebugPortOptionName) {
@@ -1,8 +1,5 @@
1
- /* eslint-disable import/max-dependencies */
2
- import { fork as forkChildProcess } from "node:child_process"
3
-
1
+ import { fork } from "node:child_process"
4
2
  import { uneval } from "@jsenv/uneval"
5
- import { createCancellationToken } from "@jsenv/cancellation"
6
3
  import { createLogger, createDetailedMessage } from "@jsenv/logger"
7
4
  import {
8
5
  urlToFileSystemPath,
@@ -10,14 +7,17 @@ import {
10
7
  assertFilePresence,
11
8
  } from "@jsenv/filesystem"
12
9
 
10
+ import { Abortable } from "@jsenv/core/src/abort/main.js"
11
+ import { raceCallbacks } from "@jsenv/core/src/abort/callback_race.js"
12
+ import { createSignal } from "@jsenv/core/src/signal/signal.js"
13
13
  import { nodeSupportsDynamicImport } from "../runtime/node-feature-detect/nodeSupportsDynamicImport.js"
14
14
  import { jsenvCoreDirectoryUrl } from "../jsenvCoreDirectoryUrl.js"
15
- import { require } from "../require.js"
16
15
  import { createChildProcessOptions } from "./createChildProcessOptions.js"
17
16
  import {
18
17
  processOptionsFromExecArgv,
19
18
  execArgvFromProcessOptions,
20
19
  } from "./processOptions.js"
20
+ import { killProcessTree } from "./kill_process_tree.js"
21
21
 
22
22
  const nodeControllableFileUrl = resolveUrl(
23
23
  "./src/internal/node-launcher/nodeControllableFile.mjs",
@@ -40,7 +40,7 @@ const STOP_SIGNAL = "SIGKILL"
40
40
  const GRACEFUL_STOP_FAILED_SIGNAL = "SIGKILL"
41
41
 
42
42
  export const createControllableNodeProcess = async ({
43
- cancellationToken = createCancellationToken(),
43
+ signal = new AbortController().signal,
44
44
  logLevel,
45
45
  debugPort,
46
46
  debugMode,
@@ -61,7 +61,7 @@ export const createControllableNodeProcess = async ({
61
61
  }
62
62
 
63
63
  const childProcessOptions = await createChildProcessOptions({
64
- cancellationToken,
64
+ signal,
65
65
  debugPort,
66
66
  debugMode,
67
67
  debugModeInheritBreak,
@@ -81,22 +81,19 @@ export const createControllableNodeProcess = async ({
81
81
  ...(inheritProcessEnv ? process.env : {}),
82
82
  ...env,
83
83
  }
84
- const childProcess = forkChildProcess(
85
- urlToFileSystemPath(nodeControllableFileUrl),
86
- {
87
- execArgv,
88
- // silent: true
89
- stdio: ["pipe", "pipe", "pipe", "ipc"],
90
- env: envForChildProcess,
91
- },
92
- )
84
+ const childProcess = fork(urlToFileSystemPath(nodeControllableFileUrl), {
85
+ execArgv,
86
+ // silent: true
87
+ stdio: ["pipe", "pipe", "pipe", "ipc"],
88
+ env: envForChildProcess,
89
+ })
93
90
 
94
- logger.debug(`fork child process pid ${childProcess.pid}
95
- --- execArgv ---
96
- ${execArgv.join(`
97
- `)}
98
- --- custom env ---
99
- ${JSON.stringify(env, null, " ")}`)
91
+ logger.debug(
92
+ createDetailedMessage(`fork child process pid ${childProcess.pid}`, {
93
+ "execArgv": execArgv.join(`\n`),
94
+ "custom env": JSON.stringify(env, null, " "),
95
+ }),
96
+ )
100
97
 
101
98
  // if we passe stream, pipe them https://github.com/sindresorhus/execa/issues/81
102
99
  if (typeof stdin === "object") {
@@ -120,199 +117,121 @@ ${JSON.stringify(env, null, " ")}`)
120
117
  onceProcessMessage(childProcess, "ready", resolve)
121
118
  })
122
119
 
123
- const consoleCallbackArray = []
124
- const registerConsoleCallback = (callback) => {
125
- consoleCallbackArray.push(callback)
126
- }
120
+ const outputSignal = createSignal()
127
121
  const removeOutputListener = installProcessOutputListener(
128
122
  childProcess,
129
123
  ({ type, text }) => {
130
- consoleCallbackArray.forEach((callback) => {
131
- callback({
132
- type,
133
- text,
134
- })
135
- })
136
- },
137
- )
138
- // keep listening process outputs while child process is killed to catch
139
- // outputs until it's actually disconnected
140
- // registerCleanupCallback(removeProcessOutputListener)
141
-
142
- const errorCallbackArray = []
143
- const registerErrorCallback = (callback) => {
144
- errorCallbackArray.push(callback)
145
- return () => {
146
- const index = errorCallbackArray.indexOf(callback)
147
- if (index > -1) {
148
- errorCallbackArray.splice(index, 1)
149
- }
150
- }
151
- }
152
- let killing = false
153
- const removeErrorListener = installProcessErrorListener(
154
- childProcess,
155
- (error) => {
156
- removeOutputListener()
157
- if (!childProcess.connected && error.code === "ERR_IPC_DISCONNECTED") {
158
- return
159
- }
160
- // on windows killProcessTree uses taskkill which seems to kill the process
161
- // with an exitCode of 1
162
- if (process.platform === "win32" && killing && error.exitCode === 1) {
163
- return
164
- }
165
- errorCallbackArray.forEach((callback) => {
166
- callback(error)
167
- })
124
+ outputSignal.emit({ type, text })
168
125
  },
169
126
  )
170
- // keep listening process errors while child process is killed to catch
171
- // errors until it's actually disconnected
172
- // registerCleanupCallback(removeProcessErrorListener)
173
-
174
- // https://nodejs.org/api/child_process.html#child_process_event_disconnect
175
- let resolveDisconnect
176
- let hasExitedOrDisconnected = false
177
- const disconnected = new Promise((resolve) => {
178
- resolveDisconnect = () => {
179
- hasExitedOrDisconnected = true
180
- removeExitListener()
181
- removeDisconnectListener()
182
- }
183
127
 
184
- const removeDisconnectListener = onceProcessEvent(
185
- childProcess,
186
- "disconnect",
187
- () => {
188
- hasExitedOrDisconnected = true
189
- removeErrorListener()
190
- removeExitListener()
191
- resolve()
192
- },
193
- )
128
+ const errorSignal = createSignal()
194
129
 
195
- const removeExitListener = onceProcessEvent(childProcess, "exit", () => {
196
- hasExitedOrDisconnected = true
197
- removeErrorListener()
198
- removeDisconnectListener()
199
- resolve()
200
- })
130
+ const stoppedSignal = createSignal({
131
+ once: true,
201
132
  })
202
-
203
- const disconnectChildProcess = async () => {
204
- if (hasExitedOrDisconnected) {
205
- return
206
- }
207
- try {
208
- childProcess.disconnect()
209
- } catch (e) {
210
- if (e.code === "ERR_IPC_DISCONNECTED") {
211
- resolveDisconnect()
212
- } else {
213
- throw e
214
- }
215
- }
216
- await disconnected
217
- }
218
-
219
- const killChildProcess = async ({ signal }) => {
220
- if (hasExitedOrDisconnected) {
221
- await disconnectChildProcess()
222
- return
223
- }
224
-
225
- killing = true
226
- logger.debug(`send ${signal} to child process with pid ${childProcess.pid}`)
227
-
228
- await new Promise((resolve) => {
229
- // see also https://github.com/sindresorhus/execa/issues/96
230
- const killProcessTree = require("tree-kill")
231
- killProcessTree(childProcess.pid, signal, (error) => {
232
- if (error) {
233
- // on windows: process with pid cannot be found
234
- if (
235
- error.stack.includes(`The process "${childProcess.pid}" not found`)
236
- ) {
237
- resolve()
238
- return
239
- }
240
- // on windows: child process with a pid cannot be found
133
+ raceCallbacks(
134
+ {
135
+ // https://nodejs.org/api/child_process.html#child_process_event_disconnect
136
+ // disconnect: (cb) => {
137
+ // return onceProcessEvent(childProcess, "disconnect", cb)
138
+ // },
139
+ // https://nodejs.org/api/child_process.html#child_process_event_error
140
+ error: (cb) => {
141
+ return onceProcessEvent(childProcess, "error", cb)
142
+ },
143
+ exit: (cb) => {
144
+ return onceProcessEvent(childProcess, "exit", (code, signal) => {
145
+ cb({ code, signal })
146
+ })
147
+ },
148
+ },
149
+ (winner) => {
150
+ const raceEffects = {
151
+ // disconnect: () => {
152
+ // stoppedSignal.emit()
153
+ // },
154
+ error: (error) => {
155
+ removeOutputListener()
241
156
  if (
242
- error.stack.includes(
243
- "Reason: There is no running instance of the task",
244
- )
157
+ !childProcess.connected &&
158
+ error.code === "ERR_IPC_DISCONNECTED"
245
159
  ) {
246
- resolve()
247
160
  return
248
161
  }
249
- // windows too
162
+ errorSignal.emit(error)
163
+ },
164
+ exit: ({ code, signal }) => {
165
+ // process.exit(1) in child process or process.exitCode = 1 + process.exit()
166
+ // means there was an error even if we don't know exactly what.
250
167
  if (
251
- error.stack.includes("The operation attempted is not supported")
168
+ code !== null &&
169
+ code !== 0 &&
170
+ code !== SIGINT_EXIT_CODE &&
171
+ code !== SIGTERM_EXIT_CODE &&
172
+ code !== SIGABORT_EXIT_CODE
252
173
  ) {
253
- resolve()
254
- return
174
+ errorSignal.emit(createExitWithFailureCodeError(code))
255
175
  }
176
+ stoppedSignal.emit({ code, signal })
177
+ },
178
+ }
179
+ raceEffects[winner.name](winner.data)
180
+ },
181
+ )
256
182
 
257
- logger.warn(
258
- createDetailedMessage(
259
- `error while killing process tree with ${signal}`,
260
- {
261
- ["error stack"]: error.stack,
262
- ["process.pid"]: childProcess.pid,
263
- },
264
- ),
265
- )
266
-
267
- // even if we could not kill the child
268
- // we will ask it to disconnect
269
- resolve()
270
- return
271
- }
183
+ const stop = async ({ gracefulStopAllocatedMs } = {}) => {
184
+ if (stoppedSignal.emitted) {
185
+ return {}
186
+ }
272
187
 
273
- resolve()
274
- })
275
- })
188
+ const createStoppedPromise = async () => {
189
+ if (stoppedSignal.emitted) {
190
+ return
191
+ }
192
+ await new Promise((resolve) => stoppedSignal.addCallback(resolve))
193
+ }
276
194
 
277
- // in case the child process did not disconnect by itself at this point
278
- // something is keeping it alive and it cannot be propely killed.
279
- // wait for the child process to disconnect by itself
280
- await disconnectChildProcess()
281
- }
195
+ if (gracefulStopAllocatedMs) {
196
+ try {
197
+ await killProcessTree(childProcess.pid, {
198
+ signal: GRACEFUL_STOP_SIGNAL,
199
+ timeout: gracefulStopAllocatedMs,
200
+ })
201
+ await createStoppedPromise()
202
+ return { graceful: true }
203
+ } catch (e) {
204
+ if (e.code === "TIMEOUT") {
205
+ logger.debug(
206
+ `kill with SIGTERM because gracefulStop still pending after ${gracefulStopAllocatedMs}ms`,
207
+ )
208
+ await killProcessTree(childProcess.pid, {
209
+ signal: GRACEFUL_STOP_FAILED_SIGNAL,
210
+ })
211
+ await createStoppedPromise()
212
+ return { graceful: false }
213
+ }
214
+ throw e
215
+ }
216
+ }
282
217
 
283
- const stop = async ({ gracefulFailed } = {}) => {
284
- let unregisterErrorCallback
285
- await Promise.race([
286
- new Promise((resolve, reject) => {
287
- unregisterErrorCallback = registerErrorCallback(reject)
288
- }),
289
- killChildProcess({
290
- signal: gracefulFailed ? GRACEFUL_STOP_FAILED_SIGNAL : STOP_SIGNAL,
291
- }),
292
- ])
293
- unregisterErrorCallback()
218
+ await killProcessTree(childProcess.pid, { signal: STOP_SIGNAL })
219
+ await createStoppedPromise()
220
+ return { graceful: false }
294
221
  }
295
222
 
296
- const gracefulStop = async () => {
297
- let unregisterErrorCallback
298
- await Promise.race([
299
- new Promise((resolve, reject) => {
300
- unregisterErrorCallback = registerErrorCallback(reject)
301
- }),
302
- killChildProcess({ signal: GRACEFUL_STOP_SIGNAL }),
303
- ])
304
- unregisterErrorCallback()
305
- }
223
+ const requestActionOnChildProcess = ({
224
+ signal,
225
+ actionType,
226
+ actionParams,
227
+ }) => {
228
+ const actionAbortable = Abortable.fromSignal(signal)
306
229
 
307
- const requestActionOnChildProcess = ({ actionType, actionParams }) => {
308
230
  return new Promise(async (resolve, reject) => {
231
+ Abortable.throwIfAborted(actionAbortable)
232
+ await childProcessReadyPromise
233
+
309
234
  onceProcessMessage(childProcess, "action-result", ({ status, value }) => {
310
- logger.debug(
311
- createDetailedMessage(`child process sent an action result.`, {
312
- status,
313
- value: JSON.stringify(value, null, " "),
314
- }),
315
- )
316
235
  if (status === "action-completed") {
317
236
  resolve(value)
318
237
  } else {
@@ -327,13 +246,16 @@ ${JSON.stringify(env, null, " ")}`)
327
246
  }),
328
247
  )
329
248
 
330
- await childProcessReadyPromise
331
249
  try {
250
+ Abortable.throwIfAborted(actionAbortable)
332
251
  await sendToProcess(childProcess, "action", {
333
252
  actionType,
334
253
  actionParams,
335
254
  })
336
255
  } catch (e) {
256
+ if (actionAbortable.signal.aborted && e.name === "AbortError") {
257
+ throw e
258
+ }
337
259
  logger.error(
338
260
  createDetailedMessage(`error while sending message to child`, {
339
261
  ["error stack"]: e.stack,
@@ -346,15 +268,11 @@ ${JSON.stringify(env, null, " ")}`)
346
268
 
347
269
  return {
348
270
  execArgv,
349
- gracefulStop,
271
+ stoppedSignal,
272
+ errorSignal,
273
+ outputSignal,
350
274
  stop,
351
- disconnected,
352
- registerErrorCallback,
353
- registerConsoleCallback,
354
275
  requestActionOnChildProcess,
355
- onceChildProcessEvent: (event, callback) => {
356
- onceProcessEvent(childProcess, event, callback)
357
- },
358
276
  }
359
277
  }
360
278
 
@@ -390,36 +308,6 @@ const installProcessOutputListener = (childProcess, callback) => {
390
308
  }
391
309
  }
392
310
 
393
- const installProcessErrorListener = (childProcess, callback) => {
394
- // https://nodejs.org/api/child_process.html#child_process_event_error
395
- const removeErrorListener = onceProcessMessage(
396
- childProcess,
397
- "error",
398
- (error) => {
399
- removeExitListener() // if an error occured we ignore the child process exitCode
400
- callback(error)
401
- },
402
- )
403
- // process.exit(1) in child process or process.exitCode = 1 + process.exit()
404
- // means there was an error even if we don't know exactly what.
405
- const removeExitListener = onceProcessEvent(childProcess, "exit", (code) => {
406
- if (
407
- code !== null &&
408
- code !== 0 &&
409
- code !== SIGINT_EXIT_CODE &&
410
- code !== SIGTERM_EXIT_CODE &&
411
- code !== SIGABORT_EXIT_CODE
412
- ) {
413
- removeErrorListener()
414
- callback(createExitWithFailureCodeError(code))
415
- }
416
- })
417
- return () => {
418
- removeErrorListener()
419
- removeExitListener()
420
- }
421
- }
422
-
423
311
  const createExitWithFailureCodeError = (code) => {
424
312
  if (code === 12) {
425
313
  return new Error(
@@ -0,0 +1,76 @@
1
+ import { require } from "../require.js"
2
+
3
+ // see also https://github.com/sindresorhus/execa/issues/96
4
+ export const killProcessTree = async (
5
+ processId,
6
+ { signal, timeout = 2000 },
7
+ ) => {
8
+ var pidtree = require("pidtree")
9
+
10
+ let descendantProcessIds
11
+ try {
12
+ descendantProcessIds = await pidtree(processId)
13
+ } catch (e) {
14
+ if (e.message === "No matching pid found") {
15
+ descendantProcessIds = []
16
+ } else {
17
+ throw e
18
+ }
19
+ }
20
+ descendantProcessIds.forEach((descendantProcessId) => {
21
+ try {
22
+ process.kill(descendantProcessId, signal)
23
+ } catch (error) {
24
+ // ignore
25
+ }
26
+ })
27
+
28
+ try {
29
+ process.kill(processId, signal)
30
+ } catch (e) {
31
+ if (e.code !== "ESRCH") {
32
+ throw e
33
+ }
34
+ }
35
+
36
+ let remainingIds = [...descendantProcessIds, processId]
37
+
38
+ const updateRemainingIds = () => {
39
+ remainingIds = remainingIds.filter((remainingId) => {
40
+ try {
41
+ process.kill(remainingId, 0)
42
+ return true
43
+ } catch (e) {
44
+ return false
45
+ }
46
+ })
47
+ }
48
+
49
+ let timeSpentWaiting = 0
50
+
51
+ const check = async () => {
52
+ updateRemainingIds()
53
+ if (remainingIds.length === 0) {
54
+ return
55
+ }
56
+
57
+ if (timeSpentWaiting > timeout) {
58
+ const timeoutError = new Error(
59
+ `timed out waiting for ${
60
+ remainingIds.length
61
+ } process to exit (${remainingIds.join(" ")})`,
62
+ )
63
+ timeoutError.code = "TIMEOUT"
64
+ throw timeoutError
65
+ }
66
+
67
+ await new Promise((resolve) => setTimeout(resolve, 400))
68
+ timeSpentWaiting += 400
69
+ await check()
70
+ }
71
+
72
+ await new Promise((resolve) => {
73
+ setTimeout(resolve, 0)
74
+ })
75
+ await check()
76
+ }
@@ -1,3 +1,4 @@
1
+ import v8 from "node:v8"
1
2
  import { uneval } from "@jsenv/uneval"
2
3
 
3
4
  import { jsenvNodeSystemFileInfo } from "@jsenv/core/src/internal/jsenvInternalFiles.js"
@@ -116,13 +117,6 @@ const onceProcessMessage = (type, callback) => {
116
117
  return removeListener
117
118
  }
118
119
 
119
- const onceSIGTERM = (callback) => {
120
- process.once("SIGTERM", callback)
121
- return () => {
122
- process.removeListener("SIGTERM", callback)
123
- }
124
- }
125
-
126
120
  const removeActionRequestListener = onceProcessMessage(
127
121
  ACTION_REQUEST_EVENT_NAME,
128
122
  async ({ actionType, actionParams }) => {
@@ -141,6 +135,15 @@ const removeActionRequestListener = onceProcessMessage(
141
135
  value = e
142
136
  }
143
137
 
138
+ if (process.env.NODE_V8_COVERAGE) {
139
+ v8.takeCoverage()
140
+ // if (actionParams.stopCoverageAfterExecution) {
141
+ // v8.stopCoverage()
142
+ // }
143
+ }
144
+
145
+ // setTimeout(() => {}, 100)
146
+
144
147
  if (failed) {
145
148
  sendActionFailed(value)
146
149
  } else {
@@ -149,8 +152,10 @@ const removeActionRequestListener = onceProcessMessage(
149
152
 
150
153
  // removeActionRequestListener()
151
154
  if (actionParams.exitAfterAction) {
152
- // for some reason this fixes v8 coverage directory somtimes empty
153
- process.exit()
155
+ removeActionRequestListener()
156
+
157
+ // for some reason this fixes v8 coverage directory sometimes empty on Ubuntu
158
+ // process.exit()
154
159
  }
155
160
  },
156
161
  )
@@ -158,6 +163,7 @@ const removeActionRequestListener = onceProcessMessage(
158
163
  // remove listener to process.on('message')
159
164
  // which is sufficient to let child process die
160
165
  // assuming nothing else keeps it alive
161
- onceSIGTERM(removeActionRequestListener)
166
+ // process.once("SIGTERM", removeActionRequestListener)
167
+ // process.once("SIGINT", removeActionRequestListener)
162
168
 
163
169
  setTimeout(() => sendToParent("ready"))
@@ -1,4 +1,4 @@
1
- export const promiseTrackRace = (promiseArray) => {
1
+ export const racePromises = (promiseArray) => {
2
2
  return new Promise((resolve, reject) => {
3
3
  let resolved = false
4
4
 
@@ -7,7 +7,7 @@ export const promiseTrackRace = (promiseArray) => {
7
7
  promise.then((value) => {
8
8
  if (resolved) return
9
9
  resolved = true
10
- resolve({ winner: promise, value, index })
10
+ resolve({ promise, value, index })
11
11
  }, reject)
12
12
  }
13
13