selfstore 1.8.28 → 1.8.29

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/dist/advanced.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { peer, fromSession as fromSession$3, connect as connect$3 } from './chunk-GHSHIDF3.js';
2
2
  import { fromSession as fromSession$2, connect as connect$2 } from './chunk-IDW3WQKF.js';
3
- export { RESERVED_STORE_MODES, createLocalStore, deriveStatus, gisDriveAuth, indexedDbCache, isLockableCache, isReservedStoreMode, memoryCache, preloadGoogleIdentity } from './chunk-GM47BISW.js';
3
+ export { RESERVED_STORE_MODES, createLocalStore, deriveStatus, gisDriveAuth, indexedDbCache, isLockableCache, isReservedStoreMode, memoryCache, preloadGoogleIdentity } from './chunk-OCWNUAXV.js';
4
4
  import './chunk-GSZSZGSD.js';
5
5
  export { datedName, pickFromDisk, saveToDisk } from './chunk-Q3NO477P.js';
6
6
  import './chunk-FJGWTBAL.js';
@@ -553,6 +553,33 @@ function createLocalStore(opts) {
553
553
  return null;
554
554
  }
555
555
  }
556
+ async function statBeforeOverwrite() {
557
+ return durable?.stat ? durable.stat() : null;
558
+ }
559
+ async function flushBeforeLeaving() {
560
+ try {
561
+ await persistLocal();
562
+ } catch (e) {
563
+ logger.warn("[selfstore] local save before a switch failed", e);
564
+ }
565
+ if (await pushDurable("push")) return;
566
+ if (await contentDigest(await gather()) === lastSavedContentHash) return;
567
+ throw new SelfstoreError(
568
+ "TARGET_WRITE_FAILED",
569
+ "attachTarget(): an edit not yet saved to the current destination could not reach it; try again online before switching."
570
+ );
571
+ }
572
+ async function settleRewrite(written, ...followUps) {
573
+ lastError = null;
574
+ try {
575
+ await setRemoteVersion(typeof written === "string" ? written : await statRemote());
576
+ for (const followUp of followUps) await followUp();
577
+ } catch (e) {
578
+ logger.error("[selfstore] bookkeeping after a key rewrite failed; the new key is kept", e);
579
+ }
580
+ await publishReplicas();
581
+ notify();
582
+ }
556
583
  async function requireReady(where) {
557
584
  let ok;
558
585
  try {
@@ -572,7 +599,18 @@ function createLocalStore(opts) {
572
599
  }
573
600
  async function convergeBeforeRewrite(where) {
574
601
  await requireReady(where);
575
- const seen = await statRemote();
602
+ let seen;
603
+ try {
604
+ seen = await statBeforeOverwrite();
605
+ } catch (e) {
606
+ if (isAuthExpired(e)) {
607
+ raiseGate("AUTH_EXPIRED", "Access to the destination expired or was revoked.");
608
+ }
609
+ throw new SelfstoreError(
610
+ "TARGET_WRITE_FAILED",
611
+ `${where}(): cannot tell whether the destination moved; try again online.`
612
+ );
613
+ }
576
614
  if (seen === null || remoteVersion === null || seen === remoteVersion) return;
577
615
  const pulled = await pullRemoteIntoLocal("push", seen);
578
616
  if (locked() || needsAttention) {
@@ -620,7 +658,7 @@ function createLocalStore(opts) {
620
658
  lastError = transient("Save deferred: destination momentarily unreachable.");
621
659
  return false;
622
660
  }
623
- const seen = await statRemote();
661
+ const seen = await statBeforeOverwrite();
624
662
  if (seen !== null && remoteVersion !== null && seen !== remoteVersion) {
625
663
  const pulled = await pullRemoteIntoLocal(source, seen);
626
664
  if (locked() || needsAttention) return false;
@@ -1236,11 +1274,7 @@ function createLocalStore(opts) {
1236
1274
  await becomePersistent();
1237
1275
  const strategy = attachOpts?.strategy ?? "merge";
1238
1276
  if (strategy === "replace-local" && durable && !locked() && !needsAttention) {
1239
- try {
1240
- await persistLocal();
1241
- await pushDurable("push");
1242
- } catch {
1243
- }
1277
+ await flushBeforeLeaving();
1244
1278
  }
1245
1279
  if (g) {
1246
1280
  const stored = await kv.get(groupSeqKey(g.manifest.group));
@@ -1500,13 +1534,11 @@ function createLocalStore(opts) {
1500
1534
  encrypted = !!password;
1501
1535
  setPass(password || null);
1502
1536
  envelope = null;
1537
+ let written;
1503
1538
  try {
1504
1539
  await persistLocal();
1505
1540
  pendingEdit = false;
1506
- const written = await durable.save(await buildBlob());
1507
- await setRemoteVersion(typeof written === "string" ? written : await statRemote());
1508
- lastError = null;
1509
- await kv.set(KEY.enc, encrypted);
1541
+ written = await durable.save(await buildBlob());
1510
1542
  } catch (e) {
1511
1543
  encrypted = prevEncrypted;
1512
1544
  setPass(prevPass);
@@ -1515,10 +1547,10 @@ function createLocalStore(opts) {
1515
1547
  if (isAuthExpired(e)) {
1516
1548
  raiseGate("AUTH_EXPIRED", "Access to the destination expired or was revoked.");
1517
1549
  }
1518
- throw e;
1519
- } finally {
1520
1550
  notify();
1551
+ throw e;
1521
1552
  }
1553
+ await settleRewrite(written, () => kv.set(KEY.enc, encrypted));
1522
1554
  });
1523
1555
  },
1524
1556
  async addEncryptionKey(password, id) {
@@ -1565,22 +1597,21 @@ function createLocalStore(opts) {
1565
1597
  const prevEnvelope = envelope;
1566
1598
  const slot = await mintSlot(password, envelope.dataKey, id);
1567
1599
  envelope = { dataKey: envelope.dataKey, slots: [...envelope.slots, slot] };
1600
+ let written;
1568
1601
  try {
1569
1602
  const blob = await buildBlob();
1570
1603
  await readBoxWithSync(await asBytes(blob), password);
1571
- const written = await durable.save(blob);
1572
- await setRemoteVersion(typeof written === "string" ? written : await statRemote());
1573
- lastError = null;
1574
- return slot.id;
1604
+ written = await durable.save(blob);
1575
1605
  } catch (e) {
1576
1606
  envelope = prevEnvelope;
1577
1607
  if (isAuthExpired(e)) {
1578
1608
  raiseGate("AUTH_EXPIRED", "Access to the destination expired or was revoked.");
1579
1609
  }
1580
- throw e;
1581
- } finally {
1582
1610
  notify();
1611
+ throw e;
1583
1612
  }
1613
+ await settleRewrite(written);
1614
+ return slot.id;
1584
1615
  });
1585
1616
  },
1586
1617
  async removeEncryptionKey(id) {
@@ -1636,21 +1667,20 @@ function createLocalStore(opts) {
1636
1667
  }
1637
1668
  const prevEnvelope = envelope;
1638
1669
  envelope = { dataKey: envelope.dataKey, slots: remaining };
1670
+ let written;
1639
1671
  try {
1640
1672
  const blob = await buildBlob();
1641
1673
  await readBoxWithSync(await asBytes(blob), pass);
1642
- const written = await durable.save(blob);
1643
- await setRemoteVersion(typeof written === "string" ? written : await statRemote());
1644
- lastError = null;
1674
+ written = await durable.save(blob);
1645
1675
  } catch (e) {
1646
1676
  envelope = prevEnvelope;
1647
1677
  if (isAuthExpired(e)) {
1648
1678
  raiseGate("AUTH_EXPIRED", "Access to the destination expired or was revoked.");
1649
1679
  }
1650
- throw e;
1651
- } finally {
1652
1680
  notify();
1681
+ throw e;
1653
1682
  }
1683
+ await settleRewrite(written);
1654
1684
  });
1655
1685
  },
1656
1686
  async setExternalEncryption(secret, keyRef) {
@@ -1682,6 +1712,7 @@ function createLocalStore(opts) {
1682
1712
  pass = null;
1683
1713
  envelope = { dataKey, slots: [slot] };
1684
1714
  armIdleLock();
1715
+ let written;
1685
1716
  try {
1686
1717
  await persistLocal();
1687
1718
  pendingEdit = false;
@@ -1693,10 +1724,7 @@ function createLocalStore(opts) {
1693
1724
  void 0,
1694
1725
  async () => secret
1695
1726
  );
1696
- const written = await durable.save(blob);
1697
- await setRemoteVersion(typeof written === "string" ? written : await statRemote());
1698
- lastError = null;
1699
- await kv.set(KEY.enc, true);
1727
+ written = await durable.save(blob);
1700
1728
  } catch (e) {
1701
1729
  encrypted = prevEncrypted;
1702
1730
  pass = prevPass;
@@ -1706,10 +1734,10 @@ function createLocalStore(opts) {
1706
1734
  if (isAuthExpired(e)) {
1707
1735
  raiseGate("AUTH_EXPIRED", "Access to the destination expired or was revoked.");
1708
1736
  }
1709
- throw e;
1710
- } finally {
1711
1737
  notify();
1738
+ throw e;
1712
1739
  }
1740
+ await settleRewrite(written, () => kv.set(KEY.enc, true));
1713
1741
  });
1714
1742
  },
1715
1743
  async addExternalKey(secret, keyRef, id) {
@@ -1752,6 +1780,7 @@ function createLocalStore(opts) {
1752
1780
  const prevEnvelope = envelope;
1753
1781
  const slot = await mintExternalSlot(secret, keyRef, envelope.dataKey, id);
1754
1782
  envelope = { dataKey: envelope.dataKey, slots: [...envelope.slots, slot] };
1783
+ let written;
1755
1784
  try {
1756
1785
  const blob = await buildBlob();
1757
1786
  await readBoxWithSync(
@@ -1761,19 +1790,17 @@ function createLocalStore(opts) {
1761
1790
  void 0,
1762
1791
  async () => secret
1763
1792
  );
1764
- const written = await durable.save(blob);
1765
- await setRemoteVersion(typeof written === "string" ? written : await statRemote());
1766
- lastError = null;
1767
- return slot.id;
1793
+ written = await durable.save(blob);
1768
1794
  } catch (e) {
1769
1795
  envelope = prevEnvelope;
1770
1796
  if (isAuthExpired(e)) {
1771
1797
  raiseGate("AUTH_EXPIRED", "Access to the destination expired or was revoked.");
1772
1798
  }
1773
- throw e;
1774
- } finally {
1775
1799
  notify();
1800
+ throw e;
1776
1801
  }
1802
+ await settleRewrite(written);
1803
+ return slot.id;
1777
1804
  });
1778
1805
  },
1779
1806
  async unlockWithExternal(secret) {
@@ -2150,5 +2177,5 @@ function gisDriveAuth(opts) {
2150
2177
  }
2151
2178
 
2152
2179
  export { RESERVED_STORE_MODES, createLocalStore, deriveStatus, gisDriveAuth, indexedDbCache, isLockableCache, isReservedStoreMode, memoryCache, preloadGoogleIdentity };
2153
- //# sourceMappingURL=chunk-GM47BISW.js.map
2154
- //# sourceMappingURL=chunk-GM47BISW.js.map
2180
+ //# sourceMappingURL=chunk-OCWNUAXV.js.map
2181
+ //# sourceMappingURL=chunk-OCWNUAXV.js.map