@nxtedition/nxt-undici 6.0.2 → 6.0.4
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/lib/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import undici from '@nxtedition/undici'
|
|
2
2
|
import { parseHeaders } from './utils.js'
|
|
3
3
|
import { request as _request } from './request.js'
|
|
4
|
-
import {
|
|
4
|
+
import { cacheStores } from 'undici'
|
|
5
5
|
|
|
6
6
|
const dispatcherCache = new WeakMap()
|
|
7
7
|
|
|
@@ -20,7 +20,7 @@ export const interceptors = {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export const cache = {
|
|
23
|
-
SqliteCacheStore,
|
|
23
|
+
SqliteCacheStore: cacheStores.SqliteCacheStore,
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export { parseHeaders } from './utils.js'
|
package/lib/interceptor/cache.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { cacheStores } from 'undici'
|
|
2
2
|
import { DecoratorHandler, parseCacheControl } from '../utils.js'
|
|
3
3
|
|
|
4
|
-
const DEFAULT_STORE = new SqliteCacheStore({ location: ':memory:' })
|
|
4
|
+
const DEFAULT_STORE = new cacheStores.SqliteCacheStore({ location: ':memory:' })
|
|
5
5
|
const MAX_ENTRY_SIZE = 128 * 1024
|
|
6
6
|
|
|
7
7
|
class CacheHandler extends DecoratorHandler {
|
package/lib/interceptor/dns.js
CHANGED
|
@@ -27,10 +27,10 @@ class Handler extends DecoratorHandler {
|
|
|
27
27
|
|
|
28
28
|
export default () => (dispatch) => {
|
|
29
29
|
const cache = new Map()
|
|
30
|
-
const
|
|
30
|
+
const promises = new Map()
|
|
31
31
|
|
|
32
32
|
function resolve(hostname) {
|
|
33
|
-
let promise =
|
|
33
|
+
let promise = promises.get(hostname)
|
|
34
34
|
if (!promise) {
|
|
35
35
|
promise = resolve4(hostname, { ttl: true })
|
|
36
36
|
.then((records) => {
|
|
@@ -45,9 +45,9 @@ export default () => (dispatch) => {
|
|
|
45
45
|
return next
|
|
46
46
|
})
|
|
47
47
|
.finally(() => {
|
|
48
|
-
|
|
48
|
+
promises.delete(hostname)
|
|
49
49
|
})
|
|
50
|
-
|
|
50
|
+
promises.set(hostname, promise)
|
|
51
51
|
}
|
|
52
52
|
return promise
|
|
53
53
|
}
|
|
@@ -71,7 +71,7 @@ export default () => (dispatch) => {
|
|
|
71
71
|
|
|
72
72
|
if (records == null || records.every((x) => x.stats.errored || x.expires < now)) {
|
|
73
73
|
records = await resolve(hostname)
|
|
74
|
-
} else if (records.some((x) => x.errored || x.expires + 1e3
|
|
74
|
+
} else if (records.some((x) => x.errored || x.expires < now + 1e3)) {
|
|
75
75
|
resolve(hostname).catch(noop)
|
|
76
76
|
}
|
|
77
77
|
|
|
@@ -103,7 +103,9 @@ export default () => (dispatch) => {
|
|
|
103
103
|
{ ...opts, origin, headers: { ...opts.headers, host } },
|
|
104
104
|
new Handler(handler, (err) => {
|
|
105
105
|
record.pending--
|
|
106
|
-
if (err
|
|
106
|
+
if (err == null) {
|
|
107
|
+
record.stats.errored = 0
|
|
108
|
+
} else if (err.name === 'AbortError') {
|
|
107
109
|
// Do nothing...
|
|
108
110
|
} else if (err.statusCode == null || err.statusCode >= 500) {
|
|
109
111
|
record.stats.errored++
|
|
@@ -84,7 +84,7 @@ class Handler extends DecoratorHandler {
|
|
|
84
84
|
|
|
85
85
|
#decorateError(err) {
|
|
86
86
|
try {
|
|
87
|
-
err.url ??= new URL(this.#opts.path, this.#opts.origin).href
|
|
87
|
+
err.url ??= this.#opts.origin ? new URL(this.#opts.path, this.#opts.origin).href : null
|
|
88
88
|
|
|
89
89
|
err.req = {
|
|
90
90
|
method: this.#opts?.method,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nxtedition/nxt-undici",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.4",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Robert Nagy <robert.nagy@boffins.se>",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@nxtedition/undici": "^10.0.2",
|
|
13
13
|
"cache-control-parser": "^2.0.6",
|
|
14
|
-
"http-errors": "^2.0.0"
|
|
14
|
+
"http-errors": "^2.0.0",
|
|
15
|
+
"undici": "^7.3.0"
|
|
15
16
|
},
|
|
16
17
|
"devDependencies": {
|
|
17
18
|
"@types/node": "^22.10.7",
|
|
@@ -1,445 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
import { assertCacheKey, assertCacheValue } from './util.js'
|
|
4
|
-
|
|
5
|
-
let DatabaseSync
|
|
6
|
-
try {
|
|
7
|
-
DatabaseSync = await import('node:sqlite').then((x) => x.DatabaseSync)
|
|
8
|
-
} catch {
|
|
9
|
-
// Do nothing...
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const VERSION = 3
|
|
13
|
-
|
|
14
|
-
// 2gb
|
|
15
|
-
const MAX_ENTRY_SIZE = 2 * 1000 * 1000 * 1000
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* @typedef {import('undici-types/cache-interceptor.d.ts').default.CacheStore} CacheStore
|
|
19
|
-
* @implements {CacheStore}
|
|
20
|
-
*
|
|
21
|
-
* @typedef {{
|
|
22
|
-
* id: Readonly<number>
|
|
23
|
-
* body?: Buffer
|
|
24
|
-
* statusCode: number
|
|
25
|
-
* statusMessage: string
|
|
26
|
-
* headers?: string
|
|
27
|
-
* vary?: string
|
|
28
|
-
* etag?: string
|
|
29
|
-
* cacheControlDirectives?: string
|
|
30
|
-
* cachedAt: number
|
|
31
|
-
* staleAt: number
|
|
32
|
-
* deleteAt: number
|
|
33
|
-
* }} SqliteStoreValue
|
|
34
|
-
*/
|
|
35
|
-
|
|
36
|
-
export class SqliteCacheStore {
|
|
37
|
-
#maxEntrySize = MAX_ENTRY_SIZE
|
|
38
|
-
#maxCount = Infinity
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* @type {import('node:sqlite').DatabaseSync}
|
|
42
|
-
*/
|
|
43
|
-
#db
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* @type {import('node:sqlite').StatementSync}
|
|
47
|
-
*/
|
|
48
|
-
#getValuesQuery
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* @type {import('node:sqlite').StatementSync}
|
|
52
|
-
*/
|
|
53
|
-
#updateValueQuery
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* @type {import('node:sqlite').StatementSync}
|
|
57
|
-
*/
|
|
58
|
-
#insertValueQuery
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* @type {import('node:sqlite').StatementSync}
|
|
62
|
-
*/
|
|
63
|
-
#deleteExpiredValuesQuery
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* @type {import('node:sqlite').StatementSync}
|
|
67
|
-
*/
|
|
68
|
-
#deleteByUrlQuery
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* @type {import('node:sqlite').StatementSync}
|
|
72
|
-
*/
|
|
73
|
-
#countEntriesQuery
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* @type {import('node:sqlite').StatementSync}
|
|
77
|
-
*/
|
|
78
|
-
#deleteOldValuesQuery
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* @param {import('undici-types/cache-interceptor.d.ts').default.SqliteCacheStoreOpts | undefined} opts
|
|
82
|
-
*/
|
|
83
|
-
constructor(opts) {
|
|
84
|
-
if (opts) {
|
|
85
|
-
if (typeof opts !== 'object') {
|
|
86
|
-
throw new TypeError('SqliteCacheStore options must be an object')
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (opts.maxEntrySize !== undefined) {
|
|
90
|
-
if (
|
|
91
|
-
typeof opts.maxEntrySize !== 'number' ||
|
|
92
|
-
!Number.isInteger(opts.maxEntrySize) ||
|
|
93
|
-
opts.maxEntrySize < 0
|
|
94
|
-
) {
|
|
95
|
-
throw new TypeError(
|
|
96
|
-
'SqliteCacheStore options.maxEntrySize must be a non-negative integer',
|
|
97
|
-
)
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (opts.maxEntrySize > MAX_ENTRY_SIZE) {
|
|
101
|
-
throw new TypeError('SqliteCacheStore options.maxEntrySize must be less than 2gb')
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
this.#maxEntrySize = opts.maxEntrySize
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (opts.maxCount !== undefined) {
|
|
108
|
-
if (
|
|
109
|
-
typeof opts.maxCount !== 'number' ||
|
|
110
|
-
!Number.isInteger(opts.maxCount) ||
|
|
111
|
-
opts.maxCount < 0
|
|
112
|
-
) {
|
|
113
|
-
throw new TypeError('SqliteCacheStore options.maxCount must be a non-negative integer')
|
|
114
|
-
}
|
|
115
|
-
this.#maxCount = opts.maxCount
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
this.#db = new DatabaseSync(opts?.location ?? ':memory:')
|
|
120
|
-
|
|
121
|
-
this.#db.exec(`
|
|
122
|
-
CREATE TABLE IF NOT EXISTS cacheInterceptorV${VERSION} (
|
|
123
|
-
-- Data specific to us
|
|
124
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
125
|
-
url TEXT NOT NULL,
|
|
126
|
-
method TEXT NOT NULL,
|
|
127
|
-
|
|
128
|
-
-- Data returned to the interceptor
|
|
129
|
-
body BUF NULL,
|
|
130
|
-
deleteAt INTEGER NOT NULL,
|
|
131
|
-
statusCode INTEGER NOT NULL,
|
|
132
|
-
statusMessage TEXT NOT NULL,
|
|
133
|
-
headers TEXT NULL,
|
|
134
|
-
cacheControlDirectives TEXT NULL,
|
|
135
|
-
etag TEXT NULL,
|
|
136
|
-
vary TEXT NULL,
|
|
137
|
-
cachedAt INTEGER NOT NULL,
|
|
138
|
-
staleAt INTEGER NOT NULL
|
|
139
|
-
);
|
|
140
|
-
|
|
141
|
-
CREATE INDEX IF NOT EXISTS idx_cacheInterceptorV${VERSION}_url ON cacheInterceptorV${VERSION}(url);
|
|
142
|
-
CREATE INDEX IF NOT EXISTS idx_cacheInterceptorV${VERSION}_method ON cacheInterceptorV${VERSION}(method);
|
|
143
|
-
CREATE INDEX IF NOT EXISTS idx_cacheInterceptorV${VERSION}_deleteAt ON cacheInterceptorV${VERSION}(deleteAt);
|
|
144
|
-
`)
|
|
145
|
-
|
|
146
|
-
this.#getValuesQuery = this.#db.prepare(`
|
|
147
|
-
SELECT
|
|
148
|
-
id,
|
|
149
|
-
body,
|
|
150
|
-
deleteAt,
|
|
151
|
-
statusCode,
|
|
152
|
-
statusMessage,
|
|
153
|
-
headers,
|
|
154
|
-
etag,
|
|
155
|
-
cacheControlDirectives,
|
|
156
|
-
vary,
|
|
157
|
-
cachedAt,
|
|
158
|
-
staleAt
|
|
159
|
-
FROM cacheInterceptorV${VERSION}
|
|
160
|
-
WHERE
|
|
161
|
-
url = ?
|
|
162
|
-
AND method = ?
|
|
163
|
-
ORDER BY
|
|
164
|
-
deleteAt ASC
|
|
165
|
-
`)
|
|
166
|
-
|
|
167
|
-
this.#updateValueQuery = this.#db.prepare(`
|
|
168
|
-
UPDATE cacheInterceptorV${VERSION} SET
|
|
169
|
-
body = ?,
|
|
170
|
-
deleteAt = ?,
|
|
171
|
-
statusCode = ?,
|
|
172
|
-
statusMessage = ?,
|
|
173
|
-
headers = ?,
|
|
174
|
-
etag = ?,
|
|
175
|
-
cacheControlDirectives = ?,
|
|
176
|
-
cachedAt = ?,
|
|
177
|
-
staleAt = ?,
|
|
178
|
-
deleteAt = ?
|
|
179
|
-
WHERE
|
|
180
|
-
id = ?
|
|
181
|
-
`)
|
|
182
|
-
|
|
183
|
-
this.#insertValueQuery = this.#db.prepare(`
|
|
184
|
-
INSERT INTO cacheInterceptorV${VERSION} (
|
|
185
|
-
url,
|
|
186
|
-
method,
|
|
187
|
-
body,
|
|
188
|
-
deleteAt,
|
|
189
|
-
statusCode,
|
|
190
|
-
statusMessage,
|
|
191
|
-
headers,
|
|
192
|
-
etag,
|
|
193
|
-
cacheControlDirectives,
|
|
194
|
-
vary,
|
|
195
|
-
cachedAt,
|
|
196
|
-
staleAt,
|
|
197
|
-
deleteAt
|
|
198
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
199
|
-
`)
|
|
200
|
-
|
|
201
|
-
this.#deleteByUrlQuery = this.#db.prepare(
|
|
202
|
-
`DELETE FROM cacheInterceptorV${VERSION} WHERE url = ?`,
|
|
203
|
-
)
|
|
204
|
-
|
|
205
|
-
this.#countEntriesQuery = this.#db.prepare(
|
|
206
|
-
`SELECT COUNT(*) AS total FROM cacheInterceptorV${VERSION}`,
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
this.#deleteExpiredValuesQuery = this.#db.prepare(
|
|
210
|
-
`DELETE FROM cacheInterceptorV${VERSION} WHERE deleteAt <= ?`,
|
|
211
|
-
)
|
|
212
|
-
|
|
213
|
-
this.#deleteOldValuesQuery =
|
|
214
|
-
this.#maxCount === Infinity
|
|
215
|
-
? null
|
|
216
|
-
: this.#db.prepare(`
|
|
217
|
-
DELETE FROM cacheInterceptorV${VERSION}
|
|
218
|
-
WHERE id IN (
|
|
219
|
-
SELECT
|
|
220
|
-
id
|
|
221
|
-
FROM cacheInterceptorV${VERSION}
|
|
222
|
-
ORDER BY cachedAt DESC
|
|
223
|
-
LIMIT ?
|
|
224
|
-
)
|
|
225
|
-
`)
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
close() {
|
|
229
|
-
this.#db.close()
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* @param {import('undici-types/cache-interceptor.d.ts').default.CacheKey} key
|
|
234
|
-
* @returns {import('undici-types/cache-interceptor.d.ts').default.GetResult | undefined}
|
|
235
|
-
*/
|
|
236
|
-
get(key) {
|
|
237
|
-
assertCacheKey(key)
|
|
238
|
-
|
|
239
|
-
const value = this.#findValue(key)
|
|
240
|
-
|
|
241
|
-
if (!value) {
|
|
242
|
-
return undefined
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* @type {import('undici-types/cache-interceptor.d.ts').default.GetResult}
|
|
247
|
-
*/
|
|
248
|
-
const result = {
|
|
249
|
-
body: value.body ? Buffer.from(value.body) : null,
|
|
250
|
-
statusCode: value.statusCode,
|
|
251
|
-
statusMessage: value.statusMessage,
|
|
252
|
-
headers: value.headers ? JSON.parse(value.headers) : undefined,
|
|
253
|
-
etag: value.etag ? value.etag : undefined,
|
|
254
|
-
vary: value.vary ? JSON.parse(value.vary) : undefined,
|
|
255
|
-
cacheControlDirectives: value.cacheControlDirectives
|
|
256
|
-
? JSON.parse(value.cacheControlDirectives)
|
|
257
|
-
: undefined,
|
|
258
|
-
cachedAt: value.cachedAt,
|
|
259
|
-
staleAt: value.staleAt,
|
|
260
|
-
deleteAt: value.deleteAt,
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
return result
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* @param {import('undici-types/cache-interceptor.d.ts').default.CacheKey} key
|
|
268
|
-
* @param {import('undici-types/cache-interceptor.d.ts').default.CacheValue & { body: Buffer | Array<Buffer> | null }} value
|
|
269
|
-
*/
|
|
270
|
-
set(key, value) {
|
|
271
|
-
assertCacheKey(key)
|
|
272
|
-
assertCacheValue(value)
|
|
273
|
-
|
|
274
|
-
const body = Array.isArray(value.body) ? Buffer.concat(value.body) : value.body
|
|
275
|
-
const size = body ? body.byteLength : 0
|
|
276
|
-
|
|
277
|
-
if (size > this.#maxEntrySize) {
|
|
278
|
-
return false
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
const url = this.#makeValueUrl(key)
|
|
282
|
-
|
|
283
|
-
const existingValue = this.#findValue(key, true)
|
|
284
|
-
if (existingValue) {
|
|
285
|
-
// Updating an existing response, let's overwrite it
|
|
286
|
-
this.#updateValueQuery.run(
|
|
287
|
-
body,
|
|
288
|
-
value.deleteAt,
|
|
289
|
-
value.statusCode,
|
|
290
|
-
value.statusMessage,
|
|
291
|
-
value.headers ? JSON.stringify(value.headers) : null,
|
|
292
|
-
value.etag ? value.etag : null,
|
|
293
|
-
value.cacheControlDirectives ? JSON.stringify(value.cacheControlDirectives) : null,
|
|
294
|
-
value.cachedAt,
|
|
295
|
-
value.staleAt,
|
|
296
|
-
value.deleteAt,
|
|
297
|
-
existingValue.id,
|
|
298
|
-
)
|
|
299
|
-
} else {
|
|
300
|
-
this.#prune()
|
|
301
|
-
// New response, let's insert it
|
|
302
|
-
this.#insertValueQuery.run(
|
|
303
|
-
url,
|
|
304
|
-
key.method,
|
|
305
|
-
body,
|
|
306
|
-
value.deleteAt,
|
|
307
|
-
value.statusCode,
|
|
308
|
-
value.statusMessage,
|
|
309
|
-
value.headers ? JSON.stringify(value.headers) : null,
|
|
310
|
-
value.etag ? value.etag : null,
|
|
311
|
-
value.cacheControlDirectives ? JSON.stringify(value.cacheControlDirectives) : null,
|
|
312
|
-
value.vary ? JSON.stringify(value.vary) : null,
|
|
313
|
-
value.cachedAt,
|
|
314
|
-
value.staleAt,
|
|
315
|
-
value.deleteAt,
|
|
316
|
-
)
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
return true
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
/**
|
|
323
|
-
* @param {import('undici-types/cache-interceptor.d.ts').default.CacheKey} key
|
|
324
|
-
*/
|
|
325
|
-
delete(key) {
|
|
326
|
-
if (typeof key !== 'object') {
|
|
327
|
-
throw new TypeError(`expected key to be object, got ${typeof key}`)
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
this.#deleteByUrlQuery.run(this.#makeValueUrl(key))
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
#prune() {
|
|
334
|
-
if (this.size <= this.#maxCount) {
|
|
335
|
-
return 0
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
{
|
|
339
|
-
const removed = this.#deleteExpiredValuesQuery.run(Date.now()).changes
|
|
340
|
-
if (removed > 0) {
|
|
341
|
-
return removed
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
{
|
|
346
|
-
const removed = this.#deleteOldValuesQuery.run(
|
|
347
|
-
Math.max(Math.floor(this.#maxCount * 0.1), 1),
|
|
348
|
-
).changes
|
|
349
|
-
if (removed > 0) {
|
|
350
|
-
return removed
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
return 0
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
/**
|
|
358
|
-
* Counts the number of rows in the cache
|
|
359
|
-
* @returns {Number}
|
|
360
|
-
*/
|
|
361
|
-
get size() {
|
|
362
|
-
const { total } = this.#countEntriesQuery.get()
|
|
363
|
-
return total
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
/**
|
|
367
|
-
* @param {import('undici-types/cache-interceptor.d.ts').default.CacheKey} key
|
|
368
|
-
* @returns {string}
|
|
369
|
-
*/
|
|
370
|
-
#makeValueUrl(key) {
|
|
371
|
-
return `${key.origin}/${key.path}`
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
/**
|
|
375
|
-
* @param {import('undici-types/cache-interceptor.d.ts').default.CacheKey} key
|
|
376
|
-
* @param {boolean} [canBeExpired=false]
|
|
377
|
-
* @returns {SqliteStoreValue | undefined}
|
|
378
|
-
*/
|
|
379
|
-
#findValue(key, canBeExpired = false) {
|
|
380
|
-
const url = this.#makeValueUrl(key)
|
|
381
|
-
const { headers, method } = key
|
|
382
|
-
|
|
383
|
-
/**
|
|
384
|
-
* @type {SqliteStoreValue[]}
|
|
385
|
-
*/
|
|
386
|
-
const values = this.#getValuesQuery.all(url, method)
|
|
387
|
-
|
|
388
|
-
if (values.length === 0) {
|
|
389
|
-
return undefined
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
const now = Date.now()
|
|
393
|
-
for (const value of values) {
|
|
394
|
-
if (now >= value.deleteAt && !canBeExpired) {
|
|
395
|
-
return undefined
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
let matches = true
|
|
399
|
-
|
|
400
|
-
if (value.vary) {
|
|
401
|
-
if (!headers) {
|
|
402
|
-
return undefined
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
const vary = JSON.parse(value.vary)
|
|
406
|
-
|
|
407
|
-
for (const header in vary) {
|
|
408
|
-
if (!headerValueEquals(headers[header], vary[header])) {
|
|
409
|
-
matches = false
|
|
410
|
-
break
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
if (matches) {
|
|
416
|
-
return value
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
return undefined
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
/**
|
|
425
|
-
* @param {string|string[]|null|undefined} lhs
|
|
426
|
-
* @param {string|string[]|null|undefined} rhs
|
|
427
|
-
* @returns {boolean}
|
|
428
|
-
*/
|
|
429
|
-
function headerValueEquals(lhs, rhs) {
|
|
430
|
-
if (Array.isArray(lhs) && Array.isArray(rhs)) {
|
|
431
|
-
if (lhs.length !== rhs.length) {
|
|
432
|
-
return false
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
for (let i = 0; i < lhs.length; i++) {
|
|
436
|
-
if (rhs.includes(lhs[i])) {
|
|
437
|
-
return false
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
return true
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
return lhs === rhs
|
|
445
|
-
}
|
package/lib/cache/util.js
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @param {any} key
|
|
3
|
-
*/
|
|
4
|
-
export function assertCacheKey(key) {
|
|
5
|
-
if (typeof key !== 'object') {
|
|
6
|
-
throw new TypeError(`expected key to be object, got ${typeof key}`)
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
for (const property of ['origin', 'method', 'path']) {
|
|
10
|
-
if (typeof key[property] !== 'string') {
|
|
11
|
-
throw new TypeError(`expected key.${property} to be string, got ${typeof key[property]}`)
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
if (key.headers !== undefined && typeof key.headers !== 'object') {
|
|
16
|
-
throw new TypeError(`expected headers to be object, got ${typeof key}`)
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* @param {any} value
|
|
22
|
-
*/
|
|
23
|
-
export function assertCacheValue(value) {
|
|
24
|
-
if (typeof value !== 'object') {
|
|
25
|
-
throw new TypeError(`expected value to be object, got ${typeof value}`)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
for (const property of ['statusCode', 'cachedAt', 'staleAt', 'deleteAt']) {
|
|
29
|
-
if (typeof value[property] !== 'number') {
|
|
30
|
-
throw new TypeError(`expected value.${property} to be number, got ${typeof value[property]}`)
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (typeof value.statusMessage !== 'string') {
|
|
35
|
-
throw new TypeError(
|
|
36
|
-
`expected value.statusMessage to be string, got ${typeof value.statusMessage}`,
|
|
37
|
-
)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (value.headers != null && typeof value.headers !== 'object') {
|
|
41
|
-
throw new TypeError(`expected value.rawHeaders to be object, got ${typeof value.headers}`)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (value.vary !== undefined && typeof value.vary !== 'object') {
|
|
45
|
-
throw new TypeError(`expected value.vary to be object, got ${typeof value.vary}`)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (value.etag !== undefined && typeof value.etag !== 'string') {
|
|
49
|
-
throw new TypeError(`expected value.etag to be string, got ${typeof value.etag}`)
|
|
50
|
-
}
|
|
51
|
-
}
|