Expand description
Cross-process disk cache for compiled QuickJS bytecode.
Compiling a rolldown bundle to bytecode (bundle_and_compile) or a
plugin file (compile_and_extract_plugins) costs ~15 ms cold per
process. The in-memory cache only helps within one process; a fresh
ferridriver bdd / MCP server start pays it again. This persists the
bytecode (plus its source map / manifests) to disk so an unchanged
source tree skips BOTH rolldown and the QuickJS compile entirely.
§Soundness
Module::load on bytecode is unsafe: it trusts the input was
produced by an identical QuickJS build with native endianness. A disk
cache crosses process (and machine) boundaries, so every entry lives
under an abi_tag-named directory folding the QuickJS version
(which tracks the on-disk BC_VERSION), target arch, endianness, and
pointer width. Bytecode is only ever loaded from the directory
matching the running toolchain — a mismatched build simply misses and
recompiles. Bumping rquickjs changes JS_GetVersion() and thus the
directory, so stale bytecode is never loaded.
§Freshness
A bundle inlines its whole import graph, so the entry file’s hash is
not enough — an edited (but still-imported) helper must invalidate.
Each entry records the content hash of every transitive input (the
source map’s sources); a load re-hashes them all and misses on any
change, addition, or deletion.
Structs§
- Cache
Entry - One cached compile: the bytecode plus the auxiliary data each caller needs to reconstruct its result without re-running rolldown.
Functions§
- collect_
inputs - Collect the transitive input set for a bundle: the entry files plus
every
sourcesentry in the source map, canonicalized and deduped. - entry_
key - A stable key for a set of entry paths (canonicalized, order-independent). The transitive content check on load is what actually guards freshness; this only needs to be collision-free across distinct bundle requests.
- load
- Load a cached compile for
key, validating that every recorded input still hashes identically. ReturnsNoneon any miss, mismatch, or IO error (the caller then compiles andstores). - store
- Persist a freshly compiled
key-> bytecode entry. Best-effort: any IO failure is swallowed (the cache is an optimization, never a correctness dependency). Writes are atomic via temp-file + rename so a concurrent or crashed writer never exposes a torn manifest.