fix module loading for node under fake web workers

This commit is contained in:
Florian Dold 2017-05-27 22:55:52 +02:00
parent 9a1b2c8ccc
commit 478a089e52
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
13 changed files with 45 additions and 430 deletions

View File

@ -1,9 +0,0 @@
/.idea/
/node_modules/
/src/
/test/
.eslintrc
.git
.gitignore
.travis.yml
Gruntfile.js

28
node_modules/tiny-worker/LICENSE generated vendored
View File

@ -1,28 +0,0 @@
Copyright (c) 2017, Jason Mulligan
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of tiny-worker nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

115
node_modules/tiny-worker/README.md generated vendored
View File

@ -1,115 +0,0 @@
# tiny-worker
Tiny WebWorker for Server
`require()` is available for flexible inline Worker scripts. Optional parameters `args` Array & `options` Object; see `child_process.fork()` documentation.
[![build status](https://secure.travis-ci.org/avoidwork/tiny-worker.svg)](http://travis-ci.org/avoidwork/tiny-worker)
## Example
#### Creating a Worker from a file
The worker script:
```javascript
onmessage = function (ev) {
postMessage(ev.data);
};
```
The core script:
```javascript
var Worker = require("tiny-worker");
var worker = new Worker("repeat.js");
worker.onmessage = function (ev) {
console.log(ev.data);
worker.terminate();
};
worker.postMessage("Hello World!");
```
#### Creating a Worker from a Function
```javascript
var Worker = require("tiny-worker");
var worker = new Worker(function () {
self.onmessage = function (ev) {
postMessage(ev.data);
};
});
worker.onmessage = function (ev) {
console.log(ev.data);
worker.terminate();
};
worker.postMessage("Hello World!");
```
# Debugging
To be able to debug a child process, it must have a differnt debug port than the parent.
Tiny worker does this by adding a random port within a range to the parents debug port.
The default Range is `[1, 300]`, it can be changed with the `setRange(min, max)` method.
To disable any automatic port redirection set `options.noDebugRedirection = true`.
### automatic redirection
```javascript
//parent is started with '--debug=1234'
var Worker = require("tiny-worker");
Worker.setRange(2, 20);
var worker = new Worker(function () {
postMessage(process.debugPort);
});
worker.onmessage = function (ev) {
console.log(ev.data); //prints any number between 1236 and 1254
worker.terminate();
}
```
### manual redirection
```javascript
//parent is started with '--debug=1234'
var Worker = require("tiny-worker");
var worker = new Worker(function () {
postMessage(process.debugPort);
}, [], {noDebugRedirection: true, execArgv: ["--debug=1235"]});
worker.onmessage = function (ev) {
console.log(ev.data); //prints 1235
worker.terminate();
}
```
## Properties
#### onmessage
Message handler, accepts an `Event`
#### onerror
Error handler, accepts an `Event`
## API
#### addEventListener(event, fn)
Adds an event listener
#### postMessage()
Broadcasts a message to the `Worker`
#### terminate()
Terminates the `Worker`
#### static setRange(min, max)
Sets range for debug ports, only affects current process.
Returns true if successful.
## FAQ
1. I have an orphaned child process that lives on past the parent process' lifespan
* Most likely a `SIGTERM` or `SIGINT` is not reaching the child process
2. How do I insure all process are terminated?
* In your core script register a listener for `SIGTERM` or `SIGINT` via `process.on()` which terminates (all) worker process(es) and then gracefully shutdowns via `process.exit(0);`
3. Why `SIGTERM` or `SIGINT`?
* Unix/BSD will work with `SIGTERM`, but if you also need to support Windows use `SIGINT`
## License
Copyright (c) 2017 Jason Mulligan
Licensed under the BSD-3 license

137
node_modules/tiny-worker/lib/index.js generated vendored
View File

@ -1,137 +0,0 @@
"use strict";
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var path = require("path"),
fork = require("child_process").fork,
worker = path.join(__dirname, "worker.js"),
events = /^(error|message)$/,
defaultPorts = { inspect: 9229, debug: 5858 };
var range = { min: 1, max: 300 };
var Worker = function () {
function Worker(arg) {
var _this = this;
var args = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : { cwd: process.cwd() };
_classCallCheck(this, Worker);
var isfn = typeof arg === "function",
input = isfn ? arg.toString() : arg;
if (!options.cwd) {
options.cwd = process.cwd();
}
//get all debug related parameters
var debugVars = process.execArgv.filter(function (execArg) {
return (/(debug|inspect)/.test(execArg)
);
});
if (debugVars.length > 0 && !options.noDebugRedirection) {
if (!options.execArgv) {
//if no execArgs are given copy all arguments
debugVars = Array.from(process.execArgv);
options.execArgv = [];
}
var inspectIndex = debugVars.findIndex(function (debugArg) {
//get index of inspect parameter
return (/^--inspect(-brk)?(=\d+)?$/.test(debugArg)
);
});
var debugIndex = debugVars.findIndex(function (debugArg) {
//get index of debug parameter
return (/^--debug(-brk)?(=\d+)?$/.test(debugArg)
);
});
var portIndex = inspectIndex >= 0 ? inspectIndex : debugIndex; //get index of port, inspect has higher priority
if (portIndex >= 0) {
var match = /^--(debug|inspect)(?:-brk)?(?:=(\d+))?$/.exec(debugVars[portIndex]); //get port
var port = defaultPorts[match[1]];
if (match[2]) {
port = parseInt(match[2]);
}
debugVars[portIndex] = "--" + match[1] + "=" + (port + range.min + Math.floor(Math.random() * (range.max - range.min))); //new parameter
if (debugIndex >= 0 && debugIndex !== portIndex) {
//remove "-brk" from debug if there
match = /^(--debug)(?:-brk)?(.*)/.exec(debugVars[debugIndex]);
debugVars[debugIndex] = match[1] + (match[2] ? match[2] : "");
}
}
options.execArgv = options.execArgv.concat(debugVars);
}
delete options.noDebugRedirection;
this.child = fork(worker, args, options);
this.onerror = undefined;
this.onmessage = undefined;
this.child.on("error", function (e) {
if (_this.onerror) {
_this.onerror.call(_this, e);
}
});
this.child.on("message", function (msg) {
var message = JSON.parse(msg);
var error = void 0;
if (!message.error && _this.onmessage) {
_this.onmessage.call(_this, message);
}
if (message.error && _this.onerror) {
error = new Error(message.error);
error.stack = message.stack;
_this.onerror.call(_this, error);
}
});
this.child.send({ input: input, isfn: isfn, cwd: options.cwd });
}
_createClass(Worker, [{
key: "addEventListener",
value: function addEventListener(event, fn) {
if (events.test(event)) {
this["on" + event] = fn;
}
}
}, {
key: "postMessage",
value: function postMessage(msg) {
this.child.send(JSON.stringify({ data: msg }, null, 0));
}
}, {
key: "terminate",
value: function terminate() {
this.child.kill("SIGINT");
}
}], [{
key: "setRange",
value: function setRange(min, max) {
if (min >= max) {
return false;
}
range.min = min;
range.max = max;
return true;
}
}]);
return Worker;
}();
module.exports = Worker;

View File

@ -1,5 +0,0 @@
"use strict";
module.exports = function () {
return void 0;
};

View File

@ -1,83 +0,0 @@
"use strict";
var fs = require("fs"),
path = require("path"),
vm = require("vm"),
noop = require(path.join(__dirname, "noop.js")),
events = /^(error|message)$/;
function trim(arg) {
return arg.replace(/^(\s+|\t+|\n+)|(\s+|\t+|\n+)$/g, "");
}
function explode(arg) {
return trim(arg).split(new RegExp("\\s*,\\s*"));
}
function toFunction(arg) {
var args = trim(arg.replace(/^.*\(/, "").replace(/[\t|\r|\n|\"|\']+/g, "").replace(/\).*/, "")),
body = trim(arg.replace(/^.*\{/, "").replace(/\}$/, ""));
return Function.apply(Function, explode(args).concat([body]));
}
// Bootstraps the Worker
process.once("message", function (obj) {
var exp = obj.isfn ? toFunction(obj.input) : fs.readFileSync(obj.input, "utf8");
global.self = {
close: function close() {
process.exit(0);
},
postMessage: function postMessage(msg) {
process.send(JSON.stringify({ data: msg }, null, 0));
},
onmessage: void 0,
onerror: function onerror(err) {
process.send(JSON.stringify({ error: err.message, stack: err.stack }, null, 0));
},
addEventListener: function addEventListener(event, fn) {
if (events.test(event)) {
global["on" + event] = global.self["on" + event] = fn;
}
}
};
global.__dirname = obj.cwd;
global.__filename = __filename;
global.require = require;
global.importScripts = function () {
for (var _len = arguments.length, files = Array(_len), _key = 0; _key < _len; _key++) {
files[_key] = arguments[_key];
}
if (files.length > 0) {
vm.createScript(files.map(function (file) {
return fs.readFileSync(file, "utf8");
}).join("\n")).runInThisContext();
}
};
Object.keys(global.self).forEach(function (key) {
global[key] = global.self[key];
});
process.on("message", function (msg) {
try {
(global.onmessage || global.self.onmessage || noop)(JSON.parse(msg));
} catch (err) {
(global.onerror || global.self.onerror || noop)(err);
}
});
process.on("error", function (err) {
(global.onerror || global.self.onerror || noop)(err);
});
if (typeof exp === "function") {
exp();
} else {
vm.createScript(exp).runInThisContext();
}
});

View File

@ -1,35 +0,0 @@
{
"name": "tiny-worker",
"version": "2.1.1",
"description": "Tiny WebWorker for Server",
"main": "lib/index.js",
"scripts": {
"test": "grunt test"
},
"repository": {
"type": "git",
"url": "https://github.com/avoidwork/tiny-worker.git"
},
"keywords": [
"web",
"worker",
"ps",
"webworker"
],
"author": "Jason Mulligan <jason.mulligan@avoidwork.com>",
"license": "BSD-3-Clause",
"bugs": {
"url": "https://github.com/avoidwork/tiny-worker/issues"
},
"homepage": "https://github.com/avoidwork/tiny-worker",
"devDependencies": {
"babel-preset-es2015": "~6.22.0",
"grunt": "~1.0.1",
"grunt-babel": "~6.0.0",
"grunt-cli": "~1.2.0",
"grunt-contrib-nodeunit": "~1.0.0",
"grunt-contrib-watch": "~1.0.0",
"grunt-eslint": "~19.0.0"
},
"dependencies": {}
}

View File

@ -40,7 +40,6 @@
"react": "^15.5.4",
"react-dom": "^15.5.4",
"through2": "^2.0.1",
"tiny-worker": "^2.1.1",
"ts-loader": "^2.0.3",
"typedoc": "^0.7.1",
"typescript": "next",

View File

@ -38,6 +38,9 @@ import {
OfferRecord,
CoinWithDenom,
} from "../wallet";
import * as timer from "../timer";
import { startWorker } from "./startWorker";
/**
@ -57,7 +60,7 @@ interface WorkerState {
/**
* Timer to terminate the worker if it's not busy enough.
*/
terminationTimerHandle: number|null;
terminationTimerHandle: timer.TimerHandle|null;
}
interface WorkItem {
@ -73,6 +76,8 @@ interface WorkItem {
}
/**
* Number of different priorities. Each priority p
* must be 0 <= p < NUM_PRIO.
@ -98,7 +103,7 @@ export class CryptoApi {
ws.currentWorkItem = work;
this.numBusy++;
if (!ws.w) {
let w = new Worker("/dist/cryptoWorker-bundle.js");
let w = startWorker();
w.onmessage = (m: MessageEvent) => this.handleWorkerMessage(ws, m);
w.onerror = (e: ErrorEvent) => this.handleWorkerError(ws, e);
ws.w = w;
@ -114,7 +119,8 @@ export class CryptoApi {
resetWorkerTimeout(ws: WorkerState) {
if (ws.terminationTimerHandle != null) {
clearTimeout(ws.terminationTimerHandle);
ws.terminationTimerHandle.clear();
ws.terminationTimerHandle = null;
}
let destroy = () => {
// terminate worker if it's idle
@ -123,7 +129,7 @@ export class CryptoApi {
ws.w = null;
}
};
ws.terminationTimerHandle = window.setTimeout(destroy, 20 * 1000);
ws.terminationTimerHandle = timer.after(20 * 1000, destroy);
}
handleWorkerError(ws: WorkerState, e: ErrorEvent) {
@ -182,7 +188,14 @@ export class CryptoApi {
}
constructor() {
this.workers = new Array<WorkerState>((navigator as any)["hardwareConcurrency"] || 2);
let concurrency = 2;
try {
// only works in the browser
concurrency = (navigator as any)["hardwareConcurrency"];
} catch (e) {
// ignore
}
this.workers = new Array<WorkerState>(concurrency);
for (let i = 0; i < this.workers.length; i++) {
this.workers[i] = {
@ -199,7 +212,7 @@ export class CryptoApi {
private doRpc<T>(operation: string, priority: number,
...args: any[]): Promise<T> {
let start = performance.now();
let start = timer.performanceNow();
let p = new Promise((resolve, reject) => {
let rpcId = this.nextRpcId++;
@ -228,7 +241,7 @@ export class CryptoApi {
});
return p.then((r: T) => {
console.log(`rpc ${operation} took ${performance.now() - start}ms`);
console.log(`rpc ${operation} took ${timer.performanceNow() - start}ms`);
return r;
});
}

View File

@ -31,6 +31,22 @@
* be globally available. Inside node, require is used.
*/
export function getLib() {
if (typeof require !== "undefined") {
// Make sure that TypeScript doesn't try
// to check the taler-emscripten-lib.
const indirectRequire = require;
const g = global as any;
// unavoidable hack, so that emscripten detects
// the environment as node even though importScripts
// is present.
const savedImportScripts = g.importScripts;
delete g.importScripts;
// Assume that the code is run from the build/ directory.
const lib = indirectRequire("../../../emscripten/taler-emscripten-lib.js");
g.importScripts = savedImportScripts;
return lib;
}
if (typeof importScripts !== "undefined") {
importScripts('/src/emscripten/taler-emscripten-lib.js')
if (TalerEmscriptenLib) {
@ -39,14 +55,6 @@ export function getLib() {
return TalerEmscriptenLib
}
if (typeof require !== "undefined") {
// Make sure that TypeScript doesn't try
// to check the taler-emscripten-lib.
const fn = require;
// Assume that the code is run from the build/ directory.
return fn("../../../emscripten/taler-emscripten-lib.js");
}
if (typeof window !== "undefined") {
if (window.TalerEmscriptenLib) {
return TalerEmscriptenLib;

View File

@ -53,6 +53,9 @@ test("contract validation", t => {
timestamp: "Date(12345)",
transaction_id: 1234,
fulfillment_url: "foo",
wire_method: "test",
order_id: "test_order",
pay_url: "https://example.com/pay",
};
types.Contract.checked(c);

View File

@ -587,10 +587,10 @@ export class Contract {
@Checkable.String
fulfillment_url: string;
@Checkable.Number
@Checkable.Optional(Checkable.Number)
wire_fee_amortization?: number;
@Checkable.Value(AmountJson)
@Checkable.Optional(Checkable.Value(AmountJson))
max_wire_fee?: AmountJson;
@Checkable.Any

View File

@ -34,6 +34,9 @@
"src/crypto/emscInterface.ts",
"src/crypto/emscLoader.d.ts",
"src/crypto/emscLoader.js",
"src/crypto/nodeWorker.ts",
"src/crypto/nodeWorkerEntry.ts",
"src/crypto/startWorker.js",
"src/helpers-test.ts",
"src/helpers.ts",
"src/http.ts",
@ -52,6 +55,7 @@
"src/pages/tree.tsx",
"src/query.ts",
"src/renderHtml.tsx",
"src/timer.ts",
"src/types-test.ts",
"src/types.ts",
"src/wallet-test.ts",