@legendapp/state 2.2.0-next.7 → 2.2.0-next.71

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.
Files changed (298) hide show
  1. package/README.md +4 -2
  2. package/babel.js.map +1 -1
  3. package/config/enable$get.d.ts +8 -0
  4. package/config/enable$get.js +24 -0
  5. package/config/enable$get.js.map +1 -0
  6. package/config/enable$get.mjs +21 -0
  7. package/config/enable$get.mjs.map +1 -0
  8. package/config/enableReactComponents.js.map +1 -1
  9. package/config/enableReactComponents.mjs.map +1 -1
  10. package/config/enableReactNativeComponents.js.map +1 -1
  11. package/config/enableReactNativeComponents.mjs.map +1 -1
  12. package/config/enableReactTracking.d.ts +0 -9
  13. package/config/enableReactTracking.js.map +1 -1
  14. package/config/enableReactTracking.mjs.map +1 -1
  15. package/config/enableReactUse.d.ts +1 -1
  16. package/config/enableReactUse.js +1 -0
  17. package/config/enableReactUse.js.map +1 -1
  18. package/config/enableReactUse.mjs +1 -0
  19. package/config/enableReactUse.mjs.map +1 -1
  20. package/config/enable_peek.d.ts +8 -0
  21. package/config/{enableDirectPeek.js → enable_peek.js} +6 -3
  22. package/config/enable_peek.js.map +1 -0
  23. package/config/{enableDirectPeek.mjs → enable_peek.mjs} +5 -3
  24. package/config/enable_peek.mjs.map +1 -0
  25. package/helpers/fetch.d.ts +4 -3
  26. package/helpers/fetch.js.map +1 -1
  27. package/helpers/fetch.mjs.map +1 -1
  28. package/helpers/pageHash.js.map +1 -1
  29. package/helpers/pageHash.mjs.map +1 -1
  30. package/helpers/pageHashParams.js.map +1 -1
  31. package/helpers/pageHashParams.mjs.map +1 -1
  32. package/helpers/time.d.ts +2 -2
  33. package/helpers/time.js.map +1 -1
  34. package/helpers/time.mjs.map +1 -1
  35. package/history.js +2 -2
  36. package/history.js.map +1 -1
  37. package/history.mjs +3 -3
  38. package/history.mjs.map +1 -1
  39. package/index.d.ts +30 -9
  40. package/index.js +877 -661
  41. package/index.js.map +1 -1
  42. package/index.mjs +874 -658
  43. package/index.mjs.map +1 -1
  44. package/package.json +22 -25
  45. package/persist-plugins/async-storage.d.ts +3 -3
  46. package/persist-plugins/async-storage.js +8 -7
  47. package/persist-plugins/async-storage.js.map +1 -1
  48. package/persist-plugins/async-storage.mjs +9 -8
  49. package/persist-plugins/async-storage.mjs.map +1 -1
  50. package/persist-plugins/fetch.js.map +1 -1
  51. package/persist-plugins/fetch.mjs.map +1 -1
  52. package/persist-plugins/firebase.d.ts +1 -1
  53. package/persist-plugins/firebase.js +12 -11
  54. package/persist-plugins/firebase.js.map +1 -1
  55. package/persist-plugins/firebase.mjs +13 -12
  56. package/persist-plugins/firebase.mjs.map +1 -1
  57. package/persist-plugins/indexeddb.d.ts +10 -10
  58. package/persist-plugins/indexeddb.js +2 -2
  59. package/persist-plugins/indexeddb.js.map +1 -1
  60. package/persist-plugins/indexeddb.mjs +2 -2
  61. package/persist-plugins/indexeddb.mjs.map +1 -1
  62. package/persist-plugins/local-storage.d.ts +3 -4
  63. package/persist-plugins/local-storage.js +19 -7
  64. package/persist-plugins/local-storage.js.map +1 -1
  65. package/persist-plugins/local-storage.mjs +20 -9
  66. package/persist-plugins/local-storage.mjs.map +1 -1
  67. package/persist-plugins/mmkv.d.ts +8 -8
  68. package/persist-plugins/mmkv.js +5 -4
  69. package/persist-plugins/mmkv.js.map +1 -1
  70. package/persist-plugins/mmkv.mjs +6 -5
  71. package/persist-plugins/mmkv.mjs.map +1 -1
  72. package/persist-plugins/query.js.map +1 -1
  73. package/persist-plugins/query.mjs.map +1 -1
  74. package/persist.d.ts +2 -14
  75. package/persist.js +1250 -268
  76. package/persist.js.map +1 -1
  77. package/persist.mjs +1250 -269
  78. package/persist.mjs.map +1 -1
  79. package/react-hooks/createObservableHook.js +1 -1
  80. package/react-hooks/createObservableHook.js.map +1 -1
  81. package/react-hooks/createObservableHook.mjs +1 -1
  82. package/react-hooks/createObservableHook.mjs.map +1 -1
  83. package/react-hooks/useFetch.d.ts +4 -3
  84. package/react-hooks/useFetch.js.map +1 -1
  85. package/react-hooks/useFetch.mjs.map +1 -1
  86. package/react-hooks/useHover.js.map +1 -1
  87. package/react-hooks/useHover.mjs.map +1 -1
  88. package/react-hooks/useMeasure.js.map +1 -1
  89. package/react-hooks/useMeasure.mjs.map +1 -1
  90. package/react-hooks/useObservableNextRouter.js.map +1 -1
  91. package/react-hooks/useObservableNextRouter.mjs.map +1 -1
  92. package/react-hooks/useObservableQuery.js.map +1 -1
  93. package/react-hooks/useObservableQuery.mjs.map +1 -1
  94. package/react-hooks/usePersistedObservable.d.ts +5 -3
  95. package/react-hooks/usePersistedObservable.js +5 -2
  96. package/react-hooks/usePersistedObservable.js.map +1 -1
  97. package/react-hooks/usePersistedObservable.mjs +5 -2
  98. package/react-hooks/usePersistedObservable.mjs.map +1 -1
  99. package/react.js +61 -75
  100. package/react.js.map +1 -1
  101. package/react.mjs +61 -75
  102. package/react.mjs.map +1 -1
  103. package/src/ObservableObject.ts +1184 -0
  104. package/src/ObservablePrimitive.ts +62 -0
  105. package/src/babel/index.ts +70 -0
  106. package/src/batching.ts +372 -0
  107. package/src/computed.ts +16 -0
  108. package/src/config/enable$get.ts +30 -0
  109. package/src/config/enableReactComponents.ts +26 -0
  110. package/src/config/enableReactNativeComponents.ts +102 -0
  111. package/src/config/enableReactTracking.ts +60 -0
  112. package/src/config/enableReactUse.ts +23 -0
  113. package/src/config/enable_peek.ts +31 -0
  114. package/src/config.ts +47 -0
  115. package/src/createObservable.ts +46 -0
  116. package/src/event.ts +26 -0
  117. package/src/globals.ts +224 -0
  118. package/src/helpers/fetch.ts +26 -0
  119. package/src/helpers/pageHash.ts +41 -0
  120. package/src/helpers/pageHashParams.ts +55 -0
  121. package/src/helpers/time.ts +30 -0
  122. package/src/helpers.ts +221 -0
  123. package/src/history/trackHistory.ts +29 -0
  124. package/src/is.ts +56 -0
  125. package/src/linked.ts +6 -0
  126. package/src/observable.ts +32 -0
  127. package/src/observableInterfaces.ts +165 -0
  128. package/src/observableTypes.ts +221 -0
  129. package/src/observe.ts +89 -0
  130. package/src/onChange.ts +136 -0
  131. package/src/persist/configureObservablePersistence.ts +7 -0
  132. package/src/persist/fieldTransformer.ts +149 -0
  133. package/src/persist/observablePersistRemoteFunctionsAdapter.ts +39 -0
  134. package/src/persist/persistObservable.ts +1029 -0
  135. package/src/persist-plugins/async-storage.ts +102 -0
  136. package/src/persist-plugins/fetch.ts +33 -0
  137. package/src/persist-plugins/firebase.ts +1050 -0
  138. package/src/persist-plugins/indexeddb.ts +433 -0
  139. package/src/persist-plugins/local-storage.ts +90 -0
  140. package/src/persist-plugins/mmkv.ts +90 -0
  141. package/src/persist-plugins/query.ts +133 -0
  142. package/src/persistTypes.ts +226 -0
  143. package/src/proxy.ts +28 -0
  144. package/src/react/Computed.tsx +7 -0
  145. package/src/react/For.tsx +116 -0
  146. package/src/react/Memo.tsx +4 -0
  147. package/src/react/Reactive.tsx +53 -0
  148. package/src/react/Show.tsx +33 -0
  149. package/src/react/Switch.tsx +43 -0
  150. package/src/react/react-globals.ts +3 -0
  151. package/src/react/{reactInterfaces.d.ts → reactInterfaces.ts} +15 -7
  152. package/src/react/reactive-observer.tsx +210 -0
  153. package/src/react/useComputed.ts +36 -0
  154. package/src/react/useEffectOnce.ts +41 -0
  155. package/src/react/useIsMounted.ts +16 -0
  156. package/src/react/useMount.ts +15 -0
  157. package/src/react/useObservable.ts +24 -0
  158. package/src/react/useObservableReducer.ts +52 -0
  159. package/src/react/useObservableState.ts +30 -0
  160. package/src/react/useObserve.ts +54 -0
  161. package/src/react/useObserveEffect.ts +40 -0
  162. package/src/react/usePauseProvider.tsx +13 -0
  163. package/src/react/useSelector.ts +167 -0
  164. package/src/react/useUnmount.ts +8 -0
  165. package/src/react/useWhen.ts +9 -0
  166. package/src/react-hooks/createObservableHook.ts +53 -0
  167. package/src/react-hooks/useFetch.ts +16 -0
  168. package/src/react-hooks/useHover.ts +40 -0
  169. package/src/react-hooks/useMeasure.ts +48 -0
  170. package/src/react-hooks/useObservableNextRouter.ts +137 -0
  171. package/src/react-hooks/useObservableQuery.ts +205 -0
  172. package/src/react-hooks/usePersistedObservable.ts +24 -0
  173. package/src/retry.ts +69 -0
  174. package/src/setupTracking.ts +26 -0
  175. package/src/sync/activateSyncedNode.ts +146 -0
  176. package/src/sync/configureObservableSync.ts +7 -0
  177. package/src/sync/syncHelpers.ts +15 -0
  178. package/src/sync/syncObservable.ts +989 -0
  179. package/src/sync/syncObservableAdapter.ts +30 -0
  180. package/src/sync/synced.ts +20 -0
  181. package/src/sync-plugins/fetch.ts +42 -0
  182. package/src/syncTypes.ts +163 -0
  183. package/src/trace/traceHelpers.ts +11 -0
  184. package/src/trace/useTraceListeners.ts +34 -0
  185. package/src/trace/useTraceUpdates.ts +24 -0
  186. package/src/trace/useVerifyNotTracking.ts +33 -0
  187. package/src/trace/useVerifyOneRender.ts +10 -0
  188. package/src/trackSelector.ts +52 -0
  189. package/src/tracking.ts +43 -0
  190. package/src/types/babel.d.ts +12 -0
  191. package/src/when.ts +70 -0
  192. package/sync-plugins/fetch.d.ts +11 -0
  193. package/sync-plugins/fetch.js +24 -0
  194. package/sync-plugins/fetch.js.map +1 -0
  195. package/sync-plugins/fetch.mjs +22 -0
  196. package/sync-plugins/fetch.mjs.map +1 -0
  197. package/sync.d.ts +8 -0
  198. package/sync.js +919 -0
  199. package/sync.js.map +1 -0
  200. package/sync.mjs +912 -0
  201. package/sync.mjs.map +1 -0
  202. package/trace.js +13 -10
  203. package/trace.js.map +1 -1
  204. package/trace.mjs +11 -8
  205. package/trace.mjs.map +1 -1
  206. package/types/babel.d.ts +3 -3
  207. package/config/enableDirectAccess.d.ts +0 -7
  208. package/config/enableDirectAccess.js +0 -25
  209. package/config/enableDirectAccess.js.map +0 -1
  210. package/config/enableDirectAccess.mjs +0 -23
  211. package/config/enableDirectAccess.mjs.map +0 -1
  212. package/config/enableDirectPeek.d.ts +0 -7
  213. package/config/enableDirectPeek.js.map +0 -1
  214. package/config/enableDirectPeek.mjs.map +0 -1
  215. package/config/enableReactDirectRender.d.ts +0 -2
  216. package/config/enableReactDirectRender.js +0 -78
  217. package/config/enableReactDirectRender.js.map +0 -1
  218. package/config/enableReactDirectRender.mjs +0 -75
  219. package/config/enableReactDirectRender.mjs.map +0 -1
  220. package/src/ObservableObject.d.ts +0 -14
  221. package/src/ObservablePrimitive.d.ts +0 -7
  222. package/src/babel/index.d.ts +0 -17
  223. package/src/batching.d.ts +0 -6
  224. package/src/computed.d.ts +0 -4
  225. package/src/config/enableDirectAccess.d.ts +0 -7
  226. package/src/config/enableDirectPeek.d.ts +0 -7
  227. package/src/config/enableReactComponents.d.ts +0 -7
  228. package/src/config/enableReactDirectRender.d.ts +0 -2
  229. package/src/config/enableReactNativeComponents.d.ts +0 -20
  230. package/src/config/enableReactTracking.d.ts +0 -15
  231. package/src/config/enableReactUse.d.ts +0 -7
  232. package/src/config.d.ts +0 -8
  233. package/src/createObservable.d.ts +0 -2
  234. package/src/event.d.ts +0 -2
  235. package/src/globals.d.ts +0 -32
  236. package/src/helpers/fetch.d.ts +0 -6
  237. package/src/helpers/pageHash.d.ts +0 -7
  238. package/src/helpers/pageHashParams.d.ts +0 -7
  239. package/src/helpers/time.d.ts +0 -3
  240. package/src/helpers.d.ts +0 -13
  241. package/src/history/trackHistory.d.ts +0 -4
  242. package/src/is.d.ts +0 -10
  243. package/src/observable.d.ts +0 -16
  244. package/src/observableInterfaces.d.ts +0 -456
  245. package/src/observe.d.ts +0 -6
  246. package/src/onChange.d.ts +0 -7
  247. package/src/persist/configureObservablePersistence.d.ts +0 -3
  248. package/src/persist/fieldTransformer.d.ts +0 -8
  249. package/src/persist/observablePersistRemoteFunctionsAdapter.d.ts +0 -2
  250. package/src/persist/persistActivateNode.d.ts +0 -1
  251. package/src/persist/persistHelpers.d.ts +0 -1
  252. package/src/persist/persistObservable.d.ts +0 -25
  253. package/src/persist-plugins/async-storage.d.ts +0 -14
  254. package/src/persist-plugins/fetch.d.ts +0 -10
  255. package/src/persist-plugins/firebase.d.ts +0 -51
  256. package/src/persist-plugins/indexeddb.d.ts +0 -25
  257. package/src/persist-plugins/local-storage.d.ts +0 -21
  258. package/src/persist-plugins/mmkv.d.ts +0 -14
  259. package/src/persist-plugins/query.d.ts +0 -18
  260. package/src/proxy.d.ts +0 -5
  261. package/src/react/Computed.d.ts +0 -5
  262. package/src/react/For.d.ts +0 -15
  263. package/src/react/Memo.d.ts +0 -3
  264. package/src/react/Reactive.d.ts +0 -9
  265. package/src/react/Show.d.ts +0 -18
  266. package/src/react/Switch.d.ts +0 -14
  267. package/src/react/react-globals.d.ts +0 -3
  268. package/src/react/reactive-observer.d.ts +0 -14
  269. package/src/react/useComputed.d.ts +0 -5
  270. package/src/react/useEffectOnce.d.ts +0 -1
  271. package/src/react/useIsMounted.d.ts +0 -2
  272. package/src/react/useMount.d.ts +0 -2
  273. package/src/react/useObservable.d.ts +0 -9
  274. package/src/react/useObservableReducer.d.ts +0 -7
  275. package/src/react/useObservableState.d.ts +0 -2
  276. package/src/react/useObserve.d.ts +0 -4
  277. package/src/react/useObserveEffect.d.ts +0 -4
  278. package/src/react/usePauseProvider.d.ts +0 -8
  279. package/src/react/useSelector.d.ts +0 -3
  280. package/src/react/useUnmount.d.ts +0 -2
  281. package/src/react/useWhen.d.ts +0 -3
  282. package/src/react-hooks/createObservableHook.d.ts +0 -2
  283. package/src/react-hooks/useFetch.d.ts +0 -6
  284. package/src/react-hooks/useHover.d.ts +0 -3
  285. package/src/react-hooks/useMeasure.d.ts +0 -6
  286. package/src/react-hooks/useObservableNextRouter.d.ts +0 -33
  287. package/src/react-hooks/useObservableQuery.d.ts +0 -6
  288. package/src/react-hooks/usePersistedObservable.d.ts +0 -11
  289. package/src/retry.d.ts +0 -9
  290. package/src/setupTracking.d.ts +0 -2
  291. package/src/trace/traceHelpers.d.ts +0 -2
  292. package/src/trace/useTraceListeners.d.ts +0 -1
  293. package/src/trace/useTraceUpdates.d.ts +0 -1
  294. package/src/trace/useVerifyNotTracking.d.ts +0 -1
  295. package/src/trace/useVerifyOneRender.d.ts +0 -1
  296. package/src/trackSelector.d.ts +0 -7
  297. package/src/tracking.d.ts +0 -13
  298. package/src/when.d.ts +0 -3
package/index.js CHANGED
@@ -8,14 +8,17 @@ 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';
15
15
  }
16
16
  function isPrimitive(arg) {
17
17
  const type = typeof arg;
18
- return arg !== undefined && type !== 'object' && type !== 'function';
18
+ return arg !== undefined && (isDate(arg) || (type !== 'object' && type !== 'function'));
19
+ }
20
+ function isDate(obj) {
21
+ return obj instanceof Date;
19
22
  }
20
23
  function isSymbol(obj) {
21
24
  return typeof obj === 'symbol';
@@ -40,6 +43,9 @@ function isEmpty(obj) {
40
43
  }
41
44
  return true;
42
45
  }
46
+ function isNullOrUndefined(value) {
47
+ return value === undefined || value === null;
48
+ }
43
49
  const setPrimitives = new Set(['boolean', 'string', 'number']);
44
50
  /** @internal */
45
51
  function isActualPrimitive(arg) {
@@ -55,72 +61,110 @@ const symbolGetNode = Symbol('getNode');
55
61
  const symbolDelete = /* @__PURE__ */ Symbol('delete');
56
62
  const symbolOpaque = Symbol('opaque');
57
63
  const optimized = Symbol('optimized');
58
- // TODOV3 Remove these
59
- const extraPrimitiveActivators = new Map();
60
- const extraPrimitiveProps = new Map();
64
+ const symbolLinked = Symbol('linked');
61
65
  const globalState = {
62
66
  isLoadingLocal: false,
63
67
  isMerging: false,
64
- isLoadingRemote$: undefined,
65
- activateNode: undefined,
68
+ isLoadingRemote: false,
69
+ activateSyncedNode: undefined,
70
+ pendingNodes: new Map(),
71
+ dirtyNodes: new Set(),
72
+ replacer: undefined,
73
+ reviver: undefined,
66
74
  };
67
- function isObservable(obs) {
68
- return !!obs && !!obs[symbolGetNode];
69
- }
70
- function isComputed(obs) {
71
- var _a;
72
- return obs && ((_a = obs[symbolGetNode]) === null || _a === void 0 ? void 0 : _a.isComputed);
75
+ function getPathType(value) {
76
+ return isArray(value) ? 'array' : value instanceof Map ? 'map' : value instanceof Set ? 'set' : 'object';
77
+ }
78
+ function replacer(key, value) {
79
+ if (value instanceof Map) {
80
+ return {
81
+ __LSType: 'Map',
82
+ value: Array.from(value.entries()), // or with spread: value: [...value]
83
+ };
84
+ }
85
+ else if (value instanceof Set) {
86
+ return {
87
+ __LSType: 'Set',
88
+ value: Array.from(value), // or with spread: value: [...value]
89
+ };
90
+ }
91
+ else if (globalState.replacer) {
92
+ value = globalState.replacer(key, value);
93
+ }
94
+ return value;
73
95
  }
74
- function checkActivate(node) {
75
- var _a;
76
- const root = node.root;
77
- (_a = root.activate) === null || _a === void 0 ? void 0 : _a.call(root);
78
- if (root.toActivate) {
79
- root.toActivate.forEach(checkActivate);
80
- delete root.toActivate;
96
+ const ISO8601 = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/;
97
+ function reviver(key, value) {
98
+ if (value) {
99
+ if (typeof value === 'string' && ISO8601.test(value)) {
100
+ return new Date(value);
101
+ }
102
+ if (typeof value === 'object') {
103
+ if (value.__LSType === 'Map') {
104
+ return new Map(value.value);
105
+ }
106
+ else if (value.__LSType === 'Set') {
107
+ return new Set(value.value);
108
+ }
109
+ }
110
+ if (globalState.reviver) {
111
+ value = globalState.reviver(key, value);
112
+ }
81
113
  }
114
+ return value;
115
+ }
116
+ function safeStringify(value) {
117
+ return JSON.stringify(value, replacer);
118
+ }
119
+ function safeParse(value) {
120
+ return JSON.parse(value, reviver);
121
+ }
122
+ function clone(value) {
123
+ return safeParse(safeStringify(value));
124
+ }
125
+ function isObservable(obs) {
126
+ return !!obs && !!obs[symbolGetNode];
82
127
  }
83
128
  function getNode(obs) {
84
129
  return obs && obs[symbolGetNode];
85
130
  }
86
- function setNodeValue(node, newValue) {
131
+ function isEvent(obs) {
87
132
  var _a;
133
+ return obs && ((_a = obs[symbolGetNode]) === null || _a === void 0 ? void 0 : _a.isEvent);
134
+ }
135
+ function setNodeValue(node, newValue) {
136
+ var _a, _b, _c;
88
137
  const parentNode = (_a = node.parent) !== null && _a !== void 0 ? _a : node;
89
138
  const key = node.parent ? node.key : '_';
90
139
  const isDelete = newValue === symbolDelete;
91
140
  if (isDelete)
92
141
  newValue = undefined;
93
142
  // Get the value of the parent
94
- // const parentValue = isRoot ? node.root : ensureNodeValue(node);
95
143
  const parentValue = node.parent ? ensureNodeValue(parentNode) : parentNode.root;
96
144
  // Save the previous value first
97
145
  const prevValue = parentValue[key];
98
146
  const isFunc = isFunction(newValue);
99
147
  // Compute newValue if newValue is a function or an observable
100
- newValue = !parentNode.isAssigning && isFunc ? newValue(prevValue) : newValue;
101
- // If setting an observable, set a link to the observable instead
102
- if (isObservable(newValue) && !isComputed(newValue)) {
103
- const val = newValue;
104
- node.lazy = () => val;
105
- newValue = undefined;
106
- }
107
- try {
108
- parentNode.isSetting = (parentNode.isSetting || 0) + 1;
109
- // Save the new value
110
- if (isDelete) {
111
- delete parentValue[key];
148
+ newValue = !parentNode.isAssigning && isFunc && !isFunction(prevValue) ? newValue(prevValue) : newValue;
149
+ if (!globalState.isMerging ||
150
+ prevValue === undefined ||
151
+ isFunction(prevValue) ||
152
+ !((_c = (_b = node.parent) === null || _b === void 0 ? void 0 : _b.functions) === null || _c === void 0 ? void 0 : _c.get(key))) {
153
+ try {
154
+ parentNode.isSetting = (parentNode.isSetting || 0) + 1;
155
+ // Save the new value
156
+ if (isDelete) {
157
+ delete parentValue[key];
158
+ }
159
+ else {
160
+ parentValue[key] = newValue;
161
+ }
112
162
  }
113
- else {
114
- parentValue[key] = newValue;
163
+ finally {
164
+ parentNode.isSetting--;
115
165
  }
116
166
  }
117
- finally {
118
- parentNode.isSetting--;
119
- }
120
- if (parentNode.root.locked && parentNode.root.set) {
121
- parentNode.root.set(parentNode.root._);
122
- }
123
- return { prevValue, newValue, parentValue };
167
+ return { prevValue, newValue };
124
168
  }
125
169
  const arrNodeKeys = [];
126
170
  function getNodeValue(node) {
@@ -137,14 +181,8 @@ function getNodeValue(node) {
137
181
  }
138
182
  return child;
139
183
  }
140
- const cloneFunction = (originalFunction) => {
141
- const length = originalFunction.length;
142
- return length > 1
143
- ? (arg1, arg2) => originalFunction(arg1, arg2)
144
- : (...args) => originalFunction(...args);
145
- };
146
184
  function getChildNode(node, key, asFunction) {
147
- var _a;
185
+ var _a, _b;
148
186
  // Get the child by key
149
187
  let child = (_a = node.children) === null || _a === void 0 ? void 0 : _a.get(key);
150
188
  // Create the child node if it doesn't already exist
@@ -155,11 +193,13 @@ function getChildNode(node, key, asFunction) {
155
193
  key,
156
194
  lazy: true,
157
195
  };
158
- if (asFunction) {
159
- child = Object.assign(cloneFunction(asFunction), child);
196
+ // Lookup functions are bound with the child key
197
+ if (((_b = node.lazyFn) === null || _b === void 0 ? void 0 : _b.length) === 1) {
198
+ asFunction = node.lazyFn.bind(node, key);
160
199
  }
161
- else if (node.proxyFn2) {
162
- child = Object.assign(node.proxyFn2.bind(node, key), child);
200
+ if (isFunction(asFunction)) {
201
+ child = Object.assign(() => { }, child);
202
+ child.lazyFn = asFunction;
163
203
  }
164
204
  if (!node.children) {
165
205
  node.children = new Map();
@@ -170,7 +210,7 @@ function getChildNode(node, key, asFunction) {
170
210
  }
171
211
  function ensureNodeValue(node) {
172
212
  let value = getNodeValue(node);
173
- if (!value) {
213
+ if (!value || isFunction(value)) {
174
214
  if (isChildNodeValue(node)) {
175
215
  const parent = ensureNodeValue(node.parent);
176
216
  value = parent[node.key] = {};
@@ -183,17 +223,19 @@ function ensureNodeValue(node) {
183
223
  }
184
224
  function findIDKey(obj, node) {
185
225
  var _a, _b;
186
- let idKey = isObject(obj)
187
- ? 'id' in obj
188
- ? 'id'
189
- : 'key' in obj
190
- ? 'key'
191
- : '_id' in obj
192
- ? '_id'
193
- : '__id' in obj
194
- ? '__id'
195
- : undefined
196
- : undefined;
226
+ let idKey = isObservable(obj)
227
+ ? undefined
228
+ : isObject(obj)
229
+ ? 'id' in obj
230
+ ? 'id'
231
+ : 'key' in obj
232
+ ? 'key'
233
+ : '_id' in obj
234
+ ? '_id'
235
+ : '__id' in obj
236
+ ? '__id'
237
+ : undefined
238
+ : undefined;
197
239
  if (!idKey && node.parent) {
198
240
  const k = node.key + '_keyExtractor';
199
241
  const keyExtractor = (_b = (_a = node.functions) === null || _a === void 0 ? void 0 : _a.get(k)) !== null && _b !== void 0 ? _b : getNodeValue(node.parent)[node.key + '_keyExtractor'];
@@ -203,26 +245,20 @@ function findIDKey(obj, node) {
203
245
  }
204
246
  return idKey;
205
247
  }
206
- function extractFunction(node, key, fnOrComputed, computedChildNode) {
248
+ function extractFunction(node, key, fnOrComputed) {
207
249
  if (!node.functions) {
208
250
  node.functions = new Map();
209
251
  }
210
252
  node.functions.set(key, fnOrComputed);
211
- if (computedChildNode) {
212
- computedChildNode.parentOther = getChildNode(node, key);
213
- if (!node.root.toActivate) {
214
- node.root.toActivate = [];
215
- }
216
- node.root.toActivate.push(computedChildNode);
217
- }
253
+ }
254
+ function equals(a, b) {
255
+ return a === b || (isDate(a) && isDate(b) && +a === +b);
218
256
  }
219
257
 
220
258
  let timeout;
221
259
  let numInBatch = 0;
222
260
  let isRunningBatch = false;
223
261
  let didDelayEndBatch = false;
224
- let _afterBatch = [];
225
- let _queuedBatches = [];
226
262
  let _batchMap = new Map();
227
263
  function onActionTimeout() {
228
264
  if (_batchMap.size > 0) {
@@ -241,23 +277,34 @@ function isArraySubset(mainArr, subsetArr) {
241
277
  return true;
242
278
  }
243
279
  function createPreviousHandlerInner(value, changes) {
244
- // Clones the current state and inject the previous data at the changed path
245
- let clone = value ? JSON.parse(JSON.stringify(value)) : {};
246
- for (let i = 0; i < changes.length; i++) {
247
- const { path, prevAtPath } = changes[i];
248
- let o = clone;
249
- if (path.length > 0) {
250
- let i;
251
- for (i = 0; i < path.length - 1; i++) {
252
- o = o[path[i]];
280
+ try {
281
+ // Clones the current state and inject the previous data at the changed path
282
+ let cloned = value ? clone(value) : {};
283
+ for (let i = 0; i < changes.length; i++) {
284
+ const { path, prevAtPath } = changes[i];
285
+ let o = cloned;
286
+ if (path.length > 0) {
287
+ let i;
288
+ for (i = 0; i < path.length - 1; i++) {
289
+ o = o[path[i]];
290
+ }
291
+ const key = path[i];
292
+ if (o instanceof Map) {
293
+ o.set(key, prevAtPath);
294
+ }
295
+ else {
296
+ o[key] = prevAtPath;
297
+ }
298
+ }
299
+ else {
300
+ cloned = prevAtPath;
253
301
  }
254
- o[path[i]] = prevAtPath;
255
- }
256
- else {
257
- clone = prevAtPath;
258
302
  }
303
+ return cloned;
304
+ }
305
+ catch (_a) {
306
+ return undefined;
259
307
  }
260
- return clone;
261
308
  }
262
309
  function createPreviousHandler(value, changes) {
263
310
  // Create a function that generates the previous state
@@ -270,9 +317,13 @@ function createPreviousHandler(value, changes) {
270
317
  function notify(node, value, prev, level, whenOptimizedOnlyIf) {
271
318
  // Run immediate listeners if there are any
272
319
  const changesInBatch = new Map();
273
- computeChangesRecursive(changesInBatch, node, value, [], [], value, prev,
320
+ computeChangesRecursive(changesInBatch, node,
321
+ /*loading*/ globalState.isLoadingLocal,
322
+ /*remote*/ globalState.isLoadingRemote, value, [], [], value, prev,
274
323
  /*immediate*/ true, level, whenOptimizedOnlyIf);
275
- batchNotifyChanges(changesInBatch, /*immediate*/ true);
324
+ if (changesInBatch.size) {
325
+ batchNotifyChanges(changesInBatch, /*immediate*/ true);
326
+ }
276
327
  // Update the current batch
277
328
  const existing = _batchMap.get(node);
278
329
  if (existing) {
@@ -280,14 +331,21 @@ function notify(node, value, prev, level, whenOptimizedOnlyIf) {
280
331
  // TODO: level, whenOptimizedOnlyIf
281
332
  }
282
333
  else {
283
- _batchMap.set(node, { value, prev, level, whenOptimizedOnlyIf });
334
+ _batchMap.set(node, {
335
+ value,
336
+ prev,
337
+ level,
338
+ whenOptimizedOnlyIf,
339
+ remote: globalState.isLoadingRemote,
340
+ loading: globalState.isLoadingLocal,
341
+ });
284
342
  }
285
343
  // If not in a batch run it immediately
286
344
  if (numInBatch <= 0) {
287
345
  runBatch();
288
346
  }
289
347
  }
290
- function computeChangesAtNode(changesInBatch, node, value, path, pathTypes, valueAtPath, prevAtPath, immediate, level, whenOptimizedOnlyIf) {
348
+ function computeChangesAtNode(changesInBatch, node, loading, remote, value, path, pathTypes, valueAtPath, prevAtPath, immediate, level, whenOptimizedOnlyIf) {
291
349
  // If there are listeners at this node compute the changes that need to be run
292
350
  if (immediate ? node.listenersImmediate : node.listeners) {
293
351
  const change = {
@@ -308,18 +366,21 @@ function computeChangesAtNode(changesInBatch, node, value, path, pathTypes, valu
308
366
  changesInBatch.set(node, {
309
367
  level,
310
368
  value,
369
+ remote,
370
+ loading,
311
371
  whenOptimizedOnlyIf,
312
372
  changes: [change],
313
373
  });
314
374
  }
315
375
  }
316
376
  }
317
- function computeChangesRecursive(changesInBatch, node, value, path, pathTypes, valueAtPath, prevAtPath, immediate, level, whenOptimizedOnlyIf) {
377
+ function computeChangesRecursive(changesInBatch, node, loading, remote, value, path, pathTypes, valueAtPath, prevAtPath, immediate, level, whenOptimizedOnlyIf) {
318
378
  // Do the compute at this node
319
- computeChangesAtNode(changesInBatch, node, value, path, pathTypes, valueAtPath, prevAtPath, immediate, level, whenOptimizedOnlyIf);
379
+ computeChangesAtNode(changesInBatch, node, loading, remote, value, path, pathTypes, valueAtPath, prevAtPath, immediate, level, whenOptimizedOnlyIf);
320
380
  if (node.linkedFromNodes) {
321
381
  for (const linkedFromNode of node.linkedFromNodes) {
322
- computeChangesAtNode(changesInBatch, linkedFromNode, value, path, pathTypes, valueAtPath, prevAtPath, immediate, level, whenOptimizedOnlyIf);
382
+ const childNode = getNodeAtPath(linkedFromNode, path);
383
+ computeChangesRecursive(changesInBatch, childNode, loading, remote, valueAtPath, [], [], valueAtPath, prevAtPath, immediate, 0, whenOptimizedOnlyIf);
323
384
  }
324
385
  }
325
386
  // If not root notify up through parents
@@ -327,14 +388,14 @@ function computeChangesRecursive(changesInBatch, node, value, path, pathTypes, v
327
388
  const parent = node.parent;
328
389
  if (parent) {
329
390
  const parentValue = getNodeValue(parent);
330
- computeChangesRecursive(changesInBatch, parent, parentValue, [node.key].concat(path), [(isArray(value) ? 'array' : 'object')].concat(pathTypes), valueAtPath, prevAtPath, immediate, level + 1, whenOptimizedOnlyIf);
391
+ computeChangesRecursive(changesInBatch, parent, loading, remote, parentValue, [node.key].concat(path), [getPathType(value)].concat(pathTypes), valueAtPath, prevAtPath, immediate, level + 1, whenOptimizedOnlyIf);
331
392
  }
332
393
  }
333
394
  }
334
395
  function batchNotifyChanges(changesInBatch, immediate) {
335
396
  const listenersNotified = new Set();
336
397
  // For each change in the batch, notify all of the listeners
337
- changesInBatch.forEach(({ changes, level, value, whenOptimizedOnlyIf }, node) => {
398
+ changesInBatch.forEach(({ changes, level, value, loading, remote, whenOptimizedOnlyIf }, node) => {
338
399
  const listeners = immediate ? node.listenersImmediate : node.listeners;
339
400
  if (listeners) {
340
401
  let listenerParams;
@@ -351,6 +412,8 @@ function batchNotifyChanges(changesInBatch, immediate) {
351
412
  if (!noArgs && !listenerParams) {
352
413
  listenerParams = {
353
414
  value,
415
+ loading,
416
+ remote,
354
417
  getPrevious: createPreviousHandler(value, changes),
355
418
  changes,
356
419
  };
@@ -366,32 +429,30 @@ function batchNotifyChanges(changesInBatch, immediate) {
366
429
  });
367
430
  }
368
431
  function runBatch() {
432
+ const dirtyNodes = Array.from(globalState.dirtyNodes);
433
+ globalState.dirtyNodes.clear();
434
+ dirtyNodes.forEach((node) => {
435
+ var _a;
436
+ (_a = node.dirtyFn) === null || _a === void 0 ? void 0 : _a.call(node);
437
+ node.dirtyFn = undefined;
438
+ });
369
439
  // Save batch locally and reset _batchMap first because a new batch could begin while looping over callbacks.
370
- // This can happen with observableComputed for example.
440
+ // This can happen with computeds for example.
371
441
  const map = _batchMap;
372
442
  _batchMap = new Map();
373
443
  const changesInBatch = new Map();
374
444
  // First compute all of the changes at each node. It's important to do this first before
375
445
  // running all the notifications because createPreviousHandler depends on knowing
376
446
  // all of the changes happening at the node.
377
- map.forEach(({ value, prev, level, whenOptimizedOnlyIf }, node) => {
378
- computeChangesRecursive(changesInBatch, node, value, [], [], value, prev, false, level, whenOptimizedOnlyIf);
447
+ map.forEach(({ value, prev, level, loading, remote, whenOptimizedOnlyIf }, node) => {
448
+ computeChangesRecursive(changesInBatch, node, loading, remote, value, [], [], value, prev, false, level, whenOptimizedOnlyIf);
379
449
  });
380
450
  // Once all changes are computed, notify all listeners for each node with the computed changes.
381
- batchNotifyChanges(changesInBatch, false);
382
- }
383
- function batch(fn, onComplete) {
384
- if (onComplete) {
385
- // If there's an onComplete we need a batch that's fully isolated from others to ensure it wraps only the given changes.
386
- // So if already batching, push this batch onto a queue and run it after the current batch is fully done.
387
- if (isRunningBatch) {
388
- _queuedBatches.push([fn, onComplete]);
389
- return;
390
- }
391
- else {
392
- _afterBatch.push(onComplete);
393
- }
451
+ if (changesInBatch.size) {
452
+ batchNotifyChanges(changesInBatch, false);
394
453
  }
454
+ }
455
+ function batch(fn) {
395
456
  beginBatch();
396
457
  try {
397
458
  fn();
@@ -420,45 +481,57 @@ function endBatch(force) {
420
481
  timeout = undefined;
421
482
  }
422
483
  numInBatch = 0;
423
- // Save batch locally and reset _batch first because a new batch could begin while looping over callbacks.
424
- // This can happen with observableComputed for example.
425
- const after = _afterBatch;
426
- if (after.length) {
427
- _afterBatch = [];
428
- }
429
484
  isRunningBatch = true;
430
485
  runBatch();
431
486
  isRunningBatch = false;
432
- // Run after functions at the end of this batch before running the next batch.
433
- // This needs to run before the delayed endBatch because the after functions need
434
- // to run before any side effects of the batch
435
- for (let i = 0; i < after.length; i++) {
436
- after[i]();
437
- }
438
487
  // If an endBatch was delayed run it now
439
488
  if (didDelayEndBatch) {
440
489
  didDelayEndBatch = false;
441
490
  endBatch(true);
442
491
  }
443
- const queued = _queuedBatches;
444
- if (queued.length) {
445
- _queuedBatches = [];
446
- for (let i = 0; i < queued.length; i++) {
447
- const [fn, onComplete] = queued[i];
448
- batch(fn, onComplete);
449
- }
450
- }
451
492
  }
452
493
  }
453
494
  }
495
+ function getNodeAtPath(obj, path) {
496
+ let o = obj;
497
+ for (let i = 0; i < path.length; i++) {
498
+ const p = path[i];
499
+ o = getChildNode(o, p);
500
+ }
501
+ return o;
502
+ }
454
503
 
455
- function isEvent(obs) {
456
- var _a;
457
- return obs && ((_a = obs[symbolGetNode]) === null || _a === void 0 ? void 0 : _a.isEvent);
504
+ function createObservable(value, makePrimitive, extractPromise, createObject, createPrimitive) {
505
+ if (isObservable(value)) {
506
+ return value;
507
+ }
508
+ const valueIsPromise = isPromise(value);
509
+ const valueIsFunction = isFunction(value);
510
+ const root = {
511
+ _: value,
512
+ };
513
+ let node = {
514
+ root,
515
+ lazy: true,
516
+ };
517
+ if (valueIsFunction) {
518
+ node = Object.assign(() => { }, node);
519
+ node.lazyFn = value;
520
+ }
521
+ const prim = makePrimitive || isActualPrimitive(value);
522
+ const obs = prim
523
+ ? new createPrimitive(node)
524
+ : createObject(node);
525
+ if (valueIsPromise) {
526
+ setNodeValue(node, undefined);
527
+ extractPromise(node, value);
528
+ }
529
+ return obs;
458
530
  }
531
+
459
532
  function computeSelector(selector, e, retainObservable) {
460
533
  let c = selector;
461
- if (isFunction(c)) {
534
+ if (!isObservable(c) && isFunction(c)) {
462
535
  c = e ? c(e) : c();
463
536
  }
464
537
  return isObservable(c) && !retainObservable ? c.get() : c;
@@ -474,45 +547,49 @@ function opaqueObject(value) {
474
547
  }
475
548
  return value;
476
549
  }
477
- function lockObservable(obs, value) {
478
- var _a;
479
- const root = (_a = getNode(obs)) === null || _a === void 0 ? void 0 : _a.root;
480
- if (root) {
481
- root.locked = value;
482
- }
483
- }
484
- function setAtPath(obj, path, pathTypes, value, fullObj, restore) {
550
+ function setAtPath(obj, path, pathTypes, value, mode, fullObj, restore) {
485
551
  let o = obj;
486
552
  let oFull = fullObj;
553
+ let p = undefined;
487
554
  if (path.length > 0) {
488
555
  for (let i = 0; i < path.length; i++) {
489
- const p = path[i];
490
- if (i === path.length - 1) {
491
- // Don't set if the value is the same. This prevents creating a new key
492
- // when setting undefined on an object without this key
493
- if (o[p] !== value) {
494
- o[p] = value;
495
- }
496
- }
497
- else if (o[p] === symbolDelete) {
556
+ p = path[i];
557
+ if (o[p] === symbolDelete) {
498
558
  // If this was previously deleted, restore it
499
559
  if (oFull) {
500
560
  o[p] = oFull[p];
501
561
  restore === null || restore === void 0 ? void 0 : restore(path.slice(0, i + 1), o[p]);
502
562
  }
503
- break;
563
+ return obj;
504
564
  }
505
565
  else if (o[p] === undefined || o[p] === null) {
506
- o[p] = pathTypes[i] === 'array' ? [] : {};
566
+ o[p] = initializePathType(pathTypes[i]);
507
567
  }
508
- o = o[p];
509
- if (oFull) {
510
- oFull = oFull[p];
568
+ if (i < path.length - 1) {
569
+ o = o[p];
570
+ if (oFull) {
571
+ oFull = oFull[p];
572
+ }
511
573
  }
512
574
  }
513
575
  }
576
+ // Don't set if the value is the same. This prevents creating a new key
577
+ // when setting undefined on an object without this key
578
+ if (p === undefined) {
579
+ if (mode === 'merge') {
580
+ obj = _mergeIntoObservable(obj, value);
581
+ }
582
+ else {
583
+ obj = value;
584
+ }
585
+ }
514
586
  else {
515
- obj = value;
587
+ if (mode === 'merge') {
588
+ o[p] = _mergeIntoObservable(o[p], value);
589
+ }
590
+ else {
591
+ o[p] = value;
592
+ }
516
593
  }
517
594
  return obj;
518
595
  }
@@ -521,8 +598,8 @@ function setInObservableAtPath(obs, path, pathTypes, value, mode) {
521
598
  let v = value;
522
599
  for (let i = 0; i < path.length; i++) {
523
600
  const p = path[i];
524
- if (!o.peek()[p] && pathTypes[i] === 'array') {
525
- o[p].set([]);
601
+ if (!o.peek()[p]) {
602
+ o[p].set(initializePathType(pathTypes[i]));
526
603
  }
527
604
  o = o[p];
528
605
  v = v[p];
@@ -534,6 +611,9 @@ function setInObservableAtPath(obs, path, pathTypes, value, mode) {
534
611
  else if (mode === 'assign' && o.assign && isObject(o.peek())) {
535
612
  o.assign(v);
536
613
  }
614
+ else if (mode === 'merge') {
615
+ mergeIntoObservable(o, v);
616
+ }
537
617
  else {
538
618
  o.set(v);
539
619
  }
@@ -563,7 +643,9 @@ function _mergeIntoObservable(target, source) {
563
643
  const key = keys[i];
564
644
  const sourceValue = source[key];
565
645
  if (sourceValue === symbolDelete) {
566
- needsSet && ((_a = target[key]) === null || _a === void 0 ? void 0 : _a.delete) ? target[key].delete() : delete target[key];
646
+ needsSet && ((_a = target[key]) === null || _a === void 0 ? void 0 : _a.delete)
647
+ ? target[key].delete()
648
+ : delete target[key];
567
649
  }
568
650
  else {
569
651
  const isObj = isObject(sourceValue);
@@ -596,7 +678,7 @@ function constructObjectWithPath(path, pathTypes, value) {
596
678
  let o = (out = {});
597
679
  for (let i = 0; i < path.length; i++) {
598
680
  const p = path[i];
599
- o[p] = i === path.length - 1 ? value : pathTypes[i] === 'array' ? [] : {};
681
+ o[p] = i === path.length - 1 ? value : initializePathType(pathTypes[i]);
600
682
  o = o[p];
601
683
  }
602
684
  }
@@ -609,7 +691,7 @@ function deconstructObjectWithPath(path, pathTypes, value) {
609
691
  let o = value;
610
692
  for (let i = 0; i < path.length; i++) {
611
693
  const p = path[i];
612
- o = o ? o[p] : pathTypes[i] === 'array' ? [] : {};
694
+ o = o ? o[p] : initializePathType(pathTypes[i]);
613
695
  }
614
696
  return o;
615
697
  }
@@ -620,8 +702,21 @@ function setSilently(obs, newValue) {
620
702
  const node = getNode(obs);
621
703
  return setNodeValue(node, newValue).newValue;
622
704
  }
705
+ function initializePathType(pathType) {
706
+ switch (pathType) {
707
+ case 'array':
708
+ return [];
709
+ case 'object':
710
+ return {};
711
+ case 'map':
712
+ return new Map();
713
+ case 'set':
714
+ return new Set();
715
+ }
716
+ }
623
717
 
624
- function onChange(node, callback, options = {}) {
718
+ function onChange(node, callback, options = {}, fromLinks) {
719
+ var _a;
625
720
  const { initial, immediate, noArgs } = options;
626
721
  const { trackingType } = options;
627
722
  let listeners = immediate ? node.listenersImmediate : node.listeners;
@@ -634,7 +729,6 @@ function onChange(node, callback, options = {}) {
634
729
  node.listeners = listeners;
635
730
  }
636
731
  }
637
- checkActivate(node);
638
732
  const listener = {
639
733
  listener: callback,
640
734
  track: trackingType,
@@ -645,6 +739,8 @@ function onChange(node, callback, options = {}) {
645
739
  const value = getNodeValue(node);
646
740
  callback({
647
741
  value,
742
+ loading: true,
743
+ remote: false,
648
744
  changes: [
649
745
  {
650
746
  path: [],
@@ -656,7 +752,80 @@ function onChange(node, callback, options = {}) {
656
752
  getPrevious: () => undefined,
657
753
  });
658
754
  }
659
- return () => listeners.delete(listener);
755
+ let extraDisposes;
756
+ function addLinkedNodeListeners(childNode, cb = callback, from) {
757
+ // Don't add listeners for the same node more than once
758
+ if (!(fromLinks === null || fromLinks === void 0 ? void 0 : fromLinks.has(childNode))) {
759
+ fromLinks || (fromLinks = new Set());
760
+ fromLinks.add(from || node);
761
+ cb || (cb = callback);
762
+ const childOptions = {
763
+ trackingType: true,
764
+ ...options,
765
+ };
766
+ // onChange for the linked node
767
+ extraDisposes = [...(extraDisposes || []), onChange(childNode, cb, childOptions, fromLinks)];
768
+ }
769
+ }
770
+ // Add listeners for linked to nodes
771
+ if (node.linkedToNode) {
772
+ addLinkedNodeListeners(node.linkedToNode);
773
+ }
774
+ // Add listeners for linked from nodes
775
+ (_a = node.linkedFromNodes) === null || _a === void 0 ? void 0 : _a.forEach((linkedFromNode) => addLinkedNodeListeners(linkedFromNode));
776
+ // Go up through the parents and add listeners for linked from nodes
777
+ let parent = node.parent;
778
+ let pathParent = [node.key];
779
+ while (parent) {
780
+ if (parent.linkedFromNodes) {
781
+ for (const linkedFromNode of parent.linkedFromNodes) {
782
+ if (!(fromLinks === null || fromLinks === void 0 ? void 0 : fromLinks.has(linkedFromNode))) {
783
+ const cb = createCb(linkedFromNode, pathParent, callback);
784
+ addLinkedNodeListeners(linkedFromNode, cb, parent);
785
+ }
786
+ }
787
+ }
788
+ pathParent = [parent.key, ...pathParent];
789
+ parent = parent.parent;
790
+ }
791
+ return () => {
792
+ listeners.delete(listener);
793
+ extraDisposes === null || extraDisposes === void 0 ? void 0 : extraDisposes.forEach((fn) => fn());
794
+ };
795
+ }
796
+ function createCb(linkedFromNode, path, callback) {
797
+ // Create a callback for a path that calls it with the current value at the path
798
+ let { valueAtPath: prevAtPath } = getValueAtPath(getNodeValue(linkedFromNode), path);
799
+ return function ({ value: valueA, loading, remote }) {
800
+ const { valueAtPath, pathTypes } = getValueAtPath(valueA, path);
801
+ if (valueAtPath !== prevAtPath) {
802
+ callback({
803
+ value: valueAtPath,
804
+ loading,
805
+ remote,
806
+ changes: [
807
+ {
808
+ path,
809
+ pathTypes,
810
+ prevAtPath,
811
+ valueAtPath,
812
+ },
813
+ ],
814
+ getPrevious: () => prevAtPath,
815
+ });
816
+ }
817
+ prevAtPath = valueAtPath;
818
+ };
819
+ }
820
+ function getValueAtPath(obj, path) {
821
+ let o = obj;
822
+ const pathTypes = [];
823
+ for (let i = 0; o && i < path.length; i++) {
824
+ pathTypes.push(isArray(o) ? 'array' : 'object');
825
+ const p = path[i];
826
+ o = o[p];
827
+ }
828
+ return { valueAtPath: o, pathTypes };
660
829
  }
661
830
 
662
831
  function setupTracking(nodes, update, noArgs, immediate) {
@@ -717,42 +886,39 @@ function updateTracking(node, track) {
717
886
 
718
887
  function trackSelector(selector, update, observeEvent, observeOptions, createResubscribe) {
719
888
  var _a;
720
- let nodes;
721
- let value;
722
889
  let dispose;
723
- let tracker;
724
890
  let resubscribe;
725
891
  let updateFn = update;
726
- if (isObservable(selector)) {
727
- value = selector.peek();
728
- dispose = selector.onChange(update);
729
- resubscribe = createResubscribe ? selector.onChange(update) : undefined;
730
- }
731
- else {
732
- // Compute the selector inside a tracking context
733
- beginTracking();
734
- value = selector ? computeSelector(selector, observeEvent, observeOptions === null || observeOptions === void 0 ? void 0 : observeOptions.fromComputed) : selector;
735
- tracker = tracking.current;
736
- nodes = tracker.nodes;
737
- endTracking();
738
- if ((process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') && tracker && nodes) {
739
- (_a = tracker.traceListeners) === null || _a === void 0 ? void 0 : _a.call(tracker, nodes);
740
- if (tracker.traceUpdates) {
741
- updateFn = tracker.traceUpdates(update);
742
- }
743
- // Clear tracing so it doesn't leak to other components
744
- tracker.traceListeners = undefined;
745
- tracker.traceUpdates = undefined;
746
- }
892
+ beginTracking();
893
+ const value = selector ? computeSelector(selector, observeEvent, observeOptions === null || observeOptions === void 0 ? void 0 : observeOptions.fromComputed) : selector;
894
+ const tracker = tracking.current;
895
+ const nodes = tracker.nodes;
896
+ endTracking();
897
+ if ((process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') && tracker && nodes) {
898
+ (_a = tracker.traceListeners) === null || _a === void 0 ? void 0 : _a.call(tracker, nodes);
899
+ if (tracker.traceUpdates) {
900
+ updateFn = tracker.traceUpdates(update);
901
+ }
902
+ // Clear tracing so it doesn't leak to other components
903
+ tracker.traceListeners = undefined;
904
+ tracker.traceUpdates = undefined;
747
905
  }
748
906
  if (!(observeEvent === null || observeEvent === void 0 ? void 0 : observeEvent.cancel)) {
749
907
  // Do tracing if it was requested
750
908
  // useSyncExternalStore doesn't subscribe until after the component mount.
751
909
  // We want to subscribe immediately so we don't miss any updates
752
910
  dispose = setupTracking(nodes, updateFn, false, observeOptions === null || observeOptions === void 0 ? void 0 : observeOptions.immediate);
753
- resubscribe = createResubscribe ? () => setupTracking(nodes, updateFn) : undefined;
911
+ if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
912
+ resubscribe = createResubscribe
913
+ ? () => {
914
+ dispose === null || dispose === void 0 ? void 0 : dispose();
915
+ dispose = setupTracking(nodes, updateFn);
916
+ return dispose;
917
+ }
918
+ : undefined;
919
+ }
754
920
  }
755
- return { value, dispose, resubscribe };
921
+ return { nodes, value, dispose, resubscribe };
756
922
  }
757
923
 
758
924
  function observe(selectorOrRun, reactionOrOptions, options) {
@@ -777,9 +943,11 @@ function observe(selectorOrRun, reactionOrOptions, options) {
777
943
  delete e.value;
778
944
  // Dispose listeners from previous run
779
945
  dispose === null || dispose === void 0 ? void 0 : dispose();
780
- const { dispose: _dispose, value } = trackSelector(selectorOrRun, update, e, options);
946
+ const { dispose: _dispose, value, nodes } = trackSelector(selectorOrRun, update, e, options);
781
947
  dispose = _dispose;
782
948
  e.value = value;
949
+ e.nodes = nodes;
950
+ e.refresh = update;
783
951
  if (e.onCleanupReaction) {
784
952
  e.onCleanupReaction();
785
953
  e.onCleanupReaction = undefined;
@@ -787,7 +955,9 @@ function observe(selectorOrRun, reactionOrOptions, options) {
787
955
  endBatch();
788
956
  // Call the reaction if there is one and the value changed
789
957
  if (reaction &&
790
- ((options === null || options === void 0 ? void 0 : options.fromComputed) || ((e.num > 0 || !isEvent(selectorOrRun)) && e.previous !== e.value))) {
958
+ ((options === null || options === void 0 ? void 0 : options.fromComputed) ||
959
+ ((e.num > 0 || !isEvent(selectorOrRun)) &&
960
+ (e.previous !== e.value || typeof e.value === 'object')))) {
791
961
  reaction(e);
792
962
  }
793
963
  // Update the previous value
@@ -813,6 +983,7 @@ function _when(predicate, effect, checkReady) {
813
983
  return effect ? predicate.then(effect) : predicate;
814
984
  }
815
985
  let value;
986
+ let effectValue;
816
987
  // Create a wrapping fn that calls the effect if predicate returns true
817
988
  function run(e) {
818
989
  const ret = computeSelector(predicate);
@@ -825,7 +996,7 @@ function _when(predicate, effect, checkReady) {
825
996
  }
826
997
  function doEffect() {
827
998
  // If value is truthy then run the effect
828
- effect === null || effect === void 0 ? void 0 : effect(value);
999
+ effectValue = effect === null || effect === void 0 ? void 0 : effect(value);
829
1000
  }
830
1001
  // Run in an observe
831
1002
  observe(run, doEffect);
@@ -835,17 +1006,17 @@ function _when(predicate, effect, checkReady) {
835
1006
  return effect ? value.then(effect) : value;
836
1007
  }
837
1008
  else if (value !== undefined) {
838
- return Promise.resolve(value);
1009
+ return effect ? effectValue : Promise.resolve(value);
839
1010
  }
840
1011
  else {
841
1012
  // Wrap it in a promise
842
1013
  const promise = new Promise((resolve) => {
843
1014
  if (effect) {
844
1015
  const originalEffect = effect;
845
- effect = (value) => {
1016
+ effect = ((value) => {
846
1017
  const effectValue = originalEffect(value);
847
- resolve(effectValue);
848
- };
1018
+ resolve(isPromise(effectValue) ? effectValue.then((value) => value) : effectValue);
1019
+ });
849
1020
  }
850
1021
  else {
851
1022
  effect = resolve;
@@ -861,64 +1032,6 @@ function whenReady(predicate, effect) {
861
1032
  return _when(predicate, effect, true);
862
1033
  }
863
1034
 
864
- function createObservable(value, makePrimitive, extractPromise, createObject, createPrimitive) {
865
- const valueIsPromise = isPromise(value);
866
- const valueIsFunction = isFunction(value);
867
- const root = {
868
- _: value,
869
- };
870
- let node = {
871
- root,
872
- lazy: true,
873
- };
874
- if (valueIsFunction) {
875
- node = Object.assign(cloneFunction(value), node);
876
- }
877
- const prim = makePrimitive || isActualPrimitive(value);
878
- const obs = prim
879
- ? new createPrimitive(node)
880
- : createObject(node);
881
- if (valueIsPromise) {
882
- setNodeValue(node, undefined);
883
- extractPromise(node, value);
884
- }
885
- return obs;
886
- }
887
-
888
- function setupRetry(retryOptions, refresh, attemptNum) {
889
- const timeout = {};
890
- let didGiveUp = false;
891
- const { backoff, delay = 1000, infinite, times = 3, maxDelay = 30000 } = retryOptions;
892
- let handleError;
893
- attemptNum.current++;
894
- if (infinite || attemptNum.current < times) {
895
- const delayTime = Math.min(delay * (backoff === 'constant' ? 1 : 2 ** attemptNum.current), maxDelay);
896
- handleError = () => {
897
- timeout.current = setTimeout(refresh, delayTime);
898
- };
899
- }
900
- else {
901
- handleError = () => {
902
- didGiveUp = true;
903
- };
904
- }
905
- if (typeof window !== 'undefined') {
906
- window.addEventListener('online', () => {
907
- if (didGiveUp || timeout) {
908
- if (timeout) {
909
- clearTimeout(timeout.current);
910
- timeout.current = undefined;
911
- }
912
- // Restart the backoff when coming back online
913
- attemptNum.current = 0;
914
- didGiveUp = false;
915
- refresh();
916
- }
917
- });
918
- }
919
- return { handleError, timeout };
920
- }
921
-
922
1035
  const ArrayModifiers = new Set([
923
1036
  'copyWithin',
924
1037
  'fill',
@@ -937,9 +1050,9 @@ const ArrayLoopers = new Set([
937
1050
  'find',
938
1051
  'findIndex',
939
1052
  'forEach',
940
- 'includes',
941
1053
  'join',
942
1054
  'map',
1055
+ 'reduce',
943
1056
  'some',
944
1057
  ]);
945
1058
  const ArrayLoopersReturn = new Set(['filter', 'find']);
@@ -959,7 +1072,8 @@ if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
959
1072
  }
960
1073
  function collectionSetter(node, target, prop, ...args) {
961
1074
  var _a;
962
- if (prop === 'push') {
1075
+ if (prop === 'push' && args.length === 1) {
1076
+ // Fast path for push to just append to the end
963
1077
  setKey(node, target.length + '', args[0]);
964
1078
  }
965
1079
  else {
@@ -982,7 +1096,7 @@ function getKeys(obj, isArr, isMap) {
982
1096
  return isArr ? undefined : obj ? (isMap ? Array.from(obj.keys()) : Object.keys(obj)) : [];
983
1097
  }
984
1098
  function updateNodes(parent, obj, prevValue) {
985
- var _a, _b;
1099
+ var _a, _b, _c;
986
1100
  if ((process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') &&
987
1101
  typeof __devUpdateNodes !== 'undefined' &&
988
1102
  isObject(obj)) {
@@ -1011,8 +1125,9 @@ function updateNodes(parent, obj, prevValue) {
1011
1125
  let prevChildrenById;
1012
1126
  let moved;
1013
1127
  const isMap = obj instanceof Map;
1128
+ const isPrevMap = prevValue instanceof Map;
1014
1129
  const keys = getKeys(obj, isArr, isMap);
1015
- const keysPrev = getKeys(prevValue, isArr, isMap);
1130
+ const keysPrev = getKeys(prevValue, isArr, isPrevMap);
1016
1131
  const length = ((_a = (keys || obj)) === null || _a === void 0 ? void 0 : _a.length) || 0;
1017
1132
  const lengthPrev = ((_b = (keysPrev || prevValue)) === null || _b === void 0 ? void 0 : _b.length) || 0;
1018
1133
  let idField;
@@ -1029,15 +1144,22 @@ function updateNodes(parent, obj, prevValue) {
1029
1144
  isIdFieldFunction = isFunction(idField);
1030
1145
  prevChildrenById = new Map();
1031
1146
  moved = [];
1032
- const keysSeen = process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test'
1033
- ? new Set()
1034
- : undefined;
1035
- if (parent.children) {
1036
- for (let i = 0; i < prevValue.length; i++) {
1037
- const p = prevValue[i];
1038
- if (p) {
1039
- const child = parent.children.get(i + '');
1040
- if (child) {
1147
+ }
1148
+ const keysSeen = process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test'
1149
+ ? new Set()
1150
+ : undefined;
1151
+ if (parent.children) {
1152
+ for (let i = 0; i < prevValue.length; i++) {
1153
+ const p = prevValue[i];
1154
+ if (p) {
1155
+ const child = parent.children.get(i + '');
1156
+ if (child) {
1157
+ if (!obj[i]) {
1158
+ // If the previous value is not in the new array and it
1159
+ // is an activated, disable its listeners
1160
+ handleDeletedChild(child, p);
1161
+ }
1162
+ if (idField) {
1041
1163
  const key = isIdFieldFunction
1042
1164
  ? idField(p)
1043
1165
  : p[idField];
@@ -1064,14 +1186,9 @@ function updateNodes(parent, obj, prevValue) {
1064
1186
  if (!keys.includes(key)) {
1065
1187
  hasADiff = true;
1066
1188
  const child = getChildNode(parent, key);
1067
- const prev = isMap ? prevValue.get(key) : prevValue[key];
1189
+ const prev = isPrevMap ? prevValue.get(key) : prevValue[key];
1068
1190
  if (prev !== undefined) {
1069
- if (!isPrimitive(prev)) {
1070
- updateNodes(child, undefined, prev);
1071
- }
1072
- if (child.listeners || child.listenersImmediate) {
1073
- notify(child, undefined, prev, 0);
1074
- }
1191
+ handleDeletedChild(child, prev);
1075
1192
  }
1076
1193
  }
1077
1194
  }
@@ -1082,16 +1199,31 @@ function updateNodes(parent, obj, prevValue) {
1082
1199
  let didMove = false;
1083
1200
  for (let i = 0; i < length; i++) {
1084
1201
  const key = isArr ? i + '' : keys[i];
1085
- const value = isMap ? obj.get(key) : obj[key];
1086
- const prev = isMap ? prevValue === null || prevValue === void 0 ? void 0 : prevValue.get(key) : prevValue === null || prevValue === void 0 ? void 0 : prevValue[key];
1087
- let isDiff = value !== prev;
1202
+ let value = isMap ? obj.get(key) : obj[key];
1203
+ const prev = isPrevMap ? prevValue === null || prevValue === void 0 ? void 0 : prevValue.get(key) : prevValue === null || prevValue === void 0 ? void 0 : prevValue[key];
1204
+ let isDiff = !equals(value, prev);
1088
1205
  if (isDiff) {
1089
1206
  const id = idField && value
1090
1207
  ? isIdFieldFunction
1091
1208
  ? idField(value)
1092
1209
  : value[idField]
1093
1210
  : undefined;
1094
- let child = getChildNode(parent, key);
1211
+ const existingChild = (_c = parent.children) === null || _c === void 0 ? void 0 : _c.get(key);
1212
+ if (isObservable(value)) {
1213
+ const valueNode = getNode(value);
1214
+ if ((existingChild === null || existingChild === void 0 ? void 0 : existingChild.linkedToNode) === valueNode) {
1215
+ const targetValue = getNodeValue(valueNode);
1216
+ isMap ? obj.set(key, targetValue) : (obj[key] = targetValue);
1217
+ continue;
1218
+ }
1219
+ const obs = value;
1220
+ value = () => obs;
1221
+ }
1222
+ let child = getChildNode(parent, key, value);
1223
+ if (!child.lazy && (isFunction(value) || isObservable(value))) {
1224
+ reactivateNode(child, value);
1225
+ peekInternal(child);
1226
+ }
1095
1227
  // Detect moves within an array. Need to move the original proxy to the new position to keep
1096
1228
  // the proxy stable, so that listeners to this node will be unaffected by the array shift.
1097
1229
  if (isArr && id !== undefined) {
@@ -1121,7 +1253,10 @@ function updateNodes(parent, obj, prevValue) {
1121
1253
  if (isDiff) {
1122
1254
  // Array has a new / modified element
1123
1255
  // If object iterate through its children
1124
- if (isPrimitive(value)) {
1256
+ if (isFunction(value) || isObservable(value)) {
1257
+ extractFunctionOrComputed(parent, key, value);
1258
+ }
1259
+ else if (isPrimitive(value)) {
1125
1260
  hasADiff = true;
1126
1261
  }
1127
1262
  else {
@@ -1162,6 +1297,19 @@ function updateNodes(parent, obj, prevValue) {
1162
1297
  }
1163
1298
  return retValue !== null && retValue !== void 0 ? retValue : false;
1164
1299
  }
1300
+ function handleDeletedChild(child, p) {
1301
+ var _a, _b;
1302
+ // If the previous value is not in the new array and it
1303
+ // is an activated, disable its listeners
1304
+ (_a = child.linkedToNodeDispose) === null || _a === void 0 ? void 0 : _a.call(child);
1305
+ (_b = child.activatedObserveDispose) === null || _b === void 0 ? void 0 : _b.call(child);
1306
+ if (!isPrimitive(p)) {
1307
+ updateNodes(child, undefined, p);
1308
+ }
1309
+ if (child.listeners || child.listenersImmediate) {
1310
+ notify(child, undefined, p, 0);
1311
+ }
1312
+ }
1165
1313
  function getProxy(node, p, asFunction) {
1166
1314
  // Get the child node if p prop
1167
1315
  if (p !== undefined)
@@ -1169,6 +1317,15 @@ function getProxy(node, p, asFunction) {
1169
1317
  // Create a proxy if not already cached and return it
1170
1318
  return (node.proxy || (node.proxy = new Proxy(node, proxyHandler)));
1171
1319
  }
1320
+ function flushPending() {
1321
+ // Need to short circuit the computed batching because the user called get() or peek()
1322
+ // in which case the set needs to run immediately so that the values are up to date.
1323
+ if (globalState.pendingNodes.size > 0) {
1324
+ const nodes = Array.from(globalState.pendingNodes.values());
1325
+ globalState.pendingNodes.clear();
1326
+ nodes.forEach((fn) => fn());
1327
+ }
1328
+ }
1172
1329
  const proxyHandler = {
1173
1330
  get(node, p, receiver) {
1174
1331
  var _a;
@@ -1180,12 +1337,19 @@ const proxyHandler = {
1180
1337
  if (p === symbolGetNode) {
1181
1338
  return node;
1182
1339
  }
1183
- const value = peek(node);
1340
+ if (p === 'apply') {
1341
+ const nodeValue = getNodeValue(node);
1342
+ if (isFunction(nodeValue)) {
1343
+ return nodeValue.apply;
1344
+ }
1345
+ }
1346
+ let value = peekInternal(node, /*activateRecursive*/ p === 'get');
1184
1347
  // If this node is linked to another observable then forward to the target's handler.
1185
1348
  // The exception is onChange because it needs to listen to this node for changes.
1186
1349
  // This needs to be below peek because it activates there.
1187
- if (node.linkedToNode && p !== 'onChange') {
1188
- return proxyHandler.get(node.linkedToNode, p, receiver);
1350
+ const targetNode = node.linkedToNode || (value === null || value === void 0 ? void 0 : value[symbolGetNode]);
1351
+ if (targetNode && p !== 'onChange') {
1352
+ return proxyHandler.get(targetNode, p, receiver);
1189
1353
  }
1190
1354
  if (value instanceof Map || value instanceof WeakMap || value instanceof Set || value instanceof WeakSet) {
1191
1355
  const ret = handlerMapSet(node, p, value);
@@ -1196,6 +1360,9 @@ const proxyHandler = {
1196
1360
  const fn = observableFns.get(p);
1197
1361
  // If this is an observable function, call it
1198
1362
  if (fn) {
1363
+ if (p === 'get' || p === 'peek') {
1364
+ flushPending();
1365
+ }
1199
1366
  return function (a, b, c) {
1200
1367
  const l = arguments.length;
1201
1368
  // Array call and apply are slow so micro-optimize this hot path.
@@ -1213,33 +1380,11 @@ const proxyHandler = {
1213
1380
  }
1214
1381
  };
1215
1382
  }
1216
- if (node.isComputed) {
1217
- if (node.proxyFn && !fn) {
1218
- return node.proxyFn(p);
1219
- }
1220
- else {
1221
- checkActivate(node);
1222
- }
1223
- }
1224
1383
  const property = observableProperties.get(p);
1225
1384
  if (property) {
1226
1385
  return property.get(node);
1227
1386
  }
1228
- // TODOV3 Remove this
1229
- const isValuePrimitive = isPrimitive(value);
1230
- // If accessing a key that doesn't already exist, and this node has been activated with extra keys
1231
- // then return the values that were set. This is used by enableLegendStateReact for example.
1232
- if (value === undefined || value === null || isValuePrimitive) {
1233
- if (extraPrimitiveProps.size && (node.isActivatedPrimitive || extraPrimitiveActivators.has(p))) {
1234
- node.isActivatedPrimitive = true;
1235
- const vPrim = extraPrimitiveProps.get(p);
1236
- if (vPrim !== undefined) {
1237
- return isFunction(vPrim) ? vPrim(getProxy(node)) : vPrim;
1238
- }
1239
- }
1240
- }
1241
- // /TODOV3 Remove this
1242
- const vProp = value === null || value === void 0 ? void 0 : value[p];
1387
+ let vProp = value === null || value === void 0 ? void 0 : value[p];
1243
1388
  if (isObject(value) && value[symbolOpaque]) {
1244
1389
  return vProp;
1245
1390
  }
@@ -1252,6 +1397,11 @@ const proxyHandler = {
1252
1397
  return getProxy(node, p, fnOrComputed);
1253
1398
  }
1254
1399
  }
1400
+ if (isNullOrUndefined(value) && vProp === undefined && (ArrayModifiers.has(p) || ArrayLoopers.has(p))) {
1401
+ value = [];
1402
+ setNodeValue(node, value);
1403
+ vProp = value[p];
1404
+ }
1255
1405
  // Handle function calls
1256
1406
  if (isFunction(vProp)) {
1257
1407
  if (isArray(value)) {
@@ -1263,30 +1413,29 @@ const proxyHandler = {
1263
1413
  // Update that this node was accessed for observers
1264
1414
  updateTracking(node);
1265
1415
  return function (cbOrig, thisArg) {
1266
- // If callback needs to run on the observable proxies, use a wrapped callback
1267
- function cbWrapped(_, index, array) {
1268
- return cbOrig(getProxy(node, index + ''), index, array);
1416
+ const isReduce = p === 'reduce';
1417
+ const cbWrapped = isReduce
1418
+ ? (previousValue, currentValue, currentIndex, array) => {
1419
+ return cbOrig(previousValue, getProxy(node, currentIndex + '', currentValue), currentIndex, array);
1420
+ }
1421
+ : (val, index, array) => {
1422
+ return cbOrig(getProxy(node, index + '', val), index, array);
1423
+ };
1424
+ if (isReduce || !ArrayLoopersReturn.has(p)) {
1425
+ return value[p](cbWrapped, thisArg);
1269
1426
  }
1270
- // If return value needs to be observable proxies, use our own looping logic and return the proxy when found
1271
- if (ArrayLoopersReturn.has(p)) {
1272
- const isFind = p === 'find';
1273
- const out = [];
1274
- for (let i = 0; i < value.length; i++) {
1275
- if (cbWrapped(value[i], i, value)) {
1276
- const proxy = getProxy(node, i + '');
1277
- if (isFind) {
1278
- return proxy;
1279
- }
1280
- else {
1281
- out.push(proxy);
1282
- }
1427
+ const isFind = p === 'find';
1428
+ const out = [];
1429
+ for (let i = 0; i < value.length; i++) {
1430
+ if (cbWrapped(value[i], i, value)) {
1431
+ const proxy = getProxy(node, i + '');
1432
+ if (isFind) {
1433
+ return proxy;
1283
1434
  }
1435
+ out.push(proxy);
1284
1436
  }
1285
- return isFind ? undefined : out;
1286
- }
1287
- else {
1288
- return value[p](cbWrapped, thisArg);
1289
1437
  }
1438
+ return isFind ? undefined : out;
1290
1439
  };
1291
1440
  }
1292
1441
  }
@@ -1301,10 +1450,6 @@ const proxyHandler = {
1301
1450
  return vProp;
1302
1451
  }
1303
1452
  }
1304
- // TODOV3: Remove "state"
1305
- if (vProp === undefined && (p === 'state' || p === '_state') && node.state) {
1306
- return node.state;
1307
- }
1308
1453
  // Return an observable proxy to the property
1309
1454
  return getProxy(node, p);
1310
1455
  },
@@ -1314,12 +1459,13 @@ const proxyHandler = {
1314
1459
  return value !== null && typeof value === 'object' ? Reflect.getPrototypeOf(value) : null;
1315
1460
  },
1316
1461
  ownKeys(node) {
1317
- const value = getNodeValue(node);
1462
+ // TODO: Temporary workaround to fix a bug - the first peek may not return the correct value
1463
+ // if the value is a cached. This fixes the test "cache with initial ownKeys"
1464
+ peekInternal(node);
1465
+ const value = get(node, true);
1318
1466
  if (isPrimitive(value))
1319
1467
  return [];
1320
1468
  const keys = value ? Reflect.ownKeys(value) : [];
1321
- // Update that this node was accessed for observers
1322
- updateTracking(node, true);
1323
1469
  // This is required to fix this error:
1324
1470
  // TypeError: 'getOwnPropertyDescriptor' on proxy: trap reported non-configurability for
1325
1471
  // property 'length' which is either non-existent or configurable in the proxy node
@@ -1369,22 +1515,21 @@ const proxyHandler = {
1369
1515
  },
1370
1516
  apply(target, thisArg, argArray) {
1371
1517
  // If it's a function call it as a function
1372
- return Reflect.apply(target, thisArg, argArray);
1518
+ return Reflect.apply(target.lazyFn || target, thisArg, argArray);
1373
1519
  },
1374
1520
  };
1375
1521
  function set(node, newValue) {
1376
1522
  if (node.parent) {
1377
- return setKey(node.parent, node.key, newValue);
1523
+ setKey(node.parent, node.key, newValue);
1378
1524
  }
1379
1525
  else {
1380
- return setKey(node, '_', newValue);
1526
+ setKey(node, '_', newValue);
1381
1527
  }
1382
1528
  }
1383
1529
  function toggle(node) {
1384
1530
  const value = getNodeValue(node);
1385
- if (value === undefined || isBoolean(value)) {
1531
+ if (value === undefined || value === null || isBoolean(value)) {
1386
1532
  set(node, !value);
1387
- return !value;
1388
1533
  }
1389
1534
  else if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
1390
1535
  throw new Error('[legend-state] Cannot toggle a non-boolean value');
@@ -1396,31 +1541,27 @@ function setKey(node, key, newValue, level) {
1396
1541
  console.warn(`[legend-state] Set an HTMLElement into state. You probably don't want to do that.`);
1397
1542
  }
1398
1543
  }
1399
- if (node.root.locked && !node.root.set) {
1400
- // This happens when modifying a locked observable such as a computed.
1401
- // If merging this could be happening deep in a hierarchy so we don't want to throw errors so we'll just do nothing.
1402
- // This could happen during persistence local load for example.
1403
- if (globalState.isMerging) {
1404
- return;
1405
- }
1406
- else {
1407
- throw new Error(process.env.NODE_ENV === 'development'
1408
- ? '[legend-state] Cannot modify an observable while it is locked. Please make sure that you unlock the observable before making changes.'
1409
- : '[legend-state] Modified locked observable');
1410
- }
1411
- }
1412
1544
  const isRoot = !node.parent && key === '_';
1545
+ if (node.parent && !getNodeValue(node) && !isFunction(newValue)) {
1546
+ set(node, { [key]: newValue });
1547
+ }
1413
1548
  // Get the child node for updating and notifying
1414
- const childNode = isRoot ? node : getChildNode(node, key, isFunction(newValue) ? newValue : undefined);
1415
- // Set the raw value on the parent object
1416
- const { newValue: savedValue, prevValue, parentValue } = setNodeValue(childNode, newValue);
1417
- const isFunc = isFunction(savedValue);
1418
- const isPrim = isPrimitive(savedValue) || savedValue instanceof Date;
1419
- if (savedValue !== prevValue) {
1420
- updateNodesAndNotify(node, savedValue, prevValue, childNode, isPrim, isRoot, level);
1549
+ const childNode = isRoot ? node : getChildNode(node, key, newValue);
1550
+ if (isObservable(newValue)) {
1551
+ setToObservable(childNode, newValue);
1552
+ }
1553
+ else {
1554
+ // Set the raw value on the parent object
1555
+ const { newValue: savedValue, prevValue } = setNodeValue(childNode, newValue);
1556
+ const isPrim = isPrimitive(savedValue) || savedValue instanceof Date;
1557
+ if (!equals(savedValue, prevValue)) {
1558
+ updateNodesAndNotify(node, savedValue, prevValue, childNode, isPrim, isRoot, level);
1559
+ }
1560
+ if (!isPrim) {
1561
+ childNode.needsExtract = true;
1562
+ }
1563
+ extractFunctionOrComputed(node, key, savedValue);
1421
1564
  }
1422
- extractFunctionOrComputed(node, parentValue, key, savedValue);
1423
- return isFunc ? savedValue : isRoot ? getProxy(node) : getProxy(node, key);
1424
1565
  }
1425
1566
  function assign(node, value) {
1426
1567
  const proxy = getProxy(node);
@@ -1445,7 +1586,13 @@ function deleteFn(node, key) {
1445
1586
  key = node.key;
1446
1587
  node = node.parent;
1447
1588
  }
1448
- setKey(node, key !== null && key !== void 0 ? key : '_', symbolDelete, /*level*/ -1);
1589
+ const value = getNodeValue(node);
1590
+ if (isArray(value)) {
1591
+ collectionSetter(node, value, 'splice', key, 1);
1592
+ }
1593
+ else {
1594
+ setKey(node, key !== null && key !== void 0 ? key : '_', symbolDelete, /*level*/ -1);
1595
+ }
1449
1596
  }
1450
1597
  function handlerMapSet(node, p, value) {
1451
1598
  const vProp = value === null || value === void 0 ? void 0 : value[p];
@@ -1525,8 +1672,11 @@ function handlerMapSet(node, p, value) {
1525
1672
  function updateNodesAndNotify(node, newValue, prevValue, childNode, isPrim, isRoot, level) {
1526
1673
  if (!childNode)
1527
1674
  childNode = node;
1528
- // Make sure we don't call too many listeners for ever property set
1675
+ // Make sure we don't call too many listeners for every property set
1529
1676
  beginBatch();
1677
+ if (isPrim === undefined) {
1678
+ isPrim = isPrimitive(newValue);
1679
+ }
1530
1680
  let hasADiff = isPrim;
1531
1681
  let whenOptimizedOnlyIf = false;
1532
1682
  // If new value is an object or array update notify down the tree
@@ -1546,7 +1696,7 @@ function updateNodesAndNotify(node, newValue, prevValue, childNode, isPrim, isRo
1546
1696
  }
1547
1697
  endBatch();
1548
1698
  }
1549
- function extractPromise(node, value, setter, handleError) {
1699
+ function extractPromise(node, value, setter) {
1550
1700
  if (!node.state) {
1551
1701
  node.state = createObservable({
1552
1702
  isLoaded: false,
@@ -1562,30 +1712,29 @@ function extractPromise(node, value, setter, handleError) {
1562
1712
  })
1563
1713
  .catch((error) => {
1564
1714
  node.state.error.set(error);
1565
- if (handleError) {
1566
- handleError(error);
1567
- }
1568
1715
  });
1569
1716
  }
1570
- function extractFunctionOrComputed(node, obj, k, v) {
1717
+ function extractFunctionOrComputed(node, k, v, activateRecursive) {
1571
1718
  if (isPromise(v)) {
1572
1719
  const childNode = getChildNode(node, k);
1573
1720
  extractPromise(childNode, v);
1574
1721
  setNodeValue(childNode, undefined);
1575
1722
  }
1723
+ else if (isObservable(v)) {
1724
+ const fn = () => v;
1725
+ extractFunction(node, k, fn);
1726
+ const childNode = getChildNode(node, k, fn);
1727
+ const targetNode = getNode(v);
1728
+ // Set node to target's value if activating or it's already activated
1729
+ const initialValue = activateRecursive || !targetNode.lazy ? peekInternal(targetNode) : undefined;
1730
+ setNodeValue(childNode, initialValue);
1731
+ }
1576
1732
  else if (typeof v === 'function') {
1577
1733
  extractFunction(node, k, v);
1578
- delete obj[k];
1579
1734
  }
1580
- else if (typeof v == 'object' && v !== null && v !== undefined) {
1581
- const childNode = getNode(v);
1582
- if (childNode === null || childNode === void 0 ? void 0 : childNode.isComputed) {
1583
- extractFunction(node, k, v, childNode);
1584
- delete obj[k];
1585
- }
1586
- else {
1587
- return true;
1588
- }
1735
+ else if (activateRecursive && typeof v == 'object' && v !== null && v !== undefined) {
1736
+ const childNode = getChildNode(node, k);
1737
+ checkLazy(childNode, v, activateRecursive);
1589
1738
  }
1590
1739
  }
1591
1740
  function get(node, options) {
@@ -1595,210 +1744,285 @@ function get(node, options) {
1595
1744
  return peek(node);
1596
1745
  }
1597
1746
  function peek(node) {
1598
- const value = getNodeValue(node);
1747
+ const value = peekInternal(node, true);
1748
+ return value;
1749
+ }
1750
+ function peekInternal(node, activateRecursive) {
1751
+ if (node.dirtyFn) {
1752
+ node.dirtyFn();
1753
+ globalState.dirtyNodes.delete(node);
1754
+ node.dirtyFn = undefined;
1755
+ }
1756
+ let value = getNodeValue(node);
1757
+ value = checkLazy(node, value, !!activateRecursive);
1758
+ return value;
1759
+ }
1760
+ function checkLazy(node, value, activateRecursive) {
1599
1761
  // If node is not yet lazily computed go do that
1600
1762
  const lazy = node.lazy;
1601
1763
  if (lazy) {
1764
+ const lazyFn = node.lazyFn;
1602
1765
  delete node.lazy;
1603
- if (isFunction(node) || isFunction(lazy)) {
1604
- activateNodeFunction(node, lazy);
1605
- }
1606
- else {
1607
- for (const key in value) {
1608
- if (hasOwnProperty.call(value, key)) {
1609
- extractFunctionOrComputed(node, value, key, value[key]);
1766
+ if (isFunction(lazyFn)) {
1767
+ if (lazyFn.length === 1) {
1768
+ // This is a lookup function, so return a record object
1769
+ value = {};
1770
+ }
1771
+ else {
1772
+ if (node.parent) {
1773
+ const parentValue = getNodeValue(node.parent);
1774
+ if (parentValue) {
1775
+ delete parentValue[node.key];
1776
+ }
1777
+ else {
1778
+ node.root._ = undefined;
1779
+ }
1610
1780
  }
1781
+ value = activateNodeFunction(node, lazyFn);
1782
+ }
1783
+ }
1784
+ }
1785
+ if (lazy || node.needsExtract || activateRecursive) {
1786
+ for (const key in value) {
1787
+ if (hasOwnProperty.call(value, key)) {
1788
+ extractFunctionOrComputed(node, key, value[key], activateRecursive);
1611
1789
  }
1612
1790
  }
1613
1791
  }
1614
- // Check if computed needs to activate
1615
- checkActivate(node);
1616
1792
  return value;
1617
1793
  }
1618
- function createNodeActivationParams(node) {
1619
- node.activationState = {
1620
- lastSync: {},
1621
- };
1622
- const state = node.activationState;
1623
- // The onSet function handles the observable being set
1624
- // and forwards the set elsewhere
1625
- const onSet = (onSetFnParam) => {
1626
- state.onSetFn = onSetFnParam;
1627
- };
1628
- // The onSet function handles the observable being set
1629
- // and forwards the set elsewhere
1630
- const updateLastSync = (fn) => {
1631
- state.lastSync.value = fn;
1632
- };
1633
- // The subscribe function runs a function that listens to
1634
- // a data source and sends updates into the observable
1635
- const subscribe = (fn) => {
1636
- if (!state.subscriber) {
1637
- state.subscriber = fn;
1638
- }
1639
- };
1640
- const cache = (fn) => {
1641
- if (!state.cacheOptions) {
1642
- state.cacheOptions = isFunction(fn) ? fn() : fn;
1643
- }
1644
- return new Promise((resolve) => {
1645
- const wait = () => {
1646
- if (node.state) {
1647
- when(node.state.isLoadedLocal, () => {
1648
- const dateModified = node.state.dateModified.get();
1649
- resolve({ dateModified, value: peek(node) });
1650
- });
1651
- }
1652
- else {
1653
- queueMicrotask(wait);
1654
- }
1655
- };
1656
- wait();
1657
- });
1658
- };
1659
- const retry = (params) => {
1660
- if (!state.retryOptions) {
1661
- state.retryOptions = params || {};
1662
- }
1663
- };
1664
- // The proxy function simply marks the node as a proxy with this function
1665
- // so that child nodes will be created with this function, and then simply
1666
- // activated as a function
1667
- const proxy = (fn) => {
1668
- node.proxyFn2 = fn;
1669
- };
1670
- return {
1671
- onSet,
1672
- proxy,
1673
- cache,
1674
- retry,
1675
- subscribe,
1676
- updateLastSync,
1677
- obs$: getProxy(node),
1678
- };
1794
+ function reactivateNode(node, lazyFn) {
1795
+ var _a, _b;
1796
+ (_a = node.activatedObserveDispose) === null || _a === void 0 ? void 0 : _a.call(node);
1797
+ (_b = node.linkedToNodeDispose) === null || _b === void 0 ? void 0 : _b.call(node);
1798
+ node.activatedObserveDispose = node.linkedToNodeDispose = node.linkedToNode = undefined;
1799
+ node.lazyFn = lazyFn;
1800
+ node.lazy = true;
1679
1801
  }
1680
1802
  function activateNodeFunction(node, lazyFn) {
1681
- let prevTarget$;
1682
- let curTarget$;
1803
+ // let prevTarget$: Observable<any>;
1804
+ // let curTarget$: Observable<any>;
1683
1805
  let update;
1684
1806
  let wasPromise;
1685
- let timeoutRetry;
1686
- const attemptNum = { current: 0 };
1687
- const activator = (isFunction(node) ? node : lazyFn);
1688
- const refresh = () => node.state.refreshNum.set((v) => v + 1);
1689
- observe(() => {
1690
- const params = createNodeActivationParams(node);
1807
+ let ignoreThisUpdate;
1808
+ const activateFn = lazyFn;
1809
+ let activatedValue;
1810
+ let disposes = [];
1811
+ let refreshFn;
1812
+ function markDirty() {
1813
+ node.dirtyFn = refreshFn;
1814
+ globalState.dirtyNodes.add(node);
1815
+ }
1816
+ node.activatedObserveDispose = observe(() => {
1817
+ var _a, _b, _c, _d;
1691
1818
  // Run the function at this node
1692
- let value = activator(params);
1693
- // If target is an observable, get() it to make sure we listen to its changes
1694
- // and set up an onSet to write changes back to it
1819
+ let value = activateFn();
1820
+ // If target is an observable, make this node a link to it
1695
1821
  if (isObservable(value)) {
1696
- prevTarget$ = curTarget$;
1697
- curTarget$ = value;
1698
- params.onSet(({ value: newValue, getPrevious }) => {
1699
- // Don't set the target observable if the target has changed since the last run
1700
- if (!prevTarget$ || curTarget$ === prevTarget$) {
1701
- // Set the node value back to what it was before before setting it.
1702
- // This is a workaround for linked objects because it might not notify
1703
- // if setting a property of an object
1704
- // TODO: Is there a way to not do this? Or at least only do it in a
1705
- // small subset of cases?
1706
- setNodeValue(getNode(curTarget$), getPrevious());
1707
- // Set the value on the curTarget
1708
- curTarget$.set(newValue);
1709
- }
1710
- });
1711
- // Get the value from the observable because we still want the raw value
1712
- // for the effect.
1713
- value = value.get();
1822
+ value = setToObservable(node, value);
1714
1823
  }
1715
- else {
1716
- wasPromise = isPromise(value) ? value : undefined;
1824
+ if (isFunction(value)) {
1825
+ value = value();
1826
+ }
1827
+ const activated = !isObservable(value)
1828
+ ? value === null || value === void 0 ? void 0 : value[symbolLinked]
1829
+ : undefined;
1830
+ if (activated) {
1831
+ node.activationState = activated;
1832
+ value = undefined;
1717
1833
  }
1834
+ ignoreThisUpdate = false;
1835
+ wasPromise = isPromise(value);
1718
1836
  // Activate this node if not activated already (may be called recursively)
1719
1837
  // TODO: Is calling recursively bad? If so can it be fixed?
1720
1838
  if (!node.activated) {
1721
1839
  node.activated = true;
1722
- const activateNodeFn = wasPromise ? globalState.activateNode : activateNodeBase;
1723
- update = activateNodeFn(node, refresh, !!wasPromise, value).update;
1840
+ let activateNodeFn = activateNodeBase;
1841
+ // If this is a Synced then run it through persistence instead of base
1842
+ if (activated === null || activated === void 0 ? void 0 : activated.synced) {
1843
+ activateNodeFn = globalState.activateSyncedNode;
1844
+ ignoreThisUpdate = true;
1845
+ }
1846
+ const { update: newUpdate, value: newValue } = activateNodeFn(node, value);
1847
+ update = newUpdate;
1848
+ value = newValue !== null && newValue !== void 0 ? newValue : activated === null || activated === void 0 ? void 0 : activated.initial;
1849
+ }
1850
+ else if (node.activationState) {
1851
+ const activated = node.activationState;
1852
+ if ((_b = (_a = node.state) === null || _a === void 0 ? void 0 : _a.peek()) === null || _b === void 0 ? void 0 : _b.sync) {
1853
+ node.state.sync();
1854
+ ignoreThisUpdate = true;
1855
+ }
1856
+ else {
1857
+ value = (_d = (_c = activated.get) === null || _c === void 0 ? void 0 : _c.call(activated)) !== null && _d !== void 0 ? _d : activated.initial;
1858
+ }
1724
1859
  }
1725
- node.state.refreshNum.get();
1860
+ // value is undefined if it's in a persisted retry
1861
+ wasPromise = wasPromise || isPromise(value);
1726
1862
  return value;
1727
- }, ({ value }) => {
1728
- if (!globalState.isLoadingRemote$.peek()) {
1729
- if (wasPromise) {
1730
- const { retryOptions } = node.activationState;
1731
- let onError;
1732
- if (retryOptions) {
1733
- if (timeoutRetry) {
1734
- clearTimeout(timeoutRetry.current);
1863
+ }, (e) => {
1864
+ if (!ignoreThisUpdate) {
1865
+ const { value, nodes, refresh } = e;
1866
+ refreshFn = refresh;
1867
+ if (!wasPromise || !globalState.isLoadingRemote) {
1868
+ if (wasPromise) {
1869
+ if (node.activationState) {
1870
+ const { initial } = node.activationState;
1871
+ if (value && isPromise(value)) {
1872
+ // Extract the promise to make it set the value/error when it comes in
1873
+ extractPromise(node, value, update);
1874
+ }
1875
+ // Set this to undefined only if it's replacing the activation function,
1876
+ // so we don't overwrite it if it already has real data from either local
1877
+ // cache or a previous run
1878
+ if (isFunction(getNodeValue(node))) {
1879
+ setNodeValue(node, initial !== null && initial !== void 0 ? initial : undefined);
1880
+ }
1881
+ }
1882
+ else if (node.activated) {
1883
+ // Extract the promise to make it set the value/error when it comes in
1884
+ extractPromise(node, value, update);
1885
+ // Set this to undefined only if it's replacing the activation function,
1886
+ // so we don't overwrite it if it already has real data from either local
1887
+ // cache or a previous run
1888
+ if (isFunction(getNodeValue(node))) {
1889
+ setNodeValue(node, undefined);
1890
+ }
1735
1891
  }
1736
- const { handleError, timeout } = setupRetry(retryOptions, refresh, attemptNum);
1737
- onError = handleError;
1738
- timeoutRetry = timeout;
1739
1892
  }
1740
- // Extract the promise to make it set the value/error when it comes in
1741
- extractPromise(node, value, update, onError);
1742
- // Set this to undefined only if it's replacing the activation function,
1743
- // so we don't overwrite it if it already has real data from either local
1744
- // cache or a previous run
1745
- if (isFunction(getNodeValue(node))) {
1746
- setNodeValue(node, undefined);
1893
+ else {
1894
+ activatedValue = value;
1895
+ if (node.state.isLoaded.peek()) {
1896
+ node.isComputing = true;
1897
+ set(node, value);
1898
+ node.isComputing = false;
1899
+ }
1900
+ else {
1901
+ setNodeValue(node, value);
1902
+ node.state.assign({
1903
+ isLoaded: true,
1904
+ error: undefined,
1905
+ });
1906
+ }
1747
1907
  }
1748
1908
  }
1749
- else {
1750
- set(node, value);
1751
- node.state.assign({
1752
- isLoaded: true,
1753
- error: undefined,
1754
- });
1755
- }
1909
+ disposes.forEach((fn) => fn());
1910
+ disposes = [];
1911
+ nodes === null || nodes === void 0 ? void 0 : nodes.forEach(({ node }) => {
1912
+ disposes.push(onChange(node, markDirty, { immediate: true }));
1913
+ });
1756
1914
  }
1757
- }, { immediate: true, fromComputed: true });
1915
+ e.cancel = true;
1916
+ }, { fromComputed: true });
1917
+ return activatedValue;
1758
1918
  }
1759
- const activateNodeBase = (globalState.activateNode = function activateNodeBase(node, refresh, wasPromise) {
1760
- const { onSetFn, subscriber } = node.activationState;
1761
- let isSetting = false;
1919
+ function activateNodeBase(node, value) {
1762
1920
  if (!node.state) {
1763
1921
  node.state = createObservable({
1764
1922
  isLoaded: false,
1765
1923
  }, false, extractPromise, getProxy);
1766
1924
  }
1767
- if (onSetFn) {
1768
- const doSet = (params) => {
1769
- // Don't call the set if this is the first value coming in
1770
- if (!isSetting) {
1771
- if ((!wasPromise || node.state.isLoaded.get()) &&
1772
- (params.changes.length > 1 || !isFunction(params.changes[0].prevAtPath))) {
1773
- isSetting = true;
1774
- batch(() => onSetFn(params, { update }), () => {
1775
- isSetting = false;
1776
- });
1925
+ if (node.activationState) {
1926
+ const { set: setFn, get: getFn, initial } = node.activationState;
1927
+ value = getFn === null || getFn === void 0 ? void 0 : getFn();
1928
+ if (value == undefined || value === null) {
1929
+ value = initial;
1930
+ }
1931
+ if (setFn) {
1932
+ let allChanges = [];
1933
+ let latestValue = undefined;
1934
+ let runNumber = 0;
1935
+ const runChanges = (listenerParams) => {
1936
+ // Don't call the set if this is the first value coming in
1937
+ if (allChanges.length > 0) {
1938
+ let changes;
1939
+ let value;
1940
+ let loading = false;
1941
+ let remote = false;
1942
+ let getPrevious;
1943
+ if (listenerParams) {
1944
+ changes = listenerParams.changes;
1945
+ value = listenerParams.value;
1946
+ loading = listenerParams.loading;
1947
+ remote = listenerParams.remote;
1948
+ getPrevious = listenerParams.getPrevious;
1949
+ }
1950
+ else {
1951
+ // If this is called by flushPending then get the change array
1952
+ // that we've been building up.
1953
+ changes = allChanges;
1954
+ value = latestValue;
1955
+ getPrevious = createPreviousHandler(value, changes);
1956
+ }
1957
+ allChanges = [];
1958
+ latestValue = undefined;
1959
+ globalState.pendingNodes.delete(node);
1960
+ runNumber++;
1961
+ const thisRunNumber = runNumber;
1962
+ const run = () => {
1963
+ if (thisRunNumber !== runNumber) {
1964
+ // set may get called multiple times before it loads so ignore any previous runs
1965
+ return;
1966
+ }
1967
+ node.isComputing = true;
1968
+ setFn({
1969
+ value,
1970
+ changes,
1971
+ loading,
1972
+ remote,
1973
+ getPrevious,
1974
+ });
1975
+ node.isComputing = false;
1976
+ };
1977
+ whenReady(node.state.isLoaded, run);
1777
1978
  }
1778
- }
1779
- };
1780
- onChange(node, doSet, wasPromise ? undefined : { immediate: true });
1781
- }
1782
- if (process.env.NODE_ENV === 'development' && node.activationState.cacheOptions) {
1783
- // TODO Better message
1784
- console.log('[legend-state] Using cacheOptions without setting up persistence first');
1785
- }
1786
- if (process.env.NODE_ENV === 'development' && node.activationState.retryOptions) {
1787
- // TODO Better message
1788
- console.log('[legend-state] Using retryOptions without setting up persistence first');
1979
+ };
1980
+ const onChangeImmediate = ({ value, changes }) => {
1981
+ if (!node.isComputing) {
1982
+ if (changes.length > 1 || !isFunction(changes[0].prevAtPath)) {
1983
+ latestValue = value;
1984
+ if (allChanges.length > 0) {
1985
+ changes = changes.filter((change) => !isArraySubset(allChanges[0].path, change.path));
1986
+ }
1987
+ allChanges.push(...changes);
1988
+ globalState.pendingNodes.set(node, runChanges);
1989
+ }
1990
+ }
1991
+ };
1992
+ // Create an immediate listener to mark this node as pending. Then actually run
1993
+ // the changes at the end of the batch so everything is properly batched.
1994
+ // However, this can be short circuited if the user calls get() or peek()
1995
+ // in which case the set needs to run immediately so that the values are up to date.
1996
+ onChange(node, onChangeImmediate, { immediate: true });
1997
+ onChange(node, runChanges);
1998
+ }
1789
1999
  }
1790
2000
  const update = ({ value }) => {
1791
- // TODO: This isSetting might not be necessary? Tests still work if removing it.
1792
- // Write tests that would break it if removed? I'd guess a combination of subscribe and
1793
- if (!isSetting) {
2001
+ if (!node.isComputing) {
2002
+ node.isComputing = true;
1794
2003
  set(node, value);
2004
+ node.isComputing = false;
1795
2005
  }
1796
2006
  };
1797
- if (subscriber) {
1798
- subscriber({ update, refresh });
2007
+ return { update, value };
2008
+ }
2009
+ function setToObservable(node, value) {
2010
+ var _a;
2011
+ // If the computed is a proxy to another observable
2012
+ // link it to the target observable
2013
+ const linkedNode = getNode(value);
2014
+ if (linkedNode !== node && (linkedNode === null || linkedNode === void 0 ? void 0 : linkedNode.linkedToNode) !== node) {
2015
+ node.linkedToNode = linkedNode;
2016
+ linkedNode.linkedFromNodes || (linkedNode.linkedFromNodes = new Set());
2017
+ linkedNode.linkedFromNodes.add(node);
2018
+ (_a = node.linkedToNodeDispose) === null || _a === void 0 ? void 0 : _a.call(node);
2019
+ node.linkedToNodeDispose = onChange(linkedNode, () => {
2020
+ value = peek(linkedNode);
2021
+ set(node, value);
2022
+ }, { initial: true }, new Set([node]));
1799
2023
  }
1800
- return { update };
1801
- });
2024
+ return value;
2025
+ }
1802
2026
 
1803
2027
  const fns = ['get', 'set', 'peek', 'onChange', 'toggle'];
1804
2028
  function ObservablePrimitiveClass(node) {
@@ -1815,8 +2039,14 @@ function proto(key, fn) {
1815
2039
  return fn.call(this, this._node, ...args);
1816
2040
  };
1817
2041
  }
1818
- proto('peek', peek);
1819
- proto('get', get);
2042
+ proto('peek', (node) => {
2043
+ flushPending();
2044
+ return peek(node);
2045
+ });
2046
+ proto('get', (node, options) => {
2047
+ flushPending();
2048
+ return get(node, options);
2049
+ });
1820
2050
  proto('set', set);
1821
2051
  proto('onChange', onChange);
1822
2052
  // Getters
@@ -1828,13 +2058,12 @@ Object.defineProperty(ObservablePrimitiveClass.prototype, symbolGetNode, {
1828
2058
  });
1829
2059
  ObservablePrimitiveClass.prototype.toggle = function () {
1830
2060
  const value = this.peek();
1831
- if (value === undefined || isBoolean(value)) {
2061
+ if (value === undefined || value === null || isBoolean(value)) {
1832
2062
  this.set(!value);
1833
2063
  }
1834
2064
  else if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
1835
2065
  throw new Error('[legend-state] Cannot toggle a non-boolean value');
1836
2066
  }
1837
- return !value;
1838
2067
  };
1839
2068
  ObservablePrimitiveClass.prototype.delete = function () {
1840
2069
  this.set(undefined);
@@ -1847,92 +2076,26 @@ function observable(value) {
1847
2076
  function observablePrimitive(value) {
1848
2077
  return createObservable(value, true, extractPromise, getProxy, ObservablePrimitiveClass);
1849
2078
  }
1850
- globalState.isLoadingRemote$ = observable(false);
1851
-
1852
- function computed(compute, set$1) {
1853
- // Create an observable for this computed variable
1854
- const obs = observable();
1855
- lockObservable(obs, true);
2079
+ function syncState(obs) {
1856
2080
  const node = getNode(obs);
1857
- node.isComputed = true;
1858
- let isSetAfterActivated = false;
1859
- const setInner = function (val) {
1860
- const prevNode = node.linkedToNode;
1861
- // If it was previously linked to a node remove self
1862
- // from its linkedFromNodes
1863
- if (prevNode) {
1864
- prevNode.linkedFromNodes.delete(node);
1865
- node.linkedToNode = undefined;
1866
- }
1867
- const { parentOther } = node;
1868
- if (isObservable(val)) {
1869
- // If the computed is a proxy to another observable
1870
- // link it to the target observable
1871
- const linkedNode = getNode(val);
1872
- node.linkedToNode = linkedNode;
1873
- if (!linkedNode.linkedFromNodes) {
1874
- linkedNode.linkedFromNodes = new Set();
1875
- }
1876
- linkedNode.linkedFromNodes.add(node);
1877
- if (node.parentOther) {
1878
- onChange(linkedNode, ({ value }) => {
1879
- setNodeValue(node.parentOther, value);
1880
- }, { initial: true });
1881
- }
1882
- // If the target observable is different then notify for the change
1883
- if (prevNode) {
1884
- const value = getNodeValue(linkedNode);
1885
- const prevValue = getNodeValue(prevNode);
1886
- notify(node, value, prevValue, 0);
1887
- }
1888
- }
1889
- else if (val !== obs.peek()) {
1890
- // Unlock computed node before setting the value
1891
- lockObservable(obs, false);
1892
- const setter = isSetAfterActivated ? set : setNodeValue;
1893
- // Update the computed value
1894
- setter(node, val);
1895
- // If the computed is a child of an observable set the value on it
1896
- if (parentOther) {
1897
- let didUnlock = false;
1898
- if (parentOther.root.locked) {
1899
- parentOther.root.locked = false;
1900
- didUnlock = true;
1901
- }
1902
- setter(parentOther, val);
1903
- if (didUnlock) {
1904
- parentOther.root.locked = true;
1905
- }
1906
- }
1907
- // Re-lock the computed node
1908
- lockObservable(obs, true);
1909
- }
1910
- else if (parentOther) {
1911
- setNodeValue(parentOther, val);
1912
- }
1913
- isSetAfterActivated = true;
1914
- };
1915
- // Lazily activate the observable when get is called
1916
- node.root.activate = () => {
1917
- node.root.activate = undefined;
1918
- observe(compute, ({ value }) => {
1919
- if (isPromise(value)) {
1920
- value.then((v) => setInner(v));
1921
- }
1922
- else {
1923
- setInner(value);
1924
- }
1925
- }, { immediate: true, fromComputed: true });
1926
- };
1927
- if (set$1) {
1928
- node.root.set = (value) => {
1929
- batch(() => set$1(value));
1930
- };
2081
+ if (!node.state) {
2082
+ peekInternal(node);
1931
2083
  }
1932
- return obs;
2084
+ if (!node.state) {
2085
+ node.state = observable({});
2086
+ }
2087
+ return node.state;
2088
+ }
2089
+
2090
+ function computed(get, set) {
2091
+ return observable(set ? () => ({ [symbolLinked]: { get, set: ({ value }) => set(value) } }) : get);
2092
+ }
2093
+
2094
+ function linked(params) {
2095
+ return (() => ({ [symbolLinked]: params }));
1933
2096
  }
1934
2097
 
1935
- function configureLegendState({ observableFunctions, observableProperties: observableProperties$1, }) {
2098
+ function configureLegendState({ observableFunctions, observableProperties: observableProperties$1, jsonReplacer, jsonReviver, }) {
1936
2099
  if (observableFunctions) {
1937
2100
  for (const key in observableFunctions) {
1938
2101
  const fn = observableFunctions[key];
@@ -1957,6 +2120,12 @@ function configureLegendState({ observableFunctions, observableProperties: obser
1957
2120
  });
1958
2121
  }
1959
2122
  }
2123
+ if (jsonReplacer) {
2124
+ globalState.replacer = jsonReplacer;
2125
+ }
2126
+ if (jsonReviver) {
2127
+ globalState.reviver = jsonReviver;
2128
+ }
1960
2129
  }
1961
2130
 
1962
2131
  function event() {
@@ -1983,56 +2152,102 @@ function event() {
1983
2152
  }
1984
2153
 
1985
2154
  function proxy(get, set) {
1986
- // Create an observable for this computed variable
1987
- const obs = observable({});
1988
- lockObservable(obs, true);
1989
- const mapTargets = new Map();
1990
- const node = getNode(obs);
1991
- node.isComputed = true;
1992
- node.proxyFn = (key) => {
1993
- let target = mapTargets.get(key);
1994
- if (!target) {
1995
- // Note: Coercing typescript to allow undefined for set in computed because we don't want the public interface to allow undefined
1996
- target = computed(() => get(key), (set ? (value) => set(key, value) : undefined));
1997
- mapTargets.set(key, target);
1998
- extractFunction(node, key, target, getNode(target));
1999
- if (node.parentOther) {
2000
- onChange(getNode(target), ({ value, getPrevious }) => {
2001
- const previous = getPrevious();
2002
- // Set the raw value on the proxy's parent
2003
- setNodeValue(node.parentOther, node.root._);
2004
- // Notify the proxy
2005
- notify(getChildNode(node, key), value, previous, 0);
2155
+ return observable((key) => set
2156
+ ? linked({
2157
+ get: () => get(key),
2158
+ set: ({ value }) => set(key, value),
2159
+ })
2160
+ : get(key));
2161
+ }
2162
+
2163
+ function calculateRetryDelay(retryOptions, attemptNum) {
2164
+ const { backoff, delay = 1000, infinite, times = 3, maxDelay = 30000 } = retryOptions;
2165
+ if (infinite || attemptNum < times) {
2166
+ const delayTime = Math.min(delay * (backoff === 'constant' ? 1 : 2 ** attemptNum), maxDelay);
2167
+ return delayTime;
2168
+ }
2169
+ return null;
2170
+ }
2171
+ function createRetryTimeout(retryOptions, attemptNum, fn) {
2172
+ const delayTime = calculateRetryDelay(retryOptions, attemptNum);
2173
+ if (delayTime) {
2174
+ return setTimeout(fn, delayTime);
2175
+ }
2176
+ }
2177
+ function runWithRetry(node, state, fn) {
2178
+ const { retry, waitFor } = node.activationState;
2179
+ const e = { cancel: false };
2180
+ let value = undefined;
2181
+ if (waitFor) {
2182
+ value = whenReady(waitFor, () => {
2183
+ node.activationState.waitFor = undefined;
2184
+ return fn(e);
2185
+ });
2186
+ }
2187
+ else {
2188
+ value = fn(e);
2189
+ }
2190
+ if (isPromise(value) && retry) {
2191
+ let timeoutRetry;
2192
+ return new Promise((resolve) => {
2193
+ const run = () => {
2194
+ value
2195
+ .then((val) => {
2196
+ node.activationState.persistedRetry = false;
2197
+ resolve(val);
2198
+ })
2199
+ .catch(() => {
2200
+ state.attemptNum++;
2201
+ if (timeoutRetry) {
2202
+ clearTimeout(timeoutRetry);
2203
+ }
2204
+ if (!e.cancel) {
2205
+ timeoutRetry = createRetryTimeout(retry, state.attemptNum, () => {
2206
+ value = fn(e);
2207
+ run();
2208
+ });
2209
+ }
2210
+ })
2211
+ .finally(() => {
2212
+ node.activationState.persistedRetry = false;
2006
2213
  });
2007
- }
2008
- }
2009
- return target;
2010
- };
2011
- return obs;
2214
+ };
2215
+ run();
2216
+ });
2217
+ }
2218
+ return value;
2012
2219
  }
2013
2220
 
2014
2221
  const internal = {
2222
+ createPreviousHandler,
2223
+ clone,
2015
2224
  ensureNodeValue,
2016
2225
  findIDKey,
2017
2226
  get,
2018
2227
  getNode,
2228
+ getNodeValue,
2229
+ getPathType,
2019
2230
  getProxy,
2020
2231
  globalState,
2232
+ initializePathType,
2021
2233
  observableFns,
2022
2234
  optimized,
2023
2235
  peek,
2236
+ runWithRetry,
2237
+ safeParse,
2238
+ safeStringify,
2024
2239
  set,
2025
2240
  setAtPath,
2026
2241
  setNodeValue,
2027
- setupRetry,
2242
+ symbolLinked,
2028
2243
  symbolDelete,
2244
+ tracking,
2029
2245
  };
2030
2246
 
2031
2247
  exports.ObservablePrimitiveClass = ObservablePrimitiveClass;
2032
2248
  exports.batch = batch;
2033
2249
  exports.beginBatch = beginBatch;
2034
2250
  exports.beginTracking = beginTracking;
2035
- exports.checkActivate = checkActivate;
2036
2251
  exports.computeSelector = computeSelector;
2037
2252
  exports.computed = computed;
2038
2253
  exports.configureLegendState = configureLegendState;
@@ -2041,8 +2256,6 @@ exports.deconstructObjectWithPath = deconstructObjectWithPath;
2041
2256
  exports.endBatch = endBatch;
2042
2257
  exports.endTracking = endTracking;
2043
2258
  exports.event = event;
2044
- exports.extraPrimitiveActivators = extraPrimitiveActivators;
2045
- exports.extraPrimitiveProps = extraPrimitiveProps;
2046
2259
  exports.findIDKey = findIDKey;
2047
2260
  exports.getNode = getNode;
2048
2261
  exports.getNodeValue = getNodeValue;
@@ -2051,8 +2264,10 @@ exports.hasOwnProperty = hasOwnProperty;
2051
2264
  exports.internal = internal;
2052
2265
  exports.isArray = isArray;
2053
2266
  exports.isBoolean = isBoolean;
2267
+ exports.isDate = isDate;
2054
2268
  exports.isEmpty = isEmpty;
2055
2269
  exports.isFunction = isFunction;
2270
+ exports.isNullOrUndefined = isNullOrUndefined;
2056
2271
  exports.isObject = isObject;
2057
2272
  exports.isObservable = isObservable;
2058
2273
  exports.isObservableValueReady = isObservableValueReady;
@@ -2060,7 +2275,7 @@ exports.isPrimitive = isPrimitive;
2060
2275
  exports.isPromise = isPromise;
2061
2276
  exports.isString = isString;
2062
2277
  exports.isSymbol = isSymbol;
2063
- exports.lockObservable = lockObservable;
2278
+ exports.linked = linked;
2064
2279
  exports.mergeIntoObservable = mergeIntoObservable;
2065
2280
  exports.observable = observable;
2066
2281
  exports.observablePrimitive = observablePrimitive;
@@ -2073,6 +2288,7 @@ exports.setInObservableAtPath = setInObservableAtPath;
2073
2288
  exports.setSilently = setSilently;
2074
2289
  exports.setupTracking = setupTracking;
2075
2290
  exports.symbolDelete = symbolDelete;
2291
+ exports.syncState = syncState;
2076
2292
  exports.trackSelector = trackSelector;
2077
2293
  exports.tracking = tracking;
2078
2294
  exports.updateTracking = updateTracking;