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.
- package/package.json +1 -1
- package/src/index.js +153 -58
package/package.json
CHANGED
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', {
|
|
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
|
|
96
|
-
|
|
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
|
-
|
|
122
|
-
if (isCacheInvalid(scopeName, args)) {
|
|
123
|
-
let result = func({
|
|
124
|
-
|
|
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
|
-
|
|
204
|
+
function getState() {
|
|
133
205
|
if (options.emitOnFetch === true)
|
|
134
|
-
|
|
206
|
+
this.emit('fetchScope', { store: this, scopeName: path });
|
|
135
207
|
|
|
136
|
-
let currentState = Nife.get(
|
|
208
|
+
let currentState = Nife.get(this[INTERNAL_STATE], path);
|
|
137
209
|
return currentState;
|
|
138
|
-
}
|
|
210
|
+
}
|
|
139
211
|
|
|
140
|
-
|
|
141
|
-
|
|
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
|
-
|
|
221
|
+
this[INTERNAL_STATE] = setPath(this[INTERNAL_STATE], path, value);
|
|
147
222
|
|
|
148
223
|
cache = {};
|
|
149
224
|
|
|
150
|
-
if (
|
|
151
|
-
|
|
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) ?
|
|
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
|
-
|
|
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(
|
|
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
|
|
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:
|
|
328
|
+
configurable: false,
|
|
204
329
|
value: {},
|
|
205
330
|
});
|
|
206
331
|
|
|
207
|
-
let constructedStore = createStoreSubsection(
|
|
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
|
};
|