@jsenv/core 23.2.2 → 23.4.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 (56) hide show
  1. package/{license → LICENSE} +0 -0
  2. package/package.json +13 -14
  3. package/readme.md +4 -4
  4. package/src/buildProject.js +12 -13
  5. package/src/execute.js +92 -93
  6. package/src/executeTestPlan.js +9 -8
  7. package/src/internal/browser-launcher/executeHtmlFile.js +26 -23
  8. package/src/internal/building/buildUsingRollup.js +3 -4
  9. package/src/internal/building/build_logs.js +7 -6
  10. package/src/internal/building/createJsenvRollupPlugin.js +9 -14
  11. package/src/internal/building/url_trace.js +3 -4
  12. package/src/internal/compiling/createCompiledFileService.js +21 -6
  13. package/src/internal/compiling/startCompileServer.js +55 -46
  14. package/src/internal/executing/coverage/babel_plugin_instrument.js +1 -0
  15. package/src/internal/executing/coverage/reportToCoverage.js +147 -120
  16. package/src/internal/executing/{coverage → coverage_empty}/createEmptyCoverage.js +0 -0
  17. package/src/internal/executing/coverage_empty/list_files_not_covered.js +20 -0
  18. package/src/internal/executing/{coverage → coverage_empty}/relativeUrlToEmptyCoverage.js +3 -4
  19. package/src/internal/executing/{coverage/generateCoverageHtmlDirectory.js → coverage_reporter/coverage_reporter_html_directory.js} +11 -4
  20. package/src/internal/executing/{coverage/generateCoverageJsonFile.js → coverage_reporter/coverage_reporter_json_file.js} +0 -0
  21. package/src/internal/executing/{coverage/generateCoverageTextLog.js → coverage_reporter/coverage_reporter_text_log.js} +4 -2
  22. package/src/internal/executing/{coverage → coverage_reporter}/istanbulCoverageMapFromCoverage.js +0 -0
  23. package/src/internal/executing/{coverage/normalizeIstanbulCoverage.js → coverage_utils/file_by_file_coverage.js} +9 -7
  24. package/src/internal/executing/coverage_utils/istanbul_coverage_composition.js +28 -0
  25. package/src/internal/executing/coverage_utils/v8_and_istanbul.js +38 -0
  26. package/src/internal/executing/coverage_utils/v8_coverage_composition.js +23 -0
  27. package/src/internal/executing/coverage_utils/v8_coverage_from_directory.js +65 -0
  28. package/src/internal/executing/coverage_utils/v8_coverage_to_istanbul.js +90 -0
  29. package/src/internal/executing/createSummaryLog.js +15 -15
  30. package/src/internal/executing/executeConcurrently.js +92 -32
  31. package/src/internal/executing/executePlan.js +84 -81
  32. package/src/internal/executing/executionLogs.js +14 -18
  33. package/src/internal/executing/execution_colors.js +6 -12
  34. package/src/internal/executing/launchAndExecute.js +172 -169
  35. package/src/internal/node-launcher/createControllableNodeProcess.js +26 -23
  36. package/src/launchBrowser.js +72 -69
  37. package/src/launchNode.js +11 -99
  38. package/src/startExploring.js +2 -17
  39. package/src/abort/abortable.js +0 -172
  40. package/src/abort/callback_list.js +0 -64
  41. package/src/abort/callback_race.js +0 -34
  42. package/src/abort/cleaner.js +0 -22
  43. package/src/abort/main.js +0 -32
  44. package/src/abort/process_teardown_events.js +0 -59
  45. package/src/internal/createCallbackList.js +0 -21
  46. package/src/internal/executing/coverage/composeIstanbulCoverages.js +0 -108
  47. package/src/internal/executing/coverage/composeV8Coverages.js +0 -20
  48. package/src/internal/executing/coverage/istanbulCoverageFromCoverages.js +0 -43
  49. package/src/internal/executing/coverage/istanbulCoverageFromV8Coverage.js +0 -79
  50. package/src/internal/executing/coverage/v8CoverageFromAllV8Coverages.js +0 -40
  51. package/src/internal/executing/coverage/v8CoverageFromNodeV8Directory.js +0 -67
  52. package/src/internal/executing/logUtils.js +0 -30
  53. package/src/internal/executing/writeLog.js +0 -106
  54. package/src/internal/executing/writeLog.test-manual.js +0 -62
  55. package/src/internal/logs/log_style.js +0 -40
  56. package/src/signal/signal.js +0 -65
@@ -1,12 +1,9 @@
1
+ import cuid from "cuid"
1
2
  import { createLogger, createDetailedMessage } from "@jsenv/logger"
3
+ import { Abort, raceCallbacks } from "@jsenv/abort"
4
+ import { resolveUrl, writeFile } from "@jsenv/filesystem"
2
5
 
3
- import { Abortable } from "@jsenv/core/src/abort/main.js"
4
- import { raceCallbacks } from "../../abort/callback_race.js"
5
- import { composeIstanbulCoverages } from "./coverage/composeIstanbulCoverages.js"
6
-
7
- const TIMING_BEFORE_EXECUTION = "before-execution"
8
- const TIMING_DURING_EXECUTION = "during-execution"
9
- const TIMING_AFTER_EXECUTION = "after-execution"
6
+ import { composeTwoFileByFileIstanbulCoverages } from "./coverage_utils/istanbul_coverage_composition.js"
10
7
 
11
8
  export const launchAndExecute = async ({
12
9
  signal = new AbortController().signal,
@@ -24,6 +21,7 @@ export const launchAndExecute = async ({
24
21
  collectRuntimeVersion = false,
25
22
  inheritCoverage = false,
26
23
  collectCoverage = false,
24
+ coverageTempDirectoryUrl,
27
25
  measurePerformance,
28
26
  collectPerformance = false,
29
27
 
@@ -32,7 +30,7 @@ export const launchAndExecute = async ({
32
30
  // however unit test will pass true because they want to move on
33
31
  stopAfterExecute = false,
34
32
  stopAfterExecuteReason = "stop after execute",
35
- // when launch returns { stoppedSignal, gracefulStop, stop }
33
+ // when launch returns { stoppedCallbackList, gracefulStop, stop }
36
34
  // the launched runtime have that amount of ms for disconnected to resolve
37
35
  // before we call stop
38
36
  gracefulStopAllocatedMs = 4000,
@@ -44,8 +42,6 @@ export const launchAndExecute = async ({
44
42
  // by default throw on error after execution
45
43
  throw error
46
44
  },
47
-
48
- coverageV8MergeConflictIsExpected,
49
45
  } = {}) => {
50
46
  const logger = createLogger({ logLevel: launchAndExecuteLogLevel })
51
47
 
@@ -60,27 +56,20 @@ export const launchAndExecute = async ({
60
56
 
61
57
  let executionResultTransformer = (executionResult) => executionResult
62
58
 
63
- const launchAndExecuteOperation = Abortable.fromSignal(signal)
59
+ const launchAndExecuteOperation = Abort.startOperation()
60
+ launchAndExecuteOperation.addAbortSignal(signal)
64
61
 
65
62
  const hasAllocatedMs =
66
63
  typeof allocatedMs === "number" && allocatedMs !== Infinity
67
- let timeoutAbortEffect
64
+ let timeoutAbortSource
68
65
 
69
66
  if (hasAllocatedMs) {
70
- timeoutAbortEffect = Abortable.timeout(
71
- launchAndExecuteOperation,
67
+ timeoutAbortSource = launchAndExecuteOperation.timeout(
72
68
  // FIXME: if allocatedMs is veryyyyyy big
73
69
  // setTimeout may be called immediatly
74
70
  // in that case we should just throw that the number is too big
75
71
  allocatedMs,
76
72
  )
77
- executionResultTransformer = composeTransformer(
78
- executionResultTransformer,
79
- (executionResult) => {
80
- timeoutAbortEffect.cleanup()
81
- return executionResult
82
- },
83
- )
84
73
  }
85
74
 
86
75
  if (mirrorConsole) {
@@ -143,40 +132,61 @@ export const launchAndExecute = async ({
143
132
  executionResultTransformer = composeTransformer(
144
133
  executionResultTransformer,
145
134
  (executionResult) => {
146
- const { coverage, ...rest } = executionResult
147
- // ensure the coverage of the executed file is taken into account
148
- global.__coverage__ = composeIstanbulCoverages([
135
+ const { coverage } = executionResult
136
+ // propagate coverage from execution to this process
137
+ global.__coverage__ = composeTwoFileByFileIstanbulCoverages(
149
138
  global.__coverage__ || {},
150
139
  coverage || {},
151
- ])
152
- if (collectCoverageSaved) {
153
- return executionResult
140
+ )
141
+
142
+ if (!collectCoverageSaved) {
143
+ delete executionResult.coverage
154
144
  }
155
- return rest
145
+
146
+ return executionResult
156
147
  },
157
148
  )
158
149
  }
159
150
 
160
- // indirectCoverage is a feature making possible to collect
161
- // coverage generated by executing a node process which executes
162
- // a browser. The coverage coming the browser executionwould be lost
163
- // if not propagated somehow.
164
- // This is possible if the node process collect the browser coverage
165
- // and write it into global.__indirectCoverage__
166
- // This is used by jsenv during tests execution
167
151
  if (collectCoverage) {
168
152
  executionResultTransformer = composeTransformer(
169
153
  executionResultTransformer,
170
- (executionResult) => {
171
- const { coverage = {}, indirectCoverage } = executionResult
154
+ async (executionResult) => {
155
+ // we do not keep coverage in memory, it can grow very big
156
+ // instead we store it on the filesystem, they will be read and merged together later on
157
+ // in "executeConcurrently"
158
+ const { coverage } = executionResult
159
+ if (coverage) {
160
+ const coverageFileUrl = resolveUrl(
161
+ `./${runtime.name}/${cuid()}`,
162
+ coverageTempDirectoryUrl,
163
+ )
164
+ await writeFile(coverageFileUrl, JSON.stringify(coverage, null, " "))
165
+ executionResult.coverageFileUrl = coverageFileUrl
166
+ delete executionResult.coverage
167
+ }
168
+
169
+ // indirectCoverage is a feature making possible to collect
170
+ // coverage generated by executing a node process which executes
171
+ // a browser. The coverage coming the browser execution would be lost
172
+ // if not propagated somehow.
173
+ // This is possible if the node process collect the browser coverage
174
+ // and write it into global.__indirectCoverage__
175
+ // This is used by jsenv during tests execution
176
+ const { indirectCoverage } = executionResult
172
177
  if (indirectCoverage) {
173
- executionResult.coverage = composeIstanbulCoverages(
174
- [coverage, indirectCoverage],
175
- {
176
- coverageV8MergeConflictIsExpected,
177
- },
178
+ const indirectCoverageFileUrl = resolveUrl(
179
+ `./${runtime.name}/${cuid()}`,
180
+ coverageTempDirectoryUrl,
181
+ )
182
+ await writeFile(
183
+ indirectCoverageFileUrl,
184
+ JSON.stringify(indirectCoverage, null, " "),
178
185
  )
186
+ executionResult.indirectCoverageFileUrl = indirectCoverageFileUrl
187
+ delete executionResult.indirectCoverage
179
188
  }
189
+
180
190
  return executionResult
181
191
  },
182
192
  )
@@ -188,31 +198,12 @@ export const launchAndExecute = async ({
188
198
  // executionResult.coverage is undefined or {}
189
199
  // we delete it just to have a cleaner object
190
200
  delete executionResult.coverage
201
+ delete executionResult.indirectCoverage
191
202
  return executionResult
192
203
  },
193
204
  )
194
205
  }
195
206
 
196
- if (stopAfterExecute) {
197
- executionResultTransformer = composeTransformer(
198
- executionResultTransformer,
199
- async (executionResult) => {
200
- // if there is an error while cleaning (stopping the runtime)
201
- // the execution is considered as failed
202
- try {
203
- await launchAndExecuteOperation.cleaner.clean(stopAfterExecuteReason)
204
- return executionResult
205
- } catch (e) {
206
- return executionResultTransformer(
207
- createErroredExecutionResult({
208
- error: e,
209
- }),
210
- )
211
- }
212
- },
213
- )
214
- }
215
-
216
207
  if (measureDuration) {
217
208
  const startMs = Date.now()
218
209
  executionResultTransformer = composeTransformer(
@@ -230,7 +221,7 @@ export const launchAndExecute = async ({
230
221
  const runtimeLabel = `${runtime.name}/${runtime.version}`
231
222
  logger.debug(`launch ${runtimeLabel} to execute something in it`)
232
223
 
233
- Abortable.throwIfAborted(launchAndExecuteOperation)
224
+ launchAndExecuteOperation.throwIfAborted()
234
225
  const launchReturnValue = await runtime.launch({
235
226
  signal: launchAndExecuteOperation.signal,
236
227
  logger,
@@ -240,14 +231,19 @@ export const launchAndExecute = async ({
240
231
  ...runtimeParams,
241
232
  })
242
233
  validateLaunchReturnValue(launchReturnValue)
243
- launchAndExecuteOperation.cleaner.addCallback(async (reason) => {
244
- await stopRuntime({
245
- logger,
246
- runtimeLabel,
247
- launchReturnValue,
248
- gracefulStopAllocatedMs,
249
- reason,
250
- })
234
+
235
+ const stopRuntime = async (reason) => {
236
+ const { stop } = launchReturnValue
237
+ logger.debug(`${runtimeLabel}: stop() because ${reason}`)
238
+ const { graceful } = await stop({ reason, gracefulStopAllocatedMs })
239
+ if (graceful) {
240
+ logger.debug(`${runtimeLabel}: runtime stopped gracefully`)
241
+ } else {
242
+ logger.debug(`${runtimeLabel}: runtime stopped`)
243
+ }
244
+ }
245
+ launchAndExecuteOperation.addAbortCallback(async () => {
246
+ await stopRuntime("Operation aborted")
251
247
  })
252
248
 
253
249
  logger.debug(createDetailedMessage(`${runtimeLabel}: runtime launched`))
@@ -255,9 +251,9 @@ export const launchAndExecute = async ({
255
251
 
256
252
  logger.debug(`${runtimeLabel}: start execution`)
257
253
  const {
258
- errorSignal,
259
- outputSignal,
260
- stoppedSignal,
254
+ errorCallbackList,
255
+ outputCallbackList,
256
+ stoppedCallbackList,
261
257
  execute,
262
258
  finalizeExecutionResult = (executionResult) => executionResult,
263
259
  } = launchReturnValue
@@ -265,104 +261,60 @@ export const launchAndExecute = async ({
265
261
  executionResultTransformer,
266
262
  finalizeExecutionResult,
267
263
  )
268
- outputSignal.addCallback(runtimeConsoleCallback)
269
-
270
- let timing = TIMING_BEFORE_EXECUTION
271
- stoppedSignal.addCallback(() => {
272
- logger.debug(`${runtimeLabel}: runtime stopped ${timing}`)
273
- runtimeStoppedCallback()
274
- })
264
+ outputCallbackList.add(runtimeConsoleCallback)
275
265
 
276
- const winnerPromise = new Promise((resolve, reject) => {
277
- raceCallbacks(
278
- {
279
- aborted: (cb) => {
280
- launchAndExecuteOperation.signal.addEventListener("abort", cb)
281
- return () => {
282
- launchAndExecuteOperation.signal.removeEventListener("abort", cb)
283
- }
284
- },
285
- error: (cb) => {
286
- return errorSignal.addCallback(cb)
287
- },
288
- stopped: (cb) => {
289
- return stoppedSignal.addCallback(cb)
290
- },
291
- executed: (cb) => {
292
- timing = TIMING_DURING_EXECUTION
293
- const executed = execute({
294
- signal: launchAndExecuteOperation.signal,
295
- ...executeParams,
296
- })
297
- executed.then(cb, reject)
298
- },
299
- },
300
- resolve,
301
- )
266
+ let executionResult = await callExecute({
267
+ launchAndExecuteOperation,
268
+ errorCallbackList,
269
+ stoppedCallbackList,
270
+ execute,
271
+ executeParams,
302
272
  })
303
273
 
304
- Abortable.throwIfAborted(launchAndExecuteOperation)
305
-
306
- const winner = await winnerPromise
307
-
308
- if (winner.name === "aborted") {
309
- Abortable.throwIfAborted(launchAndExecuteOperation)
310
- }
311
-
312
- if (winner.name === "error") {
313
- return executionResultTransformer(
314
- createErroredExecutionResult({
315
- error: winner.data,
316
- }),
317
- )
318
- }
319
-
320
- if (winner.name === "stopped") {
321
- return executionResultTransformer(
322
- createErroredExecutionResult({
323
- error: new Error(`runtime stopped during execution`),
324
- }),
325
- )
326
- }
327
-
328
- timing = TIMING_AFTER_EXECUTION
329
-
330
- if (!stopAfterExecute) {
274
+ if (stopAfterExecute) {
275
+ // stopping runtime is part of the execution
276
+ try {
277
+ await stopRuntime(stopAfterExecuteReason)
278
+ } catch (e) {
279
+ executionResult = createErroredExecutionResult({
280
+ error: e,
281
+ })
282
+ }
283
+ } else {
331
284
  // when the process is still alive
332
285
  // we want to catch error to notify runtimeErrorAfterExecutionCallback
333
286
  // and throw that error by default
334
- errorSignal.addCallback((error) => {
287
+ errorCallbackList.add((error) => {
335
288
  runtimeErrorAfterExecutionCallback(error)
336
289
  })
290
+ stoppedCallbackList.add(() => {
291
+ logger.debug(`${runtimeLabel}: runtime stopped after execution`)
292
+ runtimeStoppedCallback()
293
+ })
337
294
  }
338
295
 
339
- const executeResult = winner.data
340
- const { status } = executeResult
341
-
342
- if (status === "errored") {
296
+ if (executionResult.status === "errored") {
343
297
  // debug log level because this error happens during execution
344
298
  // there is no need to log it.
345
299
  // the code will know the execution errored because it receives
346
300
  // an errored execution result
347
301
  logger.debug(
348
- createDetailedMessage(`error ${TIMING_DURING_EXECUTION}`, {
349
- ["error stack"]: executeResult.error.stack,
302
+ createDetailedMessage(`error during execution`, {
303
+ ["error stack"]: executionResult.error.stack,
350
304
  ["execute params"]: JSON.stringify(executeParams, null, " "),
351
305
  ["runtime"]: runtime,
352
306
  }),
353
307
  )
354
- return executionResultTransformer(
355
- createErroredExecutionResult(executeResult),
356
- )
308
+ } else {
309
+ logger.debug(`${runtimeLabel}: execution ${executionResult.status}`)
357
310
  }
358
311
 
359
- logger.debug(`${runtimeLabel}: execution completed`)
360
- return executionResultTransformer(
361
- createCompletedExecutionResult(executeResult),
362
- )
312
+ return executionResultTransformer(executionResult)
363
313
  } catch (e) {
364
- if (e.name === "AbortError") {
365
- if (timeoutAbortEffect && timeoutAbortEffect.signal.aborted) {
314
+ if (Abort.isAbortError(e)) {
315
+ // we should stop runtime too
316
+
317
+ if (timeoutAbortSource && timeoutAbortSource.signal.aborted) {
366
318
  const executionResult = createTimedoutExecutionResult()
367
319
  return executionResultTransformer(executionResult)
368
320
  }
@@ -370,24 +322,71 @@ export const launchAndExecute = async ({
370
322
  return executionResultTransformer(executionResult)
371
323
  }
372
324
  throw e
325
+ } finally {
326
+ await launchAndExecuteOperation.end()
373
327
  }
374
328
  }
375
329
 
376
- const stopRuntime = async ({
377
- logger,
378
- runtimeLabel,
379
- launchReturnValue,
380
- gracefulStopAllocatedMs,
381
- reason,
330
+ const callExecute = async ({
331
+ launchAndExecuteOperation,
332
+ errorCallbackList,
333
+ stoppedCallbackList,
334
+ execute,
335
+ executeParams,
382
336
  }) => {
383
- const { stop } = launchReturnValue
384
- logger.debug(`${runtimeLabel}: stop() because ${reason}`)
385
- const { graceful } = await stop({ reason, gracefulStopAllocatedMs })
386
- if (graceful) {
387
- logger.debug(`${runtimeLabel}: runtime stopped gracefully`)
388
- } else {
389
- logger.debug(`${runtimeLabel}: runtime stopped`)
337
+ const winnerPromise = new Promise((resolve, reject) => {
338
+ raceCallbacks(
339
+ {
340
+ aborted: (cb) => {
341
+ launchAndExecuteOperation.signal.addEventListener("abort", cb)
342
+ return () => {
343
+ launchAndExecuteOperation.signal.removeEventListener("abort", cb)
344
+ }
345
+ },
346
+ error: (cb) => {
347
+ return errorCallbackList.add(cb)
348
+ },
349
+ stopped: (cb) => {
350
+ return stoppedCallbackList.add(cb)
351
+ },
352
+ executed: (cb) => {
353
+ const executed = execute({
354
+ signal: launchAndExecuteOperation.signal,
355
+ ...executeParams,
356
+ })
357
+ executed.then(cb, reject)
358
+ },
359
+ },
360
+ resolve,
361
+ )
362
+ })
363
+
364
+ launchAndExecuteOperation.throwIfAborted()
365
+ const winner = await winnerPromise
366
+
367
+ if (winner.name === "aborted") {
368
+ launchAndExecuteOperation.throwIfAborted()
369
+ }
370
+
371
+ if (winner.name === "error") {
372
+ return createErroredExecutionResult({
373
+ error: winner.data,
374
+ })
390
375
  }
376
+
377
+ if (winner.name === "stopped") {
378
+ return createErroredExecutionResult({
379
+ error: new Error(`runtime stopped during execution`),
380
+ })
381
+ }
382
+
383
+ const executeResult = winner.data
384
+ const { status } = executeResult
385
+
386
+ if (status === "errored") {
387
+ return createErroredExecutionResult(executeResult)
388
+ }
389
+ return createCompletedExecutionResult(executeResult)
391
390
  }
392
391
 
393
392
  const createAbortedExecutionResult = () => {
@@ -444,22 +443,26 @@ const composeTransformer = (previousTransformer, transformer) => {
444
443
 
445
444
  const validateLaunchReturnValue = (launchReturnValue) => {
446
445
  if (launchReturnValue === null) {
447
- throw new Error(`launch must return an object, got null`)
446
+ throw new Error(`runtime.launch must return an object, got null`)
448
447
  }
449
448
 
450
449
  if (typeof launchReturnValue !== "object") {
451
- throw new Error(`launch must return an object, got ${launchReturnValue}`)
450
+ throw new Error(
451
+ `runtime.launch must return an object, got ${launchReturnValue}`,
452
+ )
452
453
  }
453
454
 
454
455
  const { execute } = launchReturnValue
455
456
  if (typeof execute !== "function") {
456
- throw new Error(`launch must return an execute function, got ${execute}`)
457
+ throw new Error(
458
+ `runtime.launch must return an execute function, got ${execute}`,
459
+ )
457
460
  }
458
461
 
459
- const { stoppedSignal } = launchReturnValue
460
- if (!stoppedSignal) {
462
+ const { stoppedCallbackList } = launchReturnValue
463
+ if (!stoppedCallbackList) {
461
464
  throw new Error(
462
- `launch must return a stoppedSignal object, got ${stoppedSignal}`,
465
+ `runtime.launch must return a stoppedCallbackList object, got ${stoppedCallbackList}`,
463
466
  )
464
467
  }
465
468
  }
@@ -6,10 +6,13 @@ import {
6
6
  resolveUrl,
7
7
  assertFilePresence,
8
8
  } from "@jsenv/filesystem"
9
+ import {
10
+ Abort,
11
+ raceCallbacks,
12
+ createCallbackList,
13
+ createCallbackListNotifiedOnce,
14
+ } from "@jsenv/abort"
9
15
 
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
16
  import { nodeSupportsDynamicImport } from "../runtime/node-feature-detect/nodeSupportsDynamicImport.js"
14
17
  import { jsenvCoreDirectoryUrl } from "../jsenvCoreDirectoryUrl.js"
15
18
  import { createChildProcessOptions } from "./createChildProcessOptions.js"
@@ -117,19 +120,18 @@ export const createControllableNodeProcess = async ({
117
120
  onceProcessMessage(childProcess, "ready", resolve)
118
121
  })
119
122
 
120
- const outputSignal = createSignal()
123
+ const outputCallbackList = createCallbackList()
121
124
  const removeOutputListener = installProcessOutputListener(
122
125
  childProcess,
123
126
  ({ type, text }) => {
124
- outputSignal.emit({ type, text })
127
+ outputCallbackList.notify({ type, text })
125
128
  },
126
129
  )
127
130
 
128
- const errorSignal = createSignal()
131
+ const errorCallbackList = createCallbackList()
132
+
133
+ const stoppedCallbackList = createCallbackListNotifiedOnce()
129
134
 
130
- const stoppedSignal = createSignal({
131
- once: true,
132
- })
133
135
  raceCallbacks(
134
136
  {
135
137
  // https://nodejs.org/api/child_process.html#child_process_event_disconnect
@@ -149,7 +151,7 @@ export const createControllableNodeProcess = async ({
149
151
  (winner) => {
150
152
  const raceEffects = {
151
153
  // disconnect: () => {
152
- // stoppedSignal.emit()
154
+ // stoppedCallbackList.notify()
153
155
  // },
154
156
  error: (error) => {
155
157
  removeOutputListener()
@@ -159,7 +161,7 @@ export const createControllableNodeProcess = async ({
159
161
  ) {
160
162
  return
161
163
  }
162
- errorSignal.emit(error)
164
+ errorCallbackList.notify(error)
163
165
  },
164
166
  exit: ({ code, signal }) => {
165
167
  // process.exit(1) in child process or process.exitCode = 1 + process.exit()
@@ -171,9 +173,9 @@ export const createControllableNodeProcess = async ({
171
173
  code !== SIGTERM_EXIT_CODE &&
172
174
  code !== SIGABORT_EXIT_CODE
173
175
  ) {
174
- errorSignal.emit(createExitWithFailureCodeError(code))
176
+ errorCallbackList.notify(createExitWithFailureCodeError(code))
175
177
  }
176
- stoppedSignal.emit({ code, signal })
178
+ stoppedCallbackList.notify({ code, signal })
177
179
  },
178
180
  }
179
181
  raceEffects[winner.name](winner.data)
@@ -181,15 +183,15 @@ export const createControllableNodeProcess = async ({
181
183
  )
182
184
 
183
185
  const stop = async ({ gracefulStopAllocatedMs } = {}) => {
184
- if (stoppedSignal.emitted) {
186
+ if (stoppedCallbackList.notified) {
185
187
  return {}
186
188
  }
187
189
 
188
190
  const createStoppedPromise = async () => {
189
- if (stoppedSignal.emitted) {
191
+ if (stoppedCallbackList.notified) {
190
192
  return
191
193
  }
192
- await new Promise((resolve) => stoppedSignal.addCallback(resolve))
194
+ await new Promise((resolve) => stoppedCallbackList.add(resolve))
193
195
  }
194
196
 
195
197
  if (gracefulStopAllocatedMs) {
@@ -225,10 +227,11 @@ export const createControllableNodeProcess = async ({
225
227
  actionType,
226
228
  actionParams,
227
229
  }) => {
228
- const actionAbortable = Abortable.fromSignal(signal)
230
+ const actionOperation = Abort.startOperation()
231
+ actionOperation.addAbortSignal(signal)
229
232
 
230
233
  return new Promise(async (resolve, reject) => {
231
- Abortable.throwIfAborted(actionAbortable)
234
+ actionOperation.throwIfAborted()
232
235
  await childProcessReadyPromise
233
236
 
234
237
  onceProcessMessage(childProcess, "action-result", ({ status, value }) => {
@@ -247,13 +250,13 @@ export const createControllableNodeProcess = async ({
247
250
  )
248
251
 
249
252
  try {
250
- Abortable.throwIfAborted(actionAbortable)
253
+ actionOperation.throwIfAborted()
251
254
  await sendToProcess(childProcess, "action", {
252
255
  actionType,
253
256
  actionParams,
254
257
  })
255
258
  } catch (e) {
256
- if (actionAbortable.signal.aborted && e.name === "AbortError") {
259
+ if (Abort.isAbortError(e) && actionOperation.signal.aborted) {
257
260
  throw e
258
261
  }
259
262
  logger.error(
@@ -268,9 +271,9 @@ export const createControllableNodeProcess = async ({
268
271
 
269
272
  return {
270
273
  execArgv,
271
- stoppedSignal,
272
- errorSignal,
273
- outputSignal,
274
+ stoppedCallbackList,
275
+ errorCallbackList,
276
+ outputCallbackList,
274
277
  stop,
275
278
  requestActionOnChildProcess,
276
279
  }