@xylabs/threads 3.0.4

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 (171) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +21 -0
  3. package/README.md +227 -0
  4. package/dist/common.d.ts +4 -0
  5. package/dist/common.js +18 -0
  6. package/dist/index.d.ts +7 -0
  7. package/dist/index.js +27 -0
  8. package/dist/master/get-bundle-url.browser.d.ts +3 -0
  9. package/dist/master/get-bundle-url.browser.js +29 -0
  10. package/dist/master/implementation.browser.d.ts +4 -0
  11. package/dist/master/implementation.browser.js +69 -0
  12. package/dist/master/implementation.d.ts +6 -0
  13. package/dist/master/implementation.js +41 -0
  14. package/dist/master/implementation.node.d.ts +5 -0
  15. package/dist/master/implementation.node.js +255 -0
  16. package/dist/master/index.d.ts +13 -0
  17. package/dist/master/index.js +16 -0
  18. package/dist/master/invocation-proxy.d.ts +3 -0
  19. package/dist/master/invocation-proxy.js +130 -0
  20. package/dist/master/pool-types.d.ts +65 -0
  21. package/dist/master/pool-types.js +15 -0
  22. package/dist/master/pool.d.ts +90 -0
  23. package/dist/master/pool.js +281 -0
  24. package/dist/master/register.d.ts +1 -0
  25. package/dist/master/register.js +12 -0
  26. package/dist/master/spawn.d.ts +20 -0
  27. package/dist/master/spawn.js +130 -0
  28. package/dist/master/thread.d.ts +12 -0
  29. package/dist/master/thread.js +22 -0
  30. package/dist/observable-promise.d.ts +38 -0
  31. package/dist/observable-promise.js +156 -0
  32. package/dist/observable.d.ts +19 -0
  33. package/dist/observable.js +43 -0
  34. package/dist/ponyfills.d.ts +8 -0
  35. package/dist/ponyfills.js +22 -0
  36. package/dist/promise.d.ts +5 -0
  37. package/dist/promise.js +29 -0
  38. package/dist/serializers.d.ts +16 -0
  39. package/dist/serializers.js +41 -0
  40. package/dist/symbols.d.ts +5 -0
  41. package/dist/symbols.js +8 -0
  42. package/dist/transferable.d.ts +42 -0
  43. package/dist/transferable.js +28 -0
  44. package/dist/types/master.d.ts +99 -0
  45. package/dist/types/master.js +14 -0
  46. package/dist/types/messages.d.ts +62 -0
  47. package/dist/types/messages.js +20 -0
  48. package/dist/types/worker.d.ts +11 -0
  49. package/dist/types/worker.js +2 -0
  50. package/dist/worker/bundle-entry.d.ts +1 -0
  51. package/dist/worker/bundle-entry.js +27 -0
  52. package/dist/worker/implementation.browser.d.ts +7 -0
  53. package/dist/worker/implementation.browser.js +28 -0
  54. package/dist/worker/implementation.d.ts +3 -0
  55. package/dist/worker/implementation.js +24 -0
  56. package/dist/worker/implementation.tiny-worker.d.ts +7 -0
  57. package/dist/worker/implementation.tiny-worker.js +38 -0
  58. package/dist/worker/implementation.worker_threads.d.ts +8 -0
  59. package/dist/worker/implementation.worker_threads.js +42 -0
  60. package/dist/worker/index.d.ts +13 -0
  61. package/dist/worker/index.js +195 -0
  62. package/dist/worker_threads.d.ts +8 -0
  63. package/dist/worker_threads.js +17 -0
  64. package/dist-esm/common.js +12 -0
  65. package/dist-esm/index.js +6 -0
  66. package/dist-esm/master/get-bundle-url.browser.js +25 -0
  67. package/dist-esm/master/implementation.browser.js +64 -0
  68. package/dist-esm/master/implementation.js +15 -0
  69. package/dist-esm/master/implementation.node.js +224 -0
  70. package/dist-esm/master/index.js +9 -0
  71. package/dist-esm/master/invocation-proxy.js +122 -0
  72. package/dist-esm/master/pool-types.js +12 -0
  73. package/dist-esm/master/pool.js +273 -0
  74. package/dist-esm/master/register.js +10 -0
  75. package/dist-esm/master/spawn.js +123 -0
  76. package/dist-esm/master/thread.js +19 -0
  77. package/dist-esm/observable-promise.js +152 -0
  78. package/dist-esm/observable.js +38 -0
  79. package/dist-esm/ponyfills.js +18 -0
  80. package/dist-esm/promise.js +25 -0
  81. package/dist-esm/serializers.js +37 -0
  82. package/dist-esm/symbols.js +5 -0
  83. package/dist-esm/transferable.js +23 -0
  84. package/dist-esm/types/master.js +11 -0
  85. package/dist-esm/types/messages.js +17 -0
  86. package/dist-esm/types/worker.js +1 -0
  87. package/dist-esm/worker/bundle-entry.js +11 -0
  88. package/dist-esm/worker/implementation.browser.js +26 -0
  89. package/dist-esm/worker/implementation.js +19 -0
  90. package/dist-esm/worker/implementation.tiny-worker.js +36 -0
  91. package/dist-esm/worker/implementation.worker_threads.js +37 -0
  92. package/dist-esm/worker/index.js +186 -0
  93. package/dist-esm/worker_threads.js +14 -0
  94. package/index.mjs +11 -0
  95. package/observable.d.ts +2 -0
  96. package/observable.js +3 -0
  97. package/observable.mjs +5 -0
  98. package/package.json +141 -0
  99. package/register.d.ts +3 -0
  100. package/register.js +3 -0
  101. package/register.mjs +2 -0
  102. package/rollup.config.js +16 -0
  103. package/src/common.ts +16 -0
  104. package/src/index.ts +8 -0
  105. package/src/master/get-bundle-url.browser.ts +31 -0
  106. package/src/master/implementation.browser.ts +80 -0
  107. package/src/master/implementation.node.ts +284 -0
  108. package/src/master/implementation.ts +21 -0
  109. package/src/master/index.ts +20 -0
  110. package/src/master/invocation-proxy.ts +146 -0
  111. package/src/master/pool-types.ts +83 -0
  112. package/src/master/pool.ts +391 -0
  113. package/src/master/register.ts +10 -0
  114. package/src/master/spawn.ts +172 -0
  115. package/src/master/thread.ts +26 -0
  116. package/src/observable-promise.ts +181 -0
  117. package/src/observable.ts +43 -0
  118. package/src/ponyfills.ts +31 -0
  119. package/src/promise.ts +26 -0
  120. package/src/serializers.ts +67 -0
  121. package/src/symbols.ts +5 -0
  122. package/src/transferable.ts +68 -0
  123. package/src/types/master.ts +130 -0
  124. package/src/types/messages.ts +81 -0
  125. package/src/types/worker.ts +14 -0
  126. package/src/worker/bundle-entry.ts +10 -0
  127. package/src/worker/implementation.browser.ts +40 -0
  128. package/src/worker/implementation.tiny-worker.ts +52 -0
  129. package/src/worker/implementation.ts +23 -0
  130. package/src/worker/implementation.worker_threads.ts +50 -0
  131. package/src/worker/index.ts +228 -0
  132. package/src/worker_threads.ts +28 -0
  133. package/test/lib/serialization.ts +38 -0
  134. package/test/observable-promise.test.ts +189 -0
  135. package/test/observable.test.ts +86 -0
  136. package/test/pool.test.ts +173 -0
  137. package/test/serialization.test.ts +21 -0
  138. package/test/spawn.chromium.mocha.ts +49 -0
  139. package/test/spawn.test.ts +71 -0
  140. package/test/streaming.test.ts +27 -0
  141. package/test/transferables.test.ts +69 -0
  142. package/test/workers/arraybuffer-xor.ts +11 -0
  143. package/test/workers/count-to-five.ts +13 -0
  144. package/test/workers/counter.ts +20 -0
  145. package/test/workers/faulty-function.ts +6 -0
  146. package/test/workers/hello-world.ts +6 -0
  147. package/test/workers/increment.ts +9 -0
  148. package/test/workers/minmax.ts +25 -0
  149. package/test/workers/serialization.ts +12 -0
  150. package/test/workers/top-level-throw.ts +1 -0
  151. package/test-tooling/rollup/app.js +20 -0
  152. package/test-tooling/rollup/rollup.config.ts +15 -0
  153. package/test-tooling/rollup/rollup.test.ts +44 -0
  154. package/test-tooling/rollup/worker.js +7 -0
  155. package/test-tooling/tsconfig/minimal-tsconfig.test.ts +7 -0
  156. package/test-tooling/tsconfig/minimal.ts +10 -0
  157. package/test-tooling/webpack/addition-worker.ts +10 -0
  158. package/test-tooling/webpack/app-with-inlined-worker.ts +29 -0
  159. package/test-tooling/webpack/app.ts +58 -0
  160. package/test-tooling/webpack/pool-worker.ts +6 -0
  161. package/test-tooling/webpack/raw-loader.d.ts +4 -0
  162. package/test-tooling/webpack/webpack.chromium.mocha.ts +21 -0
  163. package/test-tooling/webpack/webpack.node.config.js +38 -0
  164. package/test-tooling/webpack/webpack.test.ts +90 -0
  165. package/test-tooling/webpack/webpack.web.config.js +35 -0
  166. package/types/is-observable.d.ts +7 -0
  167. package/types/tiny-worker.d.ts +4 -0
  168. package/types/webworker.d.ts +9 -0
  169. package/worker.d.ts +2 -0
  170. package/worker.js +3 -0
  171. package/worker.mjs +7 -0
@@ -0,0 +1,42 @@
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 worker_threads_1 = __importDefault(require("../worker_threads"));
7
+ function assertMessagePort(port) {
8
+ if (!port) {
9
+ throw new Error('Invariant violation: MessagePort to parent is not available.');
10
+ }
11
+ return port;
12
+ }
13
+ const isWorkerRuntime = function isWorkerRuntime() {
14
+ return !(0, worker_threads_1.default)().isMainThread;
15
+ };
16
+ const postMessageToMaster = function postMessageToMaster(data, transferList) {
17
+ assertMessagePort((0, worker_threads_1.default)().parentPort).postMessage(data, transferList);
18
+ };
19
+ const subscribeToMasterMessages = function subscribeToMasterMessages(onMessage) {
20
+ const parentPort = (0, worker_threads_1.default)().parentPort;
21
+ if (!parentPort) {
22
+ throw new Error('Invariant violation: MessagePort to parent is not available.');
23
+ }
24
+ const messageHandler = (message) => {
25
+ onMessage(message);
26
+ };
27
+ const unsubscribe = () => {
28
+ assertMessagePort(parentPort).off('message', messageHandler);
29
+ };
30
+ assertMessagePort(parentPort).on('message', messageHandler);
31
+ return unsubscribe;
32
+ };
33
+ function testImplementation() {
34
+ // Will throw if `worker_threads` are not available
35
+ (0, worker_threads_1.default)();
36
+ }
37
+ exports.default = {
38
+ isWorkerRuntime,
39
+ postMessageToMaster,
40
+ subscribeToMasterMessages,
41
+ testImplementation,
42
+ };
@@ -0,0 +1,13 @@
1
+ import { WorkerFunction, WorkerModule } from '../types/worker';
2
+ export { registerSerializer } from '../common';
3
+ export { Transfer } from '../transferable';
4
+ /** Returns `true` if this code is currently running in a worker. */
5
+ export declare const isWorkerRuntime: () => boolean;
6
+ /**
7
+ * Expose a function or a module (an object whose values are functions)
8
+ * to the main thread. Must be called exactly once in every worker thread
9
+ * to signal its API to the main thread.
10
+ *
11
+ * @param exposed Function or object whose values are functions
12
+ */
13
+ export declare function expose(exposed: WorkerFunction | WorkerModule<any>): void;
@@ -0,0 +1,195 @@
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.expose = exports.isWorkerRuntime = exports.Transfer = exports.registerSerializer = void 0;
7
+ /* eslint-disable @typescript-eslint/no-floating-promises */
8
+ /* eslint-disable @typescript-eslint/no-explicit-any */
9
+ const is_observable_1 = __importDefault(require("is-observable"));
10
+ const common_1 = require("../common");
11
+ const transferable_1 = require("../transferable");
12
+ const messages_1 = require("../types/messages");
13
+ const implementation_1 = __importDefault(require("./implementation"));
14
+ var common_2 = require("../common");
15
+ Object.defineProperty(exports, "registerSerializer", { enumerable: true, get: function () { return common_2.registerSerializer; } });
16
+ var transferable_2 = require("../transferable");
17
+ Object.defineProperty(exports, "Transfer", { enumerable: true, get: function () { return transferable_2.Transfer; } });
18
+ /** Returns `true` if this code is currently running in a worker. */
19
+ exports.isWorkerRuntime = implementation_1.default.isWorkerRuntime;
20
+ let exposeCalled = false;
21
+ const activeSubscriptions = new Map();
22
+ const isMasterJobCancelMessage = (thing) => thing && thing.type === messages_1.MasterMessageType.cancel;
23
+ const isMasterJobRunMessage = (thing) => thing && thing.type === messages_1.MasterMessageType.run;
24
+ /**
25
+ * There are issues with `is-observable` not recognizing zen-observable's instances.
26
+ * We are using `observable-fns`, but it's based on zen-observable, too.
27
+ */
28
+ const isObservable = (thing) => (0, is_observable_1.default)(thing) || isZenObservable(thing);
29
+ function isZenObservable(thing) {
30
+ return thing && typeof thing === 'object' && typeof thing.subscribe === 'function';
31
+ }
32
+ function deconstructTransfer(thing) {
33
+ return (0, transferable_1.isTransferDescriptor)(thing) ? { payload: thing.send, transferables: thing.transferables } : { payload: thing, transferables: undefined };
34
+ }
35
+ function postFunctionInitMessage() {
36
+ const initMessage = {
37
+ exposed: {
38
+ type: 'function',
39
+ },
40
+ type: messages_1.WorkerMessageType.init,
41
+ };
42
+ implementation_1.default.postMessageToMaster(initMessage);
43
+ }
44
+ function postModuleInitMessage(methodNames) {
45
+ const initMessage = {
46
+ exposed: {
47
+ methods: methodNames,
48
+ type: 'module',
49
+ },
50
+ type: messages_1.WorkerMessageType.init,
51
+ };
52
+ implementation_1.default.postMessageToMaster(initMessage);
53
+ }
54
+ function postJobErrorMessage(uid, rawError) {
55
+ const { payload: error, transferables } = deconstructTransfer(rawError);
56
+ const errorMessage = {
57
+ error: (0, common_1.serialize)(error),
58
+ type: messages_1.WorkerMessageType.error,
59
+ uid,
60
+ };
61
+ implementation_1.default.postMessageToMaster(errorMessage, transferables);
62
+ }
63
+ function postJobResultMessage(uid, completed, resultValue) {
64
+ const { payload, transferables } = deconstructTransfer(resultValue);
65
+ const resultMessage = {
66
+ complete: completed ? true : undefined,
67
+ payload,
68
+ type: messages_1.WorkerMessageType.result,
69
+ uid,
70
+ };
71
+ implementation_1.default.postMessageToMaster(resultMessage, transferables);
72
+ }
73
+ function postJobStartMessage(uid, resultType) {
74
+ const startMessage = {
75
+ resultType,
76
+ type: messages_1.WorkerMessageType.running,
77
+ uid,
78
+ };
79
+ implementation_1.default.postMessageToMaster(startMessage);
80
+ }
81
+ function postUncaughtErrorMessage(error) {
82
+ try {
83
+ const errorMessage = {
84
+ error: (0, common_1.serialize)(error),
85
+ type: messages_1.WorkerMessageType.uncaughtError,
86
+ };
87
+ implementation_1.default.postMessageToMaster(errorMessage);
88
+ }
89
+ catch (subError) {
90
+ // tslint:disable-next-line no-console
91
+ console.error('Not reporting uncaught error back to master thread as it ' + 'occured while reporting an uncaught error already.' + '\nLatest error:', subError, '\nOriginal error:', error);
92
+ }
93
+ }
94
+ async function runFunction(jobUID, fn, args) {
95
+ let syncResult;
96
+ try {
97
+ syncResult = fn(...args);
98
+ }
99
+ catch (error) {
100
+ return postJobErrorMessage(jobUID, error);
101
+ }
102
+ const resultType = isObservable(syncResult) ? 'observable' : 'promise';
103
+ postJobStartMessage(jobUID, resultType);
104
+ if (isObservable(syncResult)) {
105
+ const subscription = syncResult.subscribe((value) => postJobResultMessage(jobUID, false, (0, common_1.serialize)(value)), (error) => {
106
+ postJobErrorMessage(jobUID, (0, common_1.serialize)(error));
107
+ activeSubscriptions.delete(jobUID);
108
+ }, () => {
109
+ postJobResultMessage(jobUID, true);
110
+ activeSubscriptions.delete(jobUID);
111
+ });
112
+ activeSubscriptions.set(jobUID, subscription);
113
+ }
114
+ else {
115
+ try {
116
+ const result = await syncResult;
117
+ postJobResultMessage(jobUID, true, (0, common_1.serialize)(result));
118
+ }
119
+ catch (error) {
120
+ postJobErrorMessage(jobUID, (0, common_1.serialize)(error));
121
+ }
122
+ }
123
+ }
124
+ /**
125
+ * Expose a function or a module (an object whose values are functions)
126
+ * to the main thread. Must be called exactly once in every worker thread
127
+ * to signal its API to the main thread.
128
+ *
129
+ * @param exposed Function or object whose values are functions
130
+ */
131
+ function expose(exposed) {
132
+ if (!implementation_1.default.isWorkerRuntime()) {
133
+ throw new Error('expose() called in the master thread.');
134
+ }
135
+ if (exposeCalled) {
136
+ throw new Error('expose() called more than once. This is not possible. Pass an object to expose() if you want to expose multiple functions.');
137
+ }
138
+ exposeCalled = true;
139
+ if (typeof exposed === 'function') {
140
+ implementation_1.default.subscribeToMasterMessages((messageData) => {
141
+ if (isMasterJobRunMessage(messageData) && !messageData.method) {
142
+ runFunction(messageData.uid, exposed, messageData.args.map(common_1.deserialize));
143
+ }
144
+ });
145
+ postFunctionInitMessage();
146
+ }
147
+ else if (typeof exposed === 'object' && exposed) {
148
+ implementation_1.default.subscribeToMasterMessages((messageData) => {
149
+ if (isMasterJobRunMessage(messageData) && messageData.method) {
150
+ runFunction(messageData.uid, exposed[messageData.method], messageData.args.map(common_1.deserialize));
151
+ }
152
+ });
153
+ const methodNames = Object.keys(exposed).filter((key) => typeof exposed[key] === 'function');
154
+ postModuleInitMessage(methodNames);
155
+ }
156
+ else {
157
+ throw new Error(`Invalid argument passed to expose(). Expected a function or an object, got: ${exposed}`);
158
+ }
159
+ implementation_1.default.subscribeToMasterMessages((messageData) => {
160
+ if (isMasterJobCancelMessage(messageData)) {
161
+ const jobUID = messageData.uid;
162
+ const subscription = activeSubscriptions.get(jobUID);
163
+ if (subscription) {
164
+ subscription.unsubscribe();
165
+ activeSubscriptions.delete(jobUID);
166
+ }
167
+ }
168
+ });
169
+ }
170
+ exports.expose = expose;
171
+ if (typeof self !== 'undefined' && typeof self.addEventListener === 'function' && implementation_1.default.isWorkerRuntime()) {
172
+ self.addEventListener('error', (event) => {
173
+ // Post with some delay, so the master had some time to subscribe to messages
174
+ setTimeout(() => postUncaughtErrorMessage(event.error || event), 250);
175
+ });
176
+ self.addEventListener('unhandledrejection', (event) => {
177
+ const error = event.reason;
178
+ if (error && typeof error.message === 'string') {
179
+ // Post with some delay, so the master had some time to subscribe to messages
180
+ setTimeout(() => postUncaughtErrorMessage(error), 250);
181
+ }
182
+ });
183
+ }
184
+ if (typeof process !== 'undefined' && typeof process.on === 'function' && implementation_1.default.isWorkerRuntime()) {
185
+ process.on('uncaughtException', (error) => {
186
+ // Post with some delay, so the master had some time to subscribe to messages
187
+ setTimeout(() => postUncaughtErrorMessage(error), 250);
188
+ });
189
+ process.on('unhandledRejection', (error) => {
190
+ if (error && typeof error.message === 'string') {
191
+ // Post with some delay, so the master had some time to subscribe to messages
192
+ setTimeout(() => postUncaughtErrorMessage(error), 250);
193
+ }
194
+ });
195
+ }
@@ -0,0 +1,8 @@
1
+ type MessagePort = any;
2
+ interface WorkerThreadsModule {
3
+ MessagePort: typeof MessagePort;
4
+ isMainThread: boolean;
5
+ parentPort: MessagePort;
6
+ }
7
+ export default function getImplementation(): WorkerThreadsModule;
8
+ export {};
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ /* eslint-disable import/no-default-export */
3
+ /* eslint-disable @typescript-eslint/no-explicit-any */
4
+ // Webpack hack
5
+ // tslint:disable no-eval
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ let implementation;
8
+ function selectImplementation() {
9
+ return typeof __non_webpack_require__ === 'function' ? __non_webpack_require__('worker_threads') : eval('require')('worker_threads');
10
+ }
11
+ function getImplementation() {
12
+ if (!implementation) {
13
+ implementation = selectImplementation();
14
+ }
15
+ return implementation;
16
+ }
17
+ exports.default = getImplementation;
@@ -0,0 +1,12 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { DefaultSerializer, extendSerializer } from './serializers';
3
+ let registeredSerializer = DefaultSerializer;
4
+ export function registerSerializer(serializer) {
5
+ registeredSerializer = extendSerializer(registeredSerializer, serializer);
6
+ }
7
+ export function deserialize(message) {
8
+ return registeredSerializer.deserialize(message);
9
+ }
10
+ export function serialize(input) {
11
+ return registeredSerializer.serialize(input);
12
+ }
@@ -0,0 +1,6 @@
1
+ /* eslint-disable import/no-internal-modules */
2
+ export { registerSerializer } from './common';
3
+ export * from './master/index';
4
+ export { DefaultSerializer } from './serializers';
5
+ export { Transfer } from './transferable';
6
+ export { expose } from './worker/index';
@@ -0,0 +1,25 @@
1
+ // Source: <https://github.com/parcel-bundler/parcel/blob/master/packages/core/parcel-bundler/src/builtins/bundle-url.js>
2
+ let bundleURL;
3
+ function getBundleURLCached() {
4
+ if (!bundleURL) {
5
+ bundleURL = getBundleURL();
6
+ }
7
+ return bundleURL;
8
+ }
9
+ function getBundleURL() {
10
+ // Attempt to find the URL of the current script and use that as the base URL
11
+ try {
12
+ throw new Error('getBundleURL failed');
13
+ }
14
+ catch (err) {
15
+ const matches = ('' + err.stack).match(/(https?|file|ftp|chrome-extension|moz-extension):\/\/[^\n)]+/g);
16
+ if (matches) {
17
+ return getBaseURL(matches[0]);
18
+ }
19
+ }
20
+ return '/';
21
+ }
22
+ function getBaseURL(url) {
23
+ return ('' + url).replace(/^((?:https?|file|ftp|chrome-extension|moz-extension):\/\/.+)?\/[^/]+(?:\?.*)?$/, '$1') + '/';
24
+ }
25
+ export { getBaseURL, getBundleURLCached as getBundleURL };
@@ -0,0 +1,64 @@
1
+ // tslint:disable max-classes-per-file
2
+ import { getBundleURL } from './get-bundle-url.browser';
3
+ export const defaultPoolSize = typeof navigator !== 'undefined' && navigator.hardwareConcurrency ? navigator.hardwareConcurrency : 4;
4
+ const isAbsoluteURL = (value) => /^[A-Za-z][\d+.A-Za-z\-]*:/.test(value);
5
+ function createSourceBlobURL(code) {
6
+ const blob = new Blob([code], { type: 'application/javascript' });
7
+ return URL.createObjectURL(blob);
8
+ }
9
+ function selectWorkerImplementation() {
10
+ if (typeof Worker === 'undefined') {
11
+ // Might happen on Safari, for instance
12
+ // The idea is to only fail if the constructor is actually used
13
+ return class NoWebWorker {
14
+ constructor() {
15
+ throw new 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.");
16
+ }
17
+ };
18
+ }
19
+ class WebWorker extends Worker {
20
+ constructor(url, options) {
21
+ if (typeof url === 'string' && options && options._baseURL) {
22
+ url = new URL(url, options._baseURL);
23
+ }
24
+ else if (typeof url === 'string' && !isAbsoluteURL(url) && /^file:\/\//i.test(getBundleURL())) {
25
+ url = new URL(url, getBundleURL().replace(/\/[^/]+$/, '/'));
26
+ if (options?.CORSWorkaround ?? true) {
27
+ url = createSourceBlobURL(`importScripts(${JSON.stringify(url)});`);
28
+ }
29
+ }
30
+ if (typeof url === 'string' &&
31
+ isAbsoluteURL(url) && // Create source code blob loading JS file via `importScripts()`
32
+ // to circumvent worker CORS restrictions
33
+ (options?.CORSWorkaround ?? true)) {
34
+ url = createSourceBlobURL(`importScripts(${JSON.stringify(url)});`);
35
+ }
36
+ super(url, options);
37
+ }
38
+ }
39
+ class BlobWorker extends WebWorker {
40
+ constructor(blob, options) {
41
+ const url = window.URL.createObjectURL(blob);
42
+ super(url, options);
43
+ }
44
+ static fromText(source, options) {
45
+ const blob = new window.Blob([source], { type: 'text/javascript' });
46
+ return new BlobWorker(blob, options);
47
+ }
48
+ }
49
+ return {
50
+ blob: BlobWorker,
51
+ default: WebWorker,
52
+ };
53
+ }
54
+ let implementation;
55
+ export function getWorkerImplementation() {
56
+ if (!implementation) {
57
+ implementation = selectWorkerImplementation();
58
+ }
59
+ return implementation;
60
+ }
61
+ export function isWorkerRuntime() {
62
+ const isWindowContext = typeof self !== 'undefined' && typeof Window !== 'undefined' && self instanceof Window;
63
+ return typeof self !== 'undefined' && self['postMessage'] && !isWindowContext ? true : false;
64
+ }
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This file is only a stub to make './implementation' resolve to the right module.
3
+ */
4
+ // We alias `src/master/implementation` to `src/master/implementation.browser` for web
5
+ // browsers already in the package.json, so if get here, it's safe to pass-through the
6
+ // node implementation
7
+ import * as BrowserImplementation from './implementation.browser';
8
+ import * as NodeImplementation from './implementation.node';
9
+ const runningInNode = typeof process !== 'undefined' && process.arch !== 'browser' && 'pid' in process;
10
+ const implementation = runningInNode ? NodeImplementation : BrowserImplementation;
11
+ /** Default size of pools. Depending on the platform the value might vary from device to device. */
12
+ export const defaultPoolSize = implementation.defaultPoolSize;
13
+ export const getWorkerImplementation = implementation.getWorkerImplementation;
14
+ /** Returns `true` if this code is currently running in a worker. */
15
+ export const isWorkerRuntime = implementation.isWorkerRuntime;
@@ -0,0 +1,224 @@
1
+ /* eslint-disable unicorn/prefer-logical-operator-over-ternary */
2
+ /* eslint-disable unicorn/prefer-regexp-test */
3
+ /* eslint-disable @typescript-eslint/no-var-requires */
4
+ /* eslint-disable unicorn/prefer-add-event-listener */
5
+ /* eslint-disable unicorn/prefer-event-target */
6
+ /* eslint-disable @typescript-eslint/no-explicit-any */
7
+ /* eslint-disable unicorn/text-encoding-identifier-case */
8
+ /* eslint-disable unicorn/no-process-exit */
9
+ /// <reference lib="dom" />
10
+ import { EventEmitter } from 'node:events';
11
+ import { cpus } from 'node:os';
12
+ import * as path from 'node:path';
13
+ import { fileURLToPath } from 'node:url';
14
+ import getCallsites from 'callsites';
15
+ let tsNodeAvailable;
16
+ export const defaultPoolSize = cpus().length;
17
+ function detectTsNode() {
18
+ if (typeof __non_webpack_require__ === 'function') {
19
+ // Webpack build: => No ts-node required or possible
20
+ return false;
21
+ }
22
+ if (tsNodeAvailable) {
23
+ return tsNodeAvailable;
24
+ }
25
+ try {
26
+ eval('require').resolve('ts-node');
27
+ tsNodeAvailable = true;
28
+ }
29
+ catch (error) {
30
+ if (error && error.code === 'MODULE_NOT_FOUND') {
31
+ tsNodeAvailable = false;
32
+ }
33
+ else {
34
+ // Re-throw
35
+ throw error;
36
+ }
37
+ }
38
+ return tsNodeAvailable;
39
+ }
40
+ function createTsNodeModule(scriptPath) {
41
+ const content = `
42
+ require("ts-node/register/transpile-only");
43
+ require(${JSON.stringify(scriptPath)});
44
+ `;
45
+ return content;
46
+ }
47
+ function rebaseScriptPath(scriptPath, ignoreRegex) {
48
+ const parentCallSite = getCallsites().find((callsite) => {
49
+ const filename = callsite.getFileName();
50
+ return Boolean(filename && !filename.match(ignoreRegex) && !/[/\\]master[/\\]implementation/.test(filename) && !/^internal\/process/.test(filename));
51
+ });
52
+ const rawCallerPath = parentCallSite ? parentCallSite.getFileName() : null;
53
+ let callerPath = rawCallerPath ? rawCallerPath : null;
54
+ if (callerPath && callerPath.startsWith('file:')) {
55
+ callerPath = fileURLToPath(callerPath);
56
+ }
57
+ const rebasedScriptPath = callerPath ? path.join(path.dirname(callerPath), scriptPath) : scriptPath;
58
+ return rebasedScriptPath;
59
+ }
60
+ function resolveScriptPath(scriptPath, baseURL) {
61
+ const makeRelative = (filePath) => {
62
+ // eval() hack is also webpack-related
63
+ return path.isAbsolute(filePath) ? filePath : path.join(baseURL || eval('__dirname'), filePath);
64
+ };
65
+ const workerFilePath = typeof __non_webpack_require__ === 'function' ?
66
+ __non_webpack_require__.resolve(makeRelative(scriptPath))
67
+ : eval('require').resolve(makeRelative(rebaseScriptPath(scriptPath, /[/\\]worker_threads[/\\]/)));
68
+ return workerFilePath;
69
+ }
70
+ function initWorkerThreadsWorker() {
71
+ // Webpack hack
72
+ const NativeWorker = typeof __non_webpack_require__ === 'function' ? __non_webpack_require__('worker_threads').Worker : eval('require')('worker_threads').Worker;
73
+ let allWorkers = [];
74
+ class Worker extends NativeWorker {
75
+ mappedEventListeners;
76
+ constructor(scriptPath, options) {
77
+ const resolvedScriptPath = options && options.fromSource ? null : resolveScriptPath(scriptPath, (options || {})._baseURL);
78
+ if (!resolvedScriptPath) {
79
+ // `options.fromSource` is true
80
+ const sourceCode = scriptPath;
81
+ super(sourceCode, { ...options, eval: true });
82
+ }
83
+ else if (/\.tsx?$/i.test(resolvedScriptPath) && detectTsNode()) {
84
+ super(createTsNodeModule(resolvedScriptPath), { ...options, eval: true });
85
+ }
86
+ else if (/\.asar[/\\]/.test(resolvedScriptPath)) {
87
+ // See <https://github.com/andywer/threads-plugin/issues/17>
88
+ super(resolvedScriptPath.replace(/\.asar([/\\])/, '.asar.unpacked$1'), options);
89
+ }
90
+ else {
91
+ super(resolvedScriptPath, options);
92
+ }
93
+ this.mappedEventListeners = new WeakMap();
94
+ allWorkers.push(this);
95
+ }
96
+ addEventListener(eventName, rawListener) {
97
+ const listener = (message) => {
98
+ rawListener({ data: message });
99
+ };
100
+ this.mappedEventListeners.set(rawListener, listener);
101
+ this.on(eventName, listener);
102
+ }
103
+ removeEventListener(eventName, rawListener) {
104
+ const listener = this.mappedEventListeners.get(rawListener) || rawListener;
105
+ this.off(eventName, listener);
106
+ }
107
+ }
108
+ const terminateWorkersAndMaster = () => {
109
+ // we should terminate all workers and then gracefully shutdown self process
110
+ Promise.all(allWorkers.map((worker) => worker.terminate())).then(() => process.exit(0), () => process.exit(1));
111
+ allWorkers = [];
112
+ };
113
+ // Take care to not leave orphaned processes behind. See #147.
114
+ process.on('SIGINT', () => terminateWorkersAndMaster());
115
+ process.on('SIGTERM', () => terminateWorkersAndMaster());
116
+ class BlobWorker extends Worker {
117
+ constructor(blob, options) {
118
+ super(Buffer.from(blob).toString('utf-8'), { ...options, fromSource: true });
119
+ }
120
+ static fromText(source, options) {
121
+ return new Worker(source, { ...options, fromSource: true });
122
+ }
123
+ }
124
+ return {
125
+ blob: BlobWorker,
126
+ default: Worker,
127
+ };
128
+ }
129
+ function initTinyWorker() {
130
+ const TinyWorker = require('tiny-worker');
131
+ let allWorkers = [];
132
+ class Worker extends TinyWorker {
133
+ emitter;
134
+ constructor(scriptPath, options) {
135
+ // Need to apply a work-around for Windows or it will choke upon the absolute path
136
+ // (`Error [ERR_INVALID_PROTOCOL]: Protocol 'c:' not supported`)
137
+ const resolvedScriptPath = options && options.fromSource ? null
138
+ : process.platform === 'win32' ? `file:///${resolveScriptPath(scriptPath).replaceAll('\\', '/')}`
139
+ : resolveScriptPath(scriptPath);
140
+ if (!resolvedScriptPath) {
141
+ // `options.fromSource` is true
142
+ const sourceCode = scriptPath;
143
+ super(new Function(sourceCode), [], { esm: true });
144
+ }
145
+ else if (/\.tsx?$/i.test(resolvedScriptPath) && detectTsNode()) {
146
+ super(new Function(createTsNodeModule(resolveScriptPath(scriptPath))), [], { esm: true });
147
+ }
148
+ else if (/\.asar[/\\]/.test(resolvedScriptPath)) {
149
+ // See <https://github.com/andywer/threads-plugin/issues/17>
150
+ super(resolvedScriptPath.replace(/\.asar([/\\])/, '.asar.unpacked$1'), [], { esm: true });
151
+ }
152
+ else {
153
+ super(resolvedScriptPath, [], { esm: true });
154
+ }
155
+ allWorkers.push(this);
156
+ this.emitter = new EventEmitter();
157
+ this.onerror = (error) => this.emitter.emit('error', error);
158
+ this.onmessage = (message) => this.emitter.emit('message', message);
159
+ }
160
+ addEventListener(eventName, listener) {
161
+ this.emitter.addListener(eventName, listener);
162
+ }
163
+ removeEventListener(eventName, listener) {
164
+ this.emitter.removeListener(eventName, listener);
165
+ }
166
+ terminate() {
167
+ allWorkers = allWorkers.filter((worker) => worker !== this);
168
+ return super.terminate();
169
+ }
170
+ }
171
+ const terminateWorkersAndMaster = () => {
172
+ // we should terminate all workers and then gracefully shutdown self process
173
+ Promise.all(allWorkers.map((worker) => worker.terminate())).then(() => process.exit(0), () => process.exit(1));
174
+ allWorkers = [];
175
+ };
176
+ // Take care to not leave orphaned processes behind
177
+ // See <https://github.com/avoidwork/tiny-worker#faq>
178
+ process.on('SIGINT', () => terminateWorkersAndMaster());
179
+ process.on('SIGTERM', () => terminateWorkersAndMaster());
180
+ class BlobWorker extends Worker {
181
+ constructor(blob, options) {
182
+ super(Buffer.from(blob).toString('utf-8'), { ...options, fromSource: true });
183
+ }
184
+ static fromText(source, options) {
185
+ return new Worker(source, { ...options, fromSource: true });
186
+ }
187
+ }
188
+ return {
189
+ blob: BlobWorker,
190
+ default: Worker,
191
+ };
192
+ }
193
+ let implementation;
194
+ let isTinyWorker;
195
+ function selectWorkerImplementation() {
196
+ try {
197
+ isTinyWorker = false;
198
+ return initWorkerThreadsWorker();
199
+ }
200
+ catch {
201
+ // tslint:disable-next-line no-console
202
+ console.debug('Node worker_threads not available. Trying to fall back to tiny-worker polyfill...');
203
+ isTinyWorker = true;
204
+ return initTinyWorker();
205
+ }
206
+ }
207
+ export function getWorkerImplementation() {
208
+ if (!implementation) {
209
+ implementation = selectWorkerImplementation();
210
+ }
211
+ return implementation;
212
+ }
213
+ export function isWorkerRuntime() {
214
+ if (isTinyWorker) {
215
+ return self !== undefined && self['postMessage'] ? true : false;
216
+ }
217
+ else {
218
+ // Webpack hack
219
+ const isMainThread = typeof __non_webpack_require__ === 'function' ?
220
+ __non_webpack_require__('worker_threads').isMainThread
221
+ : eval('require')('worker_threads').isMainThread;
222
+ return !isMainThread;
223
+ }
224
+ }
@@ -0,0 +1,9 @@
1
+ import { getWorkerImplementation } from './implementation';
2
+ export { Pool } from './pool';
3
+ export { spawn } from './spawn';
4
+ export { Thread } from './thread';
5
+ /** Separate class to spawn workers from source code blobs or strings. */
6
+ export const BlobWorker = getWorkerImplementation().blob;
7
+ /** Worker implementation. Either web worker or a node.js Worker class. */
8
+ export const Worker = getWorkerImplementation().default;
9
+ export { isWorkerRuntime } from './implementation';