@nxtedition/nxt-undici 6.2.20 → 6.2.23
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/interceptor/cache.js +4 -1
- package/lib/sqlite-cache-store.js +6 -106
- package/package.json +1 -1
package/lib/interceptor/cache.js
CHANGED
|
@@ -4,6 +4,7 @@ import { SqliteCacheStore } from '../sqlite-cache-store.js'
|
|
|
4
4
|
|
|
5
5
|
const DEFAULT_STORE = new SqliteCacheStore({ location: ':memory:' })
|
|
6
6
|
const DEFAULT_MAX_ENTRY_SIZE = 128 * 1024
|
|
7
|
+
const DEFAULT_MAX_ENTRY_TTL = 14 * 24 * 3600
|
|
7
8
|
const NOOP = () => {}
|
|
8
9
|
|
|
9
10
|
class CacheHandler extends DecoratorHandler {
|
|
@@ -12,6 +13,7 @@ class CacheHandler extends DecoratorHandler {
|
|
|
12
13
|
#store
|
|
13
14
|
#logger
|
|
14
15
|
#maxEntrySize
|
|
16
|
+
#maxEntryTTL
|
|
15
17
|
|
|
16
18
|
constructor(key, { store, logger, handler, maxEntrySize }) {
|
|
17
19
|
super(handler)
|
|
@@ -21,6 +23,7 @@ class CacheHandler extends DecoratorHandler {
|
|
|
21
23
|
this.#value = null
|
|
22
24
|
this.#store = store
|
|
23
25
|
this.#maxEntrySize = maxEntrySize ?? store.maxEntrySize ?? DEFAULT_MAX_ENTRY_SIZE
|
|
26
|
+
this.#maxEntryTTL = maxEntrySize ?? store.maxEntryTTL ?? DEFAULT_MAX_ENTRY_TTL
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
onConnect(abort) {
|
|
@@ -124,7 +127,7 @@ class CacheHandler extends DecoratorHandler {
|
|
|
124
127
|
body: [],
|
|
125
128
|
start,
|
|
126
129
|
end,
|
|
127
|
-
deleteAt: cachedAt + ttl * 1e3,
|
|
130
|
+
deleteAt: cachedAt + Math.min(ttl, this.#maxEntryTTL) * 1e3,
|
|
128
131
|
statusCode,
|
|
129
132
|
statusMessage: '',
|
|
130
133
|
headers,
|
|
@@ -4,9 +4,6 @@ import { parseRangeHeader } from './utils.js'
|
|
|
4
4
|
|
|
5
5
|
const VERSION = 6
|
|
6
6
|
|
|
7
|
-
// 2gb
|
|
8
|
-
const MAX_ENTRY_SIZE = 2 * 1000 * 1000 * 1000
|
|
9
|
-
|
|
10
7
|
/**
|
|
11
8
|
* @typedef {import('undici-types/cache-interceptor.d.ts').default.CacheStore} CacheStore
|
|
12
9
|
* @implements {CacheStore}
|
|
@@ -28,9 +25,6 @@ const MAX_ENTRY_SIZE = 2 * 1000 * 1000 * 1000
|
|
|
28
25
|
* }} SqliteStoreValue
|
|
29
26
|
*/
|
|
30
27
|
export class SqliteCacheStore {
|
|
31
|
-
#maxEntrySize = MAX_ENTRY_SIZE
|
|
32
|
-
#maxEntryCount = 16 * 1024
|
|
33
|
-
|
|
34
28
|
/**
|
|
35
29
|
* @type {import('node:sqlite').DatabaseSync}
|
|
36
30
|
*/
|
|
@@ -57,57 +51,14 @@ export class SqliteCacheStore {
|
|
|
57
51
|
#deleteByUrlQuery
|
|
58
52
|
|
|
59
53
|
/**
|
|
60
|
-
* @type {
|
|
54
|
+
* @type {NodeJS.Timeout}
|
|
61
55
|
*/
|
|
62
|
-
#
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* @type {import('node:sqlite').StatementSync | null}
|
|
66
|
-
*/
|
|
67
|
-
#deleteOldValuesQuery
|
|
56
|
+
#pruneInterval
|
|
68
57
|
|
|
69
58
|
/**
|
|
70
59
|
* @param {import('undici-types/cache-interceptor.d.ts').default.SqliteCacheStoreOpts & { maxEntryCount?: number} | undefined} opts
|
|
71
60
|
*/
|
|
72
61
|
constructor(opts) {
|
|
73
|
-
if (opts) {
|
|
74
|
-
if (typeof opts !== 'object') {
|
|
75
|
-
throw new TypeError('SqliteCacheStore options must be an object')
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (opts.maxEntrySize !== undefined) {
|
|
79
|
-
if (
|
|
80
|
-
typeof opts.maxEntrySize !== 'number' ||
|
|
81
|
-
!Number.isInteger(opts.maxEntrySize) ||
|
|
82
|
-
opts.maxEntrySize < 0
|
|
83
|
-
) {
|
|
84
|
-
throw new TypeError(
|
|
85
|
-
'SqliteCacheStore options.maxEntrySize must be a non-negative integer',
|
|
86
|
-
)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (opts.maxEntrySize > MAX_ENTRY_SIZE) {
|
|
90
|
-
throw new TypeError('SqliteCacheStore options.maxEntrySize must be less than 2gb')
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
this.#maxEntrySize = opts.maxEntrySize
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const maxEntryCount = opts.maxEntryCount ?? opts.maxCount
|
|
97
|
-
if (maxEntryCount !== undefined) {
|
|
98
|
-
if (
|
|
99
|
-
typeof maxEntryCount !== 'number' ||
|
|
100
|
-
!Number.isInteger(maxEntryCount) ||
|
|
101
|
-
maxEntryCount < 0
|
|
102
|
-
) {
|
|
103
|
-
throw new TypeError(
|
|
104
|
-
'SqliteCacheStore options.maxEntryCount must be a non-negative integer',
|
|
105
|
-
)
|
|
106
|
-
}
|
|
107
|
-
this.#maxEntryCount = maxEntryCount
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
62
|
this.#db = new DatabaseSync(opts?.location ?? ':memory:')
|
|
112
63
|
|
|
113
64
|
this.#db.exec(`
|
|
@@ -180,30 +131,17 @@ export class SqliteCacheStore {
|
|
|
180
131
|
`DELETE FROM cacheInterceptorV${VERSION} WHERE url = ?`,
|
|
181
132
|
)
|
|
182
133
|
|
|
183
|
-
this.#countEntriesQuery = this.#db.prepare(
|
|
184
|
-
`SELECT COUNT(*) AS total FROM cacheInterceptorV${VERSION}`,
|
|
185
|
-
)
|
|
186
|
-
|
|
187
134
|
this.#deleteExpiredValuesQuery = this.#db.prepare(
|
|
188
135
|
`DELETE FROM cacheInterceptorV${VERSION} WHERE deleteAt <= ?`,
|
|
189
136
|
)
|
|
190
137
|
|
|
191
|
-
this.#
|
|
192
|
-
this.#
|
|
193
|
-
|
|
194
|
-
: this.#db.prepare(`
|
|
195
|
-
DELETE FROM cacheInterceptorV${VERSION}
|
|
196
|
-
WHERE id IN (
|
|
197
|
-
SELECT
|
|
198
|
-
id
|
|
199
|
-
FROM cacheInterceptorV${VERSION}
|
|
200
|
-
ORDER BY cachedAt DESC
|
|
201
|
-
LIMIT ?
|
|
202
|
-
)
|
|
203
|
-
`)
|
|
138
|
+
this.#pruneInterval = setInterval(() => {
|
|
139
|
+
this.#deleteExpiredValuesQuery.run(Date.now())
|
|
140
|
+
}, 60e3).unref()
|
|
204
141
|
}
|
|
205
142
|
|
|
206
143
|
close() {
|
|
144
|
+
clearInterval(this.#pruneInterval)
|
|
207
145
|
this.#db.close()
|
|
208
146
|
}
|
|
209
147
|
|
|
@@ -227,16 +165,11 @@ export class SqliteCacheStore {
|
|
|
227
165
|
assertCacheValue(value)
|
|
228
166
|
|
|
229
167
|
const body = Array.isArray(value.body) ? Buffer.concat(value.body) : value.body
|
|
230
|
-
if ((body?.byteLength ?? 0) > this.#maxEntrySize) {
|
|
231
|
-
return
|
|
232
|
-
}
|
|
233
168
|
|
|
234
169
|
assert(Number.isFinite(value.start))
|
|
235
170
|
assert(Number.isFinite(value.end))
|
|
236
171
|
assert(!body || body?.byteLength === value.end - value.start)
|
|
237
172
|
|
|
238
|
-
this.#prune()
|
|
239
|
-
|
|
240
173
|
this.#insertValueQuery.run(
|
|
241
174
|
makeValueUrl(key),
|
|
242
175
|
key.method,
|
|
@@ -266,39 +199,6 @@ export class SqliteCacheStore {
|
|
|
266
199
|
this.#deleteByUrlQuery.run(makeValueUrl(key))
|
|
267
200
|
}
|
|
268
201
|
|
|
269
|
-
#prune() {
|
|
270
|
-
if (this.size <= this.#maxEntryCount) {
|
|
271
|
-
return 0
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
{
|
|
275
|
-
const removed = this.#deleteExpiredValuesQuery.run(Date.now()).changes
|
|
276
|
-
if (removed) {
|
|
277
|
-
return removed
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
{
|
|
282
|
-
const removed = this.#deleteOldValuesQuery?.run(
|
|
283
|
-
Math.max(Math.floor(this.#maxEntryCount * 0.1), 1),
|
|
284
|
-
).changes
|
|
285
|
-
if (removed) {
|
|
286
|
-
return removed
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
return 0
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* Counts the number of rows in the cache
|
|
295
|
-
* @returns {Number}
|
|
296
|
-
*/
|
|
297
|
-
get size() {
|
|
298
|
-
const { total } = this.#countEntriesQuery.get()
|
|
299
|
-
return total
|
|
300
|
-
}
|
|
301
|
-
|
|
302
202
|
/**
|
|
303
203
|
* @param {import('undici-types/cache-interceptor.d.ts').default.CacheKey} key
|
|
304
204
|
* @param {boolean} [canBeExpired=false]
|