@legendapp/state 1.11.1 → 2.0.0-beta.0

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 (108) hide show
  1. package/CHANGELOG.md +0 -4
  2. package/index.d.ts +2 -3
  3. package/index.js +220 -226
  4. package/index.js.map +1 -1
  5. package/index.mjs +220 -224
  6. package/index.mjs.map +1 -1
  7. package/package.json +21 -24
  8. package/persist-plugins/async-storage.d.ts +14 -0
  9. package/persist-plugins/async-storage.js +100 -0
  10. package/persist-plugins/async-storage.js.map +1 -0
  11. package/persist-plugins/async-storage.mjs +98 -0
  12. package/persist-plugins/async-storage.mjs.map +1 -0
  13. package/persist-plugins/fetch.d.ts +10 -0
  14. package/persist-plugins/fetch.js +22 -0
  15. package/persist-plugins/fetch.js.map +1 -0
  16. package/persist-plugins/fetch.mjs +20 -0
  17. package/persist-plugins/fetch.mjs.map +1 -0
  18. package/persist-plugins/firebase.d.ts +51 -0
  19. package/persist-plugins/firebase.js +692 -0
  20. package/persist-plugins/firebase.js.map +1 -0
  21. package/persist-plugins/firebase.mjs +690 -0
  22. package/persist-plugins/firebase.mjs.map +1 -0
  23. package/persist-plugins/indexeddb.d.ts +2 -3
  24. package/persist-plugins/indexeddb.js +1 -16
  25. package/persist-plugins/indexeddb.js.map +1 -1
  26. package/persist-plugins/indexeddb.mjs +1 -16
  27. package/persist-plugins/indexeddb.mjs.map +1 -1
  28. package/persist-plugins/local-storage.d.ts +1 -2
  29. package/persist-plugins/local-storage.js +1 -5
  30. package/persist-plugins/local-storage.js.map +1 -1
  31. package/persist-plugins/local-storage.mjs +1 -5
  32. package/persist-plugins/local-storage.mjs.map +1 -1
  33. package/persist-plugins/mmkv.d.ts +4 -4
  34. package/persist-plugins/mmkv.js +6 -4
  35. package/persist-plugins/mmkv.js.map +1 -1
  36. package/persist-plugins/mmkv.mjs +6 -4
  37. package/persist-plugins/mmkv.mjs.map +1 -1
  38. package/persist-plugins/query.d.ts +20 -0
  39. package/persist-plugins/query.js +89 -0
  40. package/persist-plugins/query.js.map +1 -0
  41. package/persist-plugins/query.mjs +87 -0
  42. package/persist-plugins/query.mjs.map +1 -0
  43. package/persist.js +170 -143
  44. package/persist.js.map +1 -1
  45. package/persist.mjs +171 -144
  46. package/persist.mjs.map +1 -1
  47. package/react-hooks/usePersistedObservable.d.ts +2 -2
  48. package/react-hooks/usePersistedObservable.js +1 -6
  49. package/react-hooks/usePersistedObservable.js.map +1 -1
  50. package/react-hooks/usePersistedObservable.mjs +1 -6
  51. package/react-hooks/usePersistedObservable.mjs.map +1 -1
  52. package/react.d.ts +1 -6
  53. package/react.js +47 -99
  54. package/react.js.map +1 -1
  55. package/react.mjs +47 -100
  56. package/react.mjs.map +1 -1
  57. package/src/ObservableObject.d.ts +4 -0
  58. package/src/batching.d.ts +0 -7
  59. package/src/globals.d.ts +1 -5
  60. package/src/is.d.ts +1 -0
  61. package/src/observable.d.ts +5 -3
  62. package/src/observableInterfaces.d.ts +93 -69
  63. package/src/observe.d.ts +1 -1
  64. package/src/persist/observablePersistRemoteFunctionsAdapter.d.ts +2 -0
  65. package/src/persist/persistObservable.d.ts +10 -13
  66. package/src/persist-plugins/async-storage.d.ts +14 -0
  67. package/src/persist-plugins/fetch.d.ts +10 -0
  68. package/src/persist-plugins/firebase.d.ts +51 -0
  69. package/src/persist-plugins/indexeddb.d.ts +2 -3
  70. package/src/persist-plugins/local-storage.d.ts +1 -2
  71. package/src/persist-plugins/mmkv.d.ts +4 -4
  72. package/src/persist-plugins/query.d.ts +20 -0
  73. package/src/react/For.d.ts +1 -1
  74. package/src/react/react-globals.d.ts +3 -0
  75. package/src/react/reactInterfaces.d.ts +1 -1
  76. package/src/react/reactive-observer.d.ts +1 -5
  77. package/src/react/useObservableState.d.ts +2 -0
  78. package/src/react/useSelector.d.ts +1 -1
  79. package/src/react/useWhen.d.ts +3 -0
  80. package/src/react-hooks/usePersistedObservable.d.ts +2 -2
  81. package/src/trackSelector.d.ts +1 -1
  82. package/src/tracking.d.ts +2 -3
  83. package/config/disableDeprecationWarnings.d.ts +0 -1
  84. package/config/disableDeprecationWarnings.js +0 -10
  85. package/config/disableDeprecationWarnings.js.map +0 -1
  86. package/config/disableDeprecationWarnings.mjs +0 -8
  87. package/config/disableDeprecationWarnings.mjs.map +0 -1
  88. package/config/enableReactDirectRender.d.ts +0 -7
  89. package/config/enableReactDirectRender.js +0 -77
  90. package/config/enableReactDirectRender.js.map +0 -1
  91. package/config/enableReactDirectRender.mjs +0 -74
  92. package/config/enableReactDirectRender.mjs.map +0 -1
  93. package/react-components.d.ts +0 -2
  94. package/react-components.js +0 -44
  95. package/react-components.js.map +0 -1
  96. package/react-components.mjs +0 -31
  97. package/react-components.mjs.map +0 -1
  98. package/react-native-components.d.ts +0 -2
  99. package/react-native-components.js +0 -67
  100. package/react-native-components.js.map +0 -1
  101. package/react-native-components.mjs +0 -54
  102. package/react-native-components.mjs.map +0 -1
  103. package/src/config/disableDeprecationWarnings.d.ts +0 -1
  104. package/src/config/enableReactDirectRender.d.ts +0 -7
  105. package/src/react/enableLegendStateReact.d.ts +0 -1
  106. package/src/react-components/react-components.d.ts +0 -7
  107. package/src/react-native-components/rn-components.d.ts +0 -21
  108. package/types.d.ts +0 -13
package/persist.js CHANGED
@@ -135,19 +135,37 @@ if (process.env.NODE_ENV === 'development') {
135
135
  };
136
136
  }
137
137
 
138
+ function observablePersistRemoteFunctionsAdapter({ get, set, }) {
139
+ const ret = {
140
+ async get(params) {
141
+ const value = (await get(params));
142
+ params.onChange({ value, dateModified: Date.now() });
143
+ params.onGet();
144
+ },
145
+ };
146
+ if (set) {
147
+ ret.set = set;
148
+ }
149
+ return ret;
150
+ }
151
+
138
152
  const mapPersistences = new WeakMap();
139
153
  const persistState = state.observable({ inRemoteSync: false });
140
154
  const metadatas = new WeakMap();
141
155
  const promisesLocalSaves = new Set();
142
156
  function parseLocalConfig(config) {
143
- return state.isString(config) ? { table: config, config: { name: config } } : { table: config.name, config };
157
+ return config
158
+ ? state.isString(config)
159
+ ? { table: config, config: { name: config } }
160
+ : { table: config.name, config }
161
+ : {};
144
162
  }
145
163
  function doInOrder(arg1, arg2) {
146
164
  return state.isPromise(arg1) ? arg1.then(arg2) : arg2(arg1);
147
165
  }
148
- function adjustSaveData(value, path, pathTypes, { adjustData, fieldTransforms, }) {
149
- if (fieldTransforms || (adjustData === null || adjustData === void 0 ? void 0 : adjustData.save)) {
150
- const transform = () => {
166
+ function transformOutData(value, path, pathTypes, { transform, fieldTransforms }) {
167
+ if (fieldTransforms || (transform === null || transform === void 0 ? void 0 : transform.out)) {
168
+ const transformFn = () => {
151
169
  if (fieldTransforms) {
152
170
  const { obj, path: pathTransformed } = transformObjectWithPath(value, path, pathTypes, fieldTransforms);
153
171
  value = obj;
@@ -155,30 +173,30 @@ function adjustSaveData(value, path, pathTypes, { adjustData, fieldTransforms, }
155
173
  }
156
174
  return { value, path };
157
175
  };
158
- if (adjustData === null || adjustData === void 0 ? void 0 : adjustData.save) {
176
+ if (transform === null || transform === void 0 ? void 0 : transform.out) {
159
177
  const constructed = state.constructObjectWithPath(path, value, pathTypes);
160
- const saved = adjustData.save(constructed);
178
+ const saved = transform.out(constructed);
161
179
  const deconstruct = (toDeconstruct) => {
162
180
  value = state.deconstructObjectWithPath(path, toDeconstruct);
163
- return transform();
181
+ return transformFn();
164
182
  };
165
183
  return doInOrder(saved, deconstruct);
166
184
  }
167
- return transform();
185
+ return transformFn();
168
186
  }
169
187
  return { value, path };
170
188
  }
171
- function adjustLoadData(value, { adjustData, fieldTransforms, }, doUserAdjustData) {
189
+ function transformLoadData(value, { transform, fieldTransforms }, doUserTransform) {
172
190
  if (fieldTransforms) {
173
191
  const inverted = invertFieldMap(fieldTransforms);
174
192
  value = transformObject(value, inverted);
175
193
  }
176
- if (doUserAdjustData && (adjustData === null || adjustData === void 0 ? void 0 : adjustData.load)) {
177
- value = adjustData.load(value);
194
+ if (doUserTransform && (transform === null || transform === void 0 ? void 0 : transform.in)) {
195
+ value = transform.in(value);
178
196
  }
179
197
  return value;
180
198
  }
181
- async function updateMetadataImmediate(obs, localState, obsState, persistOptions, newMetadata) {
199
+ async function updateMetadataImmediate(obs, localState, syncState, persistOptions, newMetadata) {
182
200
  const saves = Array.from(promisesLocalSaves);
183
201
  if (saves.length > 0) {
184
202
  await Promise.all(saves);
@@ -193,17 +211,19 @@ async function updateMetadataImmediate(obs, localState, obsState, persistOptions
193
211
  if (needsUpdate) {
194
212
  const metadata = Object.assign({}, oldMetadata, newMetadata);
195
213
  metadatas.set(obs, metadata);
196
- await persistenceLocal.updateMetadata(table, metadata, config);
214
+ if (persistenceLocal) {
215
+ await persistenceLocal.setMetadata(table, metadata, config);
216
+ }
197
217
  if (modified) {
198
- obsState.dateModified.set(modified);
218
+ syncState.dateModified.set(modified);
199
219
  }
200
220
  }
201
221
  }
202
- function updateMetadata(obs, localState, obsState, persistOptions, newMetadata) {
222
+ function updateMetadata(obs, localState, syncState, persistOptions, newMetadata) {
203
223
  if (localState.timeoutSaveMetadata) {
204
224
  clearTimeout(localState.timeoutSaveMetadata);
205
225
  }
206
- localState.timeoutSaveMetadata = setTimeout(() => updateMetadataImmediate(obs, localState, obsState, persistOptions, newMetadata), 30);
226
+ localState.timeoutSaveMetadata = setTimeout(() => updateMetadataImmediate(obs, localState, syncState, persistOptions, newMetadata), 30);
207
227
  }
208
228
  let _queuedChanges = [];
209
229
  async function processQueuedChanges() {
@@ -211,9 +231,9 @@ async function processQueuedChanges() {
211
231
  const queuedChanges = _queuedChanges;
212
232
  _queuedChanges = [];
213
233
  // Note: Summary of the order of operations these functions:
214
- // 1. Prepare all changes for saving. This may involve waiting for promises if the user has asynchronous adjustData.
234
+ // 1. Prepare all changes for saving. This may involve waiting for promises if the user has asynchronous transform.
215
235
  // We need to prepare all of the changes in the queue before saving so that the saves happen in the correct order,
216
- // since some may take longer to adjustSaveData than others.
236
+ // since some may take longer to transformSaveData than others.
217
237
  const changes = await Promise.all(queuedChanges.map(prepChange));
218
238
  // 2. Save pending to the metadata table first. If this is the only operation that succeeds, it would try to save
219
239
  // the current value again on next load, which isn't too bad.
@@ -227,21 +247,22 @@ async function processQueuedChanges() {
227
247
  changes.forEach(doChange);
228
248
  }
229
249
  async function prepChange(queuedChange) {
230
- const { obsState, changes, localState, persistOptions, inRemoteChange, isApplyingPending } = queuedChange;
250
+ const { syncState, changes, localState, persistOptions, inRemoteChange, isApplyingPending } = queuedChange;
231
251
  const local = persistOptions.local;
252
+ const { persistenceRemote } = localState;
232
253
  const { config: configLocal } = parseLocalConfig(local);
233
254
  const configRemote = persistOptions.remote;
234
- const saveLocal = local && !configLocal.readonly && !isApplyingPending && obsState.isEnabledLocal.peek();
235
- const saveRemote = !inRemoteChange && configRemote && !configRemote.readonly && obsState.isEnabledRemote.peek();
255
+ const saveLocal = local && !configLocal.readonly && !isApplyingPending && syncState.isEnabledLocal.peek();
256
+ const saveRemote = !inRemoteChange && persistenceRemote && !(configRemote === null || configRemote === void 0 ? void 0 : configRemote.readonly) && syncState.isEnabledRemote.peek();
236
257
  if (saveLocal || saveRemote) {
237
- if (saveLocal && !obsState.isLoadedLocal.peek()) {
258
+ if (saveLocal && !syncState.isLoadedLocal.peek()) {
238
259
  console.error('[legend-state] WARNING: An observable was changed before being loaded from persistence', local);
239
260
  return;
240
261
  }
241
262
  const changesLocal = [];
242
263
  const changesRemote = [];
243
264
  const changesPaths = new Set();
244
- let promisesAdjustData = [];
265
+ let promisesTransform = [];
245
266
  // Reverse order
246
267
  for (let i = changes.length - 1; i >= 0; i--) {
247
268
  const { path } = changes[i];
@@ -263,26 +284,26 @@ async function prepChange(queuedChange) {
263
284
  changesPaths.add(pathStr);
264
285
  const { prevAtPath, valueAtPath, pathTypes } = changes[i];
265
286
  if (saveLocal) {
266
- const promiseAdjustLocal = adjustSaveData(valueAtPath, path, pathTypes, configLocal);
267
- promisesAdjustData.push(doInOrder(promiseAdjustLocal, ({ path: pathAdjusted, value: valueAdjusted }) => {
287
+ const promiseTransformLocal = transformOutData(valueAtPath, path, pathTypes, configLocal);
288
+ promisesTransform.push(doInOrder(promiseTransformLocal, ({ path: pathTransformed, value: valueTransformed }) => {
268
289
  // If path includes undefined there was a null in fieldTransforms so don't need to save it
269
- if (!pathAdjusted.includes(undefined)) {
270
- // Prepare the local change with the adjusted path/value
290
+ if (!pathTransformed.includes(undefined)) {
291
+ // Prepare the local change with the transformed path/value
271
292
  changesLocal.push({
272
- path: pathAdjusted,
293
+ path: pathTransformed,
273
294
  pathTypes,
274
295
  prevAtPath,
275
- valueAtPath: valueAdjusted,
296
+ valueAtPath: valueTransformed,
276
297
  pathStr,
277
298
  });
278
299
  }
279
300
  }));
280
301
  }
281
302
  if (saveRemote) {
282
- const promiseAdjustRemote = adjustSaveData(valueAtPath, path, pathTypes, configRemote);
283
- promisesAdjustData.push(doInOrder(promiseAdjustRemote, ({ path: pathAdjusted, value: valueAdjusted }) => {
303
+ const promiseTransformRemote = transformOutData(valueAtPath, path, pathTypes, configRemote || {});
304
+ promisesTransform.push(doInOrder(promiseTransformRemote, ({ path: pathTransformed, value: valueTransformed }) => {
284
305
  // If path includes undefined there was a null in fieldTransforms so don't need to save it
285
- if (!pathAdjusted.includes(undefined)) {
306
+ if (!pathTransformed.includes(undefined)) {
286
307
  // Prepare pending changes
287
308
  if (!localState.pendingChanges) {
288
309
  localState.pendingChanges = {};
@@ -292,15 +313,15 @@ async function prepChange(queuedChange) {
292
313
  if (!localState.pendingChanges[pathStr]) {
293
314
  localState.pendingChanges[pathStr] = { p: prevAtPath !== null && prevAtPath !== void 0 ? prevAtPath : null, t: pathTypes };
294
315
  }
295
- // Pending value is the unadjusted value because it gets loaded without adjustment
296
- // and forwarded through to onObsChange where it gets adjusted before save
316
+ // Pending value is the untransformed value because it gets loaded without transformment
317
+ // and forwarded through to onObsChange where it gets transformed before save
297
318
  localState.pendingChanges[pathStr].v = valueAtPath;
298
- // Prepare the remote change with the adjusted path/value
319
+ // Prepare the remote change with the transformed path/value
299
320
  changesRemote.push({
300
- path: pathAdjusted,
321
+ path: pathTransformed,
301
322
  pathTypes,
302
323
  prevAtPath,
303
- valueAtPath: valueAdjusted,
324
+ valueAtPath: valueTransformed,
304
325
  pathStr,
305
326
  });
306
327
  }
@@ -308,27 +329,28 @@ async function prepChange(queuedChange) {
308
329
  }
309
330
  }
310
331
  }
311
- // If there's any adjustData promises, wait for them before saving
312
- promisesAdjustData = promisesAdjustData.filter(Boolean);
313
- if (promisesAdjustData.length > 0) {
314
- await Promise.all(promisesAdjustData);
332
+ // If there's any transform promises, wait for them before saving
333
+ promisesTransform = promisesTransform.filter(Boolean);
334
+ if (promisesTransform.length > 0) {
335
+ await Promise.all(promisesTransform);
315
336
  }
316
337
  return { queuedChange, changesLocal, changesRemote };
317
338
  }
318
339
  }
319
340
  async function doChange(changeInfo) {
320
- var _a, _b;
341
+ var _a, _b, _c;
321
342
  if (!changeInfo)
322
343
  return;
323
344
  const { queuedChange, changesLocal, changesRemote } = changeInfo;
324
- const { obs, obsState, localState, persistOptions } = queuedChange;
345
+ const { obs, syncState, localState, persistOptions } = queuedChange;
325
346
  const { persistenceLocal, persistenceRemote } = localState;
326
347
  const local = persistOptions.local;
327
348
  const { table, config: configLocal } = parseLocalConfig(local);
328
349
  const configRemote = persistOptions.remote;
329
- if (changesRemote.length > 0) {
350
+ const shouldSaveMetadata = local && (configRemote === null || configRemote === void 0 ? void 0 : configRemote.offlineBehavior) === 'retry';
351
+ if (changesRemote.length > 0 && shouldSaveMetadata) {
330
352
  // First save pending changes before saving local or remote
331
- await updateMetadataImmediate(obs, localState, obsState, persistOptions, {
353
+ await updateMetadataImmediate(obs, localState, syncState, persistOptions, {
332
354
  pending: localState.pendingChanges,
333
355
  });
334
356
  }
@@ -348,74 +370,67 @@ async function doChange(changeInfo) {
348
370
  }
349
371
  if (changesRemote.length > 0) {
350
372
  // Wait for remote to be ready before saving
351
- await state.when(() => obsState.isLoadedRemote.get() || (configRemote.allowSaveIfError && obsState.remoteError.get()));
352
- const saves = await Promise.all(changesRemote.map(async (change) => {
353
- const { path, valueAtPath, prevAtPath, pathTypes, pathStr } = change;
354
- // Save to remote persistence
355
- return persistenceRemote
356
- .save({
357
- obs,
358
- state: obsState,
359
- options: persistOptions,
360
- path: path,
361
- pathTypes,
362
- valueAtPath,
363
- prevAtPath,
364
- })
365
- .then(({ changes, dateModified }) => ({ changes, dateModified, pathStr }));
366
- }));
373
+ await state.when(() => syncState.isLoadedRemote.get() || ((configRemote === null || configRemote === void 0 ? void 0 : configRemote.allowSetIfError) && syncState.remoteError.get()));
374
+ const value = obs.peek();
375
+ (_a = configRemote === null || configRemote === void 0 ? void 0 : configRemote.onBeforeSet) === null || _a === void 0 ? void 0 : _a.call(configRemote);
376
+ const saved = await persistenceRemote.set({
377
+ obs,
378
+ syncState: syncState,
379
+ options: persistOptions,
380
+ changes: changesRemote,
381
+ value,
382
+ }).catch((err) => { var _a; return (_a = configRemote === null || configRemote === void 0 ? void 0 : configRemote.onSetError) === null || _a === void 0 ? void 0 : _a.call(configRemote, err); });
367
383
  // If this remote save changed anything then update persistence and metadata
368
384
  // Because save happens after a timeout and they're batched together, some calls to save will
369
385
  // return saved data and others won't, so those can be ignored.
370
- if (saves.filter(Boolean).length > 0) {
371
- if (local) {
372
- const metadata = {};
373
- const pending = (_a = persistenceLocal.getMetadata(table, configLocal)) === null || _a === void 0 ? void 0 : _a.pending;
374
- let adjustedChanges = [];
375
- for (let i = 0; i < saves.length; i++) {
376
- const save = saves[i];
377
- if (save) {
378
- const { changes, dateModified, pathStr } = save;
386
+ if (saved) {
387
+ const pathStrs = Array.from(new Set(changesRemote.map((change) => change.pathStr)));
388
+ const { changes, dateModified } = saved;
389
+ if (pathStrs.length > 0) {
390
+ if (local) {
391
+ const metadata = {};
392
+ const pending = (_b = persistenceLocal.getMetadata(table, configLocal)) === null || _b === void 0 ? void 0 : _b.pending;
393
+ let transformedChanges = [];
394
+ for (let i = 0; i < pathStrs.length; i++) {
395
+ const pathStr = pathStrs[i];
379
396
  // Clear pending for this path
380
397
  if (pending === null || pending === void 0 ? void 0 : pending[pathStr]) {
381
- // Remove pending from the saved object
382
- delete pending[pathStr];
383
398
  // Remove pending from local state
384
- delete localState.pendingChanges[pathStr];
399
+ delete pending[pathStr];
385
400
  metadata.pending = pending;
386
401
  }
387
- if (dateModified) {
388
- metadata.modified = dateModified;
389
- }
390
- // Remote can optionally have data that needs to be merged back into the observable,
391
- // for example Firebase may update dateModified with the server timestamp
392
- if (changes && !state.isEmpty(changes)) {
393
- adjustedChanges.push(adjustLoadData(changes, persistOptions.remote, false));
402
+ }
403
+ if (dateModified) {
404
+ metadata.modified = dateModified;
405
+ }
406
+ // Remote can optionally have data that needs to be merged back into the observable,
407
+ // for example Firebase may update dateModified with the server timestamp
408
+ if (changes && !state.isEmpty(changes)) {
409
+ transformedChanges.push(transformLoadData(changes, persistOptions.remote, false));
410
+ }
411
+ if (transformedChanges.length > 0) {
412
+ if (transformedChanges.some((change) => state.isPromise(change))) {
413
+ transformedChanges = await Promise.all(transformedChanges);
394
414
  }
415
+ onChangeRemote(() => state.mergeIntoObservable(obs, ...transformedChanges));
395
416
  }
396
- }
397
- if (adjustedChanges.length > 0) {
398
- if (adjustedChanges.some((change) => state.isPromise(change))) {
399
- adjustedChanges = await Promise.all(adjustedChanges);
417
+ if (shouldSaveMetadata && !state.isEmpty(metadata)) {
418
+ updateMetadata(obs, localState, syncState, persistOptions, metadata);
400
419
  }
401
- onChangeRemote(() => state.mergeIntoObservable(obs, ...adjustedChanges));
402
- }
403
- if (local && !state.isEmpty(metadata)) {
404
- updateMetadata(obs, localState, obsState, persistOptions, metadata);
405
420
  }
421
+ (_c = configRemote === null || configRemote === void 0 ? void 0 : configRemote.onSet) === null || _c === void 0 ? void 0 : _c.call(configRemote);
406
422
  }
407
- (_b = localState.onSaveRemote) === null || _b === void 0 ? void 0 : _b.call(localState);
408
423
  }
409
424
  }
410
425
  }
411
- function onObsChange(obs, obsState, localState, persistOptions, { changes }) {
426
+ function onObsChange(obs, syncState, localState, persistOptions, { changes }) {
412
427
  if (!state.internal.globalState.isLoadingLocal) {
413
428
  const inRemoteChange = state.internal.globalState.isLoadingRemote;
414
429
  const isApplyingPending = localState.isApplyingPending;
415
430
  // Queue changes in a microtask so that multiple changes within a frame get run together
416
431
  _queuedChanges.push({
417
432
  obs: obs,
418
- obsState,
433
+ syncState,
419
434
  localState,
420
435
  persistOptions,
421
436
  changes,
@@ -438,10 +453,10 @@ function onChangeRemote(cb) {
438
453
  });
439
454
  });
440
455
  }
441
- async function loadLocal(obs, persistOptions, obsState, localState) {
442
- var _a, _b;
456
+ async function loadLocal(obs, persistOptions, syncState, localState) {
457
+ var _a;
443
458
  const { local } = persistOptions;
444
- const localPersistence = persistOptions.persistLocal || observablePersistConfiguration.persistLocal;
459
+ const localPersistence = persistOptions.pluginLocal || observablePersistConfiguration.pluginLocal;
445
460
  if (local) {
446
461
  const { table, config } = parseLocalConfig(local);
447
462
  if (!localPersistence) {
@@ -453,7 +468,7 @@ async function loadLocal(obs, persistOptions, obsState, localState) {
453
468
  const mapValue = { persist: persistenceLocal, initialized: state.observable(false) };
454
469
  mapPersistences.set(localPersistence, mapValue);
455
470
  if (persistenceLocal.initialize) {
456
- const initializePromise = (_a = persistenceLocal.initialize) === null || _a === void 0 ? void 0 : _a.call(persistenceLocal, observablePersistConfiguration.persistLocalOptions);
471
+ const initializePromise = (_a = persistenceLocal.initialize) === null || _a === void 0 ? void 0 : _a.call(persistenceLocal, observablePersistConfiguration.localOptions || {});
457
472
  if (state.isPromise(initializePromise)) {
458
473
  await initializePromise;
459
474
  }
@@ -478,20 +493,12 @@ async function loadLocal(obs, persistOptions, obsState, localState) {
478
493
  if (metadata) {
479
494
  metadatas.set(obs, metadata);
480
495
  localState.pendingChanges = metadata.pending;
481
- obsState.dateModified.set(metadata.modified);
496
+ syncState.dateModified.set(metadata.modified);
482
497
  }
483
498
  // Merge the data from local persistence into the default state
484
499
  if (value !== null && value !== undefined) {
485
- // eslint-disable-next-line prefer-const
486
- let { adjustData, fieldTransforms } = config;
487
- if (fieldTransforms) {
488
- const valueLoaded = (_b = persistenceLocal.getTableTransformed) === null || _b === void 0 ? void 0 : _b.call(persistenceLocal, table, config);
489
- if (valueLoaded) {
490
- value = valueLoaded;
491
- fieldTransforms = undefined;
492
- }
493
- }
494
- value = adjustLoadData(value, { adjustData, fieldTransforms }, true);
500
+ const { transform, fieldTransforms } = config;
501
+ value = transformLoadData(value, { transform, fieldTransforms }, true);
495
502
  if (state.isPromise(value)) {
496
503
  value = await value;
497
504
  }
@@ -505,18 +512,26 @@ async function loadLocal(obs, persistOptions, obsState, localState) {
505
512
  state.internal.globalState.isLoadingLocal = false;
506
513
  });
507
514
  }
508
- obsState.peek().clearLocal = () => Promise.all([
515
+ syncState.peek().clearLocal = () => Promise.all([
509
516
  persistenceLocal.deleteTable(table, config),
510
517
  persistenceLocal.deleteMetadata(table, config),
511
518
  ]);
512
519
  }
513
- obsState.isLoadedLocal.set(true);
520
+ syncState.isLoadedLocal.set(true);
514
521
  }
515
- function persistObservable(obs, persistOptions) {
516
- const { remote, local } = persistOptions;
517
- const remotePersistence = persistOptions.persistRemote || (observablePersistConfiguration === null || observablePersistConfiguration === void 0 ? void 0 : observablePersistConfiguration.persistRemote);
522
+ function persistObservable(initialOrObservable, persistOptions) {
523
+ const obs = (state.isObservable(initialOrObservable)
524
+ ? initialOrObservable
525
+ : state.observable(state.isFunction(initialOrObservable) ? initialOrObservable() : initialOrObservable));
526
+ // Merge remote persist options with clobal options
527
+ if (persistOptions.remote) {
528
+ persistOptions.remote = Object.assign({}, observablePersistConfiguration.remoteOptions, persistOptions.remote);
529
+ }
530
+ let { remote } = persistOptions;
531
+ const { local } = persistOptions;
532
+ const remotePersistence = persistOptions.pluginRemote || (observablePersistConfiguration === null || observablePersistConfiguration === void 0 ? void 0 : observablePersistConfiguration.pluginRemote);
518
533
  const localState = {};
519
- const obsState = state.observable({
534
+ const syncState = state.observable({
520
535
  isLoadedLocal: false,
521
536
  isLoadedRemote: false,
522
537
  isEnabledLocal: true,
@@ -526,37 +541,47 @@ function persistObservable(obs, persistOptions) {
526
541
  getPendingChanges: () => localState.pendingChanges,
527
542
  });
528
543
  if (local) {
529
- loadLocal(obs, persistOptions, obsState, localState);
544
+ loadLocal(obs, persistOptions, syncState, localState);
530
545
  }
531
- if (remote) {
546
+ if (remote || remotePersistence) {
532
547
  if (!remotePersistence) {
533
548
  throw new Error('Remote persistence is not configured');
534
549
  }
535
- // Ensure there's only one instance of the persistence plugin
536
- if (!mapPersistences.has(remotePersistence)) {
537
- mapPersistences.set(remotePersistence, { persist: new remotePersistence() });
550
+ if (!remote) {
551
+ remote = {};
552
+ }
553
+ if (state.isObject(remotePersistence)) {
554
+ localState.persistenceRemote = observablePersistRemoteFunctionsAdapter(remotePersistence);
555
+ }
556
+ else {
557
+ // Ensure there's only one instance of the persistence plugin
558
+ if (!mapPersistences.has(remotePersistence)) {
559
+ mapPersistences.set(remotePersistence, {
560
+ persist: new remotePersistence(),
561
+ });
562
+ }
563
+ localState.persistenceRemote = mapPersistences.get(remotePersistence)
564
+ .persist;
538
565
  }
539
- localState.persistenceRemote = mapPersistences.get(remotePersistence).persist;
540
566
  let isSynced = false;
541
567
  const sync = async () => {
542
- var _a, _b;
568
+ var _a;
543
569
  if (!isSynced) {
544
570
  isSynced = true;
545
- localState.onSaveRemote = (_a = persistOptions.remote) === null || _a === void 0 ? void 0 : _a.onSaveRemote;
546
- const dateModified = (_b = metadatas.get(obs)) === null || _b === void 0 ? void 0 : _b.modified;
547
- localState.persistenceRemote.listen({
548
- state: obsState,
571
+ const dateModified = (_a = metadatas.get(obs)) === null || _a === void 0 ? void 0 : _a.modified;
572
+ localState.persistenceRemote.get({
573
+ state: syncState,
549
574
  obs,
550
575
  options: persistOptions,
551
576
  dateModified,
552
- onLoad: () => {
553
- obsState.isLoadedRemote.set(true);
577
+ onGet: () => {
578
+ syncState.isLoadedRemote.set(true);
554
579
  },
555
- onChange: async ({ value, path, pathTypes, mode, dateModified }) => {
580
+ onChange: async ({ value, path = [], pathTypes = [], mode = 'set', dateModified }) => {
556
581
  // Note: value is the constructed value, path is used for setInObservableAtPath
557
582
  // to start the set into the observable from the path
558
583
  if (value !== undefined) {
559
- value = adjustLoadData(value, remote, true);
584
+ value = transformLoadData(value, remote, true);
560
585
  if (state.isPromise(value)) {
561
586
  value = await value;
562
587
  }
@@ -577,14 +602,16 @@ function persistObservable(obs, persistOptions) {
577
602
  Object.keys(pending).forEach((key) => {
578
603
  const p = key.split('/').filter((p) => p !== '');
579
604
  const { v, t } = pending[key];
580
- value = state.setAtPath(value, p, t, v, obs.peek(), (path, value) => {
581
- delete pending[key];
582
- pending[path.join('/')] = {
583
- p: null,
584
- v: value,
585
- t: t.slice(0, path.length),
586
- };
587
- });
605
+ if (value[p[0]] !== undefined) {
606
+ value = state.setAtPath(value, p, t, v, obs.peek(), (path, value) => {
607
+ delete pending[key];
608
+ pending[path.join('/')] = {
609
+ p: null,
610
+ v: value,
611
+ t: t.slice(0, path.length),
612
+ };
613
+ });
614
+ }
588
615
  });
589
616
  }
590
617
  onChangeRemote(() => {
@@ -593,14 +620,14 @@ function persistObservable(obs, persistOptions) {
593
620
  }
594
621
  }
595
622
  if (dateModified && local) {
596
- updateMetadata(obs, localState, obsState, persistOptions, {
623
+ updateMetadata(obs, localState, syncState, persistOptions, {
597
624
  modified: dateModified,
598
625
  });
599
626
  }
600
627
  },
601
628
  });
602
629
  // Wait for remote to be ready before saving pending
603
- await state.when(() => obsState.isLoadedRemote.get() || (remote.allowSaveIfError && obsState.remoteError.get()));
630
+ await state.when(() => syncState.isLoadedRemote.get() || (remote.allowSetIfError && syncState.remoteError.get()));
604
631
  const pending = localState.pendingChanges;
605
632
  if (pending && !state.isEmpty(pending)) {
606
633
  localState.isApplyingPending = true;
@@ -614,7 +641,7 @@ function persistObservable(obs, persistOptions) {
614
641
  changes.push({ path, valueAtPath: v, prevAtPath: p, pathTypes: t });
615
642
  }
616
643
  // Send the changes into onObsChange so that they get persisted remotely
617
- onObsChange(obs, obsState, localState, persistOptions, {
644
+ onObsChange(obs, syncState, localState, persistOptions, {
618
645
  value: obs.peek(),
619
646
  // TODO getPrevious if any remote persistence layers need it
620
647
  getPrevious: () => undefined,
@@ -625,16 +652,16 @@ function persistObservable(obs, persistOptions) {
625
652
  }
626
653
  };
627
654
  if (remote.manual) {
628
- obsState.assign({ sync });
655
+ syncState.assign({ sync });
629
656
  }
630
657
  else {
631
- state.when(() => !local || obsState.isLoadedLocal.get(), sync);
658
+ state.when(() => !local || syncState.isLoadedLocal.get(), sync);
632
659
  }
633
660
  }
634
- state.when(obsState.isLoadedLocal, function () {
635
- obs.onChange(onObsChange.bind(this, obs, obsState, localState, persistOptions));
661
+ state.when(!local || syncState.isLoadedLocal, function () {
662
+ obs.onChange(onObsChange.bind(this, obs, syncState, localState, persistOptions));
636
663
  });
637
- return obsState;
664
+ return [obs, syncState];
638
665
  }
639
666
 
640
667
  function isInRemoteChange() {