@eroc/core
Advanced tools
Comparing version 3.0.1 to 3.1.0
@@ -1,2 +0,2 @@ | ||
/* @eroc/core v3.0.1 2020-05-09T00:13:19.459Z licensed MIT */ | ||
/* @eroc/core v3.1.0 2020-07-23T14:06:34.744Z licensed MIT */ | ||
const startEventRecorder = (core) => { | ||
@@ -225,2 +225,75 @@ const events = []; | ||
/** | ||
only works with undefined, null, Number, Symbol, String, Big Int, Object, Array, | ||
warning | ||
does not work with cyclic objects | ||
does not work with anything created with new | ||
*/ | ||
const deepCopy = x => { | ||
if (typeof x !== `object` || x === null) { | ||
return x; | ||
} | ||
if (Array.isArray(x)) { | ||
return x.map(deepCopy); | ||
} | ||
const copy = {}; | ||
Object.entries(x).forEach(([key, value]) => { | ||
copy[key] = deepCopy(value); | ||
}); | ||
return copy; | ||
}; | ||
/** | ||
like deepCopy but supports more types | ||
works with | ||
undefined, null, Number, Symbol, String, Big Int, | ||
Object, Array, | ||
Date, RegExp, Set, Map, | ||
Uint8Array, Uint16Array, Uint32Array, | ||
Int8Array, Int16Array, Int32Array | ||
warning | ||
does not work with cyclic object | ||
does not copy internal links | ||
*/ | ||
const deepCopyAdded = x => { | ||
if (typeof x !== `object` || x === null) { | ||
return x; | ||
} | ||
if (x instanceof Date) { | ||
return new Date(x); | ||
} | ||
if (x instanceof RegExp) { | ||
return new RegExp(x); | ||
} | ||
if (x instanceof Set) { | ||
return new Set(Array.from(x, deepCopyAdded)); | ||
} | ||
if (x instanceof Map) { | ||
const map = new Map(); | ||
// todo keep internal links | ||
x.forEach((value, key) => { | ||
map.set(deepCopyAdded(key), deepCopyAdded(value)); | ||
}); | ||
return map; | ||
} | ||
if (Array.isArray(x)) { | ||
return x.map(deepCopy); | ||
} | ||
if (ArrayBuffer.isView(x) && !(x instanceof DataView)) { | ||
return new x.constructor(x); | ||
} | ||
const copy = {}; | ||
Object.entries(x).forEach(([key, value]) => { | ||
copy[key] = deepCopy(value); | ||
}); | ||
return copy; | ||
}; | ||
const ALL = Symbol(); | ||
@@ -237,6 +310,59 @@ const ERROR = Symbol(); | ||
register() { | ||
getState(name) { | ||
if (!this.moduleInstances.has(name)) { | ||
return Promise.reject(`module with name ${name} does not exist`); | ||
} | ||
const wrapper = this.moduleInstances.get(name); | ||
if (!wrapper.module.getState) { | ||
return Promise.resolve({}); | ||
} | ||
return Promise.resolve().then(() => { | ||
return wrapper.module.getState(wrapper.instance); | ||
}); | ||
} | ||
/* returns a promise with an object | ||
as keys the names of the module instances | ||
as value the state received */ | ||
getAllStates() { | ||
const promises = []; | ||
const names = []; // freeze names in case something is added ore removed while the promise is being resolved | ||
this.moduleInstances.forEach((wrapper, name) => { | ||
promises.push(this.getState(name)); | ||
names.push(name); | ||
}); | ||
return Promise.all(promises).then((results) => { | ||
// Promise.all preserves order | ||
const resultsAsObject = {}; | ||
results.forEach((result, i) => { | ||
resultsAsObject[names[i]] = result; | ||
}); | ||
return resultsAsObject; | ||
}); | ||
} | ||
restoreState (name, state) { | ||
if (!this.moduleInstances.has(name)) { | ||
return Promise.reject(`module with name ${name} does not exist`); | ||
} | ||
const wrapper = this.moduleInstances.get(name); | ||
if (!wrapper.module.restoreState) { | ||
return Promise.resolve(); | ||
} | ||
return Promise.resolve().then(() => { | ||
const stateCopy = deepCopyAdded(state); // avoid mutations | ||
return wrapper.module.restoreState(wrapper.instance, stateCopy); | ||
}); | ||
} | ||
restoreAllStates(states) { | ||
return Promise.all(Object.entries(states).map(([name, state]) => { | ||
return this.restoreState(name, state); | ||
})); | ||
} | ||
start(module, { name = Symbol() } = {}) { | ||
@@ -243,0 +369,0 @@ if (this.moduleInstances.has(name)) { |
@@ -1,2 +0,2 @@ | ||
/* @eroc/core v3.0.1 2020-05-09T00:13:19.459Z licensed MIT */ | ||
/* @eroc/core v3.1.0 2020-07-23T14:06:34.744Z licensed MIT */ | ||
var Core = (function (exports) { | ||
@@ -228,2 +228,75 @@ 'use strict'; | ||
/** | ||
only works with undefined, null, Number, Symbol, String, Big Int, Object, Array, | ||
warning | ||
does not work with cyclic objects | ||
does not work with anything created with new | ||
*/ | ||
const deepCopy = x => { | ||
if (typeof x !== `object` || x === null) { | ||
return x; | ||
} | ||
if (Array.isArray(x)) { | ||
return x.map(deepCopy); | ||
} | ||
const copy = {}; | ||
Object.entries(x).forEach(([key, value]) => { | ||
copy[key] = deepCopy(value); | ||
}); | ||
return copy; | ||
}; | ||
/** | ||
like deepCopy but supports more types | ||
works with | ||
undefined, null, Number, Symbol, String, Big Int, | ||
Object, Array, | ||
Date, RegExp, Set, Map, | ||
Uint8Array, Uint16Array, Uint32Array, | ||
Int8Array, Int16Array, Int32Array | ||
warning | ||
does not work with cyclic object | ||
does not copy internal links | ||
*/ | ||
const deepCopyAdded = x => { | ||
if (typeof x !== `object` || x === null) { | ||
return x; | ||
} | ||
if (x instanceof Date) { | ||
return new Date(x); | ||
} | ||
if (x instanceof RegExp) { | ||
return new RegExp(x); | ||
} | ||
if (x instanceof Set) { | ||
return new Set(Array.from(x, deepCopyAdded)); | ||
} | ||
if (x instanceof Map) { | ||
const map = new Map(); | ||
// todo keep internal links | ||
x.forEach((value, key) => { | ||
map.set(deepCopyAdded(key), deepCopyAdded(value)); | ||
}); | ||
return map; | ||
} | ||
if (Array.isArray(x)) { | ||
return x.map(deepCopy); | ||
} | ||
if (ArrayBuffer.isView(x) && !(x instanceof DataView)) { | ||
return new x.constructor(x); | ||
} | ||
const copy = {}; | ||
Object.entries(x).forEach(([key, value]) => { | ||
copy[key] = deepCopy(value); | ||
}); | ||
return copy; | ||
}; | ||
const ALL = Symbol(); | ||
@@ -240,6 +313,59 @@ const ERROR = Symbol(); | ||
register() { | ||
getState(name) { | ||
if (!this.moduleInstances.has(name)) { | ||
return Promise.reject(`module with name ${name} does not exist`); | ||
} | ||
const wrapper = this.moduleInstances.get(name); | ||
if (!wrapper.module.getState) { | ||
return Promise.resolve({}); | ||
} | ||
return Promise.resolve().then(() => { | ||
return wrapper.module.getState(wrapper.instance); | ||
}); | ||
} | ||
/* returns a promise with an object | ||
as keys the names of the module instances | ||
as value the state received */ | ||
getAllStates() { | ||
const promises = []; | ||
const names = []; // freeze names in case something is added ore removed while the promise is being resolved | ||
this.moduleInstances.forEach((wrapper, name) => { | ||
promises.push(this.getState(name)); | ||
names.push(name); | ||
}); | ||
return Promise.all(promises).then((results) => { | ||
// Promise.all preserves order | ||
const resultsAsObject = {}; | ||
results.forEach((result, i) => { | ||
resultsAsObject[names[i]] = result; | ||
}); | ||
return resultsAsObject; | ||
}); | ||
} | ||
restoreState (name, state) { | ||
if (!this.moduleInstances.has(name)) { | ||
return Promise.reject(`module with name ${name} does not exist`); | ||
} | ||
const wrapper = this.moduleInstances.get(name); | ||
if (!wrapper.module.restoreState) { | ||
return Promise.resolve(); | ||
} | ||
return Promise.resolve().then(() => { | ||
const stateCopy = deepCopyAdded(state); // avoid mutations | ||
return wrapper.module.restoreState(wrapper.instance, stateCopy); | ||
}); | ||
} | ||
restoreAllStates(states) { | ||
return Promise.all(Object.entries(states).map(([name, state]) => { | ||
return this.restoreState(name, state); | ||
})); | ||
} | ||
start(module, { name = Symbol() } = {}) { | ||
@@ -246,0 +372,0 @@ if (this.moduleInstances.has(name)) { |
{ | ||
"name": "@eroc/core", | ||
"version": "3.0.1", | ||
"version": "3.1.0", | ||
"description": "Lightweight framework for scalable applications", | ||
@@ -21,10 +21,11 @@ "license": "MIT", | ||
"@rollup/plugin-node-resolve": "^7.1.3", | ||
"eslint": "^6.8.0", | ||
"eslint": "^7.4.0", | ||
"eslint-config-red": "^1.7.0", | ||
"jasmine": "^3.5.0", | ||
"rollup": "^2.8.2", | ||
"serve": "^11.3.0" | ||
"rollup": "^2.19.0", | ||
"serve": "^11.3.2" | ||
}, | ||
"dependencies": { | ||
"event-e3": "^8.0.2" | ||
"event-e3": "^8.0.2", | ||
"utilsac": "^12.3.1" | ||
}, | ||
@@ -31,0 +32,0 @@ "eslintConfig": { |
@@ -49,3 +49,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) | ||
A module exports start and optionally a stop function. | ||
A module exports start. | ||
@@ -59,3 +59,14 @@ ```js | ||
}; | ||
``` | ||
A module may export a stop function. | ||
Optional: | ||
* stop | ||
* getState | ||
* restoreState | ||
```js | ||
const stop = function (instance) { | ||
@@ -65,2 +76,14 @@ // instance is what start returned | ||
}; | ||
const restoreState = function (instance, state) { | ||
// instance is what start returned | ||
// do what is necessary to restore sate | ||
}; | ||
const getState = function (instance) { | ||
// return the current state | ||
// for example in a drawing application , all the coordinates and shapes drawn | ||
//return instance.drawn; | ||
}; | ||
``` | ||
@@ -243,3 +266,3 @@ | ||
Will replay previousEvents on core. previousEvents could come from `eventRecording.events` or from a database. Make sure to initialize modules before for it to have any effect. While events are replayed regulare event emits are disabled. This avoids duplicated events in case you emit events as a consequence of another event. | ||
Will replay previousEvents on core. previousEvents could come from `eventRecording.events` or from a database. Make sure to initialize modules before for it to have any effect. While events are replayed regular event emits are disabled. This avoids duplicated events in case you emit events as a consequence of another event. | ||
@@ -257,2 +280,16 @@ | ||
### `await core.restoreAllStates({})` | ||
ReplayEvents might not be possible past a certain limit. That is why you may want to implement a state restoring mechanism. | ||
Expects an object with keys being modules names and values being the state to restore. Modules need a `restoreState` function for this to have any effect. | ||
### `await core.getAllStates()` | ||
Returns all states. Resolved value is the same shape as what `core.restoreAllStates` expects. Has no effect for modules that do not define a `getState` function. | ||
## Fast load with restoreAllState + eventPlayer | ||
Ideally neither restoreAllState nor eventPlayer are used to load a given state. EventPlayer alone would require to store all events from the beginning and replaying them 1 by one which can take huge overhead in both memory and time. And restoreAll state would lose precision, because not every state is saved. So the ideal is to periodically save state and capture the events from there on. | ||
## Maintainers | ||
@@ -282,2 +319,10 @@ | ||
### 3.1.0 | ||
* Add optional getState and restoreState function as part as a module | ||
* Add getState to the core | ||
* Add getAllStates to the core | ||
* Add restoreState to the core | ||
* Add restoreAllStates to the core | ||
### 3.0.0 | ||
@@ -284,0 +329,0 @@ |
@@ -6,2 +6,3 @@ export { Core, ALL, ERROR }; | ||
import EventEmitter from "event-e3"; | ||
import { deepCopyAdded } from "utilsac/deep.js"; | ||
@@ -20,6 +21,59 @@ | ||
register() { | ||
getState(name) { | ||
if (!this.moduleInstances.has(name)) { | ||
return Promise.reject(`module with name ${name} does not exist`); | ||
} | ||
const wrapper = this.moduleInstances.get(name); | ||
if (!wrapper.module.getState) { | ||
return Promise.resolve({}); | ||
} | ||
return Promise.resolve().then(() => { | ||
return wrapper.module.getState(wrapper.instance); | ||
}); | ||
} | ||
/* returns a promise with an object | ||
as keys the names of the module instances | ||
as value the state received */ | ||
getAllStates() { | ||
const promises = []; | ||
const names = []; // freeze names in case something is added ore removed while the promise is being resolved | ||
this.moduleInstances.forEach((wrapper, name) => { | ||
promises.push(this.getState(name)); | ||
names.push(name); | ||
}); | ||
return Promise.all(promises).then((results) => { | ||
// Promise.all preserves order | ||
const resultsAsObject = {}; | ||
results.forEach((result, i) => { | ||
resultsAsObject[names[i]] = result; | ||
}); | ||
return resultsAsObject; | ||
}); | ||
} | ||
restoreState (name, state) { | ||
if (!this.moduleInstances.has(name)) { | ||
return Promise.reject(`module with name ${name} does not exist`); | ||
} | ||
const wrapper = this.moduleInstances.get(name); | ||
if (!wrapper.module.restoreState) { | ||
return Promise.resolve(); | ||
} | ||
return Promise.resolve().then(() => { | ||
const stateCopy = deepCopyAdded(state); // avoid mutations | ||
return wrapper.module.restoreState(wrapper.instance, stateCopy); | ||
}); | ||
} | ||
restoreAllStates(states) { | ||
return Promise.all(Object.entries(states).map(([name, state]) => { | ||
return this.restoreState(name, state); | ||
})); | ||
} | ||
start(module, { name = Symbol() } = {}) { | ||
@@ -26,0 +80,0 @@ if (this.moduleInstances.has(name)) { |
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
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
59319
1398
361
2
+ Addedutilsac@^12.3.1
+ Addedutilsac@12.4.0(transitive)