@gravito/stasis 3.1.1 → 3.2.0

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