selfstore 1.8.32 → 1.8.33

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 { BackupsManager } from '../backups/manager';\nimport type { Snapshot, SnapshotFile } from '../selfstore/types';\nimport { SelfstoreError } from '../selfstore/errors';\nimport { sha256Hex } from '../selfstore/digest';\nimport { toBytes, type FileBytes } from './bytes';\nimport { type PasswordPolicy } from '../selfstore';\nimport { asBytes, readBoxWithSync } from '../selfstore/box';\nimport { RESERVED_COLLECTION_PREFIX } from '../selfstore/types';\nimport { saveToDisk } from '../selfstore/targets/local';\nimport {\n\taccount as driveAccount,\n\tconnect as driveConnect,\n\tfromSession as driveFromSession,\n\ttype DriveAuth\n} from '../persistence/targets/drive';\nimport { gisDriveAuth } from '../persistence/targets/drive-auth-gis';\nimport {\n\tconnect as fileConnect,\n\tfromSession as fileFromSession,\n\tisSupported as fileIsSupported\n} from '../persistence/targets/file';\n// WebDAV and S3 arrive on the gesture that uses them - see targets/servers.ts.\nimport { webdavConnect, webdavFromSession, s3Connect } from '../persistence/targets/servers';\nimport type { WebdavConfig } from '../persistence/targets/webdav';\nimport type { S3Config } from '../persistence/targets/s3';\n\n/** A record as the simple store sees it: plain JSON with a string id (the id\n * field name can be remapped per collection via `sync.ids`). */\nexport type SimpleRecord = Record<string, unknown>;\n\nexport type { FileBytes } from './bytes';\n\n/** A file on its way into the store. Omit `id` to get the content hash, which\n * is the id that makes the multi-device union safe - see `putFile`. */\nexport interface PutFileInput {\n\t/** Defaults to the SHA-256 of the bytes. */\n\tid?: string;\n\t/** Defaults to the id. Cosmetic: nothing keys on it. */\n\tname?: string;\n\t/** Defaults to 'application/octet-stream'. */\n\tmime?: string;\n\tbytes: FileBytes;\n}\n\n/** Unlock callback for the ultra-sensitive cacheLock mode: return the session\n * secret - a password (Argon2id-derived) or an app-provided key (e.g. a passkey\n * PRF result). Called at boot; called again with { failed: true } after a wrong\n * secret so the app can re-prompt. Throw to abort the boot (user cancelled). */\nexport type CacheUnlock = (attempt: { failed: boolean }) => Promise<string | CryptoKey>;\n\n/** Options - every one of them optional, the defaults carry a real app. */\nexport interface SimpleOptions {\n\t/** Your DATA schema version (default 1). Bump it together with `migrate`. */\n\tschema?: number;\n\t/** Upgrade a snapshot written by an older schema version. */\n\tmigrate?: (fromVersion: number, snap: Snapshot) => Snapshot;\n\t/** Per-collection merge tuning (id field mapping, strategies). The default -\n\t * records keyed by `id`, later edit wins, deletes propagate - fits most apps. */\n\tsync?: SyncConfig;\n\t/**\n\t * Google Drive, when this app uses it. Two shapes for two situations:\n\t *\n\t * `{ clientId }` is the whole wiring - the library builds the browser\n\t * connection, keeps ONE session for the app (several would mean several\n\t * consent screens), reuses its token across a reload, remembers which\n\t * account holds the backup and hands that back to Google so the chooser\n\t * stops appearing. Nothing else to write.\n\t *\n\t * A `DriveAuth` instead, for an app that gets its tokens elsewhere - a\n\t * server broker holding a refresh token, a native shell.\n\t *\n\t * Either way, providing it here is also what lets a connected Drive backup\n\t * restore itself on the next start.\n\t */\n\tdrive?: DriveAuth | { clientId: string; scope?: string };\n\t/** Where the working copy lives (default: IndexedDB named after the app;\n\t * in-memory when IndexedDB does not exist, e.g. tests or SSR). */\n\tcache?: LocalCache;\n\t/** Auto-save debounce in ms. */\n\tdebounceMs?: number;\n\t/** Cross-tab coordination (default on in browsers). */\n\tmultiTab?: boolean;\n\t/** Refuse to ever write or export a plaintext backup: connecting a\n\t * destination then demands a password (or a group). Off by default. */\n\trequireEncryption?: boolean;\n\t/** Reject a backup password weaker than this policy (length, character\n\t * classes) at protect()/addKey time. Preview the rules with\n\t * checkPasswordPolicy for a live UI hint. Omit for no policy. */\n\tpasswordPolicy?: PasswordPolicy;\n\t/** Ultra-sensitive mode: seal the local cache under a key derived from a\n\t * secret held in memory (this callback), never on disk - so a copy of the\n\t * browser profile cannot read it. One unlock per session; branch it on the\n\t * app's existing login to leave the UX unchanged. Browser (IndexedDB) only. */\n\tcacheLock?: CacheUnlock;\n\t/** Wire tab focus / network return / interval / tab hide automatically\n\t * (default true in browsers). Pass false to drive syncing yourself. */\n\tautoSync?: boolean;\n}\n\n/** What connecting a destination did. 'merged': it already held a backup and\n * both sides were folded together. 'started': it was empty, this device's data\n * is now its content. 'manual': non-Chromium file fallback - offer\n * downloadBackup() instead. 'cancelled': the user closed the picker/consent. */\nexport type ConnectOutcome = 'merged' | 'started' | 'manual' | 'cancelled';\n\nexport interface SimpleStore<\n\tS extends Record<string, SimpleRecord> = Record<string, SimpleRecord>\n> {\n\t// --- Your data ---------------------------------------------------------\n\t/** Every record of a collection (treat as read-only; write via put/remove). */\n\tall<K extends keyof S & string>(collection: K): readonly S[K][];\n\t/** One record by id, or undefined. */\n\tget<K extends keyof S & string>(collection: K, id: string): S[K] | undefined;\n\t/** Insert or replace one record (auto-saves, debounced). Throws a TypeError\n\t * when the record has no non-empty string id. */\n\tput<K extends keyof S & string>(collection: K, record: S[K]): Promise<void>;\n\t/** Insert or replace many records in one save. */\n\tputAll<K extends keyof S & string>(collection: K, records: S[K][]): Promise<void>;\n\t/** Delete one record by id (propagates to other devices). Unknown id: no-op. */\n\tremove<K extends keyof S & string>(collection: K, id: string): Promise<void>;\n\t/** Empty a collection (every removal propagates). */\n\tclear<K extends keyof S & string>(collection: K): Promise<void>;\n\t/** Called after any data change: your writes, another tab, another device,\n\t * a restore. Returns an unsubscribe. */\n\tonChange(fn: () => void): () => void;\n\n\t// --- Your files (bytes that ride along in the same backup) --------------\n\t/** Every file (treat as read-only; write via putFile/removeFile). */\n\tallFiles(): readonly SnapshotFile[];\n\t/** One file by id, or undefined. */\n\tgetFile(id: string): SnapshotFile | undefined;\n\t/**\n\t * Store bytes alongside the collections and return the file's id.\n\t *\n\t * Files are **immutable per id**. The multi-device merge unions them by id\n\t * and has no clock to order two bodies, so when two devices hold DIFFERENT\n\t * bytes under one id, one is kept and the other is dropped - with no\n\t * conflict reported, because at that level there is nothing to compare.\n\t *\n\t * Hence the default id: the SHA-256 of the bytes. Different bytes land on\n\t * different ids and the union keeps both, which is what makes a CRDT\n\t * document (Yjs, Automerge) safe to carry here - store its UPDATES, let the\n\t * union collect them, fold them at load. Tie a file's lifetime to a record\n\t * so deleting the record tells you which files to remove.\n\t *\n\t * Naming the id yourself puts that rule in your hands: identical bytes\n\t * under an existing id are a no-op, different bytes are refused with a\n\t * TypeError. `{ replace: true }` does it anyway - right for a body only\n\t * ever written on one device (a thumbnail, a rendered export), a silent\n\t * loser as soon as two devices write it.\n\t */\n\tputFile(file: PutFileInput, opts?: { replace?: boolean }): Promise<string>;\n\t/** Delete one file by id (propagates to other devices). Unknown id: no-op. */\n\tremoveFile(id: string): Promise<void>;\n\n\t// --- Where it lives ----------------------------------------------------\n\t/** Connect Google Drive as the durable home. An existing backup there is\n\t * MERGED with this device (multi-device); an encrypted one needs its\n\t * password up front (PASSWORD_REQUIRED is thrown before anything changes). */\n\tconnectDrive(auth: DriveAuth, opts?: { password?: string }): Promise<ConnectOutcome>;\n\t/** Connect a disk file (File System Access; falls back to manual download). */\n\tconnectFile(opts?: { password?: string }): Promise<ConnectOutcome>;\n\t/** Connect a WebDAV server (Nextcloud, ownCloud, your own). */\n\tconnectWebdav(config: WebdavConfig, opts?: { password?: string }): Promise<ConnectOutcome>;\n\t/** Connect an S3-compatible bucket you control (Amazon S3, R2, B2, MinIO). */\n\tconnectS3(config: S3Config, opts?: { password?: string }): Promise<ConnectOutcome>;\n\t/** Connect any custom BackupTarget with the same merge semantics. */\n\tconnectTarget(target: BackupTarget, opts?: { password?: string }): Promise<ConnectOutcome>;\n\t/** Back to device-only (the destination keeps its last backup). */\n\tdisconnect(): Promise<void>;\n\n\t// --- Backup copy (replica) ---------------------------------------------\n\t/** Also write the same encrypted backup to this second destination on\n\t * every save. A broken copy never gates the store (state.replicas). */\n\taddReplica(target: BackupTarget, opts?: { id?: string }): string;\n\t/** Stop writing the copy (the destination keeps its last backup). */\n\tremoveReplica(id: string): void;\n\n\t// --- Protection & the two gate gestures --------------------------------\n\t/** Encrypt the durable backup end to end with this password (reversible). */\n\tprotect(password: string): Promise<void>;\n\t/** Remove the backup password. */\n\tunprotect(): Promise<void>;\n\t/** status.action === 'unlock': supply the password to resume. */\n\tunlock(password: string): Promise<boolean>;\n\t/** status.action === 'reconnect': re-run the destination's auth gesture. */\n\treconnect(): Promise<boolean>;\n\n\t// --- Portable backups ---------------------------------------------------\n\t/** The portable backup file (a real ZIP; encrypted when protect() is on).\n\t *\n\t * `{ plaintext: true }` asks for a READABLE copy of a protected store: the\n\t * same ZIP with `selfstore.json` in the clear, which any tool can open and\n\t * git can diff. It is the answer to \"can I actually get my data out\", and\n\t * it costs nothing: the store, its password and its destination are\n\t * untouched, so the real backup stays encrypted. Before this, the only way\n\t * out was `unprotect()`, which rewrites the destination and leaves it\n\t * readable until you remember to undo it.\n\t *\n\t * Refused while locked (there is nothing readable to write), and refused on\n\t * a store created with `requireEncryption` - that flag means this store\n\t * never produces cleartext, and an escape hatch would make it a lie. */\n\texportBackup(opts?: { plaintext?: boolean }): Promise<Blob>;\n\t/** Download the backup (also resolves the 'manual' file mode's pending flag).\n\t * False when the user closed the save dialog: nothing was written, the\n\t * pending flag stands, and the app must not record a backup. */\n\tdownloadBackup(filename?: string): Promise<boolean>;\n\t/** Load a backup file into this store, replacing the local data (removals\n\t * propagate like edits). Throws PASSWORD_REQUIRED / DECRYPT_FAILED. */\n\timportBackup(file: Blob | Uint8Array, opts?: { password?: string }): Promise<void>;\n\n\t// --- Observability ------------------------------------------------------\n\t/** Headless status: { state, severity, action, labelKey } - map labelKey to\n\t * your own copy. */\n\treadonly status: StatusDescriptor;\n\t/** The last problem: { code, labelKey, message } - show labelKey, log message. */\n\treadonly error: StoreError | null;\n\t/** The full underlying state (advanced reads: journal, peers, mode...). */\n\treadonly state: LocalStoreState;\n\t/** Fires on any state change (status flips included) - for framework bindings. */\n\tsubscribe(fn: () => void): () => void;\n\n\t// --- Lifecycle ----------------------------------------------------------\n\t/** Save now (called for you on tab hide when autoSync is on). */\n\tflush(): Promise<void>;\n\t/** Converge with the destination now (a user gesture like pull-to-refresh). */\n\tsync(): Promise<void>;\n\t/** Drop timers and listeners (tests, SPA teardown). */\n\tdispose(): void;\n\n\t/** The escape hatch: the full advanced store this simple one is built on -\n\t * peers, groups, custom strategies, everything (see selfstore/advanced). */\n\treadonly advanced: LocalStore;\n\n\t/** The attachment point for 'selfstore/flows': the engine plus the cache KV\n\t * and backup file name a flow needs to build destination targets. Apps on\n\t * the advanced store hand a flow the same three by themselves. */\n\treadonly flowHost: { engine: LocalStore; kv: KV; backupName: string };\n\n\t/** Who holds the destination, when it is known - the Google address behind a\n\t * Drive backup. Learned on connect and remembered across sessions, so a\n\t * surface can NAME the destination (\"Google Drive\" is not an address) and a\n\t * re-grant can skip the account chooser. Null for a file, or before a first\n\t * connect. Reading it costs nothing: no call goes out. */\n\treadonly account: string | null;\n\n\t/** The destination this device already used, ready to hand to a connect flow\n\t * as `options.resume` - so a returning visitor is offered \"reopen my backup\"\n\t * ahead of the plain destinations, instead of re-deriving a decision the\n\t * library already has on file. Null when there is nothing to resume. */\n\tresumeOffer(): ResumeOffer | null;\n\n\t/**\n\t * The several-backups manager for this store's destination, or null when\n\t * there is nothing to manage several of (no Drive configured).\n\t *\n\t * The port under it - list, open by id, create, rename, delete - was the last\n\t * thing an app still assembled from the session the store had built, and the\n\t * one place it could pick the wrong file id. Built once and remembered; the\n\t * backups module is imported on demand, so an app that never asks does not\n\t * carry it.\n\t */\n\tbackups(): Promise<BackupsManager | null>;\n\n\t/**\n\t * The destinations this store can offer, ready for a connect flow or widget:\n\t * Drive first when the app configured it, a file always.\n\t *\n\t * Consumers were assembling this by hand from the very session the store\n\t * built - and the order and the \"no client id means no Drive card\" rule were\n\t * rediscovered each time. An app wanting a different offer (a WebDAV server,\n\t * its own target) still writes its own object; this is the default, not a\n\t * ceiling.\n\t */\n\tdestinations(): ConnectTargets;\n}\n\n/** The id field a collection's records are keyed by: `sync.ids` may remap it;\n * a DOTTED path (nested id) is an advanced setup the simple check skips. */\nfunction idFieldOf(sync: SyncConfig | undefined, collection: string): string | null {\n\tconst mapped = sync?.ids?.[collection];\n\tif (mapped == null) return 'id';\n\treturn mapped.includes('.') ? null : mapped;\n}\n\nfunction requireStringId(record: SimpleRecord, field: string | null, collection: string): string {\n\tif (field === null) return ''; // nested-path ids: validated by the engine's own rules\n\tconst id = record[field];\n\tif (typeof id !== 'string' || id.length === 0) {\n\t\tthrow new TypeError(\n\t\t\t`selfstore: a record in \"${collection}\" needs a non-empty STRING \"${field}\" ` +\n\t\t\t\t`(got ${JSON.stringify(id)}). String ids are what the multi-device merge keys on; ` +\n\t\t\t\t`map another field with { sync: { ids: { ${collection}: 'yourField' } } }.`\n\t\t);\n\t}\n\treturn id;\n}\n\nfunction sameBytes(a: Uint8Array, b: Uint8Array): boolean {\n\treturn a.length === b.length && a.every((v, i) => v === b[i]);\n}\n\n/** True when a browser IndexedDB is available (SSR and plain Node lack it). */\nfunction hasIndexedDb(): boolean {\n\treturn typeof indexedDB !== 'undefined';\n}\n\n/** Told apart by shape: a DriveAuth mints tokens, a `{ clientId }` asks us to. */\nfunction isDriveAuth(v: SimpleOptions['drive']): v is DriveAuth {\n\treturn !!v && typeof (v as DriveAuth).token === 'function';\n}\n\n/** Where the connected Drive address is kept, per app: it is what stops Google\n * asking WHICH account at every re-grant. An address is not a secret, and a\n * browser that refuses storage simply gets the chooser back. */\nconst driveAccountKey = (app: string): string => `selfstore.drive.account.${app}`;\n\nfunction localStore<T>(job: (store: Storage) => T): T | undefined {\n\ttry {\n\t\tconst store = globalThis.localStorage;\n\t\treturn store ? job(store) : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction rememberedDriveAccount(app: string): string | undefined {\n\treturn localStore((s) => s.getItem(driveAccountKey(app))) ?? undefined;\n}\n\n/** Learn the account behind a live Drive session, best-effort and once: it feeds\n * the next re-grant's hint, and any surface that wants to name the destination. */\nasync function learnDriveAccount(app: string, auth: DriveAuth): Promise<void> {\n\ttry {\n\t\tconst who = await driveAccount({ auth });\n\t\tif (who.email) localStore((s) => s.setItem(driveAccountKey(app), who.email!));\n\t} catch {\n\t\t// A destination that will not say costs the hint, never the connection.\n\t}\n}\n\n/**\n * The same connection, which learns whose it is the first time it hands out a\n * token.\n *\n * Learning it took an explicit connect, so anyone whose backup was attached\n * before this existed - or attached in another session - kept a destination\n * with no name on it: the panel said \"Google Drive\" where it could have said\n * which one, and the offer to reopen that backup had no address to show, which\n * is most of what makes it recognisable.\n *\n * It cannot simply be asked for at start-up: reading the account needs a token,\n * and asking for a token with no user gesture behind it opens a popup the\n * browser blocks. So it rides along instead - every token this app obtains,\n * whatever asked for it, teaches it the address at no cost. Once known, nothing\n * is called again.\n */\nfunction selfNaming(app: string, auth: DriveAuth): DriveAuth {\n\tlet learning: Promise<void> | null = null;\n\tconst learn = (): void => {\n\t\tif (learning || rememberedDriveAccount(app)) return;\n\t\tlearning = learnDriveAccount(app, auth);\n\t};\n\treturn {\n\t\tasync token(opts) {\n\t\t\tconst value = await auth.token(opts);\n\t\t\tlearn();\n\t\t\treturn value;\n\t\t},\n\t\tasync reconnect() {\n\t\t\tconst ok = await auth.reconnect();\n\t\t\tif (ok) learn();\n\t\t\treturn ok;\n\t\t},\n\t\tforget: () => auth.forget()\n\t};\n}\n\n/** The schema version a backup's sidecar names: 0 for a copy that carries\n * none (written by another tool), as a remote converge reads it. */\nfunction schemaVersionOf(sidecar: unknown): number {\n\tif (typeof sidecar !== 'object' || sidecar === null) return 0;\n\tconst v = (sidecar as { schemaVersion?: unknown }).schemaVersion;\n\treturn Number.isInteger(v) && (v as number) >= 0 ? (v as number) : 0;\n}\n\n/** The app's own collections, without the library's `__*` bookkeeping. */\nfunction withoutReserved(collections: Snapshot['collections']): Snapshot['collections'] {\n\treturn Object.fromEntries(\n\t\tObject.entries(collections).filter(([name]) => !name.startsWith(RESERVED_COLLECTION_PREFIX))\n\t);\n}\n\n/**\n * Open (or create) the app's local store. Awaits the initial load, so the\n * returned store is ready: data readable, destination restored, first converge\n * done. See SimpleOptions for the defaults.\n */\nexport async function selfstore<\n\tS extends Record<string, SimpleRecord> = Record<string, SimpleRecord>\n>(app: string, options: SimpleOptions = {}): Promise<SimpleStore<S>> {\n\tif (typeof app !== 'string' || app.length === 0) {\n\t\tthrow new TypeError('selfstore: pass your app name, e.g. selfstore(\"my-app\").');\n\t}\n\n\t// The one copy of the data. The engine pulls it on save (gather) and pushes\n\t// into it on restores/folds (apply); the facade's mutators edit it in place\n\t// copy-on-write and schedule a save.\n\tlet collections: Record<string, SimpleRecord[]> = {};\n\t// Binary files: written through putFile/removeFile, and carried untouched\n\t// through every save, backup and merge whoever wrote them.\n\tlet files: SnapshotFile[] = [];\n\n\tconst dataSubs = new Set<() => void>();\n\tconst emitData = (): void => dataSubs.forEach((fn) => fn());\n\n\tconst cache =\n\t\toptions.cache ??\n\t\t(hasIndexedDb()\n\t\t\t? indexedDbCache(app, options.cacheLock ? { lock: true } : undefined)\n\t\t\t: memoryCache());\n\tconst backupName = `${app}.zip`;\n\t// connectDrive() remembers the auth so a later restoreTarget can use it even\n\t// when options.drive was not provided up front (same session only).\n\t//\n\t// `{ clientId }` is the whole wiring done here rather than in every app: ONE\n\t// session (several would mean several consent screens), a token that survives\n\t// a reload, and the account remembered so Google stops asking which one. Each\n\t// of those was a separate decision every consumer had to discover; the app\n\t// that wrote them by hand hit all three as bugs first.\n\t/** Built at most once: two managers over one destination would each hold\n\t * their own idea of which file is active. */\n\tlet backupsManager: BackupsManager | null = null;\n\t// Wrapped whichever shape it came in as: an app that mints its own tokens\n\t// (a server broker, a native shell) has the same nameless-destination\n\t// problem, and the wrapper only ever rides along on a token already granted.\n\tlet driveAuth: DriveAuth | null = isDriveAuth(options.drive)\n\t\t? selfNaming(app, options.drive)\n\t\t: options.drive\n\t\t\t? selfNaming(\n\t\t\t\t\tapp,\n\t\t\t\t\tgisDriveAuth({\n\t\t\t\t\t\tclientId: options.drive.clientId,\n\t\t\t\t\t\tscope: options.drive.scope,\n\t\t\t\t\t\tpersist: 'session',\n\t\t\t\t\t\thint: () => rememberedDriveAccount(app)\n\t\t\t\t\t})\n\t\t\t\t)\n\t\t\t: null;\n\n\t// Ultra-sensitive: unlock the cache before the store hydrates from it. One\n\t// prompt per session, driven by the app (branch it on an existing login to\n\t// keep the UX unchanged). A wrong secret re-prompts; throw to abort.\n\tif (options.cacheLock && isLockableCache(cache)) {\n\t\tlet failed = false;\n\t\twhile (!(await cache.unlock(await options.cacheLock({ failed })))) failed = true;\n\t}\n\n\tconst schema = options.schema ?? 1;\n\tconst store = createLocalStore({\n\t\tapp,\n\t\tschemaVersion: schema,\n\t\tgather: (): Snapshot => structuredClone({ collections, files }),\n\t\tapply: (snap: Snapshot): void => {\n\t\t\tcollections = (snap.collections ?? {}) as Record<string, SimpleRecord[]>;\n\t\t\tfiles = snap.files ?? [];\n\t\t\temitData();\n\t\t},\n\t\tmigrate: options.migrate,\n\t\tsync: options.sync,\n\t\tcache,\n\t\tdebounceMs: options.debounceMs,\n\t\tmultiTab: options.multiTab,\n\t\trequireEncryption: options.requireEncryption,\n\t\tpasswordPolicy: options.passwordPolicy,\n\t\trestoreTarget: async (kind) => {\n\t\t\tif (kind === 'drive' && driveAuth) {\n\t\t\t\treturn driveFromSession({ auth: driveAuth, kv: cache.kv, fileName: backupName });\n\t\t\t}\n\t\t\tif (kind === 'file') return fileFromSession({ kv: cache.kv });\n\t\t\tif (kind === 'webdav') return webdavFromSession({ kv: cache.kv });\n\t\t\treturn null;\n\t\t}\n\t});\n\n\tawait store.init();\n\n\t// A degraded file mode is a verdict about the BROWSER, but it was persisted\n\t// like a property of the data: every later session re-read the word without\n\t// ever asking again. One session that could not open a picker - a browser\n\t// that ships the API and refuses it, a call made a moment too late, a user on\n\t// another browser that day - then pinned download-only mode forever, on a\n\t// Chromium perfectly able to hold a file, with no way back.\n\t//\n\t// Nothing writes this word except a picker that could not deliver, so a\n\t// browser that CAN deliver makes it stale. Re-examined here, in the layer\n\t// that wrote it. Dropping to 'device' is the honest state: manual mode had\n\t// already released the durable target, so there is nothing to detach - only\n\t// a stale claim to stop repeating, and a destination the host can offer again.\n\tif (store.state.targetKind === 'file-manual' && fileIsSupported()) {\n\t\tawait store.detachTarget();\n\t}\n\n\t// Auto-wired sync moments (Spring-Boot-style autoconfiguration): tab focus,\n\t// network return, a slow interval, and save-on-hide. Opt out with\n\t// { autoSync: false } to drive these yourself.\n\tconst teardown: (() => void)[] = [];\n\tif (\n\t\toptions.autoSync !== false &&\n\t\ttypeof document !== 'undefined' &&\n\t\ttypeof window !== 'undefined'\n\t) {\n\t\tconst onVisibility = (): void => {\n\t\t\tif (document.visibilityState === 'hidden') void store.flush();\n\t\t\telse void store.syncIfStale('focus');\n\t\t};\n\t\tconst onOnline = (): void => void store.syncIfStale('online');\n\t\tconst onPageHide = (): void => void store.flush();\n\t\tconst interval = setInterval(() => {\n\t\t\tif (document.visibilityState === 'visible') void store.syncIfStale('interval');\n\t\t}, 5 * 60_000);\n\t\tdocument.addEventListener('visibilitychange', onVisibility);\n\t\twindow.addEventListener('online', onOnline);\n\t\twindow.addEventListener('pagehide', onPageHide);\n\t\tteardown.push(() => {\n\t\t\tclearInterval(interval);\n\t\t\tdocument.removeEventListener('visibilitychange', onVisibility);\n\t\t\twindow.removeEventListener('online', onOnline);\n\t\t\twindow.removeEventListener('pagehide', onPageHide);\n\t\t});\n\t}\n\n\tfunction mutate(collection: string, next: SimpleRecord[]): Promise<void> {\n\t\tcollections = { ...collections, [collection]: next };\n\t\temitData();\n\t\tstore.schedule();\n\t\treturn Promise.resolve();\n\t}\n\n\tfunction mutateFiles(next: SnapshotFile[]): Promise<void> {\n\t\tfiles = next;\n\t\temitData();\n\t\tstore.schedule();\n\t\treturn Promise.resolve();\n\t}\n\n\tasync function connectTarget(\n\t\ttarget: BackupTarget,\n\t\topts: { password?: string } = {}\n\t): Promise<ConnectOutcome> {\n\t\tconst info = await store.inspectTarget(target);\n\t\tif (info.hasBackup) {\n\t\t\t// Another app's backup is refused BEFORE the password question: asking\n\t\t\t// first teaches the user their password \"does not work\" on a file that\n\t\t\t// was never this app's to open.\n\t\t\tif (info.app !== null && info.app !== store.app) {\n\t\t\t\tthrow new SelfstoreError(\n\t\t\t\t\t'FOREIGN_BACKUP',\n\t\t\t\t\t`This destination holds a backup written by app \"${info.app}\", not \"${store.app}\". Open it in its own application, or pick another file.`\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Fail before attaching: an encrypted backup without its password would\n\t\t\t// otherwise attach locked, which is a support case, not an outcome.\n\t\t\tif (info.encrypted && !opts.password) {\n\t\t\t\tthrow new SelfstoreError(\n\t\t\t\t\t'PASSWORD_REQUIRED',\n\t\t\t\t\t'This destination holds an encrypted backup: pass its password to connect.'\n\t\t\t\t);\n\t\t\t}\n\t\t\tawait store.attachTarget(target, { password: opts.password ?? null, strategy: 'merge' });\n\t\t\treturn 'merged';\n\t\t}\n\t\tawait store.attachTarget(target, {\n\t\t\tpassword: opts.password ?? null,\n\t\t\tstrategy: 'replace-remote'\n\t\t});\n\t\treturn 'started';\n\t}\n\n\tconst simple: SimpleStore<S> = {\n\t\tall<K extends keyof S & string>(collection: K): readonly S[K][] {\n\t\t\treturn (collections[collection] ?? []) as S[K][];\n\t\t},\n\t\tget<K extends keyof S & string>(collection: K, id: string): S[K] | undefined {\n\t\t\tconst field = idFieldOf(options.sync, collection) ?? 'id';\n\t\t\treturn (collections[collection] ?? []).find((r) => r[field] === id) as S[K] | undefined;\n\t\t},\n\t\tput<K extends keyof S & string>(collection: K, record: S[K]): Promise<void> {\n\t\t\tconst field = idFieldOf(options.sync, collection);\n\t\t\tconst id = requireStringId(record, field, collection);\n\t\t\tconst rows = collections[collection] ?? [];\n\t\t\tconst at = field === null ? -1 : rows.findIndex((r) => r[field] === id);\n\t\t\treturn mutate(\n\t\t\t\tcollection,\n\t\t\t\tat === -1 ? [...rows, record] : rows.map((r, i) => (i === at ? record : r))\n\t\t\t);\n\t\t},\n\t\tputAll<K extends keyof S & string>(collection: K, records: S[K][]): Promise<void> {\n\t\t\tconst field = idFieldOf(options.sync, collection);\n\t\t\tconst byId = new Map<unknown, number>();\n\t\t\tfor (const r of records) requireStringId(r, field, collection);\n\t\t\tconst rows = [...(collections[collection] ?? [])];\n\t\t\tif (field !== null) {\n\t\t\t\trows.forEach((r, i) => byId.set(r[field], i));\n\t\t\t\tfor (const r of records) {\n\t\t\t\t\tconst at = byId.get(r[field]);\n\t\t\t\t\tif (typeof at === 'number') rows[at] = r;\n\t\t\t\t\telse rows.push(r);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\trows.push(...records);\n\t\t\t}\n\t\t\treturn mutate(collection, rows);\n\t\t},\n\t\tremove<K extends keyof S & string>(collection: K, id: string): Promise<void> {\n\t\t\tconst field = idFieldOf(options.sync, collection) ?? 'id';\n\t\t\tconst rows = collections[collection] ?? [];\n\t\t\tconst next = rows.filter((r) => r[field] !== id);\n\t\t\treturn next.length === rows.length ? Promise.resolve() : mutate(collection, next);\n\t\t},\n\t\tclear<K extends keyof S & string>(collection: K): Promise<void> {\n\t\t\tif ((collections[collection] ?? []).length === 0) return Promise.resolve();\n\t\t\treturn mutate(collection, []);\n\t\t},\n\t\tonChange(fn: () => void): () => void {\n\t\t\tdataSubs.add(fn);\n\t\t\treturn () => dataSubs.delete(fn);\n\t\t},\n\n\t\tallFiles(): readonly SnapshotFile[] {\n\t\t\treturn files;\n\t\t},\n\t\tgetFile(id: string): SnapshotFile | undefined {\n\t\t\treturn files.find((f) => f.id === id);\n\t\t},\n\t\tasync putFile(file: PutFileInput, opts: { replace?: boolean } = {}): Promise<string> {\n\t\t\tconst bytes = await toBytes(file.bytes);\n\t\t\tconst id = file.id ?? (await sha256Hex(bytes));\n\t\t\tif (typeof id !== 'string' || id.length === 0) {\n\t\t\t\tthrow new TypeError(\n\t\t\t\t\t`selfstore: a file needs a non-empty STRING id (got ${JSON.stringify(file.id)}), ` +\n\t\t\t\t\t\t`or omit it to get the content hash.`\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst at = files.findIndex((f) => f.id === id);\n\t\t\t// Same bytes under the same id is the normal case for a content id -\n\t\t\t// re-putting an unchanged body must not schedule a save.\n\t\t\tif (at !== -1 && !opts.replace) {\n\t\t\t\tif (sameBytes(files[at].bytes, bytes)) return id;\n\t\t\t\tthrow new TypeError(\n\t\t\t\t\t`selfstore: file \"${id}\" already holds different bytes. Files are immutable per id ` +\n\t\t\t\t\t\t`because the multi-device merge unions them by id and cannot order two bodies: ` +\n\t\t\t\t\t\t`the other device's copy would be dropped in silence. Omit the id to get a ` +\n\t\t\t\t\t\t`content-addressed one, or pass { replace: true } if this file is only ever ` +\n\t\t\t\t\t\t`written on one device.`\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst entry: SnapshotFile = {\n\t\t\t\tid,\n\t\t\t\tname: file.name ?? id,\n\t\t\t\tmime: file.mime ?? 'application/octet-stream',\n\t\t\t\tbytes\n\t\t\t};\n\t\t\tawait mutateFiles(\n\t\t\t\tat === -1 ? [...files, entry] : files.map((f, i) => (i === at ? entry : f))\n\t\t\t);\n\t\t\treturn id;\n\t\t},\n\t\tremoveFile(id: string): Promise<void> {\n\t\t\tconst next = files.filter((f) => f.id !== id);\n\t\t\treturn next.length === files.length ? Promise.resolve() : mutateFiles(next);\n\t\t},\n\n\t\tasync connectDrive(auth: DriveAuth, opts?: { password?: string }): Promise<ConnectOutcome> {\n\t\t\tdriveAuth = auth;\n\t\t\tconst target = await driveConnect({ auth, kv: cache.kv, fileName: backupName });\n\t\t\tif (!target) return 'cancelled';\n\t\t\tconst outcome = await connectTarget(target, opts);\n\t\t\t// Learn WHICH account now that there is a session: it is the hint that\n\t\t\t// keeps the chooser away next time, and the name a panel can show. Not\n\t\t\t// awaited into the outcome - a slow metadata call must not delay the\n\t\t\t// connect the user is waiting on.\n\t\t\tif (outcome !== 'cancelled') void learnDriveAccount(app, auth);\n\t\t\treturn outcome;\n\t\t},\n\t\tasync connectFile(opts?: { password?: string }): Promise<ConnectOutcome> {\n\t\t\t// Asked twice on purpose: the first call answers from what the browser\n\t\t\t// exposes, the second from what it did when actually asked. A browser\n\t\t\t// that ships the picker and then refuses it only tells us so here.\n\t\t\tif (fileIsSupported()) {\n\t\t\t\tconst target = await fileConnect({ kv: cache.kv, fileName: backupName });\n\t\t\t\tif (target) return connectTarget(target, opts);\n\t\t\t\tif (fileIsSupported()) return 'cancelled';\n\t\t\t}\n\t\t\tawait store.setManualFile();\n\t\t\treturn 'manual';\n\t\t},\n\t\tasync connectWebdav(\n\t\t\tconfig: WebdavConfig,\n\t\t\topts?: { password?: string }\n\t\t): Promise<ConnectOutcome> {\n\t\t\tconst target = await webdavConnect({ kv: cache.kv, config });\n\t\t\tif (!target) {\n\t\t\t\tthrow new SelfstoreError(\n\t\t\t\t\t'TARGET_UNAVAILABLE',\n\t\t\t\t\t'The WebDAV server did not answer (URL, credentials or CORS).'\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn connectTarget(target, opts);\n\t\t},\n\t\tasync connectS3(config: S3Config, opts?: { password?: string }): Promise<ConnectOutcome> {\n\t\t\tconst target = await s3Connect({ kv: cache.kv, config });\n\t\t\tif (!target) {\n\t\t\t\tthrow new SelfstoreError(\n\t\t\t\t\t'TARGET_UNAVAILABLE',\n\t\t\t\t\t'The S3 endpoint did not answer (endpoint, credentials or CORS).'\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn connectTarget(target, opts);\n\t\t},\n\t\tconnectTarget,\n\t\tdisconnect: () => store.detachTarget(),\n\t\taddReplica: (target, opts) => store.attachReplica(target, opts),\n\t\tremoveReplica: (id) => store.detachReplica(id),\n\n\t\tprotect: (password: string) => store.setEncryption(password),\n\t\tunprotect: () => store.setEncryption(null),\n\t\tunlock: (password: string) => store.unlock(password),\n\t\treconnect: () => store.reconnect(),\n\n\t\texportBackup: (opts?: { plaintext?: boolean }) => store.exportBlob(opts),\n\t\tasync downloadBackup(filename?: string): Promise<boolean> {\n\t\t\tconst written = await saveToDisk(await store.exportBlob(), filename ?? backupName);\n\t\t\t// Clearing the pending flag on a cancelled dialog would drop the very\n\t\t\t// nudge that asks for the file the user still does not have.\n\t\t\tif (written) store.markDownloaded();\n\t\t\treturn written;\n\t\t},\n\t\tasync importBackup(file: Blob | Uint8Array, opts?: { password?: string }): Promise<void> {\n\t\t\t// Judged by the schema that wrote it, as a remote copy is: a newer one\n\t\t\t// is refused, an older one is migrated BEFORE it replaces anything,\n\t\t\t// since the next save stamps the current version over it for good.\n\t\t\tconst { snapshot, sidecar } = await readBoxWithSync(await asBytes(file), opts?.password);\n\t\t\tconst fromVersion = schemaVersionOf(sidecar);\n\t\t\tif (fromVersion > schema) {\n\t\t\t\tthrow new SelfstoreError(\n\t\t\t\t\t'SCHEMA_TOO_NEW',\n\t\t\t\t\t`Backup written by schema v${fromVersion}; this app reads v${schema}. Update the app.`\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Reserved bookkeeping collections are stripped; removals then\n\t\t\t// propagate because the next save diffs against the sync meta.\n\t\t\tconst read = { collections: withoutReserved(snapshot.collections), files: snapshot.files };\n\t\t\tconst snap =\n\t\t\t\toptions.migrate && fromVersion < schema ? options.migrate(fromVersion, read) : read;\n\t\t\tcollections = (snap.collections ?? {}) as Record<string, SimpleRecord[]>;\n\t\t\tfiles = snap.files ?? [];\n\t\t\temitData();\n\t\t\tstore.schedule();\n\t\t},\n\n\t\tget status(): StatusDescriptor {\n\t\t\treturn store.state.status;\n\t\t},\n\t\tget error(): StoreError | null {\n\t\t\treturn store.state.lastError;\n\t\t},\n\t\tget state(): LocalStoreState {\n\t\t\treturn store.state;\n\t\t},\n\t\tsubscribe: (fn: () => void) => store.subscribe(fn),\n\n\t\tflush: () => store.flush(),\n\t\tasync sync(): Promise<void> {\n\t\t\tawait store.syncNow();\n\t\t},\n\t\tdispose(): void {\n\t\t\tteardown.forEach((fn) => fn());\n\t\t\tteardown.length = 0;\n\t\t\tstore.dispose();\n\t\t},\n\n\t\tadvanced: store,\n\n\t\tflowHost: { engine: store, kv: cache.kv, backupName },\n\n\t\tget account(): string | null {\n\t\t\treturn rememberedDriveAccount(app) ?? null;\n\t\t},\n\n\t\tasync backups(): Promise<BackupsManager | null> {\n\t\t\tif (!driveAuth) return null;\n\t\t\tbackupsManager ??= await (async () => {\n\t\t\t\tconst { createBackupsManager, driveBackupsHost } = await import('../backups/manager-drive');\n\t\t\t\treturn createBackupsManager({\n\t\t\t\t\tstore,\n\t\t\t\t\tkv: cache.kv,\n\t\t\t\t\thost: driveBackupsHost({ auth: driveAuth!, kv: cache.kv, fileName: backupName }),\n\t\t\t\t\tnaming: { canonicalName: backupName }\n\t\t\t\t});\n\t\t\t})();\n\t\t\treturn backupsManager;\n\t\t},\n\n\t\tdestinations(): ConnectTargets {\n\t\t\t// Drive first when there is one: it is the answer that follows a user\n\t\t\t// from device to device. A file always, for whoever wants no account.\n\t\t\treturn driveAuth ? { drive: driveAuth, file: true } : { file: true };\n\t\t},\n\n\t\tresumeOffer(): ResumeOffer | null {\n\t\t\tconst detail = rememberedDriveAccount(app);\n\t\t\tif (!driveAuth || !detail) return null;\n\t\t\tconst auth = driveAuth;\n\t\t\treturn {\n\t\t\t\tkind: 'drive',\n\t\t\t\tdetail,\n\t\t\t\t// Reopening is not searching: fromSession takes the file id already\n\t\t\t\t// remembered, where a fresh connect resolves by name and could land on\n\t\t\t\t// another file carrying the same one.\n\t\t\t\tconnect: () => driveFromSession({ auth, kv: cache.kv, fileName: backupName })\n\t\t\t};\n\t\t}\n\t};\n\n\treturn simple;\n}\n"]}
1
+ {"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 { idOf } from '../sync/merge';\nimport type { ConnectTargets, ResumeOffer } from '../flows/connect';\nimport type { BackupsManager } from '../backups/manager';\nimport type { Snapshot, SnapshotFile } from '../selfstore/types';\nimport { SelfstoreError } from '../selfstore/errors';\nimport { sha256Hex } from '../selfstore/digest';\nimport { toBytes, type FileBytes } from './bytes';\nimport { type PasswordPolicy } from '../selfstore';\nimport { asBytes, readBoxWithSync } from '../selfstore/box';\nimport { RESERVED_COLLECTION_PREFIX } from '../selfstore/types';\nimport { saveToDisk } from '../selfstore/targets/local';\nimport {\n\taccount as driveAccount,\n\tconnect as driveConnect,\n\tfromSession as driveFromSession,\n\ttype DriveAuth\n} from '../persistence/targets/drive';\nimport { gisDriveAuth } from '../persistence/targets/drive-auth-gis';\nimport {\n\tconnect as fileConnect,\n\tfromSession as fileFromSession,\n\tisSupported as fileIsSupported\n} from '../persistence/targets/file';\n// WebDAV and S3 arrive on the gesture that uses them - see targets/servers.ts.\nimport {\n\twebdavConnect,\n\twebdavFromSession,\n\ts3Connect,\n\ts3FromSession\n} from '../persistence/targets/servers';\nimport type { WebdavConfig } from '../persistence/targets/webdav';\nimport type { S3Config } from '../persistence/targets/s3';\n\n/** A record as the simple store sees it: plain JSON with a string id (the id\n * field name can be remapped per collection via `sync.ids`). */\nexport type SimpleRecord = Record<string, unknown>;\n\nexport type { FileBytes } from './bytes';\n\n/** A file on its way into the store. Omit `id` to get the content hash, which\n * is the id that makes the multi-device union safe - see `putFile`. */\nexport interface PutFileInput {\n\t/** Defaults to the SHA-256 of the bytes. */\n\tid?: string;\n\t/** Defaults to the id. Cosmetic: nothing keys on it. */\n\tname?: string;\n\t/** Defaults to 'application/octet-stream'. */\n\tmime?: string;\n\tbytes: FileBytes;\n}\n\n/** Unlock callback for the ultra-sensitive cacheLock mode: return the session\n * secret - a password (Argon2id-derived) or an app-provided key (e.g. a passkey\n * PRF result). Called at boot; called again with { failed: true } after a wrong\n * secret so the app can re-prompt. Throw to abort the boot (user cancelled). */\nexport type CacheUnlock = (attempt: { failed: boolean }) => Promise<string | CryptoKey>;\n\n/** Options - every one of them optional, the defaults carry a real app. */\nexport interface SimpleOptions {\n\t/** Your DATA schema version (default 1). Bump it together with `migrate`. */\n\tschema?: number;\n\t/** Upgrade a snapshot written by an older schema version. */\n\tmigrate?: (fromVersion: number, snap: Snapshot) => Snapshot;\n\t/** Per-collection merge tuning (id field mapping, strategies). The default -\n\t * records keyed by `id`, later edit wins, deletes propagate - fits most apps. */\n\tsync?: SyncConfig;\n\t/**\n\t * Google Drive, when this app uses it. Two shapes for two situations:\n\t *\n\t * `{ clientId }` is the whole wiring - the library builds the browser\n\t * connection, keeps ONE session for the app (several would mean several\n\t * consent screens), reuses its token across a reload, remembers which\n\t * account holds the backup and hands that back to Google so the chooser\n\t * stops appearing. Nothing else to write.\n\t *\n\t * A `DriveAuth` instead, for an app that gets its tokens elsewhere - a\n\t * server broker holding a refresh token, a native shell.\n\t *\n\t * Either way, providing it here is also what lets a connected Drive backup\n\t * restore itself on the next start.\n\t */\n\tdrive?: DriveAuth | { clientId: string; scope?: string };\n\t/** Where the working copy lives (default: IndexedDB named after the app;\n\t * in-memory when IndexedDB does not exist, e.g. tests or SSR). */\n\tcache?: LocalCache;\n\t/** Auto-save debounce in ms. */\n\tdebounceMs?: number;\n\t/** Cross-tab coordination (default on in browsers). */\n\tmultiTab?: boolean;\n\t/** Refuse to ever write or export a plaintext backup: connecting a\n\t * destination then demands a password (or a group). Off by default. */\n\trequireEncryption?: boolean;\n\t/** Reject a backup password weaker than this policy (length, character\n\t * classes) at protect()/addKey time. Preview the rules with\n\t * checkPasswordPolicy for a live UI hint. Omit for no policy. */\n\tpasswordPolicy?: PasswordPolicy;\n\t/** Ultra-sensitive mode: seal the local cache under a key derived from a\n\t * secret held in memory (this callback), never on disk - so a copy of the\n\t * browser profile cannot read it. One unlock per session; branch it on the\n\t * app's existing login to leave the UX unchanged. Browser (IndexedDB) only. */\n\tcacheLock?: CacheUnlock;\n\t/** Wire tab focus / network return / interval / tab hide automatically\n\t * (default true in browsers). Pass false to drive syncing yourself. */\n\tautoSync?: boolean;\n}\n\n/** What connecting a destination did. 'merged': it already held a backup and\n * both sides were folded together. 'started': it was empty, this device's data\n * is now its content. 'manual': non-Chromium file fallback - offer\n * downloadBackup() instead. 'cancelled': the user closed the picker/consent. */\nexport type ConnectOutcome = 'merged' | 'started' | 'manual' | 'cancelled';\n\nexport interface SimpleStore<\n\tS extends Record<string, SimpleRecord> = Record<string, SimpleRecord>\n> {\n\t// --- Your data ---------------------------------------------------------\n\t/** Every record of a collection (treat as read-only; write via put/remove). */\n\tall<K extends keyof S & string>(collection: K): readonly S[K][];\n\t/** One record by id, or undefined. */\n\tget<K extends keyof S & string>(collection: K, id: string): S[K] | undefined;\n\t/** Insert or replace one record (auto-saves, debounced). Throws a TypeError\n\t * when the record has no non-empty string id. */\n\tput<K extends keyof S & string>(collection: K, record: S[K]): Promise<void>;\n\t/** Insert or replace many records in one save. */\n\tputAll<K extends keyof S & string>(collection: K, records: S[K][]): Promise<void>;\n\t/** Delete one record by id (propagates to other devices). Unknown id: no-op. */\n\tremove<K extends keyof S & string>(collection: K, id: string): Promise<void>;\n\t/** Empty a collection (every removal propagates). */\n\tclear<K extends keyof S & string>(collection: K): Promise<void>;\n\t/** Called after any data change: your writes, another tab, another device,\n\t * a restore. Returns an unsubscribe. */\n\tonChange(fn: () => void): () => void;\n\n\t// --- Your files (bytes that ride along in the same backup) --------------\n\t/** Every file (treat as read-only; write via putFile/removeFile). */\n\tallFiles(): readonly SnapshotFile[];\n\t/** One file by id, or undefined. */\n\tgetFile(id: string): SnapshotFile | undefined;\n\t/**\n\t * Store bytes alongside the collections and return the file's id.\n\t *\n\t * Files are **immutable per id**. The multi-device merge unions them by id\n\t * and has no clock to order two bodies, so when two devices hold DIFFERENT\n\t * bytes under one id, one is kept and the other is dropped - with no\n\t * conflict reported, because at that level there is nothing to compare.\n\t *\n\t * Hence the default id: the SHA-256 of the bytes. Different bytes land on\n\t * different ids and the union keeps both, which is what makes a CRDT\n\t * document (Yjs, Automerge) safe to carry here - store its UPDATES, let the\n\t * union collect them, fold them at load. Tie a file's lifetime to a record\n\t * so deleting the record tells you which files to remove.\n\t *\n\t * Naming the id yourself puts that rule in your hands: identical bytes\n\t * under an existing id are a no-op, different bytes are refused with a\n\t * TypeError. `{ replace: true }` does it anyway - right for a body only\n\t * ever written on one device (a thumbnail, a rendered export), a silent\n\t * loser as soon as two devices write it.\n\t */\n\tputFile(file: PutFileInput, opts?: { replace?: boolean }): Promise<string>;\n\t/** Delete one file by id (propagates to other devices). Unknown id: no-op. */\n\tremoveFile(id: string): Promise<void>;\n\n\t// --- Where it lives ----------------------------------------------------\n\t/** Connect Google Drive as the durable home. An existing backup there is\n\t * MERGED with this device (multi-device); an encrypted one needs its\n\t * password up front (PASSWORD_REQUIRED is thrown before anything changes). */\n\tconnectDrive(auth: DriveAuth, opts?: { password?: string }): Promise<ConnectOutcome>;\n\t/** Connect a disk file (File System Access; falls back to manual download). */\n\tconnectFile(opts?: { password?: string }): Promise<ConnectOutcome>;\n\t/** Connect a WebDAV server (Nextcloud, ownCloud, your own). */\n\tconnectWebdav(config: WebdavConfig, opts?: { password?: string }): Promise<ConnectOutcome>;\n\t/** Connect an S3-compatible bucket you control (Amazon S3, R2, B2, MinIO). */\n\tconnectS3(config: S3Config, opts?: { password?: string }): Promise<ConnectOutcome>;\n\t/** Connect any custom BackupTarget with the same merge semantics. */\n\tconnectTarget(target: BackupTarget, opts?: { password?: string }): Promise<ConnectOutcome>;\n\t/** Back to device-only (the destination keeps its last backup). */\n\tdisconnect(): Promise<void>;\n\n\t// --- Backup copy (replica) ---------------------------------------------\n\t/** Also write the same encrypted backup to this second destination on\n\t * every save. A broken copy never gates the store (state.replicas). */\n\taddReplica(target: BackupTarget, opts?: { id?: string }): string;\n\t/** Stop writing the copy (the destination keeps its last backup). */\n\tremoveReplica(id: string): void;\n\n\t// --- Protection & the two gate gestures --------------------------------\n\t/** Encrypt the durable backup end to end with this password (reversible). */\n\tprotect(password: string): Promise<void>;\n\t/** Remove the backup password. */\n\tunprotect(): Promise<void>;\n\t/** status.action === 'unlock': supply the password to resume. */\n\tunlock(password: string): Promise<boolean>;\n\t/** status.action === 'reconnect': re-run the destination's auth gesture. */\n\treconnect(): Promise<boolean>;\n\n\t// --- Portable backups ---------------------------------------------------\n\t/** The portable backup file (a real ZIP; encrypted when protect() is on).\n\t *\n\t * `{ plaintext: true }` asks for a READABLE copy of a protected store: the\n\t * same ZIP with `selfstore.json` in the clear, which any tool can open and\n\t * git can diff. It is the answer to \"can I actually get my data out\", and\n\t * it costs nothing: the store, its password and its destination are\n\t * untouched, so the real backup stays encrypted. Before this, the only way\n\t * out was `unprotect()`, which rewrites the destination and leaves it\n\t * readable until you remember to undo it.\n\t *\n\t * Refused while locked (there is nothing readable to write), and refused on\n\t * a store created with `requireEncryption` - that flag means this store\n\t * never produces cleartext, and an escape hatch would make it a lie. */\n\texportBackup(opts?: { plaintext?: boolean }): Promise<Blob>;\n\t/** Download the backup (also resolves the 'manual' file mode's pending flag).\n\t * False when the user closed the save dialog: nothing was written, the\n\t * pending flag stands, and the app must not record a backup. */\n\tdownloadBackup(filename?: string): Promise<boolean>;\n\t/** Load a backup file into this store, replacing the local data (removals\n\t * propagate like edits). Throws PASSWORD_REQUIRED / DECRYPT_FAILED. */\n\timportBackup(file: Blob | Uint8Array, opts?: { password?: string }): Promise<void>;\n\n\t// --- Observability ------------------------------------------------------\n\t/** Headless status: { state, severity, action, labelKey } - map labelKey to\n\t * your own copy. */\n\treadonly status: StatusDescriptor;\n\t/** The last problem: { code, labelKey, message } - show labelKey, log message. */\n\treadonly error: StoreError | null;\n\t/** The full underlying state (advanced reads: journal, peers, mode...). */\n\treadonly state: LocalStoreState;\n\t/** Fires on any state change (status flips included) - for framework bindings. */\n\tsubscribe(fn: () => void): () => void;\n\n\t// --- Lifecycle ----------------------------------------------------------\n\t/** Save now (called for you on tab hide when autoSync is on). */\n\tflush(): Promise<void>;\n\t/** Converge with the destination now (a user gesture like pull-to-refresh). */\n\tsync(): Promise<void>;\n\t/** Drop timers and listeners (tests, SPA teardown). */\n\tdispose(): void;\n\n\t/** The escape hatch: the full advanced store this simple one is built on -\n\t * peers, groups, custom strategies, everything (see selfstore/advanced). */\n\treadonly advanced: LocalStore;\n\n\t/** The attachment point for 'selfstore/flows': the engine plus the cache KV\n\t * and backup file name a flow needs to build destination targets. Apps on\n\t * the advanced store hand a flow the same three by themselves. */\n\treadonly flowHost: { engine: LocalStore; kv: KV; backupName: string };\n\n\t/** Who holds the destination, when it is known - the Google address behind a\n\t * Drive backup. Learned on connect and remembered across sessions, so a\n\t * surface can NAME the destination (\"Google Drive\" is not an address) and a\n\t * re-grant can skip the account chooser. Null for a file, or before a first\n\t * connect. Reading it costs nothing: no call goes out. */\n\treadonly account: string | null;\n\n\t/** The destination this device already used, ready to hand to a connect flow\n\t * as `options.resume` - so a returning visitor is offered \"reopen my backup\"\n\t * ahead of the plain destinations, instead of re-deriving a decision the\n\t * library already has on file. Null when there is nothing to resume. */\n\tresumeOffer(): ResumeOffer | null;\n\n\t/**\n\t * The several-backups manager for this store's destination, or null when\n\t * there is nothing to manage several of (no Drive configured).\n\t *\n\t * The port under it - list, open by id, create, rename, delete - was the last\n\t * thing an app still assembled from the session the store had built, and the\n\t * one place it could pick the wrong file id. Built once and remembered; the\n\t * backups module is imported on demand, so an app that never asks does not\n\t * carry it.\n\t */\n\tbackups(): Promise<BackupsManager | null>;\n\n\t/**\n\t * The destinations this store can offer, ready for a connect flow or widget:\n\t * Drive first when the app configured it, a file always.\n\t *\n\t * Consumers were assembling this by hand from the very session the store\n\t * built - and the order and the \"no client id means no Drive card\" rule were\n\t * rediscovered each time. An app wanting a different offer (a WebDAV server,\n\t * its own target) still writes its own object; this is the default, not a\n\t * ceiling.\n\t */\n\tdestinations(): ConnectTargets;\n}\n\n/** The id path a collection's records are keyed by, as the merge engine reads it. */\nconst idPathOf = (sync: SyncConfig | undefined, collection: string): string =>\n\tsync?.ids?.[collection] ?? sync?.idField ?? 'id';\n\n/** A record's id, read at the same path the merge engine reads it: a nested\n * `sync.ids` path written one way and read another made an edit add a row. */\nconst recordId = (\n\tsync: SyncConfig | undefined,\n\tcollection: string,\n\trecord: unknown\n): string | null => idOf(sync ?? {}, collection, record);\n\nfunction requireStringId(\n\tsync: SyncConfig | undefined,\n\trecord: SimpleRecord,\n\tcollection: string\n): string {\n\tconst id = recordId(sync, collection, record);\n\tif (id === null || id.length === 0) {\n\t\tconst field = idPathOf(sync, collection);\n\t\tthrow new TypeError(\n\t\t\t`selfstore: a record in \"${collection}\" needs a non-empty STRING \"${field}\" ` +\n\t\t\t\t`(got ${JSON.stringify(id)}). String ids are what the multi-device merge keys on; ` +\n\t\t\t\t`map another field with { sync: { ids: { ${collection}: 'yourField' } } }.`\n\t\t);\n\t}\n\treturn id;\n}\n\nfunction sameBytes(a: Uint8Array, b: Uint8Array): boolean {\n\treturn a.length === b.length && a.every((v, i) => v === b[i]);\n}\n\n/** True when a browser IndexedDB is available (SSR and plain Node lack it). */\nfunction hasIndexedDb(): boolean {\n\treturn typeof indexedDB !== 'undefined';\n}\n\n/** Told apart by shape: a DriveAuth mints tokens, a `{ clientId }` asks us to. */\nfunction isDriveAuth(v: SimpleOptions['drive']): v is DriveAuth {\n\treturn !!v && typeof (v as DriveAuth).token === 'function';\n}\n\n/** Where the connected Drive address is kept, per app: it is what stops Google\n * asking WHICH account at every re-grant. An address is not a secret, and a\n * browser that refuses storage simply gets the chooser back. */\nconst driveAccountKey = (app: string): string => `selfstore.drive.account.${app}`;\n\nfunction localStore<T>(job: (store: Storage) => T): T | undefined {\n\ttry {\n\t\tconst store = globalThis.localStorage;\n\t\treturn store ? job(store) : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction rememberedDriveAccount(app: string): string | undefined {\n\treturn localStore((s) => s.getItem(driveAccountKey(app))) ?? undefined;\n}\n\n/** Learn the account behind a live Drive session, best-effort and once: it feeds\n * the next re-grant's hint, and any surface that wants to name the destination. */\nasync function learnDriveAccount(app: string, auth: DriveAuth): Promise<void> {\n\ttry {\n\t\tconst who = await driveAccount({ auth });\n\t\tif (who.email) localStore((s) => s.setItem(driveAccountKey(app), who.email!));\n\t} catch {\n\t\t// A destination that will not say costs the hint, never the connection.\n\t}\n}\n\n/**\n * The same connection, which learns whose it is the first time it hands out a\n * token.\n *\n * Learning it took an explicit connect, so anyone whose backup was attached\n * before this existed - or attached in another session - kept a destination\n * with no name on it: the panel said \"Google Drive\" where it could have said\n * which one, and the offer to reopen that backup had no address to show, which\n * is most of what makes it recognisable.\n *\n * It cannot simply be asked for at start-up: reading the account needs a token,\n * and asking for a token with no user gesture behind it opens a popup the\n * browser blocks. So it rides along instead - every token this app obtains,\n * whatever asked for it, teaches it the address at no cost. Once known, nothing\n * is called again.\n */\nfunction selfNaming(app: string, auth: DriveAuth): DriveAuth {\n\tlet learning: Promise<void> | null = null;\n\tconst learn = (): void => {\n\t\tif (learning || rememberedDriveAccount(app)) return;\n\t\tlearning = learnDriveAccount(app, auth);\n\t};\n\treturn {\n\t\tasync token(opts) {\n\t\t\tconst value = await auth.token(opts);\n\t\t\tlearn();\n\t\t\treturn value;\n\t\t},\n\t\tasync reconnect() {\n\t\t\tconst ok = await auth.reconnect();\n\t\t\tif (ok) learn();\n\t\t\treturn ok;\n\t\t},\n\t\tforget: () => auth.forget()\n\t};\n}\n\n/** The schema version a backup's sidecar names: 0 for a copy that carries\n * none (written by another tool), as a remote converge reads it. */\nfunction schemaVersionOf(sidecar: unknown): number {\n\tif (typeof sidecar !== 'object' || sidecar === null) return 0;\n\tconst v = (sidecar as { schemaVersion?: unknown }).schemaVersion;\n\treturn Number.isInteger(v) && (v as number) >= 0 ? (v as number) : 0;\n}\n\n/** The app's own collections, without the library's `__*` bookkeeping. */\nfunction withoutReserved(collections: Snapshot['collections']): Snapshot['collections'] {\n\treturn Object.fromEntries(\n\t\tObject.entries(collections).filter(([name]) => !name.startsWith(RESERVED_COLLECTION_PREFIX))\n\t);\n}\n\n/**\n * Open (or create) the app's local store. Awaits the initial load, so the\n * returned store is ready: data readable, destination restored, first converge\n * done. See SimpleOptions for the defaults.\n */\nexport async function selfstore<\n\tS extends Record<string, SimpleRecord> = Record<string, SimpleRecord>\n>(app: string, options: SimpleOptions = {}): Promise<SimpleStore<S>> {\n\tif (typeof app !== 'string' || app.length === 0) {\n\t\tthrow new TypeError('selfstore: pass your app name, e.g. selfstore(\"my-app\").');\n\t}\n\n\t// The one copy of the data. The engine pulls it on save (gather) and pushes\n\t// into it on restores/folds (apply); the facade's mutators edit it in place\n\t// copy-on-write and schedule a save.\n\tlet collections: Record<string, SimpleRecord[]> = {};\n\t// Binary files: written through putFile/removeFile, and carried untouched\n\t// through every save, backup and merge whoever wrote them.\n\tlet files: SnapshotFile[] = [];\n\n\tconst dataSubs = new Set<() => void>();\n\tconst emitData = (): void => dataSubs.forEach((fn) => fn());\n\n\tconst cache =\n\t\toptions.cache ??\n\t\t(hasIndexedDb()\n\t\t\t? indexedDbCache(app, options.cacheLock ? { lock: true } : undefined)\n\t\t\t: memoryCache());\n\tconst backupName = `${app}.zip`;\n\t// connectDrive() remembers the auth so a later restoreTarget can use it even\n\t// when options.drive was not provided up front (same session only).\n\t//\n\t// `{ clientId }` is the whole wiring done here rather than in every app: ONE\n\t// session (several would mean several consent screens), a token that survives\n\t// a reload, and the account remembered so Google stops asking which one. Each\n\t// of those was a separate decision every consumer had to discover; the app\n\t// that wrote them by hand hit all three as bugs first.\n\t/** Built at most once: two managers over one destination would each hold\n\t * their own idea of which file is active. */\n\tlet backupsManager: BackupsManager | null = null;\n\t// Wrapped whichever shape it came in as: an app that mints its own tokens\n\t// (a server broker, a native shell) has the same nameless-destination\n\t// problem, and the wrapper only ever rides along on a token already granted.\n\tlet driveAuth: DriveAuth | null = isDriveAuth(options.drive)\n\t\t? selfNaming(app, options.drive)\n\t\t: options.drive\n\t\t\t? selfNaming(\n\t\t\t\t\tapp,\n\t\t\t\t\tgisDriveAuth({\n\t\t\t\t\t\tclientId: options.drive.clientId,\n\t\t\t\t\t\tscope: options.drive.scope,\n\t\t\t\t\t\tpersist: 'session',\n\t\t\t\t\t\thint: () => rememberedDriveAccount(app)\n\t\t\t\t\t})\n\t\t\t\t)\n\t\t\t: null;\n\n\t// Ultra-sensitive: unlock the cache before the store hydrates from it. One\n\t// prompt per session, driven by the app (branch it on an existing login to\n\t// keep the UX unchanged). A wrong secret re-prompts; throw to abort.\n\tif (options.cacheLock && isLockableCache(cache)) {\n\t\tlet failed = false;\n\t\twhile (!(await cache.unlock(await options.cacheLock({ failed })))) failed = true;\n\t}\n\n\tconst schema = options.schema ?? 1;\n\tconst store = createLocalStore({\n\t\tapp,\n\t\tschemaVersion: schema,\n\t\tgather: (): Snapshot => structuredClone({ collections, files }),\n\t\tapply: (snap: Snapshot): void => {\n\t\t\tcollections = (snap.collections ?? {}) as Record<string, SimpleRecord[]>;\n\t\t\tfiles = snap.files ?? [];\n\t\t\temitData();\n\t\t},\n\t\tmigrate: options.migrate,\n\t\tsync: options.sync,\n\t\tcache,\n\t\tdebounceMs: options.debounceMs,\n\t\tmultiTab: options.multiTab,\n\t\trequireEncryption: options.requireEncryption,\n\t\tpasswordPolicy: options.passwordPolicy,\n\t\trestoreTarget: async (kind) => {\n\t\t\tif (kind === 'drive' && driveAuth) {\n\t\t\t\treturn driveFromSession({ auth: driveAuth, kv: cache.kv, fileName: backupName });\n\t\t\t}\n\t\t\tif (kind === 'file') return fileFromSession({ kv: cache.kv });\n\t\t\tif (kind === 'webdav') return webdavFromSession({ kv: cache.kv });\n\t\t\tif (kind === 's3') return s3FromSession({ kv: cache.kv });\n\t\t\treturn null;\n\t\t}\n\t});\n\n\tawait store.init();\n\n\t// A degraded file mode is a verdict about the BROWSER, but it was persisted\n\t// like a property of the data: every later session re-read the word without\n\t// ever asking again. One session that could not open a picker - a browser\n\t// that ships the API and refuses it, a call made a moment too late, a user on\n\t// another browser that day - then pinned download-only mode forever, on a\n\t// Chromium perfectly able to hold a file, with no way back.\n\t//\n\t// Nothing writes this word except a picker that could not deliver, so a\n\t// browser that CAN deliver makes it stale. Re-examined here, in the layer\n\t// that wrote it. Dropping to 'device' is the honest state: manual mode had\n\t// already released the durable target, so there is nothing to detach - only\n\t// a stale claim to stop repeating, and a destination the host can offer again.\n\tif (store.state.targetKind === 'file-manual' && fileIsSupported()) {\n\t\tawait store.detachTarget();\n\t}\n\n\t// Auto-wired sync moments (Spring-Boot-style autoconfiguration): tab focus,\n\t// network return, a slow interval, and save-on-hide. Opt out with\n\t// { autoSync: false } to drive these yourself.\n\tconst teardown: (() => void)[] = [];\n\tif (\n\t\toptions.autoSync !== false &&\n\t\ttypeof document !== 'undefined' &&\n\t\ttypeof window !== 'undefined'\n\t) {\n\t\tconst onVisibility = (): void => {\n\t\t\tif (document.visibilityState === 'hidden') void store.flush();\n\t\t\telse void store.syncIfStale('focus');\n\t\t};\n\t\tconst onOnline = (): void => void store.syncIfStale('online');\n\t\tconst onPageHide = (): void => void store.flush();\n\t\tconst interval = setInterval(() => {\n\t\t\tif (document.visibilityState === 'visible') void store.syncIfStale('interval');\n\t\t}, 5 * 60_000);\n\t\tdocument.addEventListener('visibilitychange', onVisibility);\n\t\twindow.addEventListener('online', onOnline);\n\t\twindow.addEventListener('pagehide', onPageHide);\n\t\tteardown.push(() => {\n\t\t\tclearInterval(interval);\n\t\t\tdocument.removeEventListener('visibilitychange', onVisibility);\n\t\t\twindow.removeEventListener('online', onOnline);\n\t\t\twindow.removeEventListener('pagehide', onPageHide);\n\t\t});\n\t}\n\n\tfunction mutate(collection: string, next: SimpleRecord[]): Promise<void> {\n\t\tcollections = { ...collections, [collection]: next };\n\t\temitData();\n\t\tstore.schedule();\n\t\treturn Promise.resolve();\n\t}\n\n\tfunction mutateFiles(next: SnapshotFile[]): Promise<void> {\n\t\tfiles = next;\n\t\temitData();\n\t\tstore.schedule();\n\t\treturn Promise.resolve();\n\t}\n\n\tasync function connectTarget(\n\t\ttarget: BackupTarget,\n\t\topts: { password?: string } = {}\n\t): Promise<ConnectOutcome> {\n\t\tconst info = await store.inspectTarget(target);\n\t\tif (info.hasBackup) {\n\t\t\t// Another app's backup is refused BEFORE the password question: asking\n\t\t\t// first teaches the user their password \"does not work\" on a file that\n\t\t\t// was never this app's to open.\n\t\t\tif (info.app !== null && info.app !== store.app) {\n\t\t\t\tthrow new SelfstoreError(\n\t\t\t\t\t'FOREIGN_BACKUP',\n\t\t\t\t\t`This destination holds a backup written by app \"${info.app}\", not \"${store.app}\". Open it in its own application, or pick another file.`\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Fail before attaching: an encrypted backup without its password would\n\t\t\t// otherwise attach locked, which is a support case, not an outcome.\n\t\t\tif (info.encrypted && !opts.password) {\n\t\t\t\tthrow new SelfstoreError(\n\t\t\t\t\t'PASSWORD_REQUIRED',\n\t\t\t\t\t'This destination holds an encrypted backup: pass its password to connect.'\n\t\t\t\t);\n\t\t\t}\n\t\t\tawait store.attachTarget(target, { password: opts.password ?? null, strategy: 'merge' });\n\t\t\treturn 'merged';\n\t\t}\n\t\tawait store.attachTarget(target, {\n\t\t\tpassword: opts.password ?? null,\n\t\t\tstrategy: 'replace-remote'\n\t\t});\n\t\treturn 'started';\n\t}\n\n\tconst simple: SimpleStore<S> = {\n\t\tall<K extends keyof S & string>(collection: K): readonly S[K][] {\n\t\t\treturn (collections[collection] ?? []) as S[K][];\n\t\t},\n\t\tget<K extends keyof S & string>(collection: K, id: string): S[K] | undefined {\n\t\t\treturn (collections[collection] ?? []).find(\n\t\t\t\t(r) => recordId(options.sync, collection, r) === id\n\t\t\t) 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 id = requireStringId(options.sync, record, collection);\n\t\t\tconst rows = collections[collection] ?? [];\n\t\t\tconst at = rows.findIndex((r) => recordId(options.sync, collection, r) === 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 ids = records.map((r) => requireStringId(options.sync, r, collection));\n\t\t\tconst rows = [...(collections[collection] ?? [])];\n\t\t\tconst byId = new Map<string | null, number>();\n\t\t\trows.forEach((r, i) => byId.set(recordId(options.sync, collection, r), i));\n\t\t\t// The LAST occurrence of an id in one batch wins, as a later put would.\n\t\t\trecords.forEach((r, i) => {\n\t\t\t\tconst at = byId.get(ids[i]);\n\t\t\t\tif (typeof at === 'number') rows[at] = r;\n\t\t\t\telse byId.set(ids[i], rows.push(r) - 1);\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 rows = collections[collection] ?? [];\n\t\t\tconst next = rows.filter((r) => recordId(options.sync, collection, r) !== id);\n\t\t\treturn next.length === rows.length ? Promise.resolve() : mutate(collection, next);\n\t\t},\n\t\tclear<K extends keyof S & string>(collection: K): Promise<void> {\n\t\t\tif ((collections[collection] ?? []).length === 0) return Promise.resolve();\n\t\t\treturn mutate(collection, []);\n\t\t},\n\t\tonChange(fn: () => void): () => void {\n\t\t\tdataSubs.add(fn);\n\t\t\treturn () => dataSubs.delete(fn);\n\t\t},\n\n\t\tallFiles(): readonly SnapshotFile[] {\n\t\t\treturn files;\n\t\t},\n\t\tgetFile(id: string): SnapshotFile | undefined {\n\t\t\treturn files.find((f) => f.id === id);\n\t\t},\n\t\tasync putFile(file: PutFileInput, opts: { replace?: boolean } = {}): Promise<string> {\n\t\t\tconst bytes = await toBytes(file.bytes);\n\t\t\tconst id = file.id ?? (await sha256Hex(bytes));\n\t\t\tif (typeof id !== 'string' || id.length === 0) {\n\t\t\t\tthrow new TypeError(\n\t\t\t\t\t`selfstore: a file needs a non-empty STRING id (got ${JSON.stringify(file.id)}), ` +\n\t\t\t\t\t\t`or omit it to get the content hash.`\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst at = files.findIndex((f) => f.id === id);\n\t\t\t// Same bytes under the same id is the normal case for a content id -\n\t\t\t// re-putting an unchanged body must not schedule a save.\n\t\t\tif (at !== -1 && !opts.replace) {\n\t\t\t\tif (sameBytes(files[at].bytes, bytes)) return id;\n\t\t\t\tthrow new TypeError(\n\t\t\t\t\t`selfstore: file \"${id}\" already holds different bytes. Files are immutable per id ` +\n\t\t\t\t\t\t`because the multi-device merge unions them by id and cannot order two bodies: ` +\n\t\t\t\t\t\t`the other device's copy would be dropped in silence. Omit the id to get a ` +\n\t\t\t\t\t\t`content-addressed one, or pass { replace: true } if this file is only ever ` +\n\t\t\t\t\t\t`written on one device.`\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst entry: SnapshotFile = {\n\t\t\t\tid,\n\t\t\t\tname: file.name ?? id,\n\t\t\t\tmime: file.mime ?? 'application/octet-stream',\n\t\t\t\tbytes\n\t\t\t};\n\t\t\tawait mutateFiles(\n\t\t\t\tat === -1 ? [...files, entry] : files.map((f, i) => (i === at ? entry : f))\n\t\t\t);\n\t\t\treturn id;\n\t\t},\n\t\tremoveFile(id: string): Promise<void> {\n\t\t\tconst next = files.filter((f) => f.id !== id);\n\t\t\treturn next.length === files.length ? Promise.resolve() : mutateFiles(next);\n\t\t},\n\n\t\tasync connectDrive(auth: DriveAuth, opts?: { password?: string }): Promise<ConnectOutcome> {\n\t\t\tdriveAuth = auth;\n\t\t\tconst target = await driveConnect({ auth, kv: cache.kv, fileName: backupName });\n\t\t\tif (!target) return 'cancelled';\n\t\t\tconst outcome = await connectTarget(target, opts);\n\t\t\t// Learn WHICH account now that there is a session: it is the hint that\n\t\t\t// keeps the chooser away next time, and the name a panel can show. Not\n\t\t\t// awaited into the outcome - a slow metadata call must not delay the\n\t\t\t// connect the user is waiting on.\n\t\t\tif (outcome !== 'cancelled') void learnDriveAccount(app, auth);\n\t\t\treturn outcome;\n\t\t},\n\t\tasync connectFile(opts?: { password?: string }): Promise<ConnectOutcome> {\n\t\t\t// Asked twice on purpose: the first call answers from what the browser\n\t\t\t// exposes, the second from what it did when actually asked. A browser\n\t\t\t// that ships the picker and then refuses it only tells us so here.\n\t\t\tif (fileIsSupported()) {\n\t\t\t\tconst target = await fileConnect({ kv: cache.kv, fileName: backupName });\n\t\t\t\tif (target) return connectTarget(target, opts);\n\t\t\t\tif (fileIsSupported()) return 'cancelled';\n\t\t\t}\n\t\t\tawait store.setManualFile();\n\t\t\treturn 'manual';\n\t\t},\n\t\tasync connectWebdav(\n\t\t\tconfig: WebdavConfig,\n\t\t\topts?: { password?: string }\n\t\t): Promise<ConnectOutcome> {\n\t\t\tconst target = await webdavConnect({ kv: cache.kv, config });\n\t\t\tif (!target) {\n\t\t\t\tthrow new SelfstoreError(\n\t\t\t\t\t'TARGET_UNAVAILABLE',\n\t\t\t\t\t'The WebDAV server did not answer (URL, credentials or CORS).'\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn connectTarget(target, opts);\n\t\t},\n\t\tasync connectS3(config: S3Config, opts?: { password?: string }): Promise<ConnectOutcome> {\n\t\t\tconst target = await s3Connect({ kv: cache.kv, config });\n\t\t\tif (!target) {\n\t\t\t\tthrow new SelfstoreError(\n\t\t\t\t\t'TARGET_UNAVAILABLE',\n\t\t\t\t\t'The S3 endpoint did not answer (endpoint, credentials or CORS).'\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn connectTarget(target, opts);\n\t\t},\n\t\tconnectTarget,\n\t\tdisconnect: () => store.detachTarget(),\n\t\taddReplica: (target, opts) => store.attachReplica(target, opts),\n\t\tremoveReplica: (id) => store.detachReplica(id),\n\n\t\tprotect: (password: string) => store.setEncryption(password),\n\t\tunprotect: () => store.setEncryption(null),\n\t\tunlock: (password: string) => store.unlock(password),\n\t\treconnect: () => store.reconnect(),\n\n\t\texportBackup: (opts?: { plaintext?: boolean }) => store.exportBlob(opts),\n\t\tasync downloadBackup(filename?: string): Promise<boolean> {\n\t\t\tconst written = await saveToDisk(await store.exportBlob(), filename ?? backupName);\n\t\t\t// Clearing the pending flag on a cancelled dialog would drop the very\n\t\t\t// nudge that asks for the file the user still does not have.\n\t\t\tif (written) store.markDownloaded();\n\t\t\treturn written;\n\t\t},\n\t\tasync importBackup(file: Blob | Uint8Array, opts?: { password?: string }): Promise<void> {\n\t\t\t// Judged by the schema that wrote it, as a remote copy is: a newer one\n\t\t\t// is refused, an older one is migrated BEFORE it replaces anything,\n\t\t\t// since the next save stamps the current version over it for good.\n\t\t\tconst { snapshot, sidecar } = await readBoxWithSync(await asBytes(file), opts?.password);\n\t\t\tconst fromVersion = schemaVersionOf(sidecar);\n\t\t\tif (fromVersion > schema) {\n\t\t\t\tthrow new SelfstoreError(\n\t\t\t\t\t'SCHEMA_TOO_NEW',\n\t\t\t\t\t`Backup written by schema v${fromVersion}; this app reads v${schema}. Update the app.`\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Reserved bookkeeping collections are stripped; removals then\n\t\t\t// propagate because the next save diffs against the sync meta.\n\t\t\tconst read = { collections: withoutReserved(snapshot.collections), files: snapshot.files };\n\t\t\tconst snap =\n\t\t\t\toptions.migrate && fromVersion < schema ? options.migrate(fromVersion, read) : read;\n\t\t\tcollections = (snap.collections ?? {}) as Record<string, SimpleRecord[]>;\n\t\t\tfiles = snap.files ?? [];\n\t\t\temitData();\n\t\t\tstore.schedule();\n\t\t},\n\n\t\tget status(): StatusDescriptor {\n\t\t\treturn store.state.status;\n\t\t},\n\t\tget error(): StoreError | null {\n\t\t\treturn store.state.lastError;\n\t\t},\n\t\tget state(): LocalStoreState {\n\t\t\treturn store.state;\n\t\t},\n\t\tsubscribe: (fn: () => void) => store.subscribe(fn),\n\n\t\tflush: () => store.flush(),\n\t\tasync sync(): Promise<void> {\n\t\t\tawait store.syncNow();\n\t\t},\n\t\tdispose(): void {\n\t\t\tteardown.forEach((fn) => fn());\n\t\t\tteardown.length = 0;\n\t\t\tstore.dispose();\n\t\t},\n\n\t\tadvanced: store,\n\n\t\tflowHost: { engine: store, kv: cache.kv, backupName },\n\n\t\tget account(): string | null {\n\t\t\treturn rememberedDriveAccount(app) ?? null;\n\t\t},\n\n\t\tasync backups(): Promise<BackupsManager | null> {\n\t\t\tif (!driveAuth) return null;\n\t\t\tbackupsManager ??= await (async () => {\n\t\t\t\tconst { createBackupsManager, driveBackupsHost } = await import('../backups/manager-drive');\n\t\t\t\treturn createBackupsManager({\n\t\t\t\t\tstore,\n\t\t\t\t\tkv: cache.kv,\n\t\t\t\t\thost: driveBackupsHost({ auth: driveAuth!, kv: cache.kv, fileName: backupName }),\n\t\t\t\t\tnaming: { canonicalName: backupName }\n\t\t\t\t});\n\t\t\t})();\n\t\t\treturn backupsManager;\n\t\t},\n\n\t\tdestinations(): ConnectTargets {\n\t\t\t// Drive first when there is one: it is the answer that follows a user\n\t\t\t// from device to device. A file always, for whoever wants no account.\n\t\t\treturn driveAuth ? { drive: driveAuth, file: true } : { file: true };\n\t\t},\n\n\t\tresumeOffer(): ResumeOffer | null {\n\t\t\tconst detail = rememberedDriveAccount(app);\n\t\t\tif (!driveAuth || !detail) return null;\n\t\t\tconst auth = driveAuth;\n\t\t\treturn {\n\t\t\t\tkind: 'drive',\n\t\t\t\tdetail,\n\t\t\t\t// Reopening is not searching: fromSession takes the file id already\n\t\t\t\t// remembered, where a fresh connect resolves by name and could land on\n\t\t\t\t// another file carrying the same one.\n\t\t\t\tconnect: () => driveFromSession({ auth, kv: cache.kv, fileName: backupName })\n\t\t\t};\n\t\t}\n\t};\n\n\treturn simple;\n}\n"]}
@@ -1,11 +1,11 @@
1
- export { createBackupsManager, driveBackupsHost } from './chunk-5DAPYC2K.js';
1
+ export { createBackupsManager, driveBackupsHost } from './chunk-MDEEAQSU.js';
2
2
  import './chunk-2BNTCXQN.js';
3
- import './chunk-25U6AKIX.js';
3
+ import './chunk-IUVOVVEA.js';
4
4
  import './chunk-LF3CC2P3.js';
5
5
  import './chunk-7U5XMKVP.js';
6
6
  import './chunk-F4ZZEJ5H.js';
7
7
  import './chunk-H2T4S3KM.js';
8
8
  import './chunk-WXI2NAPA.js';
9
9
  import './chunk-CSYEIXNY.js';
10
- //# sourceMappingURL=manager-drive-T3RXUWAY.js.map
11
- //# sourceMappingURL=manager-drive-T3RXUWAY.js.map
10
+ //# sourceMappingURL=manager-drive-KSZIHMLF.js.map
11
+ //# sourceMappingURL=manager-drive-KSZIHMLF.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"manager-drive-T3RXUWAY.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"manager-drive-KSZIHMLF.js"}
package/dist/proof.d.ts CHANGED
@@ -195,6 +195,10 @@ interface JournalOptions {
195
195
  * receipt with `tour`, `root`, `block`, the `.ots` proof and, when the journal
196
196
  * stamped that root, the `.tsr` token (both base64). Checking the Merkle path
197
197
  * from the line to the root is the journal's verifier's job.
198
+ *
199
+ * `complete` reads the tour list first and the deposits file only when a tour
200
+ * folded after the deposit is anchored: the file lists every deposit of every
201
+ * user and grows without end, the tour list stays small.
198
202
  */
199
203
  declare function digestJournalNotary(options: JournalOptions): Notary;
200
204
 
package/dist/proof.js CHANGED
@@ -332,6 +332,15 @@ function parseLine(text) {
332
332
  throw refused2("the journal published an unreadable deposits file");
333
333
  }
334
334
  }
335
+ function mayCover(tour, depositedAt) {
336
+ if (tour.instant === void 0) return true;
337
+ const folded = Date.parse(tour.instant);
338
+ const deposited = Date.parse(depositedAt);
339
+ return Number.isNaN(folded) || Number.isNaN(deposited) || folded > deposited;
340
+ }
341
+ function worthReading(tours, depositedAt) {
342
+ return tours.some((t) => t.depots > 0 && t.ots === "bitcoin" && mayCover(t, depositedAt));
343
+ }
335
344
  function rankOf(deposits, id, hash) {
336
345
  const lines = deposits.split("\n").filter(Boolean);
337
346
  for (const [index, text] of lines.entries()) {
@@ -349,6 +358,15 @@ function digestJournalNotary(options) {
349
358
  const send = options.fetch ?? globalThis.fetch;
350
359
  const get = (path) => reach(send, `${base}${path}`, void 0, "the journal");
351
360
  const bytesOf = async (path) => toBase64(new Uint8Array(await (await get(path)).arrayBuffer()));
361
+ let deposits;
362
+ const depositsFile = async (tours) => {
363
+ const last = tours.at(-1);
364
+ const key = last === void 0 ? "" : `${last.id}:${last.depots}`;
365
+ if (deposits?.key !== key) {
366
+ deposits = { key, text: await (await get("/empreintes/depots.jsonl")).text() };
367
+ }
368
+ return deposits.text;
369
+ };
352
370
  return {
353
371
  name,
354
372
  async deposit(hash, label) {
@@ -384,9 +402,10 @@ function digestJournalNotary(options) {
384
402
  async complete(receipt) {
385
403
  const id = receipt.evidence.id;
386
404
  if (id === void 0) throw refused2("the receipt carries no journal id");
387
- const rank = rankOf(await (await get("/empreintes/depots.jsonl")).text(), id, receipt.hash);
388
- if (rank === 0) return receipt;
389
405
  const { tours } = await (await get("/tours.json")).json();
406
+ if (!worthReading(tours, receipt.depositedAt)) return receipt;
407
+ const rank = rankOf(await depositsFile(tours), id, receipt.hash);
408
+ if (rank === 0) return receipt;
390
409
  const tour = tours.find((t) => t.depots >= rank);
391
410
  if (tour?.ots !== "bitcoin") return receipt;
392
411
  const stamped = tour.horodatage !== null && tour.fichiers?.includes(`${tour.id}.racine.tsr`);
package/dist/proof.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/proof/proof.ts","../src/proof/der.ts","../src/proof/reach.ts","../src/proof/rfc3161.ts","../src/proof/journal.ts"],"names":["refused","hex"],"mappings":"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","file":"proof.js","sourcesContent":["// Proof of existence for the user's own documents: the bytes never leave the\n// device, only their SHA-256 does. A notary dates that digest; the receipt it\n// hands back is kept next to the file, in the user's own storage, and anyone\n// holding the file and the receipt can check the two still belong together.\n\nimport { sha256Hex } from '../selfstore/digest';\nimport { isSelfstoreError, SelfstoreError } from '../selfstore/errors';\nimport { toBytes, type FileBytes } from '../simple/bytes';\nimport type { SimpleRecord } from '../simple/simple';\n\n/**\n * What a notary hands back for one digest. `evidence` is the notary's own\n * material (a timestamp token, an inclusion path, a download address), as\n * strings - base64 for bytes - so a receipt is plain JSON and survives every\n * destination a store can write to.\n */\nexport interface Receipt {\n\t/** The notary that issued it (`Notary.name`). */\n\treadonly notary: string;\n\t/** Lowercase hex SHA-256 of the document. */\n\treadonly hash: string;\n\t/** The label sent with the deposit, if any. */\n\treadonly label?: string;\n\t/** ISO 8601 instant the notary vouches for (its clock, not the device's). */\n\treadonly depositedAt: string;\n\t/** False while the notary still has something to add (an anchor, a seal). */\n\treadonly complete: boolean;\n\treadonly evidence: Readonly<Record<string, string>>;\n}\n\n/**\n * Anyone who dates a digest. `deposit` must never see more than the digest and\n * the label; `complete` is called later - the next day, the next launch - and\n * returns the same receipt with whatever the notary could not give at once.\n * A notary that is final on deposit returns its argument unchanged.\n */\nexport interface Notary {\n\treadonly name: string;\n\tdeposit(hash: string, label?: string): Promise<Receipt>;\n\tcomplete(receipt: Receipt): Promise<Receipt>;\n}\n\n/** A receipt as `prove()` keeps it: the notary's, plus what the device measured. */\nexport interface Proof extends Receipt {\n\treadonly kind: 'selfstore-proof';\n\treadonly version: 1;\n\treadonly algorithm: 'SHA-256';\n\t/** Size of the document in bytes. */\n\treadonly size: number;\n\t/** The store file the proof belongs to, when there is one. */\n\treadonly file?: string;\n}\n\n/** Where proofs are kept: the simple store fits as is. */\nexport interface ProofShelf {\n\tput(collection: string, record: SimpleRecord): Promise<void>;\n}\n\n/** The collection `prove()` and `completeProof()` write to. */\nexport const PROOFS = 'proofs';\n\nexport interface ProveOptions {\n\tnotary: Notary;\n\t/** Sent to the notary with the digest. A TYPE ('invoice', 'inventory'),\n\t * never a name or a piece of the content: it leaves the device. */\n\tlabel?: string;\n\t/** Keep the proof in this store, under `PROOFS`, keyed by `file` or by hash. */\n\tstore?: ProofShelf;\n\t/** Id of the store file the document is (as `putFile` returned it). */\n\tfile?: string;\n}\n\n/** The result of `verify()`. */\nexport interface ProofCheck {\n\t/** The bytes hash to the receipt's digest (and size, when it carries one). */\n\treadonly matches: boolean;\n\t/** What the bytes hash to now. */\n\treadonly hash: string;\n\t/** Whether the notary had finished with this receipt. */\n\treadonly complete: boolean;\n}\n\nconst HEX_DIGEST = /^[0-9a-f]{64}$/;\n\nfunction refused(message: string): SelfstoreError {\n\treturn new SelfstoreError('NOTARY_REFUSED', message);\n}\n\n/** A notary that answers for another digest, or for another notary, is not believed. */\nfunction checkAnswer(answer: Receipt, hash: string, notary: Notary): void {\n\tif (typeof answer?.hash !== 'string' || answer.hash.toLowerCase() !== hash) {\n\t\tthrow refused(`${notary.name} answered for another digest`);\n\t}\n\tif (answer.notary !== notary.name) {\n\t\tthrow refused(`receipt names ${String(answer.notary)}, not ${notary.name}`);\n\t}\n\tif (Number.isNaN(Date.parse(answer.depositedAt))) {\n\t\tthrow refused(`${notary.name} gave no readable deposit time`);\n\t}\n}\n\nasync function shelve(proof: Proof, store: ProofShelf | undefined): Promise<void> {\n\tif (store) await store.put(PROOFS, { ...proof, id: proof.file ?? proof.hash });\n}\n\n/**\n * Hash `blob` on the device, deposit the digest with `options.notary`, and\n * return the proof - kept in `options.store` too when one is given. Only the\n * digest and the label leave the device.\n *\n * Throws NOTARY_UNAVAILABLE when the notary cannot be reached (retry later)\n * and NOTARY_REFUSED when it refuses or answers for something else.\n */\nexport async function prove(blob: FileBytes, options: ProveOptions): Promise<Proof> {\n\tconst bytes = await toBytes(blob);\n\tconst hash = await sha256Hex(bytes);\n\tconst answer = await options.notary.deposit(hash, options.label);\n\tcheckAnswer(answer, hash, options.notary);\n\tconst proof: Proof = {\n\t\t...answer,\n\t\thash,\n\t\tkind: 'selfstore-proof',\n\t\tversion: 1,\n\t\talgorithm: 'SHA-256',\n\t\tsize: bytes.length,\n\t\t...(options.file === undefined ? {} : { file: options.file })\n\t};\n\tawait shelve(proof, options.store);\n\treturn proof;\n}\n\n/**\n * Ask the notary for what it could not give on deposit. A complete proof is\n * returned as is, without a call. The fields the device measured are kept;\n * the notary may only add to its own.\n */\nexport async function completeProof(\n\tproof: Proof,\n\toptions: { notary: Notary; store?: ProofShelf }\n): Promise<Proof> {\n\tif (proof.complete) return proof;\n\tconst answer = await options.notary.complete(proof);\n\tcheckAnswer(answer, proof.hash, options.notary);\n\tconst completed: Proof = {\n\t\t...answer,\n\t\thash: proof.hash,\n\t\tkind: proof.kind,\n\t\tversion: proof.version,\n\t\talgorithm: proof.algorithm,\n\t\tsize: proof.size,\n\t\t...(proof.file === undefined ? {} : { file: proof.file })\n\t};\n\tawait shelve(completed, options.store);\n\treturn completed;\n}\n\n/**\n * Hash `blob` again and compare it with the receipt. This proves the bytes are\n * the ones the receipt names; that the receipt itself is genuine is what the\n * notary's evidence is for, checked with the notary's own verifier.\n *\n * Throws BAD_FORMAT on a receipt that carries no SHA-256 digest at all - a\n * damaged receipt is not a document that changed.\n */\nexport async function verify(\n\tblob: FileBytes,\n\treceipt: Pick<Receipt, 'hash' | 'complete'> & { size?: number }\n): Promise<ProofCheck> {\n\tconst expected = typeof receipt?.hash === 'string' ? receipt.hash.toLowerCase() : '';\n\tif (!HEX_DIGEST.test(expected)) {\n\t\tthrow new SelfstoreError('BAD_FORMAT', 'the receipt carries no SHA-256 digest');\n\t}\n\tconst bytes = await toBytes(blob);\n\tconst hash = await sha256Hex(bytes);\n\tconst sizeAgrees = receipt.size === undefined || receipt.size === bytes.length;\n\treturn { matches: hash === expected && sizeAgrees, hash, complete: receipt.complete === true };\n}\n\n/** Where proofs are read back from: the simple store fits as is. */\nexport interface ProofLibrary extends ProofShelf {\n\tall(collection: string): readonly SimpleRecord[];\n}\n\nfunction isProof(record: SimpleRecord): boolean {\n\treturn (\n\t\trecord.kind === 'selfstore-proof' &&\n\t\ttypeof record.hash === 'string' &&\n\t\tHEX_DIGEST.test(record.hash) &&\n\t\ttypeof record.notary === 'string' &&\n\t\ttypeof record.complete === 'boolean'\n\t);\n}\n\n/**\n * Every proof kept in `store`, oldest deposit first. Records that are not\n * proofs - another app's, a damaged one - are skipped rather than trusted.\n */\nexport function proofsIn(store: Pick<ProofLibrary, 'all'>): Proof[] {\n\treturn store\n\t\t.all(PROOFS)\n\t\t.filter(isProof)\n\t\t.map((record) => {\n\t\t\tconst { id: _id, ...proof } = record;\n\t\t\treturn proof as unknown as Proof;\n\t\t})\n\t\t.sort((a, b) => Date.parse(a.depositedAt) - Date.parse(b.depositedAt));\n}\n\n/** What `completePending()` did, proof by proof. */\nexport interface PendingRound {\n\t/** Finished this round, and written back to the store. */\n\treadonly completed: readonly Proof[];\n\t/** Still waiting: the notary has nothing to add yet, could not be reached,\n\t * or is not among the notaries given. Ask again on a later launch. */\n\treadonly pending: readonly Proof[];\n\t/** The notary answered for something else; kept as it was, not overwritten. */\n\treadonly refused: readonly Proof[];\n}\n\nfunction isCode(error: unknown, code: string): boolean {\n\treturn isSelfstoreError(error) && error.code === code;\n}\n\n/**\n * Ask each notary to finish the incomplete proofs it issued - the call an app\n * makes at launch, since a notary that anchors its digests finishes a receipt\n * hours after the deposit. A notary that cannot be reached is not asked again\n * this round; a proof whose notary is not in `notaries` waits. Only a proof\n * that came back complete is written to the store.\n */\nexport async function completePending(options: {\n\tstore: ProofLibrary;\n\tnotaries: readonly Notary[];\n}): Promise<PendingRound> {\n\tconst byName = new Map(options.notaries.map((notary) => [notary.name, notary]));\n\tconst unreachable = new Set<string>();\n\tconst completed: Proof[] = [];\n\tconst pending: Proof[] = [];\n\tconst refused: Proof[] = [];\n\tfor (const proof of proofsIn(options.store)) {\n\t\tif (proof.complete) continue;\n\t\tconst notary = byName.get(proof.notary);\n\t\tif (notary === undefined || unreachable.has(notary.name)) {\n\t\t\tpending.push(proof);\n\t\t\tcontinue;\n\t\t}\n\t\ttry {\n\t\t\tconst answer = await completeProof(proof, { notary });\n\t\t\tif (answer.complete) {\n\t\t\t\tawait shelve(answer, options.store);\n\t\t\t\tcompleted.push(answer);\n\t\t\t} else {\n\t\t\t\tpending.push(proof);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (isCode(error, 'NOTARY_UNAVAILABLE')) {\n\t\t\t\tunreachable.add(notary.name);\n\t\t\t\tpending.push(proof);\n\t\t\t} else if (isCode(error, 'NOTARY_REFUSED')) {\n\t\t\t\trefused.push(proof);\n\t\t\t} else {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t}\n\treturn { completed, pending, refused };\n}\n","// The few DER shapes an RFC 3161 exchange needs, and no more. A timestamp\n// request is five fields and the reply is read along one path, so a general\n// ASN.1 library would be a fourth runtime dependency for two functions.\n\n/** One decoded TLV: `start` and `end` bound its CONTENT. */\nexport interface DerNode {\n\treadonly tag: number;\n\treadonly start: number;\n\treadonly end: number;\n}\n\nexport class DerError extends Error {}\n\nfunction encodeLength(length: number): Uint8Array {\n\tif (length < 0x80) return Uint8Array.of(length);\n\tconst bytes: number[] = [];\n\tfor (let rest = length; rest > 0; rest = Math.floor(rest / 256)) bytes.unshift(rest % 256);\n\treturn Uint8Array.of(0x80 | bytes.length, ...bytes);\n}\n\n/** Encode one TLV around already-encoded content. */\nexport function tlv(tag: number, ...parts: Uint8Array[]): Uint8Array {\n\tconst length = parts.reduce((sum, part) => sum + part.length, 0);\n\tconst head = encodeLength(length);\n\tconst out = new Uint8Array(1 + head.length + length);\n\tout[0] = tag;\n\tout.set(head, 1);\n\tlet at = 1 + head.length;\n\tfor (const part of parts) {\n\t\tout.set(part, at);\n\t\tat += part.length;\n\t}\n\treturn out;\n}\n\n/** Read the TLV that starts at `offset`. Indefinite lengths are BER, not DER: refused. */\nexport function readNode(bytes: Uint8Array, offset: number, limit = bytes.length): DerNode {\n\tif (offset + 2 > limit) throw new DerError('truncated header');\n\tconst tag = bytes[offset];\n\tconst first = bytes[offset + 1];\n\tlet start = offset + 2;\n\tlet length = first;\n\tif (first & 0x80) {\n\t\tconst count = first & 0x7f;\n\t\tif (count === 0 || count > 4) throw new DerError('unsupported length');\n\t\tif (start + count > limit) throw new DerError('truncated length');\n\t\tlength = 0;\n\t\tfor (let i = 0; i < count; i++) length = length * 256 + bytes[start + i];\n\t\tstart += count;\n\t}\n\tconst end = start + length;\n\tif (end > limit) throw new DerError('content runs past its parent');\n\treturn { tag, start, end };\n}\n\n/** The direct children of a constructed node. */\nexport function childrenOf(bytes: Uint8Array, node: DerNode): DerNode[] {\n\tconst out: DerNode[] = [];\n\tfor (let at = node.start; at < node.end;) {\n\t\tconst child = readNode(bytes, at, node.end);\n\t\tout.push(child);\n\t\tat = child.end;\n\t}\n\treturn out;\n}\n\n/** The node's content, or a refusal when its tag is not the one expected. */\nexport function expectTag(node: DerNode | undefined, tag: number): DerNode {\n\tif (node?.tag !== tag) throw new DerError(`expected tag 0x${tag.toString(16)}`);\n\treturn node;\n}\n\nexport function contentOf(bytes: Uint8Array, node: DerNode): Uint8Array {\n\treturn bytes.subarray(node.start, node.end);\n}\n","// One way to call a notary over HTTP, so every adapter maps the same failures\n// to the same codes: unreachable, overloaded or rate-limited is worth a retry,\n// any other refusal is not.\n\nimport { SelfstoreError } from '../selfstore/errors';\n\n/** Fetch `url`, throwing NOTARY_UNAVAILABLE or NOTARY_REFUSED instead of returning a failure. */\nexport async function reach(\n\tsend: typeof fetch,\n\turl: string,\n\tinit: RequestInit | undefined,\n\twho: string\n): Promise<Response> {\n\tlet response: Response;\n\ttry {\n\t\tresponse = await send(url, init);\n\t} catch (e) {\n\t\tthrow new SelfstoreError(\n\t\t\t'NOTARY_UNAVAILABLE',\n\t\t\t`${who} could not be reached: ${(e as Error).message}`\n\t\t);\n\t}\n\tif (!response.ok) {\n\t\tconst code =\n\t\t\tresponse.status >= 500 || response.status === 429 ? 'NOTARY_UNAVAILABLE' : 'NOTARY_REFUSED';\n\t\tthrow new SelfstoreError(code, `${who} answered ${response.status}`);\n\t}\n\treturn response;\n}\n","// A notary over any RFC 3161 time-stamping authority. The authority signs the\n// digest and the time in one token, so the receipt is complete on deposit: the\n// token (the `.tsr` file) is the evidence, and `openssl ts -verify` checks it\n// against the authority's certificate without this library.\n\nimport { fromBase64, toBase64 } from '../selfstore/base64';\nimport { hex } from '../selfstore/digest';\nimport { SelfstoreError } from '../selfstore/errors';\nimport { childrenOf, contentOf, DerError, expectTag, readNode, tlv, type DerNode } from './der';\nimport type { Notary, Receipt } from './proof';\nimport { reach } from './reach';\n\nconst SEQUENCE = 0x30;\nconst INTEGER = 0x02;\nconst OCTET_STRING = 0x04;\nconst OID = 0x06;\nconst GENERALIZED_TIME = 0x18;\nconst EXPLICIT_0 = 0xa0;\n\n/** id-sha256, 2.16.840.1.101.3.4.2.1, with its NULL parameters. */\nconst SHA256_ALGORITHM = tlv(\n\tSEQUENCE,\n\ttlv(OID, Uint8Array.of(0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01)),\n\tUint8Array.of(0x05, 0x00)\n);\n\nexport interface Rfc3161Options {\n\t/** The authority's endpoint. In a browser it must answer CORS, or sit\n\t * behind a relay of yours that does: most public authorities do not. */\n\turl: string;\n\t/** Receipt `notary` field. Defaults to 'rfc3161'. */\n\tname?: string;\n\t/** Injected for tests and relays; defaults to the global fetch. */\n\tfetch?: typeof fetch;\n}\n\n/** What a timestamp token says, read without checking its signature. */\nexport interface TimestampToken {\n\t/** Lowercase hex of the digest the authority stamped. */\n\treadonly hash: string;\n\t/** ISO 8601 instant the authority signed. */\n\treadonly time: string;\n\t/** The nonce echoed back, as lowercase hex, when there is one. */\n\treadonly nonce?: string;\n}\n\nfunction hexToBytes(hex: string): Uint8Array {\n\treturn Uint8Array.from(hex.match(/../g) ?? [], (pair) => Number.parseInt(pair, 16));\n}\n\n/** Leading zero bytes are DER's sign padding, not part of the value. */\nfunction integerHex(bytes: Uint8Array): string {\n\tlet at = 0;\n\twhile (at < bytes.length - 1 && bytes[at] === 0) at++;\n\treturn hex(bytes.subarray(at));\n}\n\n/** Eight random bytes, positive and minimal as a DER INTEGER. */\nfunction newNonce(): Uint8Array {\n\tconst nonce = crypto.getRandomValues(new Uint8Array(8));\n\tnonce[0] = (nonce[0] & 0x3f) | 0x40;\n\treturn nonce;\n}\n\n/** A TimeStampReq (RFC 3161 section 2.4.1) for a SHA-256 digest, asking for the certificate. */\nexport function timestampRequest(hash: string, nonce: Uint8Array): Uint8Array {\n\treturn tlv(\n\t\tSEQUENCE,\n\t\ttlv(INTEGER, Uint8Array.of(1)),\n\t\ttlv(SEQUENCE, SHA256_ALGORITHM, tlv(OCTET_STRING, hexToBytes(hash))),\n\t\ttlv(INTEGER, nonce),\n\t\tUint8Array.of(0x01, 0x01, 0xff)\n\t);\n}\n\n/** YYYYMMDDHHMMSS[.fff]Z to ISO 8601. */\nfunction generalizedTime(text: string): string {\n\tconst parts = /^(\\d{4})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\.\\d+)?Z$/.exec(text);\n\tif (parts === null) throw new DerError(`unreadable time ${text}`);\n\tconst [, y, mo, d, h, mi, s, fraction] = parts;\n\treturn new Date(`${y}-${mo}-${d}T${h}:${mi}:${s}${fraction ?? ''}Z`).toISOString();\n}\n\n/** TimeStampResp: the status, or the token when it was granted. */\nfunction tokenOf(bytes: Uint8Array): DerNode {\n\tconst [statusInfo, token] = childrenOf(bytes, expectTag(readNode(bytes, 0), SEQUENCE));\n\tconst status = expectTag(childrenOf(bytes, expectTag(statusInfo, SEQUENCE))[0], INTEGER);\n\tconst code = Number.parseInt(integerHex(contentOf(bytes, status)), 16);\n\t// 0 granted, 1 granted with modifications; anything else is a refusal.\n\tif (code > 1) {\n\t\tthrow new SelfstoreError(\n\t\t\t'NOTARY_REFUSED',\n\t\t\t`the authority refused the request (status ${code})`\n\t\t);\n\t}\n\treturn expectTag(token, SEQUENCE);\n}\n\n/** ContentInfo > SignedData > EncapsulatedContentInfo > TSTInfo. */\nfunction tstInfoOf(bytes: Uint8Array, token: DerNode): DerNode {\n\tconst signedData = expectTag(\n\t\tchildrenOf(bytes, expectTag(childrenOf(bytes, token)[1], EXPLICIT_0))[0],\n\t\tSEQUENCE\n\t);\n\tconst encapsulated = expectTag(childrenOf(bytes, signedData)[2], SEQUENCE);\n\tconst wrapper = expectTag(childrenOf(bytes, encapsulated)[1], EXPLICIT_0);\n\tconst octets = expectTag(childrenOf(bytes, wrapper)[0], OCTET_STRING);\n\treturn expectTag(readNode(bytes, octets.start, octets.end), SEQUENCE);\n}\n\n/**\n * Read the digest, the time and the nonce out of a TimeStampResp (the `.tsr`\n * file). It does NOT check the authority's signature - that is what\n * `openssl ts -verify` is for; it is what tells a token for another document\n * apart before anything is kept. Throws NOTARY_REFUSED on a refusal or on\n * anything that is not a timestamp reply.\n */\nexport function readTimestampReply(bytes: Uint8Array): TimestampToken {\n\ttry {\n\t\tconst tstInfo = tstInfoOf(bytes, tokenOf(bytes));\n\t\t// version, policy, messageImprint, serialNumber, genTime, then optional\n\t\t// accuracy, ordering and nonce - the nonce is the only INTEGER among them.\n\t\tconst [, , imprint, , time, ...rest] = childrenOf(bytes, tstInfo);\n\t\tconst digest = expectTag(childrenOf(bytes, expectTag(imprint, SEQUENCE))[1], OCTET_STRING);\n\t\tconst nonce = rest.find((node) => node.tag === INTEGER);\n\t\tconst genTime = new TextDecoder().decode(contentOf(bytes, expectTag(time, GENERALIZED_TIME)));\n\t\treturn {\n\t\t\thash: hex(contentOf(bytes, digest)),\n\t\t\ttime: generalizedTime(genTime),\n\t\t\t...(nonce ? { nonce: integerHex(contentOf(bytes, nonce)) } : {})\n\t\t};\n\t} catch (e) {\n\t\tif (e instanceof SelfstoreError) throw e;\n\t\tthrow new SelfstoreError('NOTARY_REFUSED', `not a timestamp reply: ${(e as Error).message}`);\n\t}\n}\n\nasync function post(options: Rfc3161Options, body: Uint8Array): Promise<Uint8Array> {\n\tconst response = await reach(\n\t\toptions.fetch ?? globalThis.fetch,\n\t\toptions.url,\n\t\t{\n\t\t\tmethod: 'POST',\n\t\t\theaders: { 'content-type': 'application/timestamp-query' },\n\t\t\tbody: body as BodyInit\n\t\t},\n\t\t'the authority'\n\t);\n\treturn new Uint8Array(await response.arrayBuffer());\n}\n\n/**\n * A notary over an RFC 3161 time-stamping authority. The receipt is complete\n * on deposit; its evidence is the reply, base64 under `tsr`, and the\n * authority's address under `tsa`. The label is never sent: RFC 3161 has no\n * field for it, so it stays on the receipt.\n */\nexport function rfc3161Notary(options: Rfc3161Options): Notary {\n\tconst name = options.name ?? 'rfc3161';\n\treturn {\n\t\tname,\n\t\tasync deposit(hash: string, label?: string): Promise<Receipt> {\n\t\t\tconst nonce = newNonce();\n\t\t\tconst reply = await post(options, timestampRequest(hash, nonce));\n\t\t\tconst token = readTimestampReply(reply);\n\t\t\tif (token.hash !== hash) {\n\t\t\t\tthrow new SelfstoreError('NOTARY_REFUSED', 'the authority stamped another digest');\n\t\t\t}\n\t\t\tif (token.nonce !== integerHex(nonce)) {\n\t\t\t\tthrow new SelfstoreError('NOTARY_REFUSED', 'the reply does not answer this request');\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tnotary: name,\n\t\t\t\thash,\n\t\t\t\t...(label === undefined ? {} : { label }),\n\t\t\t\tdepositedAt: token.time,\n\t\t\t\tcomplete: true,\n\t\t\t\tevidence: { tsr: toBase64(reply), tsa: options.url }\n\t\t\t};\n\t\t},\n\t\tcomplete(receipt: Receipt): Promise<Receipt> {\n\t\t\treturn Promise.resolve(receipt);\n\t\t}\n\t};\n}\n\n/** The `.tsr` bytes of an RFC 3161 receipt, to offer as a download. */\nexport function timestampReplyOf(receipt: Pick<Receipt, 'evidence'>): Uint8Array | undefined {\n\tconst tsr = receipt.evidence.tsr;\n\treturn tsr === undefined ? undefined : fromBase64(tsr);\n}\n","// A notary over a public digest journal: a service that takes a SHA-256 with\n// no account, writes it as one line of a public deposits file, and folds that\n// file into a Merkle tree it anchors in Bitcoin (OpenTimestamps) and often\n// stamps under RFC 3161 too. The deposit answers at once with the journal's\n// own receipt; the anchor comes hours later, so `complete` is what fetches it.\n//\n// The protocol, relative to the journal's base URL:\n// POST /api/depots {\"sha256\", \"libelle\"?, \"type\"?} -> 201\n// {\"id\", \"jour\", \"place\", \"ligne\", \"recu\": {\"sha256\", \"depose\", ...}}\n// GET /empreintes/depots.jsonl one JSON line per deposit, in order, with \"recu\" = id\n// GET /tours.json {\"tours\": [{\"id\", \"racine\", \"depots\", \"ots\", \"bloc\",\n// \"horodatage\", \"fichiers\"}]}; a tour covers the\n// first `depots` lines of the deposits file\n// GET /empreintes/<tour>.racine.ots, /empreintes/<tour>.racine.tsr\n\nimport { toBase64 } from '../selfstore/base64';\nimport { SelfstoreError } from '../selfstore/errors';\nimport type { Notary, Receipt } from './proof';\nimport { reach } from './reach';\n\nexport interface JournalOptions {\n\t/** The journal's base URL, e.g. 'https://journal.example'. */\n\turl: string;\n\t/** Receipt `notary` field. Defaults to 'digest-journal'. */\n\tname?: string;\n\t/**\n\t * The journal's deposit type, sent as `type` with every deposit (a journal\n\t * that issues typed receipts, such as a permit display, keys them on it).\n\t * None by default: the deposit is the journal's ordinary one.\n\t */\n\ttype?: string;\n\t/** Injected for tests and relays; defaults to the global fetch. */\n\tfetch?: typeof fetch;\n}\n\ninterface Deposit {\n\tid: string;\n\tligne: string;\n\trecu: { sha256: string; depose: string };\n}\n\ninterface Tour {\n\tid: string;\n\tracine: string;\n\tdepots: number;\n\tots: string;\n\tbloc: number | null;\n\thorodatage: string | null;\n\tfichiers?: string[];\n}\n\nconst HEX_ID = /^[0-9a-f]{64}$/;\n\nfunction refused(message: string): SelfstoreError {\n\treturn new SelfstoreError('NOTARY_REFUSED', message);\n}\n\nfunction readDeposit(body: unknown): Deposit {\n\tconst d = body as Partial<Deposit> | null;\n\tif (\n\t\ttypeof d?.id !== 'string' ||\n\t\t!HEX_ID.test(d.id) ||\n\t\ttypeof d.ligne !== 'string' ||\n\t\ttypeof d.recu?.sha256 !== 'string' ||\n\t\ttypeof d.recu.depose !== 'string'\n\t) {\n\t\tthrow refused('the journal answered without a receipt');\n\t}\n\treturn d as Deposit;\n}\n\nfunction parseLine(text: string): { recu?: string; sha256?: string } {\n\ttry {\n\t\treturn JSON.parse(text) as { recu?: string; sha256?: string };\n\t} catch {\n\t\tthrow refused('the journal published an unreadable deposits file');\n\t}\n}\n\n/** 1-based rank of the deposit line whose receipt id is `id`, or 0. */\nfunction rankOf(deposits: string, id: string, hash: string): number {\n\tconst lines = deposits.split('\\n').filter(Boolean);\n\tfor (const [index, text] of lines.entries()) {\n\t\tconst line = parseLine(text);\n\t\tif (line.recu !== id) continue;\n\t\tif (line.sha256 !== hash) throw refused('the journal lists this receipt for another digest');\n\t\treturn index + 1;\n\t}\n\treturn 0;\n}\n\n/**\n * A notary over a public digest journal (the protocol is at the top of this\n * file). The label is sent as the journal's `libelle`: keep it a TYPE, unless\n * the journal's `type` (an option of the notary) says what the label is.\n *\n * On deposit the receipt is incomplete; its evidence holds the journal's\n * address, the receipt `id`, the public `line` and the journal's own `receipt`\n * as JSON - the file its verifier reads. `complete` finds the first tour that\n * covers the line and, once that tour is anchored in Bitcoin, completes the\n * receipt with `tour`, `root`, `block`, the `.ots` proof and, when the journal\n * stamped that root, the `.tsr` token (both base64). Checking the Merkle path\n * from the line to the root is the journal's verifier's job.\n */\nexport function digestJournalNotary(options: JournalOptions): Notary {\n\tconst name = options.name ?? 'digest-journal';\n\tconst base = options.url.replace(/\\/+$/, '');\n\tconst typed = options.type === undefined ? {} : { type: options.type };\n\tconst send = options.fetch ?? globalThis.fetch;\n\tconst get = (path: string): Promise<Response> =>\n\t\treach(send, `${base}${path}`, undefined, 'the journal');\n\tconst bytesOf = async (path: string): Promise<string> =>\n\t\ttoBase64(new Uint8Array(await (await get(path)).arrayBuffer()));\n\n\treturn {\n\t\tname,\n\t\tasync deposit(hash: string, label?: string): Promise<Receipt> {\n\t\t\tconst response = await reach(\n\t\t\t\tsend,\n\t\t\t\t`${base}/api/depots`,\n\t\t\t\t{\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\t\tsha256: hash,\n\t\t\t\t\t\t...(label === undefined ? {} : { libelle: label }),\n\t\t\t\t\t\t...typed\n\t\t\t\t\t})\n\t\t\t\t},\n\t\t\t\t'the journal'\n\t\t\t);\n\t\t\tconst deposit = readDeposit(await response.json().catch(() => null));\n\t\t\treturn {\n\t\t\t\tnotary: name,\n\t\t\t\thash: deposit.recu.sha256,\n\t\t\t\t...(label === undefined ? {} : { label }),\n\t\t\t\tdepositedAt: deposit.recu.depose,\n\t\t\t\tcomplete: false,\n\t\t\t\tevidence: {\n\t\t\t\t\tjournal: base,\n\t\t\t\t\tid: deposit.id,\n\t\t\t\t\tline: deposit.ligne,\n\t\t\t\t\treceipt: JSON.stringify(deposit.recu)\n\t\t\t\t}\n\t\t\t};\n\t\t},\n\t\tasync complete(receipt: Receipt): Promise<Receipt> {\n\t\t\tconst id = receipt.evidence.id;\n\t\t\tif (id === undefined) throw refused('the receipt carries no journal id');\n\t\t\tconst rank = rankOf(await (await get('/empreintes/depots.jsonl')).text(), id, receipt.hash);\n\t\t\tif (rank === 0) return receipt;\n\t\t\tconst { tours } = (await (await get('/tours.json')).json()) as { tours: Tour[] };\n\t\t\tconst tour = tours.find((t) => t.depots >= rank);\n\t\t\tif (tour?.ots !== 'bitcoin') return receipt;\n\t\t\tconst stamped = tour.horodatage !== null && tour.fichiers?.includes(`${tour.id}.racine.tsr`);\n\t\t\treturn {\n\t\t\t\t...receipt,\n\t\t\t\tcomplete: true,\n\t\t\t\tevidence: {\n\t\t\t\t\t...receipt.evidence,\n\t\t\t\t\ttour: tour.id,\n\t\t\t\t\troot: tour.racine,\n\t\t\t\t\t...(tour.bloc === null ? {} : { block: String(tour.bloc) }),\n\t\t\t\t\tots: await bytesOf(`/empreintes/${tour.id}.racine.ots`),\n\t\t\t\t\t...(stamped ? { tsr: await bytesOf(`/empreintes/${tour.id}.racine.tsr`) } : {})\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t};\n}\n"]}
1
+ {"version":3,"sources":["../src/proof/proof.ts","../src/proof/der.ts","../src/proof/reach.ts","../src/proof/rfc3161.ts","../src/proof/journal.ts"],"names":["refused","hex"],"mappings":"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","file":"proof.js","sourcesContent":["// Proof of existence for the user's own documents: the bytes never leave the\n// device, only their SHA-256 does. A notary dates that digest; the receipt it\n// hands back is kept next to the file, in the user's own storage, and anyone\n// holding the file and the receipt can check the two still belong together.\n\nimport { sha256Hex } from '../selfstore/digest';\nimport { isSelfstoreError, SelfstoreError } from '../selfstore/errors';\nimport { toBytes, type FileBytes } from '../simple/bytes';\nimport type { SimpleRecord } from '../simple/simple';\n\n/**\n * What a notary hands back for one digest. `evidence` is the notary's own\n * material (a timestamp token, an inclusion path, a download address), as\n * strings - base64 for bytes - so a receipt is plain JSON and survives every\n * destination a store can write to.\n */\nexport interface Receipt {\n\t/** The notary that issued it (`Notary.name`). */\n\treadonly notary: string;\n\t/** Lowercase hex SHA-256 of the document. */\n\treadonly hash: string;\n\t/** The label sent with the deposit, if any. */\n\treadonly label?: string;\n\t/** ISO 8601 instant the notary vouches for (its clock, not the device's). */\n\treadonly depositedAt: string;\n\t/** False while the notary still has something to add (an anchor, a seal). */\n\treadonly complete: boolean;\n\treadonly evidence: Readonly<Record<string, string>>;\n}\n\n/**\n * Anyone who dates a digest. `deposit` must never see more than the digest and\n * the label; `complete` is called later - the next day, the next launch - and\n * returns the same receipt with whatever the notary could not give at once.\n * A notary that is final on deposit returns its argument unchanged.\n */\nexport interface Notary {\n\treadonly name: string;\n\tdeposit(hash: string, label?: string): Promise<Receipt>;\n\tcomplete(receipt: Receipt): Promise<Receipt>;\n}\n\n/** A receipt as `prove()` keeps it: the notary's, plus what the device measured. */\nexport interface Proof extends Receipt {\n\treadonly kind: 'selfstore-proof';\n\treadonly version: 1;\n\treadonly algorithm: 'SHA-256';\n\t/** Size of the document in bytes. */\n\treadonly size: number;\n\t/** The store file the proof belongs to, when there is one. */\n\treadonly file?: string;\n}\n\n/** Where proofs are kept: the simple store fits as is. */\nexport interface ProofShelf {\n\tput(collection: string, record: SimpleRecord): Promise<void>;\n}\n\n/** The collection `prove()` and `completeProof()` write to. */\nexport const PROOFS = 'proofs';\n\nexport interface ProveOptions {\n\tnotary: Notary;\n\t/** Sent to the notary with the digest. A TYPE ('invoice', 'inventory'),\n\t * never a name or a piece of the content: it leaves the device. */\n\tlabel?: string;\n\t/** Keep the proof in this store, under `PROOFS`, keyed by `file` or by hash. */\n\tstore?: ProofShelf;\n\t/** Id of the store file the document is (as `putFile` returned it). */\n\tfile?: string;\n}\n\n/** The result of `verify()`. */\nexport interface ProofCheck {\n\t/** The bytes hash to the receipt's digest (and size, when it carries one). */\n\treadonly matches: boolean;\n\t/** What the bytes hash to now. */\n\treadonly hash: string;\n\t/** Whether the notary had finished with this receipt. */\n\treadonly complete: boolean;\n}\n\nconst HEX_DIGEST = /^[0-9a-f]{64}$/;\n\nfunction refused(message: string): SelfstoreError {\n\treturn new SelfstoreError('NOTARY_REFUSED', message);\n}\n\n/** A notary that answers for another digest, or for another notary, is not believed. */\nfunction checkAnswer(answer: Receipt, hash: string, notary: Notary): void {\n\tif (typeof answer?.hash !== 'string' || answer.hash.toLowerCase() !== hash) {\n\t\tthrow refused(`${notary.name} answered for another digest`);\n\t}\n\tif (answer.notary !== notary.name) {\n\t\tthrow refused(`receipt names ${String(answer.notary)}, not ${notary.name}`);\n\t}\n\tif (Number.isNaN(Date.parse(answer.depositedAt))) {\n\t\tthrow refused(`${notary.name} gave no readable deposit time`);\n\t}\n}\n\nasync function shelve(proof: Proof, store: ProofShelf | undefined): Promise<void> {\n\tif (store) await store.put(PROOFS, { ...proof, id: proof.file ?? proof.hash });\n}\n\n/**\n * Hash `blob` on the device, deposit the digest with `options.notary`, and\n * return the proof - kept in `options.store` too when one is given. Only the\n * digest and the label leave the device.\n *\n * Throws NOTARY_UNAVAILABLE when the notary cannot be reached (retry later)\n * and NOTARY_REFUSED when it refuses or answers for something else.\n */\nexport async function prove(blob: FileBytes, options: ProveOptions): Promise<Proof> {\n\tconst bytes = await toBytes(blob);\n\tconst hash = await sha256Hex(bytes);\n\tconst answer = await options.notary.deposit(hash, options.label);\n\tcheckAnswer(answer, hash, options.notary);\n\tconst proof: Proof = {\n\t\t...answer,\n\t\thash,\n\t\tkind: 'selfstore-proof',\n\t\tversion: 1,\n\t\talgorithm: 'SHA-256',\n\t\tsize: bytes.length,\n\t\t...(options.file === undefined ? {} : { file: options.file })\n\t};\n\tawait shelve(proof, options.store);\n\treturn proof;\n}\n\n/**\n * Ask the notary for what it could not give on deposit. A complete proof is\n * returned as is, without a call. The fields the device measured are kept;\n * the notary may only add to its own.\n */\nexport async function completeProof(\n\tproof: Proof,\n\toptions: { notary: Notary; store?: ProofShelf }\n): Promise<Proof> {\n\tif (proof.complete) return proof;\n\tconst answer = await options.notary.complete(proof);\n\tcheckAnswer(answer, proof.hash, options.notary);\n\tconst completed: Proof = {\n\t\t...answer,\n\t\thash: proof.hash,\n\t\tkind: proof.kind,\n\t\tversion: proof.version,\n\t\talgorithm: proof.algorithm,\n\t\tsize: proof.size,\n\t\t...(proof.file === undefined ? {} : { file: proof.file })\n\t};\n\tawait shelve(completed, options.store);\n\treturn completed;\n}\n\n/**\n * Hash `blob` again and compare it with the receipt. This proves the bytes are\n * the ones the receipt names; that the receipt itself is genuine is what the\n * notary's evidence is for, checked with the notary's own verifier.\n *\n * Throws BAD_FORMAT on a receipt that carries no SHA-256 digest at all - a\n * damaged receipt is not a document that changed.\n */\nexport async function verify(\n\tblob: FileBytes,\n\treceipt: Pick<Receipt, 'hash' | 'complete'> & { size?: number }\n): Promise<ProofCheck> {\n\tconst expected = typeof receipt?.hash === 'string' ? receipt.hash.toLowerCase() : '';\n\tif (!HEX_DIGEST.test(expected)) {\n\t\tthrow new SelfstoreError('BAD_FORMAT', 'the receipt carries no SHA-256 digest');\n\t}\n\tconst bytes = await toBytes(blob);\n\tconst hash = await sha256Hex(bytes);\n\tconst sizeAgrees = receipt.size === undefined || receipt.size === bytes.length;\n\treturn { matches: hash === expected && sizeAgrees, hash, complete: receipt.complete === true };\n}\n\n/** Where proofs are read back from: the simple store fits as is. */\nexport interface ProofLibrary extends ProofShelf {\n\tall(collection: string): readonly SimpleRecord[];\n}\n\nfunction isProof(record: SimpleRecord): boolean {\n\treturn (\n\t\trecord.kind === 'selfstore-proof' &&\n\t\ttypeof record.hash === 'string' &&\n\t\tHEX_DIGEST.test(record.hash) &&\n\t\ttypeof record.notary === 'string' &&\n\t\ttypeof record.complete === 'boolean'\n\t);\n}\n\n/**\n * Every proof kept in `store`, oldest deposit first. Records that are not\n * proofs - another app's, a damaged one - are skipped rather than trusted.\n */\nexport function proofsIn(store: Pick<ProofLibrary, 'all'>): Proof[] {\n\treturn store\n\t\t.all(PROOFS)\n\t\t.filter(isProof)\n\t\t.map((record) => {\n\t\t\tconst { id: _id, ...proof } = record;\n\t\t\treturn proof as unknown as Proof;\n\t\t})\n\t\t.sort((a, b) => Date.parse(a.depositedAt) - Date.parse(b.depositedAt));\n}\n\n/** What `completePending()` did, proof by proof. */\nexport interface PendingRound {\n\t/** Finished this round, and written back to the store. */\n\treadonly completed: readonly Proof[];\n\t/** Still waiting: the notary has nothing to add yet, could not be reached,\n\t * or is not among the notaries given. Ask again on a later launch. */\n\treadonly pending: readonly Proof[];\n\t/** The notary answered for something else; kept as it was, not overwritten. */\n\treadonly refused: readonly Proof[];\n}\n\nfunction isCode(error: unknown, code: string): boolean {\n\treturn isSelfstoreError(error) && error.code === code;\n}\n\n/**\n * Ask each notary to finish the incomplete proofs it issued - the call an app\n * makes at launch, since a notary that anchors its digests finishes a receipt\n * hours after the deposit. A notary that cannot be reached is not asked again\n * this round; a proof whose notary is not in `notaries` waits. Only a proof\n * that came back complete is written to the store.\n */\nexport async function completePending(options: {\n\tstore: ProofLibrary;\n\tnotaries: readonly Notary[];\n}): Promise<PendingRound> {\n\tconst byName = new Map(options.notaries.map((notary) => [notary.name, notary]));\n\tconst unreachable = new Set<string>();\n\tconst completed: Proof[] = [];\n\tconst pending: Proof[] = [];\n\tconst refused: Proof[] = [];\n\tfor (const proof of proofsIn(options.store)) {\n\t\tif (proof.complete) continue;\n\t\tconst notary = byName.get(proof.notary);\n\t\tif (notary === undefined || unreachable.has(notary.name)) {\n\t\t\tpending.push(proof);\n\t\t\tcontinue;\n\t\t}\n\t\ttry {\n\t\t\tconst answer = await completeProof(proof, { notary });\n\t\t\tif (answer.complete) {\n\t\t\t\tawait shelve(answer, options.store);\n\t\t\t\tcompleted.push(answer);\n\t\t\t} else {\n\t\t\t\tpending.push(proof);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (isCode(error, 'NOTARY_UNAVAILABLE')) {\n\t\t\t\tunreachable.add(notary.name);\n\t\t\t\tpending.push(proof);\n\t\t\t} else if (isCode(error, 'NOTARY_REFUSED')) {\n\t\t\t\trefused.push(proof);\n\t\t\t} else {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t}\n\treturn { completed, pending, refused };\n}\n","// The few DER shapes an RFC 3161 exchange needs, and no more. A timestamp\n// request is five fields and the reply is read along one path, so a general\n// ASN.1 library would be a fourth runtime dependency for two functions.\n\n/** One decoded TLV: `start` and `end` bound its CONTENT. */\nexport interface DerNode {\n\treadonly tag: number;\n\treadonly start: number;\n\treadonly end: number;\n}\n\nexport class DerError extends Error {}\n\nfunction encodeLength(length: number): Uint8Array {\n\tif (length < 0x80) return Uint8Array.of(length);\n\tconst bytes: number[] = [];\n\tfor (let rest = length; rest > 0; rest = Math.floor(rest / 256)) bytes.unshift(rest % 256);\n\treturn Uint8Array.of(0x80 | bytes.length, ...bytes);\n}\n\n/** Encode one TLV around already-encoded content. */\nexport function tlv(tag: number, ...parts: Uint8Array[]): Uint8Array {\n\tconst length = parts.reduce((sum, part) => sum + part.length, 0);\n\tconst head = encodeLength(length);\n\tconst out = new Uint8Array(1 + head.length + length);\n\tout[0] = tag;\n\tout.set(head, 1);\n\tlet at = 1 + head.length;\n\tfor (const part of parts) {\n\t\tout.set(part, at);\n\t\tat += part.length;\n\t}\n\treturn out;\n}\n\n/** Read the TLV that starts at `offset`. Indefinite lengths are BER, not DER: refused. */\nexport function readNode(bytes: Uint8Array, offset: number, limit = bytes.length): DerNode {\n\tif (offset + 2 > limit) throw new DerError('truncated header');\n\tconst tag = bytes[offset];\n\tconst first = bytes[offset + 1];\n\tlet start = offset + 2;\n\tlet length = first;\n\tif (first & 0x80) {\n\t\tconst count = first & 0x7f;\n\t\tif (count === 0 || count > 4) throw new DerError('unsupported length');\n\t\tif (start + count > limit) throw new DerError('truncated length');\n\t\tlength = 0;\n\t\tfor (let i = 0; i < count; i++) length = length * 256 + bytes[start + i];\n\t\tstart += count;\n\t}\n\tconst end = start + length;\n\tif (end > limit) throw new DerError('content runs past its parent');\n\treturn { tag, start, end };\n}\n\n/** The direct children of a constructed node. */\nexport function childrenOf(bytes: Uint8Array, node: DerNode): DerNode[] {\n\tconst out: DerNode[] = [];\n\tfor (let at = node.start; at < node.end;) {\n\t\tconst child = readNode(bytes, at, node.end);\n\t\tout.push(child);\n\t\tat = child.end;\n\t}\n\treturn out;\n}\n\n/** The node's content, or a refusal when its tag is not the one expected. */\nexport function expectTag(node: DerNode | undefined, tag: number): DerNode {\n\tif (node?.tag !== tag) throw new DerError(`expected tag 0x${tag.toString(16)}`);\n\treturn node;\n}\n\nexport function contentOf(bytes: Uint8Array, node: DerNode): Uint8Array {\n\treturn bytes.subarray(node.start, node.end);\n}\n","// One way to call a notary over HTTP, so every adapter maps the same failures\n// to the same codes: unreachable, overloaded or rate-limited is worth a retry,\n// any other refusal is not.\n\nimport { SelfstoreError } from '../selfstore/errors';\n\n/** Fetch `url`, throwing NOTARY_UNAVAILABLE or NOTARY_REFUSED instead of returning a failure. */\nexport async function reach(\n\tsend: typeof fetch,\n\turl: string,\n\tinit: RequestInit | undefined,\n\twho: string\n): Promise<Response> {\n\tlet response: Response;\n\ttry {\n\t\tresponse = await send(url, init);\n\t} catch (e) {\n\t\tthrow new SelfstoreError(\n\t\t\t'NOTARY_UNAVAILABLE',\n\t\t\t`${who} could not be reached: ${(e as Error).message}`\n\t\t);\n\t}\n\tif (!response.ok) {\n\t\tconst code =\n\t\t\tresponse.status >= 500 || response.status === 429 ? 'NOTARY_UNAVAILABLE' : 'NOTARY_REFUSED';\n\t\tthrow new SelfstoreError(code, `${who} answered ${response.status}`);\n\t}\n\treturn response;\n}\n","// A notary over any RFC 3161 time-stamping authority. The authority signs the\n// digest and the time in one token, so the receipt is complete on deposit: the\n// token (the `.tsr` file) is the evidence, and `openssl ts -verify` checks it\n// against the authority's certificate without this library.\n\nimport { fromBase64, toBase64 } from '../selfstore/base64';\nimport { hex } from '../selfstore/digest';\nimport { SelfstoreError } from '../selfstore/errors';\nimport { childrenOf, contentOf, DerError, expectTag, readNode, tlv, type DerNode } from './der';\nimport type { Notary, Receipt } from './proof';\nimport { reach } from './reach';\n\nconst SEQUENCE = 0x30;\nconst INTEGER = 0x02;\nconst OCTET_STRING = 0x04;\nconst OID = 0x06;\nconst GENERALIZED_TIME = 0x18;\nconst EXPLICIT_0 = 0xa0;\n\n/** id-sha256, 2.16.840.1.101.3.4.2.1, with its NULL parameters. */\nconst SHA256_ALGORITHM = tlv(\n\tSEQUENCE,\n\ttlv(OID, Uint8Array.of(0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01)),\n\tUint8Array.of(0x05, 0x00)\n);\n\nexport interface Rfc3161Options {\n\t/** The authority's endpoint. In a browser it must answer CORS, or sit\n\t * behind a relay of yours that does: most public authorities do not. */\n\turl: string;\n\t/** Receipt `notary` field. Defaults to 'rfc3161'. */\n\tname?: string;\n\t/** Injected for tests and relays; defaults to the global fetch. */\n\tfetch?: typeof fetch;\n}\n\n/** What a timestamp token says, read without checking its signature. */\nexport interface TimestampToken {\n\t/** Lowercase hex of the digest the authority stamped. */\n\treadonly hash: string;\n\t/** ISO 8601 instant the authority signed. */\n\treadonly time: string;\n\t/** The nonce echoed back, as lowercase hex, when there is one. */\n\treadonly nonce?: string;\n}\n\nfunction hexToBytes(hex: string): Uint8Array {\n\treturn Uint8Array.from(hex.match(/../g) ?? [], (pair) => Number.parseInt(pair, 16));\n}\n\n/** Leading zero bytes are DER's sign padding, not part of the value. */\nfunction integerHex(bytes: Uint8Array): string {\n\tlet at = 0;\n\twhile (at < bytes.length - 1 && bytes[at] === 0) at++;\n\treturn hex(bytes.subarray(at));\n}\n\n/** Eight random bytes, positive and minimal as a DER INTEGER. */\nfunction newNonce(): Uint8Array {\n\tconst nonce = crypto.getRandomValues(new Uint8Array(8));\n\tnonce[0] = (nonce[0] & 0x3f) | 0x40;\n\treturn nonce;\n}\n\n/** A TimeStampReq (RFC 3161 section 2.4.1) for a SHA-256 digest, asking for the certificate. */\nexport function timestampRequest(hash: string, nonce: Uint8Array): Uint8Array {\n\treturn tlv(\n\t\tSEQUENCE,\n\t\ttlv(INTEGER, Uint8Array.of(1)),\n\t\ttlv(SEQUENCE, SHA256_ALGORITHM, tlv(OCTET_STRING, hexToBytes(hash))),\n\t\ttlv(INTEGER, nonce),\n\t\tUint8Array.of(0x01, 0x01, 0xff)\n\t);\n}\n\n/** YYYYMMDDHHMMSS[.fff]Z to ISO 8601. */\nfunction generalizedTime(text: string): string {\n\tconst parts = /^(\\d{4})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\.\\d+)?Z$/.exec(text);\n\tif (parts === null) throw new DerError(`unreadable time ${text}`);\n\tconst [, y, mo, d, h, mi, s, fraction] = parts;\n\treturn new Date(`${y}-${mo}-${d}T${h}:${mi}:${s}${fraction ?? ''}Z`).toISOString();\n}\n\n/** TimeStampResp: the status, or the token when it was granted. */\nfunction tokenOf(bytes: Uint8Array): DerNode {\n\tconst [statusInfo, token] = childrenOf(bytes, expectTag(readNode(bytes, 0), SEQUENCE));\n\tconst status = expectTag(childrenOf(bytes, expectTag(statusInfo, SEQUENCE))[0], INTEGER);\n\tconst code = Number.parseInt(integerHex(contentOf(bytes, status)), 16);\n\t// 0 granted, 1 granted with modifications; anything else is a refusal.\n\tif (code > 1) {\n\t\tthrow new SelfstoreError(\n\t\t\t'NOTARY_REFUSED',\n\t\t\t`the authority refused the request (status ${code})`\n\t\t);\n\t}\n\treturn expectTag(token, SEQUENCE);\n}\n\n/** ContentInfo > SignedData > EncapsulatedContentInfo > TSTInfo. */\nfunction tstInfoOf(bytes: Uint8Array, token: DerNode): DerNode {\n\tconst signedData = expectTag(\n\t\tchildrenOf(bytes, expectTag(childrenOf(bytes, token)[1], EXPLICIT_0))[0],\n\t\tSEQUENCE\n\t);\n\tconst encapsulated = expectTag(childrenOf(bytes, signedData)[2], SEQUENCE);\n\tconst wrapper = expectTag(childrenOf(bytes, encapsulated)[1], EXPLICIT_0);\n\tconst octets = expectTag(childrenOf(bytes, wrapper)[0], OCTET_STRING);\n\treturn expectTag(readNode(bytes, octets.start, octets.end), SEQUENCE);\n}\n\n/**\n * Read the digest, the time and the nonce out of a TimeStampResp (the `.tsr`\n * file). It does NOT check the authority's signature - that is what\n * `openssl ts -verify` is for; it is what tells a token for another document\n * apart before anything is kept. Throws NOTARY_REFUSED on a refusal or on\n * anything that is not a timestamp reply.\n */\nexport function readTimestampReply(bytes: Uint8Array): TimestampToken {\n\ttry {\n\t\tconst tstInfo = tstInfoOf(bytes, tokenOf(bytes));\n\t\t// version, policy, messageImprint, serialNumber, genTime, then optional\n\t\t// accuracy, ordering and nonce - the nonce is the only INTEGER among them.\n\t\tconst [, , imprint, , time, ...rest] = childrenOf(bytes, tstInfo);\n\t\tconst digest = expectTag(childrenOf(bytes, expectTag(imprint, SEQUENCE))[1], OCTET_STRING);\n\t\tconst nonce = rest.find((node) => node.tag === INTEGER);\n\t\tconst genTime = new TextDecoder().decode(contentOf(bytes, expectTag(time, GENERALIZED_TIME)));\n\t\treturn {\n\t\t\thash: hex(contentOf(bytes, digest)),\n\t\t\ttime: generalizedTime(genTime),\n\t\t\t...(nonce ? { nonce: integerHex(contentOf(bytes, nonce)) } : {})\n\t\t};\n\t} catch (e) {\n\t\tif (e instanceof SelfstoreError) throw e;\n\t\tthrow new SelfstoreError('NOTARY_REFUSED', `not a timestamp reply: ${(e as Error).message}`);\n\t}\n}\n\nasync function post(options: Rfc3161Options, body: Uint8Array): Promise<Uint8Array> {\n\tconst response = await reach(\n\t\toptions.fetch ?? globalThis.fetch,\n\t\toptions.url,\n\t\t{\n\t\t\tmethod: 'POST',\n\t\t\theaders: { 'content-type': 'application/timestamp-query' },\n\t\t\tbody: body as BodyInit\n\t\t},\n\t\t'the authority'\n\t);\n\treturn new Uint8Array(await response.arrayBuffer());\n}\n\n/**\n * A notary over an RFC 3161 time-stamping authority. The receipt is complete\n * on deposit; its evidence is the reply, base64 under `tsr`, and the\n * authority's address under `tsa`. The label is never sent: RFC 3161 has no\n * field for it, so it stays on the receipt.\n */\nexport function rfc3161Notary(options: Rfc3161Options): Notary {\n\tconst name = options.name ?? 'rfc3161';\n\treturn {\n\t\tname,\n\t\tasync deposit(hash: string, label?: string): Promise<Receipt> {\n\t\t\tconst nonce = newNonce();\n\t\t\tconst reply = await post(options, timestampRequest(hash, nonce));\n\t\t\tconst token = readTimestampReply(reply);\n\t\t\tif (token.hash !== hash) {\n\t\t\t\tthrow new SelfstoreError('NOTARY_REFUSED', 'the authority stamped another digest');\n\t\t\t}\n\t\t\tif (token.nonce !== integerHex(nonce)) {\n\t\t\t\tthrow new SelfstoreError('NOTARY_REFUSED', 'the reply does not answer this request');\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tnotary: name,\n\t\t\t\thash,\n\t\t\t\t...(label === undefined ? {} : { label }),\n\t\t\t\tdepositedAt: token.time,\n\t\t\t\tcomplete: true,\n\t\t\t\tevidence: { tsr: toBase64(reply), tsa: options.url }\n\t\t\t};\n\t\t},\n\t\tcomplete(receipt: Receipt): Promise<Receipt> {\n\t\t\treturn Promise.resolve(receipt);\n\t\t}\n\t};\n}\n\n/** The `.tsr` bytes of an RFC 3161 receipt, to offer as a download. */\nexport function timestampReplyOf(receipt: Pick<Receipt, 'evidence'>): Uint8Array | undefined {\n\tconst tsr = receipt.evidence.tsr;\n\treturn tsr === undefined ? undefined : fromBase64(tsr);\n}\n","// A notary over a public digest journal: a service that takes a SHA-256 with\n// no account, writes it as one line of a public deposits file, and folds that\n// file into a Merkle tree it anchors in Bitcoin (OpenTimestamps) and often\n// stamps under RFC 3161 too. The deposit answers at once with the journal's\n// own receipt; the anchor comes hours later, so `complete` is what fetches it.\n//\n// The protocol, relative to the journal's base URL:\n// POST /api/depots {\"sha256\", \"libelle\"?, \"type\"?} -> 201\n// {\"id\", \"jour\", \"place\", \"ligne\", \"recu\": {\"sha256\", \"depose\", ...}}\n// GET /empreintes/depots.jsonl one JSON line per deposit, in order, with \"recu\" = id\n// GET /tours.json {\"tours\": [{\"id\", \"instant\"?, \"racine\", \"depots\", \"ots\",\n// \"bloc\", \"horodatage\", \"fichiers\"}]}; a tour covers the\n// first `depots` lines of the deposits file as it\n// stood at its `instant`\n// GET /empreintes/<tour>.racine.ots, /empreintes/<tour>.racine.tsr\n\nimport { toBase64 } from '../selfstore/base64';\nimport { SelfstoreError } from '../selfstore/errors';\nimport type { Notary, Receipt } from './proof';\nimport { reach } from './reach';\n\nexport interface JournalOptions {\n\t/** The journal's base URL, e.g. 'https://journal.example'. */\n\turl: string;\n\t/** Receipt `notary` field. Defaults to 'digest-journal'. */\n\tname?: string;\n\t/**\n\t * The journal's deposit type, sent as `type` with every deposit (a journal\n\t * that issues typed receipts, such as a permit display, keys them on it).\n\t * None by default: the deposit is the journal's ordinary one.\n\t */\n\ttype?: string;\n\t/** Injected for tests and relays; defaults to the global fetch. */\n\tfetch?: typeof fetch;\n}\n\ninterface Deposit {\n\tid: string;\n\tligne: string;\n\trecu: { sha256: string; depose: string };\n}\n\ninterface Tour {\n\tid: string;\n\tinstant?: string;\n\tracine: string;\n\tdepots: number;\n\tots: string;\n\tbloc: number | null;\n\thorodatage: string | null;\n\tfichiers?: string[];\n}\n\nconst HEX_ID = /^[0-9a-f]{64}$/;\n\nfunction refused(message: string): SelfstoreError {\n\treturn new SelfstoreError('NOTARY_REFUSED', message);\n}\n\nfunction readDeposit(body: unknown): Deposit {\n\tconst d = body as Partial<Deposit> | null;\n\tif (\n\t\ttypeof d?.id !== 'string' ||\n\t\t!HEX_ID.test(d.id) ||\n\t\ttypeof d.ligne !== 'string' ||\n\t\ttypeof d.recu?.sha256 !== 'string' ||\n\t\ttypeof d.recu.depose !== 'string'\n\t) {\n\t\tthrow refused('the journal answered without a receipt');\n\t}\n\treturn d as Deposit;\n}\n\nfunction parseLine(text: string): { recu?: string; sha256?: string } {\n\ttry {\n\t\treturn JSON.parse(text) as { recu?: string; sha256?: string };\n\t} catch {\n\t\tthrow refused('the journal published an unreadable deposits file');\n\t}\n}\n\n/**\n * Whether a tour can cover a line deposited at `depositedAt`: a tour folded the\n * file before the deposit cannot, whatever its size. A journal that publishes\n * no `instant` leaves the question open.\n */\nfunction mayCover(tour: Tour, depositedAt: string): boolean {\n\tif (tour.instant === undefined) return true;\n\tconst folded = Date.parse(tour.instant);\n\tconst deposited = Date.parse(depositedAt);\n\treturn Number.isNaN(folded) || Number.isNaN(deposited) || folded > deposited;\n}\n\n/**\n * Whether reading the deposits file can complete the receipt: only once a tour\n * folded after the deposit is anchored in Bitcoin. Until then the first tour\n * covering the line is either absent or not anchored, and the receipt stays\n * as it is without the file (which grows with every deposit of every user).\n */\nfunction worthReading(tours: readonly Tour[], depositedAt: string): boolean {\n\treturn tours.some((t) => t.depots > 0 && t.ots === 'bitcoin' && mayCover(t, depositedAt));\n}\n\n/** 1-based rank of the deposit line whose receipt id is `id`, or 0. */\nfunction rankOf(deposits: string, id: string, hash: string): number {\n\tconst lines = deposits.split('\\n').filter(Boolean);\n\tfor (const [index, text] of lines.entries()) {\n\t\tconst line = parseLine(text);\n\t\tif (line.recu !== id) continue;\n\t\tif (line.sha256 !== hash) throw refused('the journal lists this receipt for another digest');\n\t\treturn index + 1;\n\t}\n\treturn 0;\n}\n\n/**\n * A notary over a public digest journal (the protocol is at the top of this\n * file). The label is sent as the journal's `libelle`: keep it a TYPE, unless\n * the journal's `type` (an option of the notary) says what the label is.\n *\n * On deposit the receipt is incomplete; its evidence holds the journal's\n * address, the receipt `id`, the public `line` and the journal's own `receipt`\n * as JSON - the file its verifier reads. `complete` finds the first tour that\n * covers the line and, once that tour is anchored in Bitcoin, completes the\n * receipt with `tour`, `root`, `block`, the `.ots` proof and, when the journal\n * stamped that root, the `.tsr` token (both base64). Checking the Merkle path\n * from the line to the root is the journal's verifier's job.\n *\n * `complete` reads the tour list first and the deposits file only when a tour\n * folded after the deposit is anchored: the file lists every deposit of every\n * user and grows without end, the tour list stays small.\n */\nexport function digestJournalNotary(options: JournalOptions): Notary {\n\tconst name = options.name ?? 'digest-journal';\n\tconst base = options.url.replace(/\\/+$/, '');\n\tconst typed = options.type === undefined ? {} : { type: options.type };\n\tconst send = options.fetch ?? globalThis.fetch;\n\tconst get = (path: string): Promise<Response> =>\n\t\treach(send, `${base}${path}`, undefined, 'the journal');\n\tconst bytesOf = async (path: string): Promise<string> =>\n\t\ttoBase64(new Uint8Array(await (await get(path)).arrayBuffer()));\n\t// The deposits file, read once per state of the tour list: a line covered by\n\t// a known tour was already in the copy read after that tour was listed, so a\n\t// round that completes many receipts reads the file once, not once each.\n\tlet deposits: { key: string; text: string } | undefined;\n\tconst depositsFile = async (tours: readonly Tour[]): Promise<string> => {\n\t\tconst last = tours.at(-1);\n\t\tconst key = last === undefined ? '' : `${last.id}:${last.depots}`;\n\t\tif (deposits?.key !== key) {\n\t\t\tdeposits = { key, text: await (await get('/empreintes/depots.jsonl')).text() };\n\t\t}\n\t\treturn deposits.text;\n\t};\n\n\treturn {\n\t\tname,\n\t\tasync deposit(hash: string, label?: string): Promise<Receipt> {\n\t\t\tconst response = await reach(\n\t\t\t\tsend,\n\t\t\t\t`${base}/api/depots`,\n\t\t\t\t{\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\t\tsha256: hash,\n\t\t\t\t\t\t...(label === undefined ? {} : { libelle: label }),\n\t\t\t\t\t\t...typed\n\t\t\t\t\t})\n\t\t\t\t},\n\t\t\t\t'the journal'\n\t\t\t);\n\t\t\tconst deposit = readDeposit(await response.json().catch(() => null));\n\t\t\treturn {\n\t\t\t\tnotary: name,\n\t\t\t\thash: deposit.recu.sha256,\n\t\t\t\t...(label === undefined ? {} : { label }),\n\t\t\t\tdepositedAt: deposit.recu.depose,\n\t\t\t\tcomplete: false,\n\t\t\t\tevidence: {\n\t\t\t\t\tjournal: base,\n\t\t\t\t\tid: deposit.id,\n\t\t\t\t\tline: deposit.ligne,\n\t\t\t\t\treceipt: JSON.stringify(deposit.recu)\n\t\t\t\t}\n\t\t\t};\n\t\t},\n\t\tasync complete(receipt: Receipt): Promise<Receipt> {\n\t\t\tconst id = receipt.evidence.id;\n\t\t\tif (id === undefined) throw refused('the receipt carries no journal id');\n\t\t\tconst { tours } = (await (await get('/tours.json')).json()) as { tours: Tour[] };\n\t\t\tif (!worthReading(tours, receipt.depositedAt)) return receipt;\n\t\t\tconst rank = rankOf(await depositsFile(tours), id, receipt.hash);\n\t\t\tif (rank === 0) return receipt;\n\t\t\tconst tour = tours.find((t) => t.depots >= rank);\n\t\t\tif (tour?.ots !== 'bitcoin') return receipt;\n\t\t\tconst stamped = tour.horodatage !== null && tour.fichiers?.includes(`${tour.id}.racine.tsr`);\n\t\t\treturn {\n\t\t\t\t...receipt,\n\t\t\t\tcomplete: true,\n\t\t\t\tevidence: {\n\t\t\t\t\t...receipt.evidence,\n\t\t\t\t\ttour: tour.id,\n\t\t\t\t\troot: tour.racine,\n\t\t\t\t\t...(tour.bloc === null ? {} : { block: String(tour.bloc) }),\n\t\t\t\t\tots: await bytesOf(`/empreintes/${tour.id}.racine.ots`),\n\t\t\t\t\t...(stamped ? { tsr: await bytesOf(`/empreintes/${tour.id}.racine.tsr`) } : {})\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t};\n}\n"]}
package/dist/sync.js CHANGED
@@ -1,4 +1,4 @@
1
- export { changes, createMeta, createNode, detectConflicts, merge, stamp } from './chunk-GSZSZGSD.js';
1
+ export { changes, createMeta, createNode, detectConflicts, merge, stamp } from './chunk-A2RRJLIF.js';
2
2
  import './chunk-FJGWTBAL.js';
3
3
  import './chunk-CSYEIXNY.js';
4
4
  //# sourceMappingURL=sync.js.map
package/dist/widgets.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { saveToDisk, datedName } from './chunk-Q3NO477P.js';
2
- import { connectFlow, shareFlow, joinFlow } from './chunk-HRYIQSTH.js';
2
+ import { connectFlow, shareFlow, joinFlow } from './chunk-CI5JYSWE.js';
3
3
  import './chunk-JDDWWQEB.js';
4
4
  import { isOpenSupported } from './chunk-HVHQLPP6.js';
5
5
  import './chunk-2BNTCXQN.js';
6
- import './chunk-25U6AKIX.js';
6
+ import './chunk-IUVOVVEA.js';
7
7
  import './chunk-LF3CC2P3.js';
8
8
  import './chunk-7U5XMKVP.js';
9
9
  import './chunk-F4ZZEJ5H.js';