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

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