nlcurl 0.5.0 → 0.7.0
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/README.md +41 -8
- package/dist/cache/store.d.ts +89 -0
- package/dist/cache/store.d.ts.map +1 -0
- package/dist/cache/store.js +402 -0
- package/dist/cache/store.js.map +1 -0
- package/dist/cache/types.d.ts +101 -0
- package/dist/cache/types.d.ts.map +1 -0
- package/dist/cache/types.js +2 -0
- package/dist/cache/types.js.map +1 -0
- package/dist/cookies/jar.d.ts +20 -0
- package/dist/cookies/jar.d.ts.map +1 -1
- package/dist/cookies/jar.js +64 -10
- package/dist/cookies/jar.js.map +1 -1
- package/dist/cookies/parser.d.ts +2 -10
- package/dist/cookies/parser.d.ts.map +1 -1
- package/dist/cookies/parser.js +49 -2
- package/dist/cookies/parser.js.map +1 -1
- package/dist/cookies/psl-data.d.ts +12 -0
- package/dist/cookies/psl-data.d.ts.map +1 -0
- package/dist/cookies/psl-data.js +10166 -0
- package/dist/cookies/psl-data.js.map +1 -0
- package/dist/cookies/public-suffix.d.ts +37 -0
- package/dist/cookies/public-suffix.d.ts.map +1 -0
- package/dist/cookies/public-suffix.js +140 -0
- package/dist/cookies/public-suffix.js.map +1 -0
- package/dist/core/client.js +4 -0
- package/dist/core/client.js.map +1 -1
- package/dist/core/request.d.ts +73 -3
- package/dist/core/request.d.ts.map +1 -1
- package/dist/core/response.d.ts +22 -0
- package/dist/core/response.d.ts.map +1 -1
- package/dist/core/response.js +32 -0
- package/dist/core/response.js.map +1 -1
- package/dist/core/session.d.ts +23 -0
- package/dist/core/session.d.ts.map +1 -1
- package/dist/core/session.js +108 -4
- package/dist/core/session.js.map +1 -1
- package/dist/core/validation.d.ts +11 -0
- package/dist/core/validation.d.ts.map +1 -1
- package/dist/core/validation.js +22 -1
- package/dist/core/validation.js.map +1 -1
- package/dist/dns/codec.d.ts +37 -0
- package/dist/dns/codec.d.ts.map +1 -0
- package/dist/dns/codec.js +254 -0
- package/dist/dns/codec.js.map +1 -0
- package/dist/dns/doh-resolver.d.ts +52 -0
- package/dist/dns/doh-resolver.d.ts.map +1 -0
- package/dist/dns/doh-resolver.js +192 -0
- package/dist/dns/doh-resolver.js.map +1 -0
- package/dist/dns/https-rr.d.ts +51 -0
- package/dist/dns/https-rr.d.ts.map +1 -0
- package/dist/dns/https-rr.js +127 -0
- package/dist/dns/https-rr.js.map +1 -0
- package/dist/dns/types.d.ts +110 -0
- package/dist/dns/types.d.ts.map +1 -0
- package/dist/dns/types.js +34 -0
- package/dist/dns/types.js.map +1 -0
- package/dist/hsts/store.d.ts +41 -0
- package/dist/hsts/store.d.ts.map +1 -0
- package/dist/hsts/store.js +171 -0
- package/dist/hsts/store.js.map +1 -0
- package/dist/hsts/types.d.ts +28 -0
- package/dist/hsts/types.d.ts.map +1 -0
- package/dist/hsts/types.js +2 -0
- package/dist/hsts/types.js.map +1 -0
- package/dist/http/alt-svc.d.ts +85 -0
- package/dist/http/alt-svc.d.ts.map +1 -0
- package/dist/http/alt-svc.js +220 -0
- package/dist/http/alt-svc.js.map +1 -0
- package/dist/http/form-data.d.ts +59 -0
- package/dist/http/form-data.d.ts.map +1 -0
- package/dist/http/form-data.js +90 -0
- package/dist/http/form-data.js.map +1 -0
- package/dist/http/h1/client.d.ts.map +1 -1
- package/dist/http/h1/client.js +21 -3
- package/dist/http/h1/client.js.map +1 -1
- package/dist/http/h1/encoder.d.ts +17 -0
- package/dist/http/h1/encoder.d.ts.map +1 -1
- package/dist/http/h1/encoder.js +53 -5
- package/dist/http/h1/encoder.js.map +1 -1
- package/dist/http/h1/parser.d.ts.map +1 -1
- package/dist/http/h1/parser.js +7 -1
- package/dist/http/h1/parser.js.map +1 -1
- package/dist/http/h2/client.d.ts.map +1 -1
- package/dist/http/h2/client.js +58 -8
- package/dist/http/h2/client.js.map +1 -1
- package/dist/http/h2/hpack.d.ts +7 -0
- package/dist/http/h2/hpack.d.ts.map +1 -1
- package/dist/http/h2/hpack.js +13 -0
- package/dist/http/h2/hpack.js.map +1 -1
- package/dist/http/h3/detection.d.ts +17 -0
- package/dist/http/h3/detection.d.ts.map +1 -0
- package/dist/http/h3/detection.js +59 -0
- package/dist/http/h3/detection.js.map +1 -0
- package/dist/http/negotiator.d.ts +26 -1
- package/dist/http/negotiator.d.ts.map +1 -1
- package/dist/http/negotiator.js +93 -18
- package/dist/http/negotiator.js.map +1 -1
- package/dist/http/pool.d.ts +2 -2
- package/dist/http/pool.d.ts.map +1 -1
- package/dist/http/pool.js.map +1 -1
- package/dist/index.d.ts +19 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/dist/middleware/rate-limiter.d.ts.map +1 -1
- package/dist/middleware/rate-limiter.js +4 -0
- package/dist/middleware/rate-limiter.js.map +1 -1
- package/dist/middleware/retry.js +2 -2
- package/dist/middleware/retry.js.map +1 -1
- package/dist/proxy/env-proxy.d.ts +21 -0
- package/dist/proxy/env-proxy.d.ts.map +1 -0
- package/dist/proxy/env-proxy.js +74 -0
- package/dist/proxy/env-proxy.js.map +1 -0
- package/dist/proxy/http-proxy.d.ts +2 -0
- package/dist/proxy/http-proxy.d.ts.map +1 -1
- package/dist/proxy/http-proxy.js +29 -6
- package/dist/proxy/http-proxy.js.map +1 -1
- package/dist/proxy/socks.js +10 -1
- package/dist/proxy/socks.js.map +1 -1
- package/dist/sse/parser.d.ts +70 -0
- package/dist/sse/parser.d.ts.map +1 -0
- package/dist/sse/parser.js +153 -0
- package/dist/sse/parser.js.map +1 -0
- package/dist/tls/ech.d.ts +147 -0
- package/dist/tls/ech.d.ts.map +1 -0
- package/dist/tls/ech.js +401 -0
- package/dist/tls/ech.js.map +1 -0
- package/dist/tls/node-engine.d.ts +9 -1
- package/dist/tls/node-engine.d.ts.map +1 -1
- package/dist/tls/node-engine.js +54 -1
- package/dist/tls/node-engine.js.map +1 -1
- package/dist/tls/pin-verification.d.ts +9 -0
- package/dist/tls/pin-verification.d.ts.map +1 -0
- package/dist/tls/pin-verification.js +34 -0
- package/dist/tls/pin-verification.js.map +1 -0
- package/dist/tls/session-cache.d.ts +70 -0
- package/dist/tls/session-cache.d.ts.map +1 -0
- package/dist/tls/session-cache.js +80 -0
- package/dist/tls/session-cache.js.map +1 -0
- package/dist/tls/stealth/client-hello.d.ts +21 -0
- package/dist/tls/stealth/client-hello.d.ts.map +1 -1
- package/dist/tls/stealth/client-hello.js +116 -0
- package/dist/tls/stealth/client-hello.js.map +1 -1
- package/dist/tls/stealth/engine.d.ts.map +1 -1
- package/dist/tls/stealth/engine.js +152 -30
- package/dist/tls/stealth/engine.js.map +1 -1
- package/dist/tls/stealth/handshake.d.ts +2 -1
- package/dist/tls/stealth/handshake.d.ts.map +1 -1
- package/dist/tls/stealth/handshake.js +118 -5
- package/dist/tls/stealth/handshake.js.map +1 -1
- package/dist/tls/stealth/tls12-handshake.d.ts +14 -0
- package/dist/tls/stealth/tls12-handshake.d.ts.map +1 -0
- package/dist/tls/stealth/tls12-handshake.js +462 -0
- package/dist/tls/stealth/tls12-handshake.js.map +1 -0
- package/dist/tls/types.d.ts +40 -0
- package/dist/tls/types.d.ts.map +1 -1
- package/dist/utils/encoding.d.ts +8 -6
- package/dist/utils/encoding.d.ts.map +1 -1
- package/dist/utils/encoding.js +92 -24
- package/dist/utils/encoding.js.map +1 -1
- package/dist/utils/happy-eyeballs.d.ts +3 -0
- package/dist/utils/happy-eyeballs.d.ts.map +1 -1
- package/dist/utils/happy-eyeballs.js +42 -2
- package/dist/utils/happy-eyeballs.js.map +1 -1
- package/dist/ws/client.d.ts +3 -0
- package/dist/ws/client.d.ts.map +1 -1
- package/dist/ws/client.js +63 -7
- package/dist/ws/client.js.map +1 -1
- package/dist/ws/frame.d.ts +4 -2
- package/dist/ws/frame.d.ts.map +1 -1
- package/dist/ws/frame.js +18 -18
- package/dist/ws/frame.js.map +1 -1
- package/dist/ws/permessage-deflate.d.ts +58 -0
- package/dist/ws/permessage-deflate.d.ts.map +1 -0
- package/dist/ws/permessage-deflate.js +148 -0
- package/dist/ws/permessage-deflate.js.map +1 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -11,10 +11,14 @@ NLcURL provides session-based and one-shot HTTP APIs, browser profile impersonat
|
|
|
11
11
|
- HTTP/1.1 and HTTP/2 (ALPN negotiated) with RFC 9113 flow control
|
|
12
12
|
- Browser profile impersonation (Chrome, Firefox, Safari, Edge, Tor)
|
|
13
13
|
- Optional custom JA3 and Akamai H2 fingerprint values in request model
|
|
14
|
-
- Cookie jar with RFC 6265
|
|
14
|
+
- Cookie jar with RFC 6265 behavior, Public Suffix List enforcement, `SameSite=Lax` default, and `__Host-`/`__Secure-` prefix validation
|
|
15
|
+
- `FormData` class for `multipart/form-data` uploads (RFC 7578)
|
|
16
|
+
- mTLS and custom CA trust via `TLSOptions` (`cert`, `key`, `pfx`, `ca`, `passphrase`)
|
|
17
|
+
- `ReadableStream<Uint8Array>` upload bodies (auto-drained before encoding)
|
|
18
|
+
- Header validation per RFC 7230 (rejects CR/LF/NUL injection)
|
|
15
19
|
- Streaming response support (`stream: true`) with automatic decompression
|
|
16
20
|
- Automatic dual-stack connectivity via Happy Eyeballs (RFC 8305) — falls back to IPv4 instantly when IPv6 is unreachable, with optional `dnsFamily` pin
|
|
17
|
-
- Automatic retry on H2 RST_STREAM protocol errors (codes 1, 2, 7, 11)
|
|
21
|
+
- Automatic retry on H2 RST_STREAM protocol errors (codes 1, 2, 7, 8, 11, 13) with capped exponential backoff
|
|
18
22
|
- CLI (`nlcurl`) for scripted and interactive use
|
|
19
23
|
- WebSocket client with optional impersonated TLS handshake
|
|
20
24
|
|
|
@@ -52,6 +56,34 @@ console.log(response.status);
|
|
|
52
56
|
console.log(response.json());
|
|
53
57
|
```
|
|
54
58
|
|
|
59
|
+
### Multipart form upload
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import { post, FormData } from "nlcurl";
|
|
63
|
+
|
|
64
|
+
const form = new FormData();
|
|
65
|
+
form.append("username", "alice");
|
|
66
|
+
form.append("avatar", { data: Buffer.from("..."), filename: "avatar.png", contentType: "image/png" });
|
|
67
|
+
|
|
68
|
+
const res = await post("https://httpbin.org/post", form);
|
|
69
|
+
console.log(res.json());
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### mTLS / custom CA
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
import { request } from "nlcurl";
|
|
76
|
+
|
|
77
|
+
const res = await request({
|
|
78
|
+
url: "https://internal-api.example.com/data",
|
|
79
|
+
tls: {
|
|
80
|
+
cert: fs.readFileSync("client.pem"),
|
|
81
|
+
key: fs.readFileSync("client-key.pem"),
|
|
82
|
+
ca: fs.readFileSync("ca.pem"),
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
55
87
|
### Session-based usage
|
|
56
88
|
|
|
57
89
|
```ts
|
|
@@ -142,12 +174,13 @@ Additional commands:
|
|
|
142
174
|
|
|
143
175
|
## Documentation Index
|
|
144
176
|
|
|
145
|
-
- `docs/API.md
|
|
146
|
-
- `docs/MODULES.md
|
|
147
|
-
- `docs/ARCHITECTURE.md
|
|
148
|
-
- `docs/SETUP.md
|
|
149
|
-
- `docs/CONFIGURATION.md
|
|
150
|
-
- `docs/
|
|
177
|
+
- `docs/API.md` — exported API reference
|
|
178
|
+
- `docs/MODULES.md` — module-by-module usage guide
|
|
179
|
+
- `docs/ARCHITECTURE.md` — system architecture and request flow
|
|
180
|
+
- `docs/SETUP.md` — setup, build, and test instructions
|
|
181
|
+
- `docs/CONFIGURATION.md` — request/session/CLI configuration
|
|
182
|
+
- `docs/EXAMPLES.md` — comprehensive usage examples (API + CLI)
|
|
183
|
+
- `docs/ONBOARDING.md` — contributor onboarding guide
|
|
151
184
|
|
|
152
185
|
## License
|
|
153
186
|
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { CacheConfig, CacheDirectives, CacheEntry, CacheLookupResult, CacheMode } from "./types.js";
|
|
2
|
+
import type { NLcURLRequest } from "../core/request.js";
|
|
3
|
+
import { NLcURLResponse } from "../core/response.js";
|
|
4
|
+
/**
|
|
5
|
+
* In-memory HTTP response cache implementing RFC 9111.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Cache-Control directive parsing and freshness calculation
|
|
9
|
+
* - ETag / Last-Modified conditional request header injection
|
|
10
|
+
* - 304 Not Modified response merging
|
|
11
|
+
* - Vary header support
|
|
12
|
+
* - LRU eviction by entry count and total body size
|
|
13
|
+
* - Range request passthrough (does not cache partial responses by default)
|
|
14
|
+
*/
|
|
15
|
+
export declare class CacheStore {
|
|
16
|
+
private readonly entries;
|
|
17
|
+
private readonly maxEntries;
|
|
18
|
+
private readonly maxSize;
|
|
19
|
+
private currentSize;
|
|
20
|
+
private readonly mode;
|
|
21
|
+
/** Monotonic counter for deterministic LRU ordering within the same ms. */
|
|
22
|
+
private accessCounter;
|
|
23
|
+
constructor(config?: CacheConfig);
|
|
24
|
+
/**
|
|
25
|
+
* Generates a cache key from a request. The key is `METHOD:URL`.
|
|
26
|
+
*/
|
|
27
|
+
static cacheKey(method: string, url: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* Looks up a cached response and evaluates its freshness.
|
|
30
|
+
*
|
|
31
|
+
* @param req - The outgoing request to match against cache.
|
|
32
|
+
* @returns A lookup result with the entry and freshness status.
|
|
33
|
+
*/
|
|
34
|
+
lookup(req: NLcURLRequest): CacheLookupResult;
|
|
35
|
+
/**
|
|
36
|
+
* Determines what action to take for a request given the cache state
|
|
37
|
+
* and the configured cache mode.
|
|
38
|
+
*
|
|
39
|
+
* Returns conditional headers to add (If-None-Match / If-Modified-Since)
|
|
40
|
+
* when the entry is stale and needs revalidation.
|
|
41
|
+
*/
|
|
42
|
+
evaluate(req: NLcURLRequest, modeOverride?: CacheMode): {
|
|
43
|
+
conditionalHeaders?: Record<string, string>;
|
|
44
|
+
shouldStore: boolean;
|
|
45
|
+
matchedEntry?: CacheEntry;
|
|
46
|
+
serveCached?: CacheEntry;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Stores a response in the cache if it is cacheable per RFC 9111 §3.
|
|
50
|
+
*/
|
|
51
|
+
store(req: NLcURLRequest, response: NLcURLResponse): void;
|
|
52
|
+
/**
|
|
53
|
+
* Merges a 304 Not Modified response with a cached entry, producing
|
|
54
|
+
* a full response with updated headers.
|
|
55
|
+
*/
|
|
56
|
+
mergeNotModified(entry: CacheEntry, response304: NLcURLResponse): NLcURLResponse;
|
|
57
|
+
/**
|
|
58
|
+
* Constructs a full NLcURLResponse from a cache entry for direct serving.
|
|
59
|
+
*/
|
|
60
|
+
responseFromEntry(entry: CacheEntry, req: NLcURLRequest): NLcURLResponse;
|
|
61
|
+
/** Returns the number of cached entries. */
|
|
62
|
+
get size(): number;
|
|
63
|
+
/** Returns the total byte size of all cached response bodies. */
|
|
64
|
+
get totalSize(): number;
|
|
65
|
+
/** Clears all cached entries. */
|
|
66
|
+
clear(): void;
|
|
67
|
+
/**
|
|
68
|
+
* Removes a specific cache entry by method and URL.
|
|
69
|
+
*/
|
|
70
|
+
delete(method: string, url: string): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Checks whether request headers match the stored Vary fields.
|
|
73
|
+
*/
|
|
74
|
+
private varyMatches;
|
|
75
|
+
/**
|
|
76
|
+
* Finds the key of the least recently accessed entry.
|
|
77
|
+
*/
|
|
78
|
+
private findLRUKey;
|
|
79
|
+
/**
|
|
80
|
+
* Evicts least-recently-used entries until there is room for a new entry
|
|
81
|
+
* of the given size.
|
|
82
|
+
*/
|
|
83
|
+
private evictIfNeeded;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Parses a `Cache-Control` header value into structured directives.
|
|
87
|
+
*/
|
|
88
|
+
export declare function parseCacheControl(value: string): CacheDirectives;
|
|
89
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/cache/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,UAAU,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACzG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAWrD;;;;;;;;;;GAUG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiC;IACzD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAY;IACjC,2EAA2E;IAC3E,OAAO,CAAC,aAAa,CAAK;gBAEd,MAAM,CAAC,EAAE,WAAW;IAMhC;;OAEG;IACH,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM;IAIpD;;;;;OAKG;IACH,MAAM,CAAC,GAAG,EAAE,aAAa,GAAG,iBAAiB;IA8B7C;;;;;;OAMG;IACH,QAAQ,CACN,GAAG,EAAE,aAAa,EAClB,YAAY,CAAC,EAAE,SAAS,GACvB;QACD,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC5C,WAAW,EAAE,OAAO,CAAC;QACrB,YAAY,CAAC,EAAE,UAAU,CAAC;QAC1B,WAAW,CAAC,EAAE,UAAU,CAAC;KAC1B;IA6CD;;OAEG;IACH,KAAK,CAAC,GAAG,EAAE,aAAa,EAAE,QAAQ,EAAE,cAAc,GAAG,IAAI;IAyDzD;;;OAGG;IACH,gBAAgB,CAAC,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,GAAG,cAAc;IA0BhF;;OAEG;IACH,iBAAiB,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,aAAa,GAAG,cAAc;IAcxE,4CAA4C;IAC5C,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,iEAAiE;IACjE,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,iCAAiC;IACjC,KAAK,IAAI,IAAI;IAKb;;OAEG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO;IAW5C;;OAEG;IACH,OAAO,CAAC,WAAW;IASnB;;OAEG;IACH,OAAO,CAAC,UAAU;IAYlB;;;OAGG;IACH,OAAO,CAAC,aAAa;CAiBtB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CA0EhE"}
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
import { NLcURLResponse } from "../core/response.js";
|
|
2
|
+
const DEFAULT_MAX_ENTRIES = 1000;
|
|
3
|
+
const DEFAULT_MAX_SIZE = 50 * 1024 * 1024;
|
|
4
|
+
/** HTTP methods whose responses may be cached (RFC 9111 §3). */
|
|
5
|
+
const CACHEABLE_METHODS = new Set(["GET", "HEAD"]);
|
|
6
|
+
/** Status codes that are cacheable by default (RFC 9111 §3). */
|
|
7
|
+
const CACHEABLE_STATUS = new Set([200, 203, 204, 206, 300, 301, 308, 404, 405, 410, 414, 501]);
|
|
8
|
+
/**
|
|
9
|
+
* In-memory HTTP response cache implementing RFC 9111.
|
|
10
|
+
*
|
|
11
|
+
* Features:
|
|
12
|
+
* - Cache-Control directive parsing and freshness calculation
|
|
13
|
+
* - ETag / Last-Modified conditional request header injection
|
|
14
|
+
* - 304 Not Modified response merging
|
|
15
|
+
* - Vary header support
|
|
16
|
+
* - LRU eviction by entry count and total body size
|
|
17
|
+
* - Range request passthrough (does not cache partial responses by default)
|
|
18
|
+
*/
|
|
19
|
+
export class CacheStore {
|
|
20
|
+
entries = new Map();
|
|
21
|
+
maxEntries;
|
|
22
|
+
maxSize;
|
|
23
|
+
currentSize = 0;
|
|
24
|
+
mode;
|
|
25
|
+
/** Monotonic counter for deterministic LRU ordering within the same ms. */
|
|
26
|
+
accessCounter = 0;
|
|
27
|
+
constructor(config) {
|
|
28
|
+
this.maxEntries = config?.maxEntries ?? DEFAULT_MAX_ENTRIES;
|
|
29
|
+
this.maxSize = config?.maxSize ?? DEFAULT_MAX_SIZE;
|
|
30
|
+
this.mode = config?.mode ?? "default";
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Generates a cache key from a request. The key is `METHOD:URL`.
|
|
34
|
+
*/
|
|
35
|
+
static cacheKey(method, url) {
|
|
36
|
+
return `${method.toUpperCase()}:${url}`;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Looks up a cached response and evaluates its freshness.
|
|
40
|
+
*
|
|
41
|
+
* @param req - The outgoing request to match against cache.
|
|
42
|
+
* @returns A lookup result with the entry and freshness status.
|
|
43
|
+
*/
|
|
44
|
+
lookup(req) {
|
|
45
|
+
const method = (req.method ?? "GET").toUpperCase();
|
|
46
|
+
if (!CACHEABLE_METHODS.has(method)) {
|
|
47
|
+
return { fresh: false, staleWhileRevalidate: false };
|
|
48
|
+
}
|
|
49
|
+
const key = CacheStore.cacheKey(method, req.url);
|
|
50
|
+
const entry = this.entries.get(key);
|
|
51
|
+
if (!entry) {
|
|
52
|
+
return { fresh: false, staleWhileRevalidate: false };
|
|
53
|
+
}
|
|
54
|
+
if (!this.varyMatches(entry, req)) {
|
|
55
|
+
return { fresh: false, staleWhileRevalidate: false };
|
|
56
|
+
}
|
|
57
|
+
entry.lastAccessedAt = ++this.accessCounter;
|
|
58
|
+
const age = (Date.now() - entry.storedAt) / 1000;
|
|
59
|
+
const freshness = computeFreshnessLifetime(entry);
|
|
60
|
+
const fresh = age < freshness;
|
|
61
|
+
let staleWhileRevalidate = false;
|
|
62
|
+
if (!fresh && entry.directives.staleWhileRevalidate !== undefined) {
|
|
63
|
+
staleWhileRevalidate = age < freshness + entry.directives.staleWhileRevalidate;
|
|
64
|
+
}
|
|
65
|
+
return { entry, fresh, staleWhileRevalidate };
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Determines what action to take for a request given the cache state
|
|
69
|
+
* and the configured cache mode.
|
|
70
|
+
*
|
|
71
|
+
* Returns conditional headers to add (If-None-Match / If-Modified-Since)
|
|
72
|
+
* when the entry is stale and needs revalidation.
|
|
73
|
+
*/
|
|
74
|
+
evaluate(req, modeOverride) {
|
|
75
|
+
const mode = modeOverride ?? this.mode;
|
|
76
|
+
if (mode === "no-store") {
|
|
77
|
+
return { shouldStore: false };
|
|
78
|
+
}
|
|
79
|
+
const result = this.lookup(req);
|
|
80
|
+
if (mode === "force-cache" && result.entry) {
|
|
81
|
+
return { serveCached: result.entry, shouldStore: false };
|
|
82
|
+
}
|
|
83
|
+
if (mode === "only-if-cached") {
|
|
84
|
+
if (result.entry) {
|
|
85
|
+
return { serveCached: result.entry, shouldStore: false };
|
|
86
|
+
}
|
|
87
|
+
return { shouldStore: false };
|
|
88
|
+
}
|
|
89
|
+
if (result.fresh && mode === "default") {
|
|
90
|
+
return { serveCached: result.entry, shouldStore: false };
|
|
91
|
+
}
|
|
92
|
+
const conditionalHeaders = {};
|
|
93
|
+
let matchedEntry;
|
|
94
|
+
if (result.entry) {
|
|
95
|
+
matchedEntry = result.entry;
|
|
96
|
+
if (result.entry.etag) {
|
|
97
|
+
conditionalHeaders["if-none-match"] = result.entry.etag;
|
|
98
|
+
}
|
|
99
|
+
if (result.entry.lastModified) {
|
|
100
|
+
conditionalHeaders["if-modified-since"] = result.entry.lastModified;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const hasConditional = Object.keys(conditionalHeaders).length > 0;
|
|
104
|
+
return {
|
|
105
|
+
conditionalHeaders: hasConditional ? conditionalHeaders : undefined,
|
|
106
|
+
shouldStore: true,
|
|
107
|
+
matchedEntry,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Stores a response in the cache if it is cacheable per RFC 9111 §3.
|
|
112
|
+
*/
|
|
113
|
+
store(req, response) {
|
|
114
|
+
const method = (req.method ?? "GET").toUpperCase();
|
|
115
|
+
if (!CACHEABLE_METHODS.has(method))
|
|
116
|
+
return;
|
|
117
|
+
const directives = parseCacheControl(response.headers["cache-control"] ?? "");
|
|
118
|
+
if (directives.noStore)
|
|
119
|
+
return;
|
|
120
|
+
if (!CACHEABLE_STATUS.has(response.status) && directives.maxAge === undefined && directives.sMaxAge === undefined) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (response.status === 206)
|
|
124
|
+
return;
|
|
125
|
+
const key = CacheStore.cacheKey(method, req.url);
|
|
126
|
+
const varyFields = parseVary(response.headers["vary"] ?? "");
|
|
127
|
+
if (varyFields.includes("*"))
|
|
128
|
+
return;
|
|
129
|
+
const varyHeaders = {};
|
|
130
|
+
for (const field of varyFields) {
|
|
131
|
+
varyHeaders[field] = req.headers?.[field] ?? "";
|
|
132
|
+
}
|
|
133
|
+
const bodySize = response.rawBody.length;
|
|
134
|
+
this.evictIfNeeded(bodySize);
|
|
135
|
+
const existing = this.entries.get(key);
|
|
136
|
+
if (existing) {
|
|
137
|
+
this.currentSize -= existing.bodySize;
|
|
138
|
+
this.entries.delete(key);
|
|
139
|
+
}
|
|
140
|
+
const now = Date.now();
|
|
141
|
+
const entry = {
|
|
142
|
+
key,
|
|
143
|
+
status: response.status,
|
|
144
|
+
statusText: response.statusText,
|
|
145
|
+
headers: { ...response.headers },
|
|
146
|
+
body: Buffer.from(response.rawBody),
|
|
147
|
+
httpVersion: response.httpVersion,
|
|
148
|
+
url: response.url,
|
|
149
|
+
storedAt: now,
|
|
150
|
+
etag: response.headers["etag"],
|
|
151
|
+
lastModified: response.headers["last-modified"],
|
|
152
|
+
directives,
|
|
153
|
+
varyFields,
|
|
154
|
+
varyHeaders,
|
|
155
|
+
bodySize,
|
|
156
|
+
lastAccessedAt: ++this.accessCounter,
|
|
157
|
+
};
|
|
158
|
+
this.entries.set(key, entry);
|
|
159
|
+
this.currentSize += bodySize;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Merges a 304 Not Modified response with a cached entry, producing
|
|
163
|
+
* a full response with updated headers.
|
|
164
|
+
*/
|
|
165
|
+
mergeNotModified(entry, response304) {
|
|
166
|
+
const mergedHeaders = { ...entry.headers };
|
|
167
|
+
for (const [k, v] of Object.entries(response304.headers)) {
|
|
168
|
+
if (k === "content-length" || k === "content-encoding" || k === "transfer-encoding")
|
|
169
|
+
continue;
|
|
170
|
+
mergedHeaders[k] = v;
|
|
171
|
+
}
|
|
172
|
+
entry.headers = mergedHeaders;
|
|
173
|
+
entry.storedAt = Date.now();
|
|
174
|
+
if (response304.headers["etag"])
|
|
175
|
+
entry.etag = response304.headers["etag"];
|
|
176
|
+
if (response304.headers["last-modified"])
|
|
177
|
+
entry.lastModified = response304.headers["last-modified"];
|
|
178
|
+
entry.directives = parseCacheControl(mergedHeaders["cache-control"] ?? "");
|
|
179
|
+
return new NLcURLResponse({
|
|
180
|
+
status: entry.status,
|
|
181
|
+
statusText: entry.statusText,
|
|
182
|
+
headers: mergedHeaders,
|
|
183
|
+
rawBody: entry.body,
|
|
184
|
+
httpVersion: entry.httpVersion,
|
|
185
|
+
url: entry.url,
|
|
186
|
+
redirectCount: response304.redirectCount,
|
|
187
|
+
timings: response304.timings,
|
|
188
|
+
request: response304.request,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Constructs a full NLcURLResponse from a cache entry for direct serving.
|
|
193
|
+
*/
|
|
194
|
+
responseFromEntry(entry, req) {
|
|
195
|
+
return new NLcURLResponse({
|
|
196
|
+
status: entry.status,
|
|
197
|
+
statusText: entry.statusText,
|
|
198
|
+
headers: { ...entry.headers },
|
|
199
|
+
rawBody: entry.body,
|
|
200
|
+
httpVersion: entry.httpVersion,
|
|
201
|
+
url: entry.url,
|
|
202
|
+
redirectCount: 0,
|
|
203
|
+
timings: { dns: 0, connect: 0, tls: 0, firstByte: 0, total: 0 },
|
|
204
|
+
request: { url: req.url, method: (req.method ?? "GET"), headers: req.headers ?? {} },
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
/** Returns the number of cached entries. */
|
|
208
|
+
get size() {
|
|
209
|
+
return this.entries.size;
|
|
210
|
+
}
|
|
211
|
+
/** Returns the total byte size of all cached response bodies. */
|
|
212
|
+
get totalSize() {
|
|
213
|
+
return this.currentSize;
|
|
214
|
+
}
|
|
215
|
+
/** Clears all cached entries. */
|
|
216
|
+
clear() {
|
|
217
|
+
this.entries.clear();
|
|
218
|
+
this.currentSize = 0;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Removes a specific cache entry by method and URL.
|
|
222
|
+
*/
|
|
223
|
+
delete(method, url) {
|
|
224
|
+
const key = CacheStore.cacheKey(method, url);
|
|
225
|
+
const entry = this.entries.get(key);
|
|
226
|
+
if (entry) {
|
|
227
|
+
this.currentSize -= entry.bodySize;
|
|
228
|
+
this.entries.delete(key);
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Checks whether request headers match the stored Vary fields.
|
|
235
|
+
*/
|
|
236
|
+
varyMatches(entry, req) {
|
|
237
|
+
for (const field of entry.varyFields) {
|
|
238
|
+
const stored = entry.varyHeaders[field] ?? "";
|
|
239
|
+
const current = req.headers?.[field] ?? "";
|
|
240
|
+
if (stored !== current)
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Finds the key of the least recently accessed entry.
|
|
247
|
+
*/
|
|
248
|
+
findLRUKey() {
|
|
249
|
+
let lruKey;
|
|
250
|
+
let lruTime = Infinity;
|
|
251
|
+
for (const [key, entry] of this.entries) {
|
|
252
|
+
if (entry.lastAccessedAt < lruTime) {
|
|
253
|
+
lruTime = entry.lastAccessedAt;
|
|
254
|
+
lruKey = key;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return lruKey;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Evicts least-recently-used entries until there is room for a new entry
|
|
261
|
+
* of the given size.
|
|
262
|
+
*/
|
|
263
|
+
evictIfNeeded(incomingSize) {
|
|
264
|
+
while (this.entries.size >= this.maxEntries) {
|
|
265
|
+
const lruKey = this.findLRUKey();
|
|
266
|
+
if (lruKey === undefined)
|
|
267
|
+
break;
|
|
268
|
+
const entry = this.entries.get(lruKey);
|
|
269
|
+
if (entry)
|
|
270
|
+
this.currentSize -= entry.bodySize;
|
|
271
|
+
this.entries.delete(lruKey);
|
|
272
|
+
}
|
|
273
|
+
while (this.currentSize + incomingSize > this.maxSize && this.entries.size > 0) {
|
|
274
|
+
const lruKey = this.findLRUKey();
|
|
275
|
+
if (lruKey === undefined)
|
|
276
|
+
break;
|
|
277
|
+
const entry = this.entries.get(lruKey);
|
|
278
|
+
if (entry)
|
|
279
|
+
this.currentSize -= entry.bodySize;
|
|
280
|
+
this.entries.delete(lruKey);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Parses a `Cache-Control` header value into structured directives.
|
|
286
|
+
*/
|
|
287
|
+
export function parseCacheControl(value) {
|
|
288
|
+
const directives = {
|
|
289
|
+
noCache: false,
|
|
290
|
+
noStore: false,
|
|
291
|
+
mustRevalidate: false,
|
|
292
|
+
proxyRevalidate: false,
|
|
293
|
+
public: false,
|
|
294
|
+
private: false,
|
|
295
|
+
immutable: false,
|
|
296
|
+
};
|
|
297
|
+
if (!value)
|
|
298
|
+
return directives;
|
|
299
|
+
const parts = value.toLowerCase().split(",");
|
|
300
|
+
for (const part of parts) {
|
|
301
|
+
const trimmed = part.trim();
|
|
302
|
+
if (!trimmed)
|
|
303
|
+
continue;
|
|
304
|
+
const eqIdx = trimmed.indexOf("=");
|
|
305
|
+
const name = eqIdx === -1 ? trimmed : trimmed.slice(0, eqIdx).trim();
|
|
306
|
+
const val = eqIdx === -1
|
|
307
|
+
? undefined
|
|
308
|
+
: trimmed
|
|
309
|
+
.slice(eqIdx + 1)
|
|
310
|
+
.trim()
|
|
311
|
+
.replace(/^"|"$/g, "");
|
|
312
|
+
switch (name) {
|
|
313
|
+
case "max-age": {
|
|
314
|
+
const n = parseInt(val ?? "", 10);
|
|
315
|
+
if (Number.isFinite(n) && n >= 0)
|
|
316
|
+
directives.maxAge = n;
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
case "s-maxage": {
|
|
320
|
+
const n = parseInt(val ?? "", 10);
|
|
321
|
+
if (Number.isFinite(n) && n >= 0)
|
|
322
|
+
directives.sMaxAge = n;
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
case "no-cache":
|
|
326
|
+
directives.noCache = true;
|
|
327
|
+
break;
|
|
328
|
+
case "no-store":
|
|
329
|
+
directives.noStore = true;
|
|
330
|
+
break;
|
|
331
|
+
case "must-revalidate":
|
|
332
|
+
directives.mustRevalidate = true;
|
|
333
|
+
break;
|
|
334
|
+
case "proxy-revalidate":
|
|
335
|
+
directives.proxyRevalidate = true;
|
|
336
|
+
break;
|
|
337
|
+
case "public":
|
|
338
|
+
directives.public = true;
|
|
339
|
+
break;
|
|
340
|
+
case "private":
|
|
341
|
+
directives.private = true;
|
|
342
|
+
break;
|
|
343
|
+
case "immutable":
|
|
344
|
+
directives.immutable = true;
|
|
345
|
+
break;
|
|
346
|
+
case "stale-while-revalidate": {
|
|
347
|
+
const n = parseInt(val ?? "", 10);
|
|
348
|
+
if (Number.isFinite(n) && n >= 0)
|
|
349
|
+
directives.staleWhileRevalidate = n;
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
case "stale-if-error": {
|
|
353
|
+
const n = parseInt(val ?? "", 10);
|
|
354
|
+
if (Number.isFinite(n) && n >= 0)
|
|
355
|
+
directives.staleIfError = n;
|
|
356
|
+
break;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return directives;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Computes the freshness lifetime in seconds for a cache entry (RFC 9111 §4.2).
|
|
364
|
+
*
|
|
365
|
+
* Priority: Cache-Control max-age > Expires header > heuristic.
|
|
366
|
+
*/
|
|
367
|
+
function computeFreshnessLifetime(entry) {
|
|
368
|
+
if (entry.directives.maxAge !== undefined) {
|
|
369
|
+
return entry.directives.maxAge;
|
|
370
|
+
}
|
|
371
|
+
const expires = entry.headers["expires"];
|
|
372
|
+
if (expires) {
|
|
373
|
+
const expTime = Date.parse(expires);
|
|
374
|
+
if (!Number.isNaN(expTime)) {
|
|
375
|
+
const dateHeader = entry.headers["date"];
|
|
376
|
+
const responseDate = dateHeader ? Date.parse(dateHeader) : entry.storedAt;
|
|
377
|
+
if (!Number.isNaN(responseDate)) {
|
|
378
|
+
return Math.max(0, (expTime - responseDate) / 1000);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
if (entry.lastModified) {
|
|
383
|
+
const lmTime = Date.parse(entry.lastModified);
|
|
384
|
+
if (!Number.isNaN(lmTime)) {
|
|
385
|
+
const age = (entry.storedAt - lmTime) / 1000;
|
|
386
|
+
return Math.min(age * 0.1, 86400);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return 0;
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Parses a Vary header into an array of lowercased field names.
|
|
393
|
+
*/
|
|
394
|
+
function parseVary(value) {
|
|
395
|
+
if (!value)
|
|
396
|
+
return [];
|
|
397
|
+
return value
|
|
398
|
+
.split(",")
|
|
399
|
+
.map((v) => v.trim().toLowerCase())
|
|
400
|
+
.filter(Boolean);
|
|
401
|
+
}
|
|
402
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/cache/store.ts"],"names":[],"mappings":"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"}
|