const ALLOWED_KEYS = new Set([ "selected_model_id", "fallback_order", "decode_interval_ms", "silence_finalize_ms", "rolling_context_ms", "caption_max_chars", "minimum_display_ms", "auto_start_stream", "offline_authorization_severity", "diagnostic_caption_text", "tracks" ]); class RevisionStore { constructor(db, options = {}) { this.db = db; this.now = options.now || Date.now; this.migrate(); } migrate() { this.db.exec(`CREATE TABLE IF NOT EXISTS transcription_settings ( key TEXT PRIMARY KEY, value_json TEXT NOT NULL, revision INTEGER NOT NULL, actor_id TEXT NOT NULL, updated_at INTEGER NOT NULL );`); } list() { return Object.fromEntries(this.db.prepare("SELECT * FROM transcription_settings ORDER BY key").all().map((row) => [row.key, decode(row)])); } apply(changes, actorId) { if (!Array.isArray(changes) || !changes.length || changes.length > 50) throw new Error("One to fifty field changes are required."); const normalized = changes.map(validateChange); const applied = []; const conflicts = []; this.db.transaction(() => { for (const change of normalized) { const current = this.db.prepare("SELECT * FROM transcription_settings WHERE key = ?").get(change.key); const currentRevision = current?.revision || 0; if (change.base_revision !== currentRevision) { conflicts.push({ key: change.key, local_value: change.value, local_base_revision: change.base_revision, server: current ? decode(current) : { value: null, revision: 0, actor_id: null, updated_at: null } }); continue; } const revision = currentRevision + 1; const updatedAt = this.now(); this.db.prepare("INSERT INTO transcription_settings (key, value_json, revision, actor_id, updated_at) VALUES (?, ?, ?, ?, ?) ON CONFLICT(key) DO UPDATE SET value_json = excluded.value_json, revision = excluded.revision, actor_id = excluded.actor_id, updated_at = excluded.updated_at") .run(change.key, JSON.stringify(change.value), revision, String(actorId || "unknown"), updatedAt); applied.push({ key: change.key, value: change.value, revision, actor_id: String(actorId || "unknown"), updated_at: updatedAt }); } })(); return { applied, conflicts, current: this.list() }; } } function validateChange(change) { if (!change || !ALLOWED_KEYS.has(change.key)) throw new Error(`Setting key ${change?.key || "(missing)"} is not allowed.`); const baseRevision = Number(change.base_revision); if (!Number.isInteger(baseRevision) || baseRevision < 0) throw new Error("A non-negative base revision is required for every changed field."); const encoded = JSON.stringify(change.value); if (encoded == null || Buffer.byteLength(encoded) > 64 * 1024) throw new Error("Setting value is too large."); return { key: change.key, value: change.value, base_revision: baseRevision }; } function decode(row) { return { value: JSON.parse(row.value_json), revision: row.revision, actor_id: row.actor_id, updated_at: row.updated_at }; } module.exports = { RevisionStore, ALLOWED_KEYS };