@legendapp/state 2.2.0-next.4 → 2.2.0-next.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.
- package/babel.js.map +1 -1
- package/config/enableDirectAccess.d.ts +1 -1
- package/config/enableDirectPeek.d.ts +1 -1
- package/config/enableReactDirectRender.js.map +1 -1
- package/config/enableReactDirectRender.mjs.map +1 -1
- package/config/enableReactTracking.d.ts +4 -3
- package/config/enableReactTracking.js.map +1 -1
- package/config/enableReactTracking.mjs.map +1 -1
- package/config/enableReactUse.d.ts +1 -1
- package/helpers/fetch.d.ts +4 -3
- package/helpers/fetch.js.map +1 -1
- package/helpers/fetch.mjs.map +1 -1
- package/helpers/pageHash.js.map +1 -1
- package/helpers/pageHash.mjs.map +1 -1
- package/helpers/pageHashParams.js.map +1 -1
- package/helpers/pageHashParams.mjs.map +1 -1
- package/helpers/time.d.ts +2 -2
- package/helpers/time.js.map +1 -1
- package/helpers/time.mjs.map +1 -1
- package/history.js.map +1 -1
- package/history.mjs.map +1 -1
- package/index.d.ts +13 -4
- package/index.js +655 -448
- package/index.js.map +1 -1
- package/index.mjs +652 -447
- package/index.mjs.map +1 -1
- package/package.json +1 -12
- package/persist-plugins/async-storage.js.map +1 -1
- package/persist-plugins/async-storage.mjs.map +1 -1
- package/persist-plugins/fetch.js.map +1 -1
- package/persist-plugins/fetch.mjs.map +1 -1
- package/persist-plugins/firebase.js.map +1 -1
- package/persist-plugins/firebase.mjs.map +1 -1
- package/persist-plugins/indexeddb.js.map +1 -1
- package/persist-plugins/indexeddb.mjs.map +1 -1
- package/persist-plugins/local-storage.js +10 -2
- package/persist-plugins/local-storage.js.map +1 -1
- package/persist-plugins/local-storage.mjs +10 -2
- package/persist-plugins/local-storage.mjs.map +1 -1
- package/persist-plugins/mmkv.js.map +1 -1
- package/persist-plugins/mmkv.mjs.map +1 -1
- package/persist-plugins/query.js.map +1 -1
- package/persist-plugins/query.mjs.map +1 -1
- package/persist.d.ts +15 -1
- package/persist.js +412 -180
- package/persist.js.map +1 -1
- package/persist.mjs +413 -181
- package/persist.mjs.map +1 -1
- package/react-hooks/createObservableHook.js +1 -1
- package/react-hooks/createObservableHook.js.map +1 -1
- package/react-hooks/createObservableHook.mjs +1 -1
- package/react-hooks/createObservableHook.mjs.map +1 -1
- package/react-hooks/useFetch.d.ts +4 -3
- package/react-hooks/useFetch.js.map +1 -1
- package/react-hooks/useFetch.mjs.map +1 -1
- package/react-hooks/useHover.js.map +1 -1
- package/react-hooks/useHover.mjs.map +1 -1
- package/react-hooks/useMeasure.js.map +1 -1
- package/react-hooks/useMeasure.mjs.map +1 -1
- package/react-hooks/useObservableNextRouter.js.map +1 -1
- package/react-hooks/useObservableNextRouter.mjs.map +1 -1
- package/react-hooks/useObservableQuery.js.map +1 -1
- package/react-hooks/useObservableQuery.mjs.map +1 -1
- package/react-hooks/usePersistedObservable.d.ts +2 -2
- package/react-hooks/usePersistedObservable.js +4 -3
- package/react-hooks/usePersistedObservable.js.map +1 -1
- package/react-hooks/usePersistedObservable.mjs +4 -3
- package/react-hooks/usePersistedObservable.mjs.map +1 -1
- package/react.js +13 -8
- package/react.js.map +1 -1
- package/react.mjs +14 -9
- package/react.mjs.map +1 -1
- package/src/ObservableObject.d.ts +6 -4
- package/src/ObservablePrimitive.d.ts +2 -1
- package/src/activated.d.ts +3 -0
- package/src/batching.d.ts +3 -1
- package/src/computed.d.ts +3 -3
- package/src/config/enableDirectAccess.d.ts +1 -1
- package/src/config/enableDirectPeek.d.ts +1 -1
- package/src/config/enableReactTracking.d.ts +4 -3
- package/src/config/enableReactUse.d.ts +1 -1
- package/src/createObservable.d.ts +2 -2
- package/src/globals.d.ts +10 -8
- package/src/helpers/fetch.d.ts +4 -3
- package/src/helpers/time.d.ts +2 -2
- package/src/helpers.d.ts +3 -3
- package/src/history/trackHistory.d.ts +1 -1
- package/src/is.d.ts +2 -0
- package/src/observable.d.ts +7 -15
- package/src/observableInterfaces.d.ts +56 -348
- package/src/observableTypes.d.ts +85 -0
- package/src/persist/observablePersistRemoteFunctionsAdapter.d.ts +1 -1
- package/src/persist/persistActivateNode.d.ts +0 -17
- package/src/persist/persistHelpers.d.ts +1 -1
- package/src/persist/persistObservable.d.ts +2 -3
- package/src/persistTypes.d.ts +196 -0
- package/src/proxy.d.ts +5 -5
- package/src/react/Computed.d.ts +1 -1
- package/src/react/Switch.d.ts +3 -3
- package/src/react/reactInterfaces.d.ts +2 -1
- package/src/react/useComputed.d.ts +5 -5
- package/src/react/usePauseProvider.d.ts +3 -3
- package/src/react/useWhen.d.ts +2 -2
- package/src/react-hooks/useFetch.d.ts +4 -3
- package/src/react-hooks/usePersistedObservable.d.ts +2 -2
- package/src/retry.d.ts +6 -0
- package/src/trackSelector.d.ts +3 -2
- package/src/when.d.ts +6 -2
- package/trace.js.map +1 -1
- package/trace.mjs.map +1 -1
package/persist.js
CHANGED
|
@@ -139,9 +139,21 @@ function observablePersistRemoteFunctionsAdapter({ get, set, }) {
|
|
|
139
139
|
const ret = {};
|
|
140
140
|
if (get) {
|
|
141
141
|
ret.get = (async (params) => {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
142
|
+
try {
|
|
143
|
+
let value = get(params);
|
|
144
|
+
if (state.isPromise(value)) {
|
|
145
|
+
value = await value;
|
|
146
|
+
}
|
|
147
|
+
params.onChange({
|
|
148
|
+
value,
|
|
149
|
+
dateModified: params.dateModified,
|
|
150
|
+
lastSync: params.lastSync,
|
|
151
|
+
mode: params.mode,
|
|
152
|
+
});
|
|
153
|
+
params.onGet();
|
|
154
|
+
// eslint-disable-next-line no-empty
|
|
155
|
+
}
|
|
156
|
+
catch (_a) { }
|
|
145
157
|
});
|
|
146
158
|
}
|
|
147
159
|
if (set) {
|
|
@@ -150,6 +162,21 @@ function observablePersistRemoteFunctionsAdapter({ get, set, }) {
|
|
|
150
162
|
return ret;
|
|
151
163
|
}
|
|
152
164
|
|
|
165
|
+
function removeNullUndefined(val) {
|
|
166
|
+
if (val) {
|
|
167
|
+
Object.keys(val).forEach((key) => {
|
|
168
|
+
const v = val[key];
|
|
169
|
+
if (v === null || v === undefined) {
|
|
170
|
+
delete val[key];
|
|
171
|
+
}
|
|
172
|
+
else if (state.isObject(v)) {
|
|
173
|
+
removeNullUndefined(v);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
return val;
|
|
178
|
+
}
|
|
179
|
+
|
|
153
180
|
const { globalState: globalState$1 } = state.internal;
|
|
154
181
|
const mapPersistences = new WeakMap();
|
|
155
182
|
const metadatas = new WeakMap();
|
|
@@ -166,6 +193,7 @@ function doInOrder(arg1, arg2) {
|
|
|
166
193
|
}
|
|
167
194
|
function onChangeRemote(cb) {
|
|
168
195
|
state.when(() => !globalState$1.isLoadingRemote$.get(), () => {
|
|
196
|
+
state.endBatch(true);
|
|
169
197
|
// Remote changes should only update local state
|
|
170
198
|
globalState$1.isLoadingRemote$.set(true);
|
|
171
199
|
state.batch(cb, () => {
|
|
@@ -216,16 +244,19 @@ async function updateMetadataImmediate(obs, localState, syncState, persistOption
|
|
|
216
244
|
const { table, config } = parseLocalConfig(local);
|
|
217
245
|
// Save metadata
|
|
218
246
|
const oldMetadata = metadatas.get(obs);
|
|
219
|
-
const {
|
|
220
|
-
const needsUpdate = pending || (
|
|
247
|
+
const { lastSync, pending } = newMetadata;
|
|
248
|
+
const needsUpdate = pending || (lastSync && (!oldMetadata || lastSync !== oldMetadata.lastSync));
|
|
221
249
|
if (needsUpdate) {
|
|
222
250
|
const metadata = Object.assign({}, oldMetadata, newMetadata);
|
|
223
251
|
metadatas.set(obs, metadata);
|
|
224
252
|
if (persistenceLocal) {
|
|
225
253
|
await persistenceLocal.setMetadata(table, metadata, config);
|
|
226
254
|
}
|
|
227
|
-
if (
|
|
228
|
-
syncState.
|
|
255
|
+
if (lastSync) {
|
|
256
|
+
syncState.assign({
|
|
257
|
+
lastSync: lastSync,
|
|
258
|
+
dateModified: lastSync,
|
|
259
|
+
});
|
|
229
260
|
}
|
|
230
261
|
}
|
|
231
262
|
}
|
|
@@ -237,15 +268,54 @@ function updateMetadata(obs, localState, syncState, persistOptions, newMetadata)
|
|
|
237
268
|
localState.timeoutSaveMetadata = setTimeout(() => updateMetadataImmediate(obs, localState, syncState, persistOptions, newMetadata), ((_a = persistOptions === null || persistOptions === void 0 ? void 0 : persistOptions.remote) === null || _a === void 0 ? void 0 : _a.metadataTimeout) || 0);
|
|
238
269
|
}
|
|
239
270
|
let _queuedChanges = [];
|
|
271
|
+
let _queuedRemoteChanges = [];
|
|
272
|
+
let timeoutSaveRemote = undefined;
|
|
273
|
+
function mergeChanges(changes) {
|
|
274
|
+
const changesByPath = new Map();
|
|
275
|
+
const changesOut = [];
|
|
276
|
+
// TODO: This could be even more robust by going deeper into paths like the firebase plugin's _updatePendingSave
|
|
277
|
+
for (let i = 0; i < changes.length; i++) {
|
|
278
|
+
const change = changes[i];
|
|
279
|
+
const pathStr = change.path.join('/');
|
|
280
|
+
const existing = changesByPath.get(pathStr);
|
|
281
|
+
if (existing) {
|
|
282
|
+
existing.valueAtPath = change.valueAtPath;
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
changesByPath.set(pathStr, change);
|
|
286
|
+
changesOut.push(change);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return changesOut;
|
|
290
|
+
}
|
|
291
|
+
function mergeQueuedChanges(allChanges) {
|
|
292
|
+
const changesByObsRemote = new Map();
|
|
293
|
+
const changesByObsLocal = new Map();
|
|
294
|
+
const outRemote = new Map();
|
|
295
|
+
const outLocal = new Map();
|
|
296
|
+
for (let i = 0; i < allChanges.length; i++) {
|
|
297
|
+
const value = allChanges[i];
|
|
298
|
+
const { obs, changes, inRemoteChange } = value;
|
|
299
|
+
const changesMap = inRemoteChange ? changesByObsRemote : changesByObsLocal;
|
|
300
|
+
const existing = changesMap.get(obs);
|
|
301
|
+
const newChanges = existing ? [...existing, ...changes] : changes;
|
|
302
|
+
const merged = mergeChanges(newChanges);
|
|
303
|
+
changesMap.set(obs, merged);
|
|
304
|
+
value.changes = merged;
|
|
305
|
+
(inRemoteChange ? outRemote : outLocal).set(obs, value);
|
|
306
|
+
}
|
|
307
|
+
return Array.from(outRemote.values()).concat(Array.from(outLocal.values()));
|
|
308
|
+
}
|
|
240
309
|
async function processQueuedChanges() {
|
|
310
|
+
var _a;
|
|
241
311
|
// Get a local copy of the queued changes and clear the global queue
|
|
242
|
-
const queuedChanges = _queuedChanges;
|
|
312
|
+
const queuedChanges = mergeQueuedChanges(_queuedChanges);
|
|
243
313
|
_queuedChanges = [];
|
|
314
|
+
_queuedRemoteChanges.push(...queuedChanges.filter((c) => !c.inRemoteChange));
|
|
244
315
|
// Note: Summary of the order of operations these functions:
|
|
245
316
|
// 1. Prepare all changes for saving. This may involve waiting for promises if the user has asynchronous transform.
|
|
246
317
|
// We need to prepare all of the changes in the queue before saving so that the saves happen in the correct order,
|
|
247
318
|
// since some may take longer to transformSaveData than others.
|
|
248
|
-
const changes = await Promise.all(queuedChanges.map(prepChange));
|
|
249
319
|
// 2. Save pending to the metadata table first. If this is the only operation that succeeds, it would try to save
|
|
250
320
|
// the current value again on next load, which isn't too bad.
|
|
251
321
|
// 3. Save local changes to storage. If they never make it to remote, then on the next load they will be pending
|
|
@@ -253,25 +323,46 @@ async function processQueuedChanges() {
|
|
|
253
323
|
// 4. Wait for remote load or error if allowed
|
|
254
324
|
// 5. Save to remote
|
|
255
325
|
// 6. On successful save, merge changes (if any) back into observable
|
|
256
|
-
// 7. Lastly, update metadata to clear pending and update
|
|
326
|
+
// 7. Lastly, update metadata to clear pending and update lastSync. Doing this earlier could potentially cause
|
|
257
327
|
// sync inconsistences so it's very important that this is last.
|
|
258
|
-
|
|
328
|
+
const preppedChangesLocal = await Promise.all(queuedChanges.map(prepChangeLocal));
|
|
329
|
+
// TODO Clean this up: We only need to prep this now in ordre to save pending changes, don't need any of the other stuff. Should split that up?
|
|
330
|
+
await Promise.all(queuedChanges.map(prepChangeRemote));
|
|
331
|
+
await Promise.all(preppedChangesLocal.map(doChangeLocal));
|
|
332
|
+
const timeout = (_a = observablePersistConfiguration === null || observablePersistConfiguration === void 0 ? void 0 : observablePersistConfiguration.remoteOptions) === null || _a === void 0 ? void 0 : _a.saveTimeout;
|
|
333
|
+
if (timeout) {
|
|
334
|
+
if (timeoutSaveRemote) {
|
|
335
|
+
clearTimeout(timeoutSaveRemote);
|
|
336
|
+
}
|
|
337
|
+
timeoutSaveRemote = setTimeout(processQueuedRemoteChanges, timeout);
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
processQueuedRemoteChanges();
|
|
341
|
+
}
|
|
259
342
|
}
|
|
260
|
-
async function
|
|
343
|
+
async function processQueuedRemoteChanges() {
|
|
344
|
+
const queuedRemoteChanges = mergeQueuedChanges(_queuedRemoteChanges);
|
|
345
|
+
_queuedRemoteChanges = [];
|
|
346
|
+
const preppedChangesRemote = await Promise.all(queuedRemoteChanges.map(prepChangeRemote));
|
|
347
|
+
preppedChangesRemote.forEach(doChangeRemote);
|
|
348
|
+
}
|
|
349
|
+
async function prepChangeLocal(queuedChange) {
|
|
261
350
|
const { syncState, changes, localState, persistOptions, inRemoteChange, isApplyingPending } = queuedChange;
|
|
262
351
|
const local = persistOptions.local;
|
|
263
352
|
const { persistenceRemote } = localState;
|
|
264
353
|
const { config: configLocal } = parseLocalConfig(local);
|
|
265
354
|
const configRemote = persistOptions.remote;
|
|
266
355
|
const saveLocal = local && !configLocal.readonly && !isApplyingPending && syncState.isEnabledLocal.peek();
|
|
267
|
-
const saveRemote = !inRemoteChange &&
|
|
356
|
+
const saveRemote = !!(!inRemoteChange &&
|
|
357
|
+
(persistenceRemote === null || persistenceRemote === void 0 ? void 0 : persistenceRemote.set) &&
|
|
358
|
+
!(configRemote === null || configRemote === void 0 ? void 0 : configRemote.readonly) &&
|
|
359
|
+
syncState.isEnabledRemote.peek());
|
|
268
360
|
if (saveLocal || saveRemote) {
|
|
269
361
|
if (saveLocal && !syncState.isLoadedLocal.peek()) {
|
|
270
362
|
console.error('[legend-state] WARNING: An observable was changed before being loaded from persistence', local);
|
|
271
|
-
return;
|
|
363
|
+
return undefined;
|
|
272
364
|
}
|
|
273
365
|
const changesLocal = [];
|
|
274
|
-
const changesRemote = [];
|
|
275
366
|
const changesPaths = new Set();
|
|
276
367
|
let promisesTransform = [];
|
|
277
368
|
// Reverse order
|
|
@@ -310,6 +401,52 @@ async function prepChange(queuedChange) {
|
|
|
310
401
|
}
|
|
311
402
|
}));
|
|
312
403
|
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
// If there's any transform promises, wait for them before saving
|
|
407
|
+
promisesTransform = promisesTransform.filter(Boolean);
|
|
408
|
+
if (promisesTransform.length > 0) {
|
|
409
|
+
await Promise.all(promisesTransform);
|
|
410
|
+
}
|
|
411
|
+
return { queuedChange, changesLocal, saveRemote };
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
async function prepChangeRemote(queuedChange) {
|
|
415
|
+
const { syncState, changes, localState, persistOptions, inRemoteChange, isApplyingPending } = queuedChange;
|
|
416
|
+
const local = persistOptions.local;
|
|
417
|
+
const { persistenceRemote } = localState;
|
|
418
|
+
const { config: configLocal } = parseLocalConfig(local);
|
|
419
|
+
const configRemote = persistOptions.remote;
|
|
420
|
+
const saveLocal = local && !configLocal.readonly && !isApplyingPending && syncState.isEnabledLocal.peek();
|
|
421
|
+
const saveRemote = !inRemoteChange && (persistenceRemote === null || persistenceRemote === void 0 ? void 0 : persistenceRemote.set) && !(configRemote === null || configRemote === void 0 ? void 0 : configRemote.readonly) && syncState.isEnabledRemote.peek();
|
|
422
|
+
if (saveLocal || saveRemote) {
|
|
423
|
+
if (saveLocal && !syncState.isLoadedLocal.peek()) {
|
|
424
|
+
console.error('[legend-state] WARNING: An observable was changed before being loaded from persistence', local);
|
|
425
|
+
return undefined;
|
|
426
|
+
}
|
|
427
|
+
const changesRemote = [];
|
|
428
|
+
const changesPaths = new Set();
|
|
429
|
+
let promisesTransform = [];
|
|
430
|
+
// Reverse order
|
|
431
|
+
for (let i = changes.length - 1; i >= 0; i--) {
|
|
432
|
+
const { path } = changes[i];
|
|
433
|
+
let found = false;
|
|
434
|
+
// Optimization to only save the latest update at each path. We might have multiple changes at the same path
|
|
435
|
+
// and we only need the latest value, so it starts from the end of the array, skipping any earlier changes
|
|
436
|
+
// already processed. If a later change modifies a parent of an earlier change (which happens on delete()
|
|
437
|
+
// it should be ignored as it's superseded by the parent modification.
|
|
438
|
+
if (changesPaths.size > 0) {
|
|
439
|
+
for (let u = 0; u < path.length; u++) {
|
|
440
|
+
if (changesPaths.has((u === path.length - 1 ? path : path.slice(0, u + 1)).join('/'))) {
|
|
441
|
+
found = true;
|
|
442
|
+
break;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
if (!found) {
|
|
447
|
+
const pathStr = path.join('/');
|
|
448
|
+
changesPaths.add(pathStr);
|
|
449
|
+
const { prevAtPath, valueAtPath, pathTypes } = changes[i];
|
|
313
450
|
if (saveRemote) {
|
|
314
451
|
const promiseTransformRemote = transformOutData(valueAtPath, path, pathTypes, configRemote || {});
|
|
315
452
|
promisesTransform.push(doInOrder(promiseTransformRemote, ({ path: pathTransformed, value: valueTransformed }) => {
|
|
@@ -367,21 +504,20 @@ async function prepChange(queuedChange) {
|
|
|
367
504
|
if (promisesTransform.length > 0) {
|
|
368
505
|
await Promise.all(promisesTransform);
|
|
369
506
|
}
|
|
370
|
-
return { queuedChange,
|
|
507
|
+
return { queuedChange, changesRemote };
|
|
371
508
|
}
|
|
372
509
|
}
|
|
373
|
-
async function
|
|
374
|
-
var _a, _b, _c, _d;
|
|
510
|
+
async function doChangeLocal(changeInfo) {
|
|
375
511
|
if (!changeInfo)
|
|
376
512
|
return;
|
|
377
|
-
const { queuedChange, changesLocal,
|
|
513
|
+
const { queuedChange, changesLocal, saveRemote } = changeInfo;
|
|
378
514
|
const { obs, syncState, localState, persistOptions } = queuedChange;
|
|
379
|
-
const { persistenceLocal
|
|
515
|
+
const { persistenceLocal } = localState;
|
|
380
516
|
const local = persistOptions.local;
|
|
381
517
|
const { table, config: configLocal } = parseLocalConfig(local);
|
|
382
518
|
const configRemote = persistOptions.remote;
|
|
383
519
|
const shouldSaveMetadata = local && (configRemote === null || configRemote === void 0 ? void 0 : configRemote.offlineBehavior) === 'retry';
|
|
384
|
-
if (
|
|
520
|
+
if (saveRemote && shouldSaveMetadata) {
|
|
385
521
|
// First save pending changes before saving local or remote
|
|
386
522
|
await updateMetadataImmediate(obs, localState, syncState, persistOptions, {
|
|
387
523
|
pending: localState.pendingChanges,
|
|
@@ -401,29 +537,46 @@ async function doChange(changeInfo) {
|
|
|
401
537
|
await promiseSet;
|
|
402
538
|
}
|
|
403
539
|
}
|
|
540
|
+
}
|
|
541
|
+
async function doChangeRemote(changeInfo) {
|
|
542
|
+
var _a, _b;
|
|
543
|
+
if (!changeInfo)
|
|
544
|
+
return;
|
|
545
|
+
const { queuedChange, changesRemote } = changeInfo;
|
|
546
|
+
const { obs, syncState, localState, persistOptions } = queuedChange;
|
|
547
|
+
const { persistenceLocal, persistenceRemote } = localState;
|
|
548
|
+
const local = persistOptions.local;
|
|
549
|
+
const { table, config: configLocal } = parseLocalConfig(local);
|
|
550
|
+
const { offlineBehavior, allowSetIfError, onBeforeSet, onSetError, waitForSet, onSet } = persistOptions.remote || {};
|
|
551
|
+
const shouldSaveMetadata = local && offlineBehavior === 'retry';
|
|
404
552
|
if (changesRemote.length > 0) {
|
|
405
553
|
// Wait for remote to be ready before saving
|
|
406
|
-
await state.when(() => syncState.isLoaded.get() || (
|
|
554
|
+
await state.when(() => syncState.isLoaded.get() || (allowSetIfError && syncState.error.get()));
|
|
555
|
+
if (waitForSet) {
|
|
556
|
+
await state.when(state.isFunction(waitForSet) ? waitForSet(changesRemote) : waitForSet);
|
|
557
|
+
}
|
|
407
558
|
const value = obs.peek();
|
|
408
|
-
|
|
409
|
-
|
|
559
|
+
onBeforeSet === null || onBeforeSet === void 0 ? void 0 : onBeforeSet();
|
|
560
|
+
localState.numSavesOutstanding = (localState.numSavesOutstanding || 0) + 1;
|
|
561
|
+
const saved = await ((_a = persistenceRemote.set({
|
|
410
562
|
obs,
|
|
411
563
|
syncState: syncState,
|
|
412
564
|
options: persistOptions,
|
|
413
565
|
changes: changesRemote,
|
|
414
566
|
value,
|
|
415
|
-
})) === null ||
|
|
567
|
+
})) === null || _a === void 0 ? void 0 : _a.catch((err) => onSetError === null || onSetError === void 0 ? void 0 : onSetError(err)));
|
|
568
|
+
localState.numSavesOutstanding--;
|
|
416
569
|
// If this remote save changed anything then update persistence and metadata
|
|
417
570
|
// Because save happens after a timeout and they're batched together, some calls to save will
|
|
418
571
|
// return saved data and others won't, so those can be ignored.
|
|
419
572
|
if (saved) {
|
|
420
573
|
const pathStrs = Array.from(new Set(changesRemote.map((change) => change.pathStr)));
|
|
421
|
-
const { changes,
|
|
574
|
+
const { changes, lastSync } = saved;
|
|
422
575
|
if (pathStrs.length > 0) {
|
|
423
576
|
if (local) {
|
|
424
577
|
const metadata = {};
|
|
425
|
-
const pending = (
|
|
426
|
-
let transformedChanges =
|
|
578
|
+
const pending = (_b = persistenceLocal.getMetadata(table, configLocal)) === null || _b === void 0 ? void 0 : _b.pending;
|
|
579
|
+
let transformedChanges = undefined;
|
|
427
580
|
for (let i = 0; i < pathStrs.length; i++) {
|
|
428
581
|
const pathStr = pathStrs[i];
|
|
429
582
|
// Clear pending for this path
|
|
@@ -433,25 +586,37 @@ async function doChange(changeInfo) {
|
|
|
433
586
|
metadata.pending = pending;
|
|
434
587
|
}
|
|
435
588
|
}
|
|
436
|
-
if (
|
|
437
|
-
metadata.
|
|
589
|
+
if (lastSync) {
|
|
590
|
+
metadata.lastSync = lastSync;
|
|
438
591
|
}
|
|
439
592
|
// Remote can optionally have data that needs to be merged back into the observable,
|
|
440
593
|
// for example Firebase may update dateModified with the server timestamp
|
|
441
594
|
if (changes && !state.isEmpty(changes)) {
|
|
442
|
-
transformedChanges
|
|
595
|
+
transformedChanges = transformLoadData(changes, persistOptions.remote, false);
|
|
443
596
|
}
|
|
444
|
-
if (
|
|
445
|
-
if (transformedChanges
|
|
446
|
-
|
|
597
|
+
if (localState.numSavesOutstanding > 0) {
|
|
598
|
+
if (transformedChanges) {
|
|
599
|
+
if (!localState.pendingSaveResults) {
|
|
600
|
+
localState.pendingSaveResults = [];
|
|
601
|
+
}
|
|
602
|
+
localState.pendingSaveResults.push(transformedChanges);
|
|
447
603
|
}
|
|
448
|
-
onChangeRemote(() => state.mergeIntoObservable(obs, ...transformedChanges));
|
|
449
604
|
}
|
|
450
|
-
|
|
451
|
-
|
|
605
|
+
else {
|
|
606
|
+
let allChanges = [...(localState.pendingSaveResults || []), transformedChanges];
|
|
607
|
+
if (allChanges.length > 0) {
|
|
608
|
+
if (allChanges.some((change) => state.isPromise(change))) {
|
|
609
|
+
allChanges = await Promise.all(allChanges);
|
|
610
|
+
}
|
|
611
|
+
onChangeRemote(() => state.mergeIntoObservable(obs, ...allChanges));
|
|
612
|
+
}
|
|
613
|
+
if (shouldSaveMetadata && !state.isEmpty(metadata)) {
|
|
614
|
+
updateMetadata(obs, localState, syncState, persistOptions, metadata);
|
|
615
|
+
}
|
|
616
|
+
localState.pendingSaveResults = [];
|
|
452
617
|
}
|
|
453
618
|
}
|
|
454
|
-
|
|
619
|
+
onSet === null || onSet === void 0 ? void 0 : onSet();
|
|
455
620
|
}
|
|
456
621
|
}
|
|
457
622
|
}
|
|
@@ -499,7 +664,7 @@ async function loadLocal(obs, persistOptions, syncState, localState) {
|
|
|
499
664
|
}
|
|
500
665
|
const { persist: persistenceLocal, initialized } = mapPersistences.get(localPersistence);
|
|
501
666
|
localState.persistenceLocal = persistenceLocal;
|
|
502
|
-
if (!initialized.
|
|
667
|
+
if (!initialized.peek()) {
|
|
503
668
|
await state.when(initialized);
|
|
504
669
|
}
|
|
505
670
|
// If persistence has an asynchronous load, wait for it
|
|
@@ -513,12 +678,21 @@ async function loadLocal(obs, persistOptions, syncState, localState) {
|
|
|
513
678
|
let value = persistenceLocal.getTable(table, config);
|
|
514
679
|
const metadata = persistenceLocal.getMetadata(table, config);
|
|
515
680
|
if (metadata) {
|
|
681
|
+
// @ts-expect-error Migration from old version
|
|
682
|
+
if (!metadata.lastSync && metadata.modified) {
|
|
683
|
+
// @ts-expect-error Migration from old
|
|
684
|
+
metadata.lastSync = metadata.modified;
|
|
685
|
+
}
|
|
516
686
|
metadatas.set(obs, metadata);
|
|
517
687
|
localState.pendingChanges = metadata.pending;
|
|
518
|
-
|
|
688
|
+
// TODOV3 Remove dateModified
|
|
689
|
+
syncState.assign({
|
|
690
|
+
dateModified: metadata.lastSync,
|
|
691
|
+
lastSync: metadata.lastSync,
|
|
692
|
+
});
|
|
519
693
|
}
|
|
520
694
|
// Merge the data from local persistence into the default state
|
|
521
|
-
if (value !==
|
|
695
|
+
if (value !== undefined) {
|
|
522
696
|
const { transform, fieldTransforms } = config;
|
|
523
697
|
value = transformLoadData(value, { transform, fieldTransforms }, true);
|
|
524
698
|
if (state.isPromise(value)) {
|
|
@@ -529,7 +703,12 @@ async function loadLocal(obs, persistOptions, syncState, localState) {
|
|
|
529
703
|
// are set on the same observable
|
|
530
704
|
state.internal.globalState.isLoadingLocal = true;
|
|
531
705
|
// We want to merge the local data on top of any initial state the object is created with
|
|
532
|
-
|
|
706
|
+
if (value === null && !obs.peek()) {
|
|
707
|
+
obs.set(value);
|
|
708
|
+
}
|
|
709
|
+
else {
|
|
710
|
+
state.mergeIntoObservable(obs, value);
|
|
711
|
+
}
|
|
533
712
|
}, () => {
|
|
534
713
|
state.internal.globalState.isLoadingLocal = false;
|
|
535
714
|
});
|
|
@@ -542,18 +721,11 @@ async function loadLocal(obs, persistOptions, syncState, localState) {
|
|
|
542
721
|
}
|
|
543
722
|
syncState.isLoadedLocal.set(true);
|
|
544
723
|
}
|
|
545
|
-
function persistObservable(
|
|
546
|
-
var _a;
|
|
547
|
-
const obs = (state.isObservable(initialOrObservable)
|
|
548
|
-
? initialOrObservable
|
|
549
|
-
: state.observable(state.isFunction(initialOrObservable) ? initialOrObservable() : initialOrObservable));
|
|
724
|
+
function persistObservable(obs, persistOptions) {
|
|
550
725
|
const node = state.getNode(obs);
|
|
551
|
-
if (process.env.NODE_ENV === 'development' && ((_a = obs === null || obs === void 0 ? void 0 : obs.peek()) === null || _a === void 0 ? void 0 : _a._state)) {
|
|
552
|
-
console.warn('[legend-state] WARNING: persistObservable creates a property named "_state" but your observable already has "state" in it');
|
|
553
|
-
}
|
|
554
726
|
// Merge remote persist options with clobal options
|
|
555
727
|
if (persistOptions.remote) {
|
|
556
|
-
persistOptions.remote = Object.assign({}, observablePersistConfiguration.remoteOptions, persistOptions.remote);
|
|
728
|
+
persistOptions.remote = Object.assign({}, observablePersistConfiguration.remoteOptions, removeNullUndefined(persistOptions.remote));
|
|
557
729
|
}
|
|
558
730
|
let { remote } = persistOptions;
|
|
559
731
|
const { local } = persistOptions;
|
|
@@ -594,92 +766,95 @@ function persistObservable(initialOrObservable, persistOptions) {
|
|
|
594
766
|
let isSynced = false;
|
|
595
767
|
sync = async () => {
|
|
596
768
|
var _a, _b;
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
769
|
+
const lastSync = (_a = metadatas.get(obs)) === null || _a === void 0 ? void 0 : _a.lastSync;
|
|
770
|
+
const pending = localState.pendingChanges;
|
|
771
|
+
const get = (_b = localState.persistenceRemote.get) === null || _b === void 0 ? void 0 : _b.bind(localState.persistenceRemote);
|
|
772
|
+
if (get) {
|
|
773
|
+
const runGet = () => {
|
|
774
|
+
get({
|
|
775
|
+
state: syncState,
|
|
776
|
+
obs,
|
|
777
|
+
options: persistOptions,
|
|
778
|
+
lastSync,
|
|
779
|
+
dateModified: lastSync,
|
|
780
|
+
onError: (error) => {
|
|
781
|
+
var _a;
|
|
782
|
+
(_a = remote.onGetError) === null || _a === void 0 ? void 0 : _a.call(remote, error);
|
|
783
|
+
},
|
|
784
|
+
onGet: () => {
|
|
785
|
+
node.state.assign({
|
|
786
|
+
isLoaded: true,
|
|
787
|
+
error: undefined,
|
|
788
|
+
});
|
|
789
|
+
},
|
|
790
|
+
onChange: async ({ value, path = [], pathTypes = [], mode = 'set', lastSync }) => {
|
|
791
|
+
// Note: value is the constructed value, path is used for setInObservableAtPath
|
|
792
|
+
// to start the set into the observable from the path
|
|
793
|
+
if (value !== undefined) {
|
|
794
|
+
value = transformLoadData(value, remote, true);
|
|
795
|
+
if (state.isPromise(value)) {
|
|
796
|
+
value = await value;
|
|
619
797
|
}
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
// Note: value is the constructed value, path is used for setInObservableAtPath
|
|
627
|
-
// to start the set into the observable from the path
|
|
628
|
-
if (value !== undefined) {
|
|
629
|
-
value = transformLoadData(value, remote, true);
|
|
630
|
-
if (state.isPromise(value)) {
|
|
631
|
-
value = await value;
|
|
632
|
-
}
|
|
633
|
-
const invertedMap = remote.fieldTransforms && invertFieldMap(remote.fieldTransforms);
|
|
634
|
-
if (path.length && invertedMap) {
|
|
635
|
-
path = transformPath(path, pathTypes, invertedMap);
|
|
636
|
-
}
|
|
637
|
-
if (mode === 'dateModified') {
|
|
638
|
-
if (dateModified && !state.isEmpty(value)) {
|
|
639
|
-
onChangeRemote(() => {
|
|
640
|
-
state.setInObservableAtPath(obs, path, pathTypes, value, 'assign');
|
|
641
|
-
});
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
else {
|
|
645
|
-
const pending = localState.pendingChanges;
|
|
646
|
-
if (pending) {
|
|
647
|
-
Object.keys(pending).forEach((key) => {
|
|
648
|
-
const p = key.split('/').filter((p) => p !== '');
|
|
649
|
-
const { v, t } = pending[key];
|
|
650
|
-
if (value[p[0]] !== undefined) {
|
|
651
|
-
value = state.setAtPath(value, p, t, v, obs.peek(), (path, value) => {
|
|
652
|
-
delete pending[key];
|
|
653
|
-
pending[path.join('/')] = {
|
|
654
|
-
p: null,
|
|
655
|
-
v: value,
|
|
656
|
-
t: t.slice(0, path.length),
|
|
657
|
-
};
|
|
658
|
-
});
|
|
659
|
-
}
|
|
660
|
-
});
|
|
661
|
-
}
|
|
798
|
+
const invertedMap = remote.fieldTransforms && invertFieldMap(remote.fieldTransforms);
|
|
799
|
+
if (path.length && invertedMap) {
|
|
800
|
+
path = transformPath(path, pathTypes, invertedMap);
|
|
801
|
+
}
|
|
802
|
+
if (mode === 'lastSync' || mode === 'dateModified') {
|
|
803
|
+
if (lastSync && !state.isEmpty(value)) {
|
|
662
804
|
onChangeRemote(() => {
|
|
663
|
-
state.setInObservableAtPath(
|
|
805
|
+
state.setInObservableAtPath(
|
|
806
|
+
// @ts-expect-error Fix this type
|
|
807
|
+
obs, path, pathTypes, value, 'assign');
|
|
664
808
|
});
|
|
665
809
|
}
|
|
666
810
|
}
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
811
|
+
else {
|
|
812
|
+
const pending = localState.pendingChanges;
|
|
813
|
+
if (pending) {
|
|
814
|
+
Object.keys(pending).forEach((key) => {
|
|
815
|
+
const p = key.split('/').filter((p) => p !== '');
|
|
816
|
+
const { v, t } = pending[key];
|
|
817
|
+
if (t.length === 0 || !value) {
|
|
818
|
+
value = v;
|
|
819
|
+
}
|
|
820
|
+
else if (value[p[0]] !== undefined) {
|
|
821
|
+
value = state.setAtPath(value, p, t, v, obs.peek(), (path, value) => {
|
|
822
|
+
delete pending[key];
|
|
823
|
+
pending[path.join('/')] = {
|
|
824
|
+
p: null,
|
|
825
|
+
v: value,
|
|
826
|
+
t: t.slice(0, path.length),
|
|
827
|
+
};
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
});
|
|
831
|
+
}
|
|
832
|
+
onChangeRemote(() => {
|
|
833
|
+
// @ts-expect-error Fix this type
|
|
834
|
+
state.setInObservableAtPath(obs, path, pathTypes, value, mode);
|
|
670
835
|
});
|
|
671
836
|
}
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
837
|
+
}
|
|
838
|
+
if (lastSync && local) {
|
|
839
|
+
updateMetadata(obs, localState, syncState, persistOptions, {
|
|
840
|
+
lastSync,
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
},
|
|
844
|
+
});
|
|
845
|
+
};
|
|
846
|
+
runGet();
|
|
847
|
+
}
|
|
848
|
+
else {
|
|
849
|
+
node.state.assign({
|
|
850
|
+
isLoaded: true,
|
|
851
|
+
error: undefined,
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
if (!isSynced) {
|
|
855
|
+
isSynced = true;
|
|
680
856
|
// Wait for remote to be ready before saving pending
|
|
681
857
|
await state.when(() => syncState.isLoaded.get() || (remote.allowSetIfError && syncState.error.get()));
|
|
682
|
-
const pending = localState.pendingChanges;
|
|
683
858
|
if (pending && !state.isEmpty(pending)) {
|
|
684
859
|
localState.isApplyingPending = true;
|
|
685
860
|
const keys = Object.keys(pending);
|
|
@@ -692,6 +867,7 @@ function persistObservable(initialOrObservable, persistOptions) {
|
|
|
692
867
|
changes.push({ path, valueAtPath: v, prevAtPath: p, pathTypes: t });
|
|
693
868
|
}
|
|
694
869
|
// Send the changes into onObsChange so that they get persisted remotely
|
|
870
|
+
// TODO: Not sure why this needs to as unknown as Observable
|
|
695
871
|
onObsChange(obs, syncState, localState, persistOptions, {
|
|
696
872
|
value: obs.peek(),
|
|
697
873
|
// TODO getPrevious if any remote persistence layers need it
|
|
@@ -702,10 +878,7 @@ function persistObservable(initialOrObservable, persistOptions) {
|
|
|
702
878
|
}
|
|
703
879
|
}
|
|
704
880
|
};
|
|
705
|
-
|
|
706
|
-
if (remote.manual) {
|
|
707
|
-
syncState.assign({ sync });
|
|
708
|
-
}
|
|
881
|
+
syncState.assign({ sync });
|
|
709
882
|
}
|
|
710
883
|
// Wait for this node and all parent nodes up the hierarchy to be loaded
|
|
711
884
|
const onAllLoadedLocal = () => {
|
|
@@ -727,64 +900,123 @@ function persistObservable(initialOrObservable, persistOptions) {
|
|
|
727
900
|
}
|
|
728
901
|
obs.onChange(onObsChange.bind(this, obs, syncState, localState, persistOptions));
|
|
729
902
|
});
|
|
730
|
-
return
|
|
903
|
+
return syncState;
|
|
731
904
|
}
|
|
732
905
|
|
|
733
|
-
const { getProxy, globalState } = state.internal;
|
|
906
|
+
const { getProxy, globalState, runWithRetry, symbolActivated } = state.internal;
|
|
734
907
|
function persistActivateNode() {
|
|
735
908
|
globalState.activateNode = function activateNodePersist(node, refresh, wasPromise, newValue) {
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
params.
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
onSetFn(params, {
|
|
756
|
-
update: onChange,
|
|
757
|
-
updateLastSync: () => {
|
|
758
|
-
console.log('TODO updateLastSync');
|
|
759
|
-
},
|
|
760
|
-
applyRemoteChange: onChangeRemote,
|
|
909
|
+
if (node.activationState) {
|
|
910
|
+
// If it is an Activated
|
|
911
|
+
const { get, initial, onSet, subscribe, cache, retry, offlineBehavior, waitForSet } = node.activationState;
|
|
912
|
+
let onChange = undefined;
|
|
913
|
+
const pluginRemote = {};
|
|
914
|
+
if (get) {
|
|
915
|
+
pluginRemote.get = (params) => {
|
|
916
|
+
onChange = params.onChange;
|
|
917
|
+
const updateLastSync = (lastSync) => (params.lastSync = lastSync);
|
|
918
|
+
const setMode = (mode) => (params.mode = mode);
|
|
919
|
+
const nodeValue = state.getNodeValue(node);
|
|
920
|
+
const value = runWithRetry(node, { attemptNum: 0 }, () => {
|
|
921
|
+
return get({
|
|
922
|
+
value: state.isFunction(nodeValue) || (nodeValue === null || nodeValue === void 0 ? void 0 : nodeValue[symbolActivated]) ? undefined : nodeValue,
|
|
923
|
+
lastSync: params.lastSync,
|
|
924
|
+
updateLastSync,
|
|
925
|
+
setMode,
|
|
926
|
+
refresh,
|
|
927
|
+
});
|
|
761
928
|
});
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
929
|
+
return value;
|
|
930
|
+
};
|
|
931
|
+
}
|
|
932
|
+
if (onSet) {
|
|
933
|
+
// TODO: Work out these types better
|
|
934
|
+
pluginRemote.set = async (params) => {
|
|
935
|
+
var _a;
|
|
936
|
+
if ((_a = node.state) === null || _a === void 0 ? void 0 : _a.isLoaded.get()) {
|
|
937
|
+
const retryAttempts = { attemptNum: 0 };
|
|
938
|
+
return runWithRetry(node, retryAttempts, async (retryEvent) => {
|
|
939
|
+
let changes = {};
|
|
940
|
+
let maxModified = 0;
|
|
941
|
+
if (!node.state.isLoaded.peek()) {
|
|
942
|
+
await state.whenReady(node.state.isLoaded);
|
|
943
|
+
}
|
|
944
|
+
const cancelRetry = () => {
|
|
945
|
+
retryEvent.cancel = true;
|
|
946
|
+
};
|
|
947
|
+
await onSet({
|
|
948
|
+
...params,
|
|
949
|
+
node,
|
|
950
|
+
update: (params) => {
|
|
951
|
+
const { value, lastSync } = params;
|
|
952
|
+
maxModified = Math.max(lastSync || 0, maxModified);
|
|
953
|
+
changes = state.mergeIntoObservable(changes, value);
|
|
954
|
+
},
|
|
955
|
+
retryNum: retryAttempts.attemptNum,
|
|
956
|
+
cancelRetry,
|
|
957
|
+
refresh,
|
|
958
|
+
fromSubscribe: false,
|
|
959
|
+
});
|
|
960
|
+
return { changes, lastSync: maxModified || undefined };
|
|
961
|
+
});
|
|
771
962
|
}
|
|
772
|
-
|
|
773
|
-
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
if (subscribe) {
|
|
966
|
+
subscribe({
|
|
967
|
+
node,
|
|
968
|
+
update: (params) => {
|
|
969
|
+
if (!onChange) {
|
|
970
|
+
// TODO: Make this message better
|
|
971
|
+
console.log('[legend-state] Cannot update immediately before the first return');
|
|
972
|
+
}
|
|
973
|
+
else {
|
|
974
|
+
onChange(params);
|
|
975
|
+
}
|
|
976
|
+
},
|
|
977
|
+
refresh,
|
|
978
|
+
});
|
|
979
|
+
}
|
|
980
|
+
persistObservable(getProxy(node), {
|
|
981
|
+
pluginRemote,
|
|
982
|
+
...(cache || {}),
|
|
983
|
+
remote: {
|
|
984
|
+
retry: retry,
|
|
985
|
+
offlineBehavior,
|
|
986
|
+
waitForSet,
|
|
987
|
+
},
|
|
988
|
+
});
|
|
989
|
+
const nodeVal = state.getNodeValue(node);
|
|
990
|
+
if (nodeVal !== undefined) {
|
|
991
|
+
newValue = nodeVal;
|
|
992
|
+
}
|
|
993
|
+
else if (newValue === undefined) {
|
|
994
|
+
newValue = initial;
|
|
995
|
+
}
|
|
996
|
+
return { update: onChange, value: newValue };
|
|
997
|
+
}
|
|
998
|
+
else {
|
|
999
|
+
// If it is not an Activated
|
|
1000
|
+
let onChange = undefined;
|
|
1001
|
+
const pluginRemote = {
|
|
1002
|
+
get: async (params) => {
|
|
1003
|
+
onChange = params.onChange;
|
|
1004
|
+
if (state.isPromise(newValue)) {
|
|
1005
|
+
try {
|
|
1006
|
+
newValue = await newValue;
|
|
1007
|
+
}
|
|
1008
|
+
catch (_a) {
|
|
1009
|
+
// TODO Once we have global retry settings this should retry
|
|
1010
|
+
}
|
|
774
1011
|
}
|
|
1012
|
+
return newValue;
|
|
775
1013
|
},
|
|
776
|
-
|
|
777
|
-
|
|
1014
|
+
};
|
|
1015
|
+
persistObservable(getProxy(node), {
|
|
1016
|
+
pluginRemote,
|
|
778
1017
|
});
|
|
1018
|
+
return { update: onChange, value: newValue };
|
|
779
1019
|
}
|
|
780
|
-
persistObservable(getProxy(node), {
|
|
781
|
-
pluginRemote,
|
|
782
|
-
...(cacheOptions || {}),
|
|
783
|
-
remote: {
|
|
784
|
-
retry: retryOptions,
|
|
785
|
-
},
|
|
786
|
-
});
|
|
787
|
-
return { update: onChange };
|
|
788
1020
|
};
|
|
789
1021
|
}
|
|
790
1022
|
|