selfstore 1.8.2 → 1.8.3

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.
Files changed (54) hide show
  1. package/dist/advanced.d.ts +8 -4
  2. package/dist/advanced.js +14 -13
  3. package/dist/advanced.js.map +1 -1
  4. package/dist/backups.d.ts +31 -179
  5. package/dist/backups.js +40 -3
  6. package/dist/backups.js.map +1 -1
  7. package/dist/{cache-lock-RCN3JJ4Q.js → cache-lock-KWJUORTS.js} +3 -4
  8. package/dist/cache-lock-KWJUORTS.js.map +1 -0
  9. package/dist/{chunk-HK4VPO65.js → chunk-3OBJOMQ2.js} +28 -4
  10. package/dist/chunk-3OBJOMQ2.js.map +1 -0
  11. package/dist/{chunk-HDR75TQ7.js → chunk-DCEXWSM6.js} +3 -3
  12. package/dist/{chunk-HDR75TQ7.js.map → chunk-DCEXWSM6.js.map} +1 -1
  13. package/dist/{chunk-7N2YR3QW.js → chunk-ELDSPUTS.js} +7 -8
  14. package/dist/chunk-ELDSPUTS.js.map +1 -0
  15. package/dist/chunk-J5MFXJU6.js +310 -0
  16. package/dist/chunk-J5MFXJU6.js.map +1 -0
  17. package/dist/{chunk-BNDFMU3J.js → chunk-MGDXR3RB.js} +5 -6
  18. package/dist/chunk-MGDXR3RB.js.map +1 -0
  19. package/dist/{chunk-453KRCMM.js → chunk-O3Y6FORN.js} +3 -4
  20. package/dist/chunk-O3Y6FORN.js.map +1 -0
  21. package/dist/{chunk-JMJZIZKY.js → chunk-PNX7AJYO.js} +13 -12
  22. package/dist/chunk-PNX7AJYO.js.map +1 -0
  23. package/dist/{chunk-5NS7YQNP.js → chunk-TKWU52LK.js} +26 -312
  24. package/dist/chunk-TKWU52LK.js.map +1 -0
  25. package/dist/{chunk-U5F4GI7C.js → chunk-W22V5CRM.js} +3 -3
  26. package/dist/{chunk-U5F4GI7C.js.map → chunk-W22V5CRM.js.map} +1 -1
  27. package/dist/{drive-l_4-m1eT.d.ts → drive-CTYvGg_7.d.ts} +23 -110
  28. package/dist/{flows-C_uookek.d.ts → flows-1EntUZ91.d.ts} +2 -1
  29. package/dist/flows.d.ts +3 -2
  30. package/dist/flows.js +16 -16
  31. package/dist/flows.js.map +1 -1
  32. package/dist/groups.js +3 -4
  33. package/dist/groups.js.map +1 -1
  34. package/dist/index.d.ts +3 -2
  35. package/dist/index.js +18 -18
  36. package/dist/index.js.map +1 -1
  37. package/dist/{local-bfv4Ld7s.d.ts → local-C52j3DeH.d.ts} +1 -1
  38. package/dist/local-DPS35F5C.js +6 -0
  39. package/dist/{local-2YREU4Q2.js.map → local-DPS35F5C.js.map} +1 -1
  40. package/dist/manager-DZ1c_aSS.d.ts +181 -0
  41. package/dist/s3-DvsnA-FK.d.ts +112 -0
  42. package/dist/widgets.d.ts +4 -3
  43. package/dist/widgets.js +7 -7
  44. package/package.json +1 -1
  45. package/dist/cache-lock-RCN3JJ4Q.js.map +0 -1
  46. package/dist/chunk-453KRCMM.js.map +0 -1
  47. package/dist/chunk-5NS7YQNP.js.map +0 -1
  48. package/dist/chunk-7N2YR3QW.js.map +0 -1
  49. package/dist/chunk-BNDFMU3J.js.map +0 -1
  50. package/dist/chunk-CPN2KUD5.js +0 -29
  51. package/dist/chunk-CPN2KUD5.js.map +0 -1
  52. package/dist/chunk-HK4VPO65.js.map +0 -1
  53. package/dist/chunk-JMJZIZKY.js.map +0 -1
  54. package/dist/local-2YREU4Q2.js +0 -7
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 { Snapshot, SnapshotFile } from '../selfstore/types';\nimport { SelfstoreError } from '../selfstore/errors';\nimport { restore, type PasswordPolicy } from '../selfstore';\nimport { saveToDisk } from '../selfstore/targets/local';\nimport {\n\tconnect as driveConnect,\n\tfromSession as driveFromSession,\n\ttype DriveAuth\n} from '../persistence/targets/drive';\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/** Google Drive auth, when this app uses Drive: providing it here lets a\n\t * connected Drive backup restore itself on the next start. */\n\tdrive?: DriveAuth;\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\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/**\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\tlet driveAuth: DriveAuth | null = options.drive ?? 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\treturn connectTarget(target, opts);\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\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 { Snapshot, SnapshotFile } from '../selfstore/types';\nimport { SelfstoreError } from '../selfstore/errors';\nimport { restore, type PasswordPolicy } from '../selfstore';\nimport { saveToDisk } from '../selfstore/targets/local';\nimport {\n\tconnect as driveConnect,\n\tfromSession as driveFromSession,\n\ttype DriveAuth\n} from '../persistence/targets/drive';\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/** Google Drive auth, when this app uses Drive: providing it here lets a\n\t * connected Drive backup restore itself on the next start. */\n\tdrive?: DriveAuth;\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\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/**\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\tlet driveAuth: DriveAuth | null = options.drive ?? 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\treturn connectTarget(target, opts);\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\t};\n\n\treturn simple;\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { n as Snapshot, E as EncodeOptions, H as Header } from './store-BiQ-BuVl.js';
2
- import { D as DriveAuth } from './drive-l_4-m1eT.js';
2
+ import { i as DriveAuth } from './drive-CTYvGg_7.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>;
@@ -0,0 +1,6 @@
1
+ export { datedName, downloadBlob, pickFromDisk, pickSaveHandle, saveToDisk, writeToHandle } from './chunk-DCEXWSM6.js';
2
+ import './chunk-MGDXR3RB.js';
3
+ import './chunk-O3Y6FORN.js';
4
+ import './chunk-3OBJOMQ2.js';
5
+ //# sourceMappingURL=local-DPS35F5C.js.map
6
+ //# sourceMappingURL=local-DPS35F5C.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"local-2YREU4Q2.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"local-DPS35F5C.js"}
@@ -0,0 +1,181 @@
1
+ import { K as KV } from './cache-ilsK3Tki.js';
2
+ import { B as BackupTarget, L as LocalStore } from './store-BiQ-BuVl.js';
3
+ import { F as FlowStore } from './machine-ALMkT3mk.js';
4
+
5
+ /** What the destination reports about a listed file (one cheap metadata call). */
6
+ interface BackupFileInfo {
7
+ id: string;
8
+ name: string;
9
+ /** RFC 3339 last-write timestamp, when the destination reports one. */
10
+ modifiedTime: string | null;
11
+ }
12
+ /** The destination I/O the manager drives - the host wires its own adapter
13
+ * (e.g. over the Drive target ops). Every method may throw; the manager turns
14
+ * failures into stable error codes. */
15
+ interface BackupsHost {
16
+ /** The target kind the managed files live on (e.g. 'drive'): the manager
17
+ * only stamps an active file while the store is attached to this kind. */
18
+ kind: string;
19
+ /** The kv key the attached file id is persisted under (the same one the
20
+ * target's connect path reads first). */
21
+ activeIdKey: string;
22
+ /** List candidate backup files on the account. The manager applies the
23
+ * naming rules on what comes back; internal files simply do not match. */
24
+ list(): Promise<BackupFileInfo[]>;
25
+ /** A target over a FIXED file id - never re-resolved by name. The fixed id
26
+ * is what keeps two backups from silently merging mid-switch. */
27
+ open(fileId: string): BackupTarget;
28
+ /** Create an empty file under this name and return its id. Refuse a name
29
+ * the account already carries (a duplicate poisons find-by-name). */
30
+ create(fileName: string): Promise<{
31
+ fileId: string;
32
+ }>;
33
+ /** Delete a file for good. */
34
+ remove(fileId: string): Promise<void>;
35
+ /** Rename a file in place (metadata only: the id must not change). */
36
+ rename?(fileId: string, fileName: string): Promise<void>;
37
+ /** Find (or create) the PERSONAL file by its canonical name, ignoring any
38
+ * currently adopted id. Null when the user backs out of the session. */
39
+ findOrCreatePersonal(): Promise<{
40
+ fileId: string;
41
+ created: boolean;
42
+ } | null>;
43
+ /** Make sure a usable session exists before a remote call (re-consent when
44
+ * it lapsed). False = the user refused; the gesture aborts as 'cancelled'.
45
+ * Absent = sessions are the target's own business. */
46
+ ensureSession?(): Promise<boolean>;
47
+ /** Resolve the owner label of a file this account does not own, for the
48
+ * shared registry ("shared by X"). Best-effort. */
49
+ fileOwner?(fileId: string): Promise<{
50
+ email: string | null;
51
+ name: string | null;
52
+ } | null>;
53
+ /** File ids that must never enter the registry even though the store may
54
+ * attach them (e.g. a sharing engine's published copy file). */
55
+ excludedFileIds?(): Promise<(string | null | undefined)[]>;
56
+ }
57
+ /** How backup files are named on the destination. */
58
+ interface BackupsNaming {
59
+ /** The personal, unnamed file ("App.zip"). Its label reads as null. */
60
+ canonicalName: string;
61
+ /** label -> file name. Default: "Stem (label).ext" derived from
62
+ * canonicalName ("App.zip" + "family" -> "App (family).zip"). */
63
+ namedFileFor?(label: string): string;
64
+ /** file name -> parsed label; null = the personal file; undefined = not one
65
+ * of ours (dropped from listings). Default matches the default namedFileFor. */
66
+ parseLabel?(name: string): string | null | undefined;
67
+ }
68
+ /** The kv keys the manager persists under - override to keep a pre-existing
69
+ * install's records. */
70
+ interface BackupsKeys {
71
+ /** The known-backups registry. */
72
+ registry?: string;
73
+ /** fileId -> encrypted? map. */
74
+ encrypted?: string;
75
+ /** fileId -> shared? map. */
76
+ shared?: string;
77
+ /** Set while the ACTIVE file is not the personal one (another account's). */
78
+ joined?: string;
79
+ /** Cached owner label of that file. */
80
+ owner?: string;
81
+ }
82
+ /** Every backup this device has connected to: its own personal file and each
83
+ * OTHER account's file it was let into. Isolated snapshots, all of them. */
84
+ interface KnownBackups {
85
+ personalFileId: string | null;
86
+ shared: {
87
+ fileId: string;
88
+ ownerEmail: string | null;
89
+ ownerName: string | null;
90
+ }[];
91
+ }
92
+ /** One row of the "my backups" list: a file this account owns. */
93
+ interface BackupRow {
94
+ fileId: string;
95
+ name: string;
96
+ /** Parsed display label; null for the personal, unnamed file. */
97
+ label: string | null;
98
+ /** Last-write time, epoch ms (null when the destination did not say). */
99
+ modifiedAt: number | null;
100
+ /** true/false once known (opened, created or probed); null until then. */
101
+ encrypted: boolean | null;
102
+ /** true/false once learned (while this file was the active one); null
103
+ * until then. A share link existing is the shared state. */
104
+ shared: boolean | null;
105
+ }
106
+ /** The owner label of a joined (another account's) file. */
107
+ interface BackupOwner {
108
+ email: string | null;
109
+ name: string | null;
110
+ }
111
+ /** Stable failure codes; the host maps them to its own copy. */
112
+ type BackupsErrorCode = 'cancelled' | 'gone' | 'failed';
113
+ interface BackupsSnapshot {
114
+ registry: KnownBackups;
115
+ /** The file currently attached, when the store is on the managed kind. */
116
+ activeFileId: string | null;
117
+ /** The active file is not the personal one (someone else's). */
118
+ joined: boolean;
119
+ /** Who owns the joined file, when known. */
120
+ owner: BackupOwner | null;
121
+ /** The last gesture's failure, or null. Reset at each gesture start. */
122
+ lastError: BackupsErrorCode | null;
123
+ }
124
+ interface BackupsManager extends FlowStore<BackupsSnapshot> {
125
+ /** Boot: load registry + joined/owner from kv and stamp the active file.
126
+ * Kv-only, no destination call. */
127
+ hydrate(): Promise<void>;
128
+ /** Re-read the registry and restamp the active file. Kv-only, cheap. */
129
+ refresh(): Promise<void>;
130
+ /** After an attach the manager did not make (a connect widget, a join):
131
+ * record the now-active file in the registry and restamp joined/owner. */
132
+ markActive(fileId: string): Promise<void>;
133
+ /** Live list of this account's own files, per the naming rules, decorated
134
+ * with the learned memories. One destination listing call. */
135
+ list(): Promise<BackupRow[]>;
136
+ /** Learn a listed file's encrypted bit without opening it for real: one
137
+ * download + header read, remembered for good. Null when unreachable. */
138
+ probeEncryption(fileId: string): Promise<boolean | null>;
139
+ /** Record the ACTIVE file's live share state, keyed by file id, so its row
140
+ * stays truthful after a switch away. */
141
+ noteShared(fileId: string, shared: boolean): Promise<void>;
142
+ /** Open a known backup: an isolated snapshot replaces the local data.
143
+ * 'encrypted' = collect its passphrase and call again with it. A file the
144
+ * destination reports GONE is pruned from the registry (lastError 'gone'). */
145
+ openBackup(fileId: string, passphrase?: string | null): Promise<'ok' | 'encrypted' | 'failed'>;
146
+ /** Open (or create blank) the personal file. */
147
+ openPersonal(passphrase?: string | null): Promise<'ok' | 'encrypted' | 'failed'>;
148
+ /** Create a brand-new empty named backup and open it (starts blank - never
149
+ * seeded from the currently loaded file). */
150
+ createNamed(label: string): Promise<'ok' | 'failed'>;
151
+ /** Record a file in the SHARED registry under the sharer's label, without
152
+ * opening it. For a joined portfolio's dedicated wallet: the file may live
153
+ * on this very account, yet it presents as "shared by X", never as mine. */
154
+ registerShared(fileId: string, owner: BackupOwner): Promise<void>;
155
+ /** Create a joined portfolio's dedicated wallet: a file under the given
156
+ * name (outside the own-files naming on purpose), attached BLANK - never
157
+ * seeded from the previously loaded silo - and recorded in the shared
158
+ * registry under the sharer's label. The host stages the shared content
159
+ * afterwards (restore the projection, then save). */
160
+ createShared(fileName: string, owner: BackupOwner): Promise<'ok' | 'failed'>;
161
+ /** Rename a file in place (id unchanged, so renaming the ACTIVE file never
162
+ * detaches it). The host target refuses a name already carried. */
163
+ renameBackup(fileId: string, label: string): Promise<'ok' | 'failed'>;
164
+ /** Delete a file for good. Deleting the ACTIVE one detaches first (the app
165
+ * goes device-only, local data stays), so nothing writes mid-delete. */
166
+ deleteBackup(fileId: string): Promise<boolean>;
167
+ /** Drop a KNOWN shared (another account's) file from this device's
168
+ * registry. Forgetting only - the owner's file is never touched. */
169
+ forgetShared(fileId: string): Promise<void>;
170
+ /** The file name a label produces (the create form's preview line). */
171
+ fileNameFor(label: string): string;
172
+ }
173
+ declare function createBackupsManager(deps: {
174
+ store: LocalStore;
175
+ kv: KV;
176
+ host: BackupsHost;
177
+ naming: BackupsNaming;
178
+ keys?: BackupsKeys;
179
+ }): BackupsManager;
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 };
@@ -0,0 +1,112 @@
1
+ import { B as BackupTarget, P as PeerSource } from './store-BiQ-BuVl.js';
2
+ import { K as KV } from './cache-ilsK3Tki.js';
3
+
4
+ /**
5
+ * WebDAV backup target: the store re-writes one .selfstore file on a WebDAV
6
+ * server the USER controls (Nextcloud, ownCloud, a plain WebDAV mount...). No
7
+ * Google, no vendor broker - the browser talks straight to the server the user
8
+ * owns. The bytes are the same end-to-end-encrypted .selfstore, so the server
9
+ * only ever holds ciphertext.
10
+ *
11
+ * Two things the environment must allow, both documented for the user:
12
+ * - the server must send CORS for the app origin (methods PUT/GET/HEAD and the
13
+ * Authorization header), since browsers block cross-origin WebDAV otherwise;
14
+ * - the app's CSP connect-src must permit the server host.
15
+ *
16
+ * Credentials are stored ENCRYPTED at rest under a per-device non-extractable
17
+ * key, so a set-and-forget connection never leaves a cleartext password in
18
+ * IndexedDB. The version marker prefers ETag, falling back to Last-Modified
19
+ * (the latter is CORS-safelisted, so it is readable without extra server config).
20
+ */
21
+
22
+ interface WebdavConfig {
23
+ /** Full URL of the backup file, e.g. https://cloud.me/remote.php/dav/files/me/backup.zip */
24
+ url: string;
25
+ username: string;
26
+ password: string;
27
+ }
28
+ interface WebdavConnectOptions {
29
+ /** Where the (encrypted) server config is persisted across sessions. */
30
+ kv: KV;
31
+ config: WebdavConfig;
32
+ }
33
+ interface WebdavPeerOptions {
34
+ /** URL of ANOTHER member's published copy, read-only: a Nextcloud/ownCloud
35
+ * read-only share link, or a file URL they granted you read access to. */
36
+ url: string;
37
+ /** Optional Basic-auth credentials, when the read-only share needs them.
38
+ * Omit for a public/tokenised share link (a Nextcloud `/s/<token>/download`
39
+ * URL) that needs none. Held in memory only - a peer is re-attached each
40
+ * session (see attachPeer), so nothing is persisted here. */
41
+ username?: string;
42
+ password?: string;
43
+ /** Display label (defaults to the URL host). */
44
+ label?: string;
45
+ }
46
+ /** A read-only WebDAV peer: another member's published copy, fetched over a
47
+ * shared URL, for `store.attachPeer`. It is the read half of a WebDAV target
48
+ * (GET + HEAD only, never a write), so a group can share read-write without
49
+ * Google Drive - each member publishes to their own WebDAV and shares a
50
+ * read-only link. Failures follow the peer protocol: a 401/403 is a genuine
51
+ * loss of read access (AuthExpiredError), 404 means "not published yet"
52
+ * (null), anything else is transient. */
53
+ declare function peer(opts: WebdavPeerOptions): PeerSource;
54
+ /** Validate + persist the config, returning the target, or null when the server
55
+ * is unreachable, rejects the credentials, or blocks the cross-origin request. */
56
+ declare function connect$1(opts: WebdavConnectOptions): Promise<BackupTarget | null>;
57
+ /** Rebuild the target from the encrypted config persisted last session, or null. */
58
+ declare function fromSession$1(opts: {
59
+ kv: KV;
60
+ }): Promise<BackupTarget | null>;
61
+
62
+ /**
63
+ * S3 backup target: the store re-writes one object on an S3-compatible bucket
64
+ * the USER controls - Amazon S3, Cloudflare R2, Backblaze B2, MinIO, any server
65
+ * that speaks the S3 REST API and SigV4. No vendor broker: the browser signs
66
+ * each request itself and talks straight to the endpoint. The bytes are the
67
+ * same end-to-end-encrypted .selfstore, so the bucket only ever holds
68
+ * ciphertext.
69
+ *
70
+ * Two things the environment must allow, both documented for the user:
71
+ * - the bucket must send CORS for the app origin: allowed methods GET/PUT/HEAD,
72
+ * allowed headers authorization/x-amz-*, and ExposeHeaders ETag (so the
73
+ * version marker is readable), or browsers block the cross-origin request;
74
+ * - the app's CSP connect-src must permit the endpoint host.
75
+ *
76
+ * Credentials (the secret access key) are stored ENCRYPTED at rest under a
77
+ * per-device non-extractable key, exactly like the WebDAV target, so a
78
+ * set-and-forget connection never leaves a cleartext secret in IndexedDB.
79
+ *
80
+ * Path style is the default: the object lives at endpoint/bucket/key, which
81
+ * every S3-compatible server accepts and needs no per-bucket DNS. Set
82
+ * forcePathStyle:false for AWS virtual-hosted style (bucket as a host label).
83
+ */
84
+
85
+ interface S3Config {
86
+ /** Endpoint origin, e.g. https://s3.eu-west-3.amazonaws.com,
87
+ * https://<account>.r2.cloudflarestorage.com, https://minio.example.com. */
88
+ endpoint: string;
89
+ /** Signing region ("eu-west-3"; R2 uses "auto"; MinIO any configured value). */
90
+ region: string;
91
+ bucket: string;
92
+ /** Object key (the backup file), e.g. backups/app.selfstore. */
93
+ key: string;
94
+ accessKeyId: string;
95
+ secretAccessKey: string;
96
+ /** endpoint/bucket/key (default) vs bucket.endpoint/key. Default true. */
97
+ forcePathStyle?: boolean;
98
+ }
99
+ interface S3ConnectOptions {
100
+ /** Where the (encrypted) config is persisted across sessions. */
101
+ kv: KV;
102
+ config: S3Config;
103
+ }
104
+ /** Validate + persist the config, returning the target, or null when the bucket
105
+ * is unreachable, rejects the credentials, or blocks the cross-origin request. */
106
+ declare function connect(opts: S3ConnectOptions): Promise<BackupTarget | null>;
107
+ /** Rebuild the target from the encrypted config persisted last session, or null. */
108
+ declare function fromSession(opts: {
109
+ kv: KV;
110
+ }): Promise<BackupTarget | null>;
111
+
112
+ export { type S3Config as S, type WebdavConfig as W, connect$1 as a, fromSession$1 as b, connect as c, type S3ConnectOptions as d, type WebdavConnectOptions as e, fromSession as f, type WebdavPeerOptions as g, peer as p };
package/dist/widgets.d.ts CHANGED
@@ -1,9 +1,10 @@
1
- import { S as StoreLike, C as ConnectTargets, a as ConnectFlowOptions, b as ConnectKind, c as ConnectFlow, d as ShareEngine, e as ShareLevel, f as ShareFlow, J as JoinEngine, g as JoinFlow, R as ReplicaFlow } from './flows-C_uookek.js';
2
- import { BackupsManager } from './backups.js';
1
+ import { S as StoreLike, C as ConnectTargets, a as ConnectFlowOptions, b as ConnectKind, c as ConnectFlow, d as ShareEngine, e as ShareLevel, f as ShareFlow, J as JoinEngine, g as JoinFlow, R as ReplicaFlow } from './flows-1EntUZ91.js';
2
+ import { f as BackupsManager } from './manager-DZ1c_aSS.js';
3
3
  import './store-BiQ-BuVl.js';
4
4
  import './cache-ilsK3Tki.js';
5
5
  import './sync.js';
6
- import './drive-l_4-m1eT.js';
6
+ import './drive-CTYvGg_7.js';
7
+ import './s3-DvsnA-FK.js';
7
8
  import './machine-ALMkT3mk.js';
8
9
 
9
10
  /** Flat key -> string map; widgets merge a partial override over EN defaults. */
package/dist/widgets.js CHANGED
@@ -1,11 +1,11 @@
1
- import { connectFlow, shareFlow, joinFlow } from './chunk-JMJZIZKY.js';
2
- import { isOpenSupported } from './chunk-5NS7YQNP.js';
3
- import './chunk-BNDFMU3J.js';
4
- import './chunk-U5F4GI7C.js';
5
- import './chunk-453KRCMM.js';
1
+ import { connectFlow, shareFlow, joinFlow } from './chunk-PNX7AJYO.js';
2
+ import { isOpenSupported } from './chunk-TKWU52LK.js';
3
+ import './chunk-W22V5CRM.js';
4
+ import './chunk-J5MFXJU6.js';
5
+ import './chunk-MGDXR3RB.js';
6
6
  import './chunk-QBCU7OOP.js';
7
- import './chunk-HK4VPO65.js';
8
- import './chunk-CPN2KUD5.js';
7
+ import './chunk-O3Y6FORN.js';
8
+ import './chunk-3OBJOMQ2.js';
9
9
 
10
10
  // src/widgets/icons.ts
11
11
  var INK = "#8a8f98";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "selfstore",
3
- "version": "1.8.2",
3
+ "version": "1.8.3",
4
4
  "description": "Local-first storage for browser apps: an IndexedDB working copy, portable encrypted ZIP backups (AES-256-GCM over Argon2id), durable homes (disk, Google Drive, WebDAV) and a serverless multi-device merge.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/persistence/cache-lock.ts"],"names":[],"mappings":";;;;;AAeO,SAAS,aAAA,GAA0B;AACzC,EAAA,OAAO,cAAA,EAAe;AACvB;AAGO,SAAS,eAAA,CAAgB,UAAkB,GAAA,EAAmC;AACpF,EAAA,OAAO,SAAA,CAAU,UAAU,GAAG,CAAA;AAC/B;AAIA,eAAsB,KAAA,CAAM,KAAgB,KAAA,EAAsC;AACjF,EAAA,IAAI;AACH,IAAA,MAAM,MAAA,CAAO,KAAK,KAAK,CAAA;AACvB,IAAA,OAAO,IAAA;AAAA,EACR,CAAA,CAAA,MAAQ;AACP,IAAA,OAAO,KAAA;AAAA,EACR;AACD","file":"cache-lock-RCN3JJ4Q.js","sourcesContent":["// Seal-key material for a lock-mode cache. The key is derived from a password\n// (the backup's Argon2id KDF) or supplied by the app (e.g. a passkey PRF key),\n// and is held in memory only - never written next to the data. That is what a\n// device-key seal (cache-crypto) cannot give: a copy of the whole browser\n// profile carries the device key, but not a secret kept in RAM. Only the KDF\n// salt is persisted, and a salt is not secret.\n\nimport { unseal, type EncEnvelope } from './cache-crypto';\nimport { deriveKey, freshKdfParams } from '../selfstore/crypto';\nimport type { KdfParams } from '../selfstore/types';\n\n/** Argon2id parameters for a password-locked cache; persisted in the clear. */\nexport type CacheKdf = KdfParams;\n\n/** Fresh Argon2id parameters (random salt, current default cost). */\nexport function freshCacheKdf(): CacheKdf {\n\treturn freshKdfParams();\n}\n\n/** Derive the AES-GCM seal key from a password and its stored parameters. */\nexport function keyFromPassword(password: string, kdf: CacheKdf): Promise<CryptoKey> {\n\treturn deriveKey(password, kdf);\n}\n\n/** True if `key` decrypts `probe`. A wrong key fails AES-GCM authentication;\n * that is a wrong-secret answer, never an error to surface. */\nexport async function opens(key: CryptoKey, probe: EncEnvelope): Promise<boolean> {\n\ttry {\n\t\tawait unseal(key, probe);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/selfstore/group.ts"],"names":[],"mappings":"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","file":"chunk-453KRCMM.js","sourcesContent":["// Passwordless groups: the public-key layer under peers (see PEERS.md).\n//\n// Each member holds an Ed25519 keypair to sign the copies they publish and an\n// X25519 keypair to receive data keys. A copy is encrypted once under a fresh\n// AES-256-GCM data key, enveloped per member (ephemeral ECDH + HKDF + GCM\n// wrap - the age recipient model). Membership is a manifest signed by a\n// single admin key; a copy is trusted only when its signature verifies and\n// its author is in the manifest.\n//\n// WebCrypto only, no polyfill: the supply-chain surface stays flat, and\n// evergreen browsers plus Node 20+ have everything needed.\n\nimport type { RecipientStanza } from './types';\nimport { toBase64, fromBase64 } from './base64';\nimport { SelfstoreError } from './errors';\n\n/** The `keying` header value for group-mode boxes (see SPEC.md section 12). */\nexport const GROUP_KEYING = 'x25519-hkdf-sha256';\n\n// Enforced on both seal and open, so a member cannot publish a copy under a\n// narrower key (16 bytes -> AES-128) and have readers accept it.\nconst DATA_KEY_BYTES = 32;\nconst CURVE_PUB_BYTES = 32;\n// Group mode targets a handful of members; the cap keeps a crafted copy from\n// demanding one asymmetric operation per stanza without bound.\nexport const MAX_RECIPIENTS = 256;\n\n// Domain separation: every signature and derivation binds its purpose, so\n// bytes signed in one role can never be replayed in another.\nconst DOMAIN_BOX = 'selfstore-group-sig-v1';\nconst DOMAIN_MANIFEST = 'selfstore-group-manifest-v1';\nconst HKDF_INFO = 'selfstore-group-wrap-v1';\n\nconst utf8 = new TextEncoder();\n\n/**\n * A member's full identity: private halves PKCS#8 DER, public halves raw 32\n * bytes, all base64. Treat the *Priv fields like a password - seal them at\n * rest (identityVault) and move them only over a trusted channel.\n */\nexport interface GroupIdentity {\n\tsigPub: string;\n\tsigPriv: string;\n\tencPub: string;\n\tencPriv: string;\n}\n\n/** One member as the manifest lists them: public keys only. */\nexport interface GroupMember {\n\tid: string;\n\tlabel?: string;\n\t/** Ed25519 public key (base64 raw): verifies the copies this member signs. */\n\tsig: string;\n\t/** X25519 public key (base64 raw): receives the data-key envelopes. */\n\tenc: string;\n}\n\n/**\n * The membership document, single-writer (the admin), carried as a\n * SignedManifest. `seq` grows on every change; consumers refuse a lower seq\n * than one already applied (the store persists the high-water mark).\n */\nexport interface GroupManifest {\n\tv: 1;\n\t/** Random group id (base64), so seq high-water marks never collide. */\n\tgroup: string;\n\tseq: number;\n\t/** The admin's Ed25519 public key. Members pin it at join time (TOFU). */\n\tadmin: string;\n\tmembers: GroupMember[];\n}\n\n/**\n * A manifest as it travels: the exact signed payload bytes plus the admin's\n * signature. Signing bytes rather than re-serialized JSON sidesteps\n * canonicalization entirely.\n */\nexport interface SignedManifest {\n\tselfstoreManifest: 1;\n\tpayload: string;\n\tsig: string;\n}\n\n/** Coerce to the ArrayBuffer-backed view WebCrypto's types want (safe here). */\nfunction src(u8: Uint8Array): Uint8Array<ArrayBuffer> {\n\treturn u8 as Uint8Array<ArrayBuffer>;\n}\n\nfunction concat(...parts: Uint8Array[]): Uint8Array {\n\tconst out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));\n\tlet o = 0;\n\tfor (const p of parts) {\n\t\tout.set(p, o);\n\t\to += p.length;\n\t}\n\treturn out;\n}\n\nasync function sha256(bytes: Uint8Array): Promise<Uint8Array> {\n\treturn new Uint8Array(await crypto.subtle.digest('SHA-256', src(bytes)));\n}\n\nlet curvesOk: Promise<boolean> | null = null;\n\n/** Whether WebCrypto implements Ed25519 + X25519 here. Password mode never needs this. */\nexport function groupCryptoAvailable(): Promise<boolean> {\n\tcurvesOk ??= (async () => {\n\t\ttry {\n\t\t\tawait crypto.subtle.generateKey('Ed25519', false, ['sign', 'verify']);\n\t\t\tawait crypto.subtle.generateKey('X25519', false, ['deriveBits']);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t})();\n\treturn curvesOk;\n}\n\nasync function requireCurves(): Promise<void> {\n\tif (!(await groupCryptoAvailable())) {\n\t\tthrow new TypeError(\n\t\t\t'Group mode needs WebCrypto Ed25519 and X25519 (evergreen browsers, Node 20+); ' +\n\t\t\t\t'this platform does not provide them. Use password mode instead, or upgrade the runtime.'\n\t\t);\n\t}\n}\n\nconst importSigPriv = (b64: string): Promise<CryptoKey> =>\n\tcrypto.subtle.importKey('pkcs8', src(fromBase64(b64)), 'Ed25519', false, ['sign']);\nconst importSigPub = (b64: string): Promise<CryptoKey> =>\n\tcrypto.subtle.importKey('raw', src(fromBase64(b64)), 'Ed25519', false, ['verify']);\nconst importEncPriv = (b64: string): Promise<CryptoKey> =>\n\tcrypto.subtle.importKey('pkcs8', src(fromBase64(b64)), 'X25519', false, ['deriveBits']);\nconst importEncPub = (b64: string): Promise<CryptoKey> =>\n\tcrypto.subtle.importKey('raw', src(fromBase64(b64)), 'X25519', false, []);\n\n/** Generate a fresh member identity (two keypairs). */\nexport async function generateIdentity(): Promise<GroupIdentity> {\n\tawait requireCurves();\n\tconst sig = (await crypto.subtle.generateKey('Ed25519', true, [\n\t\t'sign',\n\t\t'verify'\n\t])) as CryptoKeyPair;\n\tconst enc = (await crypto.subtle.generateKey('X25519', true, ['deriveBits'])) as CryptoKeyPair;\n\tconst raw = async (k: CryptoKey): Promise<string> =>\n\t\ttoBase64(new Uint8Array(await crypto.subtle.exportKey('raw', k)));\n\tconst pkcs8 = async (k: CryptoKey): Promise<string> =>\n\t\ttoBase64(new Uint8Array(await crypto.subtle.exportKey('pkcs8', k)));\n\treturn {\n\t\tsigPub: await raw(sig.publicKey),\n\t\tsigPriv: await pkcs8(sig.privateKey),\n\t\tencPub: await raw(enc.publicKey),\n\t\tencPriv: await pkcs8(enc.privateKey)\n\t};\n}\n\n/** The shareable half of an identity: what the admin puts in the manifest. */\nexport function publicIdentity(id: GroupIdentity): { sig: string; enc: string } {\n\treturn { sig: id.sigPub, enc: id.encPub };\n}\n\n/** A fresh random group id for a new manifest. */\nexport function newGroupId(): string {\n\treturn toBase64(crypto.getRandomValues(new Uint8Array(16)));\n}\n\n/** First 8 bytes of SHA-256(raw X25519 public), base64: finds a reader's stanza while naming nobody. */\nexport async function keyId(encPubB64: string): Promise<string> {\n\treturn toBase64((await sha256(fromBase64(encPubB64))).subarray(0, 8));\n}\n\n// Wrap key for one stanza: HKDF-SHA256 over the X25519 shared secret, salted\n// with ephemeralPub || recipientPub so the key binds to this exact pair.\nasync function stanzaWrapKey(\n\tpriv: CryptoKey,\n\totherPub: CryptoKey,\n\tephPubRaw: Uint8Array,\n\trecipientPubRaw: Uint8Array,\n\tusage: 'encrypt' | 'decrypt'\n): Promise<CryptoKey> {\n\tconst shared = new Uint8Array(\n\t\tawait crypto.subtle.deriveBits({ name: 'X25519', public: otherPub }, priv, 256)\n\t);\n\tconst ikm = await crypto.subtle.importKey('raw', src(shared), 'HKDF', false, ['deriveBits']);\n\tconst bits = new Uint8Array(\n\t\tawait crypto.subtle.deriveBits(\n\t\t\t{\n\t\t\t\tname: 'HKDF',\n\t\t\t\thash: 'SHA-256',\n\t\t\t\tsalt: src(concat(ephPubRaw, recipientPubRaw)),\n\t\t\t\tinfo: src(utf8.encode(HKDF_INFO))\n\t\t\t},\n\t\t\tikm,\n\t\t\t256\n\t\t)\n\t);\n\treturn crypto.subtle.importKey('raw', src(bits), 'AES-GCM', false, [usage]);\n}\n\n/** Envelope a data key: one ephemeral keypair and one stanza per recipient. */\nexport async function sealDataKey(\n\tdataKey: Uint8Array,\n\trecipientEncPubsB64: string[]\n): Promise<RecipientStanza[]> {\n\tawait requireCurves();\n\tif (dataKey.length !== DATA_KEY_BYTES) {\n\t\tthrow new TypeError(`sealDataKey: data key must be ${DATA_KEY_BYTES} bytes (AES-256).`);\n\t}\n\tif (recipientEncPubsB64.length > MAX_RECIPIENTS) {\n\t\tthrow new TypeError(`sealDataKey: too many recipients (max ${MAX_RECIPIENTS}).`);\n\t}\n\tconst out: RecipientStanza[] = [];\n\tfor (const r of recipientEncPubsB64) {\n\t\tconst eph = (await crypto.subtle.generateKey('X25519', false, ['deriveBits'])) as CryptoKeyPair;\n\t\tconst ephPubRaw = new Uint8Array(await crypto.subtle.exportKey('raw', eph.publicKey));\n\t\tconst key = await stanzaWrapKey(\n\t\t\teph.privateKey,\n\t\t\tawait importEncPub(r),\n\t\t\tephPubRaw,\n\t\t\tfromBase64(r),\n\t\t\t'encrypt'\n\t\t);\n\t\tconst iv = crypto.getRandomValues(new Uint8Array(12));\n\t\tconst wrap = new Uint8Array(\n\t\t\tawait crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, src(dataKey))\n\t\t);\n\t\tout.push({\n\t\t\tkid: await keyId(r),\n\t\t\tepk: toBase64(ephPubRaw),\n\t\t\tiv: toBase64(iv),\n\t\t\twrap: toBase64(wrap)\n\t\t});\n\t}\n\treturn out;\n}\n\n/**\n * Open the stanza addressed to this identity and return the data key.\n * NOT_A_RECIPIENT when none opens (identity removed, or the copy predates\n * its joining).\n */\nexport async function openDataKey(\n\tstanzas: RecipientStanza[],\n\tidentity: Pick<GroupIdentity, 'encPub' | 'encPriv'>\n): Promise<Uint8Array> {\n\tawait requireCurves();\n\tif (stanzas.length > MAX_RECIPIENTS) {\n\t\tthrow new SelfstoreError('BAD_FORMAT', `Too many recipient stanzas (max ${MAX_RECIPIENTS}).`);\n\t}\n\tconst myKid = await keyId(identity.encPub);\n\tconst mine = stanzas.filter((s) => s.kid === myKid);\n\t// On a kid miss try them all; MAX_RECIPIENTS bounds that work.\n\tconst candidates = mine.length > 0 ? mine : stanzas;\n\tconst priv = await importEncPriv(identity.encPriv);\n\tconst myPubRaw = fromBase64(identity.encPub);\n\tfor (const s of candidates) {\n\t\ttry {\n\t\t\tconst key = await stanzaWrapKey(\n\t\t\t\tpriv,\n\t\t\t\tawait importEncPub(s.epk),\n\t\t\t\tfromBase64(s.epk),\n\t\t\t\tmyPubRaw,\n\t\t\t\t'decrypt'\n\t\t\t);\n\t\t\tconst pt = new Uint8Array(\n\t\t\t\tawait crypto.subtle.decrypt(\n\t\t\t\t\t{ name: 'AES-GCM', iv: src(fromBase64(s.iv)) },\n\t\t\t\t\tkey,\n\t\t\t\t\tsrc(fromBase64(s.wrap))\n\t\t\t\t)\n\t\t\t);\n\t\t\t// Any other length is a key-downgrade attempt, not a valid envelope.\n\t\t\tif (pt.length === DATA_KEY_BYTES) return pt;\n\t\t} catch {\n\t\t\t/* not our stanza (or tampered): try the next candidate */\n\t\t}\n\t}\n\tthrow new SelfstoreError(\n\t\t'NOT_A_RECIPIENT',\n\t\t'No envelope opens for this identity: it is not (or no longer) a recipient of this copy.'\n\t);\n}\n\n/** AES-256-GCM under a raw 32-byte key (the group data key). */\nexport async function gcmSealRaw(\n\tkey32: Uint8Array,\n\tplaintext: Uint8Array\n): Promise<{ iv: string; ciphertext: Uint8Array }> {\n\tconst key = await crypto.subtle.importKey('raw', src(key32), 'AES-GCM', false, ['encrypt']);\n\tconst iv = crypto.getRandomValues(new Uint8Array(12));\n\tconst ct = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, src(plaintext));\n\treturn { iv: toBase64(iv), ciphertext: new Uint8Array(ct) };\n}\n\n/** Decrypt AES-256-GCM under a raw key; a tag failure is DECRYPT_FAILED. */\nexport async function gcmOpenRaw(\n\tkey32: Uint8Array,\n\tivB64: string,\n\tciphertext: Uint8Array\n): Promise<Uint8Array> {\n\ttry {\n\t\tconst key = await crypto.subtle.importKey('raw', src(key32), 'AES-GCM', false, ['decrypt']);\n\t\tconst pt = await crypto.subtle.decrypt(\n\t\t\t{ name: 'AES-GCM', iv: src(fromBase64(ivB64)) },\n\t\t\tkey,\n\t\t\tsrc(ciphertext)\n\t\t);\n\t\treturn new Uint8Array(pt);\n\t} catch {\n\t\tthrow new SelfstoreError('DECRYPT_FAILED', 'Corrupted or mismatched group ciphertext.');\n\t}\n}\n\n/**\n * Ed25519 over domain || SHA-256(meta.json) || SHA-256(data.enc): the two\n * digests cover everything a reader trusts (recipients, author claim, iv,\n * ciphertext). Returns the raw 64-byte sig.bin entry.\n */\nexport async function signBox(\n\tmetaBytes: Uint8Array,\n\tdataEnc: Uint8Array,\n\tsigPrivB64: string\n): Promise<Uint8Array> {\n\tawait requireCurves();\n\tconst msg = concat(utf8.encode(DOMAIN_BOX), await sha256(metaBytes), await sha256(dataEnc));\n\tconst key = await importSigPriv(sigPrivB64);\n\treturn new Uint8Array(await crypto.subtle.sign('Ed25519', key, src(msg)));\n}\n\n/** False on any failure (bad key, bad bytes); the caller maps it to SIGNATURE_INVALID. */\nexport async function verifyBox(\n\tmetaBytes: Uint8Array,\n\tdataEnc: Uint8Array,\n\tsig: Uint8Array,\n\tsigPubB64: string\n): Promise<boolean> {\n\ttry {\n\t\tawait requireCurves();\n\t\tconst msg = concat(utf8.encode(DOMAIN_BOX), await sha256(metaBytes), await sha256(dataEnc));\n\t\treturn await crypto.subtle.verify('Ed25519', await importSigPub(sigPubB64), src(sig), src(msg));\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/** Sign a manifest with the admin key, over the exact payload bytes. */\nexport async function signManifest(\n\tmanifest: GroupManifest,\n\tadminSigPrivB64: string\n): Promise<SignedManifest> {\n\tawait requireCurves();\n\tconst payload = utf8.encode(JSON.stringify(manifest));\n\tconst key = await importSigPriv(adminSigPrivB64);\n\tconst msg = concat(utf8.encode(DOMAIN_MANIFEST), payload);\n\tconst sig = new Uint8Array(await crypto.subtle.sign('Ed25519', key, src(msg)));\n\treturn { selfstoreManifest: 1, payload: toBase64(payload), sig: toBase64(sig) };\n}\n\n/**\n * Verify and parse a signed manifest against the pinned admin key (TOFU from\n * the invite). Rollback (seq) is enforced by the store, which persists the\n * high-water mark.\n */\nexport async function openManifest(\n\tsigned: SignedManifest,\n\tadminSigPubB64: string,\n\texpectedGroupB64?: string\n): Promise<GroupManifest> {\n\tawait requireCurves();\n\tconst bad = (why: string): SelfstoreError => new SelfstoreError('SIGNATURE_INVALID', why);\n\tif (!signed || signed.selfstoreManifest !== 1 || typeof signed.payload !== 'string') {\n\t\tthrow bad('Not a signed selfstore manifest.');\n\t}\n\tlet payload: Uint8Array;\n\tlet ok: boolean;\n\ttry {\n\t\tpayload = fromBase64(signed.payload);\n\t\tconst msg = concat(utf8.encode(DOMAIN_MANIFEST), payload);\n\t\tok = await crypto.subtle.verify(\n\t\t\t'Ed25519',\n\t\t\tawait importSigPub(adminSigPubB64),\n\t\t\tsrc(fromBase64(signed.sig ?? '')),\n\t\t\tsrc(msg)\n\t\t);\n\t} catch {\n\t\tthrow bad('Malformed manifest signature or keys.');\n\t}\n\tif (!ok) throw bad('Manifest signature invalid for the pinned admin key.');\n\tconst m = JSON.parse(new TextDecoder().decode(payload)) as GroupManifest;\n\t// Reject malformed member keys here, not later at import time.\n\tconst is32ByteKey = (b64: string): boolean => {\n\t\ttry {\n\t\t\treturn fromBase64(b64).length === CURVE_PUB_BYTES;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t};\n\tconst shapeOk =\n\t\tm &&\n\t\tm.v === 1 &&\n\t\ttypeof m.group === 'string' &&\n\t\tNumber.isInteger(m.seq) &&\n\t\tm.seq >= 0 &&\n\t\tm.admin === adminSigPubB64 &&\n\t\tArray.isArray(m.members) &&\n\t\tm.members.length <= MAX_RECIPIENTS &&\n\t\tm.members.every(\n\t\t\t(mem) =>\n\t\t\t\tmem &&\n\t\t\t\ttypeof mem.id === 'string' &&\n\t\t\t\tmem.id.length > 0 &&\n\t\t\t\tis32ByteKey(mem.sig) &&\n\t\t\t\tis32ByteKey(mem.enc)\n\t\t);\n\tif (!shapeOk) throw bad('Manifest shape invalid or admin key mismatch.');\n\t// A member id resolves an author to a person; duplicates make that ambiguous.\n\tif (new Set(m.members.map((mem) => mem.id)).size !== m.members.length) {\n\t\tthrow bad('Manifest has duplicate member ids.');\n\t}\n\t// An admin who reuses one key across groups must not get a manifest for\n\t// group Y accepted where X is meant.\n\tif (expectedGroupB64 !== undefined && m.group !== expectedGroupB64) {\n\t\tthrow bad('Manifest is for a different group than expected.');\n\t}\n\treturn m;\n}\n"]}