@legendapp/state 2.2.0-next.57 → 2.2.0-next.59

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 (44) hide show
  1. package/README.md +4 -2
  2. package/index.d.ts +8 -4
  3. package/index.js +153 -79
  4. package/index.js.map +1 -1
  5. package/index.mjs +154 -79
  6. package/index.mjs.map +1 -1
  7. package/package.json +1 -1
  8. package/persist-plugins/async-storage.js +4 -3
  9. package/persist-plugins/async-storage.js.map +1 -1
  10. package/persist-plugins/async-storage.mjs +5 -4
  11. package/persist-plugins/async-storage.mjs.map +1 -1
  12. package/persist-plugins/firebase.d.ts +1 -1
  13. package/persist-plugins/firebase.js +6 -9
  14. package/persist-plugins/firebase.js.map +1 -1
  15. package/persist-plugins/firebase.mjs +7 -10
  16. package/persist-plugins/firebase.mjs.map +1 -1
  17. package/persist-plugins/local-storage.js +4 -11
  18. package/persist-plugins/local-storage.js.map +1 -1
  19. package/persist-plugins/local-storage.mjs +5 -12
  20. package/persist-plugins/local-storage.mjs.map +1 -1
  21. package/persist-plugins/mmkv.js +3 -2
  22. package/persist-plugins/mmkv.js.map +1 -1
  23. package/persist-plugins/mmkv.mjs +4 -3
  24. package/persist-plugins/mmkv.mjs.map +1 -1
  25. package/persist.js +41 -30
  26. package/persist.js.map +1 -1
  27. package/persist.mjs +42 -31
  28. package/persist.mjs.map +1 -1
  29. package/react.js +36 -36
  30. package/react.js.map +1 -1
  31. package/react.mjs +39 -39
  32. package/react.mjs.map +1 -1
  33. package/src/computed.d.ts +5 -4
  34. package/src/globals.d.ts +6 -2
  35. package/src/helpers.d.ts +2 -1
  36. package/src/observableInterfaces.d.ts +8 -11
  37. package/src/observableTypes.d.ts +18 -8
  38. package/src/persist/synced.d.ts +2 -2
  39. package/src/persist-plugins/firebase.d.ts +1 -1
  40. package/src/persistTypes.d.ts +11 -9
  41. package/src/react/useEffectOnce.d.ts +1 -1
  42. package/src/react/useMount.d.ts +1 -1
  43. package/src/trackSelector.d.ts +3 -3
  44. package/src/activated.d.ts +0 -2
package/README.md CHANGED
@@ -21,9 +21,11 @@ observe(() => {
21
21
  console.log(state$.settings.theme.get())
22
22
  })
23
23
 
24
+ // Automatically re-render components when observables change
25
+ enableReactTracking({ auto: true })
26
+
24
27
  const Component = function Component() {
25
- // use() makes this component re-render whenever it changes
26
- const theme = state$.settings.theme.use()
28
+ const theme = state$.settings.theme.get()
27
29
 
28
30
  return <div>Theme: {theme}</div>
29
31
  }
package/index.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- export { activated } from './src/activated';
2
1
  export { batch, beginBatch, endBatch } from './src/batching';
3
2
  export { computed } from './src/computed';
4
3
  export { configureLegendState } from './src/config';
@@ -15,14 +14,16 @@ export { proxy } from './src/proxy';
15
14
  export { trackSelector } from './src/trackSelector';
16
15
  export { when, whenReady } from './src/when';
17
16
  import { get, getProxy, peek, set } from './src/ObservableObject';
18
- import { ensureNodeValue, findIDKey, getNode, setNodeValue } from './src/globals';
19
- import { setAtPath } from './src/helpers';
17
+ import { clone, ensureNodeValue, findIDKey, getNode, getPathType, safeParse, safeStringify, setNodeValue } from './src/globals';
18
+ import { initializePathType, setAtPath } from './src/helpers';
20
19
  import { runWithRetry } from './src/retry';
21
20
  export declare const internal: {
21
+ clone: typeof clone;
22
22
  ensureNodeValue: typeof ensureNodeValue;
23
23
  findIDKey: typeof findIDKey;
24
24
  get: typeof get;
25
25
  getNode: typeof getNode;
26
+ getPathType: typeof getPathType;
26
27
  getProxy: typeof getProxy;
27
28
  globalState: {
28
29
  isLoadingLocal: boolean;
@@ -35,13 +36,16 @@ export declare const internal: {
35
36
  pendingNodes: Map<import("./src/observableInterfaces").NodeValue, () => void>;
36
37
  dirtyNodes: Set<import("./src/observableInterfaces").NodeValue>;
37
38
  };
39
+ initializePathType: typeof initializePathType;
38
40
  observableFns: Map<string, (node: import("./src/observableInterfaces").NodeValue, ...args: any[]) => any>;
39
41
  optimized: symbol;
40
42
  peek: typeof peek;
41
43
  runWithRetry: typeof runWithRetry;
44
+ safeParse: typeof safeParse;
45
+ safeStringify: typeof safeStringify;
42
46
  set: typeof set;
43
47
  setAtPath: typeof setAtPath;
44
48
  setNodeValue: typeof setNodeValue;
45
- symbolActivated: symbol;
49
+ symbolBound: symbol;
46
50
  symbolDelete: symbol;
47
51
  };
package/index.js CHANGED
@@ -61,7 +61,7 @@ const symbolGetNode = Symbol('getNode');
61
61
  const symbolDelete = /* @__PURE__ */ Symbol('delete');
62
62
  const symbolOpaque = Symbol('opaque');
63
63
  const optimized = Symbol('optimized');
64
- const symbolActivated = Symbol('activated');
64
+ const symbolBound = Symbol('bound');
65
65
  const globalState = {
66
66
  isLoadingLocal: false,
67
67
  isMerging: false,
@@ -70,6 +70,52 @@ const globalState = {
70
70
  pendingNodes: new Map(),
71
71
  dirtyNodes: new Set(),
72
72
  };
73
+ function getPathType(value) {
74
+ return isArray(value) ? 'array' : value instanceof Map ? 'map' : value instanceof Set ? 'set' : 'object';
75
+ }
76
+ function replacer(_, 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 {
90
+ return value;
91
+ }
92
+ }
93
+ const ISO8601 = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/;
94
+ function reviver(_, value) {
95
+ if (value) {
96
+ if (typeof value === 'string' && ISO8601.test(value)) {
97
+ return new Date(value);
98
+ }
99
+ if (typeof value === 'object') {
100
+ if (value.__LSType === 'Map') {
101
+ return new Map(value.value);
102
+ }
103
+ else if (value.__LSType === 'Set') {
104
+ return new Set(value.value);
105
+ }
106
+ }
107
+ }
108
+ return value;
109
+ }
110
+ function safeStringify(value) {
111
+ return JSON.stringify(value, replacer);
112
+ }
113
+ function safeParse(value) {
114
+ return JSON.parse(value, reviver);
115
+ }
116
+ function clone(value) {
117
+ return safeParse(safeStringify(value));
118
+ }
73
119
  function isObservable(obs) {
74
120
  return !!obs && !!obs[symbolGetNode];
75
121
  }
@@ -197,12 +243,6 @@ function extractFunction(node, key, fnOrComputed) {
197
243
  node.functions.set(key, fnOrComputed);
198
244
  }
199
245
 
200
- function activated(params) {
201
- return (() => ({
202
- [symbolActivated]: params,
203
- }));
204
- }
205
-
206
246
  let timeout;
207
247
  let numInBatch = 0;
208
248
  let isRunningBatch = false;
@@ -229,22 +269,28 @@ function isArraySubset(mainArr, subsetArr) {
229
269
  function createPreviousHandlerInner(value, changes) {
230
270
  try {
231
271
  // Clones the current state and inject the previous data at the changed path
232
- let clone = value ? JSON.parse(JSON.stringify(value)) : {};
272
+ let cloned = value ? clone(value) : {};
233
273
  for (let i = 0; i < changes.length; i++) {
234
274
  const { path, prevAtPath } = changes[i];
235
- let o = clone;
275
+ let o = cloned;
236
276
  if (path.length > 0) {
237
277
  let i;
238
278
  for (i = 0; i < path.length - 1; i++) {
239
279
  o = o[path[i]];
240
280
  }
241
- o[path[i]] = prevAtPath;
281
+ const key = path[i];
282
+ if (o instanceof Map) {
283
+ o.set(key, prevAtPath);
284
+ }
285
+ else {
286
+ o[key] = prevAtPath;
287
+ }
242
288
  }
243
289
  else {
244
- clone = prevAtPath;
290
+ cloned = prevAtPath;
245
291
  }
246
292
  }
247
- return clone;
293
+ return cloned;
248
294
  }
249
295
  catch (_a) {
250
296
  return undefined;
@@ -319,7 +365,7 @@ function computeChangesRecursive(changesInBatch, node, value, path, pathTypes, v
319
365
  const parent = node.parent;
320
366
  if (parent) {
321
367
  const parentValue = getNodeValue(parent);
322
- computeChangesRecursive(changesInBatch, parent, parentValue, [node.key].concat(path), [(isArray(value) ? 'array' : 'object')].concat(pathTypes), valueAtPath, prevAtPath, immediate, level + 1, whenOptimizedOnlyIf);
368
+ computeChangesRecursive(changesInBatch, parent, parentValue, [node.key].concat(path), [getPathType(value)].concat(pathTypes), valueAtPath, prevAtPath, immediate, level + 1, whenOptimizedOnlyIf);
323
369
  }
324
370
  }
325
371
  }
@@ -460,6 +506,9 @@ function getNodeAtPath(obj, path) {
460
506
  }
461
507
 
462
508
  function createObservable(value, makePrimitive, extractPromise, createObject, createPrimitive) {
509
+ if (isObservable(value)) {
510
+ return value;
511
+ }
463
512
  const valueIsPromise = isPromise(value);
464
513
  const valueIsFunction = isFunction(value);
465
514
  const root = {
@@ -528,7 +577,7 @@ function setAtPath(obj, path, pathTypes, value, fullObj, restore) {
528
577
  break;
529
578
  }
530
579
  else if (o[p] === undefined || o[p] === null) {
531
- o[p] = pathTypes[i] === 'array' ? [] : {};
580
+ o[p] = initializePathType(pathTypes[i]);
532
581
  }
533
582
  o = o[p];
534
583
  if (oFull) {
@@ -546,8 +595,8 @@ function setInObservableAtPath(obs, path, pathTypes, value, mode) {
546
595
  let v = value;
547
596
  for (let i = 0; i < path.length; i++) {
548
597
  const p = path[i];
549
- if (!o.peek()[p] && pathTypes[i] === 'array') {
550
- o[p].set([]);
598
+ if (!o.peek()[p]) {
599
+ o[p].set(initializePathType(pathTypes[i]));
551
600
  }
552
601
  o = o[p];
553
602
  v = v[p];
@@ -623,7 +672,7 @@ function constructObjectWithPath(path, pathTypes, value) {
623
672
  let o = (out = {});
624
673
  for (let i = 0; i < path.length; i++) {
625
674
  const p = path[i];
626
- o[p] = i === path.length - 1 ? value : pathTypes[i] === 'array' ? [] : {};
675
+ o[p] = i === path.length - 1 ? value : initializePathType(pathTypes[i]);
627
676
  o = o[p];
628
677
  }
629
678
  }
@@ -636,7 +685,7 @@ function deconstructObjectWithPath(path, pathTypes, value) {
636
685
  let o = value;
637
686
  for (let i = 0; i < path.length; i++) {
638
687
  const p = path[i];
639
- o = o ? o[p] : pathTypes[i] === 'array' ? [] : {};
688
+ o = o ? o[p] : initializePathType(pathTypes[i]);
640
689
  }
641
690
  return o;
642
691
  }
@@ -647,6 +696,18 @@ function setSilently(obs, newValue) {
647
696
  const node = getNode(obs);
648
697
  return setNodeValue(node, newValue).newValue;
649
698
  }
699
+ function initializePathType(pathType) {
700
+ switch (pathType) {
701
+ case 'array':
702
+ return [];
703
+ case 'object':
704
+ return {};
705
+ case 'map':
706
+ return new Map();
707
+ case 'set':
708
+ return new Set();
709
+ }
710
+ }
650
711
 
651
712
  function onChange(node, callback, options = {}, fromLinks) {
652
713
  var _a;
@@ -815,42 +876,39 @@ function updateTracking(node, track) {
815
876
 
816
877
  function trackSelector(selector, update, observeEvent, observeOptions, createResubscribe) {
817
878
  var _a;
818
- let nodes;
819
- let value;
820
879
  let dispose;
821
- let tracker;
822
880
  let resubscribe;
823
881
  let updateFn = update;
824
- if (isObservable(selector)) {
825
- value = selector.peek();
826
- dispose = selector.onChange(update);
827
- resubscribe = createResubscribe ? selector.onChange(update) : undefined;
828
- }
829
- else {
830
- // Compute the selector inside a tracking context
831
- beginTracking();
832
- value = selector ? computeSelector(selector, observeEvent, observeOptions === null || observeOptions === void 0 ? void 0 : observeOptions.fromComputed) : selector;
833
- tracker = tracking.current;
834
- nodes = tracker.nodes;
835
- endTracking();
836
- if ((process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') && tracker && nodes) {
837
- (_a = tracker.traceListeners) === null || _a === void 0 ? void 0 : _a.call(tracker, nodes);
838
- if (tracker.traceUpdates) {
839
- updateFn = tracker.traceUpdates(update);
840
- }
841
- // Clear tracing so it doesn't leak to other components
842
- tracker.traceListeners = undefined;
843
- tracker.traceUpdates = undefined;
844
- }
882
+ beginTracking();
883
+ const value = selector ? computeSelector(selector, observeEvent, observeOptions === null || observeOptions === void 0 ? void 0 : observeOptions.fromComputed) : selector;
884
+ const tracker = tracking.current;
885
+ const nodes = tracker.nodes;
886
+ endTracking();
887
+ if ((process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') && tracker && nodes) {
888
+ (_a = tracker.traceListeners) === null || _a === void 0 ? void 0 : _a.call(tracker, nodes);
889
+ if (tracker.traceUpdates) {
890
+ updateFn = tracker.traceUpdates(update);
891
+ }
892
+ // Clear tracing so it doesn't leak to other components
893
+ tracker.traceListeners = undefined;
894
+ tracker.traceUpdates = undefined;
845
895
  }
846
896
  if (!(observeEvent === null || observeEvent === void 0 ? void 0 : observeEvent.cancel)) {
847
897
  // Do tracing if it was requested
848
898
  // useSyncExternalStore doesn't subscribe until after the component mount.
849
899
  // We want to subscribe immediately so we don't miss any updates
850
900
  dispose = setupTracking(nodes, updateFn, false, observeOptions === null || observeOptions === void 0 ? void 0 : observeOptions.immediate);
851
- resubscribe = createResubscribe ? () => setupTracking(nodes, updateFn) : undefined;
901
+ if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
902
+ resubscribe = createResubscribe
903
+ ? () => {
904
+ dispose === null || dispose === void 0 ? void 0 : dispose();
905
+ dispose = setupTracking(nodes, updateFn);
906
+ return dispose;
907
+ }
908
+ : undefined;
909
+ }
852
910
  }
853
- return { value, nodes, dispose, resubscribe };
911
+ return { nodes, value, dispose, resubscribe };
854
912
  }
855
913
 
856
914
  function observe(selectorOrRun, reactionOrOptions, options) {
@@ -1076,15 +1134,22 @@ function updateNodes(parent, obj, prevValue) {
1076
1134
  isIdFieldFunction = isFunction(idField);
1077
1135
  prevChildrenById = new Map();
1078
1136
  moved = [];
1079
- const keysSeen = process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test'
1080
- ? new Set()
1081
- : undefined;
1082
- if (parent.children) {
1083
- for (let i = 0; i < prevValue.length; i++) {
1084
- const p = prevValue[i];
1085
- if (p) {
1086
- const child = parent.children.get(i + '');
1087
- if (child) {
1137
+ }
1138
+ const keysSeen = process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test'
1139
+ ? new Set()
1140
+ : undefined;
1141
+ if (parent.children) {
1142
+ for (let i = 0; i < prevValue.length; i++) {
1143
+ const p = prevValue[i];
1144
+ if (p) {
1145
+ const child = parent.children.get(i + '');
1146
+ if (child) {
1147
+ if (!obj[i]) {
1148
+ // If the previous value is not in the new array and it
1149
+ // is an activated, disable its listeners
1150
+ handleDeletedChild(child, p);
1151
+ }
1152
+ if (idField) {
1088
1153
  const key = isIdFieldFunction
1089
1154
  ? idField(p)
1090
1155
  : p[idField];
@@ -1113,12 +1178,7 @@ function updateNodes(parent, obj, prevValue) {
1113
1178
  const child = getChildNode(parent, key);
1114
1179
  const prev = isPrevMap ? prevValue.get(key) : prevValue[key];
1115
1180
  if (prev !== undefined) {
1116
- if (!isPrimitive(prev)) {
1117
- updateNodes(child, undefined, prev);
1118
- }
1119
- if (child.listeners || child.listenersImmediate) {
1120
- notify(child, undefined, prev, 0);
1121
- }
1181
+ handleDeletedChild(child, prev);
1122
1182
  }
1123
1183
  }
1124
1184
  }
@@ -1140,7 +1200,10 @@ function updateNodes(parent, obj, prevValue) {
1140
1200
  : undefined;
1141
1201
  const existingChild = (_c = parent.children) === null || _c === void 0 ? void 0 : _c.get(key);
1142
1202
  if (isObservable(value)) {
1143
- if ((existingChild === null || existingChild === void 0 ? void 0 : existingChild.linkedToNode) === getNode(value)) {
1203
+ const valueNode = getNode(value);
1204
+ if ((existingChild === null || existingChild === void 0 ? void 0 : existingChild.linkedToNode) === valueNode) {
1205
+ const targetValue = getNodeValue(valueNode);
1206
+ isMap ? obj.set(key, targetValue) : (obj[key] = targetValue);
1144
1207
  continue;
1145
1208
  }
1146
1209
  const obs = value;
@@ -1224,6 +1287,19 @@ function updateNodes(parent, obj, prevValue) {
1224
1287
  }
1225
1288
  return retValue !== null && retValue !== void 0 ? retValue : false;
1226
1289
  }
1290
+ function handleDeletedChild(child, p) {
1291
+ var _a, _b;
1292
+ // If the previous value is not in the new array and it
1293
+ // is an activated, disable its listeners
1294
+ (_a = child.linkedToNodeDispose) === null || _a === void 0 ? void 0 : _a.call(child);
1295
+ (_b = child.activatedObserveDispose) === null || _b === void 0 ? void 0 : _b.call(child);
1296
+ if (!isPrimitive(p)) {
1297
+ updateNodes(child, undefined, p);
1298
+ }
1299
+ if (child.listeners || child.listenersImmediate) {
1300
+ notify(child, undefined, p, 0);
1301
+ }
1302
+ }
1227
1303
  function getProxy(node, p, asFunction) {
1228
1304
  // Get the child node if p prop
1229
1305
  if (p !== undefined)
@@ -1582,6 +1658,9 @@ function updateNodesAndNotify(node, newValue, prevValue, childNode, isPrim, isRo
1582
1658
  childNode = node;
1583
1659
  // Make sure we don't call too many listeners for every property set
1584
1660
  beginBatch();
1661
+ if (isPrim === undefined) {
1662
+ isPrim = isPrimitive(newValue);
1663
+ }
1585
1664
  let hasADiff = isPrim;
1586
1665
  let whenOptimizedOnlyIf = false;
1587
1666
  // If new value is an object or array update notify down the tree
@@ -1728,7 +1807,7 @@ function activateNodeFunction(node, lazyFn) {
1728
1807
  value = value();
1729
1808
  }
1730
1809
  const activated = !isObservable(value)
1731
- ? value === null || value === void 0 ? void 0 : value[symbolActivated]
1810
+ ? value === null || value === void 0 ? void 0 : value[symbolBound]
1732
1811
  : undefined;
1733
1812
  if (activated) {
1734
1813
  node.activationState = activated;
@@ -1826,12 +1905,12 @@ function activateNodeBase(node, value) {
1826
1905
  }, false, extractPromise, getProxy);
1827
1906
  }
1828
1907
  if (node.activationState) {
1829
- const { onSet, get: getFn, initial } = node.activationState;
1908
+ const { set: setFn, get: getFn, initial } = node.activationState;
1830
1909
  value = getFn === null || getFn === void 0 ? void 0 : getFn();
1831
1910
  if (value == undefined || value === null) {
1832
1911
  value = initial;
1833
1912
  }
1834
- if (onSet) {
1913
+ if (setFn) {
1835
1914
  let allChanges = [];
1836
1915
  let latestValue = undefined;
1837
1916
  let runNumber = 0;
@@ -1865,7 +1944,7 @@ function activateNodeBase(node, value) {
1865
1944
  }
1866
1945
  node.isComputing = true;
1867
1946
  batch(() => {
1868
- onSet({
1947
+ setFn({
1869
1948
  value,
1870
1949
  changes,
1871
1950
  getPrevious,
@@ -1988,13 +2067,9 @@ function syncState(obs) {
1988
2067
  }
1989
2068
  globalState.isLoadingRemote$ = observable(false);
1990
2069
 
1991
- function computed(compute, set) {
1992
- return observable(set
1993
- ? activated({
1994
- get: compute,
1995
- onSet: ({ value }) => set(value),
1996
- })
1997
- : compute);
2070
+ function computed(get, set) {
2071
+ const bound = isObject(get) ? get : set ? { get, set: ({ value }) => set(value) } : false;
2072
+ return observable(bound ? () => ({ [symbolBound]: bound }) : get);
1998
2073
  }
1999
2074
 
2000
2075
  function configureLegendState({ observableFunctions, observableProperties: observableProperties$1, }) {
@@ -2048,12 +2123,7 @@ function event() {
2048
2123
  }
2049
2124
 
2050
2125
  function proxy(get, set) {
2051
- return observable((key) => set
2052
- ? activated({
2053
- get: () => get(key),
2054
- onSet: ({ value }) => set(key, value),
2055
- })
2056
- : get(key));
2126
+ return observable((key) => computed({ get: () => get(key), set: set ? ({ value }) => set(key, value) : undefined }));
2057
2127
  }
2058
2128
 
2059
2129
  function calculateRetryDelay(retryOptions, attemptNum) {
@@ -2115,25 +2185,29 @@ function runWithRetry(node, state, fn) {
2115
2185
  }
2116
2186
 
2117
2187
  const internal = {
2188
+ clone,
2118
2189
  ensureNodeValue,
2119
2190
  findIDKey,
2120
2191
  get,
2121
2192
  getNode,
2193
+ getPathType,
2122
2194
  getProxy,
2123
2195
  globalState,
2196
+ initializePathType,
2124
2197
  observableFns,
2125
2198
  optimized,
2126
2199
  peek,
2127
2200
  runWithRetry,
2201
+ safeParse,
2202
+ safeStringify,
2128
2203
  set,
2129
2204
  setAtPath,
2130
2205
  setNodeValue,
2131
- symbolActivated,
2206
+ symbolBound,
2132
2207
  symbolDelete,
2133
2208
  };
2134
2209
 
2135
2210
  exports.ObservablePrimitiveClass = ObservablePrimitiveClass;
2136
- exports.activated = activated;
2137
2211
  exports.batch = batch;
2138
2212
  exports.beginBatch = beginBatch;
2139
2213
  exports.beginTracking = beginTracking;