Skip to main content

browser_control/registry/
schema.rs

1//! SQLite schema for the browser registry.
2
3use anyhow::{Context, Result};
4
5const SCHEMA: &str = r#"
6CREATE TABLE IF NOT EXISTS browsers (
7  name        TEXT PRIMARY KEY,
8  kind        TEXT NOT NULL,
9  engine      TEXT NOT NULL,
10  pid         INTEGER NOT NULL,
11  endpoint    TEXT NOT NULL,
12  port        INTEGER NOT NULL,
13  profile_dir TEXT NOT NULL,
14  executable  TEXT NOT NULL,
15  headless    INTEGER NOT NULL,
16  started_at  TEXT NOT NULL
17);
18CREATE INDEX IF NOT EXISTS browsers_kind_started ON browsers(kind, started_at DESC);
19
20-- One scratch tab per browser. Used by lock-free ops (eval/fetch with no
21-- explicit tab) so they never touch a user-visible tab. The single-row
22-- shape is deliberate: concurrent CLI calls share the same scratch row
23-- (CDP allows multiple sessions per target), and recovery just rewrites
24-- target_id when the existing scratch is dead.
25CREATE TABLE IF NOT EXISTS scratches (
26  browser_name        TEXT PRIMARY KEY,
27  target_id           TEXT NOT NULL,
28  last_used_at_epoch_s INTEGER NOT NULL
29);
30
31-- Named tabs, addressed as `<browser>/<name>` across the CLI. `daemon_created`
32-- distinguishes tabs the CLI opened (eligible for sweep / LRU recycle) from
33-- tabs adopted from the user (kept verbatim, never GC'd).
34CREATE TABLE IF NOT EXISTS tabs (
35  browser_name         TEXT NOT NULL,
36  name                 TEXT NOT NULL,
37  target_id            TEXT NOT NULL,
38  last_url             TEXT NOT NULL,
39  last_used_at_epoch_s INTEGER NOT NULL,
40  daemon_created       INTEGER NOT NULL,
41  PRIMARY KEY (browser_name, name)
42);
43CREATE INDEX IF NOT EXISTS tabs_lru ON tabs(browser_name, daemon_created, last_used_at_epoch_s);
44
45-- Firefox BiDi allows one session per browser. This table arbitrates among
46-- concurrent CLI processes: holder_pid is the winning process, released
47-- on Drop (DELETE WHERE browser_name=? AND holder_pid=?). Stale rows from
48-- crashed CLIs are evicted on acquire via pid_alive().
49CREATE TABLE IF NOT EXISTS bidi_locks (
50  browser_name         TEXT PRIMARY KEY,
51  holder_pid           INTEGER NOT NULL,
52  acquired_at_epoch_s  INTEGER NOT NULL
53);
54"#;
55
56/// Apply the schema migration. Idempotent.
57pub fn apply(conn: &rusqlite::Connection) -> Result<()> {
58    conn.execute_batch(SCHEMA)
59        .context("applying registry schema")?;
60    Ok(())
61}