parallel-park 0.2.0 → 0.2.1

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/.node-version CHANGED
@@ -1 +1 @@
1
- v16.14.0
1
+ v20.11.1
package/.npm-version CHANGED
@@ -1 +1 @@
1
- 8.3.1
1
+ 10.2.4
package/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- MIT License Copyright (c) 2022 Lily Scott
1
+ MIT License Copyright (c) 2022-2024 Lily Skye
2
2
 
3
3
  Permission is hereby granted, free of
4
4
  charge, to any person obtaining a copy of this software and associated
@@ -18,4 +18,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
18
18
  EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19
19
  OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
20
  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- THE SOFTWARE.
21
+ THE SOFTWARE.
@@ -6,27 +6,37 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const fs_1 = __importDefault(require("fs"));
7
7
  const vm_1 = __importDefault(require("vm"));
8
8
  const make_module_env_1 = __importDefault(require("make-module-env"));
9
- const comms = fs_1.default.createWriteStream(
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(
10
14
  // @ts-ignore
11
15
  null, { fd: 3 });
12
- const [inputsString, fnString, callingFile] = process.argv.slice(2);
13
- function onSuccess(data) {
14
- comms.write(JSON.stringify({ type: "success", data }));
15
- }
16
- function onError(error) {
17
- comms.write(JSON.stringify({
18
- type: "error",
19
- error: {
20
- name: error.name,
21
- message: error.message,
22
- stack: error.stack,
23
- },
24
- }));
25
- }
26
- try {
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", { 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
+ }
27
38
  const wrapperFn = vm_1.default.runInThisContext(`(function moduleWrapper(exports, require, module, __filename, __dirname) {
28
- return ${fnString};})`);
29
- const inputs = JSON.parse(inputsString);
39
+ return ${fnString};})`);
30
40
  const env = (0, make_module_env_1.default)(callingFile);
31
41
  const fn = wrapperFn(env.exports, env.require, env.module, env.__filename, env.__dirname);
32
42
  const result = fn(inputs);
@@ -39,6 +49,18 @@ return ${fnString};})`);
39
49
  onSuccess(result);
40
50
  }
41
51
  }
42
- catch (err) {
43
- onError(err);
52
+ function onSuccess(data) {
53
+ debug("in onSuccess", { data });
54
+ commsOut.end(JSON.stringify({ type: "success", data }));
55
+ }
56
+ function onError(error) {
57
+ debug("in onError", { 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
+ }));
44
66
  }
@@ -5,8 +5,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.inChildProcess = void 0;
7
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 read_until_end_1 = require("./read-until-end");
11
+ const debug = (0, debug_1.default)("parallel-park:in-child-process");
8
12
  const runnerPath = require.resolve("../dist/child-process-worker");
9
13
  const inChildProcess = (...args) => {
14
+ var _a;
10
15
  const inputs = typeof args[0] === "function" ? {} : args[0];
11
16
  const functionToRun = typeof args[0] === "function" ? args[0] : args[1];
12
17
  if (typeof inputs !== "object") {
@@ -15,24 +20,33 @@ const inChildProcess = (...args) => {
15
20
  if (typeof functionToRun !== "function") {
16
21
  throw new Error("The second argument to inChildProcess should be a function to run in the child process.");
17
22
  }
18
- const here = new Error("in inChildProcess");
19
- const callingFileLine = here.stack.split("\n").slice(2)[0];
20
- const matches = callingFileLine.match(/\(([^\)]+):\d+:\d+\)/);
21
- const [_, callingFile] = matches;
22
- const child = child_process_1.default.spawn(process.argv[0], [runnerPath, JSON.stringify(inputs), functionToRun.toString(), callingFile], {
23
- stdio: ["inherit", "inherit", "inherit", "pipe"],
23
+ const here = new error_utils_1.ParsedError(new Error("here"));
24
+ const callingFrame = here.stackFrames[1];
25
+ const callingFile = (_a = callingFrame === null || callingFrame === void 0 ? void 0 : callingFrame.fileName) !== null && _a !== void 0 ? _a : "unknown file";
26
+ debug("spawning child process:", [process.argv[0], runnerPath]);
27
+ const child = child_process_1.default.spawn(process.argv[0], [runnerPath], {
28
+ stdio: ["inherit", "inherit", "inherit", "pipe", "pipe"],
24
29
  });
25
30
  return new Promise((resolve, reject) => {
26
31
  child.on("error", reject);
32
+ const commsOut = child.stdio[3];
33
+ const commsIn = child.stdio[4];
34
+ child.on("spawn", () => {
35
+ const dataToSend = JSON.stringify([
36
+ inputs,
37
+ functionToRun.toString(),
38
+ callingFile,
39
+ ]);
40
+ debug("sending inputs to child process:", dataToSend);
41
+ commsOut.end(dataToSend, "utf-8");
42
+ });
27
43
  let receivedData = "";
28
- const comms = child.stdio && child.stdio[3];
29
- if (!comms) {
30
- reject(new Error("Failed to establish communication pipe with child process"));
31
- }
32
- comms.on("data", (chunk) => {
33
- receivedData += chunk.toString();
44
+ (0, read_until_end_1.readUntilEnd)(commsIn).then((data) => {
45
+ debug("received data from child process");
46
+ receivedData = data;
34
47
  });
35
48
  child.on("close", (code, signal) => {
49
+ debug("child process closed:", { code, signal });
36
50
  if (code !== 0) {
37
51
  reject(new Error(`Child process exited with nonzero status code: ${JSON.stringify({
38
52
  code,
@@ -40,6 +54,7 @@ const inChildProcess = (...args) => {
40
54
  })}`));
41
55
  }
42
56
  else {
57
+ debug("parsing received data from child process...");
43
58
  const result = JSON.parse(receivedData);
44
59
  switch (result.type) {
45
60
  case "success": {
@@ -60,7 +75,7 @@ const inChildProcess = (...args) => {
60
75
  .filter((line) => !/node:internal|node:events/.test(line))
61
76
  .map((line) => {
62
77
  if (/evalmachine/.test(line)) {
63
- const lineWithoutEvalMachine = line.replace(/evalmachine(\.<anonymous>)?/, "<function passed into inChildProcess>");
78
+ const lineWithoutEvalMachine = line.replace(/evalmachine(?:\.<anonymous>)?/, "<function passed into inChildProcess>");
64
79
  const matches = line.match(/:(\d+):(\d+)\)?$/);
65
80
  if (!matches) {
66
81
  return lineWithoutEvalMachine;
@@ -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 = void 0;
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 = streamId;
12
+ streamId++;
13
+ return new Promise((resolve) => {
14
+ let data = "";
15
+ stream.on("data", (chunk) => {
16
+ debug("received data chunk from stream", id);
17
+ data += chunk.toString("utf-8");
18
+ });
19
+ stream.on("close", () => {
20
+ debug(`stream ${id} closed; resolving`);
21
+ resolve(data);
22
+ });
23
+ });
24
+ }
25
+ exports.readUntilEnd = readUntilEnd;
package/dist/run-jobs.js CHANGED
@@ -1,6 +1,11 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.runJobs = void 0;
7
+ const debug_1 = __importDefault(require("debug"));
8
+ const debug = (0, debug_1.default)("parallel-park:run-jobs");
4
9
  function isThenable(value) {
5
10
  return (typeof value === "object" &&
6
11
  value != null &&
@@ -8,11 +13,15 @@ function isThenable(value) {
8
13
  typeof value.then === "function");
9
14
  }
10
15
  const NOTHING = Symbol("NOTHING");
16
+ let runJobsCallId = 0;
11
17
  async function runJobs(inputs, mapper, {
12
18
  /**
13
19
  * How many jobs are allowed to run at once.
14
20
  */
15
21
  concurrency = 8, } = {}) {
22
+ const callId = runJobsCallId;
23
+ runJobsCallId++;
24
+ debug(`runJobs called (callId: ${callId})`, { inputs, mapper, concurrency });
16
25
  if (concurrency < 1) {
17
26
  throw new Error("Concurrency can't be less than one; that doesn't make any sense.");
18
27
  }
@@ -22,6 +31,7 @@ concurrency = 8, } = {}) {
22
31
  const maybeLength = Array.isArray(inputs) ? inputs.length : null;
23
32
  let iteratorDone = false;
24
33
  async function readInput() {
34
+ debug(`reading next input (callId: ${callId})`);
25
35
  let nextResult = inputIterator.next();
26
36
  if (isThenable(nextResult)) {
27
37
  nextResult = await nextResult;
@@ -50,14 +60,17 @@ concurrency = 8, } = {}) {
50
60
  const inputIndex = unstartedIndex;
51
61
  unstartedIndex++;
52
62
  const input = inputsArray[inputIndex];
63
+ debug(`mapping input into Promise (callId: ${callId})`);
53
64
  const promise = mapper(input, inputIndex, maybeLength || Infinity);
54
65
  if (!isThenable(promise)) {
55
66
  throw new Error("Mapper function passed into runJobs didn't return a Promise. The mapper function should always return a Promise. The easiest way to ensure this is the case is to make your mapper function an async function.");
56
67
  }
57
68
  const promiseWithMore = promise.then((result) => {
69
+ debug(`child Promise resolved for input (callId: ${callId}):`, input);
58
70
  results[inputIndex] = result;
59
71
  runningPromises.delete(promiseWithMore);
60
72
  }, (err) => {
73
+ debug(`child Promise rejected for input (callId: ${callId}):`, input, "with error:", err);
61
74
  runningPromises.delete(promiseWithMore);
62
75
  error = err;
63
76
  });
@@ -72,13 +85,16 @@ concurrency = 8, } = {}) {
72
85
  while (runningPromises.size > 0 && !error) {
73
86
  await Promise.race(runningPromises.values());
74
87
  if (error) {
88
+ debug(`throwing error (callId: ${callId})`);
75
89
  throw error;
76
90
  }
77
91
  await proceed();
78
92
  }
79
93
  if (error) {
94
+ debug(`throwing error (callId: ${callId})`);
80
95
  throw error;
81
96
  }
97
+ debug(`all done (callId: ${callId})`);
82
98
  return results;
83
99
  }
84
100
  exports.runJobs = runJobs;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "parallel-park",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Parallel/concurrent async work, optionally using multiple processes",
5
5
  "main": "dist/index.js",
6
6
  "types": "src/index.ts",
@@ -21,23 +21,25 @@
21
21
  "promise",
22
22
  "map"
23
23
  ],
24
- "author": "Lily Scott <me@suchipi.com>",
24
+ "author": "Lily Skye <me@suchipi.com>",
25
25
  "license": "MIT",
26
26
  "repository": {
27
27
  "type": "git",
28
- "url": "https://github.com/suchipi/parallel-park.git"
28
+ "url": "git+https://github.com/suchipi/parallel-park.git"
29
29
  },
30
30
  "devDependencies": {
31
- "@babel/core": "^7.17.5",
32
- "@babel/preset-env": "^7.16.11",
33
- "@babel/preset-typescript": "^7.16.7",
34
- "@types/jest": "^27.4.0",
35
- "@types/node": "^17.0.18",
36
- "babel-jest": "^27.5.1",
37
- "jest": "^27.5.1",
38
- "typescript": "^4.5.5"
31
+ "@babel/core": "^7.24.5",
32
+ "@babel/preset-env": "^7.24.5",
33
+ "@babel/preset-typescript": "^7.24.1",
34
+ "@types/debug": "^4.1.12",
35
+ "@types/jest": "^29.5.12",
36
+ "@types/node": "^20.12.10",
37
+ "babel-jest": "^29.7.0",
38
+ "jest": "^29.7.0",
39
+ "typescript": "^5.4.5"
39
40
  },
40
41
  "dependencies": {
41
- "make-module-env": "^1.0.1"
42
+ "@suchipi/error-utils": "^0.2.0",
43
+ "make-module-env": "^1.1.3"
42
44
  }
43
45
  }
@@ -1,39 +1,48 @@
1
1
  import fs from "fs";
2
2
  import vm from "vm";
3
3
  import makeModuleEnv from "make-module-env";
4
+ import makeDebug from "debug";
5
+ import { readUntilEnd } from "./read-until-end";
6
+ import path from "path";
4
7
 
5
- const comms = fs.createWriteStream(
8
+ const debug = makeDebug("parallel-park:child-process-worker");
9
+
10
+ const commsIn = fs.createReadStream(
6
11
  // @ts-ignore
7
12
  null,
8
13
  { fd: 3 }
9
14
  );
15
+ const commsOut = fs.createWriteStream(
16
+ // @ts-ignore
17
+ null,
18
+ { fd: 4 }
19
+ );
10
20
 
11
- const [inputsString, fnString, callingFile] = process.argv.slice(2);
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);
12
33
 
13
- function onSuccess(data: any) {
14
- comms.write(JSON.stringify({ type: "success", data }));
15
- }
34
+ function onReady(inputs: any, fnString: string, callingFile: string) {
35
+ debug("in onReady", { inputs, fnString, callingFile });
16
36
 
17
- function onError(error: Error) {
18
- comms.write(
19
- JSON.stringify({
20
- type: "error",
21
- error: {
22
- name: error.name,
23
- message: error.message,
24
- stack: error.stack,
25
- },
26
- })
27
- );
28
- }
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
+ }
29
41
 
30
- try {
31
42
  const wrapperFn = vm.runInThisContext(
32
43
  `(function moduleWrapper(exports, require, module, __filename, __dirname) {
33
- return ${fnString};})`
44
+ return ${fnString};})`
34
45
  );
35
- const inputs = JSON.parse(inputsString);
36
-
37
46
  const env = makeModuleEnv(callingFile);
38
47
  const fn = wrapperFn(
39
48
  env.exports,
@@ -53,6 +62,25 @@ return ${fnString};})`
53
62
  } else {
54
63
  onSuccess(result);
55
64
  }
56
- } catch (err) {
57
- onError(err as Error);
65
+ }
66
+
67
+ function onSuccess(data: any) {
68
+ debug("in onSuccess", { data });
69
+
70
+ commsOut.end(JSON.stringify({ type: "success", data }));
71
+ }
72
+
73
+ function onError(error: Error) {
74
+ debug("in onError", { 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
+ );
58
86
  }
@@ -1,4 +1,10 @@
1
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 { readUntilEnd } from "./read-until-end";
6
+
7
+ const debug = makeDebug("parallel-park:in-child-process");
2
8
 
3
9
  const runnerPath = require.resolve("../dist/child-process-worker");
4
10
 
@@ -11,7 +17,7 @@ type InChildProcess = {
11
17
  <Result>(functionToRun: () => Result | Promise<Result>): Promise<Result>;
12
18
  };
13
19
 
14
- export const inChildProcess: InChildProcess = (...args) => {
20
+ export const inChildProcess: InChildProcess = (...args: Array<any>) => {
15
21
  const inputs = typeof args[0] === "function" ? {} : args[0];
16
22
  const functionToRun = typeof args[0] === "function" ? args[0] : args[1];
17
23
 
@@ -27,36 +33,42 @@ export const inChildProcess: InChildProcess = (...args) => {
27
33
  );
28
34
  }
29
35
 
30
- const here = new Error("in inChildProcess");
36
+ const here = new ParsedError(new Error("here"));
37
+
38
+ const callingFrame = here.stackFrames[1];
39
+ const callingFile = callingFrame?.fileName ?? "unknown file";
31
40
 
32
- const callingFileLine = here.stack!.split("\n").slice(2)[0];
33
- const matches = callingFileLine.match(/\(([^\)]+):\d+:\d+\)/);
34
- const [_, callingFile] = matches!;
41
+ debug("spawning child process:", [process.argv[0], runnerPath]);
35
42
 
36
- const child = child_process.spawn(
37
- process.argv[0],
38
- [runnerPath, JSON.stringify(inputs), functionToRun.toString(), callingFile],
39
- {
40
- stdio: ["inherit", "inherit", "inherit", "pipe"],
41
- }
42
- );
43
+ const child = child_process.spawn(process.argv[0], [runnerPath], {
44
+ stdio: ["inherit", "inherit", "inherit", "pipe", "pipe"],
45
+ });
43
46
 
44
47
  return new Promise((resolve, reject) => {
45
48
  child.on("error", reject);
46
49
 
50
+ const commsOut: stream.Writable = child.stdio![3] as any;
51
+ const commsIn: stream.Readable = child.stdio![4] as any;
52
+
53
+ child.on("spawn", () => {
54
+ const dataToSend = JSON.stringify([
55
+ inputs,
56
+ functionToRun.toString(),
57
+ callingFile,
58
+ ]);
59
+ debug("sending inputs to child process:", dataToSend);
60
+ commsOut.end(dataToSend, "utf-8");
61
+ });
62
+
47
63
  let receivedData = "";
48
- const comms = child.stdio && child.stdio[3];
49
- if (!comms) {
50
- reject(
51
- new Error("Failed to establish communication pipe with child process")
52
- );
53
- }
54
-
55
- comms!.on("data", (chunk) => {
56
- receivedData += chunk.toString();
64
+ readUntilEnd(commsIn).then((data) => {
65
+ debug("received data from child process");
66
+ receivedData = data;
57
67
  });
58
68
 
59
69
  child.on("close", (code, signal) => {
70
+ debug("child process closed:", { code, signal });
71
+
60
72
  if (code !== 0) {
61
73
  reject(
62
74
  new Error(
@@ -67,6 +79,7 @@ export const inChildProcess: InChildProcess = (...args) => {
67
79
  )
68
80
  );
69
81
  } else {
82
+ debug("parsing received data from child process...");
70
83
  const result = JSON.parse(receivedData);
71
84
  switch (result.type) {
72
85
  case "success": {
@@ -89,7 +102,7 @@ export const inChildProcess: InChildProcess = (...args) => {
89
102
  .map((line) => {
90
103
  if (/evalmachine/.test(line)) {
91
104
  const lineWithoutEvalMachine = line.replace(
92
- /evalmachine(\.<anonymous>)?/,
105
+ /evalmachine(?:\.<anonymous>)?/,
93
106
  "<function passed into inChildProcess>"
94
107
  );
95
108
 
@@ -0,0 +1,24 @@
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 = streamId;
9
+ streamId++;
10
+
11
+ return new Promise((resolve) => {
12
+ let data = "";
13
+
14
+ stream.on("data", (chunk) => {
15
+ debug("received data chunk from stream", id);
16
+ data += chunk.toString("utf-8");
17
+ });
18
+
19
+ stream.on("close", () => {
20
+ debug(`stream ${id} closed; resolving`);
21
+ resolve(data);
22
+ });
23
+ });
24
+ }
package/src/run-jobs.ts CHANGED
@@ -1,3 +1,6 @@
1
+ import makeDebug from "debug";
2
+ const debug = makeDebug("parallel-park:run-jobs");
3
+
1
4
  function isThenable<T>(value: unknown): value is Promise<T> {
2
5
  return (
3
6
  typeof value === "object" &&
@@ -9,6 +12,8 @@ function isThenable<T>(value: unknown): value is Promise<T> {
9
12
 
10
13
  const NOTHING = Symbol("NOTHING");
11
14
 
15
+ let runJobsCallId = 0;
16
+
12
17
  export async function runJobs<T, U>(
13
18
  inputs: Iterable<T | Promise<T>> | AsyncIterable<T | Promise<T>>,
14
19
  mapper: (input: T, index: number, length: number) => Promise<U>,
@@ -24,6 +29,11 @@ export async function runJobs<T, U>(
24
29
  concurrency?: number;
25
30
  } = {}
26
31
  ): Promise<Array<U>> {
32
+ const callId = runJobsCallId;
33
+ runJobsCallId++;
34
+
35
+ debug(`runJobs called (callId: ${callId})`, { inputs, mapper, concurrency });
36
+
27
37
  if (concurrency < 1) {
28
38
  throw new Error(
29
39
  "Concurrency can't be less than one; that doesn't make any sense."
@@ -39,6 +49,7 @@ export async function runJobs<T, U>(
39
49
  let iteratorDone = false;
40
50
 
41
51
  async function readInput(): Promise<boolean> {
52
+ debug(`reading next input (callId: ${callId})`);
42
53
  let nextResult = inputIterator.next();
43
54
  if (isThenable(nextResult)) {
44
55
  nextResult = await nextResult;
@@ -70,6 +81,7 @@ export async function runJobs<T, U>(
70
81
  unstartedIndex++;
71
82
 
72
83
  const input = inputsArray[inputIndex];
84
+ debug(`mapping input into Promise (callId: ${callId})`);
73
85
  const promise = mapper(input, inputIndex, maybeLength || Infinity);
74
86
 
75
87
  if (!isThenable(promise)) {
@@ -80,10 +92,17 @@ export async function runJobs<T, U>(
80
92
 
81
93
  const promiseWithMore = promise.then(
82
94
  (result) => {
95
+ debug(`child Promise resolved for input (callId: ${callId}):`, input);
83
96
  results[inputIndex] = result;
84
97
  runningPromises.delete(promiseWithMore);
85
98
  },
86
99
  (err) => {
100
+ debug(
101
+ `child Promise rejected for input (callId: ${callId}):`,
102
+ input,
103
+ "with error:",
104
+ err
105
+ );
87
106
  runningPromises.delete(promiseWithMore);
88
107
  error = err;
89
108
  }
@@ -101,14 +120,17 @@ export async function runJobs<T, U>(
101
120
  while (runningPromises.size > 0 && !error) {
102
121
  await Promise.race(runningPromises.values());
103
122
  if (error) {
123
+ debug(`throwing error (callId: ${callId})`);
104
124
  throw error;
105
125
  }
106
126
  await proceed();
107
127
  }
108
128
 
109
129
  if (error) {
130
+ debug(`throwing error (callId: ${callId})`);
110
131
  throw error;
111
132
  }
112
133
 
134
+ debug(`all done (callId: ${callId})`);
113
135
  return results;
114
136
  }