@nxtedition/lib 26.0.8 → 26.0.10
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 +63 -23
- package/http.js +11 -8
- 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...
|
|
@@ -57,26 +79,41 @@ export class AsyncCache {
|
|
|
57
79
|
|
|
58
80
|
this.#lru =
|
|
59
81
|
opts?.lru === false || opts?.lru === null ? null : new LRUCache({ max: 4096, ...opts?.lru })
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
+
this.#db?.close()
|
|
108
|
+
this.#db = null
|
|
109
|
+
|
|
110
|
+
this.#getQuery = null
|
|
111
|
+
this.#setQuery = null
|
|
112
|
+
this.#delQuery = null
|
|
113
|
+
|
|
114
|
+
process.emitWarning(err)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
80
117
|
}
|
|
81
118
|
|
|
82
119
|
close() {
|
|
@@ -85,7 +122,7 @@ export class AsyncCache {
|
|
|
85
122
|
|
|
86
123
|
/**
|
|
87
124
|
* @param {...any} args
|
|
88
|
-
* @returns {{ value: Promise<
|
|
125
|
+
* @returns {{ value: V|Promise<V>, async: boolean }}
|
|
89
126
|
*/
|
|
90
127
|
get(...args) {
|
|
91
128
|
const key = this.#keySelector(...args)
|
|
@@ -158,7 +195,7 @@ export class AsyncCache {
|
|
|
158
195
|
|
|
159
196
|
/**
|
|
160
197
|
* @param {string} key
|
|
161
|
-
* @param {
|
|
198
|
+
* @param {V} value
|
|
162
199
|
*/
|
|
163
200
|
set(key, value) {
|
|
164
201
|
if (typeof key !== 'string' || key.length === 0) {
|
|
@@ -182,6 +219,9 @@ export class AsyncCache {
|
|
|
182
219
|
}
|
|
183
220
|
}
|
|
184
221
|
|
|
222
|
+
/**
|
|
223
|
+
* @param {string} key
|
|
224
|
+
*/
|
|
185
225
|
delete(key) {
|
|
186
226
|
if (typeof key !== 'string' || key.length === 0) {
|
|
187
227
|
throw new TypeError('key must be a non-empty string')
|
package/http.js
CHANGED
|
@@ -75,7 +75,7 @@ export class Context {
|
|
|
75
75
|
return this.#req.target
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
this.#target ??= requestTarget(this.#req)
|
|
78
|
+
this.#target ??= Object.freeze(requestTarget(this.#req))
|
|
79
79
|
|
|
80
80
|
if (!this.#target) {
|
|
81
81
|
throw new createError.BadRequest('invalid url')
|
|
@@ -89,13 +89,10 @@ export class Context {
|
|
|
89
89
|
return this.#req.query
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
|
|
93
|
-
this.#query ??=
|
|
94
|
-
this.#target.search.length > 1
|
|
95
|
-
? Object.freeze(querystring.parse(this.#target.search.slice(1)))
|
|
96
|
-
: {}
|
|
92
|
+
const search = this.target.search
|
|
97
93
|
|
|
98
|
-
return this.#query
|
|
94
|
+
return (this.#query ??=
|
|
95
|
+
search.length > 1 ? Object.freeze(querystring.parse(search.slice(1))) : {})
|
|
99
96
|
}
|
|
100
97
|
|
|
101
98
|
get userAgent() {
|
|
@@ -373,7 +370,13 @@ export class IncomingMessage extends http.IncomingMessage {
|
|
|
373
370
|
this.#target = undefined
|
|
374
371
|
}
|
|
375
372
|
|
|
376
|
-
|
|
373
|
+
this.#target ??= Object.freeze(requestTarget(this))
|
|
374
|
+
|
|
375
|
+
if (!this.#target) {
|
|
376
|
+
throw new createError.BadRequest('invalid url')
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return this.#target
|
|
377
380
|
}
|
|
378
381
|
|
|
379
382
|
get query() {
|