Package map
Argus is a pnpm workspace. Each package has one job and one rule.
| Package | Role | Rule |
|---|---|---|
@argus/core |
Pure domain types, ports, result-protocol parser, Hermes version/pin/asset logic | No adapter or runtime imports — not even node:path |
@argus/framework |
Runs inside Hermes: globals, runner, matchers, mocks, result emission | Protect the result channel |
@argus/rntl |
Synchronous component-testing facade, exposed through the argus alias |
A replaceable stopgap, maintained separately |
@argus/esbuild |
Bundles polyfills + framework + tests into one IIFE | Owns syntax lowering and the virtual entry |
@argus/hermes |
Spawns the standalone VM, resolves engines, downloads prebuilts, builds from source | Never use stdin — stdin puts Hermes in REPL mode |
@argus/sourcemap |
Remaps Hermes stack frames back to original sources | Must be total: never throw during reporting |
@argus/reporter-cli |
Terminal output and exit-code policy | Report test failures separately from infra failures |
@argus/cli |
Composition root | Wires adapters; keeps the domain pure |
Why core is this pure
Section titled “Why core is this pure”@argus/core holds things two sides must agree on exactly:
- Hermes tag parsing. The provisioning chain and the release pipeline must derive the same identity from the same ref, or a release is published under one name and downloaded under another.
- On-disk layout. The source-build adapter writes the cache; the provisioning chain reads it. Duplicated path segments drift into a cache written to one path and looked up at another.
- Asset names and release notes. CI publishes the assets, the chain downloads them. The one place that knows what an asset is called is the one place that describes it — which is also why the release body is generated by a function with a test, not inlined in YAML.
Paths are expressed as segments, not joined strings, because core may not import
node:path. Callers join them with their platform’s separator.
The framework is a different world
Section titled “The framework is a different world”@argus/framework is the only package that runs inside Hermes. Everything about it follows
from that:
- Bundled, never resolved at run time.
- Constrained to the syntax envelope.
- Index loops and captured primordials in the reporting path (why).
Host-side packages are ordinary Node and have none of these constraints. Do not carry framework discipline into the CLI, and do not carry CLI habits into the framework.
Dependency policy
Section titled “Dependency policy”@argus/cli and @argus/reporter-cli have zero external runtime dependencies. That is
deliberate: for a tool whose pitch is engine fidelity at unit-test cost, a small install is
part of the argument.
Node’s built-ins cover more than they used to:
| Need | Built-in |
|---|---|
| Argument parsing | node:util.parseArgs |
Colour, with NO_COLOR / FORCE_COLOR handling |
node:util.styleText |
| Glob discovery | fs.promises.glob |
| Loading a TypeScript config | Native type stripping |
The only external runtime dependencies in the whole workspace are esbuild (the bundler),
source-map (isolated in @argus/sourcemap), Babel (legacy class lowering only), and
React plus test-renderer inside the optional component-testing package.
Every added dependency needs a reason a built-in cannot satisfy.
File size
Section titled “File size”Files stay at or below roughly 500 lines; modules get split when they cross it. Precedent:
matchers.ts became matchers.ts + deep-equal.ts + show.ts + async-matchers.ts +
expect-state.ts; the runner’s helpers became jest-api.ts + hooks.ts.