This commit is contained in:
lalBi94
2023-03-05 13:23:23 +01:00
commit 7bc56c09b5
14034 changed files with 1834369 additions and 0 deletions

241
node_modules/workerpool/HISTORY.md generated vendored Normal file
View File

@@ -0,0 +1,241 @@
# workerpool history
https://github.com/josdejong/workerpool
## 2021-01-31, version 6.1.0
- Implemented support for sending events from the worker to the main thread,
see #51, #227. Thanks @Akryum.
- Fix an issue in Node.js nightly, see #230. Thanks @aduh95.
- Fix #232 `workerpool` not working on IE 10.
## 2021-01-16, version 6.0.4
- Make evaluation of offloaded functions a bit more secure by using
`new Function` instead of `eval`. Thanks @tjenkinson.
## 2020-10-28, version 6.0.3
- Fixes and more robustness in terminating workers. Thanks @boneskull.
## 2020-10-03, version 6.0.2
- Fix #32, #175: the promise returned by `Pool.terminate()` now waits until
subprocesses are dead before resolving. Thanks @boneskull.
## 2020-09-23, version 6.0.1
- Removed examples from the npm package. Thanks @madbence.
## 2020-05-13, version 6.0.0
WARNING: the library entry points are changed and new source maps are added.
This may have impact on your project depending on your setup.
- Created separate library entry points in package.json for node.js and browser.
Thanks @boneskull.
- Generated source maps for both minified and non-minified bundles.
- Removed deprecation warnings for `options.nodeWorker` (renamed to
`options.workerType`) and `pool.clear()` (renamed to `pool.terminate()`).
## 2019-12-31, version 5.0.4
- Fixed #121: `isMainThread` not working when using `worker_threads`.
- Drop official support for node.js 8 (end of life).
## 2019-12-23, version 5.0.3
- Fixed library not working in the browser. See #106.
## 2019-11-06, version 5.0.2
- Fixed environment detection in browser. See #106. Thanks @acgrid.
## 2019-10-13, version 5.0.1
- Fixed #96: WorkerPool not cancelling any pending tasks on termination.
## 2019-08-25, version 5.0.0
- Deprecated option `nodeWorker` and created a new, more extensive option
`workerType` giving full control over the selected type of worker.
Added new option `'web'` to enforce use a Web Worker. See #85, #74.
- In a node.js environment, the default `workerType` is changed from
`'process'` to `'thread'`. See #85, #50.
- Improved detection of environment (`browser` or `node`), fixing wrong
detection in a Jest test environment. See #85.
## 2019-08-21, version 4.0.0
- Pass argument `--max-old-space-size` to child processes. Thanks @patte.
- Removed redundant dependencies, upgraded all devDependencies.
- Fixed Webpack issues of missing modules `child_process` and `worker_threads`.
See #43.
- Bundled library changed due to the upgrade to Webpack 4. This could possibly
lead to breaking changes.
- Implemented new option `maxQueueSize`. Thanks @colomboe.
- Fixed exiting workers when the parent process is killed. Thanks @RogerKang.
- Fixed #81: Option `minWorkers: 'max'` not using the configured `maxWorkers`.
- Fixed not passing `nodeWorker` to workers initialized when creating a pool.
Thanks @spacelan.
- Internal restructure of the code: moved from `lib` to `src`.
## 2019-03-12, version 3.1.2
- Improved error message when a node.js worker unexpectedly exits (see #58).
Thanks @stefanpenner.
- Allocate debug ports safely, this fixes an issue cause workers to exit
unexpectedly if more then one worker pool is active, and the process is
started with a debugger (`node debug` or `node --inspect`).
Thanks @stefanpenner.
## 2019-02-25, version 3.1.1
- Fix option `nodeWorker: 'auto'` not using worker threads when available.
Thanks @stefanpenner.
## 2019-02-17, version 3.1.0
- Implemented support for using `worker_threads` in Node.js, via the new option
`nodeWorker: 'thread'`. Thanks @stefanpenner.
## 2018-12-11, version 3.0.0
- Enable usage in ES6 Webpack projects.
- Dropped support for AMD module system.
## 2018-09-12, version 2.3.3
- Fixed space in license field in `package.json`. Thanks @sagotsky.
## 2018-09-08, version 2.3.2
- Add licence field to `package.json`. Thanks @greyd.
## 2018-07-24, version 2.3.1
- Fixed bug where tasks that are cancelled in a Pool's queue
causes following tasks to not run. Thanks @greemo.
## 2017-09-30, version 2.3.0
- New method `Pool.terminate(force, timeout)` which will replace
`Pool.clear(force)`. Thanks @jimsugg.
- Fixed issue with never terminating zombie child processes.
Thanks @jimsugg.
## 2017-08-20, version 2.2.4
- Fixed a debug issue: look for `--inspect` within argument strings,
instead of exact match. Thanks @jimsugg.
## 2017-08-19, version 2.2.3
- Updated all examples to neatly include `.catch(...)` callbacks.
## 2017-07-08, version 2.2.2
- Fixed #25: timer of a timeout starting when the task is created
instead of when the task is started. Thanks @eclipsesk for input.
## 2017-05-07, version 2.2.1
- Fixed #2 and #19: support for debugging child processes. Thanks @tptee.
## 2016-11-26, version 2.2.0
- Implemented #18: method `pool.stats()`.
## 2016-10-11, version 2.1.0
- Implemented support for registering the workers methods asynchronously.
This enables asynchronous initialization of workers, for example when
using AMD modules. Thanks @natlibfi-arlehiko.
- Implemented environment variables `platform`, `isMainThread`, and `cpus`.
Thanks @natlibfi-arlehiko.
- Implemented option `minWorkers`. Thanks @sergei202.
## 2016-09-18, version 2.0.0
- Replaced conversion of Error-objecting using serializerr to custom
implementation to prevent issues with serializing/deserializing functions.
This conversion implementation loses the prototype object which means that
e.g. 'TypeError' will become just 'Error' in the main code. See #8.
Thanks @natlibfi-arlehiko.
## 2016-09-12, version 1.3.1
- Fix for a bug in PhantomJS (see #7). Thanks @natlibfi-arlehiko.
## 2016-08-21, version 1.3.0
- Determine `maxWorkers` as the number of CPU's minus one in browsers too. See #6.
## 2016-06-25, version 1.2.1
- Fixed #5 error when loading via AMD or bundling using Webpack.
## 2016-05-22, version 1.2.0
- Implemented serializing errors with stacktrace. Thanks @mujx.
## 2016-01-25, version 1.1.0
- Added an error message when wrongly calling `pool.proxy`.
- Fixed function `worker.pool` not accepting both a script and options. See #1.
Thanks @freund17.
## 2014-05-29, version 1.0.0
- Merged function `Pool.run` into `Pool.exec`, simplifying the API.
## 2014-05-14, version 0.2.0
- Implemented support for cancelling running tasks.
- Implemented support for cancelling running tasks after a timeout.
## 2014-05-07, version 0.1.0
- Implemented support for both node.js and the browser.
- Implemented offloading functions.
- Implemented worker proxy.
- Added docs and examples.
## 2014-05-02, version 0.0.1
- Module name registered at npm.

201
node_modules/workerpool/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

486
node_modules/workerpool/README.md generated vendored Normal file
View File

@@ -0,0 +1,486 @@
# workerpool
**workerpool** offers an easy way to create a pool of workers for both dynamically offloading computations as well as managing a pool of dedicated workers. **workerpool** basically implements a [thread pool pattern](http://en.wikipedia.org/wiki/Thread_pool_pattern). There is a pool of workers to execute tasks. New tasks are put in a queue. A worker executes one task at a time, and once finished, picks a new task from the queue. Workers can be accessed via a natural, promise based proxy, as if they are available straight in the main application.
**workerpool** runs on node.js, Chrome, Firefox, Opera, Safari, and IE10+.
## Features
- Easy to use
- Runs in the browser and on node.js
- Dynamically offload functions to a worker
- Access workers via a proxy
- Cancel running tasks
- Set a timeout on tasks
- Handles crashed workers
- Small: 5 kB minified and gzipped
## Why
JavaScript is based upon a single event loop which handles one event at a time. Jeremy Epstein [explains this clearly](http://greenash.net.au/thoughts/2012/11/nodejs-itself-is-blocking-only-its-io-is-non-blocking/):
> In Node.js everything runs in parallel, except your code.
> What this means is that all I/O code that you write in Node.js is non-blocking,
> while (conversely) all non-I/O code that you write in Node.js is blocking.
This means that CPU heavy tasks will block other tasks from being executed. In case of a browser environment, the browser will not react to user events like a mouse click while executing a CPU intensive task (the browser "hangs"). In case of a node.js server, the server will not respond to any new request while executing a single, heavy request.
For front-end processes, this is not a desired situation.
Therefore, CPU intensive tasks should be offloaded from the main event loop onto dedicated *workers*. In a browser environment, [Web Workers](http://www.html5rocks.com/en/tutorials/workers/basics/) can be used. In node.js, [child processes](https://nodejs.org/api/child_process.html) and [worker_threads](https://nodejs.org/api/worker_threads.html) are available. An application should be split in separate, decoupled parts, which can run independent of each other in a parallelized way. Effectively, this results in an architecture which achieves concurrency by means of isolated processes and message passing.
## Install
Install via npm:
npm install workerpool
## Load
To load workerpool in a node.js application (both main application as well as workers):
```js
const workerpool = require('workerpool');
```
To load workerpool in the browser:
```html
<script src="workerpool.js"></script>
```
To load workerpool in a web worker in the browser:
```js
importScripts('workerpool.js');
```
## Use
### Offload functions dynamically
In the following example there is a function `add`, which is offloaded dynamically to a worker to be executed for a given set of arguments.
**myApp.js**
```js
const workerpool = require('workerpool');
const pool = workerpool.pool();
function add(a, b) {
return a + b;
}
pool.exec(add, [3, 4])
.then(function (result) {
console.log('result', result); // outputs 7
})
.catch(function (err) {
console.error(err);
})
.then(function () {
pool.terminate(); // terminate all workers when done
});
```
Note that both function and arguments must be static and stringifiable, as they need to be sent to the worker in a serialized form. In case of large functions or function arguments, the overhead of sending the data to the worker can be significant.
### Dedicated workers
A dedicated worker can be created in a separate script, and then used via a worker pool.
**myWorker.js**
```js
const workerpool = require('workerpool');
// a deliberately inefficient implementation of the fibonacci sequence
function fibonacci(n) {
if (n < 2) return n;
return fibonacci(n - 2) + fibonacci(n - 1);
}
// create a worker and register public functions
workerpool.worker({
fibonacci: fibonacci
});
```
This worker can be used by a worker pool:
**myApp.js**
```js
const workerpool = require('workerpool');
// create a worker pool using an external worker script
const pool = workerpool.pool(__dirname + '/myWorker.js');
// run registered functions on the worker via exec
pool.exec('fibonacci', [10])
.then(function (result) {
console.log('Result: ' + result); // outputs 55
})
.catch(function (err) {
console.error(err);
})
.then(function () {
pool.terminate(); // terminate all workers when done
});
// or run registered functions on the worker via a proxy:
pool.proxy()
.then(function (worker) {
return worker.fibonacci(10);
})
.then(function (result) {
console.log('Result: ' + result); // outputs 55
})
.catch(function (err) {
console.error(err);
})
.then(function () {
pool.terminate(); // terminate all workers when done
});
```
Worker can also initialize asynchronously:
**myAsyncWorker.js**
```js
define(['workerpool/dist/workerpool'], function(workerpool) {
// a deliberately inefficient implementation of the fibonacci sequence
function fibonacci(n) {
if (n < 2) return n;
return fibonacci(n - 2) + fibonacci(n - 1);
}
// create a worker and register public functions
workerpool.worker({
fibonacci: fibonacci
});
});
```
## Examples
Examples are available in the examples directory:
[https://github.com/josdejong/workerpool/tree/master/examples](https://github.com/josdejong/workerpool/tree/master/examples)
## API
The API of workerpool consists of two parts: a function `workerpool.pool` to create a worker pool, and a function `workerpool.worker` to create a worker.
### pool
A workerpool can be created using the function `workerpool.pool`:
`workerpool.pool([script: string] [, options: Object]) : Pool`
When a `script` argument is provided, the provided script will be started as a dedicated worker. When no `script` argument is provided, a default worker is started which can be used to offload functions dynamically via `Pool.exec`. Note that on node.js, `script` must be an absolute file path like `__dirname + '/myWorker.js'`. In a browser environment, `script` can also be a data URL like `'data:application/javascript;base64,...'`. This allows embedding the bundled code of a worker in your main application. See `examples/embeddedWorker` for a demo.
The following options are available:
- `minWorkers: number | 'max'`. The minimum number of workers that must be initialized and kept available. Setting this to `'max'` will create `maxWorkers` default workers (see below).
- `maxWorkers: number`. The default number of maxWorkers is the number of CPU's minus one. When the number of CPU's could not be determined (for example in older browsers), `maxWorkers` is set to 3.
- `maxQueueSize: number`. The maximum number of tasks allowed to be queued. Can be used to prevent running out of memory. If the maximum is exceeded, adding a new task will throw an error. The default value is `Infinity`.
- `workerType: 'auto' | 'web' | 'process' | 'thread'`.
- In case of `'auto'` (default), workerpool will automatically pick a suitable type of worker: when in a browser environment, `'web'` will be used. When in a node.js environment, `worker_threads` will be used if available (Node.js >= 11.7.0), else `child_process` will be used.
- In case of `'web'`, a Web Worker will be used. Only available in a browser environment.
- In case of `'process'`, `child_process` will be used. Only available in a node.js environment.
- In case of `'thread'`, `worker_threads` will be used. If `worker_threads` are not available, an error is thrown. Only available in a node.js environment.
> Important note on `'workerType'`: when sending and receiving primitive data types (plain JSON) from and to a worker, the different worker types (`'web'`, `'process'`, `'thread'`) can be used interchangeably. However, when using more advanced data types like buffers, the API and returned results can vary. In these cases, it is best not to use the `'auto'` setting but have a fixed `'workerType'` and good unit testing in place.
A worker pool contains the following functions:
- `Pool.exec(method: Function | string, params: Array | null [, options: Object]) : Promise.<*, Error>`<br>
Execute a function on a worker with given arguments.
- When `method` is a string, a method with this name must exist at the worker and must be registered to make it accessible via the pool. The function will be executed on the worker with given parameters.
- When `method` is a function, the provided function `fn` will be stringified, send to the worker, and executed there with the provided parameters. The provided function must be static, it must not depend on variables in a surrounding scope.
- The following options are available:
- `on: (payload: any) => void`. An event listener, to handle events sent by the worker for this execution. See [Events](#events) for more details.
- `Pool.proxy() : Promise.<Object, Error>`<br>
Create a proxy for the worker pool. The proxy contains a proxy for all methods available on the worker. All methods return promises resolving the methods result.
- `Pool.stats() : Object`<br>
Retrieve statistics on workers, and active and pending tasks.
Returns an object containing the following properties:
```
{
totalWorkers: 0,
busyWorkers: 0,
idleWorkers: 0,
pendingTasks: 0,
activeTasks: 0
}
```
- `Pool.terminate([force: boolean [, timeout: number]])`
If parameter `force` is false (default), workers will finish the tasks they are working on before terminating themselves. Any pending tasks will be rejected with an error 'Pool terminated'. When `force` is true, all workers are terminated immediately without finishing running tasks. If `timeout` is provided, worker will be forced to terminate when the timeout expires and the worker has not finished.
The function `Pool.exec` and the proxy functions all return a `Promise`. The promise has the following functions available:
- `Promise.then(fn: Function.<result: *>)`<br>
Get the result of the promise once resolve.
- `Promise.catch(fn: Function.<error: Error>)`<br>
Get the error of the promise when rejected.
- `Promise.cancel()`<br>
A running task can be cancelled. The worker executing the task is enforced to terminate immediately.
The promise will be rejected with a `Promise.CancellationError`.
- `Promise.timeout(delay: number)`<br>
Cancel a running task when it is not resolved or rejected within given delay in milliseconds. The timer will start when the task is actually started, not when the task is created and queued.
The worker executing the task is enforced to terminate immediately.
The promise will be rejected with a `Promise.TimeoutError`.
Example usage:
```js
const workerpool = require('workerpool');
function add(a, b) {
return a + b;
}
const pool1 = workerpool.pool();
// offload a function to a worker
pool1.exec(add, [2, 4])
.then(function (result) {
console.log(result); // will output 6
})
.catch(function (err) {
console.error(err);
});
// create a dedicated worker
const pool2 = workerpool.pool(__dirname + '/myWorker.js');
// supposed myWorker.js contains a function 'fibonacci'
pool2.exec('fibonacci', [10])
.then(function (result) {
console.log(result); // will output 55
})
.catch(function (err) {
console.error(err);
});
// create a proxy to myWorker.js
pool2.proxy()
.then(function (myWorker) {
return myWorker.fibonacci(10)
})
.then(function (result) {
console.log(result); // will output 55
})
.catch(function (err) {
console.error(err);
});
// create a pool with a specified maximum number of workers
const pool3 = workerpool.pool({maxWorkers: 7});
```
### worker
A worker is constructed as:
`workerpool.worker([methods: Object.<String, Function>])`
Argument `methods` is optional can can be an object with functions available in the worker. Registered functions will be available via the worker pool.
Example usage:
```js
// file myWorker.js
const workerpool = require('workerpool');
function add(a, b) {
return a + b;
}
function multiply(a, b) {
return a * b;
}
// create a worker and register functions
workerpool.worker({
add: add,
multiply: multiply
});
```
Asynchronous results can be handled by returning a Promise from a function in the worker:
```js
// file myWorker.js
const workerpool = require('workerpool');
function timeout(delay) {
return new Promise(function (resolve, reject) {
setTimeout(resolve, delay)
});
}
// create a worker and register functions
workerpool.worker({
timeout: timeout
});
```
### Events
You can send data back from workers to the pool while the task is being executed using the `workerEmit` function:
`workerEmit(payload: any)`
This function only works inside a worker **and** during a task.
Example:
```js
// file myWorker.js
const workerpool = require('workerpool');
function eventExample(delay) {
workerpool.workerEmit({
status: 'in_progress'
});
workerpool.workerEmit({
status: 'complete'
});
return true;
}
// create a worker and register functions
workerpool.worker({
eventExample: eventExample
});
```
To receive those events, you can use the `on` option of the pool `exec` method:
```js
pool.exec('eventExample', [], {
on: function (payload) {
if (payload.status === 'in_progress') {
console.log('In progress...');
} else if (payload.status === 'complete') {
console.log('Done!');
}
}
})
```
### Utilities
Following properties are available for convenience:
- **platform**: The Javascript platform. Either *node* or *browser*
- **isMainThread**: Whether the code is running in main thread or not (Workers)
- **cpus**: The number of CPUs/cores available
## Roadmap
- Implement functions for parallel processing: `map`, `reduce`, `forEach`,
`filter`, `some`, `every`, ...
- Implement graceful degradation on old browsers not supporting webworkers:
fallback to processing tasks in the main application.
- Implement session support: be able to handle a series of related tasks by a
single worker, which can keep a state for the session.
## Related libraries
- https://github.com/learnboost/cluster
- https://github.com/adambom/parallel.js
- https://github.com/padolsey/operative
- https://github.com/calvinmetcalf/catiline
- https://github.com/Unitech/pm2
- https://github.com/godaddy/node-cluster-service
- https://github.com/ramesaliyev/EasyWebWorker
- https://github.com/rvagg/node-worker-farm
## Build
First clone the project from github:
git clone git://github.com/josdejong/workerpool.git
cd workerpool
Install the project dependencies:
npm install
Then, the project can be build by executing the build script via npm:
npm run build
This will build the library workerpool.js and workerpool.min.js from the source
files and put them in the folder dist.
## Test
To execute tests for the library, install the project dependencies once:
npm install
Then, the tests can be executed:
npm test
To test code coverage of the tests:
npm run coverage
To see the coverage results, open the generated report in your browser:
./coverage/lcov-report/index.html
## Publish
- Describe changes in HISTORY.md
- Update version in package.json, run `npm install` to update it in `package-lock.json` too.
- Push to github
- Deploy to npm via `npm publish`
- Add a git tag with the version number like:
```
git tag v1.2.3
git push --tags
```
## License
Copyright (C) 2014-2020 Jos de Jong <wjosdejong@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

257
node_modules/workerpool/dist/worker.js generated vendored Normal file
View File

@@ -0,0 +1,257 @@
/**
* workerpool.js
* https://github.com/josdejong/workerpool
*
* Offload tasks to a pool of workers on node.js and in the browser.
*
* @version 6.1.0
* @date 2021-01-31
*
* @license
* Copyright (C) 2014-2020 Jos de Jong <wjosdejong@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
/******/ (function() { // webpackBootstrap
/******/ var __webpack_modules__ = ({
/***/ 744:
/***/ (function(__unused_webpack_module, exports) {
var __webpack_unused_export__;
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
/**
* worker must be started as a child process or a web worker.
* It listens for RPC messages from the parent process.
*/
// source of inspiration: https://github.com/sindresorhus/require-fool-webpack
var requireFoolWebpack = eval('typeof require !== \'undefined\'' + ' ? require' + ' : function (module) { throw new Error(\'Module " + module + " not found.\') }');
/**
* Special message sent by parent which causes the worker to terminate itself.
* Not a "message object"; this string is the entire message.
*/
var TERMINATE_METHOD_ID = '__workerpool-terminate__'; // var nodeOSPlatform = require('./environment').nodeOSPlatform;
// create a worker API for sending and receiving messages which works both on
// node.js and in the browser
var worker = {
exit: function exit() {}
};
if (typeof self !== 'undefined' && typeof postMessage === 'function' && typeof addEventListener === 'function') {
// worker in the browser
worker.on = function (event, callback) {
addEventListener(event, function (message) {
callback(message.data);
});
};
worker.send = function (message) {
postMessage(message);
};
} else if (typeof process !== 'undefined') {
// node.js
var WorkerThreads;
try {
WorkerThreads = requireFoolWebpack('worker_threads');
} catch (error) {
if (_typeof(error) === 'object' && error !== null && error.code === 'MODULE_NOT_FOUND') {// no worker_threads, fallback to sub-process based workers
} else {
throw error;
}
}
if (WorkerThreads &&
/* if there is a parentPort, we are in a WorkerThread */
WorkerThreads.parentPort !== null) {
var parentPort = WorkerThreads.parentPort;
worker.send = parentPort.postMessage.bind(parentPort);
worker.on = parentPort.on.bind(parentPort);
} else {
worker.on = process.on.bind(process);
worker.send = process.send.bind(process); // register disconnect handler only for subprocess worker to exit when parent is killed unexpectedly
worker.on('disconnect', function () {
process.exit(1);
});
worker.exit = process.exit.bind(process);
}
} else {
throw new Error('Script must be executed as a worker');
}
function convertError(error) {
return Object.getOwnPropertyNames(error).reduce(function (product, name) {
return Object.defineProperty(product, name, {
value: error[name],
enumerable: true
});
}, {});
}
/**
* Test whether a value is a Promise via duck typing.
* @param {*} value
* @returns {boolean} Returns true when given value is an object
* having functions `then` and `catch`.
*/
function isPromise(value) {
return value && typeof value.then === 'function' && typeof value["catch"] === 'function';
} // functions available externally
worker.methods = {};
/**
* Execute a function with provided arguments
* @param {String} fn Stringified function
* @param {Array} [args] Function arguments
* @returns {*}
*/
worker.methods.run = function run(fn, args) {
var f = new Function('return (' + fn + ').apply(null, arguments);');
return f.apply(f, args);
};
/**
* Get a list with methods available on this worker
* @return {String[]} methods
*/
worker.methods.methods = function methods() {
return Object.keys(worker.methods);
};
var currentRequestId = null;
worker.on('message', function (request) {
if (request === TERMINATE_METHOD_ID) {
return worker.exit(0);
}
try {
var method = worker.methods[request.method];
if (method) {
currentRequestId = request.id; // execute the function
var result = method.apply(method, request.params);
if (isPromise(result)) {
// promise returned, resolve this and then return
result.then(function (result) {
worker.send({
id: request.id,
result: result,
error: null
});
currentRequestId = null;
})["catch"](function (err) {
worker.send({
id: request.id,
result: null,
error: convertError(err)
});
currentRequestId = null;
});
} else {
// immediate result
worker.send({
id: request.id,
result: result,
error: null
});
currentRequestId = null;
}
} else {
throw new Error('Unknown method "' + request.method + '"');
}
} catch (err) {
worker.send({
id: request.id,
result: null,
error: convertError(err)
});
}
});
/**
* Register methods to the worker
* @param {Object} methods
*/
worker.register = function (methods) {
if (methods) {
for (var name in methods) {
if (methods.hasOwnProperty(name)) {
worker.methods[name] = methods[name];
}
}
}
worker.send('ready');
};
worker.emit = function (payload) {
if (currentRequestId) {
worker.send({
id: currentRequestId,
isEvent: true,
payload: payload
});
}
};
if (true) {
__webpack_unused_export__ = worker.register;
__webpack_unused_export__ = worker.emit;
}
/***/ })
/******/ });
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(__webpack_module_cache__[moduleId]) {
/******/ return __webpack_module_cache__[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/************************************************************************/
/******/ // startup
/******/ // Load entry module
/******/ __webpack_require__(744);
/******/ // This entry module used 'exports' so it can't be inlined
/******/ })()
;
//# sourceMappingURL=worker.js.map

1
node_modules/workerpool/dist/worker.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

1640
node_modules/workerpool/dist/workerpool.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

1
node_modules/workerpool/dist/workerpool.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

3
node_modules/workerpool/dist/workerpool.min.js generated vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,24 @@
/**
* workerpool.js
* https://github.com/josdejong/workerpool
*
* Offload tasks to a pool of workers on node.js and in the browser.
*
* @version 6.1.0
* @date 2021-01-31
*
* @license
* Copyright (C) 2014-2020 Jos de Jong <wjosdejong@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/

1
node_modules/workerpool/dist/workerpool.min.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

52
node_modules/workerpool/package.json generated vendored Normal file
View File

@@ -0,0 +1,52 @@
{
"name": "workerpool",
"license": "Apache-2.0",
"version": "6.1.0",
"description": "Offload tasks to a pool of workers on node.js and in the browser",
"homepage": "https://github.com/josdejong/workerpool",
"author": "Jos de Jong <wjosdejong@gmail.com> (https://github.com/josdejong)",
"repository": {
"type": "git",
"url": "git://github.com/josdejong/workerpool.git"
},
"keywords": [
"worker",
"web worker",
"cluster",
"pool",
"isomorphic"
],
"main": "src/index.js",
"browser": "dist/workerpool.js",
"files": [
"dist",
"src",
"HISTORY.md",
"LICENSE",
"README.md"
],
"scripts": {
"build": "gulp",
"watch": "gulp watch",
"test": "npm run build && mocha test --timeout 2000",
"test:debug": "npm run build && mocha debug test --timeout 10000",
"coverage": "npm run build && istanbul cover _mocha -- test; echo \"\nCoverage report is available at ./coverage/lcov-report/index.html\"",
"prepublishOnly": "npm run test"
},
"devDependencies": {
"@babel/core": "7.12.10",
"@babel/preset-env": "7.12.11",
"babel-loader": "8.2.2",
"date-format": "3.0.0",
"del": "6.0.0",
"fancy-log": "1.3.3",
"find-process": "1.4.4",
"gulp": "4.0.2",
"handlebars": "4.7.6",
"istanbul": "0.4.5",
"mocha": "8.2.1",
"uglify-js": "3.12.5",
"webpack": "5.19.0"
},
"dependencies": {}
}

416
node_modules/workerpool/src/Pool.js generated vendored Normal file
View File

@@ -0,0 +1,416 @@
var Promise = require('./Promise');
var WorkerHandler = require('./WorkerHandler');
var environment = require('./environment');
var DebugPortAllocator = require('./debug-port-allocator');
var DEBUG_PORT_ALLOCATOR = new DebugPortAllocator();
/**
* A pool to manage workers
* @param {String} [script] Optional worker script
* @param {WorkerPoolOptions} [options] See docs
* @constructor
*/
function Pool(script, options) {
if (typeof script === 'string') {
this.script = script || null;
}
else {
this.script = null;
options = script;
}
this.workers = []; // queue with all workers
this.tasks = []; // queue with tasks awaiting execution
options = options || {};
this.forkArgs = options.forkArgs || [];
this.forkOpts = options.forkOpts || {};
this.debugPortStart = (options.debugPortStart || 43210);
this.nodeWorker = options.nodeWorker;
this.workerType = options.workerType || options.nodeWorker || 'auto'
this.maxQueueSize = options.maxQueueSize || Infinity;
// configuration
if (options && 'maxWorkers' in options) {
validateMaxWorkers(options.maxWorkers);
this.maxWorkers = options.maxWorkers;
}
else {
this.maxWorkers = Math.max((environment.cpus || 4) - 1, 1);
}
if (options && 'minWorkers' in options) {
if(options.minWorkers === 'max') {
this.minWorkers = this.maxWorkers;
} else {
validateMinWorkers(options.minWorkers);
this.minWorkers = options.minWorkers;
this.maxWorkers = Math.max(this.minWorkers, this.maxWorkers); // in case minWorkers is higher than maxWorkers
}
this._ensureMinWorkers();
}
this._boundNext = this._next.bind(this);
if (this.workerType === 'thread') {
WorkerHandler.ensureWorkerThreads();
}
}
/**
* Execute a function on a worker.
*
* Example usage:
*
* var pool = new Pool()
*
* // call a function available on the worker
* pool.exec('fibonacci', [6])
*
* // offload a function
* function add(a, b) {
* return a + b
* };
* pool.exec(add, [2, 4])
* .then(function (result) {
* console.log(result); // outputs 6
* })
* .catch(function(error) {
* console.log(error);
* });
*
* @param {String | Function} method Function name or function.
* If `method` is a string, the corresponding
* method on the worker will be executed
* If `method` is a Function, the function
* will be stringified and executed via the
* workers built-in function `run(fn, args)`.
* @param {Array} [params] Function arguments applied when calling the function
* @param {ExecOptions} [options] Options object
* @return {Promise.<*, Error>} result
*/
Pool.prototype.exec = function (method, params, options) {
// validate type of arguments
if (params && !Array.isArray(params)) {
throw new TypeError('Array expected as argument "params"');
}
if (typeof method === 'string') {
var resolver = Promise.defer();
if (this.tasks.length >= this.maxQueueSize) {
throw new Error('Max queue size of ' + this.maxQueueSize + ' reached');
}
// add a new task to the queue
var tasks = this.tasks;
var task = {
method: method,
params: params,
resolver: resolver,
timeout: null,
options: options
};
tasks.push(task);
// replace the timeout method of the Promise with our own,
// which starts the timer as soon as the task is actually started
var originalTimeout = resolver.promise.timeout;
resolver.promise.timeout = function timeout (delay) {
if (tasks.indexOf(task) !== -1) {
// task is still queued -> start the timer later on
task.timeout = delay;
return resolver.promise;
}
else {
// task is already being executed -> start timer immediately
return originalTimeout.call(resolver.promise, delay);
}
};
// trigger task execution
this._next();
return resolver.promise;
}
else if (typeof method === 'function') {
// send stringified function and function arguments to worker
return this.exec('run', [String(method), params]);
}
else {
throw new TypeError('Function or string expected as argument "method"');
}
};
/**
* Create a proxy for current worker. Returns an object containing all
* methods available on the worker. The methods always return a promise.
*
* @return {Promise.<Object, Error>} proxy
*/
Pool.prototype.proxy = function () {
if (arguments.length > 0) {
throw new Error('No arguments expected');
}
var pool = this;
return this.exec('methods')
.then(function (methods) {
var proxy = {};
methods.forEach(function (method) {
proxy[method] = function () {
return pool.exec(method, Array.prototype.slice.call(arguments));
}
});
return proxy;
});
};
/**
* Creates new array with the results of calling a provided callback function
* on every element in this array.
* @param {Array} array
* @param {function} callback Function taking two arguments:
* `callback(currentValue, index)`
* @return {Promise.<Array>} Returns a promise which resolves with an Array
* containing the results of the callback function
* executed for each of the array elements.
*/
/* TODO: implement map
Pool.prototype.map = function (array, callback) {
};
*/
/**
* Grab the first task from the queue, find a free worker, and assign the
* worker to the task.
* @protected
*/
Pool.prototype._next = function () {
if (this.tasks.length > 0) {
// there are tasks in the queue
// find an available worker
var worker = this._getWorker();
if (worker) {
// get the first task from the queue
var me = this;
var task = this.tasks.shift();
// check if the task is still pending (and not cancelled -> promise rejected)
if (task.resolver.promise.pending) {
// send the request to the worker
var promise = worker.exec(task.method, task.params, task.resolver, task.options)
.then(me._boundNext)
.catch(function () {
// if the worker crashed and terminated, remove it from the pool
if (worker.terminated) {
return me._removeWorker(worker);
}
}).then(function() {
me._next(); // trigger next task in the queue
});
// start queued timer now
if (typeof task.timeout === 'number') {
promise.timeout(task.timeout);
}
} else {
// The task taken was already complete (either rejected or resolved), so just trigger next task in the queue
me._next();
}
}
}
};
/**
* Get an available worker. If no worker is available and the maximum number
* of workers isn't yet reached, a new worker will be created and returned.
* If no worker is available and the maximum number of workers is reached,
* null will be returned.
*
* @return {WorkerHandler | null} worker
* @private
*/
Pool.prototype._getWorker = function() {
// find a non-busy worker
var workers = this.workers;
for (var i = 0; i < workers.length; i++) {
var worker = workers[i];
if (worker.busy() === false) {
return worker;
}
}
if (workers.length < this.maxWorkers) {
// create a new worker
worker = this._createWorkerHandler();
workers.push(worker);
return worker;
}
return null;
};
/**
* Remove a worker from the pool.
* Attempts to terminate worker if not already terminated, and ensures the minimum
* pool size is met.
* @param {WorkerHandler} worker
* @return {Promise<WorkerHandler>}
* @protected
*/
Pool.prototype._removeWorker = function(worker) {
DEBUG_PORT_ALLOCATOR.releasePort(worker.debugPort);
// _removeWorker will call this, but we need it to be removed synchronously
this._removeWorkerFromList(worker);
// If minWorkers set, spin up new workers to replace the crashed ones
this._ensureMinWorkers();
// terminate the worker (if not already terminated)
return new Promise(function(resolve, reject) {
worker.terminate(false, function(err) {
if (err) {
reject(err);
} else {
resolve(worker);
}
});
});
};
/**
* Remove a worker from the pool list.
* @param {WorkerHandler} worker
* @protected
*/
Pool.prototype._removeWorkerFromList = function(worker) {
// remove from the list with workers
var index = this.workers.indexOf(worker);
if (index !== -1) {
this.workers.splice(index, 1);
}
};
/**
* Close all active workers. Tasks currently being executed will be finished first.
* @param {boolean} [force=false] If false (default), the workers are terminated
* after finishing all tasks currently in
* progress. If true, the workers will be
* terminated immediately.
* @param {number} [timeout] If provided and non-zero, worker termination promise will be rejected
* after timeout if worker process has not been terminated.
* @return {Promise.<void, Error>}
*/
Pool.prototype.terminate = function (force, timeout) {
// cancel any pending tasks
this.tasks.forEach(function (task) {
task.resolver.reject(new Error('Pool terminated'));
});
this.tasks.length = 0;
var f = function (worker) {
this._removeWorkerFromList(worker);
};
var removeWorker = f.bind(this);
var promises = [];
var workers = this.workers.slice();
workers.forEach(function (worker) {
var termPromise = worker.terminateAndNotify(force, timeout)
.then(removeWorker);
promises.push(termPromise);
});
return Promise.all(promises);
};
/**
* Retrieve statistics on tasks and workers.
* @return {{totalWorkers: number, busyWorkers: number, idleWorkers: number, pendingTasks: number, activeTasks: number}} Returns an object with statistics
*/
Pool.prototype.stats = function () {
var totalWorkers = this.workers.length;
var busyWorkers = this.workers.filter(function (worker) {
return worker.busy();
}).length;
return {
totalWorkers: totalWorkers,
busyWorkers: busyWorkers,
idleWorkers: totalWorkers - busyWorkers,
pendingTasks: this.tasks.length,
activeTasks: busyWorkers
};
};
/**
* Ensures that a minimum of minWorkers is up and running
* @protected
*/
Pool.prototype._ensureMinWorkers = function() {
if (this.minWorkers) {
for(var i = this.workers.length; i < this.minWorkers; i++) {
this.workers.push(this._createWorkerHandler());
}
}
};
/**
* Helper function to create a new WorkerHandler and pass all options.
* @return {WorkerHandler}
* @private
*/
Pool.prototype._createWorkerHandler = function () {
return new WorkerHandler(this.script, {
forkArgs: this.forkArgs,
forkOpts: this.forkOpts,
debugPort: DEBUG_PORT_ALLOCATOR.nextAvailableStartingAt(this.debugPortStart),
workerType: this.workerType
});
}
/**
* Ensure that the maxWorkers option is an integer >= 1
* @param {*} maxWorkers
* @returns {boolean} returns true maxWorkers has a valid value
*/
function validateMaxWorkers(maxWorkers) {
if (!isNumber(maxWorkers) || !isInteger(maxWorkers) || maxWorkers < 1) {
throw new TypeError('Option maxWorkers must be an integer number >= 1');
}
}
/**
* Ensure that the minWorkers option is an integer >= 0
* @param {*} minWorkers
* @returns {boolean} returns true when minWorkers has a valid value
*/
function validateMinWorkers(minWorkers) {
if (!isNumber(minWorkers) || !isInteger(minWorkers) || minWorkers < 0) {
throw new TypeError('Option minWorkers must be an integer number >= 0');
}
}
/**
* Test whether a variable is a number
* @param {*} value
* @returns {boolean} returns true when value is a number
*/
function isNumber(value) {
return typeof value === 'number';
}
/**
* Test whether a number is an integer
* @param {number} value
* @returns {boolean} Returns true if value is an integer
*/
function isInteger(value) {
return Math.round(value) == value;
}
module.exports = Pool;

279
node_modules/workerpool/src/Promise.js generated vendored Normal file
View File

@@ -0,0 +1,279 @@
'use strict';
/**
* Promise
*
* Inspired by https://gist.github.com/RubaXa/8501359 from RubaXa <trash@rubaxa.org>
*
* @param {Function} handler Called as handler(resolve: Function, reject: Function)
* @param {Promise} [parent] Parent promise for propagation of cancel and timeout
*/
function Promise(handler, parent) {
var me = this;
if (!(this instanceof Promise)) {
throw new SyntaxError('Constructor must be called with the new operator');
}
if (typeof handler !== 'function') {
throw new SyntaxError('Function parameter handler(resolve, reject) missing');
}
var _onSuccess = [];
var _onFail = [];
// status
this.resolved = false;
this.rejected = false;
this.pending = true;
/**
* Process onSuccess and onFail callbacks: add them to the queue.
* Once the promise is resolve, the function _promise is replace.
* @param {Function} onSuccess
* @param {Function} onFail
* @private
*/
var _process = function (onSuccess, onFail) {
_onSuccess.push(onSuccess);
_onFail.push(onFail);
};
/**
* Add an onSuccess callback and optionally an onFail callback to the Promise
* @param {Function} onSuccess
* @param {Function} [onFail]
* @returns {Promise} promise
*/
this.then = function (onSuccess, onFail) {
return new Promise(function (resolve, reject) {
var s = onSuccess ? _then(onSuccess, resolve, reject) : resolve;
var f = onFail ? _then(onFail, resolve, reject) : reject;
_process(s, f);
}, me);
};
/**
* Resolve the promise
* @param {*} result
* @type {Function}
*/
var _resolve = function (result) {
// update status
me.resolved = true;
me.rejected = false;
me.pending = false;
_onSuccess.forEach(function (fn) {
fn(result);
});
_process = function (onSuccess, onFail) {
onSuccess(result);
};
_resolve = _reject = function () { };
return me;
};
/**
* Reject the promise
* @param {Error} error
* @type {Function}
*/
var _reject = function (error) {
// update status
me.resolved = false;
me.rejected = true;
me.pending = false;
_onFail.forEach(function (fn) {
fn(error);
});
_process = function (onSuccess, onFail) {
onFail(error);
};
_resolve = _reject = function () { }
return me;
};
/**
* Cancel te promise. This will reject the promise with a CancellationError
* @returns {Promise} self
*/
this.cancel = function () {
if (parent) {
parent.cancel();
}
else {
_reject(new CancellationError());
}
return me;
};
/**
* Set a timeout for the promise. If the promise is not resolved within
* the time, the promise will be cancelled and a TimeoutError is thrown.
* If the promise is resolved in time, the timeout is removed.
* @param {number} delay Delay in milliseconds
* @returns {Promise} self
*/
this.timeout = function (delay) {
if (parent) {
parent.timeout(delay);
}
else {
var timer = setTimeout(function () {
_reject(new TimeoutError('Promise timed out after ' + delay + ' ms'));
}, delay);
me.always(function () {
clearTimeout(timer);
});
}
return me;
};
// attach handler passing the resolve and reject functions
handler(function (result) {
_resolve(result);
}, function (error) {
_reject(error);
});
}
/**
* Execute given callback, then call resolve/reject based on the returned result
* @param {Function} callback
* @param {Function} resolve
* @param {Function} reject
* @returns {Function}
* @private
*/
function _then(callback, resolve, reject) {
return function (result) {
try {
var res = callback(result);
if (res && typeof res.then === 'function' && typeof res['catch'] === 'function') {
// method returned a promise
res.then(resolve, reject);
}
else {
resolve(res);
}
}
catch (error) {
reject(error);
}
}
}
/**
* Add an onFail callback to the Promise
* @param {Function} onFail
* @returns {Promise} promise
*/
Promise.prototype['catch'] = function (onFail) {
return this.then(null, onFail);
};
// TODO: add support for Promise.catch(Error, callback)
// TODO: add support for Promise.catch(Error, Error, callback)
/**
* Execute given callback when the promise either resolves or rejects.
* @param {Function} fn
* @returns {Promise} promise
*/
Promise.prototype.always = function (fn) {
return this.then(fn, fn);
};
/**
* Create a promise which resolves when all provided promises are resolved,
* and fails when any of the promises resolves.
* @param {Promise[]} promises
* @returns {Promise} promise
*/
Promise.all = function (promises){
return new Promise(function (resolve, reject) {
var remaining = promises.length,
results = [];
if (remaining) {
promises.forEach(function (p, i) {
p.then(function (result) {
results[i] = result;
remaining--;
if (remaining == 0) {
resolve(results);
}
}, function (error) {
remaining = 0;
reject(error);
});
});
}
else {
resolve(results);
}
});
};
/**
* Create a promise resolver
* @returns {{promise: Promise, resolve: Function, reject: Function}} resolver
*/
Promise.defer = function () {
var resolver = {};
resolver.promise = new Promise(function (resolve, reject) {
resolver.resolve = resolve;
resolver.reject = reject;
});
return resolver;
};
/**
* Create a cancellation error
* @param {String} [message]
* @extends Error
*/
function CancellationError(message) {
this.message = message || 'promise cancelled';
this.stack = (new Error()).stack;
}
CancellationError.prototype = new Error();
CancellationError.prototype.constructor = Error;
CancellationError.prototype.name = 'CancellationError';
Promise.CancellationError = CancellationError;
/**
* Create a timeout error
* @param {String} [message]
* @extends Error
*/
function TimeoutError(message) {
this.message = message || 'timeout exceeded';
this.stack = (new Error()).stack;
}
TimeoutError.prototype = new Error();
TimeoutError.prototype.constructor = Error;
TimeoutError.prototype.name = 'TimeoutError';
Promise.TimeoutError = TimeoutError;
module.exports = Promise;

489
node_modules/workerpool/src/WorkerHandler.js generated vendored Normal file
View File

@@ -0,0 +1,489 @@
'use strict';
var Promise = require('./Promise');
var environment = require('./environment');
var requireFoolWebpack = require('./requireFoolWebpack');
/**
* Special message sent by parent which causes a child process worker to terminate itself.
* Not a "message object"; this string is the entire message.
*/
var TERMINATE_METHOD_ID = '__workerpool-terminate__';
/**
* If sending `TERMINATE_METHOD_ID` does not cause the child process to exit in this many milliseconds,
* force-kill the child process.
*/
var CHILD_PROCESS_EXIT_TIMEOUT = 1000;
function ensureWorkerThreads() {
var WorkerThreads = tryRequireWorkerThreads()
if (!WorkerThreads) {
throw new Error('WorkerPool: workerType = \'thread\' is not supported, Node >= 11.7.0 required')
}
return WorkerThreads;
}
// check whether Worker is supported by the browser
function ensureWebWorker() {
// Workaround for a bug in PhantomJS (Or QtWebkit): https://github.com/ariya/phantomjs/issues/14534
if (typeof Worker !== 'function' && (typeof Worker !== 'object' || typeof Worker.prototype.constructor !== 'function')) {
throw new Error('WorkerPool: Web Workers not supported');
}
}
function tryRequireWorkerThreads() {
try {
return requireFoolWebpack('worker_threads');
} catch(error) {
if (typeof error === 'object' && error !== null && error.code === 'MODULE_NOT_FOUND') {
// no worker_threads available (old version of node.js)
return null;
} else {
throw error;
}
}
}
// get the default worker script
function getDefaultWorker() {
if (environment.platform === 'browser') {
// test whether the browser supports all features that we need
if (typeof Blob === 'undefined') {
throw new Error('Blob not supported by the browser');
}
if (!window.URL || typeof window.URL.createObjectURL !== 'function') {
throw new Error('URL.createObjectURL not supported by the browser');
}
// use embedded worker.js
var blob = new Blob([require('./generated/embeddedWorker')], {type: 'text/javascript'});
return window.URL.createObjectURL(blob);
}
else {
// use external worker.js in current directory
return __dirname + '/worker.js';
}
}
function setupWorker(script, options) {
if (options.workerType === 'web') { // browser only
ensureWebWorker();
return setupBrowserWorker(script, Worker);
} else if (options.workerType === 'thread') { // node.js only
WorkerThreads = ensureWorkerThreads();
return setupWorkerThreadWorker(script, WorkerThreads);
} else if (options.workerType === 'process' || !options.workerType) { // node.js only
return setupProcessWorker(script, resolveForkOptions(options), requireFoolWebpack('child_process'));
} else { // options.workerType === 'auto' or undefined
if (environment.platform === 'browser') {
ensureWebWorker();
return setupBrowserWorker(script, Worker);
}
else { // environment.platform === 'node'
var WorkerThreads = tryRequireWorkerThreads();
if (WorkerThreads) {
return setupWorkerThreadWorker(script, WorkerThreads);
} else {
return setupProcessWorker(script, resolveForkOptions(options), requireFoolWebpack('child_process'));
}
}
}
}
function setupBrowserWorker(script, Worker) {
// create the web worker
var worker = new Worker(script);
worker.isBrowserWorker = true;
// add node.js API to the web worker
worker.on = function (event, callback) {
this.addEventListener(event, function (message) {
callback(message.data);
});
};
worker.send = function (message) {
this.postMessage(message);
};
return worker;
}
function setupWorkerThreadWorker(script, WorkerThreads) {
var worker = new WorkerThreads.Worker(script, {
stdout: false, // automatically pipe worker.STDOUT to process.STDOUT
stderr: false // automatically pipe worker.STDERR to process.STDERR
});
worker.isWorkerThread = true;
// make the worker mimic a child_process
worker.send = function(message) {
this.postMessage(message);
};
worker.kill = function() {
this.terminate();
return true;
};
worker.disconnect = function() {
this.terminate();
};
return worker;
}
function setupProcessWorker(script, options, child_process) {
// no WorkerThreads, fallback to sub-process based workers
var worker = child_process.fork(
script,
options.forkArgs,
options.forkOpts
);
worker.isChildProcess = true;
return worker;
}
// add debug flags to child processes if the node inspector is active
function resolveForkOptions(opts) {
opts = opts || {};
var processExecArgv = process.execArgv.join(' ');
var inspectorActive = processExecArgv.indexOf('--inspect') !== -1;
var debugBrk = processExecArgv.indexOf('--debug-brk') !== -1;
var execArgv = [];
if (inspectorActive) {
execArgv.push('--inspect=' + opts.debugPort);
if (debugBrk) {
execArgv.push('--debug-brk');
}
}
process.execArgv.forEach(function(arg) {
if (arg.indexOf('--max-old-space-size') > -1) {
execArgv.push(arg)
}
})
return Object.assign({}, opts, {
forkArgs: opts.forkArgs,
forkOpts: Object.assign({}, opts.forkOpts, {
execArgv: (opts.forkOpts && opts.forkOpts.execArgv || [])
.concat(execArgv)
})
});
}
/**
* Converts a serialized error to Error
* @param {Object} obj Error that has been serialized and parsed to object
* @return {Error} The equivalent Error.
*/
function objectToError (obj) {
var temp = new Error('')
var props = Object.keys(obj)
for (var i = 0; i < props.length; i++) {
temp[props[i]] = obj[props[i]]
}
return temp
}
/**
* A WorkerHandler controls a single worker. This worker can be a child process
* on node.js or a WebWorker in a browser environment.
* @param {String} [script] If no script is provided, a default worker with a
* function run will be created.
* @param {WorkerPoolOptions} _options See docs
* @constructor
*/
function WorkerHandler(script, _options) {
var me = this;
var options = _options || {};
this.script = script || getDefaultWorker();
this.worker = setupWorker(this.script, options);
this.debugPort = options.debugPort;
// The ready message is only sent if the worker.add method is called (And the default script is not used)
if (!script) {
this.worker.ready = true;
}
// queue for requests that are received before the worker is ready
this.requestQueue = [];
this.worker.on('message', function (response) {
if (typeof response === 'string' && response === 'ready') {
me.worker.ready = true;
dispatchQueuedRequests();
} else {
// find the task from the processing queue, and run the tasks callback
var id = response.id;
var task = me.processing[id];
if (task !== undefined) {
if (response.isEvent) {
if (task.options && typeof task.options.on === 'function') {
task.options.on(response.payload);
}
} else {
// remove the task from the queue
delete me.processing[id];
// test if we need to terminate
if (me.terminating === true) {
// complete worker termination if all tasks are finished
me.terminate();
}
// resolve the task's promise
if (response.error) {
task.resolver.reject(objectToError(response.error));
}
else {
task.resolver.resolve(response.result);
}
}
}
}
});
// reject all running tasks on worker error
function onError(error) {
me.terminated = true;
for (var id in me.processing) {
if (me.processing[id] !== undefined) {
me.processing[id].resolver.reject(error);
}
}
me.processing = Object.create(null);
}
// send all queued requests to worker
function dispatchQueuedRequests()
{
for(const request of me.requestQueue.splice(0)) {
me.worker.send(request);
}
}
var worker = this.worker;
// listen for worker messages error and exit
this.worker.on('error', onError);
this.worker.on('exit', function (exitCode, signalCode) {
var message = 'Workerpool Worker terminated Unexpectedly\n';
message += ' exitCode: `' + exitCode + '`\n';
message += ' signalCode: `' + signalCode + '`\n';
message += ' workerpool.script: `' + me.script + '`\n';
message += ' spawnArgs: `' + worker.spawnargs + '`\n';
message += ' spawnfile: `' + worker.spawnfile + '`\n'
message += ' stdout: `' + worker.stdout + '`\n'
message += ' stderr: `' + worker.stderr + '`\n'
onError(new Error(message));
});
this.processing = Object.create(null); // queue with tasks currently in progress
this.terminating = false;
this.terminated = false;
this.terminationHandler = null;
this.lastId = 0;
}
/**
* Get a list with methods available on the worker.
* @return {Promise.<String[], Error>} methods
*/
WorkerHandler.prototype.methods = function () {
return this.exec('methods');
};
/**
* Execute a method with given parameters on the worker
* @param {String} method
* @param {Array} [params]
* @param {{resolve: Function, reject: Function}} [resolver]
* @param {ExecOptions} [options]
* @return {Promise.<*, Error>} result
*/
WorkerHandler.prototype.exec = function(method, params, resolver, options) {
if (!resolver) {
resolver = Promise.defer();
}
// generate a unique id for the task
var id = ++this.lastId;
// register a new task as being in progress
this.processing[id] = {
id: id,
resolver: resolver,
options: options
};
// build a JSON-RPC request
var request = {
id: id,
method: method,
params: params
};
if (this.terminated) {
resolver.reject(new Error('Worker is terminated'));
} else if (this.worker.ready) {
// send the request to the worker
this.worker.send(request);
} else {
this.requestQueue.push(request);
}
// on cancellation, force the worker to terminate
var me = this;
return resolver.promise.catch(function (error) {
if (error instanceof Promise.CancellationError || error instanceof Promise.TimeoutError) {
// remove this task from the queue. It is already rejected (hence this
// catch event), and else it will be rejected again when terminating
delete me.processing[id];
// terminate worker
return me.terminateAndNotify(true)
.then(function() {
throw error;
}, function(err) {
throw err;
});
} else {
throw error;
}
})
};
/**
* Test whether the worker is working or not
* @return {boolean} Returns true if the worker is busy
*/
WorkerHandler.prototype.busy = function () {
return Object.keys(this.processing).length > 0;
};
/**
* Terminate the worker.
* @param {boolean} [force=false] If false (default), the worker is terminated
* after finishing all tasks currently in
* progress. If true, the worker will be
* terminated immediately.
* @param {function} [callback=null] If provided, will be called when process terminates.
*/
WorkerHandler.prototype.terminate = function (force, callback) {
var me = this;
if (force) {
// cancel all tasks in progress
for (var id in this.processing) {
if (this.processing[id] !== undefined) {
this.processing[id].resolver.reject(new Error('Worker terminated'));
}
}
this.processing = Object.create(null);
}
if (typeof callback === 'function') {
this.terminationHandler = callback;
}
if (!this.busy()) {
// all tasks are finished. kill the worker
var cleanup = function(err) {
me.terminated = true;
me.worker = null;
me.terminating = false;
if (me.terminationHandler) {
me.terminationHandler(err, me);
} else if (err) {
throw err;
}
}
if (this.worker) {
if (typeof this.worker.kill === 'function') {
if (this.worker.killed) {
cleanup(new Error('worker already killed!'));
return;
}
if (this.worker.isChildProcess) {
var cleanExitTimeout = setTimeout(function() {
me.worker.kill();
}, CHILD_PROCESS_EXIT_TIMEOUT);
this.worker.once('exit', function() {
clearTimeout(cleanExitTimeout);
me.worker.killed = true;
cleanup();
});
if (this.worker.ready) {
this.worker.send(TERMINATE_METHOD_ID);
} else {
this.worker.requestQueue.push(TERMINATE_METHOD_ID)
}
} else {
// worker_thread
this.worker.kill();
this.worker.killed = true;
cleanup();
}
return;
}
else if (typeof this.worker.terminate === 'function') {
this.worker.terminate(); // web worker
this.worker.killed = true;
}
else {
throw new Error('Failed to terminate worker');
}
}
cleanup();
}
else {
// we can't terminate immediately, there are still tasks being executed
this.terminating = true;
}
};
/**
* Terminate the worker, returning a Promise that resolves when the termination has been done.
* @param {boolean} [force=false] If false (default), the worker is terminated
* after finishing all tasks currently in
* progress. If true, the worker will be
* terminated immediately.
* @param {number} [timeout] If provided and non-zero, worker termination promise will be rejected
* after timeout if worker process has not been terminated.
* @return {Promise.<WorkerHandler, Error>}
*/
WorkerHandler.prototype.terminateAndNotify = function (force, timeout) {
var resolver = Promise.defer();
if (timeout) {
resolver.promise.timeout = timeout;
}
this.terminate(force, function(err, worker) {
if (err) {
resolver.reject(err);
} else {
resolver.resolve(worker);
}
});
return resolver.promise;
};
module.exports = WorkerHandler;
module.exports._tryRequireWorkerThreads = tryRequireWorkerThreads;
module.exports._setupProcessWorker = setupProcessWorker;
module.exports._setupBrowserWorker = setupBrowserWorker;
module.exports._setupWorkerThreadWorker = setupWorkerThreadWorker;
module.exports.ensureWorkerThreads = ensureWorkerThreads;

28
node_modules/workerpool/src/debug-port-allocator.js generated vendored Normal file
View File

@@ -0,0 +1,28 @@
'use strict';
var MAX_PORTS = 65535;
module.exports = DebugPortAllocator;
function DebugPortAllocator() {
this.ports = Object.create(null);
this.length = 0;
}
DebugPortAllocator.prototype.nextAvailableStartingAt = function(starting) {
while (this.ports[starting] === true) {
starting++;
}
if (starting >= MAX_PORTS) {
throw new Error('WorkerPool debug port limit reached: ' + starting + '>= ' + MAX_PORTS );
}
this.ports[starting] = true;
this.length++;
return starting;
};
DebugPortAllocator.prototype.releasePort = function(port) {
delete this.ports[port];
this.length--;
};

36
node_modules/workerpool/src/environment.js generated vendored Normal file
View File

@@ -0,0 +1,36 @@
var requireFoolWebpack = require('./requireFoolWebpack');
// source: https://github.com/flexdinesh/browser-or-node
var isNode = function (nodeProcess) {
return (
typeof nodeProcess !== 'undefined' &&
nodeProcess.versions != null &&
nodeProcess.versions.node != null
);
}
module.exports.isNode = isNode
// determines the JavaScript platform: browser or node
module.exports.platform = typeof process !== 'undefined' && isNode(process)
? 'node'
: 'browser';
// determines whether the code is running in main thread or not
// note that in node.js we have to check both worker_thread and child_process
var worker_threads = tryRequireFoolWebpack('worker_threads');
module.exports.isMainThread = module.exports.platform === 'node'
? ((!worker_threads || worker_threads.isMainThread) && !process.connected)
: typeof Window !== 'undefined';
// determines the number of cpus available
module.exports.cpus = module.exports.platform === 'browser'
? self.navigator.hardwareConcurrency
: requireFoolWebpack('os').cpus().length;
function tryRequireFoolWebpack (module) {
try {
return requireFoolWebpack(module);
} catch(err) {
return null
}
}

File diff suppressed because one or more lines are too long

24
node_modules/workerpool/src/header.js generated vendored Normal file
View File

@@ -0,0 +1,24 @@
/**
* workerpool.js
* https://github.com/josdejong/workerpool
*
* Offload tasks to a pool of workers on node.js and in the browser.
*
* @version @@version
* @date @@date
*
* @license
* Copyright (C) 2014-2020 Jos de Jong <wjosdejong@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/

41
node_modules/workerpool/src/index.js generated vendored Normal file
View File

@@ -0,0 +1,41 @@
var environment = require('./environment');
/**
* Create a new worker pool
* @param {string} [script]
* @param {WorkerPoolOptions} [options]
* @returns {Pool} pool
*/
exports.pool = function pool(script, options) {
var Pool = require('./Pool');
return new Pool(script, options);
};
/**
* Create a worker and optionally register a set of methods to the worker.
* @param {Object} [methods]
*/
exports.worker = function worker(methods) {
var worker = require('./worker');
worker.add(methods);
};
/**
* Sends an event to the parent worker pool.
* @param {any} payload
*/
exports.workerEmit = function workerEmit(payload) {
var worker = require('./worker');
worker.emit(payload);
};
/**
* Create a promise.
* @type {Promise} promise
*/
exports.Promise = require('./Promise');
exports.platform = environment.platform;
exports.isMainThread = environment.isMainThread;
exports.cpus = environment.cpus;

8
node_modules/workerpool/src/requireFoolWebpack.js generated vendored Normal file
View File

@@ -0,0 +1,8 @@
// source of inspiration: https://github.com/sindresorhus/require-fool-webpack
var requireFoolWebpack = eval(
'typeof require !== \'undefined\' ' +
'? require ' +
': function (module) { throw new Error(\'Module " + module + " not found.\') }'
);
module.exports = requireFoolWebpack;

15
node_modules/workerpool/src/types.js generated vendored Normal file
View File

@@ -0,0 +1,15 @@
/**
* @typedef {Object} WorkerPoolOptions
* @property {number | 'max'} [minWorkers]
* @property {number} [maxWorkers]
* @property {number} [maxQueueSize]
* @property {'auto' | 'web' | 'process' | 'thread'} [workerType]
* @property {*} [forkArgs]
* @property {*} [forkOpts]
* @property {number} [debugPortStart]
*/
/**
* @typedef {Object} ExecOptions
* @property {(payload: any) => unknown} [on]
*/

202
node_modules/workerpool/src/worker.js generated vendored Normal file
View File

@@ -0,0 +1,202 @@
/**
* worker must be started as a child process or a web worker.
* It listens for RPC messages from the parent process.
*/
// source of inspiration: https://github.com/sindresorhus/require-fool-webpack
var requireFoolWebpack = eval(
'typeof require !== \'undefined\'' +
' ? require' +
' : function (module) { throw new Error(\'Module " + module + " not found.\') }'
);
/**
* Special message sent by parent which causes the worker to terminate itself.
* Not a "message object"; this string is the entire message.
*/
var TERMINATE_METHOD_ID = '__workerpool-terminate__';
// var nodeOSPlatform = require('./environment').nodeOSPlatform;
// create a worker API for sending and receiving messages which works both on
// node.js and in the browser
var worker = {
exit: function() {}
};
if (typeof self !== 'undefined' && typeof postMessage === 'function' && typeof addEventListener === 'function') {
// worker in the browser
worker.on = function (event, callback) {
addEventListener(event, function (message) {
callback(message.data);
})
};
worker.send = function (message) {
postMessage(message);
};
}
else if (typeof process !== 'undefined') {
// node.js
var WorkerThreads;
try {
WorkerThreads = requireFoolWebpack('worker_threads');
} catch(error) {
if (typeof error === 'object' && error !== null && error.code === 'MODULE_NOT_FOUND') {
// no worker_threads, fallback to sub-process based workers
} else {
throw error;
}
}
if (WorkerThreads &&
/* if there is a parentPort, we are in a WorkerThread */
WorkerThreads.parentPort !== null) {
var parentPort = WorkerThreads.parentPort;
worker.send = parentPort.postMessage.bind(parentPort);
worker.on = parentPort.on.bind(parentPort);
} else {
worker.on = process.on.bind(process);
worker.send = process.send.bind(process);
// register disconnect handler only for subprocess worker to exit when parent is killed unexpectedly
worker.on('disconnect', function () {
process.exit(1);
});
worker.exit = process.exit.bind(process);
}
}
else {
throw new Error('Script must be executed as a worker');
}
function convertError(error) {
return Object.getOwnPropertyNames(error).reduce(function(product, name) {
return Object.defineProperty(product, name, {
value: error[name],
enumerable: true
});
}, {});
}
/**
* Test whether a value is a Promise via duck typing.
* @param {*} value
* @returns {boolean} Returns true when given value is an object
* having functions `then` and `catch`.
*/
function isPromise(value) {
return value && (typeof value.then === 'function') && (typeof value.catch === 'function');
}
// functions available externally
worker.methods = {};
/**
* Execute a function with provided arguments
* @param {String} fn Stringified function
* @param {Array} [args] Function arguments
* @returns {*}
*/
worker.methods.run = function run(fn, args) {
var f = new Function('return (' + fn + ').apply(null, arguments);');
return f.apply(f, args);
};
/**
* Get a list with methods available on this worker
* @return {String[]} methods
*/
worker.methods.methods = function methods() {
return Object.keys(worker.methods);
};
var currentRequestId = null;
worker.on('message', function (request) {
if (request === TERMINATE_METHOD_ID) {
return worker.exit(0);
}
try {
var method = worker.methods[request.method];
if (method) {
currentRequestId = request.id;
// execute the function
var result = method.apply(method, request.params);
if (isPromise(result)) {
// promise returned, resolve this and then return
result
.then(function (result) {
worker.send({
id: request.id,
result: result,
error: null
});
currentRequestId = null;
})
.catch(function (err) {
worker.send({
id: request.id,
result: null,
error: convertError(err)
});
currentRequestId = null;
});
}
else {
// immediate result
worker.send({
id: request.id,
result: result,
error: null
});
currentRequestId = null;
}
}
else {
throw new Error('Unknown method "' + request.method + '"');
}
}
catch (err) {
worker.send({
id: request.id,
result: null,
error: convertError(err)
});
}
});
/**
* Register methods to the worker
* @param {Object} methods
*/
worker.register = function (methods) {
if (methods) {
for (var name in methods) {
if (methods.hasOwnProperty(name)) {
worker.methods[name] = methods[name];
}
}
}
worker.send('ready');
};
worker.emit = function (payload) {
if (currentRequestId) {
worker.send({
id: currentRequestId,
isEvent: true,
payload
});
}
};
if (typeof exports !== 'undefined') {
exports.add = worker.register;
exports.emit = worker.emit;
}