selfstore 1.8.28 → 1.8.30

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.
@@ -1,12 +1,12 @@
1
- import { B as BackupTarget } from './store-CKGrIFQU.js';
2
- export { A as AuthExpiredError, f as BuiltinTargetKind, E as EncodeOptions, H as Header, K as KdfParams, L as LocalStore, g as LocalStoreOptions, e as LocalStoreState, M as MirrorState, h as Mode, i as PasswordCheck, b as PasswordPolicy, j as PasswordRequirement, P as PeerSource, k as PeerState, R as RESERVED_COLLECTION_PREFIX, l as RESERVED_STORE_MODES, m as RecipientStanza, n as ReplicaState, o as SelfstoreError, p as SelfstoreErrorCode, q as Severity, a as Snapshot, c as SnapshotFile, r as StatusAction, d as StatusDescriptor, s as StatusInput, t as StorageState, S as StoreError, u as StoreGroupConfig, v as SyncJournalEntry, w as SyncSource, T as TargetKind, x as checkPasswordPolicy, y as createLocalStore, z as deriveStatus, C as errorLabelKey, D as isAuthExpired, F as isReservedStoreMode, G as isSelfstoreError } from './store-CKGrIFQU.js';
1
+ import { B as BackupTarget } from './store-CZOqxDj5.js';
2
+ export { A as AuthExpiredError, f as BuiltinTargetKind, E as EncodeOptions, H as Header, K as KdfParams, L as LocalStore, g as LocalStoreOptions, e as LocalStoreState, M as MirrorState, h as Mode, i as PasswordCheck, b as PasswordPolicy, j as PasswordRequirement, P as PeerSource, k as PeerState, R as RESERVED_COLLECTION_PREFIX, l as RESERVED_STORE_MODES, m as RecipientStanza, n as ReplicaState, o as SelfstoreError, p as SelfstoreErrorCode, q as Severity, a as Snapshot, c as SnapshotFile, r as StatusAction, d as StatusDescriptor, s as StatusInput, t as StorageState, S as StoreError, u as StoreGroupConfig, v as SyncJournalEntry, w as SyncSource, T as TargetKind, x as checkPasswordPolicy, y as createLocalStore, z as deriveStatus, C as errorLabelKey, D as isAuthExpired, F as isReservedStoreMode, G as isSelfstoreError } from './store-CZOqxDj5.js';
3
3
  import { K as KV } from './cache-ilsK3Tki.js';
4
4
  export { C as CachedFile, L as LocalCache, e as LockableCache, i as indexedDbCache, f as isLockableCache, m as memoryCache } from './cache-ilsK3Tki.js';
5
- export { G as GisDriveAuthOptions, a as GisTokenPersistence, d as datedName, e as exportSnapshot, g as gisDriveAuth, i as importSnapshot, b as inspect, c as isEncrypted, p as pickFromDisk, f as preloadGoogleIdentity, s as saveToDisk } from './local-ClXq1N2M.js';
6
- import { c as connect$2, f as fromSession$2, a as connect$3, b as fromSession$3, p as peer } from './s3-dG1SCnV1.js';
7
- export { S as S3Config, d as S3ConnectOptions, W as WebdavConfig, e as WebdavConnectOptions, g as WebdavPeerOptions } from './s3-dG1SCnV1.js';
8
- import { c as connect$1, f as fromSession$1, p as preview, b as adopt, d as findOrCreateOwnFile, l as listBackups, e as createBackup, g as deleteBackup, r as renameBackup, h as account, i as createCompanion, s as share, u as unshare, o as owner, j as secondary } from './drive--mndByvF.js';
9
- export { k as DriveAccount, a as DriveAuth, m as DriveBackupInfo, D as DriveOptions } from './drive--mndByvF.js';
5
+ export { G as GisDriveAuthOptions, a as GisTokenPersistence, d as datedName, e as exportSnapshot, g as gisDriveAuth, i as importSnapshot, b as inspect, c as isEncrypted, p as pickFromDisk, f as preloadGoogleIdentity, s as saveToDisk } from './local-CGYx16zM.js';
6
+ import { c as connect$2, f as fromSession$2, a as connect$3, b as fromSession$3, p as peer } from './s3-iiFYjype.js';
7
+ export { S as S3Config, d as S3ConnectOptions, W as WebdavConfig, e as WebdavConnectOptions, g as WebdavPeerOptions } from './s3-iiFYjype.js';
8
+ import { c as connect$1, f as fromSession$1, p as preview, b as adopt, d as findOrCreateOwnFile, l as listBackups, e as createBackup, g as deleteBackup, r as renameBackup, h as account, i as createCompanion, s as share, u as unshare, o as owner, j as secondary } from './drive-DvnzpjLM.js';
9
+ export { k as DriveAccount, a as DriveAuth, m as DriveBackupInfo, D as DriveOptions } from './drive-DvnzpjLM.js';
10
10
  import './sync.js';
11
11
 
12
12
  /**
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-TB3CKC6Z.js';
4
4
  import './chunk-GSZSZGSD.js';
5
5
  export { datedName, pickFromDisk, saveToDisk } from './chunk-Q3NO477P.js';
6
6
  import './chunk-FJGWTBAL.js';
package/dist/backups.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { B as BackupsHost } from './manager-yCM0M0FL.js';
2
- export { a as BackupFileInfo, b as BackupOwner, c as BackupRow, d as BackupsErrorCode, e as BackupsKeys, f as BackupsManager, g as BackupsNaming, h as BackupsSnapshot, K as KnownBackups, i as createBackupsManager } from './manager-yCM0M0FL.js';
3
- import { D as DriveOptions } from './drive--mndByvF.js';
1
+ import { B as BackupsHost } from './manager-B-gb3lIn.js';
2
+ export { a as BackupFileInfo, b as BackupOwner, c as BackupRow, d as BackupsErrorCode, e as BackupsKeys, f as BackupsManager, g as BackupsNaming, h as BackupsSnapshot, K as KnownBackups, i as createBackupsManager } from './manager-B-gb3lIn.js';
3
+ import { D as DriveOptions } from './drive-DvnzpjLM.js';
4
4
  import './cache-ilsK3Tki.js';
5
- import './store-CKGrIFQU.js';
5
+ import './store-CZOqxDj5.js';
6
6
  import './sync.js';
7
7
  import './machine-ALMkT3mk.js';
8
8
 
@@ -46,6 +46,14 @@ function deriveStatus(input) {
46
46
  labelKey: "status.pendingDownload"
47
47
  };
48
48
  }
49
+ if (isDurableKind(input.targetKind) && input.unsent) {
50
+ return {
51
+ state: "cache-only",
52
+ severity: "warn",
53
+ actionable: false,
54
+ labelKey: "status.unsent"
55
+ };
56
+ }
49
57
  if (input.targetKind === "device") {
50
58
  if (input.copy?.at) {
51
59
  return {
@@ -192,6 +200,7 @@ function createLocalStore(opts) {
192
200
  const warnedNoId = /* @__PURE__ */ new Set();
193
201
  let seenEpoch;
194
202
  let pendingEdit = false;
203
+ let unsent = false;
195
204
  let editSeq = 0;
196
205
  let queuedKind = null;
197
206
  let prefetched = null;
@@ -553,6 +562,33 @@ function createLocalStore(opts) {
553
562
  return null;
554
563
  }
555
564
  }
565
+ async function statBeforeOverwrite() {
566
+ return durable?.stat ? durable.stat() : null;
567
+ }
568
+ async function flushBeforeLeaving() {
569
+ try {
570
+ await persistLocal();
571
+ } catch (e) {
572
+ logger.warn("[selfstore] local save before a switch failed", e);
573
+ }
574
+ if (await pushDurable("push")) return;
575
+ if (await contentDigest(await gather()) === lastSavedContentHash) return;
576
+ throw new SelfstoreError(
577
+ "TARGET_WRITE_FAILED",
578
+ "attachTarget(): an edit not yet saved to the current destination could not reach it; try again online before switching."
579
+ );
580
+ }
581
+ async function settleRewrite(written, ...followUps) {
582
+ lastError = null;
583
+ try {
584
+ await setRemoteVersion(typeof written === "string" ? written : await statRemote());
585
+ for (const followUp of followUps) await followUp();
586
+ } catch (e) {
587
+ logger.error("[selfstore] bookkeeping after a key rewrite failed; the new key is kept", e);
588
+ }
589
+ await publishReplicas();
590
+ notify();
591
+ }
556
592
  async function requireReady(where) {
557
593
  let ok;
558
594
  try {
@@ -572,7 +608,18 @@ function createLocalStore(opts) {
572
608
  }
573
609
  async function convergeBeforeRewrite(where) {
574
610
  await requireReady(where);
575
- const seen = await statRemote();
611
+ let seen;
612
+ try {
613
+ seen = await statBeforeOverwrite();
614
+ } catch (e) {
615
+ if (isAuthExpired(e)) {
616
+ raiseGate("AUTH_EXPIRED", "Access to the destination expired or was revoked.");
617
+ }
618
+ throw new SelfstoreError(
619
+ "TARGET_WRITE_FAILED",
620
+ `${where}(): cannot tell whether the destination moved; try again online.`
621
+ );
622
+ }
576
623
  if (seen === null || remoteVersion === null || seen === remoteVersion) return;
577
624
  const pulled = await pullRemoteIntoLocal("push", seen);
578
625
  if (locked() || needsAttention) {
@@ -613,19 +660,23 @@ function createLocalStore(opts) {
613
660
  kvSetSafe(KEY.syncJournal, redactForStorage(journal), "journal");
614
661
  return entry;
615
662
  }
663
+ function deferPush() {
664
+ lastError = transient("Save deferred: destination momentarily unreachable.");
665
+ unsent = true;
666
+ }
616
667
  async function pushDurable(source = "push") {
617
668
  if (!durable || locked() || needsAttention) return false;
618
669
  try {
619
670
  if (!await durable.isReady()) {
620
- lastError = transient("Save deferred: destination momentarily unreachable.");
671
+ deferPush();
621
672
  return false;
622
673
  }
623
- const seen = await statRemote();
674
+ const seen = await statBeforeOverwrite();
624
675
  if (seen !== null && remoteVersion !== null && seen !== remoteVersion) {
625
676
  const pulled = await pullRemoteIntoLocal(source, seen);
626
677
  if (locked() || needsAttention) return false;
627
678
  if (pulled === "unreachable") {
628
- lastError = transient("Save deferred: destination momentarily unreachable.");
679
+ deferPush();
629
680
  return false;
630
681
  }
631
682
  }
@@ -633,6 +684,7 @@ function createLocalStore(opts) {
633
684
  const digest = await contentDigest(snap);
634
685
  if (digest === lastSavedContentHash) {
635
686
  lastError = null;
687
+ unsent = false;
636
688
  return true;
637
689
  }
638
690
  const written = await durable.save(await buildBlob(snap));
@@ -640,6 +692,7 @@ function createLocalStore(opts) {
640
692
  lastSavedContentHash = digest;
641
693
  kvSetSafe(KEY.savedHash, digest, "savedHash");
642
694
  lastError = null;
695
+ unsent = false;
643
696
  return true;
644
697
  } catch (e) {
645
698
  if (isAuthExpired(e)) {
@@ -647,7 +700,7 @@ function createLocalStore(opts) {
647
700
  } else if (isSelfstoreError(e) && e.code === "TARGET_GONE") {
648
701
  raiseGate("TARGET_GONE", e.message);
649
702
  } else {
650
- lastError = transient("Save deferred: destination momentarily unreachable.");
703
+ deferPush();
651
704
  }
652
705
  logger.error("[selfstore] push to durable failed", e);
653
706
  return false;
@@ -1029,7 +1082,8 @@ function createLocalStore(opts) {
1029
1082
  needsAttention,
1030
1083
  locked: locked(),
1031
1084
  pendingDownload,
1032
- copy: { at: lastCopyAt, stale: copyIsStale }
1085
+ copy: { at: lastCopyAt, stale: copyIsStale },
1086
+ unsent
1033
1087
  }),
1034
1088
  lastError,
1035
1089
  journal,
@@ -1156,7 +1210,11 @@ function createLocalStore(opts) {
1156
1210
  editSeq++;
1157
1211
  clearTimeout(timer);
1158
1212
  timer = setTimeout(
1159
- () => save().catch((e) => logger.error("[selfstore] scheduled save failed", e)),
1213
+ () => save().catch((e) => {
1214
+ unsent = true;
1215
+ notify();
1216
+ logger.error("[selfstore] scheduled save failed", e);
1217
+ }),
1160
1218
  debounceMs
1161
1219
  );
1162
1220
  armIdleLock();
@@ -1170,7 +1228,7 @@ function createLocalStore(opts) {
1170
1228
  syncIfStale(source) {
1171
1229
  return serialize(async () => {
1172
1230
  if (locked() || needsAttention || mode !== "persistent") return;
1173
- if (!durable?.stat && peerList.length === 0) return;
1231
+ if (!durable?.stat && peerList.length === 0 && !unsent) return;
1174
1232
  const nowMs = Date.now();
1175
1233
  if (nowMs - lastStaleCheck < STALE_CHECK_MS) return;
1176
1234
  lastStaleCheck = nowMs;
@@ -1180,7 +1238,13 @@ function createLocalStore(opts) {
1180
1238
  moved = seen !== null && seen !== remoteVersion;
1181
1239
  }
1182
1240
  if (!moved) moved = await anyPeerMoved();
1183
- if (!moved) return;
1241
+ if (!moved) {
1242
+ if (unsent && durable) {
1243
+ await pushDurable(source);
1244
+ notify();
1245
+ }
1246
+ return;
1247
+ }
1184
1248
  try {
1185
1249
  await doPull(source);
1186
1250
  } catch (e) {
@@ -1236,11 +1300,7 @@ function createLocalStore(opts) {
1236
1300
  await becomePersistent();
1237
1301
  const strategy = attachOpts?.strategy ?? "merge";
1238
1302
  if (strategy === "replace-local" && durable && !locked() && !needsAttention) {
1239
- try {
1240
- await persistLocal();
1241
- await pushDurable("push");
1242
- } catch {
1243
- }
1303
+ await flushBeforeLeaving();
1244
1304
  }
1245
1305
  if (g) {
1246
1306
  const stored = await kv.get(groupSeqKey(g.manifest.group));
@@ -1500,13 +1560,11 @@ function createLocalStore(opts) {
1500
1560
  encrypted = !!password;
1501
1561
  setPass(password || null);
1502
1562
  envelope = null;
1563
+ let written;
1503
1564
  try {
1504
1565
  await persistLocal();
1505
1566
  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);
1567
+ written = await durable.save(await buildBlob());
1510
1568
  } catch (e) {
1511
1569
  encrypted = prevEncrypted;
1512
1570
  setPass(prevPass);
@@ -1515,10 +1573,10 @@ function createLocalStore(opts) {
1515
1573
  if (isAuthExpired(e)) {
1516
1574
  raiseGate("AUTH_EXPIRED", "Access to the destination expired or was revoked.");
1517
1575
  }
1518
- throw e;
1519
- } finally {
1520
1576
  notify();
1577
+ throw e;
1521
1578
  }
1579
+ await settleRewrite(written, () => kv.set(KEY.enc, encrypted));
1522
1580
  });
1523
1581
  },
1524
1582
  async addEncryptionKey(password, id) {
@@ -1565,22 +1623,21 @@ function createLocalStore(opts) {
1565
1623
  const prevEnvelope = envelope;
1566
1624
  const slot = await mintSlot(password, envelope.dataKey, id);
1567
1625
  envelope = { dataKey: envelope.dataKey, slots: [...envelope.slots, slot] };
1626
+ let written;
1568
1627
  try {
1569
1628
  const blob = await buildBlob();
1570
1629
  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;
1630
+ written = await durable.save(blob);
1575
1631
  } catch (e) {
1576
1632
  envelope = prevEnvelope;
1577
1633
  if (isAuthExpired(e)) {
1578
1634
  raiseGate("AUTH_EXPIRED", "Access to the destination expired or was revoked.");
1579
1635
  }
1580
- throw e;
1581
- } finally {
1582
1636
  notify();
1637
+ throw e;
1583
1638
  }
1639
+ await settleRewrite(written);
1640
+ return slot.id;
1584
1641
  });
1585
1642
  },
1586
1643
  async removeEncryptionKey(id) {
@@ -1636,21 +1693,20 @@ function createLocalStore(opts) {
1636
1693
  }
1637
1694
  const prevEnvelope = envelope;
1638
1695
  envelope = { dataKey: envelope.dataKey, slots: remaining };
1696
+ let written;
1639
1697
  try {
1640
1698
  const blob = await buildBlob();
1641
1699
  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;
1700
+ written = await durable.save(blob);
1645
1701
  } catch (e) {
1646
1702
  envelope = prevEnvelope;
1647
1703
  if (isAuthExpired(e)) {
1648
1704
  raiseGate("AUTH_EXPIRED", "Access to the destination expired or was revoked.");
1649
1705
  }
1650
- throw e;
1651
- } finally {
1652
1706
  notify();
1707
+ throw e;
1653
1708
  }
1709
+ await settleRewrite(written);
1654
1710
  });
1655
1711
  },
1656
1712
  async setExternalEncryption(secret, keyRef) {
@@ -1682,6 +1738,7 @@ function createLocalStore(opts) {
1682
1738
  pass = null;
1683
1739
  envelope = { dataKey, slots: [slot] };
1684
1740
  armIdleLock();
1741
+ let written;
1685
1742
  try {
1686
1743
  await persistLocal();
1687
1744
  pendingEdit = false;
@@ -1693,10 +1750,7 @@ function createLocalStore(opts) {
1693
1750
  void 0,
1694
1751
  async () => secret
1695
1752
  );
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);
1753
+ written = await durable.save(blob);
1700
1754
  } catch (e) {
1701
1755
  encrypted = prevEncrypted;
1702
1756
  pass = prevPass;
@@ -1706,10 +1760,10 @@ function createLocalStore(opts) {
1706
1760
  if (isAuthExpired(e)) {
1707
1761
  raiseGate("AUTH_EXPIRED", "Access to the destination expired or was revoked.");
1708
1762
  }
1709
- throw e;
1710
- } finally {
1711
1763
  notify();
1764
+ throw e;
1712
1765
  }
1766
+ await settleRewrite(written, () => kv.set(KEY.enc, true));
1713
1767
  });
1714
1768
  },
1715
1769
  async addExternalKey(secret, keyRef, id) {
@@ -1752,6 +1806,7 @@ function createLocalStore(opts) {
1752
1806
  const prevEnvelope = envelope;
1753
1807
  const slot = await mintExternalSlot(secret, keyRef, envelope.dataKey, id);
1754
1808
  envelope = { dataKey: envelope.dataKey, slots: [...envelope.slots, slot] };
1809
+ let written;
1755
1810
  try {
1756
1811
  const blob = await buildBlob();
1757
1812
  await readBoxWithSync(
@@ -1761,19 +1816,17 @@ function createLocalStore(opts) {
1761
1816
  void 0,
1762
1817
  async () => secret
1763
1818
  );
1764
- const written = await durable.save(blob);
1765
- await setRemoteVersion(typeof written === "string" ? written : await statRemote());
1766
- lastError = null;
1767
- return slot.id;
1819
+ written = await durable.save(blob);
1768
1820
  } catch (e) {
1769
1821
  envelope = prevEnvelope;
1770
1822
  if (isAuthExpired(e)) {
1771
1823
  raiseGate("AUTH_EXPIRED", "Access to the destination expired or was revoked.");
1772
1824
  }
1773
- throw e;
1774
- } finally {
1775
1825
  notify();
1826
+ throw e;
1776
1827
  }
1828
+ await settleRewrite(written);
1829
+ return slot.id;
1777
1830
  });
1778
1831
  },
1779
1832
  async unlockWithExternal(secret) {
@@ -2150,5 +2203,5 @@ function gisDriveAuth(opts) {
2150
2203
  }
2151
2204
 
2152
2205
  export { RESERVED_STORE_MODES, createLocalStore, deriveStatus, gisDriveAuth, indexedDbCache, isLockableCache, isReservedStoreMode, memoryCache, preloadGoogleIdentity };
2153
- //# sourceMappingURL=chunk-GM47BISW.js.map
2154
- //# sourceMappingURL=chunk-GM47BISW.js.map
2206
+ //# sourceMappingURL=chunk-TB3CKC6Z.js.map
2207
+ //# sourceMappingURL=chunk-TB3CKC6Z.js.map