@nxtedition/lib 26.0.7 → 26.0.9
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/cache.js +79 -29
- package/http.js +3 -1
- package/package.json +1 -1
package/cache.js
CHANGED
|
@@ -4,13 +4,29 @@ import { fastNow } from './time.js'
|
|
|
4
4
|
|
|
5
5
|
function noop() {}
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* @template [V=unknown]
|
|
9
|
+
* @typedef {object} AsyncCacheOptions
|
|
10
|
+
* @property {number | ((value: V, key: string) => number)} [ttl]
|
|
11
|
+
* @property {number | ((value: V, key: string) => number)} [stale]
|
|
12
|
+
* @property {import('lru-cache').LRUCache.Options<string, { ttl: number, stale: number, value: V }> | false | null} [lru]
|
|
13
|
+
* @property {{ timeout?: number }} [db]
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @template [V=unknown]
|
|
18
|
+
*/
|
|
7
19
|
export class AsyncCache {
|
|
8
|
-
/** @type LRUCache<string, { ttl: number, stale: number, value:
|
|
20
|
+
/** @type LRUCache<string, { ttl: number, stale: number, value: V }> */
|
|
9
21
|
#lru
|
|
10
22
|
#valueSelector
|
|
11
23
|
#keySelector
|
|
12
24
|
#dedupe = new Map()
|
|
25
|
+
|
|
26
|
+
/** @type {(val: V, key: string) => number} */
|
|
13
27
|
#ttl
|
|
28
|
+
|
|
29
|
+
/** @type {(val: V, key: string) => number} */
|
|
14
30
|
#stale
|
|
15
31
|
|
|
16
32
|
#db
|
|
@@ -18,6 +34,12 @@ export class AsyncCache {
|
|
|
18
34
|
#setQuery
|
|
19
35
|
#delQuery
|
|
20
36
|
|
|
37
|
+
/**
|
|
38
|
+
* @param {string} location
|
|
39
|
+
* @param {((...args: any[]) => Promise<V>)|undefined} [valueSelector]
|
|
40
|
+
* @param {((...args: any[]) => string)|undefined} [keySelector]
|
|
41
|
+
* @param {AsyncCacheOptions<V>} [opts]
|
|
42
|
+
*/
|
|
21
43
|
constructor(location, valueSelector, keySelector, opts) {
|
|
22
44
|
if (typeof location === 'string') {
|
|
23
45
|
// Do nothing...
|
|
@@ -55,27 +77,38 @@ export class AsyncCache {
|
|
|
55
77
|
throw new TypeError('stale must be a undefined, number or a function')
|
|
56
78
|
}
|
|
57
79
|
|
|
58
|
-
this.#lru =
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
80
|
+
this.#lru =
|
|
81
|
+
opts?.lru === false || opts?.lru === null ? null : new LRUCache({ max: 4096, ...opts?.lru })
|
|
82
|
+
|
|
83
|
+
for (let n = 0; true; n += 1) {
|
|
84
|
+
try {
|
|
85
|
+
this.#db = new DatabaseSync(location, { timeout: 20, ...opts?.db })
|
|
86
|
+
|
|
87
|
+
this.#db.exec(`
|
|
88
|
+
PRAGMA journal_mode = WAL;
|
|
89
|
+
PRAGMA synchronous = NORMAL;
|
|
90
|
+
PRAGMA temp_store = memory;
|
|
91
|
+
PRAGMA optimize;
|
|
92
|
+
|
|
93
|
+
CREATE TABLE IF NOT EXISTS cache (
|
|
94
|
+
key TEXT PRIMARY KEY NOT NULL,
|
|
95
|
+
val TEXT NOT NULL,
|
|
96
|
+
ttl INTEGER NOT NULL,
|
|
97
|
+
stale INTEGER NOT NULL
|
|
98
|
+
);
|
|
99
|
+
`)
|
|
100
|
+
|
|
101
|
+
this.#getQuery = this.#db.prepare(`SELECT val, ttl, stale FROM cache WHERE key = ?`)
|
|
102
|
+
this.#setQuery = this.#db.prepare(
|
|
103
|
+
`INSERT OR REPLACE INTO cache (key, val, ttl, stale) VALUES (?, ?, ?, ?)`,
|
|
104
|
+
)
|
|
105
|
+
this.#delQuery = this.#db.prepare(`DELETE FROM cache WHERE key = ?`)
|
|
106
|
+
} catch (err) {
|
|
107
|
+
if (n > 128 || !/locked|busy/i.test(err?.message)) {
|
|
108
|
+
throw err
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
79
112
|
}
|
|
80
113
|
|
|
81
114
|
close() {
|
|
@@ -84,7 +117,7 @@ export class AsyncCache {
|
|
|
84
117
|
|
|
85
118
|
/**
|
|
86
119
|
* @param {...any} args
|
|
87
|
-
* @returns {{ value: Promise<
|
|
120
|
+
* @returns {{ value: V|Promise<V>, async: boolean }}
|
|
88
121
|
*/
|
|
89
122
|
get(...args) {
|
|
90
123
|
const key = this.#keySelector(...args)
|
|
@@ -95,7 +128,7 @@ export class AsyncCache {
|
|
|
95
128
|
|
|
96
129
|
const now = fastNow()
|
|
97
130
|
|
|
98
|
-
let cached = this.#lru
|
|
131
|
+
let cached = this.#lru?.get(key)
|
|
99
132
|
|
|
100
133
|
if (cached === undefined) {
|
|
101
134
|
try {
|
|
@@ -106,7 +139,7 @@ export class AsyncCache {
|
|
|
106
139
|
stale: ret.stale,
|
|
107
140
|
value: JSON.parse(ret.val),
|
|
108
141
|
}
|
|
109
|
-
this.#lru
|
|
142
|
+
this.#lru?.set(key, cached)
|
|
110
143
|
}
|
|
111
144
|
} catch {
|
|
112
145
|
// Do nothing...
|
|
@@ -120,9 +153,9 @@ export class AsyncCache {
|
|
|
120
153
|
|
|
121
154
|
if (now > cached.stale) {
|
|
122
155
|
// stale-while-revalidate has ttld, purge cached value.
|
|
123
|
-
this.#lru
|
|
156
|
+
this.#lru?.delete(key)
|
|
124
157
|
try {
|
|
125
|
-
this.#delQuery
|
|
158
|
+
this.#delQuery?.run(key)
|
|
126
159
|
} catch {
|
|
127
160
|
// Do nothing...
|
|
128
161
|
}
|
|
@@ -157,7 +190,7 @@ export class AsyncCache {
|
|
|
157
190
|
|
|
158
191
|
/**
|
|
159
192
|
* @param {string} key
|
|
160
|
-
* @param {
|
|
193
|
+
* @param {V} value
|
|
161
194
|
*/
|
|
162
195
|
set(key, value) {
|
|
163
196
|
if (typeof key !== 'string' || key.length === 0) {
|
|
@@ -171,7 +204,7 @@ export class AsyncCache {
|
|
|
171
204
|
value,
|
|
172
205
|
}
|
|
173
206
|
|
|
174
|
-
this.#lru
|
|
207
|
+
this.#lru?.set(key, cached)
|
|
175
208
|
this.#dedupe.delete(key)
|
|
176
209
|
|
|
177
210
|
try {
|
|
@@ -180,4 +213,21 @@ export class AsyncCache {
|
|
|
180
213
|
// Do nothing...
|
|
181
214
|
}
|
|
182
215
|
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* @param {string} key
|
|
219
|
+
*/
|
|
220
|
+
delete(key) {
|
|
221
|
+
if (typeof key !== 'string' || key.length === 0) {
|
|
222
|
+
throw new TypeError('key must be a non-empty string')
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
this.#lru?.delete(key)
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
this.#delQuery?.run(key)
|
|
229
|
+
} catch {
|
|
230
|
+
// Do nothing...
|
|
231
|
+
}
|
|
232
|
+
}
|
|
183
233
|
}
|
package/http.js
CHANGED
|
@@ -166,7 +166,9 @@ export async function upgradeMiddleware(ctx, next) {
|
|
|
166
166
|
await thenable
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
|
|
169
|
+
if (!socket.destroyed && !socket.writableEnded) {
|
|
170
|
+
throw new Error('Stream not completed')
|
|
171
|
+
}
|
|
170
172
|
|
|
171
173
|
const elapsedTime = performance.now() - startTime
|
|
172
174
|
|