undici 7.0.0-alpha.7 → 7.0.0-alpha.8
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/docs/docs/api/Dispatcher.md +2 -0
- package/lib/cache/memory-cache-store.js +2 -0
- package/lib/cache/sqlite-cache-store.js +32 -43
- package/lib/handler/cache-handler.js +144 -45
- package/lib/handler/cache-revalidation-handler.js +16 -5
- package/lib/handler/decorator-handler.js +3 -0
- package/lib/handler/redirect-handler.js +15 -38
- package/lib/handler/unwrap-handler.js +1 -1
- package/lib/interceptor/cache.js +253 -201
- package/lib/util/cache.js +42 -31
- package/package.json +4 -3
- package/types/cache-interceptor.d.ts +40 -0
package/lib/util/cache.js
CHANGED
|
@@ -96,36 +96,27 @@ function assertCacheValue (value) {
|
|
|
96
96
|
/**
|
|
97
97
|
* @see https://www.rfc-editor.org/rfc/rfc9111.html#name-cache-control
|
|
98
98
|
* @see https://www.iana.org/assignments/http-cache-directives/http-cache-directives.xhtml
|
|
99
|
-
|
|
100
|
-
* @typedef {{
|
|
101
|
-
* 'max-stale'?: number;
|
|
102
|
-
* 'min-fresh'?: number;
|
|
103
|
-
* 'max-age'?: number;
|
|
104
|
-
* 's-maxage'?: number;
|
|
105
|
-
* 'stale-while-revalidate'?: number;
|
|
106
|
-
* 'stale-if-error'?: number;
|
|
107
|
-
* public?: true;
|
|
108
|
-
* private?: true | string[];
|
|
109
|
-
* 'no-store'?: true;
|
|
110
|
-
* 'no-cache'?: true | string[];
|
|
111
|
-
* 'must-revalidate'?: true;
|
|
112
|
-
* 'proxy-revalidate'?: true;
|
|
113
|
-
* immutable?: true;
|
|
114
|
-
* 'no-transform'?: true;
|
|
115
|
-
* 'must-understand'?: true;
|
|
116
|
-
* 'only-if-cached'?: true;
|
|
117
|
-
* }} CacheControlDirectives
|
|
118
|
-
*
|
|
99
|
+
|
|
119
100
|
* @param {string | string[]} header
|
|
120
|
-
* @returns {CacheControlDirectives}
|
|
101
|
+
* @returns {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives}
|
|
121
102
|
*/
|
|
122
103
|
function parseCacheControlHeader (header) {
|
|
123
104
|
/**
|
|
124
|
-
* @type {import('
|
|
105
|
+
* @type {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives}
|
|
125
106
|
*/
|
|
126
107
|
const output = {}
|
|
127
108
|
|
|
128
|
-
|
|
109
|
+
let directives
|
|
110
|
+
if (Array.isArray(header)) {
|
|
111
|
+
directives = []
|
|
112
|
+
|
|
113
|
+
for (const directive of header) {
|
|
114
|
+
directives.push(...directive.split(','))
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
directives = header.split(',')
|
|
118
|
+
}
|
|
119
|
+
|
|
129
120
|
for (let i = 0; i < directives.length; i++) {
|
|
130
121
|
const directive = directives[i].toLowerCase()
|
|
131
122
|
const keyValueDelimiter = directive.indexOf('=')
|
|
@@ -133,10 +124,8 @@ function parseCacheControlHeader (header) {
|
|
|
133
124
|
let key
|
|
134
125
|
let value
|
|
135
126
|
if (keyValueDelimiter !== -1) {
|
|
136
|
-
key = directive.substring(0, keyValueDelimiter).
|
|
137
|
-
value = directive
|
|
138
|
-
.substring(keyValueDelimiter + 1)
|
|
139
|
-
.trim()
|
|
127
|
+
key = directive.substring(0, keyValueDelimiter).trimStart()
|
|
128
|
+
value = directive.substring(keyValueDelimiter + 1)
|
|
140
129
|
} else {
|
|
141
130
|
key = directive.trim()
|
|
142
131
|
}
|
|
@@ -148,16 +137,28 @@ function parseCacheControlHeader (header) {
|
|
|
148
137
|
case 's-maxage':
|
|
149
138
|
case 'stale-while-revalidate':
|
|
150
139
|
case 'stale-if-error': {
|
|
151
|
-
if (value === undefined) {
|
|
140
|
+
if (value === undefined || value[0] === ' ') {
|
|
152
141
|
continue
|
|
153
142
|
}
|
|
154
143
|
|
|
144
|
+
if (
|
|
145
|
+
value.length >= 2 &&
|
|
146
|
+
value[0] === '"' &&
|
|
147
|
+
value[value.length - 1] === '"'
|
|
148
|
+
) {
|
|
149
|
+
value = value.substring(1, value.length - 1)
|
|
150
|
+
}
|
|
151
|
+
|
|
155
152
|
const parsedValue = parseInt(value, 10)
|
|
156
153
|
// eslint-disable-next-line no-self-compare
|
|
157
154
|
if (parsedValue !== parsedValue) {
|
|
158
155
|
continue
|
|
159
156
|
}
|
|
160
157
|
|
|
158
|
+
if (key === 'max-age' && key in output && output[key] >= parsedValue) {
|
|
159
|
+
continue
|
|
160
|
+
}
|
|
161
|
+
|
|
161
162
|
output[key] = parsedValue
|
|
162
163
|
|
|
163
164
|
break
|
|
@@ -206,11 +207,19 @@ function parseCacheControlHeader (header) {
|
|
|
206
207
|
headers[headers.length - 1] = lastHeader
|
|
207
208
|
}
|
|
208
209
|
|
|
209
|
-
|
|
210
|
+
if (key in output) {
|
|
211
|
+
output[key] = output[key].concat(headers)
|
|
212
|
+
} else {
|
|
213
|
+
output[key] = headers
|
|
214
|
+
}
|
|
210
215
|
}
|
|
211
216
|
} else {
|
|
212
217
|
// Something like `no-cache=some-header`
|
|
213
|
-
|
|
218
|
+
if (key in output) {
|
|
219
|
+
output[key] = output[key].concat(value)
|
|
220
|
+
} else {
|
|
221
|
+
output[key] = [value]
|
|
222
|
+
}
|
|
214
223
|
}
|
|
215
224
|
|
|
216
225
|
break
|
|
@@ -248,7 +257,7 @@ function parseCacheControlHeader (header) {
|
|
|
248
257
|
* @returns {Record<string, string | string[]>}
|
|
249
258
|
*/
|
|
250
259
|
function parseVaryHeader (varyHeader, headers) {
|
|
251
|
-
if (typeof varyHeader === 'string' && varyHeader
|
|
260
|
+
if (typeof varyHeader === 'string' && varyHeader.includes('*')) {
|
|
252
261
|
return headers
|
|
253
262
|
}
|
|
254
263
|
|
|
@@ -262,6 +271,8 @@ function parseVaryHeader (varyHeader, headers) {
|
|
|
262
271
|
|
|
263
272
|
if (headers[trimmedHeader]) {
|
|
264
273
|
output[trimmedHeader] = headers[trimmedHeader]
|
|
274
|
+
} else {
|
|
275
|
+
return undefined
|
|
265
276
|
}
|
|
266
277
|
}
|
|
267
278
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "undici",
|
|
3
|
-
"version": "7.0.0-alpha.
|
|
3
|
+
"version": "7.0.0-alpha.8",
|
|
4
4
|
"description": "An HTTP/1.1 client, written from scratch for Node.js",
|
|
5
5
|
"homepage": "https://undici.nodejs.org",
|
|
6
6
|
"bugs": {
|
|
@@ -73,6 +73,7 @@
|
|
|
73
73
|
"test:javascript:without-intl": "npm run test:javascript:no-jest",
|
|
74
74
|
"test:busboy": "borp -p \"test/busboy/*.js\"",
|
|
75
75
|
"test:cache": "borp -p \"test/cache/*.js\"",
|
|
76
|
+
"test:sqlite": "NODE_OPTIONS=--experimental-sqlite borp -p \"test/cache-interceptor/*.js\"",
|
|
76
77
|
"test:cache-interceptor": "borp -p \"test/cache-interceptor/*.js\"",
|
|
77
78
|
"test:cookies": "borp -p \"test/cookie/*.js\"",
|
|
78
79
|
"test:eventsource": "npm run build:node && borp --expose-gc -p \"test/eventsource/*.js\"",
|
|
@@ -110,7 +111,7 @@
|
|
|
110
111
|
"@sinonjs/fake-timers": "^12.0.0",
|
|
111
112
|
"@types/node": "^18.19.50",
|
|
112
113
|
"abort-controller": "^3.0.0",
|
|
113
|
-
"borp": "^0.
|
|
114
|
+
"borp": "^0.19.0",
|
|
114
115
|
"c8": "^10.0.0",
|
|
115
116
|
"cross-env": "^7.0.3",
|
|
116
117
|
"dns-packet": "^5.4.0",
|
|
@@ -128,7 +129,7 @@
|
|
|
128
129
|
"ws": "^8.11.0"
|
|
129
130
|
},
|
|
130
131
|
"engines": {
|
|
131
|
-
"node": ">=18.
|
|
132
|
+
"node": ">=20.18.1"
|
|
132
133
|
},
|
|
133
134
|
"tsd": {
|
|
134
135
|
"directory": "test/types",
|
|
@@ -7,6 +7,10 @@ declare namespace CacheHandler {
|
|
|
7
7
|
|
|
8
8
|
export interface CacheHandlerOptions {
|
|
9
9
|
store: CacheStore
|
|
10
|
+
|
|
11
|
+
cacheByDefault?: number
|
|
12
|
+
|
|
13
|
+
type?: CacheOptions['type']
|
|
10
14
|
}
|
|
11
15
|
|
|
12
16
|
export interface CacheOptions {
|
|
@@ -20,6 +24,39 @@ declare namespace CacheHandler {
|
|
|
20
24
|
* @see https://www.rfc-editor.org/rfc/rfc9110#section-9.2.1
|
|
21
25
|
*/
|
|
22
26
|
methods?: CacheMethods[]
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* RFC9111 allows for caching responses that we aren't explicitly told to
|
|
30
|
+
* cache or to not cache.
|
|
31
|
+
* @see https://www.rfc-editor.org/rfc/rfc9111.html#section-3-5
|
|
32
|
+
* @default undefined
|
|
33
|
+
*/
|
|
34
|
+
cacheByDefault?: number
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* TODO docs
|
|
38
|
+
* @default 'shared'
|
|
39
|
+
*/
|
|
40
|
+
type?: 'shared' | 'private'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface CacheControlDirectives {
|
|
44
|
+
'max-stale'?: number;
|
|
45
|
+
'min-fresh'?: number;
|
|
46
|
+
'max-age'?: number;
|
|
47
|
+
's-maxage'?: number;
|
|
48
|
+
'stale-while-revalidate'?: number;
|
|
49
|
+
'stale-if-error'?: number;
|
|
50
|
+
public?: true;
|
|
51
|
+
private?: true | string[];
|
|
52
|
+
'no-store'?: true;
|
|
53
|
+
'no-cache'?: true | string[];
|
|
54
|
+
'must-revalidate'?: true;
|
|
55
|
+
'proxy-revalidate'?: true;
|
|
56
|
+
immutable?: true;
|
|
57
|
+
'no-transform'?: true;
|
|
58
|
+
'must-understand'?: true;
|
|
59
|
+
'only-if-cached'?: true;
|
|
23
60
|
}
|
|
24
61
|
|
|
25
62
|
export interface CacheKey {
|
|
@@ -35,6 +72,7 @@ declare namespace CacheHandler {
|
|
|
35
72
|
headers: Record<string, string | string[]>
|
|
36
73
|
vary?: Record<string, string | string[]>
|
|
37
74
|
etag?: string
|
|
75
|
+
cacheControlDirectives?: CacheControlDirectives
|
|
38
76
|
cachedAt: number
|
|
39
77
|
staleAt: number
|
|
40
78
|
deleteAt: number
|
|
@@ -50,8 +88,10 @@ declare namespace CacheHandler {
|
|
|
50
88
|
statusCode: number
|
|
51
89
|
statusMessage: string
|
|
52
90
|
headers: Record<string, string | string[]>
|
|
91
|
+
vary?: Record<string, string | string[]>
|
|
53
92
|
etag?: string
|
|
54
93
|
body: null | Readable | Iterable<Buffer> | AsyncIterable<Buffer> | Buffer | Iterable<string> | AsyncIterable<string> | string
|
|
94
|
+
cacheControlDirectives: CacheControlDirectives,
|
|
55
95
|
cachedAt: number
|
|
56
96
|
staleAt: number
|
|
57
97
|
deleteAt: number
|