@powerlines/engine 0.15.18 → 0.15.20

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 (110) hide show
  1. package/dist/api/build.cjs +1 -1
  2. package/dist/api/build.d.mts +2 -2
  3. package/dist/api/build.mjs +1 -1
  4. package/dist/api/clean.d.mts +2 -2
  5. package/dist/api/create.d.mts +2 -2
  6. package/dist/api/deploy.d.mts +2 -2
  7. package/dist/api/docs.d.mts +2 -2
  8. package/dist/api/lint.d.mts +2 -2
  9. package/dist/api/prepare.cjs +2 -2
  10. package/dist/api/prepare.d.mts +2 -2
  11. package/dist/api/prepare.mjs +2 -2
  12. package/dist/api/test.d.mts +2 -2
  13. package/dist/api/types.cjs +2 -2
  14. package/dist/api/types.d.mts +2 -2
  15. package/dist/api/types.mjs +2 -2
  16. package/dist/{api-Cdw4v0W4.d.mts → api-B2dWvRMM.d.mts} +10 -7
  17. package/dist/api-B2dWvRMM.d.mts.map +1 -0
  18. package/dist/{api-6w4hZL6n.d.cts → api-DgxYwNuZ.d.cts} +8 -5
  19. package/dist/api-DgxYwNuZ.d.cts.map +1 -0
  20. package/dist/{execution-host.cjs → api.cjs} +1 -1
  21. package/dist/{execution-host.d.mts → api.d.cts} +3 -3
  22. package/dist/api.d.cts.map +1 -0
  23. package/dist/{execution-host.d.cts → api.d.mts} +3 -3
  24. package/dist/api.d.mts.map +1 -0
  25. package/dist/{execution-host.mjs → api.mjs} +2 -2
  26. package/dist/api.mjs.map +1 -0
  27. package/dist/{config-BNe23XHx.d.mts → config-C9AD-erz.d.mts} +1 -1
  28. package/dist/{config-BNe23XHx.d.mts.map → config-C9AD-erz.d.mts.map} +1 -1
  29. package/dist/context/engine-context.cjs +4 -4
  30. package/dist/context/engine-context.d.cts +1 -1
  31. package/dist/context/engine-context.d.mts +1 -1
  32. package/dist/context/engine-context.mjs +4 -4
  33. package/dist/context/index.d.cts +1 -1
  34. package/dist/context/index.d.mts +1 -1
  35. package/dist/{context-DzgsMSWr.d.mts → context-S3XH2DWP.d.mts} +2 -2
  36. package/dist/{context-DzgsMSWr.d.mts.map → context-S3XH2DWP.d.mts.map} +1 -1
  37. package/dist/engine-DELjWFmN.mjs +564 -0
  38. package/dist/engine-DELjWFmN.mjs.map +1 -0
  39. package/dist/engine-DTKq6gFT.cjs +583 -0
  40. package/dist/{engine-context-Dw8odBCo.d.cts → engine-context-5-_EGiKp.d.cts} +1 -1
  41. package/dist/{engine-context-DEotmVzB.d.mts.map → engine-context-5-_EGiKp.d.cts.map} +1 -1
  42. package/dist/{engine-context-DEotmVzB.d.mts → engine-context-zsn1PoRl.d.mts} +5 -5
  43. package/dist/{engine-context-Dw8odBCo.d.cts.map → engine-context-zsn1PoRl.d.mts.map} +1 -1
  44. package/dist/engine.cjs +4 -266
  45. package/dist/engine.d.cts +3 -3
  46. package/dist/engine.d.cts.map +1 -1
  47. package/dist/engine.d.mts +6 -6
  48. package/dist/engine.d.mts.map +1 -1
  49. package/dist/engine.mjs +2 -263
  50. package/dist/helpers/create-execution-host.cjs +9 -2
  51. package/dist/helpers/create-execution-host.d.cts +20 -2
  52. package/dist/helpers/create-execution-host.d.cts.map +1 -0
  53. package/dist/helpers/create-execution-host.d.mts +20 -2
  54. package/dist/helpers/create-execution-host.d.mts.map +1 -0
  55. package/dist/helpers/create-execution-host.mjs +9 -2
  56. package/dist/helpers/create-execution-host.mjs.map +1 -1
  57. package/dist/helpers/finalize.d.cts +12 -2
  58. package/dist/helpers/finalize.d.cts.map +1 -0
  59. package/dist/helpers/finalize.d.mts +12 -2
  60. package/dist/helpers/finalize.d.mts.map +1 -0
  61. package/dist/helpers/index.cjs +4 -2
  62. package/dist/helpers/index.d.cts +5 -5
  63. package/dist/helpers/index.d.mts +5 -5
  64. package/dist/helpers/index.mjs +2 -2
  65. package/dist/helpers/rpc.cjs +2 -2
  66. package/dist/helpers/rpc.d.cts +7 -2
  67. package/dist/helpers/rpc.d.cts.map +1 -0
  68. package/dist/helpers/rpc.d.mts +7 -2
  69. package/dist/helpers/rpc.d.mts.map +1 -0
  70. package/dist/helpers/rpc.mjs +2 -2
  71. package/dist/helpers/stream.cjs +120 -0
  72. package/dist/helpers/stream.d.cts +45 -0
  73. package/dist/helpers/stream.d.cts.map +1 -0
  74. package/dist/helpers/stream.d.mts +45 -0
  75. package/dist/helpers/stream.d.mts.map +1 -0
  76. package/dist/helpers/stream.mjs +117 -0
  77. package/dist/helpers/stream.mjs.map +1 -0
  78. package/dist/index.cjs +2 -11
  79. package/dist/index.d.cts +3 -7
  80. package/dist/index.d.mts +5 -9
  81. package/dist/index.mjs +2 -7
  82. package/package.json +10 -12
  83. package/dist/api-6w4hZL6n.d.cts.map +0 -1
  84. package/dist/api-Cdw4v0W4.d.mts.map +0 -1
  85. package/dist/create-execution-host-CdpuL_YG.d.mts +0 -20
  86. package/dist/create-execution-host-CdpuL_YG.d.mts.map +0 -1
  87. package/dist/create-execution-host-DbOCE5bd.d.cts +0 -20
  88. package/dist/create-execution-host-DbOCE5bd.d.cts.map +0 -1
  89. package/dist/engine.mjs.map +0 -1
  90. package/dist/execution-host-worker-B8TS4clU.d.mts +0 -68
  91. package/dist/execution-host-worker-B8TS4clU.d.mts.map +0 -1
  92. package/dist/execution-host-worker-BjXO7l3Q.d.cts +0 -68
  93. package/dist/execution-host-worker-BjXO7l3Q.d.cts.map +0 -1
  94. package/dist/execution-host.d.cts.map +0 -1
  95. package/dist/execution-host.d.mts.map +0 -1
  96. package/dist/execution-host.mjs.map +0 -1
  97. package/dist/finalize-CW0cO75l.d.cts +0 -12
  98. package/dist/finalize-CW0cO75l.d.cts.map +0 -1
  99. package/dist/finalize-DeSY1r74.d.mts +0 -12
  100. package/dist/finalize-DeSY1r74.d.mts.map +0 -1
  101. package/dist/helpers/execution-host-worker.cjs +0 -315
  102. package/dist/helpers/execution-host-worker.d.cts +0 -2
  103. package/dist/helpers/execution-host-worker.d.mts +0 -2
  104. package/dist/helpers/execution-host-worker.mjs +0 -314
  105. package/dist/helpers/execution-host-worker.mjs.map +0 -1
  106. package/dist/rpc-BsFxzyAK.d.cts +0 -7
  107. package/dist/rpc-BsFxzyAK.d.cts.map +0 -1
  108. package/dist/rpc-xwbFMMNu.d.mts +0 -7
  109. package/dist/rpc-xwbFMMNu.d.mts.map +0 -1
  110. /package/dist/{index-D_TYgLX3.d.cts → index-DDRpa5CJ.d.cts} +0 -0
@@ -1,315 +0,0 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
- const require_chunk = require('../chunk-C0xms8kb.cjs');
3
- let _powerlines_core_lib_config = require("@powerlines/core/lib/config");
4
- let _stryke_fs_resolve = require("@stryke/fs/resolve");
5
- let _stryke_path_append = require("@stryke/path/append");
6
- let _stryke_type_checks_is_number = require("@stryke/type-checks/is-number");
7
- let _stryke_type_checks_is_set = require("@stryke/type-checks/is-set");
8
- let _stryke_type_checks_is_set_object = require("@stryke/type-checks/is-set-object");
9
- let _stryke_type_checks_is_set_string = require("@stryke/type-checks/is-set-string");
10
- let _stryke_type_checks_is_string = require("@stryke/type-checks/is-string");
11
- let date_fns_formatDuration = require("date-fns/formatDuration");
12
- let jest_worker = require("jest-worker");
13
- let node_stream = require("node:stream");
14
- let node_util = require("node:util");
15
-
16
- //#region src/helpers/execution-host-worker.ts
17
- const RESTARTED = Symbol("powerlines-worker:restarted");
18
- /**
19
- * Formats the debug address into a string.
20
- */
21
- const formatDebugAddress = ({ host, port }) => {
22
- return host ? `${host}:${port}` : `${port}`;
23
- };
24
- /**
25
- * Get's the debug address from the `NODE_OPTIONS` environment variable. If the
26
- * address is not found, it returns the default host (`undefined`) and port
27
- * (`9229`).
28
- *
29
- * @returns An object with the host and port of the debug address.
30
- */
31
- const getParsedDebugAddress = (address) => {
32
- if (!address || !(0, _stryke_type_checks_is_string.isString)(address)) return {
33
- host: void 0,
34
- port: 9229
35
- };
36
- if (address.includes(":")) {
37
- const [host, port] = address.split(":");
38
- if (!host || !port) throw new Error(`Invalid debug address: ${address}`);
39
- return {
40
- host,
41
- port: Number.parseInt(port, 10)
42
- };
43
- }
44
- return {
45
- host: void 0,
46
- port: Number.parseInt(address, 10)
47
- };
48
- };
49
- /**
50
- * Get the debug type from the `NODE_OPTIONS` environment variable.
51
- */
52
- function getNodeDebugType(nodeOptions) {
53
- if (nodeOptions.inspect) return "inspect";
54
- if (nodeOptions["inspect-brk"] || nodeOptions.inspect_brk) return "inspect-brk";
55
- }
56
- const cleanupWorkers = (worker) => {
57
- for (const curWorker of worker._workerPool?._workers || []) curWorker._child?.kill("SIGINT");
58
- };
59
- var ExecutionHostWorker = class ExecutionHostWorker {
60
- #worker;
61
- /**
62
- * Creates a new instance of the ExecutionHostWorker class, which manages a worker process for executing tasks related to the Powerlines Engine. The worker is initialized with the specified options and can be used to run tasks in an isolated environment, with support for automatic restarts and activity monitoring.
63
- *
64
- * @param executionHostPath - The path to the Execution Host file.
65
- * @param options - The options for configuring the worker, including the execution context, exposed methods, timeout, and mode.
66
- * @returns A promise that resolves to an instance of the ExecutionHostWorker class.
67
- */
68
- static async from(executionHostPath, options) {
69
- const mode = await (0, _powerlines_core_lib_config.getDefaultMode)(options.context.cwd);
70
- const resolvedPath = await (0, _stryke_fs_resolve.resolve)(executionHostPath, { paths: [options.context.cwd, options.root ? (0, _stryke_path_append.appendPath)(options.root, options.context.cwd) : void 0].filter(Boolean) });
71
- if (!resolvedPath) throw new Error(`Could not resolve the provided Execution Host path: \`${executionHostPath}\`.`);
72
- return new ExecutionHostWorker(resolvedPath, {
73
- mode,
74
- ...options
75
- });
76
- }
77
- /**
78
- * Create a new worker instance.
79
- *
80
- * @param executionHostPath - The path to the worker file.
81
- * @param options - The options for the worker, including exposed methods, timeout, and hooks for activity and restart.
82
- */
83
- constructor(executionHostPath, options) {
84
- this.executionHostPath = executionHostPath;
85
- this.options = options;
86
- const { timeout = 9e5, isolatedMemory = false, mode = "production", context, executionMethods } = this.options;
87
- const logger = context.extendLogger({ category: "communication" });
88
- let restartPromise;
89
- let resolveRestartPromise;
90
- let activeTasks = 0;
91
- this.#worker = void 0;
92
- process.on("exit", () => {
93
- this.close();
94
- });
95
- let nodeOptions = {};
96
- const args = [...process.execArgv];
97
- if (process.env.NODE_OPTIONS) {
98
- let isInString = false;
99
- let willStartNewArg = true;
100
- const stringifiedNodeOptions = process.env.NODE_OPTIONS.split(" ").filter((part) => (part.startsWith("\"") && part.endsWith("\"") || part.startsWith("'") && part.endsWith("'")) && part.replace(/^['"]|['"]$/g, "").split(" ").filter(_stryke_type_checks_is_set_string.isSetString).length > 0 && part.replace(/^['"]|['"]$/g, "").split(" ").filter(_stryke_type_checks_is_set_string.isSetString).every((p) => p.startsWith("--")));
101
- args.push(...stringifiedNodeOptions.map((part) => part.replace(/^['"]|['"]$/g, "")));
102
- const inputNodeModules = stringifiedNodeOptions.reduce((acc, part) => {
103
- return acc.replace(part, "").trim();
104
- }, process.env.NODE_OPTIONS);
105
- for (let i = 0; i < inputNodeModules.length; i++) {
106
- let char = inputNodeModules[i];
107
- if (char) {
108
- if (char === "\\" && isInString) {
109
- if (inputNodeModules.length === i + 1) throw new Error("Invalid escape character at the end.");
110
- char = inputNodeModules[++i];
111
- if (!char) continue;
112
- } else if (char === " " && !isInString) {
113
- willStartNewArg = true;
114
- continue;
115
- } else if (char === "\"") {
116
- isInString = !isInString;
117
- continue;
118
- }
119
- if (willStartNewArg) {
120
- args.push(char);
121
- willStartNewArg = false;
122
- } else args[args.length - 1] += char;
123
- }
124
- }
125
- if (isInString) throw new Error("Unterminated string");
126
- }
127
- if (args.length > 0) {
128
- const { values, tokens } = (0, node_util.parseArgs)({
129
- args,
130
- strict: false,
131
- tokens: true
132
- });
133
- nodeOptions = values;
134
- let orphan = null;
135
- for (let i = 0; i < tokens.length; i++) {
136
- const token = tokens[i];
137
- if (!token) continue;
138
- if (token.kind === "option-terminator") break;
139
- if (token.kind === "option") {
140
- orphan = !(0, _stryke_type_checks_is_set.isSet)(token.value) ? token : null;
141
- continue;
142
- }
143
- if (token.kind !== "positional") {
144
- orphan = null;
145
- continue;
146
- }
147
- if (!orphan) continue;
148
- if (orphan.name in nodeOptions && (0, _stryke_type_checks_is_string.isString)(nodeOptions[orphan.name])) nodeOptions[orphan.name] += ` ${token.value}`;
149
- else nodeOptions[orphan.name] = token.value;
150
- }
151
- }
152
- const originalOptions = { ...nodeOptions };
153
- delete nodeOptions.inspect;
154
- delete nodeOptions["inspect-brk"];
155
- delete nodeOptions.inspect_brk;
156
- if (mode === "development") {
157
- const nodeDebugType = getNodeDebugType(originalOptions);
158
- if (nodeDebugType) {
159
- const debuggerAddress = getParsedDebugAddress(originalOptions[nodeDebugType]);
160
- const address = {
161
- host: debuggerAddress.host,
162
- port: debuggerAddress.port === 0 ? 0 : debuggerAddress.port + 1 + 1
163
- };
164
- nodeOptions[nodeDebugType] = formatDebugAddress(address);
165
- }
166
- nodeOptions["enable-source-maps"] = true;
167
- }
168
- if (isolatedMemory) {
169
- delete nodeOptions["max-old-space-size"];
170
- delete nodeOptions.max_old_space_size;
171
- }
172
- const execArgv = [];
173
- const nodeOptionsParts = [];
174
- for (const [key, value] of Object.entries(nodeOptions)) {
175
- let formatted = null;
176
- if (value === true) formatted = `--${key}`;
177
- else if (value) formatted = `--${key}=${value.includes(" ") && !value.startsWith("\"") ? JSON.stringify(value) : value}`;
178
- if (formatted === null) continue;
179
- if ([
180
- "experimental-network-inspection",
181
- "experimental-storage-inspection",
182
- "experimental-worker-inspection",
183
- "experimental-inspector-network-resource"
184
- ].includes(key)) execArgv.push(formatted);
185
- else nodeOptionsParts.push(formatted);
186
- }
187
- const onHanging = () => {
188
- const worker = this.#worker;
189
- if (!worker) return;
190
- const resolve = resolveRestartPromise;
191
- createWorker();
192
- logger.warn(`Sending SIGTERM signal to worker due to timeout${timeout ? ` of ${(0, date_fns_formatDuration.formatDuration)({ seconds: timeout / 1e3 })}` : ""}. Subsequent errors may be a result of the worker exiting.`);
193
- worker.end().then(() => {
194
- resolve(RESTARTED);
195
- });
196
- };
197
- let hangingTimer = false;
198
- const onActivity = () => {
199
- if (hangingTimer) clearTimeout(hangingTimer);
200
- hangingTimer = activeTasks > 0 && setTimeout(onHanging, timeout);
201
- };
202
- const createWorker = () => {
203
- const env = {
204
- ...process.env,
205
- NODE_ENV: mode,
206
- NODE_OPTIONS: nodeOptionsParts.join(" "),
207
- POWERLINES_EXECUTION_HOST_WORKER: "true"
208
- };
209
- if (env.FORCE_COLOR === void 0) {
210
- if (!env.NO_COLOR && !env.CI && env.TERM !== "dumb" && (process.stdout.isTTY || process.stderr?.isTTY)) env.FORCE_COLOR = "1";
211
- }
212
- this.#worker = new jest_worker.Worker(executionHostPath, {
213
- maxRetries: 0,
214
- exposedMethods: executionMethods,
215
- computeWorkerKey: (_, ...args) => {
216
- let executionId = "default";
217
- let configIndex = 0;
218
- if (args.length > 0 && (0, _stryke_type_checks_is_set_object.isSetObject)(args[0])) {
219
- const arg = args[0];
220
- if ((0, _stryke_type_checks_is_set_object.isSetObject)(arg.options)) {
221
- configIndex = arg.options.configIndex ?? 0;
222
- executionId = arg.options.executionId || "default";
223
- }
224
- }
225
- return `${executionId}-${configIndex}`;
226
- },
227
- forkOptions: {
228
- execArgv,
229
- env
230
- }
231
- });
232
- restartPromise = new Promise((resolve) => {
233
- resolveRestartPromise = resolve;
234
- });
235
- for (const worker of this.#worker._workerPool?._workers || []) {
236
- worker._child?.on("exit", (code, signal) => {
237
- logger.debug(`Worker process exited with code ${code} and signal ${signal}`);
238
- if ((code || signal && signal !== "SIGINT") && this.#worker) {
239
- const error = /* @__PURE__ */ new Error(`Execution Host Worker exited unexpectedly with code ${code} and signal ${signal}`);
240
- logger.error(error);
241
- throw error;
242
- }
243
- });
244
- worker._child?.on("error", (error) => {
245
- logger.error({
246
- meta: { category: "communication" },
247
- message: `Worker process emitted an error: ${error.message}`,
248
- error
249
- });
250
- });
251
- worker._child?.on("message", (data) => {
252
- onActivity();
253
- if (Array.isArray(data) && data.length > 1 && (0, _stryke_type_checks_is_number.isNumber)(data[0])) if (data[0] === 0) logger.trace(`Received message from worker: ${JSON.stringify(data.slice(1), null, 2)}`);
254
- else logger.debug(`Received error message from worker: ${JSON.stringify(data.slice(1), null, 2)}`);
255
- logger.trace(`Received message from worker: ${JSON.stringify(data, null, 2)}`);
256
- });
257
- }
258
- let aborted = false;
259
- const onActivityAbort = () => {
260
- if (!aborted) aborted = true;
261
- };
262
- const abortActivityStreamOnLog = new node_stream.Transform({ transform(_chunk, _encoding, callback) {
263
- onActivityAbort();
264
- callback();
265
- } });
266
- this.#worker.getStdout().pipe(abortActivityStreamOnLog);
267
- this.#worker.getStderr().pipe(abortActivityStreamOnLog);
268
- this.#worker.getStdout().pipe(process.stdout);
269
- this.#worker.getStderr().pipe(process.stderr);
270
- };
271
- createWorker();
272
- for (const method of executionMethods) {
273
- if (method.startsWith("_")) continue;
274
- this[method] = timeout ? async (...args) => {
275
- activeTasks++;
276
- try {
277
- let attempts = 0;
278
- for (;;) {
279
- onActivity();
280
- const result = await Promise.race([this.#worker[method](args.length > 0 && args[0] ? args[0] : {}), restartPromise]);
281
- if (result !== RESTARTED) return result;
282
- logger.warn(`Execution Host Worker was restarted while calling method "${method}" (attempt ${attempts++}). Retrying the call...`);
283
- }
284
- } finally {
285
- activeTasks--;
286
- onActivity();
287
- }
288
- } : this.#worker[method].bind(this.#worker);
289
- }
290
- }
291
- /**
292
- * Ends the worker process and cleans up any resources associated with it. This method should be called when the worker is no longer needed, to ensure that it is properly terminated and does not continue to consume system resources. If the worker is already terminated or was never initialized, this method will throw an error.
293
- *
294
- * @returns A promise that resolves when the worker has been successfully terminated.
295
- */
296
- async end() {
297
- const worker = this.#worker;
298
- if (!worker) throw new Error("Execution Host Worker is not initialized");
299
- cleanupWorkers(worker);
300
- this.#worker = void 0;
301
- return worker.end();
302
- }
303
- /**
304
- * Quietly end the worker if it exists
305
- */
306
- close() {
307
- if (this.#worker) {
308
- cleanupWorkers(this.#worker);
309
- this.#worker.end();
310
- }
311
- }
312
- };
313
-
314
- //#endregion
315
- exports.ExecutionHostWorker = ExecutionHostWorker;
@@ -1,2 +0,0 @@
1
- import { n as ExecutionHostWorkerOptions, r as NodeOptions, t as ExecutionHostWorker } from "../execution-host-worker-BjXO7l3Q.cjs";
2
- export { ExecutionHostWorker, ExecutionHostWorkerOptions, NodeOptions };
@@ -1,2 +0,0 @@
1
- import { n as ExecutionHostWorkerOptions, r as NodeOptions, t as ExecutionHostWorker } from "../execution-host-worker-B8TS4clU.mjs";
2
- export { ExecutionHostWorker, ExecutionHostWorkerOptions, NodeOptions };
@@ -1,314 +0,0 @@
1
- import { getDefaultMode } from "@powerlines/core/lib/config";
2
- import { resolve } from "@stryke/fs/resolve";
3
- import { appendPath } from "@stryke/path/append";
4
- import { isNumber } from "@stryke/type-checks/is-number";
5
- import { isSet } from "@stryke/type-checks/is-set";
6
- import { isSetObject } from "@stryke/type-checks/is-set-object";
7
- import { isSetString } from "@stryke/type-checks/is-set-string";
8
- import { isString } from "@stryke/type-checks/is-string";
9
- import { formatDuration } from "date-fns/formatDuration";
10
- import { Worker } from "jest-worker";
11
- import { Transform } from "node:stream";
12
- import { parseArgs } from "node:util";
13
-
14
- //#region src/helpers/execution-host-worker.ts
15
- const RESTARTED = Symbol("powerlines-worker:restarted");
16
- /**
17
- * Formats the debug address into a string.
18
- */
19
- const formatDebugAddress = ({ host, port }) => {
20
- return host ? `${host}:${port}` : `${port}`;
21
- };
22
- /**
23
- * Get's the debug address from the `NODE_OPTIONS` environment variable. If the
24
- * address is not found, it returns the default host (`undefined`) and port
25
- * (`9229`).
26
- *
27
- * @returns An object with the host and port of the debug address.
28
- */
29
- const getParsedDebugAddress = (address) => {
30
- if (!address || !isString(address)) return {
31
- host: void 0,
32
- port: 9229
33
- };
34
- if (address.includes(":")) {
35
- const [host, port] = address.split(":");
36
- if (!host || !port) throw new Error(`Invalid debug address: ${address}`);
37
- return {
38
- host,
39
- port: Number.parseInt(port, 10)
40
- };
41
- }
42
- return {
43
- host: void 0,
44
- port: Number.parseInt(address, 10)
45
- };
46
- };
47
- /**
48
- * Get the debug type from the `NODE_OPTIONS` environment variable.
49
- */
50
- function getNodeDebugType(nodeOptions) {
51
- if (nodeOptions.inspect) return "inspect";
52
- if (nodeOptions["inspect-brk"] || nodeOptions.inspect_brk) return "inspect-brk";
53
- }
54
- const cleanupWorkers = (worker) => {
55
- for (const curWorker of worker._workerPool?._workers || []) curWorker._child?.kill("SIGINT");
56
- };
57
- var ExecutionHostWorker = class ExecutionHostWorker {
58
- #worker;
59
- /**
60
- * Creates a new instance of the ExecutionHostWorker class, which manages a worker process for executing tasks related to the Powerlines Engine. The worker is initialized with the specified options and can be used to run tasks in an isolated environment, with support for automatic restarts and activity monitoring.
61
- *
62
- * @param executionHostPath - The path to the Execution Host file.
63
- * @param options - The options for configuring the worker, including the execution context, exposed methods, timeout, and mode.
64
- * @returns A promise that resolves to an instance of the ExecutionHostWorker class.
65
- */
66
- static async from(executionHostPath, options) {
67
- const mode = await getDefaultMode(options.context.cwd);
68
- const resolvedPath = await resolve(executionHostPath, { paths: [options.context.cwd, options.root ? appendPath(options.root, options.context.cwd) : void 0].filter(Boolean) });
69
- if (!resolvedPath) throw new Error(`Could not resolve the provided Execution Host path: \`${executionHostPath}\`.`);
70
- return new ExecutionHostWorker(resolvedPath, {
71
- mode,
72
- ...options
73
- });
74
- }
75
- /**
76
- * Create a new worker instance.
77
- *
78
- * @param executionHostPath - The path to the worker file.
79
- * @param options - The options for the worker, including exposed methods, timeout, and hooks for activity and restart.
80
- */
81
- constructor(executionHostPath, options) {
82
- this.executionHostPath = executionHostPath;
83
- this.options = options;
84
- const { timeout = 9e5, isolatedMemory = false, mode = "production", context, executionMethods } = this.options;
85
- const logger = context.extendLogger({ category: "communication" });
86
- let restartPromise;
87
- let resolveRestartPromise;
88
- let activeTasks = 0;
89
- this.#worker = void 0;
90
- process.on("exit", () => {
91
- this.close();
92
- });
93
- let nodeOptions = {};
94
- const args = [...process.execArgv];
95
- if (process.env.NODE_OPTIONS) {
96
- let isInString = false;
97
- let willStartNewArg = true;
98
- const stringifiedNodeOptions = process.env.NODE_OPTIONS.split(" ").filter((part) => (part.startsWith("\"") && part.endsWith("\"") || part.startsWith("'") && part.endsWith("'")) && part.replace(/^['"]|['"]$/g, "").split(" ").filter(isSetString).length > 0 && part.replace(/^['"]|['"]$/g, "").split(" ").filter(isSetString).every((p) => p.startsWith("--")));
99
- args.push(...stringifiedNodeOptions.map((part) => part.replace(/^['"]|['"]$/g, "")));
100
- const inputNodeModules = stringifiedNodeOptions.reduce((acc, part) => {
101
- return acc.replace(part, "").trim();
102
- }, process.env.NODE_OPTIONS);
103
- for (let i = 0; i < inputNodeModules.length; i++) {
104
- let char = inputNodeModules[i];
105
- if (char) {
106
- if (char === "\\" && isInString) {
107
- if (inputNodeModules.length === i + 1) throw new Error("Invalid escape character at the end.");
108
- char = inputNodeModules[++i];
109
- if (!char) continue;
110
- } else if (char === " " && !isInString) {
111
- willStartNewArg = true;
112
- continue;
113
- } else if (char === "\"") {
114
- isInString = !isInString;
115
- continue;
116
- }
117
- if (willStartNewArg) {
118
- args.push(char);
119
- willStartNewArg = false;
120
- } else args[args.length - 1] += char;
121
- }
122
- }
123
- if (isInString) throw new Error("Unterminated string");
124
- }
125
- if (args.length > 0) {
126
- const { values, tokens } = parseArgs({
127
- args,
128
- strict: false,
129
- tokens: true
130
- });
131
- nodeOptions = values;
132
- let orphan = null;
133
- for (let i = 0; i < tokens.length; i++) {
134
- const token = tokens[i];
135
- if (!token) continue;
136
- if (token.kind === "option-terminator") break;
137
- if (token.kind === "option") {
138
- orphan = !isSet(token.value) ? token : null;
139
- continue;
140
- }
141
- if (token.kind !== "positional") {
142
- orphan = null;
143
- continue;
144
- }
145
- if (!orphan) continue;
146
- if (orphan.name in nodeOptions && isString(nodeOptions[orphan.name])) nodeOptions[orphan.name] += ` ${token.value}`;
147
- else nodeOptions[orphan.name] = token.value;
148
- }
149
- }
150
- const originalOptions = { ...nodeOptions };
151
- delete nodeOptions.inspect;
152
- delete nodeOptions["inspect-brk"];
153
- delete nodeOptions.inspect_brk;
154
- if (mode === "development") {
155
- const nodeDebugType = getNodeDebugType(originalOptions);
156
- if (nodeDebugType) {
157
- const debuggerAddress = getParsedDebugAddress(originalOptions[nodeDebugType]);
158
- const address = {
159
- host: debuggerAddress.host,
160
- port: debuggerAddress.port === 0 ? 0 : debuggerAddress.port + 1 + 1
161
- };
162
- nodeOptions[nodeDebugType] = formatDebugAddress(address);
163
- }
164
- nodeOptions["enable-source-maps"] = true;
165
- }
166
- if (isolatedMemory) {
167
- delete nodeOptions["max-old-space-size"];
168
- delete nodeOptions.max_old_space_size;
169
- }
170
- const execArgv = [];
171
- const nodeOptionsParts = [];
172
- for (const [key, value] of Object.entries(nodeOptions)) {
173
- let formatted = null;
174
- if (value === true) formatted = `--${key}`;
175
- else if (value) formatted = `--${key}=${value.includes(" ") && !value.startsWith("\"") ? JSON.stringify(value) : value}`;
176
- if (formatted === null) continue;
177
- if ([
178
- "experimental-network-inspection",
179
- "experimental-storage-inspection",
180
- "experimental-worker-inspection",
181
- "experimental-inspector-network-resource"
182
- ].includes(key)) execArgv.push(formatted);
183
- else nodeOptionsParts.push(formatted);
184
- }
185
- const onHanging = () => {
186
- const worker = this.#worker;
187
- if (!worker) return;
188
- const resolve = resolveRestartPromise;
189
- createWorker();
190
- logger.warn(`Sending SIGTERM signal to worker due to timeout${timeout ? ` of ${formatDuration({ seconds: timeout / 1e3 })}` : ""}. Subsequent errors may be a result of the worker exiting.`);
191
- worker.end().then(() => {
192
- resolve(RESTARTED);
193
- });
194
- };
195
- let hangingTimer = false;
196
- const onActivity = () => {
197
- if (hangingTimer) clearTimeout(hangingTimer);
198
- hangingTimer = activeTasks > 0 && setTimeout(onHanging, timeout);
199
- };
200
- const createWorker = () => {
201
- const env = {
202
- ...process.env,
203
- NODE_ENV: mode,
204
- NODE_OPTIONS: nodeOptionsParts.join(" "),
205
- POWERLINES_EXECUTION_HOST_WORKER: "true"
206
- };
207
- if (env.FORCE_COLOR === void 0) {
208
- if (!env.NO_COLOR && !env.CI && env.TERM !== "dumb" && (process.stdout.isTTY || process.stderr?.isTTY)) env.FORCE_COLOR = "1";
209
- }
210
- this.#worker = new Worker(executionHostPath, {
211
- maxRetries: 0,
212
- exposedMethods: executionMethods,
213
- computeWorkerKey: (_, ...args) => {
214
- let executionId = "default";
215
- let configIndex = 0;
216
- if (args.length > 0 && isSetObject(args[0])) {
217
- const arg = args[0];
218
- if (isSetObject(arg.options)) {
219
- configIndex = arg.options.configIndex ?? 0;
220
- executionId = arg.options.executionId || "default";
221
- }
222
- }
223
- return `${executionId}-${configIndex}`;
224
- },
225
- forkOptions: {
226
- execArgv,
227
- env
228
- }
229
- });
230
- restartPromise = new Promise((resolve) => {
231
- resolveRestartPromise = resolve;
232
- });
233
- for (const worker of this.#worker._workerPool?._workers || []) {
234
- worker._child?.on("exit", (code, signal) => {
235
- logger.debug(`Worker process exited with code ${code} and signal ${signal}`);
236
- if ((code || signal && signal !== "SIGINT") && this.#worker) {
237
- const error = /* @__PURE__ */ new Error(`Execution Host Worker exited unexpectedly with code ${code} and signal ${signal}`);
238
- logger.error(error);
239
- throw error;
240
- }
241
- });
242
- worker._child?.on("error", (error) => {
243
- logger.error({
244
- meta: { category: "communication" },
245
- message: `Worker process emitted an error: ${error.message}`,
246
- error
247
- });
248
- });
249
- worker._child?.on("message", (data) => {
250
- onActivity();
251
- if (Array.isArray(data) && data.length > 1 && isNumber(data[0])) if (data[0] === 0) logger.trace(`Received message from worker: ${JSON.stringify(data.slice(1), null, 2)}`);
252
- else logger.debug(`Received error message from worker: ${JSON.stringify(data.slice(1), null, 2)}`);
253
- logger.trace(`Received message from worker: ${JSON.stringify(data, null, 2)}`);
254
- });
255
- }
256
- let aborted = false;
257
- const onActivityAbort = () => {
258
- if (!aborted) aborted = true;
259
- };
260
- const abortActivityStreamOnLog = new Transform({ transform(_chunk, _encoding, callback) {
261
- onActivityAbort();
262
- callback();
263
- } });
264
- this.#worker.getStdout().pipe(abortActivityStreamOnLog);
265
- this.#worker.getStderr().pipe(abortActivityStreamOnLog);
266
- this.#worker.getStdout().pipe(process.stdout);
267
- this.#worker.getStderr().pipe(process.stderr);
268
- };
269
- createWorker();
270
- for (const method of executionMethods) {
271
- if (method.startsWith("_")) continue;
272
- this[method] = timeout ? async (...args) => {
273
- activeTasks++;
274
- try {
275
- let attempts = 0;
276
- for (;;) {
277
- onActivity();
278
- const result = await Promise.race([this.#worker[method](args.length > 0 && args[0] ? args[0] : {}), restartPromise]);
279
- if (result !== RESTARTED) return result;
280
- logger.warn(`Execution Host Worker was restarted while calling method "${method}" (attempt ${attempts++}). Retrying the call...`);
281
- }
282
- } finally {
283
- activeTasks--;
284
- onActivity();
285
- }
286
- } : this.#worker[method].bind(this.#worker);
287
- }
288
- }
289
- /**
290
- * Ends the worker process and cleans up any resources associated with it. This method should be called when the worker is no longer needed, to ensure that it is properly terminated and does not continue to consume system resources. If the worker is already terminated or was never initialized, this method will throw an error.
291
- *
292
- * @returns A promise that resolves when the worker has been successfully terminated.
293
- */
294
- async end() {
295
- const worker = this.#worker;
296
- if (!worker) throw new Error("Execution Host Worker is not initialized");
297
- cleanupWorkers(worker);
298
- this.#worker = void 0;
299
- return worker.end();
300
- }
301
- /**
302
- * Quietly end the worker if it exists
303
- */
304
- close() {
305
- if (this.#worker) {
306
- cleanupWorkers(this.#worker);
307
- this.#worker.end();
308
- }
309
- }
310
- };
311
-
312
- //#endregion
313
- export { ExecutionHostWorker };
314
- //# sourceMappingURL=execution-host-worker.mjs.map