selfstore 1.0.1 → 1.2.0

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.
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/persistence/targets/drive.ts","../src/persistence/targets/file.ts","../src/persistence/targets/webdav.ts","../src/persistence/targets/sigv4.ts","../src/persistence/targets/s3.ts"],"names":["token","connect","fromSession","DATA_DEADLINE_MS","META_DEADLINE_MS","CONFIG_KEY","KEY_KEY","configKey","saveConfig","loadConfig","labelOf","versionOf","fromConfig"],"mappings":"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","file":"chunk-P34WACTD.js","sourcesContent":["/**\n * Google Drive backup target. Does the Drive REST file operations (find-or-create,\n * upload, download) for a single backup file. It only ships bytes; the store\n * decides whether they are encrypted. How an access token is obtained (a browser\n * OAuth flow, a backend broker, ...) is injected via DriveAuth, so this target\n * stays free of any specific auth strategy or backend.\n *\n * Beyond the standard connect/fromSession lifecycle, this module exposes\n * shared-file primitives (preview, adopt, findOrCreateOwnFile) for apps whose\n * users point several devices - or several people - at one Drive-hosted backup\n * file: preview it read-only, then adopt it as this device's backup.\n */\n\nimport type { BackupTarget } from '../target';\nimport type { KV } from '../cache';\nimport { AuthExpiredError, SelfstoreError, isAuthExpired } from '../../selfstore';\n\n/** KV key holding the connected backup's Drive file id. Public because app-level\n * flows that operate on the same file (e.g. sharing it) need to read it. */\nexport const FILE_ID_KEY = 'driveFileId';\nconst DRIVE_UPLOAD = 'https://www.googleapis.com/upload/drive/v3/files';\nconst DRIVE_FILES = 'https://www.googleapis.com/drive/v3/files';\n\n/** A hung request must not spin forever behind a dead connection - the very\n * \"long moment trying to connect\" a stuck backup shows. A deadline turns the\n * hang into an ordinary error (transient: the store retries), never a gate.\n * Data transfers get more room than metadata pokes. AbortSignal.timeout is\n * minted per fetch (a fired signal cannot be reused across driveFetch's retry). */\nconst DATA_DEADLINE_MS = 30_000;\nconst META_DEADLINE_MS = 15_000;\n\n/** How the target gets access. Injected, so the auth strategy lives in the app.\n * Error protocol: a genuine loss of access (the session is gone, re-consent\n * needed) must throw AuthExpiredError - or any error with code 'AUTH_EXPIRED' -\n * while a transient offline/cold-start failure throws a plain error, so the\n * store retries it instead of raising the reconnect gate. */\nexport interface DriveAuth {\n\t/** A fresh access token, or throw if access must be re-established. Pass\n\t * `{ forceRefresh: true }` to bypass any cached token and mint a new one:\n\t * the target calls this after a 401 to tell a merely STALE token (a refresh\n\t * fixes it, no gate) from a genuinely lost session (the refresh 401s too).\n\t * An implementation that cannot force a refresh may ignore the flag; the\n\t * target then retries with the same token, no worse than before. */\n\ttoken(opts?: { forceRefresh?: boolean }): Promise<string>;\n\t/** Re-establish access with a user gesture; resolves to whether it worked. */\n\treconnect(): Promise<boolean>;\n\t/** Forget the connection, locally and server-side. */\n\tforget(): Promise<void>;\n}\n\n/** Run a Drive request; if Google answers 401 the access token may just be\n * STALE (rejected before its nominal expiry - rotation, an early revoke), so\n * force a fresh token and try once more. Only a SECOND 401 means the session\n * is genuinely gone. This is what stops a stale-token blip - common when a\n * flow makes many Drive calls, e.g. group-mode republishing every converge -\n * from raising the blocking reconnect gate over a still-valid connection. */\nasync function driveFetch(\n\tauth: DriveAuth,\n\trun: (token: string) => Promise<Response>\n): Promise<Response> {\n\tconst r = await run(await auth.token());\n\tif (r.status !== 401) return r;\n\treturn run(await auth.token({ forceRefresh: true }));\n}\n\nexport interface DriveOptions {\n\tauth: DriveAuth;\n\tkv: KV;\n\t/** Name of the backup file created in the user's Drive. */\n\tfileName: string;\n}\n\nasync function findExistingFile(token: string, fileNames: string[]): Promise<string | null> {\n\tfor (const name of fileNames) {\n\t\t// 'me' in owners: a shared backup carries the same name but lives on someone\n\t\t// else's Drive - only an OWNED file may be (re)connected as the personal\n\t\t// backup, or a fresh connect could silently re-adopt theirs.\n\t\t// Escape the name for Drive's query grammar (backslash then single-quote):\n\t\t// a name carrying a quote must not break out of the `name='...'` clause and\n\t\t// alter the filter (encodeURIComponent handles transport, not query syntax).\n\t\tconst safe = name.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\");\n\t\tconst q = encodeURIComponent(`name='${safe}' and trashed=false and 'me' in owners`);\n\t\tconst r = await fetch(`${DRIVE_FILES}?q=${q}&fields=files(id)&pageSize=1`, {\n\t\t\theaders: { Authorization: `Bearer ${token}` },\n\t\t\tsignal: AbortSignal.timeout(META_DEADLINE_MS)\n\t\t});\n\t\tif (!r.ok) continue;\n\t\tconst data: { files?: { id: string }[] } = await r.json();\n\t\tconst id = data.files?.[0]?.id;\n\t\tif (id) return id;\n\t}\n\treturn null;\n}\n\n/** Classify a non-OK Drive write we could not otherwise handle (the 401 case is\n * the caller's). A 404 - the bound file is gone (deleted, trashed, id no longer\n * valid) - or a 403 that is not a rate limit (permission withdrawn, storage\n * full) is PERMANENT: retrying writes the same doomed request forever (the\n * silent \"momentarily unreachable\" trap), so the store must raise its gate and\n * let the user reconnect to a fresh file or free space. A 403 rate limit and\n * everything else (5xx, network) stay transient. The body carries Drive's\n * `reason` (e.g. userRateLimitExceeded vs storageQuotaExceeded); absent, a bare\n * 403 is treated as permanent (the safer default - a real rate limit names\n * itself). */\nfunction isPermanentDriveWrite(status: number, body: string): boolean {\n\tif (status === 404) return true;\n\tif (status === 403) return !/ratelimit|dailylimit/i.test(body);\n\treturn false;\n}\n\nasync function createFile(token: string, fileName: string): Promise<string> {\n\tconst boundary = 'SLFS_BOUNDARY';\n\tconst meta = JSON.stringify({ name: fileName, mimeType: 'application/octet-stream' });\n\tconst body = [\n\t\t`--${boundary}\\r\\nContent-Type: application/json; charset=UTF-8\\r\\n\\r\\n`,\n\t\tmeta,\n\t\t`\\r\\n--${boundary}\\r\\nContent-Type: application/octet-stream\\r\\n\\r\\n\\r\\n--${boundary}--`\n\t].join('');\n\tconst r = await fetch(`${DRIVE_UPLOAD}?uploadType=multipart`, {\n\t\tmethod: 'POST',\n\t\theaders: {\n\t\t\tAuthorization: `Bearer ${token}`,\n\t\t\t'Content-Type': `multipart/related; boundary=${boundary}`\n\t\t},\n\t\tbody,\n\t\tsignal: AbortSignal.timeout(META_DEADLINE_MS)\n\t});\n\tif (!r.ok) throw new SelfstoreError('TARGET_WRITE_FAILED', `Drive create failed: ${r.status}`);\n\tconst data: { id: string } = await r.json();\n\treturn data.id;\n}\n\nfunction makeTarget(opts: DriveOptions, fixedId?: string): BackupTarget {\n\tconst { auth, kv } = opts;\n\t// A target is BOUND to one file for its whole life: the id is the fixed one,\n\t// or the remembered id CAPTURED on first use - never re-read per operation.\n\t// The old lazy per-call read let a backup SWITCH re-point the remembered id\n\t// while the OUTGOING target was still attached: its final courtesy flush\n\t// then wrote the old store (data and sync bookkeeping) into the file being\n\t// opened, silently merging two \"isolated\" backups.\n\tlet captured: string | undefined = fixedId;\n\tconst fileIdOf = async (): Promise<string | undefined> =>\n\t\t(captured ??= await kv.get<string>(FILE_ID_KEY));\n\treturn {\n\t\tkind: 'drive',\n\t\tlabel: 'Google Drive',\n\t\tasync save(blob: Blob): Promise<string | null> {\n\t\t\tconst fileId = await fileIdOf();\n\t\t\tif (!fileId) throw new SelfstoreError('NOT_CONNECTED', 'Drive not connected.');\n\t\t\t// fields=version: the response reports the file's new version, so the\n\t\t\t// store records exactly OUR write (no marker race with other replicas).\n\t\t\tconst r = await driveFetch(auth, (token) =>\n\t\t\t\tfetch(`${DRIVE_UPLOAD}/${fileId}?uploadType=media&fields=version`, {\n\t\t\t\t\tmethod: 'PATCH',\n\t\t\t\t\theaders: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/octet-stream' },\n\t\t\t\t\tbody: blob,\n\t\t\t\t\tsignal: AbortSignal.timeout(DATA_DEADLINE_MS)\n\t\t\t\t})\n\t\t\t);\n\t\t\tif (!r.ok) {\n\t\t\t\t// A 401 that survives a forced token refresh (driveFetch) is a genuine\n\t\t\t\t// loss of access, not a stale-token blip. A 404 (the bound file is\n\t\t\t\t// gone) or a non-rate-limit 403 (permission withdrawn, storage full) is\n\t\t\t\t// PERMANENT: gate so the user reconnects to a fresh file, never loop it\n\t\t\t\t// as transient. Everything else (5xx, network) stays transient.\n\t\t\t\tif (r.status === 401) throw new AuthExpiredError('Drive rejected the token (401).');\n\t\t\t\tconst body = await r.text().catch(() => '');\n\t\t\t\tif (isPermanentDriveWrite(r.status, body)) {\n\t\t\t\t\tthrow new SelfstoreError(\n\t\t\t\t\t\t'TARGET_GONE',\n\t\t\t\t\t\t`Drive backup unwritable (${r.status}): the file is gone or access was withdrawn.`\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthrow new SelfstoreError('TARGET_WRITE_FAILED', `Drive upload failed: ${r.status}`);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst data: { version?: string | number } = await r.json();\n\t\t\t\treturn data.version != null ? String(data.version) : null;\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t},\n\t\tasync load(): Promise<Blob | null> {\n\t\t\tconst fileId = await fileIdOf();\n\t\t\tif (!fileId) return null;\n\t\t\tconst r = await driveFetch(auth, (token) =>\n\t\t\t\tfetch(`${DRIVE_FILES}/${fileId}?alt=media`, {\n\t\t\t\t\theaders: { Authorization: `Bearer ${token}` },\n\t\t\t\t\tsignal: AbortSignal.timeout(DATA_DEADLINE_MS)\n\t\t\t\t})\n\t\t\t);\n\t\t\t// A failed read must never pass for an empty file: null tells the store\n\t\t\t// \"nothing there\", and a fresh connect would then overwrite the backup\n\t\t\t// this download merely failed to fetch. Only a 404 - the file is\n\t\t\t// genuinely gone - reads as empty; a 401 that survived driveFetch's\n\t\t\t// forced refresh is a real loss of access; anything else is a typed\n\t\t\t// transient failure (the sync legs retry it, connect flows surface it).\n\t\t\tif (r.status === 404) return null;\n\t\t\tif (r.status === 401) throw new AuthExpiredError('Drive rejected the token (401).');\n\t\t\tif (!r.ok) {\n\t\t\t\tthrow new SelfstoreError('TARGET_UNAVAILABLE', `Drive download failed: ${r.status}`);\n\t\t\t}\n\t\t\treturn r.blob();\n\t\t},\n\t\tasync stat(): Promise<string | null> {\n\t\t\ttry {\n\t\t\t\tconst fileId = await fileIdOf();\n\t\t\t\tif (!fileId) return null;\n\t\t\t\tconst r = await driveFetch(auth, (token) =>\n\t\t\t\t\tfetch(`${DRIVE_FILES}/${fileId}?fields=version`, {\n\t\t\t\t\t\theaders: { Authorization: `Bearer ${token}` },\n\t\t\t\t\t\tsignal: AbortSignal.timeout(META_DEADLINE_MS)\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t\tif (!r.ok) return null;\n\t\t\t\tconst data: { version?: string | number } = await r.json();\n\t\t\t\treturn data.version != null ? String(data.version) : null;\n\t\t\t} catch {\n\t\t\t\treturn null; // offline or token lapse: \"cannot tell\", never throws\n\t\t\t}\n\t\t},\n\t\tasync isReady(): Promise<boolean> {\n\t\t\ttry {\n\t\t\t\tawait auth.token();\n\t\t\t\treturn true;\n\t\t\t} catch (e) {\n\t\t\t\t// A transient token failure means \"not writable right now\", not a lost\n\t\t\t\t// session: report false so the store retries. Only a genuine auth loss\n\t\t\t\t// propagates, so the boot path can raise the reconnect gate.\n\t\t\t\tif (isAuthExpired(e)) throw e;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t},\n\t\tasync reconnect(): Promise<boolean> {\n\t\t\treturn auth.reconnect();\n\t\t},\n\t\tasync disconnect(): Promise<void> {\n\t\t\tawait auth.forget();\n\t\t\t// Delete the remembered id only while it still points at this target's\n\t\t\t// file: after a backup switch the key already belongs to the new\n\t\t\t// target, and a late disconnect of the old one must not orphan it.\n\t\t\tconst current = await kv.get<string>(FILE_ID_KEY);\n\t\t\tconst own = await fileIdOf();\n\t\t\tif (!current || !own || current === own) await kv.del(FILE_ID_KEY);\n\t\t}\n\t};\n}\n\n/** First-time connect: authorize (a user gesture), then find-or-create the file.\n * Returns the target, or null if the user cancelled the consent. */\nexport async function connect(opts: DriveOptions): Promise<BackupTarget | null> {\n\tif (!(await opts.auth.reconnect())) return null;\n\tconst token = await opts.auth.token();\n\tlet fileId = await opts.kv.get<string>(FILE_ID_KEY);\n\tif (!fileId) {\n\t\tfileId =\n\t\t\t(await findExistingFile(token, [opts.fileName])) ?? (await createFile(token, opts.fileName));\n\t\tawait opts.kv.set(FILE_ID_KEY, fileId);\n\t}\n\treturn makeTarget(opts);\n}\n\n/** Rebuild the target from a past session's saved connection (no UI), or null. */\nexport async function fromSession(opts: DriveOptions): Promise<BackupTarget | null> {\n\treturn (await opts.kv.get<string>(FILE_ID_KEY)) ? makeTarget(opts) : null;\n}\n\n/** A read-only-intent preview of one specific (typically shared) file, before\n * adopting it. Writes NOTHING to the KV: until the user confirms, the device's\n * own backup wiring must stay untouched. */\nexport function preview(opts: DriveOptions, fileId: string): BackupTarget {\n\treturn makeTarget(opts, fileId);\n}\n\n/** Adopt a specific (typically shared) file as this device's backup. Persists\n * the file id, so next sessions restore it like any Drive backup. */\nexport async function adopt(opts: DriveOptions, fileId: string): Promise<BackupTarget> {\n\tawait opts.kv.set(FILE_ID_KEY, fileId);\n\treturn makeTarget(opts);\n}\n\n/** Find (or create) the user's own backup file, ignoring the currently adopted\n * id (which may point at someone else's shared file). Reuses the live session\n * when possible and only falls back to the consent popup. Adopts NOTHING: the\n * caller decides whether to load the file or start it blank. Null when the\n * consent is refused. */\nexport async function findOrCreateOwnFile(\n\topts: DriveOptions\n): Promise<{ fileId: string; created: boolean } | null> {\n\tlet token: string;\n\ttry {\n\t\ttoken = await opts.auth.token();\n\t} catch {\n\t\tif (!(await opts.auth.reconnect())) return null;\n\t\ttoken = await opts.auth.token();\n\t}\n\tconst existing = await findExistingFile(token, [opts.fileName]);\n\tif (existing) return { fileId: existing, created: false };\n\treturn { fileId: await createFile(token, opts.fileName), created: true };\n}\n\n/** One row of `listBackups()`: what Drive reports about a file the app can\n * see. Whether the backup is encrypted is not here on purpose - that lives\n * inside the file (inspect it, or remember it app-side); a listing must stay\n * one cheap metadata call, never N downloads. */\nexport interface DriveBackupInfo {\n\tid: string;\n\tname: string;\n\t/** RFC 3339 timestamp of the last write, as Drive reports it. */\n\tmodifiedTime: string | null;\n\t/** Size in bytes, when Drive reports one. */\n\tsize: number | null;\n}\n\n/** List the files this app can see on the user's own Drive (the drive.file\n * scope already narrows visibility to files the app created or was handed).\n * `nameContains` narrows server-side; selfstore ships no naming convention -\n * the app applies its own exact rules on what comes back (its backups, its\n * internal files). Newest first. */\nexport async function listBackups(opts: {\n\tauth: DriveAuth;\n\tnameContains?: string;\n}): Promise<DriveBackupInfo[]> {\n\tconst clauses = ['trashed=false', \"'me' in owners\"];\n\tif (opts.nameContains) {\n\t\tconst safe = opts.nameContains.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\");\n\t\tclauses.push(`name contains '${safe}'`);\n\t}\n\tconst q = encodeURIComponent(clauses.join(' and '));\n\tconst fields = encodeURIComponent('files(id,name,modifiedTime,size)');\n\tconst r = await driveFetch(opts.auth, (token) =>\n\t\tfetch(`${DRIVE_FILES}?q=${q}&fields=${fields}&pageSize=100&orderBy=modifiedTime%20desc`, {\n\t\t\theaders: { Authorization: `Bearer ${token}` },\n\t\t\tsignal: AbortSignal.timeout(META_DEADLINE_MS)\n\t\t})\n\t);\n\tif (r.status === 401) throw new AuthExpiredError('Drive rejected the token (401).');\n\tif (!r.ok) throw new SelfstoreError('TARGET_UNAVAILABLE', `Drive list failed: ${r.status}`);\n\tconst data: {\n\t\tfiles?: { id: string; name: string; modifiedTime?: string; size?: string | number }[];\n\t} = await r.json();\n\treturn (data.files ?? []).map((f) => ({\n\t\tid: f.id,\n\t\tname: f.name,\n\t\tmodifiedTime: f.modifiedTime ?? null,\n\t\tsize: f.size != null ? Number(f.size) : null\n\t}));\n}\n\n/** Create a brand-new empty backup file with this exact name and answer its\n * id. Refuses when a non-trashed owned file already carries the name: Drive\n * happily stores duplicate names, and a duplicate would poison every\n * find-by-name reconnect that follows. Adopts nothing - the caller decides\n * to attach it (typically with `wipe: true` so it starts blank). */\nexport async function createBackup(opts: {\n\tauth: DriveAuth;\n\tfileName: string;\n}): Promise<{ fileId: string }> {\n\tconst token = await opts.auth.token();\n\tconst existing = await findExistingFile(token, [opts.fileName]);\n\tif (existing) {\n\t\tthrow new SelfstoreError(\n\t\t\t'TARGET_WRITE_FAILED',\n\t\t\t`A file named \"${opts.fileName}\" already exists on Drive.`\n\t\t);\n\t}\n\treturn { fileId: await createFile(token, opts.fileName) };\n}\n\n/** Delete a backup file from Drive, for good (not the trash: the file's data\n * must actually be gone, not lingering). A 404 is success - deleting what is\n * already gone is not an error. The caller owns the guard against deleting\n * the currently attached file. */\nexport async function deleteBackup(opts: { auth: DriveAuth; fileId: string }): Promise<void> {\n\tconst r = await driveFetch(opts.auth, (token) =>\n\t\tfetch(`${DRIVE_FILES}/${opts.fileId}`, {\n\t\t\tmethod: 'DELETE',\n\t\t\theaders: { Authorization: `Bearer ${token}` },\n\t\t\tsignal: AbortSignal.timeout(META_DEADLINE_MS)\n\t\t})\n\t);\n\tif (r.status === 401) throw new AuthExpiredError('Drive rejected the token (401).');\n\tif (r.status === 404) return;\n\tif (!r.ok) throw new SelfstoreError('TARGET_WRITE_FAILED', `Drive delete failed: ${r.status}`);\n}\n\n/** Rename a backup file (metadata only - the id, the content and every open\n * target bound to it are untouched). Refuses when another owned file already\n * carries the new name, for the same reason createBackup does: a duplicate\n * poisons find-by-name. A 404 means the file is gone, not a success here. */\nexport async function renameBackup(opts: {\n\tauth: DriveAuth;\n\tfileId: string;\n\tfileName: string;\n}): Promise<void> {\n\tconst token = await opts.auth.token();\n\tconst existing = await findExistingFile(token, [opts.fileName]);\n\tif (existing && existing !== opts.fileId) {\n\t\tthrow new SelfstoreError(\n\t\t\t'TARGET_WRITE_FAILED',\n\t\t\t`A file named \"${opts.fileName}\" already exists on Drive.`\n\t\t);\n\t}\n\tif (existing === opts.fileId) return; // already this name: nothing to do\n\tconst r = await driveFetch(opts.auth, (token) =>\n\t\tfetch(`${DRIVE_FILES}/${opts.fileId}?fields=id`, {\n\t\t\tmethod: 'PATCH',\n\t\t\theaders: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },\n\t\t\tbody: JSON.stringify({ name: opts.fileName }),\n\t\t\tsignal: AbortSignal.timeout(META_DEADLINE_MS)\n\t\t})\n\t);\n\tif (r.status === 401) throw new AuthExpiredError('Drive rejected the token (401).');\n\tif (!r.ok) throw new SelfstoreError('TARGET_WRITE_FAILED', `Drive rename failed: ${r.status}`);\n}\n","/**\n * Disk-file backup target (Chromium only). The user picks a backup file once via\n * the File System Access API; the store then re-writes that real file on every\n * change. The handle is persisted through the injected KV, so the target stays\n * storage-agnostic. The browser re-confirms write permission once per session\n * (a one-click reconnect).\n */\n\nimport type { BackupTarget } from '../target';\nimport type { KV } from '../cache';\nimport { AuthExpiredError, SelfstoreError } from '../../selfstore';\n\nconst HANDLE_KEY = 'fileHandle';\n\ninterface FileHandle {\n\tname: string;\n\tcreateWritable(): Promise<{ write(data: Blob): Promise<void>; close(): Promise<void> }>;\n\tgetFile(): Promise<File>;\n\tqueryPermission(d: { mode: 'read' | 'readwrite' }): Promise<PermissionState>;\n\trequestPermission(d: { mode: 'read' | 'readwrite' }): Promise<PermissionState>;\n}\n\ninterface PickerWindow {\n\tshowSaveFilePicker?: (o: {\n\t\tsuggestedName?: string;\n\t\ttypes?: { description: string; accept: Record<string, string[]> }[];\n\t}) => Promise<FileHandle>;\n\tshowOpenFilePicker?: (o: {\n\t\tmultiple?: boolean;\n\t\ttypes?: { description: string; accept: Record<string, string[]> }[];\n\t}) => Promise<FileHandle[]>;\n}\n\nexport interface FileConnectOptions {\n\t/** Where the picked file handle is persisted across sessions. */\n\tkv: KV;\n\t/** Suggested name for the backup file the user creates/picks. */\n\tfileName: string;\n}\n\n/** True when the browser supports picking a re-writable file (Chromium). */\nexport function isSupported(): boolean {\n\treturn typeof window !== 'undefined' && 'showSaveFilePicker' in window;\n}\n\n/** A revoked/denied file permission is a genuine loss of access: the store must\n * raise the reconnect gate (one click re-grants). Everything else is transient. */\nfunction isPermissionLoss(e: unknown): boolean {\n\treturn e instanceof DOMException && (e.name === 'NotAllowedError' || e.name === 'SecurityError');\n}\n\nfunction fromHandle(handle: FileHandle, kv: KV): BackupTarget {\n\treturn {\n\t\tkind: 'file',\n\t\tlabel: handle.name,\n\t\tasync save(blob: Blob): Promise<string | null> {\n\t\t\ttry {\n\t\t\t\tconst writable = await handle.createWritable();\n\t\t\t\tawait writable.write(blob);\n\t\t\t\tawait writable.close();\n\t\t\t} catch (e) {\n\t\t\t\tif (isPermissionLoss(e)) {\n\t\t\t\t\tthrow new AuthExpiredError('File permission was revoked.');\n\t\t\t\t}\n\t\t\t\tthrow new SelfstoreError('TARGET_WRITE_FAILED', `File write failed: ${String(e)}`);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\t// The file's new mtime = OUR write's version marker (shared-disk case).\n\t\t\t\treturn String((await handle.getFile()).lastModified);\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t},\n\t\tasync load(): Promise<Blob | null> {\n\t\t\treturn handle.getFile();\n\t\t},\n\t\tasync stat(): Promise<string | null> {\n\t\t\ttry {\n\t\t\t\treturn String((await handle.getFile()).lastModified);\n\t\t\t} catch {\n\t\t\t\treturn null; // permission not granted yet: \"cannot tell\", never throws\n\t\t\t}\n\t\t},\n\t\tasync isReady(): Promise<boolean> {\n\t\t\treturn (await handle.queryPermission({ mode: 'readwrite' })) === 'granted';\n\t\t},\n\t\tasync reconnect(): Promise<boolean> {\n\t\t\treturn (await handle.requestPermission({ mode: 'readwrite' })) === 'granted';\n\t\t},\n\t\tasync disconnect(): Promise<void> {\n\t\t\tawait kv.del(HANDLE_KEY);\n\t\t}\n\t};\n}\n\n/** Prompt the user to choose/create the backup file. Persists the handle for\n * next sessions. Returns the target, or null if cancelled. */\nexport async function connect(opts: FileConnectOptions): Promise<BackupTarget | null> {\n\tconst picker = (window as unknown as PickerWindow).showSaveFilePicker;\n\tif (!picker) return null;\n\ttry {\n\t\tconst handle = await picker({\n\t\t\tsuggestedName: opts.fileName,\n\t\t\ttypes: [{ description: 'Backup', accept: { 'application/zip': ['.zip'] } }]\n\t\t});\n\t\tawait opts.kv.set(HANDLE_KEY, handle);\n\t\treturn fromHandle(handle, opts.kv);\n\t} catch (e) {\n\t\tif (e instanceof DOMException && e.name === 'AbortError') return null; // user cancelled\n\t\tthrow e;\n\t}\n}\n\n/** Prompt the user to pick an EXISTING backup file and adopt it as the\n * destination (the connect journey then reads it like any backup: password,\n * conflict, resume). Asks for readwrite up front so the first save does not\n * stall on a second permission prompt. Returns null if cancelled. */\nexport async function openExisting(opts: { kv: KV }): Promise<BackupTarget | null> {\n\tconst picker = (window as unknown as PickerWindow).showOpenFilePicker;\n\tif (!picker) return null;\n\ttry {\n\t\tconst [handle] = await picker({\n\t\t\tmultiple: false,\n\t\t\ttypes: [{ description: 'Backup', accept: { 'application/zip': ['.zip'] } }]\n\t\t});\n\t\tif (!handle) return null;\n\t\tif ((await handle.requestPermission({ mode: 'readwrite' })) !== 'granted') return null;\n\t\tawait opts.kv.set(HANDLE_KEY, handle);\n\t\treturn fromHandle(handle, opts.kv);\n\t} catch (e) {\n\t\tif (e instanceof DOMException && e.name === 'AbortError') return null; // user cancelled\n\t\tthrow e;\n\t}\n}\n\n/** Rebuild the target from the handle persisted in a past session, or null. */\nexport async function fromSession(opts: { kv: KV }): Promise<BackupTarget | null> {\n\tconst handle = await opts.kv.get<FileHandle>(HANDLE_KEY);\n\treturn handle ? fromHandle(handle, opts.kv) : null;\n}\n","/**\n * WebDAV backup target: the store re-writes one .selfstore file on a WebDAV\n * server the USER controls (Nextcloud, ownCloud, a plain WebDAV mount...). No\n * Google, no vendor broker - the browser talks straight to the server the user\n * owns. The bytes are the same end-to-end-encrypted .selfstore, so the server\n * only ever holds ciphertext.\n *\n * Two things the environment must allow, both documented for the user:\n * - the server must send CORS for the app origin (methods PUT/GET/HEAD and the\n * Authorization header), since browsers block cross-origin WebDAV otherwise;\n * - the app's CSP connect-src must permit the server host.\n *\n * Credentials are stored ENCRYPTED at rest under a per-device non-extractable\n * key, so a set-and-forget connection never leaves a cleartext password in\n * IndexedDB. The version marker prefers ETag, falling back to Last-Modified\n * (the latter is CORS-safelisted, so it is readable without extra server config).\n */\n\nimport type { BackupTarget, PeerSource } from '../target';\nimport type { KV } from '../cache';\nimport { seal, unseal, isEnvelope, newDeviceKey } from '../cache-crypto';\nimport { AuthExpiredError, SelfstoreError } from '../../selfstore';\n\nconst CONFIG_KEY = 'webdavConfig';\nconst KEY_KEY = 'webdavKey';\n\n/** A hung server must not spin forever: a deadline turns the hang into an\n * ordinary error the store retries (never a gate). Transfers get more room than\n * the HEAD poke. */\nconst DATA_DEADLINE_MS = 30_000;\nconst META_DEADLINE_MS = 15_000;\n\nexport interface WebdavConfig {\n\t/** Full URL of the backup file, e.g. https://cloud.me/remote.php/dav/files/me/backup.zip */\n\turl: string;\n\tusername: string;\n\tpassword: string;\n}\n\nexport interface WebdavConnectOptions {\n\t/** Where the (encrypted) server config is persisted across sessions. */\n\tkv: KV;\n\tconfig: WebdavConfig;\n}\n\nasync function configKey(kv: KV): Promise<CryptoKey> {\n\tconst existing = await kv.get<CryptoKey>(KEY_KEY);\n\tif (existing) return existing;\n\tconst fresh = await newDeviceKey();\n\tawait kv.set(KEY_KEY, fresh);\n\treturn fresh;\n}\n\nasync function saveConfig(kv: KV, c: WebdavConfig): Promise<void> {\n\tconst bytes = new TextEncoder().encode(JSON.stringify(c));\n\tawait kv.set(CONFIG_KEY, await seal(await configKey(kv), bytes));\n}\n\nasync function loadConfig(kv: KV): Promise<WebdavConfig | null> {\n\tconst stored = await kv.get(CONFIG_KEY);\n\tif (!isEnvelope(stored)) return null;\n\tconst bytes = await unseal(await configKey(kv), stored);\n\treturn JSON.parse(new TextDecoder().decode(bytes)) as WebdavConfig;\n}\n\nfunction labelOf(url: string): string {\n\ttry {\n\t\treturn new URL(url).host;\n\t} catch {\n\t\treturn 'WebDAV';\n\t}\n}\n\n/** WebDAV authenticates with HTTP Basic - the password is sent on every request.\n * Over plain http that is cleartext on the wire, so refuse it. Allow https, and\n * http only for loopback (local dev against a container on the same machine). */\nfunction assertSecureUrl(url: string): void {\n\tlet u: URL;\n\ttry {\n\t\tu = new URL(url);\n\t} catch {\n\t\tthrow new TypeError(`webdavTarget: invalid URL \"${url}\".`);\n\t}\n\tconst loopback =\n\t\tu.hostname === 'localhost' || u.hostname === '127.0.0.1' || u.hostname === '[::1]';\n\tif (u.protocol !== 'https:' && !(u.protocol === 'http:' && loopback)) {\n\t\tthrow new TypeError(\n\t\t\t`webdavTarget: refusing \"${u.protocol}\" - Basic-auth credentials need https (http is allowed only for localhost).`\n\t\t);\n\t}\n}\n\nfunction versionOf(res: Response): string | null {\n\treturn res.headers.get('ETag') ?? res.headers.get('Last-Modified') ?? null;\n}\n\nfunction fromConfig(c: WebdavConfig, kv: KV): BackupTarget {\n\tconst headers = { Authorization: 'Basic ' + btoa(`${c.username}:${c.password}`) };\n\n\t/** One HEAD, or null on a network/CORS failure (transient - the caller decides\n\t * what \"cannot reach\" means for it). */\n\tasync function probe(): Promise<Response | null> {\n\t\ttry {\n\t\t\treturn await fetch(c.url, {\n\t\t\t\tmethod: 'HEAD',\n\t\t\t\theaders,\n\t\t\t\tsignal: AbortSignal.timeout(META_DEADLINE_MS)\n\t\t\t});\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\tasync function stat(): Promise<string | null> {\n\t\tconst res = await probe();\n\t\treturn res && res.ok ? versionOf(res) : null; // 404 / error / offline: \"cannot tell\"\n\t}\n\tasync function ready(): Promise<boolean> {\n\t\tconst res = await probe();\n\t\tif (!res) return false; // offline / CORS blocked: transient, the store retries later\n\t\t// 401/403 = the stored credentials are rejected: a genuine loss of access\n\t\t// only the user can fix (re-enter the config), so gate rather than loop as\n\t\t// \"momentarily unreachable\". 404 = file not created yet, still writable.\n\t\tif (res.status === 401 || res.status === 403) {\n\t\t\tthrow new AuthExpiredError(`WebDAV rejected the credentials (${res.status}).`);\n\t\t}\n\t\treturn res.ok || res.status === 404;\n\t}\n\t/** Credentials are stored; a reconnect just re-checks reachability. It can\n\t * never mint new credentials, so a rejection is reported as false (never\n\t * thrown) - only re-running connect() with a fresh config fixes bad creds. */\n\tasync function reconnectProbe(): Promise<boolean> {\n\t\tconst res = await probe();\n\t\treturn !!res && (res.ok || res.status === 404);\n\t}\n\n\treturn {\n\t\tkind: 'webdav',\n\t\tlabel: labelOf(c.url),\n\t\tasync save(blob: Blob): Promise<string | null> {\n\t\t\tconst res = await fetch(c.url, {\n\t\t\t\tmethod: 'PUT',\n\t\t\t\theaders,\n\t\t\t\tbody: blob,\n\t\t\t\tsignal: AbortSignal.timeout(DATA_DEADLINE_MS)\n\t\t\t});\n\t\t\t// 401/403 = the server rejected the stored credentials: a genuine loss of\n\t\t\t// access the user must fix. Anything else (5xx, conflict) is transient.\n\t\t\tif (res.status === 401 || res.status === 403) {\n\t\t\t\tthrow new AuthExpiredError(`WebDAV rejected the credentials (${res.status}).`);\n\t\t\t}\n\t\t\tif (!res.ok)\n\t\t\t\tthrow new SelfstoreError('TARGET_WRITE_FAILED', `WebDAV PUT failed: ${res.status}`);\n\t\t\treturn versionOf(res) ?? (await stat());\n\t\t},\n\t\tasync load(): Promise<Blob | null> {\n\t\t\tconst res = await fetch(c.url, {\n\t\t\t\tmethod: 'GET',\n\t\t\t\theaders,\n\t\t\t\tsignal: AbortSignal.timeout(DATA_DEADLINE_MS)\n\t\t\t});\n\t\t\tif (res.status === 404) return null;\n\t\t\tif (res.status === 401 || res.status === 403) {\n\t\t\t\tthrow new AuthExpiredError(`WebDAV rejected the credentials (${res.status}).`);\n\t\t\t}\n\t\t\tif (!res.ok)\n\t\t\t\tthrow new SelfstoreError('TARGET_UNAVAILABLE', `WebDAV GET failed: ${res.status}`);\n\t\t\treturn res.blob();\n\t\t},\n\t\tstat,\n\t\tisReady: ready,\n\t\treconnect: reconnectProbe, // stored credentials: re-check reachability, no gesture\n\t\tasync disconnect(): Promise<void> {\n\t\t\tawait kv.del(CONFIG_KEY);\n\t\t\tawait kv.del(KEY_KEY);\n\t\t}\n\t};\n}\n\nexport interface WebdavPeerOptions {\n\t/** URL of ANOTHER member's published copy, read-only: a Nextcloud/ownCloud\n\t * read-only share link, or a file URL they granted you read access to. */\n\turl: string;\n\t/** Optional Basic-auth credentials, when the read-only share needs them.\n\t * Omit for a public/tokenised share link (a Nextcloud `/s/<token>/download`\n\t * URL) that needs none. Held in memory only - a peer is re-attached each\n\t * session (see attachPeer), so nothing is persisted here. */\n\tusername?: string;\n\tpassword?: string;\n\t/** Display label (defaults to the URL host). */\n\tlabel?: string;\n}\n\n/** A read-only WebDAV peer: another member's published copy, fetched over a\n * shared URL, for `store.attachPeer`. It is the read half of a WebDAV target\n * (GET + HEAD only, never a write), so a group can share read-write without\n * Google Drive - each member publishes to their own WebDAV and shares a\n * read-only link. Failures follow the peer protocol: a 401/403 is a genuine\n * loss of read access (AuthExpiredError), 404 means \"not published yet\"\n * (null), anything else is transient. */\nexport function peer(opts: WebdavPeerOptions): PeerSource {\n\t// Basic-auth credentials are cleartext on the wire, so require https when they\n\t// are present; a credential-less public link carries only ciphertext, so any\n\t// host it lives on is the member's own choice.\n\tif (opts.username !== undefined) assertSecureUrl(opts.url);\n\tconst headers =\n\t\topts.username !== undefined\n\t\t\t? { Authorization: 'Basic ' + btoa(`${opts.username}:${opts.password ?? ''}`) }\n\t\t\t: undefined;\n\treturn {\n\t\tlabel: opts.label ?? labelOf(opts.url),\n\t\tasync load(): Promise<Blob | null> {\n\t\t\tconst res = await fetch(opts.url, {\n\t\t\t\tmethod: 'GET',\n\t\t\t\theaders,\n\t\t\t\tsignal: AbortSignal.timeout(DATA_DEADLINE_MS)\n\t\t\t});\n\t\t\tif (res.status === 404) return null; // the member has not published yet\n\t\t\tif (res.status === 401 || res.status === 403) {\n\t\t\t\tthrow new AuthExpiredError(`WebDAV peer rejected the credentials (${res.status}).`);\n\t\t\t}\n\t\t\tif (!res.ok) {\n\t\t\t\tthrow new SelfstoreError('TARGET_UNAVAILABLE', `WebDAV peer GET failed: ${res.status}`);\n\t\t\t}\n\t\t\treturn res.blob();\n\t\t},\n\t\tasync stat(): Promise<string | null> {\n\t\t\ttry {\n\t\t\t\tconst res = await fetch(opts.url, {\n\t\t\t\t\tmethod: 'HEAD',\n\t\t\t\t\theaders,\n\t\t\t\t\tsignal: AbortSignal.timeout(META_DEADLINE_MS)\n\t\t\t\t});\n\t\t\t\treturn res.ok ? versionOf(res) : null; // cannot tell: fall through to a full load\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t};\n}\n\n/** Validate + persist the config, returning the target, or null when the server\n * is unreachable, rejects the credentials, or blocks the cross-origin request. */\nexport async function connect(opts: WebdavConnectOptions): Promise<BackupTarget | null> {\n\tassertSecureUrl(opts.config.url); // cleartext credentials over http are refused up front\n\tconst target = fromConfig(opts.config, opts.kv);\n\t// isReady() now THROWS on a 401/403 (so a later rejection gates the store); at\n\t// first connect a rejection just means \"wrong credentials, try again\", so a\n\t// throw here reads the same as false - report null, persist nothing.\n\tlet ok: boolean;\n\ttry {\n\t\tok = await target.isReady();\n\t} catch {\n\t\tok = false;\n\t}\n\tif (!ok) return null;\n\tawait saveConfig(opts.kv, opts.config);\n\treturn target;\n}\n\n/** Rebuild the target from the encrypted config persisted last session, or null. */\nexport async function fromSession(opts: { kv: KV }): Promise<BackupTarget | null> {\n\tconst config = await loadConfig(opts.kv);\n\treturn config ? fromConfig(config, opts.kv) : null;\n}\n","// AWS Signature Version 4 for S3 REST requests, in the browser with WebCrypto.\n// Enough of the spec to sign one object's GET/PUT/HEAD - no streaming, no\n// chunked upload, no presigned URLs. Works against AWS S3 and any S3-compatible\n// endpoint (Cloudflare R2, Backblaze B2, MinIO) since they all verify SigV4.\n//\n// Signed headers are host, x-amz-content-sha256 and x-amz-date, plus whatever\n// the caller adds. The browser sets Host itself from the URL and forbids\n// overriding it, so we sign the host derived from that same URL - the value the\n// browser will send - and never try to set the header.\n\nconst encoder = new TextEncoder();\n\nfunction hex(bytes: Uint8Array): string {\n\tlet out = '';\n\tfor (const b of bytes) out += b.toString(16).padStart(2, '0');\n\treturn out;\n}\n\nexport async function sha256Hex(bytes: Uint8Array): Promise<string> {\n\treturn hex(new Uint8Array(await crypto.subtle.digest('SHA-256', bytes as BufferSource)));\n}\n\n/** SHA-256 of the empty body: the payload hash for every GET/HEAD. */\nexport const EMPTY_PAYLOAD_SHA256 =\n\t'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855';\n\nasync function hmac(key: Uint8Array, data: Uint8Array): Promise<Uint8Array> {\n\tconst k = await crypto.subtle.importKey(\n\t\t'raw',\n\t\tkey as BufferSource,\n\t\t{ name: 'HMAC', hash: 'SHA-256' },\n\t\tfalse,\n\t\t['sign']\n\t);\n\treturn new Uint8Array(await crypto.subtle.sign('HMAC', k, data as BufferSource));\n}\n\n// RFC 3986 encoding, applied per path segment (the '/' between segments stays a\n// separator). encodeURIComponent leaves !*'() alone; SigV4 requires them encoded.\nfunction encodeSegment(s: string): string {\n\treturn encodeURIComponent(s).replace(\n\t\t/[!*'()]/g,\n\t\t(c) => '%' + c.charCodeAt(0).toString(16).toUpperCase()\n\t);\n}\n\n/** Canonicalise an object path for both the request URL and the signature, so\n * the two can never disagree on encoding. Pass the RAW key path (unencoded). */\nexport function encodeS3Path(path: string): string {\n\treturn path\n\t\t.split('/')\n\t\t.map((seg) => encodeSegment(seg))\n\t\t.join('/');\n}\n\nexport interface SignS3Input {\n\tmethod: string;\n\t/** Scheme + host(+port), e.g. https://s3.eu-west-3.amazonaws.com */\n\torigin: string;\n\t/** Raw (unencoded) object path, e.g. /my-bucket/backups/app.selfstore */\n\tpath: string;\n\tregion: string;\n\taccessKeyId: string;\n\tsecretAccessKey: string;\n\t/** Hex SHA-256 of the request body (EMPTY_PAYLOAD_SHA256 for GET/HEAD). */\n\tpayloadHashHex: string;\n\t/** Extra headers to sign, e.g. { range: 'bytes=0-0' }. Names are lowercased. */\n\textraHeaders?: Record<string, string>;\n\t/** Basic timestamp (YYYYMMDDTHHMMSSZ). Injected in tests; defaults to now. */\n\tamzDate?: string;\n\tservice?: string;\n}\n\nexport interface SignedS3Request {\n\t/** The fully-encoded URL to fetch. */\n\turl: string;\n\t/** Headers to send: Authorization, x-amz-date, x-amz-content-sha256, extras. */\n\theaders: Record<string, string>;\n}\n\n/** ISO basic UTC now, YYYYMMDDTHHMMSSZ. */\nfunction basicAmzDate(): string {\n\treturn new Date().toISOString().replace(/[:-]|\\.\\d{3}/g, '');\n}\n\n/** Sign one S3 request and return the URL plus the headers to send. */\nexport async function signS3(input: SignS3Input): Promise<SignedS3Request> {\n\tconst service = input.service ?? 's3';\n\tconst amzDate = input.amzDate ?? basicAmzDate();\n\tconst dateStamp = amzDate.slice(0, 8);\n\tconst { host } = new URL(input.origin);\n\tconst canonicalUri = encodeS3Path(input.path);\n\n\tconst headers: Record<string, string> = {\n\t\thost,\n\t\t'x-amz-content-sha256': input.payloadHashHex,\n\t\t'x-amz-date': amzDate\n\t};\n\tfor (const [name, value] of Object.entries(input.extraHeaders ?? {})) {\n\t\theaders[name.toLowerCase()] = value;\n\t}\n\tconst signedNames = Object.keys(headers).sort();\n\tconst canonicalHeaders = signedNames.map((n) => `${n}:${headers[n].trim()}\\n`).join('');\n\tconst signedHeaders = signedNames.join(';');\n\n\tconst canonicalRequest = [\n\t\tinput.method,\n\t\tcanonicalUri,\n\t\t'', // no query string on any of our single-object requests\n\t\tcanonicalHeaders,\n\t\tsignedHeaders,\n\t\tinput.payloadHashHex\n\t].join('\\n');\n\n\tconst scope = `${dateStamp}/${input.region}/${service}/aws4_request`;\n\tconst stringToSign = [\n\t\t'AWS4-HMAC-SHA256',\n\t\tamzDate,\n\t\tscope,\n\t\tawait sha256Hex(encoder.encode(canonicalRequest))\n\t].join('\\n');\n\n\tconst kDate = await hmac(\n\t\tencoder.encode('AWS4' + input.secretAccessKey),\n\t\tencoder.encode(dateStamp)\n\t);\n\tconst kRegion = await hmac(kDate, encoder.encode(input.region));\n\tconst kService = await hmac(kRegion, encoder.encode(service));\n\tconst kSigning = await hmac(kService, encoder.encode('aws4_request'));\n\tconst signature = hex(await hmac(kSigning, encoder.encode(stringToSign)));\n\n\tconst authorization =\n\t\t`AWS4-HMAC-SHA256 Credential=${input.accessKeyId}/${scope}, ` +\n\t\t`SignedHeaders=${signedHeaders}, Signature=${signature}`;\n\n\t// Send every signed header except host (the browser sets Host itself and\n\t// forbids overriding it - we signed the value it will send).\n\tconst sendHeaders: Record<string, string> = { authorization };\n\tfor (const n of signedNames) if (n !== 'host') sendHeaders[n] = headers[n];\n\n\treturn { url: input.origin.replace(/\\/$/, '') + canonicalUri, headers: sendHeaders };\n}\n","/**\n * S3 backup target: the store re-writes one object on an S3-compatible bucket\n * the USER controls - Amazon S3, Cloudflare R2, Backblaze B2, MinIO, any server\n * that speaks the S3 REST API and SigV4. No vendor broker: the browser signs\n * each request itself and talks straight to the endpoint. The bytes are the\n * same end-to-end-encrypted .selfstore, so the bucket only ever holds\n * ciphertext.\n *\n * Two things the environment must allow, both documented for the user:\n * - the bucket must send CORS for the app origin: allowed methods GET/PUT/HEAD,\n * allowed headers authorization/x-amz-*, and ExposeHeaders ETag (so the\n * version marker is readable), or browsers block the cross-origin request;\n * - the app's CSP connect-src must permit the endpoint host.\n *\n * Credentials (the secret access key) are stored ENCRYPTED at rest under a\n * per-device non-extractable key, exactly like the WebDAV target, so a\n * set-and-forget connection never leaves a cleartext secret in IndexedDB.\n *\n * Path style is the default: the object lives at endpoint/bucket/key, which\n * every S3-compatible server accepts and needs no per-bucket DNS. Set\n * forcePathStyle:false for AWS virtual-hosted style (bucket as a host label).\n */\n\nimport type { BackupTarget } from '../target';\nimport type { KV } from '../cache';\nimport { seal, unseal, isEnvelope, newDeviceKey } from '../cache-crypto';\nimport { AuthExpiredError, SelfstoreError } from '../../selfstore';\nimport { signS3, sha256Hex, EMPTY_PAYLOAD_SHA256 } from './sigv4';\n\nconst CONFIG_KEY = 's3Config';\nconst KEY_KEY = 's3Key';\n\nconst DATA_DEADLINE_MS = 30_000;\nconst META_DEADLINE_MS = 15_000;\n\nexport interface S3Config {\n\t/** Endpoint origin, e.g. https://s3.eu-west-3.amazonaws.com,\n\t * https://<account>.r2.cloudflarestorage.com, https://minio.example.com. */\n\tendpoint: string;\n\t/** Signing region (\"eu-west-3\"; R2 uses \"auto\"; MinIO any configured value). */\n\tregion: string;\n\tbucket: string;\n\t/** Object key (the backup file), e.g. backups/app.selfstore. */\n\tkey: string;\n\taccessKeyId: string;\n\tsecretAccessKey: string;\n\t/** endpoint/bucket/key (default) vs bucket.endpoint/key. Default true. */\n\tforcePathStyle?: boolean;\n}\n\nexport interface S3ConnectOptions {\n\t/** Where the (encrypted) config is persisted across sessions. */\n\tkv: KV;\n\tconfig: S3Config;\n}\n\nasync function configKey(kv: KV): Promise<CryptoKey> {\n\tconst existing = await kv.get<CryptoKey>(KEY_KEY);\n\tif (existing) return existing;\n\tconst fresh = await newDeviceKey();\n\tawait kv.set(KEY_KEY, fresh);\n\treturn fresh;\n}\n\nasync function saveConfig(kv: KV, c: S3Config): Promise<void> {\n\tconst bytes = new TextEncoder().encode(JSON.stringify(c));\n\tawait kv.set(CONFIG_KEY, await seal(await configKey(kv), bytes));\n}\n\nasync function loadConfig(kv: KV): Promise<S3Config | null> {\n\tconst stored = await kv.get(CONFIG_KEY);\n\tif (!isEnvelope(stored)) return null;\n\tconst bytes = await unseal(await configKey(kv), stored);\n\treturn JSON.parse(new TextDecoder().decode(bytes)) as S3Config;\n}\n\n/** SigV4 puts the secret on the wire only as a derived signature, but the\n * request still needs https: an http endpoint would expose the object bytes\n * and headers. Allow http for loopback (a local MinIO in dev) only. */\nfunction assertSecureEndpoint(endpoint: string): URL {\n\tlet u: URL;\n\ttry {\n\t\tu = new URL(endpoint);\n\t} catch {\n\t\tthrow new TypeError(`s3Target: invalid endpoint \"${endpoint}\".`);\n\t}\n\tconst loopback =\n\t\tu.hostname === 'localhost' || u.hostname === '127.0.0.1' || u.hostname === '[::1]';\n\tif (u.protocol !== 'https:' && !(u.protocol === 'http:' && loopback)) {\n\t\tthrow new TypeError(\n\t\t\t`s3Target: refusing \"${u.protocol}\" - S3 requests need https (http is allowed only for localhost).`\n\t\t);\n\t}\n\treturn u;\n}\n\nfunction labelOf(c: S3Config): string {\n\ttry {\n\t\treturn `${c.bucket} (${new URL(c.endpoint).host})`;\n\t} catch {\n\t\treturn c.bucket || 'S3';\n\t}\n}\n\nfunction versionOf(res: Response): string | null {\n\treturn res.headers.get('ETag') ?? res.headers.get('Last-Modified') ?? null;\n}\n\nfunction fromConfig(c: S3Config, kv: KV): BackupTarget {\n\tconst endpointUrl = assertSecureEndpoint(c.endpoint);\n\tconst pathStyle = c.forcePathStyle ?? true;\n\t// Path style keeps one host and no per-bucket DNS; virtual-hosted folds the\n\t// bucket into the host label (AWS's preferred form).\n\tconst origin = pathStyle\n\t\t? `${endpointUrl.protocol}//${endpointUrl.host}`\n\t\t: `${endpointUrl.protocol}//${c.bucket}.${endpointUrl.host}`;\n\tconst objectPath = pathStyle ? `/${c.bucket}/${c.key}` : `/${c.key}`;\n\n\tasync function request(\n\t\tmethod: string,\n\t\tpayloadHashHex: string,\n\t\tbody?: BodyInit,\n\t\textraHeaders?: Record<string, string>,\n\t\tdeadlineMs = DATA_DEADLINE_MS\n\t): Promise<Response> {\n\t\tconst signed = await signS3({\n\t\t\tmethod,\n\t\t\torigin,\n\t\t\tpath: objectPath,\n\t\t\tregion: c.region,\n\t\t\taccessKeyId: c.accessKeyId,\n\t\t\tsecretAccessKey: c.secretAccessKey,\n\t\t\tpayloadHashHex,\n\t\t\textraHeaders\n\t\t});\n\t\treturn fetch(signed.url, {\n\t\t\tmethod,\n\t\t\theaders: signed.headers,\n\t\t\tbody,\n\t\t\tsignal: AbortSignal.timeout(deadlineMs)\n\t\t});\n\t}\n\n\t/** One HEAD, or null on a network/CORS failure (transient). */\n\tasync function probe(): Promise<Response | null> {\n\t\ttry {\n\t\t\treturn await request('HEAD', EMPTY_PAYLOAD_SHA256, undefined, undefined, META_DEADLINE_MS);\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\tasync function stat(): Promise<string | null> {\n\t\tconst res = await probe();\n\t\treturn res && res.ok ? versionOf(res) : null; // 404 / error / offline: cannot tell\n\t}\n\tasync function ready(): Promise<boolean> {\n\t\tconst res = await probe();\n\t\tif (!res) return false; // offline / CORS blocked: transient, retried later\n\t\t// 403 = the credentials are rejected (a HEAD with GetObject permission\n\t\t// returns 404, not 403, for a missing object - so 403 is a real auth loss,\n\t\t// not \"no backup yet\"). 404 = object not created yet, still writable.\n\t\tif (res.status === 403) {\n\t\t\tthrow new AuthExpiredError('S3 rejected the credentials (403).');\n\t\t}\n\t\treturn res.ok || res.status === 404;\n\t}\n\t/** Stored credentials: a reconnect just re-checks reachability, never mints new\n\t * keys, so a rejection is reported as false rather than thrown. */\n\tasync function reconnectProbe(): Promise<boolean> {\n\t\tconst res = await probe();\n\t\treturn !!res && (res.ok || res.status === 404);\n\t}\n\n\treturn {\n\t\tkind: 's3',\n\t\tlabel: labelOf(c),\n\t\tasync save(blob: Blob): Promise<string | null> {\n\t\t\tconst bytes = new Uint8Array(await blob.arrayBuffer());\n\t\t\tconst res = await request('PUT', await sha256Hex(bytes), bytes);\n\t\t\tif (res.status === 403) {\n\t\t\t\tthrow new AuthExpiredError('S3 rejected the credentials (403).');\n\t\t\t}\n\t\t\tif (!res.ok) throw new SelfstoreError('TARGET_WRITE_FAILED', `S3 PUT failed: ${res.status}`);\n\t\t\treturn versionOf(res) ?? (await stat());\n\t\t},\n\t\tasync load(): Promise<Blob | null> {\n\t\t\tconst res = await request('GET', EMPTY_PAYLOAD_SHA256);\n\t\t\tif (res.status === 404) return null;\n\t\t\tif (res.status === 403) {\n\t\t\t\tthrow new AuthExpiredError('S3 rejected the credentials (403).');\n\t\t\t}\n\t\t\tif (!res.ok) throw new SelfstoreError('TARGET_UNAVAILABLE', `S3 GET failed: ${res.status}`);\n\t\t\treturn res.blob();\n\t\t},\n\t\tstat,\n\t\tisReady: ready,\n\t\treconnect: reconnectProbe,\n\t\tasync disconnect(): Promise<void> {\n\t\t\tawait kv.del(CONFIG_KEY);\n\t\t\tawait kv.del(KEY_KEY);\n\t\t}\n\t};\n}\n\n/** Validate + persist the config, returning the target, or null when the bucket\n * is unreachable, rejects the credentials, or blocks the cross-origin request. */\nexport async function connect(opts: S3ConnectOptions): Promise<BackupTarget | null> {\n\tassertSecureEndpoint(opts.config.endpoint);\n\tconst target = fromConfig(opts.config, opts.kv);\n\t// isReady() throws on a 403 (a later rejection gates the store); at first\n\t// connect a rejection just means \"wrong credentials, try again\", so a throw\n\t// here reads the same as false - report null, persist nothing.\n\tlet ok: boolean;\n\ttry {\n\t\tok = await target.isReady();\n\t} catch {\n\t\tok = false;\n\t}\n\tif (!ok) return null;\n\tawait saveConfig(opts.kv, opts.config);\n\treturn target;\n}\n\n/** Rebuild the target from the encrypted config persisted last session, or null. */\nexport async function fromSession(opts: { kv: KV }): Promise<BackupTarget | null> {\n\tconst config = await loadConfig(opts.kv);\n\treturn config ? fromConfig(config, opts.kv) : null;\n}\n"]}