Files
OmniRoute/src/lib/db/creditBalance.ts
2026-04-16 12:53:37 -03:00

93 lines
2.7 KiB
TypeScript

/**
* Antigravity GOOGLE_ONE_AI credit balance persistence.
*
* Stores last-known credit balances in the `key_value` table so they survive
* server restarts. The in-memory `creditBalanceCache` in the executor is the
* primary source at runtime; this module provides the fallback read/write layer.
*/
import { getDbInstance, isBuildPhase, isCloud } from "./core";
interface StatementLike<TRow = unknown> {
get: (...params: unknown[]) => TRow | undefined;
run: (...params: unknown[]) => { changes?: number };
all: (...params: unknown[]) => TRow[];
}
interface DbLike {
prepare: <TRow = unknown>(sql: string) => StatementLike<TRow>;
}
interface KeyValueRow {
key: string;
value: string;
}
interface CreditBalanceEntry {
balance: number;
updatedAt: string;
}
const NAMESPACE = "antigravityCreditBalance";
function parseJson(raw: string): unknown {
try {
return JSON.parse(raw);
} catch {
return null;
}
}
/**
* Read the persisted credit balance for an accountId.
* Returns the balance number, or null if not found.
*/
export function getPersistedCreditBalance(accountId: string): number | null {
if (isBuildPhase || isCloud) return null;
const db = getDbInstance() as unknown as DbLike;
const row = db
.prepare("SELECT value FROM key_value WHERE namespace = ? AND key = ?")
.get(NAMESPACE, accountId) as KeyValueRow | undefined;
if (!row?.value) return null;
const parsed = parseJson(row.value) as CreditBalanceEntry | null;
if (!parsed || typeof parsed.balance !== "number") return null;
return parsed.balance;
}
/**
* Read all persisted credit balances.
* Returns a Map of accountId → balance.
*/
export function getAllPersistedCreditBalances(): Map<string, number> {
const result = new Map<string, number>();
if (isBuildPhase || isCloud) return result;
const db = getDbInstance() as unknown as DbLike;
const rows = db
.prepare("SELECT key, value FROM key_value WHERE namespace = ?")
.all(NAMESPACE) as KeyValueRow[];
for (const row of rows) {
const parsed = parseJson(row.value) as CreditBalanceEntry | null;
if (parsed && typeof parsed.balance === "number") {
result.set(row.key, parsed.balance);
}
}
return result;
}
/**
* Persist a credit balance for an accountId.
*/
export function persistCreditBalance(accountId: string, balance: number): void {
if (isBuildPhase || isCloud) return;
const db = getDbInstance() as unknown as DbLike;
const entry: CreditBalanceEntry = {
balance,
updatedAt: new Date().toISOString(),
};
db.prepare("INSERT OR REPLACE INTO key_value (namespace, key, value) VALUES (?, ?, ?)").run(
NAMESPACE,
accountId,
JSON.stringify(entry)
);
}