/** * The `.cleo/backups/sqlite/` system-backup file scheme shared by manual * backups (`system/backup.ts#createBackup`) and the store's own migration * snapshots (`store/pre-repair-snapshot.ts`): the `YYYYMMDD-HHmmss` * timestamp, the per-type rotation, and the `.meta.json` sidecar that * `cleo backup list` reads. * * A leaf module (node:fs, the logger, and a read-only node:sqlite read of the * twin-collapse markers during rotation), so store code that runs inside a domain bind * can write an inventoried snapshot without importing `system/backup.ts`, * which imports the domain binders. * * @module * @task T10315 * @task T12535 — extracted from `system/backup.ts` */ /** * Format a Date as `YYYYMMDD-HHmmss` (local time) — mirrors the helper of * the same name in `sqlite-backup.ts` so both auto-snapshot and manual * snapshot files in `.cleo/backups/sqlite/` share one timestamp convention. * * @task T10315 — unified with `sqlite-backup.ts:formatTimestamp`. */ export declare function formatBackupTimestamp(d: Date): string; /** * Rotate backups in a directory: delete the oldest BACKUPS until no more than * `maxSnapshots` remain. Files are grouped by backup id (`.`), * and a backup's files are kept or deleted together (T13710: counting files * let one backup with many payloads delete its own store snapshot). * * Only rotates files matching the `createBackup` filename scheme * (`.-YYYYMMDD-HHmmss` for the canonical timestamp shape OR * `.-` for backward compatibility) so it never * touches files produced by `vacuumIntoBackupAll` in the same directory. * Non-fatal — filesystem errors are silently swallowed. * * A pinned backup, and a pre-collapse snapshot a twin-collapse marker of the * project store references (pinned or not: 2026.9.21 wrote them without a * pin), is never rotated and does not count toward the cap. When a marker * cannot be read (malformed, or the store busy), which snapshot it names is * unknown: every `migration` backup is kept and a warning is logged, while * other backup types still rotate (pre-collapse snapshots are always * `migration` backups). `cleo doctor` names an unreadable marker. * * @task T9194 * @task T10315 — added scoping predicate so rotation never reaches * vacuum-snapshot files that share `.cleo/backups/sqlite/`. * @task T12727 — marker-referenced snapshots are never rotated. * @task T12770 — an unreadable marker keeps only `migration` backups, and says so. * @task T12729 — a rotated backup's `.meta.json` sidecar goes with it. * @task T13710 — the cap counts backups, not files. * @returns The files and sidecars deleted, and why rotation kept backups past the cap. */ export declare function rotateBackupDir(backupDir: string, maxSnapshots: number, backupType: string): BackupRotation; /** * Remove the `.meta.json` sidecars of `backupType` whose backup has * no file left (`.` for each file the sidecar lists), so * `cleo backup list` stops listing them (T12729). Builds before T12729 * deleted `..meta.json` on rotation, a name no sidecar has, * so every rotated backup left one behind. A pinned sidecar, one that does * not parse (it cannot be shown unpinned) and one listing no files are never * removed. * Non-fatal. * * @param backupDir - The backup directory. * @param backupType - The backup type whose sidecars are swept. * @returns The sidecar files removed. */ export declare function sweepOrphanSidecars(backupDir: string, backupType: string): string[]; /** What {@link rotateBackupDir} did. */ export interface BackupRotation { /** Backup files deleted. */ readonly deleted: readonly string[]; /** Sidecars removed because their backup has no file left (T12729). */ readonly sidecars: readonly string[]; /** * Why every backup of the type was kept past the cap: a marker that does * not parse, or a store that could not be read (busy, corrupt). `null` when * rotation ran. */ readonly keptAll: 'marker-unreadable' | 'store-unreadable' | null; /** The unreadable marker keys (`:twin_collapse:`). */ readonly unreadable: readonly string[]; /** The error reading the store (for example `database is locked`), or `null`. */ readonly error: string | null; } /** The twin-collapse markers of a project store, as rotation reads them. */ export interface MarkerSnapshots { /** File names of the pre-collapse snapshots the readable markers reference. */ readonly names: ReadonlySet; /** Marker keys whose value does not parse (`:twin_collapse:
`). */ readonly unreadable: readonly string[]; /** Why the store could not be read at all (busy, corrupt), or `null`. */ readonly error: string | null; } /** * The pre-collapse snapshots the twin-collapse markers of the project store * (`/../../cleo.db`) reference, and the markers that cannot be * read. No store there: no markers. * * @param backupDir - `/.cleo/backups/sqlite`. * @returns The referenced file names, the unreadable marker keys, and any read error. * @task T12727 * @task T12770 */ export declare function readMarkerSnapshots(backupDir: string): MarkerSnapshots; /** Contents of a `.meta.json` sidecar, as `cleo backup list` reads it. */ export interface BackupSidecar { /** `-YYYYMMDD-HHmmss`. */ readonly backupId: string; /** Backup category (`snapshot`, `safety`, `migration`). */ readonly type: string; /** ISO-8601 creation time. */ readonly timestamp: string; /** Free-text note shown by `cleo backup list`. */ readonly note?: string; /** Files captured, each stored as `.` next to the sidecar. */ readonly files: readonly string[]; /** * Never rotated (T12535): the snapshot a twin collapse took before it * changed the store. It is the only copy of any row the collapse replaced, * so it outlives the rotation cap until an owner removes it. */ readonly pinned?: boolean; /** Why it is pinned (shown by `cleo backup list`). */ readonly pinnedReason?: string; /** When an owner released the pin (T12767); rotation treats it as any backup. */ readonly releasedAt?: string; /** Why it was released. */ readonly releasedReason?: string; } /** * Write a `.meta.json` sidecar atomically (tmp file, then rename). * * @param backupDir - The `.cleo/backups/sqlite/` directory. * @param sidecar - The sidecar contents. * @task T12535 */ export declare function writeBackupSidecar(backupDir: string, sidecar: BackupSidecar): void; /** * Whether a backup file is pinned (its sidecar says `pinned: true`). * * @param backupDir - The backup directory. * @param fileName - The backup file (`.-`). * @param backupType - Its backup type. * @returns `true` when pinned. * @task T12535 */ export declare function isPinnedBackup(backupDir: string, fileName: string, backupType: string): boolean; /** * Pin the backup a file belongs to, so rotation never deletes it. Writes the * sidecar (`pinned: true`, `pinnedReason`), creating it for a snapshot that has * none. Idempotent and best effort: returns whether the backup is pinned now. * * @param snapshotPath - The backup file (`/.-`). * @param backupType - Its backup type (`migration` for twin-collapse snapshots). * @param reason - Why it is pinned. * @returns `true` when the backup is pinned after the call. * @task T12535 */ export declare function pinBackup(snapshotPath: string, backupType: string, reason: string): boolean; /** * Release a backup's pin (T12767) so rotation treats it as any other backup: * rewrites its sidecar with `pinned: false`, `releasedAt` and * `releasedReason`, dropping `pinnedReason`. Writes the sidecar only, never * the backup; creates the sidecar when there is none. * * @param snapshotPath - The backup file (`/.-`). * @param backupType - Its backup type. * @param reason - Why it is released. * @returns The sidecar as written. * @throws When the backup name does not carry `backupType`, or the write fails. * @task T12767 */ export declare function unpinBackup(snapshotPath: string, backupType: string, reason: string): BackupSidecar; /** * Put back a sidecar exactly as it was before {@link unpinBackup} (or remove * it when there was none). Best effort, for a release that is rolled back. * * @param snapshotPath - The backup file. * @param backupType - Its backup type. * @param previous - The sidecar before the release, or `null`. * @task T12767 */ export declare function restoreBackupSidecar(snapshotPath: string, backupType: string, previous: BackupSidecar | null): void; /** * A backup's sidecar as read back, or `null` (missing, unreadable, malformed). * * @param snapshotPath - The backup file. * @param backupType - Its backup type. * @task T12767 */ export declare function readBackupSidecar(snapshotPath: string, backupType: string): BackupSidecar | null; //# sourceMappingURL=backup-sidecar.d.ts.map