@legendapp/state 2.2.0-next.3 → 2.2.0-next.30
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/babel.js.map +1 -1
- package/config/enableDirectAccess.d.ts +1 -1
- package/config/enableDirectPeek.d.ts +1 -1
- package/config/enableReactDirectRender.js.map +1 -1
- package/config/enableReactDirectRender.mjs.map +1 -1
- package/config/enableReactTracking.d.ts +4 -3
- package/config/enableReactTracking.js.map +1 -1
- package/config/enableReactTracking.mjs.map +1 -1
- package/config/enableReactUse.d.ts +1 -1
- package/helpers/fetch.d.ts +4 -3
- package/helpers/fetch.js.map +1 -1
- package/helpers/fetch.mjs.map +1 -1
- package/helpers/pageHash.js.map +1 -1
- package/helpers/pageHash.mjs.map +1 -1
- package/helpers/pageHashParams.js.map +1 -1
- package/helpers/pageHashParams.mjs.map +1 -1
- package/helpers/time.d.ts +2 -2
- package/helpers/time.js.map +1 -1
- package/helpers/time.mjs.map +1 -1
- package/history.js.map +1 -1
- package/history.mjs.map +1 -1
- package/index.d.ts +15 -4
- package/index.js +534 -242
- package/index.js.map +1 -1
- package/index.mjs +533 -243
- package/index.mjs.map +1 -1
- package/package.json +2 -10
- package/persist-plugins/async-storage.js.map +1 -1
- package/persist-plugins/async-storage.mjs.map +1 -1
- package/persist-plugins/fetch.js.map +1 -1
- package/persist-plugins/fetch.mjs.map +1 -1
- package/persist-plugins/firebase.js.map +1 -1
- package/persist-plugins/firebase.mjs.map +1 -1
- package/persist-plugins/indexeddb.js.map +1 -1
- package/persist-plugins/indexeddb.mjs.map +1 -1
- package/persist-plugins/local-storage.js +10 -2
- package/persist-plugins/local-storage.js.map +1 -1
- package/persist-plugins/local-storage.mjs +10 -2
- package/persist-plugins/local-storage.mjs.map +1 -1
- package/persist-plugins/mmkv.js.map +1 -1
- package/persist-plugins/mmkv.mjs.map +1 -1
- package/persist-plugins/query.js.map +1 -1
- package/persist-plugins/query.mjs.map +1 -1
- package/persist.d.ts +15 -1
- package/persist.js +365 -116
- package/persist.js.map +1 -1
- package/persist.mjs +366 -117
- package/persist.mjs.map +1 -1
- package/react-hooks/createObservableHook.js +1 -1
- package/react-hooks/createObservableHook.js.map +1 -1
- package/react-hooks/createObservableHook.mjs +1 -1
- package/react-hooks/createObservableHook.mjs.map +1 -1
- package/react-hooks/useFetch.d.ts +4 -3
- package/react-hooks/useFetch.js.map +1 -1
- package/react-hooks/useFetch.mjs.map +1 -1
- package/react-hooks/useHover.js.map +1 -1
- package/react-hooks/useHover.mjs.map +1 -1
- package/react-hooks/useMeasure.js.map +1 -1
- package/react-hooks/useMeasure.mjs.map +1 -1
- package/react-hooks/useObservableNextRouter.js.map +1 -1
- package/react-hooks/useObservableNextRouter.mjs.map +1 -1
- package/react-hooks/useObservableQuery.js.map +1 -1
- package/react-hooks/useObservableQuery.mjs.map +1 -1
- package/react-hooks/usePersistedObservable.d.ts +2 -2
- package/react-hooks/usePersistedObservable.js +3 -3
- package/react-hooks/usePersistedObservable.js.map +1 -1
- package/react-hooks/usePersistedObservable.mjs +3 -3
- package/react-hooks/usePersistedObservable.mjs.map +1 -1
- package/react.js +13 -8
- package/react.js.map +1 -1
- package/react.mjs +14 -9
- package/react.mjs.map +1 -1
- package/src/ObservableObject.d.ts +8 -4
- package/src/ObservablePrimitive.d.ts +2 -1
- package/src/activated.d.ts +3 -0
- package/src/batching.d.ts +3 -1
- package/src/computed.d.ts +1 -1
- package/src/config/enableDirectAccess.d.ts +1 -1
- package/src/config/enableDirectPeek.d.ts +1 -1
- package/src/config/enableReactTracking.d.ts +4 -3
- package/src/config/enableReactUse.d.ts +1 -1
- package/src/createObservable.d.ts +2 -2
- package/src/globals.d.ts +11 -4
- package/src/helpers/fetch.d.ts +4 -3
- package/src/helpers/time.d.ts +2 -2
- package/src/helpers.d.ts +3 -2
- package/src/history/trackHistory.d.ts +1 -1
- package/src/observable.d.ts +6 -15
- package/src/observableInterfaces.d.ts +60 -331
- package/src/observableTypes.d.ts +93 -0
- package/src/persist/observablePersistRemoteFunctionsAdapter.d.ts +1 -1
- package/src/persist/persistActivateNode.d.ts +1 -0
- package/src/persist/persistHelpers.d.ts +1 -1
- package/src/persist/persistObservable.d.ts +3 -12
- package/src/persistTypes.d.ts +229 -0
- package/src/proxy.d.ts +2 -1
- package/src/react/Computed.d.ts +1 -1
- package/src/react/Switch.d.ts +3 -3
- package/src/react/reactInterfaces.d.ts +2 -1
- package/src/react/usePauseProvider.d.ts +3 -3
- package/src/react/useWhen.d.ts +2 -2
- package/src/react-hooks/useFetch.d.ts +4 -3
- package/src/react-hooks/usePersistedObservable.d.ts +2 -2
- package/src/retry.d.ts +6 -0
- package/src/trackSelector.d.ts +3 -2
- package/src/when.d.ts +6 -2
- package/trace.js.map +1 -1
- package/trace.mjs.map +1 -1
package/index.js
CHANGED
|
@@ -8,7 +8,7 @@ function isString(obj) {
|
|
|
8
8
|
return typeof obj === 'string';
|
|
9
9
|
}
|
|
10
10
|
function isObject(obj) {
|
|
11
|
-
return !!obj && typeof obj === 'object' && !isArray(obj);
|
|
11
|
+
return !!obj && typeof obj === 'object' && !(obj instanceof Date) && !isArray(obj);
|
|
12
12
|
}
|
|
13
13
|
function isFunction(obj) {
|
|
14
14
|
return typeof obj === 'function';
|
|
@@ -55,6 +55,7 @@ const symbolGetNode = Symbol('getNode');
|
|
|
55
55
|
const symbolDelete = /* @__PURE__ */ Symbol('delete');
|
|
56
56
|
const symbolOpaque = Symbol('opaque');
|
|
57
57
|
const optimized = Symbol('optimized');
|
|
58
|
+
const symbolActivated = Symbol('activated');
|
|
58
59
|
// TODOV3 Remove these
|
|
59
60
|
const extraPrimitiveActivators = new Map();
|
|
60
61
|
const extraPrimitiveProps = new Map();
|
|
@@ -63,6 +64,8 @@ const globalState = {
|
|
|
63
64
|
isMerging: false,
|
|
64
65
|
isLoadingRemote$: undefined,
|
|
65
66
|
activateNode: undefined,
|
|
67
|
+
pendingNodes: new Map(),
|
|
68
|
+
dirtyNodes: new Set(),
|
|
66
69
|
};
|
|
67
70
|
function isObservable(obs) {
|
|
68
71
|
return !!obs && !!obs[symbolGetNode];
|
|
@@ -158,8 +161,16 @@ function getChildNode(node, key, asFunction) {
|
|
|
158
161
|
if (asFunction) {
|
|
159
162
|
child = Object.assign(cloneFunction(asFunction), child);
|
|
160
163
|
}
|
|
161
|
-
else
|
|
162
|
-
|
|
164
|
+
else {
|
|
165
|
+
if (node.activationState) {
|
|
166
|
+
const { lookup } = node.activationState;
|
|
167
|
+
if (lookup) {
|
|
168
|
+
child = Object.assign(lookup.bind(node, key), child);
|
|
169
|
+
if (isFunction(child)) {
|
|
170
|
+
extractFunction(node, key, child);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
163
174
|
}
|
|
164
175
|
if (!node.children) {
|
|
165
176
|
node.children = new Map();
|
|
@@ -204,9 +215,11 @@ function findIDKey(obj, node) {
|
|
|
204
215
|
return idKey;
|
|
205
216
|
}
|
|
206
217
|
function extractFunction(node, key, fnOrComputed, computedChildNode) {
|
|
218
|
+
var _a;
|
|
207
219
|
if (!node.functions) {
|
|
208
220
|
node.functions = new Map();
|
|
209
221
|
}
|
|
222
|
+
(_a = node.children) === null || _a === void 0 ? void 0 : _a.delete(key);
|
|
210
223
|
node.functions.set(key, fnOrComputed);
|
|
211
224
|
if (computedChildNode) {
|
|
212
225
|
computedChildNode.parentOther = getChildNode(node, key);
|
|
@@ -217,6 +230,12 @@ function extractFunction(node, key, fnOrComputed, computedChildNode) {
|
|
|
217
230
|
}
|
|
218
231
|
}
|
|
219
232
|
|
|
233
|
+
function activated(params) {
|
|
234
|
+
return (() => ({
|
|
235
|
+
[symbolActivated]: params,
|
|
236
|
+
}));
|
|
237
|
+
}
|
|
238
|
+
|
|
220
239
|
let timeout;
|
|
221
240
|
let numInBatch = 0;
|
|
222
241
|
let isRunningBatch = false;
|
|
@@ -241,23 +260,28 @@ function isArraySubset(mainArr, subsetArr) {
|
|
|
241
260
|
return true;
|
|
242
261
|
}
|
|
243
262
|
function createPreviousHandlerInner(value, changes) {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
263
|
+
try {
|
|
264
|
+
// Clones the current state and inject the previous data at the changed path
|
|
265
|
+
let clone = value ? JSON.parse(JSON.stringify(value)) : {};
|
|
266
|
+
for (let i = 0; i < changes.length; i++) {
|
|
267
|
+
const { path, prevAtPath } = changes[i];
|
|
268
|
+
let o = clone;
|
|
269
|
+
if (path.length > 0) {
|
|
270
|
+
let i;
|
|
271
|
+
for (i = 0; i < path.length - 1; i++) {
|
|
272
|
+
o = o[path[i]];
|
|
273
|
+
}
|
|
274
|
+
o[path[i]] = prevAtPath;
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
clone = prevAtPath;
|
|
253
278
|
}
|
|
254
|
-
o[path[i]] = prevAtPath;
|
|
255
|
-
}
|
|
256
|
-
else {
|
|
257
|
-
clone = prevAtPath;
|
|
258
279
|
}
|
|
280
|
+
return clone;
|
|
281
|
+
}
|
|
282
|
+
catch (_a) {
|
|
283
|
+
return undefined;
|
|
259
284
|
}
|
|
260
|
-
return clone;
|
|
261
285
|
}
|
|
262
286
|
function createPreviousHandler(value, changes) {
|
|
263
287
|
// Create a function that generates the previous state
|
|
@@ -366,6 +390,13 @@ function batchNotifyChanges(changesInBatch, immediate) {
|
|
|
366
390
|
});
|
|
367
391
|
}
|
|
368
392
|
function runBatch() {
|
|
393
|
+
const dirtyNodes = Array.from(globalState.dirtyNodes);
|
|
394
|
+
globalState.dirtyNodes.clear();
|
|
395
|
+
dirtyNodes.forEach((node) => {
|
|
396
|
+
var _a;
|
|
397
|
+
(_a = node.dirtyFn) === null || _a === void 0 ? void 0 : _a.call(node);
|
|
398
|
+
node.dirtyFn = undefined;
|
|
399
|
+
});
|
|
369
400
|
// Save batch locally and reset _batchMap first because a new batch could begin while looping over callbacks.
|
|
370
401
|
// This can happen with observableComputed for example.
|
|
371
402
|
const map = _batchMap;
|
|
@@ -482,7 +513,7 @@ function isEvent(obs) {
|
|
|
482
513
|
}
|
|
483
514
|
function computeSelector(selector, e, retainObservable) {
|
|
484
515
|
let c = selector;
|
|
485
|
-
if (isFunction(c)) {
|
|
516
|
+
if (!isObservable(c) && isFunction(c)) {
|
|
486
517
|
c = e ? c(e) : c();
|
|
487
518
|
}
|
|
488
519
|
return isObservable(c) && !retainObservable ? c.get() : c;
|
|
@@ -581,14 +612,15 @@ function _mergeIntoObservable(target, source) {
|
|
|
581
612
|
const targetValue = needsSet ? target.peek() : target;
|
|
582
613
|
const isTargetArr = isArray(targetValue);
|
|
583
614
|
const isTargetObj = !isTargetArr && isObject(targetValue);
|
|
584
|
-
if ((isTargetObj && isObject(source) && !isEmpty(targetValue)) ||
|
|
585
|
-
(isTargetArr && isArray(source) && targetValue.length > 0)) {
|
|
615
|
+
if ((isTargetObj && isObject(source) && !isEmpty(targetValue)) || (isTargetArr && targetValue.length > 0)) {
|
|
586
616
|
const keys = Object.keys(source);
|
|
587
617
|
for (let i = 0; i < keys.length; i++) {
|
|
588
618
|
const key = keys[i];
|
|
589
619
|
const sourceValue = source[key];
|
|
590
620
|
if (sourceValue === symbolDelete) {
|
|
591
|
-
needsSet && ((_a = target[key]) === null || _a === void 0 ? void 0 : _a.delete)
|
|
621
|
+
needsSet && ((_a = target[key]) === null || _a === void 0 ? void 0 : _a.delete)
|
|
622
|
+
? target[key].delete()
|
|
623
|
+
: delete target[key];
|
|
592
624
|
}
|
|
593
625
|
else {
|
|
594
626
|
const isObj = isObject(sourceValue);
|
|
@@ -777,7 +809,7 @@ function trackSelector(selector, update, observeEvent, observeOptions, createRes
|
|
|
777
809
|
dispose = setupTracking(nodes, updateFn, false, observeOptions === null || observeOptions === void 0 ? void 0 : observeOptions.immediate);
|
|
778
810
|
resubscribe = createResubscribe ? () => setupTracking(nodes, updateFn) : undefined;
|
|
779
811
|
}
|
|
780
|
-
return { value, dispose, resubscribe };
|
|
812
|
+
return { value, nodes, dispose, resubscribe };
|
|
781
813
|
}
|
|
782
814
|
|
|
783
815
|
function observe(selectorOrRun, reactionOrOptions, options) {
|
|
@@ -802,9 +834,11 @@ function observe(selectorOrRun, reactionOrOptions, options) {
|
|
|
802
834
|
delete e.value;
|
|
803
835
|
// Dispose listeners from previous run
|
|
804
836
|
dispose === null || dispose === void 0 ? void 0 : dispose();
|
|
805
|
-
const { dispose: _dispose, value } = trackSelector(selectorOrRun, update, e, options);
|
|
837
|
+
const { dispose: _dispose, value, nodes } = trackSelector(selectorOrRun, update, e, options);
|
|
806
838
|
dispose = _dispose;
|
|
807
839
|
e.value = value;
|
|
840
|
+
e.nodes = nodes;
|
|
841
|
+
e.refresh = update;
|
|
808
842
|
if (e.onCleanupReaction) {
|
|
809
843
|
e.onCleanupReaction();
|
|
810
844
|
e.onCleanupReaction = undefined;
|
|
@@ -812,7 +846,9 @@ function observe(selectorOrRun, reactionOrOptions, options) {
|
|
|
812
846
|
endBatch();
|
|
813
847
|
// Call the reaction if there is one and the value changed
|
|
814
848
|
if (reaction &&
|
|
815
|
-
((options === null || options === void 0 ? void 0 : options.fromComputed) ||
|
|
849
|
+
((options === null || options === void 0 ? void 0 : options.fromComputed) ||
|
|
850
|
+
((e.num > 0 || !isEvent(selectorOrRun)) &&
|
|
851
|
+
(e.previous !== e.value || typeof e.value === 'object')))) {
|
|
816
852
|
reaction(e);
|
|
817
853
|
}
|
|
818
854
|
// Update the previous value
|
|
@@ -832,6 +868,118 @@ function observe(selectorOrRun, reactionOrOptions, options) {
|
|
|
832
868
|
};
|
|
833
869
|
}
|
|
834
870
|
|
|
871
|
+
function _when(predicate, effect, checkReady) {
|
|
872
|
+
// If predicate is a regular Promise skip all the observable stuff
|
|
873
|
+
if (isPromise(predicate)) {
|
|
874
|
+
return effect ? predicate.then(effect) : predicate;
|
|
875
|
+
}
|
|
876
|
+
let value;
|
|
877
|
+
let effectValue;
|
|
878
|
+
// Create a wrapping fn that calls the effect if predicate returns true
|
|
879
|
+
function run(e) {
|
|
880
|
+
const ret = computeSelector(predicate);
|
|
881
|
+
if (!isPromise(ret) && (checkReady ? isObservableValueReady(ret) : ret)) {
|
|
882
|
+
value = ret;
|
|
883
|
+
// Set cancel so that observe does not track anymore
|
|
884
|
+
e.cancel = true;
|
|
885
|
+
}
|
|
886
|
+
return value;
|
|
887
|
+
}
|
|
888
|
+
function doEffect() {
|
|
889
|
+
// If value is truthy then run the effect
|
|
890
|
+
effectValue = effect === null || effect === void 0 ? void 0 : effect(value);
|
|
891
|
+
}
|
|
892
|
+
// Run in an observe
|
|
893
|
+
observe(run, doEffect);
|
|
894
|
+
// If first run resulted in a truthy value just return it.
|
|
895
|
+
// It will have set e.cancel so no need to dispose
|
|
896
|
+
if (isPromise(value)) {
|
|
897
|
+
return effect ? value.then(effect) : value;
|
|
898
|
+
}
|
|
899
|
+
else if (value !== undefined) {
|
|
900
|
+
return effect ? effectValue : Promise.resolve(value);
|
|
901
|
+
}
|
|
902
|
+
else {
|
|
903
|
+
// Wrap it in a promise
|
|
904
|
+
const promise = new Promise((resolve) => {
|
|
905
|
+
if (effect) {
|
|
906
|
+
const originalEffect = effect;
|
|
907
|
+
effect = ((value) => {
|
|
908
|
+
const effectValue = originalEffect(value);
|
|
909
|
+
resolve(isPromise(effectValue) ? effectValue.then((value) => value) : effectValue);
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
else {
|
|
913
|
+
effect = resolve;
|
|
914
|
+
}
|
|
915
|
+
});
|
|
916
|
+
return promise;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
function when(predicate, effect) {
|
|
920
|
+
return _when(predicate, effect, false);
|
|
921
|
+
}
|
|
922
|
+
function whenReady(predicate, effect) {
|
|
923
|
+
return _when(predicate, effect, true);
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
function calculateRetryDelay(retryOptions, attemptNum) {
|
|
927
|
+
const { backoff, delay = 1000, infinite, times = 3, maxDelay = 30000 } = retryOptions;
|
|
928
|
+
if (infinite || attemptNum < times) {
|
|
929
|
+
const delayTime = Math.min(delay * (backoff === 'constant' ? 1 : 2 ** attemptNum), maxDelay);
|
|
930
|
+
return delayTime;
|
|
931
|
+
}
|
|
932
|
+
return null;
|
|
933
|
+
}
|
|
934
|
+
function createRetryTimeout(retryOptions, attemptNum, fn) {
|
|
935
|
+
const delayTime = calculateRetryDelay(retryOptions, attemptNum);
|
|
936
|
+
if (delayTime) {
|
|
937
|
+
return setTimeout(fn, delayTime);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
function runWithRetry(node, state, fn) {
|
|
941
|
+
const { retry, waitFor } = node.activationState;
|
|
942
|
+
const e = { cancel: false };
|
|
943
|
+
let value = undefined;
|
|
944
|
+
if (waitFor) {
|
|
945
|
+
value = whenReady(waitFor, () => {
|
|
946
|
+
node.activationState.waitFor = undefined;
|
|
947
|
+
return fn(e);
|
|
948
|
+
});
|
|
949
|
+
}
|
|
950
|
+
else {
|
|
951
|
+
value = fn(e);
|
|
952
|
+
}
|
|
953
|
+
if (isPromise(value) && retry) {
|
|
954
|
+
let timeoutRetry;
|
|
955
|
+
return new Promise((resolve) => {
|
|
956
|
+
const run = () => {
|
|
957
|
+
value
|
|
958
|
+
.then((val) => {
|
|
959
|
+
node.activationState.persistedRetry = false;
|
|
960
|
+
resolve(val);
|
|
961
|
+
})
|
|
962
|
+
.catch(() => {
|
|
963
|
+
node.activationState.persistedRetry = true;
|
|
964
|
+
state.attemptNum++;
|
|
965
|
+
if (timeoutRetry) {
|
|
966
|
+
clearTimeout(timeoutRetry);
|
|
967
|
+
}
|
|
968
|
+
if (!e.cancel) {
|
|
969
|
+
timeoutRetry = createRetryTimeout(retry, state.attemptNum, () => {
|
|
970
|
+
value = fn(e);
|
|
971
|
+
run();
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
});
|
|
975
|
+
};
|
|
976
|
+
run();
|
|
977
|
+
});
|
|
978
|
+
}
|
|
979
|
+
return value;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
const noop = () => { };
|
|
835
983
|
const ArrayModifiers = new Set([
|
|
836
984
|
'copyWithin',
|
|
837
985
|
'fill',
|
|
@@ -872,19 +1020,24 @@ if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
|
|
|
872
1020
|
}
|
|
873
1021
|
function collectionSetter(node, target, prop, ...args) {
|
|
874
1022
|
var _a;
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
const
|
|
880
|
-
const
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
1023
|
+
if (prop === 'push' && args.length === 1) {
|
|
1024
|
+
setKey(node, target.length + '', args[0]);
|
|
1025
|
+
}
|
|
1026
|
+
else {
|
|
1027
|
+
const prevValue = target.slice();
|
|
1028
|
+
const ret = target[prop].apply(target, args);
|
|
1029
|
+
if (node) {
|
|
1030
|
+
const hasParent = isChildNodeValue(node);
|
|
1031
|
+
const key = hasParent ? node.key : '_';
|
|
1032
|
+
const parentValue = hasParent ? getNodeValue(node.parent) : node.root;
|
|
1033
|
+
// Set the object to the previous value first
|
|
1034
|
+
parentValue[key] = prevValue;
|
|
1035
|
+
// Then set with the new value so it notifies with the correct prevValue
|
|
1036
|
+
setKey((_a = node.parent) !== null && _a !== void 0 ? _a : node, key, target);
|
|
1037
|
+
}
|
|
1038
|
+
// Return the original value
|
|
1039
|
+
return ret;
|
|
1040
|
+
}
|
|
888
1041
|
}
|
|
889
1042
|
function getKeys(obj, isArr, isMap) {
|
|
890
1043
|
return isArr ? undefined : obj ? (isMap ? Array.from(obj.keys()) : Object.keys(obj)) : [];
|
|
@@ -919,8 +1072,9 @@ function updateNodes(parent, obj, prevValue) {
|
|
|
919
1072
|
let prevChildrenById;
|
|
920
1073
|
let moved;
|
|
921
1074
|
const isMap = obj instanceof Map;
|
|
1075
|
+
const isPrevMap = prevValue instanceof Map;
|
|
922
1076
|
const keys = getKeys(obj, isArr, isMap);
|
|
923
|
-
const keysPrev = getKeys(prevValue, isArr,
|
|
1077
|
+
const keysPrev = getKeys(prevValue, isArr, isPrevMap);
|
|
924
1078
|
const length = ((_a = (keys || obj)) === null || _a === void 0 ? void 0 : _a.length) || 0;
|
|
925
1079
|
const lengthPrev = ((_b = (keysPrev || prevValue)) === null || _b === void 0 ? void 0 : _b.length) || 0;
|
|
926
1080
|
let idField;
|
|
@@ -972,7 +1126,7 @@ function updateNodes(parent, obj, prevValue) {
|
|
|
972
1126
|
if (!keys.includes(key)) {
|
|
973
1127
|
hasADiff = true;
|
|
974
1128
|
const child = getChildNode(parent, key);
|
|
975
|
-
const prev =
|
|
1129
|
+
const prev = isPrevMap ? prevValue.get(key) : prevValue[key];
|
|
976
1130
|
if (prev !== undefined) {
|
|
977
1131
|
if (!isPrimitive(prev)) {
|
|
978
1132
|
updateNodes(child, undefined, prev);
|
|
@@ -991,7 +1145,7 @@ function updateNodes(parent, obj, prevValue) {
|
|
|
991
1145
|
for (let i = 0; i < length; i++) {
|
|
992
1146
|
const key = isArr ? i + '' : keys[i];
|
|
993
1147
|
const value = isMap ? obj.get(key) : obj[key];
|
|
994
|
-
const prev =
|
|
1148
|
+
const prev = isPrevMap ? prevValue === null || prevValue === void 0 ? void 0 : prevValue.get(key) : prevValue === null || prevValue === void 0 ? void 0 : prevValue[key];
|
|
995
1149
|
let isDiff = value !== prev;
|
|
996
1150
|
if (isDiff) {
|
|
997
1151
|
const id = idField && value
|
|
@@ -1029,7 +1183,10 @@ function updateNodes(parent, obj, prevValue) {
|
|
|
1029
1183
|
if (isDiff) {
|
|
1030
1184
|
// Array has a new / modified element
|
|
1031
1185
|
// If object iterate through its children
|
|
1032
|
-
if (
|
|
1186
|
+
if (isFunction(value)) {
|
|
1187
|
+
extractFunctionOrComputed(parent, obj, key, value);
|
|
1188
|
+
}
|
|
1189
|
+
else if (isPrimitive(value)) {
|
|
1033
1190
|
hasADiff = true;
|
|
1034
1191
|
}
|
|
1035
1192
|
else {
|
|
@@ -1077,6 +1234,15 @@ function getProxy(node, p, asFunction) {
|
|
|
1077
1234
|
// Create a proxy if not already cached and return it
|
|
1078
1235
|
return (node.proxy || (node.proxy = new Proxy(node, proxyHandler)));
|
|
1079
1236
|
}
|
|
1237
|
+
function flushPending() {
|
|
1238
|
+
// Need to short circuit the computed batching because the user called get() or peek()
|
|
1239
|
+
// in which case the set needs to run immediately so that the values are up to date.
|
|
1240
|
+
if (globalState.pendingNodes.size > 0) {
|
|
1241
|
+
const nodes = Array.from(globalState.pendingNodes.values());
|
|
1242
|
+
globalState.pendingNodes.clear();
|
|
1243
|
+
nodes.forEach((fn) => fn());
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1080
1246
|
const proxyHandler = {
|
|
1081
1247
|
get(node, p, receiver) {
|
|
1082
1248
|
var _a;
|
|
@@ -1104,6 +1270,9 @@ const proxyHandler = {
|
|
|
1104
1270
|
const fn = observableFns.get(p);
|
|
1105
1271
|
// If this is an observable function, call it
|
|
1106
1272
|
if (fn) {
|
|
1273
|
+
if (p === 'get' || p === 'peek') {
|
|
1274
|
+
flushPending();
|
|
1275
|
+
}
|
|
1107
1276
|
return function (a, b, c) {
|
|
1108
1277
|
const l = arguments.length;
|
|
1109
1278
|
// Array call and apply are slow so micro-optimize this hot path.
|
|
@@ -1209,10 +1378,6 @@ const proxyHandler = {
|
|
|
1209
1378
|
return vProp;
|
|
1210
1379
|
}
|
|
1211
1380
|
}
|
|
1212
|
-
// TODOV3: Remove "state"
|
|
1213
|
-
if (vProp === undefined && (p === 'state' || p === '_state') && node.state) {
|
|
1214
|
-
return node.state;
|
|
1215
|
-
}
|
|
1216
1381
|
// Return an observable proxy to the property
|
|
1217
1382
|
return getProxy(node, p);
|
|
1218
1383
|
},
|
|
@@ -1290,7 +1455,7 @@ function set(node, newValue) {
|
|
|
1290
1455
|
}
|
|
1291
1456
|
function toggle(node) {
|
|
1292
1457
|
const value = getNodeValue(node);
|
|
1293
|
-
if (value === undefined || isBoolean(value)) {
|
|
1458
|
+
if (value === undefined || value === null || isBoolean(value)) {
|
|
1294
1459
|
set(node, !value);
|
|
1295
1460
|
return !value;
|
|
1296
1461
|
}
|
|
@@ -1304,12 +1469,14 @@ function setKey(node, key, newValue, level) {
|
|
|
1304
1469
|
console.warn(`[legend-state] Set an HTMLElement into state. You probably don't want to do that.`);
|
|
1305
1470
|
}
|
|
1306
1471
|
}
|
|
1472
|
+
const isRoot = !node.parent && key === '_';
|
|
1473
|
+
// TODOv3 root locking will be removed with old computeds
|
|
1307
1474
|
if (node.root.locked && !node.root.set) {
|
|
1308
1475
|
// This happens when modifying a locked observable such as a computed.
|
|
1309
1476
|
// If merging this could be happening deep in a hierarchy so we don't want to throw errors so we'll just do nothing.
|
|
1310
1477
|
// This could happen during persistence local load for example.
|
|
1311
1478
|
if (globalState.isMerging) {
|
|
1312
|
-
return;
|
|
1479
|
+
return isRoot ? getProxy(node) : getProxy(node, key);
|
|
1313
1480
|
}
|
|
1314
1481
|
else {
|
|
1315
1482
|
throw new Error(process.env.NODE_ENV === 'development'
|
|
@@ -1317,18 +1484,28 @@ function setKey(node, key, newValue, level) {
|
|
|
1317
1484
|
: '[legend-state] Modified locked observable');
|
|
1318
1485
|
}
|
|
1319
1486
|
}
|
|
1320
|
-
|
|
1487
|
+
if (node.parent && !getNodeValue(node)) {
|
|
1488
|
+
return set(node, { [key]: newValue });
|
|
1489
|
+
}
|
|
1321
1490
|
// Get the child node for updating and notifying
|
|
1322
1491
|
const childNode = isRoot ? node : getChildNode(node, key, isFunction(newValue) ? newValue : undefined);
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1492
|
+
if (isObservable(newValue)) {
|
|
1493
|
+
setToObservable(childNode, newValue);
|
|
1494
|
+
}
|
|
1495
|
+
else {
|
|
1496
|
+
// Set the raw value on the parent object
|
|
1497
|
+
const { newValue: savedValue, prevValue, parentValue } = setNodeValue(childNode, newValue);
|
|
1498
|
+
const isFunc = isFunction(savedValue);
|
|
1499
|
+
const isPrim = isPrimitive(savedValue) || savedValue instanceof Date;
|
|
1500
|
+
if (savedValue !== prevValue) {
|
|
1501
|
+
updateNodesAndNotify(node, savedValue, prevValue, childNode, isPrim, isRoot, level);
|
|
1502
|
+
}
|
|
1503
|
+
extractFunctionOrComputed(node, parentValue, key, savedValue);
|
|
1504
|
+
if (isFunc) {
|
|
1505
|
+
return savedValue;
|
|
1506
|
+
}
|
|
1329
1507
|
}
|
|
1330
|
-
|
|
1331
|
-
return isFunc ? savedValue : isRoot ? getProxy(node) : getProxy(node, key);
|
|
1508
|
+
return isRoot ? getProxy(node) : getProxy(node, key);
|
|
1332
1509
|
}
|
|
1333
1510
|
function assign(node, value) {
|
|
1334
1511
|
const proxy = getProxy(node);
|
|
@@ -1433,7 +1610,7 @@ function handlerMapSet(node, p, value) {
|
|
|
1433
1610
|
function updateNodesAndNotify(node, newValue, prevValue, childNode, isPrim, isRoot, level) {
|
|
1434
1611
|
if (!childNode)
|
|
1435
1612
|
childNode = node;
|
|
1436
|
-
// Make sure we don't call too many listeners for
|
|
1613
|
+
// Make sure we don't call too many listeners for every property set
|
|
1437
1614
|
beginBatch();
|
|
1438
1615
|
let hasADiff = isPrim;
|
|
1439
1616
|
let whenOptimizedOnlyIf = false;
|
|
@@ -1454,7 +1631,7 @@ function updateNodesAndNotify(node, newValue, prevValue, childNode, isPrim, isRo
|
|
|
1454
1631
|
}
|
|
1455
1632
|
endBatch();
|
|
1456
1633
|
}
|
|
1457
|
-
function extractPromise(node, value) {
|
|
1634
|
+
function extractPromise(node, value, setter) {
|
|
1458
1635
|
if (!node.state) {
|
|
1459
1636
|
node.state = createObservable({
|
|
1460
1637
|
isLoaded: false,
|
|
@@ -1462,28 +1639,38 @@ function extractPromise(node, value) {
|
|
|
1462
1639
|
}
|
|
1463
1640
|
value
|
|
1464
1641
|
.then((value) => {
|
|
1465
|
-
set(node, value);
|
|
1466
|
-
node.state.
|
|
1642
|
+
setter ? setter({ value }) : set(node, value);
|
|
1643
|
+
node.state.assign({
|
|
1644
|
+
isLoaded: true,
|
|
1645
|
+
error: undefined,
|
|
1646
|
+
});
|
|
1467
1647
|
})
|
|
1468
1648
|
.catch((error) => {
|
|
1469
1649
|
node.state.error.set(error);
|
|
1470
1650
|
});
|
|
1471
1651
|
}
|
|
1472
1652
|
function extractFunctionOrComputed(node, obj, k, v) {
|
|
1653
|
+
var _a;
|
|
1473
1654
|
if (isPromise(v)) {
|
|
1474
1655
|
const childNode = getChildNode(node, k);
|
|
1475
1656
|
extractPromise(childNode, v);
|
|
1476
1657
|
setNodeValue(childNode, undefined);
|
|
1477
1658
|
}
|
|
1478
1659
|
else if (typeof v === 'function') {
|
|
1660
|
+
const childNode = (_a = node.children) === null || _a === void 0 ? void 0 : _a.get(k);
|
|
1479
1661
|
extractFunction(node, k, v);
|
|
1480
|
-
|
|
1662
|
+
// If child was previously activated, then peek the new linked observable to make sure it's activated
|
|
1663
|
+
if (childNode && !childNode.lazy) {
|
|
1664
|
+
if (isObservable(v)) {
|
|
1665
|
+
const vNode = getNode(v);
|
|
1666
|
+
peek(vNode);
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1481
1669
|
}
|
|
1482
1670
|
else if (typeof v == 'object' && v !== null && v !== undefined) {
|
|
1483
1671
|
const childNode = getNode(v);
|
|
1484
1672
|
if (childNode === null || childNode === void 0 ? void 0 : childNode.isComputed) {
|
|
1485
1673
|
extractFunction(node, k, v, childNode);
|
|
1486
|
-
delete obj[k];
|
|
1487
1674
|
}
|
|
1488
1675
|
else {
|
|
1489
1676
|
return true;
|
|
@@ -1497,177 +1684,316 @@ function get(node, options) {
|
|
|
1497
1684
|
return peek(node);
|
|
1498
1685
|
}
|
|
1499
1686
|
function peek(node) {
|
|
1500
|
-
|
|
1687
|
+
if (node.dirtyFn) {
|
|
1688
|
+
node.dirtyFn();
|
|
1689
|
+
globalState.dirtyNodes.delete(node);
|
|
1690
|
+
node.dirtyFn = undefined;
|
|
1691
|
+
}
|
|
1692
|
+
let value = getNodeValue(node);
|
|
1501
1693
|
// If node is not yet lazily computed go do that
|
|
1502
1694
|
const lazy = node.lazy;
|
|
1503
1695
|
if (lazy) {
|
|
1504
1696
|
delete node.lazy;
|
|
1505
1697
|
if (isFunction(node) || isFunction(lazy)) {
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
if (hasOwnProperty.call(value, key)) {
|
|
1511
|
-
extractFunctionOrComputed(node, value, key, value[key]);
|
|
1698
|
+
if (node.parent) {
|
|
1699
|
+
const parentValue = getNodeValue(node.parent);
|
|
1700
|
+
if (parentValue) {
|
|
1701
|
+
delete parentValue[node.key];
|
|
1512
1702
|
}
|
|
1513
1703
|
}
|
|
1704
|
+
value = activateNodeFunction(node, lazy);
|
|
1705
|
+
}
|
|
1706
|
+
for (const key in value) {
|
|
1707
|
+
if (hasOwnProperty.call(value, key)) {
|
|
1708
|
+
extractFunctionOrComputed(node, value, key, value[key]);
|
|
1709
|
+
}
|
|
1514
1710
|
}
|
|
1515
1711
|
}
|
|
1516
1712
|
// Check if computed needs to activate
|
|
1517
1713
|
checkActivate(node);
|
|
1518
1714
|
return value;
|
|
1519
1715
|
}
|
|
1520
|
-
function createNodeActivationParams(node) {
|
|
1521
|
-
node.activationState = {
|
|
1522
|
-
lastSync: {},
|
|
1523
|
-
};
|
|
1524
|
-
const state = node.activationState;
|
|
1525
|
-
// The onSet function handles the observable being set
|
|
1526
|
-
// and forwards the set elsewhere
|
|
1527
|
-
const onSet = (onSetFnParam) => {
|
|
1528
|
-
state.onSetFn = onSetFnParam;
|
|
1529
|
-
};
|
|
1530
|
-
// The onSet function handles the observable being set
|
|
1531
|
-
// and forwards the set elsewhere
|
|
1532
|
-
const updateLastSync = (fn) => {
|
|
1533
|
-
state.lastSync.value = fn;
|
|
1534
|
-
};
|
|
1535
|
-
// The subscribe function runs a function that listens to
|
|
1536
|
-
// a data source and sends updates into the observable
|
|
1537
|
-
const subscribe = (fn) => {
|
|
1538
|
-
if (!state.subscriber) {
|
|
1539
|
-
state.subscriber = fn;
|
|
1540
|
-
}
|
|
1541
|
-
};
|
|
1542
|
-
const cache = (fn) => {
|
|
1543
|
-
if (!state.cacheOptions) {
|
|
1544
|
-
state.cacheOptions = isFunction(fn) ? fn() : fn;
|
|
1545
|
-
}
|
|
1546
|
-
};
|
|
1547
|
-
const retry = (params) => {
|
|
1548
|
-
if (!state.retryOptions) {
|
|
1549
|
-
state.retryOptions = params;
|
|
1550
|
-
}
|
|
1551
|
-
};
|
|
1552
|
-
// The proxy function simply marks the node as a proxy with this function
|
|
1553
|
-
// so that child nodes will be created with this function, and then simply
|
|
1554
|
-
// activated as a function
|
|
1555
|
-
const proxy = (fn) => {
|
|
1556
|
-
node.proxyFn2 = fn;
|
|
1557
|
-
};
|
|
1558
|
-
return {
|
|
1559
|
-
onSet,
|
|
1560
|
-
proxy,
|
|
1561
|
-
cache,
|
|
1562
|
-
retry,
|
|
1563
|
-
subscribe,
|
|
1564
|
-
updateLastSync,
|
|
1565
|
-
};
|
|
1566
|
-
}
|
|
1567
1716
|
function activateNodeFunction(node, lazyFn) {
|
|
1568
|
-
let prevTarget
|
|
1569
|
-
let curTarget
|
|
1570
|
-
|
|
1717
|
+
// let prevTarget$: Observable<any>;
|
|
1718
|
+
// let curTarget$: Observable<any>;
|
|
1719
|
+
let update;
|
|
1571
1720
|
let wasPromise;
|
|
1572
|
-
const
|
|
1721
|
+
const activateFn = (isFunction(node) ? node : lazyFn);
|
|
1722
|
+
const doRetry = () => { var _a; return (_a = node.state) === null || _a === void 0 ? void 0 : _a.refreshNum.set((v) => v + 1); };
|
|
1723
|
+
let activatedValue;
|
|
1724
|
+
let disposes = [];
|
|
1725
|
+
let refreshFn;
|
|
1726
|
+
function markDirty() {
|
|
1727
|
+
node.dirtyFn = refreshFn;
|
|
1728
|
+
globalState.dirtyNodes.add(node);
|
|
1729
|
+
}
|
|
1573
1730
|
observe(() => {
|
|
1574
|
-
|
|
1731
|
+
var _a, _b, _c, _d;
|
|
1732
|
+
// const params = createNodeActivationParams(node);
|
|
1575
1733
|
// Run the function at this node
|
|
1576
|
-
let value =
|
|
1577
|
-
// If target is an observable,
|
|
1578
|
-
// and set up an onSet to write changes back to it
|
|
1734
|
+
let value = activateFn();
|
|
1735
|
+
// If target is an observable, make this node a link to it
|
|
1579
1736
|
if (isObservable(value)) {
|
|
1580
|
-
|
|
1581
|
-
curTarget$ = value;
|
|
1582
|
-
params.onSet(({ value: newValue, getPrevious }) => {
|
|
1583
|
-
// Don't set the target observable if the target has changed since the last run
|
|
1584
|
-
if (!prevTarget$ || curTarget$ === prevTarget$) {
|
|
1585
|
-
// Set the node value back to what it was before before setting it.
|
|
1586
|
-
// This is a workaround for linked objects because it might not notify
|
|
1587
|
-
// if setting a property of an object
|
|
1588
|
-
// TODO: Is there a way to not do this? Or at least only do it in a
|
|
1589
|
-
// small subset of cases?
|
|
1590
|
-
setNodeValue(getNode(curTarget$), getPrevious());
|
|
1591
|
-
// Set the value on the curTarget
|
|
1592
|
-
curTarget$.set(newValue);
|
|
1593
|
-
}
|
|
1594
|
-
});
|
|
1595
|
-
// Get the value from the observable because we still want the raw value
|
|
1596
|
-
// for the effect.
|
|
1597
|
-
value = value.get();
|
|
1737
|
+
value = setToObservable(node, value);
|
|
1598
1738
|
}
|
|
1599
|
-
|
|
1600
|
-
|
|
1739
|
+
if (isFunction(value)) {
|
|
1740
|
+
value = value();
|
|
1601
1741
|
}
|
|
1742
|
+
const activated = value === null || value === void 0 ? void 0 : value[symbolActivated];
|
|
1743
|
+
if (activated) {
|
|
1744
|
+
node.activationState = activated;
|
|
1745
|
+
value = undefined;
|
|
1746
|
+
}
|
|
1747
|
+
wasPromise = isPromise(value);
|
|
1602
1748
|
// Activate this node if not activated already (may be called recursively)
|
|
1603
1749
|
// TODO: Is calling recursively bad? If so can it be fixed?
|
|
1604
1750
|
if (!node.activated) {
|
|
1605
1751
|
node.activated = true;
|
|
1752
|
+
const isCached = !!((_a = node.activationState) === null || _a === void 0 ? void 0 : _a.cache);
|
|
1753
|
+
wasPromise = wasPromise || !!isCached;
|
|
1606
1754
|
const activateNodeFn = wasPromise ? globalState.activateNode : activateNodeBase;
|
|
1607
|
-
activateNodeFn(node,
|
|
1755
|
+
const { update: newUpdate, value: newValue } = activateNodeFn(node, doRetry, !!wasPromise, value);
|
|
1756
|
+
update = newUpdate;
|
|
1757
|
+
value = newValue !== null && newValue !== void 0 ? newValue : activated === null || activated === void 0 ? void 0 : activated.initial;
|
|
1758
|
+
}
|
|
1759
|
+
else if (node.activationState) {
|
|
1760
|
+
if (!node.activationState.persistedRetry && !node.activationState.waitFor) {
|
|
1761
|
+
const activated = node.activationState;
|
|
1762
|
+
// TODO Should this have lastSync and value somehow?
|
|
1763
|
+
value =
|
|
1764
|
+
(_c = (_b = activated.get) === null || _b === void 0 ? void 0 : _b.call(activated, {
|
|
1765
|
+
updateLastSync: noop,
|
|
1766
|
+
setMode: noop,
|
|
1767
|
+
lastSync: undefined,
|
|
1768
|
+
value: undefined,
|
|
1769
|
+
refresh: doRetry,
|
|
1770
|
+
})) !== null && _c !== void 0 ? _c : activated.initial;
|
|
1771
|
+
}
|
|
1608
1772
|
}
|
|
1609
|
-
|
|
1773
|
+
wasPromise = wasPromise || isPromise(value);
|
|
1774
|
+
get(getNode((_d = node.state) === null || _d === void 0 ? void 0 : _d.refreshNum));
|
|
1610
1775
|
return value;
|
|
1611
|
-
}, (
|
|
1612
|
-
|
|
1776
|
+
}, (e) => {
|
|
1777
|
+
const { value, nodes, refresh } = e;
|
|
1778
|
+
refreshFn = refresh;
|
|
1779
|
+
if (!wasPromise || !globalState.isLoadingRemote$.peek()) {
|
|
1613
1780
|
if (wasPromise) {
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1781
|
+
if (node.activationState) {
|
|
1782
|
+
const { initial } = node.activationState;
|
|
1783
|
+
if (value && isPromise(value)) {
|
|
1784
|
+
// Extract the promise to make it set the value/error when it comes in
|
|
1785
|
+
extractPromise(node, value, update);
|
|
1786
|
+
}
|
|
1787
|
+
// Set this to undefined only if it's replacing the activation function,
|
|
1788
|
+
// so we don't overwrite it if it already has real data from either local
|
|
1789
|
+
// cache or a previous run
|
|
1790
|
+
if (isFunction(getNodeValue(node))) {
|
|
1791
|
+
setNodeValue(node, initial !== null && initial !== void 0 ? initial : undefined);
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
else if (node.activated) {
|
|
1795
|
+
// Extract the promise to make it set the value/error when it comes in
|
|
1796
|
+
extractPromise(node, value, update);
|
|
1797
|
+
// Set this to undefined only if it's replacing the activation function,
|
|
1798
|
+
// so we don't overwrite it if it already has real data from either local
|
|
1799
|
+
// cache or a previous run
|
|
1800
|
+
if (isFunction(getNodeValue(node))) {
|
|
1801
|
+
setNodeValue(node, undefined);
|
|
1802
|
+
}
|
|
1621
1803
|
}
|
|
1622
1804
|
}
|
|
1623
1805
|
else {
|
|
1806
|
+
activatedValue = value;
|
|
1624
1807
|
set(node, value);
|
|
1625
|
-
node.state.
|
|
1808
|
+
node.state.assign({
|
|
1809
|
+
isLoaded: true,
|
|
1810
|
+
error: undefined,
|
|
1811
|
+
});
|
|
1626
1812
|
}
|
|
1627
1813
|
}
|
|
1628
|
-
|
|
1814
|
+
disposes.forEach((fn) => fn());
|
|
1815
|
+
disposes = [];
|
|
1816
|
+
nodes === null || nodes === void 0 ? void 0 : nodes.forEach(({ node }) => {
|
|
1817
|
+
disposes.push(onChange(node, markDirty, { immediate: true }));
|
|
1818
|
+
});
|
|
1819
|
+
e.cancel = true;
|
|
1820
|
+
}, { fromComputed: true });
|
|
1821
|
+
return activatedValue;
|
|
1629
1822
|
}
|
|
1630
|
-
const activateNodeBase = (globalState.activateNode = function activateNodeBase(node, refresh) {
|
|
1631
|
-
const { onSetFn, subscriber } = node.activationState;
|
|
1632
|
-
let isSetting = false;
|
|
1823
|
+
const activateNodeBase = (globalState.activateNode = function activateNodeBase(node, refresh, wasPromise, value) {
|
|
1633
1824
|
if (!node.state) {
|
|
1634
1825
|
node.state = createObservable({
|
|
1635
1826
|
isLoaded: false,
|
|
1636
1827
|
}, false, extractPromise, getProxy);
|
|
1637
1828
|
}
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1829
|
+
let isSetting = false;
|
|
1830
|
+
let isSettingFromSubscribe = false;
|
|
1831
|
+
let _mode = 'set';
|
|
1832
|
+
if (node.activationState) {
|
|
1833
|
+
const { onSet, subscribe, get: getFn, initial } = node.activationState;
|
|
1834
|
+
value = getFn
|
|
1835
|
+
? runWithRetry(node, { attemptNum: 0 }, () => {
|
|
1836
|
+
return getFn({
|
|
1837
|
+
updateLastSync: noop,
|
|
1838
|
+
setMode: (mode) => (_mode = mode),
|
|
1839
|
+
lastSync: undefined,
|
|
1840
|
+
value: undefined,
|
|
1841
|
+
refresh,
|
|
1842
|
+
});
|
|
1843
|
+
})
|
|
1844
|
+
: undefined;
|
|
1845
|
+
// TODO Should this have lastSync and value somehow?
|
|
1846
|
+
if (value == undefined || value === null) {
|
|
1847
|
+
value = initial;
|
|
1848
|
+
}
|
|
1849
|
+
if (onSet) {
|
|
1850
|
+
let allChanges = [];
|
|
1851
|
+
let latestValue = undefined;
|
|
1852
|
+
let runNumber = 0;
|
|
1853
|
+
const runChanges = (listenerParams) => {
|
|
1854
|
+
// Don't call the set if this is the first value coming in
|
|
1855
|
+
if (allChanges.length > 0) {
|
|
1856
|
+
let changes;
|
|
1857
|
+
let value;
|
|
1858
|
+
let getPrevious;
|
|
1859
|
+
if (listenerParams) {
|
|
1860
|
+
changes = listenerParams.changes;
|
|
1861
|
+
value = listenerParams.value;
|
|
1862
|
+
getPrevious = listenerParams.getPrevious;
|
|
1863
|
+
}
|
|
1864
|
+
else {
|
|
1865
|
+
// If this is called by flushPending then get the change array
|
|
1866
|
+
// that we've been building up.
|
|
1867
|
+
changes = allChanges;
|
|
1868
|
+
value = latestValue;
|
|
1869
|
+
getPrevious = createPreviousHandler(value, changes);
|
|
1870
|
+
}
|
|
1871
|
+
allChanges = [];
|
|
1872
|
+
latestValue = undefined;
|
|
1873
|
+
globalState.pendingNodes.delete(node);
|
|
1874
|
+
runNumber++;
|
|
1875
|
+
const thisRunNumber = runNumber;
|
|
1876
|
+
const run = () => {
|
|
1877
|
+
if (thisRunNumber !== runNumber) {
|
|
1878
|
+
// set may get called multiple times before it loads so ignore any previous runs
|
|
1879
|
+
return;
|
|
1880
|
+
}
|
|
1881
|
+
const retryAttempts = { attemptNum: 0 };
|
|
1882
|
+
return runWithRetry(node, retryAttempts, (eventRetry) => {
|
|
1883
|
+
const cancelRetry = () => {
|
|
1884
|
+
eventRetry.cancel = true;
|
|
1885
|
+
};
|
|
1886
|
+
return new Promise((resolve, reject) => {
|
|
1887
|
+
isSetting = true;
|
|
1888
|
+
batch(() => {
|
|
1889
|
+
try {
|
|
1890
|
+
return onSet({
|
|
1891
|
+
value,
|
|
1892
|
+
changes,
|
|
1893
|
+
getPrevious,
|
|
1894
|
+
node,
|
|
1895
|
+
update,
|
|
1896
|
+
refresh,
|
|
1897
|
+
retryNum: retryAttempts.attemptNum,
|
|
1898
|
+
cancelRetry,
|
|
1899
|
+
fromSubscribe: isSettingFromSubscribe,
|
|
1900
|
+
});
|
|
1901
|
+
}
|
|
1902
|
+
catch (e) {
|
|
1903
|
+
reject(e);
|
|
1904
|
+
}
|
|
1905
|
+
}, () => {
|
|
1906
|
+
isSetting = false;
|
|
1907
|
+
resolve();
|
|
1908
|
+
});
|
|
1909
|
+
});
|
|
1910
|
+
});
|
|
1911
|
+
};
|
|
1912
|
+
whenReady(node.state.isLoaded, run);
|
|
1647
1913
|
}
|
|
1648
|
-
}
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1914
|
+
};
|
|
1915
|
+
const onChangeImmediate = ({ value, changes }) => {
|
|
1916
|
+
if (!isSetting || isSettingFromSubscribe) {
|
|
1917
|
+
if (changes.length > 1 || !isFunction(changes[0].prevAtPath)) {
|
|
1918
|
+
latestValue = value;
|
|
1919
|
+
if (allChanges.length > 0) {
|
|
1920
|
+
changes = changes.filter((change) => !isArraySubset(allChanges[0].path, change.path));
|
|
1921
|
+
}
|
|
1922
|
+
allChanges.push(...changes);
|
|
1923
|
+
globalState.pendingNodes.set(node, runChanges);
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
};
|
|
1927
|
+
// Create an immediate listener to mark this node as pending. Then actually run
|
|
1928
|
+
// the changes at the end of the batch so everything is properly batched.
|
|
1929
|
+
// However, this can be short circuited if the user calls get() or peek()
|
|
1930
|
+
// in which case the set needs to run immediately so that the values are up to date.
|
|
1931
|
+
onChange(node, onChangeImmediate, { immediate: true });
|
|
1932
|
+
onChange(node, runChanges);
|
|
1933
|
+
}
|
|
1934
|
+
if (process.env.NODE_ENV === 'development' && node.activationState.cache) {
|
|
1935
|
+
// TODO Better message
|
|
1936
|
+
console.log('[legend-state] Using cache without setting up persistence first');
|
|
1937
|
+
}
|
|
1938
|
+
if (process.env.NODE_ENV === 'development' && node.activationState.retry) {
|
|
1939
|
+
// TODO Better message
|
|
1940
|
+
console.log('[legend-state] Using retry without setting up persistence first');
|
|
1941
|
+
}
|
|
1942
|
+
if (subscribe) {
|
|
1943
|
+
const updateFromSubscribe = (params) => {
|
|
1944
|
+
whenReady(node.state.isLoaded, () => {
|
|
1945
|
+
isSettingFromSubscribe = true;
|
|
1946
|
+
update(params);
|
|
1947
|
+
isSettingFromSubscribe = false;
|
|
1948
|
+
});
|
|
1949
|
+
};
|
|
1950
|
+
subscribe({ node, update: updateFromSubscribe, refresh });
|
|
1951
|
+
}
|
|
1659
1952
|
}
|
|
1660
|
-
const update = ({ value }) => {
|
|
1953
|
+
const update = ({ value, mode }) => {
|
|
1661
1954
|
// TODO: This isSetting might not be necessary? Tests still work if removing it.
|
|
1662
1955
|
// Write tests that would break it if removed? I'd guess a combination of subscribe and
|
|
1663
1956
|
if (!isSetting) {
|
|
1664
|
-
|
|
1957
|
+
isSetting = true;
|
|
1958
|
+
if (_mode === 'assign' || mode === 'assign') {
|
|
1959
|
+
assign(node, value);
|
|
1960
|
+
}
|
|
1961
|
+
else if (_mode === 'merge' || mode === 'merge') {
|
|
1962
|
+
mergeIntoObservable(getProxy(node), value);
|
|
1963
|
+
}
|
|
1964
|
+
else {
|
|
1965
|
+
set(node, value);
|
|
1966
|
+
}
|
|
1967
|
+
isSetting = false;
|
|
1665
1968
|
}
|
|
1666
1969
|
};
|
|
1667
|
-
|
|
1668
|
-
subscriber({ update, refresh });
|
|
1669
|
-
}
|
|
1970
|
+
return { update, value };
|
|
1670
1971
|
});
|
|
1972
|
+
function setToObservable(node, value) {
|
|
1973
|
+
// If the computed is a proxy to another observable
|
|
1974
|
+
// link it to the target observable
|
|
1975
|
+
const linkedNode = getNode(value);
|
|
1976
|
+
if (linkedNode !== node) {
|
|
1977
|
+
const prevNode = node.linkedToNode;
|
|
1978
|
+
node.linkedToNode = linkedNode;
|
|
1979
|
+
if (!linkedNode.linkedFromNodes) {
|
|
1980
|
+
linkedNode.linkedFromNodes = new Set();
|
|
1981
|
+
}
|
|
1982
|
+
linkedNode.linkedFromNodes.add(node);
|
|
1983
|
+
peek(linkedNode);
|
|
1984
|
+
onChange(linkedNode, ({ value: newValue }) => {
|
|
1985
|
+
value = newValue;
|
|
1986
|
+
set(node, value);
|
|
1987
|
+
}, { initial: true });
|
|
1988
|
+
// If the target observable is different then notify for the change
|
|
1989
|
+
if (prevNode) {
|
|
1990
|
+
const value = getNodeValue(linkedNode);
|
|
1991
|
+
const prevValue = getNodeValue(prevNode);
|
|
1992
|
+
notify(node, value, prevValue, 0);
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
return value;
|
|
1996
|
+
}
|
|
1671
1997
|
|
|
1672
1998
|
const fns = ['get', 'set', 'peek', 'onChange', 'toggle'];
|
|
1673
1999
|
function ObservablePrimitiveClass(node) {
|
|
@@ -1684,8 +2010,14 @@ function proto(key, fn) {
|
|
|
1684
2010
|
return fn.call(this, this._node, ...args);
|
|
1685
2011
|
};
|
|
1686
2012
|
}
|
|
1687
|
-
proto('peek',
|
|
1688
|
-
|
|
2013
|
+
proto('peek', (node) => {
|
|
2014
|
+
flushPending();
|
|
2015
|
+
return peek(node);
|
|
2016
|
+
});
|
|
2017
|
+
proto('get', (node, options) => {
|
|
2018
|
+
flushPending();
|
|
2019
|
+
return get(node, options);
|
|
2020
|
+
});
|
|
1689
2021
|
proto('set', set);
|
|
1690
2022
|
proto('onChange', onChange);
|
|
1691
2023
|
// Getters
|
|
@@ -1697,7 +2029,7 @@ Object.defineProperty(ObservablePrimitiveClass.prototype, symbolGetNode, {
|
|
|
1697
2029
|
});
|
|
1698
2030
|
ObservablePrimitiveClass.prototype.toggle = function () {
|
|
1699
2031
|
const value = this.peek();
|
|
1700
|
-
if (value === undefined || isBoolean(value)) {
|
|
2032
|
+
if (value === undefined || value === null || isBoolean(value)) {
|
|
1701
2033
|
this.set(!value);
|
|
1702
2034
|
}
|
|
1703
2035
|
else if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
|
|
@@ -1716,6 +2048,16 @@ function observable(value) {
|
|
|
1716
2048
|
function observablePrimitive(value) {
|
|
1717
2049
|
return createObservable(value, true, extractPromise, getProxy, ObservablePrimitiveClass);
|
|
1718
2050
|
}
|
|
2051
|
+
function syncState(obs) {
|
|
2052
|
+
const node = getNode(obs);
|
|
2053
|
+
if (!node.state) {
|
|
2054
|
+
peek(node);
|
|
2055
|
+
}
|
|
2056
|
+
if (!node.state) {
|
|
2057
|
+
node.state = observable({});
|
|
2058
|
+
}
|
|
2059
|
+
return node.state;
|
|
2060
|
+
}
|
|
1719
2061
|
globalState.isLoadingRemote$ = observable(false);
|
|
1720
2062
|
|
|
1721
2063
|
function computed(compute, set$1) {
|
|
@@ -1880,60 +2222,6 @@ function proxy(get, set) {
|
|
|
1880
2222
|
return obs;
|
|
1881
2223
|
}
|
|
1882
2224
|
|
|
1883
|
-
function _when(predicate, effect, checkReady) {
|
|
1884
|
-
// If predicate is a regular Promise skip all the observable stuff
|
|
1885
|
-
if (isPromise(predicate)) {
|
|
1886
|
-
return effect ? predicate.then(effect) : predicate;
|
|
1887
|
-
}
|
|
1888
|
-
let value;
|
|
1889
|
-
// Create a wrapping fn that calls the effect if predicate returns true
|
|
1890
|
-
function run(e) {
|
|
1891
|
-
const ret = computeSelector(predicate);
|
|
1892
|
-
if (!isPromise(ret) && (checkReady ? isObservableValueReady(ret) : ret)) {
|
|
1893
|
-
value = ret;
|
|
1894
|
-
// Set cancel so that observe does not track anymore
|
|
1895
|
-
e.cancel = true;
|
|
1896
|
-
}
|
|
1897
|
-
return value;
|
|
1898
|
-
}
|
|
1899
|
-
function doEffect() {
|
|
1900
|
-
// If value is truthy then run the effect
|
|
1901
|
-
effect === null || effect === void 0 ? void 0 : effect(value);
|
|
1902
|
-
}
|
|
1903
|
-
// Run in an observe
|
|
1904
|
-
observe(run, doEffect);
|
|
1905
|
-
// If first run resulted in a truthy value just return it.
|
|
1906
|
-
// It will have set e.cancel so no need to dispose
|
|
1907
|
-
if (isPromise(value)) {
|
|
1908
|
-
return effect ? value.then(effect) : value;
|
|
1909
|
-
}
|
|
1910
|
-
else if (value !== undefined) {
|
|
1911
|
-
return Promise.resolve(value);
|
|
1912
|
-
}
|
|
1913
|
-
else {
|
|
1914
|
-
// Wrap it in a promise
|
|
1915
|
-
const promise = new Promise((resolve) => {
|
|
1916
|
-
if (effect) {
|
|
1917
|
-
const originalEffect = effect;
|
|
1918
|
-
effect = (value) => {
|
|
1919
|
-
const effectValue = originalEffect(value);
|
|
1920
|
-
resolve(effectValue);
|
|
1921
|
-
};
|
|
1922
|
-
}
|
|
1923
|
-
else {
|
|
1924
|
-
effect = resolve;
|
|
1925
|
-
}
|
|
1926
|
-
});
|
|
1927
|
-
return promise;
|
|
1928
|
-
}
|
|
1929
|
-
}
|
|
1930
|
-
function when(predicate, effect) {
|
|
1931
|
-
return _when(predicate, effect, false);
|
|
1932
|
-
}
|
|
1933
|
-
function whenReady(predicate, effect) {
|
|
1934
|
-
return _when(predicate, effect, true);
|
|
1935
|
-
}
|
|
1936
|
-
|
|
1937
2225
|
const internal = {
|
|
1938
2226
|
ensureNodeValue,
|
|
1939
2227
|
findIDKey,
|
|
@@ -1944,13 +2232,16 @@ const internal = {
|
|
|
1944
2232
|
observableFns,
|
|
1945
2233
|
optimized,
|
|
1946
2234
|
peek,
|
|
2235
|
+
runWithRetry,
|
|
1947
2236
|
set,
|
|
1948
2237
|
setAtPath,
|
|
1949
2238
|
setNodeValue,
|
|
2239
|
+
symbolActivated,
|
|
1950
2240
|
symbolDelete,
|
|
1951
2241
|
};
|
|
1952
2242
|
|
|
1953
2243
|
exports.ObservablePrimitiveClass = ObservablePrimitiveClass;
|
|
2244
|
+
exports.activated = activated;
|
|
1954
2245
|
exports.batch = batch;
|
|
1955
2246
|
exports.beginBatch = beginBatch;
|
|
1956
2247
|
exports.beginTracking = beginTracking;
|
|
@@ -1995,6 +2286,7 @@ exports.setInObservableAtPath = setInObservableAtPath;
|
|
|
1995
2286
|
exports.setSilently = setSilently;
|
|
1996
2287
|
exports.setupTracking = setupTracking;
|
|
1997
2288
|
exports.symbolDelete = symbolDelete;
|
|
2289
|
+
exports.syncState = syncState;
|
|
1998
2290
|
exports.trackSelector = trackSelector;
|
|
1999
2291
|
exports.tracking = tracking;
|
|
2000
2292
|
exports.updateTracking = updateTracking;
|