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

@danmat/query-cache

v0.1.2

Published

RFC 10008-correct caching for the HTTP QUERY method: cache keys derived from the request body, plus a tiny in-memory cache. Zero dependencies.

Downloads

26

Readme

@danmat/query-cache

CI npm version minified + gzip size License: MIT

RFC 10008-correct caching for the HTTP QUERY method. A QUERY's identity lives in its body, not its URL — so a correct cache key must incorporate the request content. This library derives such keys and ships a tiny in-memory cache built on them.

Zero dependencies. Fully typed. Isomorphic (Node 18+, Deno, Bun, browsers, edge — anywhere Web Crypto exists).

import { QueryCache } from "@danmat/query-cache";

const cache = new QueryCache({ maxEntries: 500 });

const res = await cache.wrap(
  { url: "https://api.example.com/search", body, headers },
  () => query(url, { body, headers }), // your fetcher, only called on a miss
);

The problem

RFC 10008 says QUERY responses are cacheable, but with a catch:

The cache key for a QUERY request MUST incorporate the request content and related metadata.

Every existing HTTP cache keys on the URL. For QUERY, two requests to the same URL with different bodies are different queries — key on the URL alone and you'll serve one query's results for another. This library does the body-aware keying the spec requires.

Install

npm install @danmat/query-cache

API

queryCacheKey(input, options?): Promise<string>

Derives a stable, collision-resistant SHA-256 key that incorporates the method, normalized URL (query params sorted), content type, any configured Vary headers, and the request body bytes. Accepts either a Request or a plain { url, method?, body?, headers? } descriptor.

const key = await queryCacheKey({
  url: "https://api.example.com/search",
  body: JSON.stringify({ filter: { status: "active" } }),
  headers: { "content-type": "application/json" },
});
// "9f2c…" — same bytes ⇒ same key; different body ⇒ different key

| Option | Type | Description | | --- | --- | --- | | varyHeaders | string[] | Extra request headers to fold into the key. content-type is always included. | | normalizeBody | (bytes, contentType) => Uint8Array | Canonicalize the body before hashing (e.g. sorted-key JSON) so semantically-equal bodies collapse to one key. |

class QueryCache

A minimal in-memory cache keyed by queryCacheKey. Honors Cache-Control: no-store and max-age, with optional LRU eviction and a default TTL. Responses are cloned on store and retrieval, so cached bodies stay readable.

const cache = new QueryCache({ maxEntries: 1000, ttl: 60_000 });

await cache.store(input, response);        // respects no-store / max-age
const hit = await cache.match(input);      // Response clone, or undefined
const res = await cache.wrap(input, fetch); // match-or-fetch-and-store
await cache.delete(input);
cache.clear();
cache.size; // number of live entries

Constructor options extend the key options above plus maxEntries, ttl (ms), and now (clock injection for testing).

Notes

  • Hashing uses the Web Crypto SubtleCrypto.digest global, with a node:crypto fallback for older Node — no dependencies either way.
  • Determinism: identical bytes hash identically. FormData bodies aren't recommended as cache inputs — their multipart boundary is randomized, so they won't produce stable keys. Prefer JSON/text/SQL payloads (which QUERY is designed for), or supply normalizeBody.

The @danmat QUERY suite

▶️ See them work together: query-suite-example — a runnable demo using all four, with a 🌐 live playground.

License

MIT © Dan Matthew