UNPKG

workway

Version:

A general purpose, Web Worker driven, remote namespace with classes and methods.

155 lines (154 loc) 5.35 kB
(function () {'use strict'; /*! (c) 2018 Andrea Giammarchi (ISC) */ function walkThrough(O, K) { return O[K]; } var namespace; var channels = {}; var instances = {}; var onceExposed = new Promise(function (resolve) { self.workway = function workway(exposed) { return Promise.resolve(exposed).then(function (result) { namespace = result; resolve(remoted(result)); }); }; function remoted(object) { return function $(object, current, remote) { Object.keys(object).forEach(function (key) { var value = object[key]; var path = current.concat(key); if (typeof value === 'function') { remote[key] = /^[A-Z]/.test(key) ? { type: 'class', path: path, methods: Object.getOwnPropertyNames(value.prototype) .filter(no(['constructor'])) .concat('destroy'), statics: Object.getOwnPropertyNames(value) .filter(no([ 'arguments', 'callee', 'caller', 'length', 'name', 'prototype' ])) .reduce( function (info, key) { if (typeof value[key] === 'function') { info.methods.push(key); } else { info.values.push([key, value[key]]); } return info; }, { methods: [], values: [] } ) } : { type: 'function', path: path }; } else if (remote.toString.call(value) === '[object Object]') { remote[key] = { type: 'object', path: path, value: {} }; $(value, path, remote[key].value); } else if (value !== void 0) { remote[key] = { type: 'any', path: path, value: value }; } }); return remote; }(object, [], {}); function no(within) { return function (what) { return within.indexOf(what) < 0; }; } } }); self.addEventListener('message', function (event) { var method; var data = event.data; var channel = data.channel; var message = data.message; if (channels[channel]) { event.stopImmediatePropagation(); var id = message.id; var path = message.path; var args = message.args; var resolved = function (result) { send({result: result}); }; var rejected = function (error) { if ( error != null && typeof error === 'object' && 'message' in error ) send({error: { stack: error.stack, message: error.message }}); else send({error: {source: error}}); }; var send = function (message) { message.id = id; self.postMessage({ channel: channel, message: message }); }; try { if (message.hasOwnProperty('method')) { method = message.method; var Class = path.reduce(walkThrough, namespace); if (!Class) return rejected('Unknown Class ' + path.join('.')); if (message.hasOwnProperty('object')) { var object = message.object; var instance = instances[object.id] || (instances[object.id] = new Class); if (method === 'destroy') delete instances[object.id]; else { Object.keys(object.value) .forEach(function (key) { instance[key] = object.value[key]; }); Promise.resolve(instance[method].apply(instance, args)) .then(resolved, rejected); } } else { Promise.resolve(Class[method].apply(Class, args)) .then(resolved, rejected); } } else { var context = path.slice(0, -1).reduce(walkThrough, namespace); if (!context) return rejected('Unknown namespace ' + path.slice(0, -1).join('.')); method = path[path.length - 1]; if (typeof context[method] !== 'function') return rejected('Unknown method ' + path.join('.')); Promise.resolve(context[method].apply(context, args)) .then(resolved, rejected); } } catch(error) { rejected(error); } } else if (/^(-?\d+\.\d+)$/.test(channel)) { channels[channel] = true; event.stopImmediatePropagation(); onceExposed.then(function (namespace) { self.postMessage({ channel: channel, namespace: namespace }); }); } }); }());