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