@eroc/core
Advanced tools
Comparing version 4.1.0 to 4.3.0
@@ -1,2 +0,2 @@ | ||
/* @eroc/core v4.1.0 2021-09-01T21:05:19.889Z licensed MIT */ | ||
/* @eroc/core v4.3.0 2021-09-15T19:01:12.070Z licensed MIT */ | ||
const startEventRecorder = (core) => { | ||
@@ -310,5 +310,7 @@ const events = []; | ||
const CORE_STOPPED = `CORE_STOPPED`; | ||
const CORE_GET_STATE = `CORE_GET_STATE`; | ||
const CORE_SET_STATE = `CORE_SET_STATE`; | ||
const CORE_ERROR = `CORE_ERROR`; | ||
const workerGlueCode = "/**\n * Use it as a constructor\n * or as a decorator for an existing object\n * or as a base class for extend\n * cannot be used as a mixin for a constructor's prototype\n * without calling the constructor\n */\nfunction EventEmitter3(obj) {\n (obj || this)._callbacks = Object.create(null);\n if (obj) {return Object.assign(obj, EventEmitter3.prototype);}\n}\n\n/**\n * Listen on the given `eventName` with `fn`\n *\n * @param {String | Symbol} eventName\n * @param {Function} fn\n * @api public\n */\n\nEventEmitter3.prototype.on = function (eventName, fn) {\n (this._callbacks[eventName] = this._callbacks[eventName] || [])\n .push(fn);\n};\n\n/**\n * Adds an `eventName` listener that will be invoked once then removed\n *\n * @param {String | Symbol} eventName\n * @param {Function} fn\n * @api public\n */\n\nEventEmitter3.prototype.once = function (eventName, fn) {\n const once = (data) => {\n this.off(eventName, once);\n fn(data);\n };\n\n once.fn = fn; // makes it possible to remove with off\n this.on(eventName, once);\n};\n\n/**\n * Remove a callback for `eventName` or\n * all callbacks for `eventName` or\n * all callbacks for all events\n *\n * @param {String | Symbol} eventName\n * @param {Function} fn\n * @api public\n */\n\nEventEmitter3.prototype.off = function (eventName, fn) {\n // all\n if (!eventName) {\n this._callbacks = Object.create(null);\n return;\n }\n\n // specific event\n const callbacks = this._callbacks[eventName];\n if (!callbacks) {\n return;\n }\n\n // remove all handlers\n if (!fn) {\n delete this._callbacks[eventName];\n return;\n }\n\n // remove specific handler\n const index = callbacks.findIndex(function (cb) {\n return (cb === fn || cb.fn === fn);\n });\n if (index > -1) {\n // Remove event specific arrays for the eventName type that no\n // one is subscribed for, to avoid memory leak.\n if (callbacks.length === 1) {\n delete this._callbacks[eventName];\n } else {\n callbacks.splice(index, 1);\n }\n }\n};\n\n/**\n * Emit `eventName` with data\n *\n * @param {String | Symbol} eventName\n * @param {any} data\n */\n\nEventEmitter3.prototype.emit = function (eventName, data) {\n const callbacks = this._callbacks[eventName];\n if (!callbacks) {\n return;\n }\n const frozenCallbacks = Array.from(callbacks);\n frozenCallbacks.forEach(callback => {\n callback(data);\n });\n};\n\n/**\n * Return array of callbacks for `eventName`\n *\n * @param {String | Symbol} eventName\n * @return {Array} listeners\n * @api public\n */\n\nEventEmitter3.prototype.listeners = function (eventName) {\n return this._callbacks[eventName] || [];\n};\n\n/**\n * True if this emitter has `eventName` handlers\n *\n * @param {String | Symbol} eventName\n * @return {Boolean}\n * @api public\n */\n\nEventEmitter3.prototype.hasListeners = function (eventName) {\n return Boolean(this.listeners(eventName).length);\n};\n\n/**\n * Returns an array of event names for which the emitter has registered listeners\n *\n * @return {Array <String || Symbol>}\n * @api public\n */\nEventEmitter3.prototype.eventNames = function () {\n return Reflect.ownKeys(this._callbacks);\n};\n\n/**\n * Returns an array of event anmes of type string\n * for which the emitter has registered listeners\n *\n * @return {Array <String>}\n * @api public\n */\nEventEmitter3.prototype.eventNamesStrings = function () {\n return Object.keys(this._callbacks);\n};\n\n// prefix to avoid variable name collision\r\nconst CORE_ACTION_KEY = `action`;\r\n\r\n// actions\r\nconst CORE_EVENT = `CORE_EVENT`;\r\nconst CORE_START = `CORE_START`;\r\nconst CORE_STARTED = `CORE_STARTED`;\r\nconst CORE_STOP = `CORE_STOP`;\r\nconst CORE_STOPPED = `CORE_STOPPED`;\r\nconst CORE_ERROR = `CORE_ERROR`;\n\nlet localEmitter;\r\nlet localInstance;\r\n\r\nself.addEventListener(`error`, function (errorEvent) {\r\n errorEvent.preventDefault();\r\n let asString;\r\n if (errorEvent.message) {\r\n asString = errorEvent.message;\r\n } else {\r\n asString = String(errorEvent);\r\n }\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_ERROR,\r\n error: asString,\r\n });\r\n});\r\n\r\nself.addEventListener(`message`, async function(messageEvent) {\r\n const message = messageEvent.data;\r\n if (!Object.prototype.hasOwnProperty.call(message, CORE_ACTION_KEY)) {\r\n return;\r\n }\r\n const action = message[CORE_ACTION_KEY];\r\n if (action === CORE_EVENT) {\r\n if (!localInstance) {\r\n return;\r\n }\r\n // avoid infinite loop\r\n localEmitter.originalEmit(message.name, message.data);\r\n return;\r\n }\r\n if (action === CORE_START) {\r\n localEmitter = new EventEmitter3();\r\n localEmitter.originalEmit = localEmitter.emit;\r\n localEmitter.emit = function (eventName, data) {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_EVENT,\r\n name: eventName,\r\n data,\r\n });\r\n localEmitter.originalEmit(eventName, data);\r\n };\r\n \r\n Promise.resolve().then(() => {\r\n return start(localEmitter, message.data);\r\n }).then(instance => {\r\n localInstance = instance;\r\n }).catch(errorModuleStart => {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_ERROR,\r\n time: Date.now(),\r\n phase: `module.start`,\r\n error: errorModuleStart,\r\n });\r\n }).then(() => {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_STARTED,\r\n });\r\n });\r\n return;\r\n }\r\n if (action === CORE_STOP) {\r\n if (!localInstance) {\r\n // should never happen\r\n return;\r\n }\r\n Promise.resolve().then(() => {\r\n if (typeof stop === `function`) {\r\n return stop(wrapper.instance);\r\n }\r\n }).catch(errorModuleStop => {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_ERROR,\r\n time: Date.now(),\r\n phase: `module.stop`,\r\n error: errorModuleStop,\r\n });\r\n }).then(() => {\r\n localInstance = undefined;\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_STOPPED,\r\n });\r\n });\r\n return;\r\n }\r\n\r\n // todo CORE_GET_STATE, CORE_SET_STATE\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_ERROR,\r\n error: `action ${action} not implemented`,\r\n });\r\n});\r\n\r\n// module code below (defintion of start and stop)\n"; | ||
const workerGlueCode = "/**\n * Use it as a constructor\n * or as a decorator for an existing object\n * or as a base class for extend\n * cannot be used as a mixin for a constructor's prototype\n * without calling the constructor\n */\nfunction EventEmitter3(obj) {\n (obj || this)._callbacks = Object.create(null);\n if (obj) {return Object.assign(obj, EventEmitter3.prototype);}\n}\n\n/**\n * Listen on the given `eventName` with `fn`\n *\n * @param {String | Symbol} eventName\n * @param {Function} fn\n * @api public\n */\n\nEventEmitter3.prototype.on = function (eventName, fn) {\n (this._callbacks[eventName] = this._callbacks[eventName] || [])\n .push(fn);\n};\n\n/**\n * Adds an `eventName` listener that will be invoked once then removed\n *\n * @param {String | Symbol} eventName\n * @param {Function} fn\n * @api public\n */\n\nEventEmitter3.prototype.once = function (eventName, fn) {\n const once = (data) => {\n this.off(eventName, once);\n fn(data);\n };\n\n once.fn = fn; // makes it possible to remove with off\n this.on(eventName, once);\n};\n\n/**\n * Remove a callback for `eventName` or\n * all callbacks for `eventName` or\n * all callbacks for all events\n *\n * @param {String | Symbol} eventName\n * @param {Function} fn\n * @api public\n */\n\nEventEmitter3.prototype.off = function (eventName, fn) {\n // all\n if (!eventName) {\n this._callbacks = Object.create(null);\n return;\n }\n\n // specific event\n const callbacks = this._callbacks[eventName];\n if (!callbacks) {\n return;\n }\n\n // remove all handlers\n if (!fn) {\n delete this._callbacks[eventName];\n return;\n }\n\n // remove specific handler\n const index = callbacks.findIndex(function (cb) {\n return (cb === fn || cb.fn === fn);\n });\n if (index > -1) {\n // Remove event specific arrays for the eventName type that no\n // one is subscribed for, to avoid memory leak.\n if (callbacks.length === 1) {\n delete this._callbacks[eventName];\n } else {\n callbacks.splice(index, 1);\n }\n }\n};\n\n/**\n * Emit `eventName` with data\n *\n * @param {String | Symbol} eventName\n * @param {any} data\n */\n\nEventEmitter3.prototype.emit = function (eventName, data) {\n const callbacks = this._callbacks[eventName];\n if (!callbacks) {\n return;\n }\n const frozenCallbacks = Array.from(callbacks);\n frozenCallbacks.forEach(callback => {\n callback(data);\n });\n};\n\n/**\n * Return array of callbacks for `eventName`\n *\n * @param {String | Symbol} eventName\n * @return {Array} listeners\n * @api public\n */\n\nEventEmitter3.prototype.listeners = function (eventName) {\n return this._callbacks[eventName] || [];\n};\n\n/**\n * True if this emitter has `eventName` handlers\n *\n * @param {String | Symbol} eventName\n * @return {Boolean}\n * @api public\n */\n\nEventEmitter3.prototype.hasListeners = function (eventName) {\n return Boolean(this.listeners(eventName).length);\n};\n\n/**\n * Returns an array of event names for which the emitter has registered listeners\n *\n * @return {Array <String || Symbol>}\n * @api public\n */\nEventEmitter3.prototype.eventNames = function () {\n return Reflect.ownKeys(this._callbacks);\n};\n\n/**\n * Returns an array of event anmes of type string\n * for which the emitter has registered listeners\n *\n * @return {Array <String>}\n * @api public\n */\nEventEmitter3.prototype.eventNamesStrings = function () {\n return Object.keys(this._callbacks);\n};\n\n// prefix to avoid variable name collision\r\nconst CORE_ACTION_KEY = `action`;\r\n\r\n// actions\r\nconst CORE_EVENT = `CORE_EVENT`;\r\nconst CORE_START = `CORE_START`;\r\nconst CORE_STARTED = `CORE_STARTED`;\r\nconst CORE_STOP = `CORE_STOP`;\r\nconst CORE_STOPPED = `CORE_STOPPED`;\r\nconst CORE_GET_STATE = `CORE_GET_STATE`;\r\nconst CORE_SET_STATE = `CORE_SET_STATE`;\r\nconst CORE_ERROR = `CORE_ERROR`;\n\nlet localEmitter;\r\nlet localInstance;\r\n\r\nself.addEventListener(`error`, function (errorEvent) {\r\n errorEvent.preventDefault();\r\n let asString;\r\n if (errorEvent.message) {\r\n asString = errorEvent.message;\r\n } else {\r\n asString = String(errorEvent);\r\n }\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_ERROR,\r\n error: asString,\r\n });\r\n});\r\n\r\nself.addEventListener(`message`, async function(messageEvent) {\r\n const message = messageEvent.data;\r\n if (!Object.prototype.hasOwnProperty.call(message, CORE_ACTION_KEY)) {\r\n return;\r\n }\r\n const action = message[CORE_ACTION_KEY];\r\n if (action === CORE_EVENT) {\r\n if (!localInstance) {\r\n return;\r\n }\r\n // avoid infinite loop\r\n localEmitter.originalEmit(message.name, message.data);\r\n return;\r\n }\r\n if (action === CORE_START) {\r\n localEmitter = new EventEmitter3();\r\n localEmitter.originalEmit = localEmitter.emit;\r\n localEmitter.emit = function (eventName, data) {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_EVENT,\r\n name: eventName,\r\n data,\r\n });\r\n localEmitter.originalEmit(eventName, data);\r\n };\r\n \r\n Promise.resolve().then(() => {\r\n return start(localEmitter, message.data);\r\n }).then(instance => {\r\n localInstance = instance;\r\n }).catch(errorModuleStart => {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_ERROR,\r\n time: Date.now(),\r\n phase: `module.start`,\r\n error: errorModuleStart,\r\n });\r\n }).then(() => {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_STARTED,\r\n });\r\n });\r\n return;\r\n }\r\n if (action === CORE_STOP) {\r\n if (!localInstance) {\r\n // should never happen\r\n return;\r\n }\r\n Promise.resolve().then(() => {\r\n if (typeof stop === `function`) {\r\n return stop(wrapper.instance);\r\n }\r\n }).catch(errorModuleStop => {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_ERROR,\r\n time: Date.now(),\r\n phase: `module.stop`,\r\n error: errorModuleStop,\r\n });\r\n }).then(() => {\r\n localInstance = undefined;\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_STOPPED,\r\n });\r\n });\r\n return;\r\n }\r\n\r\n if (action === CORE_SET_STATE) {\r\n Promise.resolve().then(() => {\r\n if (typeof restoreState === `function`) {\r\n return restoreState(localInstance, message.data);\r\n }\r\n }).then(() => {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_GET_STATE,\r\n });\r\n }).catch(errorModuleRestoreState => {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_ERROR,\r\n time: Date.now(),\r\n phase: `module.restoreState`,\r\n error: errorModuleRestoreState,\r\n });\r\n });\r\n return;\r\n }\r\n if (action === CORE_GET_STATE) {\r\n Promise.resolve().then(() => {\r\n if (typeof getState === `function`) {\r\n return getState(localInstance);\r\n }\r\n }).then((result) => {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_SET_STATE,\r\n data: result, // core will handle undefined\r\n });\r\n }).catch(errorModuleRestoreState => {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_ERROR,\r\n time: Date.now(),\r\n phase: `module.restoreState`,\r\n error: errorModuleRestoreState,\r\n });\r\n });\r\n return;\r\n }\r\n\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_ERROR,\r\n error: `action ${action} not implemented`,\r\n });\r\n});\r\n\r\n// module code below (defintion of start and stop)\n"; | ||
@@ -321,2 +323,6 @@ // @ts-check | ||
const prepareWorkerCode = function (moduleCodeAsIIFE) { | ||
return `${workerGlueCode};${moduleCodeAsIIFE}`; | ||
}; | ||
const createCore = function () { | ||
@@ -335,2 +341,5 @@ const core = {}; | ||
const wrapper = core.moduleInstances.get(name); | ||
if (wrapper.worker) { | ||
return core.requestStateFromWorker(wrapper) | ||
} | ||
if (!wrapper.module.getState) { | ||
@@ -344,2 +353,11 @@ return Promise.resolve({}); | ||
requestStateFromWorker(wrapper) { | ||
return new Promise(function (resolve, reject) { | ||
wrapper.getStateResolve = resolve; | ||
wrapper.worker.postMessage({ | ||
[CORE_ACTION_KEY]: CORE_GET_STATE, | ||
}); | ||
}); | ||
}, | ||
/* returns a promise with an object | ||
@@ -372,2 +390,5 @@ as keys the names of the module instances | ||
const wrapper = core.moduleInstances.get(name); | ||
if (wrapper.worker) { | ||
return core.restoreStateInWorker(wrapper); | ||
} | ||
if (!wrapper.module.restoreState) { | ||
@@ -382,2 +403,11 @@ return Promise.resolve(); | ||
}, | ||
restoreStateInWorker(wrapper) { | ||
return new Promise(function (resolve, reject) { | ||
wrapper.setStateResolve = resolve; | ||
wrapper.worker.postMessage({ | ||
[CORE_ACTION_KEY]: CORE_SET_STATE, | ||
}); | ||
}); | ||
}, | ||
@@ -393,3 +423,4 @@ restoreAllStates(states) { | ||
data = undefined, | ||
worker = false | ||
worker = false, | ||
workerReady = false, | ||
} = {}) { | ||
@@ -402,3 +433,3 @@ | ||
if (worker) { | ||
return core.startWorker(module, name, data); | ||
return core.startWorker(module, name, data, workerReady); | ||
} | ||
@@ -434,11 +465,20 @@ | ||
startWorker(moduleUrl, name, data) { | ||
startWorker(moduleUrl, name, data, workerReady) { | ||
return new Promise(function (resolve, reject) { | ||
fetch(moduleUrl).then(response => { | ||
return response.text(); | ||
}).then(moduleCode => { | ||
const workerCode = `${workerGlueCode};${moduleCode}`; | ||
const workerBlob = new Blob([workerCode], JS_MIME); | ||
const workerObjectURL = URL.createObjectURL(workerBlob); | ||
const moduleInsideWorker = new Worker(workerObjectURL, { | ||
let workerRessourcePromise; | ||
if (workerReady) { | ||
workerRessourcePromise = Promise.resolve(moduleUrl); | ||
} else { | ||
workerRessourcePromise = fetch(moduleUrl).then(response => { | ||
return response.text(); | ||
}).then(moduleCode => { | ||
const workerCode = prepareWorkerCode(moduleCode); | ||
const workerBlob = new Blob([workerCode], JS_MIME); | ||
const workerObjectURL = URL.createObjectURL(workerBlob); | ||
return workerObjectURL | ||
}); | ||
} | ||
workerRessourcePromise.then(workerRessource => { | ||
const moduleInsideWorker = new Worker(workerRessource, { | ||
type: "module", | ||
@@ -485,4 +525,8 @@ name: String(name), // help debugging | ||
} | ||
if (action === CORE_ERROR) { | ||
core.emit(ERROR, message); | ||
return; | ||
} | ||
const wrapper = core.moduleInstances.get(name); | ||
if (action === CORE_STOPPED) { | ||
const wrapper = core.moduleInstances.get(name); | ||
if (wrapper?.stopResolve) { | ||
@@ -495,7 +539,17 @@ wrapper.stopResolve(); | ||
} | ||
if (action === CORE_ERROR) { | ||
core.emit(ERROR, message); | ||
if (action === CORE_SET_STATE) { | ||
if (wrapper?.getStateResolve) { | ||
wrapper.getStateResolve(message.data || {}); | ||
wrapper.getStateResolve = undefined; | ||
} | ||
return; | ||
} | ||
// todo CORE_GET_STATE set State | ||
if (action === CORE_GET_STATE) { | ||
if (wrapper?.setStateResolve) { | ||
wrapper.setStateResolve(); | ||
wrapper.setStateResolve = undefined; | ||
} | ||
return; | ||
} | ||
}); | ||
@@ -576,2 +630,2 @@ }, | ||
export { ALL, ERROR, createCore, replayEvents, startEventRecorder, stopEventRecorder, useDefaultLogging }; | ||
export { ALL, ERROR, createCore, prepareWorkerCode, replayEvents, startEventRecorder, stopEventRecorder, useDefaultLogging }; |
@@ -1,2 +0,2 @@ | ||
/* @eroc/core v4.1.0 2021-09-01T21:05:19.889Z licensed MIT */ | ||
/* @eroc/core v4.3.0 2021-09-15T19:01:12.070Z licensed MIT */ | ||
var Core = (function (exports) { | ||
@@ -313,5 +313,7 @@ 'use strict'; | ||
const CORE_STOPPED = `CORE_STOPPED`; | ||
const CORE_GET_STATE = `CORE_GET_STATE`; | ||
const CORE_SET_STATE = `CORE_SET_STATE`; | ||
const CORE_ERROR = `CORE_ERROR`; | ||
const workerGlueCode = "/**\n * Use it as a constructor\n * or as a decorator for an existing object\n * or as a base class for extend\n * cannot be used as a mixin for a constructor's prototype\n * without calling the constructor\n */\nfunction EventEmitter3(obj) {\n (obj || this)._callbacks = Object.create(null);\n if (obj) {return Object.assign(obj, EventEmitter3.prototype);}\n}\n\n/**\n * Listen on the given `eventName` with `fn`\n *\n * @param {String | Symbol} eventName\n * @param {Function} fn\n * @api public\n */\n\nEventEmitter3.prototype.on = function (eventName, fn) {\n (this._callbacks[eventName] = this._callbacks[eventName] || [])\n .push(fn);\n};\n\n/**\n * Adds an `eventName` listener that will be invoked once then removed\n *\n * @param {String | Symbol} eventName\n * @param {Function} fn\n * @api public\n */\n\nEventEmitter3.prototype.once = function (eventName, fn) {\n const once = (data) => {\n this.off(eventName, once);\n fn(data);\n };\n\n once.fn = fn; // makes it possible to remove with off\n this.on(eventName, once);\n};\n\n/**\n * Remove a callback for `eventName` or\n * all callbacks for `eventName` or\n * all callbacks for all events\n *\n * @param {String | Symbol} eventName\n * @param {Function} fn\n * @api public\n */\n\nEventEmitter3.prototype.off = function (eventName, fn) {\n // all\n if (!eventName) {\n this._callbacks = Object.create(null);\n return;\n }\n\n // specific event\n const callbacks = this._callbacks[eventName];\n if (!callbacks) {\n return;\n }\n\n // remove all handlers\n if (!fn) {\n delete this._callbacks[eventName];\n return;\n }\n\n // remove specific handler\n const index = callbacks.findIndex(function (cb) {\n return (cb === fn || cb.fn === fn);\n });\n if (index > -1) {\n // Remove event specific arrays for the eventName type that no\n // one is subscribed for, to avoid memory leak.\n if (callbacks.length === 1) {\n delete this._callbacks[eventName];\n } else {\n callbacks.splice(index, 1);\n }\n }\n};\n\n/**\n * Emit `eventName` with data\n *\n * @param {String | Symbol} eventName\n * @param {any} data\n */\n\nEventEmitter3.prototype.emit = function (eventName, data) {\n const callbacks = this._callbacks[eventName];\n if (!callbacks) {\n return;\n }\n const frozenCallbacks = Array.from(callbacks);\n frozenCallbacks.forEach(callback => {\n callback(data);\n });\n};\n\n/**\n * Return array of callbacks for `eventName`\n *\n * @param {String | Symbol} eventName\n * @return {Array} listeners\n * @api public\n */\n\nEventEmitter3.prototype.listeners = function (eventName) {\n return this._callbacks[eventName] || [];\n};\n\n/**\n * True if this emitter has `eventName` handlers\n *\n * @param {String | Symbol} eventName\n * @return {Boolean}\n * @api public\n */\n\nEventEmitter3.prototype.hasListeners = function (eventName) {\n return Boolean(this.listeners(eventName).length);\n};\n\n/**\n * Returns an array of event names for which the emitter has registered listeners\n *\n * @return {Array <String || Symbol>}\n * @api public\n */\nEventEmitter3.prototype.eventNames = function () {\n return Reflect.ownKeys(this._callbacks);\n};\n\n/**\n * Returns an array of event anmes of type string\n * for which the emitter has registered listeners\n *\n * @return {Array <String>}\n * @api public\n */\nEventEmitter3.prototype.eventNamesStrings = function () {\n return Object.keys(this._callbacks);\n};\n\n// prefix to avoid variable name collision\r\nconst CORE_ACTION_KEY = `action`;\r\n\r\n// actions\r\nconst CORE_EVENT = `CORE_EVENT`;\r\nconst CORE_START = `CORE_START`;\r\nconst CORE_STARTED = `CORE_STARTED`;\r\nconst CORE_STOP = `CORE_STOP`;\r\nconst CORE_STOPPED = `CORE_STOPPED`;\r\nconst CORE_ERROR = `CORE_ERROR`;\n\nlet localEmitter;\r\nlet localInstance;\r\n\r\nself.addEventListener(`error`, function (errorEvent) {\r\n errorEvent.preventDefault();\r\n let asString;\r\n if (errorEvent.message) {\r\n asString = errorEvent.message;\r\n } else {\r\n asString = String(errorEvent);\r\n }\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_ERROR,\r\n error: asString,\r\n });\r\n});\r\n\r\nself.addEventListener(`message`, async function(messageEvent) {\r\n const message = messageEvent.data;\r\n if (!Object.prototype.hasOwnProperty.call(message, CORE_ACTION_KEY)) {\r\n return;\r\n }\r\n const action = message[CORE_ACTION_KEY];\r\n if (action === CORE_EVENT) {\r\n if (!localInstance) {\r\n return;\r\n }\r\n // avoid infinite loop\r\n localEmitter.originalEmit(message.name, message.data);\r\n return;\r\n }\r\n if (action === CORE_START) {\r\n localEmitter = new EventEmitter3();\r\n localEmitter.originalEmit = localEmitter.emit;\r\n localEmitter.emit = function (eventName, data) {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_EVENT,\r\n name: eventName,\r\n data,\r\n });\r\n localEmitter.originalEmit(eventName, data);\r\n };\r\n \r\n Promise.resolve().then(() => {\r\n return start(localEmitter, message.data);\r\n }).then(instance => {\r\n localInstance = instance;\r\n }).catch(errorModuleStart => {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_ERROR,\r\n time: Date.now(),\r\n phase: `module.start`,\r\n error: errorModuleStart,\r\n });\r\n }).then(() => {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_STARTED,\r\n });\r\n });\r\n return;\r\n }\r\n if (action === CORE_STOP) {\r\n if (!localInstance) {\r\n // should never happen\r\n return;\r\n }\r\n Promise.resolve().then(() => {\r\n if (typeof stop === `function`) {\r\n return stop(wrapper.instance);\r\n }\r\n }).catch(errorModuleStop => {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_ERROR,\r\n time: Date.now(),\r\n phase: `module.stop`,\r\n error: errorModuleStop,\r\n });\r\n }).then(() => {\r\n localInstance = undefined;\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_STOPPED,\r\n });\r\n });\r\n return;\r\n }\r\n\r\n // todo CORE_GET_STATE, CORE_SET_STATE\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_ERROR,\r\n error: `action ${action} not implemented`,\r\n });\r\n});\r\n\r\n// module code below (defintion of start and stop)\n"; | ||
const workerGlueCode = "/**\n * Use it as a constructor\n * or as a decorator for an existing object\n * or as a base class for extend\n * cannot be used as a mixin for a constructor's prototype\n * without calling the constructor\n */\nfunction EventEmitter3(obj) {\n (obj || this)._callbacks = Object.create(null);\n if (obj) {return Object.assign(obj, EventEmitter3.prototype);}\n}\n\n/**\n * Listen on the given `eventName` with `fn`\n *\n * @param {String | Symbol} eventName\n * @param {Function} fn\n * @api public\n */\n\nEventEmitter3.prototype.on = function (eventName, fn) {\n (this._callbacks[eventName] = this._callbacks[eventName] || [])\n .push(fn);\n};\n\n/**\n * Adds an `eventName` listener that will be invoked once then removed\n *\n * @param {String | Symbol} eventName\n * @param {Function} fn\n * @api public\n */\n\nEventEmitter3.prototype.once = function (eventName, fn) {\n const once = (data) => {\n this.off(eventName, once);\n fn(data);\n };\n\n once.fn = fn; // makes it possible to remove with off\n this.on(eventName, once);\n};\n\n/**\n * Remove a callback for `eventName` or\n * all callbacks for `eventName` or\n * all callbacks for all events\n *\n * @param {String | Symbol} eventName\n * @param {Function} fn\n * @api public\n */\n\nEventEmitter3.prototype.off = function (eventName, fn) {\n // all\n if (!eventName) {\n this._callbacks = Object.create(null);\n return;\n }\n\n // specific event\n const callbacks = this._callbacks[eventName];\n if (!callbacks) {\n return;\n }\n\n // remove all handlers\n if (!fn) {\n delete this._callbacks[eventName];\n return;\n }\n\n // remove specific handler\n const index = callbacks.findIndex(function (cb) {\n return (cb === fn || cb.fn === fn);\n });\n if (index > -1) {\n // Remove event specific arrays for the eventName type that no\n // one is subscribed for, to avoid memory leak.\n if (callbacks.length === 1) {\n delete this._callbacks[eventName];\n } else {\n callbacks.splice(index, 1);\n }\n }\n};\n\n/**\n * Emit `eventName` with data\n *\n * @param {String | Symbol} eventName\n * @param {any} data\n */\n\nEventEmitter3.prototype.emit = function (eventName, data) {\n const callbacks = this._callbacks[eventName];\n if (!callbacks) {\n return;\n }\n const frozenCallbacks = Array.from(callbacks);\n frozenCallbacks.forEach(callback => {\n callback(data);\n });\n};\n\n/**\n * Return array of callbacks for `eventName`\n *\n * @param {String | Symbol} eventName\n * @return {Array} listeners\n * @api public\n */\n\nEventEmitter3.prototype.listeners = function (eventName) {\n return this._callbacks[eventName] || [];\n};\n\n/**\n * True if this emitter has `eventName` handlers\n *\n * @param {String | Symbol} eventName\n * @return {Boolean}\n * @api public\n */\n\nEventEmitter3.prototype.hasListeners = function (eventName) {\n return Boolean(this.listeners(eventName).length);\n};\n\n/**\n * Returns an array of event names for which the emitter has registered listeners\n *\n * @return {Array <String || Symbol>}\n * @api public\n */\nEventEmitter3.prototype.eventNames = function () {\n return Reflect.ownKeys(this._callbacks);\n};\n\n/**\n * Returns an array of event anmes of type string\n * for which the emitter has registered listeners\n *\n * @return {Array <String>}\n * @api public\n */\nEventEmitter3.prototype.eventNamesStrings = function () {\n return Object.keys(this._callbacks);\n};\n\n// prefix to avoid variable name collision\r\nconst CORE_ACTION_KEY = `action`;\r\n\r\n// actions\r\nconst CORE_EVENT = `CORE_EVENT`;\r\nconst CORE_START = `CORE_START`;\r\nconst CORE_STARTED = `CORE_STARTED`;\r\nconst CORE_STOP = `CORE_STOP`;\r\nconst CORE_STOPPED = `CORE_STOPPED`;\r\nconst CORE_GET_STATE = `CORE_GET_STATE`;\r\nconst CORE_SET_STATE = `CORE_SET_STATE`;\r\nconst CORE_ERROR = `CORE_ERROR`;\n\nlet localEmitter;\r\nlet localInstance;\r\n\r\nself.addEventListener(`error`, function (errorEvent) {\r\n errorEvent.preventDefault();\r\n let asString;\r\n if (errorEvent.message) {\r\n asString = errorEvent.message;\r\n } else {\r\n asString = String(errorEvent);\r\n }\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_ERROR,\r\n error: asString,\r\n });\r\n});\r\n\r\nself.addEventListener(`message`, async function(messageEvent) {\r\n const message = messageEvent.data;\r\n if (!Object.prototype.hasOwnProperty.call(message, CORE_ACTION_KEY)) {\r\n return;\r\n }\r\n const action = message[CORE_ACTION_KEY];\r\n if (action === CORE_EVENT) {\r\n if (!localInstance) {\r\n return;\r\n }\r\n // avoid infinite loop\r\n localEmitter.originalEmit(message.name, message.data);\r\n return;\r\n }\r\n if (action === CORE_START) {\r\n localEmitter = new EventEmitter3();\r\n localEmitter.originalEmit = localEmitter.emit;\r\n localEmitter.emit = function (eventName, data) {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_EVENT,\r\n name: eventName,\r\n data,\r\n });\r\n localEmitter.originalEmit(eventName, data);\r\n };\r\n \r\n Promise.resolve().then(() => {\r\n return start(localEmitter, message.data);\r\n }).then(instance => {\r\n localInstance = instance;\r\n }).catch(errorModuleStart => {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_ERROR,\r\n time: Date.now(),\r\n phase: `module.start`,\r\n error: errorModuleStart,\r\n });\r\n }).then(() => {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_STARTED,\r\n });\r\n });\r\n return;\r\n }\r\n if (action === CORE_STOP) {\r\n if (!localInstance) {\r\n // should never happen\r\n return;\r\n }\r\n Promise.resolve().then(() => {\r\n if (typeof stop === `function`) {\r\n return stop(wrapper.instance);\r\n }\r\n }).catch(errorModuleStop => {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_ERROR,\r\n time: Date.now(),\r\n phase: `module.stop`,\r\n error: errorModuleStop,\r\n });\r\n }).then(() => {\r\n localInstance = undefined;\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_STOPPED,\r\n });\r\n });\r\n return;\r\n }\r\n\r\n if (action === CORE_SET_STATE) {\r\n Promise.resolve().then(() => {\r\n if (typeof restoreState === `function`) {\r\n return restoreState(localInstance, message.data);\r\n }\r\n }).then(() => {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_GET_STATE,\r\n });\r\n }).catch(errorModuleRestoreState => {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_ERROR,\r\n time: Date.now(),\r\n phase: `module.restoreState`,\r\n error: errorModuleRestoreState,\r\n });\r\n });\r\n return;\r\n }\r\n if (action === CORE_GET_STATE) {\r\n Promise.resolve().then(() => {\r\n if (typeof getState === `function`) {\r\n return getState(localInstance);\r\n }\r\n }).then((result) => {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_SET_STATE,\r\n data: result, // core will handle undefined\r\n });\r\n }).catch(errorModuleRestoreState => {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_ERROR,\r\n time: Date.now(),\r\n phase: `module.restoreState`,\r\n error: errorModuleRestoreState,\r\n });\r\n });\r\n return;\r\n }\r\n\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_ERROR,\r\n error: `action ${action} not implemented`,\r\n });\r\n});\r\n\r\n// module code below (defintion of start and stop)\n"; | ||
@@ -324,2 +326,6 @@ // @ts-check | ||
const prepareWorkerCode = function (moduleCodeAsIIFE) { | ||
return `${workerGlueCode};${moduleCodeAsIIFE}`; | ||
}; | ||
const createCore = function () { | ||
@@ -338,2 +344,5 @@ const core = {}; | ||
const wrapper = core.moduleInstances.get(name); | ||
if (wrapper.worker) { | ||
return core.requestStateFromWorker(wrapper) | ||
} | ||
if (!wrapper.module.getState) { | ||
@@ -347,2 +356,11 @@ return Promise.resolve({}); | ||
requestStateFromWorker(wrapper) { | ||
return new Promise(function (resolve, reject) { | ||
wrapper.getStateResolve = resolve; | ||
wrapper.worker.postMessage({ | ||
[CORE_ACTION_KEY]: CORE_GET_STATE, | ||
}); | ||
}); | ||
}, | ||
/* returns a promise with an object | ||
@@ -375,2 +393,5 @@ as keys the names of the module instances | ||
const wrapper = core.moduleInstances.get(name); | ||
if (wrapper.worker) { | ||
return core.restoreStateInWorker(wrapper); | ||
} | ||
if (!wrapper.module.restoreState) { | ||
@@ -385,2 +406,11 @@ return Promise.resolve(); | ||
}, | ||
restoreStateInWorker(wrapper) { | ||
return new Promise(function (resolve, reject) { | ||
wrapper.setStateResolve = resolve; | ||
wrapper.worker.postMessage({ | ||
[CORE_ACTION_KEY]: CORE_SET_STATE, | ||
}); | ||
}); | ||
}, | ||
@@ -396,3 +426,4 @@ restoreAllStates(states) { | ||
data = undefined, | ||
worker = false | ||
worker = false, | ||
workerReady = false, | ||
} = {}) { | ||
@@ -405,3 +436,3 @@ | ||
if (worker) { | ||
return core.startWorker(module, name, data); | ||
return core.startWorker(module, name, data, workerReady); | ||
} | ||
@@ -437,11 +468,20 @@ | ||
startWorker(moduleUrl, name, data) { | ||
startWorker(moduleUrl, name, data, workerReady) { | ||
return new Promise(function (resolve, reject) { | ||
fetch(moduleUrl).then(response => { | ||
return response.text(); | ||
}).then(moduleCode => { | ||
const workerCode = `${workerGlueCode};${moduleCode}`; | ||
const workerBlob = new Blob([workerCode], JS_MIME); | ||
const workerObjectURL = URL.createObjectURL(workerBlob); | ||
const moduleInsideWorker = new Worker(workerObjectURL, { | ||
let workerRessourcePromise; | ||
if (workerReady) { | ||
workerRessourcePromise = Promise.resolve(moduleUrl); | ||
} else { | ||
workerRessourcePromise = fetch(moduleUrl).then(response => { | ||
return response.text(); | ||
}).then(moduleCode => { | ||
const workerCode = prepareWorkerCode(moduleCode); | ||
const workerBlob = new Blob([workerCode], JS_MIME); | ||
const workerObjectURL = URL.createObjectURL(workerBlob); | ||
return workerObjectURL | ||
}); | ||
} | ||
workerRessourcePromise.then(workerRessource => { | ||
const moduleInsideWorker = new Worker(workerRessource, { | ||
type: "module", | ||
@@ -488,4 +528,8 @@ name: String(name), // help debugging | ||
} | ||
if (action === CORE_ERROR) { | ||
core.emit(ERROR, message); | ||
return; | ||
} | ||
const wrapper = core.moduleInstances.get(name); | ||
if (action === CORE_STOPPED) { | ||
const wrapper = core.moduleInstances.get(name); | ||
if (wrapper?.stopResolve) { | ||
@@ -498,7 +542,17 @@ wrapper.stopResolve(); | ||
} | ||
if (action === CORE_ERROR) { | ||
core.emit(ERROR, message); | ||
if (action === CORE_SET_STATE) { | ||
if (wrapper?.getStateResolve) { | ||
wrapper.getStateResolve(message.data || {}); | ||
wrapper.getStateResolve = undefined; | ||
} | ||
return; | ||
} | ||
// todo CORE_GET_STATE set State | ||
if (action === CORE_GET_STATE) { | ||
if (wrapper?.setStateResolve) { | ||
wrapper.setStateResolve(); | ||
wrapper.setStateResolve = undefined; | ||
} | ||
return; | ||
} | ||
}); | ||
@@ -582,2 +636,3 @@ }, | ||
exports.createCore = createCore; | ||
exports.prepareWorkerCode = prepareWorkerCode; | ||
exports.replayEvents = replayEvents; | ||
@@ -584,0 +639,0 @@ exports.startEventRecorder = startEventRecorder; |
@@ -160,2 +160,4 @@ /** | ||
const CORE_STOPPED = `CORE_STOPPED`; | ||
const CORE_GET_STATE = `CORE_GET_STATE`; | ||
const CORE_SET_STATE = `CORE_SET_STATE`; | ||
const CORE_ERROR = `CORE_ERROR`; | ||
@@ -249,3 +251,42 @@ | ||
// todo CORE_GET_STATE, CORE_SET_STATE | ||
if (action === CORE_SET_STATE) { | ||
Promise.resolve().then(() => { | ||
if (typeof restoreState === `function`) { | ||
return restoreState(localInstance, message.data); | ||
} | ||
}).then(() => { | ||
self.postMessage({ | ||
[CORE_ACTION_KEY]: CORE_GET_STATE, | ||
}); | ||
}).catch(errorModuleRestoreState => { | ||
self.postMessage({ | ||
[CORE_ACTION_KEY]: CORE_ERROR, | ||
time: Date.now(), | ||
phase: `module.restoreState`, | ||
error: errorModuleRestoreState, | ||
}); | ||
}); | ||
return; | ||
} | ||
if (action === CORE_GET_STATE) { | ||
Promise.resolve().then(() => { | ||
if (typeof getState === `function`) { | ||
return getState(localInstance); | ||
} | ||
}).then((result) => { | ||
self.postMessage({ | ||
[CORE_ACTION_KEY]: CORE_SET_STATE, | ||
data: result, // core will handle undefined | ||
}); | ||
}).catch(errorModuleRestoreState => { | ||
self.postMessage({ | ||
[CORE_ACTION_KEY]: CORE_ERROR, | ||
time: Date.now(), | ||
phase: `module.restoreState`, | ||
error: errorModuleRestoreState, | ||
}); | ||
}); | ||
return; | ||
} | ||
self.postMessage({ | ||
@@ -252,0 +293,0 @@ [CORE_ACTION_KEY]: CORE_ERROR, |
export {workerGlueCode}; | ||
const workerGlueCode = "/**\n * Use it as a constructor\n * or as a decorator for an existing object\n * or as a base class for extend\n * cannot be used as a mixin for a constructor's prototype\n * without calling the constructor\n */\nfunction EventEmitter3(obj) {\n (obj || this)._callbacks = Object.create(null);\n if (obj) {return Object.assign(obj, EventEmitter3.prototype);}\n}\n\n/**\n * Listen on the given `eventName` with `fn`\n *\n * @param {String | Symbol} eventName\n * @param {Function} fn\n * @api public\n */\n\nEventEmitter3.prototype.on = function (eventName, fn) {\n (this._callbacks[eventName] = this._callbacks[eventName] || [])\n .push(fn);\n};\n\n/**\n * Adds an `eventName` listener that will be invoked once then removed\n *\n * @param {String | Symbol} eventName\n * @param {Function} fn\n * @api public\n */\n\nEventEmitter3.prototype.once = function (eventName, fn) {\n const once = (data) => {\n this.off(eventName, once);\n fn(data);\n };\n\n once.fn = fn; // makes it possible to remove with off\n this.on(eventName, once);\n};\n\n/**\n * Remove a callback for `eventName` or\n * all callbacks for `eventName` or\n * all callbacks for all events\n *\n * @param {String | Symbol} eventName\n * @param {Function} fn\n * @api public\n */\n\nEventEmitter3.prototype.off = function (eventName, fn) {\n // all\n if (!eventName) {\n this._callbacks = Object.create(null);\n return;\n }\n\n // specific event\n const callbacks = this._callbacks[eventName];\n if (!callbacks) {\n return;\n }\n\n // remove all handlers\n if (!fn) {\n delete this._callbacks[eventName];\n return;\n }\n\n // remove specific handler\n const index = callbacks.findIndex(function (cb) {\n return (cb === fn || cb.fn === fn);\n });\n if (index > -1) {\n // Remove event specific arrays for the eventName type that no\n // one is subscribed for, to avoid memory leak.\n if (callbacks.length === 1) {\n delete this._callbacks[eventName];\n } else {\n callbacks.splice(index, 1);\n }\n }\n};\n\n/**\n * Emit `eventName` with data\n *\n * @param {String | Symbol} eventName\n * @param {any} data\n */\n\nEventEmitter3.prototype.emit = function (eventName, data) {\n const callbacks = this._callbacks[eventName];\n if (!callbacks) {\n return;\n }\n const frozenCallbacks = Array.from(callbacks);\n frozenCallbacks.forEach(callback => {\n callback(data);\n });\n};\n\n/**\n * Return array of callbacks for `eventName`\n *\n * @param {String | Symbol} eventName\n * @return {Array} listeners\n * @api public\n */\n\nEventEmitter3.prototype.listeners = function (eventName) {\n return this._callbacks[eventName] || [];\n};\n\n/**\n * True if this emitter has `eventName` handlers\n *\n * @param {String | Symbol} eventName\n * @return {Boolean}\n * @api public\n */\n\nEventEmitter3.prototype.hasListeners = function (eventName) {\n return Boolean(this.listeners(eventName).length);\n};\n\n/**\n * Returns an array of event names for which the emitter has registered listeners\n *\n * @return {Array <String || Symbol>}\n * @api public\n */\nEventEmitter3.prototype.eventNames = function () {\n return Reflect.ownKeys(this._callbacks);\n};\n\n/**\n * Returns an array of event anmes of type string\n * for which the emitter has registered listeners\n *\n * @return {Array <String>}\n * @api public\n */\nEventEmitter3.prototype.eventNamesStrings = function () {\n return Object.keys(this._callbacks);\n};\n\n// prefix to avoid variable name collision\r\nconst CORE_ACTION_KEY = `action`;\r\n\r\n// actions\r\nconst CORE_EVENT = `CORE_EVENT`;\r\nconst CORE_START = `CORE_START`;\r\nconst CORE_STARTED = `CORE_STARTED`;\r\nconst CORE_STOP = `CORE_STOP`;\r\nconst CORE_STOPPED = `CORE_STOPPED`;\r\nconst CORE_ERROR = `CORE_ERROR`;\n\nlet localEmitter;\r\nlet localInstance;\r\n\r\nself.addEventListener(`error`, function (errorEvent) {\r\n errorEvent.preventDefault();\r\n let asString;\r\n if (errorEvent.message) {\r\n asString = errorEvent.message;\r\n } else {\r\n asString = String(errorEvent);\r\n }\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_ERROR,\r\n error: asString,\r\n });\r\n});\r\n\r\nself.addEventListener(`message`, async function(messageEvent) {\r\n const message = messageEvent.data;\r\n if (!Object.prototype.hasOwnProperty.call(message, CORE_ACTION_KEY)) {\r\n return;\r\n }\r\n const action = message[CORE_ACTION_KEY];\r\n if (action === CORE_EVENT) {\r\n if (!localInstance) {\r\n return;\r\n }\r\n // avoid infinite loop\r\n localEmitter.originalEmit(message.name, message.data);\r\n return;\r\n }\r\n if (action === CORE_START) {\r\n localEmitter = new EventEmitter3();\r\n localEmitter.originalEmit = localEmitter.emit;\r\n localEmitter.emit = function (eventName, data) {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_EVENT,\r\n name: eventName,\r\n data,\r\n });\r\n localEmitter.originalEmit(eventName, data);\r\n };\r\n \r\n Promise.resolve().then(() => {\r\n return start(localEmitter, message.data);\r\n }).then(instance => {\r\n localInstance = instance;\r\n }).catch(errorModuleStart => {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_ERROR,\r\n time: Date.now(),\r\n phase: `module.start`,\r\n error: errorModuleStart,\r\n });\r\n }).then(() => {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_STARTED,\r\n });\r\n });\r\n return;\r\n }\r\n if (action === CORE_STOP) {\r\n if (!localInstance) {\r\n // should never happen\r\n return;\r\n }\r\n Promise.resolve().then(() => {\r\n if (typeof stop === `function`) {\r\n return stop(wrapper.instance);\r\n }\r\n }).catch(errorModuleStop => {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_ERROR,\r\n time: Date.now(),\r\n phase: `module.stop`,\r\n error: errorModuleStop,\r\n });\r\n }).then(() => {\r\n localInstance = undefined;\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_STOPPED,\r\n });\r\n });\r\n return;\r\n }\r\n\r\n // todo CORE_GET_STATE, CORE_SET_STATE\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_ERROR,\r\n error: `action ${action} not implemented`,\r\n });\r\n});\r\n\r\n// module code below (defintion of start and stop)\n" | ||
const workerGlueCode = "/**\n * Use it as a constructor\n * or as a decorator for an existing object\n * or as a base class for extend\n * cannot be used as a mixin for a constructor's prototype\n * without calling the constructor\n */\nfunction EventEmitter3(obj) {\n (obj || this)._callbacks = Object.create(null);\n if (obj) {return Object.assign(obj, EventEmitter3.prototype);}\n}\n\n/**\n * Listen on the given `eventName` with `fn`\n *\n * @param {String | Symbol} eventName\n * @param {Function} fn\n * @api public\n */\n\nEventEmitter3.prototype.on = function (eventName, fn) {\n (this._callbacks[eventName] = this._callbacks[eventName] || [])\n .push(fn);\n};\n\n/**\n * Adds an `eventName` listener that will be invoked once then removed\n *\n * @param {String | Symbol} eventName\n * @param {Function} fn\n * @api public\n */\n\nEventEmitter3.prototype.once = function (eventName, fn) {\n const once = (data) => {\n this.off(eventName, once);\n fn(data);\n };\n\n once.fn = fn; // makes it possible to remove with off\n this.on(eventName, once);\n};\n\n/**\n * Remove a callback for `eventName` or\n * all callbacks for `eventName` or\n * all callbacks for all events\n *\n * @param {String | Symbol} eventName\n * @param {Function} fn\n * @api public\n */\n\nEventEmitter3.prototype.off = function (eventName, fn) {\n // all\n if (!eventName) {\n this._callbacks = Object.create(null);\n return;\n }\n\n // specific event\n const callbacks = this._callbacks[eventName];\n if (!callbacks) {\n return;\n }\n\n // remove all handlers\n if (!fn) {\n delete this._callbacks[eventName];\n return;\n }\n\n // remove specific handler\n const index = callbacks.findIndex(function (cb) {\n return (cb === fn || cb.fn === fn);\n });\n if (index > -1) {\n // Remove event specific arrays for the eventName type that no\n // one is subscribed for, to avoid memory leak.\n if (callbacks.length === 1) {\n delete this._callbacks[eventName];\n } else {\n callbacks.splice(index, 1);\n }\n }\n};\n\n/**\n * Emit `eventName` with data\n *\n * @param {String | Symbol} eventName\n * @param {any} data\n */\n\nEventEmitter3.prototype.emit = function (eventName, data) {\n const callbacks = this._callbacks[eventName];\n if (!callbacks) {\n return;\n }\n const frozenCallbacks = Array.from(callbacks);\n frozenCallbacks.forEach(callback => {\n callback(data);\n });\n};\n\n/**\n * Return array of callbacks for `eventName`\n *\n * @param {String | Symbol} eventName\n * @return {Array} listeners\n * @api public\n */\n\nEventEmitter3.prototype.listeners = function (eventName) {\n return this._callbacks[eventName] || [];\n};\n\n/**\n * True if this emitter has `eventName` handlers\n *\n * @param {String | Symbol} eventName\n * @return {Boolean}\n * @api public\n */\n\nEventEmitter3.prototype.hasListeners = function (eventName) {\n return Boolean(this.listeners(eventName).length);\n};\n\n/**\n * Returns an array of event names for which the emitter has registered listeners\n *\n * @return {Array <String || Symbol>}\n * @api public\n */\nEventEmitter3.prototype.eventNames = function () {\n return Reflect.ownKeys(this._callbacks);\n};\n\n/**\n * Returns an array of event anmes of type string\n * for which the emitter has registered listeners\n *\n * @return {Array <String>}\n * @api public\n */\nEventEmitter3.prototype.eventNamesStrings = function () {\n return Object.keys(this._callbacks);\n};\n\n// prefix to avoid variable name collision\r\nconst CORE_ACTION_KEY = `action`;\r\n\r\n// actions\r\nconst CORE_EVENT = `CORE_EVENT`;\r\nconst CORE_START = `CORE_START`;\r\nconst CORE_STARTED = `CORE_STARTED`;\r\nconst CORE_STOP = `CORE_STOP`;\r\nconst CORE_STOPPED = `CORE_STOPPED`;\r\nconst CORE_GET_STATE = `CORE_GET_STATE`;\r\nconst CORE_SET_STATE = `CORE_SET_STATE`;\r\nconst CORE_ERROR = `CORE_ERROR`;\n\nlet localEmitter;\r\nlet localInstance;\r\n\r\nself.addEventListener(`error`, function (errorEvent) {\r\n errorEvent.preventDefault();\r\n let asString;\r\n if (errorEvent.message) {\r\n asString = errorEvent.message;\r\n } else {\r\n asString = String(errorEvent);\r\n }\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_ERROR,\r\n error: asString,\r\n });\r\n});\r\n\r\nself.addEventListener(`message`, async function(messageEvent) {\r\n const message = messageEvent.data;\r\n if (!Object.prototype.hasOwnProperty.call(message, CORE_ACTION_KEY)) {\r\n return;\r\n }\r\n const action = message[CORE_ACTION_KEY];\r\n if (action === CORE_EVENT) {\r\n if (!localInstance) {\r\n return;\r\n }\r\n // avoid infinite loop\r\n localEmitter.originalEmit(message.name, message.data);\r\n return;\r\n }\r\n if (action === CORE_START) {\r\n localEmitter = new EventEmitter3();\r\n localEmitter.originalEmit = localEmitter.emit;\r\n localEmitter.emit = function (eventName, data) {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_EVENT,\r\n name: eventName,\r\n data,\r\n });\r\n localEmitter.originalEmit(eventName, data);\r\n };\r\n \r\n Promise.resolve().then(() => {\r\n return start(localEmitter, message.data);\r\n }).then(instance => {\r\n localInstance = instance;\r\n }).catch(errorModuleStart => {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_ERROR,\r\n time: Date.now(),\r\n phase: `module.start`,\r\n error: errorModuleStart,\r\n });\r\n }).then(() => {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_STARTED,\r\n });\r\n });\r\n return;\r\n }\r\n if (action === CORE_STOP) {\r\n if (!localInstance) {\r\n // should never happen\r\n return;\r\n }\r\n Promise.resolve().then(() => {\r\n if (typeof stop === `function`) {\r\n return stop(wrapper.instance);\r\n }\r\n }).catch(errorModuleStop => {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_ERROR,\r\n time: Date.now(),\r\n phase: `module.stop`,\r\n error: errorModuleStop,\r\n });\r\n }).then(() => {\r\n localInstance = undefined;\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_STOPPED,\r\n });\r\n });\r\n return;\r\n }\r\n\r\n if (action === CORE_SET_STATE) {\r\n Promise.resolve().then(() => {\r\n if (typeof restoreState === `function`) {\r\n return restoreState(localInstance, message.data);\r\n }\r\n }).then(() => {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_GET_STATE,\r\n });\r\n }).catch(errorModuleRestoreState => {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_ERROR,\r\n time: Date.now(),\r\n phase: `module.restoreState`,\r\n error: errorModuleRestoreState,\r\n });\r\n });\r\n return;\r\n }\r\n if (action === CORE_GET_STATE) {\r\n Promise.resolve().then(() => {\r\n if (typeof getState === `function`) {\r\n return getState(localInstance);\r\n }\r\n }).then((result) => {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_SET_STATE,\r\n data: result, // core will handle undefined\r\n });\r\n }).catch(errorModuleRestoreState => {\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_ERROR,\r\n time: Date.now(),\r\n phase: `module.restoreState`,\r\n error: errorModuleRestoreState,\r\n });\r\n });\r\n return;\r\n }\r\n\r\n self.postMessage({\r\n [CORE_ACTION_KEY]: CORE_ERROR,\r\n error: `action ${action} not implemented`,\r\n });\r\n});\r\n\r\n// module code below (defintion of start and stop)\n" |
{ | ||
"name": "@eroc/core", | ||
"version": "4.1.0", | ||
"version": "4.3.0", | ||
"description": "Lightweight framework for scalable applications", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
@@ -184,2 +184,3 @@ # core [![Build Status](https://travis-ci.org/mauriciosoares/core.js.svg?branch=master)](https://travis-ci.org/mauriciosoares/core.js) [![Coverage Status](https://img.shields.io/coveralls/mauriciosoares/core.js.svg)](https://coveralls.io/r/mauriciosoares/core.js) [![Code Climate](https://codeclimate.com/github/mauriciosoares/core.js/badges/gpa.svg)](https://codeclimate.com/github/mauriciosoares/core.js) | ||
* worker optional, if true the module will be inside a worker see 4.1.0 limitations | ||
* workerReady, mark as true if the url has code prepared with storeWorkerCodeFromIIFEFile or prepareWorkerCode | ||
@@ -294,2 +295,15 @@ returns a promise that resolves with *moduleInstanceId* that can later be used to stop the module | ||
## Preparing worker code with prepareWorkerCode.js | ||
```js | ||
import {prepareWorkerCode, storeWorkerCodeFromIIFEFile} from "@eroc/core"; | ||
// source has to be an IIFE that exposes start and stop | ||
const source = "./examples/gameOfLife/gameOfLife.js"; | ||
const destination = "./examples/gameOfLife/gameOfLifeWorkerReady.js"; | ||
storeWorkerCodeFromIIFEFile(source, destination); | ||
``` | ||
This code can then be used with the workerReady option | ||
## Maintainers | ||
@@ -319,6 +333,14 @@ | ||
### 4.3.0 | ||
* core.js and prepareWorkerCode.js export a function prepareWorkerCode | ||
* prepareWorkerCode.js exports a function storeWorkerCodeFromIIFEFile | ||
### 4.2.0 | ||
* Modules inside worker can use restoreState and getState | ||
### 4.1.0 | ||
* Modules without imports and export, and without setState and getState can run inside worker | ||
* Modules without imports and export, without restoreState and getState can run inside worker | ||
@@ -325,0 +347,0 @@ ### 4.0.0 |
// @ts-check | ||
export { createCore, ALL, ERROR }; | ||
export { createCore, ALL, ERROR, prepareWorkerCode }; | ||
export { startEventRecorder, stopEventRecorder } from "./eventRecorder.js"; | ||
@@ -27,2 +27,6 @@ export { replayEvents } from "./eventPlayer.js"; | ||
const prepareWorkerCode = function (moduleCodeAsIIFE) { | ||
return `${workerGlueCode};${moduleCodeAsIIFE}`; | ||
}; | ||
const createCore = function () { | ||
@@ -41,2 +45,5 @@ const core = {}; | ||
const wrapper = core.moduleInstances.get(name); | ||
if (wrapper.worker) { | ||
return core.requestStateFromWorker(wrapper) | ||
} | ||
if (!wrapper.module.getState) { | ||
@@ -50,2 +57,11 @@ return Promise.resolve({}); | ||
requestStateFromWorker(wrapper) { | ||
return new Promise(function (resolve, reject) { | ||
wrapper.getStateResolve = resolve; | ||
wrapper.worker.postMessage({ | ||
[CORE_ACTION_KEY]: CORE_GET_STATE, | ||
}); | ||
}); | ||
}, | ||
/* returns a promise with an object | ||
@@ -78,2 +94,5 @@ as keys the names of the module instances | ||
const wrapper = core.moduleInstances.get(name); | ||
if (wrapper.worker) { | ||
return core.restoreStateInWorker(wrapper); | ||
} | ||
if (!wrapper.module.restoreState) { | ||
@@ -88,2 +107,11 @@ return Promise.resolve(); | ||
}, | ||
restoreStateInWorker(wrapper) { | ||
return new Promise(function (resolve, reject) { | ||
wrapper.setStateResolve = resolve; | ||
wrapper.worker.postMessage({ | ||
[CORE_ACTION_KEY]: CORE_SET_STATE, | ||
}); | ||
}); | ||
}, | ||
@@ -99,3 +127,4 @@ restoreAllStates(states) { | ||
data = undefined, | ||
worker = false | ||
worker = false, | ||
workerReady = false, | ||
} = {}) { | ||
@@ -108,3 +137,3 @@ | ||
if (worker) { | ||
return core.startWorker(module, name, data); | ||
return core.startWorker(module, name, data, workerReady); | ||
} | ||
@@ -140,11 +169,20 @@ | ||
startWorker(moduleUrl, name, data) { | ||
startWorker(moduleUrl, name, data, workerReady) { | ||
return new Promise(function (resolve, reject) { | ||
fetch(moduleUrl).then(response => { | ||
return response.text(); | ||
}).then(moduleCode => { | ||
const workerCode = `${workerGlueCode};${moduleCode}`; | ||
const workerBlob = new Blob([workerCode], JS_MIME); | ||
const workerObjectURL = URL.createObjectURL(workerBlob); | ||
const moduleInsideWorker = new Worker(workerObjectURL, { | ||
let workerRessourcePromise; | ||
if (workerReady) { | ||
workerRessourcePromise = Promise.resolve(moduleUrl); | ||
} else { | ||
workerRessourcePromise = fetch(moduleUrl).then(response => { | ||
return response.text(); | ||
}).then(moduleCode => { | ||
const workerCode = prepareWorkerCode(moduleCode); | ||
const workerBlob = new Blob([workerCode], JS_MIME); | ||
const workerObjectURL = URL.createObjectURL(workerBlob); | ||
return workerObjectURL | ||
}) | ||
} | ||
workerRessourcePromise.then(workerRessource => { | ||
const moduleInsideWorker = new Worker(workerRessource, { | ||
type: "module", | ||
@@ -191,4 +229,8 @@ name: String(name), // help debugging | ||
} | ||
if (action === CORE_ERROR) { | ||
core.emit(ERROR, message) | ||
return; | ||
} | ||
const wrapper = core.moduleInstances.get(name); | ||
if (action === CORE_STOPPED) { | ||
const wrapper = core.moduleInstances.get(name); | ||
if (wrapper?.stopResolve) { | ||
@@ -201,7 +243,17 @@ wrapper.stopResolve(); | ||
} | ||
if (action === CORE_ERROR) { | ||
core.emit(ERROR, message) | ||
if (action === CORE_SET_STATE) { | ||
if (wrapper?.getStateResolve) { | ||
wrapper.getStateResolve(message.data || {}); | ||
wrapper.getStateResolve = undefined; | ||
} | ||
return; | ||
} | ||
// todo CORE_GET_STATE set State | ||
if (action === CORE_GET_STATE) { | ||
if (wrapper?.setStateResolve) { | ||
wrapper.setStateResolve(); | ||
wrapper.setStateResolve = undefined; | ||
} | ||
return; | ||
} | ||
}); | ||
@@ -208,0 +260,0 @@ }, |
@@ -103,3 +103,42 @@ "use strict"; | ||
// todo CORE_GET_STATE, CORE_SET_STATE | ||
if (action === CORE_SET_STATE) { | ||
Promise.resolve().then(() => { | ||
if (typeof restoreState === `function`) { | ||
return restoreState(localInstance, message.data); | ||
} | ||
}).then(() => { | ||
self.postMessage({ | ||
[CORE_ACTION_KEY]: CORE_GET_STATE, | ||
}); | ||
}).catch(errorModuleRestoreState => { | ||
self.postMessage({ | ||
[CORE_ACTION_KEY]: CORE_ERROR, | ||
time: Date.now(), | ||
phase: `module.restoreState`, | ||
error: errorModuleRestoreState, | ||
}); | ||
}); | ||
return; | ||
} | ||
if (action === CORE_GET_STATE) { | ||
Promise.resolve().then(() => { | ||
if (typeof getState === `function`) { | ||
return getState(localInstance); | ||
} | ||
}).then((result) => { | ||
self.postMessage({ | ||
[CORE_ACTION_KEY]: CORE_SET_STATE, | ||
data: result, // core will handle undefined | ||
}); | ||
}).catch(errorModuleRestoreState => { | ||
self.postMessage({ | ||
[CORE_ACTION_KEY]: CORE_ERROR, | ||
time: Date.now(), | ||
phase: `module.restoreState`, | ||
error: errorModuleRestoreState, | ||
}); | ||
}); | ||
return; | ||
} | ||
self.postMessage({ | ||
@@ -106,0 +145,0 @@ [CORE_ACTION_KEY]: CORE_ERROR, |
@@ -8,2 +8,3 @@ export { | ||
CORE_STOPPED, | ||
// symmetry | ||
CORE_GET_STATE, | ||
@@ -10,0 +11,0 @@ CORE_SET_STATE, |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
136161
15
2526
406
1