Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

isoworker

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

isoworker - npm Package Compare versions

Comparing version 0.1.3 to 0.2.0

316

esm/browser.js

@@ -15,8 +15,2 @@ var wk = function (c, msg, transfer, cb) {

var fnName = function (f) {
if (f.name)
return f.name;
var ts = f.toString();
return ts.slice(0, 9) == 'function ' && ts.slice(9, ts.indexOf('(', 9));
};
var abvList = [

@@ -36,2 +30,3 @@ Int8Array,

abvList.push(BigUint64Array);
var rand = function () { return Math.ceil(Math.random() * 1073741823); };
var getAllPropertyKeys = function (o) {

@@ -44,74 +39,139 @@ var keys = Object.getOwnPropertyNames(o);

};
// optional chaining
var chainWrap = function (name, expr, short) {
return "(" + expr + "||(" + short + "={}))" + name;
var bannedFunctionKeys = getAllPropertyKeys(Function.prototype).concat('prototype');
var toReplace = '/*r*/null';
var toReplaceRE = /\/\*r\*\/null/g;
var renderCtx = function (ctx, ab, m) {
var out = 'self';
for (var _i = 0, ctx_1 = ctx; _i < ctx_1.length; _i++) {
var key = ctx_1[_i];
out += "[" + encoder[typeof key](key, ab, m) + "]";
}
return out;
};
var vDescriptors = function (v, keys, ab, m, g, s, ctx, dat) {
var out = '';
var renderedCtx = renderCtx(ctx, ab, m);
for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) {
var t = keys_1[_i];
var _a = Object.getOwnPropertyDescriptor(v, t), enumerable = _a.enumerable, configurable = _a.configurable, get = _a.get, set = _a.set, writable = _a.writable, value = _a.value;
var keyEnc = encoder[typeof t](t, ab, m);
var desc = '{', res = void 0;
var enc = typeof writable == 'boolean' &&
encoder[typeof value](value, ab, m, g, s, ctx.concat(t), dat);
var replaced = enc == toReplace;
var obj = 'v';
if (replaced) {
obj = renderedCtx;
dat.pop();
}
if (enc) {
if (writable && configurable && enumerable)
res = obj + "[" + keyEnc + "]=" + enc;
else
desc += "writable:" + writable + ",value:" + enc;
}
else
desc += "get:" + (get ? encoder["function"](get, ab, m, g, s, ctx, dat) : 'void 0') + ",set:" + (set ? encoder["function"](set, ab, m, g, s, ctx, dat) : 'void 0');
if (!res) {
desc += ",enumerable:" + enumerable + ",configurable:" + configurable + "}";
res = "Object.defineProperty(" + obj + ", " + encoder[typeof t](t, ab, m) + ", " + desc + ")";
}
if (replaced)
dat.push([res + ";", value]);
else
out += ";" + res;
}
if (Object.isSealed(v))
dat.push("Object.seal(" + renderedCtx + ");");
if (!Object.isExtensible(v))
dat.push("Object.preventExtensions(" + renderedCtx + ");");
if (Object.isFrozen(v))
dat.push("Object.freeze(" + renderedCtx + ");");
return out;
};
var encoder = {
undefined: function () { return 'void 0'; },
bigint: function (v) { return v.toString() + 'n'; },
symbol: function (v) {
bigint: function (v) { return v + 'n'; },
string: function (v) { return JSON.stringify(v); },
boolean: function (v) { return v + ''; },
number: function (v) { return v + ''; },
symbol: function (v, _, m) {
var key = Symbol.keyFor(v);
return key
? "Symbol.for(" + encoder.string(key) + ")"
: "Symbol(" + encoder.string(v.toString().slice(7, -1)) + ")";
if (key)
return "Symbol.for(" + encoder.string(key) + ")";
var gbn = m[v];
if (gbn)
return "self[" + gbn + "]";
gbn = m[v] = rand();
return "(self[" + gbn + "]=Symbol(" + encoder.string(v.toString().slice(7, -1)) + "))";
},
string: function (v) { return JSON.stringify(v); },
"function": function (v, reg, ab) {
"function": function (v, ab, m, g, s, ctx, dat) {
var st = v.toString();
var proto = v.prototype;
if (st.indexOf('[native code]', 12) != -1)
st = fnName(v);
else if (v.prototype) {
var nm = fnName(v);
if (nm) {
if (nm in reg)
return "self[" + encoder.string(nm) + "]";
reg[nm] = true;
return v.name;
if (st[0] != '(' && !proto) {
var headMatch = st.match(/^(.+?)(?=\()/g);
if (headMatch)
st = 'function' + st.slice(headMatch[0].length);
else
throw new TypeError("failed to find function body in " + st);
}
var vd = vDescriptors(v, getAllPropertyKeys(v).filter(function (key) { return bannedFunctionKeys.indexOf(key) == -1; }), ab, m, g, s, ctx, dat);
var gbn = g(v);
if (gbn)
return "(function(){var v=" + gbn + vd + ";return v})()";
if (proto) {
var superCtr = Object.getPrototypeOf(proto).constructor;
// TODO: Avoid duplicating methods for ES6 classes
var base = '(function(){';
if (superCtr == Object)
st = base + "var v=" + st;
else {
var superEnc = encoder["function"](superCtr, ab, m, g, s, ctx.concat('prototype'), dat);
if (st[0] == 'c') {
var superName = st.match(/(?<=^class(.*?)extends(.*?)(\s+))(.+?)(?=(\s*){)/g);
if (!superName)
throw new TypeError("failed to find superclass in " + st);
st = base + "var " + superName[0] + "=" + superEnc + ";var v=" + st;
}
else
st = base + "var v=" + st + ";v.prototype=Object.create(" + superEnc + ")";
}
if (st[0] != 'c') {
// Not an ES6 class; must iterate across the properties
// Ignore superclass properties, assume superclass is handled elsewhere
st = '(function(){var v=' + st;
for (var _i = 0, _a = getAllPropertyKeys(v.prototype); _i < _a.length; _i++) {
var t = _a[_i];
var val = v.prototype[t];
if (t != 'constructor') {
st += ";v[" + encoder[typeof t](t) + "]=" + encoder[typeof val](val, reg, ab);
}
for (var _i = 0, _a = getAllPropertyKeys(proto); _i < _a.length; _i++) {
var t = _a[_i];
var val = proto[t];
if (t != 'constructor') {
var key = encoder[typeof t](t, ab, m);
st += ";v.prototype[" + key + "]=" + encoder[typeof val](val, ab, m, g, s, ctx.concat('prototype', key), dat);
}
st += ';return v})()';
}
st += vd + ";return v})()";
}
return st;
else if (vd.length)
st = "(function(){var v=" + st + vd + ";return v})()";
return s(v, st);
},
object: function (v, reg, ab) {
object: function (v, ab, m, g, s, ctx, dat) {
if (v == null)
return 'null';
var proto = Object.getPrototypeOf(v);
if (abvList.indexOf(proto.constructor) != -1) {
ab.push(v.buffer);
return v;
var abv;
if (abvList.indexOf(proto.constructor) != -1)
abv = v.buffer;
else if (wk.t.indexOf(proto.constructor) != -1)
abv = v;
if (abv) {
ab.push(abv);
dat.push([renderCtx(ctx, ab, m) + "=" + toReplace + ";", v]);
return toReplace;
}
else if (wk.t.indexOf(proto.constructor) != -1) {
ab.push(v);
return v;
}
var out = '';
out += "(function(){";
var classDecl = '';
for (var i = 0, l = proto; l.constructor != Object; l = Object.getPrototypeOf(l), ++i) {
var cls = l.constructor;
var nm = fnName(cls) || '_cls' + i;
if (nm in reg)
continue;
var enc = encoder["function"](cls, reg, ab);
if (enc == nm) {
break;
}
else {
reg[nm] = true;
classDecl = "self[" + encoder.string(nm) + "]=" + enc + ";" + classDecl;
}
}
var gbn = g(v);
if (gbn)
return gbn;
var out = '(function(){var v=';
var keys = getAllPropertyKeys(v);
if (proto.constructor == Array) {
if (proto.constructor == Object)
out += "{}";
else if (proto.constructor == Array) {
var arrStr = '';

@@ -121,3 +181,3 @@ for (var i = 0; i < v.length; ++i) {

var val = v[i];
arrStr += encoder[typeof val](val, reg, ab);
arrStr += encoder[typeof val](val, ab, m, g, s, ctx.concat(i), dat);
}

@@ -129,26 +189,8 @@ arrStr += ',';

});
out += "var v=[" + arrStr.slice(0, -1) + "]";
out += "[" + arrStr.slice(0, -1) + "]";
}
else {
out +=
classDecl +
("var v=Object.create(self[" + encoder.string(fnName(proto.constructor) || '_cls0') + "].prototype);");
}
for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) {
var t = keys_1[_i];
var _a = Object.getOwnPropertyDescriptor(v, t), enumerable = _a.enumerable, configurable = _a.configurable, get = _a.get, set = _a.set, writable = _a.writable, value = _a.value;
var desc = '{';
if (typeof writable == 'boolean') {
desc += "writable:" + writable + ",value:" + encoder[typeof value](value, reg, ab);
}
else {
desc += "get:" + (get ? encoder["function"](get, reg, ab) : 'void 0') + "," + (set ? encoder["function"](set, reg, ab) : 'void 0');
}
desc += ",enumerable:" + enumerable + ",configurable:" + configurable + "}";
out += "Object.defineProperty(v, " + encoder[typeof t](t) + ", " + desc + ");";
}
return out + 'return v})()';
},
boolean: function (v) { return v.toString(); },
number: function (v) { return v.toString(); }
else
out += "Object.create(" + encoder["function"](proto.constructor, ab, m, g, s, ctx.concat('constructor'), dat) + ".prototype)";
return out + vDescriptors(v, keys, ab, m, g, s, ctx, dat) + ';return v})()';
}
};

@@ -169,30 +211,33 @@ /**

var out = '';
var reg = {};
var dat = {};
var dat = [];
var ab = [];
var symMap = {};
var gbnKey = typeof Symbol == 'undefined' ? "__iwgbn" + rand() + "__" : Symbol();
var getGBN = function (obj) {
var gbn = obj[gbnKey];
if (gbn)
return "self[" + gbn + "]";
};
var setGBN = function (obj, wrap) {
var gbn = rand();
Object.defineProperty(obj, gbnKey, {
value: gbn
});
return "(self[" + gbn + "]=" + wrap + ")";
};
for (var i = 0; i < depValues.length; ++i) {
var key = depNames[i], value = depValues[i];
var v = encoder[typeof value](value, ab, symMap, getGBN, setGBN, [key], dat);
var parts = key
.replace(/\\/, '')
.match(/^(.*?)(?=(\.|\[|$))|\[(.*?)\]|(\.(.*?))(?=(\.|\[|$))/g);
var v = encoder[typeof value](value, reg, ab);
if (typeof v == 'string') {
var pfx = 'self.' + parts[0];
var chain = pfx;
for (var i_1 = 1; i_1 < parts.length; ++i_1) {
chain = chainWrap(parts[i_1], chain, pfx);
pfx += parts[i_1];
}
out += chain + "=" + v + ";";
var pfx = 'self.' + parts[0];
var chain = pfx;
for (var i_1 = 1; i_1 < parts.length; ++i_1) {
chain = "(" + chain + "||(" + pfx + "={}))" + parts[i_1];
pfx += parts[i_1];
}
else {
// TODO: overwrite instead of assign
var obj = dat;
for (var i_2 = 0; i_2 < parts.length - 1; ++i_2) {
obj = obj[parts[i_2]] = {};
}
obj[parts[parts.length - 1]] = v;
}
out += chain + "=" + v + ";";
}
return [out, dat, ab, reg];
return [out, dat, ab];
}

@@ -211,2 +256,11 @@ var findTransferables = function (vals) {

};
var globalEnv = typeof globalThis == 'undefined'
? typeof window == 'undefined'
? typeof self == 'undefined'
? typeof global == 'undefined'
? {}
: global
: self
: window
: globalThis;
/**

@@ -219,13 +273,57 @@ * Converts a function with dependencies into a worker

* here as well.
* @param replaceTransfer The list of objects to replace the default transfer
* list with. If you provide an array of transferable
* items, they will be used; if you provide the value
* `true`, `isoworker` will refrain from the default
* behavior of automatically transferring everything
* it can.
* @returns A function that accepts parameters and, as the last argument, a
* callback to use when the worker returns.
*/
export function workerize(fn, deps) {
var _a = createContext(deps), str = _a[0], msg = _a[1], tfl = _a[2], reg = _a[3];
export function workerize(fn, deps, replaceTransfer) {
var _a = createContext(deps), str = _a[0], exec = _a[1], tfl = _a[2];
var currentCb;
var runCount = 0;
var callCount = 0;
var worker = wk(str + ";onmessage=function(e){for(var k in e.data){self[k]=e.data[k]}var h=" + encoder["function"](fn, reg, tfl) + ";var _p=function(d){d?typeof d.then=='function'?d.then(_p):postMessage(d,d.__transferList):postMessage(d)};onmessage=function(e){_p(h.apply(self,e.data))}}", msg, tfl, function (err, res) {
var assignStr = '';
var transfer = (replaceTransfer
? replaceTransfer instanceof Array
? replaceTransfer
: []
: tfl);
var msg = [];
for (var _i = 0, exec_1 = exec; _i < exec_1.length; _i++) {
var cmd = exec_1[_i];
if (typeof cmd != 'string') {
cmd = cmd[0].replace(toReplaceRE, "e.data[" + (msg.push(cmd[1]) - 1) + "]");
}
assignStr += cmd;
}
if (!replaceTransfer) {
var tfKey = typeof Symbol == 'undefined' ? "__iwtf" + rand() + "__" : Symbol();
for (var i = 0; i < transfer.length; ++i) {
var buf = transfer[i];
if (buf[tfKey])
transfer.splice(i--, 1);
buf[tfKey] = 1;
}
}
var worker = wk(str + ";onmessage=function(e){" + assignStr + "var v=" + fn + ";var _p=function(d){typeof d.then=='function'?d.then(_p,_e):postMessage(d,d?d.__transfer:[])};var _e=function(e){!(e instanceof Error)&&(e=new Error(e));postMessage({__iwerr__:{s:e.stack,m:e.message,n:e.name}})};onmessage=function(e){try{_p(v.apply(self,e.data))}catch(e){_e(e)}}}", msg, transfer, function (err, res) {
++runCount;
currentCb(err, res);
var rtErr = res &&
res.__iwerr__;
if (rtErr) {
err = new (globalEnv[rtErr.n] || Error)();
err.message = rtErr.m;
err.name = rtErr.n;
err.stack = rtErr.s;
currentCb(err, null);
}
else if (err) {
for (; runCount <= callCount; ++runCount)
currentCb(err, res);
wfn.close();
}
else
currentCb(err, res);
});

@@ -232,0 +330,0 @@ var closed = false;

@@ -1,9 +0,2 @@

import { WorkerTransfer } from './node-worker';
export declare type Registry = Record<string, boolean>;
export declare type Context = [
string,
Record<string, unknown>,
WorkerTransfer[],
Registry
];
export declare type Context = [string, Array<string | [string, unknown]>, unknown[]];
export declare type DepList = () => unknown[];

@@ -34,5 +27,11 @@ /**

* here as well.
* @param replaceTransfer The list of objects to replace the default transfer
* list with. If you provide an array of transferable
* items, they will be used; if you provide the value
* `true`, `isoworker` will refrain from the default
* behavior of automatically transferring everything
* it can.
* @returns A function that accepts parameters and, as the last argument, a
* callback to use when the worker returns.
*/
export declare function workerize<TA extends unknown[], TR>(fn: (...args: TA) => TR, deps: DepList): Workerized<TA, TR>;
export declare function workerize<TA extends unknown[], TR>(fn: (...args: TA) => TR, deps: DepList, replaceTransfer?: unknown[] | boolean): Workerized<TA, TR>;
{
"name": "isoworker",
"version": "0.1.3",
"version": "0.2.0",
"description": "Isomorphic workerization with dependencies",

@@ -30,3 +30,4 @@ "main": "./lib/index.cjs",

"scripts": {
"build": "yarn lint && tsc && tsc --project tsconfig.esm.json && node -r ts-node/register scripts/rewriteBuilds.ts",
"build": "yarn lint && yarn build:lib",
"build:lib": "tsc && tsc --project tsconfig.esm.json && node -r ts-node/register scripts/rewriteBuilds.ts",
"lint": "eslint --fix \"src/**/*.ts\"",

@@ -33,0 +34,0 @@ "prepack": "yarn build"

@@ -9,3 +9,3 @@ # isoworker

If you're not experienced with build tools or are creating a library, however, using worker threads is virtually impossible. Nothing works on all platforms, with all build tools, and doesn't require embedding the codebase as a string. That's why most "asynchronous" packages such as [JSZip](https://github.com/Stuk/jszip) still run on the main thread and still hang the browser when doing a lot of work.
If you're not experienced with build tools or are creating a library, however, using worker threads is virtually impossible. Nothing works on all platforms, with all build tools, and doesn't require embedding the codebase as a string. That's why most "asynchronous" packages such as [JSZip](https://github.com/Stuk/jszip) still run on the main thread in an event loop. While running in the event loop is fine, it doesn't allow your JS to take advantage of multiple CPU cores.

@@ -65,5 +65,3 @@ This package abstracts all difficulties away by making your standard functions magically run in a separate thread in all environments. You don't even need a new file to run your code, and unlike other workerization packages, you can actually call other functions and use variables from your worker.

// got 0 from worker
asyncCount((err, res) => {
asyncCount.close();
});
asyncCount(() => {});
// 1

@@ -74,2 +72,6 @@

console.log(number); // 0
// When you're finished using the function, call .close() to free the
// resources used by the worker thread
asyncCount.close();
```

@@ -83,3 +85,3 @@

// Promises are automatically resolved, so using async/await is fine
const runWasmSync = async (wasmName, method, ...args) => {
const runWasmMainThread = async (wasmName, method, ...args) => {
if (!wasm[wasmName]) {

@@ -92,3 +94,3 @@ wasm[wasmName] = (await WebAssembly.instantiateStreaming(

}
const runWasm = workerize(runWasmSync, () => [wasm]);
const runWasm = workerize(runWasmMainThread, () => [wasm]);

@@ -105,3 +107,82 @@ // If /wasm-files/hello.wasm exports a sayHelloTo method

The workerizer supports complex types (including symbols, functions, classes, and instances of those classes) with infinite nesting thanks to a nifty recursive serializer.
```js
// ES5 style class works
function Example1() {
this.y = 2;
}
Example1.prototype.z = function() {
return this.y * 2;
}
function Example2() {
this.x = 3;
Example1.call(this);
}
// Prototypal inheritance/extension works
Example2.prototype = Object.create(Example1.prototype);
// Normal extension works as well
class OtherClass extends Example2 {
constructor() {
super();
console.log('Created an OtherClass');
}
getResult() {
return 'z() = ' + this.z();
}
}
const dat = new OtherClass(); // Created an OtherClass
const getZ = workerize(() => {
// On the worker thread, now dat.y is increased by dat.x
dat.y += dat.x;
return dat.getResult();
}, () => [dat]);
// Note than when doing this, "Created an OtherClass" is not logged
// Your classes and objects are created without construction or mutation
getZ((err, result) => console.log(result)) // z() = 10
getZ((err, result) => console.log(result)) // z() = 16
// Nothing changed on the main thread
console.log(dat.y) // 2
console.log(dat.getResult()); // z() = 4
```
If you need to maximize performance and know how to use [Transferables](https://developer.mozilla.org/en-US/docs/Web/API/Transferable), you can set a list of transferables by returning `__transfer` in your workerized function.
```js
// Since Uint8Array and Math.random() are in the global environment,
// they don't need to be added to the dependency list
const getRandomBuffer = workerize((bufLen) => {
if (bufLen > 2 ** 30) {
throw new TypeError('cannot create over 1GB random values');
}
const buf = new Uint8Array(bufLen);
for (let i = 0; i < bufLen; ++i) {
// Uint8Array automatically takes the floor
buf[i] = Math.random() * 256;
}
buf.__transfer = [buf.buffer];
return buf;
}, () => []);
getRandomBuffer(2 ** 28, (err, result) => {
console.log(err); // null
console.log(result); // Uint8Array(1073741824) [ ... ]
});
getRandomBuffer(2 ** 31, (err, result) => {
console.log(err); // TypeError: cannot process over 1GB
console.log(result); // null
});
```
If you're a library author, you may want to use the context creation API but don't need the workerization support. In that case, use `createContext(() => [dep1, dep2])` and use the return value `[code, initMessage, transferList]`. Take a look at the source code to understand how to use these. Effectively, `code` encodes most of the dependencies, `initMessage` contains code to be executed on the first message (and occasionally values that must be passed to that code), and `transferList` is the array of `Transferable`s that can optionally be transferred in the initialization message for much better initialization performance at the cost of breaking the implementation on the main thread if it depends on values that were transferred.
## License
MIT

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc