Skip to content

0.20.1 — The types tell the truth: passes on every target

Choose a tag to compare

@danmolitor danmolitor released this 07 Sep 03:30
· 298 commits to main since this release

Patch release: the declared types now tell the truth on every target.

Fixed: passes is returned everywhere the type declares it

RenderHtmlResult.passes (the layout-pass count) was declared in @formepdf/html's types but returned by only one of the six result constructions — node renderHtml. The browser and worker entries, and renderHtmlWithLayout on all three targets, returned undefined while type-checking fine. Found building a Cloudflare Workers template against the published package.

The hole went three layers down: three hand-written per-target result objects (one updated when passes was added), a WASM binding that never carried the field on the layout path, and the engine computing the value in render_with_layout and discarding it. All three layers are fixed, and the structure that allowed the drift is gone:

  • One shared construction (result.js) now builds every result object for all three entries — a new field is added in exactly one place.
  • Declared type ↔ runtime shape is enforced in both directions: the expected key sets are derived from keyof RenderHtmlResult (a type change that the runtime doesn't match fails npm test's typecheck), and per-target tests assert the exact key set in the real runtime — plain Node, real workerd via @cloudflare/vitest-pool-workers, and real headless Chromium.
  • Fails-first was demonstrated against the registry: the published 0.20.0 layout path fails the new shape test.

Engine

  • New render_with_layout_and_passes() — render_with_layout() unchanged (now a thin wrapper).

Output is byte-identical to 0.20.0 across the fixture wall, the cross-target determinism gate, and the 15-template compat corpus (zero warnings-baseline diff). All packages in lockstep at 0.20.1: npm (@formepdf/*), crates.io (forme-pdf), PyPI (formepdf), Go (v0.20.1).