shared-http-cache 1.0.0 → 1.0.2
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/index.js +3 -3
- package/package.json +1 -1
- package/readme.md +34 -8
package/index.js
CHANGED
|
@@ -51,8 +51,7 @@ class SharedHttpCache {
|
|
|
51
51
|
const { url, options = {}, integrity, callback } = request;
|
|
52
52
|
if (!options.method) options.method = 'GET';
|
|
53
53
|
if (!options.headers) options.headers = {};
|
|
54
|
-
Object.keys(options.headers).
|
|
55
|
-
Object.keys(options.headers).some((key) => key.toLowerCase() === 'authorization' && (options.headers['authorization'] = options.headers[key]));
|
|
54
|
+
Object.keys(options.headers).forEach((key) => /\p{Lu}/u.test(key) && ((options.headers[key.toLowerCase()] = options.headers[key]), delete options.headers[key]));
|
|
56
55
|
// prettier-ignore
|
|
57
56
|
let response, buffer, headers, fromCache = true;
|
|
58
57
|
try {
|
|
@@ -96,8 +95,9 @@ class SharedHttpCache {
|
|
|
96
95
|
if (!fromCache || response?.status === 304) {
|
|
97
96
|
const responseCacheControl = parseHeader(headers['cache-control']);
|
|
98
97
|
if (options.method !== 'GET') return;
|
|
98
|
+
if (headers['vary'] || headers['content-range'] || headers['set-cookie']) return;
|
|
99
99
|
if (responseCacheControl['no-store'] || responseCacheControl['private']) return;
|
|
100
|
-
if (requestCacheControl['no-store'] ||
|
|
100
|
+
if (requestCacheControl['no-store'] || options.headers['authorization']) return;
|
|
101
101
|
const store = async () => {
|
|
102
102
|
await this.store.rm.entry(this.cacheDir, url, { removeFully: true });
|
|
103
103
|
await this.store.put(this.cacheDir, url, buffer, integrity ? { metadata: { headers }, integrity } : { metadata: { headers } });
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
2
|
title: Shared HTTP Cache
|
|
4
3
|
|
|
5
4
|
description: Node.Js Utility for fetching multiple HTTP resources with browser-like cache management.
|
|
6
|
-
|
|
7
5
|
---
|
|
8
6
|
|
|
9
7
|
## Overview
|
|
@@ -16,7 +14,10 @@ This implementation models a shared HTTP cache that follows the semantics define
|
|
|
16
14
|
|
|
17
15
|
The cache is shared (not private) and applies shared-cache rules.
|
|
18
16
|
|
|
19
|
-
-
|
|
17
|
+
- Request `methods` other than `GET` are not stored.
|
|
18
|
+
- Private responses are excluded from storage (request `Authorization` header, response `Cache-Control="private"` and `Set-Cookie` headers).
|
|
19
|
+
- Variant responses are excluded from storage (response header `Vary` present).
|
|
20
|
+
- Partial content is not stored (response `Content-Range` header).
|
|
20
21
|
- No heuristic freshness is used.
|
|
21
22
|
- Time calculations rely exclusively on locally recorded timestamps, not on server-provided `Date`.
|
|
22
23
|
- Storage and eviction are deterministic; no background or implicit cleanup is assumed.
|
|
@@ -30,9 +31,9 @@ If no cached entry exists:
|
|
|
30
31
|
- If the request requires `only-if-cached`, the cache returns a `504 HTTP status`.
|
|
31
32
|
- Otherwise, the request is sent to the origin server.
|
|
32
33
|
|
|
33
|
-
### Cache-
|
|
34
|
+
### Cache-Control exclusions
|
|
34
35
|
|
|
35
|
-
Two
|
|
36
|
+
Two `Cache-Control` directives may short-circuit normal cache usage:
|
|
36
37
|
|
|
37
38
|
- `no-cache` (request or response): cached data cannot be used without revalidation.
|
|
38
39
|
- `no-store` (request or response): the response must not be stored.
|
|
@@ -53,7 +54,7 @@ Current age is derived from local metadata:
|
|
|
53
54
|
currentAge = now − storedTime + incomingAge
|
|
54
55
|
```
|
|
55
56
|
|
|
56
|
-
The `incomingAge` is taken from the stored response `
|
|
57
|
+
The `incomingAge` is taken from the stored response `Age` header, if present.
|
|
57
58
|
|
|
58
59
|
Remaining freshness:
|
|
59
60
|
|
|
@@ -61,6 +62,12 @@ Remaining freshness:
|
|
|
61
62
|
remainingFreshness = freshnessLifetime − currentAge
|
|
62
63
|
```
|
|
63
64
|
|
|
65
|
+
If request includes `min-fresh` its value is deducted from the `remainingFreshness`:
|
|
66
|
+
|
|
67
|
+
```excel-formula
|
|
68
|
+
remainingFreshness = remainingFreshness − minimumFreshness
|
|
69
|
+
```
|
|
70
|
+
|
|
64
71
|
If `remainingFreshness ≥ 0`, the response is served as fresh.
|
|
65
72
|
|
|
66
73
|
### Stale handling
|
|
@@ -116,7 +123,7 @@ The accompanying state diagram represents the full decision flow:
|
|
|
116
123
|
Legend
|
|
117
124
|
|
|
118
125
|
1. `no-cache` may appear on request or response and always requires revalidation.
|
|
119
|
-
2. `no-store` may appear on request or response; [
|
|
126
|
+
2. `no-store` may appear on request or response; See [scope and assumptions](#scope-and-assumptions) to understand other storage limitations.
|
|
120
127
|
3. [Freshness evaluation](#freshness-evaluation) excludes `max-stale` that is evaluated only after strict freshness fails.
|
|
121
128
|
4. `410 Gone` [cleanup](#cleanup-behavior) is an explicit design choice to keep the cache coherent; no heuristic eviction is used.
|
|
122
129
|
|
|
@@ -166,6 +173,7 @@ sharedHttpCache.fetch(requests) -> Promise<this | Error[]>
|
|
|
166
173
|
```
|
|
167
174
|
|
|
168
175
|
**Syntax:**
|
|
176
|
+
|
|
169
177
|
```ts
|
|
170
178
|
fetch([{ url: string, integrity?: string, options?: RequestInit, callback?: function }]) -> Promise<this | Error[]>
|
|
171
179
|
```
|
|
@@ -187,7 +195,7 @@ await sharedHttpCache.fetch([
|
|
|
187
195
|
|
|
188
196
|
Errors encountered during fetches are collected, and the returned `promise` either `resolves` with the instance itself for successful fetches or `rejects` with a list of `errors` for failed requests.
|
|
189
197
|
|
|
190
|
-
The response is converted into a [Buffer](https://nodejs.org/api/buffer.html) served to `callback`, then stored in the `cache` along with the `response headers`.
|
|
198
|
+
The response is converted into a [Buffer](https://nodejs.org/api/buffer.html) served to `callback`, then stored in the `cache` along with the `response headers`.
|
|
191
199
|
|
|
192
200
|
```ts
|
|
193
201
|
callback({ buffer: Buffer, headers: Headers, fromCache: boolean }) -> void
|
|
@@ -214,6 +222,24 @@ await sharedHttpCache
|
|
|
214
222
|
});
|
|
215
223
|
```
|
|
216
224
|
|
|
225
|
+
### Fetch multiple files
|
|
226
|
+
|
|
227
|
+
```js
|
|
228
|
+
const urls = ['https://example.com/file1', 'https://example.com/file2'];
|
|
229
|
+
const parser = ({ url, buffer, headers, fromCache }) => {
|
|
230
|
+
console.log(url);
|
|
231
|
+
console.log(headers);
|
|
232
|
+
console.log(fromCache);
|
|
233
|
+
console.log(buffer.toString());
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const requests = urls.map((url) => ({ url, callback: (response) => parser({ ...response, url }) }));
|
|
237
|
+
|
|
238
|
+
sharedHttpCache.fetch(requests).catch((errors) => {
|
|
239
|
+
errors.forEach((entry) => console.error(entry.url, entry.error));
|
|
240
|
+
});
|
|
241
|
+
```
|
|
242
|
+
|
|
217
243
|
### Fetch with integrity
|
|
218
244
|
|
|
219
245
|
```js
|