A synchronous, in-memory implementation of the NodeOp effect runner, primarily used for testing.
import { virtual, virtualAt, emptyState } from './module.f.mjs'
const [finalState, result] = virtual(emptyState)(myEffect)
When all a run needs is a tree of files, virtualAt(root) is virtual({ ...emptyState, root }).
State carries in-memory representations of the filesystem (root), stdout/stderr output (stdout, stderr), network responses (internet), and a simulated clock (epochNs).
resolveFileModule uses a lexical path profile: a literal entry path, or an admitted portable URL-path spelling resolved against a normalized path identity. Identity is the normalized filesystem path. Only import specifiers are decoded; entry paths and parent identities are already filesystem names. The fixture filesystem has no working directory or symlinks. This preserves the virtual host's path model; it does not simulate Node's file URL/realpath rules. Compiler traversal is tested here, and native ESM comparisons test those Node rules in the Node adapter's sibling proof.
Because the virtual runner executes effects synchronously and sequentially, it serialises operations that would run concurrently in production. This makes it a useful tool for detecting potential race conditions: if two effects would conflict when run concurrently (e.g. both writing to the same file), the virtual runner will expose the problem deterministically — the second write always sees the result of the first.
Example: concurrent writes to the same file via Promise.all in the real runner may interleave arbitrarily, but the virtual runner always applies them in a fixed order, making the conflict visible and reproducible in tests.