@nxtedition/cache 2.0.2 → 2.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.d.ts +32 -1
- package/lib/index.js +149 -51
- package/lib/memory.d.ts +6 -0
- package/lib/memory.js +9 -0
- package/package.json +2 -2
package/lib/index.d.ts
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
1
2
|
import { type MemoryOptions } from './memory.ts';
|
|
2
3
|
export type { MemoryOptions } from './memory.ts';
|
|
3
4
|
export interface DatabaseOptions {
|
|
4
5
|
timeout?: number;
|
|
5
6
|
maxSize?: number;
|
|
6
7
|
}
|
|
8
|
+
export interface Serializer<V> {
|
|
9
|
+
serialize: (value: V) => Buffer | Uint8Array | string;
|
|
10
|
+
deserialize: (data: Buffer | string) => V;
|
|
11
|
+
}
|
|
7
12
|
export interface CacheOptions<V> {
|
|
8
13
|
ttl?: number | ((value: V, key: string) => number);
|
|
9
14
|
stale?: number | ((value: V, key: string) => number);
|
|
10
15
|
memory?: MemoryOptions | false | null;
|
|
11
16
|
database?: DatabaseOptions | false | null;
|
|
17
|
+
serializer?: Serializer<V>;
|
|
12
18
|
}
|
|
13
19
|
export type CacheResult<V> = {
|
|
14
20
|
value: V | undefined;
|
|
@@ -17,9 +23,34 @@ export type CacheResult<V> = {
|
|
|
17
23
|
value: Promise<V>;
|
|
18
24
|
async: true;
|
|
19
25
|
};
|
|
20
|
-
|
|
26
|
+
declare global {
|
|
27
|
+
var __nxt_cache: {
|
|
28
|
+
stats: Cache['stats'];
|
|
29
|
+
}[];
|
|
30
|
+
}
|
|
31
|
+
export declare class Cache<V = unknown, A extends unknown[] = unknown[]> extends EventEmitter {
|
|
21
32
|
#private;
|
|
22
33
|
constructor(location: string, valueSelector: (...args: A) => V | PromiseLike<V>, keySelector: (...args: A) => string, opts?: CacheOptions<V>);
|
|
34
|
+
get stats(): {
|
|
35
|
+
lock: {
|
|
36
|
+
timeout: number;
|
|
37
|
+
mean: number;
|
|
38
|
+
stddev: number;
|
|
39
|
+
};
|
|
40
|
+
dedupe: {
|
|
41
|
+
size: number;
|
|
42
|
+
};
|
|
43
|
+
memory: {
|
|
44
|
+
size: number;
|
|
45
|
+
maxSize: number;
|
|
46
|
+
count: number;
|
|
47
|
+
maxCount: number;
|
|
48
|
+
} | undefined;
|
|
49
|
+
database: {
|
|
50
|
+
location: string;
|
|
51
|
+
size: number | undefined;
|
|
52
|
+
} | undefined;
|
|
53
|
+
};
|
|
23
54
|
close(): void;
|
|
24
55
|
get(...args: A): CacheResult<V>;
|
|
25
56
|
peek(...args: A): CacheResult<V>;
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto'
|
|
2
|
+
import { EventEmitter } from 'node:events'
|
|
2
3
|
import { DatabaseSync, } from 'node:sqlite'
|
|
3
4
|
import { setTimeout as delay } from 'node:timers/promises'
|
|
4
5
|
import { MemoryCache, } from "./memory.js"
|
|
@@ -7,25 +8,18 @@ import { MemoryCache, } from "./memory
|
|
|
7
8
|
|
|
8
9
|
function noop() {}
|
|
9
10
|
|
|
11
|
+
function maybeToBuffer(value ) {
|
|
12
|
+
return ArrayBuffer.isView(value)
|
|
13
|
+
? Buffer.from(value.buffer, value.byteOffset, value.byteLength)
|
|
14
|
+
: value
|
|
15
|
+
}
|
|
16
|
+
|
|
10
17
|
function isThenable(value ) {
|
|
11
18
|
return value != null && typeof (value ).then === 'function'
|
|
12
19
|
}
|
|
13
20
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
{
|
|
17
|
-
const offPeakBC = new BroadcastChannel('nxt:offPeak')
|
|
18
|
-
offPeakBC.unref()
|
|
19
|
-
offPeakBC.onmessage = () => {
|
|
20
|
-
for (const db of dbs) {
|
|
21
|
-
try {
|
|
22
|
-
db.purgeStale()
|
|
23
|
-
} catch (err) {
|
|
24
|
-
process.emitWarning(err )
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
|
+
const dbs = new Set ()
|
|
29
23
|
|
|
30
24
|
|
|
31
25
|
|
|
@@ -44,21 +38,42 @@ const dbs = new Set ()
|
|
|
44
38
|
|
|
45
39
|
|
|
46
40
|
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
47
|
+
const defaultSerializer = {
|
|
48
|
+
serialize(value) {
|
|
49
|
+
return ArrayBuffer.isView(value) ? (value ) : JSON.stringify(value)
|
|
50
|
+
},
|
|
51
|
+
deserialize(data) {
|
|
52
|
+
return ArrayBuffer.isView(data) ? data : JSON.parse(data)
|
|
53
|
+
},
|
|
54
|
+
}
|
|
55
|
+
|
|
47
56
|
|
|
48
57
|
|
|
49
58
|
|
|
50
59
|
|
|
51
60
|
|
|
61
|
+
|
|
52
62
|
|
|
53
63
|
|
|
54
64
|
|
|
55
65
|
|
|
56
66
|
|
|
57
67
|
|
|
58
|
-
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
const VERSION = 5
|
|
59
74
|
const MAX_DURATION = 365000000e3
|
|
60
75
|
|
|
61
|
-
export class Cache {
|
|
76
|
+
export class Cache extends EventEmitter {
|
|
62
77
|
#memory
|
|
63
78
|
#dedupe = new Map ()
|
|
64
79
|
#closed = false
|
|
@@ -68,12 +83,14 @@ export class Cache {
|
|
|
68
83
|
|
|
69
84
|
#ttl
|
|
70
85
|
#stale
|
|
86
|
+
#serializer
|
|
71
87
|
|
|
72
88
|
#lockMean = 5
|
|
73
89
|
#lockVar = 0
|
|
74
90
|
#lockTimeout = 10
|
|
75
91
|
#lockId = randomUUID()
|
|
76
92
|
|
|
93
|
+
#location
|
|
77
94
|
#database = null
|
|
78
95
|
#getQuery = null
|
|
79
96
|
#setQuery = null
|
|
@@ -84,6 +101,16 @@ export class Cache {
|
|
|
84
101
|
#lockStealQuery = null
|
|
85
102
|
#lockGetQuery = null
|
|
86
103
|
#lockPurgeQuery = null
|
|
104
|
+
#pageCountQuery = null
|
|
105
|
+
#pageSizeQuery = null
|
|
106
|
+
|
|
107
|
+
#emitError(err ) {
|
|
108
|
+
if (this.listenerCount('error') > 0) {
|
|
109
|
+
this.emit('error', err)
|
|
110
|
+
} else {
|
|
111
|
+
process.emitWarning(err)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
87
114
|
|
|
88
115
|
constructor(
|
|
89
116
|
location ,
|
|
@@ -91,9 +118,11 @@ export class Cache {
|
|
|
91
118
|
keySelector ,
|
|
92
119
|
opts ,
|
|
93
120
|
) {
|
|
121
|
+
super()
|
|
94
122
|
if (typeof location !== 'string') {
|
|
95
123
|
throw new TypeError('location must be a string')
|
|
96
124
|
}
|
|
125
|
+
this.#location = location
|
|
97
126
|
|
|
98
127
|
if (typeof valueSelector !== 'function') {
|
|
99
128
|
throw new TypeError('valueSelector must be a function')
|
|
@@ -123,6 +152,19 @@ export class Cache {
|
|
|
123
152
|
throw new TypeError('stale must be a undefined, number or a function')
|
|
124
153
|
}
|
|
125
154
|
|
|
155
|
+
if (opts?.serializer !== undefined) {
|
|
156
|
+
if (typeof opts.serializer !== 'object' || opts.serializer === null) {
|
|
157
|
+
throw new TypeError('serializer must be an object')
|
|
158
|
+
}
|
|
159
|
+
if (typeof opts.serializer.serialize !== 'function') {
|
|
160
|
+
throw new TypeError('serializer.serialize must be a function')
|
|
161
|
+
}
|
|
162
|
+
if (typeof opts.serializer.deserialize !== 'function') {
|
|
163
|
+
throw new TypeError('serializer.deserialize must be a function')
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
this.#serializer = opts?.serializer ?? defaultSerializer
|
|
167
|
+
|
|
126
168
|
this.#memory =
|
|
127
169
|
opts?.memory === false || opts?.memory === null ? null : new MemoryCache(opts?.memory)
|
|
128
170
|
|
|
@@ -137,6 +179,9 @@ export class Cache {
|
|
|
137
179
|
PRAGMA journal_mode = WAL;
|
|
138
180
|
PRAGMA synchronous = NORMAL;
|
|
139
181
|
PRAGMA temp_store = memory;
|
|
182
|
+
PRAGMA cache_size = -${Math.ceil(maxSize / 1024 / 8)};
|
|
183
|
+
PRAGMA mmap_size = ${maxSize};
|
|
184
|
+
PRAGMA max_page_count = ${Math.ceil(maxSize / 4096)};
|
|
140
185
|
PRAGMA optimize;
|
|
141
186
|
|
|
142
187
|
CREATE TABLE IF NOT EXISTS cache_v${VERSION} (
|
|
@@ -144,7 +189,9 @@ export class Cache {
|
|
|
144
189
|
val BLOB NOT NULL,
|
|
145
190
|
ttl INTEGER NOT NULL,
|
|
146
191
|
stale INTEGER NOT NULL
|
|
147
|
-
);
|
|
192
|
+
) WITHOUT ROWID;
|
|
193
|
+
|
|
194
|
+
CREATE INDEX IF NOT EXISTS cache_v${VERSION}_stale_idx ON cache_v${VERSION}(stale);
|
|
148
195
|
|
|
149
196
|
CREATE TABLE IF NOT EXISTS cache_lock_v${VERSION} (
|
|
150
197
|
key TEXT PRIMARY KEY NOT NULL,
|
|
@@ -153,13 +200,6 @@ export class Cache {
|
|
|
153
200
|
);
|
|
154
201
|
`)
|
|
155
202
|
|
|
156
|
-
{
|
|
157
|
-
const { page_size } = this.#database.prepare('PRAGMA page_size').get()
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
this.#database.exec(`PRAGMA max_page_count = ${Math.ceil(maxSize / page_size)}`)
|
|
161
|
-
}
|
|
162
|
-
|
|
163
203
|
this.#getQuery = this.#database.prepare(
|
|
164
204
|
`SELECT val, ttl, stale FROM cache_v${VERSION} WHERE key = ? AND stale > ?`,
|
|
165
205
|
)
|
|
@@ -174,7 +214,7 @@ export class Cache {
|
|
|
174
214
|
`DELETE FROM cache_v${VERSION} WHERE key IN (SELECT key FROM cache_v${VERSION} ORDER BY stale ASC LIMIT ?)`,
|
|
175
215
|
)
|
|
176
216
|
|
|
177
|
-
//
|
|
217
|
+
// ON CONFLICT refreshes lock_acquired when we are the owner,
|
|
178
218
|
// preventing other processes from stealing our still-active lock.
|
|
179
219
|
this.#lockAcquireQuery = this.#database.prepare(
|
|
180
220
|
`INSERT INTO cache_lock_v${VERSION} (key, lock_acquired, lock_owner) VALUES (?, ?, ?) ON CONFLICT(key) DO UPDATE SET lock_acquired = CASE WHEN lock_owner = excluded.lock_owner THEN excluded.lock_acquired ELSE lock_acquired END RETURNING lock_acquired, lock_owner`,
|
|
@@ -182,13 +222,15 @@ export class Cache {
|
|
|
182
222
|
this.#lockStealQuery = this.#database.prepare(
|
|
183
223
|
`UPDATE cache_lock_v${VERSION} SET lock_acquired = ?, lock_owner = ? WHERE key = ? AND lock_acquired = ?`,
|
|
184
224
|
)
|
|
185
|
-
//
|
|
225
|
+
// Read the current lock state after a failed steal to get the winner's timestamp.
|
|
186
226
|
this.#lockGetQuery = this.#database.prepare(
|
|
187
227
|
`SELECT lock_acquired FROM cache_lock_v${VERSION} WHERE key = ?`,
|
|
188
228
|
)
|
|
189
229
|
this.#lockPurgeQuery = this.#database.prepare(
|
|
190
230
|
`DELETE FROM cache_lock_v${VERSION} WHERE lock_acquired <= ?`,
|
|
191
231
|
)
|
|
232
|
+
this.#pageCountQuery = this.#database.prepare('PRAGMA page_count')
|
|
233
|
+
this.#pageSizeQuery = this.#database.prepare('PRAGMA page_size')
|
|
192
234
|
break
|
|
193
235
|
} catch (err) {
|
|
194
236
|
if (n >= 16) {
|
|
@@ -204,14 +246,45 @@ export class Cache {
|
|
|
204
246
|
this.#lockStealQuery = null
|
|
205
247
|
this.#lockGetQuery = null
|
|
206
248
|
this.#lockPurgeQuery = null
|
|
249
|
+
this.#pageCountQuery = null
|
|
250
|
+
this.#pageSizeQuery = null
|
|
207
251
|
|
|
208
|
-
|
|
252
|
+
this.#emitError(err )
|
|
209
253
|
break
|
|
210
254
|
}
|
|
211
255
|
}
|
|
212
256
|
}
|
|
213
257
|
|
|
214
258
|
dbs.add(this)
|
|
259
|
+
|
|
260
|
+
globalThis.__nxt_cache ??= []
|
|
261
|
+
globalThis.__nxt_cache.push(this)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
get stats() {
|
|
265
|
+
let database
|
|
266
|
+
if (this.#database) {
|
|
267
|
+
let size
|
|
268
|
+
try {
|
|
269
|
+
const { page_count } = this.#pageCountQuery .get()
|
|
270
|
+
const { page_size } = this.#pageSizeQuery .get()
|
|
271
|
+
size = page_count * page_size
|
|
272
|
+
} catch (err) {
|
|
273
|
+
this.#emitError(err )
|
|
274
|
+
}
|
|
275
|
+
database = { location: this.#location, size }
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return {
|
|
279
|
+
lock: {
|
|
280
|
+
timeout: this.#lockTimeout,
|
|
281
|
+
mean: this.#lockMean,
|
|
282
|
+
stddev: Math.sqrt(this.#lockVar),
|
|
283
|
+
},
|
|
284
|
+
dedupe: { size: this.#dedupe.size },
|
|
285
|
+
memory: this.#memory?.stats,
|
|
286
|
+
database,
|
|
287
|
+
}
|
|
215
288
|
}
|
|
216
289
|
|
|
217
290
|
close() {
|
|
@@ -228,8 +301,15 @@ export class Cache {
|
|
|
228
301
|
this.#lockStealQuery = null
|
|
229
302
|
this.#lockGetQuery = null
|
|
230
303
|
this.#lockPurgeQuery = null
|
|
304
|
+
this.#pageCountQuery = null
|
|
305
|
+
this.#pageSizeQuery = null
|
|
231
306
|
this.#database?.close()
|
|
232
307
|
this.#database = null
|
|
308
|
+
|
|
309
|
+
const idx = globalThis.__nxt_cache?.indexOf(this) ?? -1
|
|
310
|
+
if (idx !== -1) {
|
|
311
|
+
globalThis.__nxt_cache.splice(idx, 1)
|
|
312
|
+
}
|
|
233
313
|
}
|
|
234
314
|
|
|
235
315
|
get(...args ) {
|
|
@@ -269,7 +349,7 @@ export class Cache {
|
|
|
269
349
|
this.#purgeStaleQuery?.run(Date.now())
|
|
270
350
|
this.#lockPurgeQuery?.run(Date.now() - 3_600_000)
|
|
271
351
|
} catch (err) {
|
|
272
|
-
|
|
352
|
+
this.#emitError(err )
|
|
273
353
|
}
|
|
274
354
|
}
|
|
275
355
|
|
|
@@ -293,7 +373,7 @@ export class Cache {
|
|
|
293
373
|
cached = entry
|
|
294
374
|
}
|
|
295
375
|
} catch (err) {
|
|
296
|
-
|
|
376
|
+
this.#emitError(err )
|
|
297
377
|
}
|
|
298
378
|
}
|
|
299
379
|
|
|
@@ -315,18 +395,20 @@ export class Cache {
|
|
|
315
395
|
}
|
|
316
396
|
}
|
|
317
397
|
|
|
398
|
+
if (!refresh) {
|
|
399
|
+
// peek: return stale value if available, undefined if expired or missing
|
|
400
|
+
return { value: cached?.value, async: false }
|
|
401
|
+
}
|
|
402
|
+
|
|
318
403
|
let result
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
}
|
|
326
|
-
throw err
|
|
404
|
+
try {
|
|
405
|
+
result = this.#refresh(args, key)
|
|
406
|
+
} catch (err) {
|
|
407
|
+
if (cached !== undefined) {
|
|
408
|
+
this.#emitError(err )
|
|
409
|
+
return { value: cached.value, async: false }
|
|
327
410
|
}
|
|
328
|
-
|
|
329
|
-
result = { value: undefined, async: false }
|
|
411
|
+
throw err
|
|
330
412
|
}
|
|
331
413
|
|
|
332
414
|
if (result.async && cached !== undefined) {
|
|
@@ -413,7 +495,7 @@ export class Cache {
|
|
|
413
495
|
try {
|
|
414
496
|
this.#delQuery?.run(key)
|
|
415
497
|
} catch (err) {
|
|
416
|
-
|
|
498
|
+
this.#emitError(err )
|
|
417
499
|
}
|
|
418
500
|
return
|
|
419
501
|
}
|
|
@@ -436,7 +518,7 @@ export class Cache {
|
|
|
436
518
|
return
|
|
437
519
|
}
|
|
438
520
|
|
|
439
|
-
const data =
|
|
521
|
+
const data = this.#serializer.serialize(value)
|
|
440
522
|
|
|
441
523
|
const entry = this.#createEntry(
|
|
442
524
|
key,
|
|
@@ -454,11 +536,11 @@ export class Cache {
|
|
|
454
536
|
try {
|
|
455
537
|
this.#evictQuery?.run(256)
|
|
456
538
|
this.#setQuery?.run(key, data , ttl, stale)
|
|
457
|
-
} catch {
|
|
458
|
-
|
|
539
|
+
} catch (evictErr) {
|
|
540
|
+
this.#emitError(evictErr )
|
|
459
541
|
}
|
|
460
542
|
} else {
|
|
461
|
-
|
|
543
|
+
this.#emitError(err )
|
|
462
544
|
}
|
|
463
545
|
}
|
|
464
546
|
}
|
|
@@ -473,9 +555,7 @@ export class Cache {
|
|
|
473
555
|
return {
|
|
474
556
|
ttl,
|
|
475
557
|
stale,
|
|
476
|
-
value
|
|
477
|
-
? (Buffer.from(value.buffer, value.byteOffset, value.byteLength) )
|
|
478
|
-
: value,
|
|
558
|
+
value,
|
|
479
559
|
key,
|
|
480
560
|
size,
|
|
481
561
|
index: -1,
|
|
@@ -484,7 +564,7 @@ export class Cache {
|
|
|
484
564
|
}
|
|
485
565
|
|
|
486
566
|
#loadRow(key , row ) {
|
|
487
|
-
const value =
|
|
567
|
+
const value = this.#serializer.deserialize(maybeToBuffer(row.val))
|
|
488
568
|
return this.#createEntry(
|
|
489
569
|
key,
|
|
490
570
|
value,
|
|
@@ -535,7 +615,7 @@ export class Cache {
|
|
|
535
615
|
|
|
536
616
|
return lockedAt
|
|
537
617
|
} catch (err) {
|
|
538
|
-
|
|
618
|
+
this.#emitError(err )
|
|
539
619
|
return 'unavailable'
|
|
540
620
|
}
|
|
541
621
|
}
|
|
@@ -582,7 +662,7 @@ export class Cache {
|
|
|
582
662
|
return entry.value
|
|
583
663
|
}
|
|
584
664
|
} catch (err) {
|
|
585
|
-
|
|
665
|
+
this.#emitError(err )
|
|
586
666
|
}
|
|
587
667
|
|
|
588
668
|
// Try to acquire lock for takeover
|
|
@@ -619,6 +699,24 @@ export class Cache {
|
|
|
619
699
|
}
|
|
620
700
|
}
|
|
621
701
|
|
|
702
|
+
{
|
|
703
|
+
const offPeakBC = new BroadcastChannel('nxt:offPeak')
|
|
704
|
+
offPeakBC.unref()
|
|
705
|
+
offPeakBC.onmessage = () => {
|
|
706
|
+
for (const db of dbs) {
|
|
707
|
+
try {
|
|
708
|
+
db.purgeStale()
|
|
709
|
+
} catch (err) {
|
|
710
|
+
if (db.listenerCount('error') > 0) {
|
|
711
|
+
db.emit('error', err)
|
|
712
|
+
} else {
|
|
713
|
+
process.emitWarning(err )
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
622
720
|
/** @deprecated Use `Cache` instead. */
|
|
623
721
|
export const AsyncCache = Cache
|
|
624
722
|
/** @deprecated Use `CacheOptions` instead. */
|
package/lib/memory.d.ts
CHANGED
|
@@ -18,4 +18,10 @@ export declare class MemoryCache<V> {
|
|
|
18
18
|
get(key: string): MemoryCacheEntry<V> | undefined;
|
|
19
19
|
delete(key: string): void;
|
|
20
20
|
purgeStale(now: number): void;
|
|
21
|
+
get stats(): {
|
|
22
|
+
size: number;
|
|
23
|
+
maxSize: number;
|
|
24
|
+
count: number;
|
|
25
|
+
maxCount: number;
|
|
26
|
+
};
|
|
21
27
|
}
|
package/lib/memory.js
CHANGED
|
@@ -97,6 +97,15 @@ export class MemoryCache {
|
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
get stats() {
|
|
101
|
+
return {
|
|
102
|
+
size: this.#size,
|
|
103
|
+
maxSize: this.#maxSize,
|
|
104
|
+
count: this.#count,
|
|
105
|
+
maxCount: this.#maxCount,
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
100
109
|
#prune() {
|
|
101
110
|
while (this.#size > this.#maxSize || this.#count > this.#maxCount) {
|
|
102
111
|
const e1 = this.#arr[(Math.random() * this.#arr.length) | 0]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nxtedition/cache",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -30,5 +30,5 @@
|
|
|
30
30
|
"tsd": "^0.33.0",
|
|
31
31
|
"typescript": "^5.9.3"
|
|
32
32
|
},
|
|
33
|
-
"gitHead": "
|
|
33
|
+
"gitHead": "9b8156711c1909480df222a003871e2d9cded24c"
|
|
34
34
|
}
|