seqda 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +153 -58
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "seqda",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "Sequential Data Store",
5
5
  "main": "src/index.js",
6
6
  "type": "commonjs",
package/src/index.js CHANGED
@@ -6,15 +6,62 @@ const EventEmitter = require('events');
6
6
  const QUEUE_CHANGE_EVENT = Symbol.for('__seqdaQueueChangeEvent');
7
7
  const QUEUE_CHANGE_INFO = Symbol.for('__seqdaQueueChangeInfo');
8
8
  const INTERNAL_STATE = Symbol.for('__seqdaInternalState');
9
+ const UNBOUND_METHOD = Symbol.for('__seqdaUnboundMethod');
10
+ const DISALLOW_WRITE = Symbol.for('__seqdaDisallowWrite');
11
+
12
+ function cloneStore(store, readyOnly) {
13
+ const cloneScope = (scope, _newStore) => {
14
+ let keys = Object.keys(scope);
15
+ let newScope = (!_newStore) ? new EventEmitter() : {};
16
+ let newStore = _newStore || newScope;
17
+
18
+ if (!_newStore)
19
+ newStore.setMaxListeners(Infinity);
20
+
21
+ for (let i = 0, il = keys.length; i < il; i++) {
22
+ let key = keys[i];
23
+ if (key === '_events' || key === '_eventsCount' || key === '_maxListeners')
24
+ continue;
25
+
26
+ let value = scope[key];
27
+ if (typeof value === 'function')
28
+ newScope[key] = storeUnboundMethod(value[UNBOUND_METHOD].bind(newStore), value[UNBOUND_METHOD]);
29
+ else
30
+ newScope[key] = cloneScope(value, newStore);
31
+ }
32
+
33
+ return newScope;
34
+ };
35
+
36
+ let clonedStore = cloneScope(store);
37
+ let clonedInternalState = Object.assign({}, store[INTERNAL_STATE]);
38
+
39
+ Object.defineProperties(clonedStore, {
40
+ [INTERNAL_STATE]: {
41
+ writable: (readyOnly !== true) ? true : false,
42
+ enumberable: false,
43
+ configurable: false,
44
+ value: (readyOnly !== true) ? clonedInternalState : Object.freeze(clonedInternalState),
45
+ },
46
+ });
47
+
48
+ return initializeStore(clonedStore, readyOnly);
49
+ }
9
50
 
10
51
  function queueChangeEvent(path) {
11
52
  let info = this[QUEUE_CHANGE_INFO];
12
53
  if (!info.promise) {
13
54
  info.promise = Promise.resolve();
14
55
  info.promise.then(() => {
15
- this.emit('update', { store: this, modified: Array.from(Object.keys(info.eventQueue)) });
56
+ this.emit('update', {
57
+ store: this,
58
+ previousStore: info.previousStore,
59
+ modified: Array.from(Object.keys(info.eventQueue)),
60
+ });
61
+
16
62
  info.eventQueue = {};
17
63
  info.promise = null;
64
+ info.previousStore = cloneStore(this, true);
18
65
  });
19
66
  }
20
67
 
@@ -92,8 +139,22 @@ function getPath(...parts) {
92
139
  return parts.filter(Boolean).join('.');
93
140
  }
94
141
 
95
- function createStoreSubsection(options, store, sectionTemplate, path) {
96
- const isCacheInvalid = (scopeName, args) => {
142
+ function storeUnboundMethod(boundMethod, method) {
143
+ Object.defineProperty(boundMethod, UNBOUND_METHOD, {
144
+ writable: false,
145
+ enumerable: false,
146
+ configurable: false,
147
+ value: method,
148
+ });
149
+
150
+ return boundMethod;
151
+ }
152
+
153
+ function createStoreSubsection(options, sectionTemplate, path) {
154
+ function isCacheInvalid(scopeName, args) {
155
+ if (this[DISALLOW_WRITE])
156
+ return true;
157
+
97
158
  let thisCache = cache[scopeName];
98
159
  if (!thisCache)
99
160
  return true;
@@ -108,61 +169,75 @@ function createStoreSubsection(options, store, sectionTemplate, path) {
108
169
  }
109
170
 
110
171
  return false;
111
- };
172
+ }
173
+
174
+ function setCache(scopeName, args, result) {
175
+ if (this[DISALLOW_WRITE])
176
+ return;
112
177
 
113
- const setCache = (scopeName, args, result) => {
114
178
  cache[scopeName] = {
115
179
  args,
116
180
  result,
117
181
  };
118
- };
182
+ }
119
183
 
120
184
  const createScopeMethod = (scopeName, func) => {
121
- return (...args) => {
122
- if (isCacheInvalid(scopeName, args)) {
123
- let result = func({ get, set, store }, ...args);
124
- setCache(scopeName, args, result);
185
+ let method = function(...args) {
186
+ if (isCacheInvalid.call(this, scopeName, args)) {
187
+ let result = func({
188
+ get: getState.bind(this),
189
+ set: setState.bind(this),
190
+ store: this,
191
+ }, ...args);
192
+
193
+ setCache.call(this, scopeName, args, result);
194
+
125
195
  return result;
126
196
  } else {
127
197
  return cache[scopeName].result;
128
198
  }
129
199
  };
200
+
201
+ return storeUnboundMethod(method.bind(this), method);
130
202
  };
131
203
 
132
- const get = () => {
204
+ function getState() {
133
205
  if (options.emitOnFetch === true)
134
- store.emit('fetchScope', { store, scopeName: path });
206
+ this.emit('fetchScope', { store: this, scopeName: path });
135
207
 
136
- let currentState = Nife.get(store[INTERNAL_STATE], path);
208
+ let currentState = Nife.get(this[INTERNAL_STATE], path);
137
209
  return currentState;
138
- };
210
+ }
139
211
 
140
- const set = (value) => {
141
- let currentState = Nife.get(store[INTERNAL_STATE], path);
212
+ function setState(value) {
213
+ if (this[DISALLOW_WRITE])
214
+ return;
215
+
216
+ let currentState = Nife.get(this[INTERNAL_STATE], path);
142
217
  if (value && typeof value === 'object' && value === currentState)
143
218
  throw new Error(`Error: "${getPath(path)}" the state value is the same, but it is required to be different.`);
144
219
 
145
220
  let previousState = currentState;
146
- store[INTERNAL_STATE] = setPath(store[INTERNAL_STATE], path, value);
221
+ this[INTERNAL_STATE] = setPath(this[INTERNAL_STATE], path, value);
147
222
 
148
223
  cache = {};
149
224
 
150
- if (store[QUEUE_CHANGE_EVENT])
151
- store[QUEUE_CHANGE_EVENT](path, value, previousState);
225
+ if (this[QUEUE_CHANGE_EVENT])
226
+ this[QUEUE_CHANGE_EVENT](path, value, previousState);
152
227
 
153
228
  return value;
154
- };
229
+ }
155
230
 
156
231
  if (path && !Object.prototype.hasOwnProperty.call(sectionTemplate, '_'))
157
232
  throw new Error(`Error: "${getPath}._" default value must be defined.`);
158
233
 
159
- const scope = (!path) ? store : {};
234
+ const scope = (!path) ? this : {};
160
235
  let keys = Object.keys(sectionTemplate || {});
161
236
  let subScopes = [];
162
237
  let cache = {};
163
238
 
164
239
  if (path)
165
- set(clone(sectionTemplate._));
240
+ setState.call(this, clone(sectionTemplate._));
166
241
 
167
242
  for (let i = 0, il = keys.length; i < il; i++) {
168
243
  let key = keys[i];
@@ -171,7 +246,7 @@ function createStoreSubsection(options, store, sectionTemplate, path) {
171
246
 
172
247
  let value = sectionTemplate[key];
173
248
  if (Nife.instanceOf(value, 'object')) {
174
- scope[key] = createStoreSubsection(options, store, value, getPath(path, key));
249
+ scope[key] = createStoreSubsection.call(this, options, value, getPath(path, key));
175
250
  subScopes.push(key);
176
251
  continue;
177
252
  }
@@ -183,11 +258,61 @@ function createStoreSubsection(options, store, sectionTemplate, path) {
183
258
  }
184
259
 
185
260
  if (!path)
186
- return scope; // We can't freeze the store
261
+ return this; // We can't freeze the store
187
262
  else
188
263
  return Object.freeze(scope);
189
264
  }
190
265
 
266
+ function initializeStore(store, readyOnly) {
267
+ Object.defineProperties(store, {
268
+ 'getState': {
269
+ writable: false,
270
+ enumberable: false,
271
+ configurable: false,
272
+ value: () => store[INTERNAL_STATE],
273
+ },
274
+ });
275
+
276
+ if (readyOnly !== true) {
277
+ Object.defineProperties(store, {
278
+ 'hydrate': {
279
+ writable: false,
280
+ enumberable: false,
281
+ configurable: false,
282
+ value: (value) => {
283
+ store[INTERNAL_STATE] = Object.freeze(clone(value));
284
+ queueChangeEvent.call(store, '*');
285
+ },
286
+ },
287
+ [QUEUE_CHANGE_EVENT]: {
288
+ writable: false,
289
+ enumberable: false,
290
+ configurable: false,
291
+ value: queueChangeEvent.bind(store),
292
+ },
293
+ [QUEUE_CHANGE_INFO]: {
294
+ writable: true,
295
+ enumberable: false,
296
+ configurable: false,
297
+ value: {
298
+ previousStore: cloneStore(store, true),
299
+ },
300
+ },
301
+ });
302
+ } else {
303
+ Object.defineProperties(store, {
304
+ [DISALLOW_WRITE]: {
305
+ writable: false,
306
+ enumberable: false,
307
+ configurable: false,
308
+ value: true,
309
+ },
310
+ });
311
+ }
312
+
313
+ return store;
314
+ }
315
+
191
316
  function createStore(template, _options) {
192
317
  if (!Nife.instanceOf(template, 'object'))
193
318
  throw new TypeError('createStore: provided "template" must be an object.');
@@ -200,45 +325,15 @@ function createStore(template, _options) {
200
325
  Object.defineProperty(store, INTERNAL_STATE, {
201
326
  writable: true,
202
327
  enumerable: false,
203
- configurable: true,
328
+ configurable: false,
204
329
  value: {},
205
330
  });
206
331
 
207
- let constructedStore = createStoreSubsection(options, store, template);
208
-
209
- Object.defineProperties(constructedStore, {
210
- 'getState': {
211
- writable: false,
212
- enumberable: false,
213
- configurable: false,
214
- value: () => constructedStore[INTERNAL_STATE],
215
- },
216
- 'hydrate': {
217
- writable: false,
218
- enumberable: false,
219
- configurable: false,
220
- value: (value) => {
221
- constructedStore[INTERNAL_STATE] = Object.freeze(clone(value));
222
- queueChangeEvent.call(constructedStore, '*');
223
- },
224
- },
225
- [QUEUE_CHANGE_EVENT]: {
226
- writable: false,
227
- enumberable: false,
228
- configurable: false,
229
- value: queueChangeEvent.bind(constructedStore),
230
- },
231
- [QUEUE_CHANGE_INFO]: {
232
- writable: true,
233
- enumberable: false,
234
- configurable: false,
235
- value: {},
236
- },
237
- });
238
-
239
- return constructedStore;
332
+ let constructedStore = createStoreSubsection.call(store, options, template);
333
+ return initializeStore(constructedStore);
240
334
  }
241
335
 
242
336
  module.exports = {
337
+ cloneStore,
243
338
  createStore,
244
339
  };