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.
Files changed (3) hide show
  1. package/index.js +3 -3
  2. package/package.json +1 -1
  3. 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).some((key) => key.toLowerCase() === 'cache-control' && (options.headers['cache-control'] = options.headers[key]));
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'] || requestCacheControl['authorization']) return;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shared-http-cache",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Node.Js Utility for fetching multiple HTTP resources with browser-like cache management.",
5
5
  "keywords": [
6
6
  "http-cache",
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
- - Privacy factors are considered while storing responses (`safe method` constraints, request `authorization`, response `cache-control="private"`).
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-control exclusions
34
+ ### Cache-Control exclusions
34
35
 
35
- Two cache-control directives may short-circuit normal cache usage:
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 `age` header, if present.
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; [privacy factors](#scope-and-assumptions) are considered.
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`. For any other status code, unless `stale-if-error` is present, an `error` is thrown.
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