@gravito/stasis 3.1.1 → 3.2.1

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
99
  return now >= expiresAt;
47
100
  }
48
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
+ }
150
+ }
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;
@@ -1422,22 +669,23 @@ var MarkovPredictor = class {
1422
669
  this.transitions.clear();
1423
670
  this.lastKey = null;
1424
671
  }
1425
- };
672
+ }
1426
673
 
1427
674
  // src/stores/CircuitBreakerStore.ts
1428
- var CircuitBreakerStore = class {
675
+ class CircuitBreakerStore {
676
+ primary;
677
+ state = "CLOSED";
678
+ failures = 0;
679
+ lastErrorTime = 0;
680
+ options;
1429
681
  constructor(primary, options = {}) {
1430
682
  this.primary = primary;
1431
683
  this.options = {
1432
684
  maxFailures: options.maxFailures ?? 5,
1433
- resetTimeout: options.resetTimeout ?? 6e4,
685
+ resetTimeout: options.resetTimeout ?? 60000,
1434
686
  fallback: options.fallback
1435
687
  };
1436
688
  }
1437
- state = "CLOSED";
1438
- failures = 0;
1439
- lastErrorTime = 0;
1440
- options;
1441
689
  async execute(operation, fallbackResult = null) {
1442
690
  if (this.state === "OPEN") {
1443
691
  if (Date.now() - this.lastErrorTime > this.options.resetTimeout) {
@@ -1470,8 +718,7 @@ var CircuitBreakerStore = class {
1470
718
  if (this.options.fallback) {
1471
719
  try {
1472
720
  return await operation(this.options.fallback);
1473
- } catch {
1474
- }
721
+ } catch {}
1475
722
  }
1476
723
  return fallbackResult;
1477
724
  }
@@ -1499,102 +746,71 @@ var CircuitBreakerStore = class {
1499
746
  async ttl(key) {
1500
747
  return this.execute(async (s) => s.ttl ? s.ttl(key) : null);
1501
748
  }
1502
- /**
1503
- * Returns current state for monitoring.
1504
- *
1505
- * @returns Current state of the circuit breaker.
1506
- *
1507
- * @example
1508
- * ```typescript
1509
- * const state = store.getState();
1510
- * if (state === 'OPEN') {
1511
- * console.warn('Primary cache is unavailable');
1512
- * }
1513
- * ```
1514
- */
1515
749
  getState() {
1516
750
  return this.state;
1517
751
  }
1518
- };
752
+ }
1519
753
 
1520
754
  // src/stores/FileStore.ts
1521
- import { createHash, randomUUID } from "crypto";
1522
- import { mkdir, open, readdir, readFile, rename, rm, stat, writeFile } from "fs/promises";
1523
- import { dirname, join } from "path";
1524
- var FileStore = class {
1525
- /**
1526
- * Initializes a new instance of the FileStore.
1527
- *
1528
- * @param options - Configuration settings for the store.
1529
- */
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();
1530
771
  constructor(options) {
1531
772
  this.options = options;
1532
773
  if (options.enableCleanup !== false) {
1533
- this.startCleanupDaemon(options.cleanupInterval ?? 6e4);
774
+ this.startCleanupDaemon(options.cleanupInterval ?? 60000);
1534
775
  }
1535
776
  }
1536
- cleanupTimer = null;
1537
- /**
1538
- * Starts the background process for periodic cache maintenance.
1539
- *
1540
- * @param interval - Time between cleanup cycles in milliseconds.
1541
- * @internal
1542
- */
1543
777
  startCleanupDaemon(interval) {
1544
778
  this.cleanupTimer = setInterval(() => {
1545
- this.cleanExpiredFiles().catch(() => {
1546
- });
779
+ this.cleanExpiredFiles().catch(() => {});
1547
780
  }, interval);
1548
781
  if (this.cleanupTimer.unref) {
1549
782
  this.cleanupTimer.unref();
1550
783
  }
1551
784
  }
1552
- /**
1553
- * Scans the cache directory to remove expired files and enforce capacity limits.
1554
- *
1555
- * This method performs a recursive scan of the storage directory. It deletes
1556
- * files that have passed their expiration time and, if `maxFiles` is configured,
1557
- * evicts the oldest files to stay within the limit.
1558
- *
1559
- * @returns The total number of files removed during this operation.
1560
- * @throws {Error} If the directory cannot be read or files cannot be deleted.
1561
- *
1562
- * @example
1563
- * ```typescript
1564
- * const removedCount = await store.cleanExpiredFiles();
1565
- * console.log(`Cleaned up ${removedCount} files.`);
1566
- * ```
1567
- */
1568
785
  async cleanExpiredFiles() {
1569
786
  await this.ensureDir();
1570
787
  let cleaned = 0;
1571
788
  const validFiles = [];
1572
789
  const scanDir = async (dir) => {
1573
- const entries = await readdir(dir, { withFileTypes: true });
790
+ const entries = await runtimeReadDir(this.runtime, dir);
1574
791
  for (const entry of entries) {
1575
792
  const fullPath = join(dir, entry.name);
1576
- if (entry.isDirectory()) {
793
+ if (entry.isDirectory) {
1577
794
  await scanDir(fullPath);
1578
795
  try {
1579
- await rm(fullPath, { recursive: false });
1580
- } catch {
1581
- }
1582
- } else if (entry.isFile()) {
796
+ const { rmdir } = await import("node:fs/promises");
797
+ await rmdir(fullPath);
798
+ } catch {}
799
+ } else if (entry.isFile) {
1583
800
  if (!entry.name.endsWith(".json") || entry.name.startsWith(".lock-")) {
1584
801
  continue;
1585
802
  }
1586
803
  try {
1587
- const raw = await readFile(fullPath, "utf8");
804
+ const raw = await runtimeReadText(this.runtime, fullPath);
1588
805
  const data = JSON.parse(raw);
1589
806
  if (isExpired(data.expiresAt)) {
1590
- await rm(fullPath, { force: true });
807
+ await runtimeRemoveRecursive(this.runtime, fullPath);
1591
808
  cleaned++;
1592
809
  } else if (this.options.maxFiles) {
1593
- const stats = await stat(fullPath);
810
+ const stats = await runtimeStatFull(this.runtime, fullPath);
1594
811
  validFiles.push({ path: fullPath, mtime: stats.mtimeMs });
1595
812
  }
1596
- } catch {
1597
- }
813
+ } catch {}
1598
814
  }
1599
815
  }
1600
816
  };
@@ -1602,52 +818,25 @@ var FileStore = class {
1602
818
  if (this.options.maxFiles && validFiles.length > this.options.maxFiles) {
1603
819
  validFiles.sort((a, b) => a.mtime - b.mtime);
1604
820
  const toRemove = validFiles.slice(0, validFiles.length - this.options.maxFiles);
1605
- await Promise.all(
1606
- toRemove.map(async (f) => {
1607
- try {
1608
- await rm(f.path, { force: true });
1609
- cleaned++;
1610
- } catch {
1611
- }
1612
- })
1613
- );
821
+ await Promise.all(toRemove.map(async (f) => {
822
+ try {
823
+ await runtimeRemoveRecursive(this.runtime, f.path);
824
+ cleaned++;
825
+ } catch {}
826
+ }));
1614
827
  }
1615
828
  return cleaned;
1616
829
  }
1617
- /**
1618
- * Stops the cleanup daemon and releases associated resources.
1619
- *
1620
- * Should be called when the store is no longer needed to prevent memory leaks
1621
- * and allow the process to exit gracefully.
1622
- *
1623
- * @example
1624
- * ```typescript
1625
- * await store.destroy();
1626
- * ```
1627
- */
1628
830
  async destroy() {
1629
831
  if (this.cleanupTimer) {
1630
832
  clearInterval(this.cleanupTimer);
1631
833
  this.cleanupTimer = null;
1632
834
  }
1633
835
  }
1634
- /**
1635
- * Ensures that the base storage directory exists.
1636
- *
1637
- * @throws {Error} If the directory cannot be created due to permissions or path conflicts.
1638
- * @internal
1639
- */
1640
836
  async ensureDir() {
1641
- await mkdir(this.options.directory, { recursive: true });
1642
- }
1643
- /**
1644
- * Resolves the filesystem path for a given cache key.
1645
- *
1646
- * @param key - Normalized cache key.
1647
- * @returns Absolute path to the JSON file representing the key.
1648
- * @internal
1649
- */
1650
- filePathForKey(key) {
837
+ await runtimeMkdir(this.runtime, this.options.directory, { recursive: true });
838
+ }
839
+ async filePathForKey(key) {
1651
840
  const hashed = hashKey(key);
1652
841
  if (this.options.useSubdirectories) {
1653
842
  const d1 = hashed.substring(0, 2);
@@ -1656,26 +845,12 @@ var FileStore = class {
1656
845
  }
1657
846
  return join(this.options.directory, `${hashed}.json`);
1658
847
  }
1659
- /**
1660
- * Retrieves an item from the cache by its key.
1661
- *
1662
- * If the item exists but has expired, it will be deleted and `null` will be returned.
1663
- *
1664
- * @param key - The unique identifier for the cache item.
1665
- * @returns The cached value, or `null` if not found or expired.
1666
- * @throws {Error} If the file exists but cannot be read or parsed.
1667
- *
1668
- * @example
1669
- * ```typescript
1670
- * const value = await store.get<string>('my-key');
1671
- * ```
1672
- */
1673
848
  async get(key) {
1674
849
  const normalized = normalizeCacheKey(key);
1675
850
  await this.ensureDir();
1676
- const file = this.filePathForKey(normalized);
851
+ const file = await this.filePathForKey(normalized);
1677
852
  try {
1678
- const raw = await readFile(file, "utf8");
853
+ const raw = await runtimeReadText(this.runtime, file);
1679
854
  const data = JSON.parse(raw);
1680
855
  if (isExpired(data.expiresAt)) {
1681
856
  await this.forget(normalized);
@@ -1686,58 +861,28 @@ var FileStore = class {
1686
861
  return null;
1687
862
  }
1688
863
  }
1689
- /**
1690
- * Stores an item in the cache with a specified expiration time.
1691
- *
1692
- * Uses an atomic write strategy (write to temp file then rename) to ensure
1693
- * data integrity even if the process crashes during the write operation.
1694
- *
1695
- * @param key - The unique identifier for the cache item.
1696
- * @param value - The data to be cached.
1697
- * @param ttl - Time-to-live in seconds or a Date object for absolute expiration.
1698
- * @throws {Error} If the file system is not writable or disk is full.
1699
- *
1700
- * @example
1701
- * ```typescript
1702
- * await store.put('settings', { theme: 'dark' }, 86400);
1703
- * ```
1704
- */
1705
864
  async put(key, value, ttl) {
1706
865
  const normalized = normalizeCacheKey(key);
1707
866
  await this.ensureDir();
1708
867
  const expiresAt = ttlToExpiresAt(ttl);
1709
- if (expiresAt !== null && expiresAt !== void 0 && expiresAt <= Date.now()) {
868
+ if (expiresAt !== null && expiresAt !== undefined && expiresAt <= Date.now()) {
1710
869
  await this.forget(normalized);
1711
870
  return;
1712
871
  }
1713
- const file = this.filePathForKey(normalized);
872
+ const file = await this.filePathForKey(normalized);
1714
873
  if (this.options.useSubdirectories) {
1715
- await mkdir(dirname(file), { recursive: true });
874
+ await runtimeMkdir(this.runtime, dirname(file), { recursive: true });
1716
875
  }
1717
- const tempFile = `${file}.tmp.${Date.now()}.${randomUUID()}`;
876
+ const tempFile = `${file}.tmp.${Date.now()}.${crypto.randomUUID()}`;
1718
877
  const payload = { expiresAt: expiresAt ?? null, value };
1719
878
  try {
1720
- await writeFile(tempFile, JSON.stringify(payload), "utf8");
1721
- await rename(tempFile, file);
879
+ await this.runtime.writeFile(tempFile, JSON.stringify(payload));
880
+ await runtimeRename(this.runtime, tempFile, file);
1722
881
  } catch (error) {
1723
- await rm(tempFile, { force: true }).catch(() => {
1724
- });
882
+ await runtimeRemoveRecursive(this.runtime, tempFile).catch(() => {});
1725
883
  throw error;
1726
884
  }
1727
885
  }
1728
- /**
1729
- * Stores an item in the cache only if it does not already exist.
1730
- *
1731
- * @param key - The unique identifier for the cache item.
1732
- * @param value - The data to be cached.
1733
- * @param ttl - Time-to-live in seconds or a Date object.
1734
- * @returns `true` if the item was stored, `false` if it already existed.
1735
- *
1736
- * @example
1737
- * ```typescript
1738
- * const success = await store.add('unique-task', { status: 'pending' }, 60);
1739
- * ```
1740
- */
1741
886
  async add(key, value, ttl) {
1742
887
  const normalized = normalizeCacheKey(key);
1743
888
  const existing = await this.get(normalized);
@@ -1747,61 +892,22 @@ var FileStore = class {
1747
892
  await this.put(normalized, value, ttl);
1748
893
  return true;
1749
894
  }
1750
- /**
1751
- * Removes an item from the cache by its key.
1752
- *
1753
- * @param key - The unique identifier for the cache item.
1754
- * @returns `true` if the file was deleted or didn't exist, `false` on failure.
1755
- *
1756
- * @example
1757
- * ```typescript
1758
- * await store.forget('my-key');
1759
- * ```
1760
- */
1761
895
  async forget(key) {
1762
896
  const normalized = normalizeCacheKey(key);
1763
897
  await this.ensureDir();
1764
- const file = this.filePathForKey(normalized);
898
+ const file = await this.filePathForKey(normalized);
1765
899
  try {
1766
- await rm(file, { force: true });
900
+ await runtimeRemoveRecursive(this.runtime, file);
1767
901
  return true;
1768
902
  } catch {
1769
903
  return false;
1770
904
  }
1771
905
  }
1772
- /**
1773
- * Removes all items from the cache directory.
1774
- *
1775
- * This operation deletes the entire cache directory and recreates it.
1776
- * Use with caution as it is destructive and non-reversible.
1777
- *
1778
- * @throws {Error} If the directory cannot be removed or recreated.
1779
- *
1780
- * @example
1781
- * ```typescript
1782
- * await store.flush();
1783
- * ```
1784
- */
1785
906
  async flush() {
1786
907
  await this.ensureDir();
1787
- await rm(this.options.directory, { recursive: true, force: true });
908
+ await runtimeRemoveRecursive(this.runtime, this.options.directory);
1788
909
  await this.ensureDir();
1789
910
  }
1790
- /**
1791
- * Increments the value of an integer item in the cache.
1792
- *
1793
- * If the key does not exist, it is initialized to 0 before incrementing.
1794
- *
1795
- * @param key - The unique identifier for the cache item.
1796
- * @param value - The amount to increment by.
1797
- * @returns The new value after incrementing.
1798
- * @throws {Error} If the existing value is not a number.
1799
- *
1800
- * @example
1801
- * ```typescript
1802
- * const newCount = await store.increment('page-views');
1803
- * ```
1804
- */
1805
911
  async increment(key, value = 1) {
1806
912
  const normalized = normalizeCacheKey(key);
1807
913
  const current = await this.get(normalized);
@@ -1809,77 +915,30 @@ var FileStore = class {
1809
915
  await this.put(normalized, next, null);
1810
916
  return next;
1811
917
  }
1812
- /**
1813
- * Decrements the value of an integer item in the cache.
1814
- *
1815
- * If the key does not exist, it is initialized to 0 before decrementing.
1816
- *
1817
- * @param key - The unique identifier for the cache item.
1818
- * @param value - The amount to decrement by.
1819
- * @returns The new value after decrementing.
1820
- * @throws {Error} If the existing value is not a number.
1821
- *
1822
- * @example
1823
- * ```typescript
1824
- * const newCount = await store.decrement('stock-level');
1825
- * ```
1826
- */
1827
918
  async decrement(key, value = 1) {
1828
919
  return this.increment(key, -value);
1829
920
  }
1830
- /**
1831
- * Retrieves the remaining time-to-live for a cache item in seconds.
1832
- *
1833
- * @param key - The unique identifier for the cache item.
1834
- * @returns The seconds remaining until expiration, or `null` if it never expires or doesn't exist.
1835
- *
1836
- * @example
1837
- * ```typescript
1838
- * const secondsLeft = await store.ttl('session:123');
1839
- * ```
1840
- */
1841
921
  async ttl(key) {
1842
922
  const normalized = normalizeCacheKey(key);
1843
- const file = this.filePathForKey(normalized);
923
+ const file = await this.filePathForKey(normalized);
1844
924
  try {
1845
- const raw = await readFile(file, "utf8");
925
+ const raw = await runtimeReadText(this.runtime, file);
1846
926
  const data = JSON.parse(raw);
1847
927
  if (data.expiresAt === null) {
1848
928
  return null;
1849
929
  }
1850
- const remaining = Math.ceil((data.expiresAt - Date.now()) / 1e3);
930
+ const remaining = Math.ceil((data.expiresAt - Date.now()) / 1000);
1851
931
  return remaining > 0 ? remaining : null;
1852
932
  } catch {
1853
933
  return null;
1854
934
  }
1855
935
  }
1856
- /**
1857
- * Creates a distributed lock instance based on the filesystem.
1858
- *
1859
- * Locks are implemented using atomic file creation (`wx` flag). They include
1860
- * protection against stale locks by checking process IDs and expiration times.
1861
- *
1862
- * @param name - The unique name of the lock.
1863
- * @param seconds - The duration in seconds for which the lock should be held.
1864
- * @returns A `CacheLock` instance for managing the lock lifecycle.
1865
- *
1866
- * @example
1867
- * ```typescript
1868
- * const lock = store.lock('process-report', 60);
1869
- * if (await lock.acquire()) {
1870
- * try {
1871
- * // Critical section
1872
- * } finally {
1873
- * await lock.release();
1874
- * }
1875
- * }
1876
- * ```
1877
- */
1878
936
  lock(name, seconds = 10) {
1879
937
  const normalizedName = normalizeCacheKey(name);
1880
- const lockFile = join(this.options.directory, `.lock-${hashKey(normalizedName)}`);
1881
- const ttlMillis = Math.max(1, seconds) * 1e3;
1882
- 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;
1883
942
  const isProcessAlive = (pid) => {
1884
943
  try {
1885
944
  process.kill(pid, 0);
@@ -1891,62 +950,41 @@ var FileStore = class {
1891
950
  const tryAcquire = async () => {
1892
951
  await this.ensureDir();
1893
952
  try {
1894
- const handle = await open(lockFile, "wx");
1895
953
  const lockData = {
1896
954
  owner,
1897
955
  expiresAt: Date.now() + ttlMillis,
1898
956
  pid: process.pid
1899
957
  };
1900
- await handle.writeFile(JSON.stringify(lockData), "utf8");
1901
- await handle.close();
958
+ await runtimeWriteFileExclusive(runtime, lockFile, JSON.stringify(lockData));
1902
959
  return true;
1903
960
  } catch {
1904
961
  try {
1905
- const raw = await readFile(lockFile, "utf8");
962
+ const raw = await runtimeReadText(runtime, lockFile);
1906
963
  const data = JSON.parse(raw);
1907
- const isExpired2 = !data.expiresAt || Date.now() > data.expiresAt;
964
+ const isExpiredLock = !data.expiresAt || Date.now() > data.expiresAt;
1908
965
  const isProcessDead = data.pid && !isProcessAlive(data.pid);
1909
- if (isExpired2 || isProcessDead) {
1910
- await rm(lockFile, { force: true });
966
+ if (isExpiredLock || isProcessDead) {
967
+ await runtimeRemoveRecursive(runtime, lockFile);
1911
968
  }
1912
- } catch {
1913
- }
969
+ } catch {}
1914
970
  return false;
1915
971
  }
1916
972
  };
1917
973
  return {
1918
- /**
1919
- * Attempts to acquire the lock immediately.
1920
- *
1921
- * @returns `true` if the lock was successfully acquired, `false` otherwise.
1922
- */
1923
974
  async acquire() {
1924
975
  return tryAcquire();
1925
976
  },
1926
- /**
1927
- * Releases the lock if it is owned by the current instance.
1928
- */
1929
977
  async release() {
1930
978
  try {
1931
- const raw = await readFile(lockFile, "utf8");
979
+ const raw = await runtimeReadText(runtime, lockFile);
1932
980
  const data = JSON.parse(raw);
1933
981
  if (data.owner === owner) {
1934
- await rm(lockFile, { force: true });
982
+ await runtimeRemoveRecursive(runtime, lockFile);
1935
983
  }
1936
- } catch {
1937
- }
984
+ } catch {}
1938
985
  },
1939
- /**
1940
- * Executes a callback within the lock, waiting if necessary.
1941
- *
1942
- * @param secondsToWait - Maximum time to wait for the lock in seconds.
1943
- * @param callback - The function to execute once the lock is acquired.
1944
- * @param options - Polling configuration.
1945
- * @returns The result of the callback.
1946
- * @throws {LockTimeoutError} If the lock cannot be acquired within the wait time.
1947
- */
1948
986
  async block(secondsToWait, callback, options) {
1949
- const deadline = Date.now() + Math.max(0, secondsToWait) * 1e3;
987
+ const deadline = Date.now() + Math.max(0, secondsToWait) * 1000;
1950
988
  const sleepMillis = options?.sleepMillis ?? 150;
1951
989
  while (Date.now() <= deadline) {
1952
990
  if (await this.acquire()) {
@@ -1958,94 +996,52 @@ var FileStore = class {
1958
996
  }
1959
997
  await sleep(sleepMillis);
1960
998
  }
1961
- throw new LockTimeoutError(
1962
- `Failed to acquire lock '${name}' within ${secondsToWait} seconds.`
1963
- );
999
+ throw new LockTimeoutError(`Failed to acquire lock '${name}' within ${secondsToWait} seconds.`);
1964
1000
  }
1965
1001
  };
1966
1002
  }
1967
- };
1003
+ }
1968
1004
  function hashKey(key) {
1969
- 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");
1970
1014
  }
1971
-
1972
- // src/stores/MemoryStore.ts
1973
- import { randomUUID as randomUUID2 } from "crypto";
1974
1015
 
1975
1016
  // src/utils/LRUCache.ts
1976
- var LRUCache = class {
1977
- /**
1978
- * Creates a new LRU cache instance.
1979
- *
1980
- * @param maxSize - The maximum number of items allowed in the cache. Set to 0 for unlimited.
1981
- * @param onEvict - Optional callback triggered when an item is evicted due to capacity limits.
1982
- */
1017
+ class LRUCache {
1018
+ maxSize;
1019
+ onEvict;
1020
+ map = new Map;
1021
+ head = null;
1022
+ tail = null;
1983
1023
  constructor(maxSize, onEvict) {
1984
1024
  this.maxSize = maxSize;
1985
1025
  this.onEvict = onEvict;
1986
1026
  }
1987
- map = /* @__PURE__ */ new Map();
1988
- head = null;
1989
- tail = null;
1990
- /**
1991
- * The current number of items stored in the cache.
1992
- */
1993
1027
  get size() {
1994
1028
  return this.map.size;
1995
1029
  }
1996
- /**
1997
- * Checks if a key exists in the cache without updating its access order.
1998
- *
1999
- * @param key - The identifier to look for.
2000
- * @returns True if the key exists, false otherwise.
2001
- */
2002
1030
  has(key) {
2003
1031
  return this.map.has(key);
2004
1032
  }
2005
- /**
2006
- * Retrieves an item from the cache and marks it as most recently used.
2007
- *
2008
- * @param key - The identifier of the item to retrieve.
2009
- * @returns The cached value, or undefined if not found.
2010
- *
2011
- * @example
2012
- * ```typescript
2013
- * const value = cache.get('my-key');
2014
- * ```
2015
- */
2016
1033
  get(key) {
2017
1034
  const node = this.map.get(key);
2018
1035
  if (!node) {
2019
- return void 0;
1036
+ return;
2020
1037
  }
2021
1038
  this.moveToHead(node);
2022
1039
  return node.value;
2023
1040
  }
2024
- /**
2025
- * Retrieves an item from the cache without updating its access order.
2026
- *
2027
- * Useful for inspecting the cache without affecting eviction priority.
2028
- *
2029
- * @param key - The identifier of the item to peek.
2030
- * @returns The cached value, or undefined if not found.
2031
- */
2032
1041
  peek(key) {
2033
1042
  const node = this.map.get(key);
2034
1043
  return node?.value;
2035
1044
  }
2036
- /**
2037
- * Adds or updates an item in the cache, marking it as most recently used.
2038
- *
2039
- * If the cache is at capacity, the least recently used item will be evicted.
2040
- *
2041
- * @param key - The identifier for the item.
2042
- * @param value - The data to store.
2043
- *
2044
- * @example
2045
- * ```typescript
2046
- * cache.set('user:1', { name: 'Alice' });
2047
- * ```
2048
- */
2049
1045
  set(key, value) {
2050
1046
  const existingNode = this.map.get(key);
2051
1047
  if (existingNode) {
@@ -2071,12 +1067,6 @@ var LRUCache = class {
2071
1067
  }
2072
1068
  this.map.set(key, newNode);
2073
1069
  }
2074
- /**
2075
- * Removes an item from the cache.
2076
- *
2077
- * @param key - The identifier of the item to remove.
2078
- * @returns True if the item was found and removed, false otherwise.
2079
- */
2080
1070
  delete(key) {
2081
1071
  const node = this.map.get(key);
2082
1072
  if (!node) {
@@ -2086,19 +1076,11 @@ var LRUCache = class {
2086
1076
  this.map.delete(key);
2087
1077
  return true;
2088
1078
  }
2089
- /**
2090
- * Removes all items from the cache.
2091
- */
2092
1079
  clear() {
2093
1080
  this.map.clear();
2094
1081
  this.head = null;
2095
1082
  this.tail = null;
2096
1083
  }
2097
- /**
2098
- * Moves a node to the head of the linked list (most recently used).
2099
- *
2100
- * @param node - The node to promote.
2101
- */
2102
1084
  moveToHead(node) {
2103
1085
  if (node === this.head) {
2104
1086
  return;
@@ -2119,11 +1101,6 @@ var LRUCache = class {
2119
1101
  }
2120
1102
  this.head = node;
2121
1103
  }
2122
- /**
2123
- * Removes a node from the linked list.
2124
- *
2125
- * @param node - The node to remove.
2126
- */
2127
1104
  removeNode(node) {
2128
1105
  if (node.prev) {
2129
1106
  node.prev.next = node.next;
@@ -2138,11 +1115,6 @@ var LRUCache = class {
2138
1115
  node.prev = null;
2139
1116
  node.next = null;
2140
1117
  }
2141
- /**
2142
- * Evicts the least recently used item (the tail of the list).
2143
- *
2144
- * Triggers the `onEvict` callback if provided.
2145
- */
2146
1118
  evict() {
2147
1119
  if (!this.tail) {
2148
1120
  return;
@@ -2154,37 +1126,21 @@ var LRUCache = class {
2154
1126
  this.removeNode(node);
2155
1127
  this.map.delete(node.key);
2156
1128
  }
2157
- };
1129
+ }
2158
1130
 
2159
1131
  // src/stores/MemoryStore.ts
2160
- var MemoryStore = class {
1132
+ class MemoryStore {
2161
1133
  entries;
2162
- locks = /* @__PURE__ */ new Map();
1134
+ locks = new Map;
2163
1135
  stats = { hits: 0, misses: 0, evictions: 0 };
2164
- tagToKeys = /* @__PURE__ */ new Map();
2165
- keyToTags = /* @__PURE__ */ new Map();
2166
- /**
2167
- * Creates a new MemoryStore instance.
2168
- *
2169
- * @param options - Configuration for capacity and eviction.
2170
- */
1136
+ tagToKeys = new Map;
1137
+ keyToTags = new Map;
2171
1138
  constructor(options = {}) {
2172
1139
  this.entries = new LRUCache(options.maxItems ?? 0, (key) => {
2173
1140
  this.tagIndexRemove(key);
2174
1141
  this.stats.evictions++;
2175
1142
  });
2176
1143
  }
2177
- /**
2178
- * Retrieves current performance metrics.
2179
- *
2180
- * @returns A snapshot of hits, misses, size, and eviction counts.
2181
- *
2182
- * @example
2183
- * ```typescript
2184
- * const stats = store.getStats();
2185
- * console.log(`Cache hit rate: ${stats.hitRate * 100}%`);
2186
- * ```
2187
- */
2188
1144
  getStats() {
2189
1145
  const total = this.stats.hits + this.stats.misses;
2190
1146
  return {
@@ -2201,22 +1157,9 @@ var MemoryStore = class {
2201
1157
  return;
2202
1158
  }
2203
1159
  if (isExpired(entry.expiresAt, now)) {
2204
- void this.forget(key);
2205
- }
2206
- }
2207
- /**
2208
- * Retrieves an item from the cache by its key.
2209
- *
2210
- * If the item is expired, it will be automatically removed and `null` will be returned.
2211
- *
2212
- * @param key - The unique identifier for the cached item.
2213
- * @returns The cached value, or `null` if not found or expired.
2214
- *
2215
- * @example
2216
- * ```typescript
2217
- * const value = await store.get('my-key');
2218
- * ```
2219
- */
1160
+ this.forget(key);
1161
+ }
1162
+ }
2220
1163
  async get(key) {
2221
1164
  const normalized = normalizeCacheKey(key);
2222
1165
  const entry = this.entries.get(normalized);
@@ -2232,42 +1175,15 @@ var MemoryStore = class {
2232
1175
  this.stats.hits++;
2233
1176
  return entry.value;
2234
1177
  }
2235
- /**
2236
- * Stores an item in the cache with a specific TTL.
2237
- *
2238
- * If the key already exists, it will be overwritten.
2239
- *
2240
- * @param key - The unique identifier for the item.
2241
- * @param value - The data to store.
2242
- * @param ttl - Time-to-live in seconds, or a Date object for absolute expiration.
2243
- *
2244
- * @example
2245
- * ```typescript
2246
- * await store.put('settings', { theme: 'dark' }, 3600);
2247
- * ```
2248
- */
2249
1178
  async put(key, value, ttl) {
2250
1179
  const normalized = normalizeCacheKey(key);
2251
1180
  const expiresAt = ttlToExpiresAt(ttl);
2252
- if (expiresAt !== null && expiresAt !== void 0 && expiresAt <= Date.now()) {
1181
+ if (expiresAt !== null && expiresAt !== undefined && expiresAt <= Date.now()) {
2253
1182
  await this.forget(normalized);
2254
1183
  return;
2255
1184
  }
2256
1185
  this.entries.set(normalized, { value, expiresAt: expiresAt ?? null });
2257
1186
  }
2258
- /**
2259
- * Stores an item only if it does not already exist in the cache.
2260
- *
2261
- * @param key - The unique identifier for the item.
2262
- * @param value - The data to store.
2263
- * @param ttl - Time-to-live in seconds or absolute expiration.
2264
- * @returns `true` if the item was added, `false` if it already existed.
2265
- *
2266
- * @example
2267
- * ```typescript
2268
- * const added = await store.add('unique-task', data, 60);
2269
- * ```
2270
- */
2271
1187
  async add(key, value, ttl) {
2272
1188
  const normalized = normalizeCacheKey(key);
2273
1189
  this.cleanupExpired(normalized);
@@ -2277,50 +1193,17 @@ var MemoryStore = class {
2277
1193
  await this.put(normalized, value, ttl);
2278
1194
  return true;
2279
1195
  }
2280
- /**
2281
- * Removes an item from the cache.
2282
- *
2283
- * @param key - The unique identifier for the item to remove.
2284
- * @returns `true` if the item existed and was removed, `false` otherwise.
2285
- *
2286
- * @example
2287
- * ```typescript
2288
- * await store.forget('user:session');
2289
- * ```
2290
- */
2291
1196
  async forget(key) {
2292
1197
  const normalized = normalizeCacheKey(key);
2293
1198
  const existed = this.entries.delete(normalized);
2294
1199
  this.tagIndexRemove(normalized);
2295
1200
  return existed;
2296
1201
  }
2297
- /**
2298
- * Removes all items from the cache and resets all internal indexes.
2299
- *
2300
- * @example
2301
- * ```typescript
2302
- * await store.flush();
2303
- * ```
2304
- */
2305
1202
  async flush() {
2306
1203
  this.entries.clear();
2307
1204
  this.tagToKeys.clear();
2308
1205
  this.keyToTags.clear();
2309
1206
  }
2310
- /**
2311
- * Increments the value of an item in the cache.
2312
- *
2313
- * If the key does not exist, it starts from 0.
2314
- *
2315
- * @param key - The identifier for the numeric value.
2316
- * @param value - The amount to increment by (defaults to 1).
2317
- * @returns The new incremented value.
2318
- *
2319
- * @example
2320
- * ```typescript
2321
- * const count = await store.increment('page_views');
2322
- * ```
2323
- */
2324
1207
  async increment(key, value = 1) {
2325
1208
  const normalized = normalizeCacheKey(key);
2326
1209
  const current = await this.get(normalized);
@@ -2328,32 +1211,9 @@ var MemoryStore = class {
2328
1211
  await this.put(normalized, next, null);
2329
1212
  return next;
2330
1213
  }
2331
- /**
2332
- * Decrements the value of an item in the cache.
2333
- *
2334
- * @param key - The identifier for the numeric value.
2335
- * @param value - The amount to decrement by (defaults to 1).
2336
- * @returns The new decremented value.
2337
- *
2338
- * @example
2339
- * ```typescript
2340
- * const remaining = await store.decrement('stock_count', 5);
2341
- * ```
2342
- */
2343
1214
  async decrement(key, value = 1) {
2344
1215
  return this.increment(key, -value);
2345
1216
  }
2346
- /**
2347
- * Gets the remaining time-to-live for a cached item.
2348
- *
2349
- * @param key - The identifier for the cached item.
2350
- * @returns Remaining seconds, or `null` if the item has no expiration or does not exist.
2351
- *
2352
- * @example
2353
- * ```typescript
2354
- * const secondsLeft = await store.ttl('token');
2355
- * ```
2356
- */
2357
1217
  async ttl(key) {
2358
1218
  const normalized = normalizeCacheKey(key);
2359
1219
  const entry = this.entries.peek(normalized);
@@ -2365,30 +1225,11 @@ var MemoryStore = class {
2365
1225
  await this.forget(normalized);
2366
1226
  return null;
2367
1227
  }
2368
- return Math.max(0, Math.ceil((entry.expiresAt - now) / 1e3));
2369
- }
2370
- /**
2371
- * Creates a lock instance for managing exclusive access to a resource.
2372
- *
2373
- * @param name - The name of the lock.
2374
- * @param seconds - The duration the lock should be held (defaults to 10).
2375
- * @returns A `CacheLock` instance.
2376
- *
2377
- * @example
2378
- * ```typescript
2379
- * const lock = store.lock('process-report', 30);
2380
- * if (await lock.acquire()) {
2381
- * try {
2382
- * // Critical section
2383
- * } finally {
2384
- * await lock.release();
2385
- * }
2386
- * }
2387
- * ```
2388
- */
1228
+ return Math.max(0, Math.ceil((entry.expiresAt - now) / 1000));
1229
+ }
2389
1230
  lock(name, seconds = 10) {
2390
1231
  const lockKey = `lock:${normalizeCacheKey(name)}`;
2391
- const ttlMillis = Math.max(1, seconds) * 1e3;
1232
+ const ttlMillis = Math.max(1, seconds) * 1000;
2392
1233
  const locks = this.locks;
2393
1234
  const acquire = async () => {
2394
1235
  const now = Date.now();
@@ -2396,17 +1237,12 @@ var MemoryStore = class {
2396
1237
  if (existing && existing.expiresAt > now) {
2397
1238
  return { ok: false };
2398
1239
  }
2399
- const owner2 = randomUUID2();
1240
+ const owner2 = crypto.randomUUID();
2400
1241
  locks.set(lockKey, { owner: owner2, expiresAt: now + ttlMillis });
2401
1242
  return { ok: true, owner: owner2 };
2402
1243
  };
2403
1244
  let owner;
2404
1245
  return {
2405
- /**
2406
- * Attempts to acquire the lock.
2407
- *
2408
- * @returns `true` if acquired, `false` if already held by another process.
2409
- */
2410
1246
  async acquire() {
2411
1247
  const result = await acquire();
2412
1248
  if (!result.ok) {
@@ -2415,9 +1251,6 @@ var MemoryStore = class {
2415
1251
  owner = result.owner;
2416
1252
  return true;
2417
1253
  },
2418
- /**
2419
- * Releases the lock if it is held by the current owner.
2420
- */
2421
1254
  async release() {
2422
1255
  if (!owner) {
2423
1256
  return;
@@ -2426,26 +1259,10 @@ var MemoryStore = class {
2426
1259
  if (existing?.owner === owner) {
2427
1260
  locks.delete(lockKey);
2428
1261
  }
2429
- owner = void 0;
1262
+ owner = undefined;
2430
1263
  },
2431
- /**
2432
- * Attempts to acquire the lock and execute a callback, waiting if necessary.
2433
- *
2434
- * @param secondsToWait - How long to wait for the lock before timing out.
2435
- * @param callback - The logic to execute while holding the lock.
2436
- * @param options - Polling configuration.
2437
- * @returns The result of the callback.
2438
- * @throws {LockTimeoutError} If the lock cannot be acquired within the wait time.
2439
- *
2440
- * @example
2441
- * ```typescript
2442
- * await lock.block(5, async () => {
2443
- * // This code runs exclusively
2444
- * });
2445
- * ```
2446
- */
2447
1264
  async block(secondsToWait, callback, options) {
2448
- const deadline = Date.now() + Math.max(0, secondsToWait) * 1e3;
1265
+ const deadline = Date.now() + Math.max(0, secondsToWait) * 1000;
2449
1266
  const sleepMillis = options?.sleepMillis ?? 150;
2450
1267
  while (Date.now() <= deadline) {
2451
1268
  if (await this.acquire()) {
@@ -2457,21 +1274,10 @@ var MemoryStore = class {
2457
1274
  }
2458
1275
  await sleep(sleepMillis);
2459
1276
  }
2460
- throw new LockTimeoutError(
2461
- `Failed to acquire lock '${name}' within ${secondsToWait} seconds.`
2462
- );
1277
+ throw new LockTimeoutError(`Failed to acquire lock '${name}' within ${secondsToWait} seconds.`);
2463
1278
  }
2464
1279
  };
2465
1280
  }
2466
- /**
2467
- * Generates a tagged key for storage.
2468
- *
2469
- * Used internally to prefix keys with their associated tags.
2470
- *
2471
- * @param key - The original cache key.
2472
- * @param tags - List of tags to associate with the key.
2473
- * @returns A formatted string containing tags and the key.
2474
- */
2475
1281
  tagKey(key, tags) {
2476
1282
  const normalizedKey = normalizeCacheKey(key);
2477
1283
  const normalizedTags = [...tags].map(String).filter(Boolean).sort();
@@ -2480,12 +1286,6 @@ var MemoryStore = class {
2480
1286
  }
2481
1287
  return `tags:${normalizedTags.join("|")}:${normalizedKey}`;
2482
1288
  }
2483
- /**
2484
- * Indexes a tagged key for bulk invalidation.
2485
- *
2486
- * @param tags - The tags to index.
2487
- * @param taggedKey - The full key (including tag prefix) to store.
2488
- */
2489
1289
  tagIndexAdd(tags, taggedKey) {
2490
1290
  const normalizedTags = [...tags].map(String).filter(Boolean);
2491
1291
  if (normalizedTags.length === 0) {
@@ -2494,25 +1294,20 @@ var MemoryStore = class {
2494
1294
  for (const tag of normalizedTags) {
2495
1295
  let keys = this.tagToKeys.get(tag);
2496
1296
  if (!keys) {
2497
- keys = /* @__PURE__ */ new Set();
1297
+ keys = new Set;
2498
1298
  this.tagToKeys.set(tag, keys);
2499
1299
  }
2500
1300
  keys.add(taggedKey);
2501
1301
  }
2502
1302
  let tagSet = this.keyToTags.get(taggedKey);
2503
1303
  if (!tagSet) {
2504
- tagSet = /* @__PURE__ */ new Set();
1304
+ tagSet = new Set;
2505
1305
  this.keyToTags.set(taggedKey, tagSet);
2506
1306
  }
2507
1307
  for (const tag of normalizedTags) {
2508
1308
  tagSet.add(tag);
2509
1309
  }
2510
1310
  }
2511
- /**
2512
- * Removes a key from the tag indexes.
2513
- *
2514
- * @param taggedKey - The key to remove from all tag sets.
2515
- */
2516
1311
  tagIndexRemove(taggedKey) {
2517
1312
  const tags = this.keyToTags.get(taggedKey);
2518
1313
  if (!tags) {
@@ -2530,22 +1325,12 @@ var MemoryStore = class {
2530
1325
  }
2531
1326
  this.keyToTags.delete(taggedKey);
2532
1327
  }
2533
- /**
2534
- * Invalidates all cache entries associated with any of the given tags.
2535
- *
2536
- * @param tags - The tags to flush.
2537
- *
2538
- * @example
2539
- * ```typescript
2540
- * await store.flushTags(['users', 'profiles']);
2541
- * ```
2542
- */
2543
1328
  async flushTags(tags) {
2544
1329
  const normalizedTags = [...tags].map(String).filter(Boolean);
2545
1330
  if (normalizedTags.length === 0) {
2546
1331
  return;
2547
1332
  }
2548
- const keysToDelete = /* @__PURE__ */ new Set();
1333
+ const keysToDelete = new Set;
2549
1334
  for (const tag of normalizedTags) {
2550
1335
  const keys = this.tagToKeys.get(tag);
2551
1336
  if (!keys) {
@@ -2559,131 +1344,42 @@ var MemoryStore = class {
2559
1344
  await this.forget(key);
2560
1345
  }
2561
1346
  }
2562
- };
1347
+ }
2563
1348
 
2564
1349
  // src/stores/NullStore.ts
2565
- var NullStore = class {
2566
- /**
2567
- * Simulates a cache miss for any given key.
2568
- *
2569
- * @param _key - Identifier for the cached item.
2570
- * @returns Always `null` regardless of requested key.
2571
- *
2572
- * @example
2573
- * ```typescript
2574
- * const value = await store.get('my-key');
2575
- * ```
2576
- */
1350
+ class NullStore {
2577
1351
  async get(_key) {
2578
1352
  return null;
2579
1353
  }
2580
- /**
2581
- * Discards the provided value instead of storing it.
2582
- *
2583
- * @param _key - The identifier for the item.
2584
- * @param _value - The data to be cached.
2585
- * @param _ttl - Time-to-live in seconds.
2586
- * @returns Resolves immediately after discarding the data.
2587
- *
2588
- * @example
2589
- * ```typescript
2590
- * await store.put('user:1', { id: 1 }, 3600);
2591
- * ```
2592
- */
2593
- async put(_key, _value, _ttl) {
2594
- }
2595
- /**
2596
- * Simulates a failed attempt to add an item to the cache.
2597
- *
2598
- * Since NullStore does not store data, this method always indicates that
2599
- * the item was not added.
2600
- *
2601
- * @param _key - The identifier for the item.
2602
- * @param _value - The data to be cached.
2603
- * @param _ttl - Time-to-live in seconds.
2604
- * @returns Always returns `false`.
2605
- *
2606
- * @example
2607
- * ```typescript
2608
- * const added = await store.add('key', 'value', 60); // false
2609
- * ```
2610
- */
1354
+ async put(_key, _value, _ttl) {}
2611
1355
  async add(_key, _value, _ttl) {
2612
1356
  return false;
2613
1357
  }
2614
- /**
2615
- * Simulates a failed attempt to remove an item from the cache.
2616
- *
2617
- * Since no data is ever stored, there is nothing to remove.
2618
- *
2619
- * @param _key - The identifier for the item to remove.
2620
- * @returns Always returns `false`.
2621
- *
2622
- * @example
2623
- * ```typescript
2624
- * const forgotten = await store.forget('key'); // false
2625
- * ```
2626
- */
2627
1358
  async forget(_key) {
2628
1359
  return false;
2629
1360
  }
2630
- /**
2631
- * Performs a no-op flush operation.
2632
- *
2633
- * @returns Resolves immediately as there is no data to clear.
2634
- *
2635
- * @example
2636
- * ```typescript
2637
- * await store.flush();
2638
- * ```
2639
- */
2640
- async flush() {
2641
- }
2642
- /**
2643
- * Simulates an increment operation on a non-existent key.
2644
- *
2645
- * @param _key - The identifier for the numeric item.
2646
- * @param _value - The amount to increment by.
2647
- * @returns Always returns `0`.
2648
- *
2649
- * @example
2650
- * ```typescript
2651
- * const newValue = await store.increment('counter', 1); // 0
2652
- * ```
2653
- */
1361
+ async flush() {}
2654
1362
  async increment(_key, _value = 1) {
2655
1363
  return 0;
2656
1364
  }
2657
- /**
2658
- * Simulates a decrement operation on a non-existent key.
2659
- *
2660
- * @param _key - The identifier for the numeric item.
2661
- * @param _value - The amount to decrement by.
2662
- * @returns Always returns `0`.
2663
- *
2664
- * @example
2665
- * ```typescript
2666
- * const newValue = await store.decrement('counter', 1); // 0
2667
- * ```
2668
- */
2669
1365
  async decrement(_key, _value = 1) {
2670
1366
  return 0;
2671
1367
  }
2672
- };
1368
+ }
2673
1369
 
2674
1370
  // src/stores/PredictiveStore.ts
2675
- var PredictiveStore = class {
1371
+ class PredictiveStore {
1372
+ store;
1373
+ predictor;
2676
1374
  constructor(store, options = {}) {
2677
1375
  this.store = store;
2678
- this.predictor = options.predictor ?? new MarkovPredictor();
1376
+ this.predictor = options.predictor ?? new MarkovPredictor;
2679
1377
  }
2680
- predictor;
2681
1378
  async get(key) {
2682
1379
  this.predictor.record(key);
2683
1380
  const candidates = this.predictor.predict(key);
2684
1381
  if (candidates.length > 0) {
2685
- void Promise.all(candidates.map((k) => this.store.get(k).catch(() => {
2686
- })));
1382
+ Promise.all(candidates.map((k) => this.store.get(k).catch(() => {})));
2687
1383
  }
2688
1384
  return this.store.get(key);
2689
1385
  }
@@ -2709,41 +1405,23 @@ var PredictiveStore = class {
2709
1405
  return this.store.decrement(key, value);
2710
1406
  }
2711
1407
  lock(name, seconds) {
2712
- return this.store.lock ? this.store.lock(name, seconds) : void 0;
1408
+ return this.store.lock ? this.store.lock(name, seconds) : undefined;
2713
1409
  }
2714
1410
  async ttl(key) {
2715
1411
  return this.store.ttl ? this.store.ttl(key) : null;
2716
1412
  }
2717
- };
1413
+ }
2718
1414
 
2719
1415
  // src/stores/RedisStore.ts
2720
- import { randomUUID as randomUUID3 } from "crypto";
2721
1416
  import { Redis } from "@gravito/plasma";
2722
- var RedisStore = class {
1417
+ class RedisStore {
2723
1418
  connectionName;
2724
- /**
2725
- * Initialize a new RedisStore instance.
2726
- *
2727
- * @param options - Redis connection and prefix settings.
2728
- *
2729
- * @example
2730
- * ```typescript
2731
- * const store = new RedisStore({ prefix: 'app:' });
2732
- * ```
2733
- */
2734
1419
  constructor(options = {}) {
2735
1420
  this.connectionName = options.connection;
2736
1421
  }
2737
1422
  get client() {
2738
1423
  return Redis.connection(this.connectionName);
2739
1424
  }
2740
- /**
2741
- * Retrieve an item from Redis.
2742
- *
2743
- * @param key - Unique cache key identifier.
2744
- * @returns Parsed JSON value or null if missing/expired.
2745
- * @throws {Error} If Redis connection fails or read errors occur.
2746
- */
2747
1425
  async get(key) {
2748
1426
  const normalized = normalizeCacheKey(key);
2749
1427
  const value = await this.client.get(normalized);
@@ -2756,14 +1434,6 @@ var RedisStore = class {
2756
1434
  return value;
2757
1435
  }
2758
1436
  }
2759
- /**
2760
- * Store an item in Redis.
2761
- *
2762
- * @param key - Unique cache key identifier.
2763
- * @param value - Value to serialize and store.
2764
- * @param ttl - Expiration duration.
2765
- * @throws {Error} If Redis connection fails or write errors occur.
2766
- */
2767
1437
  async put(key, value, ttl) {
2768
1438
  const normalized = normalizeCacheKey(key);
2769
1439
  const serialized = JSON.stringify(value);
@@ -2814,14 +1484,6 @@ var RedisStore = class {
2814
1484
  }
2815
1485
  return await this.client.incrby(normalized, value);
2816
1486
  }
2817
- /**
2818
- * Decrement a numeric value in Redis.
2819
- *
2820
- * @param key - Unique cache key identifier.
2821
- * @param value - Amount to subtract.
2822
- * @returns Updated numeric value.
2823
- * @throws {Error} If key is not numeric or Redis errors occur.
2824
- */
2825
1487
  async decrement(key, value = 1) {
2826
1488
  const normalized = normalizeCacheKey(key);
2827
1489
  if (value === 1) {
@@ -2829,9 +1491,6 @@ var RedisStore = class {
2829
1491
  }
2830
1492
  return await this.client.decrby(normalized, value);
2831
1493
  }
2832
- // ============================================================================
2833
- // Tags
2834
- // ============================================================================
2835
1494
  tagKey(key, _tags) {
2836
1495
  return key;
2837
1496
  }
@@ -2870,7 +1529,7 @@ var RedisStore = class {
2870
1529
  pipeline.smembers(tagKey);
2871
1530
  }
2872
1531
  const results = await pipeline.exec();
2873
- const keysToDelete = /* @__PURE__ */ new Set();
1532
+ const keysToDelete = new Set;
2874
1533
  for (const [err, keys] of results) {
2875
1534
  if (!err && Array.isArray(keys)) {
2876
1535
  for (const k of keys) {
@@ -2883,9 +1542,6 @@ var RedisStore = class {
2883
1542
  }
2884
1543
  await this.client.del(...tagKeys);
2885
1544
  }
2886
- // ============================================================================
2887
- // Locks
2888
- // ============================================================================
2889
1545
  async ttl(key) {
2890
1546
  const normalized = normalizeCacheKey(key);
2891
1547
  const result = await this.client.ttl(normalized);
@@ -2893,8 +1549,8 @@ var RedisStore = class {
2893
1549
  }
2894
1550
  lock(name, seconds = 10) {
2895
1551
  const lockKey = `lock:${normalizeCacheKey(name)}`;
2896
- const owner = randomUUID3();
2897
- const ttlMs = Math.max(1, seconds) * 1e3;
1552
+ const owner = crypto.randomUUID();
1553
+ const ttlMs = Math.max(1, seconds) * 1000;
2898
1554
  const client = this.client;
2899
1555
  return {
2900
1556
  async acquire() {
@@ -2923,13 +1579,7 @@ var RedisStore = class {
2923
1579
  end
2924
1580
  `;
2925
1581
  const evalClient = client;
2926
- const result = await evalClient.eval(
2927
- luaScript,
2928
- 1,
2929
- lockKey,
2930
- owner,
2931
- extensionSeconds.toString()
2932
- );
1582
+ const result = await evalClient.eval(luaScript, 1, lockKey, owner, extensionSeconds.toString());
2933
1583
  return result === 1;
2934
1584
  },
2935
1585
  async getRemainingTime() {
@@ -2939,7 +1589,7 @@ var RedisStore = class {
2939
1589
  const retryInterval = options?.retryInterval ?? options?.sleepMillis ?? 100;
2940
1590
  const maxRetries = options?.maxRetries ?? Number.POSITIVE_INFINITY;
2941
1591
  const signal = options?.signal;
2942
- const deadline = Date.now() + Math.max(0, secondsToWait) * 1e3;
1592
+ const deadline = Date.now() + Math.max(0, secondsToWait) * 1000;
2943
1593
  let attempt = 0;
2944
1594
  while (Date.now() <= deadline && attempt < maxRetries) {
2945
1595
  if (signal?.aborted) {
@@ -2953,25 +1603,19 @@ var RedisStore = class {
2953
1603
  }
2954
1604
  }
2955
1605
  attempt++;
2956
- 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);
2957
1607
  await sleep(delay);
2958
1608
  }
2959
- throw new LockTimeoutError(
2960
- `Failed to acquire lock '${name}' within ${secondsToWait} seconds.`
2961
- );
1609
+ throw new LockTimeoutError(`Failed to acquire lock '${name}' within ${secondsToWait} seconds.`);
2962
1610
  }
2963
1611
  };
2964
1612
  }
2965
- };
1613
+ }
2966
1614
 
2967
1615
  // src/stores/TieredStore.ts
2968
- var TieredStore = class {
2969
- /**
2970
- * Initializes a new TieredStore.
2971
- *
2972
- * @param local - The L1 cache store (usually MemoryStore).
2973
- * @param remote - The L2 cache store (usually RedisStore or FileStore).
2974
- */
1616
+ class TieredStore {
1617
+ local;
1618
+ remote;
2975
1619
  constructor(local, remote) {
2976
1620
  this.local = local;
2977
1621
  this.remote = remote;
@@ -3020,11 +1664,11 @@ var TieredStore = class {
3020
1664
  }
3021
1665
  return this.local.ttl ? this.local.ttl(key) : null;
3022
1666
  }
3023
- };
1667
+ }
3024
1668
 
3025
1669
  // src/index.ts
3026
- var MemoryCacheProvider = class {
3027
- store = new MemoryStore();
1670
+ class MemoryCacheProvider {
1671
+ store = new MemoryStore;
3028
1672
  async get(key) {
3029
1673
  return this.store.get(key);
3030
1674
  }
@@ -3037,7 +1681,7 @@ var MemoryCacheProvider = class {
3037
1681
  async clear() {
3038
1682
  await this.store.flush();
3039
1683
  }
3040
- };
1684
+ }
3041
1685
  function resolveStoreConfig(core, options) {
3042
1686
  if (options) {
3043
1687
  return options;
@@ -3055,15 +1699,15 @@ function createStoreFactory(config) {
3055
1699
  const hasExplicitStores = Object.keys(stores).length > 0;
3056
1700
  if (!storeConfig) {
3057
1701
  if (name === "memory") {
3058
- return new MemoryStore();
1702
+ return new MemoryStore;
3059
1703
  }
3060
1704
  if (name === "null") {
3061
- return new NullStore();
1705
+ return new NullStore;
3062
1706
  }
3063
1707
  if (hasExplicitStores) {
3064
1708
  throw new Error(`Cache store '${name}' is not defined.`);
3065
1709
  }
3066
- return new MemoryStore();
1710
+ return new MemoryStore;
3067
1711
  }
3068
1712
  if (storeConfig.driver === "memory") {
3069
1713
  return new MemoryStore({ maxItems: storeConfig.maxItems });
@@ -3075,7 +1719,7 @@ function createStoreFactory(config) {
3075
1719
  return new RedisStore({ connection: storeConfig.connection, prefix: storeConfig.prefix });
3076
1720
  }
3077
1721
  if (storeConfig.driver === "null") {
3078
- return new NullStore();
1722
+ return new NullStore;
3079
1723
  }
3080
1724
  if (storeConfig.driver === "custom") {
3081
1725
  return storeConfig.store;
@@ -3128,7 +1772,7 @@ function createStoreFactory(config) {
3128
1772
  if (storeConfig.driver === "circuit-breaker") {
3129
1773
  const factory = createStoreFactory(config);
3130
1774
  const primary = factory(storeConfig.primary);
3131
- const fallback = storeConfig.fallback ? factory(storeConfig.fallback) : void 0;
1775
+ const fallback = storeConfig.fallback ? factory(storeConfig.fallback) : undefined;
3132
1776
  return new CircuitBreakerStore(primary, {
3133
1777
  maxFailures: storeConfig.maxFailures,
3134
1778
  resetTimeout: storeConfig.resetTimeout,
@@ -3138,16 +1782,18 @@ function createStoreFactory(config) {
3138
1782
  throw new Error(`Unsupported cache driver '${storeConfig.driver}'.`);
3139
1783
  };
3140
1784
  }
3141
- var OrbitStasis = class {
1785
+
1786
+ class OrbitStasis {
1787
+ options;
1788
+ manager;
3142
1789
  constructor(options) {
3143
1790
  this.options = options;
3144
1791
  }
3145
- manager;
3146
1792
  install(core) {
3147
1793
  const resolvedConfig = resolveStoreConfig(core, this.options);
3148
1794
  const exposeAs = resolvedConfig.exposeAs ?? "cache";
3149
1795
  const defaultStore = resolvedConfig.default ?? (resolvedConfig.provider ? "default" : "memory");
3150
- 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;
3151
1797
  const prefix = resolvedConfig.prefix ?? "";
3152
1798
  const logger = core.logger;
3153
1799
  logger.info(`[OrbitCache] Initializing Cache (Exposed as: ${exposeAs})`);
@@ -3162,21 +1808,16 @@ var OrbitStasis = class {
3162
1808
  const key = payload.key ? ` (key: ${payload.key})` : "";
3163
1809
  logger.error(`[OrbitCache] cache event '${event}' failed${key}`, error);
3164
1810
  });
3165
- const stores = resolvedConfig.stores ?? (resolvedConfig.provider ? { default: { driver: "provider", provider: resolvedConfig.provider } } : void 0);
3166
- const manager = new CacheManager(
3167
- createStoreFactory({ ...resolvedConfig, stores }),
3168
- {
3169
- default: defaultStore,
3170
- prefix,
3171
- defaultTtl
3172
- },
3173
- events,
3174
- {
3175
- mode: resolvedConfig.eventsMode ?? "async",
3176
- throwOnError: resolvedConfig.throwOnEventError,
3177
- onError: onEventError
3178
- }
3179
- );
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
+ });
3180
1821
  this.manager = manager;
3181
1822
  core.adapter.use("*", async (c, next) => {
3182
1823
  c.set(exposeAs, manager);
@@ -3191,7 +1832,7 @@ var OrbitStasis = class {
3191
1832
  }
3192
1833
  return this.manager;
3193
1834
  }
3194
- };
1835
+ }
3195
1836
  function orbitCache(core, options = {}) {
3196
1837
  const orbit = new OrbitStasis(options);
3197
1838
  orbit.install(core);
@@ -3199,25 +1840,27 @@ function orbitCache(core, options = {}) {
3199
1840
  }
3200
1841
  var OrbitCache = OrbitStasis;
3201
1842
  export {
3202
- CacheManager,
3203
- CacheRepository,
3204
- CircuitBreakerStore,
3205
- FileStore,
3206
- LockTimeoutError,
3207
- MarkovPredictor,
3208
- MemoryCacheProvider,
3209
- MemoryStore,
3210
- NullStore,
3211
- OrbitCache,
3212
- OrbitStasis,
3213
- PredictiveStore,
3214
- RateLimiter,
3215
- RedisStore,
3216
- TieredStore,
3217
- orbitCache as default,
3218
- isExpired,
3219
- isTaggableStore,
3220
- normalizeCacheKey,
1843
+ ttlToExpiresAt,
3221
1844
  sleep,
3222
- 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
3223
1864
  };
1865
+
1866
+ //# debugId=17E2109F0F3F0E8764756E2164756E21