selfstore 1.8.9 → 1.8.11

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/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 { Snapshot, SnapshotFile } from '../selfstore/types';\nimport { SelfstoreError } from '../selfstore/errors';\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';\nimport {\n\tconnect as webdavConnect,\n\tfromSession as webdavFromSession,\n\ttype WebdavConfig\n} from '../persistence/targets/webdav';\nimport { connect as s3Connect, 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\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// --- 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\texportBackup(): 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 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\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 * 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 ride along untouched (a backup that carries them keeps them);\n\t// managing them is an advanced-store concern.\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\tlet driveAuth: DriveAuth | null = isDriveAuth(options.drive)\n\t\t? options.drive\n\t\t: options.drive\n\t\t\t? gisDriveAuth({\n\t\t\t\t\tclientId: options.drive.clientId,\n\t\t\t\t\tscope: options.drive.scope,\n\t\t\t\t\tpersist: 'session',\n\t\t\t\t\thint: () => rememberedDriveAccount(app)\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\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\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: () => store.exportBlob(),\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\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":"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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 { 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';\nimport {\n\tconnect as webdavConnect,\n\tfromSession as webdavFromSession,\n\ttype WebdavConfig\n} from '../persistence/targets/webdav';\nimport { connect as s3Connect, 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\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// --- 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\texportBackup(): 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\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 * 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 ride along untouched (a backup that carries them keeps them);\n\t// managing them is an advanced-store concern.\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\tlet driveAuth: DriveAuth | null = isDriveAuth(options.drive)\n\t\t? options.drive\n\t\t: options.drive\n\t\t\t? gisDriveAuth({\n\t\t\t\t\tclientId: options.drive.clientId,\n\t\t\t\t\tscope: options.drive.scope,\n\t\t\t\t\tpersist: 'session',\n\t\t\t\t\thint: () => rememberedDriveAccount(app)\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\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\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: () => store.exportBlob(),\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"]}
@@ -0,0 +1,8 @@
1
+ export { createBackupsManager, driveBackupsHost } from './chunk-HOG2IKEP.js';
2
+ import './chunk-W22V5CRM.js';
3
+ import './chunk-J5MFXJU6.js';
4
+ import './chunk-MGDXR3RB.js';
5
+ import './chunk-O3Y6FORN.js';
6
+ import './chunk-3OBJOMQ2.js';
7
+ //# sourceMappingURL=manager-drive-4WLKOOXO.js.map
8
+ //# sourceMappingURL=manager-drive-4WLKOOXO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"manager-drive-4WLKOOXO.js"}
package/dist/widgets.d.ts CHANGED
@@ -31,10 +31,13 @@ declare abstract class FlowWidget extends HTMLElement {
31
31
  /** Partial override map, merged over the EN defaults (also the i18n hook). */
32
32
  get labels(): WidgetLabels;
33
33
  set labels(map: WidgetLabels);
34
- /** The shipped translation for the page's language, if the widget has one.
35
- * An explicit `lang` on the element (or on any ancestor) wins over the
36
- * document's, which wins over the browser's, so a host that already sets
37
- * `<html lang>` gets translated copy without writing a single label. */
34
+ /** The language this widget speaks. An explicit `lang` on the element (or on
35
+ * any ancestor) wins over the document's, which wins over the browser's, so
36
+ * a host that already sets `<html lang>` gets translated copy without
37
+ * writing a single label. Also what to hand `Intl` when a widget formats a
38
+ * date or a duration itself. */
39
+ protected langTag(): string;
40
+ /** The shipped translation for the page's language, if the widget has one. */
38
41
  private localized;
39
42
  /**
40
43
  * The copy for a key, with `{placeholders}` filled in.
@@ -57,7 +60,33 @@ declare abstract class FlowWidget extends HTMLElement {
57
60
  /** Hands back whatever the host set before this class existed, then the
58
61
  * subclass reads its attributes and wires itself. Subclasses override this
59
62
  * and call `super.connectedCallback()` FIRST, for that reason. */
63
+ /**
64
+ * What the HOST decided before this element could hear it.
65
+ *
66
+ * Assigning a property to an element that has not upgraded yet is what every
67
+ * framework does - the widget bundle is loaded lazily, the assignment is
68
+ * not - and `upgradeOwnProperties` replays those assignments through the
69
+ * real setters. Then reading the same knob back from the markup would undo
70
+ * them: an attribute is a DEFAULT written by the page, a property is a
71
+ * decision taken by the app, and the decision has to win. A widget reads its
72
+ * attributes through this set.
73
+ */
74
+ protected assigned: Set<string>;
60
75
  connectedCallback(): void;
76
+ /**
77
+ * An attribute the host has NOT already decided in code, or null.
78
+ *
79
+ * Two ways it can have decided, and both have to be caught. It replayed
80
+ * through `assigned` on connect - or it has not been replayed yet, and its
81
+ * value is sitting on the element as an own property shadowing this very
82
+ * accessor. That second case is the one that bit: on upgrade the browser
83
+ * fires attributeChangedCallback BEFORE connectedCallback, so a widget
84
+ * writing the attribute through its own setter wrote into the shadowing own
85
+ * property instead - overwriting the decision it was about to replay.
86
+ */
87
+ protected attr(name: string): string | null;
88
+ /** The same, as a boolean: absent is null, anything but "false" is true. */
89
+ protected flag(name: string): boolean | null;
61
90
  protected emit(name: string, detail?: unknown): void;
62
91
  /** Rebuild the body, keeping the focused input's value and focus across the
63
92
  * rebuild (inputs carry data-keep so typing survives a snapshot change). */
@@ -523,7 +552,7 @@ declare class SelfstoreGateElement extends FlowWidget {
523
552
  * `flow` included). Null while the gate is shut. */
524
553
  get connect(): SelfstoreConnectElement | null;
525
554
  constructor();
526
- attributeChangedCallback(name: string, _old: string | null, value: string | null): void;
555
+ attributeChangedCallback(name: string): void;
527
556
  connectedCallback(): void;
528
557
  private host;
529
558
  private wire;
@@ -673,7 +702,7 @@ declare class SelfstoreStorageElement extends FlowWidget {
673
702
  /** Which destination the first-run screen badges as recommended. */
674
703
  get recommended(): ConnectKind | null;
675
704
  set recommended(v: ConnectKind | null);
676
- attributeChangedCallback(name: string, _old: string | null, value: string | null): void;
705
+ attributeChangedCallback(name: string): void;
677
706
  connectedCallback(): void;
678
707
  private host;
679
708
  /** What the store offers, unless the host said otherwise. */
@@ -681,10 +710,101 @@ declare class SelfstoreStorageElement extends FlowWidget {
681
710
  protected view(into: HTMLElement): void;
682
711
  }
683
712
 
713
+ /**
714
+ * <selfstore-account>: the storage control in an app's chrome.
715
+ *
716
+ * document.querySelector('selfstore-account').store = store;
717
+ *
718
+ * A trigger that states where the data is going, and a short menu that answers
719
+ * the two questions a header should answer: WHO holds my backup and when was
720
+ * it last written, and how do I change my mind.
721
+ *
722
+ * Why it is short. The panel (<selfstore-destination>) offers four gestures,
723
+ * which is right for a settings page and wrong for a menu: a header that opens
724
+ * onto "export a copy / restore a copy / change backup / stop saving here"
725
+ * asks the user to arbitrate between four irreversible-looking words before
726
+ * they know what any of them does. So this element keeps exactly two - go to
727
+ * the settings, where the panel lives, or change backup - and hands the rest
728
+ * to the page the host already has.
729
+ *
730
+ * "Change backup" detaches, and stops there. It does not run a connect journey
731
+ * of its own: an app that mounts <selfstore-gate> (or <selfstore-storage>)
732
+ * already has the first-run screen, and a store with no destination is exactly
733
+ * what that screen is for. One journey, reached the same way whether it is the
734
+ * first day or a change of mind.
735
+ */
736
+
737
+ declare class SelfstoreAccountElement extends FlowWidget {
738
+ #private;
739
+ protected defaults(): WidgetLabels;
740
+ protected packs(): Record<string, WidgetLabels>;
741
+ constructor();
742
+ /** The simple store (anything exposing `flowHost`), or a hand-built FlowHost.
743
+ * A simple store also answers `account` on its own, so nothing else is
744
+ * needed to name who holds the backup. */
745
+ get store(): StoreLike | null;
746
+ set store(v: StoreLike | null);
747
+ get account(): string | null;
748
+ set account(v: string | null);
749
+ /** Optional glyph per destination kind, same contract as the connect cards. */
750
+ get icons(): Partial<Record<ConnectKind, string>>;
751
+ set icons(v: Partial<Record<ConnectKind, string>> | null);
752
+ /** Veto on changing backup, which leaves the app with no destination until
753
+ * the user picks one. Returns false (or throws) to stop. */
754
+ get confirmAction(): ((a: DestinationAction) => boolean | Promise<boolean>) | null;
755
+ set confirmAction(fn: ((a: DestinationAction) => boolean | Promise<boolean>) | null);
756
+ /** Whether the menu is on screen. Settable, so a host can close it from its
757
+ * own router after the settings item navigated away. */
758
+ get open(): boolean;
759
+ set open(v: boolean);
760
+ connectedCallback(): void;
761
+ disconnectedCallback(): void;
762
+ /** While the menu is open: a clock for the relative time, and the two ways
763
+ * out that make it a menu rather than a page - a click anywhere else, and
764
+ * Escape. `pointerdown` and not `click`, so the gesture that closes it does
765
+ * not also land on whatever was under the menu. Shadow DOM retargets the
766
+ * event, so what is asked is the composed path, never `e.target`. */
767
+ private watch;
768
+ /** The destination's name. An unknown kind (a host's own target) has no
769
+ * shipped name: its own label is a better answer than the raw key. */
770
+ private named;
771
+ /** "just now", "3 minutes ago", "yesterday" - in the page's language, from
772
+ * Intl rather than from five label keys per language. */
773
+ private when;
774
+ /**
775
+ * The one line under the account. A plain "saved" is the only state this
776
+ * element words itself, because it is the only one with nothing to do about
777
+ * it - and the only one where WHEN is the whole information. Everything else
778
+ * (reconnect, unlock, changes to download) belongs to <selfstore-status>,
779
+ * which owns both the wording and the button that resolves it.
780
+ */
781
+ private line;
782
+ /** Ask for the settings. The host owns the route, so this only says that it
783
+ * was asked - and closes, because a menu that stays open behind a page
784
+ * transition reads as a failed click. */
785
+ private settings;
786
+ /** Change backup: detach, and let the first-run screen take it from there.
787
+ * The data stays; only the destination is let go. */
788
+ private change;
789
+ private card;
790
+ protected view(into: HTMLElement): void;
791
+ /**
792
+ * Which side the menu hangs from.
793
+ *
794
+ * A header control is usually at the right edge, so the menu hangs right -
795
+ * and the moment the header wraps on a narrow window, that same control sits
796
+ * at the LEFT edge and the menu hangs off the screen. Measured rather than
797
+ * assumed: it hangs left when there is room to the right, and right
798
+ * otherwise, which lands correctly at both edges without the host declaring
799
+ * where it put the control.
800
+ */
801
+ private anchor;
802
+ }
803
+
684
804
  /** Register the elements as <PREFIX-connect>, <PREFIX-share>, <PREFIX-join>,
685
805
  * <PREFIX-status>, <PREFIX-backups>, <PREFIX-gate> and
686
806
  * <PREFIX-destination> (default prefix 'selfstore'). Safe to call twice;
687
807
  * throws in environments without custom elements (browser code only). */
688
808
  declare function defineSelfstoreWidgets(prefix?: string): void;
689
809
 
690
- export { type BackupsAction, type DestinationAction, FlowWidget, SelfstoreBackupsElement, SelfstoreConnectElement, SelfstoreDestinationElement, SelfstoreGateElement, SelfstoreJoinElement, SelfstoreShareElement, SelfstoreStatusElement, SelfstoreStorageElement, type ShareAction, type WebdavPreset, type WidgetLabels, defineSelfstoreWidgets };
810
+ export { type BackupsAction, type DestinationAction, FlowWidget, SelfstoreAccountElement, SelfstoreBackupsElement, SelfstoreConnectElement, SelfstoreDestinationElement, SelfstoreGateElement, SelfstoreJoinElement, SelfstoreShareElement, SelfstoreStatusElement, SelfstoreStorageElement, type ShareAction, type WebdavPreset, type WidgetLabels, defineSelfstoreWidgets };