@parallel-park/in-child-process 0.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License Copyright (c) 2022-2026 Lily Skye
2
+
3
+ Permission is hereby granted, free of
4
+ charge, to any person obtaining a copy of this software and associated
5
+ documentation files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use, copy, modify, merge,
7
+ publish, distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to the
9
+ following conditions:
10
+
11
+ The above copyright notice and this permission notice
12
+ (including the next paragraph) shall be included in all copies or substantial
13
+ portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
16
+ ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
18
+ EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,129 @@
1
+ # @parallel-park/in-child-process
2
+
3
+ Run code within a child process.
4
+
5
+ ## Usage
6
+
7
+ `@parallel-park/in-child-process` exports one function: `inChildProcess`.
8
+
9
+ ### `inChildProcess`
10
+
11
+ `inChildProcess` is a function that you pass a function into, and it spawns a separate node process to run your function in. Once your function has completed, its return/resolved value will be sent back to the node process you called `inChildProcess` from.
12
+
13
+ ```ts
14
+ import { inChildProcess } from "parallel-park";
15
+
16
+ const result = await inChildProcess(() => {
17
+ return 2 + 2;
18
+ });
19
+
20
+ console.log(result); // 4
21
+ ```
22
+
23
+ Your function can also return a Promise:
24
+
25
+ ```ts
26
+ // Either by returning a Promise directly...
27
+ await inChildProcess(() => {
28
+ return Promise.resolve(2 + 2);
29
+ });
30
+
31
+ // Or by using an async function, which returns a Promise that resolves to the function's return value
32
+ await inChildProcess(async () => {
33
+ return 2 + 2;
34
+ });
35
+ ```
36
+
37
+ > ⚠️ NOTE: The return value of your function must be JSON-serializable, or else it won't make it across the gap between the parent node process and the child one.
38
+
39
+ The function you pass into `inChildProcess` will be executed in a separate node process; as such, it won't be able to access variables defined in the file calling `inChildProcess`:
40
+
41
+ ```ts
42
+ const myName = "Lily";
43
+
44
+ await inChildProcess(() => {
45
+ // Throws an error: myName is not defined
46
+ return myName + "!";
47
+ });
48
+ ```
49
+
50
+ To work around this, you can pass an object into `inChildProcess` as its first argument, before the function. When called this way, the function will receive that object:
51
+
52
+ ```ts
53
+ await inChildProcess({ myName: "Lily" }, (data) => {
54
+ const myName = data.myName;
55
+
56
+ // No longer throws an error
57
+ return myName + "!";
58
+ });
59
+ ```
60
+
61
+ It's common to use object shorthand and destructuring syntax when passing values in:
62
+
63
+ ```ts
64
+ const myName = "Lily";
65
+
66
+ await inChildProcess({ myName }, ({ myName }) => {
67
+ return myName + "!";
68
+ });
69
+ ```
70
+
71
+ > ⚠️ NOTE: The values in your input object must be JSON-serializable, or else they won't make it across the gap between the parent node process and the child one.
72
+
73
+ Because the inputs have to be JSON-serializable, you may run into an issue if trying to use an external module within the child process:
74
+
75
+ ```ts
76
+ const util = require("util");
77
+
78
+ await inChildProcess({ util }, ({ util }) => {
79
+ const someData = { something: true };
80
+ // Throws an error: util.inspect is not a function
81
+ return util.inspect(someData);
82
+ });
83
+ ```
84
+
85
+ To work around this, call `require` inside the child process function:
86
+
87
+ ```ts
88
+ await inChildProcess(() => {
89
+ const util = require("util"); // the require is inside the function now
90
+
91
+ const someData = { something: true };
92
+
93
+ // No longer throws an error
94
+ return util.inspect(someData);
95
+ });
96
+ ```
97
+
98
+ If you want to use the external module both inside of the child process and outside of it, `require` it in both places:
99
+
100
+ ```ts
101
+ const util = require("util");
102
+
103
+ await inChildProcess(() => {
104
+ const util = require("util");
105
+
106
+ const someData = { something: true };
107
+
108
+ // No longer throws an error
109
+ return util.inspect(someData);
110
+ });
111
+ ```
112
+
113
+ The `require` inside of the child process can also be used to load stuff from your own code into the child process:
114
+
115
+ ```ts
116
+ const file = "/home/lily/hello.txt";
117
+
118
+ await inChildProcess({ file }, async ({ file }) => {
119
+ const processFile = require("./process-file");
120
+
121
+ const results = await processFile(file);
122
+ console.log(results);
123
+ return results;
124
+ });
125
+ ```
126
+
127
+ ## License
128
+
129
+ MIT
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const fs_1 = __importDefault(require("fs"));
7
+ const vm_1 = __importDefault(require("vm"));
8
+ const make_module_env_1 = __importDefault(require("make-module-env"));
9
+ const debug_1 = __importDefault(require("debug"));
10
+ const read_until_end_1 = require("./read-until-end");
11
+ const path_1 = __importDefault(require("path"));
12
+ const debug = (0, debug_1.default)("parallel-park:child-process-worker");
13
+ const commsIn = fs_1.default.createReadStream(
14
+ // @ts-ignore
15
+ null, { fd: 3 });
16
+ const commsOut = fs_1.default.createWriteStream(
17
+ // @ts-ignore
18
+ null, { fd: 4 });
19
+ debug("reading input data...");
20
+ (0, read_until_end_1.readUntilEnd)(commsIn)
21
+ .then((data) => {
22
+ debug("parsing input data...");
23
+ try {
24
+ const [inputs, fnString, callingFile] = JSON.parse(data);
25
+ onReady(inputs, fnString, callingFile);
26
+ }
27
+ catch (err) {
28
+ onError(err);
29
+ }
30
+ })
31
+ .catch(onError);
32
+ function onReady(inputs, fnString, callingFile) {
33
+ debug("in onReady %o", { inputs, fnString, callingFile });
34
+ // Relevant when callingFile is eg. "REPL2" (from Node.js repl)
35
+ if (!path_1.default.isAbsolute(callingFile)) {
36
+ callingFile = path_1.default.join(process.cwd(), "fake-path.js");
37
+ }
38
+ const wrapperFn = vm_1.default.runInThisContext(`(function moduleWrapper(exports, require, module, __filename, __dirname) {
39
+ return ${fnString};})`);
40
+ const env = (0, make_module_env_1.default)(callingFile);
41
+ const fn = wrapperFn(env.exports, env.require, env.module, env.__filename, env.__dirname);
42
+ const result = fn(inputs);
43
+ if (typeof result === "object" &&
44
+ result != null &&
45
+ typeof result.then === "function") {
46
+ result.then(onSuccess, onError);
47
+ }
48
+ else {
49
+ onSuccess(result);
50
+ }
51
+ }
52
+ function onSuccess(data) {
53
+ debug("in onSuccess %o", { data });
54
+ commsOut.end(JSON.stringify({ type: "success", data }));
55
+ }
56
+ function onError(error) {
57
+ debug("in onError %o", { error });
58
+ commsOut.end(JSON.stringify({
59
+ type: "error",
60
+ error: {
61
+ name: error.name,
62
+ message: error.message,
63
+ stack: error.stack,
64
+ },
65
+ }));
66
+ }
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.inChildProcess = void 0;
7
+ const child_process_1 = __importDefault(require("child_process"));
8
+ const error_utils_1 = require("@suchipi/error-utils");
9
+ const debug_1 = __importDefault(require("debug"));
10
+ const util_1 = __importDefault(require("util"));
11
+ const read_until_end_1 = require("./read-until-end");
12
+ const debug = (0, debug_1.default)("parallel-park:in-child-process");
13
+ const runnerPath = require.resolve("../dist/child-process-worker");
14
+ const inChildProcess = (...args) => {
15
+ var _a;
16
+ const inputs = typeof args[0] === "function" ? {} : args[0];
17
+ const functionToRun = typeof args[0] === "function" ? args[0] : args[1];
18
+ if (typeof inputs !== "object") {
19
+ throw new Error("The first argument to inChildProcess should be an object of input data to pass to the child process.");
20
+ }
21
+ if (typeof functionToRun !== "function") {
22
+ throw new Error("The second argument to inChildProcess should be a function to run in the child process.");
23
+ }
24
+ const here = new error_utils_1.ParsedError(new Error("here"));
25
+ const callingFrame = here.stackFrames[1];
26
+ const callingFile = (_a = callingFrame === null || callingFrame === void 0 ? void 0 : callingFrame.fileName) !== null && _a !== void 0 ? _a : "unknown file";
27
+ debug("spawning child process: %o", [process.argv[0], runnerPath]);
28
+ const child = child_process_1.default.spawn(process.argv[0], [runnerPath], {
29
+ stdio: ["inherit", "inherit", "inherit", "pipe", "pipe"],
30
+ });
31
+ return new Promise((resolve, reject) => {
32
+ child.on("error", reject);
33
+ const commsOut = child.stdio[3];
34
+ const commsIn = child.stdio[4];
35
+ child.on("spawn", () => {
36
+ const dataToSend = JSON.stringify([
37
+ inputs,
38
+ functionToRun.toString(),
39
+ callingFile,
40
+ ]);
41
+ debug("sending inputs to child process: %o", dataToSend);
42
+ commsOut.end(dataToSend, "utf-8");
43
+ });
44
+ let receivedData = "";
45
+ (0, read_until_end_1.readUntilEnd)(commsIn).then((data) => {
46
+ debug("received data from child process: %o", data);
47
+ receivedData = data;
48
+ });
49
+ child.on("close", (code, signal) => {
50
+ debug("child process closed: %o", { code, signal });
51
+ if (code !== 0) {
52
+ reject(new Error(`Child process exited with nonzero status code: ${JSON.stringify({
53
+ code,
54
+ signal,
55
+ })}`));
56
+ }
57
+ else {
58
+ debug("parsing received data from child process...");
59
+ let result;
60
+ try {
61
+ result = JSON.parse(receivedData);
62
+ }
63
+ catch (err) {
64
+ reject(new Error(`parallel-park error: failed to parse received data as JSON. data was: ${util_1.default.inspect(receivedData, { colors: true, depth: Infinity })}`));
65
+ return;
66
+ }
67
+ switch (result.type) {
68
+ case "success": {
69
+ debug("child process finished successfully with result: %o", result.data);
70
+ resolve(result.data);
71
+ break;
72
+ }
73
+ case "error": {
74
+ debug("child process errored: %o", result.error);
75
+ const error = new Error(result.error.message);
76
+ Object.defineProperty(error, "name", { value: result.error.name });
77
+ Object.defineProperty(error, "stack", {
78
+ value: result.error.name +
79
+ ": " +
80
+ result.error.message +
81
+ "\n" +
82
+ result.error.stack
83
+ .split("\n")
84
+ .slice(1)
85
+ .filter((line) => !/node:internal|node:events/.test(line))
86
+ .map((line) => {
87
+ if (/evalmachine/.test(line)) {
88
+ const lineWithoutEvalMachine = line.replace(/evalmachine(?:\.<anonymous>)?/, "<function passed into inChildProcess>");
89
+ const matches = line.match(/:(\d+):(\d+)\)?$/);
90
+ if (!matches) {
91
+ return lineWithoutEvalMachine;
92
+ }
93
+ else {
94
+ let [_, row, col] = matches;
95
+ // subtract 1 from row to skip the module wrapper function line
96
+ row = row - 1;
97
+ // subtract the length of the `return ` keywords in front of the function
98
+ if (row === 1) {
99
+ col = col - `return `.length;
100
+ }
101
+ const hadParen = /\)$/.test(lineWithoutEvalMachine);
102
+ return lineWithoutEvalMachine.replace(/:\d+:\d+\)?$/, `:${row}:${col - 1}${hadParen ? ")" : ""}`);
103
+ }
104
+ }
105
+ else {
106
+ return line;
107
+ }
108
+ })
109
+ .join("\n") +
110
+ "\n" +
111
+ error
112
+ .stack.split("\n")
113
+ .slice(1)
114
+ .filter((line) => !/node:internal|node:events/.test(line))
115
+ .join("\n") +
116
+ "\n" +
117
+ here
118
+ .stack.split("\n")
119
+ .slice(2)
120
+ .filter((line) => !/node:internal|node:events/.test(line))
121
+ .join("\n"),
122
+ });
123
+ reject(error);
124
+ break;
125
+ }
126
+ default: {
127
+ reject(new Error(`Internal parallel-park error: unhandled result type: ${result.type}`));
128
+ }
129
+ }
130
+ }
131
+ });
132
+ });
133
+ };
134
+ exports.inChildProcess = inChildProcess;
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.inChildProcess = void 0;
4
+ var in_child_process_1 = require("./in-child-process");
5
+ Object.defineProperty(exports, "inChildProcess", { enumerable: true, get: function () { return in_child_process_1.inChildProcess; } });
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.readUntilEnd = readUntilEnd;
7
+ const debug_1 = __importDefault(require("debug"));
8
+ const debug = (0, debug_1.default)("parallel-park:read-until-end");
9
+ let streamId = 0;
10
+ function readUntilEnd(stream) {
11
+ const id = `${process.pid}-${streamId}`;
12
+ streamId++;
13
+ return new Promise((resolve) => {
14
+ let data = "";
15
+ stream.on("data", (chunk) => {
16
+ const chunkStr = chunk.toString("utf-8");
17
+ debug("received data chunk from stream %s: %o", id, chunkStr);
18
+ data += chunkStr;
19
+ });
20
+ stream.on("close", () => {
21
+ debug("stream %s closed; resolving with: %o", id, data);
22
+ resolve(data);
23
+ });
24
+ });
25
+ }
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@parallel-park/in-child-process",
3
+ "version": "0.3.0",
4
+ "description": "Run code within a child process",
5
+ "main": "dist/index.js",
6
+ "types": "src/index.ts",
7
+ "scripts": {
8
+ "test": "vitest run",
9
+ "build": "rm -rf ./dist && tsc"
10
+ },
11
+ "keywords": [
12
+ "parallel",
13
+ "concurrent",
14
+ "thread",
15
+ "multithread",
16
+ "worker",
17
+ "process",
18
+ "child",
19
+ "concurrency",
20
+ "co-operative",
21
+ "promise",
22
+ "map"
23
+ ],
24
+ "author": "Lily Skye <me@suchipi.com>",
25
+ "license": "MIT",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/suchipi/parallel-park.git"
29
+ },
30
+ "devDependencies": {
31
+ "@types/debug": "^4.1.12"
32
+ },
33
+ "dependencies": {
34
+ "@suchipi/error-utils": "^0.2.1",
35
+ "debug": "^4.4.3",
36
+ "make-module-env": "^1.1.3"
37
+ },
38
+ "publishConfig": {
39
+ "access": "public"
40
+ }
41
+ }
@@ -0,0 +1,86 @@
1
+ import fs from "fs";
2
+ import vm from "vm";
3
+ import makeModuleEnv from "make-module-env";
4
+ import makeDebug from "debug";
5
+ import { readUntilEnd } from "./read-until-end";
6
+ import path from "path";
7
+
8
+ const debug = makeDebug("parallel-park:child-process-worker");
9
+
10
+ const commsIn = fs.createReadStream(
11
+ // @ts-ignore
12
+ null,
13
+ { fd: 3 }
14
+ );
15
+ const commsOut = fs.createWriteStream(
16
+ // @ts-ignore
17
+ null,
18
+ { fd: 4 }
19
+ );
20
+
21
+ debug("reading input data...");
22
+ readUntilEnd(commsIn)
23
+ .then((data) => {
24
+ debug("parsing input data...");
25
+ try {
26
+ const [inputs, fnString, callingFile] = JSON.parse(data);
27
+ onReady(inputs, fnString, callingFile);
28
+ } catch (err) {
29
+ onError(err as Error);
30
+ }
31
+ })
32
+ .catch(onError);
33
+
34
+ function onReady(inputs: any, fnString: string, callingFile: string) {
35
+ debug("in onReady %o", { inputs, fnString, callingFile });
36
+
37
+ // Relevant when callingFile is eg. "REPL2" (from Node.js repl)
38
+ if (!path.isAbsolute(callingFile)) {
39
+ callingFile = path.join(process.cwd(), "fake-path.js");
40
+ }
41
+
42
+ const wrapperFn = vm.runInThisContext(
43
+ `(function moduleWrapper(exports, require, module, __filename, __dirname) {
44
+ return ${fnString};})`
45
+ );
46
+ const env = makeModuleEnv(callingFile);
47
+ const fn = wrapperFn(
48
+ env.exports,
49
+ env.require,
50
+ env.module,
51
+ env.__filename,
52
+ env.__dirname
53
+ );
54
+ const result = fn(inputs);
55
+
56
+ if (
57
+ typeof result === "object" &&
58
+ result != null &&
59
+ typeof result.then === "function"
60
+ ) {
61
+ result.then(onSuccess, onError);
62
+ } else {
63
+ onSuccess(result);
64
+ }
65
+ }
66
+
67
+ function onSuccess(data: any) {
68
+ debug("in onSuccess %o", { data });
69
+
70
+ commsOut.end(JSON.stringify({ type: "success", data }));
71
+ }
72
+
73
+ function onError(error: Error) {
74
+ debug("in onError %o", { error });
75
+
76
+ commsOut.end(
77
+ JSON.stringify({
78
+ type: "error",
79
+ error: {
80
+ name: error.name,
81
+ message: error.message,
82
+ stack: error.stack,
83
+ },
84
+ })
85
+ );
86
+ }
@@ -0,0 +1,180 @@
1
+ import child_process from "child_process";
2
+ import type * as stream from "stream";
3
+ import { ParsedError } from "@suchipi/error-utils";
4
+ import makeDebug from "debug";
5
+ import util from "util";
6
+ import { readUntilEnd } from "./read-until-end";
7
+
8
+ const debug = makeDebug("parallel-park:in-child-process");
9
+
10
+ const runnerPath = require.resolve("../dist/child-process-worker");
11
+
12
+ type InChildProcess = {
13
+ <Inputs extends { [key: string]: any }, Result>(
14
+ inputs: Inputs,
15
+ functionToRun: (inputs: Inputs) => Result | Promise<Result>
16
+ ): Promise<Result>;
17
+
18
+ <Result>(functionToRun: () => Result | Promise<Result>): Promise<Result>;
19
+ };
20
+
21
+ export const inChildProcess: InChildProcess = (...args: Array<any>) => {
22
+ const inputs = typeof args[0] === "function" ? {} : args[0];
23
+ const functionToRun = typeof args[0] === "function" ? args[0] : args[1];
24
+
25
+ if (typeof inputs !== "object") {
26
+ throw new Error(
27
+ "The first argument to inChildProcess should be an object of input data to pass to the child process."
28
+ );
29
+ }
30
+
31
+ if (typeof functionToRun !== "function") {
32
+ throw new Error(
33
+ "The second argument to inChildProcess should be a function to run in the child process."
34
+ );
35
+ }
36
+
37
+ const here = new ParsedError(new Error("here"));
38
+
39
+ const callingFrame = here.stackFrames[1];
40
+ const callingFile = callingFrame?.fileName ?? "unknown file";
41
+
42
+ debug("spawning child process: %o", [process.argv[0], runnerPath]);
43
+
44
+ const child = child_process.spawn(process.argv[0], [runnerPath], {
45
+ stdio: ["inherit", "inherit", "inherit", "pipe", "pipe"],
46
+ });
47
+
48
+ return new Promise((resolve, reject) => {
49
+ child.on("error", reject);
50
+
51
+ const commsOut: stream.Writable = child.stdio![3] as any;
52
+ const commsIn: stream.Readable = child.stdio![4] as any;
53
+
54
+ child.on("spawn", () => {
55
+ const dataToSend = JSON.stringify([
56
+ inputs,
57
+ functionToRun.toString(),
58
+ callingFile,
59
+ ]);
60
+ debug("sending inputs to child process: %o", dataToSend);
61
+ commsOut.end(dataToSend, "utf-8");
62
+ });
63
+
64
+ let receivedData = "";
65
+ readUntilEnd(commsIn).then((data) => {
66
+ debug("received data from child process: %o", data);
67
+ receivedData = data;
68
+ });
69
+
70
+ child.on("close", (code, signal) => {
71
+ debug("child process closed: %o", { code, signal });
72
+
73
+ if (code !== 0) {
74
+ reject(
75
+ new Error(
76
+ `Child process exited with nonzero status code: ${JSON.stringify({
77
+ code,
78
+ signal,
79
+ })}`
80
+ )
81
+ );
82
+ } else {
83
+ debug("parsing received data from child process...");
84
+ let result: any;
85
+ try {
86
+ result = JSON.parse(receivedData);
87
+ } catch (err) {
88
+ reject(
89
+ new Error(
90
+ `parallel-park error: failed to parse received data as JSON. data was: ${util.inspect(
91
+ receivedData,
92
+ { colors: true, depth: Infinity }
93
+ )}`
94
+ )
95
+ );
96
+ return;
97
+ }
98
+ switch (result.type) {
99
+ case "success": {
100
+ debug(
101
+ "child process finished successfully with result: %o",
102
+ result.data
103
+ );
104
+ resolve(result.data);
105
+ break;
106
+ }
107
+ case "error": {
108
+ debug("child process errored: %o", result.error);
109
+ const error = new Error(result.error.message);
110
+ Object.defineProperty(error, "name", { value: result.error.name });
111
+ Object.defineProperty(error, "stack", {
112
+ value:
113
+ result.error.name +
114
+ ": " +
115
+ result.error.message +
116
+ "\n" +
117
+ result.error.stack
118
+ .split("\n")
119
+ .slice(1)
120
+ .filter((line) => !/node:internal|node:events/.test(line))
121
+ .map((line) => {
122
+ if (/evalmachine/.test(line)) {
123
+ const lineWithoutEvalMachine = line.replace(
124
+ /evalmachine(?:\.<anonymous>)?/,
125
+ "<function passed into inChildProcess>"
126
+ );
127
+
128
+ const matches = line.match(/:(\d+):(\d+)\)?$/);
129
+ if (!matches) {
130
+ return lineWithoutEvalMachine;
131
+ } else {
132
+ let [_, row, col] = matches;
133
+ // subtract 1 from row to skip the module wrapper function line
134
+ row = row - 1;
135
+
136
+ // subtract the length of the `return ` keywords in front of the function
137
+ if (row === 1) {
138
+ col = col - `return `.length;
139
+ }
140
+
141
+ const hadParen = /\)$/.test(lineWithoutEvalMachine);
142
+
143
+ return lineWithoutEvalMachine.replace(
144
+ /:\d+:\d+\)?$/,
145
+ `:${row}:${col - 1}${hadParen ? ")" : ""}`
146
+ );
147
+ }
148
+ } else {
149
+ return line;
150
+ }
151
+ })
152
+ .join("\n") +
153
+ "\n" +
154
+ error
155
+ .stack!.split("\n")
156
+ .slice(1)
157
+ .filter((line) => !/node:internal|node:events/.test(line))
158
+ .join("\n") +
159
+ "\n" +
160
+ here
161
+ .stack!.split("\n")
162
+ .slice(2)
163
+ .filter((line) => !/node:internal|node:events/.test(line))
164
+ .join("\n"),
165
+ });
166
+ reject(error);
167
+ break;
168
+ }
169
+ default: {
170
+ reject(
171
+ new Error(
172
+ `Internal parallel-park error: unhandled result type: ${result.type}`
173
+ )
174
+ );
175
+ }
176
+ }
177
+ }
178
+ });
179
+ });
180
+ };
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export { inChildProcess } from "./in-child-process";
@@ -0,0 +1,25 @@
1
+ import type stream from "stream";
2
+ import makeDebug from "debug";
3
+ const debug = makeDebug("parallel-park:read-until-end");
4
+
5
+ let streamId = 0;
6
+
7
+ export function readUntilEnd(stream: stream.Readable): Promise<string> {
8
+ const id = `${process.pid}-${streamId}`;
9
+ streamId++;
10
+
11
+ return new Promise((resolve) => {
12
+ let data = "";
13
+
14
+ stream.on("data", (chunk) => {
15
+ const chunkStr = chunk.toString("utf-8");
16
+ debug("received data chunk from stream %s: %o", id, chunkStr);
17
+ data += chunkStr;
18
+ });
19
+
20
+ stream.on("close", () => {
21
+ debug("stream %s closed; resolving with: %o", id, data);
22
+ resolve(data);
23
+ });
24
+ });
25
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+
4
+ "include": ["./src/**/*.ts"],
5
+ "exclude": ["./**/*.test.ts"],
6
+
7
+ "compilerOptions": {
8
+ "outDir": "./dist"
9
+ }
10
+ }