@jsenv/core 23.2.2 → 23.3.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.
- package/{license → LICENSE} +0 -0
- package/package.json +13 -14
- package/src/buildProject.js +12 -13
- package/src/execute.js +92 -93
- package/src/internal/browser-launcher/executeHtmlFile.js +6 -7
- package/src/internal/building/buildUsingRollup.js +3 -4
- package/src/internal/building/build_logs.js +7 -6
- package/src/internal/building/createJsenvRollupPlugin.js +9 -14
- package/src/internal/building/url_trace.js +3 -4
- package/src/internal/compiling/createCompiledFileService.js +8 -5
- package/src/internal/compiling/startCompileServer.js +55 -46
- package/src/internal/executing/coverage/relativeUrlToEmptyCoverage.js +2 -3
- package/src/internal/executing/createSummaryLog.js +12 -14
- package/src/internal/executing/executeConcurrently.js +7 -5
- package/src/internal/executing/executePlan.js +80 -80
- package/src/internal/executing/executionLogs.js +14 -18
- package/src/internal/executing/execution_colors.js +6 -12
- package/src/internal/executing/launchAndExecute.js +125 -145
- package/src/internal/node-launcher/createControllableNodeProcess.js +26 -23
- package/src/launchBrowser.js +64 -61
- package/src/launchNode.js +6 -6
- package/src/startExploring.js +2 -17
- package/src/abort/abortable.js +0 -172
- package/src/abort/callback_list.js +0 -64
- package/src/abort/callback_race.js +0 -34
- package/src/abort/cleaner.js +0 -22
- package/src/abort/main.js +0 -32
- package/src/abort/process_teardown_events.js +0 -59
- package/src/internal/createCallbackList.js +0 -21
- package/src/internal/executing/logUtils.js +0 -30
- package/src/internal/executing/writeLog.js +0 -106
- package/src/internal/executing/writeLog.test-manual.js +0 -62
- package/src/internal/logs/log_style.js +0 -40
- package/src/signal/signal.js +0 -65
package/src/launchBrowser.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
// https://github.com/microsoft/playwright/blob/master/docs/api.md
|
|
2
2
|
|
|
3
3
|
import { createDetailedMessage } from "@jsenv/logger"
|
|
4
|
-
|
|
5
4
|
import {
|
|
6
|
-
|
|
5
|
+
Abort,
|
|
6
|
+
createCallbackListNotifiedOnce,
|
|
7
|
+
createCallbackList,
|
|
7
8
|
raceProcessTeardownEvents,
|
|
8
|
-
} from "@jsenv/
|
|
9
|
-
import {
|
|
9
|
+
} from "@jsenv/abort"
|
|
10
|
+
import { memoize } from "@jsenv/filesystem"
|
|
11
|
+
|
|
10
12
|
import { fetchUrl } from "./internal/fetchUrl.js"
|
|
11
13
|
import { validateResponse } from "./internal/response_validation.js"
|
|
12
14
|
import { trackPageToNotify } from "./internal/browser-launcher/trackPageToNotify.js"
|
|
@@ -45,7 +47,9 @@ chromiumRuntime.launch = async ({
|
|
|
45
47
|
stopOnExit = true,
|
|
46
48
|
share = false,
|
|
47
49
|
}) => {
|
|
48
|
-
const launchBrowserOperation =
|
|
50
|
+
const launchBrowserOperation = Abort.startOperation()
|
|
51
|
+
launchBrowserOperation.addAbortSignal(signal)
|
|
52
|
+
|
|
49
53
|
const sharingToken = share
|
|
50
54
|
? chromiumSharing.getSharingToken({
|
|
51
55
|
chromiumExecutablePath,
|
|
@@ -54,9 +58,7 @@ chromiumRuntime.launch = async ({
|
|
|
54
58
|
debugPort,
|
|
55
59
|
})
|
|
56
60
|
: chromiumSharing.getUniqueSharingToken()
|
|
57
|
-
|
|
58
61
|
if (!sharingToken.isUsed()) {
|
|
59
|
-
Abortable.throwIfAborted(launchBrowserOperation)
|
|
60
62
|
const { chromium } = await import("playwright")
|
|
61
63
|
const launchOperation = launchBrowser("chromium", {
|
|
62
64
|
browserClass: chromium,
|
|
@@ -77,10 +79,9 @@ chromiumRuntime.launch = async ({
|
|
|
77
79
|
sharingToken.setSharedValue(launchOperation)
|
|
78
80
|
}
|
|
79
81
|
|
|
80
|
-
const [
|
|
81
|
-
launchBrowserOperation.
|
|
82
|
-
|
|
83
|
-
const browser = await launchOperation
|
|
82
|
+
const [browserPromise, stopUsingBrowser] = sharingToken.useSharedValue()
|
|
83
|
+
launchBrowserOperation.addEndCallback(stopUsingBrowser)
|
|
84
|
+
const browser = await browserPromise
|
|
84
85
|
|
|
85
86
|
if (debug) {
|
|
86
87
|
// https://github.com/puppeteer/puppeteer/blob/v2.0.0/docs/api.md#browserwsendpoint
|
|
@@ -105,7 +106,6 @@ chromiumRuntime.launch = async ({
|
|
|
105
106
|
}
|
|
106
107
|
|
|
107
108
|
const browserHooks = browserToRuntimeHooks(browser, {
|
|
108
|
-
launchBrowserOperation,
|
|
109
109
|
browserServerLogLevel,
|
|
110
110
|
|
|
111
111
|
projectDirectoryUrl,
|
|
@@ -158,13 +158,13 @@ firefoxRuntime.launch = async ({
|
|
|
158
158
|
stopOnExit = true,
|
|
159
159
|
share = false,
|
|
160
160
|
}) => {
|
|
161
|
-
const launchBrowserOperation =
|
|
161
|
+
const launchBrowserOperation = Abort.startOperation()
|
|
162
|
+
launchBrowserOperation.addAbortSignal(signal)
|
|
163
|
+
|
|
162
164
|
const sharingToken = share
|
|
163
165
|
? firefoxSharing.getSharingToken({ firefoxExecutablePath, headless })
|
|
164
166
|
: firefoxSharing.getUniqueSharingToken()
|
|
165
|
-
|
|
166
167
|
if (!sharingToken.isUsed()) {
|
|
167
|
-
Abortable.throwIfAborted(launchBrowserOperation)
|
|
168
168
|
const { firefox } = await import("playwright")
|
|
169
169
|
const launchOperation = launchBrowser("firefox", {
|
|
170
170
|
browserClass: firefox,
|
|
@@ -179,10 +179,9 @@ firefoxRuntime.launch = async ({
|
|
|
179
179
|
sharingToken.setSharedValue(launchOperation)
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
-
const [
|
|
183
|
-
launchBrowserOperation.
|
|
184
|
-
|
|
185
|
-
const browser = await launchOperation
|
|
182
|
+
const [browserPromise, stopUsingBrowser] = sharingToken.useSharedValue()
|
|
183
|
+
launchBrowserOperation.addEndCallback(stopUsingBrowser)
|
|
184
|
+
const browser = await browserPromise
|
|
186
185
|
|
|
187
186
|
const browserHooks = browserToRuntimeHooks(browser, {
|
|
188
187
|
launchBrowserOperation,
|
|
@@ -237,13 +236,14 @@ webkitRuntime.launch = async ({
|
|
|
237
236
|
stopOnExit = true,
|
|
238
237
|
share = false,
|
|
239
238
|
}) => {
|
|
240
|
-
const launchBrowserOperation =
|
|
239
|
+
const launchBrowserOperation = Abort.startOperation()
|
|
240
|
+
launchBrowserOperation.addAbortSignal(signal)
|
|
241
|
+
|
|
241
242
|
const sharingToken = share
|
|
242
243
|
? webkitSharing.getSharingToken({ webkitExecutablePath, headless })
|
|
243
244
|
: webkitSharing.getUniqueSharingToken()
|
|
244
245
|
|
|
245
246
|
if (!sharingToken.isUsed()) {
|
|
246
|
-
Abortable.throwIfAborted(launchBrowserOperation)
|
|
247
247
|
const { webkit } = await import("playwright")
|
|
248
248
|
const launchOperation = launchBrowser("webkit", {
|
|
249
249
|
browserClass: webkit,
|
|
@@ -257,10 +257,9 @@ webkitRuntime.launch = async ({
|
|
|
257
257
|
sharingToken.setSharedValue(launchOperation)
|
|
258
258
|
}
|
|
259
259
|
|
|
260
|
-
const [
|
|
261
|
-
launchBrowserOperation.
|
|
262
|
-
|
|
263
|
-
const browser = await launchOperation
|
|
260
|
+
const [browserPromise, stopUsingBrowser] = sharingToken.useSharedValue()
|
|
261
|
+
launchBrowserOperation.addEndCallback(stopUsingBrowser)
|
|
262
|
+
const browser = await browserPromise
|
|
264
263
|
|
|
265
264
|
const browserHooks = browserToRuntimeHooks(browser, {
|
|
266
265
|
launchBrowserOperation,
|
|
@@ -314,8 +313,8 @@ const launchBrowser = async (
|
|
|
314
313
|
{ launchBrowserOperation, browserClass, options, stopOnExit },
|
|
315
314
|
) => {
|
|
316
315
|
if (stopOnExit) {
|
|
317
|
-
|
|
318
|
-
raceProcessTeardownEvents(
|
|
316
|
+
launchBrowserOperation.addAbortSource((abort) => {
|
|
317
|
+
return raceProcessTeardownEvents(
|
|
319
318
|
{
|
|
320
319
|
SIGHUP: true,
|
|
321
320
|
SIGTERM: true,
|
|
@@ -323,9 +322,9 @@ const launchBrowser = async (
|
|
|
323
322
|
beforeExit: true,
|
|
324
323
|
exit: true,
|
|
325
324
|
},
|
|
326
|
-
|
|
327
|
-
)
|
|
328
|
-
)
|
|
325
|
+
abort,
|
|
326
|
+
)
|
|
327
|
+
})
|
|
329
328
|
}
|
|
330
329
|
|
|
331
330
|
try {
|
|
@@ -337,19 +336,16 @@ const launchBrowser = async (
|
|
|
337
336
|
handleSIGTERM: false,
|
|
338
337
|
handleSIGHUP: false,
|
|
339
338
|
})
|
|
340
|
-
launchBrowserOperation.
|
|
341
|
-
await stopBrowser(browser)
|
|
342
|
-
})
|
|
343
|
-
if (launchBrowserOperation.signal.aborted) {
|
|
344
|
-
await launchBrowserOperation.cleaner.clean()
|
|
345
|
-
Abortable.throwIfAborted(launchBrowserOperation)
|
|
346
|
-
}
|
|
339
|
+
launchBrowserOperation.throwIfAborted()
|
|
347
340
|
return browser
|
|
348
341
|
} catch (e) {
|
|
349
342
|
if (launchBrowserOperation.signal.aborted && isTargetClosedError(e)) {
|
|
350
|
-
|
|
343
|
+
// rethrow the abort error
|
|
344
|
+
launchBrowserOperation.throwIfAborted()
|
|
351
345
|
}
|
|
352
346
|
throw e
|
|
347
|
+
} finally {
|
|
348
|
+
await launchBrowserOperation.end()
|
|
353
349
|
}
|
|
354
350
|
}
|
|
355
351
|
|
|
@@ -375,8 +371,6 @@ const stopBrowser = async (browser) => {
|
|
|
375
371
|
const browserToRuntimeHooks = (
|
|
376
372
|
browser,
|
|
377
373
|
{
|
|
378
|
-
launchBrowserOperation,
|
|
379
|
-
|
|
380
374
|
projectDirectoryUrl,
|
|
381
375
|
compileServerOrigin,
|
|
382
376
|
outDirectoryRelativeUrl,
|
|
@@ -391,26 +385,40 @@ const browserToRuntimeHooks = (
|
|
|
391
385
|
transformErrorHook = (error) => error,
|
|
392
386
|
},
|
|
393
387
|
) => {
|
|
394
|
-
const
|
|
388
|
+
const stopCallbackList = createCallbackListNotifiedOnce()
|
|
389
|
+
const stoppedCallbackList = createCallbackListNotifiedOnce()
|
|
390
|
+
const stop = memoize(async (reason) => {
|
|
391
|
+
await stopCallbackList.notify({ reason })
|
|
392
|
+
stoppedCallbackList.notify({ reason })
|
|
393
|
+
return { graceful: false }
|
|
394
|
+
})
|
|
395
|
+
|
|
395
396
|
// https://github.com/GoogleChrome/puppeteer/blob/v1.4.0/docs/api.md#event-disconnected
|
|
396
|
-
browser.on("disconnected",
|
|
397
|
+
browser.on("disconnected", () => {
|
|
398
|
+
stop()
|
|
399
|
+
})
|
|
400
|
+
|
|
401
|
+
stopCallbackList.add(async () => {
|
|
402
|
+
await stopBrowser(browser)
|
|
403
|
+
})
|
|
397
404
|
|
|
398
|
-
const
|
|
405
|
+
const errorCallbackList = createCallbackList()
|
|
399
406
|
|
|
400
|
-
const
|
|
407
|
+
const outputCallbackList = createCallbackList()
|
|
401
408
|
|
|
402
409
|
const execute = async ({
|
|
403
410
|
signal,
|
|
404
411
|
fileRelativeUrl,
|
|
405
412
|
ignoreHTTPSErrors = true, // we mostly use self signed certificates during tests
|
|
406
413
|
}) => {
|
|
407
|
-
const executeOperation =
|
|
408
|
-
|
|
414
|
+
const executeOperation = Abort.startOperation()
|
|
415
|
+
executeOperation.addAbortSignal(signal)
|
|
416
|
+
executeOperation.throwIfAborted()
|
|
409
417
|
// open a tab to execute to the file
|
|
410
418
|
const browserContext = await browser.newContext({ ignoreHTTPSErrors })
|
|
411
|
-
|
|
419
|
+
executeOperation.throwIfAborted()
|
|
412
420
|
const page = await browserContext.newPage()
|
|
413
|
-
|
|
421
|
+
executeOperation.addEndCallback(async () => {
|
|
414
422
|
try {
|
|
415
423
|
await browserContext.close()
|
|
416
424
|
} catch (e) {
|
|
@@ -425,15 +433,15 @@ const browserToRuntimeHooks = (
|
|
|
425
433
|
onError: (error) => {
|
|
426
434
|
error = transformErrorHook(error)
|
|
427
435
|
if (!ignoreErrorHook(error)) {
|
|
428
|
-
|
|
436
|
+
errorCallbackList.notify(error)
|
|
429
437
|
}
|
|
430
438
|
},
|
|
431
|
-
onConsole:
|
|
439
|
+
onConsole: outputCallbackList.notify,
|
|
432
440
|
})
|
|
433
|
-
|
|
441
|
+
stoppedCallbackList.add(stopTrackingToNotify)
|
|
434
442
|
|
|
435
443
|
const result = await executeHtmlFile(fileRelativeUrl, {
|
|
436
|
-
|
|
444
|
+
executeOperation,
|
|
437
445
|
|
|
438
446
|
projectDirectoryUrl,
|
|
439
447
|
compileServerOrigin,
|
|
@@ -451,17 +459,12 @@ const browserToRuntimeHooks = (
|
|
|
451
459
|
return result
|
|
452
460
|
}
|
|
453
461
|
|
|
454
|
-
Abortable.cleanOnAbort(launchBrowserOperation)
|
|
455
|
-
|
|
456
462
|
return {
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
463
|
+
stoppedCallbackList,
|
|
464
|
+
errorCallbackList,
|
|
465
|
+
outputCallbackList,
|
|
460
466
|
execute,
|
|
461
|
-
stop
|
|
462
|
-
await launchBrowserOperation.cleaner.clean(reason)
|
|
463
|
-
return { graceful: false }
|
|
464
|
-
},
|
|
467
|
+
stop,
|
|
465
468
|
}
|
|
466
469
|
}
|
|
467
470
|
|
package/src/launchNode.js
CHANGED
|
@@ -132,9 +132,9 @@ nodeRuntime.launch = async ({
|
|
|
132
132
|
const logLevel = loggerToLogLevel(logger)
|
|
133
133
|
const {
|
|
134
134
|
execArgv,
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
135
|
+
stoppedCallbackList,
|
|
136
|
+
errorCallbackList,
|
|
137
|
+
outputCallbackList,
|
|
138
138
|
stop,
|
|
139
139
|
requestActionOnChildProcess,
|
|
140
140
|
} = await createControllableNodeProcess({
|
|
@@ -193,9 +193,9 @@ nodeRuntime.launch = async ({
|
|
|
193
193
|
// because process.env is very big
|
|
194
194
|
// env,
|
|
195
195
|
},
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
196
|
+
stoppedCallbackList,
|
|
197
|
+
errorCallbackList,
|
|
198
|
+
outputCallbackList,
|
|
199
199
|
stop,
|
|
200
200
|
execute,
|
|
201
201
|
finalizeExecutionResult,
|
package/src/startExploring.js
CHANGED
|
@@ -4,10 +4,6 @@ import {
|
|
|
4
4
|
urlToRelativeUrl,
|
|
5
5
|
} from "@jsenv/filesystem"
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
Abortable,
|
|
9
|
-
raceProcessTeardownEvents,
|
|
10
|
-
} from "@jsenv/core/src/abort/main.js"
|
|
11
7
|
import { jsenvCoreDirectoryUrl } from "./internal/jsenvCoreDirectoryUrl.js"
|
|
12
8
|
import {
|
|
13
9
|
assertProjectDirectoryUrl,
|
|
@@ -60,18 +56,6 @@ export const startExploring = async ({
|
|
|
60
56
|
projectDirectoryUrl = assertProjectDirectoryUrl({ projectDirectoryUrl })
|
|
61
57
|
await assertProjectDirectoryExists({ projectDirectoryUrl })
|
|
62
58
|
|
|
63
|
-
const exploringServerOperation = Abortable.fromSignal(signal)
|
|
64
|
-
if (handleSIGINT) {
|
|
65
|
-
Abortable.effect(exploringServerOperation, (cb) =>
|
|
66
|
-
raceProcessTeardownEvents(
|
|
67
|
-
{
|
|
68
|
-
SIGINT: true,
|
|
69
|
-
},
|
|
70
|
-
cb,
|
|
71
|
-
),
|
|
72
|
-
)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
59
|
const outDirectoryRelativeUrl = computeOutDirectoryRelativeUrl({
|
|
76
60
|
projectDirectoryUrl,
|
|
77
61
|
jsenvDirectoryRelativeUrl,
|
|
@@ -95,7 +79,8 @@ export const startExploring = async ({
|
|
|
95
79
|
})
|
|
96
80
|
|
|
97
81
|
const compileServer = await startCompileServer({
|
|
98
|
-
signal
|
|
82
|
+
signal,
|
|
83
|
+
handleSIGINT,
|
|
99
84
|
keepProcessAlive,
|
|
100
85
|
|
|
101
86
|
projectDirectoryUrl,
|
package/src/abort/abortable.js
DELETED
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* https://github.com/whatwg/dom/issues/920
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { createCleaner } from "./cleaner.js"
|
|
6
|
-
import { raceCallbacks } from "./callback_race.js"
|
|
7
|
-
import { createCallbackList } from "./callback_list.js"
|
|
8
|
-
|
|
9
|
-
export const Abortable = {
|
|
10
|
-
throwIfAborted: (operation) => {
|
|
11
|
-
if (operation.signal.aborted) {
|
|
12
|
-
const error = new Error(`The operation was aborted`)
|
|
13
|
-
error.name = "AbortError"
|
|
14
|
-
error.type = "aborted"
|
|
15
|
-
throw error
|
|
16
|
-
}
|
|
17
|
-
},
|
|
18
|
-
|
|
19
|
-
isAbortError: (error) => {
|
|
20
|
-
return error.name === "AbortError"
|
|
21
|
-
},
|
|
22
|
-
|
|
23
|
-
start: () => {
|
|
24
|
-
const abortController = new AbortController()
|
|
25
|
-
const abort = (value) => abortController.abort(value)
|
|
26
|
-
const signal = abortController.signal
|
|
27
|
-
const cleaner = createCleaner()
|
|
28
|
-
|
|
29
|
-
// abortCallbackList is used to ignore the max listeners warning from Node.js
|
|
30
|
-
// this warning is useful but becomes problematic when it's expected
|
|
31
|
-
// (a function doing 20 http call in parallel)
|
|
32
|
-
// To be 100% sure we don't have memory leak, only Abortable.asyncCallback
|
|
33
|
-
// uses abortCallbackList to know when something is aborted
|
|
34
|
-
const abortCallbackList = createCallbackList()
|
|
35
|
-
signal.onabort = () => {
|
|
36
|
-
const callbacks = abortCallbackList.copy()
|
|
37
|
-
abortCallbackList.clear()
|
|
38
|
-
callbacks.forEach((callback) => {
|
|
39
|
-
callback()
|
|
40
|
-
})
|
|
41
|
-
}
|
|
42
|
-
cleaner.addCallback(() => {
|
|
43
|
-
abortCallbackList.clear()
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
const operation = {
|
|
47
|
-
abort,
|
|
48
|
-
signal,
|
|
49
|
-
abortCallbackList,
|
|
50
|
-
cleaner,
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return operation
|
|
54
|
-
},
|
|
55
|
-
|
|
56
|
-
fromSignal: (signal) => {
|
|
57
|
-
const operation = Abortable.start()
|
|
58
|
-
Abortable.followSignal(operation, signal)
|
|
59
|
-
return operation
|
|
60
|
-
},
|
|
61
|
-
|
|
62
|
-
followSignal: (operation, signal, cleanup = cleanupNoop) => {
|
|
63
|
-
if (operation.signal.aborted) {
|
|
64
|
-
return () => {}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (signal.aborted) {
|
|
68
|
-
operation.abort()
|
|
69
|
-
return () => {}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return raceCallbacks(
|
|
73
|
-
{
|
|
74
|
-
parent_abort: (cb) => {
|
|
75
|
-
return operation.abortCallbackList.add(cb)
|
|
76
|
-
},
|
|
77
|
-
child_abort: (cb) => {
|
|
78
|
-
return addEventListener(signal, "abort", cb)
|
|
79
|
-
},
|
|
80
|
-
cleaned: (cb) => {
|
|
81
|
-
return operation.cleaner.addCallback(cb)
|
|
82
|
-
},
|
|
83
|
-
},
|
|
84
|
-
(winner) => {
|
|
85
|
-
const raceEffects = {
|
|
86
|
-
parent_abort: () => {
|
|
87
|
-
// Nothing to do, exists to remove
|
|
88
|
-
// - "abort" event listener on parent
|
|
89
|
-
// - "abort" event listener on child
|
|
90
|
-
cleanup()
|
|
91
|
-
},
|
|
92
|
-
child_abort: () => {
|
|
93
|
-
operation.abort()
|
|
94
|
-
},
|
|
95
|
-
cleaned: () => {
|
|
96
|
-
// Nothing to do, exists to remove
|
|
97
|
-
// - "abort" event listener on parent
|
|
98
|
-
// - "abort" event listener on child
|
|
99
|
-
cleanup()
|
|
100
|
-
},
|
|
101
|
-
}
|
|
102
|
-
raceEffects[winner.name](winner.value)
|
|
103
|
-
},
|
|
104
|
-
)
|
|
105
|
-
},
|
|
106
|
-
|
|
107
|
-
effect: (operation, effect) => {
|
|
108
|
-
const abortController = new AbortController()
|
|
109
|
-
const returnValue = effect((value) => abortController.abort(value))
|
|
110
|
-
const cleanup =
|
|
111
|
-
typeof returnValue === "function" ? returnValue : cleanupNoop
|
|
112
|
-
const signal = abortController.signal
|
|
113
|
-
|
|
114
|
-
Abortable.followSignal(operation, signal, cleanup)
|
|
115
|
-
return {
|
|
116
|
-
signal,
|
|
117
|
-
cleanup,
|
|
118
|
-
}
|
|
119
|
-
},
|
|
120
|
-
|
|
121
|
-
timeout: (operation, ms) => {
|
|
122
|
-
return Abortable.effect(operation, (abort) => {
|
|
123
|
-
const timeoutId = setTimeout(abort, ms)
|
|
124
|
-
return () => {
|
|
125
|
-
clearTimeout(timeoutId)
|
|
126
|
-
}
|
|
127
|
-
})
|
|
128
|
-
},
|
|
129
|
-
|
|
130
|
-
cleanOnAbort: (operation) => {
|
|
131
|
-
const removeAbortEventListener = addEventListener(
|
|
132
|
-
operation.signal,
|
|
133
|
-
"abort",
|
|
134
|
-
() => {
|
|
135
|
-
removeAbortEventListener()
|
|
136
|
-
operation.cleaner.clean("operation aborted")
|
|
137
|
-
},
|
|
138
|
-
)
|
|
139
|
-
return removeAbortEventListener
|
|
140
|
-
},
|
|
141
|
-
|
|
142
|
-
// Provide a signal to the callback
|
|
143
|
-
// this signal won't inherit the current signal max listeners
|
|
144
|
-
asyncCallback: async (operation, asyncFunction) => {
|
|
145
|
-
Abortable.throwIfAborted(operation)
|
|
146
|
-
|
|
147
|
-
const abortController = new AbortController()
|
|
148
|
-
const signal = abortController.signal
|
|
149
|
-
|
|
150
|
-
const removeOperationAbortCallback = operation.abortCallbackList.add(() => {
|
|
151
|
-
abortController.abort()
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
try {
|
|
155
|
-
const value = await asyncFunction(signal)
|
|
156
|
-
removeOperationAbortCallback()
|
|
157
|
-
return value
|
|
158
|
-
} catch (e) {
|
|
159
|
-
removeOperationAbortCallback()
|
|
160
|
-
throw e
|
|
161
|
-
}
|
|
162
|
-
},
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const cleanupNoop = () => {}
|
|
166
|
-
|
|
167
|
-
const addEventListener = (target, eventName, cb) => {
|
|
168
|
-
target.addEventListener(eventName, cb)
|
|
169
|
-
return () => {
|
|
170
|
-
target.removeEventListener(eventName, cb)
|
|
171
|
-
}
|
|
172
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
export const createCallbackList = () => {
|
|
2
|
-
let callbacks = []
|
|
3
|
-
|
|
4
|
-
const add = (callback) => {
|
|
5
|
-
if (typeof callback !== "function") {
|
|
6
|
-
throw new Error(`callback must be a function, got ${callback}`)
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
// don't register twice
|
|
10
|
-
const existingCallback = callbacks.find((callbackCandidate) => {
|
|
11
|
-
return callbackCandidate === callback
|
|
12
|
-
})
|
|
13
|
-
if (existingCallback) {
|
|
14
|
-
if (typeof process.emitWarning === "object") {
|
|
15
|
-
process.emitWarning(`Trying to register same callback twice`, {
|
|
16
|
-
CODE: "CALLBACK_DUPLICATION",
|
|
17
|
-
detail: `It's often the sign that code is executd more than once`,
|
|
18
|
-
})
|
|
19
|
-
} else {
|
|
20
|
-
console.warn(`Trying to add same callback twice`)
|
|
21
|
-
}
|
|
22
|
-
} else {
|
|
23
|
-
callbacks = [...callbacks, callback]
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return () => {
|
|
27
|
-
remove(callback)
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const remove = (callback) => {
|
|
32
|
-
callbacks = arrayWithout(callbacks, callback)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const clear = () => {
|
|
36
|
-
callbacks = []
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const copy = () => {
|
|
40
|
-
return callbacks.slice()
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return {
|
|
44
|
-
add,
|
|
45
|
-
remove,
|
|
46
|
-
clear,
|
|
47
|
-
copy,
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const arrayWithout = (array, item) => {
|
|
52
|
-
if (array.length === 0) return array
|
|
53
|
-
const arrayWithoutItem = []
|
|
54
|
-
let i = 0
|
|
55
|
-
while (i < array.length) {
|
|
56
|
-
const value = array[i]
|
|
57
|
-
i++
|
|
58
|
-
if (value === item) {
|
|
59
|
-
continue
|
|
60
|
-
}
|
|
61
|
-
arrayWithoutItem.push(value)
|
|
62
|
-
}
|
|
63
|
-
return arrayWithoutItem
|
|
64
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* See callback_race.md
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export const raceCallbacks = (raceDescription, winnerCallback) => {
|
|
6
|
-
const cleanCallbacks = []
|
|
7
|
-
let done = false
|
|
8
|
-
|
|
9
|
-
const cleanup = () => {
|
|
10
|
-
const cleanCallbacksCopy = cleanCallbacks.slice()
|
|
11
|
-
cleanCallbacks.length = 0
|
|
12
|
-
cleanCallbacksCopy.forEach((clean) => {
|
|
13
|
-
clean()
|
|
14
|
-
})
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
Object.keys(raceDescription).forEach((candidateName) => {
|
|
18
|
-
const register = raceDescription[candidateName]
|
|
19
|
-
const returnValue = register((data) => {
|
|
20
|
-
if (done) return
|
|
21
|
-
done = true
|
|
22
|
-
cleanup()
|
|
23
|
-
winnerCallback({
|
|
24
|
-
name: candidateName,
|
|
25
|
-
data,
|
|
26
|
-
})
|
|
27
|
-
})
|
|
28
|
-
if (typeof returnValue === "function") {
|
|
29
|
-
cleanCallbacks.push(returnValue)
|
|
30
|
-
}
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
return cleanup
|
|
34
|
-
}
|
package/src/abort/cleaner.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { createCallbackList } from "./callback_list.js"
|
|
2
|
-
|
|
3
|
-
export const createCleaner = () => {
|
|
4
|
-
const callbackList = createCallbackList()
|
|
5
|
-
|
|
6
|
-
const addCallback = (callback) => {
|
|
7
|
-
return callbackList.add(callback)
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const clean = async (reason) => {
|
|
11
|
-
const callbacks = callbackList.copy()
|
|
12
|
-
callbackList.clear()
|
|
13
|
-
|
|
14
|
-
await Promise.all(
|
|
15
|
-
callbacks.map(async (callback) => {
|
|
16
|
-
await callback(reason)
|
|
17
|
-
}),
|
|
18
|
-
)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return { addCallback, clean }
|
|
22
|
-
}
|
package/src/abort/main.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* When starting an http server there is two distinct things
|
|
3
|
-
* that code may want to do:
|
|
4
|
-
* 1. Abort the server while it's starting
|
|
5
|
-
* 2. Stop server onces its started
|
|
6
|
-
*
|
|
7
|
-
* This can be achieved with the following code where
|
|
8
|
-
* server is aborted if it takes more than 1s to start
|
|
9
|
-
* and immediatly stopped once started
|
|
10
|
-
*
|
|
11
|
-
* const abortController = new AbortController()
|
|
12
|
-
* setTimeout(() => { abortController.abort() }, 1000)
|
|
13
|
-
* const server = await startServer({
|
|
14
|
-
* abortSignal: abortController.signal
|
|
15
|
-
* })
|
|
16
|
-
* await server.stop()
|
|
17
|
-
*
|
|
18
|
-
* In order to implement this kind of API two helpers are exported
|
|
19
|
-
* here:
|
|
20
|
-
* 1. "Abort" which can be used to throw an abort error
|
|
21
|
-
* while server is starting
|
|
22
|
-
* 2. "Cleanup" which can be used to track how to cleanup all the things
|
|
23
|
-
* done to start the server
|
|
24
|
-
*
|
|
25
|
-
* Same concepts could be reused when spwaning a child process, a worker, etc..
|
|
26
|
-
*
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
export { Abortable } from "./abortable.js"
|
|
30
|
-
|
|
31
|
-
// when will be a package this should be a Node.js export only
|
|
32
|
-
export { raceProcessTeardownEvents } from "./process_teardown_events.js"
|