shared-http-cache 1.0.2 → 1.0.3
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/package.json +1 -1
- package/readme.md +29 -66
- package/z.js +1 -0
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -15,11 +15,11 @@ This implementation models a shared HTTP cache that follows the semantics define
|
|
|
15
15
|
The cache is shared (not private) and applies shared-cache rules.
|
|
16
16
|
|
|
17
17
|
- Request `methods` other than `GET` are not stored.
|
|
18
|
-
- Private responses
|
|
19
|
-
- Variant responses
|
|
20
|
-
- Partial content
|
|
21
|
-
- No heuristic freshness is used.
|
|
18
|
+
- Private responses (request `Authorization` header, response `Cache-Control="private"` and `Set-Cookie` headers) are not stored.
|
|
19
|
+
- Variant responses (response header `Vary` present) are not stored.
|
|
20
|
+
- Partial content (response `Content-Range` header) is not stored.
|
|
22
21
|
- Time calculations rely exclusively on locally recorded timestamps, not on server-provided `Date`.
|
|
22
|
+
- No heuristic freshness is used.
|
|
23
23
|
- Storage and eviction are deterministic; no background or implicit cleanup is assumed.
|
|
24
24
|
|
|
25
25
|
### Request initialization and cache lookup
|
|
@@ -28,7 +28,7 @@ Each request begins by determining whether a cached response exists.
|
|
|
28
28
|
|
|
29
29
|
If no cached entry exists:
|
|
30
30
|
|
|
31
|
-
- If the request
|
|
31
|
+
- If the request `Cache-Control` header has `only-if-cached` directive, the cache returns a `504 HTTP status`.
|
|
32
32
|
- Otherwise, the request is sent to the origin server.
|
|
33
33
|
|
|
34
34
|
### Cache-Control exclusions
|
|
@@ -41,12 +41,12 @@ Two `Cache-Control` directives may short-circuit normal cache usage:
|
|
|
41
41
|
|
|
42
42
|
### Freshness evaluation
|
|
43
43
|
|
|
44
|
-
If a cached response exists and is not excluded, strict freshness is evaluated first
|
|
44
|
+
If a cached response exists and is not excluded, strict freshness is evaluated first.
|
|
45
45
|
|
|
46
46
|
Freshness lifetime is computed from response headers:
|
|
47
47
|
|
|
48
|
-
- `s-maxage` first, then `max-age`, if present
|
|
49
|
-
- otherwise `Expires
|
|
48
|
+
- `Cache-Control` header's`s-maxage` directive first, then `max-age`, if present
|
|
49
|
+
- otherwise `Expires` header, if present
|
|
50
50
|
|
|
51
51
|
Current age is derived from local metadata:
|
|
52
52
|
|
|
@@ -62,7 +62,7 @@ Remaining freshness:
|
|
|
62
62
|
remainingFreshness = freshnessLifetime − currentAge
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
-
If request includes `min-fresh` its value is deducted from the `remainingFreshness`:
|
|
65
|
+
If request `Cache-Control` header includes `min-fresh` directive, its value is deducted from the `remainingFreshness`:
|
|
66
66
|
|
|
67
67
|
```excel-formula
|
|
68
68
|
remainingFreshness = remainingFreshness − minimumFreshness
|
|
@@ -72,23 +72,23 @@ If `remainingFreshness ≥ 0`, the response is served as fresh.
|
|
|
72
72
|
|
|
73
73
|
### Stale handling
|
|
74
74
|
|
|
75
|
-
If the response is stale, `max-stale`
|
|
75
|
+
If the response is stale, request `Cache-Control` header's `max-stale` directive is evaluated, if present.
|
|
76
76
|
|
|
77
|
-
If `max-stale` value is unspecified → accept any staleness; otherwise the response is acceptable if:
|
|
77
|
+
If `max-stale` is present, but its value is unspecified → accept any staleness; otherwise the response is acceptable if:
|
|
78
78
|
|
|
79
79
|
```excel-formula
|
|
80
|
-
currentAge
|
|
80
|
+
currentAge ≤ freshnessLifetime + maximumStaleness
|
|
81
81
|
```
|
|
82
82
|
|
|
83
83
|
If staleness exceeds the acceptable `max-stale`, the cache proceeds toward revalidation or origin fetch.
|
|
84
84
|
|
|
85
85
|
### Revalidation constraints
|
|
86
86
|
|
|
87
|
-
Even
|
|
87
|
+
Even that request `Cache-Control` header's `max-stale` directive allows use of stale data:
|
|
88
88
|
|
|
89
|
-
- `must-revalidate` or `proxy-revalidate`
|
|
89
|
+
- response `Cache-Control` header's `must-revalidate` or `proxy-revalidate` diretives forbids serving stale.
|
|
90
90
|
- In that case, the cache must revalidate or fetch from the origin.
|
|
91
|
-
- If `only-if-cached` also applies, the cache returns a `504 HTTP status` instead of revalidating.
|
|
91
|
+
- If request `Cache-Control` header's `only-if-cached` directive also applies, the cache returns a `504 HTTP status` instead of revalidating.
|
|
92
92
|
- If no revalidation constraint applies, stale content may be served.
|
|
93
93
|
|
|
94
94
|
On revalidation, if the cached content includes `ETag` or `Last-Modified` automatically `If-None-Match` and `If-Modified-Since` headers are added to the request. Revalidated entries are explicitly replaced during each successful fetch to avoid unbounded growth in the index.
|
|
@@ -123,7 +123,7 @@ The accompanying state diagram represents the full decision flow:
|
|
|
123
123
|
Legend
|
|
124
124
|
|
|
125
125
|
1. `no-cache` may appear on request or response and always requires revalidation.
|
|
126
|
-
2. `no-store` may appear on request or response; See [scope and assumptions](#scope-and-assumptions)
|
|
126
|
+
2. `no-store` may appear on request or response; See [scope and assumptions](#scope-and-assumptions) for storage limitations.
|
|
127
127
|
3. [Freshness evaluation](#freshness-evaluation) excludes `max-stale` that is evaluated only after strict freshness fails.
|
|
128
128
|
4. `410 Gone` [cleanup](#cleanup-behavior) is an explicit design choice to keep the cache coherent; no heuristic eviction is used.
|
|
129
129
|
|
|
@@ -141,8 +141,6 @@ npm i shared-http-cache
|
|
|
141
141
|
new SharedHttpCache(options?) -> SharedHttpCache
|
|
142
142
|
```
|
|
143
143
|
|
|
144
|
-
### Create a shared HTTP cache instance.
|
|
145
|
-
|
|
146
144
|
```js
|
|
147
145
|
const SharedHttpCache = require('shared-http-cache');
|
|
148
146
|
const sharedHttpCache = new SharedHttpCache();
|
|
@@ -158,10 +156,7 @@ new SharedHttpCache({ cacheDir?: string, awaitStorage?: boolean }) -> SharedHttp
|
|
|
158
156
|
- `awaitStorage`: await cache writes before continuing (default `false`)
|
|
159
157
|
|
|
160
158
|
```js
|
|
161
|
-
const sharedHttpCache = new SharedHttpCache({
|
|
162
|
-
cacheDir: '/tmp/http-cache',
|
|
163
|
-
awaitStorage: true,
|
|
164
|
-
});
|
|
159
|
+
const sharedHttpCache = new SharedHttpCache({ cacheDir: '/tmp/http-cache', awaitStorage: true });
|
|
165
160
|
```
|
|
166
161
|
|
|
167
162
|
### Fetch
|
|
@@ -184,9 +179,7 @@ fetch([{ url: string, integrity?: string, options?: RequestInit, callback?: func
|
|
|
184
179
|
await sharedHttpCache.fetch([
|
|
185
180
|
{
|
|
186
181
|
url: 'https://example.com/data.txt',
|
|
187
|
-
callback: ({ buffer }) =>
|
|
188
|
-
console.log(buffer.toString());
|
|
189
|
-
},
|
|
182
|
+
callback: ({ buffer }) => console.log(buffer.toString()),
|
|
190
183
|
},
|
|
191
184
|
]);
|
|
192
185
|
```
|
|
@@ -215,11 +208,7 @@ await sharedHttpCache
|
|
|
215
208
|
},
|
|
216
209
|
},
|
|
217
210
|
])
|
|
218
|
-
.catch((errors) =>
|
|
219
|
-
errors.forEach((entry) => {
|
|
220
|
-
console.error(entry.url, entry.error);
|
|
221
|
-
});
|
|
222
|
-
});
|
|
211
|
+
.catch((errors) => errors.forEach((entry) => console.error(entry.url, entry.error)));
|
|
223
212
|
```
|
|
224
213
|
|
|
225
214
|
### Fetch multiple files
|
|
@@ -235,9 +224,7 @@ const parser = ({ url, buffer, headers, fromCache }) => {
|
|
|
235
224
|
|
|
236
225
|
const requests = urls.map((url) => ({ url, callback: (response) => parser({ ...response, url }) }));
|
|
237
226
|
|
|
238
|
-
sharedHttpCache.fetch(requests).catch((errors) =>
|
|
239
|
-
errors.forEach((entry) => console.error(entry.url, entry.error));
|
|
240
|
-
});
|
|
227
|
+
sharedHttpCache.fetch(requests).catch((errors) => errors.forEach((entry) => console.error(entry.url, entry.error)));
|
|
241
228
|
```
|
|
242
229
|
|
|
243
230
|
### Fetch with integrity
|
|
@@ -247,9 +234,7 @@ await sharedHttpCache.fetch([
|
|
|
247
234
|
{
|
|
248
235
|
url: 'https://example.com/file.bin',
|
|
249
236
|
integrity: 'sha256-abcdef...',
|
|
250
|
-
callback: ({ buffer }) =>
|
|
251
|
-
console.log(buffer.length);
|
|
252
|
-
},
|
|
237
|
+
callback: ({ buffer }) => console.log(buffer.length),
|
|
253
238
|
},
|
|
254
239
|
]);
|
|
255
240
|
```
|
|
@@ -271,14 +256,8 @@ They follow standard [RequestInit](https://developer.mozilla.org/en-US/docs/Web/
|
|
|
271
256
|
await sharedHttpCache.fetch([
|
|
272
257
|
{
|
|
273
258
|
url: 'https://api.example.com/list',
|
|
274
|
-
options: {
|
|
275
|
-
|
|
276
|
-
Accept: 'application/json',
|
|
277
|
-
},
|
|
278
|
-
},
|
|
279
|
-
callback: ({ buffer }) => {
|
|
280
|
-
console.log(buffer.toString());
|
|
281
|
-
},
|
|
259
|
+
options: { headers: { Accept: 'application/json' } },
|
|
260
|
+
callback: ({ buffer }) => console.log(buffer.toString()),
|
|
282
261
|
},
|
|
283
262
|
]);
|
|
284
263
|
```
|
|
@@ -289,14 +268,8 @@ await sharedHttpCache.fetch([
|
|
|
289
268
|
await sharedHttpCache.fetch([
|
|
290
269
|
{
|
|
291
270
|
url: 'https://example.com/data',
|
|
292
|
-
options: {
|
|
293
|
-
|
|
294
|
-
'Cache-Control': 'no-cache',
|
|
295
|
-
},
|
|
296
|
-
},
|
|
297
|
-
callback: ({ fromCache }) => {
|
|
298
|
-
console.log(fromCache);
|
|
299
|
-
},
|
|
271
|
+
options: { headers: { 'Cache-Control': 'no-cache' } },
|
|
272
|
+
callback: ({ fromCache }) => console.log(fromCache),
|
|
300
273
|
},
|
|
301
274
|
]);
|
|
302
275
|
```
|
|
@@ -307,14 +280,8 @@ await sharedHttpCache.fetch([
|
|
|
307
280
|
await sharedHttpCache.fetch([
|
|
308
281
|
{
|
|
309
282
|
url: 'https://example.com/data',
|
|
310
|
-
options: {
|
|
311
|
-
|
|
312
|
-
'Cache-Control': 'max-stale=3600',
|
|
313
|
-
},
|
|
314
|
-
},
|
|
315
|
-
callback: ({ fromCache }) => {
|
|
316
|
-
console.log(fromCache);
|
|
317
|
-
},
|
|
283
|
+
options: { headers: { 'Cache-Control': 'max-stale=3600' } },
|
|
284
|
+
callback: ({ fromCache }) => console.log(fromCache),
|
|
318
285
|
},
|
|
319
286
|
]);
|
|
320
287
|
```
|
|
@@ -325,12 +292,8 @@ await sharedHttpCache.fetch([
|
|
|
325
292
|
await sharedHttpCache.fetch([
|
|
326
293
|
{
|
|
327
294
|
url: 'https://example.com/resource',
|
|
328
|
-
options: {
|
|
329
|
-
|
|
330
|
-
},
|
|
331
|
-
callback: ({ headers }) => {
|
|
332
|
-
console.log(headers);
|
|
333
|
-
},
|
|
295
|
+
options: { method: 'HEAD' },
|
|
296
|
+
callback: ({ headers }) => console.log(headers),
|
|
334
297
|
},
|
|
335
298
|
]);
|
|
336
299
|
```
|
package/z.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
sharedHttpCache.fetch(requests).catch((errors) => errors.forEach((entry) => console.error(entry.url, entry.error)));
|