@legendapp/state 2.2.0-next.74 → 2.2.0-next.76
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/helpers/time.d.ts +2 -2
- package/index.d.ts +1 -1
- package/index.js +82 -31
- package/index.js.map +1 -1
- package/index.mjs +81 -32
- package/index.mjs.map +1 -1
- package/package.json +16 -1
- package/persist.js +122 -129
- package/persist.js.map +1 -1
- package/persist.mjs +122 -129
- package/persist.mjs.map +1 -1
- package/react.js +5 -5
- package/react.js.map +1 -1
- package/react.mjs +6 -6
- package/react.mjs.map +1 -1
- package/src/ObservableObject.ts +34 -15
- package/src/batching.ts +9 -3
- package/src/computed.ts +4 -2
- package/src/globals.ts +17 -7
- package/src/helpers.ts +3 -3
- package/src/history/undoRedo.ts +111 -0
- package/src/is.ts +7 -0
- package/src/observableInterfaces.ts +6 -5
- package/src/observableTypes.ts +5 -0
- package/src/observe.ts +1 -1
- package/src/react/For.tsx +6 -6
- package/src/sync/activateSyncedNode.ts +9 -25
- package/src/sync/syncHelpers.ts +53 -12
- package/src/sync/syncObservable.ts +117 -101
- package/src/sync-plugins/crud.ts +384 -0
- package/src/sync-plugins/fetch.ts +57 -27
- package/src/sync-plugins/keel.ts +447 -0
- package/src/sync-plugins/supabase.ts +225 -0
- package/src/syncTypes.ts +12 -6
- package/src/when.ts +6 -1
- package/sync-plugins/crud.d.ts +40 -0
- package/sync-plugins/crud.js +275 -0
- package/sync-plugins/crud.js.map +1 -0
- package/sync-plugins/crud.mjs +271 -0
- package/sync-plugins/crud.mjs.map +1 -0
- package/sync-plugins/fetch.d.ts +8 -7
- package/sync-plugins/fetch.js +34 -12
- package/sync-plugins/fetch.js.map +1 -1
- package/sync-plugins/fetch.mjs +35 -13
- package/sync-plugins/fetch.mjs.map +1 -1
- package/sync-plugins/keel.d.ts +91 -0
- package/sync-plugins/keel.js +278 -0
- package/sync-plugins/keel.js.map +1 -0
- package/sync-plugins/keel.mjs +274 -0
- package/sync-plugins/keel.mjs.map +1 -0
- package/sync-plugins/supabase.d.ts +32 -0
- package/sync-plugins/supabase.js +134 -0
- package/sync-plugins/supabase.js.map +1 -0
- package/sync-plugins/supabase.mjs +131 -0
- package/sync-plugins/supabase.mjs.map +1 -0
- package/sync.d.ts +1 -0
- package/sync.js +157 -127
- package/sync.js.map +1 -1
- package/sync.mjs +156 -129
- package/sync.mjs.map +1 -1
package/persist.js
CHANGED
|
@@ -136,19 +136,14 @@ if (process.env.NODE_ENV === 'development') {
|
|
|
136
136
|
};
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
function removeNullUndefined(
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
removeNullUndefined(v);
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
return val;
|
|
139
|
+
function removeNullUndefined(a, recursive) {
|
|
140
|
+
const out = {};
|
|
141
|
+
Object.keys(a).forEach((key) => {
|
|
142
|
+
if (a[key] !== null && a[key] !== undefined) {
|
|
143
|
+
out[key] = recursive && state.isObject(a[key]) ? removeNullUndefined(a[key]) : a[key];
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
return out;
|
|
152
147
|
}
|
|
153
148
|
|
|
154
149
|
function observablePersistRemoteFunctionsAdapter({ get, set, }) {
|
|
@@ -1279,8 +1274,7 @@ async function doChangeLocal(changeInfo) {
|
|
|
1279
1274
|
const { pluginPersist } = localState;
|
|
1280
1275
|
const persist = syncOptions.persist;
|
|
1281
1276
|
const { table, config: configLocal } = parseLocalConfig(persist);
|
|
1282
|
-
const
|
|
1283
|
-
const shouldSaveMetadata = persist && (configRemote === null || configRemote === void 0 ? void 0 : configRemote.offlineBehavior) === 'retry';
|
|
1277
|
+
const shouldSaveMetadata = persist === null || persist === void 0 ? void 0 : persist.retrySync;
|
|
1284
1278
|
if (saveRemote && shouldSaveMetadata) {
|
|
1285
1279
|
// First save pending changes before saving local or remote
|
|
1286
1280
|
await updateMetadataImmediate(obs, localState, syncState, syncOptions, {
|
|
@@ -1311,8 +1305,8 @@ async function doChangeRemote(changeInfo) {
|
|
|
1311
1305
|
const { pluginPersist, pluginSync } = localState;
|
|
1312
1306
|
const persist = syncOptions.persist;
|
|
1313
1307
|
const { table, config: configLocal } = parseLocalConfig(persist);
|
|
1314
|
-
const {
|
|
1315
|
-
const shouldSaveMetadata = persist
|
|
1308
|
+
const { allowSetIfGetError, onBeforeSet, onSetError, waitForSet, onAfterSet } = syncOptions || {};
|
|
1309
|
+
const shouldSaveMetadata = persist === null || persist === void 0 ? void 0 : persist.retrySync;
|
|
1316
1310
|
if (changesRemote.length > 0) {
|
|
1317
1311
|
// Wait for remote to be ready before saving
|
|
1318
1312
|
await state.when(() => syncState.isLoaded.get() || (allowSetIfGetError && syncState.error.get()));
|
|
@@ -1351,11 +1345,11 @@ async function doChangeRemote(changeInfo) {
|
|
|
1351
1345
|
const pathStrs = Array.from(new Set(changesRemote.map((change) => change.pathStr)));
|
|
1352
1346
|
const { changes, lastSync } = saved;
|
|
1353
1347
|
if (pathStrs.length > 0) {
|
|
1348
|
+
let transformedChanges = undefined;
|
|
1349
|
+
const metadata = {};
|
|
1354
1350
|
if (persist) {
|
|
1355
|
-
const metadata = {};
|
|
1356
1351
|
const pendingMetadata = (_b = pluginPersist.getMetadata(table, configLocal)) === null || _b === void 0 ? void 0 : _b.pending;
|
|
1357
1352
|
const pending = localState.pendingChanges;
|
|
1358
|
-
let transformedChanges = undefined;
|
|
1359
1353
|
for (let i = 0; i < pathStrs.length; i++) {
|
|
1360
1354
|
const pathStr = pathStrs[i];
|
|
1361
1355
|
// Clear pending for this path
|
|
@@ -1374,32 +1368,34 @@ async function doChangeRemote(changeInfo) {
|
|
|
1374
1368
|
if (lastSync) {
|
|
1375
1369
|
metadata.lastSync = lastSync;
|
|
1376
1370
|
}
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
localState.pendingSaveResults.push(transformedChanges);
|
|
1371
|
+
}
|
|
1372
|
+
// Remote can optionally have data that needs to be merged back into the observable,
|
|
1373
|
+
// for example Firebase may update dateModified with the server timestamp
|
|
1374
|
+
if (changes && !state.isEmpty(changes)) {
|
|
1375
|
+
transformedChanges = transformLoadData(changes, syncOptions, false);
|
|
1376
|
+
}
|
|
1377
|
+
if (localState.numSavesOutstanding > 0) {
|
|
1378
|
+
if (transformedChanges) {
|
|
1379
|
+
if (!localState.pendingSaveResults) {
|
|
1380
|
+
localState.pendingSaveResults = [];
|
|
1388
1381
|
}
|
|
1382
|
+
localState.pendingSaveResults.push(transformedChanges);
|
|
1389
1383
|
}
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
onChangeRemote(() => state.mergeIntoObservable(obs, ...allChanges));
|
|
1384
|
+
}
|
|
1385
|
+
else {
|
|
1386
|
+
let allChanges = [...(localState.pendingSaveResults || []), transformedChanges].filter((v) => v !== undefined);
|
|
1387
|
+
if (allChanges.length > 0) {
|
|
1388
|
+
if (allChanges.some((change) => state.isPromise(change))) {
|
|
1389
|
+
allChanges = await Promise.all(allChanges);
|
|
1397
1390
|
}
|
|
1391
|
+
onChangeRemote(() => state.mergeIntoObservable(obs, ...allChanges));
|
|
1392
|
+
}
|
|
1393
|
+
if (persist) {
|
|
1398
1394
|
if (shouldSaveMetadata && !state.isEmpty(metadata)) {
|
|
1399
1395
|
updateMetadata(obs, localState, syncState, syncOptions, metadata);
|
|
1400
1396
|
}
|
|
1401
|
-
localState.pendingSaveResults = [];
|
|
1402
1397
|
}
|
|
1398
|
+
localState.pendingSaveResults = [];
|
|
1403
1399
|
}
|
|
1404
1400
|
onAfterSet === null || onAfterSet === void 0 ? void 0 : onAfterSet();
|
|
1405
1401
|
}
|
|
@@ -1535,6 +1531,77 @@ function syncObservable(obs$, syncOptionsOrSynced) {
|
|
|
1535
1531
|
const get = (_b = localState.pluginSync.get) === null || _b === void 0 ? void 0 : _b.bind(localState.pluginSync);
|
|
1536
1532
|
if (get) {
|
|
1537
1533
|
const runGet = () => {
|
|
1534
|
+
const onChange = async ({ value, mode, lastSync }) => {
|
|
1535
|
+
mode = mode || syncOptions.mode || 'set';
|
|
1536
|
+
if (value !== undefined) {
|
|
1537
|
+
value = transformLoadData(value, syncOptions, true);
|
|
1538
|
+
if (state.isPromise(value)) {
|
|
1539
|
+
value = await value;
|
|
1540
|
+
}
|
|
1541
|
+
const pending = localState.pendingChanges;
|
|
1542
|
+
const currentValue = obs$.peek();
|
|
1543
|
+
if (pending) {
|
|
1544
|
+
let didChangeMetadata = false;
|
|
1545
|
+
Object.keys(pending).forEach((key) => {
|
|
1546
|
+
const p = key.split('/').filter((p) => p !== '');
|
|
1547
|
+
const { v, t } = pending[key];
|
|
1548
|
+
if (t.length === 0 || !value) {
|
|
1549
|
+
if (state.isObject(value) && state.isObject(v)) {
|
|
1550
|
+
Object.assign(value, v);
|
|
1551
|
+
}
|
|
1552
|
+
else {
|
|
1553
|
+
value = v;
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
else if (value[p[0]] !== undefined) {
|
|
1557
|
+
const curValue = getValueAtPath(currentValue, p);
|
|
1558
|
+
const newValue = getValueAtPath(value, p);
|
|
1559
|
+
if (JSON.stringify(curValue) === JSON.stringify(newValue)) {
|
|
1560
|
+
delete pending[key];
|
|
1561
|
+
didChangeMetadata = true;
|
|
1562
|
+
}
|
|
1563
|
+
else {
|
|
1564
|
+
value = state.setAtPath(value, p, t, v, 'merge', obs$.peek(), (path, value) => {
|
|
1565
|
+
delete pending[key];
|
|
1566
|
+
pending[path.join('/')] = {
|
|
1567
|
+
p: null,
|
|
1568
|
+
v: value,
|
|
1569
|
+
t: t.slice(0, path.length),
|
|
1570
|
+
};
|
|
1571
|
+
});
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
});
|
|
1575
|
+
if (didChangeMetadata) {
|
|
1576
|
+
updateMetadata(obs$, localState, syncState, syncOptions, {
|
|
1577
|
+
pending,
|
|
1578
|
+
});
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
onChangeRemote(() => {
|
|
1582
|
+
if (mode === 'assign' && state.isObject(value)) {
|
|
1583
|
+
obs$.assign(value);
|
|
1584
|
+
}
|
|
1585
|
+
else if (mode === 'append' && state.isArray(value)) {
|
|
1586
|
+
obs$.push(...value);
|
|
1587
|
+
}
|
|
1588
|
+
else if (mode === 'prepend' && state.isArray(value)) {
|
|
1589
|
+
obs$.splice(0, 0, ...value);
|
|
1590
|
+
}
|
|
1591
|
+
else if (mode === 'merge') {
|
|
1592
|
+
state.mergeIntoObservable(obs$, value);
|
|
1593
|
+
}
|
|
1594
|
+
else {
|
|
1595
|
+
obs$.set(value);
|
|
1596
|
+
}
|
|
1597
|
+
});
|
|
1598
|
+
}
|
|
1599
|
+
if (lastSync && syncOptions.persist) {
|
|
1600
|
+
updateMetadata(obs$, localState, syncState, syncOptions, {
|
|
1601
|
+
lastSync,
|
|
1602
|
+
});
|
|
1603
|
+
}
|
|
1604
|
+
};
|
|
1538
1605
|
get({
|
|
1539
1606
|
state: syncState,
|
|
1540
1607
|
obs: obs$,
|
|
@@ -1546,82 +1613,23 @@ function syncObservable(obs$, syncOptionsOrSynced) {
|
|
|
1546
1613
|
(_a = syncOptions.onGetError) === null || _a === void 0 ? void 0 : _a.call(syncOptions, error);
|
|
1547
1614
|
},
|
|
1548
1615
|
onGet: () => {
|
|
1616
|
+
const isFirstLoad = !node.state.isLoaded.peek();
|
|
1549
1617
|
node.state.assign({
|
|
1550
1618
|
isLoaded: true,
|
|
1551
1619
|
error: undefined,
|
|
1552
1620
|
});
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
const pending = localState.pendingChanges;
|
|
1562
|
-
const currentValue = obs$.peek();
|
|
1563
|
-
if (pending) {
|
|
1564
|
-
let didChangeMetadata = false;
|
|
1565
|
-
Object.keys(pending).forEach((key) => {
|
|
1566
|
-
const p = key.split('/').filter((p) => p !== '');
|
|
1567
|
-
const { v, t } = pending[key];
|
|
1568
|
-
if (t.length === 0 || !value) {
|
|
1569
|
-
if (state.isObject(value) && state.isObject(v)) {
|
|
1570
|
-
Object.assign(value, v);
|
|
1571
|
-
}
|
|
1572
|
-
else {
|
|
1573
|
-
value = v;
|
|
1574
|
-
}
|
|
1575
|
-
}
|
|
1576
|
-
else if (value[p[0]] !== undefined) {
|
|
1577
|
-
const curValue = getValueAtPath(currentValue, p);
|
|
1578
|
-
const newValue = getValueAtPath(value, p);
|
|
1579
|
-
if (JSON.stringify(curValue) === JSON.stringify(newValue)) {
|
|
1580
|
-
delete pending[key];
|
|
1581
|
-
didChangeMetadata = true;
|
|
1582
|
-
}
|
|
1583
|
-
else {
|
|
1584
|
-
value = state.setAtPath(value, p, t, v, 'merge', obs$.peek(), (path, value) => {
|
|
1585
|
-
delete pending[key];
|
|
1586
|
-
pending[path.join('/')] = {
|
|
1587
|
-
p: null,
|
|
1588
|
-
v: value,
|
|
1589
|
-
t: t.slice(0, path.length),
|
|
1590
|
-
};
|
|
1591
|
-
});
|
|
1592
|
-
}
|
|
1593
|
-
}
|
|
1594
|
-
});
|
|
1595
|
-
if (didChangeMetadata) {
|
|
1596
|
-
updateMetadata(obs$, localState, syncState, syncOptions, {
|
|
1597
|
-
pending,
|
|
1598
|
-
});
|
|
1599
|
-
}
|
|
1600
|
-
}
|
|
1601
|
-
onChangeRemote(() => {
|
|
1602
|
-
if (mode === 'assign' && state.isObject(value)) {
|
|
1603
|
-
obs$.assign(value);
|
|
1604
|
-
}
|
|
1605
|
-
else if (mode === 'append' && state.isArray(value)) {
|
|
1606
|
-
obs$.push(...value);
|
|
1607
|
-
}
|
|
1608
|
-
else if (mode === 'prepend' && state.isArray(value)) {
|
|
1609
|
-
obs$.splice(0, 0, ...value);
|
|
1610
|
-
}
|
|
1611
|
-
else if (mode === 'merge') {
|
|
1612
|
-
state.mergeIntoObservable(obs$, value);
|
|
1613
|
-
}
|
|
1614
|
-
else {
|
|
1615
|
-
obs$.set(value);
|
|
1616
|
-
}
|
|
1617
|
-
});
|
|
1618
|
-
}
|
|
1619
|
-
if (lastSync && syncOptions.persist) {
|
|
1620
|
-
updateMetadata(obs$, localState, syncState, syncOptions, {
|
|
1621
|
-
lastSync,
|
|
1621
|
+
if (isFirstLoad && syncOptions.subscribe) {
|
|
1622
|
+
syncOptions.subscribe({
|
|
1623
|
+
node,
|
|
1624
|
+
update: (params) => {
|
|
1625
|
+
params.mode || (params.mode = syncOptions.mode || 'merge');
|
|
1626
|
+
onChange(params);
|
|
1627
|
+
},
|
|
1628
|
+
refresh: sync,
|
|
1622
1629
|
});
|
|
1623
1630
|
}
|
|
1624
1631
|
},
|
|
1632
|
+
onChange,
|
|
1625
1633
|
});
|
|
1626
1634
|
};
|
|
1627
1635
|
runGet();
|
|
@@ -1691,7 +1699,7 @@ function enableActivateSyncedNode() {
|
|
|
1691
1699
|
const obs$ = getProxy(node);
|
|
1692
1700
|
if (node.activationState) {
|
|
1693
1701
|
// If it is a Synced
|
|
1694
|
-
const { get, initial, set
|
|
1702
|
+
const { get, initial, set } = node.activationState;
|
|
1695
1703
|
let onChange = undefined;
|
|
1696
1704
|
const pluginRemote = {};
|
|
1697
1705
|
let promiseReturn = undefined;
|
|
@@ -1703,16 +1711,18 @@ function enableActivateSyncedNode() {
|
|
|
1703
1711
|
pluginRemote.get = (params) => {
|
|
1704
1712
|
onChange = params.onChange;
|
|
1705
1713
|
const updateLastSync = (lastSync) => (params.lastSync = lastSync);
|
|
1706
|
-
const setMode = (mode) => (params.mode = mode);
|
|
1707
1714
|
const existingValue = getNodeValue(node);
|
|
1708
1715
|
const value = runWithRetry(node, { attemptNum: 0 }, () => {
|
|
1709
|
-
|
|
1716
|
+
const paramsToGet = {
|
|
1710
1717
|
value: state.isFunction(existingValue) || (existingValue === null || existingValue === void 0 ? void 0 : existingValue[symbolLinked$1]) ? undefined : existingValue,
|
|
1711
1718
|
lastSync: params.lastSync,
|
|
1712
1719
|
updateLastSync,
|
|
1713
|
-
|
|
1720
|
+
mode: params.mode,
|
|
1714
1721
|
refresh,
|
|
1715
|
-
}
|
|
1722
|
+
};
|
|
1723
|
+
const ret = get(paramsToGet);
|
|
1724
|
+
params.mode = paramsToGet.mode;
|
|
1725
|
+
return ret;
|
|
1716
1726
|
});
|
|
1717
1727
|
promiseReturn = value;
|
|
1718
1728
|
return value;
|
|
@@ -1764,23 +1774,6 @@ function enableActivateSyncedNode() {
|
|
|
1764
1774
|
setNodeValue(node, promiseReturn ? undefined : newValue);
|
|
1765
1775
|
// @ts-expect-error TODO fix these types
|
|
1766
1776
|
syncState = syncObservable(obs$, { ...node.activationState, ...pluginRemote });
|
|
1767
|
-
if (subscribe) {
|
|
1768
|
-
state.when(promiseReturn || true, () => {
|
|
1769
|
-
subscribe({
|
|
1770
|
-
node,
|
|
1771
|
-
update: (params) => {
|
|
1772
|
-
if (!onChange) {
|
|
1773
|
-
// TODO: Make this message better
|
|
1774
|
-
console.log('[legend-state] Cannot update immediately before the first return');
|
|
1775
|
-
}
|
|
1776
|
-
else {
|
|
1777
|
-
onChange(params);
|
|
1778
|
-
}
|
|
1779
|
-
},
|
|
1780
|
-
refresh,
|
|
1781
|
-
});
|
|
1782
|
-
});
|
|
1783
|
-
}
|
|
1784
1777
|
return { update: onChange, value: newValue };
|
|
1785
1778
|
}
|
|
1786
1779
|
else {
|