RFC 9111-compliant client cache implementation.
This software has been developed without the use of Artifical Intelligence. Pull Requests with artificially generated code are rejected.
Deprecated functionality is not implemented and will never be.
Works with runtimes implementing either Node Stream API or Web Stream API.
Requires crypto.randomUUID and Promise.withResolvers.
This software is work in progress. It needs tests. Indexing and partial content support is under consideration.
Benchmarks (+/- <1%) on AMD 5600:
cache.get: 450k req/s
cache.onResponse: 135k req/s
fetch with cache: 130k req/s
raw refresh: 88k req/s
fetch refresh: 42k req/s
Legend:
- cache.get - retrieve from cache,
- cache.onResponse - revalidate & save,
- fetch with cache - retrieve from cache using wrapped fetch,
- raw refresh - clear cache, save to cache, retrieve from cache,
- fetch refresh - raw refresh but using wrapped fetch.
Note that the raw benchmarks are an upper bound (this is a stress test). In a real application, there's lots of I/O going on, so expect the actual cache performance of at least 42k req/s (130k req/s is an upper bound).
- Trailers are not supported.
- Partial content is not yet implemented.
no-cache=#field-name,private=#field-nameresponse syntax is understood as the unqualifiedno-cacheandprivaterespectively.- Non-compliant quoted-string variants are accepted (example:
max-age="4"). no-transformhas no effect, the cache never transforms anything.- Duplicate
cache-controldirectives result in the header being parsed asno-store. - Cache extensions are not supported.
- The backing storage must be used by only a single instance of
HttpCache. If multiple instances use the same storage, this WILL cause breakage.
The underlying storage is a simple Map<string, json> (accepts async variant for disk storage). It does not provide a TTL mechanism, therefore a LRU cache is strongly recommended.
Cache groups, clear-site-data: cache and content negotiation require indexing and concurrency handling to prevent race conditions.
If you need content negotiation, use a different cache for every type of negotiated content.
See fetch.mts and example.mts.
Requires the following types:
ReadableStreamReadableStreamDefaultReaderReadableByteStreamController
In this case they are pulled from @types/node.
allowImportingTsExtensions should be set to true in tsconfig.json. This allows Node to run in watch mode with erasable TypeScript syntax.