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