@gravito/stasis 3.1.0 → 3.2.0

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/dist/index.js CHANGED
@@ -1,7 +1,60 @@
1
+ import { createRequire } from "node:module";
2
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
3
+
4
+ // src/cache-events.ts
5
+ function emitCacheEvent(event, payload, events, config) {
6
+ const { mode, throwOnError, onError } = config;
7
+ if (mode === "off") {
8
+ return;
9
+ }
10
+ const fn = events?.[event];
11
+ if (!fn) {
12
+ return;
13
+ }
14
+ const invoke = () => {
15
+ if (event === "flush") {
16
+ return fn();
17
+ }
18
+ const key = payload.key ?? "";
19
+ return fn(key);
20
+ };
21
+ const reportError = (error) => {
22
+ try {
23
+ onError?.(error, event, payload);
24
+ } catch {}
25
+ };
26
+ if (mode === "sync") {
27
+ try {
28
+ return Promise.resolve(invoke()).catch((error) => {
29
+ reportError(error);
30
+ if (throwOnError) {
31
+ throw error;
32
+ }
33
+ });
34
+ } catch (error) {
35
+ reportError(error);
36
+ if (throwOnError) {
37
+ throw error;
38
+ }
39
+ }
40
+ return;
41
+ }
42
+ queueMicrotask(() => {
43
+ try {
44
+ const result = invoke();
45
+ if (result && typeof result.catch === "function") {
46
+ result.catch(reportError);
47
+ }
48
+ } catch (error) {
49
+ reportError(error);
50
+ }
51
+ });
52
+ }
53
+
1
54
  // src/locks.ts
2
- var LockTimeoutError = class extends Error {
55
+ class LockTimeoutError extends Error {
3
56
  name = "LockTimeoutError";
4
- };
57
+ }
5
58
  function sleep(ms) {
6
59
  return new Promise((resolve) => setTimeout(resolve, ms));
7
60
  }
@@ -19,8 +72,8 @@ function normalizeCacheKey(key) {
19
72
  return key;
20
73
  }
21
74
  function ttlToExpiresAt(ttl, now = Date.now()) {
22
- if (ttl === void 0) {
23
- return void 0;
75
+ if (ttl === undefined) {
76
+ return;
24
77
  }
25
78
  if (ttl === null) {
26
79
  return null;
@@ -32,42 +85,87 @@ function ttlToExpiresAt(ttl, now = Date.now()) {
32
85
  if (ttl <= 0) {
33
86
  return now;
34
87
  }
35
- return now + ttl * 1e3;
88
+ return now + ttl * 1000;
36
89
  }
37
- return void 0;
90
+ return;
38
91
  }
39
92
  function isExpired(expiresAt, now = Date.now()) {
40
93
  if (expiresAt === null) {
41
94
  return false;
42
95
  }
43
- if (expiresAt === void 0) {
96
+ if (expiresAt === undefined) {
44
97
  return false;
45
98
  }
46
- return now > expiresAt;
99
+ return now >= expiresAt;
100
+ }
101
+
102
+ // src/tagged-store.ts
103
+ class TaggedStore {
104
+ store;
105
+ tags;
106
+ constructor(store, tags) {
107
+ this.store = store;
108
+ this.tags = tags;
109
+ }
110
+ tagged(key) {
111
+ return this.store.tagKey(normalizeCacheKey(key), this.tags);
112
+ }
113
+ async get(key) {
114
+ return this.store.get(this.tagged(key));
115
+ }
116
+ async put(key, value, ttl) {
117
+ const taggedKey = this.tagged(key);
118
+ await this.store.put(taggedKey, value, ttl);
119
+ this.store.tagIndexAdd(this.tags, taggedKey);
120
+ }
121
+ async add(key, value, ttl) {
122
+ const taggedKey = this.tagged(key);
123
+ const ok = await this.store.add(taggedKey, value, ttl);
124
+ if (ok) {
125
+ this.store.tagIndexAdd(this.tags, taggedKey);
126
+ }
127
+ return ok;
128
+ }
129
+ async forget(key) {
130
+ return this.store.forget(this.tagged(key));
131
+ }
132
+ async flush() {
133
+ return this.store.flushTags(this.tags);
134
+ }
135
+ async increment(key, value) {
136
+ const taggedKey = this.tagged(key);
137
+ const next = await this.store.increment(taggedKey, value);
138
+ this.store.tagIndexAdd(this.tags, taggedKey);
139
+ return next;
140
+ }
141
+ async decrement(key, value) {
142
+ const taggedKey = this.tagged(key);
143
+ const next = await this.store.decrement(taggedKey, value);
144
+ this.store.tagIndexAdd(this.tags, taggedKey);
145
+ return next;
146
+ }
147
+ lock(name, seconds) {
148
+ return this.store.lock ? this.store.lock(this.tagged(name), seconds) : undefined;
149
+ }
47
150
  }
48
151
 
49
152
  // src/CacheRepository.ts
50
- var CacheRepository = class _CacheRepository {
153
+ class CacheRepository {
154
+ store;
155
+ options;
156
+ refreshSemaphore = new Map;
157
+ coalesceSemaphore = new Map;
158
+ flexibleStats = { refreshCount: 0, refreshFailures: 0, totalTime: 0 };
159
+ eventEmitterConfig;
51
160
  constructor(store, options = {}) {
52
161
  this.store = store;
53
162
  this.options = options;
163
+ this.eventEmitterConfig = {
164
+ mode: options.eventsMode ?? "async",
165
+ throwOnError: options.throwOnEventError,
166
+ onError: options.onEventError
167
+ };
54
168
  }
55
- refreshSemaphore = /* @__PURE__ */ new Map();
56
- coalesceSemaphore = /* @__PURE__ */ new Map();
57
- flexibleStats = { refreshCount: 0, refreshFailures: 0, totalTime: 0 };
58
- /**
59
- * Retrieve statistics about flexible cache operations.
60
- *
61
- * Useful for monitoring the health and performance of background refreshes.
62
- *
63
- * @returns Current statistics for background refresh operations.
64
- *
65
- * @example
66
- * ```typescript
67
- * const stats = cache.getFlexibleStats();
68
- * console.log(`Refreshed ${stats.refreshCount} times`);
69
- * ```
70
- */
71
169
  getFlexibleStats() {
72
170
  return {
73
171
  refreshCount: this.flexibleStats.refreshCount,
@@ -76,53 +174,7 @@ var CacheRepository = class _CacheRepository {
76
174
  };
77
175
  }
78
176
  emit(event, payload = {}) {
79
- const mode = this.options.eventsMode ?? "async";
80
- if (mode === "off") {
81
- return;
82
- }
83
- const fn = this.options.events?.[event];
84
- if (!fn) {
85
- return;
86
- }
87
- const invoke = () => {
88
- if (event === "flush") {
89
- return fn();
90
- }
91
- const key = payload.key ?? "";
92
- return fn(key);
93
- };
94
- const reportError = (error) => {
95
- try {
96
- this.options.onEventError?.(error, event, payload);
97
- } catch {
98
- }
99
- };
100
- if (mode === "sync") {
101
- try {
102
- return Promise.resolve(invoke()).catch((error) => {
103
- reportError(error);
104
- if (this.options.throwOnEventError) {
105
- throw error;
106
- }
107
- });
108
- } catch (error) {
109
- reportError(error);
110
- if (this.options.throwOnEventError) {
111
- throw error;
112
- }
113
- }
114
- return;
115
- }
116
- queueMicrotask(() => {
117
- try {
118
- const result = invoke();
119
- if (result && typeof result.catch === "function") {
120
- void result.catch(reportError);
121
- }
122
- } catch (error) {
123
- reportError(error);
124
- }
125
- });
177
+ return emitCacheEvent(event, payload, this.options.events, this.eventEmitterConfig);
126
178
  }
127
179
  key(key) {
128
180
  const normalized = normalizeCacheKey(key);
@@ -138,23 +190,6 @@ var CacheRepository = class _CacheRepository {
138
190
  async forgetMetaKey(metaKey) {
139
191
  await this.store.forget(metaKey);
140
192
  }
141
- /**
142
- * Retrieve an item from the cache by its key.
143
- *
144
- * Fetches the value from the underlying store. If not found, returns the
145
- * provided default value or executes the factory function.
146
- *
147
- * @param key - The unique cache key.
148
- * @param defaultValue - A default value or factory function to use if the key is not found.
149
- * @returns The cached value, or the default value if not found.
150
- * @throws {Error} If the underlying store fails to retrieve the value.
151
- *
152
- * @example
153
- * ```typescript
154
- * const user = await cache.get('user:1', { name: 'Guest' });
155
- * const settings = await cache.get('settings', () => fetchSettings());
156
- * ```
157
- */
158
193
  async get(key, defaultValue) {
159
194
  const fullKey = this.key(key);
160
195
  const raw = await this.store.get(fullKey);
@@ -169,7 +204,7 @@ var CacheRepository = class _CacheRepository {
169
204
  if (e) {
170
205
  await e;
171
206
  }
172
- if (defaultValue === void 0) {
207
+ if (defaultValue === undefined) {
173
208
  return null;
174
209
  }
175
210
  if (typeof defaultValue === "function") {
@@ -177,59 +212,12 @@ var CacheRepository = class _CacheRepository {
177
212
  }
178
213
  return defaultValue;
179
214
  }
180
- /**
181
- * Determine if an item exists in the cache.
182
- *
183
- * Checks for the presence of a key without necessarily returning its value.
184
- *
185
- * @param key - The cache key.
186
- * @returns True if the item exists, false otherwise.
187
- * @throws {Error} If the underlying store fails to check existence.
188
- *
189
- * @example
190
- * ```typescript
191
- * if (await cache.has('session:active')) {
192
- * // ...
193
- * }
194
- * ```
195
- */
196
215
  async has(key) {
197
216
  return await this.get(key) !== null;
198
217
  }
199
- /**
200
- * Determine if an item is missing from the cache.
201
- *
202
- * Inverse of `has()`, used for cleaner conditional logic.
203
- *
204
- * @param key - The cache key.
205
- * @returns True if the item is missing, false otherwise.
206
- * @throws {Error} If the underlying store fails to check existence.
207
- *
208
- * @example
209
- * ```typescript
210
- * if (await cache.missing('config:loaded')) {
211
- * await loadConfig();
212
- * }
213
- * ```
214
- */
215
218
  async missing(key) {
216
219
  return !await this.has(key);
217
220
  }
218
- /**
219
- * Store an item in the cache for a specific duration.
220
- *
221
- * Persists the value in the underlying store with the given TTL.
222
- *
223
- * @param key - Unique cache key.
224
- * @param value - Value to store.
225
- * @param ttl - Expiration duration.
226
- * @throws {Error} If the underlying store fails to persist the value or serialization fails.
227
- *
228
- * @example
229
- * ```typescript
230
- * await cache.put('token', 'xyz123', 3600);
231
- * ```
232
- */
233
221
  async put(key, value, ttl) {
234
222
  const fullKey = this.key(key);
235
223
  const data = await this.compress(value);
@@ -239,41 +227,10 @@ var CacheRepository = class _CacheRepository {
239
227
  await e;
240
228
  }
241
229
  }
242
- /**
243
- * Store an item in the cache for a specific duration.
244
- *
245
- * Uses the repository's default TTL if none is provided.
246
- *
247
- * @param key - The unique cache key.
248
- * @param value - The value to store.
249
- * @param ttl - Optional time-to-live.
250
- * @throws {Error} If the underlying store fails to persist the value.
251
- *
252
- * @example
253
- * ```typescript
254
- * await cache.set('theme', 'dark');
255
- * ```
256
- */
257
230
  async set(key, value, ttl) {
258
231
  const resolved = ttl ?? this.options.defaultTtl;
259
232
  await this.put(key, value, resolved);
260
233
  }
261
- /**
262
- * Store an item in the cache only if the key does not already exist.
263
- *
264
- * Atomic operation to prevent overwriting existing data.
265
- *
266
- * @param key - The unique cache key.
267
- * @param value - The value to store.
268
- * @param ttl - Optional time-to-live.
269
- * @returns True if the item was added, false otherwise.
270
- * @throws {Error} If the underlying store fails the atomic operation.
271
- *
272
- * @example
273
- * ```typescript
274
- * const added = await cache.add('lock:process', true, 60);
275
- * ```
276
- */
277
234
  async add(key, value, ttl) {
278
235
  const fullKey = this.key(key);
279
236
  const resolved = ttl ?? this.options.defaultTtl;
@@ -286,40 +243,9 @@ var CacheRepository = class _CacheRepository {
286
243
  }
287
244
  return ok;
288
245
  }
289
- /**
290
- * Store an item in the cache indefinitely.
291
- *
292
- * Sets the TTL to null, indicating the value should not expire automatically.
293
- *
294
- * @param key - The unique cache key.
295
- * @param value - The value to store.
296
- * @throws {Error} If the underlying store fails to persist the value.
297
- *
298
- * @example
299
- * ```typescript
300
- * await cache.forever('system:id', 'node-01');
301
- * ```
302
- */
303
246
  async forever(key, value) {
304
247
  await this.put(key, value, null);
305
248
  }
306
- /**
307
- * Get an item from the cache, or execute the given callback and store the result.
308
- *
309
- * Implements the "Cache-Aside" pattern, ensuring the callback is only executed
310
- * on a cache miss.
311
- *
312
- * @param key - The unique cache key.
313
- * @param ttl - Time-to-live.
314
- * @param callback - The callback to execute if the key is not found.
315
- * @returns The cached value or the result of the callback.
316
- * @throws {Error} If the callback or the underlying store fails.
317
- *
318
- * @example
319
- * ```typescript
320
- * const data = await cache.remember('users:all', 300, () => db.users.findMany());
321
- * ```
322
- */
323
249
  async remember(key, ttl, callback) {
324
250
  const fullKey = this.key(key);
325
251
  if (this.coalesceSemaphore.has(fullKey)) {
@@ -341,38 +267,9 @@ var CacheRepository = class _CacheRepository {
341
267
  this.coalesceSemaphore.set(fullKey, promise);
342
268
  return promise;
343
269
  }
344
- /**
345
- * Get an item from the cache, or execute the given callback and store the result indefinitely.
346
- *
347
- * Similar to `remember()`, but the value is stored without an expiration time.
348
- *
349
- * @param key - The unique cache key.
350
- * @param callback - The callback to execute if the key is not found.
351
- * @returns The cached value or the result of the callback.
352
- * @throws {Error} If the callback or the underlying store fails.
353
- *
354
- * @example
355
- * ```typescript
356
- * const config = await cache.rememberForever('app:config', () => loadConfig());
357
- * ```
358
- */
359
270
  async rememberForever(key, callback) {
360
271
  return this.remember(key, null, callback);
361
272
  }
362
- /**
363
- * Retrieve multiple items from the cache by their keys.
364
- *
365
- * Efficiently fetches multiple values, returning a map of keys to values.
366
- *
367
- * @param keys - An array of unique cache keys.
368
- * @returns An object where keys are the original keys and values are the cached values.
369
- * @throws {Error} If the underlying store fails to retrieve values.
370
- *
371
- * @example
372
- * ```typescript
373
- * const results = await cache.many(['user:1', 'user:2']);
374
- * ```
375
- */
376
273
  async many(keys) {
377
274
  const out = {};
378
275
  for (const key of keys) {
@@ -380,47 +277,15 @@ var CacheRepository = class _CacheRepository {
380
277
  }
381
278
  return out;
382
279
  }
383
- /**
384
- * Store multiple items in the cache for a specific duration.
385
- *
386
- * Persists multiple key-value pairs in a single operation if supported by the store.
387
- *
388
- * @param values - An object where keys are the unique cache keys and values are the values to store.
389
- * @param ttl - Time-to-live.
390
- * @throws {Error} If the underlying store fails to persist values.
391
- *
392
- * @example
393
- * ```typescript
394
- * await cache.putMany({ 'a': 1, 'b': 2 }, 60);
395
- * ```
396
- */
397
280
  async putMany(values, ttl) {
398
281
  await Promise.all(Object.entries(values).map(([k, v]) => this.put(k, v, ttl)));
399
282
  }
400
- /**
401
- * Laravel-like flexible cache (stale-while-revalidate).
402
- *
403
- * Serves stale content while revalidating the cache in the background. This
404
- * minimizes latency for users by avoiding synchronous revalidation.
405
- *
406
- * @param key - The unique cache key.
407
- * @param ttlSeconds - How long the value is considered fresh.
408
- * @param staleSeconds - How long the stale value may be served while a refresh happens.
409
- * @param callback - The callback to execute to refresh the cache.
410
- * @returns The fresh or stale cached value.
411
- * @throws {Error} If the callback fails on a cache miss.
412
- *
413
- * @example
414
- * ```typescript
415
- * const value = await cache.flexible('stats', 60, 30, () => fetchStats());
416
- * ```
417
- */
418
283
  async flexible(key, ttlSeconds, staleSeconds, callback) {
419
284
  const fullKey = this.key(key);
420
285
  const metaKey = this.flexibleFreshUntilKey(fullKey);
421
286
  const now = Date.now();
422
- const ttlMillis = Math.max(0, ttlSeconds) * 1e3;
423
- const staleMillis = Math.max(0, staleSeconds) * 1e3;
287
+ const ttlMillis = Math.max(0, ttlSeconds) * 1000;
288
+ const staleMillis = Math.max(0, staleSeconds) * 1000;
424
289
  const [freshUntil, cachedValue] = await Promise.all([
425
290
  this.store.get(metaKey),
426
291
  this.store.get(fullKey)
@@ -438,7 +303,7 @@ var CacheRepository = class _CacheRepository {
438
303
  if (e2) {
439
304
  await e2;
440
305
  }
441
- void this.refreshFlexible(fullKey, metaKey, ttlSeconds, staleSeconds, callback);
306
+ this.refreshFlexible(fullKey, metaKey, ttlSeconds, staleSeconds, callback);
442
307
  return cachedValue;
443
308
  }
444
309
  }
@@ -474,18 +339,16 @@ var CacheRepository = class _CacheRepository {
474
339
  }
475
340
  const startTime = Date.now();
476
341
  try {
477
- const timeoutMillis = this.options.refreshTimeout ?? 3e4;
342
+ const timeoutMillis = this.options.refreshTimeout ?? 30000;
478
343
  const maxRetries = this.options.maxRetries ?? 0;
479
344
  const retryDelay = this.options.retryDelay ?? 50;
480
345
  let lastError;
481
346
  let value;
482
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
347
+ for (let attempt = 0;attempt <= maxRetries; attempt++) {
483
348
  try {
484
349
  value = await Promise.race([
485
350
  Promise.resolve(callback()),
486
- new Promise(
487
- (_, reject) => setTimeout(() => reject(new Error("Refresh timeout")), timeoutMillis)
488
- )
351
+ new Promise((_, reject) => setTimeout(() => reject(new Error("Refresh timeout")), timeoutMillis))
489
352
  ]);
490
353
  break;
491
354
  } catch (err) {
@@ -495,13 +358,13 @@ var CacheRepository = class _CacheRepository {
495
358
  }
496
359
  }
497
360
  }
498
- if (value === void 0 && lastError) {
361
+ if (value === undefined && lastError) {
499
362
  throw lastError;
500
363
  }
501
364
  const totalTtl = ttlSeconds + staleSeconds;
502
365
  const now = Date.now();
503
366
  await this.put(fullKey, value, totalTtl);
504
- await this.putMetaKey(metaKey, now + Math.max(0, ttlSeconds) * 1e3, totalTtl);
367
+ await this.putMetaKey(metaKey, now + Math.max(0, ttlSeconds) * 1000, totalTtl);
505
368
  this.flexibleStats.refreshCount++;
506
369
  this.flexibleStats.totalTime += Date.now() - startTime;
507
370
  } catch {
@@ -510,41 +373,11 @@ var CacheRepository = class _CacheRepository {
510
373
  await lock.release();
511
374
  }
512
375
  }
513
- /**
514
- * Retrieve an item from the cache and delete it.
515
- *
516
- * Atomic-like operation to fetch and immediately remove a value, often used
517
- * for one-time tokens or flash messages.
518
- *
519
- * @param key - The unique cache key.
520
- * @param defaultValue - A default value to use if the key is not found.
521
- * @returns The cached value, or the default value if not found.
522
- * @throws {Error} If the underlying store fails to retrieve or forget the value.
523
- *
524
- * @example
525
- * ```typescript
526
- * const message = await cache.pull('flash:status');
527
- * ```
528
- */
529
376
  async pull(key, defaultValue) {
530
377
  const value = await this.get(key, defaultValue);
531
378
  await this.forget(key);
532
379
  return value;
533
380
  }
534
- /**
535
- * Remove an item from the cache by its key.
536
- *
537
- * Deletes the value and any associated metadata from the underlying store.
538
- *
539
- * @param key - The cache key to remove.
540
- * @returns True if the item existed and was removed.
541
- * @throws {Error} If the underlying store fails to remove the value.
542
- *
543
- * @example
544
- * ```typescript
545
- * const deleted = await cache.forget('user:session');
546
- * ```
547
- */
548
381
  async forget(key) {
549
382
  const fullKey = this.key(key);
550
383
  const metaKey = this.flexibleFreshUntilKey(fullKey);
@@ -558,36 +391,9 @@ var CacheRepository = class _CacheRepository {
558
391
  }
559
392
  return ok;
560
393
  }
561
- /**
562
- * Alias for `forget`.
563
- *
564
- * Provides compatibility with standard `Map`-like or `Storage` APIs.
565
- *
566
- * @param key - The cache key to remove.
567
- * @returns True if the item existed and was removed.
568
- * @throws {Error} If the underlying store fails to remove the value.
569
- *
570
- * @example
571
- * ```typescript
572
- * await cache.delete('temp:data');
573
- * ```
574
- */
575
394
  async delete(key) {
576
395
  return this.forget(key);
577
396
  }
578
- /**
579
- * Remove all items from the cache storage.
580
- *
581
- * Clears the entire underlying store. Use with caution as this affects all
582
- * keys regardless of prefix.
583
- *
584
- * @throws {Error} If the underlying store fails to flush.
585
- *
586
- * @example
587
- * ```typescript
588
- * await cache.flush();
589
- * ```
590
- */
591
397
  async flush() {
592
398
  await this.store.flush();
593
399
  const e = this.emit("flush");
@@ -595,148 +401,52 @@ var CacheRepository = class _CacheRepository {
595
401
  await e;
596
402
  }
597
403
  }
598
- /**
599
- * Alias for `flush`.
600
- *
601
- * Provides compatibility with standard `Map`-like or `Storage` APIs.
602
- *
603
- * @throws {Error} If the underlying store fails to clear.
604
- *
605
- * @example
606
- * ```typescript
607
- * await cache.clear();
608
- * ```
609
- */
610
404
  async clear() {
611
405
  return this.flush();
612
406
  }
613
- /**
614
- * Increment the value of a numeric item in the cache.
615
- *
616
- * Atomically increases the value of a key. If the key does not exist, it is
617
- * typically initialized to 0 before incrementing.
618
- *
619
- * @param key - The cache key.
620
- * @param value - The amount to increment by.
621
- * @returns The new value.
622
- * @throws {Error} If the underlying store fails the atomic increment.
623
- *
624
- * @example
625
- * ```typescript
626
- * const count = await cache.increment('page:views');
627
- * ```
628
- */
629
407
  increment(key, value) {
630
408
  return this.store.increment(this.key(key), value);
631
409
  }
632
- /**
633
- * Decrement the value of a numeric item in the cache.
634
- *
635
- * Atomically decreases the value of a key.
636
- *
637
- * @param key - The cache key.
638
- * @param value - The amount to decrement by.
639
- * @returns The new value.
640
- * @throws {Error} If the underlying store fails the atomic decrement.
641
- *
642
- * @example
643
- * ```typescript
644
- * const remaining = await cache.decrement('stock:count');
645
- * ```
646
- */
647
410
  decrement(key, value) {
648
411
  return this.store.decrement(this.key(key), value);
649
412
  }
650
- /**
651
- * Get a distributed lock instance for the given name.
652
- *
653
- * Provides a mechanism for exclusive access to resources across multiple
654
- * processes or servers.
655
- *
656
- * @param name - The lock name.
657
- * @param seconds - Optional default duration for the lock in seconds.
658
- * @returns A `CacheLock` instance if supported, otherwise undefined.
659
- *
660
- * @example
661
- * ```typescript
662
- * const lock = cache.lock('process:heavy', 10);
663
- * if (await lock.acquire()) {
664
- * try {
665
- * // ...
666
- * } finally {
667
- * await lock.release();
668
- * }
669
- * }
670
- * ```
671
- */
672
413
  lock(name, seconds) {
673
- return this.store.lock ? this.store.lock(this.key(name), seconds) : void 0;
674
- }
675
- /**
676
- * Create a new repository instance with the given tags.
677
- *
678
- * Enables grouping of cache entries for collective operations like flushing
679
- * all keys associated with specific tags.
680
- *
681
- * @param tags - An array of tag names.
682
- * @returns A new `CacheRepository` instance that uses the given tags.
683
- * @throws {Error} If the underlying store does not support tagging.
684
- *
685
- * @example
686
- * ```typescript
687
- * await cache.tags(['users', 'profiles']).put('user:1', data, 3600);
688
- * await cache.tags(['users']).flush();
689
- * ```
690
- */
414
+ return this.store.lock ? this.store.lock(this.key(name), seconds) : undefined;
415
+ }
691
416
  tags(tags) {
692
417
  if (!isTaggableStore(this.store)) {
693
418
  throw new Error("This cache store does not support tags.");
694
419
  }
695
- return new _CacheRepository(new TaggedStore(this.store, tags), this.options);
696
- }
697
- /**
698
- * Retrieve the underlying cache store.
699
- *
700
- * Provides direct access to the low-level store implementation for advanced
701
- * use cases or debugging.
702
- *
703
- * @returns The low-level cache store instance.
704
- *
705
- * @example
706
- * ```typescript
707
- * const store = cache.getStore();
708
- * ```
709
- */
420
+ return new CacheRepository(new TaggedStore(this.store, tags), this.options);
421
+ }
710
422
  getStore() {
711
423
  return this.store;
712
424
  }
713
- /**
714
- * Compress a value before storage if compression is enabled and thresholds are met.
715
- */
716
425
  async compress(value) {
717
426
  const opts = this.options.compression;
718
- if (!opts?.enabled || value === null || value === void 0) {
427
+ if (!opts?.enabled || value === null || value === undefined) {
719
428
  return value;
720
429
  }
721
430
  const json = JSON.stringify(value);
722
431
  if (json.length < (opts.minSize ?? 1024)) {
723
432
  return value;
724
433
  }
725
- const { gzipSync } = await import("zlib");
726
- const compressed = gzipSync(Buffer.from(json), { level: opts.level ?? 6 });
434
+ const { getCompressionAdapter } = await import("@gravito/core");
435
+ const adapter = getCompressionAdapter();
436
+ const input = new TextEncoder().encode(json);
437
+ const compressed = adapter.gzipSync(input, { level: opts.level ?? 6 });
727
438
  return {
728
439
  __gravito_compressed: true,
729
- data: compressed.toString("base64")
440
+ data: Buffer.from(compressed).toString("base64")
730
441
  };
731
442
  }
732
- /**
733
- * Decompress a value after retrieval if it was previously compressed.
734
- */
735
443
  async decompress(value) {
736
444
  if (value !== null && typeof value === "object" && "__gravito_compressed" in value && value.__gravito_compressed === true) {
737
- const { gunzipSync } = await import("zlib");
738
- const buffer = Buffer.from(value.data, "base64");
739
- const decompressed = gunzipSync(buffer).toString();
445
+ const { getCompressionAdapter } = await import("@gravito/core");
446
+ const adapter = getCompressionAdapter();
447
+ const buffer = new Uint8Array(Buffer.from(value.data, "base64"));
448
+ const decompressedBytes = adapter.gunzipSync(buffer);
449
+ const decompressed = new TextDecoder().decode(decompressedBytes);
740
450
  try {
741
451
  return JSON.parse(decompressed);
742
452
  } catch {
@@ -745,93 +455,17 @@ var CacheRepository = class _CacheRepository {
745
455
  }
746
456
  return value;
747
457
  }
748
- };
749
- var TaggedStore = class {
750
- constructor(store, tags) {
751
- this.store = store;
752
- this.tags = tags;
753
- }
754
- tagged(key) {
755
- return this.store.tagKey(normalizeCacheKey(key), this.tags);
756
- }
757
- async get(key) {
758
- return this.store.get(this.tagged(key));
759
- }
760
- async put(key, value, ttl) {
761
- const taggedKey = this.tagged(key);
762
- await this.store.put(taggedKey, value, ttl);
763
- this.store.tagIndexAdd(this.tags, taggedKey);
764
- }
765
- async add(key, value, ttl) {
766
- const taggedKey = this.tagged(key);
767
- const ok = await this.store.add(taggedKey, value, ttl);
768
- if (ok) {
769
- this.store.tagIndexAdd(this.tags, taggedKey);
770
- }
771
- return ok;
772
- }
773
- async forget(key) {
774
- return this.store.forget(this.tagged(key));
775
- }
776
- async flush() {
777
- return this.store.flushTags(this.tags);
778
- }
779
- async increment(key, value) {
780
- const taggedKey = this.tagged(key);
781
- const next = await this.store.increment(taggedKey, value);
782
- this.store.tagIndexAdd(this.tags, taggedKey);
783
- return next;
784
- }
785
- async decrement(key, value) {
786
- const taggedKey = this.tagged(key);
787
- const next = await this.store.decrement(taggedKey, value);
788
- this.store.tagIndexAdd(this.tags, taggedKey);
789
- return next;
790
- }
791
- lock(name, seconds) {
792
- return this.store.lock ? this.store.lock(this.tagged(name), seconds) : void 0;
793
- }
794
- };
458
+ }
795
459
 
796
460
  // src/RateLimiter.ts
797
- var RateLimiter = class {
798
- /**
799
- * Creates a new RateLimiter instance.
800
- *
801
- * @param store - Cache backend used to persist attempt counts.
802
- *
803
- * @example
804
- * ```typescript
805
- * const limiter = new RateLimiter(new MemoryStore());
806
- * ```
807
- */
461
+ class RateLimiter {
462
+ store;
808
463
  constructor(store) {
809
464
  this.store = store;
810
465
  }
811
- /**
812
- * Attempt to consume a slot in the rate limit window.
813
- *
814
- * This method checks the current attempt count for the given key. If the
815
- * count is below the limit, it increments the count and allows the request.
816
- * Otherwise, it returns a rejected status with retry information.
817
- *
818
- * @param key - The unique identifier for the rate limit (e.g., IP address or user ID).
819
- * @param maxAttempts - Maximum number of attempts allowed within the decay period.
820
- * @param decaySeconds - Duration of the rate limit window in seconds.
821
- * @returns A response indicating if the attempt was successful and the remaining capacity.
822
- * @throws {Error} If the underlying cache store fails to read or write data.
823
- *
824
- * @example
825
- * ```typescript
826
- * const response = await limiter.attempt('api-client:123', 100, 3600);
827
- * if (response.allowed) {
828
- * // Proceed with request
829
- * }
830
- * ```
831
- */
832
466
  async attempt(key, maxAttempts, decaySeconds) {
833
467
  const current = await this.store.get(key);
834
- const now = Math.floor(Date.now() / 1e3);
468
+ const now = Math.floor(Date.now() / 1000);
835
469
  if (current === null) {
836
470
  await this.store.put(key, 1, decaySeconds);
837
471
  return {
@@ -856,18 +490,6 @@ var RateLimiter = class {
856
490
  reset: now + decaySeconds
857
491
  };
858
492
  }
859
- /**
860
- * Calculate the number of seconds until the rate limit resets.
861
- *
862
- * This helper method attempts to retrieve the TTL from the store. If the
863
- * store does not support TTL inspection, it falls back to the provided
864
- * decay period.
865
- *
866
- * @param key - Unique identifier for the rate limit.
867
- * @param decaySeconds - Default decay period to use as a fallback.
868
- * @returns Number of seconds until the key expires.
869
- * @throws {Error} If the store fails to retrieve TTL metadata.
870
- */
871
493
  async availableIn(key, decaySeconds) {
872
494
  if (typeof this.store.ttl === "function") {
873
495
  const remaining = await this.store.ttl(key);
@@ -877,27 +499,9 @@ var RateLimiter = class {
877
499
  }
878
500
  return decaySeconds;
879
501
  }
880
- /**
881
- * Get detailed information about the current rate limit status without consuming an attempt.
882
- *
883
- * Useful for returning rate limit headers (e.g., X-RateLimit-Limit) in
884
- * middleware or for pre-flight checks.
885
- *
886
- * @param key - The unique identifier for the rate limit.
887
- * @param maxAttempts - Maximum number of attempts allowed.
888
- * @param decaySeconds - Duration of the rate limit window in seconds.
889
- * @returns Current status including limit, remaining attempts, and reset time.
890
- * @throws {Error} If the underlying cache store fails to retrieve data.
891
- *
892
- * @example
893
- * ```typescript
894
- * const info = await limiter.getInfo('user:42', 60, 60);
895
- * console.log(`Remaining: ${info.remaining}/${info.limit}`);
896
- * ```
897
- */
898
502
  async getInfo(key, maxAttempts, decaySeconds) {
899
503
  const current = await this.store.get(key);
900
- const now = Math.floor(Date.now() / 1e3);
504
+ const now = Math.floor(Date.now() / 1000);
901
505
  if (current === null) {
902
506
  return {
903
507
  limit: maxAttempts,
@@ -906,7 +510,7 @@ var RateLimiter = class {
906
510
  };
907
511
  }
908
512
  const remaining = Math.max(0, maxAttempts - current);
909
- const retryAfter = remaining === 0 ? await this.availableIn(key, decaySeconds) : void 0;
513
+ const retryAfter = remaining === 0 ? await this.availableIn(key, decaySeconds) : undefined;
910
514
  return {
911
515
  limit: maxAttempts,
912
516
  remaining,
@@ -914,82 +518,27 @@ var RateLimiter = class {
914
518
  retryAfter
915
519
  };
916
520
  }
917
- /**
918
- * Reset the rate limit counter for a specific key.
919
- *
920
- * Use this to manually clear a block, for example after a successful
921
- * login or when an administrator manually unblocks a user.
922
- *
923
- * @param key - The unique identifier to clear.
924
- * @throws {Error} If the store fails to delete the key.
925
- *
926
- * @example
927
- * ```typescript
928
- * await limiter.clear('login-attempts:user@example.com');
929
- * ```
930
- */
931
521
  async clear(key) {
932
522
  await this.store.forget(key);
933
523
  }
934
- };
524
+ }
935
525
 
936
526
  // src/CacheManager.ts
937
- var CacheManager = class {
938
- /**
939
- * Initialize a new CacheManager instance.
940
- *
941
- * @param storeFactory - Factory function to create low-level store instances by name.
942
- * @param config - Configuration manifest for stores and global defaults.
943
- * @param events - Optional event handlers for cache lifecycle hooks.
944
- * @param eventOptions - Configuration for how events are dispatched and handled.
945
- */
527
+ class CacheManager {
528
+ storeFactory;
529
+ config;
530
+ events;
531
+ eventOptions;
532
+ stores = new Map;
946
533
  constructor(storeFactory, config = {}, events, eventOptions) {
947
534
  this.storeFactory = storeFactory;
948
535
  this.config = config;
949
536
  this.events = events;
950
537
  this.eventOptions = eventOptions;
951
538
  }
952
- /**
953
- * Internal registry of initialized cache repositories.
954
- */
955
- stores = /* @__PURE__ */ new Map();
956
- /**
957
- * Get a rate limiter instance for a specific store.
958
- *
959
- * Provides a specialized interface for throttling actions based on cache keys,
960
- * leveraging the underlying storage for persistence.
961
- *
962
- * @param name - Store name (defaults to the configured default store).
963
- * @returns A RateLimiter instance bound to the requested store.
964
- * @throws {Error} If the requested store cannot be initialized.
965
- *
966
- * @example
967
- * ```typescript
968
- * const limiter = cache.limiter('redis');
969
- * if (await limiter.tooManyAttempts('login:1', 5)) {
970
- * throw new Error('Too many attempts');
971
- * }
972
- * ```
973
- */
974
539
  limiter(name) {
975
540
  return new RateLimiter(this.store(name).getStore());
976
541
  }
977
- /**
978
- * Resolve a named cache repository.
979
- *
980
- * Lazily initializes and caches the repository instance for the given store name.
981
- * If no name is provided, it falls back to the default store.
982
- *
983
- * @param name - Store name to retrieve.
984
- * @returns Initialized CacheRepository instance.
985
- * @throws {Error} If the store factory fails to create the underlying store or the driver is unsupported.
986
- *
987
- * @example
988
- * ```typescript
989
- * const redis = cache.store('redis');
990
- * await redis.put('key', 'value', 60);
991
- * ```
992
- */
993
542
  store(name) {
994
543
  const storeName = name ?? this.config.default ?? "memory";
995
544
  const existing = this.stores.get(storeName);
@@ -1007,381 +556,79 @@ var CacheManager = class {
1007
556
  this.stores.set(storeName, repo);
1008
557
  return repo;
1009
558
  }
1010
- // Laravel-like proxy methods (default store)
1011
- /**
1012
- * Retrieve an item from the default cache store.
1013
- *
1014
- * If the key is missing, the provided default value or the result of the
1015
- * default value closure will be returned.
1016
- *
1017
- * @param key - Unique cache key.
1018
- * @param defaultValue - Fallback value or factory to execute on cache miss.
1019
- * @returns Cached value or the resolved default.
1020
- * @throws {Error} If the underlying store driver encounters a read error or connection failure.
1021
- *
1022
- * @example
1023
- * ```typescript
1024
- * const value = await cache.get('user:1', { name: 'Guest' });
1025
- * ```
1026
- */
1027
559
  get(key, defaultValue) {
1028
560
  return this.store().get(key, defaultValue);
1029
561
  }
1030
- /**
1031
- * Determine if an item exists in the default cache store.
1032
- *
1033
- * @param key - The unique cache key.
1034
- * @returns True if the key exists and has not expired.
1035
- * @throws {Error} If the underlying store driver encounters a connection error.
1036
- *
1037
- * @example
1038
- * ```typescript
1039
- * if (await cache.has('session:active')) {
1040
- * // ...
1041
- * }
1042
- * ```
1043
- */
1044
562
  has(key) {
1045
563
  return this.store().has(key);
1046
564
  }
1047
- /**
1048
- * Determine if an item is missing from the default cache store.
1049
- *
1050
- * @param key - The unique cache key.
1051
- * @returns True if the key does not exist or has expired.
1052
- * @throws {Error} If the underlying store driver encounters a connection error.
1053
- *
1054
- * @example
1055
- * ```typescript
1056
- * if (await cache.missing('config:loaded')) {
1057
- * await loadConfig();
1058
- * }
1059
- * ```
1060
- */
1061
565
  missing(key) {
1062
566
  return this.store().missing(key);
1063
567
  }
1064
- /**
1065
- * Store an item in the default cache store for a specific duration.
1066
- *
1067
- * @param key - The unique cache key.
1068
- * @param value - The data to be cached.
1069
- * @param ttl - Expiration time in seconds or a specific Date.
1070
- * @returns A promise that resolves when the write is complete.
1071
- * @throws {Error} If the value cannot be serialized or the store is read-only.
1072
- *
1073
- * @example
1074
- * ```typescript
1075
- * await cache.put('key', 'value', 60); // 60 seconds
1076
- * ```
1077
- */
1078
568
  put(key, value, ttl) {
1079
569
  return this.store().put(key, value, ttl);
1080
570
  }
1081
- /**
1082
- * Store an item in the default cache store (alias for put).
1083
- *
1084
- * @param key - The unique cache key.
1085
- * @param value - The data to be cached.
1086
- * @param ttl - Optional expiration time.
1087
- * @returns A promise that resolves when the write is complete.
1088
- * @throws {Error} If the underlying store driver encounters a write error.
1089
- *
1090
- * @example
1091
- * ```typescript
1092
- * await cache.set('theme', 'dark');
1093
- * ```
1094
- */
1095
571
  set(key, value, ttl) {
1096
572
  return this.store().set(key, value, ttl);
1097
573
  }
1098
- /**
1099
- * Store an item in the default cache store only if it does not already exist.
1100
- *
1101
- * @param key - The unique cache key.
1102
- * @param value - The data to be cached.
1103
- * @param ttl - Optional expiration time.
1104
- * @returns True if the item was added, false if it already existed.
1105
- * @throws {Error} If the underlying store driver encounters a write error.
1106
- *
1107
- * @example
1108
- * ```typescript
1109
- * const added = await cache.add('lock:process', true, 30);
1110
- * ```
1111
- */
1112
574
  add(key, value, ttl) {
1113
575
  return this.store().add(key, value, ttl);
1114
576
  }
1115
- /**
1116
- * Store an item in the default cache store indefinitely.
1117
- *
1118
- * @param key - The unique cache key.
1119
- * @param value - The data to be cached.
1120
- * @returns A promise that resolves when the write is complete.
1121
- * @throws {Error} If the underlying store driver encounters a write error.
1122
- *
1123
- * @example
1124
- * ```typescript
1125
- * await cache.forever('system:version', '1.0.0');
1126
- * ```
1127
- */
1128
577
  forever(key, value) {
1129
578
  return this.store().forever(key, value);
1130
579
  }
1131
- /**
1132
- * Get an item from the cache, or execute the callback and store the result.
1133
- *
1134
- * Ensures the value is cached after the first miss. This provides an atomic-like
1135
- * "get or set" flow to prevent multiple concurrent fetches of the same data.
1136
- *
1137
- * @param key - Unique cache key.
1138
- * @param ttl - Duration to cache the result if a miss occurs.
1139
- * @param callback - Logic to execute to fetch fresh data.
1140
- * @returns Cached or freshly fetched value.
1141
- * @throws {Error} If the callback fails or the store write operation errors.
1142
- *
1143
- * @example
1144
- * ```typescript
1145
- * const user = await cache.remember('user:1', 60, async () => {
1146
- * return await db.findUser(1);
1147
- * });
1148
- * ```
1149
- */
1150
580
  remember(key, ttl, callback) {
1151
581
  return this.store().remember(key, ttl, callback);
1152
582
  }
1153
- /**
1154
- * Get an item from the cache, or execute the callback and store the result forever.
1155
- *
1156
- * @param key - The unique cache key.
1157
- * @param callback - The closure to execute to fetch the fresh data.
1158
- * @returns The cached or freshly fetched value.
1159
- * @throws {Error} If the callback throws or the store write fails.
1160
- *
1161
- * @example
1162
- * ```typescript
1163
- * const settings = await cache.rememberForever('global:settings', () => {
1164
- * return fetchSettingsFromApi();
1165
- * });
1166
- * ```
1167
- */
1168
583
  rememberForever(key, callback) {
1169
584
  return this.store().rememberForever(key, callback);
1170
585
  }
1171
- /**
1172
- * Retrieve multiple items from the default cache store by their keys.
1173
- *
1174
- * @param keys - An array of unique cache keys.
1175
- * @returns An object mapping keys to their cached values (or null if missing).
1176
- * @throws {Error} If the underlying store driver encounters a read error.
1177
- *
1178
- * @example
1179
- * ```typescript
1180
- * const values = await cache.many(['key1', 'key2']);
1181
- * ```
1182
- */
1183
586
  many(keys) {
1184
587
  return this.store().many(keys);
1185
588
  }
1186
- /**
1187
- * Store multiple items in the default cache store for a specific duration.
1188
- *
1189
- * @param values - An object mapping keys to the values to be stored.
1190
- * @param ttl - Expiration time in seconds or a specific Date.
1191
- * @returns A promise that resolves when all writes are complete.
1192
- * @throws {Error} If the underlying store driver encounters a write error.
1193
- *
1194
- * @example
1195
- * ```typescript
1196
- * await cache.putMany({ a: 1, b: 2 }, 60);
1197
- * ```
1198
- */
1199
589
  putMany(values, ttl) {
1200
590
  return this.store().putMany(values, ttl);
1201
591
  }
1202
- /**
1203
- * Get an item from the cache, allowing stale data while refreshing in background.
1204
- *
1205
- * Implements the Stale-While-Revalidate pattern to minimize latency for
1206
- * frequently accessed but expensive data.
1207
- *
1208
- * @param key - The unique cache key.
1209
- * @param ttlSeconds - How long the value is considered fresh.
1210
- * @param staleSeconds - How long to serve stale data while refreshing.
1211
- * @param callback - The closure to execute to refresh the data.
1212
- * @returns The cached (possibly stale) or freshly fetched value.
1213
- * @throws {Error} If the callback throws during an initial fetch.
1214
- *
1215
- * @example
1216
- * ```typescript
1217
- * const data = await cache.flexible('stats', 60, 30, () => fetchStats());
1218
- * ```
1219
- */
1220
592
  flexible(key, ttlSeconds, staleSeconds, callback) {
1221
593
  return this.store().flexible(key, ttlSeconds, staleSeconds, callback);
1222
594
  }
1223
- /**
1224
- * Retrieve an item from the default cache store and then delete it.
1225
- *
1226
- * Useful for one-time notifications or temporary tokens.
1227
- *
1228
- * @param key - The unique cache key.
1229
- * @param defaultValue - Fallback value if the key is missing.
1230
- * @returns The cached value before deletion, or the default.
1231
- * @throws {Error} If the underlying store driver encounters a read or delete error.
1232
- *
1233
- * @example
1234
- * ```typescript
1235
- * const token = await cache.pull('temp_token');
1236
- * ```
1237
- */
1238
595
  pull(key, defaultValue) {
1239
596
  return this.store().pull(key, defaultValue);
1240
597
  }
1241
- /**
1242
- * Remove an item from the default cache store.
1243
- *
1244
- * @param key - The unique cache key.
1245
- * @returns True if the item was removed, false otherwise.
1246
- * @throws {Error} If the underlying store driver encounters a delete error.
1247
- *
1248
- * @example
1249
- * ```typescript
1250
- * await cache.forget('user:1');
1251
- * ```
1252
- */
1253
598
  forget(key) {
1254
599
  return this.store().forget(key);
1255
600
  }
1256
- /**
1257
- * Remove an item from the default cache store (alias for forget).
1258
- *
1259
- * @param key - The unique cache key.
1260
- * @returns True if the item was removed, false otherwise.
1261
- * @throws {Error} If the underlying store driver encounters a delete error.
1262
- *
1263
- * @example
1264
- * ```typescript
1265
- * await cache.delete('old_key');
1266
- * ```
1267
- */
1268
601
  delete(key) {
1269
602
  return this.store().delete(key);
1270
603
  }
1271
- /**
1272
- * Remove all items from the default cache store.
1273
- *
1274
- * @returns A promise that resolves when the flush is complete.
1275
- * @throws {Error} If the underlying store driver encounters a flush error.
1276
- *
1277
- * @example
1278
- * ```typescript
1279
- * await cache.flush();
1280
- * ```
1281
- */
1282
604
  flush() {
1283
605
  return this.store().flush();
1284
606
  }
1285
- /**
1286
- * Clear the entire default cache store (alias for flush).
1287
- *
1288
- * @returns A promise that resolves when the clear is complete.
1289
- * @throws {Error} If the underlying store driver encounters a clear error.
1290
- *
1291
- * @example
1292
- * ```typescript
1293
- * await cache.clear();
1294
- * ```
1295
- */
1296
607
  clear() {
1297
608
  return this.store().clear();
1298
609
  }
1299
- /**
1300
- * Increment the value of an integer item in the default cache store.
1301
- *
1302
- * @param key - Unique cache key.
1303
- * @param value - Amount to add.
1304
- * @returns New value after incrementing.
1305
- * @throws {Error} If existing value is not a number or the store is read-only.
1306
- *
1307
- * @example
1308
- * ```typescript
1309
- * const count = await cache.increment('page_views');
1310
- * ```
1311
- */
1312
610
  increment(key, value) {
1313
611
  return this.store().increment(key, value);
1314
612
  }
1315
- /**
1316
- * Decrement the value of an integer item in the default cache store.
1317
- *
1318
- * @param key - Unique cache key.
1319
- * @param value - Amount to subtract.
1320
- * @returns New value after decrementing.
1321
- * @throws {Error} If existing value is not a number or the store is read-only.
1322
- *
1323
- * @example
1324
- * ```typescript
1325
- * const remaining = await cache.decrement('stock:1');
1326
- * ```
1327
- */
1328
613
  decrement(key, value) {
1329
614
  return this.store().decrement(key, value);
1330
615
  }
1331
- /**
1332
- * Get a distributed lock instance from the default cache store.
1333
- *
1334
- * @param name - The unique name of the lock.
1335
- * @param seconds - The duration the lock should be held for.
1336
- * @returns A CacheLock instance.
1337
- * @throws {Error} If the underlying store does not support locking.
1338
- *
1339
- * @example
1340
- * ```typescript
1341
- * const lock = cache.lock('process_data', 10);
1342
- * if (await lock.get()) {
1343
- * // ...
1344
- * await lock.release();
1345
- * }
1346
- * ```
1347
- */
1348
616
  lock(name, seconds) {
1349
617
  return this.store().lock(name, seconds);
1350
618
  }
1351
- /**
1352
- * Access a tagged cache section for grouped operations.
1353
- *
1354
- * Tags allow you to clear groups of cache entries simultaneously.
1355
- * Note: This is only supported by specific drivers like 'memory'.
1356
- *
1357
- * @param tags - An array of tag names.
1358
- * @returns A tagged cache repository instance.
1359
- * @throws {Error} If the underlying store driver does not support tags.
1360
- *
1361
- * @example
1362
- * ```typescript
1363
- * await cache.tags(['users', 'profiles']).put('user:1', data, 60);
1364
- * await cache.tags(['users']).flush(); // Clears all 'users' tagged entries
1365
- * ```
1366
- */
1367
619
  tags(tags) {
1368
620
  return this.store().tags(tags);
1369
621
  }
1370
- };
622
+ }
1371
623
 
1372
624
  // src/prediction/AccessPredictor.ts
1373
- var MarkovPredictor = class {
1374
- transitions = /* @__PURE__ */ new Map();
625
+ class MarkovPredictor {
626
+ transitions = new Map;
1375
627
  lastKey = null;
1376
628
  maxNodes;
1377
629
  maxEdgesPerNode;
1378
- /**
1379
- * Initialize a new MarkovPredictor.
1380
- *
1381
- * @param options - Limits for internal transition graph to manage memory.
1382
- */
1383
630
  constructor(options = {}) {
1384
- this.maxNodes = options.maxNodes ?? 1e3;
631
+ this.maxNodes = options.maxNodes ?? 1000;
1385
632
  this.maxEdgesPerNode = options.maxEdgesPerNode ?? 10;
1386
633
  }
1387
634
  record(key) {
@@ -1390,7 +637,7 @@ var MarkovPredictor = class {
1390
637
  if (this.transitions.size >= this.maxNodes) {
1391
638
  this.transitions.clear();
1392
639
  }
1393
- this.transitions.set(this.lastKey, /* @__PURE__ */ new Map());
640
+ this.transitions.set(this.lastKey, new Map);
1394
641
  }
1395
642
  const edges = this.transitions.get(this.lastKey);
1396
643
  const count = edges.get(key) ?? 0;
@@ -1404,36 +651,41 @@ var MarkovPredictor = class {
1404
651
  minKey = k;
1405
652
  }
1406
653
  }
1407
- if (minKey) edges.delete(minKey);
654
+ if (minKey) {
655
+ edges.delete(minKey);
656
+ }
1408
657
  }
1409
658
  }
1410
659
  this.lastKey = key;
1411
660
  }
1412
661
  predict(key) {
1413
662
  const edges = this.transitions.get(key);
1414
- if (!edges) return [];
663
+ if (!edges) {
664
+ return [];
665
+ }
1415
666
  return Array.from(edges.entries()).sort((a, b) => b[1] - a[1]).map((entry) => entry[0]);
1416
667
  }
1417
668
  reset() {
1418
669
  this.transitions.clear();
1419
670
  this.lastKey = null;
1420
671
  }
1421
- };
672
+ }
1422
673
 
1423
674
  // src/stores/CircuitBreakerStore.ts
1424
- var CircuitBreakerStore = class {
675
+ class CircuitBreakerStore {
676
+ primary;
677
+ state = "CLOSED";
678
+ failures = 0;
679
+ lastErrorTime = 0;
680
+ options;
1425
681
  constructor(primary, options = {}) {
1426
682
  this.primary = primary;
1427
683
  this.options = {
1428
684
  maxFailures: options.maxFailures ?? 5,
1429
- resetTimeout: options.resetTimeout ?? 6e4,
685
+ resetTimeout: options.resetTimeout ?? 60000,
1430
686
  fallback: options.fallback
1431
687
  };
1432
688
  }
1433
- state = "CLOSED";
1434
- failures = 0;
1435
- lastErrorTime = 0;
1436
- options;
1437
689
  async execute(operation, fallbackResult = null) {
1438
690
  if (this.state === "OPEN") {
1439
691
  if (Date.now() - this.lastErrorTime > this.options.resetTimeout) {
@@ -1466,8 +718,7 @@ var CircuitBreakerStore = class {
1466
718
  if (this.options.fallback) {
1467
719
  try {
1468
720
  return await operation(this.options.fallback);
1469
- } catch {
1470
- }
721
+ } catch {}
1471
722
  }
1472
723
  return fallbackResult;
1473
724
  }
@@ -1495,102 +746,71 @@ var CircuitBreakerStore = class {
1495
746
  async ttl(key) {
1496
747
  return this.execute(async (s) => s.ttl ? s.ttl(key) : null);
1497
748
  }
1498
- /**
1499
- * Returns current state for monitoring.
1500
- *
1501
- * @returns Current state of the circuit breaker.
1502
- *
1503
- * @example
1504
- * ```typescript
1505
- * const state = store.getState();
1506
- * if (state === 'OPEN') {
1507
- * console.warn('Primary cache is unavailable');
1508
- * }
1509
- * ```
1510
- */
1511
749
  getState() {
1512
750
  return this.state;
1513
751
  }
1514
- };
752
+ }
1515
753
 
1516
754
  // src/stores/FileStore.ts
1517
- import { createHash, randomUUID } from "crypto";
1518
- import { mkdir, open, readdir, readFile, rename, rm, stat, writeFile } from "fs/promises";
1519
- import { dirname, join } from "path";
1520
- var FileStore = class {
1521
- /**
1522
- * Initializes a new instance of the FileStore.
1523
- *
1524
- * @param options - Configuration settings for the store.
1525
- */
755
+ import { dirname, join } from "node:path";
756
+ import {
757
+ getRuntimeAdapter,
758
+ runtimeMkdir,
759
+ runtimeReadDir,
760
+ runtimeReadText,
761
+ runtimeRemoveRecursive,
762
+ runtimeRename,
763
+ runtimeStatFull,
764
+ runtimeWriteFileExclusive
765
+ } from "@gravito/core";
766
+ import { NativeHasher } from "@gravito/core/ffi";
767
+ class FileStore {
768
+ options;
769
+ cleanupTimer = null;
770
+ runtime = getRuntimeAdapter();
1526
771
  constructor(options) {
1527
772
  this.options = options;
1528
773
  if (options.enableCleanup !== false) {
1529
- this.startCleanupDaemon(options.cleanupInterval ?? 6e4);
774
+ this.startCleanupDaemon(options.cleanupInterval ?? 60000);
1530
775
  }
1531
776
  }
1532
- cleanupTimer = null;
1533
- /**
1534
- * Starts the background process for periodic cache maintenance.
1535
- *
1536
- * @param interval - Time between cleanup cycles in milliseconds.
1537
- * @internal
1538
- */
1539
777
  startCleanupDaemon(interval) {
1540
778
  this.cleanupTimer = setInterval(() => {
1541
- this.cleanExpiredFiles().catch(() => {
1542
- });
779
+ this.cleanExpiredFiles().catch(() => {});
1543
780
  }, interval);
1544
781
  if (this.cleanupTimer.unref) {
1545
782
  this.cleanupTimer.unref();
1546
783
  }
1547
784
  }
1548
- /**
1549
- * Scans the cache directory to remove expired files and enforce capacity limits.
1550
- *
1551
- * This method performs a recursive scan of the storage directory. It deletes
1552
- * files that have passed their expiration time and, if `maxFiles` is configured,
1553
- * evicts the oldest files to stay within the limit.
1554
- *
1555
- * @returns The total number of files removed during this operation.
1556
- * @throws {Error} If the directory cannot be read or files cannot be deleted.
1557
- *
1558
- * @example
1559
- * ```typescript
1560
- * const removedCount = await store.cleanExpiredFiles();
1561
- * console.log(`Cleaned up ${removedCount} files.`);
1562
- * ```
1563
- */
1564
785
  async cleanExpiredFiles() {
1565
786
  await this.ensureDir();
1566
787
  let cleaned = 0;
1567
788
  const validFiles = [];
1568
789
  const scanDir = async (dir) => {
1569
- const entries = await readdir(dir, { withFileTypes: true });
790
+ const entries = await runtimeReadDir(this.runtime, dir);
1570
791
  for (const entry of entries) {
1571
792
  const fullPath = join(dir, entry.name);
1572
- if (entry.isDirectory()) {
793
+ if (entry.isDirectory) {
1573
794
  await scanDir(fullPath);
1574
795
  try {
1575
- await rm(fullPath, { recursive: false });
1576
- } catch {
1577
- }
1578
- } else if (entry.isFile()) {
796
+ const { rmdir } = await import("node:fs/promises");
797
+ await rmdir(fullPath);
798
+ } catch {}
799
+ } else if (entry.isFile) {
1579
800
  if (!entry.name.endsWith(".json") || entry.name.startsWith(".lock-")) {
1580
801
  continue;
1581
802
  }
1582
803
  try {
1583
- const raw = await readFile(fullPath, "utf8");
804
+ const raw = await runtimeReadText(this.runtime, fullPath);
1584
805
  const data = JSON.parse(raw);
1585
806
  if (isExpired(data.expiresAt)) {
1586
- await rm(fullPath, { force: true });
807
+ await runtimeRemoveRecursive(this.runtime, fullPath);
1587
808
  cleaned++;
1588
809
  } else if (this.options.maxFiles) {
1589
- const stats = await stat(fullPath);
810
+ const stats = await runtimeStatFull(this.runtime, fullPath);
1590
811
  validFiles.push({ path: fullPath, mtime: stats.mtimeMs });
1591
812
  }
1592
- } catch {
1593
- }
813
+ } catch {}
1594
814
  }
1595
815
  }
1596
816
  };
@@ -1598,52 +818,25 @@ var FileStore = class {
1598
818
  if (this.options.maxFiles && validFiles.length > this.options.maxFiles) {
1599
819
  validFiles.sort((a, b) => a.mtime - b.mtime);
1600
820
  const toRemove = validFiles.slice(0, validFiles.length - this.options.maxFiles);
1601
- await Promise.all(
1602
- toRemove.map(async (f) => {
1603
- try {
1604
- await rm(f.path, { force: true });
1605
- cleaned++;
1606
- } catch {
1607
- }
1608
- })
1609
- );
821
+ await Promise.all(toRemove.map(async (f) => {
822
+ try {
823
+ await runtimeRemoveRecursive(this.runtime, f.path);
824
+ cleaned++;
825
+ } catch {}
826
+ }));
1610
827
  }
1611
828
  return cleaned;
1612
829
  }
1613
- /**
1614
- * Stops the cleanup daemon and releases associated resources.
1615
- *
1616
- * Should be called when the store is no longer needed to prevent memory leaks
1617
- * and allow the process to exit gracefully.
1618
- *
1619
- * @example
1620
- * ```typescript
1621
- * await store.destroy();
1622
- * ```
1623
- */
1624
830
  async destroy() {
1625
831
  if (this.cleanupTimer) {
1626
832
  clearInterval(this.cleanupTimer);
1627
833
  this.cleanupTimer = null;
1628
834
  }
1629
835
  }
1630
- /**
1631
- * Ensures that the base storage directory exists.
1632
- *
1633
- * @throws {Error} If the directory cannot be created due to permissions or path conflicts.
1634
- * @internal
1635
- */
1636
836
  async ensureDir() {
1637
- await mkdir(this.options.directory, { recursive: true });
1638
- }
1639
- /**
1640
- * Resolves the filesystem path for a given cache key.
1641
- *
1642
- * @param key - Normalized cache key.
1643
- * @returns Absolute path to the JSON file representing the key.
1644
- * @internal
1645
- */
1646
- filePathForKey(key) {
837
+ await runtimeMkdir(this.runtime, this.options.directory, { recursive: true });
838
+ }
839
+ async filePathForKey(key) {
1647
840
  const hashed = hashKey(key);
1648
841
  if (this.options.useSubdirectories) {
1649
842
  const d1 = hashed.substring(0, 2);
@@ -1652,26 +845,12 @@ var FileStore = class {
1652
845
  }
1653
846
  return join(this.options.directory, `${hashed}.json`);
1654
847
  }
1655
- /**
1656
- * Retrieves an item from the cache by its key.
1657
- *
1658
- * If the item exists but has expired, it will be deleted and `null` will be returned.
1659
- *
1660
- * @param key - The unique identifier for the cache item.
1661
- * @returns The cached value, or `null` if not found or expired.
1662
- * @throws {Error} If the file exists but cannot be read or parsed.
1663
- *
1664
- * @example
1665
- * ```typescript
1666
- * const value = await store.get<string>('my-key');
1667
- * ```
1668
- */
1669
848
  async get(key) {
1670
849
  const normalized = normalizeCacheKey(key);
1671
850
  await this.ensureDir();
1672
- const file = this.filePathForKey(normalized);
851
+ const file = await this.filePathForKey(normalized);
1673
852
  try {
1674
- const raw = await readFile(file, "utf8");
853
+ const raw = await runtimeReadText(this.runtime, file);
1675
854
  const data = JSON.parse(raw);
1676
855
  if (isExpired(data.expiresAt)) {
1677
856
  await this.forget(normalized);
@@ -1682,58 +861,28 @@ var FileStore = class {
1682
861
  return null;
1683
862
  }
1684
863
  }
1685
- /**
1686
- * Stores an item in the cache with a specified expiration time.
1687
- *
1688
- * Uses an atomic write strategy (write to temp file then rename) to ensure
1689
- * data integrity even if the process crashes during the write operation.
1690
- *
1691
- * @param key - The unique identifier for the cache item.
1692
- * @param value - The data to be cached.
1693
- * @param ttl - Time-to-live in seconds or a Date object for absolute expiration.
1694
- * @throws {Error} If the file system is not writable or disk is full.
1695
- *
1696
- * @example
1697
- * ```typescript
1698
- * await store.put('settings', { theme: 'dark' }, 86400);
1699
- * ```
1700
- */
1701
864
  async put(key, value, ttl) {
1702
865
  const normalized = normalizeCacheKey(key);
1703
866
  await this.ensureDir();
1704
867
  const expiresAt = ttlToExpiresAt(ttl);
1705
- if (expiresAt !== null && expiresAt !== void 0 && expiresAt <= Date.now()) {
868
+ if (expiresAt !== null && expiresAt !== undefined && expiresAt <= Date.now()) {
1706
869
  await this.forget(normalized);
1707
870
  return;
1708
871
  }
1709
- const file = this.filePathForKey(normalized);
872
+ const file = await this.filePathForKey(normalized);
1710
873
  if (this.options.useSubdirectories) {
1711
- await mkdir(dirname(file), { recursive: true });
874
+ await runtimeMkdir(this.runtime, dirname(file), { recursive: true });
1712
875
  }
1713
- const tempFile = `${file}.tmp.${Date.now()}.${randomUUID()}`;
876
+ const tempFile = `${file}.tmp.${Date.now()}.${crypto.randomUUID()}`;
1714
877
  const payload = { expiresAt: expiresAt ?? null, value };
1715
878
  try {
1716
- await writeFile(tempFile, JSON.stringify(payload), "utf8");
1717
- await rename(tempFile, file);
879
+ await this.runtime.writeFile(tempFile, JSON.stringify(payload));
880
+ await runtimeRename(this.runtime, tempFile, file);
1718
881
  } catch (error) {
1719
- await rm(tempFile, { force: true }).catch(() => {
1720
- });
882
+ await runtimeRemoveRecursive(this.runtime, tempFile).catch(() => {});
1721
883
  throw error;
1722
884
  }
1723
885
  }
1724
- /**
1725
- * Stores an item in the cache only if it does not already exist.
1726
- *
1727
- * @param key - The unique identifier for the cache item.
1728
- * @param value - The data to be cached.
1729
- * @param ttl - Time-to-live in seconds or a Date object.
1730
- * @returns `true` if the item was stored, `false` if it already existed.
1731
- *
1732
- * @example
1733
- * ```typescript
1734
- * const success = await store.add('unique-task', { status: 'pending' }, 60);
1735
- * ```
1736
- */
1737
886
  async add(key, value, ttl) {
1738
887
  const normalized = normalizeCacheKey(key);
1739
888
  const existing = await this.get(normalized);
@@ -1743,61 +892,22 @@ var FileStore = class {
1743
892
  await this.put(normalized, value, ttl);
1744
893
  return true;
1745
894
  }
1746
- /**
1747
- * Removes an item from the cache by its key.
1748
- *
1749
- * @param key - The unique identifier for the cache item.
1750
- * @returns `true` if the file was deleted or didn't exist, `false` on failure.
1751
- *
1752
- * @example
1753
- * ```typescript
1754
- * await store.forget('my-key');
1755
- * ```
1756
- */
1757
895
  async forget(key) {
1758
896
  const normalized = normalizeCacheKey(key);
1759
897
  await this.ensureDir();
1760
- const file = this.filePathForKey(normalized);
898
+ const file = await this.filePathForKey(normalized);
1761
899
  try {
1762
- await rm(file, { force: true });
900
+ await runtimeRemoveRecursive(this.runtime, file);
1763
901
  return true;
1764
902
  } catch {
1765
903
  return false;
1766
904
  }
1767
905
  }
1768
- /**
1769
- * Removes all items from the cache directory.
1770
- *
1771
- * This operation deletes the entire cache directory and recreates it.
1772
- * Use with caution as it is destructive and non-reversible.
1773
- *
1774
- * @throws {Error} If the directory cannot be removed or recreated.
1775
- *
1776
- * @example
1777
- * ```typescript
1778
- * await store.flush();
1779
- * ```
1780
- */
1781
906
  async flush() {
1782
907
  await this.ensureDir();
1783
- await rm(this.options.directory, { recursive: true, force: true });
908
+ await runtimeRemoveRecursive(this.runtime, this.options.directory);
1784
909
  await this.ensureDir();
1785
910
  }
1786
- /**
1787
- * Increments the value of an integer item in the cache.
1788
- *
1789
- * If the key does not exist, it is initialized to 0 before incrementing.
1790
- *
1791
- * @param key - The unique identifier for the cache item.
1792
- * @param value - The amount to increment by.
1793
- * @returns The new value after incrementing.
1794
- * @throws {Error} If the existing value is not a number.
1795
- *
1796
- * @example
1797
- * ```typescript
1798
- * const newCount = await store.increment('page-views');
1799
- * ```
1800
- */
1801
911
  async increment(key, value = 1) {
1802
912
  const normalized = normalizeCacheKey(key);
1803
913
  const current = await this.get(normalized);
@@ -1805,77 +915,30 @@ var FileStore = class {
1805
915
  await this.put(normalized, next, null);
1806
916
  return next;
1807
917
  }
1808
- /**
1809
- * Decrements the value of an integer item in the cache.
1810
- *
1811
- * If the key does not exist, it is initialized to 0 before decrementing.
1812
- *
1813
- * @param key - The unique identifier for the cache item.
1814
- * @param value - The amount to decrement by.
1815
- * @returns The new value after decrementing.
1816
- * @throws {Error} If the existing value is not a number.
1817
- *
1818
- * @example
1819
- * ```typescript
1820
- * const newCount = await store.decrement('stock-level');
1821
- * ```
1822
- */
1823
918
  async decrement(key, value = 1) {
1824
919
  return this.increment(key, -value);
1825
920
  }
1826
- /**
1827
- * Retrieves the remaining time-to-live for a cache item in seconds.
1828
- *
1829
- * @param key - The unique identifier for the cache item.
1830
- * @returns The seconds remaining until expiration, or `null` if it never expires or doesn't exist.
1831
- *
1832
- * @example
1833
- * ```typescript
1834
- * const secondsLeft = await store.ttl('session:123');
1835
- * ```
1836
- */
1837
921
  async ttl(key) {
1838
922
  const normalized = normalizeCacheKey(key);
1839
- const file = this.filePathForKey(normalized);
923
+ const file = await this.filePathForKey(normalized);
1840
924
  try {
1841
- const raw = await readFile(file, "utf8");
925
+ const raw = await runtimeReadText(this.runtime, file);
1842
926
  const data = JSON.parse(raw);
1843
927
  if (data.expiresAt === null) {
1844
928
  return null;
1845
929
  }
1846
- const remaining = Math.ceil((data.expiresAt - Date.now()) / 1e3);
930
+ const remaining = Math.ceil((data.expiresAt - Date.now()) / 1000);
1847
931
  return remaining > 0 ? remaining : null;
1848
932
  } catch {
1849
933
  return null;
1850
934
  }
1851
935
  }
1852
- /**
1853
- * Creates a distributed lock instance based on the filesystem.
1854
- *
1855
- * Locks are implemented using atomic file creation (`wx` flag). They include
1856
- * protection against stale locks by checking process IDs and expiration times.
1857
- *
1858
- * @param name - The unique name of the lock.
1859
- * @param seconds - The duration in seconds for which the lock should be held.
1860
- * @returns A `CacheLock` instance for managing the lock lifecycle.
1861
- *
1862
- * @example
1863
- * ```typescript
1864
- * const lock = store.lock('process-report', 60);
1865
- * if (await lock.acquire()) {
1866
- * try {
1867
- * // Critical section
1868
- * } finally {
1869
- * await lock.release();
1870
- * }
1871
- * }
1872
- * ```
1873
- */
1874
936
  lock(name, seconds = 10) {
1875
937
  const normalizedName = normalizeCacheKey(name);
1876
- const lockFile = join(this.options.directory, `.lock-${hashKey(normalizedName)}`);
1877
- const ttlMillis = Math.max(1, seconds) * 1e3;
1878
- const owner = randomUUID();
938
+ const lockFile = join(this.options.directory, `.lock-${syncHashKey(normalizedName)}`);
939
+ const ttlMillis = Math.max(1, seconds) * 1000;
940
+ const owner = crypto.randomUUID();
941
+ const runtime = this.runtime;
1879
942
  const isProcessAlive = (pid) => {
1880
943
  try {
1881
944
  process.kill(pid, 0);
@@ -1887,62 +950,41 @@ var FileStore = class {
1887
950
  const tryAcquire = async () => {
1888
951
  await this.ensureDir();
1889
952
  try {
1890
- const handle = await open(lockFile, "wx");
1891
953
  const lockData = {
1892
954
  owner,
1893
955
  expiresAt: Date.now() + ttlMillis,
1894
956
  pid: process.pid
1895
957
  };
1896
- await handle.writeFile(JSON.stringify(lockData), "utf8");
1897
- await handle.close();
958
+ await runtimeWriteFileExclusive(runtime, lockFile, JSON.stringify(lockData));
1898
959
  return true;
1899
960
  } catch {
1900
961
  try {
1901
- const raw = await readFile(lockFile, "utf8");
962
+ const raw = await runtimeReadText(runtime, lockFile);
1902
963
  const data = JSON.parse(raw);
1903
- const isExpired2 = !data.expiresAt || Date.now() > data.expiresAt;
964
+ const isExpiredLock = !data.expiresAt || Date.now() > data.expiresAt;
1904
965
  const isProcessDead = data.pid && !isProcessAlive(data.pid);
1905
- if (isExpired2 || isProcessDead) {
1906
- await rm(lockFile, { force: true });
966
+ if (isExpiredLock || isProcessDead) {
967
+ await runtimeRemoveRecursive(runtime, lockFile);
1907
968
  }
1908
- } catch {
1909
- }
969
+ } catch {}
1910
970
  return false;
1911
971
  }
1912
972
  };
1913
973
  return {
1914
- /**
1915
- * Attempts to acquire the lock immediately.
1916
- *
1917
- * @returns `true` if the lock was successfully acquired, `false` otherwise.
1918
- */
1919
974
  async acquire() {
1920
975
  return tryAcquire();
1921
976
  },
1922
- /**
1923
- * Releases the lock if it is owned by the current instance.
1924
- */
1925
977
  async release() {
1926
978
  try {
1927
- const raw = await readFile(lockFile, "utf8");
979
+ const raw = await runtimeReadText(runtime, lockFile);
1928
980
  const data = JSON.parse(raw);
1929
981
  if (data.owner === owner) {
1930
- await rm(lockFile, { force: true });
982
+ await runtimeRemoveRecursive(runtime, lockFile);
1931
983
  }
1932
- } catch {
1933
- }
984
+ } catch {}
1934
985
  },
1935
- /**
1936
- * Executes a callback within the lock, waiting if necessary.
1937
- *
1938
- * @param secondsToWait - Maximum time to wait for the lock in seconds.
1939
- * @param callback - The function to execute once the lock is acquired.
1940
- * @param options - Polling configuration.
1941
- * @returns The result of the callback.
1942
- * @throws {LockTimeoutError} If the lock cannot be acquired within the wait time.
1943
- */
1944
986
  async block(secondsToWait, callback, options) {
1945
- const deadline = Date.now() + Math.max(0, secondsToWait) * 1e3;
987
+ const deadline = Date.now() + Math.max(0, secondsToWait) * 1000;
1946
988
  const sleepMillis = options?.sleepMillis ?? 150;
1947
989
  while (Date.now() <= deadline) {
1948
990
  if (await this.acquire()) {
@@ -1954,94 +996,52 @@ var FileStore = class {
1954
996
  }
1955
997
  await sleep(sleepMillis);
1956
998
  }
1957
- throw new LockTimeoutError(
1958
- `Failed to acquire lock '${name}' within ${secondsToWait} seconds.`
1959
- );
999
+ throw new LockTimeoutError(`Failed to acquire lock '${name}' within ${secondsToWait} seconds.`);
1960
1000
  }
1961
1001
  };
1962
1002
  }
1963
- };
1003
+ }
1964
1004
  function hashKey(key) {
1965
- return createHash("sha256").update(key).digest("hex");
1005
+ return NativeHasher.sha256(key);
1006
+ }
1007
+ function syncHashKey(key) {
1008
+ let hash = 5381;
1009
+ for (let i = 0;i < key.length; i++) {
1010
+ hash = (hash << 5) + hash ^ key.charCodeAt(i);
1011
+ hash = hash >>> 0;
1012
+ }
1013
+ return hash.toString(16).padStart(8, "0");
1966
1014
  }
1967
-
1968
- // src/stores/MemoryStore.ts
1969
- import { randomUUID as randomUUID2 } from "crypto";
1970
1015
 
1971
1016
  // src/utils/LRUCache.ts
1972
- var LRUCache = class {
1973
- /**
1974
- * Creates a new LRU cache instance.
1975
- *
1976
- * @param maxSize - The maximum number of items allowed in the cache. Set to 0 for unlimited.
1977
- * @param onEvict - Optional callback triggered when an item is evicted due to capacity limits.
1978
- */
1017
+ class LRUCache {
1018
+ maxSize;
1019
+ onEvict;
1020
+ map = new Map;
1021
+ head = null;
1022
+ tail = null;
1979
1023
  constructor(maxSize, onEvict) {
1980
1024
  this.maxSize = maxSize;
1981
1025
  this.onEvict = onEvict;
1982
1026
  }
1983
- map = /* @__PURE__ */ new Map();
1984
- head = null;
1985
- tail = null;
1986
- /**
1987
- * The current number of items stored in the cache.
1988
- */
1989
1027
  get size() {
1990
1028
  return this.map.size;
1991
1029
  }
1992
- /**
1993
- * Checks if a key exists in the cache without updating its access order.
1994
- *
1995
- * @param key - The identifier to look for.
1996
- * @returns True if the key exists, false otherwise.
1997
- */
1998
1030
  has(key) {
1999
1031
  return this.map.has(key);
2000
1032
  }
2001
- /**
2002
- * Retrieves an item from the cache and marks it as most recently used.
2003
- *
2004
- * @param key - The identifier of the item to retrieve.
2005
- * @returns The cached value, or undefined if not found.
2006
- *
2007
- * @example
2008
- * ```typescript
2009
- * const value = cache.get('my-key');
2010
- * ```
2011
- */
2012
1033
  get(key) {
2013
1034
  const node = this.map.get(key);
2014
1035
  if (!node) {
2015
- return void 0;
1036
+ return;
2016
1037
  }
2017
1038
  this.moveToHead(node);
2018
1039
  return node.value;
2019
1040
  }
2020
- /**
2021
- * Retrieves an item from the cache without updating its access order.
2022
- *
2023
- * Useful for inspecting the cache without affecting eviction priority.
2024
- *
2025
- * @param key - The identifier of the item to peek.
2026
- * @returns The cached value, or undefined if not found.
2027
- */
2028
1041
  peek(key) {
2029
1042
  const node = this.map.get(key);
2030
1043
  return node?.value;
2031
1044
  }
2032
- /**
2033
- * Adds or updates an item in the cache, marking it as most recently used.
2034
- *
2035
- * If the cache is at capacity, the least recently used item will be evicted.
2036
- *
2037
- * @param key - The identifier for the item.
2038
- * @param value - The data to store.
2039
- *
2040
- * @example
2041
- * ```typescript
2042
- * cache.set('user:1', { name: 'Alice' });
2043
- * ```
2044
- */
2045
1045
  set(key, value) {
2046
1046
  const existingNode = this.map.get(key);
2047
1047
  if (existingNode) {
@@ -2067,12 +1067,6 @@ var LRUCache = class {
2067
1067
  }
2068
1068
  this.map.set(key, newNode);
2069
1069
  }
2070
- /**
2071
- * Removes an item from the cache.
2072
- *
2073
- * @param key - The identifier of the item to remove.
2074
- * @returns True if the item was found and removed, false otherwise.
2075
- */
2076
1070
  delete(key) {
2077
1071
  const node = this.map.get(key);
2078
1072
  if (!node) {
@@ -2082,19 +1076,11 @@ var LRUCache = class {
2082
1076
  this.map.delete(key);
2083
1077
  return true;
2084
1078
  }
2085
- /**
2086
- * Removes all items from the cache.
2087
- */
2088
1079
  clear() {
2089
1080
  this.map.clear();
2090
1081
  this.head = null;
2091
1082
  this.tail = null;
2092
1083
  }
2093
- /**
2094
- * Moves a node to the head of the linked list (most recently used).
2095
- *
2096
- * @param node - The node to promote.
2097
- */
2098
1084
  moveToHead(node) {
2099
1085
  if (node === this.head) {
2100
1086
  return;
@@ -2115,11 +1101,6 @@ var LRUCache = class {
2115
1101
  }
2116
1102
  this.head = node;
2117
1103
  }
2118
- /**
2119
- * Removes a node from the linked list.
2120
- *
2121
- * @param node - The node to remove.
2122
- */
2123
1104
  removeNode(node) {
2124
1105
  if (node.prev) {
2125
1106
  node.prev.next = node.next;
@@ -2134,11 +1115,6 @@ var LRUCache = class {
2134
1115
  node.prev = null;
2135
1116
  node.next = null;
2136
1117
  }
2137
- /**
2138
- * Evicts the least recently used item (the tail of the list).
2139
- *
2140
- * Triggers the `onEvict` callback if provided.
2141
- */
2142
1118
  evict() {
2143
1119
  if (!this.tail) {
2144
1120
  return;
@@ -2150,37 +1126,21 @@ var LRUCache = class {
2150
1126
  this.removeNode(node);
2151
1127
  this.map.delete(node.key);
2152
1128
  }
2153
- };
1129
+ }
2154
1130
 
2155
1131
  // src/stores/MemoryStore.ts
2156
- var MemoryStore = class {
1132
+ class MemoryStore {
2157
1133
  entries;
2158
- locks = /* @__PURE__ */ new Map();
1134
+ locks = new Map;
2159
1135
  stats = { hits: 0, misses: 0, evictions: 0 };
2160
- tagToKeys = /* @__PURE__ */ new Map();
2161
- keyToTags = /* @__PURE__ */ new Map();
2162
- /**
2163
- * Creates a new MemoryStore instance.
2164
- *
2165
- * @param options - Configuration for capacity and eviction.
2166
- */
1136
+ tagToKeys = new Map;
1137
+ keyToTags = new Map;
2167
1138
  constructor(options = {}) {
2168
1139
  this.entries = new LRUCache(options.maxItems ?? 0, (key) => {
2169
1140
  this.tagIndexRemove(key);
2170
1141
  this.stats.evictions++;
2171
1142
  });
2172
1143
  }
2173
- /**
2174
- * Retrieves current performance metrics.
2175
- *
2176
- * @returns A snapshot of hits, misses, size, and eviction counts.
2177
- *
2178
- * @example
2179
- * ```typescript
2180
- * const stats = store.getStats();
2181
- * console.log(`Cache hit rate: ${stats.hitRate * 100}%`);
2182
- * ```
2183
- */
2184
1144
  getStats() {
2185
1145
  const total = this.stats.hits + this.stats.misses;
2186
1146
  return {
@@ -2197,22 +1157,9 @@ var MemoryStore = class {
2197
1157
  return;
2198
1158
  }
2199
1159
  if (isExpired(entry.expiresAt, now)) {
2200
- void this.forget(key);
2201
- }
2202
- }
2203
- /**
2204
- * Retrieves an item from the cache by its key.
2205
- *
2206
- * If the item is expired, it will be automatically removed and `null` will be returned.
2207
- *
2208
- * @param key - The unique identifier for the cached item.
2209
- * @returns The cached value, or `null` if not found or expired.
2210
- *
2211
- * @example
2212
- * ```typescript
2213
- * const value = await store.get('my-key');
2214
- * ```
2215
- */
1160
+ this.forget(key);
1161
+ }
1162
+ }
2216
1163
  async get(key) {
2217
1164
  const normalized = normalizeCacheKey(key);
2218
1165
  const entry = this.entries.get(normalized);
@@ -2228,42 +1175,15 @@ var MemoryStore = class {
2228
1175
  this.stats.hits++;
2229
1176
  return entry.value;
2230
1177
  }
2231
- /**
2232
- * Stores an item in the cache with a specific TTL.
2233
- *
2234
- * If the key already exists, it will be overwritten.
2235
- *
2236
- * @param key - The unique identifier for the item.
2237
- * @param value - The data to store.
2238
- * @param ttl - Time-to-live in seconds, or a Date object for absolute expiration.
2239
- *
2240
- * @example
2241
- * ```typescript
2242
- * await store.put('settings', { theme: 'dark' }, 3600);
2243
- * ```
2244
- */
2245
1178
  async put(key, value, ttl) {
2246
1179
  const normalized = normalizeCacheKey(key);
2247
1180
  const expiresAt = ttlToExpiresAt(ttl);
2248
- if (expiresAt !== null && expiresAt !== void 0 && expiresAt <= Date.now()) {
1181
+ if (expiresAt !== null && expiresAt !== undefined && expiresAt <= Date.now()) {
2249
1182
  await this.forget(normalized);
2250
1183
  return;
2251
1184
  }
2252
1185
  this.entries.set(normalized, { value, expiresAt: expiresAt ?? null });
2253
1186
  }
2254
- /**
2255
- * Stores an item only if it does not already exist in the cache.
2256
- *
2257
- * @param key - The unique identifier for the item.
2258
- * @param value - The data to store.
2259
- * @param ttl - Time-to-live in seconds or absolute expiration.
2260
- * @returns `true` if the item was added, `false` if it already existed.
2261
- *
2262
- * @example
2263
- * ```typescript
2264
- * const added = await store.add('unique-task', data, 60);
2265
- * ```
2266
- */
2267
1187
  async add(key, value, ttl) {
2268
1188
  const normalized = normalizeCacheKey(key);
2269
1189
  this.cleanupExpired(normalized);
@@ -2273,50 +1193,17 @@ var MemoryStore = class {
2273
1193
  await this.put(normalized, value, ttl);
2274
1194
  return true;
2275
1195
  }
2276
- /**
2277
- * Removes an item from the cache.
2278
- *
2279
- * @param key - The unique identifier for the item to remove.
2280
- * @returns `true` if the item existed and was removed, `false` otherwise.
2281
- *
2282
- * @example
2283
- * ```typescript
2284
- * await store.forget('user:session');
2285
- * ```
2286
- */
2287
1196
  async forget(key) {
2288
1197
  const normalized = normalizeCacheKey(key);
2289
1198
  const existed = this.entries.delete(normalized);
2290
1199
  this.tagIndexRemove(normalized);
2291
1200
  return existed;
2292
1201
  }
2293
- /**
2294
- * Removes all items from the cache and resets all internal indexes.
2295
- *
2296
- * @example
2297
- * ```typescript
2298
- * await store.flush();
2299
- * ```
2300
- */
2301
1202
  async flush() {
2302
1203
  this.entries.clear();
2303
1204
  this.tagToKeys.clear();
2304
1205
  this.keyToTags.clear();
2305
1206
  }
2306
- /**
2307
- * Increments the value of an item in the cache.
2308
- *
2309
- * If the key does not exist, it starts from 0.
2310
- *
2311
- * @param key - The identifier for the numeric value.
2312
- * @param value - The amount to increment by (defaults to 1).
2313
- * @returns The new incremented value.
2314
- *
2315
- * @example
2316
- * ```typescript
2317
- * const count = await store.increment('page_views');
2318
- * ```
2319
- */
2320
1207
  async increment(key, value = 1) {
2321
1208
  const normalized = normalizeCacheKey(key);
2322
1209
  const current = await this.get(normalized);
@@ -2324,32 +1211,9 @@ var MemoryStore = class {
2324
1211
  await this.put(normalized, next, null);
2325
1212
  return next;
2326
1213
  }
2327
- /**
2328
- * Decrements the value of an item in the cache.
2329
- *
2330
- * @param key - The identifier for the numeric value.
2331
- * @param value - The amount to decrement by (defaults to 1).
2332
- * @returns The new decremented value.
2333
- *
2334
- * @example
2335
- * ```typescript
2336
- * const remaining = await store.decrement('stock_count', 5);
2337
- * ```
2338
- */
2339
1214
  async decrement(key, value = 1) {
2340
1215
  return this.increment(key, -value);
2341
1216
  }
2342
- /**
2343
- * Gets the remaining time-to-live for a cached item.
2344
- *
2345
- * @param key - The identifier for the cached item.
2346
- * @returns Remaining seconds, or `null` if the item has no expiration or does not exist.
2347
- *
2348
- * @example
2349
- * ```typescript
2350
- * const secondsLeft = await store.ttl('token');
2351
- * ```
2352
- */
2353
1217
  async ttl(key) {
2354
1218
  const normalized = normalizeCacheKey(key);
2355
1219
  const entry = this.entries.peek(normalized);
@@ -2361,30 +1225,11 @@ var MemoryStore = class {
2361
1225
  await this.forget(normalized);
2362
1226
  return null;
2363
1227
  }
2364
- return Math.max(0, Math.ceil((entry.expiresAt - now) / 1e3));
2365
- }
2366
- /**
2367
- * Creates a lock instance for managing exclusive access to a resource.
2368
- *
2369
- * @param name - The name of the lock.
2370
- * @param seconds - The duration the lock should be held (defaults to 10).
2371
- * @returns A `CacheLock` instance.
2372
- *
2373
- * @example
2374
- * ```typescript
2375
- * const lock = store.lock('process-report', 30);
2376
- * if (await lock.acquire()) {
2377
- * try {
2378
- * // Critical section
2379
- * } finally {
2380
- * await lock.release();
2381
- * }
2382
- * }
2383
- * ```
2384
- */
1228
+ return Math.max(0, Math.ceil((entry.expiresAt - now) / 1000));
1229
+ }
2385
1230
  lock(name, seconds = 10) {
2386
1231
  const lockKey = `lock:${normalizeCacheKey(name)}`;
2387
- const ttlMillis = Math.max(1, seconds) * 1e3;
1232
+ const ttlMillis = Math.max(1, seconds) * 1000;
2388
1233
  const locks = this.locks;
2389
1234
  const acquire = async () => {
2390
1235
  const now = Date.now();
@@ -2392,17 +1237,12 @@ var MemoryStore = class {
2392
1237
  if (existing && existing.expiresAt > now) {
2393
1238
  return { ok: false };
2394
1239
  }
2395
- const owner2 = randomUUID2();
1240
+ const owner2 = crypto.randomUUID();
2396
1241
  locks.set(lockKey, { owner: owner2, expiresAt: now + ttlMillis });
2397
1242
  return { ok: true, owner: owner2 };
2398
1243
  };
2399
1244
  let owner;
2400
1245
  return {
2401
- /**
2402
- * Attempts to acquire the lock.
2403
- *
2404
- * @returns `true` if acquired, `false` if already held by another process.
2405
- */
2406
1246
  async acquire() {
2407
1247
  const result = await acquire();
2408
1248
  if (!result.ok) {
@@ -2411,9 +1251,6 @@ var MemoryStore = class {
2411
1251
  owner = result.owner;
2412
1252
  return true;
2413
1253
  },
2414
- /**
2415
- * Releases the lock if it is held by the current owner.
2416
- */
2417
1254
  async release() {
2418
1255
  if (!owner) {
2419
1256
  return;
@@ -2422,26 +1259,10 @@ var MemoryStore = class {
2422
1259
  if (existing?.owner === owner) {
2423
1260
  locks.delete(lockKey);
2424
1261
  }
2425
- owner = void 0;
1262
+ owner = undefined;
2426
1263
  },
2427
- /**
2428
- * Attempts to acquire the lock and execute a callback, waiting if necessary.
2429
- *
2430
- * @param secondsToWait - How long to wait for the lock before timing out.
2431
- * @param callback - The logic to execute while holding the lock.
2432
- * @param options - Polling configuration.
2433
- * @returns The result of the callback.
2434
- * @throws {LockTimeoutError} If the lock cannot be acquired within the wait time.
2435
- *
2436
- * @example
2437
- * ```typescript
2438
- * await lock.block(5, async () => {
2439
- * // This code runs exclusively
2440
- * });
2441
- * ```
2442
- */
2443
1264
  async block(secondsToWait, callback, options) {
2444
- const deadline = Date.now() + Math.max(0, secondsToWait) * 1e3;
1265
+ const deadline = Date.now() + Math.max(0, secondsToWait) * 1000;
2445
1266
  const sleepMillis = options?.sleepMillis ?? 150;
2446
1267
  while (Date.now() <= deadline) {
2447
1268
  if (await this.acquire()) {
@@ -2453,21 +1274,10 @@ var MemoryStore = class {
2453
1274
  }
2454
1275
  await sleep(sleepMillis);
2455
1276
  }
2456
- throw new LockTimeoutError(
2457
- `Failed to acquire lock '${name}' within ${secondsToWait} seconds.`
2458
- );
1277
+ throw new LockTimeoutError(`Failed to acquire lock '${name}' within ${secondsToWait} seconds.`);
2459
1278
  }
2460
1279
  };
2461
1280
  }
2462
- /**
2463
- * Generates a tagged key for storage.
2464
- *
2465
- * Used internally to prefix keys with their associated tags.
2466
- *
2467
- * @param key - The original cache key.
2468
- * @param tags - List of tags to associate with the key.
2469
- * @returns A formatted string containing tags and the key.
2470
- */
2471
1281
  tagKey(key, tags) {
2472
1282
  const normalizedKey = normalizeCacheKey(key);
2473
1283
  const normalizedTags = [...tags].map(String).filter(Boolean).sort();
@@ -2476,12 +1286,6 @@ var MemoryStore = class {
2476
1286
  }
2477
1287
  return `tags:${normalizedTags.join("|")}:${normalizedKey}`;
2478
1288
  }
2479
- /**
2480
- * Indexes a tagged key for bulk invalidation.
2481
- *
2482
- * @param tags - The tags to index.
2483
- * @param taggedKey - The full key (including tag prefix) to store.
2484
- */
2485
1289
  tagIndexAdd(tags, taggedKey) {
2486
1290
  const normalizedTags = [...tags].map(String).filter(Boolean);
2487
1291
  if (normalizedTags.length === 0) {
@@ -2490,25 +1294,20 @@ var MemoryStore = class {
2490
1294
  for (const tag of normalizedTags) {
2491
1295
  let keys = this.tagToKeys.get(tag);
2492
1296
  if (!keys) {
2493
- keys = /* @__PURE__ */ new Set();
1297
+ keys = new Set;
2494
1298
  this.tagToKeys.set(tag, keys);
2495
1299
  }
2496
1300
  keys.add(taggedKey);
2497
1301
  }
2498
1302
  let tagSet = this.keyToTags.get(taggedKey);
2499
1303
  if (!tagSet) {
2500
- tagSet = /* @__PURE__ */ new Set();
1304
+ tagSet = new Set;
2501
1305
  this.keyToTags.set(taggedKey, tagSet);
2502
1306
  }
2503
1307
  for (const tag of normalizedTags) {
2504
1308
  tagSet.add(tag);
2505
1309
  }
2506
1310
  }
2507
- /**
2508
- * Removes a key from the tag indexes.
2509
- *
2510
- * @param taggedKey - The key to remove from all tag sets.
2511
- */
2512
1311
  tagIndexRemove(taggedKey) {
2513
1312
  const tags = this.keyToTags.get(taggedKey);
2514
1313
  if (!tags) {
@@ -2526,22 +1325,12 @@ var MemoryStore = class {
2526
1325
  }
2527
1326
  this.keyToTags.delete(taggedKey);
2528
1327
  }
2529
- /**
2530
- * Invalidates all cache entries associated with any of the given tags.
2531
- *
2532
- * @param tags - The tags to flush.
2533
- *
2534
- * @example
2535
- * ```typescript
2536
- * await store.flushTags(['users', 'profiles']);
2537
- * ```
2538
- */
2539
1328
  async flushTags(tags) {
2540
1329
  const normalizedTags = [...tags].map(String).filter(Boolean);
2541
1330
  if (normalizedTags.length === 0) {
2542
1331
  return;
2543
1332
  }
2544
- const keysToDelete = /* @__PURE__ */ new Set();
1333
+ const keysToDelete = new Set;
2545
1334
  for (const tag of normalizedTags) {
2546
1335
  const keys = this.tagToKeys.get(tag);
2547
1336
  if (!keys) {
@@ -2555,131 +1344,42 @@ var MemoryStore = class {
2555
1344
  await this.forget(key);
2556
1345
  }
2557
1346
  }
2558
- };
1347
+ }
2559
1348
 
2560
1349
  // src/stores/NullStore.ts
2561
- var NullStore = class {
2562
- /**
2563
- * Simulates a cache miss for any given key.
2564
- *
2565
- * @param _key - Identifier for the cached item.
2566
- * @returns Always `null` regardless of requested key.
2567
- *
2568
- * @example
2569
- * ```typescript
2570
- * const value = await store.get('my-key');
2571
- * ```
2572
- */
1350
+ class NullStore {
2573
1351
  async get(_key) {
2574
1352
  return null;
2575
1353
  }
2576
- /**
2577
- * Discards the provided value instead of storing it.
2578
- *
2579
- * @param _key - The identifier for the item.
2580
- * @param _value - The data to be cached.
2581
- * @param _ttl - Time-to-live in seconds.
2582
- * @returns Resolves immediately after discarding the data.
2583
- *
2584
- * @example
2585
- * ```typescript
2586
- * await store.put('user:1', { id: 1 }, 3600);
2587
- * ```
2588
- */
2589
- async put(_key, _value, _ttl) {
2590
- }
2591
- /**
2592
- * Simulates a failed attempt to add an item to the cache.
2593
- *
2594
- * Since NullStore does not store data, this method always indicates that
2595
- * the item was not added.
2596
- *
2597
- * @param _key - The identifier for the item.
2598
- * @param _value - The data to be cached.
2599
- * @param _ttl - Time-to-live in seconds.
2600
- * @returns Always returns `false`.
2601
- *
2602
- * @example
2603
- * ```typescript
2604
- * const added = await store.add('key', 'value', 60); // false
2605
- * ```
2606
- */
1354
+ async put(_key, _value, _ttl) {}
2607
1355
  async add(_key, _value, _ttl) {
2608
1356
  return false;
2609
1357
  }
2610
- /**
2611
- * Simulates a failed attempt to remove an item from the cache.
2612
- *
2613
- * Since no data is ever stored, there is nothing to remove.
2614
- *
2615
- * @param _key - The identifier for the item to remove.
2616
- * @returns Always returns `false`.
2617
- *
2618
- * @example
2619
- * ```typescript
2620
- * const forgotten = await store.forget('key'); // false
2621
- * ```
2622
- */
2623
1358
  async forget(_key) {
2624
1359
  return false;
2625
1360
  }
2626
- /**
2627
- * Performs a no-op flush operation.
2628
- *
2629
- * @returns Resolves immediately as there is no data to clear.
2630
- *
2631
- * @example
2632
- * ```typescript
2633
- * await store.flush();
2634
- * ```
2635
- */
2636
- async flush() {
2637
- }
2638
- /**
2639
- * Simulates an increment operation on a non-existent key.
2640
- *
2641
- * @param _key - The identifier for the numeric item.
2642
- * @param _value - The amount to increment by.
2643
- * @returns Always returns `0`.
2644
- *
2645
- * @example
2646
- * ```typescript
2647
- * const newValue = await store.increment('counter', 1); // 0
2648
- * ```
2649
- */
1361
+ async flush() {}
2650
1362
  async increment(_key, _value = 1) {
2651
1363
  return 0;
2652
1364
  }
2653
- /**
2654
- * Simulates a decrement operation on a non-existent key.
2655
- *
2656
- * @param _key - The identifier for the numeric item.
2657
- * @param _value - The amount to decrement by.
2658
- * @returns Always returns `0`.
2659
- *
2660
- * @example
2661
- * ```typescript
2662
- * const newValue = await store.decrement('counter', 1); // 0
2663
- * ```
2664
- */
2665
1365
  async decrement(_key, _value = 1) {
2666
1366
  return 0;
2667
1367
  }
2668
- };
1368
+ }
2669
1369
 
2670
1370
  // src/stores/PredictiveStore.ts
2671
- var PredictiveStore = class {
1371
+ class PredictiveStore {
1372
+ store;
1373
+ predictor;
2672
1374
  constructor(store, options = {}) {
2673
1375
  this.store = store;
2674
- this.predictor = options.predictor ?? new MarkovPredictor();
1376
+ this.predictor = options.predictor ?? new MarkovPredictor;
2675
1377
  }
2676
- predictor;
2677
1378
  async get(key) {
2678
1379
  this.predictor.record(key);
2679
1380
  const candidates = this.predictor.predict(key);
2680
1381
  if (candidates.length > 0) {
2681
- void Promise.all(candidates.map((k) => this.store.get(k).catch(() => {
2682
- })));
1382
+ Promise.all(candidates.map((k) => this.store.get(k).catch(() => {})));
2683
1383
  }
2684
1384
  return this.store.get(key);
2685
1385
  }
@@ -2705,41 +1405,23 @@ var PredictiveStore = class {
2705
1405
  return this.store.decrement(key, value);
2706
1406
  }
2707
1407
  lock(name, seconds) {
2708
- return this.store.lock ? this.store.lock(name, seconds) : void 0;
1408
+ return this.store.lock ? this.store.lock(name, seconds) : undefined;
2709
1409
  }
2710
1410
  async ttl(key) {
2711
1411
  return this.store.ttl ? this.store.ttl(key) : null;
2712
1412
  }
2713
- };
1413
+ }
2714
1414
 
2715
1415
  // src/stores/RedisStore.ts
2716
- import { randomUUID as randomUUID3 } from "crypto";
2717
1416
  import { Redis } from "@gravito/plasma";
2718
- var RedisStore = class {
1417
+ class RedisStore {
2719
1418
  connectionName;
2720
- /**
2721
- * Initialize a new RedisStore instance.
2722
- *
2723
- * @param options - Redis connection and prefix settings.
2724
- *
2725
- * @example
2726
- * ```typescript
2727
- * const store = new RedisStore({ prefix: 'app:' });
2728
- * ```
2729
- */
2730
1419
  constructor(options = {}) {
2731
1420
  this.connectionName = options.connection;
2732
1421
  }
2733
1422
  get client() {
2734
1423
  return Redis.connection(this.connectionName);
2735
1424
  }
2736
- /**
2737
- * Retrieve an item from Redis.
2738
- *
2739
- * @param key - Unique cache key identifier.
2740
- * @returns Parsed JSON value or null if missing/expired.
2741
- * @throws {Error} If Redis connection fails or read errors occur.
2742
- */
2743
1425
  async get(key) {
2744
1426
  const normalized = normalizeCacheKey(key);
2745
1427
  const value = await this.client.get(normalized);
@@ -2752,14 +1434,6 @@ var RedisStore = class {
2752
1434
  return value;
2753
1435
  }
2754
1436
  }
2755
- /**
2756
- * Store an item in Redis.
2757
- *
2758
- * @param key - Unique cache key identifier.
2759
- * @param value - Value to serialize and store.
2760
- * @param ttl - Expiration duration.
2761
- * @throws {Error} If Redis connection fails or write errors occur.
2762
- */
2763
1437
  async put(key, value, ttl) {
2764
1438
  const normalized = normalizeCacheKey(key);
2765
1439
  const serialized = JSON.stringify(value);
@@ -2810,14 +1484,6 @@ var RedisStore = class {
2810
1484
  }
2811
1485
  return await this.client.incrby(normalized, value);
2812
1486
  }
2813
- /**
2814
- * Decrement a numeric value in Redis.
2815
- *
2816
- * @param key - Unique cache key identifier.
2817
- * @param value - Amount to subtract.
2818
- * @returns Updated numeric value.
2819
- * @throws {Error} If key is not numeric or Redis errors occur.
2820
- */
2821
1487
  async decrement(key, value = 1) {
2822
1488
  const normalized = normalizeCacheKey(key);
2823
1489
  if (value === 1) {
@@ -2825,9 +1491,6 @@ var RedisStore = class {
2825
1491
  }
2826
1492
  return await this.client.decrby(normalized, value);
2827
1493
  }
2828
- // ============================================================================
2829
- // Tags
2830
- // ============================================================================
2831
1494
  tagKey(key, _tags) {
2832
1495
  return key;
2833
1496
  }
@@ -2866,7 +1529,7 @@ var RedisStore = class {
2866
1529
  pipeline.smembers(tagKey);
2867
1530
  }
2868
1531
  const results = await pipeline.exec();
2869
- const keysToDelete = /* @__PURE__ */ new Set();
1532
+ const keysToDelete = new Set;
2870
1533
  for (const [err, keys] of results) {
2871
1534
  if (!err && Array.isArray(keys)) {
2872
1535
  for (const k of keys) {
@@ -2879,9 +1542,6 @@ var RedisStore = class {
2879
1542
  }
2880
1543
  await this.client.del(...tagKeys);
2881
1544
  }
2882
- // ============================================================================
2883
- // Locks
2884
- // ============================================================================
2885
1545
  async ttl(key) {
2886
1546
  const normalized = normalizeCacheKey(key);
2887
1547
  const result = await this.client.ttl(normalized);
@@ -2889,8 +1549,8 @@ var RedisStore = class {
2889
1549
  }
2890
1550
  lock(name, seconds = 10) {
2891
1551
  const lockKey = `lock:${normalizeCacheKey(name)}`;
2892
- const owner = randomUUID3();
2893
- const ttlMs = Math.max(1, seconds) * 1e3;
1552
+ const owner = crypto.randomUUID();
1553
+ const ttlMs = Math.max(1, seconds) * 1000;
2894
1554
  const client = this.client;
2895
1555
  return {
2896
1556
  async acquire() {
@@ -2919,13 +1579,7 @@ var RedisStore = class {
2919
1579
  end
2920
1580
  `;
2921
1581
  const evalClient = client;
2922
- const result = await evalClient.eval(
2923
- luaScript,
2924
- 1,
2925
- lockKey,
2926
- owner,
2927
- extensionSeconds.toString()
2928
- );
1582
+ const result = await evalClient.eval(luaScript, 1, lockKey, owner, extensionSeconds.toString());
2929
1583
  return result === 1;
2930
1584
  },
2931
1585
  async getRemainingTime() {
@@ -2935,7 +1589,7 @@ var RedisStore = class {
2935
1589
  const retryInterval = options?.retryInterval ?? options?.sleepMillis ?? 100;
2936
1590
  const maxRetries = options?.maxRetries ?? Number.POSITIVE_INFINITY;
2937
1591
  const signal = options?.signal;
2938
- const deadline = Date.now() + Math.max(0, secondsToWait) * 1e3;
1592
+ const deadline = Date.now() + Math.max(0, secondsToWait) * 1000;
2939
1593
  let attempt = 0;
2940
1594
  while (Date.now() <= deadline && attempt < maxRetries) {
2941
1595
  if (signal?.aborted) {
@@ -2949,25 +1603,19 @@ var RedisStore = class {
2949
1603
  }
2950
1604
  }
2951
1605
  attempt++;
2952
- const delay = Math.min(retryInterval * 1.5 ** Math.min(attempt, 10), 1e3);
1606
+ const delay = Math.min(retryInterval * 1.5 ** Math.min(attempt, 10), 1000);
2953
1607
  await sleep(delay);
2954
1608
  }
2955
- throw new LockTimeoutError(
2956
- `Failed to acquire lock '${name}' within ${secondsToWait} seconds.`
2957
- );
1609
+ throw new LockTimeoutError(`Failed to acquire lock '${name}' within ${secondsToWait} seconds.`);
2958
1610
  }
2959
1611
  };
2960
1612
  }
2961
- };
1613
+ }
2962
1614
 
2963
1615
  // src/stores/TieredStore.ts
2964
- var TieredStore = class {
2965
- /**
2966
- * Initializes a new TieredStore.
2967
- *
2968
- * @param local - The L1 cache store (usually MemoryStore).
2969
- * @param remote - The L2 cache store (usually RedisStore or FileStore).
2970
- */
1616
+ class TieredStore {
1617
+ local;
1618
+ remote;
2971
1619
  constructor(local, remote) {
2972
1620
  this.local = local;
2973
1621
  this.remote = remote;
@@ -3016,11 +1664,11 @@ var TieredStore = class {
3016
1664
  }
3017
1665
  return this.local.ttl ? this.local.ttl(key) : null;
3018
1666
  }
3019
- };
1667
+ }
3020
1668
 
3021
1669
  // src/index.ts
3022
- var MemoryCacheProvider = class {
3023
- store = new MemoryStore();
1670
+ class MemoryCacheProvider {
1671
+ store = new MemoryStore;
3024
1672
  async get(key) {
3025
1673
  return this.store.get(key);
3026
1674
  }
@@ -3033,7 +1681,7 @@ var MemoryCacheProvider = class {
3033
1681
  async clear() {
3034
1682
  await this.store.flush();
3035
1683
  }
3036
- };
1684
+ }
3037
1685
  function resolveStoreConfig(core, options) {
3038
1686
  if (options) {
3039
1687
  return options;
@@ -3051,15 +1699,15 @@ function createStoreFactory(config) {
3051
1699
  const hasExplicitStores = Object.keys(stores).length > 0;
3052
1700
  if (!storeConfig) {
3053
1701
  if (name === "memory") {
3054
- return new MemoryStore();
1702
+ return new MemoryStore;
3055
1703
  }
3056
1704
  if (name === "null") {
3057
- return new NullStore();
1705
+ return new NullStore;
3058
1706
  }
3059
1707
  if (hasExplicitStores) {
3060
1708
  throw new Error(`Cache store '${name}' is not defined.`);
3061
1709
  }
3062
- return new MemoryStore();
1710
+ return new MemoryStore;
3063
1711
  }
3064
1712
  if (storeConfig.driver === "memory") {
3065
1713
  return new MemoryStore({ maxItems: storeConfig.maxItems });
@@ -3071,7 +1719,7 @@ function createStoreFactory(config) {
3071
1719
  return new RedisStore({ connection: storeConfig.connection, prefix: storeConfig.prefix });
3072
1720
  }
3073
1721
  if (storeConfig.driver === "null") {
3074
- return new NullStore();
1722
+ return new NullStore;
3075
1723
  }
3076
1724
  if (storeConfig.driver === "custom") {
3077
1725
  return storeConfig.store;
@@ -3124,7 +1772,7 @@ function createStoreFactory(config) {
3124
1772
  if (storeConfig.driver === "circuit-breaker") {
3125
1773
  const factory = createStoreFactory(config);
3126
1774
  const primary = factory(storeConfig.primary);
3127
- const fallback = storeConfig.fallback ? factory(storeConfig.fallback) : void 0;
1775
+ const fallback = storeConfig.fallback ? factory(storeConfig.fallback) : undefined;
3128
1776
  return new CircuitBreakerStore(primary, {
3129
1777
  maxFailures: storeConfig.maxFailures,
3130
1778
  resetTimeout: storeConfig.resetTimeout,
@@ -3134,16 +1782,18 @@ function createStoreFactory(config) {
3134
1782
  throw new Error(`Unsupported cache driver '${storeConfig.driver}'.`);
3135
1783
  };
3136
1784
  }
3137
- var OrbitStasis = class {
1785
+
1786
+ class OrbitStasis {
1787
+ options;
1788
+ manager;
3138
1789
  constructor(options) {
3139
1790
  this.options = options;
3140
1791
  }
3141
- manager;
3142
1792
  install(core) {
3143
1793
  const resolvedConfig = resolveStoreConfig(core, this.options);
3144
1794
  const exposeAs = resolvedConfig.exposeAs ?? "cache";
3145
1795
  const defaultStore = resolvedConfig.default ?? (resolvedConfig.provider ? "default" : "memory");
3146
- const defaultTtl = resolvedConfig.defaultTtl ?? (typeof resolvedConfig.defaultTTL === "number" ? resolvedConfig.defaultTTL : void 0) ?? 60;
1796
+ const defaultTtl = resolvedConfig.defaultTtl ?? (typeof resolvedConfig.defaultTTL === "number" ? resolvedConfig.defaultTTL : undefined) ?? 60;
3147
1797
  const prefix = resolvedConfig.prefix ?? "";
3148
1798
  const logger = core.logger;
3149
1799
  logger.info(`[OrbitCache] Initializing Cache (Exposed as: ${exposeAs})`);
@@ -3158,21 +1808,16 @@ var OrbitStasis = class {
3158
1808
  const key = payload.key ? ` (key: ${payload.key})` : "";
3159
1809
  logger.error(`[OrbitCache] cache event '${event}' failed${key}`, error);
3160
1810
  });
3161
- const stores = resolvedConfig.stores ?? (resolvedConfig.provider ? { default: { driver: "provider", provider: resolvedConfig.provider } } : void 0);
3162
- const manager = new CacheManager(
3163
- createStoreFactory({ ...resolvedConfig, stores }),
3164
- {
3165
- default: defaultStore,
3166
- prefix,
3167
- defaultTtl
3168
- },
3169
- events,
3170
- {
3171
- mode: resolvedConfig.eventsMode ?? "async",
3172
- throwOnError: resolvedConfig.throwOnEventError,
3173
- onError: onEventError
3174
- }
3175
- );
1811
+ const stores = resolvedConfig.stores ?? (resolvedConfig.provider ? { default: { driver: "provider", provider: resolvedConfig.provider } } : undefined);
1812
+ const manager = new CacheManager(createStoreFactory({ ...resolvedConfig, stores }), {
1813
+ default: defaultStore,
1814
+ prefix,
1815
+ defaultTtl
1816
+ }, events, {
1817
+ mode: resolvedConfig.eventsMode ?? "async",
1818
+ throwOnError: resolvedConfig.throwOnEventError,
1819
+ onError: onEventError
1820
+ });
3176
1821
  this.manager = manager;
3177
1822
  core.adapter.use("*", async (c, next) => {
3178
1823
  c.set(exposeAs, manager);
@@ -3187,7 +1832,7 @@ var OrbitStasis = class {
3187
1832
  }
3188
1833
  return this.manager;
3189
1834
  }
3190
- };
1835
+ }
3191
1836
  function orbitCache(core, options = {}) {
3192
1837
  const orbit = new OrbitStasis(options);
3193
1838
  orbit.install(core);
@@ -3195,25 +1840,27 @@ function orbitCache(core, options = {}) {
3195
1840
  }
3196
1841
  var OrbitCache = OrbitStasis;
3197
1842
  export {
3198
- CacheManager,
3199
- CacheRepository,
3200
- CircuitBreakerStore,
3201
- FileStore,
3202
- LockTimeoutError,
3203
- MarkovPredictor,
3204
- MemoryCacheProvider,
3205
- MemoryStore,
3206
- NullStore,
3207
- OrbitCache,
3208
- OrbitStasis,
3209
- PredictiveStore,
3210
- RateLimiter,
3211
- RedisStore,
3212
- TieredStore,
3213
- orbitCache as default,
3214
- isExpired,
3215
- isTaggableStore,
3216
- normalizeCacheKey,
1843
+ ttlToExpiresAt,
3217
1844
  sleep,
3218
- ttlToExpiresAt
1845
+ normalizeCacheKey,
1846
+ isTaggableStore,
1847
+ isExpired,
1848
+ orbitCache as default,
1849
+ TieredStore,
1850
+ RedisStore,
1851
+ RateLimiter,
1852
+ PredictiveStore,
1853
+ OrbitStasis,
1854
+ OrbitCache,
1855
+ NullStore,
1856
+ MemoryStore,
1857
+ MemoryCacheProvider,
1858
+ MarkovPredictor,
1859
+ LockTimeoutError,
1860
+ FileStore,
1861
+ CircuitBreakerStore,
1862
+ CacheRepository,
1863
+ CacheManager
3219
1864
  };
1865
+
1866
+ //# debugId=1632CAF3A55DE5FF64756E2164756E21