@legendapp/state 2.2.0-next.2 → 2.2.0-next.20
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 +14 -9
- package/index.js +522 -226
- package/index.js.map +1 -1
- package/index.mjs +521 -227
- 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 +3 -3
- package/persist-plugins/firebase.js.map +1 -1
- package/persist-plugins/firebase.mjs +3 -3
- 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 +1 -1
- package/persist-plugins/local-storage.js.map +1 -1
- package/persist-plugins/local-storage.mjs +1 -1
- 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 +16 -1
- package/persist.js +279 -125
- package/persist.js.map +1 -1
- package/persist.mjs +280 -126
- 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 +10 -9
- 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 +68 -315
- 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 -3
- 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 +4 -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,115 @@ 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 createRetryTimout(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
|
+
let value = undefined;
|
|
943
|
+
if (waitFor) {
|
|
944
|
+
value = whenReady(waitFor, () => {
|
|
945
|
+
node.activationState.waitFor = undefined;
|
|
946
|
+
return fn();
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
else {
|
|
950
|
+
value = fn();
|
|
951
|
+
}
|
|
952
|
+
if (isPromise(value) && retry) {
|
|
953
|
+
let timeoutRetry;
|
|
954
|
+
return new Promise((resolve) => {
|
|
955
|
+
const run = () => {
|
|
956
|
+
value
|
|
957
|
+
.then((val) => {
|
|
958
|
+
node.activationState.persistedRetry = false;
|
|
959
|
+
resolve(val);
|
|
960
|
+
})
|
|
961
|
+
.catch(() => {
|
|
962
|
+
node.activationState.persistedRetry = true;
|
|
963
|
+
state.attemptNum++;
|
|
964
|
+
if (timeoutRetry) {
|
|
965
|
+
clearTimeout(timeoutRetry);
|
|
966
|
+
}
|
|
967
|
+
timeoutRetry = createRetryTimout(retry, state.attemptNum, () => {
|
|
968
|
+
value = fn();
|
|
969
|
+
run();
|
|
970
|
+
});
|
|
971
|
+
});
|
|
972
|
+
};
|
|
973
|
+
run();
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
return value;
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
const noop = () => { };
|
|
835
980
|
const ArrayModifiers = new Set([
|
|
836
981
|
'copyWithin',
|
|
837
982
|
'fill',
|
|
@@ -872,19 +1017,24 @@ if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
|
|
|
872
1017
|
}
|
|
873
1018
|
function collectionSetter(node, target, prop, ...args) {
|
|
874
1019
|
var _a;
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
const
|
|
880
|
-
const
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
1020
|
+
if (prop === 'push') {
|
|
1021
|
+
setKey(node, target.length + '', args[0]);
|
|
1022
|
+
}
|
|
1023
|
+
else {
|
|
1024
|
+
const prevValue = target.slice();
|
|
1025
|
+
const ret = target[prop].apply(target, args);
|
|
1026
|
+
if (node) {
|
|
1027
|
+
const hasParent = isChildNodeValue(node);
|
|
1028
|
+
const key = hasParent ? node.key : '_';
|
|
1029
|
+
const parentValue = hasParent ? getNodeValue(node.parent) : node.root;
|
|
1030
|
+
// Set the object to the previous value first
|
|
1031
|
+
parentValue[key] = prevValue;
|
|
1032
|
+
// Then set with the new value so it notifies with the correct prevValue
|
|
1033
|
+
setKey((_a = node.parent) !== null && _a !== void 0 ? _a : node, key, target);
|
|
1034
|
+
}
|
|
1035
|
+
// Return the original value
|
|
1036
|
+
return ret;
|
|
1037
|
+
}
|
|
888
1038
|
}
|
|
889
1039
|
function getKeys(obj, isArr, isMap) {
|
|
890
1040
|
return isArr ? undefined : obj ? (isMap ? Array.from(obj.keys()) : Object.keys(obj)) : [];
|
|
@@ -919,8 +1069,9 @@ function updateNodes(parent, obj, prevValue) {
|
|
|
919
1069
|
let prevChildrenById;
|
|
920
1070
|
let moved;
|
|
921
1071
|
const isMap = obj instanceof Map;
|
|
1072
|
+
const isPrevMap = prevValue instanceof Map;
|
|
922
1073
|
const keys = getKeys(obj, isArr, isMap);
|
|
923
|
-
const keysPrev = getKeys(prevValue, isArr,
|
|
1074
|
+
const keysPrev = getKeys(prevValue, isArr, isPrevMap);
|
|
924
1075
|
const length = ((_a = (keys || obj)) === null || _a === void 0 ? void 0 : _a.length) || 0;
|
|
925
1076
|
const lengthPrev = ((_b = (keysPrev || prevValue)) === null || _b === void 0 ? void 0 : _b.length) || 0;
|
|
926
1077
|
let idField;
|
|
@@ -972,7 +1123,7 @@ function updateNodes(parent, obj, prevValue) {
|
|
|
972
1123
|
if (!keys.includes(key)) {
|
|
973
1124
|
hasADiff = true;
|
|
974
1125
|
const child = getChildNode(parent, key);
|
|
975
|
-
const prev =
|
|
1126
|
+
const prev = isPrevMap ? prevValue.get(key) : prevValue[key];
|
|
976
1127
|
if (prev !== undefined) {
|
|
977
1128
|
if (!isPrimitive(prev)) {
|
|
978
1129
|
updateNodes(child, undefined, prev);
|
|
@@ -991,7 +1142,7 @@ function updateNodes(parent, obj, prevValue) {
|
|
|
991
1142
|
for (let i = 0; i < length; i++) {
|
|
992
1143
|
const key = isArr ? i + '' : keys[i];
|
|
993
1144
|
const value = isMap ? obj.get(key) : obj[key];
|
|
994
|
-
const prev =
|
|
1145
|
+
const prev = isPrevMap ? prevValue === null || prevValue === void 0 ? void 0 : prevValue.get(key) : prevValue === null || prevValue === void 0 ? void 0 : prevValue[key];
|
|
995
1146
|
let isDiff = value !== prev;
|
|
996
1147
|
if (isDiff) {
|
|
997
1148
|
const id = idField && value
|
|
@@ -1029,7 +1180,10 @@ function updateNodes(parent, obj, prevValue) {
|
|
|
1029
1180
|
if (isDiff) {
|
|
1030
1181
|
// Array has a new / modified element
|
|
1031
1182
|
// If object iterate through its children
|
|
1032
|
-
if (
|
|
1183
|
+
if (isFunction(value)) {
|
|
1184
|
+
extractFunctionOrComputed(parent, obj, key, value);
|
|
1185
|
+
}
|
|
1186
|
+
else if (isPrimitive(value)) {
|
|
1033
1187
|
hasADiff = true;
|
|
1034
1188
|
}
|
|
1035
1189
|
else {
|
|
@@ -1077,6 +1231,15 @@ function getProxy(node, p, asFunction) {
|
|
|
1077
1231
|
// Create a proxy if not already cached and return it
|
|
1078
1232
|
return (node.proxy || (node.proxy = new Proxy(node, proxyHandler)));
|
|
1079
1233
|
}
|
|
1234
|
+
function flushPending() {
|
|
1235
|
+
// Need to short circuit the computed batching because the user called get() or peek()
|
|
1236
|
+
// in which case the set needs to run immediately so that the values are up to date.
|
|
1237
|
+
if (globalState.pendingNodes.size > 0) {
|
|
1238
|
+
const nodes = Array.from(globalState.pendingNodes.values());
|
|
1239
|
+
globalState.pendingNodes.clear();
|
|
1240
|
+
nodes.forEach((fn) => fn());
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1080
1243
|
const proxyHandler = {
|
|
1081
1244
|
get(node, p, receiver) {
|
|
1082
1245
|
var _a;
|
|
@@ -1104,6 +1267,9 @@ const proxyHandler = {
|
|
|
1104
1267
|
const fn = observableFns.get(p);
|
|
1105
1268
|
// If this is an observable function, call it
|
|
1106
1269
|
if (fn) {
|
|
1270
|
+
if (p === 'get' || p === 'peek') {
|
|
1271
|
+
flushPending();
|
|
1272
|
+
}
|
|
1107
1273
|
return function (a, b, c) {
|
|
1108
1274
|
const l = arguments.length;
|
|
1109
1275
|
// Array call and apply are slow so micro-optimize this hot path.
|
|
@@ -1209,10 +1375,6 @@ const proxyHandler = {
|
|
|
1209
1375
|
return vProp;
|
|
1210
1376
|
}
|
|
1211
1377
|
}
|
|
1212
|
-
// TODOV3: Remove "state"
|
|
1213
|
-
if (vProp === undefined && (p === 'state' || p === '_state') && node.state) {
|
|
1214
|
-
return node.state;
|
|
1215
|
-
}
|
|
1216
1378
|
// Return an observable proxy to the property
|
|
1217
1379
|
return getProxy(node, p);
|
|
1218
1380
|
},
|
|
@@ -1304,12 +1466,14 @@ function setKey(node, key, newValue, level) {
|
|
|
1304
1466
|
console.warn(`[legend-state] Set an HTMLElement into state. You probably don't want to do that.`);
|
|
1305
1467
|
}
|
|
1306
1468
|
}
|
|
1469
|
+
const isRoot = !node.parent && key === '_';
|
|
1470
|
+
// TODOv3 root locking will be removed with old computeds
|
|
1307
1471
|
if (node.root.locked && !node.root.set) {
|
|
1308
1472
|
// This happens when modifying a locked observable such as a computed.
|
|
1309
1473
|
// 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
1474
|
// This could happen during persistence local load for example.
|
|
1311
1475
|
if (globalState.isMerging) {
|
|
1312
|
-
return;
|
|
1476
|
+
return isRoot ? getProxy(node) : getProxy(node, key);
|
|
1313
1477
|
}
|
|
1314
1478
|
else {
|
|
1315
1479
|
throw new Error(process.env.NODE_ENV === 'development'
|
|
@@ -1317,18 +1481,28 @@ function setKey(node, key, newValue, level) {
|
|
|
1317
1481
|
: '[legend-state] Modified locked observable');
|
|
1318
1482
|
}
|
|
1319
1483
|
}
|
|
1320
|
-
|
|
1484
|
+
if (node.parent && !getNodeValue(node)) {
|
|
1485
|
+
return set(node, { [key]: newValue });
|
|
1486
|
+
}
|
|
1321
1487
|
// Get the child node for updating and notifying
|
|
1322
1488
|
const childNode = isRoot ? node : getChildNode(node, key, isFunction(newValue) ? newValue : undefined);
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
const isFunc = isFunction(savedValue);
|
|
1326
|
-
const isPrim = isPrimitive(savedValue) || savedValue instanceof Date;
|
|
1327
|
-
if (savedValue !== prevValue) {
|
|
1328
|
-
updateNodesAndNotify(node, savedValue, prevValue, childNode, isPrim, isRoot, level);
|
|
1489
|
+
if (isObservable(newValue)) {
|
|
1490
|
+
setToObservable(childNode, newValue);
|
|
1329
1491
|
}
|
|
1330
|
-
|
|
1331
|
-
|
|
1492
|
+
else {
|
|
1493
|
+
// Set the raw value on the parent object
|
|
1494
|
+
const { newValue: savedValue, prevValue, parentValue } = setNodeValue(childNode, newValue);
|
|
1495
|
+
const isFunc = isFunction(savedValue);
|
|
1496
|
+
const isPrim = isPrimitive(savedValue) || savedValue instanceof Date;
|
|
1497
|
+
if (savedValue !== prevValue) {
|
|
1498
|
+
updateNodesAndNotify(node, savedValue, prevValue, childNode, isPrim, isRoot, level);
|
|
1499
|
+
}
|
|
1500
|
+
extractFunctionOrComputed(node, parentValue, key, savedValue);
|
|
1501
|
+
if (isFunc) {
|
|
1502
|
+
return savedValue;
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
return isRoot ? getProxy(node) : getProxy(node, key);
|
|
1332
1506
|
}
|
|
1333
1507
|
function assign(node, value) {
|
|
1334
1508
|
const proxy = getProxy(node);
|
|
@@ -1433,7 +1607,7 @@ function handlerMapSet(node, p, value) {
|
|
|
1433
1607
|
function updateNodesAndNotify(node, newValue, prevValue, childNode, isPrim, isRoot, level) {
|
|
1434
1608
|
if (!childNode)
|
|
1435
1609
|
childNode = node;
|
|
1436
|
-
// Make sure we don't call too many listeners for
|
|
1610
|
+
// Make sure we don't call too many listeners for every property set
|
|
1437
1611
|
beginBatch();
|
|
1438
1612
|
let hasADiff = isPrim;
|
|
1439
1613
|
let whenOptimizedOnlyIf = false;
|
|
@@ -1454,7 +1628,7 @@ function updateNodesAndNotify(node, newValue, prevValue, childNode, isPrim, isRo
|
|
|
1454
1628
|
}
|
|
1455
1629
|
endBatch();
|
|
1456
1630
|
}
|
|
1457
|
-
function extractPromise(node, value) {
|
|
1631
|
+
function extractPromise(node, value, setter) {
|
|
1458
1632
|
if (!node.state) {
|
|
1459
1633
|
node.state = createObservable({
|
|
1460
1634
|
isLoaded: false,
|
|
@@ -1462,26 +1636,38 @@ function extractPromise(node, value) {
|
|
|
1462
1636
|
}
|
|
1463
1637
|
value
|
|
1464
1638
|
.then((value) => {
|
|
1465
|
-
set(node, value);
|
|
1466
|
-
node.state.
|
|
1639
|
+
setter ? setter({ value }) : set(node, value);
|
|
1640
|
+
node.state.assign({
|
|
1641
|
+
isLoaded: true,
|
|
1642
|
+
error: undefined,
|
|
1643
|
+
});
|
|
1467
1644
|
})
|
|
1468
1645
|
.catch((error) => {
|
|
1469
1646
|
node.state.error.set(error);
|
|
1470
1647
|
});
|
|
1471
1648
|
}
|
|
1472
1649
|
function extractFunctionOrComputed(node, obj, k, v) {
|
|
1650
|
+
var _a;
|
|
1473
1651
|
if (isPromise(v)) {
|
|
1474
|
-
|
|
1652
|
+
const childNode = getChildNode(node, k);
|
|
1653
|
+
extractPromise(childNode, v);
|
|
1654
|
+
setNodeValue(childNode, undefined);
|
|
1475
1655
|
}
|
|
1476
1656
|
else if (typeof v === 'function') {
|
|
1657
|
+
const childNode = (_a = node.children) === null || _a === void 0 ? void 0 : _a.get(k);
|
|
1477
1658
|
extractFunction(node, k, v);
|
|
1478
|
-
|
|
1659
|
+
// If child was previously activated, then peek the new linked observable to make sure it's activated
|
|
1660
|
+
if (childNode && !childNode.lazy) {
|
|
1661
|
+
if (isObservable(v)) {
|
|
1662
|
+
const vNode = getNode(v);
|
|
1663
|
+
peek(vNode);
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1479
1666
|
}
|
|
1480
1667
|
else if (typeof v == 'object' && v !== null && v !== undefined) {
|
|
1481
1668
|
const childNode = getNode(v);
|
|
1482
1669
|
if (childNode === null || childNode === void 0 ? void 0 : childNode.isComputed) {
|
|
1483
1670
|
extractFunction(node, k, v, childNode);
|
|
1484
|
-
delete obj[k];
|
|
1485
1671
|
}
|
|
1486
1672
|
else {
|
|
1487
1673
|
return true;
|
|
@@ -1495,20 +1681,29 @@ function get(node, options) {
|
|
|
1495
1681
|
return peek(node);
|
|
1496
1682
|
}
|
|
1497
1683
|
function peek(node) {
|
|
1498
|
-
|
|
1684
|
+
if (node.dirtyFn) {
|
|
1685
|
+
node.dirtyFn();
|
|
1686
|
+
globalState.dirtyNodes.delete(node);
|
|
1687
|
+
node.dirtyFn = undefined;
|
|
1688
|
+
}
|
|
1689
|
+
let value = getNodeValue(node);
|
|
1499
1690
|
// If node is not yet lazily computed go do that
|
|
1500
1691
|
const lazy = node.lazy;
|
|
1501
1692
|
if (lazy) {
|
|
1502
1693
|
delete node.lazy;
|
|
1503
1694
|
if (isFunction(node) || isFunction(lazy)) {
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
if (hasOwnProperty.call(value, key)) {
|
|
1509
|
-
extractFunctionOrComputed(node, value, key, value[key]);
|
|
1695
|
+
if (node.parent) {
|
|
1696
|
+
const parentValue = getNodeValue(node.parent);
|
|
1697
|
+
if (parentValue) {
|
|
1698
|
+
delete parentValue[node.key];
|
|
1510
1699
|
}
|
|
1511
1700
|
}
|
|
1701
|
+
value = activateNodeFunction(node, lazy);
|
|
1702
|
+
}
|
|
1703
|
+
for (const key in value) {
|
|
1704
|
+
if (hasOwnProperty.call(value, key)) {
|
|
1705
|
+
extractFunctionOrComputed(node, value, key, value[key]);
|
|
1706
|
+
}
|
|
1512
1707
|
}
|
|
1513
1708
|
}
|
|
1514
1709
|
// Check if computed needs to activate
|
|
@@ -1516,137 +1711,272 @@ function peek(node) {
|
|
|
1516
1711
|
return value;
|
|
1517
1712
|
}
|
|
1518
1713
|
function activateNodeFunction(node, lazyFn) {
|
|
1519
|
-
let
|
|
1714
|
+
// let prevTarget$: Observable<any>;
|
|
1715
|
+
// let curTarget$: Observable<any>;
|
|
1520
1716
|
let update;
|
|
1521
|
-
let subscriber;
|
|
1522
|
-
const lastSync = {};
|
|
1523
|
-
let cacheOptions;
|
|
1524
|
-
// The onSet function handles the observable being set
|
|
1525
|
-
// and forwards the set elsewhere
|
|
1526
|
-
const onSet = (setterParam) => {
|
|
1527
|
-
setter = setterParam;
|
|
1528
|
-
};
|
|
1529
|
-
// The onSet function handles the observable being set
|
|
1530
|
-
// and forwards the set elsewhere
|
|
1531
|
-
const updateLastSync = (fn) => {
|
|
1532
|
-
lastSync.value = fn;
|
|
1533
|
-
};
|
|
1534
|
-
// The subscribe function runs a function that listens to
|
|
1535
|
-
// a data source and sends updates into the observable
|
|
1536
|
-
const subscribe = (fn) => {
|
|
1537
|
-
if (!subscriber) {
|
|
1538
|
-
subscriber = fn;
|
|
1539
|
-
}
|
|
1540
|
-
};
|
|
1541
|
-
const cache = (fn) => {
|
|
1542
|
-
if (!cacheOptions) {
|
|
1543
|
-
cacheOptions = isFunction(fn) ? fn() : fn;
|
|
1544
|
-
}
|
|
1545
|
-
};
|
|
1546
|
-
// The proxy function simply marks the node as a proxy with this function
|
|
1547
|
-
// so that child nodes will be created with this function, and then simply
|
|
1548
|
-
// activated as a function
|
|
1549
|
-
const proxy = (fn) => {
|
|
1550
|
-
node.proxyFn2 = fn;
|
|
1551
|
-
};
|
|
1552
|
-
let prevTarget$;
|
|
1553
|
-
let curTarget$;
|
|
1554
|
-
const activator = (isFunction(node) ? node : lazyFn);
|
|
1555
1717
|
let wasPromise;
|
|
1556
|
-
|
|
1718
|
+
const activateFn = (isFunction(node) ? node : lazyFn);
|
|
1719
|
+
const doRetry = () => { var _a; return (_a = node.state) === null || _a === void 0 ? void 0 : _a.refreshNum.set((v) => v + 1); };
|
|
1720
|
+
let activatedValue;
|
|
1721
|
+
let disposes = [];
|
|
1722
|
+
let refreshFn;
|
|
1723
|
+
function markDirty() {
|
|
1724
|
+
node.dirtyFn = refreshFn;
|
|
1725
|
+
globalState.dirtyNodes.add(node);
|
|
1726
|
+
}
|
|
1557
1727
|
observe(() => {
|
|
1728
|
+
var _a, _b, _c, _d;
|
|
1729
|
+
// const params = createNodeActivationParams(node);
|
|
1558
1730
|
// Run the function at this node
|
|
1559
|
-
let value =
|
|
1560
|
-
// If target is an observable,
|
|
1561
|
-
// and set up an onSet to write changes back to it
|
|
1731
|
+
let value = activateFn();
|
|
1732
|
+
// If target is an observable, make this node a link to it
|
|
1562
1733
|
if (isObservable(value)) {
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
// small subset of cases?
|
|
1573
|
-
setNodeValue(getNode(curTarget$), getPrevious());
|
|
1574
|
-
// Set the value on the curTarget
|
|
1575
|
-
curTarget$.set(newValue);
|
|
1576
|
-
}
|
|
1577
|
-
});
|
|
1578
|
-
// Get the value from the observable because we still want the raw value
|
|
1579
|
-
// for the effect.
|
|
1580
|
-
value = value.get();
|
|
1734
|
+
value = setToObservable(node, value);
|
|
1735
|
+
}
|
|
1736
|
+
if (isFunction(value)) {
|
|
1737
|
+
value = value();
|
|
1738
|
+
}
|
|
1739
|
+
const activated = value === null || value === void 0 ? void 0 : value[symbolActivated];
|
|
1740
|
+
if (activated) {
|
|
1741
|
+
node.activationState = activated;
|
|
1742
|
+
value = undefined;
|
|
1581
1743
|
}
|
|
1582
|
-
wasPromise = isPromise(value)
|
|
1744
|
+
wasPromise = isPromise(value);
|
|
1745
|
+
// Activate this node if not activated already (may be called recursively)
|
|
1746
|
+
// TODO: Is calling recursively bad? If so can it be fixed?
|
|
1583
1747
|
if (!node.activated) {
|
|
1584
1748
|
node.activated = true;
|
|
1749
|
+
const isCached = !!((_a = node.activationState) === null || _a === void 0 ? void 0 : _a.cache);
|
|
1750
|
+
wasPromise = wasPromise || !!isCached;
|
|
1585
1751
|
const activateNodeFn = wasPromise ? globalState.activateNode : activateNodeBase;
|
|
1586
|
-
update = activateNodeFn(node,
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1752
|
+
const { update: newUpdate, value: newValue } = activateNodeFn(node, doRetry, !!wasPromise, value);
|
|
1753
|
+
update = newUpdate;
|
|
1754
|
+
value = newValue !== null && newValue !== void 0 ? newValue : activated === null || activated === void 0 ? void 0 : activated.initial;
|
|
1755
|
+
}
|
|
1756
|
+
else if (node.activationState) {
|
|
1757
|
+
if (!node.activationState.persistedRetry && !node.activationState.waitFor) {
|
|
1758
|
+
const activated = node.activationState;
|
|
1759
|
+
// TODO Should this have lastSync and value somehow?
|
|
1760
|
+
value =
|
|
1761
|
+
(_c = (_b = activated.get) === null || _b === void 0 ? void 0 : _b.call(activated, {
|
|
1762
|
+
updateLastSync: noop,
|
|
1763
|
+
setMode: noop,
|
|
1764
|
+
lastSync: undefined,
|
|
1765
|
+
value: undefined,
|
|
1766
|
+
})) !== null && _c !== void 0 ? _c : activated.initial;
|
|
1767
|
+
}
|
|
1590
1768
|
}
|
|
1769
|
+
wasPromise = wasPromise || isPromise(value);
|
|
1770
|
+
get(getNode((_d = node.state) === null || _d === void 0 ? void 0 : _d.refreshNum));
|
|
1591
1771
|
return value;
|
|
1592
|
-
}, (
|
|
1593
|
-
|
|
1772
|
+
}, (e) => {
|
|
1773
|
+
const { value, nodes, refresh } = e;
|
|
1774
|
+
refreshFn = refresh;
|
|
1775
|
+
if (!wasPromise || !globalState.isLoadingRemote$.peek()) {
|
|
1594
1776
|
if (wasPromise) {
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1777
|
+
if (node.activationState) {
|
|
1778
|
+
const { initial } = node.activationState;
|
|
1779
|
+
if (value && isPromise(value)) {
|
|
1780
|
+
// Extract the promise to make it set the value/error when it comes in
|
|
1781
|
+
extractPromise(node, value, update);
|
|
1782
|
+
}
|
|
1783
|
+
// Set this to undefined only if it's replacing the activation function,
|
|
1784
|
+
// so we don't overwrite it if it already has real data from either local
|
|
1785
|
+
// cache or a previous run
|
|
1786
|
+
if (isFunction(getNodeValue(node))) {
|
|
1787
|
+
setNodeValue(node, initial !== null && initial !== void 0 ? initial : undefined);
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
else if (node.activated) {
|
|
1791
|
+
// Extract the promise to make it set the value/error when it comes in
|
|
1792
|
+
extractPromise(node, value, update);
|
|
1793
|
+
// Set this to undefined only if it's replacing the activation function,
|
|
1794
|
+
// so we don't overwrite it if it already has real data from either local
|
|
1795
|
+
// cache or a previous run
|
|
1796
|
+
if (isFunction(getNodeValue(node))) {
|
|
1797
|
+
setNodeValue(node, undefined);
|
|
1798
|
+
}
|
|
1601
1799
|
}
|
|
1602
1800
|
}
|
|
1603
1801
|
else {
|
|
1802
|
+
activatedValue = value;
|
|
1604
1803
|
set(node, value);
|
|
1605
|
-
node.state.
|
|
1804
|
+
node.state.assign({
|
|
1805
|
+
isLoaded: true,
|
|
1806
|
+
error: undefined,
|
|
1807
|
+
});
|
|
1606
1808
|
}
|
|
1607
1809
|
}
|
|
1608
|
-
|
|
1609
|
-
|
|
1810
|
+
disposes.forEach((fn) => fn());
|
|
1811
|
+
disposes = [];
|
|
1812
|
+
nodes === null || nodes === void 0 ? void 0 : nodes.forEach(({ node }) => {
|
|
1813
|
+
disposes.push(onChange(node, markDirty, { immediate: true }));
|
|
1814
|
+
});
|
|
1815
|
+
e.cancel = true;
|
|
1816
|
+
}, { fromComputed: true });
|
|
1817
|
+
return activatedValue;
|
|
1610
1818
|
}
|
|
1611
|
-
const activateNodeBase = (globalState.activateNode = function activateNodeBase(node,
|
|
1612
|
-
let isSetting = false;
|
|
1819
|
+
const activateNodeBase = (globalState.activateNode = function activateNodeBase(node, refresh, wasPromise, value) {
|
|
1613
1820
|
if (!node.state) {
|
|
1614
1821
|
node.state = createObservable({
|
|
1615
1822
|
isLoaded: false,
|
|
1616
1823
|
}, false, extractPromise, getProxy);
|
|
1617
1824
|
}
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1825
|
+
let isSetting = false;
|
|
1826
|
+
let isSettingFromSubscribe = false;
|
|
1827
|
+
let _mode = 'set';
|
|
1828
|
+
if (node.activationState) {
|
|
1829
|
+
const { onSet, subscribe, get: getFn, initial } = node.activationState;
|
|
1830
|
+
value = getFn
|
|
1831
|
+
? runWithRetry(node, { attemptNum: 0 }, () => {
|
|
1832
|
+
return getFn({
|
|
1833
|
+
updateLastSync: noop,
|
|
1834
|
+
setMode: (mode) => (_mode = mode),
|
|
1835
|
+
lastSync: undefined,
|
|
1836
|
+
value: undefined,
|
|
1837
|
+
});
|
|
1838
|
+
})
|
|
1839
|
+
: undefined;
|
|
1840
|
+
// TODO Should this have lastSync and value somehow?
|
|
1841
|
+
if (value == undefined || value === null) {
|
|
1842
|
+
value = initial;
|
|
1843
|
+
}
|
|
1844
|
+
if (onSet) {
|
|
1845
|
+
let allChanges = [];
|
|
1846
|
+
let latestValue = undefined;
|
|
1847
|
+
let runNumber = 0;
|
|
1848
|
+
const runChanges = (listenerParams) => {
|
|
1849
|
+
// Don't call the set if this is the first value coming in
|
|
1850
|
+
if (allChanges.length > 0) {
|
|
1851
|
+
let changes;
|
|
1852
|
+
let value;
|
|
1853
|
+
let getPrevious;
|
|
1854
|
+
if (listenerParams) {
|
|
1855
|
+
changes = listenerParams.changes;
|
|
1856
|
+
value = listenerParams.value;
|
|
1857
|
+
getPrevious = listenerParams.getPrevious;
|
|
1858
|
+
}
|
|
1859
|
+
else {
|
|
1860
|
+
// If this is called by flushPending then get the change array
|
|
1861
|
+
// that we've been building up.
|
|
1862
|
+
changes = allChanges;
|
|
1863
|
+
value = latestValue;
|
|
1864
|
+
getPrevious = createPreviousHandler(value, changes);
|
|
1865
|
+
}
|
|
1866
|
+
allChanges = [];
|
|
1867
|
+
latestValue = undefined;
|
|
1868
|
+
globalState.pendingNodes.delete(node);
|
|
1869
|
+
runNumber++;
|
|
1870
|
+
const thisRunNumber = runNumber;
|
|
1871
|
+
const run = () => {
|
|
1872
|
+
if (thisRunNumber !== runNumber) {
|
|
1873
|
+
// set may get called multiple times before it loads so ignore any previous runs
|
|
1874
|
+
return;
|
|
1875
|
+
}
|
|
1876
|
+
return runWithRetry(node, { attemptNum: 0 }, () => {
|
|
1877
|
+
return new Promise((resolve, reject) => {
|
|
1878
|
+
isSetting = true;
|
|
1879
|
+
batch(() => onSet({
|
|
1880
|
+
value,
|
|
1881
|
+
changes,
|
|
1882
|
+
getPrevious,
|
|
1883
|
+
node,
|
|
1884
|
+
update,
|
|
1885
|
+
refresh,
|
|
1886
|
+
onError: reject,
|
|
1887
|
+
fromSubscribe: isSettingFromSubscribe,
|
|
1888
|
+
}), () => {
|
|
1889
|
+
isSetting = false;
|
|
1890
|
+
resolve();
|
|
1891
|
+
});
|
|
1892
|
+
});
|
|
1893
|
+
});
|
|
1894
|
+
};
|
|
1895
|
+
whenReady(node.state.isLoaded, run);
|
|
1627
1896
|
}
|
|
1628
|
-
}
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1897
|
+
};
|
|
1898
|
+
const onChangeImmediate = ({ value, changes }) => {
|
|
1899
|
+
if (!isSetting || isSettingFromSubscribe) {
|
|
1900
|
+
if (changes.length > 1 || !isFunction(changes[0].prevAtPath)) {
|
|
1901
|
+
latestValue = value;
|
|
1902
|
+
if (allChanges.length > 0) {
|
|
1903
|
+
changes = changes.filter((change) => !isArraySubset(allChanges[0].path, change.path));
|
|
1904
|
+
}
|
|
1905
|
+
allChanges.push(...changes);
|
|
1906
|
+
globalState.pendingNodes.set(node, runChanges);
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
};
|
|
1910
|
+
// Create an immediate listener to mark this node as pending. Then actually run
|
|
1911
|
+
// the changes at the end of the batch so everything is properly batched.
|
|
1912
|
+
// However, this can be short circuited if the user calls get() or peek()
|
|
1913
|
+
// in which case the set needs to run immediately so that the values are up to date.
|
|
1914
|
+
onChange(node, onChangeImmediate, { immediate: true });
|
|
1915
|
+
onChange(node, runChanges);
|
|
1916
|
+
}
|
|
1917
|
+
if (process.env.NODE_ENV === 'development' && node.activationState.cache) {
|
|
1918
|
+
// TODO Better message
|
|
1919
|
+
console.log('[legend-state] Using cache without setting up persistence first');
|
|
1920
|
+
}
|
|
1921
|
+
if (process.env.NODE_ENV === 'development' && node.activationState.retry) {
|
|
1922
|
+
// TODO Better message
|
|
1923
|
+
console.log('[legend-state] Using retry without setting up persistence first');
|
|
1924
|
+
}
|
|
1925
|
+
if (subscribe) {
|
|
1926
|
+
const updateFromSubscribe = (params) => {
|
|
1927
|
+
whenReady(node.state.isLoaded, () => {
|
|
1928
|
+
isSettingFromSubscribe = true;
|
|
1929
|
+
update(params);
|
|
1930
|
+
isSettingFromSubscribe = false;
|
|
1931
|
+
});
|
|
1932
|
+
};
|
|
1933
|
+
subscribe({ node, update: updateFromSubscribe, refresh });
|
|
1934
|
+
}
|
|
1635
1935
|
}
|
|
1636
|
-
const update = ({ value }) => {
|
|
1936
|
+
const update = ({ value, mode }) => {
|
|
1937
|
+
// TODO: This isSetting might not be necessary? Tests still work if removing it.
|
|
1938
|
+
// Write tests that would break it if removed? I'd guess a combination of subscribe and
|
|
1637
1939
|
if (!isSetting) {
|
|
1638
|
-
|
|
1940
|
+
isSetting = true;
|
|
1941
|
+
if (_mode === 'assign' || mode === 'assign') {
|
|
1942
|
+
assign(node, value);
|
|
1943
|
+
}
|
|
1944
|
+
else if (_mode === 'merge' || mode === 'merge') {
|
|
1945
|
+
mergeIntoObservable(getProxy(node), value);
|
|
1946
|
+
}
|
|
1947
|
+
else {
|
|
1948
|
+
set(node, value);
|
|
1949
|
+
}
|
|
1950
|
+
isSetting = false;
|
|
1639
1951
|
}
|
|
1640
1952
|
};
|
|
1641
|
-
|
|
1642
|
-
if (isProm) {
|
|
1643
|
-
extractPromise(node, newValue);
|
|
1644
|
-
}
|
|
1645
|
-
if (subscriber) {
|
|
1646
|
-
subscriber({ update });
|
|
1647
|
-
}
|
|
1648
|
-
return { update };
|
|
1953
|
+
return { update, value };
|
|
1649
1954
|
});
|
|
1955
|
+
function setToObservable(node, value) {
|
|
1956
|
+
// If the computed is a proxy to another observable
|
|
1957
|
+
// link it to the target observable
|
|
1958
|
+
const linkedNode = getNode(value);
|
|
1959
|
+
if (linkedNode !== node) {
|
|
1960
|
+
const prevNode = node.linkedToNode;
|
|
1961
|
+
node.linkedToNode = linkedNode;
|
|
1962
|
+
if (!linkedNode.linkedFromNodes) {
|
|
1963
|
+
linkedNode.linkedFromNodes = new Set();
|
|
1964
|
+
}
|
|
1965
|
+
linkedNode.linkedFromNodes.add(node);
|
|
1966
|
+
peek(linkedNode);
|
|
1967
|
+
onChange(linkedNode, ({ value: newValue }) => {
|
|
1968
|
+
value = newValue;
|
|
1969
|
+
set(node, value);
|
|
1970
|
+
}, { initial: true });
|
|
1971
|
+
// If the target observable is different then notify for the change
|
|
1972
|
+
if (prevNode) {
|
|
1973
|
+
const value = getNodeValue(linkedNode);
|
|
1974
|
+
const prevValue = getNodeValue(prevNode);
|
|
1975
|
+
notify(node, value, prevValue, 0);
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
return value;
|
|
1979
|
+
}
|
|
1650
1980
|
|
|
1651
1981
|
const fns = ['get', 'set', 'peek', 'onChange', 'toggle'];
|
|
1652
1982
|
function ObservablePrimitiveClass(node) {
|
|
@@ -1663,8 +1993,14 @@ function proto(key, fn) {
|
|
|
1663
1993
|
return fn.call(this, this._node, ...args);
|
|
1664
1994
|
};
|
|
1665
1995
|
}
|
|
1666
|
-
proto('peek',
|
|
1667
|
-
|
|
1996
|
+
proto('peek', (node) => {
|
|
1997
|
+
flushPending();
|
|
1998
|
+
return peek(node);
|
|
1999
|
+
});
|
|
2000
|
+
proto('get', (node, options) => {
|
|
2001
|
+
flushPending();
|
|
2002
|
+
return get(node, options);
|
|
2003
|
+
});
|
|
1668
2004
|
proto('set', set);
|
|
1669
2005
|
proto('onChange', onChange);
|
|
1670
2006
|
// Getters
|
|
@@ -1695,6 +2031,16 @@ function observable(value) {
|
|
|
1695
2031
|
function observablePrimitive(value) {
|
|
1696
2032
|
return createObservable(value, true, extractPromise, getProxy, ObservablePrimitiveClass);
|
|
1697
2033
|
}
|
|
2034
|
+
function syncState(obs) {
|
|
2035
|
+
const node = getNode(obs);
|
|
2036
|
+
if (!node.state) {
|
|
2037
|
+
peek(node);
|
|
2038
|
+
}
|
|
2039
|
+
if (!node.state) {
|
|
2040
|
+
node.state = observable({});
|
|
2041
|
+
}
|
|
2042
|
+
return node.state;
|
|
2043
|
+
}
|
|
1698
2044
|
globalState.isLoadingRemote$ = observable(false);
|
|
1699
2045
|
|
|
1700
2046
|
function computed(compute, set$1) {
|
|
@@ -1859,60 +2205,6 @@ function proxy(get, set) {
|
|
|
1859
2205
|
return obs;
|
|
1860
2206
|
}
|
|
1861
2207
|
|
|
1862
|
-
function _when(predicate, effect, checkReady) {
|
|
1863
|
-
// If predicate is a regular Promise skip all the observable stuff
|
|
1864
|
-
if (isPromise(predicate)) {
|
|
1865
|
-
return effect ? predicate.then(effect) : predicate;
|
|
1866
|
-
}
|
|
1867
|
-
let value;
|
|
1868
|
-
// Create a wrapping fn that calls the effect if predicate returns true
|
|
1869
|
-
function run(e) {
|
|
1870
|
-
const ret = computeSelector(predicate);
|
|
1871
|
-
if (!isPromise(ret) && (checkReady ? isObservableValueReady(ret) : ret)) {
|
|
1872
|
-
value = ret;
|
|
1873
|
-
// Set cancel so that observe does not track anymore
|
|
1874
|
-
e.cancel = true;
|
|
1875
|
-
}
|
|
1876
|
-
return value;
|
|
1877
|
-
}
|
|
1878
|
-
function doEffect() {
|
|
1879
|
-
// If value is truthy then run the effect
|
|
1880
|
-
effect === null || effect === void 0 ? void 0 : effect(value);
|
|
1881
|
-
}
|
|
1882
|
-
// Run in an observe
|
|
1883
|
-
observe(run, doEffect);
|
|
1884
|
-
// If first run resulted in a truthy value just return it.
|
|
1885
|
-
// It will have set e.cancel so no need to dispose
|
|
1886
|
-
if (isPromise(value)) {
|
|
1887
|
-
return effect ? value.then(effect) : value;
|
|
1888
|
-
}
|
|
1889
|
-
else if (value !== undefined) {
|
|
1890
|
-
return Promise.resolve(value);
|
|
1891
|
-
}
|
|
1892
|
-
else {
|
|
1893
|
-
// Wrap it in a promise
|
|
1894
|
-
const promise = new Promise((resolve) => {
|
|
1895
|
-
if (effect) {
|
|
1896
|
-
const originalEffect = effect;
|
|
1897
|
-
effect = (value) => {
|
|
1898
|
-
const effectValue = originalEffect(value);
|
|
1899
|
-
resolve(effectValue);
|
|
1900
|
-
};
|
|
1901
|
-
}
|
|
1902
|
-
else {
|
|
1903
|
-
effect = resolve;
|
|
1904
|
-
}
|
|
1905
|
-
});
|
|
1906
|
-
return promise;
|
|
1907
|
-
}
|
|
1908
|
-
}
|
|
1909
|
-
function when(predicate, effect) {
|
|
1910
|
-
return _when(predicate, effect, false);
|
|
1911
|
-
}
|
|
1912
|
-
function whenReady(predicate, effect) {
|
|
1913
|
-
return _when(predicate, effect, true);
|
|
1914
|
-
}
|
|
1915
|
-
|
|
1916
2208
|
const internal = {
|
|
1917
2209
|
ensureNodeValue,
|
|
1918
2210
|
findIDKey,
|
|
@@ -1923,13 +2215,16 @@ const internal = {
|
|
|
1923
2215
|
observableFns,
|
|
1924
2216
|
optimized,
|
|
1925
2217
|
peek,
|
|
2218
|
+
runWithRetry,
|
|
1926
2219
|
set,
|
|
1927
2220
|
setAtPath,
|
|
1928
2221
|
setNodeValue,
|
|
2222
|
+
symbolActivated,
|
|
1929
2223
|
symbolDelete,
|
|
1930
2224
|
};
|
|
1931
2225
|
|
|
1932
2226
|
exports.ObservablePrimitiveClass = ObservablePrimitiveClass;
|
|
2227
|
+
exports.activated = activated;
|
|
1933
2228
|
exports.batch = batch;
|
|
1934
2229
|
exports.beginBatch = beginBatch;
|
|
1935
2230
|
exports.beginTracking = beginTracking;
|
|
@@ -1974,6 +2269,7 @@ exports.setInObservableAtPath = setInObservableAtPath;
|
|
|
1974
2269
|
exports.setSilently = setSilently;
|
|
1975
2270
|
exports.setupTracking = setupTracking;
|
|
1976
2271
|
exports.symbolDelete = symbolDelete;
|
|
2272
|
+
exports.syncState = syncState;
|
|
1977
2273
|
exports.trackSelector = trackSelector;
|
|
1978
2274
|
exports.tracking = tracking;
|
|
1979
2275
|
exports.updateTracking = updateTracking;
|