Config evaluation
The config is TypeScript, evaluated by an engine embedded in the Rune binary. There is no Node
process, no tsc, and no toolchain to install. A cold evaluation is a few milliseconds and a warm
run reads one cached JSON file.
What works
TypeScript syntax is erased before evaluation. Annotations, interfaces and type aliases have no runtime effect, and constructs that do carry runtime meaning keep it.
Variables, template strings, spread, Object.fromEntries, functions, enum members and relative
imports all behave as expected.
Relative imports
A config may import other files in the repository with ./ and ../ specifiers. Each imported
file goes through the same pipeline, recursively.
Forward slashes and platform separators resolve to the same file, so a Windows and a Linux machine produce the same result and share one cache entry.
The rune global
Configs get one ambient object, frozen.
Reads are recorded. A variable the config actually read enters the cache key; an unrelated variable changing does not throw away a valid entry.
Assignment to rune throws. The object is frozen, and env is a proxy that rejects writes, so a
config cannot change what Rune resolves with by mutating the global.
What does not work
The engine is not Node. Reaching for a Node global or module produces a message that lists what is available:
Type-only imports from npm packages are fine, because they are erased before anything runs:
Dynamic import() is rejected for cache integrity. A computed specifier cannot be found by the
static import walk, so it would escape the hashed import graph and serve a stale result.
Errors
A config that throws reports the file and a line in the TypeScript the user wrote, not in the JavaScript the engine ran. Type stripping reprints the file, so the two line numbers differ. Rune builds a source map on the failure path and remaps the frame, which costs nothing on the runs that succeed.
A syntax error names the file and points at the span. A missing relative import names both the missing file and the file that imported it.
Caching
The resolved config is stored at node_modules/.cache/rune/ under a key made from:
- the entry file's bytes
- the bytes of every transitively imported relative file
- the Rune binary version
- the platform
- the environment values the config read
The cache is content addressed. There is no expiry and no timestamp comparison, so restoring a file to its original bytes reuses the original entry.
Every cache failure degrades to full evaluation with nothing on stderr. An unreadable entry is a miss. An unwritable cache directory is a miss that still exits 0. A caching layer that can break a build is worse than no caching layer.
Clear it with:
Budget
A benchmark in CI fails the build when the warm total regresses.