selfstore 1.8.30 → 1.8.32

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/flows.js CHANGED
@@ -1,6 +1,6 @@
1
- export { connectFlow, joinFlow, shareFlow } from './chunk-A6OQP7DR.js';
1
+ export { connectFlow, joinFlow, shareFlow } from './chunk-HRYIQSTH.js';
2
2
  import { s3Connect, webdavConnect, webdavFromSession, s3FromSession } from './chunk-JDDWWQEB.js';
3
- import { connect as connect$1, fromSession as fromSession$1 } from './chunk-2TIRUEXR.js';
3
+ import { connect as connect$1, fromSession as fromSession$1 } from './chunk-HVHQLPP6.js';
4
4
  import { makeMachine, toStoreError } from './chunk-2BNTCXQN.js';
5
5
  export { withDeadline } from './chunk-2BNTCXQN.js';
6
6
  import { connect, fromSession } from './chunk-25U6AKIX.js';
package/dist/groups.d.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { G as GroupIdentity, K as KV } from './cache-ilsK3Tki.js';
2
- export { a as GROUP_KEYING, b as GroupManifest, c as GroupMember, S as SignedManifest, g as generateIdentity, d as groupCryptoAvailable, k as keyId, n as newGroupId, o as openManifest, p as publicIdentity, s as signManifest } from './cache-ilsK3Tki.js';
1
+ import { G as GroupIdentity } from './cache-BEv-Har2.js';
2
+ import { KV } from './advanced.js';
3
+ export { a as GROUP_KEYING, b as GroupManifest, c as GroupMember, S as SignedManifest, g as generateIdentity, d as groupCryptoAvailable, k as keyId, n as newGroupId, o as openManifest, p as publicIdentity, s as signManifest } from './cache-BEv-Har2.js';
3
4
 
4
5
  interface IdentityVault {
5
6
  /** The stored identity, or null when none exists yet. On a protected
@@ -36,4 +37,5 @@ interface IdentityVault {
36
37
  /** An identity vault over the app's kv (e.g. `indexedDbCache(app).kv`). */
37
38
  declare function identityVault(kv: KV): IdentityVault;
38
39
 
39
- export { GroupIdentity, type IdentityVault, identityVault };
40
+ export { GroupIdentity, identityVault };
41
+ export type { IdentityVault };
@@ -1,5 +1,5 @@
1
- import { B as BackupTarget, P as PeerSource, L as LocalStore } from './store-CZOqxDj5.js';
2
- import { K as KV } from './cache-ilsK3Tki.js';
1
+ import { BackupTarget, PeerSource, LocalStore } from './advanced.js';
2
+ import { KV } from './advanced.js';
3
3
  import './sync.js';
4
4
 
5
5
  /** Why a code was refused. `malformed` covers bad transport, wrong structure or
@@ -223,4 +223,5 @@ declare function createHouseholdGroup(deps: {
223
223
  wallet?: () => Promise<string | null>;
224
224
  }): HouseholdGroup;
225
225
 
226
- export { type AnnouncePayload, type CopyLink, type DriveCopyLink, HOUSEHOLD_GROUP_KEY, HouseholdCodeError, type HouseholdCodeErrorCode, type HouseholdGroup, type HouseholdGroupState, type IncomingShare, type ShareBackend, type SharePayload, createHouseholdGroup, decodeAnnounce, decodeShare, encodeAnnounce, encodeShare, randomId, toCopyLink, toRoster };
226
+ export { HOUSEHOLD_GROUP_KEY, HouseholdCodeError, createHouseholdGroup, decodeAnnounce, decodeShare, encodeAnnounce, encodeShare, randomId, toCopyLink, toRoster };
227
+ export type { AnnouncePayload, CopyLink, DriveCopyLink, HouseholdCodeErrorCode, HouseholdGroup, HouseholdGroupState, IncomingShare, ShareBackend, SharePayload };
package/dist/index.d.ts CHANGED
@@ -1,14 +1,15 @@
1
- export { C as CacheUnlock, a as ConnectOutcome, F as FileBytes, P as PutFileInput, b as SimpleOptions, S as SimpleRecord, c as SimpleStore, s as selfstore } from './simple-B430fbx0.js';
2
- export { h as changePassword, g as gisDriveAuth, p as pickFromDisk, f as preloadGoogleIdentity, s as saveToDisk } from './local-CGYx16zM.js';
3
- export { a as DriveAuth } from './drive-DvnzpjLM.js';
4
- export { S as S3Config, W as WebdavConfig } from './s3-iiFYjype.js';
5
- import { a as Snapshot, E as EncodeOptions, H as Header } from './store-CZOqxDj5.js';
6
- export { L as LocalStore, e as LocalStoreState, h as Mode, i as PasswordCheck, b as PasswordPolicy, j as PasswordRequirement, o as SelfstoreError, p as SelfstoreErrorCode, q as Severity, c as SnapshotFile, r as StatusAction, d as StatusDescriptor, t as StorageState, S as StoreError, T as TargetKind, x as checkPasswordPolicy, C as errorLabelKey, G as isSelfstoreError } from './store-CZOqxDj5.js';
1
+ export { C as CacheUnlock, a as ConnectOutcome, F as FileBytes, P as PutFileInput, b as SimpleOptions, S as SimpleRecord, c as SimpleStore, s as selfstore } from './simple-cLzoTGhN.js';
2
+ export { h as changePassword, g as gisDriveAuth, p as pickFromDisk, f as preloadGoogleIdentity, s as saveToDisk } from './local-DytMg5GC.js';
3
+ export { a as DriveAuth } from './drive-CwlKlowN.js';
4
+ export { S as S3Config, W as WebdavConfig } from './s3-Bexn9kzT.js';
5
+ import { a as Snapshot, H as Header } from './store-DSPbCCtM.js';
6
+ import { EncodeOptions } from './advanced.js';
7
+ export { L as LocalStore, e as LocalStoreState, h as Mode, i as PasswordCheck, b as PasswordPolicy, j as PasswordRequirement, o as SelfstoreError, p as SelfstoreErrorCode, q as Severity, c as SnapshotFile, r as StatusAction, d as StatusDescriptor, t as StorageState, S as StoreError, T as TargetKind, x as checkPasswordPolicy, C as errorLabelKey, G as isSelfstoreError } from './store-DSPbCCtM.js';
7
8
  export { SyncConfig } from './sync.js';
8
- import './cache-ilsK3Tki.js';
9
- import './connect-DMFxtXYO.js';
10
- import './machine-ALMkT3mk.js';
11
- import './manager-B-gb3lIn.js';
9
+ import './cache-BEv-Har2.js';
10
+ import './connect-CDazV36v.js';
11
+ import './machine-Dhbx3Eut.js';
12
+ import './manager-CnlIs-hY.js';
12
13
 
13
14
  declare const BACKUP_EXTENSION = ".zip";
14
15
  declare const BACKUP_MIME = "application/zip";
@@ -129,11 +130,11 @@ interface DesktopDialogFilter {
129
130
  * With Tauri v2 the whole wiring is the two plugin imports:
130
131
  *
131
132
  * ```ts
132
- * import { readFile, writeFile, stat, exists } from '@tauri-apps/plugin-fs';
133
+ * import { readFile, writeFile, rename, stat, exists } from '@tauri-apps/plugin-fs';
133
134
  * import { save, open } from '@tauri-apps/plugin-dialog';
134
135
  * import { useDesktopFiles } from 'selfstore';
135
136
  *
136
- * useDesktopFiles({ readFile, writeFile, stat, exists, save, open });
137
+ * useDesktopFiles({ readFile, writeFile, rename, stat, exists, save, open });
137
138
  * ```
138
139
  *
139
140
  * Do that once at start-up and every `file` destination in this library runs on
@@ -142,6 +143,11 @@ interface DesktopDialogFilter {
142
143
  interface DesktopFileBridge {
143
144
  readFile(path: string): Promise<Uint8Array>;
144
145
  writeFile(path: string, data: Uint8Array): Promise<void>;
146
+ /** Move a file over another. Optional for the shells that predate it, and
147
+ * what makes a save safe: with it, the backup is written beside and moved
148
+ * over the previous one, so a full disk or a crash mid-write leaves the
149
+ * last good copy whole instead of a truncated file. */
150
+ rename?(from: string, to: string): Promise<void>;
145
151
  /** File metadata. Only `mtime` is read, and a shell that cannot report it
146
152
  * may leave it absent: the store then falls back to its own bookkeeping. */
147
153
  stat(path: string): Promise<{
@@ -170,4 +176,5 @@ declare function useDesktopFiles(b: DesktopFileBridge | null): void;
170
176
  * real path instead of asking the browser for a handle. */
171
177
  declare function hasDesktopFiles(): boolean;
172
178
 
173
- export { BACKUP_EXTENSION, BACKUP_MIME, BackupBuilder, BackupDraft, type DesktopDialogFilter, type DesktopFileBridge, EncryptedBackupBuilder, Header, RestoreBuilder, Snapshot, backup, hasDesktopFiles, restore, useDesktopFiles };
179
+ export { BACKUP_EXTENSION, BACKUP_MIME, BackupBuilder, BackupDraft, EncryptedBackupBuilder, Header, RestoreBuilder, Snapshot, backup, hasDesktopFiles, restore, useDesktopFiles };
180
+ export type { DesktopDialogFilter, DesktopFileBridge };
package/dist/index.js CHANGED
@@ -5,11 +5,11 @@ import { saveToDisk } from './chunk-Q3NO477P.js';
5
5
  export { pickFromDisk, saveToDisk } from './chunk-Q3NO477P.js';
6
6
  import './chunk-FJGWTBAL.js';
7
7
  import { webdavFromSession, s3Connect, webdavConnect } from './chunk-JDDWWQEB.js';
8
- import { fromSession as fromSession$1, isSupported, connect } from './chunk-2TIRUEXR.js';
9
- export { hasDesktopFiles, useDesktopFiles } from './chunk-2TIRUEXR.js';
8
+ import { fromSession as fromSession$1, isSupported, connect } from './chunk-HVHQLPP6.js';
9
+ export { hasDesktopFiles, useDesktopFiles } from './chunk-HVHQLPP6.js';
10
10
  import { fromSession, connect as connect$1, account } from './chunk-25U6AKIX.js';
11
11
  import './chunk-LF3CC2P3.js';
12
- import { restore } from './chunk-7U5XMKVP.js';
12
+ import { readBoxWithSync, asBytes, RESERVED_COLLECTION_PREFIX } from './chunk-7U5XMKVP.js';
13
13
  export { BACKUP_EXTENSION, BACKUP_MIME, backup, changePassword, checkPasswordPolicy, restore } from './chunk-7U5XMKVP.js';
14
14
  import './chunk-QBCU7OOP.js';
15
15
  import './chunk-F4ZZEJ5H.js';
@@ -84,6 +84,16 @@ function selfNaming(app, auth) {
84
84
  forget: () => auth.forget()
85
85
  };
86
86
  }
87
+ function schemaVersionOf(sidecar) {
88
+ if (typeof sidecar !== "object" || sidecar === null) return 0;
89
+ const v = sidecar.schemaVersion;
90
+ return Number.isInteger(v) && v >= 0 ? v : 0;
91
+ }
92
+ function withoutReserved(collections) {
93
+ return Object.fromEntries(
94
+ Object.entries(collections).filter(([name]) => !name.startsWith(RESERVED_COLLECTION_PREFIX))
95
+ );
96
+ }
87
97
  async function selfstore(app, options = {}) {
88
98
  if (typeof app !== "string" || app.length === 0) {
89
99
  throw new TypeError('selfstore: pass your app name, e.g. selfstore("my-app").');
@@ -108,9 +118,10 @@ async function selfstore(app, options = {}) {
108
118
  let failed = false;
109
119
  while (!await cache.unlock(await options.cacheLock({ failed }))) failed = true;
110
120
  }
121
+ const schema = options.schema ?? 1;
111
122
  const store = createLocalStore({
112
123
  app,
113
- schemaVersion: options.schema ?? 1,
124
+ schemaVersion: schema,
114
125
  gather: () => structuredClone({ collections, files }),
115
126
  apply: (snap) => {
116
127
  collections = snap.collections ?? {};
@@ -331,8 +342,16 @@ async function selfstore(app, options = {}) {
331
342
  return written;
332
343
  },
333
344
  async importBackup(file, opts) {
334
- const reader = restore(file);
335
- const snap = opts?.password ? await reader.withPassword(opts.password).read() : await reader.read();
345
+ const { snapshot, sidecar } = await readBoxWithSync(await asBytes(file), opts?.password);
346
+ const fromVersion = schemaVersionOf(sidecar);
347
+ if (fromVersion > schema) {
348
+ throw new SelfstoreError(
349
+ "SCHEMA_TOO_NEW",
350
+ `Backup written by schema v${fromVersion}; this app reads v${schema}. Update the app.`
351
+ );
352
+ }
353
+ const read = { collections: withoutReserved(snapshot.collections), files: snapshot.files };
354
+ const snap = options.migrate && fromVersion < schema ? options.migrate(fromVersion, read) : read;
336
355
  collections = snap.collections ?? {};
337
356
  files = snap.files ?? [];
338
357
  emitData();
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/simple/simple.ts"],"names":["fromSession","connect"],"mappings":"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","file":"index.js","sourcesContent":["// The simple store: one call, sensible defaults, and the store owns the data.\n//\n// const store = await selfstore('notes-app');\n// await store.put('todos', { id: 't1', text: 'hello' });\n// store.onChange(render);\n//\n// Convention over configuration: schema version 1, an IndexedDB cache named\n// after the app (in-memory where IndexedDB does not exist, so tests and SSR\n// just work), debounced auto-save, and the browser sync moments wired.\n// Every default has an option; everything deeper lives on store.advanced -\n// the full createLocalStore, same instance.\n//\n// One rule is enforced rather than documented: records need a non-empty\n// string id (the merge identifies records by it), so put() throws a\n// TypeError right away. The advanced pull-model store only logs - it cannot\n// know which collections an app considers mergeable.\n\nimport {\n\tcreateLocalStore,\n\ttype LocalStore,\n\ttype LocalStoreState,\n\ttype StoreError\n} from '../persistence/store';\nimport {\n\tindexedDbCache,\n\tmemoryCache,\n\tisLockableCache,\n\ttype KV,\n\ttype LocalCache\n} from '../persistence/cache';\nimport type { BackupTarget } from '../persistence/target';\nimport type { StatusDescriptor } from '../persistence/status';\nimport type { SyncConfig } from '../sync';\nimport type { ConnectTargets, ResumeOffer } from '../flows/connect';\nimport type { BackupsManager } from '../backups/manager';\nimport type { Snapshot, SnapshotFile } from '../selfstore/types';\nimport { SelfstoreError } from '../selfstore/errors';\nimport { sha256Hex } from '../selfstore/digest';\nimport { toBytes, type FileBytes } from './bytes';\nimport { restore, type PasswordPolicy } from '../selfstore';\nimport { saveToDisk } from '../selfstore/targets/local';\nimport {\n\taccount as driveAccount,\n\tconnect as driveConnect,\n\tfromSession as driveFromSession,\n\ttype DriveAuth\n} from '../persistence/targets/drive';\nimport { gisDriveAuth } from '../persistence/targets/drive-auth-gis';\nimport {\n\tconnect as fileConnect,\n\tfromSession as fileFromSession,\n\tisSupported as fileIsSupported\n} from '../persistence/targets/file';\n// WebDAV and S3 arrive on the gesture that uses them - see targets/servers.ts.\nimport { webdavConnect, webdavFromSession, s3Connect } from '../persistence/targets/servers';\nimport type { WebdavConfig } from '../persistence/targets/webdav';\nimport type { S3Config } from '../persistence/targets/s3';\n\n/** A record as the simple store sees it: plain JSON with a string id (the id\n * field name can be remapped per collection via `sync.ids`). */\nexport type SimpleRecord = Record<string, unknown>;\n\nexport type { FileBytes } from './bytes';\n\n/** A file on its way into the store. Omit `id` to get the content hash, which\n * is the id that makes the multi-device union safe - see `putFile`. */\nexport interface PutFileInput {\n\t/** Defaults to the SHA-256 of the bytes. */\n\tid?: string;\n\t/** Defaults to the id. Cosmetic: nothing keys on it. */\n\tname?: string;\n\t/** Defaults to 'application/octet-stream'. */\n\tmime?: string;\n\tbytes: FileBytes;\n}\n\n/** Unlock callback for the ultra-sensitive cacheLock mode: return the session\n * secret - a password (Argon2id-derived) or an app-provided key (e.g. a passkey\n * PRF result). Called at boot; called again with { failed: true } after a wrong\n * secret so the app can re-prompt. Throw to abort the boot (user cancelled). */\nexport type CacheUnlock = (attempt: { failed: boolean }) => Promise<string | CryptoKey>;\n\n/** Options - every one of them optional, the defaults carry a real app. */\nexport interface SimpleOptions {\n\t/** Your DATA schema version (default 1). Bump it together with `migrate`. */\n\tschema?: number;\n\t/** Upgrade a snapshot written by an older schema version. */\n\tmigrate?: (fromVersion: number, snap: Snapshot) => Snapshot;\n\t/** Per-collection merge tuning (id field mapping, strategies). The default -\n\t * records keyed by `id`, later edit wins, deletes propagate - fits most apps. */\n\tsync?: SyncConfig;\n\t/**\n\t * Google Drive, when this app uses it. Two shapes for two situations:\n\t *\n\t * `{ clientId }` is the whole wiring - the library builds the browser\n\t * connection, keeps ONE session for the app (several would mean several\n\t * consent screens), reuses its token across a reload, remembers which\n\t * account holds the backup and hands that back to Google so the chooser\n\t * stops appearing. Nothing else to write.\n\t *\n\t * A `DriveAuth` instead, for an app that gets its tokens elsewhere - a\n\t * server broker holding a refresh token, a native shell.\n\t *\n\t * Either way, providing it here is also what lets a connected Drive backup\n\t * restore itself on the next start.\n\t */\n\tdrive?: DriveAuth | { clientId: string; scope?: string };\n\t/** Where the working copy lives (default: IndexedDB named after the app;\n\t * in-memory when IndexedDB does not exist, e.g. tests or SSR). */\n\tcache?: LocalCache;\n\t/** Auto-save debounce in ms. */\n\tdebounceMs?: number;\n\t/** Cross-tab coordination (default on in browsers). */\n\tmultiTab?: boolean;\n\t/** Refuse to ever write or export a plaintext backup: connecting a\n\t * destination then demands a password (or a group). Off by default. */\n\trequireEncryption?: boolean;\n\t/** Reject a backup password weaker than this policy (length, character\n\t * classes) at protect()/addKey time. Preview the rules with\n\t * checkPasswordPolicy for a live UI hint. Omit for no policy. */\n\tpasswordPolicy?: PasswordPolicy;\n\t/** Ultra-sensitive mode: seal the local cache under a key derived from a\n\t * secret held in memory (this callback), never on disk - so a copy of the\n\t * browser profile cannot read it. One unlock per session; branch it on the\n\t * app's existing login to leave the UX unchanged. Browser (IndexedDB) only. */\n\tcacheLock?: CacheUnlock;\n\t/** Wire tab focus / network return / interval / tab hide automatically\n\t * (default true in browsers). Pass false to drive syncing yourself. */\n\tautoSync?: boolean;\n}\n\n/** What connecting a destination did. 'merged': it already held a backup and\n * both sides were folded together. 'started': it was empty, this device's data\n * is now its content. 'manual': non-Chromium file fallback - offer\n * downloadBackup() instead. 'cancelled': the user closed the picker/consent. */\nexport type ConnectOutcome = 'merged' | 'started' | 'manual' | 'cancelled';\n\nexport interface SimpleStore<\n\tS extends Record<string, SimpleRecord> = Record<string, SimpleRecord>\n> {\n\t// --- Your data ---------------------------------------------------------\n\t/** Every record of a collection (treat as read-only; write via put/remove). */\n\tall<K extends keyof S & string>(collection: K): readonly S[K][];\n\t/** One record by id, or undefined. */\n\tget<K extends keyof S & string>(collection: K, id: string): S[K] | undefined;\n\t/** Insert or replace one record (auto-saves, debounced). Throws a TypeError\n\t * when the record has no non-empty string id. */\n\tput<K extends keyof S & string>(collection: K, record: S[K]): Promise<void>;\n\t/** Insert or replace many records in one save. */\n\tputAll<K extends keyof S & string>(collection: K, records: S[K][]): Promise<void>;\n\t/** Delete one record by id (propagates to other devices). Unknown id: no-op. */\n\tremove<K extends keyof S & string>(collection: K, id: string): Promise<void>;\n\t/** Empty a collection (every removal propagates). */\n\tclear<K extends keyof S & string>(collection: K): Promise<void>;\n\t/** Called after any data change: your writes, another tab, another device,\n\t * a restore. Returns an unsubscribe. */\n\tonChange(fn: () => void): () => void;\n\n\t// --- Your files (bytes that ride along in the same backup) --------------\n\t/** Every file (treat as read-only; write via putFile/removeFile). */\n\tallFiles(): readonly SnapshotFile[];\n\t/** One file by id, or undefined. */\n\tgetFile(id: string): SnapshotFile | undefined;\n\t/**\n\t * Store bytes alongside the collections and return the file's id.\n\t *\n\t * Files are **immutable per id**. The multi-device merge unions them by id\n\t * and has no clock to order two bodies, so when two devices hold DIFFERENT\n\t * bytes under one id, one is kept and the other is dropped - with no\n\t * conflict reported, because at that level there is nothing to compare.\n\t *\n\t * Hence the default id: the SHA-256 of the bytes. Different bytes land on\n\t * different ids and the union keeps both, which is what makes a CRDT\n\t * document (Yjs, Automerge) safe to carry here - store its UPDATES, let the\n\t * union collect them, fold them at load. Tie a file's lifetime to a record\n\t * so deleting the record tells you which files to remove.\n\t *\n\t * Naming the id yourself puts that rule in your hands: identical bytes\n\t * under an existing id are a no-op, different bytes are refused with a\n\t * TypeError. `{ replace: true }` does it anyway - right for a body only\n\t * ever written on one device (a thumbnail, a rendered export), a silent\n\t * loser as soon as two devices write it.\n\t */\n\tputFile(file: PutFileInput, opts?: { replace?: boolean }): Promise<string>;\n\t/** Delete one file by id (propagates to other devices). Unknown id: no-op. */\n\tremoveFile(id: string): Promise<void>;\n\n\t// --- Where it lives ----------------------------------------------------\n\t/** Connect Google Drive as the durable home. An existing backup there is\n\t * MERGED with this device (multi-device); an encrypted one needs its\n\t * password up front (PASSWORD_REQUIRED is thrown before anything changes). */\n\tconnectDrive(auth: DriveAuth, opts?: { password?: string }): Promise<ConnectOutcome>;\n\t/** Connect a disk file (File System Access; falls back to manual download). */\n\tconnectFile(opts?: { password?: string }): Promise<ConnectOutcome>;\n\t/** Connect a WebDAV server (Nextcloud, ownCloud, your own). */\n\tconnectWebdav(config: WebdavConfig, opts?: { password?: string }): Promise<ConnectOutcome>;\n\t/** Connect an S3-compatible bucket you control (Amazon S3, R2, B2, MinIO). */\n\tconnectS3(config: S3Config, opts?: { password?: string }): Promise<ConnectOutcome>;\n\t/** Connect any custom BackupTarget with the same merge semantics. */\n\tconnectTarget(target: BackupTarget, opts?: { password?: string }): Promise<ConnectOutcome>;\n\t/** Back to device-only (the destination keeps its last backup). */\n\tdisconnect(): Promise<void>;\n\n\t// --- Backup copy (replica) ---------------------------------------------\n\t/** Also write the same encrypted backup to this second destination on\n\t * every save. A broken copy never gates the store (state.replicas). */\n\taddReplica(target: BackupTarget, opts?: { id?: string }): string;\n\t/** Stop writing the copy (the destination keeps its last backup). */\n\tremoveReplica(id: string): void;\n\n\t// --- Protection & the two gate gestures --------------------------------\n\t/** Encrypt the durable backup end to end with this password (reversible). */\n\tprotect(password: string): Promise<void>;\n\t/** Remove the backup password. */\n\tunprotect(): Promise<void>;\n\t/** status.action === 'unlock': supply the password to resume. */\n\tunlock(password: string): Promise<boolean>;\n\t/** status.action === 'reconnect': re-run the destination's auth gesture. */\n\treconnect(): Promise<boolean>;\n\n\t// --- Portable backups ---------------------------------------------------\n\t/** The portable backup file (a real ZIP; encrypted when protect() is on).\n\t *\n\t * `{ plaintext: true }` asks for a READABLE copy of a protected store: the\n\t * same ZIP with `selfstore.json` in the clear, which any tool can open and\n\t * git can diff. It is the answer to \"can I actually get my data out\", and\n\t * it costs nothing: the store, its password and its destination are\n\t * untouched, so the real backup stays encrypted. Before this, the only way\n\t * out was `unprotect()`, which rewrites the destination and leaves it\n\t * readable until you remember to undo it.\n\t *\n\t * Refused while locked (there is nothing readable to write), and refused on\n\t * a store created with `requireEncryption` - that flag means this store\n\t * never produces cleartext, and an escape hatch would make it a lie. */\n\texportBackup(opts?: { plaintext?: boolean }): Promise<Blob>;\n\t/** Download the backup (also resolves the 'manual' file mode's pending flag).\n\t * False when the user closed the save dialog: nothing was written, the\n\t * pending flag stands, and the app must not record a backup. */\n\tdownloadBackup(filename?: string): Promise<boolean>;\n\t/** Load a backup file into this store, replacing the local data (removals\n\t * propagate like edits). Throws PASSWORD_REQUIRED / DECRYPT_FAILED. */\n\timportBackup(file: Blob | Uint8Array, opts?: { password?: string }): Promise<void>;\n\n\t// --- Observability ------------------------------------------------------\n\t/** Headless status: { state, severity, action, labelKey } - map labelKey to\n\t * your own copy. */\n\treadonly status: StatusDescriptor;\n\t/** The last problem: { code, labelKey, message } - show labelKey, log message. */\n\treadonly error: StoreError | null;\n\t/** The full underlying state (advanced reads: journal, peers, mode...). */\n\treadonly state: LocalStoreState;\n\t/** Fires on any state change (status flips included) - for framework bindings. */\n\tsubscribe(fn: () => void): () => void;\n\n\t// --- Lifecycle ----------------------------------------------------------\n\t/** Save now (called for you on tab hide when autoSync is on). */\n\tflush(): Promise<void>;\n\t/** Converge with the destination now (a user gesture like pull-to-refresh). */\n\tsync(): Promise<void>;\n\t/** Drop timers and listeners (tests, SPA teardown). */\n\tdispose(): void;\n\n\t/** The escape hatch: the full advanced store this simple one is built on -\n\t * peers, groups, custom strategies, everything (see selfstore/advanced). */\n\treadonly advanced: LocalStore;\n\n\t/** The attachment point for 'selfstore/flows': the engine plus the cache KV\n\t * and backup file name a flow needs to build destination targets. Apps on\n\t * the advanced store hand a flow the same three by themselves. */\n\treadonly flowHost: { engine: LocalStore; kv: KV; backupName: string };\n\n\t/** Who holds the destination, when it is known - the Google address behind a\n\t * Drive backup. Learned on connect and remembered across sessions, so a\n\t * surface can NAME the destination (\"Google Drive\" is not an address) and a\n\t * re-grant can skip the account chooser. Null for a file, or before a first\n\t * connect. Reading it costs nothing: no call goes out. */\n\treadonly account: string | null;\n\n\t/** The destination this device already used, ready to hand to a connect flow\n\t * as `options.resume` - so a returning visitor is offered \"reopen my backup\"\n\t * ahead of the plain destinations, instead of re-deriving a decision the\n\t * library already has on file. Null when there is nothing to resume. */\n\tresumeOffer(): ResumeOffer | null;\n\n\t/**\n\t * The several-backups manager for this store's destination, or null when\n\t * there is nothing to manage several of (no Drive configured).\n\t *\n\t * The port under it - list, open by id, create, rename, delete - was the last\n\t * thing an app still assembled from the session the store had built, and the\n\t * one place it could pick the wrong file id. Built once and remembered; the\n\t * backups module is imported on demand, so an app that never asks does not\n\t * carry it.\n\t */\n\tbackups(): Promise<BackupsManager | null>;\n\n\t/**\n\t * The destinations this store can offer, ready for a connect flow or widget:\n\t * Drive first when the app configured it, a file always.\n\t *\n\t * Consumers were assembling this by hand from the very session the store\n\t * built - and the order and the \"no client id means no Drive card\" rule were\n\t * rediscovered each time. An app wanting a different offer (a WebDAV server,\n\t * its own target) still writes its own object; this is the default, not a\n\t * ceiling.\n\t */\n\tdestinations(): ConnectTargets;\n}\n\n/** The id field a collection's records are keyed by: `sync.ids` may remap it;\n * a DOTTED path (nested id) is an advanced setup the simple check skips. */\nfunction idFieldOf(sync: SyncConfig | undefined, collection: string): string | null {\n\tconst mapped = sync?.ids?.[collection];\n\tif (mapped == null) return 'id';\n\treturn mapped.includes('.') ? null : mapped;\n}\n\nfunction requireStringId(record: SimpleRecord, field: string | null, collection: string): string {\n\tif (field === null) return ''; // nested-path ids: validated by the engine's own rules\n\tconst id = record[field];\n\tif (typeof id !== 'string' || id.length === 0) {\n\t\tthrow new TypeError(\n\t\t\t`selfstore: a record in \"${collection}\" needs a non-empty STRING \"${field}\" ` +\n\t\t\t\t`(got ${JSON.stringify(id)}). String ids are what the multi-device merge keys on; ` +\n\t\t\t\t`map another field with { sync: { ids: { ${collection}: 'yourField' } } }.`\n\t\t);\n\t}\n\treturn id;\n}\n\nfunction sameBytes(a: Uint8Array, b: Uint8Array): boolean {\n\treturn a.length === b.length && a.every((v, i) => v === b[i]);\n}\n\n/** True when a browser IndexedDB is available (SSR and plain Node lack it). */\nfunction hasIndexedDb(): boolean {\n\treturn typeof indexedDB !== 'undefined';\n}\n\n/** Told apart by shape: a DriveAuth mints tokens, a `{ clientId }` asks us to. */\nfunction isDriveAuth(v: SimpleOptions['drive']): v is DriveAuth {\n\treturn !!v && typeof (v as DriveAuth).token === 'function';\n}\n\n/** Where the connected Drive address is kept, per app: it is what stops Google\n * asking WHICH account at every re-grant. An address is not a secret, and a\n * browser that refuses storage simply gets the chooser back. */\nconst driveAccountKey = (app: string): string => `selfstore.drive.account.${app}`;\n\nfunction localStore<T>(job: (store: Storage) => T): T | undefined {\n\ttry {\n\t\tconst store = globalThis.localStorage;\n\t\treturn store ? job(store) : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction rememberedDriveAccount(app: string): string | undefined {\n\treturn localStore((s) => s.getItem(driveAccountKey(app))) ?? undefined;\n}\n\n/** Learn the account behind a live Drive session, best-effort and once: it feeds\n * the next re-grant's hint, and any surface that wants to name the destination. */\nasync function learnDriveAccount(app: string, auth: DriveAuth): Promise<void> {\n\ttry {\n\t\tconst who = await driveAccount({ auth });\n\t\tif (who.email) localStore((s) => s.setItem(driveAccountKey(app), who.email!));\n\t} catch {\n\t\t// A destination that will not say costs the hint, never the connection.\n\t}\n}\n\n/**\n * The same connection, which learns whose it is the first time it hands out a\n * token.\n *\n * Learning it took an explicit connect, so anyone whose backup was attached\n * before this existed - or attached in another session - kept a destination\n * with no name on it: the panel said \"Google Drive\" where it could have said\n * which one, and the offer to reopen that backup had no address to show, which\n * is most of what makes it recognisable.\n *\n * It cannot simply be asked for at start-up: reading the account needs a token,\n * and asking for a token with no user gesture behind it opens a popup the\n * browser blocks. So it rides along instead - every token this app obtains,\n * whatever asked for it, teaches it the address at no cost. Once known, nothing\n * is called again.\n */\nfunction selfNaming(app: string, auth: DriveAuth): DriveAuth {\n\tlet learning: Promise<void> | null = null;\n\tconst learn = (): void => {\n\t\tif (learning || rememberedDriveAccount(app)) return;\n\t\tlearning = learnDriveAccount(app, auth);\n\t};\n\treturn {\n\t\tasync token(opts) {\n\t\t\tconst value = await auth.token(opts);\n\t\t\tlearn();\n\t\t\treturn value;\n\t\t},\n\t\tasync reconnect() {\n\t\t\tconst ok = await auth.reconnect();\n\t\t\tif (ok) learn();\n\t\t\treturn ok;\n\t\t},\n\t\tforget: () => auth.forget()\n\t};\n}\n\n/**\n * Open (or create) the app's local store. Awaits the initial load, so the\n * returned store is ready: data readable, destination restored, first converge\n * done. See SimpleOptions for the defaults.\n */\nexport async function selfstore<\n\tS extends Record<string, SimpleRecord> = Record<string, SimpleRecord>\n>(app: string, options: SimpleOptions = {}): Promise<SimpleStore<S>> {\n\tif (typeof app !== 'string' || app.length === 0) {\n\t\tthrow new TypeError('selfstore: pass your app name, e.g. selfstore(\"my-app\").');\n\t}\n\n\t// The one copy of the data. The engine pulls it on save (gather) and pushes\n\t// into it on restores/folds (apply); the facade's mutators edit it in place\n\t// copy-on-write and schedule a save.\n\tlet collections: Record<string, SimpleRecord[]> = {};\n\t// Binary files: written through putFile/removeFile, and carried untouched\n\t// through every save, backup and merge whoever wrote them.\n\tlet files: SnapshotFile[] = [];\n\n\tconst dataSubs = new Set<() => void>();\n\tconst emitData = (): void => dataSubs.forEach((fn) => fn());\n\n\tconst cache =\n\t\toptions.cache ??\n\t\t(hasIndexedDb()\n\t\t\t? indexedDbCache(app, options.cacheLock ? { lock: true } : undefined)\n\t\t\t: memoryCache());\n\tconst backupName = `${app}.zip`;\n\t// connectDrive() remembers the auth so a later restoreTarget can use it even\n\t// when options.drive was not provided up front (same session only).\n\t//\n\t// `{ clientId }` is the whole wiring done here rather than in every app: ONE\n\t// session (several would mean several consent screens), a token that survives\n\t// a reload, and the account remembered so Google stops asking which one. Each\n\t// of those was a separate decision every consumer had to discover; the app\n\t// that wrote them by hand hit all three as bugs first.\n\t/** Built at most once: two managers over one destination would each hold\n\t * their own idea of which file is active. */\n\tlet backupsManager: BackupsManager | null = null;\n\t// Wrapped whichever shape it came in as: an app that mints its own tokens\n\t// (a server broker, a native shell) has the same nameless-destination\n\t// problem, and the wrapper only ever rides along on a token already granted.\n\tlet driveAuth: DriveAuth | null = isDriveAuth(options.drive)\n\t\t? selfNaming(app, options.drive)\n\t\t: options.drive\n\t\t\t? selfNaming(\n\t\t\t\t\tapp,\n\t\t\t\t\tgisDriveAuth({\n\t\t\t\t\t\tclientId: options.drive.clientId,\n\t\t\t\t\t\tscope: options.drive.scope,\n\t\t\t\t\t\tpersist: 'session',\n\t\t\t\t\t\thint: () => rememberedDriveAccount(app)\n\t\t\t\t\t})\n\t\t\t\t)\n\t\t\t: null;\n\n\t// Ultra-sensitive: unlock the cache before the store hydrates from it. One\n\t// prompt per session, driven by the app (branch it on an existing login to\n\t// keep the UX unchanged). A wrong secret re-prompts; throw to abort.\n\tif (options.cacheLock && isLockableCache(cache)) {\n\t\tlet failed = false;\n\t\twhile (!(await cache.unlock(await options.cacheLock({ failed })))) failed = true;\n\t}\n\n\tconst store = createLocalStore({\n\t\tapp,\n\t\tschemaVersion: options.schema ?? 1,\n\t\tgather: (): Snapshot => structuredClone({ collections, files }),\n\t\tapply: (snap: Snapshot): void => {\n\t\t\tcollections = (snap.collections ?? {}) as Record<string, SimpleRecord[]>;\n\t\t\tfiles = snap.files ?? [];\n\t\t\temitData();\n\t\t},\n\t\tmigrate: options.migrate,\n\t\tsync: options.sync,\n\t\tcache,\n\t\tdebounceMs: options.debounceMs,\n\t\tmultiTab: options.multiTab,\n\t\trequireEncryption: options.requireEncryption,\n\t\tpasswordPolicy: options.passwordPolicy,\n\t\trestoreTarget: async (kind) => {\n\t\t\tif (kind === 'drive' && driveAuth) {\n\t\t\t\treturn driveFromSession({ auth: driveAuth, kv: cache.kv, fileName: backupName });\n\t\t\t}\n\t\t\tif (kind === 'file') return fileFromSession({ kv: cache.kv });\n\t\t\tif (kind === 'webdav') return webdavFromSession({ kv: cache.kv });\n\t\t\treturn null;\n\t\t}\n\t});\n\n\tawait store.init();\n\n\t// A degraded file mode is a verdict about the BROWSER, but it was persisted\n\t// like a property of the data: every later session re-read the word without\n\t// ever asking again. One session that could not open a picker - a browser\n\t// that ships the API and refuses it, a call made a moment too late, a user on\n\t// another browser that day - then pinned download-only mode forever, on a\n\t// Chromium perfectly able to hold a file, with no way back.\n\t//\n\t// Nothing writes this word except a picker that could not deliver, so a\n\t// browser that CAN deliver makes it stale. Re-examined here, in the layer\n\t// that wrote it. Dropping to 'device' is the honest state: manual mode had\n\t// already released the durable target, so there is nothing to detach - only\n\t// a stale claim to stop repeating, and a destination the host can offer again.\n\tif (store.state.targetKind === 'file-manual' && fileIsSupported()) {\n\t\tawait store.detachTarget();\n\t}\n\n\t// Auto-wired sync moments (Spring-Boot-style autoconfiguration): tab focus,\n\t// network return, a slow interval, and save-on-hide. Opt out with\n\t// { autoSync: false } to drive these yourself.\n\tconst teardown: (() => void)[] = [];\n\tif (\n\t\toptions.autoSync !== false &&\n\t\ttypeof document !== 'undefined' &&\n\t\ttypeof window !== 'undefined'\n\t) {\n\t\tconst onVisibility = (): void => {\n\t\t\tif (document.visibilityState === 'hidden') void store.flush();\n\t\t\telse void store.syncIfStale('focus');\n\t\t};\n\t\tconst onOnline = (): void => void store.syncIfStale('online');\n\t\tconst onPageHide = (): void => void store.flush();\n\t\tconst interval = setInterval(() => {\n\t\t\tif (document.visibilityState === 'visible') void store.syncIfStale('interval');\n\t\t}, 5 * 60_000);\n\t\tdocument.addEventListener('visibilitychange', onVisibility);\n\t\twindow.addEventListener('online', onOnline);\n\t\twindow.addEventListener('pagehide', onPageHide);\n\t\tteardown.push(() => {\n\t\t\tclearInterval(interval);\n\t\t\tdocument.removeEventListener('visibilitychange', onVisibility);\n\t\t\twindow.removeEventListener('online', onOnline);\n\t\t\twindow.removeEventListener('pagehide', onPageHide);\n\t\t});\n\t}\n\n\tfunction mutate(collection: string, next: SimpleRecord[]): Promise<void> {\n\t\tcollections = { ...collections, [collection]: next };\n\t\temitData();\n\t\tstore.schedule();\n\t\treturn Promise.resolve();\n\t}\n\n\tfunction mutateFiles(next: SnapshotFile[]): Promise<void> {\n\t\tfiles = next;\n\t\temitData();\n\t\tstore.schedule();\n\t\treturn Promise.resolve();\n\t}\n\n\tasync function connectTarget(\n\t\ttarget: BackupTarget,\n\t\topts: { password?: string } = {}\n\t): Promise<ConnectOutcome> {\n\t\tconst info = await store.inspectTarget(target);\n\t\tif (info.hasBackup) {\n\t\t\t// Another app's backup is refused BEFORE the password question: asking\n\t\t\t// first teaches the user their password \"does not work\" on a file that\n\t\t\t// was never this app's to open.\n\t\t\tif (info.app !== null && info.app !== store.app) {\n\t\t\t\tthrow new SelfstoreError(\n\t\t\t\t\t'FOREIGN_BACKUP',\n\t\t\t\t\t`This destination holds a backup written by app \"${info.app}\", not \"${store.app}\". Open it in its own application, or pick another file.`\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Fail before attaching: an encrypted backup without its password would\n\t\t\t// otherwise attach locked, which is a support case, not an outcome.\n\t\t\tif (info.encrypted && !opts.password) {\n\t\t\t\tthrow new SelfstoreError(\n\t\t\t\t\t'PASSWORD_REQUIRED',\n\t\t\t\t\t'This destination holds an encrypted backup: pass its password to connect.'\n\t\t\t\t);\n\t\t\t}\n\t\t\tawait store.attachTarget(target, { password: opts.password ?? null, strategy: 'merge' });\n\t\t\treturn 'merged';\n\t\t}\n\t\tawait store.attachTarget(target, {\n\t\t\tpassword: opts.password ?? null,\n\t\t\tstrategy: 'replace-remote'\n\t\t});\n\t\treturn 'started';\n\t}\n\n\tconst simple: SimpleStore<S> = {\n\t\tall<K extends keyof S & string>(collection: K): readonly S[K][] {\n\t\t\treturn (collections[collection] ?? []) as S[K][];\n\t\t},\n\t\tget<K extends keyof S & string>(collection: K, id: string): S[K] | undefined {\n\t\t\tconst field = idFieldOf(options.sync, collection) ?? 'id';\n\t\t\treturn (collections[collection] ?? []).find((r) => r[field] === id) as S[K] | undefined;\n\t\t},\n\t\tput<K extends keyof S & string>(collection: K, record: S[K]): Promise<void> {\n\t\t\tconst field = idFieldOf(options.sync, collection);\n\t\t\tconst id = requireStringId(record, field, collection);\n\t\t\tconst rows = collections[collection] ?? [];\n\t\t\tconst at = field === null ? -1 : rows.findIndex((r) => r[field] === id);\n\t\t\treturn mutate(\n\t\t\t\tcollection,\n\t\t\t\tat === -1 ? [...rows, record] : rows.map((r, i) => (i === at ? record : r))\n\t\t\t);\n\t\t},\n\t\tputAll<K extends keyof S & string>(collection: K, records: S[K][]): Promise<void> {\n\t\t\tconst field = idFieldOf(options.sync, collection);\n\t\t\tconst byId = new Map<unknown, number>();\n\t\t\tfor (const r of records) requireStringId(r, field, collection);\n\t\t\tconst rows = [...(collections[collection] ?? [])];\n\t\t\tif (field !== null) {\n\t\t\t\trows.forEach((r, i) => byId.set(r[field], i));\n\t\t\t\tfor (const r of records) {\n\t\t\t\t\tconst at = byId.get(r[field]);\n\t\t\t\t\tif (typeof at === 'number') rows[at] = r;\n\t\t\t\t\telse rows.push(r);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\trows.push(...records);\n\t\t\t}\n\t\t\treturn mutate(collection, rows);\n\t\t},\n\t\tremove<K extends keyof S & string>(collection: K, id: string): Promise<void> {\n\t\t\tconst field = idFieldOf(options.sync, collection) ?? 'id';\n\t\t\tconst rows = collections[collection] ?? [];\n\t\t\tconst next = rows.filter((r) => r[field] !== id);\n\t\t\treturn next.length === rows.length ? Promise.resolve() : mutate(collection, next);\n\t\t},\n\t\tclear<K extends keyof S & string>(collection: K): Promise<void> {\n\t\t\tif ((collections[collection] ?? []).length === 0) return Promise.resolve();\n\t\t\treturn mutate(collection, []);\n\t\t},\n\t\tonChange(fn: () => void): () => void {\n\t\t\tdataSubs.add(fn);\n\t\t\treturn () => dataSubs.delete(fn);\n\t\t},\n\n\t\tallFiles(): readonly SnapshotFile[] {\n\t\t\treturn files;\n\t\t},\n\t\tgetFile(id: string): SnapshotFile | undefined {\n\t\t\treturn files.find((f) => f.id === id);\n\t\t},\n\t\tasync putFile(file: PutFileInput, opts: { replace?: boolean } = {}): Promise<string> {\n\t\t\tconst bytes = await toBytes(file.bytes);\n\t\t\tconst id = file.id ?? (await sha256Hex(bytes));\n\t\t\tif (typeof id !== 'string' || id.length === 0) {\n\t\t\t\tthrow new TypeError(\n\t\t\t\t\t`selfstore: a file needs a non-empty STRING id (got ${JSON.stringify(file.id)}), ` +\n\t\t\t\t\t\t`or omit it to get the content hash.`\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst at = files.findIndex((f) => f.id === id);\n\t\t\t// Same bytes under the same id is the normal case for a content id -\n\t\t\t// re-putting an unchanged body must not schedule a save.\n\t\t\tif (at !== -1 && !opts.replace) {\n\t\t\t\tif (sameBytes(files[at].bytes, bytes)) return id;\n\t\t\t\tthrow new TypeError(\n\t\t\t\t\t`selfstore: file \"${id}\" already holds different bytes. Files are immutable per id ` +\n\t\t\t\t\t\t`because the multi-device merge unions them by id and cannot order two bodies: ` +\n\t\t\t\t\t\t`the other device's copy would be dropped in silence. Omit the id to get a ` +\n\t\t\t\t\t\t`content-addressed one, or pass { replace: true } if this file is only ever ` +\n\t\t\t\t\t\t`written on one device.`\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst entry: SnapshotFile = {\n\t\t\t\tid,\n\t\t\t\tname: file.name ?? id,\n\t\t\t\tmime: file.mime ?? 'application/octet-stream',\n\t\t\t\tbytes\n\t\t\t};\n\t\t\tawait mutateFiles(\n\t\t\t\tat === -1 ? [...files, entry] : files.map((f, i) => (i === at ? entry : f))\n\t\t\t);\n\t\t\treturn id;\n\t\t},\n\t\tremoveFile(id: string): Promise<void> {\n\t\t\tconst next = files.filter((f) => f.id !== id);\n\t\t\treturn next.length === files.length ? Promise.resolve() : mutateFiles(next);\n\t\t},\n\n\t\tasync connectDrive(auth: DriveAuth, opts?: { password?: string }): Promise<ConnectOutcome> {\n\t\t\tdriveAuth = auth;\n\t\t\tconst target = await driveConnect({ auth, kv: cache.kv, fileName: backupName });\n\t\t\tif (!target) return 'cancelled';\n\t\t\tconst outcome = await connectTarget(target, opts);\n\t\t\t// Learn WHICH account now that there is a session: it is the hint that\n\t\t\t// keeps the chooser away next time, and the name a panel can show. Not\n\t\t\t// awaited into the outcome - a slow metadata call must not delay the\n\t\t\t// connect the user is waiting on.\n\t\t\tif (outcome !== 'cancelled') void learnDriveAccount(app, auth);\n\t\t\treturn outcome;\n\t\t},\n\t\tasync connectFile(opts?: { password?: string }): Promise<ConnectOutcome> {\n\t\t\t// Asked twice on purpose: the first call answers from what the browser\n\t\t\t// exposes, the second from what it did when actually asked. A browser\n\t\t\t// that ships the picker and then refuses it only tells us so here.\n\t\t\tif (fileIsSupported()) {\n\t\t\t\tconst target = await fileConnect({ kv: cache.kv, fileName: backupName });\n\t\t\t\tif (target) return connectTarget(target, opts);\n\t\t\t\tif (fileIsSupported()) return 'cancelled';\n\t\t\t}\n\t\t\tawait store.setManualFile();\n\t\t\treturn 'manual';\n\t\t},\n\t\tasync connectWebdav(\n\t\t\tconfig: WebdavConfig,\n\t\t\topts?: { password?: string }\n\t\t): Promise<ConnectOutcome> {\n\t\t\tconst target = await webdavConnect({ kv: cache.kv, config });\n\t\t\tif (!target) {\n\t\t\t\tthrow new SelfstoreError(\n\t\t\t\t\t'TARGET_UNAVAILABLE',\n\t\t\t\t\t'The WebDAV server did not answer (URL, credentials or CORS).'\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn connectTarget(target, opts);\n\t\t},\n\t\tasync connectS3(config: S3Config, opts?: { password?: string }): Promise<ConnectOutcome> {\n\t\t\tconst target = await s3Connect({ kv: cache.kv, config });\n\t\t\tif (!target) {\n\t\t\t\tthrow new SelfstoreError(\n\t\t\t\t\t'TARGET_UNAVAILABLE',\n\t\t\t\t\t'The S3 endpoint did not answer (endpoint, credentials or CORS).'\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn connectTarget(target, opts);\n\t\t},\n\t\tconnectTarget,\n\t\tdisconnect: () => store.detachTarget(),\n\t\taddReplica: (target, opts) => store.attachReplica(target, opts),\n\t\tremoveReplica: (id) => store.detachReplica(id),\n\n\t\tprotect: (password: string) => store.setEncryption(password),\n\t\tunprotect: () => store.setEncryption(null),\n\t\tunlock: (password: string) => store.unlock(password),\n\t\treconnect: () => store.reconnect(),\n\n\t\texportBackup: (opts?: { plaintext?: boolean }) => store.exportBlob(opts),\n\t\tasync downloadBackup(filename?: string): Promise<boolean> {\n\t\t\tconst written = await saveToDisk(await store.exportBlob(), filename ?? backupName);\n\t\t\t// Clearing the pending flag on a cancelled dialog would drop the very\n\t\t\t// nudge that asks for the file the user still does not have.\n\t\t\tif (written) store.markDownloaded();\n\t\t\treturn written;\n\t\t},\n\t\tasync importBackup(file: Blob | Uint8Array, opts?: { password?: string }): Promise<void> {\n\t\t\t// The fluent read() strips reserved bookkeeping collections; removals\n\t\t\t// then propagate because the next save diffs against the sync meta.\n\t\t\tconst reader = restore(file);\n\t\t\tconst snap = opts?.password\n\t\t\t\t? await reader.withPassword(opts.password).read()\n\t\t\t\t: await reader.read();\n\t\t\tcollections = (snap.collections ?? {}) as Record<string, SimpleRecord[]>;\n\t\t\tfiles = snap.files ?? [];\n\t\t\temitData();\n\t\t\tstore.schedule();\n\t\t},\n\n\t\tget status(): StatusDescriptor {\n\t\t\treturn store.state.status;\n\t\t},\n\t\tget error(): StoreError | null {\n\t\t\treturn store.state.lastError;\n\t\t},\n\t\tget state(): LocalStoreState {\n\t\t\treturn store.state;\n\t\t},\n\t\tsubscribe: (fn: () => void) => store.subscribe(fn),\n\n\t\tflush: () => store.flush(),\n\t\tasync sync(): Promise<void> {\n\t\t\tawait store.syncNow();\n\t\t},\n\t\tdispose(): void {\n\t\t\tteardown.forEach((fn) => fn());\n\t\t\tteardown.length = 0;\n\t\t\tstore.dispose();\n\t\t},\n\n\t\tadvanced: store,\n\n\t\tflowHost: { engine: store, kv: cache.kv, backupName },\n\n\t\tget account(): string | null {\n\t\t\treturn rememberedDriveAccount(app) ?? null;\n\t\t},\n\n\t\tasync backups(): Promise<BackupsManager | null> {\n\t\t\tif (!driveAuth) return null;\n\t\t\tbackupsManager ??= await (async () => {\n\t\t\t\tconst { createBackupsManager, driveBackupsHost } = await import('../backups/manager-drive');\n\t\t\t\treturn createBackupsManager({\n\t\t\t\t\tstore,\n\t\t\t\t\tkv: cache.kv,\n\t\t\t\t\thost: driveBackupsHost({ auth: driveAuth!, kv: cache.kv, fileName: backupName }),\n\t\t\t\t\tnaming: { canonicalName: backupName }\n\t\t\t\t});\n\t\t\t})();\n\t\t\treturn backupsManager;\n\t\t},\n\n\t\tdestinations(): ConnectTargets {\n\t\t\t// Drive first when there is one: it is the answer that follows a user\n\t\t\t// from device to device. A file always, for whoever wants no account.\n\t\t\treturn driveAuth ? { drive: driveAuth, file: true } : { file: true };\n\t\t},\n\n\t\tresumeOffer(): ResumeOffer | null {\n\t\t\tconst detail = rememberedDriveAccount(app);\n\t\t\tif (!driveAuth || !detail) return null;\n\t\t\tconst auth = driveAuth;\n\t\t\treturn {\n\t\t\t\tkind: 'drive',\n\t\t\t\tdetail,\n\t\t\t\t// Reopening is not searching: fromSession takes the file id already\n\t\t\t\t// remembered, where a fresh connect resolves by name and could land on\n\t\t\t\t// another file carrying the same one.\n\t\t\t\tconnect: () => driveFromSession({ auth, kv: cache.kv, fileName: backupName })\n\t\t\t};\n\t\t}\n\t};\n\n\treturn simple;\n}\n"]}
1
+ {"version":3,"sources":["../src/simple/simple.ts"],"names":["fromSession","connect"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAyTA,SAAS,SAAA,CAAU,MAA8B,UAAA,EAAmC;AACnF,EAAA,MAAM,MAAA,GAAS,IAAA,EAAM,GAAA,GAAM,UAAU,CAAA;AACrC,EAAA,IAAI,MAAA,IAAU,MAAM,OAAO,IAAA;AAC3B,EAAA,OAAO,MAAA,CAAO,QAAA,CAAS,GAAG,CAAA,GAAI,IAAA,GAAO,MAAA;AACtC;AAEA,SAAS,eAAA,CAAgB,MAAA,EAAsB,KAAA,EAAsB,UAAA,EAA4B;AAChG,EAAA,IAAI,KAAA,KAAU,MAAM,OAAO,EAAA;AAC3B,EAAA,MAAM,EAAA,GAAK,OAAO,KAAK,CAAA;AACvB,EAAA,IAAI,OAAO,EAAA,KAAO,QAAA,IAAY,EAAA,CAAG,WAAW,CAAA,EAAG;AAC9C,IAAA,MAAM,IAAI,SAAA;AAAA,MACT,CAAA,wBAAA,EAA2B,UAAU,CAAA,4BAAA,EAA+B,KAAK,CAAA,OAAA,EAChE,KAAK,SAAA,CAAU,EAAE,CAAC,CAAA,+FAAA,EACiB,UAAU,CAAA,oBAAA;AAAA,KACvD;AAAA,EACD;AACA,EAAA,OAAO,EAAA;AACR;AAEA,SAAS,SAAA,CAAU,GAAe,CAAA,EAAwB;AACzD,EAAA,OAAO,CAAA,CAAE,MAAA,KAAW,CAAA,CAAE,MAAA,IAAU,CAAA,CAAE,KAAA,CAAM,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,KAAM,CAAA,CAAE,CAAC,CAAC,CAAA;AAC7D;AAGA,SAAS,YAAA,GAAwB;AAChC,EAAA,OAAO,OAAO,SAAA,KAAc,WAAA;AAC7B;AAGA,SAAS,YAAY,CAAA,EAA2C;AAC/D,EAAA,OAAO,CAAC,CAAC,CAAA,IAAK,OAAQ,EAAgB,KAAA,KAAU,UAAA;AACjD;AAKA,IAAM,eAAA,GAAkB,CAAC,GAAA,KAAwB,CAAA,wBAAA,EAA2B,GAAG,CAAA,CAAA;AAE/E,SAAS,WAAc,GAAA,EAA2C;AACjE,EAAA,IAAI;AACH,IAAA,MAAM,QAAQ,UAAA,CAAW,YAAA;AACzB,IAAA,OAAO,KAAA,GAAQ,GAAA,CAAI,KAAK,CAAA,GAAI,KAAA,CAAA;AAAA,EAC7B,CAAA,CAAA,MAAQ;AACP,IAAA,OAAO,MAAA;AAAA,EACR;AACD;AAEA,SAAS,uBAAuB,GAAA,EAAiC;AAChE,EAAA,OAAO,UAAA,CAAW,CAAC,CAAA,KAAM,CAAA,CAAE,QAAQ,eAAA,CAAgB,GAAG,CAAC,CAAC,CAAA,IAAK,MAAA;AAC9D;AAIA,eAAe,iBAAA,CAAkB,KAAa,IAAA,EAAgC;AAC7E,EAAA,IAAI;AACH,IAAA,MAAM,GAAA,GAAM,MAAM,OAAA,CAAa,EAAE,MAAM,CAAA;AACvC,IAAA,IAAI,GAAA,CAAI,KAAA,EAAO,UAAA,CAAW,CAAC,CAAA,KAAM,CAAA,CAAE,OAAA,CAAQ,eAAA,CAAgB,GAAG,CAAA,EAAG,GAAA,CAAI,KAAM,CAAC,CAAA;AAAA,EAC7E,CAAA,CAAA,MAAQ;AAAA,EAER;AACD;AAkBA,SAAS,UAAA,CAAW,KAAa,IAAA,EAA4B;AAC5D,EAAA,IAAI,QAAA,GAAiC,IAAA;AACrC,EAAA,MAAM,QAAQ,MAAY;AACzB,IAAA,IAAI,QAAA,IAAY,sBAAA,CAAuB,GAAG,CAAA,EAAG;AAC7C,IAAA,QAAA,GAAW,iBAAA,CAAkB,KAAK,IAAI,CAAA;AAAA,EACvC,CAAA;AACA,EAAA,OAAO;AAAA,IACN,MAAM,MAAM,IAAA,EAAM;AACjB,MAAA,MAAM,KAAA,GAAQ,MAAM,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA;AACnC,MAAA,KAAA,EAAM;AACN,MAAA,OAAO,KAAA;AAAA,IACR,CAAA;AAAA,IACA,MAAM,SAAA,GAAY;AACjB,MAAA,MAAM,EAAA,GAAK,MAAM,IAAA,CAAK,SAAA,EAAU;AAChC,MAAA,IAAI,IAAI,KAAA,EAAM;AACd,MAAA,OAAO,EAAA;AAAA,IACR,CAAA;AAAA,IACA,MAAA,EAAQ,MAAM,IAAA,CAAK,MAAA;AAAO,GAC3B;AACD;AAIA,SAAS,gBAAgB,OAAA,EAA0B;AAClD,EAAA,IAAI,OAAO,OAAA,KAAY,QAAA,IAAY,OAAA,KAAY,MAAM,OAAO,CAAA;AAC5D,EAAA,MAAM,IAAK,OAAA,CAAwC,aAAA;AACnD,EAAA,OAAO,OAAO,SAAA,CAAU,CAAC,CAAA,IAAM,CAAA,IAAgB,IAAK,CAAA,GAAe,CAAA;AACpE;AAGA,SAAS,gBAAgB,WAAA,EAA+D;AACvF,EAAA,OAAO,MAAA,CAAO,WAAA;AAAA,IACb,MAAA,CAAO,OAAA,CAAQ,WAAW,CAAA,CAAE,MAAA,CAAO,CAAC,CAAC,IAAI,CAAA,KAAM,CAAC,IAAA,CAAK,UAAA,CAAW,0BAA0B,CAAC;AAAA,GAC5F;AACD;AAOA,eAAsB,SAAA,CAEpB,GAAA,EAAa,OAAA,GAAyB,EAAC,EAA4B;AACpE,EAAA,IAAI,OAAO,GAAA,KAAQ,QAAA,IAAY,GAAA,CAAI,WAAW,CAAA,EAAG;AAChD,IAAA,MAAM,IAAI,UAAU,0DAA0D,CAAA;AAAA,EAC/E;AAKA,EAAA,IAAI,cAA8C,EAAC;AAGnD,EAAA,IAAI,QAAwB,EAAC;AAE7B,EAAA,MAAM,QAAA,uBAAe,GAAA,EAAgB;AACrC,EAAA,MAAM,WAAW,MAAY,QAAA,CAAS,QAAQ,CAAC,EAAA,KAAO,IAAI,CAAA;AAE1D,EAAA,MAAM,KAAA,GACL,OAAA,CAAQ,KAAA,KACP,YAAA,KACE,cAAA,CAAe,GAAA,EAAK,OAAA,CAAQ,SAAA,GAAY,EAAE,IAAA,EAAM,IAAA,EAAK,GAAI,MAAS,IAClE,WAAA,EAAY,CAAA;AAChB,EAAA,MAAM,UAAA,GAAa,GAAG,GAAG,CAAA,IAAA,CAAA;AAWzB,EAAA,IAAI,cAAA,GAAwC,IAAA;AAI5C,EAAA,IAAI,SAAA,GAA8B,WAAA,CAAY,OAAA,CAAQ,KAAK,CAAA,GACxD,UAAA,CAAW,GAAA,EAAK,OAAA,CAAQ,KAAK,CAAA,GAC7B,OAAA,CAAQ,KAAA,GACP,UAAA;AAAA,IACA,GAAA;AAAA,IACA,YAAA,CAAa;AAAA,MACZ,QAAA,EAAU,QAAQ,KAAA,CAAM,QAAA;AAAA,MACxB,KAAA,EAAO,QAAQ,KAAA,CAAM,KAAA;AAAA,MACrB,OAAA,EAAS,SAAA;AAAA,MACT,IAAA,EAAM,MAAM,sBAAA,CAAuB,GAAG;AAAA,KACtC;AAAA,GACF,GACC,IAAA;AAKJ,EAAA,IAAI,OAAA,CAAQ,SAAA,IAAa,eAAA,CAAgB,KAAK,CAAA,EAAG;AAChD,IAAA,IAAI,MAAA,GAAS,KAAA;AACb,IAAA,OAAO,CAAE,MAAM,KAAA,CAAM,MAAA,CAAO,MAAM,OAAA,CAAQ,SAAA,CAAU,EAAE,MAAA,EAAQ,CAAC,CAAA,EAAI,MAAA,GAAS,IAAA;AAAA,EAC7E;AAEA,EAAA,MAAM,MAAA,GAAS,QAAQ,MAAA,IAAU,CAAA;AACjC,EAAA,MAAM,QAAQ,gBAAA,CAAiB;AAAA,IAC9B,GAAA;AAAA,IACA,aAAA,EAAe,MAAA;AAAA,IACf,QAAQ,MAAgB,eAAA,CAAgB,EAAE,WAAA,EAAa,OAAO,CAAA;AAAA,IAC9D,KAAA,EAAO,CAAC,IAAA,KAAyB;AAChC,MAAA,WAAA,GAAe,IAAA,CAAK,eAAe,EAAC;AACpC,MAAA,KAAA,GAAQ,IAAA,CAAK,SAAS,EAAC;AACvB,MAAA,QAAA,EAAS;AAAA,IACV,CAAA;AAAA,IACA,SAAS,OAAA,CAAQ,OAAA;AAAA,IACjB,MAAM,OAAA,CAAQ,IAAA;AAAA,IACd,KAAA;AAAA,IACA,YAAY,OAAA,CAAQ,UAAA;AAAA,IACpB,UAAU,OAAA,CAAQ,QAAA;AAAA,IAClB,mBAAmB,OAAA,CAAQ,iBAAA;AAAA,IAC3B,gBAAgB,OAAA,CAAQ,cAAA;AAAA,IACxB,aAAA,EAAe,OAAO,IAAA,KAAS;AAC9B,MAAA,IAAI,IAAA,KAAS,WAAW,SAAA,EAAW;AAClC,QAAA,OAAO,WAAA,CAAiB,EAAE,IAAA,EAAM,SAAA,EAAW,IAAI,KAAA,CAAM,EAAA,EAAI,QAAA,EAAU,UAAA,EAAY,CAAA;AAAA,MAChF;AACA,MAAA,IAAI,IAAA,KAAS,QAAQ,OAAOA,aAAAA,CAAgB,EAAE,EAAA,EAAI,KAAA,CAAM,IAAI,CAAA;AAC5D,MAAA,IAAI,IAAA,KAAS,UAAU,OAAO,iBAAA,CAAkB,EAAE,EAAA,EAAI,KAAA,CAAM,IAAI,CAAA;AAChE,MAAA,OAAO,IAAA;AAAA,IACR;AAAA,GACA,CAAA;AAED,EAAA,MAAM,MAAM,IAAA,EAAK;AAcjB,EAAA,IAAI,KAAA,CAAM,KAAA,CAAM,UAAA,KAAe,aAAA,IAAiB,aAAgB,EAAG;AAClE,IAAA,MAAM,MAAM,YAAA,EAAa;AAAA,EAC1B;AAKA,EAAA,MAAM,WAA2B,EAAC;AAClC,EAAA,IACC,OAAA,CAAQ,aAAa,KAAA,IACrB,OAAO,aAAa,WAAA,IACpB,OAAO,WAAW,WAAA,EACjB;AACD,IAAA,MAAM,eAAe,MAAY;AAChC,MAAA,IAAI,QAAA,CAAS,eAAA,KAAoB,QAAA,EAAU,KAAK,MAAM,KAAA,EAAM;AAAA,WACvD,KAAK,KAAA,CAAM,WAAA,CAAY,OAAO,CAAA;AAAA,IACpC,CAAA;AACA,IAAA,MAAM,QAAA,GAAW,MAAY,KAAK,KAAA,CAAM,YAAY,QAAQ,CAAA;AAC5D,IAAA,MAAM,UAAA,GAAa,MAAY,KAAK,KAAA,CAAM,KAAA,EAAM;AAChD,IAAA,MAAM,QAAA,GAAW,YAAY,MAAM;AAClC,MAAA,IAAI,SAAS,eAAA,KAAoB,SAAA,EAAW,KAAK,KAAA,CAAM,YAAY,UAAU,CAAA;AAAA,IAC9E,CAAA,EAAG,IAAI,GAAM,CAAA;AACb,IAAA,QAAA,CAAS,gBAAA,CAAiB,oBAAoB,YAAY,CAAA;AAC1D,IAAA,MAAA,CAAO,gBAAA,CAAiB,UAAU,QAAQ,CAAA;AAC1C,IAAA,MAAA,CAAO,gBAAA,CAAiB,YAAY,UAAU,CAAA;AAC9C,IAAA,QAAA,CAAS,KAAK,MAAM;AACnB,MAAA,aAAA,CAAc,QAAQ,CAAA;AACtB,MAAA,QAAA,CAAS,mBAAA,CAAoB,oBAAoB,YAAY,CAAA;AAC7D,MAAA,MAAA,CAAO,mBAAA,CAAoB,UAAU,QAAQ,CAAA;AAC7C,MAAA,MAAA,CAAO,mBAAA,CAAoB,YAAY,UAAU,CAAA;AAAA,IAClD,CAAC,CAAA;AAAA,EACF;AAEA,EAAA,SAAS,MAAA,CAAO,YAAoB,IAAA,EAAqC;AACxE,IAAA,WAAA,GAAc,EAAE,GAAG,WAAA,EAAa,CAAC,UAAU,GAAG,IAAA,EAAK;AACnD,IAAA,QAAA,EAAS;AACT,IAAA,KAAA,CAAM,QAAA,EAAS;AACf,IAAA,OAAO,QAAQ,OAAA,EAAQ;AAAA,EACxB;AAEA,EAAA,SAAS,YAAY,IAAA,EAAqC;AACzD,IAAA,KAAA,GAAQ,IAAA;AACR,IAAA,QAAA,EAAS;AACT,IAAA,KAAA,CAAM,QAAA,EAAS;AACf,IAAA,OAAO,QAAQ,OAAA,EAAQ;AAAA,EACxB;AAEA,EAAA,eAAe,aAAA,CACd,MAAA,EACA,IAAA,GAA8B,EAAC,EACL;AAC1B,IAAA,MAAM,IAAA,GAAO,MAAM,KAAA,CAAM,aAAA,CAAc,MAAM,CAAA;AAC7C,IAAA,IAAI,KAAK,SAAA,EAAW;AAInB,MAAA,IAAI,KAAK,GAAA,KAAQ,IAAA,IAAQ,IAAA,CAAK,GAAA,KAAQ,MAAM,GAAA,EAAK;AAChD,QAAA,MAAM,IAAI,cAAA;AAAA,UACT,gBAAA;AAAA,UACA,CAAA,gDAAA,EAAmD,IAAA,CAAK,GAAG,CAAA,QAAA,EAAW,MAAM,GAAG,CAAA,wDAAA;AAAA,SAChF;AAAA,MACD;AAGA,MAAA,IAAI,IAAA,CAAK,SAAA,IAAa,CAAC,IAAA,CAAK,QAAA,EAAU;AACrC,QAAA,MAAM,IAAI,cAAA;AAAA,UACT,mBAAA;AAAA,UACA;AAAA,SACD;AAAA,MACD;AACA,MAAA,MAAM,KAAA,CAAM,YAAA,CAAa,MAAA,EAAQ,EAAE,QAAA,EAAU,KAAK,QAAA,IAAY,IAAA,EAAM,QAAA,EAAU,OAAA,EAAS,CAAA;AACvF,MAAA,OAAO,QAAA;AAAA,IACR;AACA,IAAA,MAAM,KAAA,CAAM,aAAa,MAAA,EAAQ;AAAA,MAChC,QAAA,EAAU,KAAK,QAAA,IAAY,IAAA;AAAA,MAC3B,QAAA,EAAU;AAAA,KACV,CAAA;AACD,IAAA,OAAO,SAAA;AAAA,EACR;AAEA,EAAA,MAAM,MAAA,GAAyB;AAAA,IAC9B,IAAgC,UAAA,EAAgC;AAC/D,MAAA,OAAQ,WAAA,CAAY,UAAU,CAAA,IAAK,EAAC;AAAA,IACrC,CAAA;AAAA,IACA,GAAA,CAAgC,YAAe,EAAA,EAA8B;AAC5E,MAAA,MAAM,KAAA,GAAQ,SAAA,CAAU,OAAA,CAAQ,IAAA,EAAM,UAAU,CAAA,IAAK,IAAA;AACrD,MAAA,OAAA,CAAQ,WAAA,CAAY,UAAU,CAAA,IAAK,EAAC,EAAG,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,KAAK,CAAA,KAAM,EAAE,CAAA;AAAA,IACnE,CAAA;AAAA,IACA,GAAA,CAAgC,YAAe,MAAA,EAA6B;AAC3E,MAAA,MAAM,KAAA,GAAQ,SAAA,CAAU,OAAA,CAAQ,IAAA,EAAM,UAAU,CAAA;AAChD,MAAA,MAAM,EAAA,GAAK,eAAA,CAAgB,MAAA,EAAQ,KAAA,EAAO,UAAU,CAAA;AACpD,MAAA,MAAM,IAAA,GAAO,WAAA,CAAY,UAAU,CAAA,IAAK,EAAC;AACzC,MAAA,MAAM,EAAA,GAAK,KAAA,KAAU,IAAA,GAAO,EAAA,GAAK,IAAA,CAAK,SAAA,CAAU,CAAC,CAAA,KAAM,CAAA,CAAE,KAAK,CAAA,KAAM,EAAE,CAAA;AACtE,MAAA,OAAO,MAAA;AAAA,QACN,UAAA;AAAA,QACA,EAAA,KAAO,EAAA,GAAK,CAAC,GAAG,MAAM,MAAM,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,EAAG,CAAA,KAAO,CAAA,KAAM,EAAA,GAAK,SAAS,CAAE;AAAA,OAC3E;AAAA,IACD,CAAA;AAAA,IACA,MAAA,CAAmC,YAAe,OAAA,EAAgC;AACjF,MAAA,MAAM,KAAA,GAAQ,SAAA,CAAU,OAAA,CAAQ,IAAA,EAAM,UAAU,CAAA;AAChD,MAAA,MAAM,IAAA,uBAAW,GAAA,EAAqB;AACtC,MAAA,KAAA,MAAW,CAAA,IAAK,OAAA,EAAS,eAAA,CAAgB,CAAA,EAAG,OAAO,UAAU,CAAA;AAC7D,MAAA,MAAM,OAAO,CAAC,GAAI,YAAY,UAAU,CAAA,IAAK,EAAG,CAAA;AAChD,MAAA,IAAI,UAAU,IAAA,EAAM;AACnB,QAAA,IAAA,CAAK,OAAA,CAAQ,CAAC,CAAA,EAAG,CAAA,KAAM,IAAA,CAAK,IAAI,CAAA,CAAE,KAAK,CAAA,EAAG,CAAC,CAAC,CAAA;AAC5C,QAAA,KAAA,MAAW,KAAK,OAAA,EAAS;AACxB,UAAA,MAAM,EAAA,GAAK,IAAA,CAAK,GAAA,CAAI,CAAA,CAAE,KAAK,CAAC,CAAA;AAC5B,UAAA,IAAI,OAAO,EAAA,KAAO,QAAA,EAAU,IAAA,CAAK,EAAE,CAAA,GAAI,CAAA;AAAA,eAClC,IAAA,CAAK,KAAK,CAAC,CAAA;AAAA,QACjB;AAAA,MACD,CAAA,MAAO;AACN,QAAA,IAAA,CAAK,IAAA,CAAK,GAAG,OAAO,CAAA;AAAA,MACrB;AACA,MAAA,OAAO,MAAA,CAAO,YAAY,IAAI,CAAA;AAAA,IAC/B,CAAA;AAAA,IACA,MAAA,CAAmC,YAAe,EAAA,EAA2B;AAC5E,MAAA,MAAM,KAAA,GAAQ,SAAA,CAAU,OAAA,CAAQ,IAAA,EAAM,UAAU,CAAA,IAAK,IAAA;AACrD,MAAA,MAAM,IAAA,GAAO,WAAA,CAAY,UAAU,CAAA,IAAK,EAAC;AACzC,MAAA,MAAM,IAAA,GAAO,KAAK,MAAA,CAAO,CAAC,MAAM,CAAA,CAAE,KAAK,MAAM,EAAE,CAAA;AAC/C,MAAA,OAAO,IAAA,CAAK,WAAW,IAAA,CAAK,MAAA,GAAS,QAAQ,OAAA,EAAQ,GAAI,MAAA,CAAO,UAAA,EAAY,IAAI,CAAA;AAAA,IACjF,CAAA;AAAA,IACA,MAAkC,UAAA,EAA8B;AAC/D,MAAA,IAAA,CAAK,WAAA,CAAY,UAAU,CAAA,IAAK,IAAI,MAAA,KAAW,CAAA,EAAG,OAAO,OAAA,CAAQ,OAAA,EAAQ;AACzE,MAAA,OAAO,MAAA,CAAO,UAAA,EAAY,EAAE,CAAA;AAAA,IAC7B,CAAA;AAAA,IACA,SAAS,EAAA,EAA4B;AACpC,MAAA,QAAA,CAAS,IAAI,EAAE,CAAA;AACf,MAAA,OAAO,MAAM,QAAA,CAAS,MAAA,CAAO,EAAE,CAAA;AAAA,IAChC,CAAA;AAAA,IAEA,QAAA,GAAoC;AACnC,MAAA,OAAO,KAAA;AAAA,IACR,CAAA;AAAA,IACA,QAAQ,EAAA,EAAsC;AAC7C,MAAA,OAAO,MAAM,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,OAAO,EAAE,CAAA;AAAA,IACrC,CAAA;AAAA,IACA,MAAM,OAAA,CAAQ,IAAA,EAAoB,IAAA,GAA8B,EAAC,EAAoB;AACpF,MAAA,MAAM,KAAA,GAAQ,MAAM,OAAA,CAAQ,IAAA,CAAK,KAAK,CAAA;AACtC,MAAA,MAAM,EAAA,GAAK,IAAA,CAAK,EAAA,IAAO,MAAM,UAAU,KAAK,CAAA;AAC5C,MAAA,IAAI,OAAO,EAAA,KAAO,QAAA,IAAY,EAAA,CAAG,WAAW,CAAA,EAAG;AAC9C,QAAA,MAAM,IAAI,SAAA;AAAA,UACT,CAAA,mDAAA,EAAsD,IAAA,CAAK,SAAA,CAAU,IAAA,CAAK,EAAE,CAAC,CAAA,sCAAA;AAAA,SAE9E;AAAA,MACD;AACA,MAAA,MAAM,KAAK,KAAA,CAAM,SAAA,CAAU,CAAC,CAAA,KAAM,CAAA,CAAE,OAAO,EAAE,CAAA;AAG7C,MAAA,IAAI,EAAA,KAAO,EAAA,IAAM,CAAC,IAAA,CAAK,OAAA,EAAS;AAC/B,QAAA,IAAI,UAAU,KAAA,CAAM,EAAE,EAAE,KAAA,EAAO,KAAK,GAAG,OAAO,EAAA;AAC9C,QAAA,MAAM,IAAI,SAAA;AAAA,UACT,oBAAoB,EAAE,CAAA,qTAAA;AAAA,SAKvB;AAAA,MACD;AACA,MAAA,MAAM,KAAA,GAAsB;AAAA,QAC3B,EAAA;AAAA,QACA,IAAA,EAAM,KAAK,IAAA,IAAQ,EAAA;AAAA,QACnB,IAAA,EAAM,KAAK,IAAA,IAAQ,0BAAA;AAAA,QACnB;AAAA,OACD;AACA,MAAA,MAAM,WAAA;AAAA,QACL,EAAA,KAAO,EAAA,GAAK,CAAC,GAAG,OAAO,KAAK,CAAA,GAAI,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,EAAG,CAAA,KAAO,CAAA,KAAM,EAAA,GAAK,QAAQ,CAAE;AAAA,OAC3E;AACA,MAAA,OAAO,EAAA;AAAA,IACR,CAAA;AAAA,IACA,WAAW,EAAA,EAA2B;AACrC,MAAA,MAAM,OAAO,KAAA,CAAM,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,OAAO,EAAE,CAAA;AAC5C,MAAA,OAAO,IAAA,CAAK,WAAW,KAAA,CAAM,MAAA,GAAS,QAAQ,OAAA,EAAQ,GAAI,YAAY,IAAI,CAAA;AAAA,IAC3E,CAAA;AAAA,IAEA,MAAM,YAAA,CAAa,IAAA,EAAiB,IAAA,EAAuD;AAC1F,MAAA,SAAA,GAAY,IAAA;AACZ,MAAA,MAAM,MAAA,GAAS,MAAMC,SAAA,CAAa,EAAE,IAAA,EAAM,IAAI,KAAA,CAAM,EAAA,EAAI,QAAA,EAAU,UAAA,EAAY,CAAA;AAC9E,MAAA,IAAI,CAAC,QAAQ,OAAO,WAAA;AACpB,MAAA,MAAM,OAAA,GAAU,MAAM,aAAA,CAAc,MAAA,EAAQ,IAAI,CAAA;AAKhD,MAAA,IAAI,OAAA,KAAY,WAAA,EAAa,KAAK,iBAAA,CAAkB,KAAK,IAAI,CAAA;AAC7D,MAAA,OAAO,OAAA;AAAA,IACR,CAAA;AAAA,IACA,MAAM,YAAY,IAAA,EAAuD;AAIxE,MAAA,IAAI,aAAgB,EAAG;AACtB,QAAA,MAAM,MAAA,GAAS,MAAMA,OAAAA,CAAY,EAAE,IAAI,KAAA,CAAM,EAAA,EAAI,QAAA,EAAU,UAAA,EAAY,CAAA;AACvE,QAAA,IAAI,MAAA,EAAQ,OAAO,aAAA,CAAc,MAAA,EAAQ,IAAI,CAAA;AAC7C,QAAA,IAAI,WAAA,IAAmB,OAAO,WAAA;AAAA,MAC/B;AACA,MAAA,MAAM,MAAM,aAAA,EAAc;AAC1B,MAAA,OAAO,QAAA;AAAA,IACR,CAAA;AAAA,IACA,MAAM,aAAA,CACL,MAAA,EACA,IAAA,EAC0B;AAC1B,MAAA,MAAM,MAAA,GAAS,MAAM,aAAA,CAAc,EAAE,IAAI,KAAA,CAAM,EAAA,EAAI,QAAQ,CAAA;AAC3D,MAAA,IAAI,CAAC,MAAA,EAAQ;AACZ,QAAA,MAAM,IAAI,cAAA;AAAA,UACT,oBAAA;AAAA,UACA;AAAA,SACD;AAAA,MACD;AACA,MAAA,OAAO,aAAA,CAAc,QAAQ,IAAI,CAAA;AAAA,IAClC,CAAA;AAAA,IACA,MAAM,SAAA,CAAU,MAAA,EAAkB,IAAA,EAAuD;AACxF,MAAA,MAAM,MAAA,GAAS,MAAM,SAAA,CAAU,EAAE,IAAI,KAAA,CAAM,EAAA,EAAI,QAAQ,CAAA;AACvD,MAAA,IAAI,CAAC,MAAA,EAAQ;AACZ,QAAA,MAAM,IAAI,cAAA;AAAA,UACT,oBAAA;AAAA,UACA;AAAA,SACD;AAAA,MACD;AACA,MAAA,OAAO,aAAA,CAAc,QAAQ,IAAI,CAAA;AAAA,IAClC,CAAA;AAAA,IACA,aAAA;AAAA,IACA,UAAA,EAAY,MAAM,KAAA,CAAM,YAAA,EAAa;AAAA,IACrC,YAAY,CAAC,MAAA,EAAQ,SAAS,KAAA,CAAM,aAAA,CAAc,QAAQ,IAAI,CAAA;AAAA,IAC9D,aAAA,EAAe,CAAC,EAAA,KAAO,KAAA,CAAM,cAAc,EAAE,CAAA;AAAA,IAE7C,OAAA,EAAS,CAAC,QAAA,KAAqB,KAAA,CAAM,cAAc,QAAQ,CAAA;AAAA,IAC3D,SAAA,EAAW,MAAM,KAAA,CAAM,aAAA,CAAc,IAAI,CAAA;AAAA,IACzC,MAAA,EAAQ,CAAC,QAAA,KAAqB,KAAA,CAAM,OAAO,QAAQ,CAAA;AAAA,IACnD,SAAA,EAAW,MAAM,KAAA,CAAM,SAAA,EAAU;AAAA,IAEjC,YAAA,EAAc,CAAC,IAAA,KAAmC,KAAA,CAAM,WAAW,IAAI,CAAA;AAAA,IACvE,MAAM,eAAe,QAAA,EAAqC;AACzD,MAAA,MAAM,OAAA,GAAU,MAAM,UAAA,CAAW,MAAM,MAAM,UAAA,EAAW,EAAG,YAAY,UAAU,CAAA;AAGjF,MAAA,IAAI,OAAA,QAAe,cAAA,EAAe;AAClC,MAAA,OAAO,OAAA;AAAA,IACR,CAAA;AAAA,IACA,MAAM,YAAA,CAAa,IAAA,EAAyB,IAAA,EAA6C;AAIxF,MAAA,MAAM,EAAE,QAAA,EAAU,OAAA,EAAQ,GAAI,MAAM,eAAA,CAAgB,MAAM,OAAA,CAAQ,IAAI,CAAA,EAAG,IAAA,EAAM,QAAQ,CAAA;AACvF,MAAA,MAAM,WAAA,GAAc,gBAAgB,OAAO,CAAA;AAC3C,MAAA,IAAI,cAAc,MAAA,EAAQ;AACzB,QAAA,MAAM,IAAI,cAAA;AAAA,UACT,gBAAA;AAAA,UACA,CAAA,0BAAA,EAA6B,WAAW,CAAA,kBAAA,EAAqB,MAAM,CAAA,iBAAA;AAAA,SACpE;AAAA,MACD;AAGA,MAAA,MAAM,IAAA,GAAO,EAAE,WAAA,EAAa,eAAA,CAAgB,SAAS,WAAW,CAAA,EAAG,KAAA,EAAO,QAAA,CAAS,KAAA,EAAM;AACzF,MAAA,MAAM,IAAA,GACL,QAAQ,OAAA,IAAW,WAAA,GAAc,SAAS,OAAA,CAAQ,OAAA,CAAQ,WAAA,EAAa,IAAI,CAAA,GAAI,IAAA;AAChF,MAAA,WAAA,GAAe,IAAA,CAAK,eAAe,EAAC;AACpC,MAAA,KAAA,GAAQ,IAAA,CAAK,SAAS,EAAC;AACvB,MAAA,QAAA,EAAS;AACT,MAAA,KAAA,CAAM,QAAA,EAAS;AAAA,IAChB,CAAA;AAAA,IAEA,IAAI,MAAA,GAA2B;AAC9B,MAAA,OAAO,MAAM,KAAA,CAAM,MAAA;AAAA,IACpB,CAAA;AAAA,IACA,IAAI,KAAA,GAA2B;AAC9B,MAAA,OAAO,MAAM,KAAA,CAAM,SAAA;AAAA,IACpB,CAAA;AAAA,IACA,IAAI,KAAA,GAAyB;AAC5B,MAAA,OAAO,KAAA,CAAM,KAAA;AAAA,IACd,CAAA;AAAA,IACA,SAAA,EAAW,CAAC,EAAA,KAAmB,KAAA,CAAM,UAAU,EAAE,CAAA;AAAA,IAEjD,KAAA,EAAO,MAAM,KAAA,CAAM,KAAA,EAAM;AAAA,IACzB,MAAM,IAAA,GAAsB;AAC3B,MAAA,MAAM,MAAM,OAAA,EAAQ;AAAA,IACrB,CAAA;AAAA,IACA,OAAA,GAAgB;AACf,MAAA,QAAA,CAAS,OAAA,CAAQ,CAAC,EAAA,KAAO,EAAA,EAAI,CAAA;AAC7B,MAAA,QAAA,CAAS,MAAA,GAAS,CAAA;AAClB,MAAA,KAAA,CAAM,OAAA,EAAQ;AAAA,IACf,CAAA;AAAA,IAEA,QAAA,EAAU,KAAA;AAAA,IAEV,UAAU,EAAE,MAAA,EAAQ,OAAO,EAAA,EAAI,KAAA,CAAM,IAAI,UAAA,EAAW;AAAA,IAEpD,IAAI,OAAA,GAAyB;AAC5B,MAAA,OAAO,sBAAA,CAAuB,GAAG,CAAA,IAAK,IAAA;AAAA,IACvC,CAAA;AAAA,IAEA,MAAM,OAAA,GAA0C;AAC/C,MAAA,IAAI,CAAC,WAAW,OAAO,IAAA;AACvB,MAAA,cAAA,KAAmB,OAAO,YAAY;AACrC,QAAA,MAAM,EAAE,oBAAA,EAAsB,gBAAA,EAAiB,GAAI,MAAM,OAAO,6BAA0B,CAAA;AAC1F,QAAA,OAAO,oBAAA,CAAqB;AAAA,UAC3B,KAAA;AAAA,UACA,IAAI,KAAA,CAAM,EAAA;AAAA,UACV,IAAA,EAAM,gBAAA,CAAiB,EAAE,IAAA,EAAM,SAAA,EAAY,IAAI,KAAA,CAAM,EAAA,EAAI,QAAA,EAAU,UAAA,EAAY,CAAA;AAAA,UAC/E,MAAA,EAAQ,EAAE,aAAA,EAAe,UAAA;AAAW,SACpC,CAAA;AAAA,MACF,CAAA,GAAG;AACH,MAAA,OAAO,cAAA;AAAA,IACR,CAAA;AAAA,IAEA,YAAA,GAA+B;AAG9B,MAAA,OAAO,SAAA,GAAY,EAAE,KAAA,EAAO,SAAA,EAAW,MAAM,IAAA,EAAK,GAAI,EAAE,IAAA,EAAM,IAAA,EAAK;AAAA,IACpE,CAAA;AAAA,IAEA,WAAA,GAAkC;AACjC,MAAA,MAAM,MAAA,GAAS,uBAAuB,GAAG,CAAA;AACzC,MAAA,IAAI,CAAC,SAAA,IAAa,CAAC,MAAA,EAAQ,OAAO,IAAA;AAClC,MAAA,MAAM,IAAA,GAAO,SAAA;AACb,MAAA,OAAO;AAAA,QACN,IAAA,EAAM,OAAA;AAAA,QACN,MAAA;AAAA;AAAA;AAAA;AAAA,QAIA,OAAA,EAAS,MAAM,WAAA,CAAiB,EAAE,IAAA,EAAM,IAAI,KAAA,CAAM,EAAA,EAAI,QAAA,EAAU,UAAA,EAAY;AAAA,OAC7E;AAAA,IACD;AAAA,GACD;AAEA,EAAA,OAAO,MAAA;AACR","file":"index.js","sourcesContent":["// The simple store: one call, sensible defaults, and the store owns the data.\n//\n// const store = await selfstore('notes-app');\n// await store.put('todos', { id: 't1', text: 'hello' });\n// store.onChange(render);\n//\n// Convention over configuration: schema version 1, an IndexedDB cache named\n// after the app (in-memory where IndexedDB does not exist, so tests and SSR\n// just work), debounced auto-save, and the browser sync moments wired.\n// Every default has an option; everything deeper lives on store.advanced -\n// the full createLocalStore, same instance.\n//\n// One rule is enforced rather than documented: records need a non-empty\n// string id (the merge identifies records by it), so put() throws a\n// TypeError right away. The advanced pull-model store only logs - it cannot\n// know which collections an app considers mergeable.\n\nimport {\n\tcreateLocalStore,\n\ttype LocalStore,\n\ttype LocalStoreState,\n\ttype StoreError\n} from '../persistence/store';\nimport {\n\tindexedDbCache,\n\tmemoryCache,\n\tisLockableCache,\n\ttype KV,\n\ttype LocalCache\n} from '../persistence/cache';\nimport type { BackupTarget } from '../persistence/target';\nimport type { StatusDescriptor } from '../persistence/status';\nimport type { SyncConfig } from '../sync';\nimport type { ConnectTargets, ResumeOffer } from '../flows/connect';\nimport type { BackupsManager } from '../backups/manager';\nimport type { Snapshot, SnapshotFile } from '../selfstore/types';\nimport { SelfstoreError } from '../selfstore/errors';\nimport { sha256Hex } from '../selfstore/digest';\nimport { toBytes, type FileBytes } from './bytes';\nimport { type PasswordPolicy } from '../selfstore';\nimport { asBytes, readBoxWithSync } from '../selfstore/box';\nimport { RESERVED_COLLECTION_PREFIX } from '../selfstore/types';\nimport { saveToDisk } from '../selfstore/targets/local';\nimport {\n\taccount as driveAccount,\n\tconnect as driveConnect,\n\tfromSession as driveFromSession,\n\ttype DriveAuth\n} from '../persistence/targets/drive';\nimport { gisDriveAuth } from '../persistence/targets/drive-auth-gis';\nimport {\n\tconnect as fileConnect,\n\tfromSession as fileFromSession,\n\tisSupported as fileIsSupported\n} from '../persistence/targets/file';\n// WebDAV and S3 arrive on the gesture that uses them - see targets/servers.ts.\nimport { webdavConnect, webdavFromSession, s3Connect } from '../persistence/targets/servers';\nimport type { WebdavConfig } from '../persistence/targets/webdav';\nimport type { S3Config } from '../persistence/targets/s3';\n\n/** A record as the simple store sees it: plain JSON with a string id (the id\n * field name can be remapped per collection via `sync.ids`). */\nexport type SimpleRecord = Record<string, unknown>;\n\nexport type { FileBytes } from './bytes';\n\n/** A file on its way into the store. Omit `id` to get the content hash, which\n * is the id that makes the multi-device union safe - see `putFile`. */\nexport interface PutFileInput {\n\t/** Defaults to the SHA-256 of the bytes. */\n\tid?: string;\n\t/** Defaults to the id. Cosmetic: nothing keys on it. */\n\tname?: string;\n\t/** Defaults to 'application/octet-stream'. */\n\tmime?: string;\n\tbytes: FileBytes;\n}\n\n/** Unlock callback for the ultra-sensitive cacheLock mode: return the session\n * secret - a password (Argon2id-derived) or an app-provided key (e.g. a passkey\n * PRF result). Called at boot; called again with { failed: true } after a wrong\n * secret so the app can re-prompt. Throw to abort the boot (user cancelled). */\nexport type CacheUnlock = (attempt: { failed: boolean }) => Promise<string | CryptoKey>;\n\n/** Options - every one of them optional, the defaults carry a real app. */\nexport interface SimpleOptions {\n\t/** Your DATA schema version (default 1). Bump it together with `migrate`. */\n\tschema?: number;\n\t/** Upgrade a snapshot written by an older schema version. */\n\tmigrate?: (fromVersion: number, snap: Snapshot) => Snapshot;\n\t/** Per-collection merge tuning (id field mapping, strategies). The default -\n\t * records keyed by `id`, later edit wins, deletes propagate - fits most apps. */\n\tsync?: SyncConfig;\n\t/**\n\t * Google Drive, when this app uses it. Two shapes for two situations:\n\t *\n\t * `{ clientId }` is the whole wiring - the library builds the browser\n\t * connection, keeps ONE session for the app (several would mean several\n\t * consent screens), reuses its token across a reload, remembers which\n\t * account holds the backup and hands that back to Google so the chooser\n\t * stops appearing. Nothing else to write.\n\t *\n\t * A `DriveAuth` instead, for an app that gets its tokens elsewhere - a\n\t * server broker holding a refresh token, a native shell.\n\t *\n\t * Either way, providing it here is also what lets a connected Drive backup\n\t * restore itself on the next start.\n\t */\n\tdrive?: DriveAuth | { clientId: string; scope?: string };\n\t/** Where the working copy lives (default: IndexedDB named after the app;\n\t * in-memory when IndexedDB does not exist, e.g. tests or SSR). */\n\tcache?: LocalCache;\n\t/** Auto-save debounce in ms. */\n\tdebounceMs?: number;\n\t/** Cross-tab coordination (default on in browsers). */\n\tmultiTab?: boolean;\n\t/** Refuse to ever write or export a plaintext backup: connecting a\n\t * destination then demands a password (or a group). Off by default. */\n\trequireEncryption?: boolean;\n\t/** Reject a backup password weaker than this policy (length, character\n\t * classes) at protect()/addKey time. Preview the rules with\n\t * checkPasswordPolicy for a live UI hint. Omit for no policy. */\n\tpasswordPolicy?: PasswordPolicy;\n\t/** Ultra-sensitive mode: seal the local cache under a key derived from a\n\t * secret held in memory (this callback), never on disk - so a copy of the\n\t * browser profile cannot read it. One unlock per session; branch it on the\n\t * app's existing login to leave the UX unchanged. Browser (IndexedDB) only. */\n\tcacheLock?: CacheUnlock;\n\t/** Wire tab focus / network return / interval / tab hide automatically\n\t * (default true in browsers). Pass false to drive syncing yourself. */\n\tautoSync?: boolean;\n}\n\n/** What connecting a destination did. 'merged': it already held a backup and\n * both sides were folded together. 'started': it was empty, this device's data\n * is now its content. 'manual': non-Chromium file fallback - offer\n * downloadBackup() instead. 'cancelled': the user closed the picker/consent. */\nexport type ConnectOutcome = 'merged' | 'started' | 'manual' | 'cancelled';\n\nexport interface SimpleStore<\n\tS extends Record<string, SimpleRecord> = Record<string, SimpleRecord>\n> {\n\t// --- Your data ---------------------------------------------------------\n\t/** Every record of a collection (treat as read-only; write via put/remove). */\n\tall<K extends keyof S & string>(collection: K): readonly S[K][];\n\t/** One record by id, or undefined. */\n\tget<K extends keyof S & string>(collection: K, id: string): S[K] | undefined;\n\t/** Insert or replace one record (auto-saves, debounced). Throws a TypeError\n\t * when the record has no non-empty string id. */\n\tput<K extends keyof S & string>(collection: K, record: S[K]): Promise<void>;\n\t/** Insert or replace many records in one save. */\n\tputAll<K extends keyof S & string>(collection: K, records: S[K][]): Promise<void>;\n\t/** Delete one record by id (propagates to other devices). Unknown id: no-op. */\n\tremove<K extends keyof S & string>(collection: K, id: string): Promise<void>;\n\t/** Empty a collection (every removal propagates). */\n\tclear<K extends keyof S & string>(collection: K): Promise<void>;\n\t/** Called after any data change: your writes, another tab, another device,\n\t * a restore. Returns an unsubscribe. */\n\tonChange(fn: () => void): () => void;\n\n\t// --- Your files (bytes that ride along in the same backup) --------------\n\t/** Every file (treat as read-only; write via putFile/removeFile). */\n\tallFiles(): readonly SnapshotFile[];\n\t/** One file by id, or undefined. */\n\tgetFile(id: string): SnapshotFile | undefined;\n\t/**\n\t * Store bytes alongside the collections and return the file's id.\n\t *\n\t * Files are **immutable per id**. The multi-device merge unions them by id\n\t * and has no clock to order two bodies, so when two devices hold DIFFERENT\n\t * bytes under one id, one is kept and the other is dropped - with no\n\t * conflict reported, because at that level there is nothing to compare.\n\t *\n\t * Hence the default id: the SHA-256 of the bytes. Different bytes land on\n\t * different ids and the union keeps both, which is what makes a CRDT\n\t * document (Yjs, Automerge) safe to carry here - store its UPDATES, let the\n\t * union collect them, fold them at load. Tie a file's lifetime to a record\n\t * so deleting the record tells you which files to remove.\n\t *\n\t * Naming the id yourself puts that rule in your hands: identical bytes\n\t * under an existing id are a no-op, different bytes are refused with a\n\t * TypeError. `{ replace: true }` does it anyway - right for a body only\n\t * ever written on one device (a thumbnail, a rendered export), a silent\n\t * loser as soon as two devices write it.\n\t */\n\tputFile(file: PutFileInput, opts?: { replace?: boolean }): Promise<string>;\n\t/** Delete one file by id (propagates to other devices). Unknown id: no-op. */\n\tremoveFile(id: string): Promise<void>;\n\n\t// --- Where it lives ----------------------------------------------------\n\t/** Connect Google Drive as the durable home. An existing backup there is\n\t * MERGED with this device (multi-device); an encrypted one needs its\n\t * password up front (PASSWORD_REQUIRED is thrown before anything changes). */\n\tconnectDrive(auth: DriveAuth, opts?: { password?: string }): Promise<ConnectOutcome>;\n\t/** Connect a disk file (File System Access; falls back to manual download). */\n\tconnectFile(opts?: { password?: string }): Promise<ConnectOutcome>;\n\t/** Connect a WebDAV server (Nextcloud, ownCloud, your own). */\n\tconnectWebdav(config: WebdavConfig, opts?: { password?: string }): Promise<ConnectOutcome>;\n\t/** Connect an S3-compatible bucket you control (Amazon S3, R2, B2, MinIO). */\n\tconnectS3(config: S3Config, opts?: { password?: string }): Promise<ConnectOutcome>;\n\t/** Connect any custom BackupTarget with the same merge semantics. */\n\tconnectTarget(target: BackupTarget, opts?: { password?: string }): Promise<ConnectOutcome>;\n\t/** Back to device-only (the destination keeps its last backup). */\n\tdisconnect(): Promise<void>;\n\n\t// --- Backup copy (replica) ---------------------------------------------\n\t/** Also write the same encrypted backup to this second destination on\n\t * every save. A broken copy never gates the store (state.replicas). */\n\taddReplica(target: BackupTarget, opts?: { id?: string }): string;\n\t/** Stop writing the copy (the destination keeps its last backup). */\n\tremoveReplica(id: string): void;\n\n\t// --- Protection & the two gate gestures --------------------------------\n\t/** Encrypt the durable backup end to end with this password (reversible). */\n\tprotect(password: string): Promise<void>;\n\t/** Remove the backup password. */\n\tunprotect(): Promise<void>;\n\t/** status.action === 'unlock': supply the password to resume. */\n\tunlock(password: string): Promise<boolean>;\n\t/** status.action === 'reconnect': re-run the destination's auth gesture. */\n\treconnect(): Promise<boolean>;\n\n\t// --- Portable backups ---------------------------------------------------\n\t/** The portable backup file (a real ZIP; encrypted when protect() is on).\n\t *\n\t * `{ plaintext: true }` asks for a READABLE copy of a protected store: the\n\t * same ZIP with `selfstore.json` in the clear, which any tool can open and\n\t * git can diff. It is the answer to \"can I actually get my data out\", and\n\t * it costs nothing: the store, its password and its destination are\n\t * untouched, so the real backup stays encrypted. Before this, the only way\n\t * out was `unprotect()`, which rewrites the destination and leaves it\n\t * readable until you remember to undo it.\n\t *\n\t * Refused while locked (there is nothing readable to write), and refused on\n\t * a store created with `requireEncryption` - that flag means this store\n\t * never produces cleartext, and an escape hatch would make it a lie. */\n\texportBackup(opts?: { plaintext?: boolean }): Promise<Blob>;\n\t/** Download the backup (also resolves the 'manual' file mode's pending flag).\n\t * False when the user closed the save dialog: nothing was written, the\n\t * pending flag stands, and the app must not record a backup. */\n\tdownloadBackup(filename?: string): Promise<boolean>;\n\t/** Load a backup file into this store, replacing the local data (removals\n\t * propagate like edits). Throws PASSWORD_REQUIRED / DECRYPT_FAILED. */\n\timportBackup(file: Blob | Uint8Array, opts?: { password?: string }): Promise<void>;\n\n\t// --- Observability ------------------------------------------------------\n\t/** Headless status: { state, severity, action, labelKey } - map labelKey to\n\t * your own copy. */\n\treadonly status: StatusDescriptor;\n\t/** The last problem: { code, labelKey, message } - show labelKey, log message. */\n\treadonly error: StoreError | null;\n\t/** The full underlying state (advanced reads: journal, peers, mode...). */\n\treadonly state: LocalStoreState;\n\t/** Fires on any state change (status flips included) - for framework bindings. */\n\tsubscribe(fn: () => void): () => void;\n\n\t// --- Lifecycle ----------------------------------------------------------\n\t/** Save now (called for you on tab hide when autoSync is on). */\n\tflush(): Promise<void>;\n\t/** Converge with the destination now (a user gesture like pull-to-refresh). */\n\tsync(): Promise<void>;\n\t/** Drop timers and listeners (tests, SPA teardown). */\n\tdispose(): void;\n\n\t/** The escape hatch: the full advanced store this simple one is built on -\n\t * peers, groups, custom strategies, everything (see selfstore/advanced). */\n\treadonly advanced: LocalStore;\n\n\t/** The attachment point for 'selfstore/flows': the engine plus the cache KV\n\t * and backup file name a flow needs to build destination targets. Apps on\n\t * the advanced store hand a flow the same three by themselves. */\n\treadonly flowHost: { engine: LocalStore; kv: KV; backupName: string };\n\n\t/** Who holds the destination, when it is known - the Google address behind a\n\t * Drive backup. Learned on connect and remembered across sessions, so a\n\t * surface can NAME the destination (\"Google Drive\" is not an address) and a\n\t * re-grant can skip the account chooser. Null for a file, or before a first\n\t * connect. Reading it costs nothing: no call goes out. */\n\treadonly account: string | null;\n\n\t/** The destination this device already used, ready to hand to a connect flow\n\t * as `options.resume` - so a returning visitor is offered \"reopen my backup\"\n\t * ahead of the plain destinations, instead of re-deriving a decision the\n\t * library already has on file. Null when there is nothing to resume. */\n\tresumeOffer(): ResumeOffer | null;\n\n\t/**\n\t * The several-backups manager for this store's destination, or null when\n\t * there is nothing to manage several of (no Drive configured).\n\t *\n\t * The port under it - list, open by id, create, rename, delete - was the last\n\t * thing an app still assembled from the session the store had built, and the\n\t * one place it could pick the wrong file id. Built once and remembered; the\n\t * backups module is imported on demand, so an app that never asks does not\n\t * carry it.\n\t */\n\tbackups(): Promise<BackupsManager | null>;\n\n\t/**\n\t * The destinations this store can offer, ready for a connect flow or widget:\n\t * Drive first when the app configured it, a file always.\n\t *\n\t * Consumers were assembling this by hand from the very session the store\n\t * built - and the order and the \"no client id means no Drive card\" rule were\n\t * rediscovered each time. An app wanting a different offer (a WebDAV server,\n\t * its own target) still writes its own object; this is the default, not a\n\t * ceiling.\n\t */\n\tdestinations(): ConnectTargets;\n}\n\n/** The id field a collection's records are keyed by: `sync.ids` may remap it;\n * a DOTTED path (nested id) is an advanced setup the simple check skips. */\nfunction idFieldOf(sync: SyncConfig | undefined, collection: string): string | null {\n\tconst mapped = sync?.ids?.[collection];\n\tif (mapped == null) return 'id';\n\treturn mapped.includes('.') ? null : mapped;\n}\n\nfunction requireStringId(record: SimpleRecord, field: string | null, collection: string): string {\n\tif (field === null) return ''; // nested-path ids: validated by the engine's own rules\n\tconst id = record[field];\n\tif (typeof id !== 'string' || id.length === 0) {\n\t\tthrow new TypeError(\n\t\t\t`selfstore: a record in \"${collection}\" needs a non-empty STRING \"${field}\" ` +\n\t\t\t\t`(got ${JSON.stringify(id)}). String ids are what the multi-device merge keys on; ` +\n\t\t\t\t`map another field with { sync: { ids: { ${collection}: 'yourField' } } }.`\n\t\t);\n\t}\n\treturn id;\n}\n\nfunction sameBytes(a: Uint8Array, b: Uint8Array): boolean {\n\treturn a.length === b.length && a.every((v, i) => v === b[i]);\n}\n\n/** True when a browser IndexedDB is available (SSR and plain Node lack it). */\nfunction hasIndexedDb(): boolean {\n\treturn typeof indexedDB !== 'undefined';\n}\n\n/** Told apart by shape: a DriveAuth mints tokens, a `{ clientId }` asks us to. */\nfunction isDriveAuth(v: SimpleOptions['drive']): v is DriveAuth {\n\treturn !!v && typeof (v as DriveAuth).token === 'function';\n}\n\n/** Where the connected Drive address is kept, per app: it is what stops Google\n * asking WHICH account at every re-grant. An address is not a secret, and a\n * browser that refuses storage simply gets the chooser back. */\nconst driveAccountKey = (app: string): string => `selfstore.drive.account.${app}`;\n\nfunction localStore<T>(job: (store: Storage) => T): T | undefined {\n\ttry {\n\t\tconst store = globalThis.localStorage;\n\t\treturn store ? job(store) : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction rememberedDriveAccount(app: string): string | undefined {\n\treturn localStore((s) => s.getItem(driveAccountKey(app))) ?? undefined;\n}\n\n/** Learn the account behind a live Drive session, best-effort and once: it feeds\n * the next re-grant's hint, and any surface that wants to name the destination. */\nasync function learnDriveAccount(app: string, auth: DriveAuth): Promise<void> {\n\ttry {\n\t\tconst who = await driveAccount({ auth });\n\t\tif (who.email) localStore((s) => s.setItem(driveAccountKey(app), who.email!));\n\t} catch {\n\t\t// A destination that will not say costs the hint, never the connection.\n\t}\n}\n\n/**\n * The same connection, which learns whose it is the first time it hands out a\n * token.\n *\n * Learning it took an explicit connect, so anyone whose backup was attached\n * before this existed - or attached in another session - kept a destination\n * with no name on it: the panel said \"Google Drive\" where it could have said\n * which one, and the offer to reopen that backup had no address to show, which\n * is most of what makes it recognisable.\n *\n * It cannot simply be asked for at start-up: reading the account needs a token,\n * and asking for a token with no user gesture behind it opens a popup the\n * browser blocks. So it rides along instead - every token this app obtains,\n * whatever asked for it, teaches it the address at no cost. Once known, nothing\n * is called again.\n */\nfunction selfNaming(app: string, auth: DriveAuth): DriveAuth {\n\tlet learning: Promise<void> | null = null;\n\tconst learn = (): void => {\n\t\tif (learning || rememberedDriveAccount(app)) return;\n\t\tlearning = learnDriveAccount(app, auth);\n\t};\n\treturn {\n\t\tasync token(opts) {\n\t\t\tconst value = await auth.token(opts);\n\t\t\tlearn();\n\t\t\treturn value;\n\t\t},\n\t\tasync reconnect() {\n\t\t\tconst ok = await auth.reconnect();\n\t\t\tif (ok) learn();\n\t\t\treturn ok;\n\t\t},\n\t\tforget: () => auth.forget()\n\t};\n}\n\n/** The schema version a backup's sidecar names: 0 for a copy that carries\n * none (written by another tool), as a remote converge reads it. */\nfunction schemaVersionOf(sidecar: unknown): number {\n\tif (typeof sidecar !== 'object' || sidecar === null) return 0;\n\tconst v = (sidecar as { schemaVersion?: unknown }).schemaVersion;\n\treturn Number.isInteger(v) && (v as number) >= 0 ? (v as number) : 0;\n}\n\n/** The app's own collections, without the library's `__*` bookkeeping. */\nfunction withoutReserved(collections: Snapshot['collections']): Snapshot['collections'] {\n\treturn Object.fromEntries(\n\t\tObject.entries(collections).filter(([name]) => !name.startsWith(RESERVED_COLLECTION_PREFIX))\n\t);\n}\n\n/**\n * Open (or create) the app's local store. Awaits the initial load, so the\n * returned store is ready: data readable, destination restored, first converge\n * done. See SimpleOptions for the defaults.\n */\nexport async function selfstore<\n\tS extends Record<string, SimpleRecord> = Record<string, SimpleRecord>\n>(app: string, options: SimpleOptions = {}): Promise<SimpleStore<S>> {\n\tif (typeof app !== 'string' || app.length === 0) {\n\t\tthrow new TypeError('selfstore: pass your app name, e.g. selfstore(\"my-app\").');\n\t}\n\n\t// The one copy of the data. The engine pulls it on save (gather) and pushes\n\t// into it on restores/folds (apply); the facade's mutators edit it in place\n\t// copy-on-write and schedule a save.\n\tlet collections: Record<string, SimpleRecord[]> = {};\n\t// Binary files: written through putFile/removeFile, and carried untouched\n\t// through every save, backup and merge whoever wrote them.\n\tlet files: SnapshotFile[] = [];\n\n\tconst dataSubs = new Set<() => void>();\n\tconst emitData = (): void => dataSubs.forEach((fn) => fn());\n\n\tconst cache =\n\t\toptions.cache ??\n\t\t(hasIndexedDb()\n\t\t\t? indexedDbCache(app, options.cacheLock ? { lock: true } : undefined)\n\t\t\t: memoryCache());\n\tconst backupName = `${app}.zip`;\n\t// connectDrive() remembers the auth so a later restoreTarget can use it even\n\t// when options.drive was not provided up front (same session only).\n\t//\n\t// `{ clientId }` is the whole wiring done here rather than in every app: ONE\n\t// session (several would mean several consent screens), a token that survives\n\t// a reload, and the account remembered so Google stops asking which one. Each\n\t// of those was a separate decision every consumer had to discover; the app\n\t// that wrote them by hand hit all three as bugs first.\n\t/** Built at most once: two managers over one destination would each hold\n\t * their own idea of which file is active. */\n\tlet backupsManager: BackupsManager | null = null;\n\t// Wrapped whichever shape it came in as: an app that mints its own tokens\n\t// (a server broker, a native shell) has the same nameless-destination\n\t// problem, and the wrapper only ever rides along on a token already granted.\n\tlet driveAuth: DriveAuth | null = isDriveAuth(options.drive)\n\t\t? selfNaming(app, options.drive)\n\t\t: options.drive\n\t\t\t? selfNaming(\n\t\t\t\t\tapp,\n\t\t\t\t\tgisDriveAuth({\n\t\t\t\t\t\tclientId: options.drive.clientId,\n\t\t\t\t\t\tscope: options.drive.scope,\n\t\t\t\t\t\tpersist: 'session',\n\t\t\t\t\t\thint: () => rememberedDriveAccount(app)\n\t\t\t\t\t})\n\t\t\t\t)\n\t\t\t: null;\n\n\t// Ultra-sensitive: unlock the cache before the store hydrates from it. One\n\t// prompt per session, driven by the app (branch it on an existing login to\n\t// keep the UX unchanged). A wrong secret re-prompts; throw to abort.\n\tif (options.cacheLock && isLockableCache(cache)) {\n\t\tlet failed = false;\n\t\twhile (!(await cache.unlock(await options.cacheLock({ failed })))) failed = true;\n\t}\n\n\tconst schema = options.schema ?? 1;\n\tconst store = createLocalStore({\n\t\tapp,\n\t\tschemaVersion: schema,\n\t\tgather: (): Snapshot => structuredClone({ collections, files }),\n\t\tapply: (snap: Snapshot): void => {\n\t\t\tcollections = (snap.collections ?? {}) as Record<string, SimpleRecord[]>;\n\t\t\tfiles = snap.files ?? [];\n\t\t\temitData();\n\t\t},\n\t\tmigrate: options.migrate,\n\t\tsync: options.sync,\n\t\tcache,\n\t\tdebounceMs: options.debounceMs,\n\t\tmultiTab: options.multiTab,\n\t\trequireEncryption: options.requireEncryption,\n\t\tpasswordPolicy: options.passwordPolicy,\n\t\trestoreTarget: async (kind) => {\n\t\t\tif (kind === 'drive' && driveAuth) {\n\t\t\t\treturn driveFromSession({ auth: driveAuth, kv: cache.kv, fileName: backupName });\n\t\t\t}\n\t\t\tif (kind === 'file') return fileFromSession({ kv: cache.kv });\n\t\t\tif (kind === 'webdav') return webdavFromSession({ kv: cache.kv });\n\t\t\treturn null;\n\t\t}\n\t});\n\n\tawait store.init();\n\n\t// A degraded file mode is a verdict about the BROWSER, but it was persisted\n\t// like a property of the data: every later session re-read the word without\n\t// ever asking again. One session that could not open a picker - a browser\n\t// that ships the API and refuses it, a call made a moment too late, a user on\n\t// another browser that day - then pinned download-only mode forever, on a\n\t// Chromium perfectly able to hold a file, with no way back.\n\t//\n\t// Nothing writes this word except a picker that could not deliver, so a\n\t// browser that CAN deliver makes it stale. Re-examined here, in the layer\n\t// that wrote it. Dropping to 'device' is the honest state: manual mode had\n\t// already released the durable target, so there is nothing to detach - only\n\t// a stale claim to stop repeating, and a destination the host can offer again.\n\tif (store.state.targetKind === 'file-manual' && fileIsSupported()) {\n\t\tawait store.detachTarget();\n\t}\n\n\t// Auto-wired sync moments (Spring-Boot-style autoconfiguration): tab focus,\n\t// network return, a slow interval, and save-on-hide. Opt out with\n\t// { autoSync: false } to drive these yourself.\n\tconst teardown: (() => void)[] = [];\n\tif (\n\t\toptions.autoSync !== false &&\n\t\ttypeof document !== 'undefined' &&\n\t\ttypeof window !== 'undefined'\n\t) {\n\t\tconst onVisibility = (): void => {\n\t\t\tif (document.visibilityState === 'hidden') void store.flush();\n\t\t\telse void store.syncIfStale('focus');\n\t\t};\n\t\tconst onOnline = (): void => void store.syncIfStale('online');\n\t\tconst onPageHide = (): void => void store.flush();\n\t\tconst interval = setInterval(() => {\n\t\t\tif (document.visibilityState === 'visible') void store.syncIfStale('interval');\n\t\t}, 5 * 60_000);\n\t\tdocument.addEventListener('visibilitychange', onVisibility);\n\t\twindow.addEventListener('online', onOnline);\n\t\twindow.addEventListener('pagehide', onPageHide);\n\t\tteardown.push(() => {\n\t\t\tclearInterval(interval);\n\t\t\tdocument.removeEventListener('visibilitychange', onVisibility);\n\t\t\twindow.removeEventListener('online', onOnline);\n\t\t\twindow.removeEventListener('pagehide', onPageHide);\n\t\t});\n\t}\n\n\tfunction mutate(collection: string, next: SimpleRecord[]): Promise<void> {\n\t\tcollections = { ...collections, [collection]: next };\n\t\temitData();\n\t\tstore.schedule();\n\t\treturn Promise.resolve();\n\t}\n\n\tfunction mutateFiles(next: SnapshotFile[]): Promise<void> {\n\t\tfiles = next;\n\t\temitData();\n\t\tstore.schedule();\n\t\treturn Promise.resolve();\n\t}\n\n\tasync function connectTarget(\n\t\ttarget: BackupTarget,\n\t\topts: { password?: string } = {}\n\t): Promise<ConnectOutcome> {\n\t\tconst info = await store.inspectTarget(target);\n\t\tif (info.hasBackup) {\n\t\t\t// Another app's backup is refused BEFORE the password question: asking\n\t\t\t// first teaches the user their password \"does not work\" on a file that\n\t\t\t// was never this app's to open.\n\t\t\tif (info.app !== null && info.app !== store.app) {\n\t\t\t\tthrow new SelfstoreError(\n\t\t\t\t\t'FOREIGN_BACKUP',\n\t\t\t\t\t`This destination holds a backup written by app \"${info.app}\", not \"${store.app}\". Open it in its own application, or pick another file.`\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Fail before attaching: an encrypted backup without its password would\n\t\t\t// otherwise attach locked, which is a support case, not an outcome.\n\t\t\tif (info.encrypted && !opts.password) {\n\t\t\t\tthrow new SelfstoreError(\n\t\t\t\t\t'PASSWORD_REQUIRED',\n\t\t\t\t\t'This destination holds an encrypted backup: pass its password to connect.'\n\t\t\t\t);\n\t\t\t}\n\t\t\tawait store.attachTarget(target, { password: opts.password ?? null, strategy: 'merge' });\n\t\t\treturn 'merged';\n\t\t}\n\t\tawait store.attachTarget(target, {\n\t\t\tpassword: opts.password ?? null,\n\t\t\tstrategy: 'replace-remote'\n\t\t});\n\t\treturn 'started';\n\t}\n\n\tconst simple: SimpleStore<S> = {\n\t\tall<K extends keyof S & string>(collection: K): readonly S[K][] {\n\t\t\treturn (collections[collection] ?? []) as S[K][];\n\t\t},\n\t\tget<K extends keyof S & string>(collection: K, id: string): S[K] | undefined {\n\t\t\tconst field = idFieldOf(options.sync, collection) ?? 'id';\n\t\t\treturn (collections[collection] ?? []).find((r) => r[field] === id) as S[K] | undefined;\n\t\t},\n\t\tput<K extends keyof S & string>(collection: K, record: S[K]): Promise<void> {\n\t\t\tconst field = idFieldOf(options.sync, collection);\n\t\t\tconst id = requireStringId(record, field, collection);\n\t\t\tconst rows = collections[collection] ?? [];\n\t\t\tconst at = field === null ? -1 : rows.findIndex((r) => r[field] === id);\n\t\t\treturn mutate(\n\t\t\t\tcollection,\n\t\t\t\tat === -1 ? [...rows, record] : rows.map((r, i) => (i === at ? record : r))\n\t\t\t);\n\t\t},\n\t\tputAll<K extends keyof S & string>(collection: K, records: S[K][]): Promise<void> {\n\t\t\tconst field = idFieldOf(options.sync, collection);\n\t\t\tconst byId = new Map<unknown, number>();\n\t\t\tfor (const r of records) requireStringId(r, field, collection);\n\t\t\tconst rows = [...(collections[collection] ?? [])];\n\t\t\tif (field !== null) {\n\t\t\t\trows.forEach((r, i) => byId.set(r[field], i));\n\t\t\t\tfor (const r of records) {\n\t\t\t\t\tconst at = byId.get(r[field]);\n\t\t\t\t\tif (typeof at === 'number') rows[at] = r;\n\t\t\t\t\telse rows.push(r);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\trows.push(...records);\n\t\t\t}\n\t\t\treturn mutate(collection, rows);\n\t\t},\n\t\tremove<K extends keyof S & string>(collection: K, id: string): Promise<void> {\n\t\t\tconst field = idFieldOf(options.sync, collection) ?? 'id';\n\t\t\tconst rows = collections[collection] ?? [];\n\t\t\tconst next = rows.filter((r) => r[field] !== id);\n\t\t\treturn next.length === rows.length ? Promise.resolve() : mutate(collection, next);\n\t\t},\n\t\tclear<K extends keyof S & string>(collection: K): Promise<void> {\n\t\t\tif ((collections[collection] ?? []).length === 0) return Promise.resolve();\n\t\t\treturn mutate(collection, []);\n\t\t},\n\t\tonChange(fn: () => void): () => void {\n\t\t\tdataSubs.add(fn);\n\t\t\treturn () => dataSubs.delete(fn);\n\t\t},\n\n\t\tallFiles(): readonly SnapshotFile[] {\n\t\t\treturn files;\n\t\t},\n\t\tgetFile(id: string): SnapshotFile | undefined {\n\t\t\treturn files.find((f) => f.id === id);\n\t\t},\n\t\tasync putFile(file: PutFileInput, opts: { replace?: boolean } = {}): Promise<string> {\n\t\t\tconst bytes = await toBytes(file.bytes);\n\t\t\tconst id = file.id ?? (await sha256Hex(bytes));\n\t\t\tif (typeof id !== 'string' || id.length === 0) {\n\t\t\t\tthrow new TypeError(\n\t\t\t\t\t`selfstore: a file needs a non-empty STRING id (got ${JSON.stringify(file.id)}), ` +\n\t\t\t\t\t\t`or omit it to get the content hash.`\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst at = files.findIndex((f) => f.id === id);\n\t\t\t// Same bytes under the same id is the normal case for a content id -\n\t\t\t// re-putting an unchanged body must not schedule a save.\n\t\t\tif (at !== -1 && !opts.replace) {\n\t\t\t\tif (sameBytes(files[at].bytes, bytes)) return id;\n\t\t\t\tthrow new TypeError(\n\t\t\t\t\t`selfstore: file \"${id}\" already holds different bytes. Files are immutable per id ` +\n\t\t\t\t\t\t`because the multi-device merge unions them by id and cannot order two bodies: ` +\n\t\t\t\t\t\t`the other device's copy would be dropped in silence. Omit the id to get a ` +\n\t\t\t\t\t\t`content-addressed one, or pass { replace: true } if this file is only ever ` +\n\t\t\t\t\t\t`written on one device.`\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst entry: SnapshotFile = {\n\t\t\t\tid,\n\t\t\t\tname: file.name ?? id,\n\t\t\t\tmime: file.mime ?? 'application/octet-stream',\n\t\t\t\tbytes\n\t\t\t};\n\t\t\tawait mutateFiles(\n\t\t\t\tat === -1 ? [...files, entry] : files.map((f, i) => (i === at ? entry : f))\n\t\t\t);\n\t\t\treturn id;\n\t\t},\n\t\tremoveFile(id: string): Promise<void> {\n\t\t\tconst next = files.filter((f) => f.id !== id);\n\t\t\treturn next.length === files.length ? Promise.resolve() : mutateFiles(next);\n\t\t},\n\n\t\tasync connectDrive(auth: DriveAuth, opts?: { password?: string }): Promise<ConnectOutcome> {\n\t\t\tdriveAuth = auth;\n\t\t\tconst target = await driveConnect({ auth, kv: cache.kv, fileName: backupName });\n\t\t\tif (!target) return 'cancelled';\n\t\t\tconst outcome = await connectTarget(target, opts);\n\t\t\t// Learn WHICH account now that there is a session: it is the hint that\n\t\t\t// keeps the chooser away next time, and the name a panel can show. Not\n\t\t\t// awaited into the outcome - a slow metadata call must not delay the\n\t\t\t// connect the user is waiting on.\n\t\t\tif (outcome !== 'cancelled') void learnDriveAccount(app, auth);\n\t\t\treturn outcome;\n\t\t},\n\t\tasync connectFile(opts?: { password?: string }): Promise<ConnectOutcome> {\n\t\t\t// Asked twice on purpose: the first call answers from what the browser\n\t\t\t// exposes, the second from what it did when actually asked. A browser\n\t\t\t// that ships the picker and then refuses it only tells us so here.\n\t\t\tif (fileIsSupported()) {\n\t\t\t\tconst target = await fileConnect({ kv: cache.kv, fileName: backupName });\n\t\t\t\tif (target) return connectTarget(target, opts);\n\t\t\t\tif (fileIsSupported()) return 'cancelled';\n\t\t\t}\n\t\t\tawait store.setManualFile();\n\t\t\treturn 'manual';\n\t\t},\n\t\tasync connectWebdav(\n\t\t\tconfig: WebdavConfig,\n\t\t\topts?: { password?: string }\n\t\t): Promise<ConnectOutcome> {\n\t\t\tconst target = await webdavConnect({ kv: cache.kv, config });\n\t\t\tif (!target) {\n\t\t\t\tthrow new SelfstoreError(\n\t\t\t\t\t'TARGET_UNAVAILABLE',\n\t\t\t\t\t'The WebDAV server did not answer (URL, credentials or CORS).'\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn connectTarget(target, opts);\n\t\t},\n\t\tasync connectS3(config: S3Config, opts?: { password?: string }): Promise<ConnectOutcome> {\n\t\t\tconst target = await s3Connect({ kv: cache.kv, config });\n\t\t\tif (!target) {\n\t\t\t\tthrow new SelfstoreError(\n\t\t\t\t\t'TARGET_UNAVAILABLE',\n\t\t\t\t\t'The S3 endpoint did not answer (endpoint, credentials or CORS).'\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn connectTarget(target, opts);\n\t\t},\n\t\tconnectTarget,\n\t\tdisconnect: () => store.detachTarget(),\n\t\taddReplica: (target, opts) => store.attachReplica(target, opts),\n\t\tremoveReplica: (id) => store.detachReplica(id),\n\n\t\tprotect: (password: string) => store.setEncryption(password),\n\t\tunprotect: () => store.setEncryption(null),\n\t\tunlock: (password: string) => store.unlock(password),\n\t\treconnect: () => store.reconnect(),\n\n\t\texportBackup: (opts?: { plaintext?: boolean }) => store.exportBlob(opts),\n\t\tasync downloadBackup(filename?: string): Promise<boolean> {\n\t\t\tconst written = await saveToDisk(await store.exportBlob(), filename ?? backupName);\n\t\t\t// Clearing the pending flag on a cancelled dialog would drop the very\n\t\t\t// nudge that asks for the file the user still does not have.\n\t\t\tif (written) store.markDownloaded();\n\t\t\treturn written;\n\t\t},\n\t\tasync importBackup(file: Blob | Uint8Array, opts?: { password?: string }): Promise<void> {\n\t\t\t// Judged by the schema that wrote it, as a remote copy is: a newer one\n\t\t\t// is refused, an older one is migrated BEFORE it replaces anything,\n\t\t\t// since the next save stamps the current version over it for good.\n\t\t\tconst { snapshot, sidecar } = await readBoxWithSync(await asBytes(file), opts?.password);\n\t\t\tconst fromVersion = schemaVersionOf(sidecar);\n\t\t\tif (fromVersion > schema) {\n\t\t\t\tthrow new SelfstoreError(\n\t\t\t\t\t'SCHEMA_TOO_NEW',\n\t\t\t\t\t`Backup written by schema v${fromVersion}; this app reads v${schema}. Update the app.`\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Reserved bookkeeping collections are stripped; removals then\n\t\t\t// propagate because the next save diffs against the sync meta.\n\t\t\tconst read = { collections: withoutReserved(snapshot.collections), files: snapshot.files };\n\t\t\tconst snap =\n\t\t\t\toptions.migrate && fromVersion < schema ? options.migrate(fromVersion, read) : read;\n\t\t\tcollections = (snap.collections ?? {}) as Record<string, SimpleRecord[]>;\n\t\t\tfiles = snap.files ?? [];\n\t\t\temitData();\n\t\t\tstore.schedule();\n\t\t},\n\n\t\tget status(): StatusDescriptor {\n\t\t\treturn store.state.status;\n\t\t},\n\t\tget error(): StoreError | null {\n\t\t\treturn store.state.lastError;\n\t\t},\n\t\tget state(): LocalStoreState {\n\t\t\treturn store.state;\n\t\t},\n\t\tsubscribe: (fn: () => void) => store.subscribe(fn),\n\n\t\tflush: () => store.flush(),\n\t\tasync sync(): Promise<void> {\n\t\t\tawait store.syncNow();\n\t\t},\n\t\tdispose(): void {\n\t\t\tteardown.forEach((fn) => fn());\n\t\t\tteardown.length = 0;\n\t\t\tstore.dispose();\n\t\t},\n\n\t\tadvanced: store,\n\n\t\tflowHost: { engine: store, kv: cache.kv, backupName },\n\n\t\tget account(): string | null {\n\t\t\treturn rememberedDriveAccount(app) ?? null;\n\t\t},\n\n\t\tasync backups(): Promise<BackupsManager | null> {\n\t\t\tif (!driveAuth) return null;\n\t\t\tbackupsManager ??= await (async () => {\n\t\t\t\tconst { createBackupsManager, driveBackupsHost } = await import('../backups/manager-drive');\n\t\t\t\treturn createBackupsManager({\n\t\t\t\t\tstore,\n\t\t\t\t\tkv: cache.kv,\n\t\t\t\t\thost: driveBackupsHost({ auth: driveAuth!, kv: cache.kv, fileName: backupName }),\n\t\t\t\t\tnaming: { canonicalName: backupName }\n\t\t\t\t});\n\t\t\t})();\n\t\t\treturn backupsManager;\n\t\t},\n\n\t\tdestinations(): ConnectTargets {\n\t\t\t// Drive first when there is one: it is the answer that follows a user\n\t\t\t// from device to device. A file always, for whoever wants no account.\n\t\t\treturn driveAuth ? { drive: driveAuth, file: true } : { file: true };\n\t\t},\n\n\t\tresumeOffer(): ResumeOffer | null {\n\t\t\tconst detail = rememberedDriveAccount(app);\n\t\t\tif (!driveAuth || !detail) return null;\n\t\t\tconst auth = driveAuth;\n\t\t\treturn {\n\t\t\t\tkind: 'drive',\n\t\t\t\tdetail,\n\t\t\t\t// Reopening is not searching: fromSession takes the file id already\n\t\t\t\t// remembered, where a fresh connect resolves by name and could land on\n\t\t\t\t// another file carrying the same one.\n\t\t\t\tconnect: () => driveFromSession({ auth, kv: cache.kv, fileName: backupName })\n\t\t\t};\n\t\t}\n\t};\n\n\treturn simple;\n}\n"]}
@@ -1,5 +1,5 @@
1
- import { a as Snapshot, E as EncodeOptions, H as Header } from './store-CZOqxDj5.js';
2
- import { a as DriveAuth } from './drive-DvnzpjLM.js';
1
+ import { a as Snapshot, E as EncodeOptions, H as Header } from './store-DSPbCCtM.js';
2
+ import { a as DriveAuth } from './drive-CwlKlowN.js';
3
3
 
4
4
  /** Serialize a snapshot to a portable backup blob (compressed; encrypted if a password is given). */
5
5
  declare function exportSnapshot(snapshot: Snapshot, opts: EncodeOptions): Promise<Blob>;
@@ -108,4 +108,5 @@ declare function datedName(filename: string, when?: Date): string;
108
108
  /** Prompt the user to pick a backup file from disk. Resolves null if cancelled. */
109
109
  declare function pickFromDisk(): Promise<File | null>;
110
110
 
111
- export { type GisDriveAuthOptions as G, type GisTokenPersistence as a, inspect as b, isEncrypted as c, datedName as d, exportSnapshot as e, preloadGoogleIdentity as f, gisDriveAuth as g, changePassword as h, importSnapshot as i, pickFromDisk as p, saveToDisk as s };
111
+ export { inspect as b, isEncrypted as c, datedName as d, exportSnapshot as e, preloadGoogleIdentity as f, gisDriveAuth as g, changePassword as h, importSnapshot as i, pickFromDisk as p, saveToDisk as s };
112
+ export type { GisDriveAuthOptions as G, GisTokenPersistence as a };
@@ -14,4 +14,5 @@ interface FlowStore<T> {
14
14
  * people are allowed to be slower than a deadline. */
15
15
  declare function withDeadline<T>(work: Promise<T>, ms: number, what: string): Promise<T>;
16
16
 
17
- export { type FlowStore as F, withDeadline as w };
17
+ export { withDeadline as w };
18
+ export type { FlowStore as F };
@@ -1,6 +1,6 @@
1
- import { K as KV } from './cache-ilsK3Tki.js';
2
- import { B as BackupTarget, L as LocalStore } from './store-CZOqxDj5.js';
3
- import { F as FlowStore } from './machine-ALMkT3mk.js';
1
+ import { K as KV } from './cache-BEv-Har2.js';
2
+ import { B as BackupTarget, L as LocalStore } from './store-DSPbCCtM.js';
3
+ import { F as FlowStore } from './machine-Dhbx3Eut.js';
4
4
 
5
5
  /** What the destination reports about a listed file (one cheap metadata call). */
6
6
  interface BackupFileInfo {
@@ -178,4 +178,5 @@ declare function createBackupsManager(deps: {
178
178
  keys?: BackupsKeys;
179
179
  }): BackupsManager;
180
180
 
181
- export { type BackupsHost as B, type KnownBackups as K, type BackupFileInfo as a, type BackupOwner as b, type BackupRow as c, type BackupsErrorCode as d, type BackupsKeys as e, type BackupsManager as f, type BackupsNaming as g, type BackupsSnapshot as h, createBackupsManager as i };
181
+ export { createBackupsManager as i };
182
+ export type { BackupsHost as B, KnownBackups as K, BackupFileInfo as a, BackupOwner as b, BackupRow as c, BackupsErrorCode as d, BackupsKeys as e, BackupsManager as f, BackupsNaming as g, BackupsSnapshot as h };
package/dist/passkey.d.ts CHANGED
@@ -83,4 +83,5 @@ interface PasskeyUnlock {
83
83
  }
84
84
  declare function passkeyUnlock(options: PasskeyUnlockOptions): PasskeyUnlock;
85
85
 
86
- export { type PasskeyUnlock, type PasskeyUnlockOptions, passkeyUnlock };
86
+ export { passkeyUnlock };
87
+ export type { PasskeyUnlock, PasskeyUnlockOptions };
package/dist/proof.d.ts CHANGED
@@ -1,12 +1,12 @@
1
- import { S as SimpleRecord, F as FileBytes } from './simple-B430fbx0.js';
2
- import './store-CZOqxDj5.js';
3
- import './cache-ilsK3Tki.js';
1
+ import { SimpleRecord, FileBytes } from './index.js';
2
+ import './store-DSPbCCtM.js';
3
+ import './cache-BEv-Har2.js';
4
4
  import './sync.js';
5
- import './connect-DMFxtXYO.js';
6
- import './drive-DvnzpjLM.js';
7
- import './s3-iiFYjype.js';
8
- import './machine-ALMkT3mk.js';
9
- import './manager-B-gb3lIn.js';
5
+ import './connect-CDazV36v.js';
6
+ import './drive-CwlKlowN.js';
7
+ import './s3-Bexn9kzT.js';
8
+ import './machine-Dhbx3Eut.js';
9
+ import './manager-CnlIs-hY.js';
10
10
 
11
11
  /**
12
12
  * What a notary hands back for one digest. `evidence` is the notary's own
@@ -174,12 +174,19 @@ interface JournalOptions {
174
174
  url: string;
175
175
  /** Receipt `notary` field. Defaults to 'digest-journal'. */
176
176
  name?: string;
177
+ /**
178
+ * The journal's deposit type, sent as `type` with every deposit (a journal
179
+ * that issues typed receipts, such as a permit display, keys them on it).
180
+ * None by default: the deposit is the journal's ordinary one.
181
+ */
182
+ type?: string;
177
183
  /** Injected for tests and relays; defaults to the global fetch. */
178
184
  fetch?: typeof fetch;
179
185
  }
180
186
  /**
181
187
  * A notary over a public digest journal (the protocol is at the top of this
182
- * file). The label is sent as the journal's `libelle`: keep it a TYPE.
188
+ * file). The label is sent as the journal's `libelle`: keep it a TYPE, unless
189
+ * the journal's `type` (an option of the notary) says what the label is.
183
190
  *
184
191
  * On deposit the receipt is incomplete; its evidence holds the journal's
185
192
  * address, the receipt `id`, the public `line` and the journal's own `receipt`
@@ -191,4 +198,5 @@ interface JournalOptions {
191
198
  */
192
199
  declare function digestJournalNotary(options: JournalOptions): Notary;
193
200
 
194
- export { type JournalOptions, type Notary, PROOFS, type PendingRound, type Proof, type ProofCheck, type ProofLibrary, type ProofShelf, type ProveOptions, type Receipt, type Rfc3161Options, type TimestampToken, completePending, completeProof, digestJournalNotary, proofsIn, prove, readTimestampReply, rfc3161Notary, timestampReplyOf, verify };
201
+ export { PROOFS, completePending, completeProof, digestJournalNotary, proofsIn, prove, readTimestampReply, rfc3161Notary, timestampReplyOf, verify };
202
+ export type { JournalOptions, Notary, PendingRound, Proof, ProofCheck, ProofLibrary, ProofShelf, ProveOptions, Receipt, Rfc3161Options, TimestampToken };
package/dist/proof.js CHANGED
@@ -345,6 +345,7 @@ function rankOf(deposits, id, hash) {
345
345
  function digestJournalNotary(options) {
346
346
  const name = options.name ?? "digest-journal";
347
347
  const base = options.url.replace(/\/+$/, "");
348
+ const typed = options.type === void 0 ? {} : { type: options.type };
348
349
  const send = options.fetch ?? globalThis.fetch;
349
350
  const get = (path) => reach(send, `${base}${path}`, void 0, "the journal");
350
351
  const bytesOf = async (path) => toBase64(new Uint8Array(await (await get(path)).arrayBuffer()));
@@ -357,7 +358,11 @@ function digestJournalNotary(options) {
357
358
  {
358
359
  method: "POST",
359
360
  headers: { "content-type": "application/json" },
360
- body: JSON.stringify({ sha256: hash, ...label === void 0 ? {} : { libelle: label } })
361
+ body: JSON.stringify({
362
+ sha256: hash,
363
+ ...label === void 0 ? {} : { libelle: label },
364
+ ...typed
365
+ })
361
366
  },
362
367
  "the journal"
363
368
  );