// SQLite-backed HTTP response cache for the dev server's /__proxy__ endpoint.
//
// Project-local: stored at
/http-cache.db, where is the project's
// .solidrt-data. Entries live forever; delete the file to drop them.
//
// Cached: GET (and HEAD) 2xx responses with no Authorization on the request
// and no Cache-Control: no-store on either side. The cache key is
// method + "\n" + url, stored raw; headers are intentionally not part of the
// key. (The Bun predecessor hashed the key with sha256, which was cosmetic:
// old hashed entries simply miss and re-populate.)
import { Database } from "flux:sqlite"
import { dir } from "flux:fs"
import { join } from "flux:path"
const CACHE_FILE = "http-cache.db"
export type Decision = "hit" | "miss" | "bypass" | "skip"
export type Entry = {
method: string
url: string
status: number
headers: Record
body: Uint8Array
cachedAt: number
}
let db: Database | null = null
let enabled = false
export async function initCache(opts: { dir: string }) {
await dir(opts.dir).create()
let d = await Database.open(join(opts.dir, CACHE_FILE), "rw+")
await d.exec(`CREATE TABLE IF NOT EXISTS entries (
key TEXT PRIMARY KEY,
method TEXT NOT NULL,
url TEXT NOT NULL,
status INTEGER NOT NULL,
headers TEXT NOT NULL,
body BLOB NOT NULL,
cached_at INTEGER NOT NULL
)`)
db = d
enabled = true
}
export function isEnabled(): boolean {
return enabled
}
function keyFor(method: string, url: string): string {
return method + "\n" + url
}
function cacheableMethod(method: string): boolean {
return method === "GET" || method === "HEAD"
}
function hasNoStore(headerVal: string | null): boolean {
if (!headerVal) return false
return /(^|,)\s*no-store(\s*,|$)/i.test(headerVal)
}
function hasNoCache(headerVal: string | null): boolean {
if (!headerVal) return false
return /(^|,)\s*no-cache(\s*,|$)/i.test(headerVal)
}
export function shouldConsider(method: string, reqHeaders: Headers): { skip: boolean } {
if (!enabled) return { skip: true }
if (!cacheableMethod(method)) return { skip: true }
if (reqHeaders.has("authorization")) return { skip: true }
if (hasNoStore(reqHeaders.get("cache-control"))) return { skip: true }
return { skip: false }
}
export function isBypass(reqHeaders: Headers): boolean {
if (reqHeaders.get("x-solidrt-cache")?.toLowerCase() === "bypass") return true
if (hasNoCache(reqHeaders.get("cache-control"))) return true
return false
}
export async function get(method: string, url: string): Promise {
if (!db || !enabled) return null
let row = await db
.query("SELECT method, url, status, headers, body, cached_at FROM entries WHERE key = ?")
.get([keyFor(method, url)])
if (!row) return null
return {
method: row.method as string,
url: row.url as string,
status: row.status as number,
headers: JSON.parse(row.headers as string),
body: row.body as Uint8Array,
cachedAt: row.cached_at as number,
}
}
export async function put(
method: string,
url: string,
status: number,
headers: Record,
body: Uint8Array,
) {
if (!db || !enabled) return
if (status < 200 || status >= 300) return
if (hasNoStore(headers["cache-control"] ?? null)) return
await db.run(
`INSERT OR REPLACE INTO entries
(key, method, url, status, headers, body, cached_at)
VALUES (?, ?, ?, ?, ?, ?, ?)`,
[keyFor(method, url), method, url, status, JSON.stringify(headers), body, Date.now()],
)
}