selfstore 1.8.35 → 1.8.36
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/README.md +6 -2
- package/dist/advanced.d.ts +9 -9
- package/dist/advanced.js +1 -1
- package/dist/backups.d.ts +4 -4
- package/dist/{cache-C1edLncd.d.ts → cache-DBIn_Uo8.d.ts} +6 -0
- package/dist/{chunk-SHX36MIE.js → chunk-JE3BLKXO.js} +60 -17
- package/dist/chunk-JE3BLKXO.js.map +1 -0
- package/dist/{chunk-VRWAV3GI.js → chunk-UU4PS3UH.js} +19 -9
- package/dist/chunk-UU4PS3UH.js.map +1 -0
- package/dist/{connect-D7Yzdi9N.d.ts → connect-CdlhiTBy.d.ts} +4 -4
- package/dist/{drive-3K6pBAmG.d.ts → drive-BCRil91Q.d.ts} +2 -2
- package/dist/flows.d.ts +4 -4
- package/dist/flows.js +1 -1
- package/dist/groups.d.ts +2 -2
- package/dist/households.d.ts +7 -0
- package/dist/households.js +13 -3
- package/dist/households.js.map +1 -1
- package/dist/index.d.ts +9 -9
- package/dist/index.js +2 -2
- package/dist/{local-Cr5cCaQ2.d.ts → local-CpoBeJWk.d.ts} +2 -2
- package/dist/{manager-Bl16FR-e.d.ts → manager-0puQEpZc.d.ts} +2 -2
- package/dist/passkey.d.ts +6 -4
- package/dist/passkey.js +5 -4
- package/dist/passkey.js.map +1 -1
- package/dist/proof.d.ts +6 -6
- package/dist/{s3-C2PrgMpD.d.ts → s3-kikEHI-g.d.ts} +2 -2
- package/dist/{simple-Dp1tbrwo.d.ts → simple-BmUQpsBM.d.ts} +6 -6
- package/dist/{store-B4Zo0f6G.d.ts → store-bp1VRW7R.d.ts} +1 -1
- package/dist/widgets.d.ts +5 -5
- package/dist/widgets.js +1 -1
- package/llms.txt +2 -1
- package/package.json +1 -1
- package/dist/chunk-SHX36MIE.js.map +0 -1
- package/dist/chunk-VRWAV3GI.js.map +0 -1
package/README.md
CHANGED
|
@@ -66,13 +66,17 @@ in the environment where writing a real file is easiest. Hand the shell's own
|
|
|
66
66
|
calls over once and it comes back, on a real path:
|
|
67
67
|
|
|
68
68
|
```ts
|
|
69
|
-
import { readFile, writeFile, stat, exists } from '@tauri-apps/plugin-fs';
|
|
69
|
+
import { readFile, writeFile, rename, stat, exists } from '@tauri-apps/plugin-fs';
|
|
70
70
|
import { save, open } from '@tauri-apps/plugin-dialog';
|
|
71
71
|
import { useDesktopFiles } from 'selfstore';
|
|
72
72
|
|
|
73
|
-
useDesktopFiles({ readFile, writeFile, stat, exists, save, open });
|
|
73
|
+
useDesktopFiles({ readFile, writeFile, rename, stat, exists, save, open });
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
+
`rename` is what makes a save safe: the backup is written beside the previous
|
|
77
|
+
one and moved over it, so a full disk or a crash mid-write leaves the last good
|
|
78
|
+
copy whole. Without it the file is overwritten in place.
|
|
79
|
+
|
|
76
80
|
That is the whole integration. `connectFile()`, `file: true` in the connect
|
|
77
81
|
flow and the widgets all keep meaning what they meant; they just write a path.
|
|
78
82
|
The path outlives the session, so the app reopens on its file with nothing
|
package/dist/advanced.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { B as BackupTarget } from './store-
|
|
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-
|
|
3
|
-
import { K as KV } from './cache-
|
|
4
|
-
export { C as CachedFile, L as LocalCache, e as LockableCache, i as indexedDbCache, f as isLockableCache, m as memoryCache } from './cache-
|
|
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-
|
|
6
|
-
import { c as connect$2, f as fromSession$2, a as connect$3, b as fromSession$3, p as peer } from './s3-
|
|
7
|
-
export { S as S3Config, d as S3ConnectOptions, W as WebdavConfig, e as WebdavConnectOptions, g as WebdavPeerOptions } from './s3-
|
|
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-
|
|
9
|
-
export { k as DriveAccount, a as DriveAuth, m as DriveBackupInfo, D as DriveOptions } from './drive-
|
|
1
|
+
import { B as BackupTarget } from './store-bp1VRW7R.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-bp1VRW7R.js';
|
|
3
|
+
import { K as KV } from './cache-DBIn_Uo8.js';
|
|
4
|
+
export { C as CachedFile, L as LocalCache, e as LockableCache, i as indexedDbCache, f as isLockableCache, m as memoryCache } from './cache-DBIn_Uo8.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-CpoBeJWk.js';
|
|
6
|
+
import { c as connect$2, f as fromSession$2, a as connect$3, b as fromSession$3, p as peer } from './s3-kikEHI-g.js';
|
|
7
|
+
export { S as S3Config, d as S3ConnectOptions, W as WebdavConfig, e as WebdavConnectOptions, g as WebdavPeerOptions } from './s3-kikEHI-g.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-BCRil91Q.js';
|
|
9
|
+
export { k as DriveAccount, a as DriveAuth, m as DriveBackupInfo, D as DriveOptions } from './drive-BCRil91Q.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-
|
|
3
|
+
export { RESERVED_STORE_MODES, createLocalStore, deriveStatus, gisDriveAuth, indexedDbCache, isLockableCache, isReservedStoreMode, memoryCache, preloadGoogleIdentity } from './chunk-JE3BLKXO.js';
|
|
4
4
|
import './chunk-3P2W3XQD.js';
|
|
5
5
|
export { datedName, pickFromDisk, saveToDisk } from './chunk-XAONTUEC.js';
|
|
6
6
|
import './chunk-FJGWTBAL.js';
|
package/dist/backups.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { B as BackupsHost } from './manager-
|
|
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-
|
|
1
|
+
import { B as BackupsHost } from './manager-0puQEpZc.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-0puQEpZc.js';
|
|
3
3
|
import { DriveOptions } from './advanced.js';
|
|
4
|
-
import './cache-
|
|
5
|
-
import './store-
|
|
4
|
+
import './cache-DBIn_Uo8.js';
|
|
5
|
+
import './store-bp1VRW7R.js';
|
|
6
6
|
import './sync.js';
|
|
7
7
|
import './machine-Dhbx3Eut.js';
|
|
8
8
|
|
|
@@ -81,6 +81,12 @@ interface KV {
|
|
|
81
81
|
}
|
|
82
82
|
interface LocalCache {
|
|
83
83
|
readonly kv: KV;
|
|
84
|
+
/** A key/value space whose VALUES are sealed under the same key as the
|
|
85
|
+
* payload (the device key, or the session secret of a locked cache): for
|
|
86
|
+
* the bookkeeping that is itself a secret - a share key, a mailbox id, an
|
|
87
|
+
* invitation capability. A locked cache rejects it with PASSWORD_REQUIRED
|
|
88
|
+
* until unlock(). Optional: a cache with nothing at rest has no need of it. */
|
|
89
|
+
readonly sealedKv?: KV;
|
|
84
90
|
/** Load the persisted collections + files, or null if nothing was ever saved. */
|
|
85
91
|
load(): Promise<{
|
|
86
92
|
collections: Record<string, unknown[]>;
|
|
@@ -562,6 +562,11 @@ function createLocalStore(opts) {
|
|
|
562
562
|
}
|
|
563
563
|
return now !== null && now === p.marker ? p.blob : null;
|
|
564
564
|
}
|
|
565
|
+
async function readIncoming(t) {
|
|
566
|
+
const blob = await takeFreshPrefetch(t) ?? await t.load();
|
|
567
|
+
if (blob && blob.size > 0) await inspect(blob);
|
|
568
|
+
return blob;
|
|
569
|
+
}
|
|
565
570
|
async function statRemote() {
|
|
566
571
|
if (!durable?.stat) return null;
|
|
567
572
|
try {
|
|
@@ -999,9 +1004,9 @@ function createLocalStore(opts) {
|
|
|
999
1004
|
return entry;
|
|
1000
1005
|
}
|
|
1001
1006
|
const pull = (source) => serialize(() => doPull(source));
|
|
1002
|
-
async function loadFromRemote() {
|
|
1007
|
+
async function loadFromRemote(incoming) {
|
|
1003
1008
|
if (!durable) return;
|
|
1004
|
-
const remote = await takeFreshPrefetch(durable) ?? await durable.load();
|
|
1009
|
+
const remote = incoming === void 0 ? await takeFreshPrefetch(durable) ?? await durable.load() : incoming;
|
|
1005
1010
|
if (remote) {
|
|
1006
1011
|
const decoded = await importLocked(remote);
|
|
1007
1012
|
if (decoded === "locked") return;
|
|
@@ -1305,8 +1310,9 @@ function createLocalStore(opts) {
|
|
|
1305
1310
|
"attachTarget(): this identity is not a member of the manifest - the admin must add it first."
|
|
1306
1311
|
);
|
|
1307
1312
|
}
|
|
1308
|
-
await becomePersistent();
|
|
1309
1313
|
const strategy = attachOpts?.strategy ?? "merge";
|
|
1314
|
+
const incoming = strategy === "replace-local" ? await readIncoming(target) : void 0;
|
|
1315
|
+
await becomePersistent();
|
|
1310
1316
|
if (strategy === "replace-local" && durable && !locked() && !needsAttention) {
|
|
1311
1317
|
await flushBeforeLeaving();
|
|
1312
1318
|
}
|
|
@@ -1340,7 +1346,7 @@ function createLocalStore(opts) {
|
|
|
1340
1346
|
await kv.set(KEY.targetKind, target.kind);
|
|
1341
1347
|
needsAttention = false;
|
|
1342
1348
|
notify();
|
|
1343
|
-
if (strategy === "replace-local") await loadFromRemote();
|
|
1349
|
+
if (strategy === "replace-local") await loadFromRemote(incoming);
|
|
1344
1350
|
else if (strategy === "replace-remote") await doSave();
|
|
1345
1351
|
else await doPull("connect");
|
|
1346
1352
|
notify();
|
|
@@ -1867,20 +1873,13 @@ function isLockableCache(cache) {
|
|
|
1867
1873
|
}
|
|
1868
1874
|
function memoryCache() {
|
|
1869
1875
|
const kvMap = /* @__PURE__ */ new Map();
|
|
1876
|
+
const sealedMap = /* @__PURE__ */ new Map();
|
|
1870
1877
|
let collections;
|
|
1871
1878
|
const files = /* @__PURE__ */ new Map();
|
|
1872
1879
|
return {
|
|
1873
|
-
kv:
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
},
|
|
1877
|
-
async set(key, value) {
|
|
1878
|
-
kvMap.set(key, value);
|
|
1879
|
-
},
|
|
1880
|
-
async del(key) {
|
|
1881
|
-
kvMap.delete(key);
|
|
1882
|
-
}
|
|
1883
|
-
},
|
|
1880
|
+
kv: mapKv(kvMap),
|
|
1881
|
+
// Nothing is at rest, so "sealed" is a separate space, not a cipher.
|
|
1882
|
+
sealedKv: mapKv(sealedMap),
|
|
1884
1883
|
async load() {
|
|
1885
1884
|
if (collections === void 0) return null;
|
|
1886
1885
|
return { collections: structuredClone(collections), files: [...files.values()] };
|
|
@@ -1894,14 +1893,29 @@ function memoryCache() {
|
|
|
1894
1893
|
},
|
|
1895
1894
|
async clear() {
|
|
1896
1895
|
kvMap.clear();
|
|
1896
|
+
sealedMap.clear();
|
|
1897
1897
|
collections = void 0;
|
|
1898
1898
|
files.clear();
|
|
1899
1899
|
}
|
|
1900
1900
|
};
|
|
1901
1901
|
}
|
|
1902
|
+
function mapKv(map) {
|
|
1903
|
+
return {
|
|
1904
|
+
async get(key) {
|
|
1905
|
+
return map.get(key);
|
|
1906
|
+
},
|
|
1907
|
+
async set(key, value) {
|
|
1908
|
+
map.set(key, value);
|
|
1909
|
+
},
|
|
1910
|
+
async del(key) {
|
|
1911
|
+
map.delete(key);
|
|
1912
|
+
}
|
|
1913
|
+
};
|
|
1914
|
+
}
|
|
1902
1915
|
var COLLECTIONS_KEY = "__collections";
|
|
1903
1916
|
var DEVICE_KEY = "__deviceKey";
|
|
1904
1917
|
var CACHE_KDF = "__cacheKdf";
|
|
1918
|
+
var SEALED_PREFIX = "s:";
|
|
1905
1919
|
async function sealFile(k, meta, bytes) {
|
|
1906
1920
|
const sealedMeta = await seal(k, new TextEncoder().encode(JSON.stringify(meta)));
|
|
1907
1921
|
return { meta: sealedMeta, ...await seal(k, bytes) };
|
|
@@ -1972,10 +1986,31 @@ function indexedDbCache(name, opts) {
|
|
|
1972
1986
|
await (await db()).delete("kv", `m:${key2}`);
|
|
1973
1987
|
}
|
|
1974
1988
|
};
|
|
1989
|
+
const sealedKv = {
|
|
1990
|
+
async get(k) {
|
|
1991
|
+
const raw = await (await db()).get("kv", `${SEALED_PREFIX}${k}`);
|
|
1992
|
+
if (raw === void 0) return void 0;
|
|
1993
|
+
if (!isEnvelope(raw)) {
|
|
1994
|
+
throw new SelfstoreError(
|
|
1995
|
+
"BAD_FORMAT",
|
|
1996
|
+
"Unrecognized cache record (not a sealed envelope)."
|
|
1997
|
+
);
|
|
1998
|
+
}
|
|
1999
|
+
return JSON.parse(new TextDecoder().decode(await unseal(await key(), raw)));
|
|
2000
|
+
},
|
|
2001
|
+
async set(k, value) {
|
|
2002
|
+
const env = await seal(await key(), new TextEncoder().encode(JSON.stringify(value)));
|
|
2003
|
+
await (await db()).put("kv", env, `${SEALED_PREFIX}${k}`);
|
|
2004
|
+
},
|
|
2005
|
+
async del(k) {
|
|
2006
|
+
await (await db()).delete("kv", `${SEALED_PREFIX}${k}`);
|
|
2007
|
+
}
|
|
2008
|
+
};
|
|
1975
2009
|
const written = /* @__PURE__ */ new Map();
|
|
1976
2010
|
const changed = (f, have) => !have.has(f.id) || written.has(f.id) && written.get(f.id) !== f.blob;
|
|
1977
2011
|
const base = {
|
|
1978
2012
|
kv,
|
|
2013
|
+
sealedKv,
|
|
1979
2014
|
async load() {
|
|
1980
2015
|
const d = await db();
|
|
1981
2016
|
const rawCollections = await d.get("kv", COLLECTIONS_KEY);
|
|
@@ -2070,11 +2105,19 @@ function indexedDbCache(name, opts) {
|
|
|
2070
2105
|
const meta = await openFileMeta(previous, v);
|
|
2071
2106
|
files.push({ id: fileKey, rec: await sealFile(next, meta, await unseal(previous, v)) });
|
|
2072
2107
|
}
|
|
2108
|
+
const sealedRecords = [];
|
|
2109
|
+
for (const kvKey of await d.getAllKeys("kv")) {
|
|
2110
|
+
if (typeof kvKey !== "string" || !kvKey.startsWith(SEALED_PREFIX)) continue;
|
|
2111
|
+
const v = await d.get("kv", kvKey);
|
|
2112
|
+
if (!isEnvelope(v)) continue;
|
|
2113
|
+
sealedRecords.push({ id: kvKey, rec: await seal(next, await unseal(previous, v)) });
|
|
2114
|
+
}
|
|
2073
2115
|
const tx = d.transaction(["kv", "files"], "readwrite");
|
|
2074
2116
|
const store = tx.objectStore("kv");
|
|
2075
2117
|
if (kdf) store.put(kdf, CACHE_KDF);
|
|
2076
2118
|
else store.delete(CACHE_KDF);
|
|
2077
2119
|
if (collections) store.put(collections, COLLECTIONS_KEY);
|
|
2120
|
+
for (const { id, rec } of sealedRecords) store.put(rec, id);
|
|
2078
2121
|
const fileStore = tx.objectStore("files");
|
|
2079
2122
|
for (const { id, rec } of files) fileStore.put(rec, id);
|
|
2080
2123
|
await tx.done;
|
|
@@ -2236,5 +2279,5 @@ function gisDriveAuth(opts) {
|
|
|
2236
2279
|
}
|
|
2237
2280
|
|
|
2238
2281
|
export { RESERVED_STORE_MODES, createLocalStore, deriveStatus, gisDriveAuth, indexedDbCache, isLockableCache, isReservedStoreMode, memoryCache, preloadGoogleIdentity };
|
|
2239
|
-
//# sourceMappingURL=chunk-
|
|
2240
|
-
//# sourceMappingURL=chunk-
|
|
2282
|
+
//# sourceMappingURL=chunk-JE3BLKXO.js.map
|
|
2283
|
+
//# sourceMappingURL=chunk-JE3BLKXO.js.map
|