Comparing version 0.6.2 to 0.6.3
@@ -16,447 +16,449 @@ /** | ||
var Clooney = (function (exports) { | ||
'use strict'; | ||
'use strict'; | ||
/** | ||
* Copyright 2017 Google Inc. All Rights Reserved. | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
const Comlink = (function () { | ||
const TRANSFERABLE_TYPES = [ArrayBuffer, MessagePort]; | ||
const uid = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); | ||
const proxyValueSymbol = Symbol('proxyValue'); | ||
const throwSymbol = Symbol('throw'); | ||
const proxyTransferHandler = { | ||
canHandle: (obj) => obj && obj[proxyValueSymbol], | ||
serialize: (obj) => { | ||
const { port1, port2 } = new MessageChannel(); | ||
expose(obj, port1); | ||
return port2; | ||
}, | ||
deserialize: (obj) => { | ||
return proxy(obj); | ||
}, | ||
}; | ||
const throwTransferHandler = { | ||
canHandle: (obj) => obj && obj[throwSymbol], | ||
serialize: (obj) => obj.toString() + '\n' + obj.stack, | ||
deserialize: (obj) => { | ||
throw Error(obj); | ||
}, | ||
}; | ||
/* export */ const transferHandlers = new Map([ | ||
['PROXY', proxyTransferHandler], | ||
['THROW', throwTransferHandler], | ||
]); | ||
let pingPongMessageCounter = 0; | ||
/* export */ function proxy(endpoint) { | ||
if (isWindow(endpoint)) | ||
endpoint = windowEndpoint(endpoint); | ||
if (!isEndpoint(endpoint)) | ||
throw Error('endpoint does not have all of addEventListener, removeEventListener and postMessage defined'); | ||
activateEndpoint(endpoint); | ||
return cbProxy(async (irequest) => { | ||
let args = []; | ||
if (irequest.type === 'APPLY' || irequest.type === 'CONSTRUCT') | ||
args = irequest.argumentsList.map(wrapValue); | ||
const response = await pingPongMessage(endpoint, Object.assign({}, irequest, { argumentsList: args }), transferableProperties(args)); | ||
const result = response.data; | ||
return unwrapValue(result.value); | ||
}); | ||
} | ||
/* export */ function proxyValue(obj) { | ||
obj[proxyValueSymbol] = true; | ||
return obj; | ||
} | ||
/* export */ function expose(rootObj, endpoint) { | ||
if (isWindow(endpoint)) | ||
endpoint = windowEndpoint(endpoint); | ||
if (!isEndpoint(endpoint)) | ||
throw Error('endpoint does not have all of addEventListener, removeEventListener and postMessage defined'); | ||
activateEndpoint(endpoint); | ||
attachMessageHandler(endpoint, async function (event) { | ||
if (!event.data.id) | ||
return; | ||
const irequest = event.data; | ||
let that = await irequest.callPath.slice(0, -1).reduce((obj, propName) => obj[propName], rootObj); | ||
let obj = await irequest.callPath.reduce((obj, propName) => obj[propName], rootObj); | ||
let iresult = obj; | ||
let args = []; | ||
if (irequest.type === 'APPLY' || irequest.type === 'CONSTRUCT') | ||
args = irequest.argumentsList.map(unwrapValue); | ||
if (irequest.type === 'APPLY') { | ||
try { | ||
iresult = await obj.apply(that, args); | ||
/** | ||
* Copyright 2017 Google Inc. All Rights Reserved. | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
const Comlink = (function () { | ||
const TRANSFERABLE_TYPES = [ArrayBuffer, MessagePort]; | ||
const uid = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); | ||
const proxyValueSymbol = Symbol('proxyValue'); | ||
const throwSymbol = Symbol('throw'); | ||
const proxyTransferHandler = { | ||
canHandle: (obj) => obj && obj[proxyValueSymbol], | ||
serialize: (obj) => { | ||
const { port1, port2 } = new MessageChannel(); | ||
expose(obj, port1); | ||
return port2; | ||
}, | ||
deserialize: (obj) => { | ||
return proxy(obj); | ||
}, | ||
}; | ||
const throwTransferHandler = { | ||
canHandle: (obj) => obj && obj[throwSymbol], | ||
serialize: (obj) => obj.toString() + '\n' + obj.stack, | ||
deserialize: (obj) => { | ||
throw Error(obj); | ||
}, | ||
}; | ||
/* export */ const transferHandlers = new Map([ | ||
['PROXY', proxyTransferHandler], | ||
['THROW', throwTransferHandler], | ||
]); | ||
let pingPongMessageCounter = 0; | ||
/* export */ function proxy(endpoint) { | ||
if (isWindow(endpoint)) | ||
endpoint = windowEndpoint(endpoint); | ||
if (!isEndpoint(endpoint)) | ||
throw Error('endpoint does not have all of addEventListener, removeEventListener and postMessage defined'); | ||
activateEndpoint(endpoint); | ||
return cbProxy(async (irequest) => { | ||
let args = []; | ||
if (irequest.type === 'APPLY' || irequest.type === 'CONSTRUCT') | ||
args = irequest.argumentsList.map(wrapValue); | ||
const response = await pingPongMessage(endpoint, Object.assign({}, irequest, { argumentsList: args }), transferableProperties(args)); | ||
const result = response.data; | ||
return unwrapValue(result.value); | ||
}); | ||
} | ||
/* export */ function proxyValue(obj) { | ||
obj[proxyValueSymbol] = true; | ||
return obj; | ||
} | ||
/* export */ function expose(rootObj, endpoint) { | ||
if (isWindow(endpoint)) | ||
endpoint = windowEndpoint(endpoint); | ||
if (!isEndpoint(endpoint)) | ||
throw Error('endpoint does not have all of addEventListener, removeEventListener and postMessage defined'); | ||
activateEndpoint(endpoint); | ||
attachMessageHandler(endpoint, async function (event) { | ||
if (!event.data.id) | ||
return; | ||
const irequest = event.data; | ||
let that = await irequest.callPath.slice(0, -1).reduce((obj, propName) => obj[propName], rootObj); | ||
let obj = await irequest.callPath.reduce((obj, propName) => obj[propName], rootObj); | ||
let iresult = obj; | ||
let args = []; | ||
if (irequest.type === 'APPLY' || irequest.type === 'CONSTRUCT') | ||
args = irequest.argumentsList.map(unwrapValue); | ||
if (irequest.type === 'APPLY') { | ||
try { | ||
iresult = await obj.apply(that, args); | ||
} | ||
catch (e) { | ||
iresult = e; | ||
iresult[throwSymbol] = true; | ||
} | ||
} | ||
catch (e) { | ||
iresult = e; | ||
iresult[throwSymbol] = true; | ||
if (irequest.type === 'CONSTRUCT') { | ||
try { | ||
iresult = new obj(...args); // eslint-disable-line new-cap | ||
iresult = proxyValue(iresult); | ||
} | ||
catch (e) { | ||
iresult = e; | ||
iresult[throwSymbol] = true; | ||
} | ||
} | ||
} | ||
if (irequest.type === 'CONSTRUCT') { | ||
try { | ||
iresult = new obj(...args); // eslint-disable-line new-cap | ||
iresult = proxyValue(iresult); | ||
if (irequest.type === 'SET') { | ||
obj[irequest.property] = irequest.value; | ||
// FIXME: ES6 Proxy Handler `set` methods are supposed to return a | ||
// boolean. To show good will, we return true asynchronously ¯\_(ツ)_/¯ | ||
iresult = true; | ||
} | ||
catch (e) { | ||
iresult = e; | ||
iresult[throwSymbol] = true; | ||
iresult = makeInvocationResult(iresult); | ||
iresult.id = irequest.id; | ||
return endpoint.postMessage(iresult, transferableProperties([iresult])); | ||
}); | ||
} | ||
function wrapValue(arg) { | ||
// Is arg itself handled by a TransferHandler? | ||
for (const [key, transferHandler] of transferHandlers.entries()) { | ||
if (transferHandler.canHandle(arg)) { | ||
return { | ||
type: key, | ||
value: transferHandler.serialize(arg), | ||
}; | ||
} | ||
} | ||
if (irequest.type === 'SET') { | ||
obj[irequest.property] = irequest.value; | ||
// FIXME: ES6 Proxy Handler `set` methods are supposed to return a | ||
// boolean. To show good will, we return true asynchronously ¯\_(ツ)_/¯ | ||
iresult = true; | ||
// If not, traverse the entire object and find handled values. | ||
let wrappedChildren = []; | ||
for (const item of iterateAllProperties(arg)) { | ||
for (const [key, transferHandler] of transferHandlers.entries()) { | ||
if (transferHandler.canHandle(item.value)) { | ||
wrappedChildren.push({ | ||
path: item.path, | ||
wrappedValue: { | ||
type: key, | ||
value: transferHandler.serialize(item.value), | ||
}, | ||
}); | ||
} | ||
} | ||
} | ||
iresult = makeInvocationResult(iresult); | ||
iresult.id = irequest.id; | ||
return endpoint.postMessage(iresult, transferableProperties([iresult])); | ||
}); | ||
} | ||
function wrapValue(arg) { | ||
// Is arg itself handled by a TransferHandler? | ||
for (const [key, transferHandler] of transferHandlers.entries()) { | ||
if (transferHandler.canHandle(arg)) { | ||
return { | ||
type: key, | ||
value: transferHandler.serialize(arg), | ||
}; | ||
for (const wrappedChild of wrappedChildren) { | ||
const container = wrappedChild.path.slice(0, -1).reduce((obj, key) => obj[key], arg); | ||
container[wrappedChild.path[wrappedChild.path.length - 1]] = null; | ||
} | ||
return { | ||
type: 'RAW', | ||
value: arg, | ||
wrappedChildren, | ||
}; | ||
} | ||
// If not, traverse the entire object and find handled values. | ||
let wrappedChildren = []; | ||
for (const item of iterateAllProperties(arg)) { | ||
for (const [key, transferHandler] of transferHandlers.entries()) { | ||
if (transferHandler.canHandle(item.value)) { | ||
wrappedChildren.push({ | ||
path: item.path, | ||
wrappedValue: { | ||
type: key, | ||
value: transferHandler.serialize(item.value), | ||
}, | ||
}); | ||
function unwrapValue(arg) { | ||
if (transferHandlers.has(arg.type)) { | ||
const transferHandler = transferHandlers.get(arg.type); | ||
return transferHandler.deserialize(arg.value); | ||
} | ||
else if (isRawWrappedValue(arg)) { | ||
for (const wrappedChildValue of (arg.wrappedChildren || [])) { | ||
if (!transferHandlers.has(wrappedChildValue.wrappedValue.type)) | ||
throw Error(`Unknown value type "${arg.type}" at ${wrappedChildValue.path.join('.')}`); | ||
const transferHandler = transferHandlers.get(wrappedChildValue.wrappedValue.type); | ||
const newValue = transferHandler.deserialize(wrappedChildValue.wrappedValue.value); | ||
replaceValueInObjectAtPath(arg.value, wrappedChildValue.path, newValue); | ||
} | ||
return arg.value; | ||
} | ||
else { | ||
throw Error(`Unknown value type "${arg.type}"`); | ||
} | ||
} | ||
for (const wrappedChild of wrappedChildren) { | ||
const container = wrappedChild.path.slice(0, -1).reduce((obj, key) => obj[key], arg); | ||
container[wrappedChild.path[wrappedChild.path.length - 1]] = null; | ||
function replaceValueInObjectAtPath(obj, path, newVal) { | ||
const lastKey = path.slice(-1)[0]; | ||
const lastObj = path.slice(0, -1).reduce((obj, key) => obj[key], obj); | ||
lastObj[lastKey] = newVal; | ||
} | ||
return { | ||
type: 'RAW', | ||
value: arg, | ||
wrappedChildren, | ||
}; | ||
} | ||
function unwrapValue(arg) { | ||
if (transferHandlers.has(arg.type)) { | ||
const transferHandler = transferHandlers.get(arg.type); | ||
return transferHandler.deserialize(arg.value); | ||
function isRawWrappedValue(arg) { | ||
return arg.type === 'RAW'; | ||
} | ||
else if (isRawWrappedValue(arg)) { | ||
for (const wrappedChildValue of (arg.wrappedChildren || [])) { | ||
if (!transferHandlers.has(wrappedChildValue.wrappedValue.type)) | ||
throw Error(`Unknown value type "${arg.type}" at ${wrappedChildValue.path.join('.')}`); | ||
const transferHandler = transferHandlers.get(wrappedChildValue.wrappedValue.type); | ||
const newValue = transferHandler.deserialize(wrappedChildValue.wrappedValue.value); | ||
replaceValueInObjectAtPath(arg.value, wrappedChildValue.path, newValue); | ||
} | ||
return arg.value; | ||
function windowEndpoint(w) { | ||
if (self.constructor.name !== 'Window') | ||
throw Error('self is not a window'); | ||
return { | ||
addEventListener: self.addEventListener.bind(self), | ||
removeEventListener: self.removeEventListener.bind(self), | ||
postMessage: (msg, transfer) => w.postMessage(msg, '*', transfer), | ||
}; | ||
} | ||
else { | ||
throw Error(`Unknown value type "${arg.type}"`); | ||
function isEndpoint(endpoint) { | ||
return 'addEventListener' in endpoint && 'removeEventListener' in endpoint && 'postMessage' in endpoint; | ||
} | ||
} | ||
function replaceValueInObjectAtPath(obj, path, newVal) { | ||
const lastKey = path.slice(-1)[0]; | ||
const lastObj = path.slice(0, -1).reduce((obj, key) => obj[key], obj); | ||
lastObj[lastKey] = newVal; | ||
} | ||
function isRawWrappedValue(arg) { | ||
return arg.type === 'RAW'; | ||
} | ||
function windowEndpoint(w) { | ||
if (self.constructor.name !== 'Window') | ||
throw Error('self is not a window'); | ||
return { | ||
addEventListener: self.addEventListener.bind(self), | ||
removeEventListener: self.removeEventListener.bind(self), | ||
postMessage: (msg, transfer) => w.postMessage(msg, '*', transfer), | ||
}; | ||
} | ||
function isEndpoint(endpoint) { | ||
return 'addEventListener' in endpoint && 'removeEventListener' in endpoint && 'postMessage' in endpoint; | ||
} | ||
function activateEndpoint(endpoint) { | ||
if (isMessagePort(endpoint)) | ||
endpoint.start(); | ||
} | ||
function attachMessageHandler(endpoint, f) { | ||
// Checking all possible types of `endpoint` manually satisfies TypeScript’s | ||
// type checker. Not sure why the inference is failing here. Since it’s | ||
// unnecessary code I’m going to resort to `any` for now. | ||
// if(isWorker(endpoint)) | ||
// endpoint.addEventListener('message', f); | ||
// if(isMessagePort(endpoint)) | ||
// endpoint.addEventListener('message', f); | ||
// if(isOtherWindow(endpoint)) | ||
// endpoint.addEventListener('message', f); | ||
endpoint.addEventListener('message', f); | ||
} | ||
function detachMessageHandler(endpoint, f) { | ||
// Same as above. | ||
endpoint.removeEventListener('message', f); | ||
} | ||
function isMessagePort(endpoint) { | ||
return endpoint.constructor.name === 'MessagePort'; | ||
} | ||
function isWindow(endpoint) { | ||
// TODO: This doesn’t work on cross-origin iframes. | ||
// return endpoint.constructor.name === 'Window'; | ||
return ['window', 'length', 'location', 'parent', 'opener'].every(prop => prop in endpoint); | ||
} | ||
/** | ||
* `pingPongMessage` sends a `postMessage` and waits for a reply. Replies are | ||
* identified by a unique id that is attached to the payload. | ||
*/ | ||
function pingPongMessage(endpoint, msg, transferables) { | ||
const id = `${uid}-${pingPongMessageCounter++}`; | ||
return new Promise(resolve => { | ||
attachMessageHandler(endpoint, function handler(event) { | ||
if (event.data.id !== id) | ||
return; | ||
detachMessageHandler(endpoint, handler); | ||
resolve(event); | ||
function activateEndpoint(endpoint) { | ||
if (isMessagePort(endpoint)) | ||
endpoint.start(); | ||
} | ||
function attachMessageHandler(endpoint, f) { | ||
// Checking all possible types of `endpoint` manually satisfies TypeScript’s | ||
// type checker. Not sure why the inference is failing here. Since it’s | ||
// unnecessary code I’m going to resort to `any` for now. | ||
// if(isWorker(endpoint)) | ||
// endpoint.addEventListener('message', f); | ||
// if(isMessagePort(endpoint)) | ||
// endpoint.addEventListener('message', f); | ||
// if(isOtherWindow(endpoint)) | ||
// endpoint.addEventListener('message', f); | ||
endpoint.addEventListener('message', f); | ||
} | ||
function detachMessageHandler(endpoint, f) { | ||
// Same as above. | ||
endpoint.removeEventListener('message', f); | ||
} | ||
function isMessagePort(endpoint) { | ||
return endpoint.constructor.name === 'MessagePort'; | ||
} | ||
function isWindow(endpoint) { | ||
// TODO: This doesn’t work on cross-origin iframes. | ||
// return endpoint.constructor.name === 'Window'; | ||
return ['window', 'length', 'location', 'parent', 'opener'].every(prop => prop in endpoint); | ||
} | ||
/** | ||
* `pingPongMessage` sends a `postMessage` and waits for a reply. Replies are | ||
* identified by a unique id that is attached to the payload. | ||
*/ | ||
function pingPongMessage(endpoint, msg, transferables) { | ||
const id = `${uid}-${pingPongMessageCounter++}`; | ||
return new Promise(resolve => { | ||
attachMessageHandler(endpoint, function handler(event) { | ||
if (event.data.id !== id) | ||
return; | ||
detachMessageHandler(endpoint, handler); | ||
resolve(event); | ||
}); | ||
// Copy msg and add `id` property | ||
msg = Object.assign({}, msg, { id }); | ||
endpoint.postMessage(msg, transferables); | ||
}); | ||
// Copy msg and add `id` property | ||
msg = Object.assign({}, msg, { id }); | ||
endpoint.postMessage(msg, transferables); | ||
}); | ||
} | ||
function cbProxy(cb, callPath = []) { | ||
return new Proxy(function () { }, { | ||
construct(_target, argumentsList, proxy) { | ||
return cb({ | ||
type: 'CONSTRUCT', | ||
callPath, | ||
argumentsList, | ||
}); | ||
}, | ||
apply(_target, _thisArg, argumentsList) { | ||
// We use `bind` as an indicator to have a remote function bound locally. | ||
// The actual target for `bind()` is currently ignored. | ||
if (callPath[callPath.length - 1] === 'bind') | ||
return cbProxy(cb, callPath.slice(0, -1)); | ||
return cb({ | ||
type: 'APPLY', | ||
callPath, | ||
argumentsList, | ||
}); | ||
}, | ||
get(_target, property, proxy) { | ||
if (property === 'then' && callPath.length === 0) { | ||
return { then: () => proxy }; | ||
} | ||
else if (property === 'then') { | ||
const r = cb({ | ||
type: 'GET', | ||
} | ||
function cbProxy(cb, callPath = []) { | ||
return new Proxy(function () { }, { | ||
construct(_target, argumentsList, proxy) { | ||
return cb({ | ||
type: 'CONSTRUCT', | ||
callPath, | ||
argumentsList, | ||
}); | ||
return Promise.resolve(r).then.bind(r); | ||
} | ||
else { | ||
return cbProxy(cb, callPath.concat(property)); | ||
} | ||
}, | ||
set(_target, property, value, _proxy) { | ||
return cb({ | ||
type: 'SET', | ||
callPath, | ||
property, | ||
value, | ||
}); | ||
}, | ||
}); | ||
} | ||
function isTransferable(thing) { | ||
return TRANSFERABLE_TYPES.some(type => thing instanceof type); | ||
} | ||
function* iterateAllProperties(value, path = [], visited = null) { | ||
if (!value) | ||
return; | ||
if (!visited) | ||
visited = new WeakSet(); | ||
if (visited.has(value)) | ||
return; | ||
if (typeof value === 'string') | ||
return; | ||
if (typeof value === 'object') | ||
visited.add(value); | ||
yield { value, path }; | ||
let keys = Object.keys(value); | ||
for (const key of keys) | ||
yield* iterateAllProperties(value[key], [...path, key], visited); | ||
} | ||
function transferableProperties(obj) { | ||
const r = []; | ||
for (const prop of iterateAllProperties(obj)) { | ||
if (isTransferable(prop.value)) | ||
r.push(prop.value); | ||
}, | ||
apply(_target, _thisArg, argumentsList) { | ||
// We use `bind` as an indicator to have a remote function bound locally. | ||
// The actual target for `bind()` is currently ignored. | ||
if (callPath[callPath.length - 1] === 'bind') | ||
return cbProxy(cb, callPath.slice(0, -1)); | ||
return cb({ | ||
type: 'APPLY', | ||
callPath, | ||
argumentsList, | ||
}); | ||
}, | ||
get(_target, property, proxy) { | ||
if (property === 'then' && callPath.length === 0) { | ||
return { then: () => proxy }; | ||
} | ||
else if (property === 'then') { | ||
const r = cb({ | ||
type: 'GET', | ||
callPath, | ||
}); | ||
return Promise.resolve(r).then.bind(r); | ||
} | ||
else { | ||
return cbProxy(cb, callPath.concat(property)); | ||
} | ||
}, | ||
set(_target, property, value, _proxy) { | ||
return cb({ | ||
type: 'SET', | ||
callPath, | ||
property, | ||
value, | ||
}); | ||
}, | ||
}); | ||
} | ||
return r; | ||
} | ||
function makeInvocationResult(obj) { | ||
for (const [type, transferHandler] of transferHandlers.entries()) { | ||
if (transferHandler.canHandle(obj)) { | ||
const value = transferHandler.serialize(obj); | ||
return { | ||
value: { type, value }, | ||
}; | ||
function isTransferable(thing) { | ||
return TRANSFERABLE_TYPES.some(type => thing instanceof type); | ||
} | ||
function* iterateAllProperties(value, path = [], visited = null) { | ||
if (!value) | ||
return; | ||
if (!visited) | ||
visited = new WeakSet(); | ||
if (visited.has(value)) | ||
return; | ||
if (typeof value === 'string') | ||
return; | ||
if (typeof value === 'object') | ||
visited.add(value); | ||
if (ArrayBuffer.isView(value)) | ||
return; | ||
yield { value, path }; | ||
const keys = Object.keys(value); | ||
for (const key of keys) | ||
yield* iterateAllProperties(value[key], [...path, key], visited); | ||
} | ||
function transferableProperties(obj) { | ||
const r = []; | ||
for (const prop of iterateAllProperties(obj)) { | ||
if (isTransferable(prop.value)) | ||
r.push(prop.value); | ||
} | ||
return r; | ||
} | ||
return { | ||
value: { | ||
type: 'RAW', | ||
value: obj, | ||
}, | ||
}; | ||
} | ||
return { proxy, proxyValue, transferHandlers, expose }; | ||
})(); | ||
function makeInvocationResult(obj) { | ||
for (const [type, transferHandler] of transferHandlers.entries()) { | ||
if (transferHandler.canHandle(obj)) { | ||
const value = transferHandler.serialize(obj); | ||
return { | ||
value: { type, value }, | ||
}; | ||
} | ||
} | ||
return { | ||
value: { | ||
type: 'RAW', | ||
value: obj, | ||
}, | ||
}; | ||
} | ||
return { proxy, proxyValue, transferHandlers, expose }; | ||
})(); | ||
/** | ||
* Copyright 2018 Google Inc. All Rights Reserved. | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
// Automatically proxy functions | ||
Comlink.transferHandlers.set('FUNCTION', { | ||
canHandle(obj) { | ||
return obj instanceof Function; | ||
}, | ||
serialize(obj) { | ||
const { port1, port2 } = new MessageChannel(); | ||
Comlink.expose(obj, port1); | ||
return port2; | ||
}, | ||
deserialize(obj) { | ||
return Comlink.proxy(obj); | ||
}, | ||
}); | ||
// Automatically proxy events | ||
Comlink.transferHandlers.set('EVENT', { | ||
canHandle(obj) { | ||
return obj instanceof Event; | ||
}, | ||
serialize(obj) { | ||
return { | ||
targetId: obj && obj.target && obj.target.id, | ||
targetClassList: obj && obj.target && obj.target.classList && [...obj.target.classList], | ||
detail: obj && obj.detail, | ||
data: obj && obj.data, | ||
}; | ||
}, | ||
deserialize(obj) { | ||
return obj; | ||
}, | ||
}); | ||
/** | ||
* `asRemoteValue` marks a value. If a marked value is used as an parameter or return value, it will not be transferred but instead proxied. | ||
*/ | ||
const asRemoteValue = Comlink.proxyValue; | ||
/** | ||
* `defaultWorkerSrc` is the path passed to the `new Worker()` call. It’s recommended to not change this variable but instead overload `newWorkerFunc`. | ||
*/ | ||
let defaultWorkerSrc = 'document' in self ? document.currentScript && document.currentScript.src : ''; | ||
const defaultOpts = { | ||
maxNumContainers: 1, | ||
newWorkerFunc: async () => new Worker(defaultWorkerSrc), | ||
}; | ||
/** | ||
* `RoundRobingStrategy` creates up to n containers and cycles through the containers with every `spawn` call. | ||
*/ | ||
class RoundRobinStrategy { | ||
constructor(opts = {}) { | ||
this._nextIndex = 0; | ||
this._options = Object.assign({}, defaultOpts, opts); | ||
this._containers = new Array(this._options.maxNumContainers).fill(null); | ||
} | ||
async _initOrGetContainer(i) { | ||
if (i >= this._containers.length) | ||
throw Error('No worker available'); | ||
if (!this._containers[i]) { | ||
const worker = await this._options.newWorkerFunc(); | ||
const remote = Comlink.proxy(worker); | ||
this._containers[i] = { | ||
spawn: remote.spawn.bind(spawn), | ||
terminate: worker.terminate.bind(worker), | ||
/** | ||
* Copyright 2018 Google Inc. All Rights Reserved. | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
// Automatically proxy functions | ||
Comlink.transferHandlers.set('FUNCTION', { | ||
canHandle(obj) { | ||
return obj instanceof Function; | ||
}, | ||
serialize(obj) { | ||
const { port1, port2 } = new MessageChannel(); | ||
Comlink.expose(obj, port1); | ||
return port2; | ||
}, | ||
deserialize(obj) { | ||
return Comlink.proxy(obj); | ||
}, | ||
}); | ||
// Automatically proxy events | ||
Comlink.transferHandlers.set('EVENT', { | ||
canHandle(obj) { | ||
return obj instanceof Event; | ||
}, | ||
serialize(obj) { | ||
return { | ||
targetId: obj && obj.target && obj.target.id, | ||
targetClassList: obj && obj.target && obj.target.classList && [...obj.target.classList], | ||
detail: obj && obj.detail, | ||
data: obj && obj.data, | ||
}; | ||
}, | ||
deserialize(obj) { | ||
return obj; | ||
}, | ||
}); | ||
/** | ||
* `asRemoteValue` marks a value. If a marked value is used as an parameter or return value, it will not be transferred but instead proxied. | ||
*/ | ||
const asRemoteValue = Comlink.proxyValue; | ||
/** | ||
* `defaultWorkerSrc` is the path passed to the `new Worker()` call. It’s recommended to not change this variable but instead overload `newWorkerFunc`. | ||
*/ | ||
let defaultWorkerSrc = 'document' in self ? document.currentScript && document.currentScript.src : ''; | ||
const defaultOpts = { | ||
maxNumContainers: 1, | ||
newWorkerFunc: async () => new Worker(defaultWorkerSrc), | ||
}; | ||
/** | ||
* `RoundRobingStrategy` creates up to n containers and cycles through the containers with every `spawn` call. | ||
*/ | ||
class RoundRobinStrategy { | ||
constructor(opts = {}) { | ||
this._nextIndex = 0; | ||
this._options = Object.assign({}, defaultOpts, opts); | ||
this._containers = new Array(this._options.maxNumContainers).fill(null); | ||
} | ||
return this._containers[i]; | ||
async _initOrGetContainer(i) { | ||
if (i >= this._containers.length) | ||
throw Error('No worker available'); | ||
if (!this._containers[i]) { | ||
const worker = await this._options.newWorkerFunc(); | ||
const remote = Comlink.proxy(worker); | ||
this._containers[i] = { | ||
spawn: remote.spawn.bind(spawn), | ||
terminate: worker.terminate.bind(worker), | ||
}; | ||
} | ||
return this._containers[i]; | ||
} | ||
async _getNextContainer(opts) { | ||
const w = await this._initOrGetContainer(this._nextIndex); | ||
this._nextIndex = (this._nextIndex + 1) % this._options.maxNumContainers; | ||
return w; | ||
} | ||
async spawn(actor, constructorArgs = [], opts = {}) { | ||
const container = await this._getNextContainer(opts); | ||
return await container.spawn(actor.toString(), constructorArgs); | ||
} | ||
async terminate() { | ||
this._containers.filter(c => c).forEach(container => container.terminate()); | ||
this._containers.length = 0; | ||
} | ||
get terminated() { | ||
return this._containers.length <= 0; | ||
} | ||
} | ||
async _getNextContainer(opts) { | ||
const w = await this._initOrGetContainer(this._nextIndex); | ||
this._nextIndex = (this._nextIndex + 1) % this._options.maxNumContainers; | ||
return w; | ||
let defaultStrategy = new RoundRobinStrategy(); | ||
async function spawn(actor, constructorArgs = [], opts = {}) { | ||
return defaultStrategy.spawn(actor, constructorArgs, opts); | ||
} | ||
async spawn(actor, constructorArgs = [], opts = {}) { | ||
const container = await this._getNextContainer(opts); | ||
return await container.spawn(actor.toString(), constructorArgs); | ||
function makeContainer(endpoint = self) { | ||
Comlink.expose({ | ||
async spawn(actorCode, constructorArgs) { | ||
const actor = (new Function(`return ${actorCode};`))(); | ||
return Comlink.proxyValue(new actor(...constructorArgs)); // eslint-disable-line new-cap | ||
}, | ||
}, endpoint); | ||
} | ||
async terminate() { | ||
this._containers.filter(c => c).forEach(container => container.terminate()); | ||
this._containers.length = 0; | ||
function isWorker() { | ||
// I’d have to import lib.webworker.d.ts to have access to | ||
// WorkerGlobalScope, but I can’t because it conflicts with lib.dom.d.ts. | ||
const wgs = self['WorkerGlobalScope']; | ||
return wgs && self instanceof wgs; | ||
} | ||
get terminated() { | ||
return this._containers.length <= 0; | ||
} | ||
} | ||
let defaultStrategy = new RoundRobinStrategy(); | ||
async function spawn(actor, constructorArgs = [], opts = {}) { | ||
return defaultStrategy.spawn(actor, constructorArgs, opts); | ||
} | ||
function makeContainer(endpoint = self) { | ||
Comlink.expose({ | ||
async spawn(actorCode, constructorArgs) { | ||
const actor = (new Function(`return ${actorCode};`))(); | ||
return Comlink.proxyValue(new actor(...constructorArgs)); // eslint-disable-line new-cap | ||
}, | ||
}, endpoint); | ||
} | ||
function isWorker() { | ||
// I’d have to import lib.webworker.d.ts to have access to | ||
// WorkerGlobalScope, but I can’t because it conflicts with lib.dom.d.ts. | ||
const wgs = self['WorkerGlobalScope']; | ||
return wgs && self instanceof wgs; | ||
} | ||
// TODO: Find a way to opt-out of autostart | ||
if (isWorker()) | ||
makeContainer(); | ||
// TODO: Find a way to opt-out of autostart | ||
if (isWorker()) | ||
makeContainer(); | ||
exports.asRemoteValue = asRemoteValue; | ||
exports.defaultWorkerSrc = defaultWorkerSrc; | ||
exports.defaultOpts = defaultOpts; | ||
exports.RoundRobinStrategy = RoundRobinStrategy; | ||
exports.defaultStrategy = defaultStrategy; | ||
exports.spawn = spawn; | ||
exports.makeContainer = makeContainer; | ||
exports.Comlink = Comlink; | ||
exports.asRemoteValue = asRemoteValue; | ||
exports.defaultWorkerSrc = defaultWorkerSrc; | ||
exports.defaultOpts = defaultOpts; | ||
exports.RoundRobinStrategy = RoundRobinStrategy; | ||
exports.defaultStrategy = defaultStrategy; | ||
exports.spawn = spawn; | ||
exports.makeContainer = makeContainer; | ||
exports.Comlink = Comlink; | ||
return exports; | ||
return exports; | ||
}({})); |
@@ -14,2 +14,2 @@ /** | ||
*/ | ||
var Clooney=function(a){'use strict';async function b(a,b=[],c={}){return i.spawn(a,b,c)}function c(a=self){d.expose({async spawn(a,b){const c=new Function(`return ${a};`)();return d.proxyValue(new c(...b))}},a)}const d=function(){function a(a){if(n(a)&&(a=h(a)),!i(a))throw Error('endpoint does not have all of addEventListener, removeEventListener and postMessage defined');return j(a),p(async(b)=>{let c=[];('APPLY'===b.type||'CONSTRUCT'===b.type)&&(c=b.argumentsList.map(d));const f=await o(a,Object.assign({},b,{argumentsList:c}),r(c)),g=f.data;return e(g.value)})}function b(a){return a[w]=!0,a}function c(a,c){if(n(c)&&(c=h(c)),!i(c))throw Error('endpoint does not have all of addEventListener, removeEventListener and postMessage defined');j(c),k(c,async function(d){if(!d.data.id)return;const f=d.data;let g=await f.callPath.slice(0,-1).reduce((a,b)=>a[b],a),h=await f.callPath.reduce((a,b)=>a[b],a),i=h,j=[];if(('APPLY'===f.type||'CONSTRUCT'===f.type)&&(j=f.argumentsList.map(e)),'APPLY'===f.type)try{i=await h.apply(g,j)}catch(a){i=a,i[x]=!0}if('CONSTRUCT'===f.type)try{i=new h(...j),i=b(i)}catch(a){i=a,i[x]=!0}return'SET'===f.type&&(h[f.property]=f.value,i=!0),i=t(i),i.id=f.id,c.postMessage(i,r([i]))})}function d(a){for(const[b,c]of y.entries())if(c.canHandle(a))return{type:b,value:c.serialize(a)};let b=[];for(const c of s(a))for(const[a,d]of y.entries())d.canHandle(c.value)&&b.push({path:c.path,wrappedValue:{type:a,value:d.serialize(c.value)}});for(const c of b){const b=c.path.slice(0,-1).reduce((a,b)=>a[b],a);b[c.path[c.path.length-1]]=null}return{type:'RAW',value:a,wrappedChildren:b}}function e(a){if(y.has(a.type)){const b=y.get(a.type);return b.deserialize(a.value)}if(g(a)){for(const b of a.wrappedChildren||[]){if(!y.has(b.wrappedValue.type))throw Error(`Unknown value type "${a.type}" at ${b.path.join('.')}`);const c=y.get(b.wrappedValue.type),d=c.deserialize(b.wrappedValue.value);f(a.value,b.path,d)}return a.value}throw Error(`Unknown value type "${a.type}"`)}function f(a,b,c){const d=b.slice(-1)[0],e=b.slice(0,-1).reduce((a,b)=>a[b],a);e[d]=c}function g(a){return'RAW'===a.type}function h(a){if('Window'!==self.constructor.name)throw Error('self is not a window');return{addEventListener:self.addEventListener.bind(self),removeEventListener:self.removeEventListener.bind(self),postMessage:(b,c)=>a.postMessage(b,'*',c)}}function i(a){return'addEventListener'in a&&'removeEventListener'in a&&'postMessage'in a}function j(a){m(a)&&a.start()}function k(a,b){a.addEventListener('message',b)}function l(a,b){a.removeEventListener('message',b)}function m(a){return'MessagePort'===a.constructor.name}function n(a){return['window','length','location','parent','opener'].every((b)=>b in a)}function o(a,b,c){const d=`${v}-${z++}`;return new Promise((e)=>{k(a,function b(c){c.data.id!==d||(l(a,b),e(c))}),b=Object.assign({},b,{id:d}),a.postMessage(b,c)})}function p(a,b=[]){return new Proxy(function(){},{construct(c,d){return a({type:'CONSTRUCT',callPath:b,argumentsList:d})},apply(c,d,e){return'bind'===b[b.length-1]?p(a,b.slice(0,-1)):a({type:'APPLY',callPath:b,argumentsList:e})},get(c,d,e){if('then'===d&&0===b.length)return{then:()=>e};if('then'===d){const c=a({type:'GET',callPath:b});return Promise.resolve(c).then.bind(c)}return p(a,b.concat(d))},set(c,d,e){return a({type:'SET',callPath:b,property:d,value:e})}})}function q(a){return u.some((b)=>a instanceof b)}function*s(a,b=[],c=null){if(a&&(c||(c=new WeakSet),!c.has(a))&&'string'!=typeof a){'object'==typeof a&&c.add(a),yield{value:a,path:b};let d=Object.keys(a);for(const e of d)yield*s(a[e],[...b,e],c)}}function r(a){const b=[];for(const c of s(a))q(c.value)&&b.push(c.value);return b}function t(a){for(const[b,c]of y.entries())if(c.canHandle(a)){const d=c.serialize(a);return{value:{type:b,value:d}}}return{value:{type:'RAW',value:a}}}const u=[ArrayBuffer,MessagePort],v=Math.floor(Math.random()*Number.MAX_SAFE_INTEGER),w=Symbol('proxyValue'),x=Symbol('throw'),y=new Map([['PROXY',{canHandle:(a)=>a&&a[w],serialize:(a)=>{const{port1:b,port2:d}=new MessageChannel;return c(a,b),d},deserialize:(b)=>a(b)}],['THROW',{canHandle:(a)=>a&&a[x],serialize:(a)=>a.toString()+'\n'+a.stack,deserialize:(a)=>{throw Error(a)}}]]);let z=0;return{proxy:a,proxyValue:b,transferHandlers:y,expose:c}}();d.transferHandlers.set('FUNCTION',{canHandle(a){return a instanceof Function},serialize(a){const{port1:b,port2:c}=new MessageChannel;return d.expose(a,b),c},deserialize(a){return d.proxy(a)}}),d.transferHandlers.set('EVENT',{canHandle(a){return a instanceof Event},serialize(a){return{targetId:a&&a.target&&a.target.id,targetClassList:a&&a.target&&a.target.classList&&[...a.target.classList],detail:a&&a.detail,data:a&&a.data}},deserialize(a){return a}});const e=d.proxyValue;let f='document'in self?document.currentScript&&document.currentScript.src:'';const g={maxNumContainers:1,newWorkerFunc:async()=>new Worker(f)};class h{constructor(a={}){this._nextIndex=0,this._options=Object.assign({},g,a),this._containers=Array(this._options.maxNumContainers).fill(null)}async _initOrGetContainer(a){if(a>=this._containers.length)throw Error('No worker available');if(!this._containers[a]){const c=await this._options.newWorkerFunc(),e=d.proxy(c);this._containers[a]={spawn:e.spawn.bind(b),terminate:c.terminate.bind(c)}}return this._containers[a]}async _getNextContainer(){const a=await this._initOrGetContainer(this._nextIndex);return this._nextIndex=(this._nextIndex+1)%this._options.maxNumContainers,a}async spawn(a,b=[],c={}){const d=await this._getNextContainer(c);return await d.spawn(a.toString(),b)}async terminate(){this._containers.filter((a)=>a).forEach((a)=>a.terminate()),this._containers.length=0}get terminated(){return 0>=this._containers.length}}let i=new h;return function(){const a=self.WorkerGlobalScope;return a&&self instanceof a}()&&c(),a.asRemoteValue=e,a.defaultWorkerSrc=f,a.defaultOpts=g,a.RoundRobinStrategy=h,a.defaultStrategy=i,a.spawn=b,a.makeContainer=c,a.Comlink=d,a}({}); | ||
var Clooney=function(a){'use strict';async function b(a,b=[],c={}){return i.spawn(a,b,c)}function c(a=self){d.expose({async spawn(a,b){const c=new Function(`return ${a};`)();return d.proxyValue(new c(...b))}},a)}const d=function(){function a(a){if(n(a)&&(a=h(a)),!i(a))throw Error('endpoint does not have all of addEventListener, removeEventListener and postMessage defined');return j(a),p(async(b)=>{let c=[];('APPLY'===b.type||'CONSTRUCT'===b.type)&&(c=b.argumentsList.map(d));const f=await o(a,Object.assign({},b,{argumentsList:c}),r(c)),g=f.data;return e(g.value)})}function b(a){return a[w]=!0,a}function c(a,c){if(n(c)&&(c=h(c)),!i(c))throw Error('endpoint does not have all of addEventListener, removeEventListener and postMessage defined');j(c),k(c,async function(d){if(!d.data.id)return;const f=d.data;let g=await f.callPath.slice(0,-1).reduce((a,b)=>a[b],a),h=await f.callPath.reduce((a,b)=>a[b],a),i=h,j=[];if(('APPLY'===f.type||'CONSTRUCT'===f.type)&&(j=f.argumentsList.map(e)),'APPLY'===f.type)try{i=await h.apply(g,j)}catch(a){i=a,i[x]=!0}if('CONSTRUCT'===f.type)try{i=new h(...j),i=b(i)}catch(a){i=a,i[x]=!0}return'SET'===f.type&&(h[f.property]=f.value,i=!0),i=t(i),i.id=f.id,c.postMessage(i,r([i]))})}function d(a){for(const[b,c]of y.entries())if(c.canHandle(a))return{type:b,value:c.serialize(a)};let b=[];for(const c of s(a))for(const[a,d]of y.entries())d.canHandle(c.value)&&b.push({path:c.path,wrappedValue:{type:a,value:d.serialize(c.value)}});for(const c of b){const b=c.path.slice(0,-1).reduce((a,b)=>a[b],a);b[c.path[c.path.length-1]]=null}return{type:'RAW',value:a,wrappedChildren:b}}function e(a){if(y.has(a.type)){const b=y.get(a.type);return b.deserialize(a.value)}if(g(a)){for(const b of a.wrappedChildren||[]){if(!y.has(b.wrappedValue.type))throw Error(`Unknown value type "${a.type}" at ${b.path.join('.')}`);const c=y.get(b.wrappedValue.type),d=c.deserialize(b.wrappedValue.value);f(a.value,b.path,d)}return a.value}throw Error(`Unknown value type "${a.type}"`)}function f(a,b,c){const d=b.slice(-1)[0],e=b.slice(0,-1).reduce((a,b)=>a[b],a);e[d]=c}function g(a){return'RAW'===a.type}function h(a){if('Window'!==self.constructor.name)throw Error('self is not a window');return{addEventListener:self.addEventListener.bind(self),removeEventListener:self.removeEventListener.bind(self),postMessage:(b,c)=>a.postMessage(b,'*',c)}}function i(a){return'addEventListener'in a&&'removeEventListener'in a&&'postMessage'in a}function j(a){m(a)&&a.start()}function k(a,b){a.addEventListener('message',b)}function l(a,b){a.removeEventListener('message',b)}function m(a){return'MessagePort'===a.constructor.name}function n(a){return['window','length','location','parent','opener'].every((b)=>b in a)}function o(a,b,c){const d=`${v}-${z++}`;return new Promise((e)=>{k(a,function b(c){c.data.id!==d||(l(a,b),e(c))}),b=Object.assign({},b,{id:d}),a.postMessage(b,c)})}function p(a,b=[]){return new Proxy(function(){},{construct(c,d){return a({type:'CONSTRUCT',callPath:b,argumentsList:d})},apply(c,d,e){return'bind'===b[b.length-1]?p(a,b.slice(0,-1)):a({type:'APPLY',callPath:b,argumentsList:e})},get(c,d,e){if('then'===d&&0===b.length)return{then:()=>e};if('then'===d){const c=a({type:'GET',callPath:b});return Promise.resolve(c).then.bind(c)}return p(a,b.concat(d))},set(c,d,e){return a({type:'SET',callPath:b,property:d,value:e})}})}function q(a){return u.some((b)=>a instanceof b)}function*s(a,b=[],c=null){if(a&&(c||(c=new WeakSet),!c.has(a))&&'string'!=typeof a&&('object'==typeof a&&c.add(a),!ArrayBuffer.isView(a))){yield{value:a,path:b};const d=Object.keys(a);for(const e of d)yield*s(a[e],[...b,e],c)}}function r(a){const b=[];for(const c of s(a))q(c.value)&&b.push(c.value);return b}function t(a){for(const[b,c]of y.entries())if(c.canHandle(a)){const d=c.serialize(a);return{value:{type:b,value:d}}}return{value:{type:'RAW',value:a}}}const u=[ArrayBuffer,MessagePort],v=Math.floor(Math.random()*Number.MAX_SAFE_INTEGER),w=Symbol('proxyValue'),x=Symbol('throw'),y=new Map([['PROXY',{canHandle:(a)=>a&&a[w],serialize:(a)=>{const{port1:b,port2:d}=new MessageChannel;return c(a,b),d},deserialize:(b)=>a(b)}],['THROW',{canHandle:(a)=>a&&a[x],serialize:(a)=>a.toString()+'\n'+a.stack,deserialize:(a)=>{throw Error(a)}}]]);let z=0;return{proxy:a,proxyValue:b,transferHandlers:y,expose:c}}();d.transferHandlers.set('FUNCTION',{canHandle(a){return a instanceof Function},serialize(a){const{port1:b,port2:c}=new MessageChannel;return d.expose(a,b),c},deserialize(a){return d.proxy(a)}}),d.transferHandlers.set('EVENT',{canHandle(a){return a instanceof Event},serialize(a){return{targetId:a&&a.target&&a.target.id,targetClassList:a&&a.target&&a.target.classList&&[...a.target.classList],detail:a&&a.detail,data:a&&a.data}},deserialize(a){return a}});const e=d.proxyValue;let f='document'in self?document.currentScript&&document.currentScript.src:'';const g={maxNumContainers:1,newWorkerFunc:async()=>new Worker(f)};class h{constructor(a={}){this._nextIndex=0,this._options=Object.assign({},g,a),this._containers=Array(this._options.maxNumContainers).fill(null)}async _initOrGetContainer(a){if(a>=this._containers.length)throw Error('No worker available');if(!this._containers[a]){const c=await this._options.newWorkerFunc(),e=d.proxy(c);this._containers[a]={spawn:e.spawn.bind(b),terminate:c.terminate.bind(c)}}return this._containers[a]}async _getNextContainer(){const a=await this._initOrGetContainer(this._nextIndex);return this._nextIndex=(this._nextIndex+1)%this._options.maxNumContainers,a}async spawn(a,b=[],c={}){const d=await this._getNextContainer(c);return await d.spawn(a.toString(),b)}async terminate(){this._containers.filter((a)=>a).forEach((a)=>a.terminate()),this._containers.length=0}get terminated(){return 0>=this._containers.length}}let i=new h;return function(){const a=self.WorkerGlobalScope;return a&&self instanceof a}()&&c(),a.asRemoteValue=e,a.defaultWorkerSrc=f,a.defaultOpts=g,a.RoundRobinStrategy=h,a.defaultStrategy=i,a.spawn=b,a.makeContainer=c,a.Comlink=d,a}({}); |
@@ -13,4 +13,4 @@ /** | ||
*/ | ||
import { Endpoint } from 'comlink'; | ||
export { Comlink } from 'comlink'; | ||
import { Endpoint } from 'comlinkjs'; | ||
export { Comlink } from 'comlinkjs'; | ||
/** | ||
@@ -17,0 +17,0 @@ * `asRemoteValue` marks a value. If a marked value is used as an parameter or return value, it will not be transferred but instead proxied. |
@@ -13,3 +13,3 @@ /** | ||
*/ | ||
import { Comlink } from 'comlink'; // eslint-disable-line no-unused-vars | ||
import { Comlink } from 'comlinkjs'; // eslint-disable-line no-unused-vars | ||
// Automatically proxy functions | ||
@@ -46,3 +46,3 @@ Comlink.transferHandlers.set('FUNCTION', { | ||
}); | ||
export { Comlink } from 'comlink'; | ||
export { Comlink } from 'comlinkjs'; | ||
/** | ||
@@ -49,0 +49,0 @@ * `asRemoteValue` marks a value. If a marked value is used as an parameter or return value, it will not be transferred but instead proxied. |
@@ -14,2 +14,2 @@ /** | ||
*/ | ||
import{Comlink}from'comlink';Comlink.transferHandlers.set('FUNCTION',{canHandle(a){return a instanceof Function},serialize(a){const{port1:b,port2:c}=new MessageChannel;return Comlink.expose(a,b),c},deserialize(a){return Comlink.proxy(a)}}),Comlink.transferHandlers.set('EVENT',{canHandle(a){return a instanceof Event},serialize(a){return{targetId:a&&a.target&&a.target.id,targetClassList:a&&a.target&&a.target.classList&&[...a.target.classList],detail:a&&a.detail,data:a&&a.data}},deserialize(a){return a}});export{Comlink}from'comlink';export const asRemoteValue=Comlink.proxyValue;export let defaultWorkerSrc='document'in self?document.currentScript&&document.currentScript.src:'';export const defaultOpts={maxNumContainers:1,newWorkerFunc:async()=>new Worker(defaultWorkerSrc)};export class RoundRobinStrategy{constructor(a={}){this._nextIndex=0,this._options=Object.assign({},defaultOpts,a),this._containers=Array(this._options.maxNumContainers).fill(null)}async _initOrGetContainer(a){if(a>=this._containers.length)throw Error('No worker available');if(!this._containers[a]){const b=await this._options.newWorkerFunc(),c=Comlink.proxy(b);this._containers[a]={spawn:c.spawn.bind(spawn),terminate:b.terminate.bind(b)}}return this._containers[a]}async _getNextContainer(){const a=await this._initOrGetContainer(this._nextIndex);return this._nextIndex=(this._nextIndex+1)%this._options.maxNumContainers,a}async spawn(a,b=[],c={}){const d=await this._getNextContainer(c);return await d.spawn(a.toString(),b)}async terminate(){this._containers.filter((a)=>a).forEach((a)=>a.terminate()),this._containers.length=0}get terminated(){return 0>=this._containers.length}}export let defaultStrategy=new RoundRobinStrategy;export async function spawn(a,b=[],c={}){return defaultStrategy.spawn(a,b,c)}export function makeContainer(a=self){Comlink.expose({async spawn(a,b){const c=new Function(`return ${a};`)();return Comlink.proxyValue(new c(...b))}},a)}function isWorker(){const a=self.WorkerGlobalScope;return a&&self instanceof a}isWorker()&&makeContainer(); | ||
import{Comlink}from'comlinkjs';Comlink.transferHandlers.set('FUNCTION',{canHandle(a){return a instanceof Function},serialize(a){const{port1:b,port2:c}=new MessageChannel;return Comlink.expose(a,b),c},deserialize(a){return Comlink.proxy(a)}}),Comlink.transferHandlers.set('EVENT',{canHandle(a){return a instanceof Event},serialize(a){return{targetId:a&&a.target&&a.target.id,targetClassList:a&&a.target&&a.target.classList&&[...a.target.classList],detail:a&&a.detail,data:a&&a.data}},deserialize(a){return a}});export{Comlink}from'comlinkjs';export const asRemoteValue=Comlink.proxyValue;export let defaultWorkerSrc='document'in self?document.currentScript&&document.currentScript.src:'';export const defaultOpts={maxNumContainers:1,newWorkerFunc:async()=>new Worker(defaultWorkerSrc)};export class RoundRobinStrategy{constructor(a={}){this._nextIndex=0,this._options=Object.assign({},defaultOpts,a),this._containers=Array(this._options.maxNumContainers).fill(null)}async _initOrGetContainer(a){if(a>=this._containers.length)throw Error('No worker available');if(!this._containers[a]){const b=await this._options.newWorkerFunc(),c=Comlink.proxy(b);this._containers[a]={spawn:c.spawn.bind(spawn),terminate:b.terminate.bind(b)}}return this._containers[a]}async _getNextContainer(){const a=await this._initOrGetContainer(this._nextIndex);return this._nextIndex=(this._nextIndex+1)%this._options.maxNumContainers,a}async spawn(a,b=[],c={}){const d=await this._getNextContainer(c);return await d.spawn(a.toString(),b)}async terminate(){this._containers.filter((a)=>a).forEach((a)=>a.terminate()),this._containers.length=0}get terminated(){return 0>=this._containers.length}}export let defaultStrategy=new RoundRobinStrategy;export async function spawn(a,b=[],c={}){return defaultStrategy.spawn(a,b,c)}export function makeContainer(a=self){Comlink.expose({async spawn(a,b){const c=new Function(`return ${a};`)();return Comlink.proxyValue(new c(...b))}},a)}function isWorker(){const a=self.WorkerGlobalScope;return a&&self instanceof a}isWorker()&&makeContainer(); |
{ | ||
"name": "clooneyjs", | ||
"version": "0.6.2", | ||
"version": "0.6.3", | ||
"description": "Clooney is an actor library for the web", | ||
@@ -34,3 +34,3 @@ "main": "clooney.js", | ||
"chai": "4.1.2", | ||
"comlinkjs": "2.3.4", | ||
"comlinkjs": "^2.3.5", | ||
"eslint": "4.19.0", | ||
@@ -37,0 +37,0 @@ "eslint-config-google": "0.9.1", |
@@ -106,3 +106,3 @@ # Clooney | ||
```html | ||
<script src="https://cdn.jsdelivr.net/npm/clooneyjs@0.6.2/clooney.bundle.min.js"></script> | ||
<script src="https://cdn.jsdelivr.net/npm/clooneyjs@0.6.3/clooney.bundle.min.js"></script> | ||
<script> | ||
@@ -109,0 +109,0 @@ async function newWorkerFunc() { |
42460
676