@legendapp/state 2.1.2 → 2.2.0-next.10

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 (81) hide show
  1. package/config/enableDirectAccess.d.ts +1 -1
  2. package/config/enableDirectPeek.d.ts +1 -1
  3. package/config/enableReactTracking.d.ts +4 -3
  4. package/config/enableReactTracking.js.map +1 -1
  5. package/config/enableReactTracking.mjs.map +1 -1
  6. package/config/enableReactUse.d.ts +1 -1
  7. package/helpers/fetch.d.ts +4 -3
  8. package/helpers/fetch.js.map +1 -1
  9. package/helpers/fetch.mjs.map +1 -1
  10. package/helpers/time.d.ts +2 -2
  11. package/history.js +1 -1
  12. package/history.js.map +1 -1
  13. package/history.mjs +1 -1
  14. package/history.mjs.map +1 -1
  15. package/index.d.ts +16 -8
  16. package/index.js +834 -492
  17. package/index.js.map +1 -1
  18. package/index.mjs +834 -493
  19. package/index.mjs.map +1 -1
  20. package/package.json +2 -10
  21. package/persist-plugins/async-storage.js +3 -4
  22. package/persist-plugins/async-storage.js.map +1 -1
  23. package/persist-plugins/async-storage.mjs +4 -5
  24. package/persist-plugins/async-storage.mjs.map +1 -1
  25. package/persist-plugins/firebase.js +9 -6
  26. package/persist-plugins/firebase.js.map +1 -1
  27. package/persist-plugins/firebase.mjs +10 -7
  28. package/persist-plugins/firebase.mjs.map +1 -1
  29. package/persist-plugins/local-storage.js +2 -3
  30. package/persist-plugins/local-storage.js.map +1 -1
  31. package/persist-plugins/local-storage.mjs +3 -4
  32. package/persist-plugins/local-storage.mjs.map +1 -1
  33. package/persist-plugins/mmkv.js +2 -3
  34. package/persist-plugins/mmkv.js.map +1 -1
  35. package/persist-plugins/mmkv.mjs +3 -4
  36. package/persist-plugins/mmkv.mjs.map +1 -1
  37. package/persist.d.ts +17 -2
  38. package/persist.js +261 -82
  39. package/persist.js.map +1 -1
  40. package/persist.mjs +263 -82
  41. package/persist.mjs.map +1 -1
  42. package/react-hooks/createObservableHook.js +1 -1
  43. package/react-hooks/createObservableHook.js.map +1 -1
  44. package/react-hooks/createObservableHook.mjs +1 -1
  45. package/react-hooks/createObservableHook.mjs.map +1 -1
  46. package/react-hooks/useFetch.d.ts +4 -3
  47. package/react-hooks/useFetch.js.map +1 -1
  48. package/react-hooks/useFetch.mjs.map +1 -1
  49. package/react-hooks/useObservableQuery.js.map +1 -1
  50. package/react-hooks/useObservableQuery.mjs.map +1 -1
  51. package/react.js +2 -0
  52. package/react.js.map +1 -1
  53. package/react.mjs +2 -0
  54. package/react.mjs.map +1 -1
  55. package/src/ObservableObject.d.ts +8 -4
  56. package/src/ObservablePrimitive.d.ts +2 -1
  57. package/src/activated.d.ts +3 -0
  58. package/src/computed.d.ts +1 -1
  59. package/src/config/enableDirectAccess.d.ts +1 -1
  60. package/src/config/enableDirectPeek.d.ts +1 -1
  61. package/src/config/enableReactTracking.d.ts +4 -3
  62. package/src/config/enableReactUse.d.ts +1 -1
  63. package/src/createObservable.d.ts +2 -2
  64. package/src/globals.d.ts +14 -3
  65. package/src/helpers/fetch.d.ts +4 -3
  66. package/src/helpers/time.d.ts +2 -2
  67. package/src/helpers.d.ts +4 -10
  68. package/src/history/trackHistory.d.ts +1 -1
  69. package/src/observable.d.ts +9 -3
  70. package/src/observableInterfaces.d.ts +67 -302
  71. package/src/observableTypes.d.ts +92 -0
  72. package/src/persist/persistActivateNode.d.ts +1 -0
  73. package/src/persist/persistObservable.d.ts +2 -5
  74. package/src/persistTypes.d.ts +224 -0
  75. package/src/proxy.d.ts +2 -1
  76. package/src/react/Computed.d.ts +1 -1
  77. package/src/react/reactInterfaces.d.ts +2 -1
  78. package/src/react/usePauseProvider.d.ts +3 -3
  79. package/src/react-hooks/useFetch.d.ts +4 -3
  80. package/src/retry.d.ts +9 -0
  81. package/src/trackSelector.d.ts +3 -2
package/index.mjs CHANGED
@@ -1,3 +1,5 @@
1
+ import { noop } from '@babel/types';
2
+
1
3
  const hasOwnProperty = Object.prototype.hasOwnProperty;
2
4
  function isArray(obj) {
3
5
  return Array.isArray(obj);
@@ -53,14 +55,25 @@ const symbolGetNode = Symbol('getNode');
53
55
  const symbolDelete = /* @__PURE__ */ Symbol('delete');
54
56
  const symbolOpaque = Symbol('opaque');
55
57
  const optimized = Symbol('optimized');
58
+ const symbolActivated = Symbol('activated');
56
59
  // TODOV3 Remove these
57
60
  const extraPrimitiveActivators = new Map();
58
61
  const extraPrimitiveProps = new Map();
59
62
  const globalState = {
60
63
  isLoadingLocal: false,
61
- isLoadingRemote: false,
62
64
  isMerging: false,
65
+ isLoadingRemote$: undefined,
66
+ activateNode: undefined,
67
+ pendingNodes: new Map(),
68
+ dirtyNodes: new Set(),
63
69
  };
70
+ function isObservable(obs) {
71
+ return !!obs && !!obs[symbolGetNode];
72
+ }
73
+ function isComputed(obs) {
74
+ var _a;
75
+ return obs && ((_a = obs[symbolGetNode]) === null || _a === void 0 ? void 0 : _a.isComputed);
76
+ }
64
77
  function checkActivate(node) {
65
78
  var _a;
66
79
  const root = node.root;
@@ -87,12 +100,13 @@ function setNodeValue(node, newValue) {
87
100
  const prevValue = parentValue[key];
88
101
  const isFunc = isFunction(newValue);
89
102
  // Compute newValue if newValue is a function or an observable
90
- newValue =
91
- !parentNode.isAssigning && isFunc
92
- ? newValue(prevValue)
93
- : isObject(newValue) && (newValue === null || newValue === void 0 ? void 0 : newValue[symbolGetNode])
94
- ? newValue.peek()
95
- : newValue;
103
+ newValue = !parentNode.isAssigning && isFunc ? newValue(prevValue) : newValue;
104
+ // If setting an observable, set a link to the observable instead
105
+ if (isObservable(newValue) && !isComputed(newValue)) {
106
+ const val = newValue;
107
+ node.lazy = () => val;
108
+ newValue = undefined;
109
+ }
96
110
  try {
97
111
  parentNode.isSetting = (parentNode.isSetting || 0) + 1;
98
112
  // Save the new value
@@ -126,7 +140,13 @@ function getNodeValue(node) {
126
140
  }
127
141
  return child;
128
142
  }
129
- function getChildNode(node, key) {
143
+ const cloneFunction = (originalFunction) => {
144
+ const length = originalFunction.length;
145
+ return length > 1
146
+ ? (arg1, arg2) => originalFunction(arg1, arg2)
147
+ : (...args) => originalFunction(...args);
148
+ };
149
+ function getChildNode(node, key, asFunction) {
130
150
  var _a;
131
151
  // Get the child by key
132
152
  let child = (_a = node.children) === null || _a === void 0 ? void 0 : _a.get(key);
@@ -138,6 +158,20 @@ function getChildNode(node, key) {
138
158
  key,
139
159
  lazy: true,
140
160
  };
161
+ if (asFunction) {
162
+ child = Object.assign(cloneFunction(asFunction), child);
163
+ }
164
+ else {
165
+ if (node.activationState) {
166
+ const { lookup } = node.activationState;
167
+ if (lookup) {
168
+ child = Object.assign(lookup.bind(node, key), child);
169
+ if (isFunction(child)) {
170
+ extractFunction(node, key, child);
171
+ }
172
+ }
173
+ }
174
+ }
141
175
  if (!node.children) {
142
176
  node.children = new Map();
143
177
  }
@@ -159,6 +193,7 @@ function ensureNodeValue(node) {
159
193
  return value;
160
194
  }
161
195
  function findIDKey(obj, node) {
196
+ var _a, _b;
162
197
  let idKey = isObject(obj)
163
198
  ? 'id' in obj
164
199
  ? 'id'
@@ -171,7 +206,8 @@ function findIDKey(obj, node) {
171
206
  : undefined
172
207
  : undefined;
173
208
  if (!idKey && node.parent) {
174
- const keyExtractor = getNodeValue(node.parent)[node.key + '_keyExtractor'];
209
+ const k = node.key + '_keyExtractor';
210
+ 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'];
175
211
  if (keyExtractor && isFunction(keyExtractor)) {
176
212
  idKey = keyExtractor;
177
213
  }
@@ -179,9 +215,11 @@ function findIDKey(obj, node) {
179
215
  return idKey;
180
216
  }
181
217
  function extractFunction(node, key, fnOrComputed, computedChildNode) {
218
+ var _a;
182
219
  if (!node.functions) {
183
220
  node.functions = new Map();
184
221
  }
222
+ (_a = node.children) === null || _a === void 0 ? void 0 : _a.delete(key);
185
223
  node.functions.set(key, fnOrComputed);
186
224
  if (computedChildNode) {
187
225
  computedChildNode.parentOther = getChildNode(node, key);
@@ -192,226 +230,10 @@ function extractFunction(node, key, fnOrComputed, computedChildNode) {
192
230
  }
193
231
  }
194
232
 
195
- function isObservable(obs) {
196
- return obs && !!obs[symbolGetNode];
197
- }
198
- function isEvent(obs) {
199
- var _a;
200
- return obs && ((_a = obs[symbolGetNode]) === null || _a === void 0 ? void 0 : _a.isEvent);
201
- }
202
- function computeSelector(selector, e, retainObservable) {
203
- let c = selector;
204
- if (isFunction(c)) {
205
- c = e ? c(e) : c();
206
- }
207
- return isObservable(c) && !retainObservable ? c.get() : c;
208
- }
209
- function getObservableIndex(obs) {
210
- const node = getNode(obs);
211
- const n = +node.key;
212
- return n - n < 1 ? +n : -1;
213
- }
214
- function opaqueObject(value) {
215
- if (value) {
216
- value[symbolOpaque] = true;
217
- }
218
- return value;
219
- }
220
- function lockObservable(obs, value) {
221
- var _a;
222
- const root = (_a = getNode(obs)) === null || _a === void 0 ? void 0 : _a.root;
223
- if (root) {
224
- root.locked = value;
225
- }
226
- }
227
- function setAtPath(obj, path, pathTypes, value, fullObj, restore) {
228
- let o = obj;
229
- let oFull = fullObj;
230
- if (path.length > 0) {
231
- for (let i = 0; i < path.length; i++) {
232
- const p = path[i];
233
- if (i === path.length - 1) {
234
- // Don't set if the value is the same. This prevents creating a new key
235
- // when setting undefined on an object without this key
236
- if (o[p] !== value) {
237
- o[p] = value;
238
- }
239
- }
240
- else if (o[p] === symbolDelete) {
241
- // If this was previously deleted, restore it
242
- if (oFull) {
243
- o[p] = oFull[p];
244
- restore === null || restore === void 0 ? void 0 : restore(path.slice(0, i + 1), o[p]);
245
- }
246
- break;
247
- }
248
- else if (o[p] === undefined || o[p] === null) {
249
- o[p] = initializePathType(pathTypes[i]);
250
- }
251
- o = o[p];
252
- if (oFull) {
253
- oFull = oFull[p];
254
- }
255
- }
256
- }
257
- else {
258
- obj = value;
259
- }
260
- return obj;
261
- }
262
- function setInObservableAtPath(obs, path, pathTypes, value, mode) {
263
- let o = obs;
264
- let v = value;
265
- for (let i = 0; i < path.length; i++) {
266
- const p = path[i];
267
- if (!o.peek()[p]) {
268
- o[p].set(initializePathType(pathTypes[i]));
269
- }
270
- o = o[p];
271
- v = v[p];
272
- }
273
- if (v === symbolDelete) {
274
- o.delete();
275
- }
276
- // Assign if possible, or set otherwise
277
- else if (mode === 'assign' && o.assign && isObject(o.peek())) {
278
- o.assign(v);
279
- }
280
- else {
281
- o.set(v);
282
- }
283
- }
284
- function mergeIntoObservable(target, ...sources) {
285
- beginBatch();
286
- globalState.isMerging = true;
287
- for (let i = 0; i < sources.length; i++) {
288
- target = _mergeIntoObservable(target, sources[i]);
289
- }
290
- globalState.isMerging = false;
291
- endBatch();
292
- return target;
293
- }
294
- function _mergeIntoObservable(target, source) {
295
- var _a;
296
- const needsSet = isObservable(target);
297
- const targetValue = needsSet ? target.peek() : target;
298
- const isTargetArr = isArray(targetValue);
299
- const isTargetObj = !isTargetArr && isObject(targetValue);
300
- if ((isTargetObj && isObject(source) && !isEmpty(targetValue)) ||
301
- (isTargetArr && isArray(source) && targetValue.length > 0)) {
302
- const keys = Object.keys(source);
303
- for (let i = 0; i < keys.length; i++) {
304
- const key = keys[i];
305
- const sourceValue = source[key];
306
- if (sourceValue === symbolDelete) {
307
- needsSet && ((_a = target[key]) === null || _a === void 0 ? void 0 : _a.delete) ? target[key].delete() : delete target[key];
308
- }
309
- else {
310
- const isObj = isObject(sourceValue);
311
- const isArr = !isObj && isArray(sourceValue);
312
- const targetChild = target[key];
313
- if ((isObj || isArr) && targetChild && (needsSet || !isEmpty(targetChild))) {
314
- if (!needsSet && (!targetChild || (isObj ? !isObject(targetChild) : !isArray(targetChild)))) {
315
- target[key] = sourceValue;
316
- }
317
- else {
318
- _mergeIntoObservable(targetChild, sourceValue);
319
- }
320
- }
321
- else {
322
- needsSet
323
- ? targetChild.set(sourceValue)
324
- : (target[key] = sourceValue);
325
- }
326
- }
327
- }
328
- }
329
- else if (source !== undefined) {
330
- needsSet ? target.set(source) : (target = source);
331
- }
332
- return target;
333
- }
334
- function constructObjectWithPath(path, pathTypes, value) {
335
- let out;
336
- if (path.length > 0) {
337
- let o = (out = {});
338
- for (let i = 0; i < path.length; i++) {
339
- const p = path[i];
340
- o[p] = i === path.length - 1 ? value : initializePathType(pathTypes[i]);
341
- o = o[p];
342
- }
343
- }
344
- else {
345
- out = value;
346
- }
347
- return out;
348
- }
349
- function deconstructObjectWithPath(path, pathTypes, value) {
350
- let o = value;
351
- for (let i = 0; i < path.length; i++) {
352
- const p = path[i];
353
- o = o ? o[p] : initializePathType(pathTypes[i]);
354
- }
355
- return o;
356
- }
357
- function isObservableValueReady(value) {
358
- return !!value && ((!isObject(value) && !isArray(value)) || !isEmpty(value));
359
- }
360
- function setSilently(obs, newValue) {
361
- const node = getNode(obs);
362
- return setNodeValue(node, newValue).newValue;
363
- }
364
- function getPathType(value) {
365
- return isArray(value) ? 'array' : value instanceof Map ? 'map' : value instanceof Set ? 'set' : 'object';
366
- }
367
- function initializePathType(pathType) {
368
- switch (pathType) {
369
- case 'array':
370
- return [];
371
- case 'object':
372
- return {};
373
- case 'map':
374
- return new Map();
375
- case 'set':
376
- return new Set();
377
- }
378
- }
379
- function replacer(_, value) {
380
- if (value instanceof Map) {
381
- return {
382
- __LSType: 'Map',
383
- value: Array.from(value.entries()), // or with spread: value: [...value]
384
- };
385
- }
386
- else if (value instanceof Set) {
387
- return {
388
- __LSType: 'Set',
389
- value: Array.from(value), // or with spread: value: [...value]
390
- };
391
- }
392
- else {
393
- return value;
394
- }
395
- }
396
- function reviver(_, value) {
397
- if (typeof value === 'object' && value) {
398
- if (value.__LSType === 'Map') {
399
- return new Map(value.value);
400
- }
401
- else if (value.__LSType === 'Set') {
402
- return new Set(value.value);
403
- }
404
- }
405
- return value;
406
- }
407
- function safeStringify(value) {
408
- return JSON.stringify(value, replacer);
409
- }
410
- function safeParse(value) {
411
- return JSON.parse(value, reviver);
412
- }
413
- function clone(value) {
414
- return safeParse(safeStringify(value));
233
+ function activated(params) {
234
+ return (() => ({
235
+ [symbolActivated]: params,
236
+ }));
415
237
  }
416
238
 
417
239
  let timeout;
@@ -439,28 +261,22 @@ function isArraySubset(mainArr, subsetArr) {
439
261
  }
440
262
  function createPreviousHandlerInner(value, changes) {
441
263
  // Clones the current state and inject the previous data at the changed path
442
- let cloned = value ? clone(value) : {};
264
+ let clone = value ? JSON.parse(JSON.stringify(value)) : {};
443
265
  for (let i = 0; i < changes.length; i++) {
444
266
  const { path, prevAtPath } = changes[i];
445
- let o = cloned;
267
+ let o = clone;
446
268
  if (path.length > 0) {
447
269
  let i;
448
270
  for (i = 0; i < path.length - 1; i++) {
449
271
  o = o[path[i]];
450
272
  }
451
- const key = path[i];
452
- if (o instanceof Map) {
453
- o.set(key, prevAtPath);
454
- }
455
- else {
456
- o[key] = prevAtPath;
457
- }
273
+ o[path[i]] = prevAtPath;
458
274
  }
459
275
  else {
460
- cloned = prevAtPath;
276
+ clone = prevAtPath;
461
277
  }
462
278
  }
463
- return cloned;
279
+ return clone;
464
280
  }
465
281
  function createPreviousHandler(value, changes) {
466
282
  // Create a function that generates the previous state
@@ -530,7 +346,7 @@ function computeChangesRecursive(changesInBatch, node, value, path, pathTypes, v
530
346
  const parent = node.parent;
531
347
  if (parent) {
532
348
  const parentValue = getNodeValue(parent);
533
- computeChangesRecursive(changesInBatch, parent, parentValue, [node.key].concat(path), [getPathType(value)].concat(pathTypes), valueAtPath, prevAtPath, immediate, level + 1, whenOptimizedOnlyIf);
349
+ computeChangesRecursive(changesInBatch, parent, parentValue, [node.key].concat(path), [(isArray(value) ? 'array' : 'object')].concat(pathTypes), valueAtPath, prevAtPath, immediate, level + 1, whenOptimizedOnlyIf);
534
350
  }
535
351
  }
536
352
  }
@@ -569,6 +385,13 @@ function batchNotifyChanges(changesInBatch, immediate) {
569
385
  });
570
386
  }
571
387
  function runBatch() {
388
+ const dirtyNodes = Array.from(globalState.dirtyNodes);
389
+ globalState.dirtyNodes.clear();
390
+ dirtyNodes.forEach((node) => {
391
+ var _a;
392
+ (_a = node.dirtyFn) === null || _a === void 0 ? void 0 : _a.call(node);
393
+ node.dirtyFn = undefined;
394
+ });
572
395
  // Save batch locally and reset _batchMap first because a new batch could begin while looping over callbacks.
573
396
  // This can happen with observableComputed for example.
574
397
  const map = _batchMap;
@@ -655,68 +478,261 @@ function endBatch(force) {
655
478
  }
656
479
  }
657
480
 
658
- function createObservable(value, makePrimitive, createObject, createPrimitive) {
481
+ function createObservable(value, makePrimitive, extractPromise, createObject, createPrimitive) {
659
482
  const valueIsPromise = isPromise(value);
483
+ const valueIsFunction = isFunction(value);
660
484
  const root = {
661
485
  _: value,
662
486
  };
663
- const node = {
487
+ let node = {
664
488
  root,
665
489
  lazy: true,
666
490
  };
491
+ if (valueIsFunction) {
492
+ node = Object.assign(cloneFunction(value), node);
493
+ }
667
494
  const prim = makePrimitive || isActualPrimitive(value);
668
495
  const obs = prim
669
496
  ? new createPrimitive(node)
670
497
  : createObject(node);
671
498
  if (valueIsPromise) {
499
+ setNodeValue(node, undefined);
672
500
  extractPromise(node, value);
673
501
  }
674
502
  return obs;
675
503
  }
676
504
 
677
- function onChange(node, callback, options = {}) {
678
- const { initial, immediate, noArgs } = options;
679
- const { trackingType } = options;
680
- let listeners = immediate ? node.listenersImmediate : node.listeners;
681
- if (!listeners) {
682
- listeners = new Set();
683
- if (immediate) {
684
- node.listenersImmediate = listeners;
685
- }
686
- else {
687
- node.listeners = listeners;
688
- }
505
+ function isEvent(obs) {
506
+ var _a;
507
+ return obs && ((_a = obs[symbolGetNode]) === null || _a === void 0 ? void 0 : _a.isEvent);
508
+ }
509
+ function computeSelector(selector, e, retainObservable) {
510
+ let c = selector;
511
+ if (!isObservable(c) && isFunction(c)) {
512
+ c = e ? c(e) : c();
689
513
  }
690
- checkActivate(node);
691
- const listener = {
692
- listener: callback,
693
- track: trackingType,
694
- noArgs,
695
- };
696
- listeners.add(listener);
697
- if (initial) {
698
- const value = getNodeValue(node);
699
- callback({
700
- value,
701
- changes: [
702
- {
703
- path: [],
704
- pathTypes: [],
705
- prevAtPath: value,
706
- valueAtPath: value,
707
- },
708
- ],
709
- getPrevious: () => undefined,
710
- });
514
+ return isObservable(c) && !retainObservable ? c.get() : c;
515
+ }
516
+ function getObservableIndex(obs) {
517
+ const node = getNode(obs);
518
+ const n = +node.key;
519
+ return n - n < 1 ? +n : -1;
520
+ }
521
+ function opaqueObject(value) {
522
+ if (value) {
523
+ value[symbolOpaque] = true;
711
524
  }
712
- return () => listeners.delete(listener);
525
+ return value;
713
526
  }
714
-
715
- let trackCount = 0;
716
- const trackingQueue = [];
717
- const tracking = {
718
- current: undefined,
719
- };
527
+ function lockObservable(obs, value) {
528
+ var _a;
529
+ const root = (_a = getNode(obs)) === null || _a === void 0 ? void 0 : _a.root;
530
+ if (root) {
531
+ root.locked = value;
532
+ }
533
+ }
534
+ function setAtPath(obj, path, pathTypes, value, fullObj, restore) {
535
+ let o = obj;
536
+ let oFull = fullObj;
537
+ if (path.length > 0) {
538
+ for (let i = 0; i < path.length; i++) {
539
+ const p = path[i];
540
+ if (i === path.length - 1) {
541
+ // Don't set if the value is the same. This prevents creating a new key
542
+ // when setting undefined on an object without this key
543
+ if (o[p] !== value) {
544
+ o[p] = value;
545
+ }
546
+ }
547
+ else if (o[p] === symbolDelete) {
548
+ // If this was previously deleted, restore it
549
+ if (oFull) {
550
+ o[p] = oFull[p];
551
+ restore === null || restore === void 0 ? void 0 : restore(path.slice(0, i + 1), o[p]);
552
+ }
553
+ break;
554
+ }
555
+ else if (o[p] === undefined || o[p] === null) {
556
+ o[p] = pathTypes[i] === 'array' ? [] : {};
557
+ }
558
+ o = o[p];
559
+ if (oFull) {
560
+ oFull = oFull[p];
561
+ }
562
+ }
563
+ }
564
+ else {
565
+ obj = value;
566
+ }
567
+ return obj;
568
+ }
569
+ function setInObservableAtPath(obs, path, pathTypes, value, mode) {
570
+ let o = obs;
571
+ let v = value;
572
+ for (let i = 0; i < path.length; i++) {
573
+ const p = path[i];
574
+ if (!o.peek()[p] && pathTypes[i] === 'array') {
575
+ o[p].set([]);
576
+ }
577
+ o = o[p];
578
+ v = v[p];
579
+ }
580
+ if (v === symbolDelete) {
581
+ o.delete();
582
+ }
583
+ // Assign if possible, or set otherwise
584
+ else if (mode === 'assign' && o.assign && isObject(o.peek())) {
585
+ o.assign(v);
586
+ }
587
+ else {
588
+ o.set(v);
589
+ }
590
+ }
591
+ function mergeIntoObservable(target, ...sources) {
592
+ beginBatch();
593
+ globalState.isMerging = true;
594
+ for (let i = 0; i < sources.length; i++) {
595
+ target = _mergeIntoObservable(target, sources[i]);
596
+ }
597
+ globalState.isMerging = false;
598
+ endBatch();
599
+ return target;
600
+ }
601
+ function _mergeIntoObservable(target, source) {
602
+ var _a;
603
+ if (isObservable(source)) {
604
+ source = source.peek();
605
+ }
606
+ const needsSet = isObservable(target);
607
+ const targetValue = needsSet ? target.peek() : target;
608
+ const isTargetArr = isArray(targetValue);
609
+ const isTargetObj = !isTargetArr && isObject(targetValue);
610
+ if ((isTargetObj && isObject(source) && !isEmpty(targetValue)) || (isTargetArr && targetValue.length > 0)) {
611
+ const keys = Object.keys(source);
612
+ for (let i = 0; i < keys.length; i++) {
613
+ const key = keys[i];
614
+ const sourceValue = source[key];
615
+ if (sourceValue === symbolDelete) {
616
+ needsSet && ((_a = target[key]) === null || _a === void 0 ? void 0 : _a.delete)
617
+ ? target[key].delete()
618
+ : delete target[key];
619
+ }
620
+ else {
621
+ const isObj = isObject(sourceValue);
622
+ const isArr = !isObj && isArray(sourceValue);
623
+ const targetChild = target[key];
624
+ if ((isObj || isArr) && targetChild && (needsSet || !isEmpty(targetChild))) {
625
+ if (!needsSet && (!targetChild || (isObj ? !isObject(targetChild) : !isArray(targetChild)))) {
626
+ target[key] = sourceValue;
627
+ }
628
+ else {
629
+ _mergeIntoObservable(targetChild, sourceValue);
630
+ }
631
+ }
632
+ else {
633
+ needsSet
634
+ ? targetChild.set(sourceValue)
635
+ : (target[key] = sourceValue);
636
+ }
637
+ }
638
+ }
639
+ }
640
+ else if (source !== undefined) {
641
+ needsSet ? target.set(source) : (target = source);
642
+ }
643
+ return target;
644
+ }
645
+ function constructObjectWithPath(path, pathTypes, value) {
646
+ let out;
647
+ if (path.length > 0) {
648
+ let o = (out = {});
649
+ for (let i = 0; i < path.length; i++) {
650
+ const p = path[i];
651
+ o[p] = i === path.length - 1 ? value : pathTypes[i] === 'array' ? [] : {};
652
+ o = o[p];
653
+ }
654
+ }
655
+ else {
656
+ out = value;
657
+ }
658
+ return out;
659
+ }
660
+ function deconstructObjectWithPath(path, pathTypes, value) {
661
+ let o = value;
662
+ for (let i = 0; i < path.length; i++) {
663
+ const p = path[i];
664
+ o = o ? o[p] : pathTypes[i] === 'array' ? [] : {};
665
+ }
666
+ return o;
667
+ }
668
+ function isObservableValueReady(value) {
669
+ return !!value && ((!isObject(value) && !isArray(value)) || !isEmpty(value));
670
+ }
671
+ function setSilently(obs, newValue) {
672
+ const node = getNode(obs);
673
+ return setNodeValue(node, newValue).newValue;
674
+ }
675
+
676
+ function onChange(node, callback, options = {}) {
677
+ const { initial, immediate, noArgs } = options;
678
+ const { trackingType } = options;
679
+ let listeners = immediate ? node.listenersImmediate : node.listeners;
680
+ if (!listeners) {
681
+ listeners = new Set();
682
+ if (immediate) {
683
+ node.listenersImmediate = listeners;
684
+ }
685
+ else {
686
+ node.listeners = listeners;
687
+ }
688
+ }
689
+ checkActivate(node);
690
+ const listener = {
691
+ listener: callback,
692
+ track: trackingType,
693
+ noArgs,
694
+ };
695
+ listeners.add(listener);
696
+ if (initial) {
697
+ const value = getNodeValue(node);
698
+ callback({
699
+ value,
700
+ changes: [
701
+ {
702
+ path: [],
703
+ pathTypes: [],
704
+ prevAtPath: value,
705
+ valueAtPath: value,
706
+ },
707
+ ],
708
+ getPrevious: () => undefined,
709
+ });
710
+ }
711
+ return () => listeners.delete(listener);
712
+ }
713
+
714
+ function setupTracking(nodes, update, noArgs, immediate) {
715
+ let listeners = [];
716
+ // Listen to tracked nodes
717
+ nodes === null || nodes === void 0 ? void 0 : nodes.forEach((tracked) => {
718
+ const { node, track } = tracked;
719
+ listeners.push(onChange(node, update, { trackingType: track, immediate, noArgs }));
720
+ });
721
+ return () => {
722
+ if (listeners) {
723
+ for (let i = 0; i < listeners.length; i++) {
724
+ listeners[i]();
725
+ }
726
+ listeners = undefined;
727
+ }
728
+ };
729
+ }
730
+
731
+ let trackCount = 0;
732
+ const trackingQueue = [];
733
+ const tracking = {
734
+ current: undefined,
735
+ };
720
736
  function beginTracking() {
721
737
  // Keep a copy of the previous tracking context so it can be restored
722
738
  // when this context is complete
@@ -745,11 +761,194 @@ function updateTracking(node, track) {
745
761
  existing.num++;
746
762
  }
747
763
  else {
748
- tracker.nodes.set(node, { node, track, num: 1 });
764
+ tracker.nodes.set(node, { node, track, num: 1 });
765
+ }
766
+ }
767
+ }
768
+ }
769
+
770
+ function trackSelector(selector, update, observeEvent, observeOptions, createResubscribe) {
771
+ var _a;
772
+ let nodes;
773
+ let value;
774
+ let dispose;
775
+ let tracker;
776
+ let resubscribe;
777
+ let updateFn = update;
778
+ if (isObservable(selector)) {
779
+ value = selector.peek();
780
+ dispose = selector.onChange(update);
781
+ resubscribe = createResubscribe ? selector.onChange(update) : undefined;
782
+ }
783
+ else {
784
+ // Compute the selector inside a tracking context
785
+ beginTracking();
786
+ value = selector ? computeSelector(selector, observeEvent, observeOptions === null || observeOptions === void 0 ? void 0 : observeOptions.fromComputed) : selector;
787
+ tracker = tracking.current;
788
+ nodes = tracker.nodes;
789
+ endTracking();
790
+ if ((process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') && tracker && nodes) {
791
+ (_a = tracker.traceListeners) === null || _a === void 0 ? void 0 : _a.call(tracker, nodes);
792
+ if (tracker.traceUpdates) {
793
+ updateFn = tracker.traceUpdates(update);
794
+ }
795
+ // Clear tracing so it doesn't leak to other components
796
+ tracker.traceListeners = undefined;
797
+ tracker.traceUpdates = undefined;
798
+ }
799
+ }
800
+ if (!(observeEvent === null || observeEvent === void 0 ? void 0 : observeEvent.cancel)) {
801
+ // Do tracing if it was requested
802
+ // useSyncExternalStore doesn't subscribe until after the component mount.
803
+ // We want to subscribe immediately so we don't miss any updates
804
+ dispose = setupTracking(nodes, updateFn, false, observeOptions === null || observeOptions === void 0 ? void 0 : observeOptions.immediate);
805
+ resubscribe = createResubscribe ? () => setupTracking(nodes, updateFn) : undefined;
806
+ }
807
+ return { value, nodes, dispose, resubscribe };
808
+ }
809
+
810
+ function observe(selectorOrRun, reactionOrOptions, options) {
811
+ let reaction;
812
+ if (isFunction(reactionOrOptions)) {
813
+ reaction = reactionOrOptions;
814
+ }
815
+ else {
816
+ options = reactionOrOptions;
817
+ }
818
+ let dispose;
819
+ const e = { num: 0 };
820
+ // Wrap it in a function so it doesn't pass all the arguments to run()
821
+ const update = function () {
822
+ if (e.onCleanup) {
823
+ e.onCleanup();
824
+ e.onCleanup = undefined;
825
+ }
826
+ // Run in a batch so changes don't happen until we're done tracking here
827
+ beginBatch();
828
+ // Run the function/selector
829
+ delete e.value;
830
+ // Dispose listeners from previous run
831
+ dispose === null || dispose === void 0 ? void 0 : dispose();
832
+ const { dispose: _dispose, value, nodes } = trackSelector(selectorOrRun, update, e, options);
833
+ dispose = _dispose;
834
+ e.value = value;
835
+ e.nodes = nodes;
836
+ e.refresh = update;
837
+ if (e.onCleanupReaction) {
838
+ e.onCleanupReaction();
839
+ e.onCleanupReaction = undefined;
840
+ }
841
+ endBatch();
842
+ // Call the reaction if there is one and the value changed
843
+ if (reaction &&
844
+ ((options === null || options === void 0 ? void 0 : options.fromComputed) || ((e.num > 0 || !isEvent(selectorOrRun)) && e.previous !== e.value))) {
845
+ reaction(e);
846
+ }
847
+ // Update the previous value
848
+ e.previous = e.value;
849
+ // Increment the counter
850
+ e.num++;
851
+ };
852
+ update();
853
+ // Return function calling dispose because dispose may be changed in update()
854
+ return () => {
855
+ var _a, _b;
856
+ (_a = e.onCleanup) === null || _a === void 0 ? void 0 : _a.call(e);
857
+ e.onCleanup = undefined;
858
+ (_b = e.onCleanupReaction) === null || _b === void 0 ? void 0 : _b.call(e);
859
+ e.onCleanupReaction = undefined;
860
+ dispose === null || dispose === void 0 ? void 0 : dispose();
861
+ };
862
+ }
863
+
864
+ function setupRetry(retryOptions, refresh, attemptNum) {
865
+ const timeout = {};
866
+ // let didGiveUp = false;
867
+ const { backoff, delay = 1000, infinite, times = 3, maxDelay = 30000 } = retryOptions;
868
+ let handleError;
869
+ attemptNum.current++;
870
+ if (infinite || attemptNum.current < times) {
871
+ const delayTime = Math.min(delay * (backoff === 'constant' ? 1 : 2 ** attemptNum.current), maxDelay);
872
+ handleError = () => {
873
+ timeout.current = setTimeout(refresh, delayTime);
874
+ };
875
+ }
876
+ else {
877
+ handleError = () => {
878
+ // didGiveUp = true;
879
+ };
880
+ }
881
+ // TODO: Make an easy way to opt into this if
882
+ // if (typeof window !== 'undefined') {
883
+ // window.addEventListener('online', () => {
884
+ // if (didGiveUp || timeout) {
885
+ // if (timeout) {
886
+ // clearTimeout(timeout.current);
887
+ // timeout.current = undefined;
888
+ // }
889
+ // // Restart the backoff when coming back online
890
+ // attemptNum.current = 0;
891
+ // didGiveUp = false;
892
+ // refresh();
893
+ // }
894
+ // });
895
+ // }
896
+ return { handleError, timeout };
897
+ }
898
+
899
+ function _when(predicate, effect, checkReady) {
900
+ // If predicate is a regular Promise skip all the observable stuff
901
+ if (isPromise(predicate)) {
902
+ return effect ? predicate.then(effect) : predicate;
903
+ }
904
+ let value;
905
+ // Create a wrapping fn that calls the effect if predicate returns true
906
+ function run(e) {
907
+ const ret = computeSelector(predicate);
908
+ if (!isPromise(ret) && (checkReady ? isObservableValueReady(ret) : ret)) {
909
+ value = ret;
910
+ // Set cancel so that observe does not track anymore
911
+ e.cancel = true;
912
+ }
913
+ return value;
914
+ }
915
+ function doEffect() {
916
+ // If value is truthy then run the effect
917
+ effect === null || effect === void 0 ? void 0 : effect(value);
918
+ }
919
+ // Run in an observe
920
+ observe(run, doEffect);
921
+ // If first run resulted in a truthy value just return it.
922
+ // It will have set e.cancel so no need to dispose
923
+ if (isPromise(value)) {
924
+ return effect ? value.then(effect) : value;
925
+ }
926
+ else if (value !== undefined) {
927
+ return Promise.resolve(value);
928
+ }
929
+ else {
930
+ // Wrap it in a promise
931
+ const promise = new Promise((resolve) => {
932
+ if (effect) {
933
+ const originalEffect = effect;
934
+ effect = (value) => {
935
+ const effectValue = originalEffect(value);
936
+ resolve(effectValue);
937
+ };
938
+ }
939
+ else {
940
+ effect = resolve;
749
941
  }
750
- }
942
+ });
943
+ return promise;
751
944
  }
752
945
  }
946
+ function when(predicate, effect) {
947
+ return _when(predicate, effect, false);
948
+ }
949
+ function whenReady(predicate, effect) {
950
+ return _when(predicate, effect, true);
951
+ }
753
952
 
754
953
  const ArrayModifiers = new Set([
755
954
  'copyWithin',
@@ -791,19 +990,24 @@ if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
791
990
  }
792
991
  function collectionSetter(node, target, prop, ...args) {
793
992
  var _a;
794
- const prevValue = (isArray(target) && target.slice()) || target;
795
- const ret = target[prop].apply(target, args);
796
- if (node) {
797
- const hasParent = isChildNodeValue(node);
798
- const key = hasParent ? node.key : '_';
799
- const parentValue = hasParent ? getNodeValue(node.parent) : node.root;
800
- // Set the object to the previous value first
801
- parentValue[key] = prevValue;
802
- // Then set with the new value so it notifies with the correct prevValue
803
- setKey((_a = node.parent) !== null && _a !== void 0 ? _a : node, key, target);
804
- }
805
- // Return the original value
806
- return ret;
993
+ if (prop === 'push') {
994
+ setKey(node, target.length + '', args[0]);
995
+ }
996
+ else {
997
+ const prevValue = target.slice();
998
+ const ret = target[prop].apply(target, args);
999
+ if (node) {
1000
+ const hasParent = isChildNodeValue(node);
1001
+ const key = hasParent ? node.key : '_';
1002
+ const parentValue = hasParent ? getNodeValue(node.parent) : node.root;
1003
+ // Set the object to the previous value first
1004
+ parentValue[key] = prevValue;
1005
+ // Then set with the new value so it notifies with the correct prevValue
1006
+ setKey((_a = node.parent) !== null && _a !== void 0 ? _a : node, key, target);
1007
+ }
1008
+ // Return the original value
1009
+ return ret;
1010
+ }
807
1011
  }
808
1012
  function getKeys(obj, isArr, isMap) {
809
1013
  return isArr ? undefined : obj ? (isMap ? Array.from(obj.keys()) : Object.keys(obj)) : [];
@@ -989,13 +1193,22 @@ function updateNodes(parent, obj, prevValue) {
989
1193
  }
990
1194
  return retValue !== null && retValue !== void 0 ? retValue : false;
991
1195
  }
992
- function getProxy(node, p) {
1196
+ function getProxy(node, p, asFunction) {
993
1197
  // Get the child node if p prop
994
1198
  if (p !== undefined)
995
- node = getChildNode(node, p);
1199
+ node = getChildNode(node, p, asFunction);
996
1200
  // Create a proxy if not already cached and return it
997
1201
  return (node.proxy || (node.proxy = new Proxy(node, proxyHandler)));
998
1202
  }
1203
+ function flushPending() {
1204
+ // Need to short circuit the computed batching because the user called get() or peek()
1205
+ // in which case the set needs to run immediately so that the values are up to date.
1206
+ if (globalState.pendingNodes.size > 0) {
1207
+ const nodes = Array.from(globalState.pendingNodes.values());
1208
+ globalState.pendingNodes.clear();
1209
+ nodes.forEach((fn) => fn());
1210
+ }
1211
+ }
999
1212
  const proxyHandler = {
1000
1213
  get(node, p, receiver) {
1001
1214
  var _a;
@@ -1023,6 +1236,9 @@ const proxyHandler = {
1023
1236
  const fn = observableFns.get(p);
1024
1237
  // If this is an observable function, call it
1025
1238
  if (fn) {
1239
+ if (p === 'get' || p === 'peek') {
1240
+ flushPending();
1241
+ }
1026
1242
  return function (a, b, c) {
1027
1243
  const l = arguments.length;
1028
1244
  // Array call and apply are slow so micro-optimize this hot path.
@@ -1070,6 +1286,15 @@ const proxyHandler = {
1070
1286
  if (isObject(value) && value[symbolOpaque]) {
1071
1287
  return vProp;
1072
1288
  }
1289
+ const fnOrComputed = (_a = node.functions) === null || _a === void 0 ? void 0 : _a.get(p);
1290
+ if (fnOrComputed) {
1291
+ if (isObservable(fnOrComputed)) {
1292
+ return fnOrComputed;
1293
+ }
1294
+ else {
1295
+ return getProxy(node, p, fnOrComputed);
1296
+ }
1297
+ }
1073
1298
  // Handle function calls
1074
1299
  if (isFunction(vProp)) {
1075
1300
  if (isArray(value)) {
@@ -1116,15 +1341,9 @@ const proxyHandler = {
1116
1341
  // Update that this primitive node was accessed for observers
1117
1342
  if (isArray(value) && p === 'length') {
1118
1343
  updateTracking(node, true);
1119
- // } else if (!isPrimitive(value)) {
1120
- // updateTracking(getChildNode(node, p));
1121
1344
  return vProp;
1122
1345
  }
1123
1346
  }
1124
- const fnOrComputed = (_a = node.functions) === null || _a === void 0 ? void 0 : _a.get(p);
1125
- if (fnOrComputed) {
1126
- return fnOrComputed;
1127
- }
1128
1347
  // TODOV3: Remove "state"
1129
1348
  if (vProp === undefined && (p === 'state' || p === '_state') && node.state) {
1130
1349
  return node.state;
@@ -1191,6 +1410,10 @@ const proxyHandler = {
1191
1410
  const value = getNodeValue(node);
1192
1411
  return Reflect.has(value, prop);
1193
1412
  },
1413
+ apply(target, thisArg, argArray) {
1414
+ // If it's a function call it as a function
1415
+ return Reflect.apply(target, thisArg, argArray);
1416
+ },
1194
1417
  };
1195
1418
  function set(node, newValue) {
1196
1419
  if (node.parent) {
@@ -1216,12 +1439,14 @@ function setKey(node, key, newValue, level) {
1216
1439
  console.warn(`[legend-state] Set an HTMLElement into state. You probably don't want to do that.`);
1217
1440
  }
1218
1441
  }
1442
+ const isRoot = !node.parent && key === '_';
1443
+ // TODOv3 root locking will be removed with old computeds
1219
1444
  if (node.root.locked && !node.root.set) {
1220
1445
  // This happens when modifying a locked observable such as a computed.
1221
1446
  // If merging this could be happening deep in a hierarchy so we don't want to throw errors so we'll just do nothing.
1222
1447
  // This could happen during persistence local load for example.
1223
1448
  if (globalState.isMerging) {
1224
- return;
1449
+ return isRoot ? getProxy(node) : getProxy(node, key);
1225
1450
  }
1226
1451
  else {
1227
1452
  throw new Error(process.env.NODE_ENV === 'development'
@@ -1229,9 +1454,11 @@ function setKey(node, key, newValue, level) {
1229
1454
  : '[legend-state] Modified locked observable');
1230
1455
  }
1231
1456
  }
1232
- const isRoot = !node.parent && key === '_';
1457
+ if (node.parent && !getNodeValue(node)) {
1458
+ return set(node, { [key]: newValue });
1459
+ }
1233
1460
  // Get the child node for updating and notifying
1234
- const childNode = isRoot ? node : getChildNode(node, key);
1461
+ const childNode = isRoot ? node : getChildNode(node, key, isFunction(newValue) ? newValue : undefined);
1235
1462
  // Set the raw value on the parent object
1236
1463
  const { newValue: savedValue, prevValue, parentValue } = setNodeValue(childNode, newValue);
1237
1464
  const isFunc = isFunction(savedValue);
@@ -1347,9 +1574,6 @@ function updateNodesAndNotify(node, newValue, prevValue, childNode, isPrim, isRo
1347
1574
  childNode = node;
1348
1575
  // Make sure we don't call too many listeners for ever property set
1349
1576
  beginBatch();
1350
- if (isPrim === undefined) {
1351
- isPrim = isPrimitive(newValue);
1352
- }
1353
1577
  let hasADiff = isPrim;
1354
1578
  let whenOptimizedOnlyIf = false;
1355
1579
  // If new value is an object or array update notify down the tree
@@ -1369,27 +1593,36 @@ function updateNodesAndNotify(node, newValue, prevValue, childNode, isPrim, isRo
1369
1593
  }
1370
1594
  endBatch();
1371
1595
  }
1372
- function extractPromise(node, value) {
1596
+ function extractPromise(node, value, setter, handleError) {
1373
1597
  if (!node.state) {
1374
1598
  node.state = createObservable({
1375
1599
  isLoaded: false,
1376
- }, false, getProxy);
1600
+ }, false, extractPromise, getProxy);
1377
1601
  }
1378
1602
  value
1379
1603
  .then((value) => {
1380
- set(node, value);
1381
- node.state.isLoaded.set(true);
1604
+ setter ? setter({ value }) : set(node, value);
1605
+ node.state.assign({
1606
+ isLoaded: true,
1607
+ error: undefined,
1608
+ });
1382
1609
  })
1383
1610
  .catch((error) => {
1384
1611
  node.state.error.set(error);
1612
+ if (handleError) {
1613
+ handleError(error);
1614
+ }
1385
1615
  });
1386
1616
  }
1387
1617
  function extractFunctionOrComputed(node, obj, k, v) {
1388
1618
  if (isPromise(v)) {
1389
- extractPromise(getChildNode(node, k), v);
1619
+ const childNode = getChildNode(node, k);
1620
+ extractPromise(childNode, v);
1621
+ setNodeValue(childNode, undefined);
1390
1622
  }
1391
1623
  else if (typeof v === 'function') {
1392
1624
  extractFunction(node, k, v);
1625
+ delete obj[k];
1393
1626
  }
1394
1627
  else if (typeof v == 'object' && v !== null && v !== undefined) {
1395
1628
  const childNode = getNode(v);
@@ -1409,10 +1642,19 @@ function get(node, options) {
1409
1642
  return peek(node);
1410
1643
  }
1411
1644
  function peek(node) {
1412
- const value = getNodeValue(node);
1645
+ if (node.dirtyFn) {
1646
+ node.dirtyFn();
1647
+ globalState.dirtyNodes.delete(node);
1648
+ node.dirtyFn = undefined;
1649
+ }
1650
+ let value = getNodeValue(node);
1413
1651
  // If node is not yet lazily computed go do that
1414
- if (node.lazy) {
1652
+ const lazy = node.lazy;
1653
+ if (lazy) {
1415
1654
  delete node.lazy;
1655
+ if (isFunction(node) || isFunction(lazy)) {
1656
+ value = activateNodeFunction(node, lazy);
1657
+ }
1416
1658
  for (const key in value) {
1417
1659
  if (hasOwnProperty.call(value, key)) {
1418
1660
  extractFunctionOrComputed(node, value, key, value[key]);
@@ -1423,6 +1665,265 @@ function peek(node) {
1423
1665
  checkActivate(node);
1424
1666
  return value;
1425
1667
  }
1668
+ function activateNodeFunction(node, lazyFn) {
1669
+ // let prevTarget$: Observable<any>;
1670
+ // let curTarget$: Observable<any>;
1671
+ let update;
1672
+ let wasPromise;
1673
+ let timeoutRetry;
1674
+ const attemptNum = { current: 0 };
1675
+ const activateFn = (isFunction(node) ? node : lazyFn);
1676
+ const doRetry = () => { var _a; return (_a = node.state) === null || _a === void 0 ? void 0 : _a.refreshNum.set((v) => v + 1); };
1677
+ let activatedValue;
1678
+ let disposes = [];
1679
+ let refreshFn;
1680
+ function markDirty() {
1681
+ node.dirtyFn = refreshFn;
1682
+ globalState.dirtyNodes.add(node);
1683
+ }
1684
+ observe(() => {
1685
+ var _a, _b, _c, _d;
1686
+ // const params = createNodeActivationParams(node);
1687
+ // Run the function at this node
1688
+ let value = activateFn();
1689
+ // If target is an observable, make this node a link to it
1690
+ if (isObservable(value)) {
1691
+ // If the computed is a proxy to another observable
1692
+ // link it to the target observable
1693
+ const linkedNode = getNode(value);
1694
+ const prevNode = node.linkedToNode;
1695
+ node.linkedToNode = linkedNode;
1696
+ if (!linkedNode.linkedFromNodes) {
1697
+ linkedNode.linkedFromNodes = new Set();
1698
+ }
1699
+ linkedNode.linkedFromNodes.add(node);
1700
+ onChange(linkedNode, ({ value: newValue }) => {
1701
+ value = newValue;
1702
+ set(node, value);
1703
+ }, { initial: true });
1704
+ // If the target observable is different then notify for the change
1705
+ if (prevNode) {
1706
+ const value = getNodeValue(linkedNode);
1707
+ const prevValue = getNodeValue(prevNode);
1708
+ notify(node, value, prevValue, 0);
1709
+ }
1710
+ }
1711
+ if (isFunction(value)) {
1712
+ value = value();
1713
+ }
1714
+ const activated = value === null || value === void 0 ? void 0 : value[symbolActivated];
1715
+ if (activated) {
1716
+ node.activationState = activated;
1717
+ value = activated.initial;
1718
+ }
1719
+ wasPromise = isPromise(value);
1720
+ // Activate this node if not activated already (may be called recursively)
1721
+ // TODO: Is calling recursively bad? If so can it be fixed?
1722
+ if (!node.activated) {
1723
+ node.activated = true;
1724
+ const isCached = !!((_a = node.activationState) === null || _a === void 0 ? void 0 : _a.cache);
1725
+ wasPromise = wasPromise || !!isCached;
1726
+ const activateNodeFn = wasPromise ? globalState.activateNode : activateNodeBase;
1727
+ const { update: newUpdate, value: newValue } = activateNodeFn(node, doRetry, !!wasPromise, value);
1728
+ update = newUpdate;
1729
+ value = newValue;
1730
+ }
1731
+ else if (node.activationState) {
1732
+ if (!node.activationState.persistedRetry) {
1733
+ const activated = node.activationState;
1734
+ value = (_c = (_b = activated.get) === null || _b === void 0 ? void 0 : _b.call(activated, {})) !== null && _c !== void 0 ? _c : activated.initial;
1735
+ }
1736
+ }
1737
+ wasPromise = wasPromise || isPromise(value);
1738
+ get(getNode((_d = node.state) === null || _d === void 0 ? void 0 : _d.refreshNum));
1739
+ return value;
1740
+ }, (e) => {
1741
+ const { value, nodes, refresh } = e;
1742
+ refreshFn = refresh;
1743
+ if (!globalState.isLoadingRemote$.peek()) {
1744
+ if (wasPromise) {
1745
+ if (node.activationState) {
1746
+ const { retry, initial } = node.activationState;
1747
+ let onError;
1748
+ if (retry) {
1749
+ if (timeoutRetry === null || timeoutRetry === void 0 ? void 0 : timeoutRetry.current) {
1750
+ clearTimeout(timeoutRetry.current);
1751
+ }
1752
+ const { handleError, timeout } = setupRetry(retry, doRetry, attemptNum);
1753
+ onError = handleError;
1754
+ timeoutRetry = timeout;
1755
+ node.activationState.onError = onError;
1756
+ }
1757
+ if (value && isPromise(value)) {
1758
+ // Extract the promise to make it set the value/error when it comes in
1759
+ extractPromise(node, value, update, onError);
1760
+ }
1761
+ // Set this to undefined only if it's replacing the activation function,
1762
+ // so we don't overwrite it if it already has real data from either local
1763
+ // cache or a previous run
1764
+ if (isFunction(getNodeValue(node))) {
1765
+ setNodeValue(node, initial !== null && initial !== void 0 ? initial : undefined);
1766
+ }
1767
+ }
1768
+ else if (node.activated) {
1769
+ let onError;
1770
+ // Extract the promise to make it set the value/error when it comes in
1771
+ extractPromise(node, value, update, onError);
1772
+ // Set this to undefined only if it's replacing the activation function,
1773
+ // so we don't overwrite it if it already has real data from either local
1774
+ // cache or a previous run
1775
+ if (isFunction(getNodeValue(node))) {
1776
+ setNodeValue(node, undefined);
1777
+ }
1778
+ }
1779
+ }
1780
+ else {
1781
+ activatedValue = value;
1782
+ set(node, value);
1783
+ node.state.assign({
1784
+ isLoaded: true,
1785
+ error: undefined,
1786
+ });
1787
+ }
1788
+ }
1789
+ disposes.forEach((fn) => fn());
1790
+ disposes = [];
1791
+ nodes === null || nodes === void 0 ? void 0 : nodes.forEach(({ node }) => {
1792
+ disposes.push(onChange(node, markDirty, { immediate: true }));
1793
+ });
1794
+ e.cancel = true;
1795
+ }, { fromComputed: true });
1796
+ return activatedValue;
1797
+ }
1798
+ const activateNodeBase = (globalState.activateNode = function activateNodeBase(node, refresh, wasPromise, value) {
1799
+ if (!node.state) {
1800
+ node.state = createObservable({
1801
+ isLoaded: false,
1802
+ }, false, extractPromise, getProxy);
1803
+ }
1804
+ let isSetting = false;
1805
+ let isSettingFromSubscribe = false;
1806
+ let _mode = 'set';
1807
+ if (node.activationState) {
1808
+ const { onSet, subscribe, get: getFn, initial, retry, waitFor } = node.activationState;
1809
+ // @ts-expect-error asdf
1810
+ const run = () => getFn({ updateLastSync: noop, setMode: (mode) => (_mode = mode) });
1811
+ value = getFn
1812
+ ? waitFor
1813
+ ? new Promise((resolve) => {
1814
+ whenReady(waitFor, () => {
1815
+ resolve(run());
1816
+ });
1817
+ })
1818
+ : run()
1819
+ : undefined;
1820
+ if (value == undefined || value === null) {
1821
+ value = initial;
1822
+ }
1823
+ let timeoutRetry;
1824
+ if (onSet) {
1825
+ let allChanges = [];
1826
+ let latestValue = undefined;
1827
+ const runChanges = (listenerParams) => {
1828
+ // Don't call the set if this is the first value coming in
1829
+ if (allChanges.length > 0) {
1830
+ let changes;
1831
+ let value;
1832
+ if (listenerParams) {
1833
+ changes = listenerParams.changes;
1834
+ value = listenerParams.value;
1835
+ }
1836
+ else {
1837
+ // If this is called by flushPending then get the change array
1838
+ // that we've been building up.
1839
+ changes = allChanges;
1840
+ value = latestValue;
1841
+ }
1842
+ allChanges = [];
1843
+ latestValue = undefined;
1844
+ globalState.pendingNodes.delete(node);
1845
+ const attemptNum = { current: 0 };
1846
+ const run = () => {
1847
+ let onError;
1848
+ if (retry) {
1849
+ if (timeoutRetry === null || timeoutRetry === void 0 ? void 0 : timeoutRetry.current) {
1850
+ clearTimeout(timeoutRetry.current);
1851
+ }
1852
+ const { handleError, timeout } = setupRetry(retry, run, attemptNum);
1853
+ onError = handleError;
1854
+ timeoutRetry = timeout;
1855
+ }
1856
+ isSetting = true;
1857
+ batch(() => onSet({
1858
+ value,
1859
+ changes,
1860
+ getPrevious: () => {
1861
+ // TODO
1862
+ // debugger;
1863
+ },
1864
+ }, {
1865
+ node,
1866
+ update,
1867
+ refresh,
1868
+ onError,
1869
+ fromSubscribe: isSettingFromSubscribe,
1870
+ }), () => {
1871
+ isSetting = false;
1872
+ });
1873
+ };
1874
+ run();
1875
+ }
1876
+ };
1877
+ const onChangeImmediate = ({ value, changes }) => {
1878
+ if (!isSetting || isSettingFromSubscribe) {
1879
+ if (get(getNode(node.state.isLoaded)) &&
1880
+ (changes.length > 1 || !isFunction(changes[0].prevAtPath))) {
1881
+ latestValue = value;
1882
+ allChanges.push(...changes);
1883
+ globalState.pendingNodes.set(node, runChanges);
1884
+ }
1885
+ }
1886
+ };
1887
+ // Create an immediate listener to mark this node as pending. Then actually run
1888
+ // the changes at the end of the batch so everything is properly batched.
1889
+ // However, this can be short circuited if the user calls get() or peek()
1890
+ // in which case the set needs to run immediately so that the values are up to date.
1891
+ onChange(node, onChangeImmediate, { immediate: true });
1892
+ onChange(node, runChanges);
1893
+ }
1894
+ if (process.env.NODE_ENV === 'development' && node.activationState.cache) {
1895
+ // TODO Better message
1896
+ console.log('[legend-state] Using cache without setting up persistence first');
1897
+ }
1898
+ if (process.env.NODE_ENV === 'development' && node.activationState.retry) {
1899
+ // TODO Better message
1900
+ console.log('[legend-state] Using retry without setting up persistence first');
1901
+ }
1902
+ if (subscribe) {
1903
+ const updateFromSubscribe = (params) => {
1904
+ isSettingFromSubscribe = true;
1905
+ update(params);
1906
+ isSettingFromSubscribe = false;
1907
+ };
1908
+ subscribe({ node, update: updateFromSubscribe, refresh });
1909
+ }
1910
+ }
1911
+ const update = ({ value, mode }) => {
1912
+ // TODO: This isSetting might not be necessary? Tests still work if removing it.
1913
+ // Write tests that would break it if removed? I'd guess a combination of subscribe and
1914
+ if (!isSetting) {
1915
+ isSetting = true;
1916
+ if (_mode === 'assign' || mode === 'assign') {
1917
+ assign(node, value);
1918
+ }
1919
+ else {
1920
+ set(node, value);
1921
+ }
1922
+ isSetting = false;
1923
+ }
1924
+ };
1925
+ return { update, value };
1926
+ });
1426
1927
 
1427
1928
  const fns = ['get', 'set', 'peek', 'onChange', 'toggle'];
1428
1929
  function ObservablePrimitiveClass(node) {
@@ -1439,8 +1940,14 @@ function proto(key, fn) {
1439
1940
  return fn.call(this, this._node, ...args);
1440
1941
  };
1441
1942
  }
1442
- proto('peek', peek);
1443
- proto('get', get);
1943
+ proto('peek', (node) => {
1944
+ flushPending();
1945
+ return peek(node);
1946
+ });
1947
+ proto('get', (node, options) => {
1948
+ flushPending();
1949
+ return get(node, options);
1950
+ });
1444
1951
  proto('set', set);
1445
1952
  proto('onChange', onChange);
1446
1953
  // Getters
@@ -1466,121 +1973,12 @@ ObservablePrimitiveClass.prototype.delete = function () {
1466
1973
  };
1467
1974
 
1468
1975
  function observable(value) {
1469
- return createObservable(value, false, getProxy, ObservablePrimitiveClass);
1976
+ return createObservable(value, false, extractPromise, getProxy, ObservablePrimitiveClass);
1470
1977
  }
1471
1978
  function observablePrimitive(value) {
1472
- return createObservable(value, true, getProxy, ObservablePrimitiveClass);
1473
- }
1474
-
1475
- function setupTracking(nodes, update, noArgs, immediate) {
1476
- let listeners = [];
1477
- // Listen to tracked nodes
1478
- nodes === null || nodes === void 0 ? void 0 : nodes.forEach((tracked) => {
1479
- const { node, track } = tracked;
1480
- listeners.push(onChange(node, update, { trackingType: track, immediate, noArgs }));
1481
- });
1482
- return () => {
1483
- if (listeners) {
1484
- for (let i = 0; i < listeners.length; i++) {
1485
- listeners[i]();
1486
- }
1487
- listeners = undefined;
1488
- }
1489
- };
1490
- }
1491
-
1492
- function trackSelector(selector, update, observeEvent, observeOptions, createResubscribe) {
1493
- var _a;
1494
- let nodes;
1495
- let value;
1496
- let dispose;
1497
- let tracker;
1498
- let resubscribe;
1499
- let updateFn = update;
1500
- if (isObservable(selector)) {
1501
- value = selector.peek();
1502
- dispose = selector.onChange(update);
1503
- resubscribe = createResubscribe ? selector.onChange(update) : undefined;
1504
- }
1505
- else {
1506
- // Compute the selector inside a tracking context
1507
- beginTracking();
1508
- value = selector ? computeSelector(selector, observeEvent, observeOptions === null || observeOptions === void 0 ? void 0 : observeOptions.fromComputed) : selector;
1509
- tracker = tracking.current;
1510
- nodes = tracker.nodes;
1511
- endTracking();
1512
- if ((process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') && tracker && nodes) {
1513
- (_a = tracker.traceListeners) === null || _a === void 0 ? void 0 : _a.call(tracker, nodes);
1514
- if (tracker.traceUpdates) {
1515
- updateFn = tracker.traceUpdates(update);
1516
- }
1517
- // Clear tracing so it doesn't leak to other components
1518
- tracker.traceListeners = undefined;
1519
- tracker.traceUpdates = undefined;
1520
- }
1521
- }
1522
- if (!(observeEvent === null || observeEvent === void 0 ? void 0 : observeEvent.cancel)) {
1523
- // Do tracing if it was requested
1524
- // useSyncExternalStore doesn't subscribe until after the component mount.
1525
- // We want to subscribe immediately so we don't miss any updates
1526
- dispose = setupTracking(nodes, updateFn, false, observeOptions === null || observeOptions === void 0 ? void 0 : observeOptions.immediate);
1527
- resubscribe = createResubscribe ? () => setupTracking(nodes, updateFn) : undefined;
1528
- }
1529
- return { value, dispose, resubscribe };
1530
- }
1531
-
1532
- function observe(selectorOrRun, reactionOrOptions, options) {
1533
- let reaction;
1534
- if (isFunction(reactionOrOptions)) {
1535
- reaction = reactionOrOptions;
1536
- }
1537
- else {
1538
- options = reactionOrOptions;
1539
- }
1540
- let dispose;
1541
- const e = { num: 0 };
1542
- // Wrap it in a function so it doesn't pass all the arguments to run()
1543
- const update = function () {
1544
- if (e.onCleanup) {
1545
- e.onCleanup();
1546
- e.onCleanup = undefined;
1547
- }
1548
- // Run in a batch so changes don't happen until we're done tracking here
1549
- beginBatch();
1550
- // Run the function/selector
1551
- delete e.value;
1552
- // Dispose listeners from previous run
1553
- dispose === null || dispose === void 0 ? void 0 : dispose();
1554
- const { dispose: _dispose, value } = trackSelector(selectorOrRun, update, e, options);
1555
- dispose = _dispose;
1556
- e.value = value;
1557
- if (e.onCleanupReaction) {
1558
- e.onCleanupReaction();
1559
- e.onCleanupReaction = undefined;
1560
- }
1561
- endBatch();
1562
- // Call the reaction if there is one and the value changed
1563
- if (reaction &&
1564
- (e.num > 0 || !isEvent(selectorOrRun)) &&
1565
- (e.previous !== e.value || (options === null || options === void 0 ? void 0 : options.fromComputed))) {
1566
- reaction(e);
1567
- }
1568
- // Update the previous value
1569
- e.previous = e.value;
1570
- // Increment the counter
1571
- e.num++;
1572
- };
1573
- update();
1574
- // Return function calling dispose because dispose may be changed in update()
1575
- return () => {
1576
- var _a, _b;
1577
- (_a = e.onCleanup) === null || _a === void 0 ? void 0 : _a.call(e);
1578
- e.onCleanup = undefined;
1579
- (_b = e.onCleanupReaction) === null || _b === void 0 ? void 0 : _b.call(e);
1580
- e.onCleanupReaction = undefined;
1581
- dispose === null || dispose === void 0 ? void 0 : dispose();
1582
- };
1979
+ return createObservable(value, true, extractPromise, getProxy, ObservablePrimitiveClass);
1583
1980
  }
1981
+ globalState.isLoadingRemote$ = observable(false);
1584
1982
 
1585
1983
  function computed(compute, set$1) {
1586
1984
  // Create an observable for this computed variable
@@ -1744,80 +2142,23 @@ function proxy(get, set) {
1744
2142
  return obs;
1745
2143
  }
1746
2144
 
1747
- function _when(predicate, effect, checkReady) {
1748
- // If predicate is a regular Promise skip all the observable stuff
1749
- if (isPromise(predicate)) {
1750
- return effect ? predicate.then(effect) : predicate;
1751
- }
1752
- let value;
1753
- // Create a wrapping fn that calls the effect if predicate returns true
1754
- function run(e) {
1755
- const ret = computeSelector(predicate);
1756
- if (!isPromise(ret) && (checkReady ? isObservableValueReady(ret) : ret)) {
1757
- value = ret;
1758
- // Set cancel so that observe does not track anymore
1759
- e.cancel = true;
1760
- }
1761
- return value;
1762
- }
1763
- function doEffect() {
1764
- // If value is truthy then run the effect
1765
- effect === null || effect === void 0 ? void 0 : effect(value);
1766
- }
1767
- // Run in an observe
1768
- observe(run, doEffect);
1769
- // If first run resulted in a truthy value just return it.
1770
- // It will have set e.cancel so no need to dispose
1771
- if (isPromise(value)) {
1772
- return effect ? value.then(effect) : value;
1773
- }
1774
- else if (value !== undefined) {
1775
- return Promise.resolve(value);
1776
- }
1777
- else {
1778
- // Wrap it in a promise
1779
- const promise = new Promise((resolve) => {
1780
- if (effect) {
1781
- const originalEffect = effect;
1782
- effect = (value) => {
1783
- const effectValue = originalEffect(value);
1784
- resolve(effectValue);
1785
- };
1786
- }
1787
- else {
1788
- effect = resolve;
1789
- }
1790
- });
1791
- return promise;
1792
- }
1793
- }
1794
- function when(predicate, effect) {
1795
- return _when(predicate, effect, false);
1796
- }
1797
- function whenReady(predicate, effect) {
1798
- return _when(predicate, effect, true);
1799
- }
1800
-
1801
2145
  const internal = {
1802
- clone,
1803
2146
  ensureNodeValue,
1804
2147
  findIDKey,
1805
2148
  get,
1806
2149
  getNode,
1807
- getPathType,
1808
2150
  getProxy,
1809
2151
  globalState,
1810
- initializePathType,
1811
2152
  observableFns,
1812
2153
  optimized,
1813
2154
  peek,
1814
- safeParse,
1815
- safeStringify,
1816
2155
  set,
1817
2156
  setAtPath,
1818
2157
  setNodeValue,
2158
+ setupRetry,
2159
+ symbolActivated,
1819
2160
  symbolDelete,
1820
2161
  };
1821
2162
 
1822
- 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 };
2163
+ export { ObservablePrimitiveClass, activated, 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 };
1823
2164
  //# sourceMappingURL=index.mjs.map