Public: anyone can read this; the crew can change it.
forked from trevato/cs · proposes · 4 behind
The engine loads without a browser, and a test holds it to that #30
Check · Review · Land · Live done — Landed and live in production.
`src/engine/purity.test.ts` now holds the engine to the second half of what makes it a package: it imports nothing outside itself (`boundary.test.ts`) and it needs nothing from a browser to load (this). The list of modules is `sources()` from `boundary.test.ts`, filtered to what ships, so a module added tomorrow is covered the day it lands; the imports happen in a second process because a dynamic…
shipped production is serving it deploy ae07c2d
Sign in to join this conversation.
new above
offline
src/engine/purity.test.tsnow holds the engine to the second half of what makes it a package: it imports nothing outside itself (boundary.test.ts) and it needs nothing from a browser to load (this). The list of modules issources()fromboundary.test.ts, filtered to what ships, so a module added tomorrow is covered the day it lands; the imports happen in a second process because a dynamicimport()is cached per process and the engine's own tests have already loaded every module by the time the runner gets here — a second import in-process would execute nothing and prove nothing.That process is
src/engine/purity.probe.ts: oneBun.spawnof a constant command (process.execPath run <probe beside the test>, no arguments from anywhere), the module names on stdin, each checked against a name pattern and rejected if it is not a plain relative.tspath, and one JSON line of report back. Before it imports anything it installswindow,document,navigator,AudioContext,webkitAudioContext,requestAnimationFrameandlocalStorageas getters that record the access and answerundefined, so reading one and coping fails just as hard as throwing. It then readsAudioContexton purpose — the parent asserts that canary is in the report, so a trap that silently failed to install cannot report a clean run — restores every descriptor in afinally, and reports any global the undo did not put back; the parent asserts that list is empty and that its own globals were never touched.Demonstrated both failures by hand and reverted them. A top-level
const ctx = new AudioContext()insound.tsfailed two tests:failurescame back[{module: "sound.ts", error: "undefined is not a constructor (evaluating 'new AudioContext')"}]andtouchescame back["sound.ts: read AudioContext"]. The quieter version,const HAS_AUDIO = typeof AudioContext !== "undefined", throws nothing and still failed on["sound.ts: read AudioContext"]— which is the case this exists for.The engine needed no fixing:
sound.tsalready opens its context on first sound behind aCtorparameter andcontrols.tsalready takes its host, and the run is clean. README's shape section states both rules in one sentence each, side by side.bun testis green at 255 tests across 33 files in 3.7–4.0 s (it was 3.6–4.1 s before; the probe's process costs about 170 ms for the whole file, boundary's tests registering a third time included).🤖 Generated with Claude Code
Closes #26