v1.7.0 — native primitives where the runtime has them
Two places where Cloudflare ships a native implementation of something this package was doing in
JavaScript. Both feature-detected, because this package also runs on Node, Deno and Bun.
The decode stage relays through IdentityTransformStream when there is no cap
Measured on the edge, CPU per MB of decompressed output, every shape interleaved in one isolate:
| decode shape | ms/MB |
|---|---|
| this package's wrapper, BYOB + counting | 7.00 |
a standard new TransformStream() hop |
13.33 |
an IdentityTransformStream hop |
3.67 |
DecompressionStream + native collect (floor) |
3.33 |
The native identity hop is within noise of the floor. The standard TransformStream — the
obvious way to write the same thing — is nearly twice as expensive as doing nothing at all. One is
C++ and one is JavaScript, and nothing in the API surface says so.
On the shipped path: 7.33 → 4.00 ms/MB, −45%, or 29 ms → 16 ms of decode on a 4 MB body.
This applies only when maxBodyBytes is Infinity. The cap is enforced by counting bytes,
counting requires seeing them in JS, and seeing them in JS is exactly the cost being removed — the
two are mutually exclusive. A caller who passed Infinity has already taken responsibility for
bounding the body, so that caller gets the fast path.
timingSafeEqual prefers crypto.subtle.timingSafeEqual
A correctness fix, not a performance one. The hand-written version is a JS loop, and a JS loop
cannot promise constant time — V8 may vectorise it, exit early, or branch on data. It guards TLS
Finished verification. The native one is compiled.
Bound once at module load with a probe rather than a typeof check, so a property that exists but
throws is found at startup rather than inside a handshake.
Measured and rejected, recorded so nobody repeats them
- Routing the HTTP/2 body through an
IdentityTransformStream: +54%. It adds a layer rather
than replacing one — native is only cheaper when it displaces JavaScript. - Returning a
PromisefromdecodeBody: 4.33, worse than the relay, and it breaks the API. - Removing the cap's counting: saves nothing.
FixedLengthStreamcannot serve as a byte cap — its own docs say too few bytes is an error too,
so every body under the limit would fail.
On the tests
The four new ones inject a stand-in for IdentityTransformStream, because it does not exist in the
runtime the offline suite runs in — without that the path would have shipped having executed nowhere
but the edge. One caught a real bug before commit: pipeTo's default aborts the destination with
the source's error, so a truncated gzip reached the consumer as a bare zlib message instead of a
typed error naming the coding.
1248 offline tests pass.