@nxtedition/lib 26.0.8 → 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.
Files changed (2) hide show
  1. package/cache.js +58 -23
  2. 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: any } **/
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,36 @@ 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
- this.#db = new DatabaseSync(location, { timeout: 20, ...opts?.db })
61
- this.#db.exec(`
62
- PRAGMA journal_mode = WAL;
63
- PRAGMA synchronous = NORMAL;
64
- PRAGMA temp_store = memory;
65
- PRAGMA optimize;
66
-
67
- CREATE TABLE IF NOT EXISTS cache (
68
- key TEXT PRIMARY KEY NOT NULL,
69
- val TEXT NOT NULL,
70
- ttl INTEGER NOT NULL,
71
- stale INTEGER NOT NULL
72
- );
73
- `)
74
-
75
- this.#getQuery = this.#db.prepare(`SELECT val, ttl, stale FROM cache WHERE key = ?`)
76
- this.#setQuery = this.#db.prepare(
77
- `INSERT OR REPLACE INTO cache (key, val, ttl, stale) VALUES (?, ?, ?, ?)`,
78
- )
79
- this.#delQuery = this.#db.prepare(`DELETE FROM cache WHERE key = ?`)
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
+ }
80
112
  }
81
113
 
82
114
  close() {
@@ -85,7 +117,7 @@ export class AsyncCache {
85
117
 
86
118
  /**
87
119
  * @param {...any} args
88
- * @returns {{ value: Promise<unknown>|unknown, async: boolean }}
120
+ * @returns {{ value: V|Promise<V>, async: boolean }}
89
121
  */
90
122
  get(...args) {
91
123
  const key = this.#keySelector(...args)
@@ -158,7 +190,7 @@ export class AsyncCache {
158
190
 
159
191
  /**
160
192
  * @param {string} key
161
- * @param {any} value
193
+ * @param {V} value
162
194
  */
163
195
  set(key, value) {
164
196
  if (typeof key !== 'string' || key.length === 0) {
@@ -182,6 +214,9 @@ export class AsyncCache {
182
214
  }
183
215
  }
184
216
 
217
+ /**
218
+ * @param {string} key
219
+ */
185
220
  delete(key) {
186
221
  if (typeof key !== 'string' || key.length === 0) {
187
222
  throw new TypeError('key must be a non-empty string')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxtedition/lib",
3
- "version": "26.0.8",
3
+ "version": "26.0.9",
4
4
  "license": "MIT",
5
5
  "author": "Robert Nagy <robert.nagy@boffins.se>",
6
6
  "type": "module",