v0.2.3
Say when a run cannot drain, and stop ignoring a nested config block
Two reports, both about the runner going quiet where it should speak.
bin/helix.js already told you when a finished run could not drain, but
ream test does not go through it — it goes through
@c9up/helix-plugin-ream, which just returned. So the summary printed,
every test green, and then nothing: no prompt, no output, and under a CI
timeout an exit 124 scored as a failure. The guard is extracted here as
armDrainGuard so the plugin runs the same code. bin/helix.js keeps
its own copy on purpose — it calls the guard from the error path too,
where the build may never have been imported, and an exit handler that
can fail to load is worse than a duplicated one.
Separately, a config whose settings sit under a tests block — the
shape of reamrc.ts, and of adonisrc.ts before it — was read as one
unknown key and dropped. Nothing said so: the declared bootstrap was
ignored and the conventional tests/bootstrap.ts ran in its place. In
the reported case that bootstrap starts the application, so 25 tests
that needed no server opened one and then hung — the first bug,
triggered by a file that had nothing to do with them. The nested form is
now read, with the flat one winning where both carry a key so a working
config keeps working.
Release the work that landed after the last published version
The registry now carries the version this package.json was still on, so
everything committed since ships under the next one rather than
retroactively changing what a published version means.
Build the cross-platform matrix only when the version moves
The five-runner matrix exists to produce the prebuilt binaries a release
ships. It ran on every push to main, rebuilding artefacts nobody
downloads — five runners, every time, for a comment fix.
A version-gate job now compares the package version at HEAD^ with
the one at HEAD and the matrix runs only when they differ. Anything
that is not a push passes the gate unconditionally, so workflow_dispatch
— how a release is actually cut — is unaffected, and so is publish,
which still waits on the full matrix. A missing HEAD^ reads as a bump:
erring towards building is the safe direction.
The test signal deliberately does NOT move with it. quality and the
cargo/integration jobs were already independent of the matrix, but
vitest ran INSIDE it, so gating the matrix alone would have quietly
taken the TypeScript suite off every ordinary push. A ts-tests job now
runs it on ubuntu, building its own napi binary rather than waiting on a
gated artefact. On a push without a bump that leaves typecheck, lint,
cargo and vitest — one runner instead of five.
Derive the native TypeScript surface from the Rust
The hand-written interface describing the .node binary was a second
description of the same thing, and nothing on this side noticed when the
first one changed: a pub fn could gain a parameter, stop being async
or change its return with the declaration still claiming otherwise.
napi-derive can emit the declarations itself. Its type-def feature
writes one JSON line per #[napi] item while cargo compiles;
scripts/build-napi-types.mjs collects them and
scripts/generate-napi-types.mjs turns them into
src/native/generated.ts. build:napi regenerates it, and the
TypeScript side consumes it instead of restating it.
Three things the generation had to handle:
type-defAPPENDS to its output file, so a parallel cargo build
interleaves the writes and definitions go missing, silently, leaving
the generated file short. Crates are built one at a time, and a crate
that emits nothing fails the script rather than producing a partial
surface.- A Rust doc example holding a cron expression (
0 */5 * * *) closes
the generated comment early. Every*/is escaped except the one
that legitimately closes the block — escaping that one breaks the
file just as thoroughly. - The driver is Node rather than bash, because
build:napialso runs
on the Windows prebuild runner: a Git Bashmktemppath is not
something the native proc-macro can write to, and the type-def file
would come back empty with nothing to explain why.
The generated file is a .ts holding only ambient declarations rather
than a .d.ts, so tsc carries it into dist/native/ and the
reference from the emitted declarations still resolves for consumers.
Changes since v0.2.2.