Repository navigation
chore(release): version packages - #596
Open
github-actions[bot] wants to merge 1 commit into
Open
github-actions[bot] wants to merge 1 commit into
github-actions[bot] wants to merge 1 commit into
Conversation
|
Important Review skippedBot user detected. To trigger a single review, invoke the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
github-actions
Bot
force-pushed
the
changeset-release/develop
branch
28 times, most recently
from
October 4, 2026 04:32
2693697 to
7f9f2fa
Compare
github-actions
Bot
force-pushed
the
changeset-release/develop
branch
6 times, most recently
from
October 4, 2026 05:11
b9484bd to
313aaa4
Compare
41 of 62 tasks
github-actions
Bot
force-pushed
the
changeset-release/develop
branch
from
October 4, 2026 05:13
313aaa4 to
de28313
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and publish to npm yourself or setup this action to publish automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to develop, this PR will be updated.
Releases
@derodero24/comprs@2.1.0
Minor Changes
0ef61e6: Give browsers the
*Asyncfunctions and@derodero24/comprs/streams, sothat code written for the native addon runs there too. In 2.0.x, the types
of the
browsercondition declared the*Asyncfunctions, but the browserentry did not export them, and
@derodero24/comprs/streamsloaded thenative addon even in browser builds. The browser entry now exports all 23
*Asyncfunctions, and its declarations declare them. A browser has nothread pool to run them on: each one runs its synchronous function on the
calling thread before it returns, and returns a Promise of the result,
which rejects on any error, including an invalid argument.
@derodero24/comprs/streamsnow has abrowsercondition for imports,which resolves to the same Web Streams helpers on the WebAssembly build,
with their own declarations;
require()keeps loading the native addon.@derodero24/comprs/noderemains for Node.js only.b5fee8a: Add an optional
maxOutputSizeargument todecompress()anddecompressAsync(), in the native addon and the WASM build. Like themaxOutputSizeofcreateDecompressStream(), it limits the decompressedsize in bytes whatever the detected format, and it defaults to 256 MB, so
code that auto-detects the format no longer has to call
detectFormat()and dispatch to the
*DecompressWithCapacity()functions to set anotherlimit. A second argument used to be silently ignored and is now the limit,
so code that passes one by accident, such as
buffers.map(decompress),which passes the array index, must call
buffers.map((data) => decompress(data))instead. Values that are not integers from 0 toNumber.MAX_SAFE_INTEGERthrow.9ddb3cd: Stop building and publishing
@derodero24/comprs-wasm32-wasi, the WASIbuild of the native addon; 2.0.2 is its last version. Since 2.0.2 it was no
longer an optional dependency of
@derodero24/comprs, so it was only usedwhere it had been installed by hand. Node.js, Deno and Bun keep loading the
native addon for their platform. The wasm-bindgen build, which the
package's
browserentry loads, is now the only WebAssembly build.If you installed
@derodero24/comprs-wasm32-wasior setNAPI_RS_FORCE_WASI, uninstall the package and unset the variable: on asupported platform the native addon loads instead, and in browsers the
wasm-bindgen build. The generated loader still looks for the WASI package
when no native binary loads or when
NAPI_RS_FORCE_WASIistrueorerror, so a copy left innode_moduleswould be loaded with a newer@derodero24/comprswhose API it does not match. On a platform without anative binary, loading comprs in Node.js, Deno or Bun throws
Cannot find native binding; if you relied on the WASI build there, stay on@derodero24/comprs2.0.x.c40243c: Report the native memory of the stream contexts to V8 and allow releasing
it early. A context keeps its encoder or decoder state outside the
JavaScript heap, from a few hundred kilobytes for gzip to about 90 MB for
zstd at level 19, but V8 saw only a small object and had no reason to
collect it. A server that dropped contexts, such as the streams of aborted
responses, could grow to gigabytes before an unrelated garbage collection
freed them. The contexts now report their memory, so that V8 collects
abandoned contexts in time.
Every context class gains a
close()method that releases the native stateright away; later calls throw
<format> stream already closed. Contexts arealso disposable, so
using ctx = new ZstdCompressContext()closes thecontext at the end of the scope.
finish()releases the state too, andLz4DecompressContextgains thefinish()that the other contexts have.The Web streams and Node.js Transforms close their context when they end,
fail, or are cancelled or destroyed. The contexts of the browser build gain
close()andLz4DecompressContext.finish()as well.Patch Changes
80da8a5: Reject the Promise of the
*Asyncfunctions on invalid arguments instead ofthrowing synchronously. An invalid level, quality,
capacity,maxOutputSizeormaxDictSize, and an argument of the wrong type, such asa string instead of a
Buffer, used to throw before any Promise existed, sopromise.catch()missed the error. Every*Asyncfunction now returns aPromise that rejects with the error that the synchronous function throws
for the same arguments, with the same message and
code. Code that awaitsthe call inside
try/catchis unaffected; code that relied on asynchronous throw from a call it does not await now gets a rejection
instead.
b5fee8a: Detect every supported format reliably in
detectFormat(),decompress(),decompressAsync(),createDecompressStream()andcreateDecompressTransform(). The empty brotli stream thatbrotliCompress()writes for empty input, zstd and LZ4 frames that followskippable frames, and LZ4 legacy frames (
lz4 -l) used to be reported as'unknown'; they are now detected. The auto-detecting streams decided onthe format after 4 bytes and failed on brotli input that arrived in small
chunks; they now buffer the input until the format is detected, up to
64 KiB.
Brotli, which has no magic number, is now recognized by decoding up to the
first 64 KiB of the input instead of a single byte. Random data shorter than
64 KiB and raw deflate, some of which used to be reported as
'brotli', arenow
'unknown', and so is data that continues after the end of a brotlistream shorter than 64 KiB, which
decompress()used to decompress whileignoring the rest. When data detected as brotli does not decode, including
a truncated brotli stream,
decompress()throws theunable to detect compression formaterror instead of a brotli error. That error now alsopoints to
deflateDecompress()for raw deflate.74cece7: Enforce
maxOutputSizewhile streaming decompression runs instead of aftereach chunk. The gzip, deflate and brotli decompression streams and contexts
used to inflate a whole input chunk before checking the limit, so one small,
highly compressed chunk could allocate gigabytes before the size-limit error.
They now stop as soon as the output would exceed the limit, keeping memory
near
maxOutputSize.finish()onGzipDecompressContextandDeflateDecompressContextnow counts its output toward the limit too.Reject truncated input instead of returning a shorter result. The zstd,
brotli and raw deflate decompression streams,
DeflateDecompressContextandthe
deflateDecompress*()functions used to succeed with whatever had beendecoded when the input ended mid-stream; they now throw
<format> stream is truncated: unexpected end of input. The zstd and brotli decompressioncontexts gain a
finish()method that performs this check, and theirstreams, including the auto-detecting ones, call it when their input ends.
Empty input now throws for every format, in the one-shot functions and in
the streams alike, because no format has a valid zero-length encoding.
zstdDecompress*(),deflateDecompress*(),lz4Decompress*()and mostdecompression streams used to return an empty result for it.
59055c4: Work around two bugs in the brotli 9.0.0 encoder that
brotliCompressWithDict(),brotliCompressWithDictAsync()andBrotliCompressDictContexthit for someinputs. At qualities 2 to 9, including the default, the encoder panicked,
which aborted the Node.js process; at qualities 10 and 11 it returned a
stream that
brotliDecompressWithDict()rejected with "Invalid Data". Whenthe encoder panics, or when quality 10 or 11 output does not decode back to
the input, comprs now compresses that input without the dictionary instead.
The result is a valid brotli stream that decodes with or without the
dictionary, only less compressed in these cases. In the WebAssembly build,
where panics cannot be caught, the panic still traps until brotli is fixed.
118ed81: Reject Large Window Brotli streams in every brotli decompression function,
stream context and
decompress(), as RFC 7932 decoders and Node.js zlibdo. Their header declares a window of up to 1 GiB, and the decoder
reserved a buffer of that size before writing any output, whatever the
output limit: 12 bytes of input grew the memory of the WebAssembly build
to 1.5 GiB, which it never returns. comprs does not write these streams;
only encoders with the extension explicitly enabled do.
694894d: The one-shot brotli decompression functions (
brotliDecompress(),brotliDecompressWithDict(), theirWithCapacityandAsyncvariants,and
decompress()anddecompressAsync()on brotli data) hand the decoderthe whole input at once. Small brotli decompressions no longer allocate a
4 MiB ring buffer: 10 KB of incompressible data decompresses about 40 times
faster, and 1 MB of incompressible data about twice as fast.
6e5f891: Make the browser entry work with bundlers. In 2.0.x, browser builds of
import … from '@derodero24/comprs'failed with esbuild and webpack, andVite produced a bundle that threw on the first call: resolvers picked the
Node.js entry before the
browserone, webpack refused to parse thebrowser files as ES modules,
"sideEffects": falselet bundlers drop theWebAssembly initialisation, esbuild could not bundle the
.wasmimport,and the 2.0.2 browser entry imported the WASI package, which 2.0.2 does not
install.
Imports under the
browsercondition now resolve to the WebAssembly build,ahead of the Node.js entry, with type declarations that describe it rather
than the native addon. The browser files moved to a
browser/directorythat declares
"type": "module", and the entry instantiates theWebAssembly module with top-level
await, fromnew URL('./comprs-wasm_bg.wasm', import.meta.url): the functions areready once the import resolves, with no init call. Bundlers must support
top-level
await. webpack 5 and Vite emit the.wasmfile as an asset;with esbuild, copy
browser/comprs-wasm_bg.wasmnext to the bundle. OnVite 7 and older,
vite devneedsoptimizeDeps: { exclude: ['@derodero24/comprs'] }, and before Vite 7,vite buildneedsbuild.target: 'es2022'.require()cannot load a module that uses top-levelawait, sorequire('@derodero24/comprs')keeps loading the native addon even wherethe
browsercondition is set, as in Jest with a jsdom environment. UnderJest's ES module support, that environment now imports the WebAssembly
build, which does not load in Jest; set
testEnvironmentOptions: { customExportConditions: ['node', 'node-addons'] }to keep the native addon. The top-level browser files (
browser-entry.js,browser.js,browser-streaming.jsandcomprs-wasm*) and thebrowserfield of
package.json, which pointed atbrowser-entry.js, are gone.89a005b: Accept any
ArrayBufferorArrayBufferViewchunk, and aSharedArrayBufferas well, in the browser build of the Web streams that@derodero24/comprs/streamsprovides, as the streams of the native addonnow do. A bare
ArrayBufferorSharedArrayBufferused to fail, andcreateDecompressStream()read aDataViewas empty and aUint16Arrayelement by element, so that it failed to detect the format; it could also
keep a view of an
ArrayBufferchunk, which the writer might overwrite,while it waited for enough input to detect the format. Every chunk is now
read byte for byte, and a chunk that is not binary data errors the stream
with a
TypeError. The browser typings accept these chunks too.0ef61e6: Make the stream contexts of the browser entry stream. They were JavaScript
stand-ins that kept every chunk until the end and then ran the one-shot
function: they held the whole input in memory, kept a view of each chunk
rather than a copy, so a caller that reused its buffer compressed
overwritten data, returned nothing from
flush()(or, when decompressingzstd, brotli or LZ4, ended the stream there), reported corrupt input only
at the end, and passed a zstd
maxOutputSizeon as a capacity that grewthe WebAssembly memory to that size. The browser entry now exports the
stream contexts of the WebAssembly build, which work like the native
ones:
transform()andflush()return output as soon as it is ready,so code that kept only what
finish()returned must keep what every callreturns, as on Node.js. Like those, they have
close(), which[Symbol.dispose]()calls, andLz4DecompressContext.finish(); theyalso have a
free()method, which frees the context object and itsWebAssembly memory before garbage collection does.
e939fc2: Derive the ES module entry from the CommonJS one.
index.mjslisted everyexport by hand and has drifted from the native addon before; it now
re-exports the CommonJS loader and the stream helpers with
export *, sothe two entries cannot drift apart. Node.js and Deno see the same exports
as before. Bundlers now follow the entry into the loader and bundle it,
instead of leaving a
require('./index.js')that fails once the bundlemoves; as before, the native addon itself must stay external. Bun and
bundlers read the loader's names at run time, so their view of the package
root now also lists the
*Taskclasses thatrequire()has alwaysexported; they are not part of the API.
e939fc2: Fix the type declarations of the ES module entry.
index.d.mtsre-exporteddeclaration files, which TypeScript rejects with TS2846, so ES module
projects that type-check their dependencies (
skipLibCheck: false) got twoerrors from
node_modules. It now re-exports./index.jsand./streams.js, and the exported types are unchanged.0f15e49: Reject gzip header filenames that cannot be stored.
gzipCompressWithHeader()with a
filenamethat contains a NUL character ('\u0000') aborted theNode.js process, and trapped with
RuntimeError: unreachablein the WASMbuild. It now throws
gzip filename must not contain NUL characters. Afilename longer than 65535 bytes in UTF-8 was written, but
gzipDecompress()and
gzipReadHeader()could not read the result back; it now throwsgzip filename must be at most 65535 bytes long.15b4d0b: Stop gzip decompression from reserving memory because of a forged size
trailer.
gzipDecompress(),gzipDecompressWithCapacity(), their asyncvariants and
decompress()sized their initial output buffer from theISIZE trailer, which nothing verifies until decoding ends, so a few bytes of
input could reserve up to 4 GiB, capped only by the output limit (256 MB by
default). The initial buffer is now also capped by what the input can
expand to and by 64 MiB, and larger outputs grow the buffer as they decode.
gzip, brotli and LZ4 decompression also report a failed reservation of the
initial output buffer as an error instead of aborting the process.
3d7ddef: Write a content checksum in LZ4 frames.
lz4Compress(),lz4CompressAsync(),Lz4CompressContextand the LZ4 compression streamsnow add an xxHash32 of the data to each frame, as the
lz4CLI does bydefault, so decompression detects corrupted data instead of returning it.
The output is still a standard LZ4 frame that any LZ4 decoder reads, 4 bytes
longer than before, and its FLG byte changes from
0x60to0x64.Computing the checksum makes compression about 15% slower, and verifying it
makes decompressing these frames up to about 30% slower. Frames without a
checksum, as other encoders may write them, still decode.
3d7ddef: Decode every frame of LZ4 input and reject truncated or trailing data.
lz4Decompress(),lz4DecompressWithCapacity(), their async variants,decompress(),Lz4DecompressContextand the LZ4 decompression streamsused to stop at the end of the first frame, or at the first empty block, and
silently drop whatever followed, and they accepted a frame cut short at a
block boundary. They now decode concatenated frames like the
lz4CLI.Input that ends inside a frame, including a frame without its end mark,
throws
lz4 stream is truncated: unexpected end of input, and a framefollowed by data that is not a frame throws an
unexpected data after the end of a frameerror. The output limit (capacity,maxOutputSize) coversall frames together.
The LZ4-specific functions, context and streams also skip skippable frames,
which used to fail with
SkippableFrame, and accept a legacy frame(
lz4 -l) followed by further frames, or with blocks of data that does notcompress, which used to fail with
BlockTooBig.decompress()and theauto-detecting streams still recognise LZ4 input only by the standard frame
magic number, so input that starts with a skippable or legacy frame needs
lz4Decompress().89a005b: Push the output of the Node.js transforms from
@derodero24/comprs/nodeinchunks of at most
readableHighWaterMarkbytes (64 KiB by default). A smallinput chunk that decompressed to many megabytes used to arrive as one chunk
of that size, which downstream consumers had to take in at once. The chunks
are views of the native output, not copies.
694894d: Buffers returned by the one-shot compression and decompression functions
no longer retain an allocation sized from the input. The encoders reserved
an output buffer as large as the input, and the decoders grew theirs by
doubling; the whole allocation lived as long as the returned Buffer, so 50
retained
brotliCompress()results of an 8 MB input held about 400 MB ofmemory for less than 1 KB of output. Results now release large spare
capacity before they are returned. The LZ4 compression streams also stop
copying each output chunk.
89a005b: Accept any
ArrayBufferorArrayBufferViewchunk, asCompressionStreamdoes, and a
SharedArrayBufferas well, in the Web streams that@derodero24/comprs/streamsprovides with the native addon (Node.js, Denoand Bun). A bare
ArrayBufferused to fail withValue is none of these types, andcreateDecompressStream()failed to detect the format ofDataViewandUint16Arraychunks, which it read as empty or element byelement. Every chunk is now read byte for byte, and a chunk that is not
binary data errors the stream with a
TypeError. The typings accept thesechunks and remain assignable to
TransformStream<Uint8Array, Uint8Array>.54bf460: Fix a crash in Bun and Deno when a method of a native context class is
called with an instance of another context class as
this, such asZstdCompressContext.prototype.transform.call(gzipContext, chunk). Themethod used the other class's native state as its own, and the process
crashed with a segmentation fault. It now throws an
InvalidArgerror;Node.js already rejected such calls with
Illegal invocation.The fix comes with the update of the native addon to napi 3.13.0,
napi-derive 3.6.9 and napi-build 2.5.0, which tag every class instance
and check the tag before a method uses it. The crates were held at 3.9.1 /
3.5.6 / 2.3.2 for the WASI build, which is no longer published. The
JavaScript API, the TypeScript types and the generated loader are
unchanged.
0f15e49: Reject invalid numeric arguments instead of silently converting them.
Compression levels and qualities, the
crc32()initial value and the gzipheader
mtimewere converted to 32-bit integers before any check ran, soNaNandInfinitybecame 0 (no compression for gzip, deflate and brotli),1.9became 1,2 ** 32 + 1became 1 andcrc32(data, -1)used0xFFFFFFFF. This applied to the one-shot and async functions and to thestream contexts, in the native addon and in the WASM build, which also
turned non-numeric strings and objects into 0.
maxOutputSizetruncatedfractions (
0.5became a limit of 0 bytes), and acapacityof2 ** 64passed validation. All of these now throw an error that names the argument
and its accepted range, for example
gzip compression level must be an integer between 0 and 9; the messages of the existing range errors changedto this form.
capacityandmaxOutputSizeaccept integers from 0 toNumber.MAX_SAFE_INTEGERon every platform (the WASM build used to reject acapacityof2 ** 32or more), and a limit of 0 accepts only data thatdecompresses to nothing.
maxDictSizeaccepts integers from 0 to 16777216.7cf02d8: Make the WebAssembly build take and return what the native addon does.
Its
gzipCompressWithHeader()took(data, level, filename, mtime)rather than the
(data, header, level)of the published types, sogzipCompressWithHeader(data, { filename }, 6)failed withRuntimeError: memory access out of bounds. It now takes(data, header, level), as the native addon does; the positional form wasnever part of the published types, and calls that use it must pass the
header as an object.
gzipReadHeader()now leaves out the header fieldsthat are absent, rather than setting them to
null, as the native addondoes.
Byte array arguments are now checked as the native addon checks them:
anything but a
Uint8Array(aBufferis one) or anotherArrayBufferview throws an
Error. The WebAssembly build used to compress anArrayBufferas empty input and a string as one zero byte per character,and its stream contexts took an
ArrayBufferor an array of numbers as achunk.
7cf02d8: Log the message of a panic in the WebAssembly build. A panic aborts the
call with a trap, which throws a bare
RuntimeError: unreachable; thebuild now logs the panic message with
console.error()first.2180aaf: Shrink the WebAssembly build that browsers load.
comprs-wasm_bg.wasmisnow compiled for size: it is 1.87 MB instead of 2.37 MB, and 14% smaller to
download with gzip (792 KB at level 9) and 12% smaller with brotli (545 KB
at quality 11). In exchange, brotli compression runs about 40% slower in
the browser, brotli decompression about 30%, gzip decompression about 20%
and LZ4 compression about 10% slower; zstd, gzip compression and LZ4
decompression keep their speed. The native addon is unchanged.
bae7130: Link the C runtime statically into the Windows ARM64 binary
(
@derodero24/comprs-win32-arm64-msvc), as the x64 one already did. Up to2.0.2 it imported
VCRUNTIME140.dlland theapi-ms-win-crt-*DLLs, soloading comprs failed on Windows on Arm machines without the Visual C++
Redistributable for ARM64. It now loads without it.
15b4d0b: Size zstd one-shot decompression output from the data instead of reserving
it up front.
zstdDecompress(),zstdDecompressWithDict(), their asyncvariants and
decompress()reserved 256 MB for every frame without acontent size, which streaming encoders such as
ZstdCompressContextwrite,and the
*WithCapacity()variants allocated the wholecapacity, so a valuesuch as
2 ** 40aborted the process. The output buffer now grows with thedecompressed data: frames that declare their size still decode straight
into a buffer of that size, as long as the input could actually expand to
it, and everything else goes through the streaming decoder.
capacityisonly a limit, and output over it throws
zstd decompress exceeded maximum size of <capacity> byteslike the other formats, instead ofDestination buffer is too small.The one-shot zstd functions now accept concatenated frames and skippable
frames, as the streaming API already did, and report truncated input as
zstd stream is truncated: unexpected end of input; data after the lastframe now usually reports
Unknown frame descriptorinstead ofSrc size is incorrect. Like the streaming API andzstd -d, they reject a framewithout a content size whose window exceeds 128 MiB (written by
zstd --long=28or higher on piped input) withFrame requires too much memory for decoding.15b4d0b: Reject a
maxDictSizeabove 16 MiB (16777216 bytes) inzstdTrainDictionary()andzstdTrainDictionaryAsync(). The value wasallocated up front, so one that could not be allocated, such as
2 ** 40,aborted the process (a trap in the WASM build). zstd recommends
dictionaries of about 100 KB, and training allocates several buffers of
maxDictSizebytes.8f6967f: Speed up small one-shot zstd calls.
zstdCompress(),zstdDecompress(),zstdDecompressWithCapacity(), their*Asyncvariants, anddecompress()and
decompressAsync()for zstd input reuse one compression and onedecompression context per thread instead of creating one per call:
compressing messages of about 110 bytes is about 6 times faster, and
decompressing them about 12 times. A thread keeps a context only while it
holds at most 8 MiB, so a large or high-level call does not leave its
workspace behind. The output is unchanged, and the dictionary functions
still create a context per call.
8f6967f: Speed up zstd stream compression with small chunks.
ZstdCompressContext,ZstdCompressDictContextand the zstd compression streams no longerzero-fill a 128 KiB output buffer on every call: 10 MiB written in 1 KiB
chunks compresses about 10 times faster for highly compressible data and
up to twice as fast for JSON lines. The output is unchanged.
@derodero24/comprs-middleware@1.1.0
Minor Changes
4f19597: The Fastify plugin now compresses Web
ReadableStreamandResponsepayloads, which it used to send uncompressed. The status and headers of a
Responseare applied to the reply first, as Fastify does, so the built-inchecks see them.
string,BufferandUint8Arraypayloads are compressedin one call on the libuv thread pool instead of through a stream, and are
sent with the
Content-Lengthof the compressed body instead of chunked; ifthat compression fails, the reply is sent uncompressed and a warning is
logged. A stream whose reply declares a
Content-Lengthbelowthresholdis no longer compressed.
Add the
shouldCompress(request, reply)option, which receives the Fastifyrequest and reply and so sees the headers set with
reply.header()orreply.type().filter(req, res)keeps receivingrequest.rawandreply.raw, which do not have those headers yet when it runs. A route canopt out with
config: { compress: false }, which the plugin adds toFastify's route config type. The plugin is now wrapped with
fastify-pluginand named
@derodero24/comprs-middleware, so other plugins can list it intheir
dependencies, and Fastify checks that it runs on Fastify 5.fastify-pluginbecomes a dependency of the package.af30a69: Follow RFC 9110 in all adapters.
deflateresponses now use the zlib format(RFC 1950) that the
deflatecontent coding requires, instead of raw DEFLATEthat strict clients such as
zlib.inflateSync()reject.Accept-Encodingisparsed case-insensitively, including the
qparameter, and an element whoseweight is not a valid
qvalueis ignored instead of read asq=1; theserver's
encodingsorder still decides among the encodings the clientaccepts.
Vary: Accept-Encodingis added to every response that could becompressed, also for
HEADrequests and requests withoutAccept-Encoding,and to no other response. As the
filtertakes part in that decision, it isnow also called for requests that do not get compressed. Responses with
status 204, 304 or 206, with a
Content-Rangeheader, or with an empty bodyare no longer compressed, and compressed responses get a weak
ETag.text/event-streamresponses are no longer compressed, so Server-Sent Eventsare not held back. The Hono adapter no longer keeps a
Content-Lengthset bythe handler on a compressed response, and the Fastify adapter no longer
responds with 500 when a route sets
VaryorCache-Controlas an array.The options are now checked when the middleware is created: an empty or
unsupported
encodingslist, a level that is not an integer in its range orthat names an unknown encoding, a
thresholdthat is not a finite number of 0or more, or a
filterthat is not a function throws aTypeErrororRangeErrorinstead of failing on each request. zstd levels 20 to 22 arerejected, because their window exceeds the 8 MiB that RFC 9659 allows for
HTTP and Chromium refuses such responses.
encodingsand thepreferredargument of
negotiate()accept readonly arrays.Patch Changes
2429445: Fix the Express adapter for handlers that send the response headers before
the body. The adapter now decides whether to compress when the headers are
emitted, so
res.writeHead()andres.flushHeaders()no longer make theresponse fail with
ERR_HTTP_HEADERS_SENT. Compressed responses honorbackpressure:
res.write()returnsfalsewhile the client is slow and'drain'follows, sostream.pipe(res)pauses instead of buffering the wholebody. A compressor error after the headers were sent aborts the response
instead of throwing an uncaught exception, and a client disconnect releases
the compressor.
Cache-Controlvalues set as arrays are checked forno-transform. Writes afterres.end()fail as they do on a plainServerResponse, passingERR_STREAM_WRITE_AFTER_ENDto the callback andemitting it as
'error', andres.end()callbacks run once the response hasfinished.
Deciding at header emission also changes three details of the Express
adapter: a bare
res.end()counts as an empty body forthresholdinsteadof producing an empty compressed stream, responses with status 204 or 304 are
no longer compressed, and
Vary: Accept-Encodingis only added to responsesthat could be compressed.
dd3f2b6: The Hono middleware now compresses streamed responses while they are sent,
instead of reading the whole body first. A
stream()orstreamText()response reaches the client as the handler writes it, and one that never ends
no longer hangs; the body is read only as fast as the client takes it, and is
no longer held twice through
Response.clone(). A body that is available atonce, such as that of
c.text()orc.json(), is compressed in one call onthe libuv thread pool instead of on the event loop. Errors are no longer
swallowed: one that occurs before the response is sent, such as a failed
compression or a body stream that fails right away, goes to the app's error
handler, and a body stream that fails later aborts the response. The README
now states that the Hono middleware runs on Node.js and Bun, not on edge
runtimes such as Cloudflare Workers.
This also changes three details of the Hono middleware: a failed compression
produces the error handler's response instead of the uncompressed body; a
streamed body has no known size, so
thresholdonly applies to it when theresponse declares a
Content-Length; and a body that is available at onceloses the
Transfer-Encoding: chunkedthatstreamText()sets, so that theserver can send it with a
Content-Length.8d84a8d: The package can now be loaded with
require()as well asimport. Itsentry points were exported for
importonly, sorequire()failed withERR_PACKAGE_PATH_NOT_EXPORTED, and TypeScript projects that compile toCommonJS reported
TS2307.@derodero24/comprs-middleware/package.jsonisexported too. The package still ships ES modules only, so it now requires
Node.js 22.12 or later instead of 22.0: 22.12 is the first Node.js 22 release
whose
require()loads ES modules without a flag. A TypeScript project thatcompiles to CommonJS needs
moduleset tonodenextornode20.Express is no longer a peer dependency: the Express adapter only uses the
Node.js request and response, and works with Express 4 and 5.
The
@derodero24/comprspeer range is now^2.0.2(it was^2.0.0). Whenboth packages are released together, the middleware is now published only
after
@derodero24/comprs, so its peer range can always be met. Thepublished manifest no longer lists
workspace:*for the@derodero24/comprsdevDependency, and the package now includes its license file.
4f19597: Read
Varyas a list of field names when addingAccept-Encodingto it. AVarythat names a field merely containingaccept-encoding, such asX-Accept-Encoding-Hint, now getsAccept-Encodingadded, which it used tomiss, and a
Varythat lists*next to other field names is left as it is.