seqda 1.0.2 → 1.1.2
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 +159 -58
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,61 +172,78 @@ function createStoreSubsection(options, 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
|
-
|
|
207
|
+
function getState() {
|
|
133
208
|
if (options.emitOnFetch === true)
|
|
134
|
-
|
|
209
|
+
this.emit('fetchScope', { store: this, scopeName: path });
|
|
135
210
|
|
|
136
|
-
let currentState = Nife.get(
|
|
211
|
+
let currentState = Nife.get(this[INTERNAL_STATE], path);
|
|
137
212
|
return currentState;
|
|
138
|
-
}
|
|
213
|
+
}
|
|
139
214
|
|
|
140
|
-
|
|
141
|
-
|
|
215
|
+
function setState(value) {
|
|
216
|
+
if (this[DISALLOW_WRITE])
|
|
217
|
+
return;
|
|
218
|
+
|
|
219
|
+
let currentState = Nife.get(this[INTERNAL_STATE], path);
|
|
142
220
|
if (value && typeof value === 'object' && value === currentState)
|
|
143
221
|
throw new Error(`Error: "${getPath(path)}" the state value is the same, but it is required to be different.`);
|
|
144
222
|
|
|
223
|
+
if (!Nife.propsDiffer(value, currentState))
|
|
224
|
+
return;
|
|
225
|
+
|
|
145
226
|
let previousState = currentState;
|
|
146
|
-
|
|
227
|
+
this[INTERNAL_STATE] = setPath(this[INTERNAL_STATE], path, value);
|
|
147
228
|
|
|
148
229
|
cache = {};
|
|
149
230
|
|
|
150
|
-
if (
|
|
151
|
-
|
|
231
|
+
if (this[QUEUE_CHANGE_EVENT])
|
|
232
|
+
this[QUEUE_CHANGE_EVENT](path, value, previousState);
|
|
152
233
|
|
|
153
234
|
return value;
|
|
154
|
-
}
|
|
235
|
+
}
|
|
155
236
|
|
|
156
237
|
if (path && !Object.prototype.hasOwnProperty.call(sectionTemplate, '_'))
|
|
157
238
|
throw new Error(`Error: "${getPath}._" default value must be defined.`);
|
|
158
239
|
|
|
159
|
-
const scope = (!path) ?
|
|
240
|
+
const scope = (!path) ? this : {};
|
|
160
241
|
let keys = Object.keys(sectionTemplate || {});
|
|
161
242
|
let subScopes = [];
|
|
162
243
|
let cache = {};
|
|
163
244
|
|
|
164
245
|
if (path)
|
|
165
|
-
|
|
246
|
+
setState.call(this, clone(sectionTemplate._));
|
|
166
247
|
|
|
167
248
|
for (let i = 0, il = keys.length; i < il; i++) {
|
|
168
249
|
let key = keys[i];
|
|
@@ -171,7 +252,7 @@ function createStoreSubsection(options, store, sectionTemplate, path) {
|
|
|
171
252
|
|
|
172
253
|
let value = sectionTemplate[key];
|
|
173
254
|
if (Nife.instanceOf(value, 'object')) {
|
|
174
|
-
scope[key] = createStoreSubsection(
|
|
255
|
+
scope[key] = createStoreSubsection.call(this, options, value, getPath(path, key));
|
|
175
256
|
subScopes.push(key);
|
|
176
257
|
continue;
|
|
177
258
|
}
|
|
@@ -183,11 +264,61 @@ function createStoreSubsection(options, store, sectionTemplate, path) {
|
|
|
183
264
|
}
|
|
184
265
|
|
|
185
266
|
if (!path)
|
|
186
|
-
return
|
|
267
|
+
return this; // We can't freeze the store
|
|
187
268
|
else
|
|
188
269
|
return Object.freeze(scope);
|
|
189
270
|
}
|
|
190
271
|
|
|
272
|
+
function initializeStore(store, readyOnly) {
|
|
273
|
+
Object.defineProperties(store, {
|
|
274
|
+
'getState': {
|
|
275
|
+
writable: false,
|
|
276
|
+
enumberable: false,
|
|
277
|
+
configurable: false,
|
|
278
|
+
value: () => store[INTERNAL_STATE],
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
if (readyOnly !== true) {
|
|
283
|
+
Object.defineProperties(store, {
|
|
284
|
+
'hydrate': {
|
|
285
|
+
writable: false,
|
|
286
|
+
enumberable: false,
|
|
287
|
+
configurable: false,
|
|
288
|
+
value: (value) => {
|
|
289
|
+
store[INTERNAL_STATE] = Object.freeze(clone(value));
|
|
290
|
+
queueChangeEvent.call(store, '*');
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
[QUEUE_CHANGE_EVENT]: {
|
|
294
|
+
writable: false,
|
|
295
|
+
enumberable: false,
|
|
296
|
+
configurable: false,
|
|
297
|
+
value: queueChangeEvent.bind(store),
|
|
298
|
+
},
|
|
299
|
+
[QUEUE_CHANGE_INFO]: {
|
|
300
|
+
writable: true,
|
|
301
|
+
enumberable: false,
|
|
302
|
+
configurable: false,
|
|
303
|
+
value: {
|
|
304
|
+
previousStore: cloneStore(store, true),
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
});
|
|
308
|
+
} else {
|
|
309
|
+
Object.defineProperties(store, {
|
|
310
|
+
[DISALLOW_WRITE]: {
|
|
311
|
+
writable: false,
|
|
312
|
+
enumberable: false,
|
|
313
|
+
configurable: false,
|
|
314
|
+
value: true,
|
|
315
|
+
},
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return store;
|
|
320
|
+
}
|
|
321
|
+
|
|
191
322
|
function createStore(template, _options) {
|
|
192
323
|
if (!Nife.instanceOf(template, 'object'))
|
|
193
324
|
throw new TypeError('createStore: provided "template" must be an object.');
|
|
@@ -200,45 +331,15 @@ function createStore(template, _options) {
|
|
|
200
331
|
Object.defineProperty(store, INTERNAL_STATE, {
|
|
201
332
|
writable: true,
|
|
202
333
|
enumerable: false,
|
|
203
|
-
configurable:
|
|
334
|
+
configurable: false,
|
|
204
335
|
value: {},
|
|
205
336
|
});
|
|
206
337
|
|
|
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;
|
|
338
|
+
let constructedStore = createStoreSubsection.call(store, options, template);
|
|
339
|
+
return initializeStore(constructedStore);
|
|
240
340
|
}
|
|
241
341
|
|
|
242
342
|
module.exports = {
|
|
343
|
+
cloneStore,
|
|
243
344
|
createStore,
|
|
244
345
|
};
|