@legendapp/state 2.2.0-next.8 → 2.2.0-next.80

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