Persist profile state in SQLite with background writes (#22612)

Migrate profile state to SQLite and move writes and backups into a background worker. Acknowledge terminal, SSH and automation changes only after durable saves. Preserve JSON import, recovery, rollback and compatibility exports.

Validate migration, worker failures, maintenance, cross-profile moves and terminal lifetime races with unit, integration and end-to-end coverage.
This commit is contained in:
OrcaWin
2026-09-25 22:47:33 -07:00
committed by GitHub
parent f0a3610928
commit 82412dab8b
490 changed files with 41290 additions and 3485 deletions
+25 -1
View File
@@ -1,5 +1,5 @@
import { existsSync } from 'node:fs'
import type { DatabaseSync, StatementSync, SQLInputValue } from 'node:sqlite'
import type { backup, BackupOptions, DatabaseSync, StatementSync, SQLInputValue } from 'node:sqlite'
type SqlitePath = ConstructorParameters<typeof DatabaseSync>[0]
@@ -36,6 +36,15 @@ function loadDatabaseSync(): typeof DatabaseSync {
.DatabaseSync
}
function hasBackup(value: unknown): value is { backup: typeof backup } {
return (
typeof value === 'object' &&
value !== null &&
'backup' in value &&
typeof value.backup === 'function'
)
}
class SyncDatabase {
private readonly db: DatabaseSync
private readonly statementCache = new Map<string, StatementSync>()
@@ -100,6 +109,21 @@ class SyncDatabase {
return this.db.isTransaction
}
/** The source connection must remain open until the native backup settles. */
async backup(path: string, options?: BackupOptions): Promise<number> {
const sqlite: unknown =
typeof process.getBuiltinModule === 'function'
? process.getBuiltinModule('node:sqlite')
: undefined
if (!hasBackup(sqlite)) {
throw new Error('Asynchronous SQLite backup is unavailable in this Node.js runtime')
}
if (this.db.isTransaction) {
throw new Error('Asynchronous SQLite backup requires an idle database connection')
}
return sqlite.backup(this.db, path, options ?? {})
}
close(): void {
this.statementCache.clear()
this.db.close()