@legendapp/state 3.0.0-alpha.4 → 3.0.0-alpha.41

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 (94) hide show
  1. package/.DS_Store +0 -0
  2. package/CHANGELOG.md +831 -1
  3. package/LICENSE +21 -1
  4. package/README.md +141 -1
  5. package/babel.js +0 -2
  6. package/babel.mjs +0 -2
  7. package/config/configureLegendState.d.mts +13 -0
  8. package/config/configureLegendState.d.ts +13 -0
  9. package/config/configureLegendState.js +45 -0
  10. package/config/configureLegendState.mjs +43 -0
  11. package/config/enable$GetSet.js +2 -1
  12. package/config/enable$GetSet.mjs +2 -1
  13. package/config/enableReactTracking.js +2 -1
  14. package/config/enableReactTracking.mjs +2 -1
  15. package/config/enableReactUse.js +2 -1
  16. package/config/enableReactUse.mjs +2 -1
  17. package/config/enable_PeekAssign.js +2 -1
  18. package/config/enable_PeekAssign.mjs +2 -1
  19. package/helpers/trackHistory.js +2 -2
  20. package/helpers/trackHistory.mjs +2 -2
  21. package/index.d.mts +106 -81
  22. package/index.d.ts +106 -81
  23. package/index.js +347 -335
  24. package/index.mjs +344 -333
  25. package/package.json +36 -1
  26. package/persist-plugins/async-storage.d.mts +6 -3
  27. package/persist-plugins/async-storage.d.ts +6 -3
  28. package/persist-plugins/async-storage.js +8 -4
  29. package/persist-plugins/async-storage.mjs +8 -5
  30. package/persist-plugins/indexeddb.d.mts +6 -4
  31. package/persist-plugins/indexeddb.d.ts +6 -4
  32. package/persist-plugins/indexeddb.js +35 -15
  33. package/persist-plugins/indexeddb.mjs +35 -16
  34. package/persist-plugins/mmkv.d.mts +5 -1
  35. package/persist-plugins/mmkv.d.ts +5 -1
  36. package/persist-plugins/mmkv.js +10 -5
  37. package/persist-plugins/mmkv.mjs +10 -6
  38. package/react-reactive/enableReactComponents.d.mts +9 -0
  39. package/react-reactive/enableReactComponents.d.ts +9 -0
  40. package/react-reactive/enableReactComponents.js +19 -0
  41. package/react-reactive/enableReactComponents.mjs +17 -0
  42. package/react-reactive/enableReactNativeComponents.d.mts +22 -0
  43. package/react-reactive/enableReactNativeComponents.d.ts +22 -0
  44. package/react-reactive/enableReactNativeComponents.js +53 -0
  45. package/react-reactive/enableReactNativeComponents.mjs +51 -0
  46. package/react-reactive/enableReactive.d.mts +5 -0
  47. package/react-reactive/enableReactive.d.ts +5 -0
  48. package/react-reactive/enableReactive.js +24 -0
  49. package/react-reactive/enableReactive.mjs +22 -0
  50. package/react-reactive/enableReactive.native.d.mts +5 -0
  51. package/react-reactive/enableReactive.native.d.ts +5 -0
  52. package/react-reactive/enableReactive.native.js +58 -0
  53. package/react-reactive/enableReactive.native.mjs +56 -0
  54. package/react-reactive/enableReactive.web.d.mts +5 -0
  55. package/react-reactive/enableReactive.web.d.ts +5 -0
  56. package/react-reactive/enableReactive.web.js +58 -0
  57. package/react-reactive/enableReactive.web.mjs +56 -0
  58. package/react.d.mts +39 -34
  59. package/react.d.ts +39 -34
  60. package/react.js +54 -27
  61. package/react.mjs +55 -28
  62. package/sync-plugins/crud.d.mts +21 -24
  63. package/sync-plugins/crud.d.ts +21 -24
  64. package/sync-plugins/crud.js +241 -140
  65. package/sync-plugins/crud.mjs +243 -142
  66. package/sync-plugins/fetch.js +12 -8
  67. package/sync-plugins/fetch.mjs +13 -9
  68. package/sync-plugins/firebase.d.mts +27 -0
  69. package/sync-plugins/firebase.d.ts +27 -0
  70. package/sync-plugins/firebase.js +373 -0
  71. package/sync-plugins/firebase.mjs +368 -0
  72. package/sync-plugins/keel.d.mts +43 -26
  73. package/sync-plugins/keel.d.ts +43 -26
  74. package/sync-plugins/keel.js +145 -100
  75. package/sync-plugins/keel.mjs +147 -100
  76. package/sync-plugins/supabase.d.mts +19 -9
  77. package/sync-plugins/supabase.d.ts +19 -9
  78. package/sync-plugins/supabase.js +52 -22
  79. package/sync-plugins/supabase.mjs +53 -23
  80. package/sync-plugins/tanstack-query.d.mts +2 -2
  81. package/sync-plugins/tanstack-query.d.ts +2 -2
  82. package/sync-plugins/tanstack-query.js +22 -5
  83. package/sync-plugins/tanstack-query.mjs +22 -5
  84. package/sync-plugins/tanstack-react-query.d.mts +1 -1
  85. package/sync-plugins/tanstack-react-query.d.ts +1 -1
  86. package/sync-plugins/tanstack-react-query.js +8 -1
  87. package/sync-plugins/tanstack-react-query.mjs +8 -1
  88. package/sync.d.mts +74 -200
  89. package/sync.d.ts +74 -200
  90. package/sync.js +510 -307
  91. package/sync.mjs +516 -313
  92. package/types/babel.d.ts +12 -1
  93. /package/config/{enable_GetSet.d.mts → enable$GetSet.d.mts} +0 -0
  94. /package/config/{enable_GetSet.d.ts → enable$GetSet.d.ts} +0 -0
package/sync.mjs CHANGED
@@ -1,6 +1,4 @@
1
- import { isObject, isDate, isNullOrUndefined, isString, endBatch, beginBatch, isFunction, mergeIntoObservable, observable, when, linked, internal, isPromise, isEmpty, shouldIgnoreUnobserved, whenReady, constructObjectWithPath, deconstructObjectWithPath, setAtPath, isArray } from '@legendapp/state';
2
-
3
- // sync.ts
1
+ import { isObject, isDate, isNullOrUndefined, isString, endBatch, beginBatch, isFunction, syncState, when, linked, internal, observable, isPromise as isPromise$1, mergeIntoObservable, isEmpty, shouldIgnoreUnobserved, whenReady, trackSelector, constructObjectWithPath, setAtPath, isPlainObject, ObservableHint, isArray } from '@legendapp/state';
4
2
 
5
3
  // src/sync/configureObservableSync.ts
6
4
  var observableSyncConfiguration = {};
@@ -137,41 +135,87 @@ function transformStringifyDates(...args) {
137
135
  }
138
136
  };
139
137
  }
140
- function syncObservableAdapter({ get, set }) {
141
- const ret = {};
142
- if (get) {
143
- ret.get = async (params) => {
144
- try {
145
- let value = get(params);
146
- if (isPromise(value)) {
147
- value = await value;
148
- }
149
- params.onChange({
150
- value,
151
- lastSync: params.lastSync,
152
- mode: params.mode
138
+
139
+ // src/is.ts
140
+ function isPromise(obj) {
141
+ return obj instanceof Promise;
142
+ }
143
+
144
+ // src/sync/retry.ts
145
+ function calculateRetryDelay(retryOptions, retryNum) {
146
+ const { backoff, delay = 1e3, infinite, times = 3, maxDelay = 3e4 } = retryOptions;
147
+ if (infinite || retryNum < times) {
148
+ const delayTime = Math.min(delay * (backoff === "constant" ? 1 : 2 ** retryNum), maxDelay);
149
+ return delayTime;
150
+ }
151
+ return null;
152
+ }
153
+ function createRetryTimeout(retryOptions, retryNum, fn) {
154
+ const delayTime = calculateRetryDelay(retryOptions, retryNum);
155
+ if (delayTime) {
156
+ return setTimeout(fn, delayTime);
157
+ } else {
158
+ return false;
159
+ }
160
+ }
161
+ var mapRetryTimeouts = /* @__PURE__ */ new Map();
162
+ function runWithRetry(state, retryOptions, fn, onError) {
163
+ let value = fn(state);
164
+ if (isPromise(value) && retryOptions) {
165
+ let timeoutRetry;
166
+ if (mapRetryTimeouts.has(state.node)) {
167
+ clearTimeout(mapRetryTimeouts.get(state.node));
168
+ }
169
+ return new Promise((resolve, reject) => {
170
+ const run = () => {
171
+ value.then((val) => {
172
+ resolve(val);
173
+ }).catch((error) => {
174
+ state.retryNum++;
175
+ if (timeoutRetry) {
176
+ clearTimeout(timeoutRetry);
177
+ }
178
+ if (onError) {
179
+ onError(error, state);
180
+ }
181
+ if (!state.cancelRetry) {
182
+ const timeout = createRetryTimeout(retryOptions, state.retryNum, () => {
183
+ value = fn(state);
184
+ run();
185
+ });
186
+ if (timeout === false) {
187
+ state.cancelRetry = true;
188
+ reject(error);
189
+ } else {
190
+ mapRetryTimeouts.set(state.node, timeout);
191
+ timeoutRetry = timeout;
192
+ }
193
+ }
153
194
  });
154
- params.onGet();
155
- } catch (e) {
156
- }
157
- };
195
+ };
196
+ run();
197
+ });
158
198
  }
159
- if (set) {
160
- ret.set = set;
199
+ return value;
200
+ }
201
+ async function waitForSet(waitForSet2, changes, value, params = {}) {
202
+ const waitFn = isFunction(waitForSet2) ? waitForSet2({ changes, value, ...params }) : waitForSet2;
203
+ if (waitFn) {
204
+ await when(waitFn);
161
205
  }
162
- return ret;
163
206
  }
164
207
 
165
208
  // src/sync/syncObservable.ts
166
- var { createPreviousHandler, clone, getValueAtPath, globalState, symbolLinked, getNode, getNodeValue } = internal;
209
+ var { clone, deepMerge, getNode, getNodeValue, getValueAtPath, globalState, symbolLinked, createPreviousHandler } = internal;
167
210
  var mapSyncPlugins = /* @__PURE__ */ new WeakMap();
211
+ var allSyncStates = /* @__PURE__ */ new Map();
168
212
  var metadatas = /* @__PURE__ */ new WeakMap();
169
213
  var promisesLocalSaves = /* @__PURE__ */ new Set();
170
214
  function parseLocalConfig(config) {
171
215
  return config ? isString(config) ? { table: config, config: { name: config } } : { table: config.name, config } : {};
172
216
  }
173
217
  function doInOrder(arg1, arg2) {
174
- return isPromise(arg1) ? arg1.then(arg2) : arg2(arg1);
218
+ return isPromise$1(arg1) ? arg1.then(arg2) : arg2(arg1);
175
219
  }
176
220
  function onChangeRemote(cb) {
177
221
  endBatch(true);
@@ -181,13 +225,19 @@ function onChangeRemote(cb) {
181
225
  globalState.isLoadingRemote = false;
182
226
  endBatch(true);
183
227
  }
184
- function transformSaveData(value, path, pathTypes, { transform }) {
228
+ async function transformSaveData(value, path, pathTypes, { transform }) {
185
229
  if (transform == null ? void 0 : transform.save) {
186
230
  const constructed = constructObjectWithPath(path, pathTypes, value);
187
- const saved = transform.save(constructed);
188
- value = deconstructObjectWithPath(path, pathTypes, saved);
231
+ const saved = await transform.save(constructed);
232
+ value = saved;
233
+ const outPath = [];
234
+ for (let i = 0; i < path.length; i++) {
235
+ outPath[i] = Object.keys(value)[0];
236
+ value = value[outPath[i]];
237
+ }
238
+ path = outPath;
189
239
  }
190
- return value;
240
+ return { value, path };
191
241
  }
192
242
  function transformLoadData(value, { transform }, doUserTransform, method) {
193
243
  if (doUserTransform && (transform == null ? void 0 : transform.load)) {
@@ -195,13 +245,13 @@ function transformLoadData(value, { transform }, doUserTransform, method) {
195
245
  }
196
246
  return value;
197
247
  }
198
- async function updateMetadataImmediate(value$, localState, syncState, syncOptions, newMetadata) {
248
+ async function updateMetadataImmediate(value$, localState, syncState2, syncOptions, newMetadata) {
199
249
  const saves = Array.from(promisesLocalSaves);
200
250
  if (saves.length > 0) {
201
251
  await Promise.all(saves);
202
252
  }
203
253
  const { pluginPersist } = localState;
204
- const { table, config } = parseLocalConfig(syncOptions == null ? void 0 : syncOptions.persist);
254
+ const { table, config } = parseLocalConfig(syncOptions.persist);
205
255
  const oldMetadata = metadatas.get(value$);
206
256
  const { lastSync } = newMetadata;
207
257
  const metadata = Object.assign({}, oldMetadata, newMetadata);
@@ -210,17 +260,17 @@ async function updateMetadataImmediate(value$, localState, syncState, syncOption
210
260
  await pluginPersist.setMetadata(table, metadata, config);
211
261
  }
212
262
  if (lastSync) {
213
- syncState.assign({
263
+ syncState2.assign({
214
264
  lastSync
215
265
  });
216
266
  }
217
267
  }
218
- function updateMetadata(value$, localState, syncState, syncOptions, newMetadata) {
268
+ function updateMetadata(value$, localState, syncState2, syncOptions, newMetadata) {
219
269
  if (localState.timeoutSaveMetadata) {
220
270
  clearTimeout(localState.timeoutSaveMetadata);
221
271
  }
222
272
  localState.timeoutSaveMetadata = setTimeout(
223
- () => updateMetadataImmediate(value$, localState, syncState, syncOptions, newMetadata),
273
+ () => updateMetadataImmediate(value$, localState, syncState2, syncOptions, newMetadata),
224
274
  0
225
275
  );
226
276
  }
@@ -248,26 +298,21 @@ function mergeChanges(changes) {
248
298
  return changesOut;
249
299
  }
250
300
  function mergeQueuedChanges(allChanges) {
251
- const changesByObsRemote = /* @__PURE__ */ new Map();
252
- const changesByObsLocal = /* @__PURE__ */ new Map();
253
- const previousByObs = /* @__PURE__ */ new Map();
301
+ const changesByOptionsRemote = /* @__PURE__ */ new Map();
302
+ const changesByOptionsLocal = /* @__PURE__ */ new Map();
254
303
  const outRemote = /* @__PURE__ */ new Map();
255
304
  const outLocal = /* @__PURE__ */ new Map();
256
305
  for (let i = 0; i < allChanges.length; i++) {
257
306
  const value = allChanges[i];
258
- const { value$: obs, changes, inRemoteChange, getPrevious } = value;
307
+ const { changes, inRemoteChange, syncOptions } = value;
259
308
  const targetMap = inRemoteChange ? outRemote : outLocal;
260
- const changesMap = inRemoteChange ? changesByObsRemote : changesByObsLocal;
261
- const existing = changesMap.get(obs);
309
+ const changesMap = inRemoteChange ? changesByOptionsRemote : changesByOptionsLocal;
310
+ const existing = changesMap.get(syncOptions);
262
311
  const newChanges = existing ? [...existing, ...changes] : changes;
263
312
  const merged = mergeChanges(newChanges);
264
- changesMap.set(obs, merged);
313
+ changesMap.set(syncOptions, merged);
265
314
  value.changes = merged;
266
- if (!previousByObs.has(obs)) {
267
- previousByObs.set(obs, getPrevious());
268
- }
269
- value.valuePrevious = previousByObs.get(obs);
270
- targetMap.set(obs, value);
315
+ targetMap.set(syncOptions, value);
271
316
  }
272
317
  return Array.from(outRemote.values()).concat(Array.from(outLocal.values()));
273
318
  }
@@ -313,15 +358,13 @@ async function processQueuedRemoteChanges(syncOptions) {
313
358
  }
314
359
  }
315
360
  async function prepChangeLocal(queuedChange) {
316
- const { syncState, changes, localState, syncOptions, inRemoteChange, isApplyingPending } = queuedChange;
361
+ const { syncState: syncState2, changes, syncOptions, inRemoteChange, isApplyingPending } = queuedChange;
317
362
  const persist = syncOptions.persist;
318
- const { pluginSync } = localState;
319
363
  const { config: configLocal } = parseLocalConfig(persist);
320
- const configRemote = syncOptions;
321
- const saveLocal = (persist == null ? void 0 : persist.name) && !configLocal.readonly && !isApplyingPending && syncState.isPersistEnabled.peek();
322
- const saveRemote = !!(!inRemoteChange && (pluginSync == null ? void 0 : pluginSync.set) && (configRemote == null ? void 0 : configRemote.enableSync) !== false && syncState.isSyncEnabled.peek());
364
+ const saveLocal = (persist == null ? void 0 : persist.name) && !configLocal.readonly && !isApplyingPending && syncState2.isPersistEnabled.peek();
365
+ const saveRemote = !!(!inRemoteChange && (syncOptions == null ? void 0 : syncOptions.set) && syncState2.isSyncEnabled.peek());
323
366
  if (saveLocal || saveRemote) {
324
- if (saveLocal && !syncState.isPersistLoaded.peek()) {
367
+ if (saveLocal && !syncState2.isPersistLoaded.peek()) {
325
368
  console.error(
326
369
  "[legend-state] WARNING: An observable was changed before being loaded from persist",
327
370
  persist
@@ -354,13 +397,13 @@ async function prepChangeLocal(queuedChange) {
354
397
  configLocal
355
398
  );
356
399
  promisesTransform.push(
357
- doInOrder(promiseTransformLocal, (valueTransformed) => {
400
+ doInOrder(promiseTransformLocal, ({ value: valueTransformed, path: pathTransformed }) => {
358
401
  changesLocal.push({
359
- path,
402
+ path: pathTransformed,
360
403
  pathTypes,
361
404
  prevAtPath,
362
405
  valueAtPath: valueTransformed,
363
- pathStr
406
+ pathStr: path === pathTransformed ? pathStr : pathTransformed.join("/")
364
407
  });
365
408
  })
366
409
  );
@@ -376,22 +419,19 @@ async function prepChangeLocal(queuedChange) {
376
419
  }
377
420
  async function prepChangeRemote(queuedChange) {
378
421
  const {
379
- syncState,
422
+ syncState: syncState2,
380
423
  changes,
381
424
  localState,
382
425
  syncOptions,
383
426
  inRemoteChange,
384
- isApplyingPending,
385
- valuePrevious
427
+ isApplyingPending
386
428
  } = queuedChange;
387
429
  const persist = syncOptions.persist;
388
- const { pluginSync } = localState;
389
430
  const { config: configLocal } = parseLocalConfig(persist);
390
- const configRemote = syncOptions;
391
- const saveLocal = persist && !configLocal.readonly && !isApplyingPending && syncState.isPersistEnabled.peek();
392
- const saveRemote = !inRemoteChange && (pluginSync == null ? void 0 : pluginSync.set) && (configRemote == null ? void 0 : configRemote.enableSync) !== false && syncState.isSyncEnabled.peek();
431
+ const saveLocal = persist && !configLocal.readonly && !isApplyingPending && syncState2.isPersistEnabled.peek();
432
+ const saveRemote = !inRemoteChange && (syncOptions == null ? void 0 : syncOptions.set) && syncState2.isSyncEnabled.peek();
393
433
  if (saveLocal || saveRemote) {
394
- if (saveLocal && !syncState.isPersistLoaded.peek()) {
434
+ if (saveLocal && !syncState2.isPersistLoaded.peek()) {
395
435
  console.error(
396
436
  "[legend-state] WARNING: An observable was changed before being loaded from persist",
397
437
  persist
@@ -421,20 +461,20 @@ async function prepChangeRemote(queuedChange) {
421
461
  valueAtPath,
422
462
  path,
423
463
  pathTypes,
424
- configRemote || {}
464
+ syncOptions || {}
425
465
  );
426
466
  promisesTransform.push(
427
- doInOrder(promiseTransformRemote, (valueTransformed) => {
467
+ doInOrder(promiseTransformRemote, ({ value: valueTransformed, path: pathTransformed }) => {
428
468
  var _a;
429
469
  if (!localState.pendingChanges) {
430
470
  localState.pendingChanges = {};
431
471
  }
432
472
  let found2 = false;
433
- for (let i2 = 0; !found2 && i2 < path.length - 1; i2++) {
434
- const pathParent = path.slice(0, i2 + 1).join("/");
473
+ for (let i2 = 0; !found2 && i2 < pathTransformed.length - 1; i2++) {
474
+ const pathParent = pathTransformed.slice(0, i2 + 1).join("/");
435
475
  if ((_a = localState.pendingChanges[pathParent]) == null ? void 0 : _a.v) {
436
476
  found2 = true;
437
- const pathChild = path.slice(i2 + 1);
477
+ const pathChild = pathTransformed.slice(i2 + 1);
438
478
  const pathTypesChild = pathTypes.slice(i2 + 1);
439
479
  setAtPath(
440
480
  localState.pendingChanges[pathParent].v,
@@ -456,12 +496,11 @@ async function prepChangeRemote(queuedChange) {
456
496
  localState.pendingChanges[pathStr].v = valueAtPath;
457
497
  }
458
498
  changesRemote.push({
459
- path,
499
+ path: pathTransformed,
460
500
  pathTypes,
461
501
  prevAtPath,
462
502
  valueAtPath: valueTransformed,
463
- pathStr,
464
- valuePrevious
503
+ pathStr
465
504
  });
466
505
  })
467
506
  );
@@ -479,24 +518,27 @@ async function doChangeLocal(changeInfo) {
479
518
  if (!changeInfo)
480
519
  return;
481
520
  const { queuedChange, changesLocal, saveRemote } = changeInfo;
482
- const { value$: obs, syncState, localState, syncOptions } = queuedChange;
521
+ const { value$: obs, syncState: syncState2, localState, syncOptions } = queuedChange;
483
522
  const { pluginPersist } = localState;
484
523
  const persist = syncOptions.persist;
485
- const { table, config: configLocal } = parseLocalConfig(persist);
486
- const shouldSaveMetadata = persist == null ? void 0 : persist.retrySync;
487
- if (saveRemote && shouldSaveMetadata) {
488
- await updateMetadataImmediate(obs, localState, syncState, syncOptions, {
489
- pending: localState.pendingChanges
490
- });
491
- }
492
- if (changesLocal.length > 0) {
493
- let promiseSet = pluginPersist.set(table, changesLocal, configLocal);
494
- if (promiseSet) {
495
- promiseSet = promiseSet.then(() => {
496
- promisesLocalSaves.delete(promiseSet);
524
+ const saveLocal = !!(persist == null ? void 0 : persist.name);
525
+ if (saveLocal) {
526
+ const { table, config: configLocal } = parseLocalConfig(persist);
527
+ const shouldSaveMetadata = persist == null ? void 0 : persist.retrySync;
528
+ if (saveRemote && shouldSaveMetadata) {
529
+ await updateMetadataImmediate(obs, localState, syncState2, syncOptions, {
530
+ pending: localState.pendingChanges
497
531
  });
498
- promisesLocalSaves.add(promiseSet);
499
- await promiseSet;
532
+ }
533
+ if (changesLocal.length > 0) {
534
+ let promiseSet = pluginPersist.set(table, changesLocal, configLocal);
535
+ if (promiseSet) {
536
+ promiseSet = promiseSet.then(() => {
537
+ promisesLocalSaves.delete(promiseSet);
538
+ });
539
+ promisesLocalSaves.add(promiseSet);
540
+ await promiseSet;
541
+ }
500
542
  }
501
543
  }
502
544
  }
@@ -505,102 +547,148 @@ async function doChangeRemote(changeInfo) {
505
547
  if (!changeInfo)
506
548
  return;
507
549
  const { queuedChange, changesRemote } = changeInfo;
508
- const { value$: obs, syncState, localState, syncOptions, valuePrevious: previous } = queuedChange;
509
- const { pluginPersist, pluginSync } = localState;
550
+ const { value$: obs$, syncState: syncState2, localState, syncOptions } = queuedChange;
551
+ const { pluginPersist } = localState;
552
+ const node = getNode(obs$);
553
+ const state$ = node.state;
510
554
  const persist = syncOptions.persist;
511
555
  const { table, config: configLocal } = parseLocalConfig(persist);
512
- const { allowSetIfGetError, onBeforeSet, onSetError, waitForSet, onAfterSet } = syncOptions || {};
556
+ const { onBeforeSet, waitForSet: waitForSetParam, onAfterSet } = syncOptions || {};
513
557
  const shouldSaveMetadata = persist == null ? void 0 : persist.retrySync;
558
+ const saveLocal = !!(persist == null ? void 0 : persist.name);
514
559
  if (changesRemote.length > 0) {
515
- await when(() => syncState.isLoaded.get() || allowSetIfGetError && syncState.error.get());
516
- if (waitForSet) {
517
- const waitFor = isFunction(waitForSet) ? waitForSet({ changes: changesRemote, value: obs.peek() }) : waitForSet;
518
- if (waitFor) {
519
- await when(waitFor);
560
+ if (!syncState2.isLoaded.peek()) {
561
+ await when(syncState2.isLoaded);
562
+ const pending = localState.pendingChanges;
563
+ if (pending) {
564
+ changesRemote.forEach((change) => {
565
+ const key = change.pathStr;
566
+ const pendingAtPath = pending[key];
567
+ if (!isNullOrUndefined(pendingAtPath)) {
568
+ const { p } = pendingAtPath;
569
+ change.prevAtPath = p;
570
+ }
571
+ });
520
572
  }
521
573
  }
522
- let value = obs.peek();
574
+ if (waitForSetParam) {
575
+ await waitForSet(waitForSetParam, changesRemote, obs$.peek());
576
+ }
577
+ let value = clone(obs$.peek());
523
578
  const transformSave = (_a = syncOptions == null ? void 0 : syncOptions.transform) == null ? void 0 : _a.save;
524
579
  if (transformSave) {
525
- value = transformSave(clone(value));
526
- }
527
- onBeforeSet == null ? void 0 : onBeforeSet();
528
- localState.numSavesOutstanding = (localState.numSavesOutstanding || 0) + 1;
529
- let savedPromise = pluginSync.set({
530
- value$: obs,
531
- syncState,
532
- options: syncOptions,
533
- changes: changesRemote,
534
- value,
535
- valuePrevious: previous
536
- });
537
- if (isPromise(savedPromise)) {
538
- savedPromise = savedPromise.catch((err) => onSetError == null ? void 0 : onSetError(err));
539
- }
540
- const saved = await savedPromise;
541
- localState.numSavesOutstanding--;
542
- if (saved !== void 0) {
543
- const pathStrs = Array.from(new Set(changesRemote.map((change) => change.pathStr)));
544
- const { changes, lastSync } = saved;
545
- if (pathStrs.length > 0) {
546
- let transformedChanges = void 0;
547
- const metadata = {};
548
- if (persist) {
549
- const pendingMetadata = (_b = pluginPersist.getMetadata(table, configLocal)) == null ? void 0 : _b.pending;
550
- const pending = localState.pendingChanges;
551
- for (let i = 0; i < pathStrs.length; i++) {
552
- const pathStr = pathStrs[i];
553
- if (pendingMetadata == null ? void 0 : pendingMetadata[pathStr]) {
554
- delete pendingMetadata[pathStr];
555
- metadata.pending = pendingMetadata;
580
+ value = transformSave(value);
581
+ }
582
+ state$.numPendingSets.set((v) => (v || 0) + 1);
583
+ state$.isSetting.set(true);
584
+ const beforeSetParams = {
585
+ cancel: false
586
+ };
587
+ onBeforeSet == null ? void 0 : onBeforeSet(beforeSetParams);
588
+ if (!beforeSetParams.cancel) {
589
+ let updateResult = void 0;
590
+ let errorHandled = false;
591
+ const onError = (error, retryParams) => {
592
+ var _a2;
593
+ state$.error.set(error);
594
+ if (!errorHandled) {
595
+ (_a2 = syncOptions.onError) == null ? void 0 : _a2.call(syncOptions, error, {
596
+ setParams,
597
+ source: "set",
598
+ value$: obs$,
599
+ retryParams
600
+ });
601
+ }
602
+ errorHandled = true;
603
+ };
604
+ const setParams = {
605
+ node,
606
+ value$: obs$,
607
+ changes: changesRemote,
608
+ value,
609
+ onError,
610
+ update: (params) => {
611
+ if (updateResult) {
612
+ const { value: value2, lastSync, mode } = params;
613
+ updateResult = {
614
+ lastSync: Math.max(updateResult.lastSync || 0, lastSync || 0),
615
+ value: deepMerge(updateResult.value, value2),
616
+ mode
617
+ };
618
+ } else {
619
+ updateResult = params;
620
+ }
621
+ },
622
+ refresh: syncState2.sync,
623
+ retryNum: 0,
624
+ cancelRetry: false
625
+ };
626
+ const savedPromise = runWithRetry(
627
+ setParams,
628
+ syncOptions.retry,
629
+ async () => {
630
+ return syncOptions.set(setParams);
631
+ },
632
+ onError
633
+ );
634
+ let didError = false;
635
+ if (isPromise$1(savedPromise)) {
636
+ await savedPromise.catch(() => {
637
+ didError = true;
638
+ });
639
+ }
640
+ if (!didError) {
641
+ const pathStrs = Array.from(new Set(changesRemote.map((change) => change.pathStr)));
642
+ const { value: changes, lastSync } = updateResult || {};
643
+ if (pathStrs.length > 0) {
644
+ let transformedChanges = void 0;
645
+ const metadata = {};
646
+ if (saveLocal) {
647
+ const pendingMetadata = (_b = pluginPersist.getMetadata(table, configLocal)) == null ? void 0 : _b.pending;
648
+ const pending = localState.pendingChanges;
649
+ for (let i = 0; i < pathStrs.length; i++) {
650
+ const pathStr = pathStrs[i];
651
+ if (pendingMetadata == null ? void 0 : pendingMetadata[pathStr]) {
652
+ delete pendingMetadata[pathStr];
653
+ metadata.pending = pendingMetadata;
654
+ }
655
+ if (pending == null ? void 0 : pending[pathStr]) {
656
+ delete pending[pathStr];
657
+ }
556
658
  }
557
- if (pending == null ? void 0 : pending[pathStr]) {
558
- delete pending[pathStr];
659
+ if (lastSync) {
660
+ metadata.lastSync = lastSync;
559
661
  }
560
662
  }
561
- if (lastSync) {
562
- metadata.lastSync = lastSync;
563
- }
564
- }
565
- if (changes && !isEmpty(changes)) {
566
- transformedChanges = transformLoadData(changes, syncOptions, false, "set");
567
- }
568
- if (localState.numSavesOutstanding > 0) {
569
- if (transformedChanges) {
570
- if (!localState.pendingSaveResults) {
571
- localState.pendingSaveResults = [];
572
- }
573
- localState.pendingSaveResults.push(transformedChanges);
663
+ if (changes && !isEmpty(changes)) {
664
+ transformedChanges = transformLoadData(changes, syncOptions, false, "set");
574
665
  }
575
- } else {
576
- let allChanges = [...localState.pendingSaveResults || [], transformedChanges].filter(
577
- (v) => v !== void 0
578
- );
579
- if (allChanges.length > 0) {
580
- if (allChanges.some((change) => isPromise(change))) {
581
- allChanges = await Promise.all(allChanges);
666
+ if (transformedChanges !== void 0) {
667
+ if (isPromise$1(transformedChanges)) {
668
+ transformedChanges = await transformedChanges;
582
669
  }
583
- onChangeRemote(() => mergeIntoObservable(obs, ...allChanges));
670
+ onChangeRemote(() => mergeIntoObservable(obs$, transformedChanges));
584
671
  }
585
- if (persist) {
672
+ if (saveLocal) {
586
673
  if (shouldSaveMetadata && !isEmpty(metadata)) {
587
- updateMetadata(obs, localState, syncState, syncOptions, metadata);
674
+ updateMetadata(obs$, localState, syncState2, syncOptions, metadata);
588
675
  }
589
676
  }
590
- localState.pendingSaveResults = [];
591
677
  }
678
+ state$.numPendingSets.set((v) => v - 1);
679
+ state$.isSetting.set(state$.numPendingSets.peek() > 0);
592
680
  onAfterSet == null ? void 0 : onAfterSet();
593
681
  }
594
682
  }
595
683
  }
596
684
  }
597
- function onObsChange(value$, syncState, localState, syncOptions, { changes, loading, remote, getPrevious }) {
598
- if (!loading) {
599
- const inRemoteChange = remote;
685
+ function onObsChange(value$, syncState2, localState, syncOptions, { changes, isFromPersist, isFromSync, getPrevious }) {
686
+ if (!isFromPersist) {
687
+ const inRemoteChange = isFromSync;
600
688
  const isApplyingPending = localState.isApplyingPending;
601
689
  _queuedChanges.push({
602
690
  value$,
603
- syncState,
691
+ syncState: syncState2,
604
692
  localState,
605
693
  syncOptions,
606
694
  changes,
@@ -613,23 +701,27 @@ function onObsChange(value$, syncState, localState, syncOptions, { changes, load
613
701
  }
614
702
  }
615
703
  }
616
- async function loadLocal(value$, syncOptions, syncState, localState) {
704
+ async function loadLocal(value$, syncOptions, syncState$, localState) {
617
705
  var _a, _b, _c;
618
706
  const { persist } = syncOptions;
619
- if (persist) {
707
+ const node = getNode(value$);
708
+ const nodeValue = getNodeValue(getNode(node.state));
709
+ const syncStateValue = syncState$.peek();
710
+ const prevResetPersistence = nodeValue.resetPersistence;
711
+ if (persist == null ? void 0 : persist.name) {
620
712
  const PersistPlugin = persist.plugin || ((_a = observableSyncConfiguration.persist) == null ? void 0 : _a.plugin);
621
713
  const { table, config } = parseLocalConfig(persist);
622
- const node = getNode(value$);
714
+ syncStateValue.numPendingLocalLoads = (syncStateValue.numPendingLocalLoads || 0) + 1;
623
715
  if (!PersistPlugin) {
624
716
  throw new Error("Local persist is not configured");
625
717
  }
626
718
  if (!mapSyncPlugins.has(PersistPlugin)) {
627
- const persistPlugin2 = new PersistPlugin();
719
+ const persistPlugin2 = isFunction(PersistPlugin) ? new PersistPlugin() : PersistPlugin;
628
720
  const mapValue = { plugin: persistPlugin2, initialized: observable(false) };
629
721
  mapSyncPlugins.set(PersistPlugin, mapValue);
630
722
  if (persistPlugin2.initialize) {
631
723
  const initializePromise = (_c = persistPlugin2.initialize) == null ? void 0 : _c.call(persistPlugin2, ((_b = observableSyncConfiguration) == null ? void 0 : _b.persist) || {});
632
- if (isPromise(initializePromise)) {
724
+ if (isPromise$1(initializePromise)) {
633
725
  await initializePromise;
634
726
  }
635
727
  }
@@ -653,14 +745,14 @@ async function loadLocal(value$, syncOptions, syncState, localState) {
653
745
  if (metadata) {
654
746
  metadatas.set(value$, metadata);
655
747
  localState.pendingChanges = metadata.pending;
656
- syncState.assign({
748
+ syncState$.assign({
657
749
  lastSync: metadata.lastSync
658
750
  });
659
751
  }
660
752
  if (value !== void 0) {
661
753
  const { transform } = config;
662
754
  value = transformLoadData(value, { transform }, true, "get");
663
- if (isPromise(value)) {
755
+ if (isPromise$1(value)) {
664
756
  value = await value;
665
757
  }
666
758
  internal.globalState.isLoadingLocal = true;
@@ -671,12 +763,19 @@ async function loadLocal(value$, syncOptions, syncState, localState) {
671
763
  }
672
764
  internal.globalState.isLoadingLocal = false;
673
765
  }
674
- getNodeValue(getNode(node.state)).clearPersist = () => Promise.all([
675
- persistPlugin.deleteTable(table, config),
676
- persistPlugin.deleteMetadata(table, config)
677
- ]);
766
+ syncStateValue.numPendingLocalLoads--;
767
+ nodeValue.resetPersistence = () => Promise.all(
768
+ [
769
+ prevResetPersistence,
770
+ persistPlugin.deleteTable(table, config),
771
+ persistPlugin.deleteMetadata(table, config)
772
+ ].filter(Boolean)
773
+ );
774
+ } else {
775
+ nodeValue.resetPersistence = () => prevResetPersistence == null ? void 0 : prevResetPersistence();
678
776
  }
679
- syncState.isPersistLoaded.set(true);
777
+ nodeValue.clearPersist = nodeValue.resetPersistence;
778
+ syncState$.isPersistLoaded.set(!(syncStateValue.numPendingLocalLoads > 0));
680
779
  }
681
780
  function syncObservable(obs$, syncOptionsOrSynced) {
682
781
  let syncOptions = syncOptionsOrSynced;
@@ -687,7 +786,7 @@ function syncObservable(obs$, syncOptionsOrSynced) {
687
786
  if ((process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test") && (!obs$ || !node)) {
688
787
  throw new Error("[legend-state] syncObservable called with undefined observable");
689
788
  }
690
- syncOptions = mergeIntoObservable(
789
+ syncOptions = deepMerge(
691
790
  {
692
791
  syncMode: "auto"
693
792
  },
@@ -696,24 +795,54 @@ function syncObservable(obs$, syncOptionsOrSynced) {
696
795
  );
697
796
  const localState = {};
698
797
  let sync;
699
- const syncState = node.state = observable({
700
- isPersistLoaded: false,
701
- isLoaded: !syncOptions.get,
702
- isPersistEnabled: true,
703
- isSyncEnabled: true,
704
- clearPersist: void 0,
705
- sync: () => Promise.resolve(),
706
- getPendingChanges: () => localState.pendingChanges
707
- });
708
- loadLocal(obs$, syncOptions, syncState, localState);
709
- localState.pluginSync = syncObservableAdapter(syncOptions);
798
+ const syncState$ = syncState(obs$);
799
+ const syncStateValue = getNodeValue(getNode(syncState$));
800
+ allSyncStates.set(syncState$, node);
801
+ syncStateValue.getPendingChanges = () => localState.pendingChanges;
802
+ let errorHandled = false;
803
+ const onGetError = (error, params) => {
804
+ var _a;
805
+ syncState$.error.set(error);
806
+ if (!errorHandled) {
807
+ (_a = syncOptions.onError) == null ? void 0 : _a.call(syncOptions, error, { ...params, value$: obs$ });
808
+ }
809
+ errorHandled = true;
810
+ };
811
+ loadLocal(obs$, syncOptions, syncState$, localState);
812
+ let isWaitingForLoad = !!syncOptions.get;
813
+ if (isWaitingForLoad) {
814
+ syncStateValue.numPendingRemoteLoads = (syncStateValue.numPendingRemoteLoads || 0) + 1;
815
+ }
816
+ syncState$.isLoaded.set(!syncState$.numPendingRemoteLoads.peek());
817
+ let isSynced = false;
818
+ let isSubscribed = false;
819
+ let unsubscribe = void 0;
820
+ const applyPending = (pending) => {
821
+ if (pending && !isEmpty(pending)) {
822
+ localState.isApplyingPending = true;
823
+ const keys = Object.keys(pending);
824
+ const changes = [];
825
+ for (let i = 0; i < keys.length; i++) {
826
+ const key = keys[i];
827
+ const path = key.split("/").filter((p2) => p2 !== "");
828
+ const { p, v, t } = pending[key];
829
+ changes.push({ path, valueAtPath: v, prevAtPath: p, pathTypes: t });
830
+ }
831
+ const value = getNodeValue(node);
832
+ onObsChange(obs$, syncState$, localState, syncOptions, {
833
+ value,
834
+ isFromPersist: false,
835
+ isFromSync: false,
836
+ getPrevious: createPreviousHandler(value, changes),
837
+ changes
838
+ });
839
+ localState.isApplyingPending = false;
840
+ }
841
+ };
710
842
  if (syncOptions.get) {
711
- let isSynced = false;
712
- let isSubscribed = false;
713
- let unsubscribe = void 0;
714
843
  sync = async () => {
715
- var _a, _b;
716
- if (isSynced && shouldIgnoreUnobserved(node, sync)) {
844
+ var _a;
845
+ if (isSynced && (!getNodeValue(getNode(syncState$)).isSyncEnabled || shouldIgnoreUnobserved(node, sync))) {
717
846
  if (unsubscribe) {
718
847
  isSubscribed = false;
719
848
  unsubscribe();
@@ -723,14 +852,16 @@ function syncObservable(obs$, syncOptionsOrSynced) {
723
852
  }
724
853
  const lastSync = (_a = metadatas.get(obs$)) == null ? void 0 : _a.lastSync;
725
854
  const pending = localState.pendingChanges;
726
- const get = (_b = localState.pluginSync.get) == null ? void 0 : _b.bind(localState.pluginSync);
855
+ const get = syncOptions.get;
727
856
  if (get) {
857
+ const { waitFor } = syncOptions;
728
858
  const runGet = () => {
859
+ var _a2;
729
860
  const onChange = async ({ value, mode, lastSync: lastSync2 }) => {
730
861
  mode = mode || syncOptions.mode || "set";
731
862
  if (value !== void 0) {
732
863
  value = transformLoadData(value, syncOptions, true, "get");
733
- if (isPromise(value)) {
864
+ if (isPromise$1(value)) {
734
865
  value = await value;
735
866
  }
736
867
  const pending2 = localState.pendingChanges;
@@ -738,9 +869,11 @@ function syncObservable(obs$, syncOptionsOrSynced) {
738
869
  if (pending2) {
739
870
  let didChangeMetadata = false;
740
871
  Object.keys(pending2).forEach((key) => {
741
- const p = key.split("/").filter((p2) => p2 !== "");
872
+ const p = key.split("/").filter((k) => k !== "");
742
873
  const { v, t } = pending2[key];
743
874
  if (t.length === 0 || !value) {
875
+ const oldValue = clone(value);
876
+ pending2[key].p = oldValue;
744
877
  if (isObject(value) && isObject(v)) {
745
878
  Object.assign(value, v);
746
879
  } else {
@@ -753,6 +886,8 @@ function syncObservable(obs$, syncOptionsOrSynced) {
753
886
  delete pending2[key];
754
887
  didChangeMetadata = true;
755
888
  } else {
889
+ const oldValue = clone(value);
890
+ pending2[key].p = getValueAtPath(oldValue, p);
756
891
  value = setAtPath(
757
892
  value,
758
893
  p,
@@ -772,18 +907,27 @@ function syncObservable(obs$, syncOptionsOrSynced) {
772
907
  }
773
908
  }
774
909
  });
775
- if (didChangeMetadata) {
776
- updateMetadata(obs$, localState, syncState, syncOptions, {
910
+ if (didChangeMetadata && syncOptions.persist) {
911
+ updateMetadata(obs$, localState, syncState$, syncOptions, {
777
912
  pending: pending2
778
913
  });
779
914
  }
780
915
  }
781
916
  onChangeRemote(() => {
782
- if (mode === "assign" && isObject(value)) {
917
+ if (isPlainObject(value)) {
918
+ value = ObservableHint.plain(value);
919
+ }
920
+ if (mode === "assign") {
783
921
  obs$.assign(value);
784
- } else if (mode === "append" && isArray(value)) {
922
+ } else if (mode === "append") {
923
+ if ((process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test") && !isArray(value)) {
924
+ console.error("[legend-state] mode:append expects the value to be an array");
925
+ }
785
926
  obs$.push(...value);
786
- } else if (mode === "prepend" && isArray(value)) {
927
+ } else if (mode === "prepend") {
928
+ if ((process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test") && !isArray(value)) {
929
+ console.error("[legend-state] mode:prepend expects the value to be an array");
930
+ }
787
931
  obs$.splice(0, 0, ...value);
788
932
  } else if (mode === "merge") {
789
933
  mergeIntoObservable(obs$, value);
@@ -793,78 +937,180 @@ function syncObservable(obs$, syncOptionsOrSynced) {
793
937
  });
794
938
  }
795
939
  if (lastSync2 && syncOptions.persist) {
796
- updateMetadata(obs$, localState, syncState, syncOptions, {
940
+ updateMetadata(obs$, localState, syncState$, syncOptions, {
797
941
  lastSync: lastSync2
798
942
  });
799
943
  }
800
944
  };
801
- get({
802
- state: syncState,
945
+ if (node.activationState) {
946
+ node.activationState.onChange = onChange;
947
+ }
948
+ if (!isSubscribed && syncOptions.subscribe) {
949
+ const subscribe = syncOptions.subscribe;
950
+ isSubscribed = true;
951
+ const doSubscribe = () => {
952
+ const subscribeParams = {
953
+ node,
954
+ value$: obs$,
955
+ lastSync,
956
+ update: (params) => {
957
+ when(syncState$.isLoaded, () => {
958
+ when(waitFor || true, () => {
959
+ params.mode || (params.mode = syncOptions.mode || "merge");
960
+ onChange(params);
961
+ });
962
+ });
963
+ },
964
+ refresh: () => when(syncState$.isLoaded, sync),
965
+ onError: (error) => onGetError(error, { source: "subscribe", subscribeParams })
966
+ };
967
+ unsubscribe = subscribe(subscribeParams);
968
+ };
969
+ if (waitFor) {
970
+ whenReady(waitFor, doSubscribe);
971
+ } else {
972
+ doSubscribe();
973
+ }
974
+ }
975
+ const existingValue = getNodeValue(node);
976
+ const onError = (error) => onGetError(error, { getParams, source: "get" });
977
+ const getParams = {
978
+ node,
803
979
  value$: obs$,
980
+ value: isFunction(existingValue) || (existingValue == null ? void 0 : existingValue[symbolLinked]) ? void 0 : existingValue,
981
+ mode: syncOptions.mode,
982
+ refresh: sync,
804
983
  options: syncOptions,
805
984
  lastSync,
806
- dateModified: lastSync,
807
- onError: (error) => {
808
- var _a2;
809
- (_a2 = syncOptions.onGetError) == null ? void 0 : _a2.call(syncOptions, error);
810
- },
811
- onGet: () => {
812
- node.state.assign({
813
- isLoaded: true,
814
- error: void 0
985
+ updateLastSync: (lastSync2) => getParams.lastSync = lastSync2,
986
+ onError,
987
+ retryNum: 0,
988
+ cancelRetry: false
989
+ };
990
+ let modeBeforeReset = void 0;
991
+ const beforeGetParams = {
992
+ value: getParams.value,
993
+ lastSync,
994
+ pendingChanges: pending && !isEmpty(pending) ? pending : void 0,
995
+ clearPendingChanges: async () => {
996
+ localState.pendingChanges = {};
997
+ await updateMetadataImmediate(obs$, localState, syncState$, syncOptions, {
998
+ pending: localState.pendingChanges
815
999
  });
816
1000
  },
817
- onChange
818
- });
819
- if (!isSubscribed && syncOptions.subscribe) {
820
- isSubscribed = true;
821
- unsubscribe = syncOptions.subscribe({
822
- node,
823
- value$: obs$,
824
- update: (params) => {
825
- when(node.state.isLoaded, () => {
826
- params.mode || (params.mode = syncOptions.mode || "merge");
827
- onChange(params);
828
- });
829
- },
830
- refresh: () => when(node.state.isLoaded, sync)
1001
+ resetCache: () => {
1002
+ var _a3;
1003
+ modeBeforeReset = getParams.mode;
1004
+ getParams.mode = "set";
1005
+ return (_a3 = syncStateValue.resetPersistence) == null ? void 0 : _a3.call(syncStateValue);
1006
+ },
1007
+ cancel: false
1008
+ };
1009
+ (_a2 = syncOptions.onBeforeGet) == null ? void 0 : _a2.call(syncOptions, beforeGetParams);
1010
+ if (!beforeGetParams.cancel) {
1011
+ syncState$.assign({
1012
+ numPendingGets: (syncStateValue.numPendingGets || 0) + 1,
1013
+ isGetting: true
831
1014
  });
1015
+ const got = runWithRetry(
1016
+ getParams,
1017
+ syncOptions.retry,
1018
+ (retryEvent) => {
1019
+ const params = getParams;
1020
+ params.cancelRetry = retryEvent.cancelRetry;
1021
+ params.retryNum = retryEvent.retryNum;
1022
+ return get(params);
1023
+ },
1024
+ onError
1025
+ );
1026
+ const numGets = node.numGets = (node.numGets || 0) + 1;
1027
+ const handle = (value) => {
1028
+ syncState$.numPendingGets.set((v) => v - 1);
1029
+ if (isWaitingForLoad) {
1030
+ isWaitingForLoad = false;
1031
+ syncStateValue.numPendingRemoteLoads--;
1032
+ }
1033
+ if (numGets >= (node.getNumResolved || 0)) {
1034
+ node.getNumResolved = node.numGets;
1035
+ onChange({
1036
+ value,
1037
+ lastSync: getParams.lastSync,
1038
+ mode: getParams.mode
1039
+ });
1040
+ }
1041
+ if (modeBeforeReset) {
1042
+ getParams.mode = modeBeforeReset;
1043
+ modeBeforeReset = void 0;
1044
+ }
1045
+ syncState$.assign({
1046
+ isLoaded: syncStateValue.numPendingRemoteLoads < 1,
1047
+ error: void 0,
1048
+ isGetting: syncStateValue.numPendingGets > 0
1049
+ });
1050
+ };
1051
+ if (isPromise$1(got)) {
1052
+ got.then(handle);
1053
+ } else {
1054
+ handle(got);
1055
+ }
832
1056
  }
833
1057
  };
834
- runGet();
1058
+ if (waitFor) {
1059
+ whenReady(waitFor, () => trackSelector(runGet, sync));
1060
+ } else {
1061
+ trackSelector(runGet, sync);
1062
+ }
835
1063
  } else {
836
- node.state.assign({
1064
+ syncState$.assign({
837
1065
  isLoaded: true,
838
1066
  error: void 0
839
1067
  });
840
1068
  }
841
1069
  if (!isSynced) {
842
1070
  isSynced = true;
843
- await when(() => syncState.isLoaded.get() || syncOptions.allowSetIfGetError && syncState.error.get());
844
- if (pending && !isEmpty(pending)) {
845
- localState.isApplyingPending = true;
846
- const keys = Object.keys(pending);
847
- const changes = [];
848
- for (let i = 0; i < keys.length; i++) {
849
- const key = keys[i];
850
- const path = key.split("/").filter((p2) => p2 !== "");
851
- const { p, v, t } = pending[key];
852
- changes.push({ path, valueAtPath: v, prevAtPath: p, pathTypes: t });
853
- }
854
- const value = getNodeValue(node);
855
- onObsChange(obs$, syncState, localState, syncOptions, {
856
- value,
857
- loading: false,
858
- remote: false,
859
- getPrevious: createPreviousHandler(value, changes),
860
- changes
861
- });
862
- localState.isApplyingPending = false;
863
- }
1071
+ await when(syncState$.isLoaded);
1072
+ applyPending(pending);
864
1073
  }
865
1074
  };
866
- syncState.assign({ sync });
1075
+ syncStateValue.sync = sync;
1076
+ } else {
1077
+ if (!isSynced) {
1078
+ applyPending(localState.pendingChanges);
1079
+ }
867
1080
  }
1081
+ syncStateValue.reset = async () => {
1082
+ const wasPersistEnabled = syncStateValue.isPersistEnabled;
1083
+ const wasSyncEnabled = syncStateValue.isSyncEnabled;
1084
+ const metadata = metadatas.get(obs$);
1085
+ if (metadata) {
1086
+ Object.assign(metadata, { lastSync: void 0, pending: void 0 });
1087
+ }
1088
+ Object.assign(syncStateValue, {
1089
+ isPersistEnabled: false,
1090
+ isSyncEnabled: false,
1091
+ lastSync: void 0,
1092
+ numPendingGets: 0,
1093
+ isLoaded: false,
1094
+ isGetting: false,
1095
+ isSetting: false,
1096
+ numPendingSets: 0,
1097
+ syncCount: 0
1098
+ });
1099
+ isSynced = false;
1100
+ isSubscribed = false;
1101
+ unsubscribe == null ? void 0 : unsubscribe();
1102
+ unsubscribe = void 0;
1103
+ const promise = syncStateValue.resetPersistence();
1104
+ onChangeRemote(() => {
1105
+ var _a;
1106
+ obs$.set((_a = syncOptions.initial) != null ? _a : void 0);
1107
+ });
1108
+ syncState$.isLoaded.set(false);
1109
+ syncStateValue.isPersistEnabled = wasPersistEnabled;
1110
+ syncStateValue.isSyncEnabled = wasSyncEnabled;
1111
+ node.dirtyFn = sync;
1112
+ await promise;
1113
+ };
868
1114
  const onAllPersistLoaded = () => {
869
1115
  var _a, _b;
870
1116
  let parentNode = node;
@@ -882,77 +1128,27 @@ function syncObservable(obs$, syncOptionsOrSynced) {
882
1128
  }
883
1129
  if ((syncOptions == null ? void 0 : syncOptions.set) || (syncOptions == null ? void 0 : syncOptions.persist)) {
884
1130
  obs$.onChange(
885
- onObsChange.bind(this, obs$, syncState, localState, syncOptions)
1131
+ onObsChange.bind(this, obs$, syncState$, localState, syncOptions)
886
1132
  );
887
1133
  }
888
1134
  });
889
- return syncState;
1135
+ return syncState$;
890
1136
  }
891
- var { getProxy, globalState: globalState2, runWithRetry, symbolLinked: symbolLinked2, setNodeValue, getNodeValue: getNodeValue2 } = internal;
1137
+ var { getProxy, globalState: globalState2, setNodeValue, getNodeValue: getNodeValue2 } = internal;
892
1138
  function enableActivateSyncedNode() {
893
1139
  globalState2.activateSyncedNode = function activateSyncedNode(node, newValue) {
894
1140
  const obs$ = getProxy(node);
895
1141
  if (node.activationState) {
896
- const { get, initial, set, retry } = node.activationState;
897
- let onChange = void 0;
898
- const pluginRemote = {};
1142
+ const {
1143
+ get: getOrig,
1144
+ initial,
1145
+ set,
1146
+ onChange
1147
+ } = node.activationState;
899
1148
  let promiseReturn = void 0;
900
- let syncState;
901
- const refresh = () => syncState == null ? void 0 : syncState.sync();
902
- if (get) {
903
- pluginRemote.get = (params) => {
904
- var _a;
905
- onChange = params.onChange;
906
- const updateLastSync = (lastSync) => params.lastSync = lastSync;
907
- const existingValue = getNodeValue2(node);
908
- const value = runWithRetry(node, { attemptNum: 0, retry: retry || ((_a = params.options) == null ? void 0 : _a.retry) }, () => {
909
- const paramsToGet = {
910
- value: isFunction(existingValue) || (existingValue == null ? void 0 : existingValue[symbolLinked2]) ? void 0 : existingValue,
911
- lastSync: params.lastSync,
912
- updateLastSync,
913
- mode: params.mode,
914
- refresh
915
- };
916
- const ret = get(paramsToGet);
917
- params.mode = paramsToGet.mode;
918
- return ret;
919
- });
920
- promiseReturn = value;
921
- return value;
922
- };
923
- }
924
- if (set) {
925
- pluginRemote.set = async (params) => {
926
- var _a, _b;
927
- if ((_a = node.state) == null ? void 0 : _a.isLoaded.get()) {
928
- const retryAttempts = { attemptNum: 0, retry: retry || ((_b = params.options) == null ? void 0 : _b.retry) };
929
- return runWithRetry(node, retryAttempts, async (retryEvent) => {
930
- let changes = {};
931
- let maxModified = 0;
932
- if (!node.state.isLoaded.peek()) {
933
- await whenReady(node.state.isLoaded);
934
- }
935
- const cancelRetry = () => {
936
- retryEvent.cancel = true;
937
- };
938
- await set({
939
- ...params,
940
- node,
941
- update: (params2) => {
942
- const { value, lastSync } = params2;
943
- maxModified = Math.max(lastSync || 0, maxModified);
944
- changes = mergeIntoObservable(changes, value);
945
- },
946
- retryNum: retryAttempts.attemptNum,
947
- cancelRetry,
948
- refresh,
949
- fromSubscribe: false
950
- });
951
- return { changes, lastSync: maxModified || void 0 };
952
- });
953
- }
954
- };
955
- }
1149
+ const get = getOrig ? (params) => {
1150
+ return promiseReturn = getOrig(params);
1151
+ } : void 0;
956
1152
  const nodeVal = getNodeValue2(node);
957
1153
  if (promiseReturn !== void 0) {
958
1154
  newValue = promiseReturn;
@@ -962,13 +1158,13 @@ function enableActivateSyncedNode() {
962
1158
  newValue = initial;
963
1159
  }
964
1160
  setNodeValue(node, promiseReturn ? void 0 : newValue);
965
- syncState = syncObservable(obs$, { ...node.activationState, ...pluginRemote });
1161
+ syncObservable(obs$, { ...node.activationState, get, set });
966
1162
  return { update: onChange, value: newValue };
967
1163
  } else {
968
1164
  let update = void 0;
969
1165
  const get = async (params) => {
970
1166
  update = params.refresh;
971
- if (isPromise(newValue)) {
1167
+ if (isPromise$1(newValue)) {
972
1168
  try {
973
1169
  newValue = await newValue;
974
1170
  } catch (e) {
@@ -999,13 +1195,20 @@ function installPersistActivateNode() {
999
1195
  didInstall = true;
1000
1196
  }
1001
1197
  }
1198
+ var { deepMerge: deepMerge2 } = internal;
1199
+ function configureSynced(fnOrOrigOptions, origOptions) {
1200
+ const fn = origOptions ? fnOrOrigOptions : synced;
1201
+ origOptions = origOptions != null ? origOptions : fnOrOrigOptions;
1202
+ return (options) => {
1203
+ const merged = deepMerge2(origOptions, options);
1204
+ return fn(merged);
1205
+ };
1206
+ }
1002
1207
 
1003
1208
  // sync.ts
1004
- function isInRemoteChange() {
1005
- return internal.globalState.isLoadingRemote;
1006
- }
1007
- var internal3 = {
1008
- observableSyncConfiguration
1209
+ var internal4 = {
1210
+ observableSyncConfiguration,
1211
+ waitForSet
1009
1212
  };
1010
1213
 
1011
- export { combineTransforms, configureObservableSync, deepEqual, diffObjects, internal3 as internal, isInRemoteChange, mapSyncPlugins, onChangeRemote, removeNullUndefined, syncObservable, synced, transformStringifyDates, transformStringifyKeys };
1214
+ export { combineTransforms, configureObservableSync, configureSynced, deepEqual, diffObjects, internal4 as internal, mapSyncPlugins, onChangeRemote, removeNullUndefined, syncObservable, synced, transformStringifyDates, transformStringifyKeys };