seqda 1.0.1 → 1.1.1
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.
- package/package.json +1 -1
- package/src/index.js +162 -59
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -6,15 +6,65 @@ 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
|
-
|
|
56
|
+
let modified = Array.from(Object.keys(info.eventQueue));
|
|
57
|
+
let previousStore = info.previousStore;
|
|
58
|
+
|
|
16
59
|
info.eventQueue = {};
|
|
17
60
|
info.promise = null;
|
|
61
|
+
info.previousStore = cloneStore(this, true);
|
|
62
|
+
|
|
63
|
+
this.emit('update', {
|
|
64
|
+
store: this,
|
|
65
|
+
previousStore,
|
|
66
|
+
modified,
|
|
67
|
+
});
|
|
18
68
|
});
|
|
19
69
|
}
|
|
20
70
|
|
|
@@ -92,8 +142,22 @@ function getPath(...parts) {
|
|
|
92
142
|
return parts.filter(Boolean).join('.');
|
|
93
143
|
}
|
|
94
144
|
|
|
95
|
-
function
|
|
96
|
-
|
|
145
|
+
function storeUnboundMethod(boundMethod, method) {
|
|
146
|
+
Object.defineProperty(boundMethod, UNBOUND_METHOD, {
|
|
147
|
+
writable: false,
|
|
148
|
+
enumerable: false,
|
|
149
|
+
configurable: false,
|
|
150
|
+
value: method,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
return boundMethod;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function createStoreSubsection(options, sectionTemplate, path) {
|
|
157
|
+
function isCacheInvalid(scopeName, args) {
|
|
158
|
+
if (this[DISALLOW_WRITE])
|
|
159
|
+
return true;
|
|
160
|
+
|
|
97
161
|
let thisCache = cache[scopeName];
|
|
98
162
|
if (!thisCache)
|
|
99
163
|
return true;
|
|
@@ -108,59 +172,75 @@ function createStoreSubsection(store, sectionTemplate, path) {
|
|
|
108
172
|
}
|
|
109
173
|
|
|
110
174
|
return false;
|
|
111
|
-
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function setCache(scopeName, args, result) {
|
|
178
|
+
if (this[DISALLOW_WRITE])
|
|
179
|
+
return;
|
|
112
180
|
|
|
113
|
-
const setCache = (scopeName, args, result) => {
|
|
114
181
|
cache[scopeName] = {
|
|
115
182
|
args,
|
|
116
183
|
result,
|
|
117
184
|
};
|
|
118
|
-
}
|
|
185
|
+
}
|
|
119
186
|
|
|
120
187
|
const createScopeMethod = (scopeName, func) => {
|
|
121
|
-
|
|
122
|
-
if (isCacheInvalid(scopeName, args)) {
|
|
123
|
-
let result = func({
|
|
124
|
-
|
|
188
|
+
let method = function(...args) {
|
|
189
|
+
if (isCacheInvalid.call(this, scopeName, args)) {
|
|
190
|
+
let result = func({
|
|
191
|
+
get: getState.bind(this),
|
|
192
|
+
set: setState.bind(this),
|
|
193
|
+
store: this,
|
|
194
|
+
}, ...args);
|
|
195
|
+
|
|
196
|
+
setCache.call(this, scopeName, args, result);
|
|
197
|
+
|
|
125
198
|
return result;
|
|
126
199
|
} else {
|
|
127
200
|
return cache[scopeName].result;
|
|
128
201
|
}
|
|
129
202
|
};
|
|
203
|
+
|
|
204
|
+
return storeUnboundMethod(method.bind(this), method);
|
|
130
205
|
};
|
|
131
206
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
207
|
+
function getState() {
|
|
208
|
+
if (options.emitOnFetch === true)
|
|
209
|
+
this.emit('fetchScope', { store: this, scopeName: path });
|
|
210
|
+
|
|
211
|
+
let currentState = Nife.get(this[INTERNAL_STATE], path);
|
|
135
212
|
return currentState;
|
|
136
|
-
}
|
|
213
|
+
}
|
|
137
214
|
|
|
138
|
-
|
|
139
|
-
|
|
215
|
+
function setState(value) {
|
|
216
|
+
if (this[DISALLOW_WRITE])
|
|
217
|
+
return;
|
|
218
|
+
|
|
219
|
+
let currentState = Nife.get(this[INTERNAL_STATE], path);
|
|
140
220
|
if (value && typeof value === 'object' && value === currentState)
|
|
141
221
|
throw new Error(`Error: "${getPath(path)}" the state value is the same, but it is required to be different.`);
|
|
142
222
|
|
|
143
223
|
let previousState = currentState;
|
|
144
|
-
|
|
224
|
+
this[INTERNAL_STATE] = setPath(this[INTERNAL_STATE], path, value);
|
|
145
225
|
|
|
146
226
|
cache = {};
|
|
147
227
|
|
|
148
|
-
if (
|
|
149
|
-
|
|
228
|
+
if (this[QUEUE_CHANGE_EVENT])
|
|
229
|
+
this[QUEUE_CHANGE_EVENT](path, value, previousState);
|
|
150
230
|
|
|
151
231
|
return value;
|
|
152
|
-
}
|
|
232
|
+
}
|
|
153
233
|
|
|
154
234
|
if (path && !Object.prototype.hasOwnProperty.call(sectionTemplate, '_'))
|
|
155
235
|
throw new Error(`Error: "${getPath}._" default value must be defined.`);
|
|
156
236
|
|
|
157
|
-
const scope = (!path) ?
|
|
237
|
+
const scope = (!path) ? this : {};
|
|
158
238
|
let keys = Object.keys(sectionTemplate || {});
|
|
159
239
|
let subScopes = [];
|
|
160
240
|
let cache = {};
|
|
161
241
|
|
|
162
242
|
if (path)
|
|
163
|
-
|
|
243
|
+
setState.call(this, clone(sectionTemplate._));
|
|
164
244
|
|
|
165
245
|
for (let i = 0, il = keys.length; i < il; i++) {
|
|
166
246
|
let key = keys[i];
|
|
@@ -169,7 +249,7 @@ function createStoreSubsection(store, sectionTemplate, path) {
|
|
|
169
249
|
|
|
170
250
|
let value = sectionTemplate[key];
|
|
171
251
|
if (Nife.instanceOf(value, 'object')) {
|
|
172
|
-
scope[key] = createStoreSubsection(
|
|
252
|
+
scope[key] = createStoreSubsection.call(this, options, value, getPath(path, key));
|
|
173
253
|
subScopes.push(key);
|
|
174
254
|
continue;
|
|
175
255
|
}
|
|
@@ -181,59 +261,82 @@ function createStoreSubsection(store, sectionTemplate, path) {
|
|
|
181
261
|
}
|
|
182
262
|
|
|
183
263
|
if (!path)
|
|
184
|
-
return
|
|
264
|
+
return this; // We can't freeze the store
|
|
185
265
|
else
|
|
186
266
|
return Object.freeze(scope);
|
|
187
267
|
}
|
|
188
268
|
|
|
189
|
-
function
|
|
269
|
+
function initializeStore(store, readyOnly) {
|
|
270
|
+
Object.defineProperties(store, {
|
|
271
|
+
'getState': {
|
|
272
|
+
writable: false,
|
|
273
|
+
enumberable: false,
|
|
274
|
+
configurable: false,
|
|
275
|
+
value: () => store[INTERNAL_STATE],
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
if (readyOnly !== true) {
|
|
280
|
+
Object.defineProperties(store, {
|
|
281
|
+
'hydrate': {
|
|
282
|
+
writable: false,
|
|
283
|
+
enumberable: false,
|
|
284
|
+
configurable: false,
|
|
285
|
+
value: (value) => {
|
|
286
|
+
store[INTERNAL_STATE] = Object.freeze(clone(value));
|
|
287
|
+
queueChangeEvent.call(store, '*');
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
[QUEUE_CHANGE_EVENT]: {
|
|
291
|
+
writable: false,
|
|
292
|
+
enumberable: false,
|
|
293
|
+
configurable: false,
|
|
294
|
+
value: queueChangeEvent.bind(store),
|
|
295
|
+
},
|
|
296
|
+
[QUEUE_CHANGE_INFO]: {
|
|
297
|
+
writable: true,
|
|
298
|
+
enumberable: false,
|
|
299
|
+
configurable: false,
|
|
300
|
+
value: {
|
|
301
|
+
previousStore: cloneStore(store, true),
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
} else {
|
|
306
|
+
Object.defineProperties(store, {
|
|
307
|
+
[DISALLOW_WRITE]: {
|
|
308
|
+
writable: false,
|
|
309
|
+
enumberable: false,
|
|
310
|
+
configurable: false,
|
|
311
|
+
value: true,
|
|
312
|
+
},
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return store;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function createStore(template, _options) {
|
|
190
320
|
if (!Nife.instanceOf(template, 'object'))
|
|
191
321
|
throw new TypeError('createStore: provided "template" must be an object.');
|
|
192
322
|
|
|
323
|
+
const options = _options || {};
|
|
193
324
|
const store = new EventEmitter();
|
|
194
325
|
|
|
326
|
+
store.setMaxListeners(Infinity);
|
|
327
|
+
|
|
195
328
|
Object.defineProperty(store, INTERNAL_STATE, {
|
|
196
329
|
writable: true,
|
|
197
330
|
enumerable: false,
|
|
198
|
-
configurable:
|
|
331
|
+
configurable: false,
|
|
199
332
|
value: {},
|
|
200
333
|
});
|
|
201
334
|
|
|
202
|
-
let constructedStore = createStoreSubsection(store, template);
|
|
203
|
-
|
|
204
|
-
Object.defineProperties(constructedStore, {
|
|
205
|
-
'getState': {
|
|
206
|
-
writable: false,
|
|
207
|
-
enumberable: false,
|
|
208
|
-
configurable: false,
|
|
209
|
-
value: () => constructedStore[INTERNAL_STATE],
|
|
210
|
-
},
|
|
211
|
-
'hydrate': {
|
|
212
|
-
writable: false,
|
|
213
|
-
enumberable: false,
|
|
214
|
-
configurable: false,
|
|
215
|
-
value: (value) => {
|
|
216
|
-
constructedStore[INTERNAL_STATE] = Object.freeze(clone(value));
|
|
217
|
-
queueChangeEvent.call(constructedStore, '*');
|
|
218
|
-
},
|
|
219
|
-
},
|
|
220
|
-
[QUEUE_CHANGE_EVENT]: {
|
|
221
|
-
writable: false,
|
|
222
|
-
enumberable: false,
|
|
223
|
-
configurable: false,
|
|
224
|
-
value: queueChangeEvent.bind(constructedStore),
|
|
225
|
-
},
|
|
226
|
-
[QUEUE_CHANGE_INFO]: {
|
|
227
|
-
writable: true,
|
|
228
|
-
enumberable: false,
|
|
229
|
-
configurable: false,
|
|
230
|
-
value: {},
|
|
231
|
-
},
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
return constructedStore;
|
|
335
|
+
let constructedStore = createStoreSubsection.call(store, options, template);
|
|
336
|
+
return initializeStore(constructedStore);
|
|
235
337
|
}
|
|
236
338
|
|
|
237
339
|
module.exports = {
|
|
340
|
+
cloneStore,
|
|
238
341
|
createStore,
|
|
239
342
|
};
|