npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@celsian/compress

v0.6.3

Published

Compression middleware for CelsianJS

Readme

@celsian/compress

Response compression plugin for CelsianJS using Web Standard CompressionStream (gzip/deflate).

Install

npm install @celsian/compress

Usage

import { compress } from '@celsian/compress';

await app.register(compress({ threshold: 1024 }));

Options

| Option | Default | Description | | --- | --- | --- | | threshold | 1024 | Minimum response size in bytes before compressing. | | encodings | ['gzip', 'deflate'] | Encodings this server will produce, in preference order. | | filter | textual allow-list | (request, reply, contentType) => boolean. Return false to send uncompressed. |

What gets compressed

By default only textual content types are compressed: text/*, application/json, application/xml, application/javascript, any +json or +xml suffix, and image/svg+xml. Images, video, archives, and other already-compressed payloads are passed through untouched.

Compression is also skipped when the response already carries a Content-Encoding, when the payload is binary (Uint8Array / ArrayBuffer), and when the handler returns a raw Response.

An explicitly set Content-Type is never overwritten.

BREACH

Compressing a response is not free of risk. BREACH recovers a secret from a compressed response when that response contains BOTH a secret (a CSRF token, an API key, part of a session identifier) AND attacker-influenced content that is reflected back, compression ratio then leaks the secret a byte at a time.

Use filter to exclude any route whose response mixes a secret with reflected input:

await app.register(compress({
  filter: (request) => !new URL(request.url).pathname.startsWith('/api/csrf'),
}));

The safer structural fix is to not return a secret in the same response as reflected user input.

Content negotiation

Accept-Encoding is parsed as (coding, q) pairs per RFC 9110. A q=0 is an explicit refusal and is honored, so Accept-Encoding: gzip;q=0, deflate never yields gzip. Among the codings the client accepts, the highest q wins; ties fall back to the server's encodings order. * is supported.

Vary

Vary: Accept-Encoding is set on every response the plugin handles, not just compressed ones, and it is merged with any Vary the handler already set. Without this a shared cache or CDN can serve a stored uncompressed body to a gzip client (or the reverse).

Cookies

Compressed responses preserve every Set-Cookie, including repeated ones. The plugin builds the real response through the reply's own builder and then wraps it, rather than re-deriving headers, cookies do not live in reply.headers, so re-deriving silently dropped them (a compressed clearCookie() logout never logged anyone out).

Documentation

See the main repository for full docs, examples, and API reference.

License

MIT