rigjs
Version:
A multi-repos dev tool based on yarn and git.Rigjs is intended to be the simplest way to develop,share and deliver codes between different developers or different projects.
101 lines (91 loc) • 3.47 kB
text/typescript
import fs from 'fs';
import { paths } from './paths';
// Lazy load so unit tests that don't touch the DB don't need the native binary.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type Database = any;
let cached: Database | null = null;
export function getDb(): Database {
if (cached) return cached;
fs.mkdirSync(paths.home, { recursive: true });
// eslint-disable-next-line @typescript-eslint/no-var-requires
const BetterSqlite3 = require('better-sqlite3');
const db = new BetterSqlite3(paths.stateDb);
db.pragma('journal_mode = WAL');
db.pragma('foreign_keys = ON');
migrate(db);
cached = db;
return db;
}
export function closeDb() {
if (cached) {
try { cached.close(); } catch { /* ignore */ }
cached = null;
}
}
function migrate(db: Database): void {
db.exec(`
CREATE TABLE IF NOT EXISTS source_sha (
wiki TEXT NOT NULL,
path TEXT NOT NULL,
sha TEXT NOT NULL,
mtime INTEGER NOT NULL,
PRIMARY KEY (wiki, path)
);
CREATE TABLE IF NOT EXISTS last_run (
wiki TEXT NOT NULL,
op TEXT NOT NULL,
ts INTEGER NOT NULL,
exit_code INTEGER NOT NULL,
PRIMARY KEY (wiki, op)
);
CREATE TABLE IF NOT EXISTS ingest_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
wiki TEXT NOT NULL,
source_path TEXT NOT NULL,
ts INTEGER NOT NULL,
diff_hash TEXT NOT NULL,
applied INTEGER NOT NULL
);
CREATE VIRTUAL TABLE IF NOT EXISTS pages_fts5 USING fts5(
wiki, slug, body, tokenize='unicode61 remove_diacritics 2'
);
`);
}
export function recordLastRun(wiki: string, op: string, exitCode: number): void {
const db = getDb();
db.prepare(
`INSERT INTO last_run (wiki, op, ts, exit_code) VALUES (?, ?, ?, ?)
ON CONFLICT(wiki, op) DO UPDATE SET ts = excluded.ts, exit_code = excluded.exit_code`
).run(wiki, op, Date.now(), exitCode);
}
export function getLastRun(wiki: string, op: string): { ts: number; exit_code: number } | undefined {
return getDb()
.prepare('SELECT ts, exit_code FROM last_run WHERE wiki = ? AND op = ?')
.get(wiki, op) as { ts: number; exit_code: number } | undefined;
}
/**
* Upsert one source-sha row. Path is the **root-relative** path (same key
* `scan` uses), so subsequent `scan` runs can detect drift via the same map.
*/
export function upsertSourceSha(wiki: string, relPath: string, sha: string, mtimeMs: number): void {
getDb().prepare(
`INSERT INTO source_sha (wiki, path, sha, mtime) VALUES (?, ?, ?, ?)
ON CONFLICT(wiki, path) DO UPDATE SET sha = excluded.sha, mtime = excluded.mtime`
).run(wiki, relPath, sha, Math.floor(mtimeMs));
}
/** Bulk-upsert variant for `scan --baseline`. Uses a single transaction. */
export function upsertSourceShasBulk(wiki: string, rows: { path: string; sha: string; mtimeMs: number }[]): void {
if (rows.length === 0) return;
const db = getDb();
const stmt = db.prepare(
`INSERT INTO source_sha (wiki, path, sha, mtime) VALUES (?, ?, ?, ?)
ON CONFLICT(wiki, path) DO UPDATE SET sha = excluded.sha, mtime = excluded.mtime`
);
const tx = db.transaction((items: { path: string; sha: string; mtimeMs: number }[]) => {
for (const r of items) stmt.run(wiki, r.path, r.sha, Math.floor(r.mtimeMs));
});
tx(rows);
}
export function deleteSourceSha(wiki: string, relPath: string): void {
getDb().prepare('DELETE FROM source_sha WHERE wiki = ? AND path = ?').run(wiki, relPath);
}