@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.
- package/CHANGELOG.md +0 -4
- package/index.d.ts +2 -3
- package/index.js +220 -226
- package/index.js.map +1 -1
- package/index.mjs +220 -224
- package/index.mjs.map +1 -1
- package/package.json +21 -24
- package/persist-plugins/async-storage.d.ts +14 -0
- package/persist-plugins/async-storage.js +100 -0
- package/persist-plugins/async-storage.js.map +1 -0
- package/persist-plugins/async-storage.mjs +98 -0
- package/persist-plugins/async-storage.mjs.map +1 -0
- package/persist-plugins/fetch.d.ts +10 -0
- package/persist-plugins/fetch.js +22 -0
- package/persist-plugins/fetch.js.map +1 -0
- package/persist-plugins/fetch.mjs +20 -0
- package/persist-plugins/fetch.mjs.map +1 -0
- package/persist-plugins/firebase.d.ts +51 -0
- package/persist-plugins/firebase.js +692 -0
- package/persist-plugins/firebase.js.map +1 -0
- package/persist-plugins/firebase.mjs +690 -0
- package/persist-plugins/firebase.mjs.map +1 -0
- package/persist-plugins/indexeddb.d.ts +2 -3
- package/persist-plugins/indexeddb.js +1 -16
- package/persist-plugins/indexeddb.js.map +1 -1
- package/persist-plugins/indexeddb.mjs +1 -16
- package/persist-plugins/indexeddb.mjs.map +1 -1
- package/persist-plugins/local-storage.d.ts +1 -2
- package/persist-plugins/local-storage.js +1 -5
- package/persist-plugins/local-storage.js.map +1 -1
- package/persist-plugins/local-storage.mjs +1 -5
- package/persist-plugins/local-storage.mjs.map +1 -1
- package/persist-plugins/mmkv.d.ts +4 -4
- package/persist-plugins/mmkv.js +6 -4
- package/persist-plugins/mmkv.js.map +1 -1
- package/persist-plugins/mmkv.mjs +6 -4
- package/persist-plugins/mmkv.mjs.map +1 -1
- package/persist-plugins/query.d.ts +20 -0
- package/persist-plugins/query.js +89 -0
- package/persist-plugins/query.js.map +1 -0
- package/persist-plugins/query.mjs +87 -0
- package/persist-plugins/query.mjs.map +1 -0
- package/persist.js +170 -143
- package/persist.js.map +1 -1
- package/persist.mjs +171 -144
- package/persist.mjs.map +1 -1
- package/react-hooks/usePersistedObservable.d.ts +2 -2
- package/react-hooks/usePersistedObservable.js +1 -6
- package/react-hooks/usePersistedObservable.js.map +1 -1
- package/react-hooks/usePersistedObservable.mjs +1 -6
- package/react-hooks/usePersistedObservable.mjs.map +1 -1
- package/react.d.ts +1 -6
- package/react.js +47 -99
- package/react.js.map +1 -1
- package/react.mjs +47 -100
- package/react.mjs.map +1 -1
- package/src/ObservableObject.d.ts +4 -0
- package/src/batching.d.ts +0 -7
- package/src/globals.d.ts +1 -5
- package/src/is.d.ts +1 -0
- package/src/observable.d.ts +5 -3
- package/src/observableInterfaces.d.ts +93 -69
- package/src/observe.d.ts +1 -1
- package/src/persist/observablePersistRemoteFunctionsAdapter.d.ts +2 -0
- package/src/persist/persistObservable.d.ts +10 -13
- package/src/persist-plugins/async-storage.d.ts +14 -0
- package/src/persist-plugins/fetch.d.ts +10 -0
- package/src/persist-plugins/firebase.d.ts +51 -0
- package/src/persist-plugins/indexeddb.d.ts +2 -3
- package/src/persist-plugins/local-storage.d.ts +1 -2
- package/src/persist-plugins/mmkv.d.ts +4 -4
- package/src/persist-plugins/query.d.ts +20 -0
- package/src/react/For.d.ts +1 -1
- package/src/react/react-globals.d.ts +3 -0
- package/src/react/reactInterfaces.d.ts +1 -1
- package/src/react/reactive-observer.d.ts +1 -5
- package/src/react/useObservableState.d.ts +2 -0
- package/src/react/useSelector.d.ts +1 -1
- package/src/react/useWhen.d.ts +3 -0
- package/src/react-hooks/usePersistedObservable.d.ts +2 -2
- package/src/trackSelector.d.ts +1 -1
- package/src/tracking.d.ts +2 -3
- package/config/disableDeprecationWarnings.d.ts +0 -1
- package/config/disableDeprecationWarnings.js +0 -10
- package/config/disableDeprecationWarnings.js.map +0 -1
- package/config/disableDeprecationWarnings.mjs +0 -8
- package/config/disableDeprecationWarnings.mjs.map +0 -1
- package/config/enableReactDirectRender.d.ts +0 -7
- package/config/enableReactDirectRender.js +0 -77
- package/config/enableReactDirectRender.js.map +0 -1
- package/config/enableReactDirectRender.mjs +0 -74
- package/config/enableReactDirectRender.mjs.map +0 -1
- package/react-components.d.ts +0 -2
- package/react-components.js +0 -44
- package/react-components.js.map +0 -1
- package/react-components.mjs +0 -31
- package/react-components.mjs.map +0 -1
- package/react-native-components.d.ts +0 -2
- package/react-native-components.js +0 -67
- package/react-native-components.js.map +0 -1
- package/react-native-components.mjs +0 -54
- package/react-native-components.mjs.map +0 -1
- package/src/config/disableDeprecationWarnings.d.ts +0 -1
- package/src/config/enableReactDirectRender.d.ts +0 -7
- package/src/react/enableLegendStateReact.d.ts +0 -1
- package/src/react-components/react-components.d.ts +0 -7
- package/src/react-native-components/rn-components.d.ts +0 -21
- 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
|
|
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
|
|
149
|
-
if (fieldTransforms || (
|
|
150
|
-
const
|
|
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 (
|
|
176
|
+
if (transform === null || transform === void 0 ? void 0 : transform.out) {
|
|
159
177
|
const constructed = state.constructObjectWithPath(path, value, pathTypes);
|
|
160
|
-
const saved =
|
|
178
|
+
const saved = transform.out(constructed);
|
|
161
179
|
const deconstruct = (toDeconstruct) => {
|
|
162
180
|
value = state.deconstructObjectWithPath(path, toDeconstruct);
|
|
163
|
-
return
|
|
181
|
+
return transformFn();
|
|
164
182
|
};
|
|
165
183
|
return doInOrder(saved, deconstruct);
|
|
166
184
|
}
|
|
167
|
-
return
|
|
185
|
+
return transformFn();
|
|
168
186
|
}
|
|
169
187
|
return { value, path };
|
|
170
188
|
}
|
|
171
|
-
function
|
|
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 (
|
|
177
|
-
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,
|
|
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
|
-
|
|
214
|
+
if (persistenceLocal) {
|
|
215
|
+
await persistenceLocal.setMetadata(table, metadata, config);
|
|
216
|
+
}
|
|
197
217
|
if (modified) {
|
|
198
|
-
|
|
218
|
+
syncState.dateModified.set(modified);
|
|
199
219
|
}
|
|
200
220
|
}
|
|
201
221
|
}
|
|
202
|
-
function updateMetadata(obs, localState,
|
|
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,
|
|
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
|
|
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
|
|
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 {
|
|
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 &&
|
|
235
|
-
const saveRemote = !inRemoteChange &&
|
|
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 && !
|
|
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
|
|
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
|
|
267
|
-
|
|
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 (!
|
|
270
|
-
// Prepare the local change with the
|
|
290
|
+
if (!pathTransformed.includes(undefined)) {
|
|
291
|
+
// Prepare the local change with the transformed path/value
|
|
271
292
|
changesLocal.push({
|
|
272
|
-
path:
|
|
293
|
+
path: pathTransformed,
|
|
273
294
|
pathTypes,
|
|
274
295
|
prevAtPath,
|
|
275
|
-
valueAtPath:
|
|
296
|
+
valueAtPath: valueTransformed,
|
|
276
297
|
pathStr,
|
|
277
298
|
});
|
|
278
299
|
}
|
|
279
300
|
}));
|
|
280
301
|
}
|
|
281
302
|
if (saveRemote) {
|
|
282
|
-
const
|
|
283
|
-
|
|
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 (!
|
|
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
|
|
296
|
-
// and forwarded through to onObsChange where it gets
|
|
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
|
|
319
|
+
// Prepare the remote change with the transformed path/value
|
|
299
320
|
changesRemote.push({
|
|
300
|
-
path:
|
|
321
|
+
path: pathTransformed,
|
|
301
322
|
pathTypes,
|
|
302
323
|
prevAtPath,
|
|
303
|
-
valueAtPath:
|
|
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
|
|
312
|
-
|
|
313
|
-
if (
|
|
314
|
-
await Promise.all(
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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(() =>
|
|
352
|
-
const
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
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 (
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
const
|
|
377
|
-
|
|
378
|
-
|
|
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
|
|
399
|
+
delete pending[pathStr];
|
|
385
400
|
metadata.pending = pending;
|
|
386
401
|
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
442
|
-
var _a
|
|
456
|
+
async function loadLocal(obs, persistOptions, syncState, localState) {
|
|
457
|
+
var _a;
|
|
443
458
|
const { local } = persistOptions;
|
|
444
|
-
const localPersistence = persistOptions.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
486
|
-
|
|
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
|
-
|
|
515
|
+
syncState.peek().clearLocal = () => Promise.all([
|
|
509
516
|
persistenceLocal.deleteTable(table, config),
|
|
510
517
|
persistenceLocal.deleteMetadata(table, config),
|
|
511
518
|
]);
|
|
512
519
|
}
|
|
513
|
-
|
|
520
|
+
syncState.isLoadedLocal.set(true);
|
|
514
521
|
}
|
|
515
|
-
function persistObservable(
|
|
516
|
-
const
|
|
517
|
-
|
|
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
|
|
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,
|
|
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
|
-
|
|
536
|
-
|
|
537
|
-
|
|
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
|
|
568
|
+
var _a;
|
|
543
569
|
if (!isSynced) {
|
|
544
570
|
isSynced = true;
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
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
|
-
|
|
553
|
-
|
|
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 =
|
|
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
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
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,
|
|
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(() =>
|
|
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,
|
|
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
|
-
|
|
655
|
+
syncState.assign({ sync });
|
|
629
656
|
}
|
|
630
657
|
else {
|
|
631
|
-
state.when(() => !local ||
|
|
658
|
+
state.when(() => !local || syncState.isLoadedLocal.get(), sync);
|
|
632
659
|
}
|
|
633
660
|
}
|
|
634
|
-
state.when(
|
|
635
|
-
obs.onChange(onObsChange.bind(this, obs,
|
|
661
|
+
state.when(!local || syncState.isLoadedLocal, function () {
|
|
662
|
+
obs.onChange(onObsChange.bind(this, obs, syncState, localState, persistOptions));
|
|
636
663
|
});
|
|
637
|
-
return
|
|
664
|
+
return [obs, syncState];
|
|
638
665
|
}
|
|
639
666
|
|
|
640
667
|
function isInRemoteChange() {
|