threadsx 2.0.2 → 2.1.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.
Files changed (53) hide show
  1. package/dist/errors.d.ts +16 -0
  2. package/dist/errors.js +31 -0
  3. package/dist/index.d.ts +1 -0
  4. package/dist/index.js +3 -1
  5. package/dist/master/implementation.node.js +22 -2
  6. package/dist/master/invocation-proxy.js +45 -11
  7. package/dist/master/pool.d.ts +1 -24
  8. package/dist/master/pool.js +25 -3
  9. package/dist/transferable.d.ts +1 -0
  10. package/dist/types/master.d.ts +22 -1
  11. package/dist/types/master.js +0 -1
  12. package/dist/types/worker.d.ts +1 -0
  13. package/dist/worker/implementation.browser.d.ts +1 -1
  14. package/dist/worker/implementation.worker_threads.d.ts +1 -1
  15. package/dist-esm/errors.js +26 -0
  16. package/dist-esm/index.js +1 -0
  17. package/dist-esm/master/implementation.node.js +22 -2
  18. package/dist-esm/master/invocation-proxy.js +45 -11
  19. package/dist-esm/master/pool.js +25 -3
  20. package/dist-esm/types/master.js +0 -1
  21. package/esm/common.mjs +20 -0
  22. package/esm/errors.mjs +34 -0
  23. package/esm/index.mjs +16 -0
  24. package/esm/master/get-bundle-url.browser.mjs +32 -0
  25. package/esm/master/implementation.browser.mjs +76 -0
  26. package/esm/master/implementation.mjs +24 -0
  27. package/esm/master/implementation.node.mjs +190 -0
  28. package/esm/master/index.mjs +17 -0
  29. package/esm/master/invocation-proxy.mjs +164 -0
  30. package/esm/master/pool-types.mjs +20 -0
  31. package/esm/master/pool.mjs +313 -0
  32. package/esm/master/register.mjs +14 -0
  33. package/esm/master/spawn.mjs +162 -0
  34. package/esm/master/thread.mjs +28 -0
  35. package/esm/observable-promise.mjs +155 -0
  36. package/esm/observable.mjs +45 -0
  37. package/esm/ponyfills.mjs +26 -0
  38. package/esm/promise.mjs +32 -0
  39. package/esm/serializers.mjs +55 -0
  40. package/esm/symbols.mjs +13 -0
  41. package/esm/transferable.mjs +31 -0
  42. package/esm/types/master.mjs +15 -0
  43. package/esm/types/messages.mjs +25 -0
  44. package/esm/worker/implementation.browser.mjs +31 -0
  45. package/esm/worker/implementation.mjs +22 -0
  46. package/esm/worker/implementation.worker_threads.mjs +46 -0
  47. package/esm/worker/index.mjs +224 -0
  48. package/esm/worker_threads.mjs +21 -0
  49. package/package.json +20 -20
  50. package/index.mjs +0 -4
  51. package/observable.mjs +0 -1
  52. package/register.mjs +0 -2
  53. package/worker.mjs +0 -1
package/esm/common.mjs ADDED
@@ -0,0 +1,20 @@
1
+ import { createRequire as __createRequire } from "module";
2
+ import { fileURLToPath as __fileURLToPath } from "url";
3
+ import { dirname as __pathDirname } from "path";
4
+ const require = __createRequire(import.meta.url);
5
+ const __filename = __fileURLToPath(import.meta.url);
6
+ const __dirname = __pathDirname(__filename);
7
+ import { DefaultSerializer, extendSerializer } from './serializers.mjs';
8
+
9
+ let registeredSerializer = DefaultSerializer;
10
+ function registerSerializer(serializer) {
11
+ registeredSerializer = extendSerializer(registeredSerializer, serializer);
12
+ }
13
+ function deserialize(message) {
14
+ return registeredSerializer.deserialize(message);
15
+ }
16
+ function serialize(input) {
17
+ return registeredSerializer.serialize(input);
18
+ }
19
+
20
+ export { deserialize, registerSerializer, serialize };
package/esm/errors.mjs ADDED
@@ -0,0 +1,34 @@
1
+ import { createRequire as __createRequire } from "module";
2
+ import { fileURLToPath as __fileURLToPath } from "url";
3
+ import { dirname as __pathDirname } from "path";
4
+ const require = __createRequire(import.meta.url);
5
+ const __filename = __fileURLToPath(import.meta.url);
6
+ const __dirname = __pathDirname(__filename);
7
+ /**
8
+ * Thrown when a value passed to a thread cannot be cloned by the structured
9
+ * clone algorithm (e.g. functions, class instances with methods, or other
10
+ * non-serializable values). Wraps the underlying `DataCloneError` / `DOMException`
11
+ * with an actionable message. Access the original error via `.cause`.
12
+ */
13
+ class ThreadCloneError extends Error {
14
+ constructor(message, cause) {
15
+ super(message);
16
+ this.name = "ThreadCloneError";
17
+ this.cause = cause;
18
+ // Restore the prototype chain — extending built-ins breaks `instanceof`
19
+ // when compiled down to ES2015 without this.
20
+ Object.setPrototypeOf(this, ThreadCloneError.prototype);
21
+ }
22
+ }
23
+ /**
24
+ * Whether an error is a structured-clone failure. Both browsers and Node's
25
+ * `worker_threads` throw a `DOMException`/error named `"DataCloneError"` when a
26
+ * value cannot be cloned across the thread boundary.
27
+ */
28
+ function isDataCloneError(error) {
29
+ return Boolean(error &&
30
+ typeof error === "object" &&
31
+ error.name === "DataCloneError");
32
+ }
33
+
34
+ export { ThreadCloneError, isDataCloneError };
package/esm/index.mjs ADDED
@@ -0,0 +1,16 @@
1
+ import { createRequire as __createRequire } from "module";
2
+ import { fileURLToPath as __fileURLToPath } from "url";
3
+ import { dirname as __pathDirname } from "path";
4
+ const require = __createRequire(import.meta.url);
5
+ const __filename = __fileURLToPath(import.meta.url);
6
+ const __dirname = __pathDirname(__filename);
7
+ export { registerSerializer } from './common.mjs';
8
+ export { ThreadCloneError } from './errors.mjs';
9
+ export { BlobWorker, Worker } from './master/index.mjs';
10
+ export { expose } from './worker/index.mjs';
11
+ export { DefaultSerializer } from './serializers.mjs';
12
+ export { Transfer } from './transferable.mjs';
13
+ export { Pool } from './master/pool.mjs';
14
+ export { Thread } from './master/thread.mjs';
15
+ export { isWorkerRuntime } from './master/implementation.mjs';
16
+ export { spawn } from './master/spawn.mjs';
@@ -0,0 +1,32 @@
1
+ import { createRequire as __createRequire } from "module";
2
+ import { fileURLToPath as __fileURLToPath } from "url";
3
+ import { dirname as __pathDirname } from "path";
4
+ const require = __createRequire(import.meta.url);
5
+ const __filename = __fileURLToPath(import.meta.url);
6
+ const __dirname = __pathDirname(__filename);
7
+ // Source: <https://github.com/parcel-bundler/parcel/blob/master/packages/core/parcel-bundler/src/builtins/bundle-url.js>
8
+ let bundleURL;
9
+ function getBundleURLCached() {
10
+ if (!bundleURL) {
11
+ bundleURL = getBundleURL();
12
+ }
13
+ return bundleURL;
14
+ }
15
+ function getBundleURL() {
16
+ // Attempt to find the URL of the current script and use that as the base URL
17
+ try {
18
+ throw new Error;
19
+ }
20
+ catch (err) {
21
+ const matches = ("" + err.stack).match(/(https?|file|ftp|chrome-extension|moz-extension):\/\/[^)\n]+/g);
22
+ if (matches) {
23
+ return getBaseURL(matches[0]);
24
+ }
25
+ }
26
+ return "/";
27
+ }
28
+ function getBaseURL(url) {
29
+ return ("" + url).replace(/^((?:https?|file|ftp|chrome-extension|moz-extension):\/\/.+)?\/[^/]+(?:\?.*)?$/, '$1') + '/';
30
+ }
31
+
32
+ export { getBaseURL, getBundleURLCached as getBundleURL };
@@ -0,0 +1,76 @@
1
+ import { createRequire as __createRequire } from "module";
2
+ import { fileURLToPath as __fileURLToPath } from "url";
3
+ import { dirname as __pathDirname } from "path";
4
+ const require = __createRequire(import.meta.url);
5
+ const __filename = __fileURLToPath(import.meta.url);
6
+ const __dirname = __pathDirname(__filename);
7
+ import { getBundleURL as getBundleURLCached } from './get-bundle-url.browser.mjs';
8
+
9
+ const defaultPoolSize = typeof navigator !== "undefined" && navigator.hardwareConcurrency
10
+ ? navigator.hardwareConcurrency
11
+ : 4;
12
+ const isAbsoluteURL = (value) => /^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(value);
13
+ function createSourceBlobURL(code) {
14
+ const blob = new Blob([code], { type: "application/javascript" });
15
+ return URL.createObjectURL(blob);
16
+ }
17
+ function selectWorkerImplementation() {
18
+ if (typeof Worker === "undefined") {
19
+ // Might happen on Safari, for instance
20
+ // The idea is to only fail if the constructor is actually used
21
+ return class NoWebWorker {
22
+ constructor() {
23
+ throw Error("No web worker implementation available. You might have tried to spawn a worker within a worker in a browser that doesn't support workers in workers.");
24
+ }
25
+ };
26
+ }
27
+ class WebWorker extends Worker {
28
+ constructor(url, options) {
29
+ var _a, _b;
30
+ if (typeof url === "string" && options && options._baseURL) {
31
+ url = new URL(url, options._baseURL);
32
+ }
33
+ else if (typeof url === "string" && !isAbsoluteURL(url) && getBundleURLCached().match(/^file:\/\//i)) {
34
+ url = new URL(url, getBundleURLCached().replace(/\/[^/]+$/, "/"));
35
+ if ((_a = options === null || options === void 0 ? void 0 : options.CORSWorkaround) !== null && _a !== void 0 ? _a : true) {
36
+ url = createSourceBlobURL(`importScripts(${JSON.stringify(url)});`);
37
+ }
38
+ }
39
+ if (typeof url === "string" && isAbsoluteURL(url)) {
40
+ // Create source code blob loading JS file via `importScripts()`
41
+ // to circumvent worker CORS restrictions
42
+ if ((_b = options === null || options === void 0 ? void 0 : options.CORSWorkaround) !== null && _b !== void 0 ? _b : true) {
43
+ url = createSourceBlobURL(`importScripts(${JSON.stringify(url)});`);
44
+ }
45
+ }
46
+ super(url, options);
47
+ }
48
+ }
49
+ class BlobWorker extends WebWorker {
50
+ constructor(blob, options) {
51
+ const url = window.URL.createObjectURL(blob);
52
+ super(url, options);
53
+ }
54
+ static fromText(source, options) {
55
+ const blob = new window.Blob([source], { type: "text/javascript" });
56
+ return new BlobWorker(blob, options);
57
+ }
58
+ }
59
+ return {
60
+ blob: BlobWorker,
61
+ default: WebWorker
62
+ };
63
+ }
64
+ let implementation;
65
+ function getWorkerImplementation() {
66
+ if (!implementation) {
67
+ implementation = selectWorkerImplementation();
68
+ }
69
+ return implementation;
70
+ }
71
+ function isWorkerRuntime() {
72
+ const isWindowContext = typeof self !== "undefined" && typeof Window !== "undefined" && self instanceof Window;
73
+ return typeof self !== "undefined" && typeof self.postMessage === "function" && !isWindowContext;
74
+ }
75
+
76
+ export { defaultPoolSize, getWorkerImplementation, isWorkerRuntime };
@@ -0,0 +1,24 @@
1
+ import { createRequire as __createRequire } from "module";
2
+ import { fileURLToPath as __fileURLToPath } from "url";
3
+ import { dirname as __pathDirname } from "path";
4
+ const require = __createRequire(import.meta.url);
5
+ const __filename = __fileURLToPath(import.meta.url);
6
+ const __dirname = __pathDirname(__filename);
7
+ import * as implementation_browser from './implementation.browser.mjs';
8
+ import * as implementation_node from './implementation.node.mjs';
9
+
10
+ /*
11
+ * This file is only a stub to make './implementation' resolve to the right module.
12
+ */
13
+ // We alias `src/master/implementation` to `src/master/implementation.browser` for web
14
+ // browsers already in the package.json, so if get here, it's safe to pass-through the
15
+ // node implementation
16
+ const runningInNode = typeof process !== 'undefined' && process.arch !== 'browser' && 'pid' in process;
17
+ const implementation = runningInNode ? implementation_node : implementation_browser;
18
+ /** Default size of pools. Depending on the platform the value might vary from device to device. */
19
+ const defaultPoolSize = implementation.defaultPoolSize;
20
+ const getWorkerImplementation = implementation.getWorkerImplementation;
21
+ /** Returns `true` if this code is currently running in a worker. */
22
+ const isWorkerRuntime = implementation.isWorkerRuntime;
23
+
24
+ export { defaultPoolSize, getWorkerImplementation, isWorkerRuntime };
@@ -0,0 +1,190 @@
1
+ import { createRequire as __createRequire } from "module";
2
+ import { fileURLToPath as __fileURLToPath } from "url";
3
+ import { dirname as __pathDirname } from "path";
4
+ const require = __createRequire(import.meta.url);
5
+ const __filename = __fileURLToPath(import.meta.url);
6
+ const __dirname = __pathDirname(__filename);
7
+ import getCallsites from 'callsites';
8
+ import { cpus } from 'os';
9
+ import * as path from 'path';
10
+ import { fileURLToPath } from 'url';
11
+
12
+ /// <reference lib="dom" />
13
+ // NOTE: `callsites` is intentionally pinned to ^3. v4+ is ESM-only and would
14
+ // break the CommonJS (dist/) build. Do not bump it to v4 without moving the
15
+ // package to ESM-first output. (See .github/dependabot.yml, which also ignores
16
+ // its major updates.)
17
+ let detectedTsRuntime;
18
+ const defaultPoolSize = cpus().length;
19
+ /**
20
+ * Whether the application opted out of threadsx installing SIGINT/SIGTERM
21
+ * handlers (which call `process.exit()`), via `THREADS_SKIP_SIGNAL_HANDLERS`.
22
+ */
23
+ function signalHandlersDisabled() {
24
+ const value = typeof process !== "undefined" && process.env
25
+ ? process.env.THREADS_SKIP_SIGNAL_HANDLERS
26
+ : undefined;
27
+ return value === "1" || value === "true";
28
+ }
29
+ /**
30
+ * Detects an available TypeScript runtime so `.ts`/`.tsx` worker files can be
31
+ * spawned directly during development. Prefers `tsx`, falls back to `ts-node`.
32
+ */
33
+ function detectTsRuntime() {
34
+ if (typeof __non_webpack_require__ === "function") {
35
+ // Webpack build: => No TS runtime required or possible
36
+ return null;
37
+ }
38
+ if (detectedTsRuntime !== undefined) {
39
+ return detectedTsRuntime;
40
+ }
41
+ detectedTsRuntime = null;
42
+ for (const candidate of ["tsx", "ts-node"]) {
43
+ try {
44
+ eval("require").resolve(candidate);
45
+ detectedTsRuntime = candidate;
46
+ break;
47
+ }
48
+ catch (error) {
49
+ if (error && error.code === "MODULE_NOT_FOUND") {
50
+ continue;
51
+ }
52
+ // Re-throw
53
+ throw error;
54
+ }
55
+ }
56
+ return detectedTsRuntime;
57
+ }
58
+ function createTsRuntimeModule(scriptPath, runtime) {
59
+ const register = runtime === "tsx"
60
+ ? `require("tsx/cjs");`
61
+ : `require("ts-node/register/transpile-only");`;
62
+ const content = `
63
+ ${register}
64
+ require(${JSON.stringify(scriptPath)});
65
+ `;
66
+ return content;
67
+ }
68
+ function rebaseScriptPath(scriptPath, ignoreRegex) {
69
+ // An already-absolute path (e.g. from `new Worker(new URL("./w", import.meta.url))`,
70
+ // which threadsx normalizes to a filesystem path) must not be rebased onto the
71
+ // caller's directory — doing so would prepend it and corrupt the path.
72
+ if (path.isAbsolute(scriptPath)) {
73
+ return scriptPath;
74
+ }
75
+ const parentCallSite = getCallsites().find((callsite) => {
76
+ const filename = callsite.getFileName();
77
+ return Boolean(filename &&
78
+ !filename.match(ignoreRegex) &&
79
+ !filename.match(/[/\\]master[/\\]implementation/) &&
80
+ !filename.match(/^internal\/process/));
81
+ });
82
+ const rawCallerPath = parentCallSite ? parentCallSite.getFileName() : null;
83
+ let callerPath = rawCallerPath ? rawCallerPath : null;
84
+ if (callerPath && callerPath.startsWith('file:')) {
85
+ callerPath = fileURLToPath(callerPath);
86
+ }
87
+ const rebasedScriptPath = callerPath ? path.join(path.dirname(callerPath), scriptPath) : scriptPath;
88
+ return rebasedScriptPath;
89
+ }
90
+ function resolveScriptPath(scriptPath, baseURL) {
91
+ const makeRelative = (filePath) => {
92
+ // eval() hack is also webpack-related
93
+ return path.isAbsolute(filePath) ? filePath : path.join(baseURL || eval("__dirname"), filePath);
94
+ };
95
+ const workerFilePath = typeof __non_webpack_require__ === "function"
96
+ ? __non_webpack_require__.resolve(makeRelative(scriptPath))
97
+ : eval("require").resolve(makeRelative(rebaseScriptPath(scriptPath, /[/\\]worker_threads[/\\]/)));
98
+ return workerFilePath;
99
+ }
100
+ function initWorkerThreadsWorker() {
101
+ // Webpack hack
102
+ const NativeWorker = typeof __non_webpack_require__ === "function"
103
+ ? __non_webpack_require__("worker_threads").Worker
104
+ : eval("require")("worker_threads").Worker;
105
+ let allWorkers = [];
106
+ class Worker extends NativeWorker {
107
+ constructor(scriptPath, options) {
108
+ // Bundlers like webpack 5 pass a `URL` (from `new Worker(new URL(...))`)
109
+ // pointing at the emitted worker chunk.
110
+ const normalizedScriptPath = scriptPath instanceof URL ? fileURLToPath(scriptPath) : scriptPath;
111
+ const resolvedScriptPath = options && options.fromSource
112
+ ? null
113
+ : resolveScriptPath(normalizedScriptPath, (options || {})._baseURL);
114
+ const tsRuntime = resolvedScriptPath && /\.tsx?$/i.test(resolvedScriptPath) ? detectTsRuntime() : null;
115
+ if (!resolvedScriptPath) {
116
+ // `options.fromSource` is true
117
+ const sourceCode = scriptPath;
118
+ super(sourceCode, Object.assign(Object.assign({}, options), { eval: true }));
119
+ }
120
+ else if (tsRuntime) {
121
+ super(createTsRuntimeModule(resolvedScriptPath, tsRuntime), Object.assign(Object.assign({}, options), { eval: true }));
122
+ }
123
+ else if (resolvedScriptPath.match(/\.asar[/\\]/)) {
124
+ // See <https://github.com/andywer/threads-plugin/issues/17>
125
+ super(resolvedScriptPath.replace(/\.asar([/\\])/, ".asar.unpacked$1"), options);
126
+ }
127
+ else {
128
+ super(resolvedScriptPath, options);
129
+ }
130
+ this.mappedEventListeners = new WeakMap();
131
+ allWorkers.push(this);
132
+ }
133
+ addEventListener(eventName, rawListener) {
134
+ const listener = (message) => {
135
+ rawListener({ data: message });
136
+ };
137
+ this.mappedEventListeners.set(rawListener, listener);
138
+ this.on(eventName, listener);
139
+ }
140
+ removeEventListener(eventName, rawListener) {
141
+ const listener = this.mappedEventListeners.get(rawListener) || rawListener;
142
+ this.off(eventName, listener);
143
+ }
144
+ }
145
+ const terminateWorkersAndMaster = () => {
146
+ // we should terminate all workers and then gracefully shutdown self process
147
+ Promise.all(allWorkers.map(worker => worker.terminate())).then(() => process.exit(0), () => process.exit(1));
148
+ allWorkers = [];
149
+ };
150
+ // Take care to not leave orphaned processes behind. See #147.
151
+ //
152
+ // These handlers call process.exit(), which hijacks the host application's
153
+ // own shutdown. Applications that manage their own graceful shutdown can opt
154
+ // out by setting THREADS_SKIP_SIGNAL_HANDLERS. See upstream #388 / #484.
155
+ if (!signalHandlersDisabled()) {
156
+ process.on("SIGINT", () => terminateWorkersAndMaster());
157
+ process.on("SIGTERM", () => terminateWorkersAndMaster());
158
+ }
159
+ class BlobWorker extends Worker {
160
+ constructor(blob, options) {
161
+ super(Buffer.from(blob).toString("utf-8"), Object.assign(Object.assign({}, options), { fromSource: true }));
162
+ }
163
+ static fromText(source, options) {
164
+ return new Worker(source, Object.assign(Object.assign({}, options), { fromSource: true }));
165
+ }
166
+ }
167
+ return {
168
+ blob: BlobWorker,
169
+ default: Worker
170
+ };
171
+ }
172
+ let implementation;
173
+ function selectWorkerImplementation() {
174
+ return initWorkerThreadsWorker();
175
+ }
176
+ function getWorkerImplementation() {
177
+ if (!implementation) {
178
+ implementation = selectWorkerImplementation();
179
+ }
180
+ return implementation;
181
+ }
182
+ function isWorkerRuntime() {
183
+ // Webpack hack
184
+ const isMainThread = typeof __non_webpack_require__ === "function"
185
+ ? __non_webpack_require__("worker_threads").isMainThread
186
+ : eval("require")("worker_threads").isMainThread;
187
+ return !isMainThread;
188
+ }
189
+
190
+ export { defaultPoolSize, getWorkerImplementation, isWorkerRuntime };
@@ -0,0 +1,17 @@
1
+ import { createRequire as __createRequire } from "module";
2
+ import { fileURLToPath as __fileURLToPath } from "url";
3
+ import { dirname as __pathDirname } from "path";
4
+ const require = __createRequire(import.meta.url);
5
+ const __filename = __fileURLToPath(import.meta.url);
6
+ const __dirname = __pathDirname(__filename);
7
+ import { getWorkerImplementation } from './implementation.mjs';
8
+ export { isWorkerRuntime } from './implementation.mjs';
9
+ export { Pool } from './pool.mjs';
10
+ export { spawn } from './spawn.mjs';
11
+
12
+ /** Separate class to spawn workers from source code blobs or strings. */
13
+ const BlobWorker = getWorkerImplementation().blob;
14
+ /** Worker implementation. Either web worker or a node.js Worker class. */
15
+ const Worker = getWorkerImplementation().default;
16
+
17
+ export { BlobWorker, Worker };
@@ -0,0 +1,164 @@
1
+ import { createRequire as __createRequire } from "module";
2
+ import { fileURLToPath as __fileURLToPath } from "url";
3
+ import { dirname as __pathDirname } from "path";
4
+ const require = __createRequire(import.meta.url);
5
+ const __filename = __fileURLToPath(import.meta.url);
6
+ const __dirname = __pathDirname(__filename);
7
+ import DebugLogger from 'debug';
8
+ import { multicast, Observable } from 'observable-fns';
9
+ import { serialize, deserialize } from '../common.mjs';
10
+ import { isDataCloneError, ThreadCloneError } from '../errors.mjs';
11
+ import { ObservablePromise } from '../observable-promise.mjs';
12
+ import { isTransferDescriptor } from '../transferable.mjs';
13
+ import { MasterMessageType, WorkerMessageType } from '../types/messages.mjs';
14
+
15
+ /*
16
+ * This source file contains the code for proxying calls in the master thread to calls in the workers
17
+ * by `.postMessage()`-ing.
18
+ *
19
+ * Keep in mind that this code can make or break the program's performance! Need to optimize more…
20
+ */
21
+ const debugMessages = DebugLogger("threads:master:messages");
22
+ let nextJobUID = 1;
23
+ const dedupe = (array) => Array.from(new Set(array));
24
+ const isJobErrorMessage = (data) => data && data.type === WorkerMessageType.error;
25
+ const isJobResultMessage = (data) => data && data.type === WorkerMessageType.result;
26
+ const isJobStartMessage = (data) => data && data.type === WorkerMessageType.running;
27
+ function createObservableForJob(worker, jobUID) {
28
+ return new Observable(observer => {
29
+ let asyncType;
30
+ let settled = false;
31
+ const cleanup = () => {
32
+ worker.removeEventListener("message", messageHandler);
33
+ worker.removeEventListener("error", errorHandler);
34
+ worker.removeEventListener("exit", exitHandler);
35
+ };
36
+ const messageHandler = ((event) => {
37
+ debugMessages("Message from worker:", event.data);
38
+ if (!event.data || event.data.uid !== jobUID)
39
+ return;
40
+ if (isJobStartMessage(event.data)) {
41
+ asyncType = event.data.resultType;
42
+ }
43
+ else if (isJobResultMessage(event.data)) {
44
+ if (asyncType === "promise") {
45
+ if (typeof event.data.payload !== "undefined") {
46
+ observer.next(deserialize(event.data.payload));
47
+ }
48
+ settled = true;
49
+ observer.complete();
50
+ cleanup();
51
+ }
52
+ else {
53
+ if (typeof event.data.payload !== "undefined") {
54
+ observer.next(deserialize(event.data.payload));
55
+ }
56
+ if (event.data.complete) {
57
+ settled = true;
58
+ observer.complete();
59
+ cleanup();
60
+ }
61
+ }
62
+ }
63
+ else if (isJobErrorMessage(event.data)) {
64
+ const error = deserialize(event.data.error);
65
+ settled = true;
66
+ observer.error(error);
67
+ cleanup();
68
+ }
69
+ });
70
+ // If the worker crashes or is terminated before the job produces a result,
71
+ // reject the pending job instead of leaving the promise hanging forever.
72
+ // See #386.
73
+ const errorHandler = ((event) => {
74
+ if (settled)
75
+ return;
76
+ settled = true;
77
+ const error = event && event.data instanceof Error
78
+ ? event.data
79
+ : Error(String((event && event.data) || "Worker errored before the job completed."));
80
+ observer.error(error);
81
+ cleanup();
82
+ });
83
+ const exitHandler = ((event) => {
84
+ if (settled)
85
+ return;
86
+ settled = true;
87
+ const exitCode = event ? event.data : undefined;
88
+ observer.error(Error(`Worker terminated before the job completed (exit code: ${exitCode}).`));
89
+ cleanup();
90
+ });
91
+ worker.addEventListener("message", messageHandler);
92
+ worker.addEventListener("error", errorHandler);
93
+ worker.addEventListener("exit", exitHandler);
94
+ return () => {
95
+ if (asyncType === "observable" || !asyncType) {
96
+ const cancelMessage = {
97
+ type: MasterMessageType.cancel,
98
+ uid: jobUID
99
+ };
100
+ worker.postMessage(cancelMessage);
101
+ }
102
+ cleanup();
103
+ };
104
+ });
105
+ }
106
+ function prepareArguments(rawArgs) {
107
+ if (rawArgs.length === 0) {
108
+ // Exit early if possible
109
+ return {
110
+ args: [],
111
+ transferables: []
112
+ };
113
+ }
114
+ const args = [];
115
+ const transferables = [];
116
+ for (const arg of rawArgs) {
117
+ if (isTransferDescriptor(arg)) {
118
+ args.push(serialize(arg.send));
119
+ transferables.push(...arg.transferables);
120
+ }
121
+ else {
122
+ args.push(serialize(arg));
123
+ }
124
+ }
125
+ return {
126
+ args,
127
+ transferables: transferables.length === 0 ? transferables : dedupe(transferables)
128
+ };
129
+ }
130
+ function createProxyFunction(worker, method) {
131
+ return ((...rawArgs) => {
132
+ const uid = nextJobUID++;
133
+ const { args, transferables } = prepareArguments(rawArgs);
134
+ const runMessage = {
135
+ type: MasterMessageType.run,
136
+ uid,
137
+ method,
138
+ args
139
+ };
140
+ debugMessages("Sending command to run function to worker:", runMessage);
141
+ try {
142
+ worker.postMessage(runMessage, transferables);
143
+ }
144
+ catch (error) {
145
+ if (isDataCloneError(error)) {
146
+ const cloneError = new ThreadCloneError(`Cannot send arguments to the worker thread: a value is not structured-cloneable. ` +
147
+ `Functions, class instances and other non-serializable values cannot be passed to a thread. ` +
148
+ `Original error: ${error.message}`, error);
149
+ return ObservablePromise.from(Promise.reject(cloneError));
150
+ }
151
+ return ObservablePromise.from(Promise.reject(error));
152
+ }
153
+ return ObservablePromise.from(multicast(createObservableForJob(worker, uid)));
154
+ });
155
+ }
156
+ function createProxyModule(worker, methodNames) {
157
+ const proxy = {};
158
+ for (const methodName of methodNames) {
159
+ proxy[methodName] = createProxyFunction(worker, methodName);
160
+ }
161
+ return proxy;
162
+ }
163
+
164
+ export { createProxyFunction, createProxyModule };
@@ -0,0 +1,20 @@
1
+ import { createRequire as __createRequire } from "module";
2
+ import { fileURLToPath as __fileURLToPath } from "url";
3
+ import { dirname as __pathDirname } from "path";
4
+ const require = __createRequire(import.meta.url);
5
+ const __filename = __fileURLToPath(import.meta.url);
6
+ const __dirname = __pathDirname(__filename);
7
+ /** Pool event type. Specifies the type of each `PoolEvent`. */
8
+ var PoolEventType;
9
+ (function (PoolEventType) {
10
+ PoolEventType["initialized"] = "initialized";
11
+ PoolEventType["taskCanceled"] = "taskCanceled";
12
+ PoolEventType["taskCompleted"] = "taskCompleted";
13
+ PoolEventType["taskFailed"] = "taskFailed";
14
+ PoolEventType["taskQueued"] = "taskQueued";
15
+ PoolEventType["taskQueueDrained"] = "taskQueueDrained";
16
+ PoolEventType["taskStart"] = "taskStart";
17
+ PoolEventType["terminated"] = "terminated";
18
+ })(PoolEventType || (PoolEventType = {}));
19
+
20
+ export { PoolEventType };