@legendapp/state 3.0.0-alpha.1 → 3.0.0-alpha.3
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/.DS_Store +0 -0
- package/CHANGELOG.md +1 -831
- package/LICENSE +1 -21
- package/README.md +1 -141
- package/as/arrayAsRecord.d.mts +5 -0
- package/as/arrayAsRecord.d.ts +5 -0
- package/as/arrayAsRecord.js +28 -0
- package/as/arrayAsRecord.mjs +26 -0
- package/as/arrayAsSet.d.mts +5 -0
- package/as/arrayAsSet.d.ts +5 -0
- package/as/arrayAsSet.js +13 -0
- package/as/arrayAsSet.mjs +11 -0
- package/as/arrayAsString.d.mts +5 -0
- package/as/arrayAsString.d.ts +5 -0
- package/as/arrayAsString.js +13 -0
- package/as/arrayAsString.mjs +11 -0
- package/as/numberAsString.d.mts +5 -0
- package/as/numberAsString.d.ts +5 -0
- package/as/numberAsString.js +13 -0
- package/as/numberAsString.mjs +11 -0
- package/as/recordAsArray.d.mts +5 -0
- package/as/recordAsArray.d.ts +5 -0
- package/as/recordAsArray.js +25 -0
- package/as/recordAsArray.mjs +23 -0
- package/as/recordAsString.d.mts +5 -0
- package/as/recordAsString.d.ts +5 -0
- package/as/recordAsString.js +13 -0
- package/as/recordAsString.mjs +11 -0
- package/as/setAsArray.d.mts +5 -0
- package/as/setAsArray.d.ts +5 -0
- package/as/setAsArray.js +13 -0
- package/as/setAsArray.mjs +11 -0
- package/as/setAsString.d.mts +5 -0
- package/as/setAsString.d.ts +5 -0
- package/as/setAsString.js +13 -0
- package/as/setAsString.mjs +11 -0
- package/as/stringAsArray.d.mts +5 -0
- package/as/stringAsArray.d.ts +5 -0
- package/as/stringAsArray.js +13 -0
- package/as/stringAsArray.mjs +11 -0
- package/as/stringAsNumber.d.mts +5 -0
- package/as/stringAsNumber.d.ts +5 -0
- package/as/stringAsNumber.js +16 -0
- package/as/stringAsNumber.mjs +14 -0
- package/as/stringAsRecord.d.mts +5 -0
- package/as/stringAsRecord.d.ts +5 -0
- package/as/stringAsRecord.js +15 -0
- package/as/stringAsRecord.mjs +13 -0
- package/as/stringAsSet.d.mts +5 -0
- package/as/stringAsSet.d.ts +5 -0
- package/as/stringAsSet.js +13 -0
- package/as/stringAsSet.mjs +11 -0
- package/babel.d.mts +21 -0
- package/babel.d.ts +21 -2
- package/babel.js +57 -53
- package/babel.mjs +65 -0
- package/config/enable$GetSet.js +13 -14
- package/config/enable$GetSet.mjs +13 -14
- package/config/enableReactComponents.d.mts +9 -0
- package/config/enableReactComponents.d.ts +4 -2
- package/config/enableReactComponents.js +13 -10
- package/config/enableReactComponents.mjs +13 -10
- package/config/enableReactNativeComponents.d.mts +22 -0
- package/config/enableReactNativeComponents.d.ts +6 -4
- package/config/enableReactNativeComponents.js +43 -47
- package/config/enableReactNativeComponents.mjs +43 -47
- package/config/enableReactTracking.d.mts +7 -0
- package/config/enableReactTracking.d.ts +3 -2
- package/config/enableReactTracking.js +33 -38
- package/config/enableReactTracking.mjs +33 -38
- package/config/enableReactUse.d.mts +10 -0
- package/config/enableReactUse.d.ts +4 -1
- package/config/enableReactUse.js +15 -14
- package/config/enableReactUse.mjs +15 -14
- package/config/{enable$GetSet.d.ts → enable_GetSet.d.mts} +4 -2
- package/config/enable_GetSet.d.ts +10 -0
- package/config/enable_PeekAssign.d.mts +10 -0
- package/config/enable_PeekAssign.d.ts +4 -2
- package/config/enable_PeekAssign.js +13 -14
- package/config/enable_PeekAssign.mjs +13 -14
- package/helpers/pageHash.d.mts +9 -0
- package/helpers/pageHash.d.ts +2 -0
- package/helpers/pageHash.js +25 -30
- package/helpers/pageHash.mjs +25 -30
- package/helpers/pageHashParams.d.mts +9 -0
- package/helpers/pageHashParams.d.ts +2 -0
- package/helpers/pageHashParams.js +34 -37
- package/helpers/pageHashParams.mjs +34 -37
- package/helpers/time.d.mts +6 -0
- package/helpers/time.d.ts +6 -3
- package/helpers/time.js +17 -17
- package/helpers/time.mjs +17 -17
- package/helpers/trackHistory.d.mts +6 -0
- package/helpers/trackHistory.d.ts +4 -2
- package/helpers/trackHistory.js +13 -16
- package/helpers/trackHistory.mjs +13 -16
- package/helpers/undoRedo.d.mts +37 -0
- package/helpers/undoRedo.d.ts +5 -3
- package/helpers/undoRedo.js +59 -94
- package/helpers/undoRedo.mjs +59 -94
- package/index.d.mts +404 -0
- package/index.d.ts +371 -28
- package/index.js +2015 -2166
- package/index.mjs +2015 -2166
- package/package.json +254 -195
- package/persist-plugins/async-storage.d.mts +18 -0
- package/persist-plugins/async-storage.d.ts +6 -3
- package/persist-plugins/async-storage.js +79 -86
- package/persist-plugins/async-storage.mjs +79 -86
- package/persist-plugins/indexeddb.d.mts +29 -0
- package/persist-plugins/indexeddb.d.ts +6 -3
- package/persist-plugins/indexeddb.js +331 -352
- package/persist-plugins/indexeddb.mjs +331 -352
- package/persist-plugins/local-storage.d.mts +23 -0
- package/persist-plugins/local-storage.d.ts +8 -5
- package/persist-plugins/local-storage.js +74 -76
- package/persist-plugins/local-storage.mjs +74 -76
- package/persist-plugins/mmkv.d.mts +18 -0
- package/persist-plugins/mmkv.d.ts +6 -3
- package/persist-plugins/mmkv.js +82 -86
- package/persist-plugins/mmkv.mjs +82 -86
- package/react-hooks/createObservableHook.d.mts +5 -0
- package/react-hooks/createObservableHook.d.ts +4 -1
- package/react-hooks/createObservableHook.js +29 -30
- package/react-hooks/createObservableHook.mjs +25 -30
- package/react-hooks/useHover.d.mts +5 -0
- package/react-hooks/useHover.d.ts +5 -3
- package/react-hooks/useHover.js +29 -29
- package/react-hooks/useHover.mjs +29 -29
- package/react-hooks/useMeasure.d.mts +9 -0
- package/react-hooks/useMeasure.d.ts +5 -2
- package/react-hooks/useMeasure.js +30 -32
- package/react-hooks/useMeasure.mjs +30 -32
- package/react-hooks/useObservableNextRouter.d.mts +35 -0
- package/react-hooks/useObservableNextRouter.d.ts +9 -7
- package/react-hooks/useObservableNextRouter.js +64 -77
- package/react-hooks/useObservableNextRouter.mjs +60 -77
- package/react.d.mts +157 -0
- package/react.d.ts +157 -21
- package/react.js +458 -749
- package/react.mjs +457 -752
- package/sync-plugins/crud.d.mts +54 -0
- package/sync-plugins/crud.d.ts +12 -10
- package/sync-plugins/crud.js +253 -270
- package/sync-plugins/crud.mjs +253 -270
- package/sync-plugins/fetch.d.mts +21 -0
- package/sync-plugins/fetch.d.ts +7 -4
- package/sync-plugins/fetch.js +50 -37
- package/sync-plugins/fetch.mjs +50 -37
- package/sync-plugins/keel.d.mts +108 -0
- package/sync-plugins/keel.d.ts +17 -15
- package/sync-plugins/keel.js +229 -462
- package/sync-plugins/keel.mjs +227 -464
- package/sync-plugins/supabase.d.mts +39 -0
- package/sync-plugins/supabase.d.ts +16 -14
- package/sync-plugins/supabase.js +128 -128
- package/sync-plugins/supabase.mjs +128 -128
- package/sync-plugins/tanstack-query.d.mts +14 -0
- package/sync-plugins/tanstack-query.d.ts +7 -4
- package/sync-plugins/tanstack-query.js +51 -57
- package/sync-plugins/tanstack-query.mjs +51 -57
- package/sync-plugins/tanstack-react-query.d.mts +8 -0
- package/sync-plugins/tanstack-react-query.d.ts +6 -1
- package/sync-plugins/tanstack-react-query.js +2 -2
- package/sync-plugins/tanstack-react-query.mjs +2 -2
- package/sync.d.mts +351 -0
- package/sync.d.ts +349 -9
- package/sync.js +910 -964
- package/sync.mjs +920 -974
- package/trace.d.mts +9 -0
- package/trace.d.ts +9 -4
- package/trace.js +72 -62
- package/trace.mjs +72 -62
- package/types/babel.d.ts +1 -12
- package/babel.js.map +0 -1
- package/config/enable$GetSet.js.map +0 -1
- package/config/enable$GetSet.mjs.map +0 -1
- package/config/enableReactComponents.js.map +0 -1
- package/config/enableReactComponents.mjs.map +0 -1
- package/config/enableReactNativeComponents.js.map +0 -1
- package/config/enableReactNativeComponents.mjs.map +0 -1
- package/config/enableReactTracking.js.map +0 -1
- package/config/enableReactTracking.mjs.map +0 -1
- package/config/enableReactUse.js.map +0 -1
- package/config/enableReactUse.mjs.map +0 -1
- package/config/enable_PeekAssign.js.map +0 -1
- package/config/enable_PeekAssign.mjs.map +0 -1
- package/helpers/pageHash.js.map +0 -1
- package/helpers/pageHash.mjs.map +0 -1
- package/helpers/pageHashParams.js.map +0 -1
- package/helpers/pageHashParams.mjs.map +0 -1
- package/helpers/time.js.map +0 -1
- package/helpers/time.mjs.map +0 -1
- package/helpers/trackHistory.js.map +0 -1
- package/helpers/trackHistory.mjs.map +0 -1
- package/helpers/undoRedo.js.map +0 -1
- package/helpers/undoRedo.mjs.map +0 -1
- package/history.d.ts +0 -1
- package/history.js +0 -24
- package/history.js.map +0 -1
- package/history.mjs +0 -22
- package/history.mjs.map +0 -1
- package/index.js.map +0 -1
- package/index.mjs.map +0 -1
- package/persist-plugins/async-storage.js.map +0 -1
- package/persist-plugins/async-storage.mjs.map +0 -1
- package/persist-plugins/indexeddb.js.map +0 -1
- package/persist-plugins/indexeddb.mjs.map +0 -1
- package/persist-plugins/local-storage.js.map +0 -1
- package/persist-plugins/local-storage.mjs.map +0 -1
- package/persist-plugins/mmkv.js.map +0 -1
- package/persist-plugins/mmkv.mjs.map +0 -1
- package/react-hooks/createObservableHook.js.map +0 -1
- package/react-hooks/createObservableHook.mjs.map +0 -1
- package/react-hooks/useHover.js.map +0 -1
- package/react-hooks/useHover.mjs.map +0 -1
- package/react-hooks/useMeasure.js.map +0 -1
- package/react-hooks/useMeasure.mjs.map +0 -1
- package/react-hooks/useObservableNextRouter.js.map +0 -1
- package/react-hooks/useObservableNextRouter.mjs.map +0 -1
- package/react.js.map +0 -1
- package/react.mjs.map +0 -1
- package/src/ObservableObject.ts +0 -1350
- package/src/ObservablePrimitive.ts +0 -62
- package/src/babel/index.ts +0 -83
- package/src/batching.ts +0 -357
- package/src/computed.ts +0 -18
- package/src/config/enable$GetSet.ts +0 -30
- package/src/config/enableReactComponents.ts +0 -26
- package/src/config/enableReactNativeComponents.ts +0 -102
- package/src/config/enableReactTracking.ts +0 -62
- package/src/config/enableReactUse.ts +0 -32
- package/src/config/enable_PeekAssign.ts +0 -31
- package/src/config.ts +0 -47
- package/src/createObservable.ts +0 -47
- package/src/event.ts +0 -26
- package/src/globals.ts +0 -235
- package/src/helpers/pageHash.ts +0 -41
- package/src/helpers/pageHashParams.ts +0 -55
- package/src/helpers/time.ts +0 -30
- package/src/helpers/trackHistory.ts +0 -29
- package/src/helpers/undoRedo.ts +0 -111
- package/src/helpers.ts +0 -231
- package/src/is.ts +0 -63
- package/src/linked.ts +0 -17
- package/src/observable.ts +0 -32
- package/src/observableInterfaces.ts +0 -151
- package/src/observableTypes.ts +0 -232
- package/src/observe.ts +0 -89
- package/src/old-plugins/firebase.ts +0 -1053
- package/src/onChange.ts +0 -146
- package/src/persist/configureObservablePersistence.ts +0 -7
- package/src/persist/fieldTransformer.ts +0 -149
- package/src/persist/observablePersistRemoteFunctionsAdapter.ts +0 -39
- package/src/persist/persistObservable.ts +0 -1034
- package/src/persist-plugins/async-storage.ts +0 -99
- package/src/persist-plugins/indexeddb.ts +0 -439
- package/src/persist-plugins/local-storage.ts +0 -86
- package/src/persist-plugins/mmkv.ts +0 -91
- package/src/proxy.ts +0 -28
- package/src/react/Computed.tsx +0 -8
- package/src/react/For.tsx +0 -116
- package/src/react/Memo.tsx +0 -4
- package/src/react/Reactive.tsx +0 -53
- package/src/react/Show.tsx +0 -33
- package/src/react/Switch.tsx +0 -43
- package/src/react/react-globals.ts +0 -3
- package/src/react/reactInterfaces.ts +0 -32
- package/src/react/reactive-observer.tsx +0 -210
- package/src/react/useComputed.ts +0 -36
- package/src/react/useEffectOnce.ts +0 -41
- package/src/react/useIsMounted.ts +0 -16
- package/src/react/useMount.ts +0 -15
- package/src/react/useObservable.ts +0 -24
- package/src/react/useObservableReducer.ts +0 -52
- package/src/react/useObservableState.ts +0 -30
- package/src/react/useObserve.ts +0 -54
- package/src/react/useObserveEffect.ts +0 -40
- package/src/react/usePauseProvider.tsx +0 -16
- package/src/react/useSelector.ts +0 -167
- package/src/react/useUnmount.ts +0 -8
- package/src/react/useWhen.ts +0 -9
- package/src/react-hooks/createObservableHook.ts +0 -53
- package/src/react-hooks/useHover.ts +0 -40
- package/src/react-hooks/useMeasure.ts +0 -48
- package/src/react-hooks/useObservableNextRouter.ts +0 -137
- package/src/retry.ts +0 -71
- package/src/setupTracking.ts +0 -26
- package/src/sync/activateSyncedNode.ts +0 -128
- package/src/sync/configureObservableSync.ts +0 -7
- package/src/sync/persistTypes.ts +0 -216
- package/src/sync/syncHelpers.ts +0 -180
- package/src/sync/syncObservable.ts +0 -1056
- package/src/sync/syncObservableAdapter.ts +0 -31
- package/src/sync/syncTypes.ts +0 -189
- package/src/sync/synced.ts +0 -21
- package/src/sync-plugins/crud.ts +0 -412
- package/src/sync-plugins/fetch.ts +0 -80
- package/src/sync-plugins/keel.ts +0 -495
- package/src/sync-plugins/supabase.ts +0 -249
- package/src/sync-plugins/tanstack-query.ts +0 -113
- package/src/sync-plugins/tanstack-react-query.ts +0 -12
- package/src/trace/traceHelpers.ts +0 -11
- package/src/trace/useTraceListeners.ts +0 -34
- package/src/trace/useTraceUpdates.ts +0 -24
- package/src/trace/useVerifyNotTracking.ts +0 -33
- package/src/trace/useVerifyOneRender.ts +0 -10
- package/src/trackSelector.ts +0 -52
- package/src/tracking.ts +0 -43
- package/src/types/babel.d.ts +0 -12
- package/src/when.ts +0 -75
- package/sync-plugins/crud.js.map +0 -1
- package/sync-plugins/crud.mjs.map +0 -1
- package/sync-plugins/fetch.js.map +0 -1
- package/sync-plugins/fetch.mjs.map +0 -1
- package/sync-plugins/keel.js.map +0 -1
- package/sync-plugins/keel.mjs.map +0 -1
- package/sync-plugins/supabase.js.map +0 -1
- package/sync-plugins/supabase.mjs.map +0 -1
- package/sync-plugins/tanstack-query.js.map +0 -1
- package/sync-plugins/tanstack-query.mjs.map +0 -1
- package/sync-plugins/tanstack-react-query.js.map +0 -1
- package/sync-plugins/tanstack-react-query.mjs.map +0 -1
- package/sync.js.map +0 -1
- package/sync.mjs.map +0 -1
- package/trace.js.map +0 -1
- package/trace.mjs.map +0 -1
package/src/ObservableObject.ts
DELETED
|
@@ -1,1350 +0,0 @@
|
|
|
1
|
-
import { beginBatch, createPreviousHandler, endBatch, isArraySubset, notify } from './batching';
|
|
2
|
-
import { createObservable } from './createObservable';
|
|
3
|
-
import {
|
|
4
|
-
equals,
|
|
5
|
-
extractFunction,
|
|
6
|
-
findIDKey,
|
|
7
|
-
getChildNode,
|
|
8
|
-
getNode,
|
|
9
|
-
getNodeValue,
|
|
10
|
-
globalState,
|
|
11
|
-
isObservable,
|
|
12
|
-
optimized,
|
|
13
|
-
setNodeValue,
|
|
14
|
-
symbolDelete,
|
|
15
|
-
symbolGetNode,
|
|
16
|
-
symbolLinked,
|
|
17
|
-
symbolOpaque,
|
|
18
|
-
symbolToPrimitive,
|
|
19
|
-
} from './globals';
|
|
20
|
-
import {
|
|
21
|
-
hasOwnProperty,
|
|
22
|
-
isArray,
|
|
23
|
-
isBoolean,
|
|
24
|
-
isChildNodeValue,
|
|
25
|
-
isEmpty,
|
|
26
|
-
isFunction,
|
|
27
|
-
isMap,
|
|
28
|
-
isNullOrUndefined,
|
|
29
|
-
isObject,
|
|
30
|
-
isPrimitive,
|
|
31
|
-
isPromise,
|
|
32
|
-
} from './is';
|
|
33
|
-
import { linked } from './linked';
|
|
34
|
-
import type {
|
|
35
|
-
Change,
|
|
36
|
-
ChildNodeValue,
|
|
37
|
-
GetOptions,
|
|
38
|
-
LinkedOptions,
|
|
39
|
-
ListenerParams,
|
|
40
|
-
NodeValue,
|
|
41
|
-
TrackingType,
|
|
42
|
-
UpdateFn,
|
|
43
|
-
} from './observableInterfaces';
|
|
44
|
-
import { Observable, ObservableState } from './observableTypes';
|
|
45
|
-
import { observe } from './observe';
|
|
46
|
-
import { onChange } from './onChange';
|
|
47
|
-
import { updateTracking } from './tracking';
|
|
48
|
-
import { whenReady } from './when';
|
|
49
|
-
|
|
50
|
-
const ArrayModifiers = new Set([
|
|
51
|
-
'copyWithin',
|
|
52
|
-
'fill',
|
|
53
|
-
'from',
|
|
54
|
-
'pop',
|
|
55
|
-
'push',
|
|
56
|
-
'reverse',
|
|
57
|
-
'shift',
|
|
58
|
-
'sort',
|
|
59
|
-
'splice',
|
|
60
|
-
'unshift',
|
|
61
|
-
]);
|
|
62
|
-
const ArrayLoopers = new Set<keyof Array<any>>([
|
|
63
|
-
'every',
|
|
64
|
-
'filter',
|
|
65
|
-
'find',
|
|
66
|
-
'findIndex',
|
|
67
|
-
'forEach',
|
|
68
|
-
'join',
|
|
69
|
-
'map',
|
|
70
|
-
'reduce',
|
|
71
|
-
'some',
|
|
72
|
-
]);
|
|
73
|
-
const ArrayLoopersReturn = new Set<keyof Array<any>>(['filter', 'find']);
|
|
74
|
-
export const observableProperties = new Map<
|
|
75
|
-
string,
|
|
76
|
-
{ get: (node: NodeValue, ...args: any[]) => any; set: (node: NodeValue, value: any) => any }
|
|
77
|
-
>();
|
|
78
|
-
export const observableFns = new Map<string, (node: NodeValue, ...args: any[]) => any>([
|
|
79
|
-
['get', get],
|
|
80
|
-
['set', set],
|
|
81
|
-
['peek', peek],
|
|
82
|
-
['onChange', onChange],
|
|
83
|
-
['assign', assign],
|
|
84
|
-
['delete', deleteFn],
|
|
85
|
-
['toggle', toggle],
|
|
86
|
-
]);
|
|
87
|
-
|
|
88
|
-
if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
|
|
89
|
-
// eslint-disable-next-line no-var
|
|
90
|
-
var __devUpdateNodes = new Set();
|
|
91
|
-
}
|
|
92
|
-
function collectionSetter(node: NodeValue, target: any[], prop: keyof Array<any>, ...args: any[]) {
|
|
93
|
-
if (prop === 'push' && args.length === 1) {
|
|
94
|
-
// Fast path for push to just append to the end
|
|
95
|
-
setKey(node, target.length + '', args[0]);
|
|
96
|
-
} else {
|
|
97
|
-
const prevValue = target.slice();
|
|
98
|
-
|
|
99
|
-
const ret = (target[prop] as Function).apply(target, args);
|
|
100
|
-
|
|
101
|
-
if (node) {
|
|
102
|
-
const hasParent = isChildNodeValue(node);
|
|
103
|
-
const key: string = hasParent ? node.key : '_';
|
|
104
|
-
const parentValue = hasParent ? getNodeValue(node.parent) : node.root;
|
|
105
|
-
|
|
106
|
-
// Set the object to the previous value first
|
|
107
|
-
parentValue[key] = prevValue;
|
|
108
|
-
|
|
109
|
-
// Then set with the new value so it notifies with the correct prevValue
|
|
110
|
-
setKey(node.parent ?? node, key, target);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Return the original value
|
|
114
|
-
return ret;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function getKeys(obj: Record<any, any> | Array<any> | undefined, isArr: boolean, isMap: boolean): string[] {
|
|
119
|
-
return isArr ? (undefined as any) : obj ? (isMap ? Array.from(obj.keys()) : Object.keys(obj)) : [];
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function updateNodes(parent: NodeValue, obj: Record<any, any> | Array<any> | undefined, prevValue: any): boolean {
|
|
123
|
-
if (
|
|
124
|
-
(process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') &&
|
|
125
|
-
typeof __devUpdateNodes !== 'undefined' &&
|
|
126
|
-
isObject(obj)
|
|
127
|
-
) {
|
|
128
|
-
if (__devUpdateNodes.has(obj)) {
|
|
129
|
-
console.error(
|
|
130
|
-
'[legend-state] Circular reference detected in object. You may want to use opaqueObject to stop traversing child nodes.',
|
|
131
|
-
obj,
|
|
132
|
-
);
|
|
133
|
-
return false;
|
|
134
|
-
}
|
|
135
|
-
__devUpdateNodes.add(obj);
|
|
136
|
-
}
|
|
137
|
-
if (
|
|
138
|
-
(isObject(obj) && (obj as Record<any, any>)[symbolOpaque as any]) ||
|
|
139
|
-
(isObject(prevValue) && prevValue[symbolOpaque as any])
|
|
140
|
-
) {
|
|
141
|
-
const isDiff = obj !== prevValue;
|
|
142
|
-
if (isDiff) {
|
|
143
|
-
if (parent.listeners || parent.listenersImmediate) {
|
|
144
|
-
notify(parent, obj, prevValue, 0);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
if (
|
|
148
|
-
(process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') &&
|
|
149
|
-
typeof __devUpdateNodes !== 'undefined' &&
|
|
150
|
-
obj !== undefined
|
|
151
|
-
) {
|
|
152
|
-
__devUpdateNodes.delete(obj);
|
|
153
|
-
}
|
|
154
|
-
return isDiff;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const isArr = isArray(obj);
|
|
158
|
-
|
|
159
|
-
let prevChildrenById: Map<string, ChildNodeValue> | undefined;
|
|
160
|
-
let moved: [string, ChildNodeValue][] | undefined;
|
|
161
|
-
|
|
162
|
-
const isCurMap = isMap(obj);
|
|
163
|
-
const isPrevMap = isMap(prevValue);
|
|
164
|
-
|
|
165
|
-
const keys = getKeys(obj, isArr, isCurMap);
|
|
166
|
-
const keysPrev = getKeys(prevValue, isArr, isPrevMap);
|
|
167
|
-
const length = (keys || obj)?.length || 0;
|
|
168
|
-
const lengthPrev = (keysPrev || prevValue)?.length || 0;
|
|
169
|
-
|
|
170
|
-
let idField: string | ((value: any) => string) | undefined;
|
|
171
|
-
let isIdFieldFunction;
|
|
172
|
-
let hasADiff = false;
|
|
173
|
-
let retValue: boolean | undefined;
|
|
174
|
-
|
|
175
|
-
if (isArr && isArray(prevValue)) {
|
|
176
|
-
// Construct a map of previous indices for computing move
|
|
177
|
-
if (prevValue.length > 0) {
|
|
178
|
-
const firstPrevValue = prevValue[0];
|
|
179
|
-
if (firstPrevValue !== undefined) {
|
|
180
|
-
idField = findIDKey(firstPrevValue, parent);
|
|
181
|
-
|
|
182
|
-
if (idField) {
|
|
183
|
-
isIdFieldFunction = isFunction(idField);
|
|
184
|
-
prevChildrenById = new Map();
|
|
185
|
-
moved = [];
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
const keysSeen: Set<string> =
|
|
189
|
-
process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test'
|
|
190
|
-
? new Set()
|
|
191
|
-
: (undefined as unknown as Set<string>);
|
|
192
|
-
if (parent.children) {
|
|
193
|
-
for (let i = 0; i < prevValue.length; i++) {
|
|
194
|
-
const p = prevValue[i];
|
|
195
|
-
if (p) {
|
|
196
|
-
const child = parent.children.get(i + '');
|
|
197
|
-
if (child) {
|
|
198
|
-
if (!obj[i]) {
|
|
199
|
-
// If the previous value is not in the new array and it
|
|
200
|
-
// is an activated, disable its listeners
|
|
201
|
-
handleDeletedChild(child, p);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
if (idField) {
|
|
205
|
-
const key = isIdFieldFunction
|
|
206
|
-
? (idField as (value: any) => string)(p)
|
|
207
|
-
: p[idField as string];
|
|
208
|
-
|
|
209
|
-
if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
|
|
210
|
-
if (keysSeen.has(key)) {
|
|
211
|
-
console.warn(
|
|
212
|
-
`[legend-state] Warning: Multiple elements in array have the same ID. Key field: ${idField}, Array:`,
|
|
213
|
-
prevValue,
|
|
214
|
-
);
|
|
215
|
-
}
|
|
216
|
-
keysSeen.add(key);
|
|
217
|
-
}
|
|
218
|
-
prevChildrenById!.set(key, child);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
} else if (prevValue && (!obj || isObject(obj))) {
|
|
227
|
-
// For keys that have been removed from object, notify and update children recursively
|
|
228
|
-
const lengthPrev = keysPrev.length;
|
|
229
|
-
for (let i = 0; i < lengthPrev; i++) {
|
|
230
|
-
const key = keysPrev[i];
|
|
231
|
-
if (!keys.includes(key)) {
|
|
232
|
-
hasADiff = true;
|
|
233
|
-
const child = getChildNode(parent, key);
|
|
234
|
-
|
|
235
|
-
const prev = isPrevMap ? prevValue.get(key) : prevValue[key];
|
|
236
|
-
if (prev !== undefined) {
|
|
237
|
-
handleDeletedChild(child, prev);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
if (obj && !isPrimitive(obj)) {
|
|
244
|
-
hasADiff = hasADiff || length !== lengthPrev;
|
|
245
|
-
const isArrDiff = hasADiff;
|
|
246
|
-
let didMove = false;
|
|
247
|
-
|
|
248
|
-
for (let i = 0; i < length; i++) {
|
|
249
|
-
const key = isArr ? i + '' : keys[i];
|
|
250
|
-
let value = isCurMap ? obj.get(key) : (obj as any)[key];
|
|
251
|
-
const prev = isPrevMap ? prevValue?.get(key) : prevValue?.[key];
|
|
252
|
-
|
|
253
|
-
let isDiff = !equals(value, prev);
|
|
254
|
-
if (isDiff) {
|
|
255
|
-
const id =
|
|
256
|
-
idField && value
|
|
257
|
-
? isIdFieldFunction
|
|
258
|
-
? (idField as (value: any) => string)(value)
|
|
259
|
-
: value[idField as string]
|
|
260
|
-
: undefined;
|
|
261
|
-
|
|
262
|
-
const existingChild = parent.children?.get(key);
|
|
263
|
-
|
|
264
|
-
if (isObservable(value)) {
|
|
265
|
-
const valueNode = getNode(value);
|
|
266
|
-
if (existingChild?.linkedToNode === valueNode) {
|
|
267
|
-
const targetValue = getNodeValue(valueNode);
|
|
268
|
-
isCurMap ? obj.set(key, targetValue) : ((obj as any)[key] = targetValue);
|
|
269
|
-
continue;
|
|
270
|
-
}
|
|
271
|
-
const obs = value;
|
|
272
|
-
value = () => obs;
|
|
273
|
-
}
|
|
274
|
-
let child = getChildNode(parent, key, value);
|
|
275
|
-
|
|
276
|
-
if (!child.lazy && (isFunction(value) || isObservable(value))) {
|
|
277
|
-
reactivateNode(child, value);
|
|
278
|
-
peekInternal(child);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Detect moves within an array. Need to move the original proxy to the new position to keep
|
|
282
|
-
// the proxy stable, so that listeners to this node will be unaffected by the array shift.
|
|
283
|
-
if (isArr && id !== undefined) {
|
|
284
|
-
// Find the previous position of this element in the array
|
|
285
|
-
const prevChild = id !== undefined ? prevChildrenById?.get(id) : undefined;
|
|
286
|
-
if (!prevChild) {
|
|
287
|
-
// This id was not in the array before so it does not need to notify children
|
|
288
|
-
// It does need to notify itself so isDiff should remain.
|
|
289
|
-
hasADiff = true;
|
|
290
|
-
} else if (prevChild !== undefined && prevChild.key !== key) {
|
|
291
|
-
const valuePrevChild = prevValue[prevChild.key];
|
|
292
|
-
// If array length changed then move the original node to the current position.
|
|
293
|
-
// That should be faster than notifying every single element that
|
|
294
|
-
// it's in a new position.
|
|
295
|
-
if (isArrDiff) {
|
|
296
|
-
child = prevChild;
|
|
297
|
-
parent.children!.delete(child.key);
|
|
298
|
-
child.key = key;
|
|
299
|
-
moved!.push([key, child]);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
didMove = true;
|
|
303
|
-
|
|
304
|
-
// And check for diff against the previous value in the previous position
|
|
305
|
-
isDiff = valuePrevChild !== value;
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
if (isDiff) {
|
|
310
|
-
// Array has a new / modified element
|
|
311
|
-
// If object iterate through its children
|
|
312
|
-
if (isFunction(value) || isObservable(value)) {
|
|
313
|
-
extractFunctionOrComputed(parent, key, value);
|
|
314
|
-
} else if (isPrimitive(value)) {
|
|
315
|
-
hasADiff = true;
|
|
316
|
-
} else {
|
|
317
|
-
// Always need to updateNodes so we notify through all children
|
|
318
|
-
const updatedNodes = updateNodes(child, value, prev);
|
|
319
|
-
hasADiff = hasADiff || updatedNodes;
|
|
320
|
-
isDiff = updatedNodes;
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
if (isDiff || (isArr && !isArrDiff)) {
|
|
324
|
-
// Notify for this child if this element is different and it has listeners
|
|
325
|
-
// Or if the position changed in an array whose length did not change
|
|
326
|
-
// But do not notify child if the parent is an array with changing length -
|
|
327
|
-
// the array's listener will cover it
|
|
328
|
-
if (child.listeners || child.listenersImmediate) {
|
|
329
|
-
notify(child, value, prev, 0, !isArrDiff);
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
if (moved) {
|
|
336
|
-
for (let i = 0; i < moved.length; i++) {
|
|
337
|
-
const [key, child] = moved[i];
|
|
338
|
-
parent.children!.set(key, child);
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// The full array does not need to re-render if the length is the same
|
|
343
|
-
// So don't notify shallow listeners
|
|
344
|
-
retValue = hasADiff || didMove;
|
|
345
|
-
} else if (prevValue !== undefined) {
|
|
346
|
-
// If value got set to undefined, it has a diff
|
|
347
|
-
retValue = true;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
if (
|
|
351
|
-
(process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') &&
|
|
352
|
-
typeof __devUpdateNodes !== 'undefined' &&
|
|
353
|
-
obj !== undefined
|
|
354
|
-
) {
|
|
355
|
-
__devUpdateNodes.delete(obj);
|
|
356
|
-
}
|
|
357
|
-
return retValue ?? false;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
function handleDeletedChild(child: NodeValue, p: any) {
|
|
361
|
-
// If the previous value is not in the new array and it
|
|
362
|
-
// is an activated, disable its listeners
|
|
363
|
-
child.linkedToNodeDispose?.();
|
|
364
|
-
child.activatedObserveDispose?.();
|
|
365
|
-
|
|
366
|
-
if (!isPrimitive(p)) {
|
|
367
|
-
updateNodes(child, undefined, p);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
if (child.listeners || child.listenersImmediate) {
|
|
371
|
-
notify(child, undefined, p, 0);
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
export function getProxy(node: NodeValue, p?: string, asFunction?: Function): Observable {
|
|
376
|
-
// Get the child node if p prop
|
|
377
|
-
if (p !== undefined) node = getChildNode(node, p, asFunction);
|
|
378
|
-
|
|
379
|
-
// Create a proxy if not already cached and return it
|
|
380
|
-
return (node.proxy || (node.proxy = new Proxy<NodeValue>(node, proxyHandler))) as Observable<any>;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
export function flushPending() {
|
|
384
|
-
// Need to short circuit the computed batching because the user called get() or peek()
|
|
385
|
-
// in which case the set needs to run immediately so that the values are up to date.
|
|
386
|
-
if (globalState.pendingNodes.size > 0) {
|
|
387
|
-
const nodes = Array.from(globalState.pendingNodes.values());
|
|
388
|
-
globalState.pendingNodes.clear();
|
|
389
|
-
nodes.forEach((fn) => fn());
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
const proxyHandler: ProxyHandler<any> = {
|
|
394
|
-
get(node: NodeValue, p: any, receiver: any) {
|
|
395
|
-
if (p === symbolToPrimitive) {
|
|
396
|
-
throw new Error(
|
|
397
|
-
process.env.NODE_ENV === 'development'
|
|
398
|
-
? '[legend-state] observable should not be used as a primitive. You may have forgotten to use .get() or .peek() to get the value of the observable.'
|
|
399
|
-
: '[legend-state] observable is not a primitive.',
|
|
400
|
-
);
|
|
401
|
-
}
|
|
402
|
-
if (p === symbolGetNode) {
|
|
403
|
-
return node;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
if (p === 'apply') {
|
|
407
|
-
const nodeValue = getNodeValue(node);
|
|
408
|
-
if (isFunction(nodeValue)) {
|
|
409
|
-
return nodeValue.apply;
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
let value = peekInternal(node, /*activateRecursive*/ p === 'get' || p === 'peek');
|
|
414
|
-
|
|
415
|
-
// If this node is linked to another observable then forward to the target's handler.
|
|
416
|
-
// The exception is onChange because it needs to listen to this node for changes.
|
|
417
|
-
// This needs to be below peek because it activates there.
|
|
418
|
-
const targetNode = node.linkedToNode || value?.[symbolGetNode];
|
|
419
|
-
if (targetNode && p !== 'onChange') {
|
|
420
|
-
return proxyHandler.get!(targetNode, p, receiver);
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
if (isMap(value) || value instanceof WeakMap || value instanceof Set || value instanceof WeakSet) {
|
|
424
|
-
const ret = handlerMapSet(node, p, value);
|
|
425
|
-
if (ret !== undefined) {
|
|
426
|
-
return ret;
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
const fn = observableFns.get(p);
|
|
431
|
-
// If this is an observable function, call it
|
|
432
|
-
if (fn) {
|
|
433
|
-
if (p === 'get' || p === 'peek') {
|
|
434
|
-
flushPending();
|
|
435
|
-
}
|
|
436
|
-
return function (a: any, b: any, c: any) {
|
|
437
|
-
const l = arguments.length;
|
|
438
|
-
|
|
439
|
-
// Array call and apply are slow so micro-optimize this hot path.
|
|
440
|
-
// The observable functions depends on the number of arguments so we have to
|
|
441
|
-
// call it with the correct arguments, not just undefined
|
|
442
|
-
switch (l) {
|
|
443
|
-
case 0:
|
|
444
|
-
return fn(node);
|
|
445
|
-
case 1:
|
|
446
|
-
return fn(node, a);
|
|
447
|
-
case 2:
|
|
448
|
-
return fn(node, a, b);
|
|
449
|
-
default:
|
|
450
|
-
return fn(node, a, b, c);
|
|
451
|
-
}
|
|
452
|
-
};
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
const property = observableProperties.get(p);
|
|
456
|
-
if (property) {
|
|
457
|
-
return property.get(node);
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
let vProp = value?.[p];
|
|
461
|
-
|
|
462
|
-
if (isObject(value) && value[symbolOpaque as any]) {
|
|
463
|
-
return vProp;
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
const fnOrComputed = node.functions?.get(p);
|
|
467
|
-
if (fnOrComputed) {
|
|
468
|
-
if (isObservable(fnOrComputed)) {
|
|
469
|
-
return fnOrComputed;
|
|
470
|
-
} else {
|
|
471
|
-
return getProxy(node, p, fnOrComputed as Function);
|
|
472
|
-
}
|
|
473
|
-
} else {
|
|
474
|
-
vProp = checkProperty(value, p);
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
if (isNullOrUndefined(value) && vProp === undefined && (ArrayModifiers.has(p) || ArrayLoopers.has(p))) {
|
|
478
|
-
value = [];
|
|
479
|
-
setNodeValue(node, value);
|
|
480
|
-
vProp = value[p];
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
// Handle function calls
|
|
484
|
-
if (isFunction(vProp)) {
|
|
485
|
-
if (isArray(value)) {
|
|
486
|
-
if (ArrayModifiers.has(p)) {
|
|
487
|
-
// Call the wrapped modifier function
|
|
488
|
-
return (...args: any[]) => collectionSetter(node, value, p, ...args);
|
|
489
|
-
} else if (ArrayLoopers.has(p)) {
|
|
490
|
-
// Update that this node was accessed for observers
|
|
491
|
-
updateTracking(node, true);
|
|
492
|
-
|
|
493
|
-
return function (cbOrig: any, thisArg: any) {
|
|
494
|
-
const isReduce = p === 'reduce';
|
|
495
|
-
// Callbacks are given the Proxy rather than the underlying data
|
|
496
|
-
const cbWrapped = isReduce
|
|
497
|
-
? (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => {
|
|
498
|
-
return cbOrig(
|
|
499
|
-
previousValue,
|
|
500
|
-
getProxy(node, currentIndex + '', currentValue),
|
|
501
|
-
currentIndex,
|
|
502
|
-
array,
|
|
503
|
-
);
|
|
504
|
-
}
|
|
505
|
-
: (val: any, index: number, array: any[]) => {
|
|
506
|
-
return cbOrig(getProxy(node, index + '', val), index, array);
|
|
507
|
-
};
|
|
508
|
-
|
|
509
|
-
if (isReduce || !ArrayLoopersReturn.has(p)) {
|
|
510
|
-
return value[p](cbWrapped, thisArg);
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
const isFind = p === 'find';
|
|
514
|
-
const out = [];
|
|
515
|
-
for (let i = 0; i < value.length; i++) {
|
|
516
|
-
type LooperType = Parameters<typeof Array.prototype.map>[0];
|
|
517
|
-
if ((cbWrapped as LooperType)(value[i], i, value)) {
|
|
518
|
-
const proxy = getProxy(node, i + '');
|
|
519
|
-
// find returns the first match, otherwise it returns an array
|
|
520
|
-
if (isFind) {
|
|
521
|
-
return proxy;
|
|
522
|
-
}
|
|
523
|
-
out.push(proxy);
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
return isFind ? undefined : out;
|
|
527
|
-
};
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
extractFunctionOrComputed(node, p, vProp);
|
|
532
|
-
|
|
533
|
-
const fnOrComputed2 = node.functions?.get(p);
|
|
534
|
-
if (fnOrComputed2) {
|
|
535
|
-
return getProxy(node, p, fnOrComputed2 as Function);
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
// Return the function bound to the value
|
|
539
|
-
return vProp.bind(value);
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
// Accessing primitive returns the raw value
|
|
543
|
-
if (isPrimitive(vProp)) {
|
|
544
|
-
// Update that this primitive node was accessed for observers
|
|
545
|
-
if (isArray(value) && p === 'length') {
|
|
546
|
-
updateTracking(node, true);
|
|
547
|
-
return vProp;
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
// Return an observable proxy to the property
|
|
552
|
-
return getProxy(node, p);
|
|
553
|
-
},
|
|
554
|
-
// Forward all proxy properties to the target's value
|
|
555
|
-
getPrototypeOf(node: NodeValue) {
|
|
556
|
-
const value = getNodeValue(node);
|
|
557
|
-
return value !== null && typeof value === 'object' ? Reflect.getPrototypeOf(value) : null;
|
|
558
|
-
},
|
|
559
|
-
ownKeys(node: NodeValue) {
|
|
560
|
-
// TODO: Temporary workaround to fix a bug - the first peek may not return the correct value
|
|
561
|
-
// if the value is a cached. This fixes the test "cache with initial ownKeys"
|
|
562
|
-
peekInternal(node);
|
|
563
|
-
|
|
564
|
-
const value = get(node, true);
|
|
565
|
-
if (isPrimitive(value)) return [];
|
|
566
|
-
|
|
567
|
-
const keys = value ? Reflect.ownKeys(value) : [];
|
|
568
|
-
|
|
569
|
-
// This is required to fix this error:
|
|
570
|
-
// TypeError: 'getOwnPropertyDescriptor' on proxy: trap reported non-configurability for
|
|
571
|
-
// property 'length' which is either non-existent or configurable in the proxy node
|
|
572
|
-
if (isArray(value) && keys[keys.length - 1] === 'length') {
|
|
573
|
-
keys.splice(keys.length - 1, 1);
|
|
574
|
-
}
|
|
575
|
-
if (isFunction(node)) {
|
|
576
|
-
const reflectedKeys = Reflect.ownKeys(node);
|
|
577
|
-
['caller', 'arguments', 'prototype'].forEach((key) => reflectedKeys.includes(key) && keys.push(key));
|
|
578
|
-
}
|
|
579
|
-
return keys;
|
|
580
|
-
},
|
|
581
|
-
getOwnPropertyDescriptor(node: NodeValue, prop: string) {
|
|
582
|
-
if (prop === 'caller' || prop === 'arguments' || prop === 'prototype') {
|
|
583
|
-
return { configurable: false, enumerable: false };
|
|
584
|
-
}
|
|
585
|
-
const value = getNodeValue(node);
|
|
586
|
-
return isPrimitive(value) ? undefined : Reflect.getOwnPropertyDescriptor(value, prop);
|
|
587
|
-
},
|
|
588
|
-
set(node: NodeValue, prop: string, value: any) {
|
|
589
|
-
// If this assignment comes from within an observable function it's allowed
|
|
590
|
-
if (node.isSetting) {
|
|
591
|
-
return Reflect.set(node, prop, value);
|
|
592
|
-
}
|
|
593
|
-
if (node.isAssigning) {
|
|
594
|
-
setKey(node, prop, value);
|
|
595
|
-
return true;
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
const property = observableProperties.get(prop);
|
|
599
|
-
if (property) {
|
|
600
|
-
property.set(node, value);
|
|
601
|
-
return true;
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
if (process.env.NODE_ENV === 'development') {
|
|
605
|
-
console.warn('[legend-state]: Error: Cannot set a value directly:', prop, value);
|
|
606
|
-
}
|
|
607
|
-
return false;
|
|
608
|
-
},
|
|
609
|
-
deleteProperty(node: NodeValue, prop: string) {
|
|
610
|
-
// If this delete comes from within an observable function it's allowed
|
|
611
|
-
if (node.isSetting) {
|
|
612
|
-
return Reflect.deleteProperty(node, prop);
|
|
613
|
-
} else {
|
|
614
|
-
if (process.env.NODE_ENV === 'development') {
|
|
615
|
-
console.warn('[legend-state]: Error: Cannot delete a value directly:', prop);
|
|
616
|
-
}
|
|
617
|
-
return false;
|
|
618
|
-
}
|
|
619
|
-
},
|
|
620
|
-
has(node: NodeValue, prop: string) {
|
|
621
|
-
const value = getNodeValue(node);
|
|
622
|
-
return Reflect.has(value, prop);
|
|
623
|
-
},
|
|
624
|
-
apply(target, thisArg, argArray) {
|
|
625
|
-
// If it's a function call it as a function
|
|
626
|
-
if (isObservable(thisArg)) {
|
|
627
|
-
thisArg = thisArg.peek();
|
|
628
|
-
}
|
|
629
|
-
return Reflect.apply(target.lazyFn || target, thisArg, argArray);
|
|
630
|
-
},
|
|
631
|
-
};
|
|
632
|
-
|
|
633
|
-
export function set(node: NodeValue, newValue?: any) {
|
|
634
|
-
if (node.parent) {
|
|
635
|
-
setKey(node.parent, node.key, newValue);
|
|
636
|
-
} else {
|
|
637
|
-
setKey(node, '_', newValue);
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
function toggle(node: NodeValue) {
|
|
641
|
-
const value = getNodeValue(node);
|
|
642
|
-
if (value === undefined || value === null || isBoolean(value)) {
|
|
643
|
-
set(node, !value);
|
|
644
|
-
} else if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
|
|
645
|
-
throw new Error('[legend-state] Cannot toggle a non-boolean value');
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
function setKey(node: NodeValue, key: string, newValue?: any, level?: number) {
|
|
650
|
-
if (process.env.NODE_ENV === 'development') {
|
|
651
|
-
if (typeof HTMLElement !== 'undefined' && newValue instanceof HTMLElement) {
|
|
652
|
-
console.warn(`[legend-state] Set an HTMLElement into state. You probably don't want to do that.`);
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
const isRoot = !node.parent && key === '_';
|
|
657
|
-
|
|
658
|
-
if (node.parent && !getNodeValue(node) && !isFunction(newValue)) {
|
|
659
|
-
set(node, { [key]: newValue });
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
// Get the child node for updating and notifying
|
|
663
|
-
const childNode: NodeValue = isRoot ? node : getChildNode(node, key, newValue);
|
|
664
|
-
|
|
665
|
-
if (isObservable(newValue)) {
|
|
666
|
-
setToObservable(childNode, newValue);
|
|
667
|
-
} else {
|
|
668
|
-
// Set the raw value on the parent object
|
|
669
|
-
const { newValue: savedValue, prevValue } = setNodeValue(childNode, newValue);
|
|
670
|
-
|
|
671
|
-
const isPrim = isPrimitive(savedValue) || savedValue instanceof Date;
|
|
672
|
-
|
|
673
|
-
if (!equals(savedValue, prevValue)) {
|
|
674
|
-
updateNodesAndNotify(node, savedValue, prevValue, childNode, isPrim, isRoot, level);
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
if (!isPrim) {
|
|
678
|
-
childNode.needsExtract = true;
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
extractFunctionOrComputed(node, key, savedValue);
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
function assign(node: NodeValue, value: any) {
|
|
686
|
-
const proxy = getProxy(node);
|
|
687
|
-
|
|
688
|
-
beginBatch();
|
|
689
|
-
|
|
690
|
-
if (isPrimitive(node.root._)) {
|
|
691
|
-
node.root._ = {};
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
if (isMap(value)) {
|
|
695
|
-
const currentValue = getNodeValue(node);
|
|
696
|
-
if (isMap(currentValue)) {
|
|
697
|
-
value.forEach((value, key) => currentValue.set(key, value));
|
|
698
|
-
}
|
|
699
|
-
} else {
|
|
700
|
-
// Set inAssign to allow setting on safe observables
|
|
701
|
-
node.isAssigning = (node.isAssigning || 0) + 1;
|
|
702
|
-
try {
|
|
703
|
-
// TODO: If current value is a Map how to assign into the Map?
|
|
704
|
-
Object.assign(proxy, value);
|
|
705
|
-
} finally {
|
|
706
|
-
node.isAssigning--;
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
endBatch();
|
|
711
|
-
|
|
712
|
-
return proxy;
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
function deleteFn(node: NodeValue, key?: string) {
|
|
716
|
-
// If called without a key, delete by key from the parent node
|
|
717
|
-
if (key === undefined && isChildNodeValue(node)) {
|
|
718
|
-
key = node.key;
|
|
719
|
-
node = node.parent;
|
|
720
|
-
}
|
|
721
|
-
const value = getNodeValue(node);
|
|
722
|
-
if (isArray(value)) {
|
|
723
|
-
collectionSetter(node, value, 'splice', key!, 1);
|
|
724
|
-
} else {
|
|
725
|
-
setKey(node, key ?? '_', symbolDelete, /*level*/ -1);
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
function handlerMapSet(node: NodeValue, p: any, value: Map<any, any> | WeakMap<any, any> | Set<any> | WeakSet<any>) {
|
|
730
|
-
const vProp = (value as any)?.[p];
|
|
731
|
-
if (p === 'size') {
|
|
732
|
-
return getProxy(node, p);
|
|
733
|
-
} else if (isFunction(vProp)) {
|
|
734
|
-
return function (a: any, b: any, c: any) {
|
|
735
|
-
const l = arguments.length;
|
|
736
|
-
const valueMap = value as Map<any, any>;
|
|
737
|
-
|
|
738
|
-
if (p === 'get') {
|
|
739
|
-
if (l > 0 && typeof a !== 'boolean' && a !== optimized) {
|
|
740
|
-
return getProxy(node, a);
|
|
741
|
-
}
|
|
742
|
-
} else if (p === 'set') {
|
|
743
|
-
if (l === 2) {
|
|
744
|
-
const prev = valueMap.get(a);
|
|
745
|
-
const ret = valueMap.set(a, b);
|
|
746
|
-
if (prev !== b) {
|
|
747
|
-
updateNodesAndNotify(getChildNode(node, a), b, prev);
|
|
748
|
-
}
|
|
749
|
-
return ret;
|
|
750
|
-
} else if (l === 1 && isMap(value)) {
|
|
751
|
-
set(node, a);
|
|
752
|
-
}
|
|
753
|
-
} else if (p === 'delete') {
|
|
754
|
-
if (l > 0) {
|
|
755
|
-
// Support Set by just returning a if it doesn't have get, meaning it's not a Map
|
|
756
|
-
const prev = (value as Map<any, any>).get ? valueMap.get(a) : a;
|
|
757
|
-
const ret = value.delete(a);
|
|
758
|
-
if (ret) {
|
|
759
|
-
updateNodesAndNotify(getChildNode(node, a), undefined, prev);
|
|
760
|
-
}
|
|
761
|
-
return ret;
|
|
762
|
-
}
|
|
763
|
-
} else if (p === 'clear') {
|
|
764
|
-
const prev = new Map(valueMap);
|
|
765
|
-
const size = valueMap.size;
|
|
766
|
-
valueMap.clear();
|
|
767
|
-
if (size) {
|
|
768
|
-
updateNodesAndNotify(node, value, prev);
|
|
769
|
-
}
|
|
770
|
-
return;
|
|
771
|
-
} else if (p === 'add') {
|
|
772
|
-
const prev = new Set(value as unknown as Set<any>);
|
|
773
|
-
const ret = (value as unknown as Set<any>).add(a);
|
|
774
|
-
if (!(value as unknown as Set<any>).has(p)) {
|
|
775
|
-
notify(node, ret, prev, 0);
|
|
776
|
-
}
|
|
777
|
-
return ret;
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
// TODO: This is duplicated from proxy handler, how to dedupe with best performance?
|
|
781
|
-
const fn = observableFns.get(p);
|
|
782
|
-
if (fn) {
|
|
783
|
-
// Array call and apply are slow so micro-optimize this hot path.
|
|
784
|
-
// The observable functions depends on the number of arguments so we have to
|
|
785
|
-
// call it with the correct arguments, not just undefined
|
|
786
|
-
switch (l) {
|
|
787
|
-
case 0:
|
|
788
|
-
return fn(node);
|
|
789
|
-
case 1:
|
|
790
|
-
return fn(node, a);
|
|
791
|
-
case 2:
|
|
792
|
-
return fn(node, a, b);
|
|
793
|
-
default:
|
|
794
|
-
return fn(node, a, b, c);
|
|
795
|
-
}
|
|
796
|
-
} else {
|
|
797
|
-
return (value as any)[p](a, b);
|
|
798
|
-
}
|
|
799
|
-
};
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
function updateNodesAndNotify(
|
|
804
|
-
node: NodeValue,
|
|
805
|
-
newValue: any,
|
|
806
|
-
prevValue: any,
|
|
807
|
-
childNode?: NodeValue,
|
|
808
|
-
isPrim?: boolean,
|
|
809
|
-
isRoot?: boolean,
|
|
810
|
-
level?: number,
|
|
811
|
-
) {
|
|
812
|
-
if (!childNode) childNode = node;
|
|
813
|
-
// Make sure we don't call too many listeners for every property set
|
|
814
|
-
beginBatch();
|
|
815
|
-
|
|
816
|
-
if (isPrim === undefined) {
|
|
817
|
-
isPrim = isPrimitive(newValue);
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
let hasADiff = isPrim;
|
|
821
|
-
let whenOptimizedOnlyIf = false;
|
|
822
|
-
// If new value is an object or array update notify down the tree
|
|
823
|
-
if (!isPrim || (prevValue && !isPrimitive(prevValue))) {
|
|
824
|
-
if (
|
|
825
|
-
(process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') &&
|
|
826
|
-
typeof __devUpdateNodes !== 'undefined'
|
|
827
|
-
) {
|
|
828
|
-
__devUpdateNodes.clear();
|
|
829
|
-
}
|
|
830
|
-
hasADiff = updateNodes(childNode, newValue, prevValue);
|
|
831
|
-
if (isArray(newValue)) {
|
|
832
|
-
whenOptimizedOnlyIf = newValue?.length !== prevValue?.length;
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
if (isPrim || !newValue || (isEmpty(newValue) && !isEmpty(prevValue)) ? newValue !== prevValue : hasADiff) {
|
|
837
|
-
// Notify for this element if something inside it has changed
|
|
838
|
-
notify(
|
|
839
|
-
isPrim && isRoot ? node : childNode,
|
|
840
|
-
newValue,
|
|
841
|
-
prevValue,
|
|
842
|
-
level ?? prevValue === undefined ? -1 : hasADiff ? 0 : 1,
|
|
843
|
-
whenOptimizedOnlyIf,
|
|
844
|
-
);
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
endBatch();
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
export function extractPromise(node: NodeValue, value: Promise<any>, setter?: (params: { value: any }) => void) {
|
|
851
|
-
if (!node.state) {
|
|
852
|
-
node.state = createObservable<ObservableState>(
|
|
853
|
-
{
|
|
854
|
-
isLoaded: false,
|
|
855
|
-
} as ObservableState,
|
|
856
|
-
false,
|
|
857
|
-
extractPromise,
|
|
858
|
-
getProxy,
|
|
859
|
-
) as any;
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
value
|
|
863
|
-
.then((value) => {
|
|
864
|
-
setter ? setter({ value }) : set(node, value);
|
|
865
|
-
node.state!.assign({
|
|
866
|
-
isLoaded: true,
|
|
867
|
-
error: undefined,
|
|
868
|
-
});
|
|
869
|
-
})
|
|
870
|
-
.catch((error) => {
|
|
871
|
-
node.state!.error.set(error);
|
|
872
|
-
});
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
function extractFunctionOrComputed(node: NodeValue, k: string, v: any) {
|
|
876
|
-
// We want to extract these types of values from the observable's raw value so that
|
|
877
|
-
// the raw value does not contain Promises, Observables or functions
|
|
878
|
-
if (isPromise(v)) {
|
|
879
|
-
const childNode = getChildNode(node, k);
|
|
880
|
-
extractPromise(childNode, v);
|
|
881
|
-
setNodeValue(childNode, undefined);
|
|
882
|
-
return undefined;
|
|
883
|
-
} else if (isObservable(v)) {
|
|
884
|
-
const fn = () => v;
|
|
885
|
-
extractFunction(node, k, fn);
|
|
886
|
-
const childNode = getChildNode(node, k, fn);
|
|
887
|
-
const targetNode = getNode(v);
|
|
888
|
-
// Set node to target's value if activating or it's already activated
|
|
889
|
-
const initialValue = peek(targetNode);
|
|
890
|
-
setNodeValue(childNode, initialValue);
|
|
891
|
-
return getNodeValue(childNode);
|
|
892
|
-
} else if (typeof v === 'function') {
|
|
893
|
-
extractFunction(node, k, v);
|
|
894
|
-
return k;
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
export function get(node: NodeValue, options?: TrackingType | GetOptions) {
|
|
899
|
-
const track = options ? (isObject(options) ? (options.shallow as TrackingType) : options) : undefined;
|
|
900
|
-
// Track by default
|
|
901
|
-
updateTracking(node, track);
|
|
902
|
-
|
|
903
|
-
return peek(node);
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
export function peek(node: NodeValue) {
|
|
907
|
-
return peekInternal(node, true);
|
|
908
|
-
}
|
|
909
|
-
|
|
910
|
-
let isFlushing = false;
|
|
911
|
-
|
|
912
|
-
export function peekInternal(node: NodeValue, activateRecursive?: boolean) {
|
|
913
|
-
isFlushing = true;
|
|
914
|
-
// Need to refresh all dirty children when getting
|
|
915
|
-
if (activateRecursive && node.dirtyChildren?.size) {
|
|
916
|
-
const dirty = Array.from(node.dirtyChildren);
|
|
917
|
-
node.dirtyChildren.clear();
|
|
918
|
-
dirty.forEach((node) => node.dirtyFn && peekInternal(node));
|
|
919
|
-
}
|
|
920
|
-
if (node.dirtyFn) {
|
|
921
|
-
const dirtyFn = node.dirtyFn;
|
|
922
|
-
node.dirtyFn = undefined;
|
|
923
|
-
globalState.dirtyNodes.delete(node);
|
|
924
|
-
dirtyFn();
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
isFlushing = false;
|
|
928
|
-
|
|
929
|
-
let value = getNodeValue(node);
|
|
930
|
-
value = checkLazy(node, value, !!activateRecursive);
|
|
931
|
-
|
|
932
|
-
return value;
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
function checkLazy(node: NodeValue, value: any, activateRecursive: boolean) {
|
|
936
|
-
const origValue = value;
|
|
937
|
-
// If node is not yet lazily computed go do that
|
|
938
|
-
const lazy = node.lazy;
|
|
939
|
-
if (lazy) {
|
|
940
|
-
const lazyFn = node.lazyFn!;
|
|
941
|
-
delete node.lazy;
|
|
942
|
-
if (isFunction(lazyFn)) {
|
|
943
|
-
if (lazyFn.length === 1) {
|
|
944
|
-
// This is a lookup function, so return a record object
|
|
945
|
-
value = {};
|
|
946
|
-
} else {
|
|
947
|
-
if (node.parent) {
|
|
948
|
-
const parentValue = getNodeValue(node.parent);
|
|
949
|
-
if (parentValue) {
|
|
950
|
-
delete parentValue[node.key];
|
|
951
|
-
} else {
|
|
952
|
-
node.root._ = undefined;
|
|
953
|
-
}
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
value = activateNodeFunction(node as any, lazyFn);
|
|
957
|
-
}
|
|
958
|
-
} else if (isObservable(value)) {
|
|
959
|
-
value = extractFunctionOrComputed(node.parent!, node.key!, value);
|
|
960
|
-
}
|
|
961
|
-
}
|
|
962
|
-
|
|
963
|
-
if ((lazy || node.needsExtract) && !isObservable(value) && !isPrimitive(value)) {
|
|
964
|
-
// If this is a purposeful get, check descendants for observable or auto activated linked
|
|
965
|
-
if (activateRecursive) {
|
|
966
|
-
recursivelyAutoActivate(value, node);
|
|
967
|
-
}
|
|
968
|
-
// If this is an extractable node, extract it from parent before it's accessed
|
|
969
|
-
if (node.parent) {
|
|
970
|
-
extractFunctionOrComputed(node.parent!, node.key!, origValue);
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
|
|
974
|
-
return value;
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
function checkProperty(value: any, key: string) {
|
|
978
|
-
if (value) {
|
|
979
|
-
const property = Object.getOwnPropertyDescriptor(value, key);
|
|
980
|
-
if (property?.get) {
|
|
981
|
-
delete value[key];
|
|
982
|
-
value[key] = property.set
|
|
983
|
-
? linked({
|
|
984
|
-
get: property.get,
|
|
985
|
-
set: ({ value }) => property.set!(value),
|
|
986
|
-
})
|
|
987
|
-
: property.get;
|
|
988
|
-
}
|
|
989
|
-
return value[key];
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
|
|
993
|
-
function reactivateNode(node: NodeValue, lazyFn: Function) {
|
|
994
|
-
node.activatedObserveDispose?.();
|
|
995
|
-
node.linkedToNodeDispose?.();
|
|
996
|
-
node.activatedObserveDispose = node.linkedToNodeDispose = node.linkedToNode = undefined;
|
|
997
|
-
node.lazyFn = lazyFn;
|
|
998
|
-
node.lazy = true;
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
export function isObserved(node: NodeValue) {
|
|
1002
|
-
let parent = node;
|
|
1003
|
-
let hasListeners = node.numListenersRecursive > 0;
|
|
1004
|
-
while (parent && !hasListeners) {
|
|
1005
|
-
if (!!parent.listeners?.size || !!parent.listenersImmediate?.size) {
|
|
1006
|
-
hasListeners = true;
|
|
1007
|
-
}
|
|
1008
|
-
parent = parent.parent!;
|
|
1009
|
-
}
|
|
1010
|
-
return hasListeners;
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
export function shouldIgnoreUnobserved(node: NodeValue, refreshFn: () => void) {
|
|
1014
|
-
if (!isFlushing) {
|
|
1015
|
-
const hasListeners = isObserved(node);
|
|
1016
|
-
if (!hasListeners) {
|
|
1017
|
-
if (refreshFn) {
|
|
1018
|
-
node.dirtyFn = refreshFn;
|
|
1019
|
-
}
|
|
1020
|
-
let parent = node;
|
|
1021
|
-
while (parent) {
|
|
1022
|
-
if (!parent.dirtyChildren) {
|
|
1023
|
-
parent.dirtyChildren = new Set();
|
|
1024
|
-
}
|
|
1025
|
-
parent.dirtyChildren.add(node);
|
|
1026
|
-
|
|
1027
|
-
parent = parent.parent!;
|
|
1028
|
-
}
|
|
1029
|
-
return true;
|
|
1030
|
-
}
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
function activateNodeFunction(node: NodeValue, lazyFn: Function) {
|
|
1035
|
-
// let prevTarget$: Observable<any>;
|
|
1036
|
-
// let curTarget$: Observable<any>;
|
|
1037
|
-
let update: UpdateFn;
|
|
1038
|
-
let wasPromise: boolean | undefined;
|
|
1039
|
-
let ignoreThisUpdate: boolean | undefined;
|
|
1040
|
-
let isFirst = true;
|
|
1041
|
-
const activateFn = lazyFn;
|
|
1042
|
-
let activatedValue;
|
|
1043
|
-
let disposes: (() => void)[] = [];
|
|
1044
|
-
let refreshFn: () => void;
|
|
1045
|
-
function markDirty() {
|
|
1046
|
-
node.dirtyFn = refreshFn;
|
|
1047
|
-
globalState.dirtyNodes.add(node);
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
node.activatedObserveDispose = observe(
|
|
1051
|
-
() => {
|
|
1052
|
-
// Set it to undefined so that the activation function gets undefined
|
|
1053
|
-
// if it peeks itself
|
|
1054
|
-
if (isFirst) {
|
|
1055
|
-
isFirst = false;
|
|
1056
|
-
setNodeValue(node, undefined);
|
|
1057
|
-
} else if (!isFlushing && refreshFn) {
|
|
1058
|
-
if (shouldIgnoreUnobserved(node, refreshFn)) {
|
|
1059
|
-
ignoreThisUpdate = true;
|
|
1060
|
-
return;
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
// Run the function at this node
|
|
1064
|
-
let value = activateFn();
|
|
1065
|
-
|
|
1066
|
-
let didSetToObs = false;
|
|
1067
|
-
// If target is an observable, make this node a link to it
|
|
1068
|
-
if (isObservable(value)) {
|
|
1069
|
-
didSetToObs = true;
|
|
1070
|
-
value = setToObservable(node, value);
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
if (isFunction(value)) {
|
|
1074
|
-
value = value();
|
|
1075
|
-
}
|
|
1076
|
-
const activated = !isObservable(value)
|
|
1077
|
-
? (value?.[symbolLinked] as LinkedOptions & { synced: boolean })
|
|
1078
|
-
: undefined;
|
|
1079
|
-
if (activated) {
|
|
1080
|
-
node.activationState = activated;
|
|
1081
|
-
value = undefined;
|
|
1082
|
-
}
|
|
1083
|
-
ignoreThisUpdate = false;
|
|
1084
|
-
wasPromise = isPromise(value);
|
|
1085
|
-
|
|
1086
|
-
// Activate this node if not activated already (may be called recursively)
|
|
1087
|
-
// TODO: Is calling recursively bad? If so can it be fixed?
|
|
1088
|
-
if (!node.activated) {
|
|
1089
|
-
node.activated = true;
|
|
1090
|
-
let activateNodeFn = activateNodeBase;
|
|
1091
|
-
// If this is a Synced then run it through persistence instead of base
|
|
1092
|
-
if (activated?.synced) {
|
|
1093
|
-
activateNodeFn = globalState.activateSyncedNode;
|
|
1094
|
-
ignoreThisUpdate = true;
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
const result = activateNodeFn(node, value);
|
|
1098
|
-
update = result.update;
|
|
1099
|
-
let newValue = result.value;
|
|
1100
|
-
if (!didSetToObs && isObservable(newValue)) {
|
|
1101
|
-
newValue = setToObservable(node, newValue);
|
|
1102
|
-
}
|
|
1103
|
-
value = newValue ?? activated?.initial;
|
|
1104
|
-
} else if (node.activationState) {
|
|
1105
|
-
const activated = node.activationState! as LinkedOptions;
|
|
1106
|
-
if (node.state?.peek()?.sync) {
|
|
1107
|
-
node.state.sync();
|
|
1108
|
-
ignoreThisUpdate = true;
|
|
1109
|
-
} else {
|
|
1110
|
-
value = activated.get?.() ?? activated.initial;
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
// value is undefined if it's in a persisted retry
|
|
1114
|
-
wasPromise = wasPromise || isPromise(value);
|
|
1115
|
-
|
|
1116
|
-
return value;
|
|
1117
|
-
},
|
|
1118
|
-
(e) => {
|
|
1119
|
-
const { value, nodes, refresh } = e;
|
|
1120
|
-
refreshFn = refresh;
|
|
1121
|
-
if (!ignoreThisUpdate) {
|
|
1122
|
-
if (!wasPromise || !globalState.isLoadingRemote) {
|
|
1123
|
-
if (wasPromise) {
|
|
1124
|
-
if (node.activationState) {
|
|
1125
|
-
const { initial } = node.activationState!;
|
|
1126
|
-
|
|
1127
|
-
if (value && isPromise(value)) {
|
|
1128
|
-
// Extract the promise to make it set the value/error when it comes in
|
|
1129
|
-
extractPromise(node, value, update);
|
|
1130
|
-
}
|
|
1131
|
-
// Set this to undefined only if it's replacing the activation function,
|
|
1132
|
-
// so we don't overwrite it if it already has real data from either local
|
|
1133
|
-
// cache or a previous run
|
|
1134
|
-
if (isFunction(getNodeValue(node))) {
|
|
1135
|
-
setNodeValue(node, initial ?? undefined);
|
|
1136
|
-
}
|
|
1137
|
-
} else if (node.activated) {
|
|
1138
|
-
// Extract the promise to make it set the value/error when it comes in
|
|
1139
|
-
extractPromise(node, value, update);
|
|
1140
|
-
// Set this to undefined only if it's replacing the activation function,
|
|
1141
|
-
// so we don't overwrite it if it already has real data from either local
|
|
1142
|
-
// cache or a previous run
|
|
1143
|
-
if (isFunction(getNodeValue(node))) {
|
|
1144
|
-
setNodeValue(node, undefined);
|
|
1145
|
-
}
|
|
1146
|
-
}
|
|
1147
|
-
} else {
|
|
1148
|
-
activatedValue = value;
|
|
1149
|
-
if (node.state!.isLoaded.peek()) {
|
|
1150
|
-
node.isComputing = true;
|
|
1151
|
-
set(node, value);
|
|
1152
|
-
node.isComputing = false;
|
|
1153
|
-
} else {
|
|
1154
|
-
setNodeValue(node, value);
|
|
1155
|
-
node.state!.assign({
|
|
1156
|
-
isLoaded: true,
|
|
1157
|
-
error: undefined,
|
|
1158
|
-
});
|
|
1159
|
-
}
|
|
1160
|
-
}
|
|
1161
|
-
}
|
|
1162
|
-
|
|
1163
|
-
disposes.forEach((fn) => fn());
|
|
1164
|
-
disposes = [];
|
|
1165
|
-
nodes?.forEach(({ node, track }) => {
|
|
1166
|
-
disposes.push(onChange(node, markDirty, { immediate: true, trackingType: track }));
|
|
1167
|
-
});
|
|
1168
|
-
}
|
|
1169
|
-
e.cancel = true;
|
|
1170
|
-
},
|
|
1171
|
-
{ fromComputed: true },
|
|
1172
|
-
);
|
|
1173
|
-
return activatedValue;
|
|
1174
|
-
}
|
|
1175
|
-
|
|
1176
|
-
function activateNodeBase(node: NodeValue, value: any) {
|
|
1177
|
-
if (!node.state) {
|
|
1178
|
-
node.state = createObservable<ObservableState>(
|
|
1179
|
-
{
|
|
1180
|
-
isLoaded: false,
|
|
1181
|
-
} as ObservableState,
|
|
1182
|
-
false,
|
|
1183
|
-
extractPromise,
|
|
1184
|
-
getProxy,
|
|
1185
|
-
) as any;
|
|
1186
|
-
}
|
|
1187
|
-
if (node.activationState) {
|
|
1188
|
-
const { set: setFn, get: getFn, initial } = node.activationState as LinkedOptions;
|
|
1189
|
-
|
|
1190
|
-
value = getFn?.();
|
|
1191
|
-
|
|
1192
|
-
if (value == undefined || value === null) {
|
|
1193
|
-
value = initial;
|
|
1194
|
-
}
|
|
1195
|
-
|
|
1196
|
-
if (setFn) {
|
|
1197
|
-
let allChanges: Change[] = [];
|
|
1198
|
-
let latestValue: any = undefined;
|
|
1199
|
-
let runNumber = 0;
|
|
1200
|
-
const runChanges = (listenerParams?: ListenerParams) => {
|
|
1201
|
-
// Don't call the set if this is the first value coming in
|
|
1202
|
-
if (allChanges.length > 0) {
|
|
1203
|
-
let changes: Change[];
|
|
1204
|
-
let value: any;
|
|
1205
|
-
let loading = false;
|
|
1206
|
-
let remote = false;
|
|
1207
|
-
let getPrevious: () => any;
|
|
1208
|
-
if (listenerParams) {
|
|
1209
|
-
changes = listenerParams.changes;
|
|
1210
|
-
value = listenerParams.value;
|
|
1211
|
-
loading = listenerParams.loading;
|
|
1212
|
-
remote = listenerParams.remote;
|
|
1213
|
-
getPrevious = listenerParams.getPrevious;
|
|
1214
|
-
} else {
|
|
1215
|
-
// If this is called by flushPending then get the change array
|
|
1216
|
-
// that we've been building up.
|
|
1217
|
-
changes = allChanges;
|
|
1218
|
-
value = latestValue;
|
|
1219
|
-
getPrevious = createPreviousHandler(value, changes);
|
|
1220
|
-
}
|
|
1221
|
-
allChanges = [];
|
|
1222
|
-
latestValue = undefined;
|
|
1223
|
-
globalState.pendingNodes.delete(node);
|
|
1224
|
-
|
|
1225
|
-
runNumber++;
|
|
1226
|
-
const thisRunNumber = runNumber;
|
|
1227
|
-
|
|
1228
|
-
const run = () => {
|
|
1229
|
-
if (thisRunNumber !== runNumber) {
|
|
1230
|
-
// set may get called multiple times before it loads so ignore any previous runs
|
|
1231
|
-
return;
|
|
1232
|
-
}
|
|
1233
|
-
|
|
1234
|
-
node.isComputing = true;
|
|
1235
|
-
setFn({
|
|
1236
|
-
value,
|
|
1237
|
-
changes,
|
|
1238
|
-
loading,
|
|
1239
|
-
remote,
|
|
1240
|
-
getPrevious,
|
|
1241
|
-
});
|
|
1242
|
-
node.isComputing = false;
|
|
1243
|
-
};
|
|
1244
|
-
whenReady(node.state!.isLoaded, run);
|
|
1245
|
-
}
|
|
1246
|
-
};
|
|
1247
|
-
|
|
1248
|
-
const onChangeImmediate = ({ value, changes }: ListenerParams) => {
|
|
1249
|
-
if (!node.isComputing) {
|
|
1250
|
-
if (changes.length > 1 || !isFunction(changes[0].prevAtPath)) {
|
|
1251
|
-
latestValue = value;
|
|
1252
|
-
if (allChanges.length > 0) {
|
|
1253
|
-
changes = changes.filter((change) => !isArraySubset(allChanges[0].path, change.path));
|
|
1254
|
-
}
|
|
1255
|
-
allChanges.push(...changes);
|
|
1256
|
-
globalState.pendingNodes.set(node, runChanges);
|
|
1257
|
-
}
|
|
1258
|
-
}
|
|
1259
|
-
};
|
|
1260
|
-
|
|
1261
|
-
// Create an immediate listener to mark this node as pending. Then actually run
|
|
1262
|
-
// the changes at the end of the batch so everything is properly batched.
|
|
1263
|
-
// However, this can be short circuited if the user calls get() or peek()
|
|
1264
|
-
// in which case the set needs to run immediately so that the values are up to date.
|
|
1265
|
-
onChange(node, onChangeImmediate as any, { immediate: true });
|
|
1266
|
-
onChange(node, runChanges);
|
|
1267
|
-
}
|
|
1268
|
-
}
|
|
1269
|
-
const update: UpdateFn = ({ value }) => {
|
|
1270
|
-
if (!node.isComputing) {
|
|
1271
|
-
node.isComputing = true;
|
|
1272
|
-
set(node, value);
|
|
1273
|
-
node.isComputing = false;
|
|
1274
|
-
}
|
|
1275
|
-
};
|
|
1276
|
-
return { update, value };
|
|
1277
|
-
}
|
|
1278
|
-
|
|
1279
|
-
function setToObservable(node: NodeValue, value: any) {
|
|
1280
|
-
// If the computed is a proxy to another observable
|
|
1281
|
-
// link it to the target observable
|
|
1282
|
-
const linkedNode = getNode(value);
|
|
1283
|
-
if (linkedNode !== node && linkedNode?.linkedToNode !== node) {
|
|
1284
|
-
node.linkedToNode = linkedNode;
|
|
1285
|
-
linkedNode.linkedFromNodes ||= new Set();
|
|
1286
|
-
linkedNode.linkedFromNodes.add(node);
|
|
1287
|
-
node.linkedToNodeDispose?.();
|
|
1288
|
-
node.linkedToNodeDispose = onChange(
|
|
1289
|
-
linkedNode,
|
|
1290
|
-
() => {
|
|
1291
|
-
value = peekInternal(linkedNode);
|
|
1292
|
-
set(node, value);
|
|
1293
|
-
},
|
|
1294
|
-
{ initial: true },
|
|
1295
|
-
new Set([node]),
|
|
1296
|
-
);
|
|
1297
|
-
}
|
|
1298
|
-
return value;
|
|
1299
|
-
}
|
|
1300
|
-
|
|
1301
|
-
function recursivelyAutoActivate(obj: Record<string, any>, node: NodeValue) {
|
|
1302
|
-
if (isObject(obj) || isArray(obj)) {
|
|
1303
|
-
const pathStack: { key: string; value: any }[] = []; // Maintain a stack for path tracking
|
|
1304
|
-
const getNodeAtPath = () => {
|
|
1305
|
-
let childNode = node;
|
|
1306
|
-
for (let i = 0; i < pathStack.length; i++) {
|
|
1307
|
-
const { key } = pathStack[i];
|
|
1308
|
-
const value = getNodeValue(childNode)?.[key];
|
|
1309
|
-
childNode = getChildNode(childNode, key, isFunction(value) ? value : undefined);
|
|
1310
|
-
peekInternal(childNode);
|
|
1311
|
-
}
|
|
1312
|
-
|
|
1313
|
-
return childNode;
|
|
1314
|
-
};
|
|
1315
|
-
recursivelyAutoActivateInner(obj, pathStack, getNodeAtPath);
|
|
1316
|
-
}
|
|
1317
|
-
}
|
|
1318
|
-
|
|
1319
|
-
function recursivelyAutoActivateInner(
|
|
1320
|
-
obj: Record<string, any>,
|
|
1321
|
-
pathStack: { key: string; value: any }[],
|
|
1322
|
-
getNodeAtPath: () => NodeValue,
|
|
1323
|
-
) {
|
|
1324
|
-
for (const key in obj) {
|
|
1325
|
-
if (hasOwnProperty.call(obj, key)) {
|
|
1326
|
-
const value = obj[key];
|
|
1327
|
-
|
|
1328
|
-
if (isObservable(value)) {
|
|
1329
|
-
const childNode = getNodeAtPath();
|
|
1330
|
-
extractFunctionOrComputed(childNode, key, value);
|
|
1331
|
-
delete childNode.lazy;
|
|
1332
|
-
} else {
|
|
1333
|
-
const linkedOptions = isFunction(value) && value.prototype?.[symbolLinked];
|
|
1334
|
-
if (linkedOptions) {
|
|
1335
|
-
const activate = linkedOptions.activate;
|
|
1336
|
-
if (!activate || activate === 'auto') {
|
|
1337
|
-
const childNode = getNodeAtPath();
|
|
1338
|
-
peek(getChildNode(childNode, key, value));
|
|
1339
|
-
}
|
|
1340
|
-
}
|
|
1341
|
-
}
|
|
1342
|
-
|
|
1343
|
-
if (typeof value === 'object') {
|
|
1344
|
-
pathStack.push({ key, value });
|
|
1345
|
-
recursivelyAutoActivateInner(value, pathStack, getNodeAtPath); // Recursively traverse
|
|
1346
|
-
pathStack.pop();
|
|
1347
|
-
}
|
|
1348
|
-
}
|
|
1349
|
-
}
|
|
1350
|
-
}
|