@blaze-cache/fetch 0.0.0-stage → 0.2.1-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Blaze Cache
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,194 @@
1
- # Temporary Holding Version
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/blaze-cache/blaze-cache/main/docs/blaze-cache-logo.png" alt="Blaze Cache logo" width="150">
3
+ </p>
2
4
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
5
+ <h1 align="center">@blaze-cache/fetch</h1>
6
+
7
+ A drop-in `fetch()` replacement backed by a WebAssembly cache — TTLs, `Cache-Control`,
8
+ stale-while-revalidate and an `x-blaze-cache: HIT | MISS` marker.
9
+
10
+ ## Install
11
+
12
+ ```sh
13
+ npm install @blaze-cache/fetch@beta @blaze-cache/core@beta
14
+ ```
15
+
16
+ `@beta` is npm's dist-tag for the prerelease; drop it to install the last stable
17
+ release.
18
+
19
+ ## Quick start
20
+
21
+ ```ts
22
+ import { createCache } from '@blaze-cache/core';
23
+ import { createBlazeFetch } from '@blaze-cache/fetch';
24
+
25
+ const cache = await createCache({ capacity: 1000 });
26
+ const fetch = createBlazeFetch(cache, { ttlMs: 30_000 });
27
+
28
+ const response = await fetch('/api/users');
29
+ response.headers.get('x-blaze-cache'); // 'MISS' …
30
+ ```
31
+
32
+ ```ts
33
+ const again = await fetch('/api/users');
34
+ again.headers.get('x-blaze-cache'); // … then 'HIT'
35
+ await again.json(); // the body replayed from the cache
36
+ ```
37
+
38
+ The wrapper **is** a `fetch`: same signature, same `Response` back. Hand it to anything that
39
+ expects the platform function.
40
+
41
+ ```ts
42
+ const asFetch: typeof fetch = fetch;
43
+ window.fetch = fetch; // now every request in the app is cached
44
+ ```
45
+
46
+ ## Behaviour
47
+
48
+ * **Only `GET` is cached** — pass `methods: ['GET', 'HEAD']` to include `HEAD`. Everything
49
+ else (including errors from uncached methods) always goes to the network.
50
+ * **`Cache-Control` is honoured on both sides** when `cacheControl` is on (the default):
51
+ on the request, `no-store`/`no-cache` and `max-age=0` skip the read; on the response,
52
+ `max-age` overrides `ttlMs`, `Age` is deducted, and `no-store`/`no-cache` — the per-field
53
+ `no-cache="set-cookie"` form included — stop it being stored.
54
+ `must-revalidate`/`proxy-revalidate` gets no stale window, however short the fresh one.
55
+ * **Identical concurrent requests coalesce** — one caller leads and the rest replay the
56
+ record it stored, so a burst of the same `GET` opens one connection. Only cacheable
57
+ requests lead, callers that demanded the network are never joined, and a leader that
58
+ fails opens the gate over an empty cache so its joiners fetch in turn.
59
+ * **Only successful responses are stored** — the default `shouldCache` keeps `2xx`; replace
60
+ it to widen or narrow that.
61
+ * **`Authorization` participates in the default key** (hashed, never stored verbatim), so a
62
+ personalized response is never replayed to a different user. Tune with `varyHeaders`.
63
+ * **The response's own `Vary` is honoured** — `Vary: *` is never stored (RFC 9111 §3), and
64
+ a response varying on a header you have not put in `varyHeaders` is left uncached rather
65
+ than replay a variant to the wrong caller. Server headers missing from `varyHeaders`?
66
+ That is the safe default working as intended — list them to opt in. The check runs
67
+ before *every* store, `cacheControl: false` included: variant correctness is not a
68
+ freshness preference.
69
+ * **A hit is a real `Response`** — status, status text, headers, body, `url` after a
70
+ redirect and `redirected` are replayed exactly, so your code cannot tell the difference
71
+ apart from the marker header.
72
+ * **Binary, `Blob` and chunked bodies are first-class** — the payload is buffered as raw
73
+ bytes whether it arrived as a file, a `ReadableStream` or plain text, so an image
74
+ replays byte-for-byte with its `content-type` intact and `blob().type`/`blob().size`
75
+ still correct. Buffering runs on a teed clone *after* your `Response` is handed back, so
76
+ a streaming body is never held up waiting for the cache — and because the record is
77
+ complete before it is written, a hit can never half-load.
78
+ * **Transport headers are corrected to the bytes stored** — `fetch` decodes
79
+ `content-encoding` for you, so the record drops `content-encoding` and
80
+ `transfer-encoding` and rewrites `content-length` to the payload's real size. Without
81
+ that, a hit would claim `content-length: 1425` (the compressed size) for 6504 stored
82
+ bytes — which breaks anything that measures a download. `HEAD` keeps its headers: no
83
+ body was sent, so its `content-length` is what a GET *would* return.
84
+ * **Failures behave like plain `fetch`** — a rejected network call still rejects; a cache
85
+ that cannot store a response never breaks the request that produced it. Reads, writes
86
+ and the `shouldCache` callback are all guarded: the first cache-layer failure is
87
+ announced once on the console, then the wrapper keeps working *without* the cache rather
88
+ than warning on every call.
89
+
90
+ ## Options
91
+
92
+ ```ts
93
+ const fetch = createBlazeFetch(cache, {
94
+ ttlMs: 30_000, // number | (response, url, init) => number
95
+ methods: ['GET'], // which methods are cacheable
96
+ cacheControl: true, // honour Cache-Control on request + response
97
+ staleWhileRevalidate: false, // true | milliseconds — see below
98
+ key: (url, init) => `${init.method} ${url}`, // custom cache key
99
+ shouldCache: (response) => response.ok, // which responses are stored
100
+ tags: (response, url) => ['fetch', url], // tags on stored entries
101
+ varyHeaders: ['authorization'], // headers folded into the default key
102
+ markerHeader: 'x-blaze-cache', // or false to disable the marker
103
+ fetch: globalThis.fetch, // the real network function (for tests)
104
+ });
105
+ ```
106
+
107
+ | Option | Default | What it does |
108
+ | --- | --- | --- |
109
+ | `ttlMs` | `60_000` | Freshness window. Overridden by a response `max-age`. |
110
+ | `methods` | `['GET']` | Methods eligible for caching. |
111
+ | `cacheControl` | `true` | Honour `Cache-Control`/`Age` on both sides. |
112
+ | `staleWhileRevalidate` | `false` | Serve stale entries while refreshing. A number is the fallback window when the response omits the directive. |
113
+ | `key` | method + URL + vary hash | Custom cache key. |
114
+ | `shouldCache` | `2xx` | Which responses are stored. |
115
+ | `tags` | `['fetch', '<method> <url>']` | Tags for `cache.invalidateTags(...)`. |
116
+ | `varyHeaders` | `['authorization']` | Request headers folded into the default key. |
117
+ | `markerHeader` | `'x-blaze-cache'` | Header carrying `HIT`/`MISS`/`STALE`; `false` disables it. |
118
+ | `fetch` | `globalThis.fetch` | The real network call — pass a stub in tests. |
119
+
120
+ Requests can force the behaviour per call:
121
+
122
+ ```ts
123
+ await fetch('/api/users', { forceRefresh: true }); // bypass the cache read
124
+ await fetch('/api/users', { cache: 'no-cache' }); // the standard spelling of the same
125
+ await fetch('/api/users', { cache: 'no-store' }); // neither read nor write
126
+ ```
127
+
128
+ ## Stale-while-revalidate
129
+
130
+ ```ts
131
+ const fetch = createBlazeFetch(cache, { ttlMs: 10_000, staleWhileRevalidate: 30_000 });
132
+ ```
133
+
134
+ Within the stale window a request is answered **immediately** from the cache with
135
+ `x-blaze-cache: STALE`, while a background refresh rewrites the entry — the next request is
136
+ a fresh `HIT`. One refresh runs per key at a time, and its failure is swallowed: a stale
137
+ entry is better than a failed request.
138
+
139
+ The window comes from the response's `Cache-Control: stale-while-revalidate=<seconds>`;
140
+ the number you pass is only the fallback for servers that do not send it.
141
+
142
+ ## Invalidating
143
+
144
+ Stored responses carry two tags, so they plug straight into the cache's tag API:
145
+
146
+ ```ts
147
+ cache.invalidateTags('fetch'); // everything this wrapper stored
148
+ cache.invalidateTags('get /api/users'); // one resource
149
+ cache.set('get /api/users', /* … */); // … or just overwrite it
150
+ ```
151
+
152
+ ## Custom keys
153
+
154
+ ```ts
155
+ import { createBlazeFetch, defaultCacheKey } from '@blaze-cache/fetch';
156
+
157
+ const fetch = createBlazeFetch(cache, {
158
+ // Keep the default (method + URL + vary hash) and add a version prefix:
159
+ key: (url, init) => `v2:${defaultCacheKey(url, init, ['authorization'])}`,
160
+ });
161
+ ```
162
+
163
+ ## Testing
164
+
165
+ Two suites, both run against the built package:
166
+
167
+ ```sh
168
+ pnpm --filter @blaze-cache/fetch test # stubbed network — deterministic, offline
169
+ pnpm --filter @blaze-cache/fetch test:live # real public API — opt-in, needs the network
170
+ ```
171
+
172
+ The live suite calls `api.github.com`, `raw.githubusercontent.com` and
173
+ `jsonplaceholder.typicode.com` — all keyless, and well within GitHub's unauthenticated 60
174
+ requests/hour — to prove that *real* `Cache-Control` and `Vary` headers are read
175
+ correctly; a stub can only simulate that. It also lists each server's vary headers in
176
+ `varyHeaders`, which is exactly the configuration a real deployment needs: a response
177
+ varying on a header the key does not fold in is deliberately left uncached.
178
+ Every test in it is skipped unless `BLAZE_CACHE_LIVE=1` is set, so the default suite always
179
+ passes with no network at all.
180
+
181
+ ## Documentation
182
+
183
+ Setup, core concepts and the complete API reference live in the
184
+ [repository README](https://github.com/blaze-cache/blaze-cache#readme).
185
+
186
+ ## Part of Blaze Cache
187
+
188
+ * [`@blaze-cache/core`](https://www.npmjs.com/package/@blaze-cache/core) — the cache itself
189
+ * [`@blaze-cache/axios`](https://www.npmjs.com/package/@blaze-cache/axios) — the axios adapter
190
+ * [`@blaze-cache/react-query`](https://www.npmjs.com/package/@blaze-cache/react-query) — TanStack Query v5 mirror
191
+
192
+ ## License
193
+
194
+ [MIT](https://github.com/blaze-cache/blaze-cache/blob/main/LICENSE) © Blaze Cache
@@ -0,0 +1,55 @@
1
+ /**
2
+ * What `Cache-Control` (and the related `Age`) say about a request or a response — plus the
3
+ * one `Vary` question a keying client must answer before storing anything.
4
+ *
5
+ * Package-local on purpose: this wrapper needs the `stale-while-revalidate` directive, which
6
+ * `@blaze-cache/axios` does not, and each package is free to grow its own directives without
7
+ * dragging the others along.
8
+ */
9
+ export interface CacheControl {
10
+ /** `no-store` — neither read nor write the cache. */
11
+ noStore: boolean;
12
+ /** `no-cache` — must not be served without revalidating, per-field spellings (`no-cache="…"`) included. */
13
+ noCache: boolean;
14
+ /** `must-revalidate` (or the shared-cache spelling `proxy-revalidate`) — never serve a stale response. */
15
+ mustRevalidate: boolean;
16
+ /** `max-age=<seconds>`, converted to milliseconds. */
17
+ maxAgeMs: number | undefined;
18
+ /** `stale-while-revalidate=<seconds>`, converted to milliseconds. */
19
+ staleWhileRevalidateMs: number | undefined;
20
+ }
21
+ /** The value used when parsing is switched off (`cacheControl: false`) or a header is absent. */
22
+ export declare const NO_DIRECTIVES: CacheControl;
23
+ /**
24
+ * Minimal `Cache-Control` parser — only the directives a client-side cache acts on.
25
+ *
26
+ * ```ts
27
+ * parseCacheControl('max-age=30, stale-while-revalidate=120').maxAgeMs; // 30_000
28
+ * ```
29
+ */
30
+ export declare function parseCacheControl(value: string | undefined | null): CacheControl;
31
+ /**
32
+ * Whether a response carrying `Vary: <responseVary>` may be stored under a key that already
33
+ * folds in `varyHeaders`.
34
+ *
35
+ * `Vary` names the *request* headers the response's identity depends on — `Cookie`,
36
+ * `Accept-Language`, `Origin`. A cache may only replay a stored response to a request whose
37
+ * keyed headers match, so a response varying on anything **not** folded into the key cannot
38
+ * be keyed correctly, and the only safe verdict is "store nothing": storing anyway would
39
+ * replay one caller's variant — another user's private payload, another locale's page — to
40
+ * the next caller whose unkeyed header happens to differ. `Vary: *` advertises variants
41
+ * that are unknowable by construction and is never cacheable (RFC 9111 §3).
42
+ *
43
+ * An absent, empty or unparsable header varies on nothing and is always storable; a listed
44
+ * name that *is* folded in (case-insensitively, as header names are) makes the response
45
+ * storable.
46
+ */
47
+ export declare function varyIsStorable(responseVary: string | null, varyHeaders: readonly string[]): boolean;
48
+ /**
49
+ * `Age: <seconds>` — how long an intermediate cache has been holding the response.
50
+ *
51
+ * The freshness window shrinks by this much: a response that is already halfway to its
52
+ * `max-age` deadline must not look brand new to us. Anything unparsable counts as zero.
53
+ */
54
+ export declare function parseAgeMs(value: string | undefined | null): number;
55
+ //# sourceMappingURL=cache-control.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-control.d.ts","sourceRoot":"","sources":["../src/cache-control.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,YAAY;IAC3B,qDAAqD;IACrD,OAAO,EAAE,OAAO,CAAC;IACjB,2GAA2G;IAC3G,OAAO,EAAE,OAAO,CAAC;IACjB,0GAA0G;IAC1G,cAAc,EAAE,OAAO,CAAC;IACxB,sDAAsD;IACtD,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,qEAAqE;IACrE,sBAAsB,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5C;AAED,iGAAiG;AACjG,eAAO,MAAM,aAAa,EAAE,YAM1B,CAAC;AAEH;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,YAAY,CAiChF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,EAAE,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAYnG;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAInE"}
@@ -0,0 +1,110 @@
1
+ /**
2
+ * What `Cache-Control` (and the related `Age`) say about a request or a response — plus the
3
+ * one `Vary` question a keying client must answer before storing anything.
4
+ *
5
+ * Package-local on purpose: this wrapper needs the `stale-while-revalidate` directive, which
6
+ * `@blaze-cache/axios` does not, and each package is free to grow its own directives without
7
+ * dragging the others along.
8
+ */
9
+ /** The value used when parsing is switched off (`cacheControl: false`) or a header is absent. */
10
+ export const NO_DIRECTIVES = Object.freeze({
11
+ noStore: false,
12
+ noCache: false,
13
+ mustRevalidate: false,
14
+ maxAgeMs: undefined,
15
+ staleWhileRevalidateMs: undefined,
16
+ });
17
+ /**
18
+ * Minimal `Cache-Control` parser — only the directives a client-side cache acts on.
19
+ *
20
+ * ```ts
21
+ * parseCacheControl('max-age=30, stale-while-revalidate=120').maxAgeMs; // 30_000
22
+ * ```
23
+ */
24
+ export function parseCacheControl(value) {
25
+ const result = {
26
+ noStore: false,
27
+ noCache: false,
28
+ mustRevalidate: false,
29
+ maxAgeMs: undefined,
30
+ staleWhileRevalidateMs: undefined,
31
+ };
32
+ if (!value)
33
+ return result;
34
+ for (const rawDirective of value.split(',')) {
35
+ const directive = rawDirective.trim().toLowerCase();
36
+ if (directive === 'no-store') {
37
+ result.noStore = true;
38
+ }
39
+ else if (directive === 'no-cache') {
40
+ result.noCache = true;
41
+ }
42
+ else if (directive.startsWith('no-cache=')) {
43
+ // `no-cache="set-cookie, field2"` — reuse is forbidden until *those fields* have been
44
+ // revalidated, and this cache cannot revalidate individual fields, so it must read as
45
+ // the whole-response spelling. Matching only the bare directive would silently turn
46
+ // this opt-out back into "cacheable".
47
+ result.noCache = true;
48
+ }
49
+ else if (directive === 'must-revalidate' || directive === 'proxy-revalidate') {
50
+ // Two spellings of one rule — never serve a stale response. `proxy-revalidate` is the
51
+ // shared-cache name; a browser cache ignoring it would be just as wrong.
52
+ result.mustRevalidate = true;
53
+ }
54
+ else if (directive.startsWith('max-age=')) {
55
+ result.maxAgeMs = seconds(directive.slice('max-age='.length));
56
+ }
57
+ else if (directive.startsWith('stale-while-revalidate=')) {
58
+ result.staleWhileRevalidateMs = seconds(directive.slice('stale-while-revalidate='.length));
59
+ }
60
+ }
61
+ return result;
62
+ }
63
+ /**
64
+ * Whether a response carrying `Vary: <responseVary>` may be stored under a key that already
65
+ * folds in `varyHeaders`.
66
+ *
67
+ * `Vary` names the *request* headers the response's identity depends on — `Cookie`,
68
+ * `Accept-Language`, `Origin`. A cache may only replay a stored response to a request whose
69
+ * keyed headers match, so a response varying on anything **not** folded into the key cannot
70
+ * be keyed correctly, and the only safe verdict is "store nothing": storing anyway would
71
+ * replay one caller's variant — another user's private payload, another locale's page — to
72
+ * the next caller whose unkeyed header happens to differ. `Vary: *` advertises variants
73
+ * that are unknowable by construction and is never cacheable (RFC 9111 §3).
74
+ *
75
+ * An absent, empty or unparsable header varies on nothing and is always storable; a listed
76
+ * name that *is* folded in (case-insensitively, as header names are) makes the response
77
+ * storable.
78
+ */
79
+ export function varyIsStorable(responseVary, varyHeaders) {
80
+ if (responseVary === null)
81
+ return true;
82
+ const names = responseVary
83
+ .split(',')
84
+ .map((name) => name.trim().toLowerCase())
85
+ .filter((name) => name.length > 0);
86
+ if (names.length === 0)
87
+ return true; // `Vary:` with no names varies on nothing
88
+ if (names.includes('*'))
89
+ return false; // RFC 9111 §3: never usable from a cache
90
+ const keyed = new Set(varyHeaders.map((name) => name.toLowerCase()));
91
+ return names.every((name) => keyed.has(name));
92
+ }
93
+ /**
94
+ * `Age: <seconds>` — how long an intermediate cache has been holding the response.
95
+ *
96
+ * The freshness window shrinks by this much: a response that is already halfway to its
97
+ * `max-age` deadline must not look brand new to us. Anything unparsable counts as zero.
98
+ */
99
+ export function parseAgeMs(value) {
100
+ if (!value)
101
+ return 0;
102
+ const secondsValue = Number(value.trim());
103
+ return Number.isFinite(secondsValue) && secondsValue >= 0 ? secondsValue * 1000 : 0;
104
+ }
105
+ /** Seconds → milliseconds, or `undefined` when the value is not a usable non-negative number. */
106
+ function seconds(raw) {
107
+ const value = Number(raw.trim());
108
+ return Number.isFinite(value) && value >= 0 ? value * 1000 : undefined;
109
+ }
110
+ //# sourceMappingURL=cache-control.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-control.js","sourceRoot":"","sources":["../src/cache-control.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { BlazeFetch, BlazeFetchCacheLike, BlazeFetchOptions } from './types.js';
2
+ /**
3
+ * Wraps the platform `fetch` with a Blaze Cache layer.
4
+ *
5
+ * ```ts
6
+ * import { createCache } from '@blaze-cache/core';
7
+ * import { createBlazeFetch } from '@blaze-cache/fetch';
8
+ *
9
+ * const cache = await createCache({ capacity: 1000 });
10
+ * const fetch = createBlazeFetch(cache, { ttlMs: 30_000 });
11
+ *
12
+ * const response = await fetch('/api/users');
13
+ * response.headers.get('x-blaze-cache'); // 'MISS', then 'HIT' on the next call
14
+ * ```
15
+ *
16
+ * The returned function keeps the platform signature — hand it to anything expecting
17
+ * `typeof fetch`. Requests outside {@link BlazeFetchOptions.methods} pass through untouched,
18
+ * and a failing network call rejects exactly as it would have without the wrapper.
19
+ */
20
+ export declare function createBlazeFetch(cache: BlazeFetchCacheLike, options?: BlazeFetchOptions): BlazeFetch;
21
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,UAAU,EACV,mBAAmB,EAInB,iBAAiB,EAElB,MAAM,YAAY,CAAC;AAgBpB;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,mBAAmB,EAC1B,OAAO,GAAE,iBAAsB,GAC9B,UAAU,CA2TZ"}