ui5-test-runner 6.0.0-beta.5 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -29
- package/dist/Command.js +52 -0
- package/dist/Npm.js +154 -74
- package/dist/browsers/RecoverableBrowserError.js +6 -0
- package/dist/browsers/chromeArguments.js +5 -0
- package/dist/browsers/chromeOptions.js +29 -0
- package/dist/browsers/consoleMessage.js +23 -0
- package/dist/browsers/factory.js +168 -3
- package/dist/browsers/networkResponse.js +15 -0
- package/dist/browsers/playwright.js +104 -0
- package/dist/browsers/puppeteer.js +54 -88
- package/dist/browsers/seleniumWebdriver.js +124 -0
- package/dist/browsers/webdriverio.js +85 -0
- package/dist/cli.js +18 -12
- package/dist/configuration/CommandLine.js +46 -26
- package/dist/configuration/ConfigurationValidator.js +132 -18
- package/dist/configuration/OptionValidationError.js +50 -2
- package/dist/configuration/derivations.js +8 -0
- package/dist/configuration/options.js +380 -23
- package/dist/configuration/validations.js +135 -0
- package/dist/configuration/validators/boolean.js +1 -1
- package/dist/configuration/validators/enumeration.js +7 -0
- package/dist/configuration/validators/fsEntry.js +12 -12
- package/dist/configuration/validators/index.js +8 -2
- package/dist/configuration/validators/integer.js +7 -1
- package/dist/configuration/validators/json.js +23 -0
- package/dist/configuration/validators/libraryMapping.js +28 -0
- package/dist/configuration/validators/percent.js +2 -2
- package/dist/configuration/validators/regexp.js +2 -2
- package/dist/configuration/validators/secret.js +8 -0
- package/dist/configuration/validators/string.js +1 -1
- package/dist/configuration/validators/timeout.js +2 -2
- package/dist/configuration/validators/url.js +3 -3
- package/dist/end.js +58 -0
- package/dist/if.js +22 -0
- package/dist/modes/Modes.js +2 -1
- package/dist/modes/batch/BatchItem.js +1 -0
- package/dist/modes/batch/batchTask.js +159 -0
- package/dist/modes/batch/index.js +98 -0
- package/dist/modes/batch/output.js +22 -0
- package/dist/modes/batch/report.js +45 -0
- package/dist/modes/batch/resolve.js +105 -0
- package/dist/modes/dumpConfig.js +6 -0
- package/dist/modes/execute.js +6 -5
- package/dist/modes/help.js +86 -2
- package/dist/modes/log/LogReader.js +3 -2
- package/dist/modes/log/LogStorage.js +31 -27
- package/dist/modes/log/index.js +38 -6
- package/dist/modes/log/{REserve.js → reserve.js} +25 -2
- package/dist/modes/mcp/IDocument.js +2 -0
- package/dist/modes/mcp/frontmatter.js +142 -0
- package/dist/modes/mcp/index.js +49 -0
- package/dist/modes/mcp/knowledgeBase.js +104 -0
- package/dist/modes/mcp/remoteKnowledgeBase.js +79 -0
- package/dist/modes/mcp/reserve.js +175 -0
- package/dist/modes/mcp/tools/getTopic.js +46 -0
- package/dist/modes/mcp/tools/index.js +6 -0
- package/dist/modes/mcp/tools/listTopics.js +14 -0
- package/dist/modes/test/PageContext.js +1 -0
- package/dist/modes/test/Server.js +99 -0
- package/dist/modes/test/browser.js +31 -17
- package/dist/modes/test/browserConfig.js +17 -0
- package/dist/modes/test/coverage/collect.js +38 -0
- package/dist/modes/test/coverage/index.js +3 -0
- package/dist/modes/test/coverage/instrument.js +70 -0
- package/dist/modes/test/coverage/nyc.js +15 -0
- package/dist/modes/test/coverage/report.js +71 -0
- package/dist/modes/test/coverage/settings.js +53 -0
- package/dist/modes/test/index.js +94 -34
- package/dist/modes/test/pageTask.js +293 -98
- package/dist/modes/test/report.js +20 -3
- package/dist/modes/test/reserve.js +76 -0
- package/dist/modes/test/screenshot.js +49 -0
- package/dist/platform/Crypto.js +9 -0
- package/dist/platform/Exit.js +33 -24
- package/dist/platform/FileSystem.js +6 -2
- package/dist/platform/Host.js +9 -1
- package/dist/platform/Http.js +54 -29
- package/dist/platform/Module.js +2 -1
- package/dist/platform/Path.js +5 -1
- package/dist/platform/Process.js +67 -13
- package/dist/platform/Terminal.js +12 -8
- package/dist/platform/Thread.js +1 -1
- package/dist/platform/assert.js +13 -12
- package/dist/platform/environment.js +9 -18
- package/dist/platform/index.js +1 -0
- package/dist/platform/logger/CompressionContext.js +117 -0
- package/dist/platform/logger/allCompressed.js +8 -7
- package/dist/platform/logger/compress.js +29 -129
- package/dist/platform/logger/output/BaseLoggerOutput.js +49 -31
- package/dist/platform/logger/output/InteractiveLoggerOutput.js +1 -1
- package/dist/platform/logger/output/LlmLoggerOutput.js +29 -0
- package/dist/platform/logger/output/factory.js +5 -1
- package/dist/platform/logger/output.js +24 -6
- package/dist/platform/logger/toInternalLogAttributes.js +10 -11
- package/dist/platform/logger/traceFileName.js +1 -0
- package/dist/platform/logger.js +52 -21
- package/dist/platform/mock.js +110 -19
- package/dist/platform/version.js +1 -2
- package/dist/platform/workerBootstrap.js +1 -1
- package/dist/reports/initReportBuilder.js +18 -0
- package/dist/reports/junit.js +64 -0
- package/dist/reports/saveReport.js +10 -0
- package/dist/reports/textReport.js +92 -0
- package/dist/reserveLogger.js +22 -0
- package/dist/sendToParentProcess.js +6 -0
- package/dist/start.js +79 -0
- package/dist/types/ISecretValue.js +1 -0
- package/dist/types/SecretValue.js +15 -0
- package/dist/ui/agent.js +4 -2
- package/dist/ui/html-report.js +2 -2
- package/dist/ui/lib.js +1 -1
- package/dist/ui/log-viewer.js +1 -1
- package/dist/utils/node/Folder.js +2 -4
- package/dist/utils/node/FramedStreamReader.js +4 -5
- package/dist/utils/node/FramedStreamWriter.js +6 -5
- package/dist/utils/node/anonymize.js +11 -0
- package/dist/utils/node/timeout.js +11 -0
- package/dist/utils/shared/ProgressBar.js +1 -2
- package/dist/utils/shared/TestReportBuilder.js +11 -7
- package/dist/utils/shared/host.js +11 -0
- package/dist/utils/shared/object.js +12 -3
- package/dist/utils/shared/schema.js +20 -0
- package/dist/utils/shared/string.js +1 -0
- package/dist/utils/shared/toIError.js +3 -7
- package/dist/utils/shared/version.js +52 -0
- package/package.json +2 -78
- package/dist/configuration/validators/browser.js +0 -11
- package/dist/modes/test/REserve.js +0 -31
- package/dist/modes/test/server.js +0 -109
package/dist/platform/Exit.js
CHANGED
|
@@ -26,14 +26,14 @@ const socketHandleDescriptor = (handle) => {
|
|
|
26
26
|
if (handle._handle) {
|
|
27
27
|
const underlyingHandle = handle._handle;
|
|
28
28
|
const underlyingClassName = underlyingHandle && underlyingHandle.constructor && underlyingHandle.constructor.name;
|
|
29
|
-
return
|
|
29
|
+
return underlyingClassName || 'handle unknown';
|
|
30
30
|
}
|
|
31
31
|
return 'unknown';
|
|
32
32
|
};
|
|
33
33
|
const handleDescriptors = {
|
|
34
34
|
ChildProcess: (handle) => {
|
|
35
35
|
return (`pid: ${handle.pid}` +
|
|
36
|
-
(handle.spawnargs ? ` ${handle.spawnargs.map((value) =>
|
|
36
|
+
(handle.spawnargs ? ` ${handle.spawnargs.map((value) => value.replaceAll(' ', '␣'))}` : ' unknown'));
|
|
37
37
|
},
|
|
38
38
|
ReadStream: (handle) => (handle.fd === 0 ? `stdin isTTY: ${handle.isTTY}` : `fd: ${handle.fd}`),
|
|
39
39
|
Server: (handle) => `connections: ${handle._connections} events: ${handle._eventsCount}`,
|
|
@@ -44,10 +44,12 @@ const handleDescriptors = {
|
|
|
44
44
|
: `fd: ${handle.fd}`
|
|
45
45
|
};
|
|
46
46
|
const isStdStream = (handle) => 'fd' in handle && handle.fd >= 0 && handle.fd < 3;
|
|
47
|
-
const unknownHandleDescriptor = () => 'unknown';
|
|
47
|
+
const unknownHandleDescriptor = (_handle) => 'unknown';
|
|
48
48
|
const describeHandle = (handle) => {
|
|
49
|
-
const className = handle && handle.constructor && handle.constructor.name;
|
|
50
|
-
return
|
|
49
|
+
const className = (handle && handle.constructor && handle.constructor.name);
|
|
50
|
+
return className
|
|
51
|
+
? { className, label: (handleDescriptors[className] ?? unknownHandleDescriptor)(handle) }
|
|
52
|
+
: { className: 'unknown', label: unknownHandleDescriptor(handle) };
|
|
51
53
|
};
|
|
52
54
|
export class Exit {
|
|
53
55
|
static _asyncTaskId = 0;
|
|
@@ -55,38 +57,42 @@ export class Exit {
|
|
|
55
57
|
static _checkForHandlesLeak() {
|
|
56
58
|
const undocumentedProcess = process;
|
|
57
59
|
if (!undocumentedProcess._getActiveHandles) {
|
|
58
|
-
logger?.warn({ source: 'exit/handle', message: '
|
|
60
|
+
logger?.warn({ source: 'exit/handle', message: 'missing process._getActiveHandles' });
|
|
59
61
|
return;
|
|
60
62
|
}
|
|
61
63
|
const activeHandles = undocumentedProcess._getActiveHandles();
|
|
62
|
-
let
|
|
64
|
+
let isMessagePortFound = false;
|
|
63
65
|
for (const handle of activeHandles) {
|
|
64
66
|
const { className, label } = describeHandle(handle);
|
|
65
67
|
if (isStdStream(handle)) {
|
|
66
68
|
logger?.debug({ source: 'exit/handle', message: `${className} ${label}` });
|
|
67
69
|
}
|
|
68
|
-
else if (className === 'MessagePort' && !
|
|
69
|
-
|
|
70
|
+
else if (className === 'MessagePort' && !isMessagePortFound) {
|
|
71
|
+
isMessagePortFound = true;
|
|
70
72
|
logger?.debug({ source: 'exit/handle', message: `${className} ${label}` });
|
|
71
73
|
}
|
|
74
|
+
else if (['TLSSocket', 'Socket'].includes(className)) {
|
|
75
|
+
logger?.debug({ source: 'exit/handle', message: `destroying leak ${className} ${label}` });
|
|
76
|
+
handle.destroy();
|
|
77
|
+
}
|
|
72
78
|
else {
|
|
73
79
|
logger?.warn({ source: 'exit/handle', message: `possible leak ${className} ${label}` });
|
|
74
|
-
if (className === 'TLSSocket' || className === 'Socket') {
|
|
75
|
-
handle.destroy();
|
|
76
|
-
}
|
|
77
80
|
}
|
|
78
81
|
}
|
|
79
82
|
}
|
|
83
|
+
static get code() {
|
|
84
|
+
return (process.exitCode ?? 0);
|
|
85
|
+
}
|
|
80
86
|
static set code(code) {
|
|
81
87
|
assert(Thread.isMainThread, 'Exit.code can be set only on main thread');
|
|
82
88
|
process.exitCode = code;
|
|
83
89
|
}
|
|
84
90
|
static registerAsyncTask(task) {
|
|
85
91
|
assert(Thread.isMainThread, 'Exit.registerAsyncTask can be called only on main thread');
|
|
86
|
-
if (
|
|
92
|
+
if (this._enteringShutdown) {
|
|
87
93
|
throw new ExitShutdownError();
|
|
88
94
|
}
|
|
89
|
-
const id = ++
|
|
95
|
+
const id = ++this._asyncTaskId;
|
|
90
96
|
this._asyncTasks.push({
|
|
91
97
|
id,
|
|
92
98
|
...task
|
|
@@ -95,7 +101,7 @@ export class Exit {
|
|
|
95
101
|
[Symbol.dispose]() {
|
|
96
102
|
const index = Exit._asyncTasks.findIndex((task) => task.id === id);
|
|
97
103
|
try {
|
|
98
|
-
assert(index !== -1,
|
|
104
|
+
assert(Exit._enteringShutdown || index !== -1, `unable to find Exit async task "${task.name}" to unregister (shutdown=${Exit._enteringShutdown})`);
|
|
99
105
|
Exit._asyncTasks.splice(index, 1);
|
|
100
106
|
}
|
|
101
107
|
catch {
|
|
@@ -107,10 +113,13 @@ export class Exit {
|
|
|
107
113
|
static _logLevel = 'debug';
|
|
108
114
|
static async shutdown() {
|
|
109
115
|
assert(Thread.isMainThread, 'Exit.shutdown can be called only on main thread');
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
116
|
+
if (this._enteringShutdown) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
this._enteringShutdown = true;
|
|
120
|
+
const logLevel = this._logLevel;
|
|
121
|
+
while (this._asyncTasks.length > 0) {
|
|
122
|
+
const task = this._asyncTasks.at(-1);
|
|
114
123
|
try {
|
|
115
124
|
logger?.[logLevel]({ source: 'exit', message: `Stopping ${task.name}...` });
|
|
116
125
|
await task.stop();
|
|
@@ -120,20 +129,20 @@ export class Exit {
|
|
|
120
129
|
logger?.[logLevel]({ source: 'exit', message: `Failed while stopping ${task.name}...`, error });
|
|
121
130
|
}
|
|
122
131
|
finally {
|
|
123
|
-
if (task ===
|
|
124
|
-
|
|
132
|
+
if (task === this._asyncTasks.at(-1)) {
|
|
133
|
+
this._asyncTasks.pop();
|
|
125
134
|
}
|
|
126
135
|
}
|
|
127
136
|
}
|
|
128
|
-
|
|
137
|
+
this._checkForHandlesLeak();
|
|
129
138
|
logger?.[logLevel]({ source: 'exit', message: `Stopping logger...` });
|
|
130
139
|
await logger?.stop();
|
|
131
140
|
}
|
|
132
141
|
static sigInt() {
|
|
133
142
|
Exit._logLevel = 'info';
|
|
134
|
-
void
|
|
143
|
+
void this.shutdown();
|
|
135
144
|
}
|
|
136
145
|
}
|
|
137
146
|
if (Thread.isMainThread) {
|
|
138
|
-
process.on('SIGINT', Exit.sigInt);
|
|
147
|
+
process.on('SIGINT', () => Exit.sigInt());
|
|
139
148
|
}
|
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
import { access, stat, constants, readFile, mkdir, rm } from 'node:fs/promises';
|
|
2
|
-
import { createReadStream, createWriteStream, writeFileSync } from 'node:fs';
|
|
1
|
+
import { access, stat, constants, readFile, readdir, mkdir, rename, rm, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { createReadStream, createWriteStream, watch, writeFileSync } from 'node:fs';
|
|
3
3
|
export class FileSystem {
|
|
4
4
|
static access = access;
|
|
5
5
|
static constants = constants;
|
|
6
6
|
static createReadStream = createReadStream;
|
|
7
7
|
static createWriteStream = createWriteStream;
|
|
8
8
|
static mkdir = mkdir;
|
|
9
|
+
static readdir = readdir;
|
|
9
10
|
static readFile = readFile;
|
|
11
|
+
static rename = rename;
|
|
10
12
|
static rm = rm;
|
|
11
13
|
static stat = stat;
|
|
14
|
+
static writeFile = writeFile;
|
|
15
|
+
static watch = watch;
|
|
12
16
|
static writeFileSync = writeFileSync;
|
|
13
17
|
}
|
package/dist/platform/Host.js
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
|
-
import { machine, cpus, platform } from 'node:os';
|
|
1
|
+
import { machine, cpus, platform, release, version, homedir, tmpdir } from 'node:os';
|
|
2
2
|
export class Host {
|
|
3
|
+
static argv = process.argv;
|
|
4
|
+
static nodePath = process.argv[0];
|
|
3
5
|
static cpus = cpus;
|
|
4
6
|
static cwd = process.cwd.bind(process);
|
|
7
|
+
static env = process.env;
|
|
8
|
+
static tmpdir = tmpdir;
|
|
9
|
+
static homedir = homedir;
|
|
5
10
|
static machine = machine;
|
|
6
11
|
static memoryUsage = process.memoryUsage.bind(process);
|
|
7
12
|
static nodeVersion = process.version;
|
|
13
|
+
static osRelease = release;
|
|
14
|
+
static osVersion = version;
|
|
8
15
|
static pid = process.pid;
|
|
9
16
|
static platform = platform;
|
|
17
|
+
static version = process.version;
|
|
10
18
|
}
|
package/dist/platform/Http.js
CHANGED
|
@@ -1,38 +1,63 @@
|
|
|
1
1
|
import { logger } from './logger.js';
|
|
2
2
|
import { Exit } from './Exit.js';
|
|
3
|
-
let
|
|
3
|
+
let _lastRequestId = 0;
|
|
4
|
+
const _fetch = async (url, init) => {
|
|
5
|
+
const { requestId = ++_lastRequestId, controller = new AbortController(), ...fetchInit } = init ?? {};
|
|
6
|
+
let abortForwarder;
|
|
7
|
+
if (init?.signal) {
|
|
8
|
+
abortForwarder = () => controller.abort();
|
|
9
|
+
init.signal.addEventListener('abort', abortForwarder);
|
|
10
|
+
}
|
|
11
|
+
const loggedInit = Object.keys(fetchInit).length > 0 ? fetchInit : undefined;
|
|
12
|
+
using _ = Exit.registerAsyncTask({
|
|
13
|
+
name: `http.fetch#${requestId}`,
|
|
14
|
+
stop: () => controller.abort()
|
|
15
|
+
});
|
|
16
|
+
logger.debug({
|
|
17
|
+
source: 'http',
|
|
18
|
+
message: `${fetchInit?.method ?? 'GET'} ${url}`,
|
|
19
|
+
data: { requestId, init: loggedInit }
|
|
20
|
+
});
|
|
21
|
+
try {
|
|
22
|
+
const response = await fetch(url, {
|
|
23
|
+
...fetchInit,
|
|
24
|
+
signal: controller.signal
|
|
25
|
+
});
|
|
26
|
+
const headers = Object.fromEntries(response.headers);
|
|
27
|
+
logger.debug({
|
|
28
|
+
source: 'http',
|
|
29
|
+
message: `${response.status} ${response.statusText}`,
|
|
30
|
+
data: { requestId, status: response.status, headers }
|
|
31
|
+
});
|
|
32
|
+
return response;
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
logger.debug({ source: 'http', message: 'error caught', data: { requestId }, error });
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
finally {
|
|
39
|
+
if (abortForwarder) {
|
|
40
|
+
init?.signal?.removeEventListener('abort', abortForwarder);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
4
44
|
export const Http = {
|
|
5
|
-
async
|
|
6
|
-
|
|
45
|
+
async fetch(url, init) {
|
|
46
|
+
return _fetch(url, init);
|
|
47
|
+
},
|
|
48
|
+
async getAsText(url) {
|
|
49
|
+
const requestId = ++_lastRequestId;
|
|
7
50
|
const controller = new AbortController();
|
|
51
|
+
const response = await _fetch(url, { method: 'GET', requestId, controller });
|
|
52
|
+
if (!response.ok) {
|
|
53
|
+
throw new Error('HTTP request failed');
|
|
54
|
+
}
|
|
8
55
|
using _ = Exit.registerAsyncTask({
|
|
9
|
-
name: `http.
|
|
56
|
+
name: `http.getAsText#${requestId}`,
|
|
10
57
|
stop: () => controller.abort()
|
|
11
58
|
});
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
signal: controller.signal
|
|
16
|
-
});
|
|
17
|
-
const headers = {};
|
|
18
|
-
for (const [name, value] of response.headers) {
|
|
19
|
-
headers[name] = value;
|
|
20
|
-
}
|
|
21
|
-
logger.debug({
|
|
22
|
-
source: 'http',
|
|
23
|
-
message: `${response.status} ${response.statusText}`,
|
|
24
|
-
data: { requestId, status: response.status, headers }
|
|
25
|
-
});
|
|
26
|
-
if (!response.ok) {
|
|
27
|
-
throw new Error('HTTP request failed');
|
|
28
|
-
}
|
|
29
|
-
const text = await response.text();
|
|
30
|
-
logger.debug({ source: 'http', message: text, data: { requestId } });
|
|
31
|
-
return text;
|
|
32
|
-
}
|
|
33
|
-
catch (error) {
|
|
34
|
-
logger.debug({ source: 'http', message: 'error caught', data: { requestId }, error });
|
|
35
|
-
throw error;
|
|
36
|
-
}
|
|
59
|
+
const text = await response.text();
|
|
60
|
+
logger.debug({ source: 'http', message: text, data: { requestId } });
|
|
61
|
+
return text;
|
|
37
62
|
}
|
|
38
63
|
};
|
package/dist/platform/Module.js
CHANGED
package/dist/platform/Path.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { basename, dirname, extname, isAbsolute, join, relative } from 'node:path';
|
|
2
2
|
export class Path {
|
|
3
|
+
static basename = basename;
|
|
4
|
+
static dirname = dirname;
|
|
5
|
+
static extname = extname;
|
|
3
6
|
static isAbsolute = isAbsolute;
|
|
4
7
|
static join = join;
|
|
8
|
+
static relative = relative;
|
|
5
9
|
}
|
package/dist/platform/Process.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Host } from './Host.js';
|
|
2
2
|
import { spawn } from 'node:child_process';
|
|
3
|
+
import { setTimeout as setTimeoutPromise } from 'node:timers/promises';
|
|
3
4
|
import { logger } from './logger.js';
|
|
4
5
|
import { Exit } from './Exit.js';
|
|
5
6
|
class ProcessStopper {
|
|
@@ -12,9 +13,23 @@ class ProcessStopper {
|
|
|
12
13
|
return this._process?.closed;
|
|
13
14
|
}
|
|
14
15
|
}
|
|
16
|
+
export const splitLines = (string) => string.split(/\r?\n/).filter((line, index, array) => index < array.length - 1 || line.length > 0);
|
|
17
|
+
function sanitizeSpawnOptions(options) {
|
|
18
|
+
if (!options.env)
|
|
19
|
+
return options;
|
|
20
|
+
const { env, ...rest } = options;
|
|
21
|
+
return { ...rest, env: Object.keys(env) };
|
|
22
|
+
}
|
|
15
23
|
export class Process {
|
|
24
|
+
static sendToParent = (message) => {
|
|
25
|
+
if (process.send === undefined) {
|
|
26
|
+
throw new Error('process.send is not defined: this process has not been created with IPC mappings');
|
|
27
|
+
}
|
|
28
|
+
process.send(message);
|
|
29
|
+
};
|
|
16
30
|
static spawn = (command, arguments_, options = {}) => {
|
|
17
|
-
const
|
|
31
|
+
const { onMessage, forceRender: _forceRender, ...spawnOptions } = options;
|
|
32
|
+
const finalCommand = command === 'node' ? Host.nodePath : command;
|
|
18
33
|
let asyncTask;
|
|
19
34
|
try {
|
|
20
35
|
const stopper = new ProcessStopper();
|
|
@@ -22,14 +37,21 @@ export class Process {
|
|
|
22
37
|
name: `Process.spawn(${command},${arguments_.join(',')})`,
|
|
23
38
|
stop: () => stopper.stop()
|
|
24
39
|
});
|
|
25
|
-
|
|
26
|
-
|
|
40
|
+
if (onMessage) {
|
|
41
|
+
const stdio = spawnOptions.stdio;
|
|
42
|
+
spawnOptions.stdio = Array.isArray(stdio)
|
|
43
|
+
? [...stdio, 'ipc']
|
|
44
|
+
: [stdio ?? 'pipe', stdio ?? 'pipe', stdio ?? 'pipe', 'ipc'];
|
|
45
|
+
}
|
|
46
|
+
const childProcess = spawn(finalCommand, arguments_, spawnOptions);
|
|
47
|
+
const logMethod = childProcess.pid ? 'debug' : 'warn';
|
|
48
|
+
logger[logMethod]({
|
|
27
49
|
source: 'process',
|
|
28
50
|
processId: childProcess.pid,
|
|
29
51
|
message: 'spawned',
|
|
30
|
-
data: { command, arguments: arguments_, options }
|
|
52
|
+
data: { command, arguments: arguments_, options: sanitizeSpawnOptions(spawnOptions) }
|
|
31
53
|
});
|
|
32
|
-
const process = new Process(childProcess, asyncTask);
|
|
54
|
+
const process = new Process(childProcess, asyncTask, options);
|
|
33
55
|
stopper.process = process;
|
|
34
56
|
return process;
|
|
35
57
|
}
|
|
@@ -37,7 +59,11 @@ export class Process {
|
|
|
37
59
|
logger.error({
|
|
38
60
|
source: 'process',
|
|
39
61
|
message: 'spawn failed',
|
|
40
|
-
data: {
|
|
62
|
+
data: {
|
|
63
|
+
command,
|
|
64
|
+
arguments: arguments_,
|
|
65
|
+
options: sanitizeSpawnOptions(spawnOptions)
|
|
66
|
+
},
|
|
41
67
|
error
|
|
42
68
|
});
|
|
43
69
|
asyncTask?.[Symbol.dispose]();
|
|
@@ -62,31 +88,49 @@ export class Process {
|
|
|
62
88
|
}
|
|
63
89
|
_childProcess;
|
|
64
90
|
_asyncTask;
|
|
65
|
-
constructor(childProcess, asyncTask) {
|
|
91
|
+
constructor(childProcess, asyncTask, options) {
|
|
66
92
|
this._childProcess = childProcess;
|
|
67
93
|
this._asyncTask = asyncTask;
|
|
68
94
|
const { promise, resolve } = Promise.withResolvers();
|
|
69
95
|
this._closed = promise;
|
|
96
|
+
if (options.onMessage) {
|
|
97
|
+
this._childProcess.on('message', options.onMessage);
|
|
98
|
+
}
|
|
99
|
+
const { forceRender } = options;
|
|
70
100
|
this._childProcess.stdout?.on('data', (buffer) => {
|
|
71
101
|
const message = buffer.toString();
|
|
72
|
-
|
|
102
|
+
for (const line of splitLines(message)) {
|
|
103
|
+
logger.info({ source: 'process/stdout', processId: this.pid, forceRender, message: line });
|
|
104
|
+
}
|
|
73
105
|
this._stdout.push(message);
|
|
74
106
|
});
|
|
75
107
|
this._childProcess.stderr?.on('data', (buffer) => {
|
|
76
108
|
const message = buffer.toString();
|
|
77
|
-
|
|
109
|
+
for (const line of splitLines(message)) {
|
|
110
|
+
logger.error({ source: 'process/stderr', processId: this.pid, forceRender, message: line });
|
|
111
|
+
}
|
|
78
112
|
this._stderr.push(message);
|
|
79
113
|
});
|
|
80
|
-
this._childProcess
|
|
114
|
+
this._childProcess
|
|
115
|
+
.on('error', (error) => {
|
|
116
|
+
logger.error({
|
|
117
|
+
source: 'process',
|
|
118
|
+
processId: childProcess.pid,
|
|
119
|
+
message: error.message,
|
|
120
|
+
error
|
|
121
|
+
});
|
|
122
|
+
})
|
|
123
|
+
.on('close', (code) => {
|
|
81
124
|
this._code = code ?? 0;
|
|
82
|
-
|
|
125
|
+
const logLevel = this._code === 0 ? 'debug' : 'warn';
|
|
126
|
+
logger[logLevel]({
|
|
83
127
|
source: 'process',
|
|
84
128
|
processId: this.pid,
|
|
85
129
|
message: 'closed',
|
|
86
130
|
data: { code: this._code }
|
|
87
131
|
});
|
|
88
132
|
this._asyncTask[Symbol.dispose]();
|
|
89
|
-
resolve
|
|
133
|
+
setTimeout(resolve, 0);
|
|
90
134
|
});
|
|
91
135
|
}
|
|
92
136
|
get pid() {
|
|
@@ -112,7 +156,16 @@ export class Process {
|
|
|
112
156
|
process.kill(-this.pid);
|
|
113
157
|
}
|
|
114
158
|
catch {
|
|
115
|
-
|
|
159
|
+
logger.warn({
|
|
160
|
+
source: 'process',
|
|
161
|
+
processId: this.pid,
|
|
162
|
+
message: 'group kill failed; killing pid only — if this process spawned children, they may be orphaned (was it spawned with detached:true?)'
|
|
163
|
+
});
|
|
164
|
+
try {
|
|
165
|
+
process.kill(this.pid);
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
}
|
|
116
169
|
}
|
|
117
170
|
}
|
|
118
171
|
logger.debug({
|
|
@@ -130,4 +183,5 @@ export class Process {
|
|
|
130
183
|
});
|
|
131
184
|
}
|
|
132
185
|
}
|
|
186
|
+
static sleep = (ms) => setTimeoutPromise(ms);
|
|
133
187
|
}
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import { stripVTControlCharacters } from 'node:util';
|
|
2
|
-
const ASCII_ESCAPE = '\
|
|
2
|
+
const ASCII_ESCAPE = '\u{1B}';
|
|
3
3
|
const CSI = `${ASCII_ESCAPE}[`;
|
|
4
|
+
let isRawModeSet = false;
|
|
4
5
|
export class Terminal {
|
|
5
6
|
static isTTY = process.stdout.isTTY;
|
|
6
7
|
static onResize(callback) {
|
|
7
|
-
process.stdout.on('resize', () => callback(
|
|
8
|
+
process.stdout.on('resize', () => callback(this.width));
|
|
8
9
|
}
|
|
9
10
|
static setRawMode(callback) {
|
|
10
11
|
if (callback === false) {
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
if (isRawModeSet) {
|
|
13
|
+
process.stdin.setRawMode(false);
|
|
14
|
+
process.stdin.pause();
|
|
15
|
+
}
|
|
13
16
|
}
|
|
14
17
|
else {
|
|
18
|
+
isRawModeSet = true;
|
|
15
19
|
process.stdin.setRawMode(true);
|
|
16
20
|
process.stdin.on('data', callback);
|
|
17
21
|
}
|
|
@@ -24,17 +28,17 @@ export class Terminal {
|
|
|
24
28
|
process.stdout.write(text);
|
|
25
29
|
}
|
|
26
30
|
static hideCursor() {
|
|
27
|
-
|
|
31
|
+
this.write(`${CSI}?25l`);
|
|
28
32
|
}
|
|
29
33
|
static showCursor() {
|
|
30
|
-
|
|
34
|
+
this.write(`${CSI}?25h`);
|
|
31
35
|
}
|
|
32
36
|
static eraseToEnd(lines) {
|
|
33
37
|
if (lines === 0) {
|
|
34
|
-
|
|
38
|
+
this.write(`${CSI}0G`);
|
|
35
39
|
}
|
|
36
40
|
else {
|
|
37
|
-
|
|
41
|
+
this.write(`${CSI}0G${CSI}${lines}A${CSI}0J`);
|
|
38
42
|
}
|
|
39
43
|
}
|
|
40
44
|
static BLUE = `${CSI}34m`;
|
package/dist/platform/Thread.js
CHANGED
|
@@ -25,7 +25,7 @@ export class Thread {
|
|
|
25
25
|
}
|
|
26
26
|
logger?.debug({ source: 'thread', message: `Creating worker for ${name}`, data });
|
|
27
27
|
const bootstrapPath = Path.join(__sourcesRoot, 'platform/workerBootstrap' + extension);
|
|
28
|
-
const js2tsUrl = new URL('js2ts.mjs', import.meta.url).
|
|
28
|
+
const js2tsUrl = new URL('js2ts.mjs', import.meta.url).href;
|
|
29
29
|
const execArgv = extension === '.ts' ? ['--no-warnings', '--import', js2tsUrl] : [];
|
|
30
30
|
const worker = new Worker(bootstrapPath, {
|
|
31
31
|
execArgv,
|
package/dist/platform/assert.js
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { AssertionError } from 'node:assert/strict';
|
|
2
2
|
import { __developmentMode } from './constants.js';
|
|
3
3
|
import { logger } from './logger/proxy.js';
|
|
4
|
-
export const assert = (
|
|
5
|
-
if (
|
|
6
|
-
|
|
7
|
-
if (__developmentMode) {
|
|
8
|
-
let { stack } = error;
|
|
9
|
-
stack = stack ? stack.split('\n').slice(1).join('\n') : '';
|
|
10
|
-
logger?.fatal({ source: 'assert', message: (message ?? 'Assertion failed') + stack, error });
|
|
11
|
-
}
|
|
12
|
-
else {
|
|
13
|
-
logger?.fatal({ source: 'assert', message: message ?? 'Assertion failed', error });
|
|
14
|
-
}
|
|
15
|
-
throw error;
|
|
4
|
+
export const assert = (shouldBeTrue, messageIfNotTrue = 'Assertion failed') => {
|
|
5
|
+
if (shouldBeTrue) {
|
|
6
|
+
return;
|
|
16
7
|
}
|
|
8
|
+
const error = new AssertionError({ message: messageIfNotTrue });
|
|
9
|
+
if (__developmentMode) {
|
|
10
|
+
let { stack } = error;
|
|
11
|
+
stack = stack ? stack.split('\n').slice(1).join('\n') : '';
|
|
12
|
+
logger?.fatal({ source: 'assert', message: messageIfNotTrue + stack, error });
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
logger?.fatal({ source: 'assert', message: messageIfNotTrue, error });
|
|
16
|
+
}
|
|
17
|
+
throw error;
|
|
17
18
|
};
|
|
@@ -1,28 +1,19 @@
|
|
|
1
1
|
import { Host } from './Host.js';
|
|
2
2
|
import { logger } from './logger.js';
|
|
3
3
|
import { version } from './version.js';
|
|
4
|
+
import { countCpuModels } from '../utils/shared/host.js';
|
|
4
5
|
export const logEnvironnement = async () => {
|
|
5
|
-
const
|
|
6
|
+
const { name, version: semver } = await version();
|
|
6
7
|
const now = new Date();
|
|
7
8
|
logger.info({
|
|
8
9
|
source: 'job',
|
|
9
|
-
message: `${
|
|
10
|
+
message: `${name}@${semver} / Node.js ${Host.nodeVersion} / ${now.toISOString()} (${now.getTimezoneOffset()})`
|
|
10
11
|
});
|
|
11
|
-
const
|
|
12
|
-
for (const { model } of Host.cpus()) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
cpus[model] = 1;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
for (const [model, count] of Object.entries(cpus)) {
|
|
21
|
-
if (count === 1) {
|
|
22
|
-
logger.info({ source: 'job', message: `${Host.machine()} / ${model}` });
|
|
23
|
-
}
|
|
24
|
-
else {
|
|
25
|
-
logger.info({ source: 'job', message: `${Host.machine()} / ${count}x ${model}` });
|
|
26
|
-
}
|
|
12
|
+
const machine = Host.machine();
|
|
13
|
+
for (const { model, count } of countCpuModels(Host.cpus())) {
|
|
14
|
+
logger.info({
|
|
15
|
+
source: 'job',
|
|
16
|
+
message: count === 1 ? `${machine} / ${model}` : `${machine} / ${count}x ${model}`
|
|
17
|
+
});
|
|
27
18
|
}
|
|
28
19
|
};
|