@legendapp/state 3.0.0-beta.3 → 3.0.0-beta.30

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 (74) hide show
  1. package/.DS_Store +0 -0
  2. package/config/enableReactComponents.js +3 -1
  3. package/config/enableReactComponents.mjs +3 -1
  4. package/config/enableReactTracking.d.mts +2 -1
  5. package/config/enableReactTracking.d.ts +2 -1
  6. package/config/enableReactTracking.js +32 -13
  7. package/config/enableReactTracking.mjs +32 -13
  8. package/index.d.mts +33 -4
  9. package/index.d.ts +33 -4
  10. package/index.js +191 -29
  11. package/index.mjs +191 -29
  12. package/package.json +35 -1
  13. package/persist-plugins/async-storage.js +17 -9
  14. package/persist-plugins/async-storage.mjs +17 -9
  15. package/persist-plugins/expo-sqlite.d.mts +19 -0
  16. package/persist-plugins/expo-sqlite.d.ts +19 -0
  17. package/persist-plugins/expo-sqlite.js +72 -0
  18. package/persist-plugins/expo-sqlite.mjs +69 -0
  19. package/react-native.d.mts +4 -0
  20. package/react-native.d.ts +4 -0
  21. package/react-native.js +53 -0
  22. package/react-native.mjs +40 -0
  23. package/react-reactive/Components.d.mts +19 -0
  24. package/react-reactive/Components.d.ts +19 -0
  25. package/react-reactive/Components.js +53 -0
  26. package/react-reactive/Components.mjs +40 -0
  27. package/react-reactive/enableReactComponents.d.mts +3 -2
  28. package/react-reactive/enableReactComponents.d.ts +3 -2
  29. package/react-reactive/enableReactComponents.js +10 -3
  30. package/react-reactive/enableReactComponents.mjs +10 -3
  31. package/react-reactive/enableReactNativeComponents.d.mts +3 -20
  32. package/react-reactive/enableReactNativeComponents.d.ts +3 -20
  33. package/react-reactive/enableReactNativeComponents.js +8 -3
  34. package/react-reactive/enableReactNativeComponents.mjs +8 -3
  35. package/react-reactive/enableReactive.js +10 -3
  36. package/react-reactive/enableReactive.mjs +10 -3
  37. package/react-reactive/enableReactive.native.js +8 -3
  38. package/react-reactive/enableReactive.native.mjs +8 -3
  39. package/react-reactive/enableReactive.web.js +8 -3
  40. package/react-reactive/enableReactive.web.mjs +8 -3
  41. package/react-web.d.mts +6 -0
  42. package/react-web.d.ts +6 -0
  43. package/react-web.js +39 -0
  44. package/react-web.mjs +37 -0
  45. package/react.d.mts +41 -21
  46. package/react.d.ts +41 -21
  47. package/react.js +36 -23
  48. package/react.mjs +37 -25
  49. package/sync-plugins/crud.d.mts +24 -9
  50. package/sync-plugins/crud.d.ts +24 -9
  51. package/sync-plugins/crud.js +250 -116
  52. package/sync-plugins/crud.mjs +251 -117
  53. package/sync-plugins/firebase.d.mts +7 -3
  54. package/sync-plugins/firebase.d.ts +7 -3
  55. package/sync-plugins/firebase.js +4 -2
  56. package/sync-plugins/firebase.mjs +4 -2
  57. package/sync-plugins/keel.d.mts +12 -13
  58. package/sync-plugins/keel.d.ts +12 -13
  59. package/sync-plugins/keel.js +60 -52
  60. package/sync-plugins/keel.mjs +61 -48
  61. package/sync-plugins/supabase.d.mts +7 -3
  62. package/sync-plugins/supabase.d.ts +7 -3
  63. package/sync-plugins/supabase.js +90 -33
  64. package/sync-plugins/supabase.mjs +91 -34
  65. package/sync-plugins/tanstack-query.d.mts +3 -3
  66. package/sync-plugins/tanstack-query.d.ts +3 -3
  67. package/sync.d.mts +16 -8
  68. package/sync.d.ts +16 -8
  69. package/sync.js +324 -215
  70. package/sync.mjs +323 -215
  71. package/trace.js +5 -6
  72. package/trace.mjs +5 -6
  73. package/types/reactive-native.d.ts +19 -0
  74. package/types/reactive-web.d.ts +7 -0
package/sync.js CHANGED
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var state = require('@legendapp/state');
4
+ var sync = require('@legendapp/state/sync');
4
5
 
5
6
  // src/sync/configureObservableSync.ts
6
7
  var observableSyncConfiguration = {};
@@ -35,12 +36,12 @@ function diffObjects(obj1, obj2, deep = false) {
35
36
  return diff;
36
37
  }
37
38
  function deepEqual(a, b, ignoreFields, nullVsUndefined) {
38
- if (a === b) {
39
+ if (a === b)
39
40
  return true;
40
- }
41
- if (state.isNullOrUndefined(a) !== state.isNullOrUndefined(b)) {
41
+ if (state.isNullOrUndefined(a) !== state.isNullOrUndefined(b))
42
42
  return false;
43
- }
43
+ if (!state.isObject(a) || !state.isObject(b))
44
+ return a === b;
44
45
  if (nullVsUndefined) {
45
46
  a = removeNullUndefined(
46
47
  a,
@@ -53,8 +54,18 @@ function deepEqual(a, b, ignoreFields, nullVsUndefined) {
53
54
  true
54
55
  );
55
56
  }
56
- const replacer = ignoreFields ? (key, value) => ignoreFields.includes(key) ? void 0 : value : void 0;
57
- return JSON.stringify(a, replacer) === JSON.stringify(b, replacer);
57
+ const keysA = Object.keys(a).filter((key) => !(ignoreFields == null ? void 0 : ignoreFields.includes(key)));
58
+ const keysB = Object.keys(b).filter((key) => !(ignoreFields == null ? void 0 : ignoreFields.includes(key)));
59
+ if (keysA.length !== keysB.length)
60
+ return false;
61
+ return keysA.every((key) => {
62
+ if (!Object.prototype.hasOwnProperty.call(b, key))
63
+ return false;
64
+ if (state.isDate(a[key]) && state.isDate(b[key])) {
65
+ return a[key].getTime() === b[key].getTime();
66
+ }
67
+ return deepEqual(a[key], b[key], ignoreFields, nullVsUndefined);
68
+ });
58
69
  }
59
70
  function combineTransforms(...transforms) {
60
71
  return {
@@ -161,13 +172,13 @@ function createRetryTimeout(retryOptions, retryNum, fn) {
161
172
  }
162
173
  }
163
174
  var mapRetryTimeouts = /* @__PURE__ */ new Map();
164
- function runWithRetry(state, retryOptions, fn, onError) {
175
+ function runWithRetry(state, retryOptions, retryId, fn) {
165
176
  try {
166
177
  let value = fn(state);
167
178
  if (isPromise(value) && retryOptions) {
168
179
  let timeoutRetry;
169
- if (mapRetryTimeouts.has(state.node)) {
170
- clearTimeout(mapRetryTimeouts.get(state.node));
180
+ if (mapRetryTimeouts.has(retryId)) {
181
+ clearTimeout(mapRetryTimeouts.get(retryId));
171
182
  }
172
183
  return new Promise((resolve, reject) => {
173
184
  const run = () => {
@@ -178,9 +189,6 @@ function runWithRetry(state, retryOptions, fn, onError) {
178
189
  if (timeoutRetry) {
179
190
  clearTimeout(timeoutRetry);
180
191
  }
181
- if (onError) {
182
- onError(error, state);
183
- }
184
192
  if (!state.cancelRetry) {
185
193
  const timeout = createRetryTimeout(retryOptions, state.retryNum, () => {
186
194
  value = fn(state);
@@ -190,7 +198,7 @@ function runWithRetry(state, retryOptions, fn, onError) {
190
198
  state.cancelRetry = true;
191
199
  reject(error);
192
200
  } else {
193
- mapRetryTimeouts.set(state.node, timeout);
201
+ mapRetryTimeouts.set(retryId, timeout);
194
202
  timeoutRetry = timeout;
195
203
  }
196
204
  }
@@ -210,9 +218,33 @@ async function waitForSet(waitForSet2, changes, value, params = {}) {
210
218
  await state.when(waitFn);
211
219
  }
212
220
  }
221
+ var { clone } = state.internal;
222
+ function createRevertChanges(obs$, changes) {
223
+ return () => {
224
+ const previous = state.applyChanges(
225
+ clone(obs$.peek()),
226
+ changes,
227
+ /*applyPrevious*/
228
+ true
229
+ );
230
+ sync.onChangeRemote(() => {
231
+ obs$.set(previous);
232
+ });
233
+ };
234
+ }
213
235
 
214
236
  // src/sync/syncObservable.ts
215
- var { clone, deepMerge, getNode, getNodeValue, getValueAtPath, globalState, symbolLinked, createPreviousHandler } = state.internal;
237
+ var {
238
+ clone: clone2,
239
+ createPreviousHandler,
240
+ deepMerge,
241
+ getNode,
242
+ getNodeValue,
243
+ getValueAtPath,
244
+ globalState,
245
+ registerMiddleware,
246
+ symbolLinked
247
+ } = state.internal;
216
248
  var mapSyncPlugins = /* @__PURE__ */ new WeakMap();
217
249
  var allSyncStates = /* @__PURE__ */ new Map();
218
250
  var metadatas = /* @__PURE__ */ new WeakMap();
@@ -223,7 +255,7 @@ function parseLocalConfig(config) {
223
255
  function doInOrder(arg1, arg2) {
224
256
  return state.isPromise(arg1) ? arg1.then(arg2) : arg2(arg1);
225
257
  }
226
- function onChangeRemote(cb) {
258
+ function onChangeRemote2(cb) {
227
259
  state.endBatch(true);
228
260
  globalState.isLoadingRemote = true;
229
261
  state.beginBatch();
@@ -275,10 +307,10 @@ function updateMetadata(value$, localState, syncState2, syncOptions, newMetadata
275
307
  if (localState.timeoutSaveMetadata) {
276
308
  clearTimeout(localState.timeoutSaveMetadata);
277
309
  }
278
- localState.timeoutSaveMetadata = setTimeout(
279
- () => updateMetadataImmediate(value$, localState, syncState2, syncOptions, newMetadata),
280
- 0
281
- );
310
+ metadatas.set(value$, { ...metadatas.get(value$) || {}, ...newMetadata });
311
+ localState.timeoutSaveMetadata = setTimeout(() => {
312
+ updateMetadataImmediate(value$, localState, syncState2, syncOptions, metadatas.get(value$));
313
+ }, 0);
282
314
  }
283
315
  var _queuedChanges = [];
284
316
  var _queuedRemoteChanges = /* @__PURE__ */ new Map();
@@ -297,8 +329,25 @@ function mergeChanges(changes) {
297
329
  existing.valueAtPath = change.valueAtPath;
298
330
  }
299
331
  } else {
300
- changesByPath.set(pathStr, change);
301
- changesOut.push(change);
332
+ let found = false;
333
+ for (let u = 0; u < change.path.length; u++) {
334
+ const path = change.path.slice(0, u).join("/");
335
+ if (changesByPath.has(path)) {
336
+ const remaining = change.path.slice(u);
337
+ state.setAtPath(
338
+ changesByPath.get(path).valueAtPath,
339
+ remaining,
340
+ change.pathTypes.slice(u),
341
+ change.valueAtPath
342
+ );
343
+ found = true;
344
+ break;
345
+ }
346
+ }
347
+ if (!found) {
348
+ changesByPath.set(pathStr, change);
349
+ changesOut.push(change);
350
+ }
302
351
  }
303
352
  }
304
353
  return changesOut;
@@ -580,7 +629,7 @@ async function doChangeRemote(changeInfo) {
580
629
  if (waitForSetParam) {
581
630
  await waitForSet(waitForSetParam, changesRemote, obs$.peek());
582
631
  }
583
- let value = clone(obs$.peek());
632
+ let value = clone2(obs$.peek());
584
633
  const transformSave = (_a = syncOptions == null ? void 0 : syncOptions.transform) == null ? void 0 : _a.save;
585
634
  if (transformSave) {
586
635
  value = transformSave(value);
@@ -593,33 +642,41 @@ async function doChangeRemote(changeInfo) {
593
642
  onBeforeSet == null ? void 0 : onBeforeSet(beforeSetParams);
594
643
  if (!beforeSetParams.cancel) {
595
644
  let updateResult = void 0;
596
- let errorHandled = false;
597
- const onError = (error, retryParams) => {
645
+ let lastErrorHandled;
646
+ const onSetError = (error, params, noThrow) => {
598
647
  var _a2;
599
- state$.error.set(error);
600
- if (!errorHandled) {
601
- (_a2 = syncOptions.onError) == null ? void 0 : _a2.call(syncOptions, error, {
602
- setParams,
603
- source: "set",
604
- value$: obs$,
605
- retryParams
606
- });
648
+ if (lastErrorHandled !== error) {
649
+ if (!params) {
650
+ params = {
651
+ setParams,
652
+ source: "set",
653
+ type: "set",
654
+ input: value,
655
+ retry: setParams,
656
+ revert: createRevertChanges(setParams.value$, setParams.changes)
657
+ };
658
+ }
659
+ state$.error.set(error);
660
+ (_a2 = syncOptions.onError) == null ? void 0 : _a2.call(syncOptions, error, params);
661
+ lastErrorHandled = error;
662
+ if (!noThrow) {
663
+ throw error;
664
+ }
607
665
  }
608
- errorHandled = true;
609
666
  };
610
667
  const setParams = {
611
668
  node,
612
669
  value$: obs$,
613
670
  changes: changesRemote,
614
671
  value,
615
- onError,
672
+ onError: onSetError,
616
673
  update: (params) => {
617
674
  if (updateResult) {
618
- const { value: value2, lastSync, mode } = params;
675
+ const { value: value2, mode, changes } = params;
619
676
  updateResult = {
620
- lastSync: Math.max(updateResult.lastSync || 0, lastSync || 0),
621
677
  value: deepMerge(updateResult.value, value2),
622
- mode
678
+ mode,
679
+ changes: changes ? [...updateResult.changes || [], ...changes] : updateResult.changes
623
680
  };
624
681
  } else {
625
682
  updateResult = params;
@@ -629,26 +686,23 @@ async function doChangeRemote(changeInfo) {
629
686
  retryNum: 0,
630
687
  cancelRetry: false
631
688
  };
632
- const savedPromise = runWithRetry(
633
- setParams,
634
- syncOptions.retry,
635
- async () => {
636
- return syncOptions.set(setParams);
637
- },
638
- onError
639
- );
689
+ const savedPromise = runWithRetry(setParams, syncOptions.retry, node, async () => {
690
+ return syncOptions.set(setParams);
691
+ });
640
692
  let didError = false;
641
693
  if (state.isPromise(savedPromise)) {
642
694
  await savedPromise.catch((error) => {
643
695
  didError = true;
644
696
  if (!syncOptions.retry) {
645
- onError(error);
697
+ onSetError(error, void 0, true);
646
698
  }
647
699
  });
648
700
  }
649
- if (!didError) {
650
- const pathStrs = Array.from(new Set(changesRemote.map((change) => change.pathStr)));
651
- const { value: changes, lastSync } = updateResult || {};
701
+ if (!didError || (updateResult == null ? void 0 : updateResult.changes)) {
702
+ const { value: updateValue, changes: updateChanges = changesRemote } = updateResult || {};
703
+ const pathStrs = Array.from(
704
+ new Set(updateChanges.map((change) => change.pathStr))
705
+ );
652
706
  if (pathStrs.length > 0) {
653
707
  let transformedChanges = void 0;
654
708
  const metadata = {};
@@ -665,18 +719,15 @@ async function doChangeRemote(changeInfo) {
665
719
  delete pending[pathStr];
666
720
  }
667
721
  }
668
- if (lastSync) {
669
- metadata.lastSync = lastSync;
670
- }
671
722
  }
672
- if (changes && !state.isEmpty(changes)) {
673
- transformedChanges = transformLoadData(changes, syncOptions, false, "set");
723
+ if (updateValue && !state.isEmpty(updateValue)) {
724
+ transformedChanges = transformLoadData(updateValue, syncOptions, false, "set");
674
725
  }
675
726
  if (transformedChanges !== void 0) {
676
727
  if (state.isPromise(transformedChanges)) {
677
728
  transformedChanges = await transformedChanges;
678
729
  }
679
- onChangeRemote(() => state.mergeIntoObservable(obs$, transformedChanges));
730
+ onChangeRemote2(() => state.mergeIntoObservable(obs$, transformedChanges));
680
731
  }
681
732
  if (saveLocal) {
682
733
  if (shouldSaveMetadata && !state.isEmpty(metadata)) {
@@ -743,9 +794,20 @@ async function loadLocal(value$, syncOptions, syncState$, localState) {
743
794
  await state.when(initialized$);
744
795
  }
745
796
  if (persistPlugin.loadTable) {
746
- const promise = persistPlugin.loadTable(table, config);
747
- if (promise) {
748
- await promise;
797
+ try {
798
+ const promise = persistPlugin.loadTable(table, config);
799
+ if (promise) {
800
+ await promise;
801
+ }
802
+ } catch (err) {
803
+ if (process.env.NODE_ENV === "development") {
804
+ console.error(
805
+ "[legend-state] Error loading local cache. This would be a crashing error in production.",
806
+ err
807
+ );
808
+ } else {
809
+ throw err;
810
+ }
749
811
  }
750
812
  }
751
813
  const prevValue = getNodeValue(node);
@@ -764,12 +826,14 @@ async function loadLocal(value$, syncOptions, syncState$, localState) {
764
826
  if (state.isPromise(value)) {
765
827
  value = await value;
766
828
  }
829
+ node.root.isLoadingLocal = true;
767
830
  state.internal.globalState.isLoadingLocal = true;
768
831
  if (value === null && (!prevValue || prevValue[symbolLinked])) {
769
832
  value$.set(value);
770
833
  } else {
771
834
  state.mergeIntoObservable(value$, value);
772
835
  }
836
+ node.root.isLoadingLocal = false;
773
837
  state.internal.globalState.isLoadingLocal = false;
774
838
  }
775
839
  syncStateValue.numPendingLocalLoads--;
@@ -781,6 +845,11 @@ async function loadLocal(value$, syncOptions, syncState$, localState) {
781
845
  ].filter(Boolean)
782
846
  );
783
847
  } else {
848
+ if ((process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test") && persist && persist.plugin && !Object.hasOwn(persist, "name")) {
849
+ console.warn(
850
+ "[legend-state] Trying to syncObservable without `name` defined. Please include a `name` property in the `persist` configuration."
851
+ );
852
+ }
784
853
  nodeValue.resetPersistence = () => prevResetPersistence == null ? void 0 : prevResetPersistence();
785
854
  }
786
855
  nodeValue.clearPersist = nodeValue.resetPersistence;
@@ -808,14 +877,24 @@ function syncObservable(obs$, syncOptionsOrSynced) {
808
877
  const syncStateValue = getNodeValue(getNode(syncState$));
809
878
  allSyncStates.set(syncState$, node);
810
879
  syncStateValue.getPendingChanges = () => localState.pendingChanges;
811
- let errorHandled = false;
812
- const onGetError = (error, params) => {
880
+ let lastErrorHandled;
881
+ const onGetError = (error, params, noThrow) => {
813
882
  var _a;
814
- syncState$.error.set(error);
815
- if (!errorHandled) {
816
- (_a = syncOptions.onError) == null ? void 0 : _a.call(syncOptions, error, { ...params, value$: obs$ });
883
+ if (lastErrorHandled !== error) {
884
+ if (!params) {
885
+ params = {
886
+ source: "get",
887
+ type: "get",
888
+ retry: params
889
+ };
890
+ }
891
+ syncState$.error.set(error);
892
+ (_a = syncOptions.onError) == null ? void 0 : _a.call(syncOptions, error, params);
893
+ lastErrorHandled = error;
894
+ if (!noThrow) {
895
+ throw error;
896
+ }
817
897
  }
818
- errorHandled = true;
819
898
  };
820
899
  loadLocal(obs$, syncOptions, syncState$, localState);
821
900
  let isWaitingForLoad = !!syncOptions.get;
@@ -825,30 +904,37 @@ function syncObservable(obs$, syncOptionsOrSynced) {
825
904
  syncState$.isLoaded.set(!syncState$.numPendingRemoteLoads.peek());
826
905
  let isSynced = false;
827
906
  let isSubscribed = false;
907
+ let isApplyingPendingAfterSync = false;
828
908
  let unsubscribe = void 0;
829
909
  const applyPending = (pending) => {
830
910
  if (pending && !state.isEmpty(pending)) {
831
- localState.isApplyingPending = true;
832
911
  const keys = Object.keys(pending);
912
+ const value = getNodeValue(node);
833
913
  const changes = [];
834
914
  for (let i = 0; i < keys.length; i++) {
835
915
  const key = keys[i];
836
916
  const path = key.split("/").filter((p2) => p2 !== "");
837
- const { p, v, t } = pending[key];
838
- changes.push({ path, valueAtPath: v, prevAtPath: p, pathTypes: t });
917
+ const { p, t, v } = pending[key];
918
+ const valueAtPath = getValueAtPath(value, path);
919
+ if (isApplyingPendingAfterSync || !deepEqual(valueAtPath, v)) {
920
+ changes.push({ path, valueAtPath: v, prevAtPath: p, pathTypes: t });
921
+ }
922
+ }
923
+ if (changes.length > 0) {
924
+ localState.isApplyingPending = true;
925
+ onObsChange(obs$, syncState$, localState, syncOptions, {
926
+ value,
927
+ isFromPersist: false,
928
+ isFromSync: false,
929
+ getPrevious: createPreviousHandler(value, changes),
930
+ changes
931
+ });
932
+ localState.isApplyingPending = false;
839
933
  }
840
- const value = getNodeValue(node);
841
- onObsChange(obs$, syncState$, localState, syncOptions, {
842
- value,
843
- isFromPersist: false,
844
- isFromSync: false,
845
- getPrevious: createPreviousHandler(value, changes),
846
- changes
847
- });
848
- localState.isApplyingPending = false;
849
934
  }
850
935
  };
851
- if (syncOptions.get) {
936
+ const { get, subscribe } = syncOptions;
937
+ if (get || subscribe) {
852
938
  sync = async () => {
853
939
  var _a;
854
940
  if (isSynced && (!getNodeValue(getNode(syncState$)).isSyncEnabled || state.shouldIgnoreUnobserved(node, sync))) {
@@ -861,128 +947,154 @@ function syncObservable(obs$, syncOptionsOrSynced) {
861
947
  }
862
948
  const lastSync = (_a = metadatas.get(obs$)) == null ? void 0 : _a.lastSync;
863
949
  const pending = localState.pendingChanges;
864
- const get = syncOptions.get;
865
- if (get) {
866
- const { waitFor } = syncOptions;
867
- const runGet = () => {
868
- var _a2;
869
- const onChange = async ({ value, mode, lastSync: lastSync2 }) => {
870
- mode = mode || syncOptions.mode || "set";
871
- if (value !== void 0) {
872
- value = transformLoadData(value, syncOptions, true, "get");
873
- if (state.isPromise(value)) {
874
- value = await value;
875
- }
876
- const pending2 = localState.pendingChanges;
877
- const currentValue = obs$.peek();
878
- if (pending2) {
879
- let didChangeMetadata = false;
880
- Object.keys(pending2).forEach((key) => {
881
- const p = key.split("/").filter((k) => k !== "");
882
- const { v, t } = pending2[key];
883
- if (t.length === 0 || !value) {
884
- const oldValue = clone(value);
885
- pending2[key].p = oldValue;
886
- if (state.isObject(value) && state.isObject(v)) {
887
- Object.assign(value, v);
888
- } else {
889
- value = v;
890
- }
891
- } else if (value[p[0]] !== void 0) {
892
- const curValue = getValueAtPath(currentValue, p);
893
- const newValue = getValueAtPath(value, p);
894
- if (JSON.stringify(curValue) === JSON.stringify(newValue)) {
895
- delete pending2[key];
896
- didChangeMetadata = true;
897
- } else {
898
- const oldValue = clone(value);
899
- pending2[key].p = getValueAtPath(oldValue, p);
900
- value = state.setAtPath(
901
- value,
902
- p,
903
- t,
904
- v,
905
- "merge",
906
- obs$.peek(),
907
- (path, value2) => {
908
- delete pending2[key];
909
- pending2[path.join("/")] = {
910
- p: null,
911
- v: value2,
912
- t: t.slice(0, path.length)
913
- };
914
- }
915
- );
916
- }
917
- }
918
- });
919
- if (didChangeMetadata && syncOptions.persist) {
920
- updateMetadata(obs$, localState, syncState$, syncOptions, {
921
- pending: pending2
922
- });
923
- }
924
- }
925
- onChangeRemote(() => {
926
- if (state.isPlainObject(value)) {
927
- value = state.ObservableHint.plain(value);
928
- }
929
- if (mode === "assign") {
930
- obs$.assign(value);
931
- } else if (mode === "append") {
932
- if ((process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test") && !state.isArray(value)) {
933
- console.error("[legend-state] mode:append expects the value to be an array");
950
+ const { waitFor } = syncOptions;
951
+ const runGet = () => {
952
+ var _a2;
953
+ const onChange = async ({ value, mode, lastSync: lastSync2 }) => {
954
+ mode = mode || syncOptions.mode || "set";
955
+ if (value !== void 0) {
956
+ value = transformLoadData(value, syncOptions, true, "get");
957
+ if (state.isPromise(value)) {
958
+ value = await value;
959
+ }
960
+ const pending2 = localState.pendingChanges;
961
+ const currentValue = obs$.peek();
962
+ if (pending2) {
963
+ let didChangeMetadata = false;
964
+ Object.keys(pending2).forEach((key) => {
965
+ const p = key.split("/").filter((k) => k !== "");
966
+ const { v, t } = pending2[key];
967
+ if (t.length === 0 || !value) {
968
+ const oldValue = clone2(value);
969
+ pending2[key].p = key ? oldValue[key] : oldValue;
970
+ if (state.isObject(value) && state.isObject(v)) {
971
+ Object.assign(value, key ? { [key]: v } : v);
972
+ } else if (!key) {
973
+ value = v;
934
974
  }
935
- obs$.push(...value);
936
- } else if (mode === "prepend") {
937
- if ((process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test") && !state.isArray(value)) {
938
- console.error("[legend-state] mode:prepend expects the value to be an array");
975
+ } else if (value[p[0]] !== void 0) {
976
+ const curValue = getValueAtPath(currentValue, p);
977
+ const newValue = getValueAtPath(value, p);
978
+ if (JSON.stringify(curValue) === JSON.stringify(newValue)) {
979
+ delete pending2[key];
980
+ didChangeMetadata = true;
981
+ } else {
982
+ const oldValue = clone2(value);
983
+ pending2[key].p = getValueAtPath(oldValue, p);
984
+ didChangeMetadata = true;
985
+ value = state.setAtPath(
986
+ value,
987
+ p,
988
+ t,
989
+ v,
990
+ "merge",
991
+ obs$.peek(),
992
+ (path, value2) => {
993
+ delete pending2[key];
994
+ pending2[path.join("/")] = {
995
+ p: null,
996
+ v: value2,
997
+ t: t.slice(0, path.length)
998
+ };
999
+ }
1000
+ );
939
1001
  }
940
- obs$.splice(0, 0, ...value);
941
- } else if (mode === "merge") {
942
- state.mergeIntoObservable(obs$, value);
943
- } else {
944
- obs$.set(value);
945
1002
  }
946
1003
  });
1004
+ if (didChangeMetadata && syncOptions.persist) {
1005
+ updateMetadataImmediate(obs$, localState, syncState$, syncOptions, {
1006
+ pending: pending2
1007
+ });
1008
+ }
947
1009
  }
948
- if (lastSync2 && syncOptions.persist) {
949
- updateMetadata(obs$, localState, syncState$, syncOptions, {
950
- lastSync: lastSync2
951
- });
952
- }
953
- };
954
- if (node.activationState) {
955
- node.activationState.onChange = onChange;
1010
+ onChangeRemote2(() => {
1011
+ if (state.isPlainObject(value)) {
1012
+ value = state.ObservableHint.plain(value);
1013
+ }
1014
+ if (mode === "assign") {
1015
+ obs$.assign(value);
1016
+ } else if (mode === "append") {
1017
+ if ((process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test") && !state.isArray(value)) {
1018
+ console.error("[legend-state] mode:append expects the value to be an array");
1019
+ }
1020
+ obs$.push(...value);
1021
+ } else if (mode === "prepend") {
1022
+ if ((process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test") && !state.isArray(value)) {
1023
+ console.error("[legend-state] mode:prepend expects the value to be an array");
1024
+ }
1025
+ obs$.splice(0, 0, ...value);
1026
+ } else if (mode === "merge") {
1027
+ state.mergeIntoObservable(obs$, value);
1028
+ } else {
1029
+ obs$.set(value);
1030
+ }
1031
+ });
1032
+ }
1033
+ if (lastSync2 && syncOptions.persist) {
1034
+ updateMetadata(obs$, localState, syncState$, syncOptions, {
1035
+ lastSync: lastSync2
1036
+ });
956
1037
  }
957
- if (!isSubscribed && syncOptions.subscribe) {
958
- const subscribe = syncOptions.subscribe;
1038
+ };
1039
+ if (node.activationState) {
1040
+ node.activationState.onChange = onChange;
1041
+ }
1042
+ if (!isSubscribed && syncOptions.subscribe) {
1043
+ const subscribe2 = syncOptions.subscribe;
1044
+ const doSubscribe = () => {
959
1045
  isSubscribed = true;
960
- const doSubscribe = () => {
961
- const subscribeParams = {
962
- node,
963
- value$: obs$,
964
- lastSync,
965
- update: (params) => {
966
- state.when(syncState$.isLoaded, () => {
1046
+ const subscribeParams = {
1047
+ node,
1048
+ value$: obs$,
1049
+ lastSync,
1050
+ update: (params) => {
1051
+ state.when(
1052
+ () => !get || syncState$.isLoaded.get(),
1053
+ () => {
967
1054
  state.when(waitFor || true, () => {
968
1055
  params.mode || (params.mode = syncOptions.mode || "merge");
969
1056
  onChange(params);
1057
+ if (!syncState$.isLoaded.peek()) {
1058
+ syncState$.assign({
1059
+ isLoaded: syncStateValue.numPendingRemoteLoads < 1,
1060
+ error: void 0,
1061
+ isGetting: syncStateValue.numPendingGets > 0
1062
+ });
1063
+ }
970
1064
  });
971
- });
972
- },
973
- refresh: () => state.when(syncState$.isLoaded, sync),
974
- onError: (error) => onGetError(error, { source: "subscribe", subscribeParams })
975
- };
976
- unsubscribe = subscribe(subscribeParams);
1065
+ }
1066
+ );
1067
+ },
1068
+ refresh: () => state.when(syncState$.isLoaded, sync),
1069
+ onError: (error) => onGetError(error, {
1070
+ source: "subscribe",
1071
+ subscribeParams,
1072
+ type: "get",
1073
+ retry: {}
1074
+ })
977
1075
  };
978
- if (waitFor) {
979
- state.whenReady(waitFor, doSubscribe);
980
- } else {
981
- doSubscribe();
982
- }
1076
+ unsubscribe = subscribe2(subscribeParams);
1077
+ registerMiddleware(node, "listeners-cleared", () => {
1078
+ if (unsubscribe) {
1079
+ isSubscribed = false;
1080
+ unsubscribe();
1081
+ unsubscribe = void 0;
1082
+ }
1083
+ });
1084
+ registerMiddleware(node, "listener-added", () => {
1085
+ if (!isSubscribed) {
1086
+ doSubscribe();
1087
+ }
1088
+ });
1089
+ };
1090
+ if (waitFor) {
1091
+ state.whenReady(waitFor, doSubscribe);
1092
+ } else {
1093
+ doSubscribe();
983
1094
  }
984
- const existingValue = getNodeValue(node);
985
- const onError = (error) => onGetError(error, { getParams, source: "get" });
1095
+ }
1096
+ const existingValue = getNodeValue(node);
1097
+ if (get) {
986
1098
  const getParams = {
987
1099
  node,
988
1100
  value$: obs$,
@@ -992,7 +1104,7 @@ function syncObservable(obs$, syncOptionsOrSynced) {
992
1104
  options: syncOptions,
993
1105
  lastSync,
994
1106
  updateLastSync: (lastSync2) => getParams.lastSync = lastSync2,
995
- onError,
1107
+ onError: onGetError,
996
1108
  retryNum: 0,
997
1109
  cancelRetry: false
998
1110
  };
@@ -1021,17 +1133,12 @@ function syncObservable(obs$, syncOptionsOrSynced) {
1021
1133
  numPendingGets: (syncStateValue.numPendingGets || 0) + 1,
1022
1134
  isGetting: true
1023
1135
  });
1024
- const got = runWithRetry(
1025
- getParams,
1026
- syncOptions.retry,
1027
- (retryEvent) => {
1028
- const params = getParams;
1029
- params.cancelRetry = retryEvent.cancelRetry;
1030
- params.retryNum = retryEvent.retryNum;
1031
- return get(params);
1032
- },
1033
- onError
1034
- );
1136
+ const got = runWithRetry(getParams, syncOptions.retry, node, (retryEvent) => {
1137
+ const params = getParams;
1138
+ params.cancelRetry = retryEvent.cancelRetry;
1139
+ params.retryNum = retryEvent.retryNum;
1140
+ return get(params);
1141
+ });
1035
1142
  const numGets = node.numGets = (node.numGets || 0) + 1;
1036
1143
  const handle = (value) => {
1037
1144
  syncState$.numPendingGets.set((v) => v - 1);
@@ -1058,33 +1165,33 @@ function syncObservable(obs$, syncOptionsOrSynced) {
1058
1165
  });
1059
1166
  };
1060
1167
  if (state.isPromise(got)) {
1061
- got.then(handle).catch(onError);
1168
+ got.then(handle).catch((error) => {
1169
+ onGetError(error, { getParams, source: "get", type: "get", retry: getParams }, true);
1170
+ });
1062
1171
  } else {
1063
1172
  handle(got);
1064
1173
  }
1065
1174
  }
1066
- };
1067
- if (waitFor) {
1068
- state.whenReady(waitFor, () => state.trackSelector(runGet, sync));
1069
- } else {
1070
- state.trackSelector(runGet, sync);
1071
1175
  }
1176
+ };
1177
+ if (waitFor) {
1178
+ state.whenReady(waitFor, () => state.trackSelector(runGet, sync));
1072
1179
  } else {
1073
- syncState$.assign({
1074
- isLoaded: true,
1075
- error: void 0
1076
- });
1180
+ state.trackSelector(runGet, sync);
1077
1181
  }
1078
1182
  if (!isSynced) {
1079
1183
  isSynced = true;
1080
- await state.when(syncState$.isLoaded);
1184
+ isApplyingPendingAfterSync = true;
1081
1185
  applyPending(pending);
1186
+ isApplyingPendingAfterSync = false;
1082
1187
  }
1083
1188
  };
1084
1189
  syncStateValue.sync = sync;
1085
1190
  } else {
1086
1191
  if (!isSynced) {
1192
+ isApplyingPendingAfterSync = true;
1087
1193
  applyPending(localState.pendingChanges);
1194
+ isApplyingPendingAfterSync = false;
1088
1195
  }
1089
1196
  }
1090
1197
  syncStateValue.reset = async () => {
@@ -1110,7 +1217,7 @@ function syncObservable(obs$, syncOptionsOrSynced) {
1110
1217
  unsubscribe == null ? void 0 : unsubscribe();
1111
1218
  unsubscribe = void 0;
1112
1219
  const promise = syncStateValue.resetPersistence();
1113
- onChangeRemote(() => {
1220
+ onChangeRemote2(() => {
1114
1221
  var _a;
1115
1222
  obs$.set((_a = syncOptions.initial) != null ? _a : void 0);
1116
1223
  });
@@ -1132,7 +1239,7 @@ function syncObservable(obs$, syncOptionsOrSynced) {
1132
1239
  return true;
1133
1240
  };
1134
1241
  state.when(onAllPersistLoaded, function() {
1135
- if (syncOptions.get && syncOptions.syncMode === "auto") {
1242
+ if ((syncOptions.get || syncOptions.subscribe) && syncOptions.syncMode === "auto") {
1136
1243
  sync();
1137
1244
  }
1138
1245
  if ((syncOptions == null ? void 0 : syncOptions.set) || (syncOptions == null ? void 0 : syncOptions.persist)) {
@@ -1215,19 +1322,21 @@ function configureSynced(fnOrOrigOptions, origOptions) {
1215
1322
  }
1216
1323
 
1217
1324
  // sync.ts
1218
- var internal4 = {
1325
+ var internal5 = {
1219
1326
  observableSyncConfiguration,
1220
- waitForSet
1327
+ waitForSet,
1328
+ runWithRetry
1221
1329
  };
1222
1330
 
1223
1331
  exports.combineTransforms = combineTransforms;
1224
1332
  exports.configureObservableSync = configureObservableSync;
1225
1333
  exports.configureSynced = configureSynced;
1334
+ exports.createRevertChanges = createRevertChanges;
1226
1335
  exports.deepEqual = deepEqual;
1227
1336
  exports.diffObjects = diffObjects;
1228
- exports.internal = internal4;
1337
+ exports.internal = internal5;
1229
1338
  exports.mapSyncPlugins = mapSyncPlugins;
1230
- exports.onChangeRemote = onChangeRemote;
1339
+ exports.onChangeRemote = onChangeRemote2;
1231
1340
  exports.removeNullUndefined = removeNullUndefined;
1232
1341
  exports.syncObservable = syncObservable;
1233
1342
  exports.synced = synced;