Initial project import
This commit is contained in:
367
node_modules/troika-worker-utils/CHANGELOG.md
generated
vendored
Normal file
367
node_modules/troika-worker-utils/CHANGELOG.md
generated
vendored
Normal file
@@ -0,0 +1,367 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [0.52.0](https://github.com/protectwise/troika/compare/v0.51.1...v0.52.0) (2024-11-11)
|
||||
|
||||
**Note:** Version bump only for package troika-worker-utils
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.51.1](https://github.com/protectwise/troika/compare/v0.51.0...v0.51.1) (2024-11-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **troika-worker-utils:** Avoid no-workers log errors when forcing main thread ([d396e51](https://github.com/protectwise/troika/commit/d396e51708d151d9cee04394d3cd8ac87c1a7dc0)), closes [#337](https://github.com/protectwise/troika/issues/337)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.51.0](https://github.com/protectwise/troika/compare/v0.50.3...v0.51.0) (2024-11-10)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **troika-worker-utils:** Add a handle to force running worker modules on main thread ([4f8bc13](https://github.com/protectwise/troika/commit/4f8bc13cf32a1665e2177ab2f62f60f2fe17ad66))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.50.0](https://github.com/protectwise/troika/compare/v0.49.1...v0.50.0) (2024-10-11)
|
||||
|
||||
**Note:** Version bump only for package troika-worker-utils
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.49.0](https://github.com/protectwise/troika/compare/v0.48.1...v0.49.0) (2023-10-08)
|
||||
|
||||
**Note:** Version bump only for package troika-worker-utils
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.48.0](https://github.com/protectwise/troika/compare/v0.47.2...v0.48.0) (2023-09-09)
|
||||
|
||||
**Note:** Version bump only for package troika-worker-utils
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.47.2](https://github.com/protectwise/troika/compare/v0.47.1...v0.47.2) (2023-05-15)
|
||||
|
||||
**Note:** Version bump only for package troika-worker-utils
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.47.0](https://github.com/protectwise/troika/compare/v0.46.3...v0.47.0) (2022-12-15)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* remove custom Thenable polyfill in favor of native promises ([7af402e](https://github.com/protectwise/troika/commit/7af402e254675ca2fc182467a65d2d4f860845e4))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.46.0](https://github.com/protectwise/troika/compare/v0.45.0...v0.46.0) (2022-03-05)
|
||||
|
||||
**Note:** Version bump only for package troika-worker-utils
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.45.0](https://github.com/protectwise/troika/compare/v0.44.0...v0.45.0) (2022-01-02)
|
||||
|
||||
**Note:** Version bump only for package troika-worker-utils
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.44.0](https://github.com/protectwise/troika/compare/v0.43.1-alpha.0...v0.44.0) (2021-11-14)
|
||||
|
||||
**Note:** Version bump only for package troika-worker-utils
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.43.0](https://github.com/protectwise/troika/compare/v0.42.0...v0.43.0) (2021-09-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remove warnings about many open worker module requests ([164fb8f](https://github.com/protectwise/troika/commit/164fb8fe836b95fffe7ca94fcf96d536c7281f7e)), closes [#156](https://github.com/protectwise/troika/issues/156)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **troika-worker-utils:** add a terminateWorker function ([33b8455](https://github.com/protectwise/troika/commit/33b8455c3794e13a342a7910d6c2c4beddd295da))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.42.0](https://github.com/protectwise/troika/compare/v0.41.2...v0.42.0) (2021-05-17)
|
||||
|
||||
**Note:** Version bump only for package troika-worker-utils
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.41.0](https://github.com/protectwise/troika/compare/v0.40.0...v0.41.0) (2021-04-19)
|
||||
|
||||
**Note:** Version bump only for package troika-worker-utils
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.40.0](https://github.com/protectwise/troika/compare/v0.39.2...v0.40.0) (2021-02-28)
|
||||
|
||||
**Note:** Version bump only for package troika-worker-utils
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.39.0](https://github.com/protectwise/troika/compare/v0.38.1...v0.39.0) (2021-02-15)
|
||||
|
||||
**Note:** Version bump only for package troika-worker-utils
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.38.1](https://github.com/protectwise/troika/compare/v0.38.0...v0.38.1) (2021-02-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **troika-worker-utils:** properly track open requests count ([a01d903](https://github.com/protectwise/troika/commit/a01d903245eee3b9798bcfac7397108fb3bb03e7))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.38.0](https://github.com/protectwise/troika/compare/v0.37.0...v0.38.0) (2021-01-24)
|
||||
|
||||
**Note:** Version bump only for package troika-worker-utils
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.37.0](https://github.com/protectwise/troika/compare/v0.36.1...v0.37.0) (2021-01-18)
|
||||
|
||||
**Note:** Version bump only for package troika-worker-utils
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.36.0](https://github.com/protectwise/troika/compare/v0.35.0...v0.36.0) (2020-12-04)
|
||||
|
||||
**Note:** Version bump only for package troika-worker-utils
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.35.0](https://github.com/protectwise/troika/compare/v0.34.2...v0.35.0) (2020-11-16)
|
||||
|
||||
**Note:** Version bump only for package troika-worker-utils
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.34.1](https://github.com/protectwise/troika/compare/v0.34.0...v0.34.1) (2020-10-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* check for process env 'test' ([4f7f8f2](https://github.com/protectwise/troika/commit/4f7f8f24a9d4f4b655b61e1e16e19061c1911b02))
|
||||
* check if process is undefined ([2b6d56a](https://github.com/protectwise/troika/commit/2b6d56af78e175a74fb03442efdc0a10d18fa4c8))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.34.0](https://github.com/protectwise/troika/compare/v0.33.1...v0.34.0) (2020-10-19)
|
||||
|
||||
**Note:** Version bump only for package troika-worker-utils
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.33.0](https://github.com/protectwise/troika/compare/v0.32.0...v0.33.0) (2020-10-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add "sideEffects":false to package.json files to assist treeshaking ([61109b2](https://github.com/protectwise/troika/commit/61109b2e3d21dc794ef66b3f28cf63bbdd34150e))
|
||||
* add PURE annotations to make troika-three-text treeshakeable ([8e76b5c](https://github.com/protectwise/troika/commit/8e76b5c31a3cbda86595654ba9d66d8d635e44a1))
|
||||
* remove redundant "browser" and defunct "jsnext:main" fields from package.json files ([0abec40](https://github.com/protectwise/troika/commit/0abec40e3af06d3ae4d990bf198d871b46730f1f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.32.0](https://github.com/protectwise/troika/compare/v0.31.0...v0.32.0) (2020-09-16)
|
||||
|
||||
**Note:** Version bump only for package troika-worker-utils
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.31.0](https://github.com/protectwise/troika/compare/v0.30.2...v0.31.0) (2020-08-11)
|
||||
|
||||
**Note:** Version bump only for package troika-worker-utils
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.30.0](https://github.com/protectwise/troika/compare/v0.29.0...v0.30.0) (2020-07-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **troika-worker-utils:** decrease main thread message level from warn to log ([d7cee6d](https://github.com/protectwise/troika/commit/d7cee6d534c6a01f9c3bc984015f7f824f0b458f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.29.0](https://github.com/protectwise/troika/compare/v0.28.1...v0.29.0) (2020-07-06)
|
||||
|
||||
**Note:** Version bump only for package troika-worker-utils
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.28.0](https://github.com/protectwise/troika/compare/v0.27.1...v0.28.0) (2020-06-09)
|
||||
|
||||
**Note:** Version bump only for package troika-worker-utils
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.27.0](https://github.com/protectwise/troika/compare/v0.26.1...v0.27.0) (2020-06-02)
|
||||
|
||||
**Note:** Version bump only for package troika-worker-utils
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.26.1](https://github.com/protectwise/troika/compare/v0.26.0...v0.26.1) (2020-05-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **troika-worker-modules:** silence fallback warning in non-browser environments ([3dedb8f](https://github.com/protectwise/troika/commit/3dedb8f2b338e9345c107831863152b115ca50d2))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.26.0](https://github.com/protectwise/troika/compare/v0.25.0...v0.26.0) (2020-05-24)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **troika-worker-utils:** add main thread fallback when web workers are not allowed ([c754d0b](https://github.com/protectwise/troika/commit/c754d0b2e716eadb2e478bb12fb2880d8a4ad63f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.25.0](https://github.com/protectwise/troika/compare/v0.24.1...v0.25.0) (2020-05-19)
|
||||
|
||||
**Note:** Version bump only for package troika-worker-utils
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.24.0](https://github.com/protectwise/troika/compare/v0.23.0...v0.24.0) (2020-04-27)
|
||||
|
||||
**Note:** Version bump only for package troika-worker-utils
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.23.0](https://github.com/protectwise/troika/compare/v0.22.0...v0.23.0) (2020-04-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **troika-worker-modules:** improve rehydration of functions in worker ([8f63090](https://github.com/protectwise/troika/commit/8f63090a5ad4fa3569faeade8e5c532ebfb065c5)), closes [#31](https://github.com/protectwise/troika/issues/31)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.22.0](https://github.com/protectwise/troika/compare/v0.21.0...v0.22.0) (2020-04-02)
|
||||
|
||||
**Note:** Version bump only for package troika-worker-utils
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.21.0](https://github.com/protectwise/troika/compare/v0.20.0...v0.21.0) (2020-03-27)
|
||||
|
||||
**Note:** Version bump only for package troika-worker-utils
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.20.0](https://github.com/protectwise/troika/compare/v0.19.0...v0.20.0) (2020-03-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **troika-worker-utils:** export function for stringifying functions ([977634b](https://github.com/protectwise/troika/commit/977634b5eecb41e4e7aa61addf5b7bfd721ab9e2))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.19.0](https://github.com/protectwise/troika/compare/v0.19.0-alpha.0...v0.19.0) (2020-02-28)
|
||||
|
||||
**Note:** Version bump only for package troika-worker-utils
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.18.0](https://github.com/protectwise/troika/compare/v0.17.1...v0.18.0) (2020-02-21)
|
||||
|
||||
**Note:** Version bump only for package troika-worker-utils
|
||||
22
node_modules/troika-worker-utils/LICENSE
generated
vendored
Normal file
22
node_modules/troika-worker-utils/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 ProtectWise
|
||||
Copyright (c) 2021 Jason Johnston
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
85
node_modules/troika-worker-utils/README.md
generated
vendored
Normal file
85
node_modules/troika-worker-utils/README.md
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
# `troika-worker-utils`
|
||||
|
||||
This package provides utilities for making Web Workers easier to use.
|
||||
|
||||
|
||||
## Worker Modules
|
||||
|
||||
Troika's "Worker Modules" system, exposed by the `defineWorkerModule` export, allows you to define a function that will be executed with a web worker. This provides a simple interface for moving chunks of logic off the main thread, which can be critical in WebGL/WebXR scenes where frame rate cannot be interrupted by long-running code.
|
||||
|
||||
Similar utilities like [Greenlet](https://github.com/developit/greenlet) have existed for a while. However, `defineWorkerModule` introduces the ability for worker modules to _depend on one another_. This means you can define modular chunks of code in separate functions, and then inject them into a worker where they can reference and call each other.
|
||||
|
||||
### `defineWorkerModule(options)`
|
||||
|
||||
This function defines a Worker Module. It takes an `options` object that can contain the following:
|
||||
|
||||
#### options.init
|
||||
|
||||
Required. This is the main function that initializes the module; it will be executed within the Worker the first time it is invoked. If any `dependencies` are defined, the resolved values of those dependencies will be passed in as arguments.
|
||||
|
||||
Its return value becomes the module's "value". That can be:
|
||||
|
||||
- A function, which can be called any number of times from the main thread by calling the function returned from `defineWorkerModule()`.
|
||||
|
||||
- Any other value, which will be used as the value passed to the `init` of other worker modules using it as a dependency.
|
||||
|
||||
> Note: As with any function-in-worker utility, the `init` function must not use any variables from the parent closure in which it is defined; its internal content must be completely standalone. Any external values you want to use must be passed in as `dependencies`.
|
||||
|
||||
#### options.dependencies
|
||||
|
||||
An optional array of dependencies required by the init function. Dependencies can be:
|
||||
|
||||
- Primitives like strings, numbers, booleans
|
||||
|
||||
- Functions; these will be stringified and rehydrated within the worker so they must not depend on anything from their parent closures
|
||||
|
||||
- Other worker modules created by `defineWorkerModule`; these will be resolved within the worker, and therefore modules that provide functions can be called without having to cross the worker/main thread boundary.
|
||||
|
||||
#### options.getTransferables
|
||||
|
||||
An optional function that will be run in the worker just before posting the response value from a module call back to the main thread. This function will be passed that response value, and if it returns an array then that will be used as the "transferables" parameter to `postMessage`. Use this if there are values in the response that can/should be transfered rather than cloned.
|
||||
|
||||
#### options.name
|
||||
|
||||
An optional descriptive name for this module; this can be useful for debugging (it will be inserted as a comment into the Blob sent to the worker) but is not currently used for anything else.
|
||||
|
||||
#### options.workerId
|
||||
|
||||
By default all modules will run in the same dedicated worker, but if you want to use multiple workers you can pass a `workerId` string to indicate a specific worker to spawn. Note that each worker is completely standalone and no data or state will be shared between them. If a worker module is used as a dependency by worker modules using different `workerId`s, then that dependency will be re-registered in each worker.
|
||||
|
||||
#### Return Value
|
||||
|
||||
The value returned by `defineWorkerModule` is a function. If your `options.init` returned a function, then this will be how you can invoke that within the worker. Call it, and it will give you a Promise for its return value.
|
||||
|
||||
### Contrived Example
|
||||
|
||||
```js
|
||||
import { defineWorkerModule } from 'troika-worker-utils'
|
||||
|
||||
// A simple module with a value:
|
||||
const workerModuleA = defineWorkerModule({
|
||||
init: function() {
|
||||
return Math.PI
|
||||
}
|
||||
})
|
||||
|
||||
// A module that depends on the previous module:
|
||||
const workerModuleB = defineWorkerModule({
|
||||
dependencies: [
|
||||
workerModuleA
|
||||
],
|
||||
init: function(moduleAValue) {
|
||||
// moduleAValue here is "I'm the value of Module A!" from the first init function
|
||||
|
||||
// This return function can be invoked by calling workerModuleB in the main thread:
|
||||
let callCount = 0
|
||||
return function(arg) {
|
||||
return `Called module B ${++callCount} times, `
|
||||
+ `with arg "${arg}". Module A's value was ${moduleAValue}.`
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
workerModuleB('foo') // "Called module B 1 times, with arg "foo". Module A's value was 3.141592653589793."
|
||||
workerModuleB('bar') // "Called module B 2 times, with arg "bar". Module A's value was 3.141592653589793."
|
||||
```
|
||||
327
node_modules/troika-worker-utils/__tests__/WorkerModules.test.js
generated
vendored
Normal file
327
node_modules/troika-worker-utils/__tests__/WorkerModules.test.js
generated
vendored
Normal file
@@ -0,0 +1,327 @@
|
||||
/*global require,global,_testDataBucket*/
|
||||
|
||||
require('./_jsdom-worker.js')
|
||||
const {defineWorkerModule} = require('../src/WorkerModules.js')
|
||||
|
||||
beforeEach(() => {
|
||||
// Functions that are run in the worker can place breadcrumbs in this object that
|
||||
// `expect` matchers can then retrieve and test later on.
|
||||
global._testDataBucket = {}
|
||||
})
|
||||
|
||||
|
||||
|
||||
test('requires an `options` argument', () => {
|
||||
expect(() => {
|
||||
defineWorkerModule()
|
||||
}).toThrow()
|
||||
})
|
||||
|
||||
test('requires an `init` function option', () => {
|
||||
expect(() => {
|
||||
defineWorkerModule({notInit: 'blerp'})
|
||||
}).toThrow()
|
||||
expect(() => {
|
||||
defineWorkerModule({init: 'not_a_function'})
|
||||
}).toThrow()
|
||||
expect(() => {
|
||||
defineWorkerModule({init: () => {}})
|
||||
}).not.toThrow()
|
||||
})
|
||||
|
||||
test('returns a function', () => {
|
||||
expect(typeof defineWorkerModule({init: () => {}})).toBe('function')
|
||||
})
|
||||
|
||||
describe('calling the module function', () => {
|
||||
test('throws if init did not return a function', () => {
|
||||
const moduleFn = defineWorkerModule({
|
||||
init: function() {
|
||||
return 'not a function'
|
||||
}
|
||||
})
|
||||
return expect(moduleFn()).rejects.toThrow()
|
||||
})
|
||||
|
||||
test('does not throw if init returns a function', () => {
|
||||
const moduleFn = defineWorkerModule({
|
||||
init: function() {
|
||||
return function() {}
|
||||
}
|
||||
})
|
||||
return expect(moduleFn()).resolves.toBe(undefined)
|
||||
})
|
||||
|
||||
test('returns a thenable', () => {
|
||||
const moduleFn = defineWorkerModule({
|
||||
init: function() {return function() {}}
|
||||
})
|
||||
expect(moduleFn()).toEqual(expect.objectContaining({then: expect.any(Function)}))
|
||||
})
|
||||
|
||||
test('invokes (a clone of) the init function', function() {
|
||||
const moduleFn = defineWorkerModule({
|
||||
init: function() {
|
||||
_testDataBucket.initWasCalled = true
|
||||
return function() {}
|
||||
}
|
||||
})
|
||||
return moduleFn().then(() => {
|
||||
expect(_testDataBucket.initWasCalled).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
test('invokes a function returned by the init function and resolves the thenable with its return value', () => {
|
||||
const moduleFn = defineWorkerModule({
|
||||
init: function() {
|
||||
return function() {
|
||||
_testDataBucket.initReturnFnWasCalled = true
|
||||
return 'this is the return value'
|
||||
}
|
||||
}
|
||||
})
|
||||
return moduleFn().then((result) => {
|
||||
expect(_testDataBucket.initReturnFnWasCalled).toBe(true)
|
||||
expect(result).toEqual('this is the return value')
|
||||
})
|
||||
})
|
||||
|
||||
test('waits for a thenable returned by the init function', () => {
|
||||
const moduleFn = defineWorkerModule({
|
||||
init: function() {
|
||||
return function() {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
resolve('promise resolution value')
|
||||
}, 50)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
return moduleFn().then((result) => {
|
||||
expect(result).toEqual('promise resolution value')
|
||||
})
|
||||
})
|
||||
|
||||
test('throws if the init function throws', () => {
|
||||
const moduleFn = defineWorkerModule({
|
||||
init: function() {
|
||||
const err = new Error('blerp')
|
||||
err.noLog = true
|
||||
throw err
|
||||
}
|
||||
})
|
||||
return expect(moduleFn()).rejects.toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
describe('dependencies', () => {
|
||||
test('are passed to the init function', () => {
|
||||
const moduleFn = defineWorkerModule({
|
||||
dependencies: [
|
||||
1,
|
||||
true,
|
||||
'three'
|
||||
],
|
||||
init: function(one, two, three) {
|
||||
expect(one).toBe(1)
|
||||
expect(two).toBe(true)
|
||||
expect(three).toBe('three')
|
||||
return function() {}
|
||||
}
|
||||
})
|
||||
return moduleFn()
|
||||
})
|
||||
|
||||
test('can be functions', () => {
|
||||
const moduleFn = defineWorkerModule({
|
||||
dependencies: [
|
||||
function() {
|
||||
_testDataBucket.firstDepFnCalled = true
|
||||
},
|
||||
function(arg1, arg2) {
|
||||
_testDataBucket.secondDepFnArgs = [arg1, arg2]
|
||||
}
|
||||
],
|
||||
init: function(dep1, dep2) {
|
||||
expect(typeof dep1).toBe('function')
|
||||
expect(typeof dep2).toBe('function')
|
||||
dep1()
|
||||
dep2('hey', 'jude')
|
||||
return function() {}
|
||||
}
|
||||
})
|
||||
return moduleFn().then(() => {
|
||||
expect(_testDataBucket.firstDepFnCalled).toBe(true)
|
||||
expect(_testDataBucket.secondDepFnArgs).toEqual(['hey', 'jude'])
|
||||
})
|
||||
})
|
||||
|
||||
test('can be other worker modules', () => {
|
||||
const depMod1 = defineWorkerModule({
|
||||
dependencies: [123, 456],
|
||||
init: function(n1, n2) {
|
||||
return n1 + n2
|
||||
}
|
||||
})
|
||||
const depMod2 = defineWorkerModule({
|
||||
dependencies: ['abc', 'xyz'],
|
||||
init: function(s1, s2) {
|
||||
return s2 + s1
|
||||
}
|
||||
})
|
||||
const depMod3 = defineWorkerModule({
|
||||
dependencies: [depMod1, depMod2],
|
||||
init: function(d1, d2) {
|
||||
return d1 + d2
|
||||
}
|
||||
})
|
||||
|
||||
const moduleFn = defineWorkerModule({
|
||||
dependencies: [
|
||||
depMod1,
|
||||
depMod2,
|
||||
depMod3
|
||||
],
|
||||
init: function(dep1, dep2, dep3) {
|
||||
expect(dep1).toEqual(579)
|
||||
expect(dep2).toEqual('xyzabc')
|
||||
expect(dep3).toEqual('579xyzabc')
|
||||
return function() {
|
||||
return [dep1, dep2, dep3]
|
||||
}
|
||||
}
|
||||
})
|
||||
return moduleFn().then((result) => {
|
||||
expect(result).toEqual([579, 'xyzabc', '579xyzabc'])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
describe('getTransferables', () => {
|
||||
test('is called before result is sent to main thread', () => {
|
||||
_testDataBucket.callOrder = []
|
||||
|
||||
const moduleFn = defineWorkerModule({
|
||||
init: function() {
|
||||
_testDataBucket.callOrder.push('init')
|
||||
return function() {
|
||||
_testDataBucket.callOrder.push('module')
|
||||
return {data: new Uint8Array(32)}
|
||||
}
|
||||
},
|
||||
getTransferables: function (result) {
|
||||
_testDataBucket.callOrder.push('getTransferables')
|
||||
return result.data
|
||||
}
|
||||
})
|
||||
|
||||
return moduleFn().then(() => {
|
||||
_testDataBucket.callOrder.push('response')
|
||||
}).then(() => {
|
||||
expect(_testDataBucket.callOrder).toEqual(['init', 'module', 'getTransferables', 'response'])
|
||||
})
|
||||
})
|
||||
|
||||
test('is called with the module return value as param', () => {
|
||||
const moduleFn = defineWorkerModule({
|
||||
init: function() {
|
||||
return function() {
|
||||
return {data: new Uint8Array(32)}
|
||||
}
|
||||
},
|
||||
getTransferables: function (result) {
|
||||
_testDataBucket.getTransferablesParam = result
|
||||
return result.data
|
||||
}
|
||||
})
|
||||
|
||||
return moduleFn().then(() => {
|
||||
expect(_testDataBucket.getTransferablesParam).toEqual({data: expect.any(Uint8Array)})
|
||||
})
|
||||
})
|
||||
|
||||
// TODO not working yet, see comment below
|
||||
test.skip('its return value is used as the `transferables` in postMessage', () => {
|
||||
const moduleFn = defineWorkerModule({
|
||||
init: function() {
|
||||
// Hack: override the worker's postMessage with one we can spy on
|
||||
// TODO this doesn't work, because `init` is invoked in the worker's global scope but
|
||||
// the WorkerModule framework code (that calls postMessage) is wrapped in a narrower
|
||||
// scope by the jsdom-worker polyfill. Will need to find a way around that before this
|
||||
// test can work.
|
||||
const _origPostMessage = postMessage
|
||||
// eslint-disable-next-line no-global-assign
|
||||
postMessage = function(e, transferList) {
|
||||
_testDataBucket.postMessageTransferList = transferList
|
||||
_origPostMessage(e)
|
||||
}
|
||||
|
||||
return function() {
|
||||
return {data: new Uint8Array(32)}
|
||||
}
|
||||
},
|
||||
getTransferables: function (result) {
|
||||
return [result.data]
|
||||
}
|
||||
})
|
||||
|
||||
return moduleFn().then(() => {
|
||||
expect(_testDataBucket.postMessageTransferList).toEqual([expect.any(Uint8Array)])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('multiple workers', () => {
|
||||
test('different `workerId`s execute in different workers', () => {
|
||||
|
||||
const commonDep = defineWorkerModule({
|
||||
init: function() {
|
||||
return Math.random() //give a unique value per worker
|
||||
}
|
||||
})
|
||||
|
||||
// 1a and 1b share a worker so their dependency should init with the same value;
|
||||
// 2 is a different worker so the dependency should init with a different value.
|
||||
const module1a = defineWorkerModule({
|
||||
workerId: 'worker1',
|
||||
dependencies: [commonDep],
|
||||
init: function(depVal) {
|
||||
return () => depVal
|
||||
}
|
||||
})
|
||||
const module1b = defineWorkerModule({
|
||||
workerId: 'worker1',
|
||||
dependencies: [commonDep],
|
||||
init: function(depVal) {
|
||||
return () => depVal
|
||||
}
|
||||
})
|
||||
const module2 = defineWorkerModule({
|
||||
workerId: 'worker2',
|
||||
dependencies: [commonDep],
|
||||
init: function(depVal) {
|
||||
return () => depVal
|
||||
}
|
||||
})
|
||||
const module3 = defineWorkerModule({
|
||||
workerId: null, //default worker
|
||||
dependencies: [commonDep],
|
||||
init: function(depVal) {
|
||||
return () => depVal
|
||||
}
|
||||
})
|
||||
|
||||
return Promise.all([module1a(), module1b(), module2(), module3()]).then(([result1a, result1b, result2, result3]) => {
|
||||
expect(typeof result1a).toEqual('number')
|
||||
expect(typeof result1b).toEqual('number')
|
||||
expect(typeof result2).toEqual('number')
|
||||
expect(typeof result3).toEqual('number')
|
||||
expect(result1a).toEqual(result1b)
|
||||
expect(result2).not.toEqual(result1a)
|
||||
expect(result3).not.toEqual(result1a)
|
||||
expect(result3).not.toEqual(result2)
|
||||
})
|
||||
})
|
||||
})
|
||||
135
node_modules/troika-worker-utils/__tests__/_jsdom-worker.js
generated
vendored
Normal file
135
node_modules/troika-worker-utils/__tests__/_jsdom-worker.js
generated
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
/*global global*/
|
||||
|
||||
/**
|
||||
* This is a forked version of the MIT-licensed jsdom-worker polyfill, taken from
|
||||
* https://github.com/developit/jsdom-worker/commit/4a400f72ec901fc926f0301ab4680f9f2c318439
|
||||
*
|
||||
* We keep a local forked copy because:
|
||||
* 1) That repo appears to be unmaintained and some of its commits have not made it to npm
|
||||
* 2) We need to patch in some extra support for things like importScripts.
|
||||
*
|
||||
* All edits are marked with [TROIKA EDIT] below.
|
||||
*/
|
||||
|
||||
import mitt from 'mitt';
|
||||
import uuid from 'uuid-v4';
|
||||
import fetch, { Response } from 'node-fetch';
|
||||
|
||||
if (!global.URL) global.URL = {};
|
||||
if (!global.URL.$$objects) {
|
||||
global.URL.$$objects = new Map();
|
||||
global.URL.createObjectURL = blob => {
|
||||
let id = uuid();
|
||||
global.URL.$$objects[id] = blob;
|
||||
return `blob:http://localhost/${id}`;
|
||||
};
|
||||
|
||||
// [TROIKA EDIT]: add revokeObjectURL as no-op:
|
||||
global.URL.revokeObjectURL = function() {}
|
||||
}
|
||||
|
||||
// [TROIKA EDIT]: `fetch` is overwritten with jsdom's version on each suite run, but
|
||||
// URL.$$objects hangs around between runs, so it wouldn't be re-patched. We move it
|
||||
// to its own conditional.
|
||||
if (!global.fetch || !global.fetch._patched) {
|
||||
let oldFetch = global.fetch || fetch;
|
||||
global.fetch = function(url, opts) {
|
||||
if (url.match(/^blob:/)) {
|
||||
return new Promise( (resolve, reject) => {
|
||||
let fr = new FileReader();
|
||||
fr.onload = () => {
|
||||
let Res = global.Response || Response;
|
||||
resolve(new Res(fr.result, { status: 200, statusText: 'OK' }));
|
||||
};
|
||||
fr.onerror = () => {
|
||||
reject(fr.error);
|
||||
};
|
||||
let id = url.match(/[^/]+$/)[0];
|
||||
fr.readAsText(global.URL.$$objects[id]);
|
||||
});
|
||||
}
|
||||
return oldFetch.call(this, url, opts);
|
||||
};
|
||||
global.fetch._patched = true
|
||||
}
|
||||
|
||||
// [TROIKA EDIT]: To enable a synchronous Blob read without jsdom support for FileReaderSync,
|
||||
// we override the Blob constructor to save a reference to its original text input.
|
||||
const blobContents = new WeakMap()
|
||||
const _Blob = global.Blob;
|
||||
global.Blob = function(...args) {
|
||||
let b = new _Blob(...args)
|
||||
blobContents.set(b, args[0])
|
||||
return b
|
||||
}
|
||||
|
||||
|
||||
if (!global.document) {
|
||||
global.document = {};
|
||||
}
|
||||
|
||||
function Event(type) { this.type = type; }
|
||||
Event.prototype.initEvent = Object;
|
||||
if (!global.document.createEvent) {
|
||||
global.document.createEvent = function(type) {
|
||||
let Ctor = global[type] || Event;
|
||||
return new Ctor(type);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
global.Worker = function Worker(url) {
|
||||
let messageQueue = [],
|
||||
inside = mitt(),
|
||||
outside = mitt(),
|
||||
scope = {
|
||||
onmessage: null,
|
||||
dispatchEvent: inside.emit,
|
||||
addEventListener: inside.on,
|
||||
removeEventListener: inside.off,
|
||||
postMessage(data) {
|
||||
outside.emit('message', { data });
|
||||
},
|
||||
fetch: global.fetch,
|
||||
importScripts(...urls) {
|
||||
// [TROIKA EDIT]: Implement importScripts, for text-based blob urls only for now:
|
||||
urls.forEach(url => {
|
||||
if (url.match(/^blob:/)) {
|
||||
let id = url.match(/[^/]+$/)[0];
|
||||
let code = blobContents.get(global.URL.$$objects[id]).join('');
|
||||
let vars = 'var self=this,global=self';
|
||||
for (let k in scope) vars += `,${k}=self.${k}`;
|
||||
new Function(vars+';\n'+code).call(scope);
|
||||
} else {
|
||||
throw Error('importScripts only supports blob urls for now.')
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
getScopeVar;
|
||||
inside.on('message', e => { let f = getScopeVar('onmessage'); if (f) f.call(scope, e); });
|
||||
this.addEventListener = outside.on;
|
||||
this.removeEventListener = outside.off;
|
||||
this.dispatchEvent = outside.emit;
|
||||
outside.on('message', e => { this.onmessage && this.onmessage(e); });
|
||||
this.postMessage = data => {
|
||||
if (messageQueue!=null) messageQueue.push(data);
|
||||
else inside.emit('message', { data });
|
||||
};
|
||||
this.terminate = () => {
|
||||
// [TROIKA EDIT]: don't throw.
|
||||
// throw Error('Not Supported');
|
||||
};
|
||||
global.fetch(url)
|
||||
.then( r => r.text() )
|
||||
.then( code => {
|
||||
let vars = 'var self=this,global=self';
|
||||
for (let k in scope) vars += `,${k}=self.${k}`;
|
||||
getScopeVar = eval('(function() {'+vars+';\n'+code+'\nreturn function(__){return eval(__)}})').call(scope);
|
||||
let q = messageQueue;
|
||||
messageQueue = null;
|
||||
q.forEach(this.postMessage);
|
||||
})
|
||||
.catch( e => { outside.emit('error', e); console.error(e); });
|
||||
};
|
||||
|
||||
424
node_modules/troika-worker-utils/dist/troika-worker-utils.esm.js
generated
vendored
Normal file
424
node_modules/troika-worker-utils/dist/troika-worker-utils.esm.js
generated
vendored
Normal file
@@ -0,0 +1,424 @@
|
||||
/**
|
||||
* Main content for the worker that handles the loading and execution of
|
||||
* modules within it.
|
||||
*/
|
||||
function workerBootstrap() {
|
||||
var modules = Object.create(null);
|
||||
|
||||
// Handle messages for registering a module
|
||||
function registerModule(ref, callback) {
|
||||
var id = ref.id;
|
||||
var name = ref.name;
|
||||
var dependencies = ref.dependencies; if ( dependencies === void 0 ) dependencies = [];
|
||||
var init = ref.init; if ( init === void 0 ) init = function(){};
|
||||
var getTransferables = ref.getTransferables; if ( getTransferables === void 0 ) getTransferables = null;
|
||||
|
||||
// Only register once
|
||||
if (modules[id]) { return }
|
||||
|
||||
try {
|
||||
// If any dependencies are modules, ensure they're registered and grab their value
|
||||
dependencies = dependencies.map(function (dep) {
|
||||
if (dep && dep.isWorkerModule) {
|
||||
registerModule(dep, function (depResult) {
|
||||
if (depResult instanceof Error) { throw depResult }
|
||||
});
|
||||
dep = modules[dep.id].value;
|
||||
}
|
||||
return dep
|
||||
});
|
||||
|
||||
// Rehydrate functions
|
||||
init = rehydrate(("<" + name + ">.init"), init);
|
||||
if (getTransferables) {
|
||||
getTransferables = rehydrate(("<" + name + ">.getTransferables"), getTransferables);
|
||||
}
|
||||
|
||||
// Initialize the module and store its value
|
||||
var value = null;
|
||||
if (typeof init === 'function') {
|
||||
value = init.apply(void 0, dependencies);
|
||||
} else {
|
||||
console.error('worker module init function failed to rehydrate');
|
||||
}
|
||||
modules[id] = {
|
||||
id: id,
|
||||
value: value,
|
||||
getTransferables: getTransferables
|
||||
};
|
||||
callback(value);
|
||||
} catch(err) {
|
||||
if (!(err && err.noLog)) {
|
||||
console.error(err);
|
||||
}
|
||||
callback(err);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle messages for calling a registered module's result function
|
||||
function callModule(ref, callback) {
|
||||
var ref$1;
|
||||
|
||||
var id = ref.id;
|
||||
var args = ref.args;
|
||||
if (!modules[id] || typeof modules[id].value !== 'function') {
|
||||
callback(new Error(("Worker module " + id + ": not found or its 'init' did not return a function")));
|
||||
}
|
||||
try {
|
||||
var result = (ref$1 = modules[id]).value.apply(ref$1, args);
|
||||
if (result && typeof result.then === 'function') {
|
||||
result.then(handleResult, function (rej) { return callback(rej instanceof Error ? rej : new Error('' + rej)); });
|
||||
} else {
|
||||
handleResult(result);
|
||||
}
|
||||
} catch(err) {
|
||||
callback(err);
|
||||
}
|
||||
function handleResult(result) {
|
||||
try {
|
||||
var tx = modules[id].getTransferables && modules[id].getTransferables(result);
|
||||
if (!tx || !Array.isArray(tx) || !tx.length) {
|
||||
tx = undefined; //postMessage is very picky about not passing null or empty transferables
|
||||
}
|
||||
callback(result, tx);
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
callback(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function rehydrate(name, str) {
|
||||
var result = void 0;
|
||||
self.troikaDefine = function (r) { return result = r; };
|
||||
var url = URL.createObjectURL(
|
||||
new Blob(
|
||||
[("/** " + (name.replace(/\*/g, '')) + " **/\n\ntroikaDefine(\n" + str + "\n)")],
|
||||
{type: 'application/javascript'}
|
||||
)
|
||||
);
|
||||
try {
|
||||
importScripts(url);
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
}
|
||||
URL.revokeObjectURL(url);
|
||||
delete self.troikaDefine;
|
||||
return result
|
||||
}
|
||||
|
||||
// Handler for all messages within the worker
|
||||
self.addEventListener('message', function (e) {
|
||||
var ref = e.data;
|
||||
var messageId = ref.messageId;
|
||||
var action = ref.action;
|
||||
var data = ref.data;
|
||||
try {
|
||||
// Module registration
|
||||
if (action === 'registerModule') {
|
||||
registerModule(data, function (result) {
|
||||
if (result instanceof Error) {
|
||||
postMessage({
|
||||
messageId: messageId,
|
||||
success: false,
|
||||
error: result.message
|
||||
});
|
||||
} else {
|
||||
postMessage({
|
||||
messageId: messageId,
|
||||
success: true,
|
||||
result: {isCallable: typeof result === 'function'}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
// Invocation
|
||||
if (action === 'callModule') {
|
||||
callModule(data, function (result, transferables) {
|
||||
if (result instanceof Error) {
|
||||
postMessage({
|
||||
messageId: messageId,
|
||||
success: false,
|
||||
error: result.message
|
||||
});
|
||||
} else {
|
||||
postMessage({
|
||||
messageId: messageId,
|
||||
success: true,
|
||||
result: result
|
||||
}, transferables || undefined);
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch(err) {
|
||||
postMessage({
|
||||
messageId: messageId,
|
||||
success: false,
|
||||
error: err.stack
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fallback for `defineWorkerModule` that behaves identically but runs in the main
|
||||
* thread, for when the execution environment doesn't support web workers or they
|
||||
* are disallowed due to e.g. CSP security restrictions.
|
||||
*/
|
||||
function defineMainThreadModule(options) {
|
||||
var moduleFunc = function() {
|
||||
var args = [], len = arguments.length;
|
||||
while ( len-- ) args[ len ] = arguments[ len ];
|
||||
|
||||
return moduleFunc._getInitResult().then(function (initResult) {
|
||||
if (typeof initResult === 'function') {
|
||||
return initResult.apply(void 0, args)
|
||||
} else {
|
||||
throw new Error('Worker module function was called but `init` did not return a callable function')
|
||||
}
|
||||
})
|
||||
};
|
||||
moduleFunc._getInitResult = function() {
|
||||
// We can ignore getTransferables in main thread. TODO workerId?
|
||||
var dependencies = options.dependencies;
|
||||
var init = options.init;
|
||||
|
||||
// Resolve dependencies
|
||||
dependencies = Array.isArray(dependencies) ? dependencies.map(function (dep) {
|
||||
if (dep) {
|
||||
// If it's a worker module, use its main thread impl
|
||||
dep = dep.onMainThread || dep;
|
||||
// If it's a main thread worker module, use its init return value
|
||||
if (dep._getInitResult) {
|
||||
dep = dep._getInitResult();
|
||||
}
|
||||
}
|
||||
return dep
|
||||
}) : [];
|
||||
|
||||
// Invoke init with the resolved dependencies
|
||||
var initPromise = Promise.all(dependencies).then(function (deps) {
|
||||
return init.apply(null, deps)
|
||||
});
|
||||
|
||||
// Cache the resolved promise for subsequent calls
|
||||
moduleFunc._getInitResult = function () { return initPromise; };
|
||||
|
||||
return initPromise
|
||||
};
|
||||
return moduleFunc
|
||||
}
|
||||
|
||||
var supportsWorkers = function () {
|
||||
var supported = false;
|
||||
|
||||
// Only attempt worker initialization in browsers; elsewhere it would just be
|
||||
// noise e.g. loading into a Node environment for SSR.
|
||||
if (typeof window !== 'undefined' && typeof window.document !== 'undefined') {
|
||||
try {
|
||||
// TODO additional checks for things like importScripts within the worker?
|
||||
// Would need to be an async check.
|
||||
var worker = new Worker(
|
||||
URL.createObjectURL(new Blob([''], { type: 'application/javascript' }))
|
||||
);
|
||||
worker.terminate();
|
||||
supported = true;
|
||||
} catch (err) {
|
||||
if (typeof process !== 'undefined' && process.env.NODE_ENV === 'test') ; else {
|
||||
console.log(
|
||||
("Troika createWorkerModule: web workers not allowed; falling back to main thread execution. Cause: [" + (err.message) + "]")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cached result
|
||||
supportsWorkers = function () { return supported; };
|
||||
return supported
|
||||
};
|
||||
|
||||
var _workerModuleId = 0;
|
||||
var _messageId = 0;
|
||||
var _allowInitAsString = false;
|
||||
var workers = Object.create(null);
|
||||
var registeredModules = Object.create(null); //workerId -> Set<unregisterFn>
|
||||
var openRequests = Object.create(null);
|
||||
|
||||
|
||||
/**
|
||||
* Define a module of code that will be executed with a web worker. This provides a simple
|
||||
* interface for moving chunks of logic off the main thread, and managing their dependencies
|
||||
* among one another.
|
||||
*
|
||||
* @param {object} options
|
||||
* @param {function} options.init
|
||||
* @param {array} [options.dependencies]
|
||||
* @param {function} [options.getTransferables]
|
||||
* @param {string} [options.name]
|
||||
* @param {string} [options.workerId]
|
||||
* @return {function(...[*]): {then}}
|
||||
*/
|
||||
function defineWorkerModule(options) {
|
||||
if ((!options || typeof options.init !== 'function') && !_allowInitAsString) {
|
||||
throw new Error('requires `options.init` function')
|
||||
}
|
||||
var dependencies = options.dependencies;
|
||||
var init = options.init;
|
||||
var getTransferables = options.getTransferables;
|
||||
var workerId = options.workerId;
|
||||
|
||||
var onMainThread = defineMainThreadModule(options);
|
||||
|
||||
if (workerId == null) {
|
||||
workerId = '#default';
|
||||
}
|
||||
var id = "workerModule" + (++_workerModuleId);
|
||||
var name = options.name || id;
|
||||
var registrationPromise = null;
|
||||
|
||||
dependencies = dependencies && dependencies.map(function (dep) {
|
||||
// Wrap raw functions as worker modules with no dependencies
|
||||
if (typeof dep === 'function' && !dep.workerModuleData) {
|
||||
_allowInitAsString = true;
|
||||
dep = defineWorkerModule({
|
||||
workerId: workerId,
|
||||
name: ("<" + name + "> function dependency: " + (dep.name)),
|
||||
init: ("function(){return (\n" + (stringifyFunction(dep)) + "\n)}")
|
||||
});
|
||||
_allowInitAsString = false;
|
||||
}
|
||||
// Grab postable data for worker modules
|
||||
if (dep && dep.workerModuleData) {
|
||||
dep = dep.workerModuleData;
|
||||
}
|
||||
return dep
|
||||
});
|
||||
|
||||
function moduleFunc() {
|
||||
var args = [], len = arguments.length;
|
||||
while ( len-- ) args[ len ] = arguments[ len ];
|
||||
|
||||
if (!supportsWorkers()) {
|
||||
return onMainThread.apply(void 0, args)
|
||||
}
|
||||
|
||||
// Register this module if needed
|
||||
if (!registrationPromise) {
|
||||
registrationPromise = callWorker(workerId,'registerModule', moduleFunc.workerModuleData);
|
||||
var unregister = function () {
|
||||
registrationPromise = null;
|
||||
registeredModules[workerId].delete(unregister);
|
||||
}
|
||||
;(registeredModules[workerId] || (registeredModules[workerId] = new Set())).add(unregister);
|
||||
}
|
||||
|
||||
// Invoke the module, returning a promise
|
||||
return registrationPromise.then(function (ref) {
|
||||
var isCallable = ref.isCallable;
|
||||
|
||||
if (isCallable) {
|
||||
return callWorker(workerId,'callModule', {id: id, args: args})
|
||||
} else {
|
||||
throw new Error('Worker module function was called but `init` did not return a callable function')
|
||||
}
|
||||
})
|
||||
}
|
||||
moduleFunc.workerModuleData = {
|
||||
isWorkerModule: true,
|
||||
id: id,
|
||||
name: name,
|
||||
dependencies: dependencies,
|
||||
init: stringifyFunction(init),
|
||||
getTransferables: getTransferables && stringifyFunction(getTransferables)
|
||||
};
|
||||
|
||||
moduleFunc.onMainThread = onMainThread;
|
||||
|
||||
return moduleFunc
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminate an active Worker by a workerId that was passed to defineWorkerModule.
|
||||
* This only terminates the Worker itself; the worker module will remain available
|
||||
* and if you call it again its Worker will be respawned.
|
||||
* @param {string} workerId
|
||||
*/
|
||||
function terminateWorker(workerId) {
|
||||
// Unregister all modules that were registered in that worker
|
||||
if (registeredModules[workerId]) {
|
||||
registeredModules[workerId].forEach(function (unregister) {
|
||||
unregister();
|
||||
});
|
||||
}
|
||||
// Terminate the Worker object
|
||||
if (workers[workerId]) {
|
||||
workers[workerId].terminate();
|
||||
delete workers[workerId];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stringifies a function into a form that can be deserialized in the worker
|
||||
* @param fn
|
||||
*/
|
||||
function stringifyFunction(fn) {
|
||||
var str = fn.toString();
|
||||
// If it was defined in object method/property format, it needs to be modified
|
||||
if (!/^function/.test(str) && /^\w+\s*\(/.test(str)) {
|
||||
str = 'function ' + str;
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
|
||||
function getWorker(workerId) {
|
||||
var worker = workers[workerId];
|
||||
if (!worker) {
|
||||
// Bootstrap the worker's content
|
||||
var bootstrap = stringifyFunction(workerBootstrap);
|
||||
|
||||
// Create the worker from the bootstrap function content
|
||||
worker = workers[workerId] = new Worker(
|
||||
URL.createObjectURL(
|
||||
new Blob(
|
||||
[("/** Worker Module Bootstrap: " + (workerId.replace(/\*/g, '')) + " **/\n\n;(" + bootstrap + ")()")],
|
||||
{type: 'application/javascript'}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
// Single handler for response messages from the worker
|
||||
worker.onmessage = function (e) {
|
||||
var response = e.data;
|
||||
var msgId = response.messageId;
|
||||
var callback = openRequests[msgId];
|
||||
if (!callback) {
|
||||
throw new Error('WorkerModule response with empty or unknown messageId')
|
||||
}
|
||||
delete openRequests[msgId];
|
||||
callback(response);
|
||||
};
|
||||
}
|
||||
return worker
|
||||
}
|
||||
|
||||
// Issue a call to the worker with a callback to handle the response
|
||||
function callWorker(workerId, action, data) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var messageId = ++_messageId;
|
||||
openRequests[messageId] = function (response) {
|
||||
if (response.success) {
|
||||
resolve(response.result);
|
||||
} else {
|
||||
reject(new Error(("Error in worker " + action + " call: " + (response.error))));
|
||||
}
|
||||
};
|
||||
getWorker(workerId).postMessage({
|
||||
messageId: messageId,
|
||||
action: action,
|
||||
data: data
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
export { defineWorkerModule, stringifyFunction, terminateWorker };
|
||||
436
node_modules/troika-worker-utils/dist/troika-worker-utils.umd.js
generated
vendored
Normal file
436
node_modules/troika-worker-utils/dist/troika-worker-utils.umd.js
generated
vendored
Normal file
@@ -0,0 +1,436 @@
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
||||
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.troika_worker_utils = {}));
|
||||
}(this, (function (exports) { 'use strict';
|
||||
|
||||
/**
|
||||
* Main content for the worker that handles the loading and execution of
|
||||
* modules within it.
|
||||
*/
|
||||
function workerBootstrap() {
|
||||
var modules = Object.create(null);
|
||||
|
||||
// Handle messages for registering a module
|
||||
function registerModule(ref, callback) {
|
||||
var id = ref.id;
|
||||
var name = ref.name;
|
||||
var dependencies = ref.dependencies; if ( dependencies === void 0 ) dependencies = [];
|
||||
var init = ref.init; if ( init === void 0 ) init = function(){};
|
||||
var getTransferables = ref.getTransferables; if ( getTransferables === void 0 ) getTransferables = null;
|
||||
|
||||
// Only register once
|
||||
if (modules[id]) { return }
|
||||
|
||||
try {
|
||||
// If any dependencies are modules, ensure they're registered and grab their value
|
||||
dependencies = dependencies.map(function (dep) {
|
||||
if (dep && dep.isWorkerModule) {
|
||||
registerModule(dep, function (depResult) {
|
||||
if (depResult instanceof Error) { throw depResult }
|
||||
});
|
||||
dep = modules[dep.id].value;
|
||||
}
|
||||
return dep
|
||||
});
|
||||
|
||||
// Rehydrate functions
|
||||
init = rehydrate(("<" + name + ">.init"), init);
|
||||
if (getTransferables) {
|
||||
getTransferables = rehydrate(("<" + name + ">.getTransferables"), getTransferables);
|
||||
}
|
||||
|
||||
// Initialize the module and store its value
|
||||
var value = null;
|
||||
if (typeof init === 'function') {
|
||||
value = init.apply(void 0, dependencies);
|
||||
} else {
|
||||
console.error('worker module init function failed to rehydrate');
|
||||
}
|
||||
modules[id] = {
|
||||
id: id,
|
||||
value: value,
|
||||
getTransferables: getTransferables
|
||||
};
|
||||
callback(value);
|
||||
} catch(err) {
|
||||
if (!(err && err.noLog)) {
|
||||
console.error(err);
|
||||
}
|
||||
callback(err);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle messages for calling a registered module's result function
|
||||
function callModule(ref, callback) {
|
||||
var ref$1;
|
||||
|
||||
var id = ref.id;
|
||||
var args = ref.args;
|
||||
if (!modules[id] || typeof modules[id].value !== 'function') {
|
||||
callback(new Error(("Worker module " + id + ": not found or its 'init' did not return a function")));
|
||||
}
|
||||
try {
|
||||
var result = (ref$1 = modules[id]).value.apply(ref$1, args);
|
||||
if (result && typeof result.then === 'function') {
|
||||
result.then(handleResult, function (rej) { return callback(rej instanceof Error ? rej : new Error('' + rej)); });
|
||||
} else {
|
||||
handleResult(result);
|
||||
}
|
||||
} catch(err) {
|
||||
callback(err);
|
||||
}
|
||||
function handleResult(result) {
|
||||
try {
|
||||
var tx = modules[id].getTransferables && modules[id].getTransferables(result);
|
||||
if (!tx || !Array.isArray(tx) || !tx.length) {
|
||||
tx = undefined; //postMessage is very picky about not passing null or empty transferables
|
||||
}
|
||||
callback(result, tx);
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
callback(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function rehydrate(name, str) {
|
||||
var result = void 0;
|
||||
self.troikaDefine = function (r) { return result = r; };
|
||||
var url = URL.createObjectURL(
|
||||
new Blob(
|
||||
[("/** " + (name.replace(/\*/g, '')) + " **/\n\ntroikaDefine(\n" + str + "\n)")],
|
||||
{type: 'application/javascript'}
|
||||
)
|
||||
);
|
||||
try {
|
||||
importScripts(url);
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
}
|
||||
URL.revokeObjectURL(url);
|
||||
delete self.troikaDefine;
|
||||
return result
|
||||
}
|
||||
|
||||
// Handler for all messages within the worker
|
||||
self.addEventListener('message', function (e) {
|
||||
var ref = e.data;
|
||||
var messageId = ref.messageId;
|
||||
var action = ref.action;
|
||||
var data = ref.data;
|
||||
try {
|
||||
// Module registration
|
||||
if (action === 'registerModule') {
|
||||
registerModule(data, function (result) {
|
||||
if (result instanceof Error) {
|
||||
postMessage({
|
||||
messageId: messageId,
|
||||
success: false,
|
||||
error: result.message
|
||||
});
|
||||
} else {
|
||||
postMessage({
|
||||
messageId: messageId,
|
||||
success: true,
|
||||
result: {isCallable: typeof result === 'function'}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
// Invocation
|
||||
if (action === 'callModule') {
|
||||
callModule(data, function (result, transferables) {
|
||||
if (result instanceof Error) {
|
||||
postMessage({
|
||||
messageId: messageId,
|
||||
success: false,
|
||||
error: result.message
|
||||
});
|
||||
} else {
|
||||
postMessage({
|
||||
messageId: messageId,
|
||||
success: true,
|
||||
result: result
|
||||
}, transferables || undefined);
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch(err) {
|
||||
postMessage({
|
||||
messageId: messageId,
|
||||
success: false,
|
||||
error: err.stack
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fallback for `defineWorkerModule` that behaves identically but runs in the main
|
||||
* thread, for when the execution environment doesn't support web workers or they
|
||||
* are disallowed due to e.g. CSP security restrictions.
|
||||
*/
|
||||
function defineMainThreadModule(options) {
|
||||
var moduleFunc = function() {
|
||||
var args = [], len = arguments.length;
|
||||
while ( len-- ) args[ len ] = arguments[ len ];
|
||||
|
||||
return moduleFunc._getInitResult().then(function (initResult) {
|
||||
if (typeof initResult === 'function') {
|
||||
return initResult.apply(void 0, args)
|
||||
} else {
|
||||
throw new Error('Worker module function was called but `init` did not return a callable function')
|
||||
}
|
||||
})
|
||||
};
|
||||
moduleFunc._getInitResult = function() {
|
||||
// We can ignore getTransferables in main thread. TODO workerId?
|
||||
var dependencies = options.dependencies;
|
||||
var init = options.init;
|
||||
|
||||
// Resolve dependencies
|
||||
dependencies = Array.isArray(dependencies) ? dependencies.map(function (dep) {
|
||||
if (dep) {
|
||||
// If it's a worker module, use its main thread impl
|
||||
dep = dep.onMainThread || dep;
|
||||
// If it's a main thread worker module, use its init return value
|
||||
if (dep._getInitResult) {
|
||||
dep = dep._getInitResult();
|
||||
}
|
||||
}
|
||||
return dep
|
||||
}) : [];
|
||||
|
||||
// Invoke init with the resolved dependencies
|
||||
var initPromise = Promise.all(dependencies).then(function (deps) {
|
||||
return init.apply(null, deps)
|
||||
});
|
||||
|
||||
// Cache the resolved promise for subsequent calls
|
||||
moduleFunc._getInitResult = function () { return initPromise; };
|
||||
|
||||
return initPromise
|
||||
};
|
||||
return moduleFunc
|
||||
}
|
||||
|
||||
var supportsWorkers = function () {
|
||||
var supported = false;
|
||||
|
||||
// Only attempt worker initialization in browsers; elsewhere it would just be
|
||||
// noise e.g. loading into a Node environment for SSR.
|
||||
if (typeof window !== 'undefined' && typeof window.document !== 'undefined') {
|
||||
try {
|
||||
// TODO additional checks for things like importScripts within the worker?
|
||||
// Would need to be an async check.
|
||||
var worker = new Worker(
|
||||
URL.createObjectURL(new Blob([''], { type: 'application/javascript' }))
|
||||
);
|
||||
worker.terminate();
|
||||
supported = true;
|
||||
} catch (err) {
|
||||
if (typeof process !== 'undefined' && process.env.NODE_ENV === 'test') ; else {
|
||||
console.log(
|
||||
("Troika createWorkerModule: web workers not allowed; falling back to main thread execution. Cause: [" + (err.message) + "]")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cached result
|
||||
supportsWorkers = function () { return supported; };
|
||||
return supported
|
||||
};
|
||||
|
||||
var _workerModuleId = 0;
|
||||
var _messageId = 0;
|
||||
var _allowInitAsString = false;
|
||||
var workers = Object.create(null);
|
||||
var registeredModules = Object.create(null); //workerId -> Set<unregisterFn>
|
||||
var openRequests = Object.create(null);
|
||||
|
||||
|
||||
/**
|
||||
* Define a module of code that will be executed with a web worker. This provides a simple
|
||||
* interface for moving chunks of logic off the main thread, and managing their dependencies
|
||||
* among one another.
|
||||
*
|
||||
* @param {object} options
|
||||
* @param {function} options.init
|
||||
* @param {array} [options.dependencies]
|
||||
* @param {function} [options.getTransferables]
|
||||
* @param {string} [options.name]
|
||||
* @param {string} [options.workerId]
|
||||
* @return {function(...[*]): {then}}
|
||||
*/
|
||||
function defineWorkerModule(options) {
|
||||
if ((!options || typeof options.init !== 'function') && !_allowInitAsString) {
|
||||
throw new Error('requires `options.init` function')
|
||||
}
|
||||
var dependencies = options.dependencies;
|
||||
var init = options.init;
|
||||
var getTransferables = options.getTransferables;
|
||||
var workerId = options.workerId;
|
||||
|
||||
var onMainThread = defineMainThreadModule(options);
|
||||
|
||||
if (workerId == null) {
|
||||
workerId = '#default';
|
||||
}
|
||||
var id = "workerModule" + (++_workerModuleId);
|
||||
var name = options.name || id;
|
||||
var registrationPromise = null;
|
||||
|
||||
dependencies = dependencies && dependencies.map(function (dep) {
|
||||
// Wrap raw functions as worker modules with no dependencies
|
||||
if (typeof dep === 'function' && !dep.workerModuleData) {
|
||||
_allowInitAsString = true;
|
||||
dep = defineWorkerModule({
|
||||
workerId: workerId,
|
||||
name: ("<" + name + "> function dependency: " + (dep.name)),
|
||||
init: ("function(){return (\n" + (stringifyFunction(dep)) + "\n)}")
|
||||
});
|
||||
_allowInitAsString = false;
|
||||
}
|
||||
// Grab postable data for worker modules
|
||||
if (dep && dep.workerModuleData) {
|
||||
dep = dep.workerModuleData;
|
||||
}
|
||||
return dep
|
||||
});
|
||||
|
||||
function moduleFunc() {
|
||||
var args = [], len = arguments.length;
|
||||
while ( len-- ) args[ len ] = arguments[ len ];
|
||||
|
||||
if (!supportsWorkers()) {
|
||||
return onMainThread.apply(void 0, args)
|
||||
}
|
||||
|
||||
// Register this module if needed
|
||||
if (!registrationPromise) {
|
||||
registrationPromise = callWorker(workerId,'registerModule', moduleFunc.workerModuleData);
|
||||
var unregister = function () {
|
||||
registrationPromise = null;
|
||||
registeredModules[workerId].delete(unregister);
|
||||
}
|
||||
;(registeredModules[workerId] || (registeredModules[workerId] = new Set())).add(unregister);
|
||||
}
|
||||
|
||||
// Invoke the module, returning a promise
|
||||
return registrationPromise.then(function (ref) {
|
||||
var isCallable = ref.isCallable;
|
||||
|
||||
if (isCallable) {
|
||||
return callWorker(workerId,'callModule', {id: id, args: args})
|
||||
} else {
|
||||
throw new Error('Worker module function was called but `init` did not return a callable function')
|
||||
}
|
||||
})
|
||||
}
|
||||
moduleFunc.workerModuleData = {
|
||||
isWorkerModule: true,
|
||||
id: id,
|
||||
name: name,
|
||||
dependencies: dependencies,
|
||||
init: stringifyFunction(init),
|
||||
getTransferables: getTransferables && stringifyFunction(getTransferables)
|
||||
};
|
||||
|
||||
moduleFunc.onMainThread = onMainThread;
|
||||
|
||||
return moduleFunc
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminate an active Worker by a workerId that was passed to defineWorkerModule.
|
||||
* This only terminates the Worker itself; the worker module will remain available
|
||||
* and if you call it again its Worker will be respawned.
|
||||
* @param {string} workerId
|
||||
*/
|
||||
function terminateWorker(workerId) {
|
||||
// Unregister all modules that were registered in that worker
|
||||
if (registeredModules[workerId]) {
|
||||
registeredModules[workerId].forEach(function (unregister) {
|
||||
unregister();
|
||||
});
|
||||
}
|
||||
// Terminate the Worker object
|
||||
if (workers[workerId]) {
|
||||
workers[workerId].terminate();
|
||||
delete workers[workerId];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stringifies a function into a form that can be deserialized in the worker
|
||||
* @param fn
|
||||
*/
|
||||
function stringifyFunction(fn) {
|
||||
var str = fn.toString();
|
||||
// If it was defined in object method/property format, it needs to be modified
|
||||
if (!/^function/.test(str) && /^\w+\s*\(/.test(str)) {
|
||||
str = 'function ' + str;
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
|
||||
function getWorker(workerId) {
|
||||
var worker = workers[workerId];
|
||||
if (!worker) {
|
||||
// Bootstrap the worker's content
|
||||
var bootstrap = stringifyFunction(workerBootstrap);
|
||||
|
||||
// Create the worker from the bootstrap function content
|
||||
worker = workers[workerId] = new Worker(
|
||||
URL.createObjectURL(
|
||||
new Blob(
|
||||
[("/** Worker Module Bootstrap: " + (workerId.replace(/\*/g, '')) + " **/\n\n;(" + bootstrap + ")()")],
|
||||
{type: 'application/javascript'}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
// Single handler for response messages from the worker
|
||||
worker.onmessage = function (e) {
|
||||
var response = e.data;
|
||||
var msgId = response.messageId;
|
||||
var callback = openRequests[msgId];
|
||||
if (!callback) {
|
||||
throw new Error('WorkerModule response with empty or unknown messageId')
|
||||
}
|
||||
delete openRequests[msgId];
|
||||
callback(response);
|
||||
};
|
||||
}
|
||||
return worker
|
||||
}
|
||||
|
||||
// Issue a call to the worker with a callback to handle the response
|
||||
function callWorker(workerId, action, data) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var messageId = ++_messageId;
|
||||
openRequests[messageId] = function (response) {
|
||||
if (response.success) {
|
||||
resolve(response.result);
|
||||
} else {
|
||||
reject(new Error(("Error in worker " + action + " call: " + (response.error))));
|
||||
}
|
||||
};
|
||||
getWorker(workerId).postMessage({
|
||||
messageId: messageId,
|
||||
action: action,
|
||||
data: data
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
exports.defineWorkerModule = defineWorkerModule;
|
||||
exports.stringifyFunction = stringifyFunction;
|
||||
exports.terminateWorker = terminateWorker;
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
})));
|
||||
10
node_modules/troika-worker-utils/dist/troika-worker-utils.umd.min.js
generated
vendored
Normal file
10
node_modules/troika-worker-utils/dist/troika-worker-utils.umd.min.js
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
'use strict';(function(k,n){"object"===typeof exports&&"undefined"!==typeof module?n(exports):"function"===typeof define&&define.amd?define(["exports"],n):(k="undefined"!==typeof globalThis?globalThis:k||self,n(k.troika_worker_utils={}))})(this,function(k){function n(){function b(a,d){var c=a.id,h=a.name,l=a.dependencies;void 0===l&&(l=[]);var g=a.init;void 0===g&&(g=function(){});a=a.getTransferables;void 0===a&&(a=null);if(!f[c])try{l=l.map(function(a){a&&a.isWorkerModule&&(b(a,function(a){if(a instanceof
|
||||
Error)throw a;}),a=f[a.id].value);return a}),g=e("<"+h+">.init",g),a&&(a=e("<"+h+">.getTransferables",a)),h=null,"function"===typeof g?h=g.apply(void 0,l):console.error("worker module init function failed to rehydrate"),f[c]={id:c,value:h,getTransferables:a},d(h)}catch(m){m&&m.noLog||console.error(m),d(m)}}function c(a,d){function b(a){try{var b=f[e].getTransferables&&f[e].getTransferables(a);b&&Array.isArray(b)&&b.length||(b=void 0);d(a,b)}catch(v){console.error(v),d(v)}}var c,e=a.id;a=a.args;f[e]&&
|
||||
"function"===typeof f[e].value||d(Error("Worker module "+e+": not found or its 'init' did not return a function"));try{var g=(c=f[e]).value.apply(c,a);g&&"function"===typeof g.then?g.then(b,function(a){return d(a instanceof Error?a:Error(""+a))}):b(g)}catch(m){d(m)}}function e(a,b){var d=void 0;self.troikaDefine=function(a){return d=a};a=URL.createObjectURL(new Blob(["/** "+a.replace(/\*/g,"")+" **/\n\ntroikaDefine(\n"+b+"\n)"],{type:"application/javascript"}));try{importScripts(a)}catch(h){console.error(h)}URL.revokeObjectURL(a);
|
||||
delete self.troikaDefine;return d}var f=Object.create(null);self.addEventListener("message",function(a){var d=a.data,e=d.messageId;a=d.action;d=d.data;try{"registerModule"===a&&b(d,function(a){a instanceof Error?postMessage({messageId:e,success:!1,error:a.message}):postMessage({messageId:e,success:!0,result:{isCallable:"function"===typeof a}})}),"callModule"===a&&c(d,function(a,b){a instanceof Error?postMessage({messageId:e,success:!1,error:a.message}):postMessage({messageId:e,success:!0,result:a},
|
||||
b||void 0)})}catch(h){postMessage({messageId:e,success:!1,error:h.stack})}})}function z(b){var c=function(){for(var b=[],f=arguments.length;f--;)b[f]=arguments[f];return c._getInitResult().then(function(a){if("function"===typeof a)return a.apply(void 0,b);throw Error("Worker module function was called but `init` did not return a callable function");})};c._getInitResult=function(){var e=b.dependencies,f=b.init;e=Array.isArray(e)?e.map(function(a){a&&(a=a.onMainThread||a,a._getInitResult&&(a=a._getInitResult()));
|
||||
return a}):[];var a=Promise.all(e).then(function(a){return f.apply(null,a)});c._getInitResult=function(){return a};return a};return c}function w(b){function c(){for(var a=[],b=arguments.length;b--;)a[b]=arguments[b];if(!x())return k.apply(void 0,a);if(!g){g=y(d,"registerModule",c.workerModuleData);var e=function(){g=null;p[d].delete(e)};(p[d]||(p[d]=new Set)).add(e)}return g.then(function(b){if(b.isCallable)return y(d,"callModule",{id:h,args:a});throw Error("Worker module function was called but `init` did not return a callable function");
|
||||
})}if(!(b&&"function"===typeof b.init||t))throw Error("requires `options.init` function");var e=b.dependencies,f=b.init,a=b.getTransferables,d=b.workerId,k=z(b);null==d&&(d="#default");var h="workerModule"+ ++A,l=b.name||h,g=null;e=e&&e.map(function(a){"function"!==typeof a||a.workerModuleData||(t=!0,a=w({workerId:d,name:"<"+l+"> function dependency: "+a.name,init:"function(){return (\n"+q(a)+"\n)}"}),t=!1);a&&a.workerModuleData&&(a=a.workerModuleData);return a});c.workerModuleData={isWorkerModule:!0,
|
||||
id:h,name:l,dependencies:e,init:q(f),getTransferables:a&&q(a)};c.onMainThread=k;return c}function q(b){b=b.toString();!/^function/.test(b)&&/^\w+\s*\(/.test(b)&&(b="function "+b);return b}function B(b){var c=r[b];c||(c=q(n),c=r[b]=new Worker(URL.createObjectURL(new Blob(["/** Worker Module Bootstrap: "+b.replace(/\*/g,"")+" **/\n\n;("+c+")()"],{type:"application/javascript"}))),c.onmessage=function(b){b=b.data;var c=b.messageId,a=u[c];if(!a)throw Error("WorkerModule response with empty or unknown messageId");
|
||||
delete u[c];a(b)});return c}function y(b,c,e){return new Promise(function(f,a){var d=++C;u[d]=function(b){b.success?f(b.result):a(Error("Error in worker "+c+" call: "+b.error))};B(b).postMessage({messageId:d,action:c,data:e})})}var x=function(){var b=!1;if("undefined"!==typeof window&&"undefined"!==typeof window.document)try{(new Worker(URL.createObjectURL(new Blob([""],{type:"application/javascript"})))).terminate(),b=!0}catch(c){"undefined"!==typeof process&&"test"===process.env.NODE_ENV||console.log("Troika createWorkerModule: web workers not allowed; falling back to main thread execution. Cause: ["+
|
||||
c.message+"]")}x=function(){return b};return b},A=0,C=0,t=!1,r=Object.create(null),p=Object.create(null),u=Object.create(null);k.defineWorkerModule=w;k.stringifyFunction=q;k.terminateWorker=function(b){p[b]&&p[b].forEach(function(b){b()});r[b]&&(r[b].terminate(),delete r[b])};Object.defineProperty(k,"__esModule",{value:!0})})
|
||||
17
node_modules/troika-worker-utils/package.json
generated
vendored
Normal file
17
node_modules/troika-worker-utils/package.json
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "troika-worker-utils",
|
||||
"version": "0.52.0",
|
||||
"description": "Utilities for executing code in Web Workers",
|
||||
"author": "Jason Johnston <jason.johnston@protectwise.com>",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/protectwise/troika.git",
|
||||
"directory": "packages/troika-worker-utils"
|
||||
},
|
||||
"license": "MIT",
|
||||
"sideEffects": false,
|
||||
"main": "dist/troika-worker-utils.umd.js",
|
||||
"module": "dist/troika-worker-utils.esm.js",
|
||||
"module:src": "src/index.js",
|
||||
"gitHead": "d2eab048d7eac8856e3d19b4254cb746a52a4d4d"
|
||||
}
|
||||
180
node_modules/troika-worker-utils/src/WorkerModules.js
generated
vendored
Normal file
180
node_modules/troika-worker-utils/src/WorkerModules.js
generated
vendored
Normal file
@@ -0,0 +1,180 @@
|
||||
import { workerBootstrap } from './workerBootstrap.js'
|
||||
import { defineMainThreadModule } from './mainThreadFallback.js'
|
||||
import { supportsWorkers } from './supportsWorkers.js'
|
||||
|
||||
let _workerModuleId = 0
|
||||
let _messageId = 0
|
||||
let _allowInitAsString = false
|
||||
const workers = Object.create(null)
|
||||
const registeredModules = Object.create(null) //workerId -> Set<unregisterFn>
|
||||
const openRequests = Object.create(null)
|
||||
|
||||
|
||||
/**
|
||||
* Define a module of code that will be executed with a web worker. This provides a simple
|
||||
* interface for moving chunks of logic off the main thread, and managing their dependencies
|
||||
* among one another.
|
||||
*
|
||||
* @param {object} options
|
||||
* @param {function} options.init
|
||||
* @param {array} [options.dependencies]
|
||||
* @param {function} [options.getTransferables]
|
||||
* @param {string} [options.name]
|
||||
* @param {string} [options.workerId]
|
||||
* @return {function(...[*]): {then}}
|
||||
*/
|
||||
export function defineWorkerModule(options) {
|
||||
if ((!options || typeof options.init !== 'function') && !_allowInitAsString) {
|
||||
throw new Error('requires `options.init` function')
|
||||
}
|
||||
let {dependencies, init, getTransferables, workerId} = options
|
||||
|
||||
const onMainThread = defineMainThreadModule(options)
|
||||
|
||||
if (workerId == null) {
|
||||
workerId = '#default'
|
||||
}
|
||||
const id = `workerModule${++_workerModuleId}`
|
||||
const name = options.name || id
|
||||
let registrationPromise = null
|
||||
|
||||
dependencies = dependencies && dependencies.map(dep => {
|
||||
// Wrap raw functions as worker modules with no dependencies
|
||||
if (typeof dep === 'function' && !dep.workerModuleData) {
|
||||
_allowInitAsString = true
|
||||
dep = defineWorkerModule({
|
||||
workerId,
|
||||
name: `<${name}> function dependency: ${dep.name}`,
|
||||
init: `function(){return (\n${stringifyFunction(dep)}\n)}`
|
||||
})
|
||||
_allowInitAsString = false
|
||||
}
|
||||
// Grab postable data for worker modules
|
||||
if (dep && dep.workerModuleData) {
|
||||
dep = dep.workerModuleData
|
||||
}
|
||||
return dep
|
||||
})
|
||||
|
||||
function moduleFunc(...args) {
|
||||
if (!supportsWorkers()) {
|
||||
return onMainThread(...args)
|
||||
}
|
||||
|
||||
// Register this module if needed
|
||||
if (!registrationPromise) {
|
||||
registrationPromise = callWorker(workerId,'registerModule', moduleFunc.workerModuleData)
|
||||
const unregister = () => {
|
||||
registrationPromise = null
|
||||
registeredModules[workerId].delete(unregister)
|
||||
}
|
||||
;(registeredModules[workerId] || (registeredModules[workerId] = new Set())).add(unregister)
|
||||
}
|
||||
|
||||
// Invoke the module, returning a promise
|
||||
return registrationPromise.then(({isCallable}) => {
|
||||
if (isCallable) {
|
||||
return callWorker(workerId,'callModule', {id, args})
|
||||
} else {
|
||||
throw new Error('Worker module function was called but `init` did not return a callable function')
|
||||
}
|
||||
})
|
||||
}
|
||||
moduleFunc.workerModuleData = {
|
||||
isWorkerModule: true,
|
||||
id,
|
||||
name,
|
||||
dependencies,
|
||||
init: stringifyFunction(init),
|
||||
getTransferables: getTransferables && stringifyFunction(getTransferables)
|
||||
}
|
||||
|
||||
moduleFunc.onMainThread = onMainThread;
|
||||
|
||||
return moduleFunc
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminate an active Worker by a workerId that was passed to defineWorkerModule.
|
||||
* This only terminates the Worker itself; the worker module will remain available
|
||||
* and if you call it again its Worker will be respawned.
|
||||
* @param {string} workerId
|
||||
*/
|
||||
export function terminateWorker(workerId) {
|
||||
// Unregister all modules that were registered in that worker
|
||||
if (registeredModules[workerId]) {
|
||||
registeredModules[workerId].forEach(unregister => {
|
||||
unregister()
|
||||
})
|
||||
}
|
||||
// Terminate the Worker object
|
||||
if (workers[workerId]) {
|
||||
workers[workerId].terminate()
|
||||
delete workers[workerId]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stringifies a function into a form that can be deserialized in the worker
|
||||
* @param fn
|
||||
*/
|
||||
export function stringifyFunction(fn) {
|
||||
let str = fn.toString()
|
||||
// If it was defined in object method/property format, it needs to be modified
|
||||
if (!/^function/.test(str) && /^\w+\s*\(/.test(str)) {
|
||||
str = 'function ' + str
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
|
||||
function getWorker(workerId) {
|
||||
let worker = workers[workerId]
|
||||
if (!worker) {
|
||||
// Bootstrap the worker's content
|
||||
const bootstrap = stringifyFunction(workerBootstrap)
|
||||
|
||||
// Create the worker from the bootstrap function content
|
||||
worker = workers[workerId] = new Worker(
|
||||
URL.createObjectURL(
|
||||
new Blob(
|
||||
[`/** Worker Module Bootstrap: ${workerId.replace(/\*/g, '')} **/\n\n;(${bootstrap})()`],
|
||||
{type: 'application/javascript'}
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
// Single handler for response messages from the worker
|
||||
worker.onmessage = e => {
|
||||
const response = e.data
|
||||
const msgId = response.messageId
|
||||
const callback = openRequests[msgId]
|
||||
if (!callback) {
|
||||
throw new Error('WorkerModule response with empty or unknown messageId')
|
||||
}
|
||||
delete openRequests[msgId]
|
||||
callback(response)
|
||||
}
|
||||
}
|
||||
return worker
|
||||
}
|
||||
|
||||
// Issue a call to the worker with a callback to handle the response
|
||||
function callWorker(workerId, action, data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const messageId = ++_messageId
|
||||
openRequests[messageId] = response => {
|
||||
if (response.success) {
|
||||
resolve(response.result)
|
||||
} else {
|
||||
reject(new Error(`Error in worker ${action} call: ${response.error}`))
|
||||
}
|
||||
}
|
||||
getWorker(workerId).postMessage({
|
||||
messageId,
|
||||
action,
|
||||
data
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
3
node_modules/troika-worker-utils/src/index.js
generated
vendored
Normal file
3
node_modules/troika-worker-utils/src/index.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
// Troika worker utility exports
|
||||
|
||||
export {defineWorkerModule, terminateWorker, stringifyFunction} from './WorkerModules.js'
|
||||
44
node_modules/troika-worker-utils/src/mainThreadFallback.js
generated
vendored
Normal file
44
node_modules/troika-worker-utils/src/mainThreadFallback.js
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Fallback for `defineWorkerModule` that behaves identically but runs in the main
|
||||
* thread, for when the execution environment doesn't support web workers or they
|
||||
* are disallowed due to e.g. CSP security restrictions.
|
||||
*/
|
||||
export function defineMainThreadModule(options) {
|
||||
let moduleFunc = function(...args) {
|
||||
return moduleFunc._getInitResult().then(initResult => {
|
||||
if (typeof initResult === 'function') {
|
||||
return initResult(...args)
|
||||
} else {
|
||||
throw new Error('Worker module function was called but `init` did not return a callable function')
|
||||
}
|
||||
})
|
||||
}
|
||||
moduleFunc._getInitResult = function() {
|
||||
// We can ignore getTransferables in main thread. TODO workerId?
|
||||
let {dependencies, init} = options
|
||||
|
||||
// Resolve dependencies
|
||||
dependencies = Array.isArray(dependencies) ? dependencies.map(dep => {
|
||||
if (dep) {
|
||||
// If it's a worker module, use its main thread impl
|
||||
dep = dep.onMainThread || dep
|
||||
// If it's a main thread worker module, use its init return value
|
||||
if (dep._getInitResult) {
|
||||
dep = dep._getInitResult()
|
||||
}
|
||||
}
|
||||
return dep
|
||||
}) : []
|
||||
|
||||
// Invoke init with the resolved dependencies
|
||||
let initPromise = Promise.all(dependencies).then(deps => {
|
||||
return init.apply(null, deps)
|
||||
})
|
||||
|
||||
// Cache the resolved promise for subsequent calls
|
||||
moduleFunc._getInitResult = () => initPromise
|
||||
|
||||
return initPromise
|
||||
}
|
||||
return moduleFunc
|
||||
}
|
||||
31
node_modules/troika-worker-utils/src/supportsWorkers.js
generated
vendored
Normal file
31
node_modules/troika-worker-utils/src/supportsWorkers.js
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
let supportsWorkers = () => {
|
||||
let supported = false
|
||||
|
||||
// Only attempt worker initialization in browsers; elsewhere it would just be
|
||||
// noise e.g. loading into a Node environment for SSR.
|
||||
if (typeof window !== 'undefined' && typeof window.document !== 'undefined') {
|
||||
try {
|
||||
// TODO additional checks for things like importScripts within the worker?
|
||||
// Would need to be an async check.
|
||||
let worker = new Worker(
|
||||
URL.createObjectURL(new Blob([''], { type: 'application/javascript' }))
|
||||
)
|
||||
worker.terminate()
|
||||
supported = true
|
||||
} catch (err) {
|
||||
if (typeof process !== 'undefined' && process.env.NODE_ENV === 'test') {
|
||||
// No console log for node env 'test' (e.g. tests with Jest)
|
||||
} else {
|
||||
console.log(
|
||||
`Troika createWorkerModule: web workers not allowed; falling back to main thread execution. Cause: [${err.message}]`
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cached result
|
||||
supportsWorkers = () => supported
|
||||
return supported
|
||||
}
|
||||
|
||||
export { supportsWorkers }
|
||||
148
node_modules/troika-worker-utils/src/workerBootstrap.js
generated
vendored
Normal file
148
node_modules/troika-worker-utils/src/workerBootstrap.js
generated
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
/**
|
||||
* Main content for the worker that handles the loading and execution of
|
||||
* modules within it.
|
||||
*/
|
||||
export function workerBootstrap() {
|
||||
const modules = Object.create(null)
|
||||
|
||||
// Handle messages for registering a module
|
||||
function registerModule({id, name, dependencies=[], init=function(){}, getTransferables=null}, callback) {
|
||||
// Only register once
|
||||
if (modules[id]) return
|
||||
|
||||
try {
|
||||
// If any dependencies are modules, ensure they're registered and grab their value
|
||||
dependencies = dependencies.map(dep => {
|
||||
if (dep && dep.isWorkerModule) {
|
||||
registerModule(dep, depResult => {
|
||||
if (depResult instanceof Error) throw depResult
|
||||
})
|
||||
dep = modules[dep.id].value
|
||||
}
|
||||
return dep
|
||||
})
|
||||
|
||||
// Rehydrate functions
|
||||
init = rehydrate(`<${name}>.init`, init)
|
||||
if (getTransferables) {
|
||||
getTransferables = rehydrate(`<${name}>.getTransferables`, getTransferables)
|
||||
}
|
||||
|
||||
// Initialize the module and store its value
|
||||
let value = null
|
||||
if (typeof init === 'function') {
|
||||
value = init(...dependencies)
|
||||
} else {
|
||||
console.error('worker module init function failed to rehydrate')
|
||||
}
|
||||
modules[id] = {
|
||||
id,
|
||||
value,
|
||||
getTransferables
|
||||
}
|
||||
callback(value)
|
||||
} catch(err) {
|
||||
if (!(err && err.noLog)) {
|
||||
console.error(err)
|
||||
}
|
||||
callback(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Handle messages for calling a registered module's result function
|
||||
function callModule({id, args}, callback) {
|
||||
if (!modules[id] || typeof modules[id].value !== 'function') {
|
||||
callback(new Error(`Worker module ${id}: not found or its 'init' did not return a function`))
|
||||
}
|
||||
try {
|
||||
const result = modules[id].value(...args)
|
||||
if (result && typeof result.then === 'function') {
|
||||
result.then(handleResult, rej => callback(rej instanceof Error ? rej : new Error('' + rej)))
|
||||
} else {
|
||||
handleResult(result)
|
||||
}
|
||||
} catch(err) {
|
||||
callback(err)
|
||||
}
|
||||
function handleResult(result) {
|
||||
try {
|
||||
let tx = modules[id].getTransferables && modules[id].getTransferables(result)
|
||||
if (!tx || !Array.isArray(tx) || !tx.length) {
|
||||
tx = undefined //postMessage is very picky about not passing null or empty transferables
|
||||
}
|
||||
callback(result, tx)
|
||||
} catch(err) {
|
||||
console.error(err)
|
||||
callback(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function rehydrate(name, str) {
|
||||
let result = void 0
|
||||
self.troikaDefine = r => result = r
|
||||
let url = URL.createObjectURL(
|
||||
new Blob(
|
||||
[`/** ${name.replace(/\*/g, '')} **/\n\ntroikaDefine(\n${str}\n)`],
|
||||
{type: 'application/javascript'}
|
||||
)
|
||||
)
|
||||
try {
|
||||
importScripts(url)
|
||||
} catch(err) {
|
||||
console.error(err)
|
||||
}
|
||||
URL.revokeObjectURL(url)
|
||||
delete self.troikaDefine
|
||||
return result
|
||||
}
|
||||
|
||||
// Handler for all messages within the worker
|
||||
self.addEventListener('message', e => {
|
||||
const {messageId, action, data} = e.data
|
||||
try {
|
||||
// Module registration
|
||||
if (action === 'registerModule') {
|
||||
registerModule(data, result => {
|
||||
if (result instanceof Error) {
|
||||
postMessage({
|
||||
messageId,
|
||||
success: false,
|
||||
error: result.message
|
||||
})
|
||||
} else {
|
||||
postMessage({
|
||||
messageId,
|
||||
success: true,
|
||||
result: {isCallable: typeof result === 'function'}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
// Invocation
|
||||
if (action === 'callModule') {
|
||||
callModule(data, (result, transferables) => {
|
||||
if (result instanceof Error) {
|
||||
postMessage({
|
||||
messageId,
|
||||
success: false,
|
||||
error: result.message
|
||||
})
|
||||
} else {
|
||||
postMessage({
|
||||
messageId,
|
||||
success: true,
|
||||
result
|
||||
}, transferables || undefined)
|
||||
}
|
||||
})
|
||||
}
|
||||
} catch(err) {
|
||||
postMessage({
|
||||
messageId,
|
||||
success: false,
|
||||
error: err.stack
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user