@gravito/stasis 3.1.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.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
- return now > expiresAt;
151
+ return now >= expiresAt;
152
+ }
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
+ }
103
202
  }
104
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;
@@ -1460,36 +703,41 @@ var MarkovPredictor = class {
1460
703
  minKey = k;
1461
704
  }
1462
705
  }
1463
- if (minKey) edges.delete(minKey);
706
+ if (minKey) {
707
+ edges.delete(minKey);
708
+ }
1464
709
  }
1465
710
  }
1466
711
  this.lastKey = key;
1467
712
  }
1468
713
  predict(key) {
1469
714
  const edges = this.transitions.get(key);
1470
- if (!edges) return [];
715
+ if (!edges) {
716
+ return [];
717
+ }
1471
718
  return Array.from(edges.entries()).sort((a, b) => b[1] - a[1]).map((entry) => entry[0]);
1472
719
  }
1473
720
  reset() {
1474
721
  this.transitions.clear();
1475
722
  this.lastKey = null;
1476
723
  }
1477
- };
724
+ }
1478
725
 
1479
726
  // src/stores/CircuitBreakerStore.ts
1480
- var CircuitBreakerStore = class {
727
+ class CircuitBreakerStore {
728
+ primary;
729
+ state = "CLOSED";
730
+ failures = 0;
731
+ lastErrorTime = 0;
732
+ options;
1481
733
  constructor(primary, options = {}) {
1482
734
  this.primary = primary;
1483
735
  this.options = {
1484
736
  maxFailures: options.maxFailures ?? 5,
1485
- resetTimeout: options.resetTimeout ?? 6e4,
737
+ resetTimeout: options.resetTimeout ?? 60000,
1486
738
  fallback: options.fallback
1487
739
  };
1488
740
  }
1489
- state = "CLOSED";
1490
- failures = 0;
1491
- lastErrorTime = 0;
1492
- options;
1493
741
  async execute(operation, fallbackResult = null) {
1494
742
  if (this.state === "OPEN") {
1495
743
  if (Date.now() - this.lastErrorTime > this.options.resetTimeout) {
@@ -1522,8 +770,7 @@ var CircuitBreakerStore = class {
1522
770
  if (this.options.fallback) {
1523
771
  try {
1524
772
  return await operation(this.options.fallback);
1525
- } catch {
1526
- }
773
+ } catch {}
1527
774
  }
1528
775
  return fallbackResult;
1529
776
  }
@@ -1551,102 +798,62 @@ var CircuitBreakerStore = class {
1551
798
  async ttl(key) {
1552
799
  return this.execute(async (s) => s.ttl ? s.ttl(key) : null);
1553
800
  }
1554
- /**
1555
- * Returns current state for monitoring.
1556
- *
1557
- * @returns Current state of the circuit breaker.
1558
- *
1559
- * @example
1560
- * ```typescript
1561
- * const state = store.getState();
1562
- * if (state === 'OPEN') {
1563
- * console.warn('Primary cache is unavailable');
1564
- * }
1565
- * ```
1566
- */
1567
801
  getState() {
1568
802
  return this.state;
1569
803
  }
1570
- };
804
+ }
1571
805
 
1572
806
  // src/stores/FileStore.ts
1573
- var import_node_crypto = require("crypto");
1574
- var import_promises = require("fs/promises");
1575
- var import_node_path = require("path");
1576
- var FileStore = class {
1577
- /**
1578
- * Initializes a new instance of the FileStore.
1579
- *
1580
- * @param options - Configuration settings for the store.
1581
- */
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();
1582
814
  constructor(options) {
1583
815
  this.options = options;
1584
816
  if (options.enableCleanup !== false) {
1585
- this.startCleanupDaemon(options.cleanupInterval ?? 6e4);
817
+ this.startCleanupDaemon(options.cleanupInterval ?? 60000);
1586
818
  }
1587
819
  }
1588
- cleanupTimer = null;
1589
- /**
1590
- * Starts the background process for periodic cache maintenance.
1591
- *
1592
- * @param interval - Time between cleanup cycles in milliseconds.
1593
- * @internal
1594
- */
1595
820
  startCleanupDaemon(interval) {
1596
821
  this.cleanupTimer = setInterval(() => {
1597
- this.cleanExpiredFiles().catch(() => {
1598
- });
822
+ this.cleanExpiredFiles().catch(() => {});
1599
823
  }, interval);
1600
824
  if (this.cleanupTimer.unref) {
1601
825
  this.cleanupTimer.unref();
1602
826
  }
1603
827
  }
1604
- /**
1605
- * Scans the cache directory to remove expired files and enforce capacity limits.
1606
- *
1607
- * This method performs a recursive scan of the storage directory. It deletes
1608
- * files that have passed their expiration time and, if `maxFiles` is configured,
1609
- * evicts the oldest files to stay within the limit.
1610
- *
1611
- * @returns The total number of files removed during this operation.
1612
- * @throws {Error} If the directory cannot be read or files cannot be deleted.
1613
- *
1614
- * @example
1615
- * ```typescript
1616
- * const removedCount = await store.cleanExpiredFiles();
1617
- * console.log(`Cleaned up ${removedCount} files.`);
1618
- * ```
1619
- */
1620
828
  async cleanExpiredFiles() {
1621
829
  await this.ensureDir();
1622
830
  let cleaned = 0;
1623
831
  const validFiles = [];
1624
832
  const scanDir = async (dir) => {
1625
- const entries = await (0, import_promises.readdir)(dir, { withFileTypes: true });
833
+ const entries = await import_core.runtimeReadDir(this.runtime, dir);
1626
834
  for (const entry of entries) {
1627
- const fullPath = (0, import_node_path.join)(dir, entry.name);
1628
- if (entry.isDirectory()) {
835
+ const fullPath = import_node_path.join(dir, entry.name);
836
+ if (entry.isDirectory) {
1629
837
  await scanDir(fullPath);
1630
838
  try {
1631
- await (0, import_promises.rm)(fullPath, { recursive: false });
1632
- } catch {
1633
- }
1634
- } else if (entry.isFile()) {
839
+ const { rmdir } = await import("node:fs/promises");
840
+ await rmdir(fullPath);
841
+ } catch {}
842
+ } else if (entry.isFile) {
1635
843
  if (!entry.name.endsWith(".json") || entry.name.startsWith(".lock-")) {
1636
844
  continue;
1637
845
  }
1638
846
  try {
1639
- const raw = await (0, import_promises.readFile)(fullPath, "utf8");
847
+ const raw = await import_core.runtimeReadText(this.runtime, fullPath);
1640
848
  const data = JSON.parse(raw);
1641
849
  if (isExpired(data.expiresAt)) {
1642
- await (0, import_promises.rm)(fullPath, { force: true });
850
+ await import_core.runtimeRemoveRecursive(this.runtime, fullPath);
1643
851
  cleaned++;
1644
852
  } else if (this.options.maxFiles) {
1645
- const stats = await (0, import_promises.stat)(fullPath);
853
+ const stats = await import_core.runtimeStatFull(this.runtime, fullPath);
1646
854
  validFiles.push({ path: fullPath, mtime: stats.mtimeMs });
1647
855
  }
1648
- } catch {
1649
- }
856
+ } catch {}
1650
857
  }
1651
858
  }
1652
859
  };
@@ -1654,80 +861,39 @@ var FileStore = class {
1654
861
  if (this.options.maxFiles && validFiles.length > this.options.maxFiles) {
1655
862
  validFiles.sort((a, b) => a.mtime - b.mtime);
1656
863
  const toRemove = validFiles.slice(0, validFiles.length - this.options.maxFiles);
1657
- await Promise.all(
1658
- toRemove.map(async (f) => {
1659
- try {
1660
- await (0, import_promises.rm)(f.path, { force: true });
1661
- cleaned++;
1662
- } catch {
1663
- }
1664
- })
1665
- );
864
+ await Promise.all(toRemove.map(async (f) => {
865
+ try {
866
+ await import_core.runtimeRemoveRecursive(this.runtime, f.path);
867
+ cleaned++;
868
+ } catch {}
869
+ }));
1666
870
  }
1667
871
  return cleaned;
1668
872
  }
1669
- /**
1670
- * Stops the cleanup daemon and releases associated resources.
1671
- *
1672
- * Should be called when the store is no longer needed to prevent memory leaks
1673
- * and allow the process to exit gracefully.
1674
- *
1675
- * @example
1676
- * ```typescript
1677
- * await store.destroy();
1678
- * ```
1679
- */
1680
873
  async destroy() {
1681
874
  if (this.cleanupTimer) {
1682
875
  clearInterval(this.cleanupTimer);
1683
876
  this.cleanupTimer = null;
1684
877
  }
1685
878
  }
1686
- /**
1687
- * Ensures that the base storage directory exists.
1688
- *
1689
- * @throws {Error} If the directory cannot be created due to permissions or path conflicts.
1690
- * @internal
1691
- */
1692
879
  async ensureDir() {
1693
- await (0, import_promises.mkdir)(this.options.directory, { recursive: true });
1694
- }
1695
- /**
1696
- * Resolves the filesystem path for a given cache key.
1697
- *
1698
- * @param key - Normalized cache key.
1699
- * @returns Absolute path to the JSON file representing the key.
1700
- * @internal
1701
- */
1702
- filePathForKey(key) {
880
+ await import_core.runtimeMkdir(this.runtime, this.options.directory, { recursive: true });
881
+ }
882
+ async filePathForKey(key) {
1703
883
  const hashed = hashKey(key);
1704
884
  if (this.options.useSubdirectories) {
1705
885
  const d1 = hashed.substring(0, 2);
1706
886
  const d2 = hashed.substring(2, 4);
1707
- return (0, import_node_path.join)(this.options.directory, d1, d2, `${hashed}.json`);
1708
- }
1709
- return (0, import_node_path.join)(this.options.directory, `${hashed}.json`);
1710
- }
1711
- /**
1712
- * Retrieves an item from the cache by its key.
1713
- *
1714
- * If the item exists but has expired, it will be deleted and `null` will be returned.
1715
- *
1716
- * @param key - The unique identifier for the cache item.
1717
- * @returns The cached value, or `null` if not found or expired.
1718
- * @throws {Error} If the file exists but cannot be read or parsed.
1719
- *
1720
- * @example
1721
- * ```typescript
1722
- * const value = await store.get<string>('my-key');
1723
- * ```
1724
- */
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
+ }
1725
891
  async get(key) {
1726
892
  const normalized = normalizeCacheKey(key);
1727
893
  await this.ensureDir();
1728
- const file = this.filePathForKey(normalized);
894
+ const file = await this.filePathForKey(normalized);
1729
895
  try {
1730
- const raw = await (0, import_promises.readFile)(file, "utf8");
896
+ const raw = await import_core.runtimeReadText(this.runtime, file);
1731
897
  const data = JSON.parse(raw);
1732
898
  if (isExpired(data.expiresAt)) {
1733
899
  await this.forget(normalized);
@@ -1738,58 +904,28 @@ var FileStore = class {
1738
904
  return null;
1739
905
  }
1740
906
  }
1741
- /**
1742
- * Stores an item in the cache with a specified expiration time.
1743
- *
1744
- * Uses an atomic write strategy (write to temp file then rename) to ensure
1745
- * data integrity even if the process crashes during the write operation.
1746
- *
1747
- * @param key - The unique identifier for the cache item.
1748
- * @param value - The data to be cached.
1749
- * @param ttl - Time-to-live in seconds or a Date object for absolute expiration.
1750
- * @throws {Error} If the file system is not writable or disk is full.
1751
- *
1752
- * @example
1753
- * ```typescript
1754
- * await store.put('settings', { theme: 'dark' }, 86400);
1755
- * ```
1756
- */
1757
907
  async put(key, value, ttl) {
1758
908
  const normalized = normalizeCacheKey(key);
1759
909
  await this.ensureDir();
1760
910
  const expiresAt = ttlToExpiresAt(ttl);
1761
- if (expiresAt !== null && expiresAt !== void 0 && expiresAt <= Date.now()) {
911
+ if (expiresAt !== null && expiresAt !== undefined && expiresAt <= Date.now()) {
1762
912
  await this.forget(normalized);
1763
913
  return;
1764
914
  }
1765
- const file = this.filePathForKey(normalized);
915
+ const file = await this.filePathForKey(normalized);
1766
916
  if (this.options.useSubdirectories) {
1767
- 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 });
1768
918
  }
1769
- const tempFile = `${file}.tmp.${Date.now()}.${(0, import_node_crypto.randomUUID)()}`;
919
+ const tempFile = `${file}.tmp.${Date.now()}.${crypto.randomUUID()}`;
1770
920
  const payload = { expiresAt: expiresAt ?? null, value };
1771
921
  try {
1772
- await (0, import_promises.writeFile)(tempFile, JSON.stringify(payload), "utf8");
1773
- 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);
1774
924
  } catch (error) {
1775
- await (0, import_promises.rm)(tempFile, { force: true }).catch(() => {
1776
- });
925
+ await import_core.runtimeRemoveRecursive(this.runtime, tempFile).catch(() => {});
1777
926
  throw error;
1778
927
  }
1779
928
  }
1780
- /**
1781
- * Stores an item in the cache only if it does not already exist.
1782
- *
1783
- * @param key - The unique identifier for the cache item.
1784
- * @param value - The data to be cached.
1785
- * @param ttl - Time-to-live in seconds or a Date object.
1786
- * @returns `true` if the item was stored, `false` if it already existed.
1787
- *
1788
- * @example
1789
- * ```typescript
1790
- * const success = await store.add('unique-task', { status: 'pending' }, 60);
1791
- * ```
1792
- */
1793
929
  async add(key, value, ttl) {
1794
930
  const normalized = normalizeCacheKey(key);
1795
931
  const existing = await this.get(normalized);
@@ -1799,61 +935,22 @@ var FileStore = class {
1799
935
  await this.put(normalized, value, ttl);
1800
936
  return true;
1801
937
  }
1802
- /**
1803
- * Removes an item from the cache by its key.
1804
- *
1805
- * @param key - The unique identifier for the cache item.
1806
- * @returns `true` if the file was deleted or didn't exist, `false` on failure.
1807
- *
1808
- * @example
1809
- * ```typescript
1810
- * await store.forget('my-key');
1811
- * ```
1812
- */
1813
938
  async forget(key) {
1814
939
  const normalized = normalizeCacheKey(key);
1815
940
  await this.ensureDir();
1816
- const file = this.filePathForKey(normalized);
941
+ const file = await this.filePathForKey(normalized);
1817
942
  try {
1818
- await (0, import_promises.rm)(file, { force: true });
943
+ await import_core.runtimeRemoveRecursive(this.runtime, file);
1819
944
  return true;
1820
945
  } catch {
1821
946
  return false;
1822
947
  }
1823
948
  }
1824
- /**
1825
- * Removes all items from the cache directory.
1826
- *
1827
- * This operation deletes the entire cache directory and recreates it.
1828
- * Use with caution as it is destructive and non-reversible.
1829
- *
1830
- * @throws {Error} If the directory cannot be removed or recreated.
1831
- *
1832
- * @example
1833
- * ```typescript
1834
- * await store.flush();
1835
- * ```
1836
- */
1837
949
  async flush() {
1838
950
  await this.ensureDir();
1839
- await (0, import_promises.rm)(this.options.directory, { recursive: true, force: true });
951
+ await import_core.runtimeRemoveRecursive(this.runtime, this.options.directory);
1840
952
  await this.ensureDir();
1841
953
  }
1842
- /**
1843
- * Increments the value of an integer item in the cache.
1844
- *
1845
- * If the key does not exist, it is initialized to 0 before incrementing.
1846
- *
1847
- * @param key - The unique identifier for the cache item.
1848
- * @param value - The amount to increment by.
1849
- * @returns The new value after incrementing.
1850
- * @throws {Error} If the existing value is not a number.
1851
- *
1852
- * @example
1853
- * ```typescript
1854
- * const newCount = await store.increment('page-views');
1855
- * ```
1856
- */
1857
954
  async increment(key, value = 1) {
1858
955
  const normalized = normalizeCacheKey(key);
1859
956
  const current = await this.get(normalized);
@@ -1861,77 +958,30 @@ var FileStore = class {
1861
958
  await this.put(normalized, next, null);
1862
959
  return next;
1863
960
  }
1864
- /**
1865
- * Decrements the value of an integer item in the cache.
1866
- *
1867
- * If the key does not exist, it is initialized to 0 before decrementing.
1868
- *
1869
- * @param key - The unique identifier for the cache item.
1870
- * @param value - The amount to decrement by.
1871
- * @returns The new value after decrementing.
1872
- * @throws {Error} If the existing value is not a number.
1873
- *
1874
- * @example
1875
- * ```typescript
1876
- * const newCount = await store.decrement('stock-level');
1877
- * ```
1878
- */
1879
961
  async decrement(key, value = 1) {
1880
962
  return this.increment(key, -value);
1881
963
  }
1882
- /**
1883
- * Retrieves the remaining time-to-live for a cache item in seconds.
1884
- *
1885
- * @param key - The unique identifier for the cache item.
1886
- * @returns The seconds remaining until expiration, or `null` if it never expires or doesn't exist.
1887
- *
1888
- * @example
1889
- * ```typescript
1890
- * const secondsLeft = await store.ttl('session:123');
1891
- * ```
1892
- */
1893
964
  async ttl(key) {
1894
965
  const normalized = normalizeCacheKey(key);
1895
- const file = this.filePathForKey(normalized);
966
+ const file = await this.filePathForKey(normalized);
1896
967
  try {
1897
- const raw = await (0, import_promises.readFile)(file, "utf8");
968
+ const raw = await import_core.runtimeReadText(this.runtime, file);
1898
969
  const data = JSON.parse(raw);
1899
970
  if (data.expiresAt === null) {
1900
971
  return null;
1901
972
  }
1902
- const remaining = Math.ceil((data.expiresAt - Date.now()) / 1e3);
973
+ const remaining = Math.ceil((data.expiresAt - Date.now()) / 1000);
1903
974
  return remaining > 0 ? remaining : null;
1904
975
  } catch {
1905
976
  return null;
1906
977
  }
1907
978
  }
1908
- /**
1909
- * Creates a distributed lock instance based on the filesystem.
1910
- *
1911
- * Locks are implemented using atomic file creation (`wx` flag). They include
1912
- * protection against stale locks by checking process IDs and expiration times.
1913
- *
1914
- * @param name - The unique name of the lock.
1915
- * @param seconds - The duration in seconds for which the lock should be held.
1916
- * @returns A `CacheLock` instance for managing the lock lifecycle.
1917
- *
1918
- * @example
1919
- * ```typescript
1920
- * const lock = store.lock('process-report', 60);
1921
- * if (await lock.acquire()) {
1922
- * try {
1923
- * // Critical section
1924
- * } finally {
1925
- * await lock.release();
1926
- * }
1927
- * }
1928
- * ```
1929
- */
1930
979
  lock(name, seconds = 10) {
1931
980
  const normalizedName = normalizeCacheKey(name);
1932
- const lockFile = (0, import_node_path.join)(this.options.directory, `.lock-${hashKey(normalizedName)}`);
1933
- const ttlMillis = Math.max(1, seconds) * 1e3;
1934
- 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;
1935
985
  const isProcessAlive = (pid) => {
1936
986
  try {
1937
987
  process.kill(pid, 0);
@@ -1943,62 +993,41 @@ var FileStore = class {
1943
993
  const tryAcquire = async () => {
1944
994
  await this.ensureDir();
1945
995
  try {
1946
- const handle = await (0, import_promises.open)(lockFile, "wx");
1947
996
  const lockData = {
1948
997
  owner,
1949
998
  expiresAt: Date.now() + ttlMillis,
1950
999
  pid: process.pid
1951
1000
  };
1952
- await handle.writeFile(JSON.stringify(lockData), "utf8");
1953
- await handle.close();
1001
+ await import_core.runtimeWriteFileExclusive(runtime, lockFile, JSON.stringify(lockData));
1954
1002
  return true;
1955
1003
  } catch {
1956
1004
  try {
1957
- const raw = await (0, import_promises.readFile)(lockFile, "utf8");
1005
+ const raw = await import_core.runtimeReadText(runtime, lockFile);
1958
1006
  const data = JSON.parse(raw);
1959
- const isExpired2 = !data.expiresAt || Date.now() > data.expiresAt;
1007
+ const isExpiredLock = !data.expiresAt || Date.now() > data.expiresAt;
1960
1008
  const isProcessDead = data.pid && !isProcessAlive(data.pid);
1961
- if (isExpired2 || isProcessDead) {
1962
- await (0, import_promises.rm)(lockFile, { force: true });
1009
+ if (isExpiredLock || isProcessDead) {
1010
+ await import_core.runtimeRemoveRecursive(runtime, lockFile);
1963
1011
  }
1964
- } catch {
1965
- }
1012
+ } catch {}
1966
1013
  return false;
1967
1014
  }
1968
1015
  };
1969
1016
  return {
1970
- /**
1971
- * Attempts to acquire the lock immediately.
1972
- *
1973
- * @returns `true` if the lock was successfully acquired, `false` otherwise.
1974
- */
1975
1017
  async acquire() {
1976
1018
  return tryAcquire();
1977
1019
  },
1978
- /**
1979
- * Releases the lock if it is owned by the current instance.
1980
- */
1981
1020
  async release() {
1982
1021
  try {
1983
- const raw = await (0, import_promises.readFile)(lockFile, "utf8");
1022
+ const raw = await import_core.runtimeReadText(runtime, lockFile);
1984
1023
  const data = JSON.parse(raw);
1985
1024
  if (data.owner === owner) {
1986
- await (0, import_promises.rm)(lockFile, { force: true });
1025
+ await import_core.runtimeRemoveRecursive(runtime, lockFile);
1987
1026
  }
1988
- } catch {
1989
- }
1027
+ } catch {}
1990
1028
  },
1991
- /**
1992
- * Executes a callback within the lock, waiting if necessary.
1993
- *
1994
- * @param secondsToWait - Maximum time to wait for the lock in seconds.
1995
- * @param callback - The function to execute once the lock is acquired.
1996
- * @param options - Polling configuration.
1997
- * @returns The result of the callback.
1998
- * @throws {LockTimeoutError} If the lock cannot be acquired within the wait time.
1999
- */
2000
1029
  async block(secondsToWait, callback, options) {
2001
- const deadline = Date.now() + Math.max(0, secondsToWait) * 1e3;
1030
+ const deadline = Date.now() + Math.max(0, secondsToWait) * 1000;
2002
1031
  const sleepMillis = options?.sleepMillis ?? 150;
2003
1032
  while (Date.now() <= deadline) {
2004
1033
  if (await this.acquire()) {
@@ -2010,94 +1039,52 @@ var FileStore = class {
2010
1039
  }
2011
1040
  await sleep(sleepMillis);
2012
1041
  }
2013
- throw new LockTimeoutError(
2014
- `Failed to acquire lock '${name}' within ${secondsToWait} seconds.`
2015
- );
1042
+ throw new LockTimeoutError(`Failed to acquire lock '${name}' within ${secondsToWait} seconds.`);
2016
1043
  }
2017
1044
  };
2018
1045
  }
2019
- };
1046
+ }
2020
1047
  function hashKey(key) {
2021
- 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");
2022
1057
  }
2023
-
2024
- // src/stores/MemoryStore.ts
2025
- var import_node_crypto2 = require("crypto");
2026
1058
 
2027
1059
  // src/utils/LRUCache.ts
2028
- var LRUCache = class {
2029
- /**
2030
- * Creates a new LRU cache instance.
2031
- *
2032
- * @param maxSize - The maximum number of items allowed in the cache. Set to 0 for unlimited.
2033
- * @param onEvict - Optional callback triggered when an item is evicted due to capacity limits.
2034
- */
1060
+ class LRUCache {
1061
+ maxSize;
1062
+ onEvict;
1063
+ map = new Map;
1064
+ head = null;
1065
+ tail = null;
2035
1066
  constructor(maxSize, onEvict) {
2036
1067
  this.maxSize = maxSize;
2037
1068
  this.onEvict = onEvict;
2038
1069
  }
2039
- map = /* @__PURE__ */ new Map();
2040
- head = null;
2041
- tail = null;
2042
- /**
2043
- * The current number of items stored in the cache.
2044
- */
2045
1070
  get size() {
2046
1071
  return this.map.size;
2047
1072
  }
2048
- /**
2049
- * Checks if a key exists in the cache without updating its access order.
2050
- *
2051
- * @param key - The identifier to look for.
2052
- * @returns True if the key exists, false otherwise.
2053
- */
2054
1073
  has(key) {
2055
1074
  return this.map.has(key);
2056
1075
  }
2057
- /**
2058
- * Retrieves an item from the cache and marks it as most recently used.
2059
- *
2060
- * @param key - The identifier of the item to retrieve.
2061
- * @returns The cached value, or undefined if not found.
2062
- *
2063
- * @example
2064
- * ```typescript
2065
- * const value = cache.get('my-key');
2066
- * ```
2067
- */
2068
1076
  get(key) {
2069
1077
  const node = this.map.get(key);
2070
1078
  if (!node) {
2071
- return void 0;
1079
+ return;
2072
1080
  }
2073
1081
  this.moveToHead(node);
2074
1082
  return node.value;
2075
1083
  }
2076
- /**
2077
- * Retrieves an item from the cache without updating its access order.
2078
- *
2079
- * Useful for inspecting the cache without affecting eviction priority.
2080
- *
2081
- * @param key - The identifier of the item to peek.
2082
- * @returns The cached value, or undefined if not found.
2083
- */
2084
1084
  peek(key) {
2085
1085
  const node = this.map.get(key);
2086
1086
  return node?.value;
2087
1087
  }
2088
- /**
2089
- * Adds or updates an item in the cache, marking it as most recently used.
2090
- *
2091
- * If the cache is at capacity, the least recently used item will be evicted.
2092
- *
2093
- * @param key - The identifier for the item.
2094
- * @param value - The data to store.
2095
- *
2096
- * @example
2097
- * ```typescript
2098
- * cache.set('user:1', { name: 'Alice' });
2099
- * ```
2100
- */
2101
1088
  set(key, value) {
2102
1089
  const existingNode = this.map.get(key);
2103
1090
  if (existingNode) {
@@ -2123,12 +1110,6 @@ var LRUCache = class {
2123
1110
  }
2124
1111
  this.map.set(key, newNode);
2125
1112
  }
2126
- /**
2127
- * Removes an item from the cache.
2128
- *
2129
- * @param key - The identifier of the item to remove.
2130
- * @returns True if the item was found and removed, false otherwise.
2131
- */
2132
1113
  delete(key) {
2133
1114
  const node = this.map.get(key);
2134
1115
  if (!node) {
@@ -2138,19 +1119,11 @@ var LRUCache = class {
2138
1119
  this.map.delete(key);
2139
1120
  return true;
2140
1121
  }
2141
- /**
2142
- * Removes all items from the cache.
2143
- */
2144
1122
  clear() {
2145
1123
  this.map.clear();
2146
1124
  this.head = null;
2147
1125
  this.tail = null;
2148
1126
  }
2149
- /**
2150
- * Moves a node to the head of the linked list (most recently used).
2151
- *
2152
- * @param node - The node to promote.
2153
- */
2154
1127
  moveToHead(node) {
2155
1128
  if (node === this.head) {
2156
1129
  return;
@@ -2171,11 +1144,6 @@ var LRUCache = class {
2171
1144
  }
2172
1145
  this.head = node;
2173
1146
  }
2174
- /**
2175
- * Removes a node from the linked list.
2176
- *
2177
- * @param node - The node to remove.
2178
- */
2179
1147
  removeNode(node) {
2180
1148
  if (node.prev) {
2181
1149
  node.prev.next = node.next;
@@ -2190,11 +1158,6 @@ var LRUCache = class {
2190
1158
  node.prev = null;
2191
1159
  node.next = null;
2192
1160
  }
2193
- /**
2194
- * Evicts the least recently used item (the tail of the list).
2195
- *
2196
- * Triggers the `onEvict` callback if provided.
2197
- */
2198
1161
  evict() {
2199
1162
  if (!this.tail) {
2200
1163
  return;
@@ -2206,37 +1169,21 @@ var LRUCache = class {
2206
1169
  this.removeNode(node);
2207
1170
  this.map.delete(node.key);
2208
1171
  }
2209
- };
1172
+ }
2210
1173
 
2211
1174
  // src/stores/MemoryStore.ts
2212
- var MemoryStore = class {
1175
+ class MemoryStore {
2213
1176
  entries;
2214
- locks = /* @__PURE__ */ new Map();
1177
+ locks = new Map;
2215
1178
  stats = { hits: 0, misses: 0, evictions: 0 };
2216
- tagToKeys = /* @__PURE__ */ new Map();
2217
- keyToTags = /* @__PURE__ */ new Map();
2218
- /**
2219
- * Creates a new MemoryStore instance.
2220
- *
2221
- * @param options - Configuration for capacity and eviction.
2222
- */
1179
+ tagToKeys = new Map;
1180
+ keyToTags = new Map;
2223
1181
  constructor(options = {}) {
2224
1182
  this.entries = new LRUCache(options.maxItems ?? 0, (key) => {
2225
1183
  this.tagIndexRemove(key);
2226
1184
  this.stats.evictions++;
2227
1185
  });
2228
1186
  }
2229
- /**
2230
- * Retrieves current performance metrics.
2231
- *
2232
- * @returns A snapshot of hits, misses, size, and eviction counts.
2233
- *
2234
- * @example
2235
- * ```typescript
2236
- * const stats = store.getStats();
2237
- * console.log(`Cache hit rate: ${stats.hitRate * 100}%`);
2238
- * ```
2239
- */
2240
1187
  getStats() {
2241
1188
  const total = this.stats.hits + this.stats.misses;
2242
1189
  return {
@@ -2253,22 +1200,9 @@ var MemoryStore = class {
2253
1200
  return;
2254
1201
  }
2255
1202
  if (isExpired(entry.expiresAt, now)) {
2256
- void this.forget(key);
2257
- }
2258
- }
2259
- /**
2260
- * Retrieves an item from the cache by its key.
2261
- *
2262
- * If the item is expired, it will be automatically removed and `null` will be returned.
2263
- *
2264
- * @param key - The unique identifier for the cached item.
2265
- * @returns The cached value, or `null` if not found or expired.
2266
- *
2267
- * @example
2268
- * ```typescript
2269
- * const value = await store.get('my-key');
2270
- * ```
2271
- */
1203
+ this.forget(key);
1204
+ }
1205
+ }
2272
1206
  async get(key) {
2273
1207
  const normalized = normalizeCacheKey(key);
2274
1208
  const entry = this.entries.get(normalized);
@@ -2284,42 +1218,15 @@ var MemoryStore = class {
2284
1218
  this.stats.hits++;
2285
1219
  return entry.value;
2286
1220
  }
2287
- /**
2288
- * Stores an item in the cache with a specific TTL.
2289
- *
2290
- * If the key already exists, it will be overwritten.
2291
- *
2292
- * @param key - The unique identifier for the item.
2293
- * @param value - The data to store.
2294
- * @param ttl - Time-to-live in seconds, or a Date object for absolute expiration.
2295
- *
2296
- * @example
2297
- * ```typescript
2298
- * await store.put('settings', { theme: 'dark' }, 3600);
2299
- * ```
2300
- */
2301
1221
  async put(key, value, ttl) {
2302
1222
  const normalized = normalizeCacheKey(key);
2303
1223
  const expiresAt = ttlToExpiresAt(ttl);
2304
- if (expiresAt !== null && expiresAt !== void 0 && expiresAt <= Date.now()) {
1224
+ if (expiresAt !== null && expiresAt !== undefined && expiresAt <= Date.now()) {
2305
1225
  await this.forget(normalized);
2306
1226
  return;
2307
1227
  }
2308
1228
  this.entries.set(normalized, { value, expiresAt: expiresAt ?? null });
2309
1229
  }
2310
- /**
2311
- * Stores an item only if it does not already exist in the cache.
2312
- *
2313
- * @param key - The unique identifier for the item.
2314
- * @param value - The data to store.
2315
- * @param ttl - Time-to-live in seconds or absolute expiration.
2316
- * @returns `true` if the item was added, `false` if it already existed.
2317
- *
2318
- * @example
2319
- * ```typescript
2320
- * const added = await store.add('unique-task', data, 60);
2321
- * ```
2322
- */
2323
1230
  async add(key, value, ttl) {
2324
1231
  const normalized = normalizeCacheKey(key);
2325
1232
  this.cleanupExpired(normalized);
@@ -2329,50 +1236,17 @@ var MemoryStore = class {
2329
1236
  await this.put(normalized, value, ttl);
2330
1237
  return true;
2331
1238
  }
2332
- /**
2333
- * Removes an item from the cache.
2334
- *
2335
- * @param key - The unique identifier for the item to remove.
2336
- * @returns `true` if the item existed and was removed, `false` otherwise.
2337
- *
2338
- * @example
2339
- * ```typescript
2340
- * await store.forget('user:session');
2341
- * ```
2342
- */
2343
1239
  async forget(key) {
2344
1240
  const normalized = normalizeCacheKey(key);
2345
1241
  const existed = this.entries.delete(normalized);
2346
1242
  this.tagIndexRemove(normalized);
2347
1243
  return existed;
2348
1244
  }
2349
- /**
2350
- * Removes all items from the cache and resets all internal indexes.
2351
- *
2352
- * @example
2353
- * ```typescript
2354
- * await store.flush();
2355
- * ```
2356
- */
2357
1245
  async flush() {
2358
1246
  this.entries.clear();
2359
1247
  this.tagToKeys.clear();
2360
1248
  this.keyToTags.clear();
2361
1249
  }
2362
- /**
2363
- * Increments the value of an item in the cache.
2364
- *
2365
- * If the key does not exist, it starts from 0.
2366
- *
2367
- * @param key - The identifier for the numeric value.
2368
- * @param value - The amount to increment by (defaults to 1).
2369
- * @returns The new incremented value.
2370
- *
2371
- * @example
2372
- * ```typescript
2373
- * const count = await store.increment('page_views');
2374
- * ```
2375
- */
2376
1250
  async increment(key, value = 1) {
2377
1251
  const normalized = normalizeCacheKey(key);
2378
1252
  const current = await this.get(normalized);
@@ -2380,32 +1254,9 @@ var MemoryStore = class {
2380
1254
  await this.put(normalized, next, null);
2381
1255
  return next;
2382
1256
  }
2383
- /**
2384
- * Decrements the value of an item in the cache.
2385
- *
2386
- * @param key - The identifier for the numeric value.
2387
- * @param value - The amount to decrement by (defaults to 1).
2388
- * @returns The new decremented value.
2389
- *
2390
- * @example
2391
- * ```typescript
2392
- * const remaining = await store.decrement('stock_count', 5);
2393
- * ```
2394
- */
2395
1257
  async decrement(key, value = 1) {
2396
1258
  return this.increment(key, -value);
2397
1259
  }
2398
- /**
2399
- * Gets the remaining time-to-live for a cached item.
2400
- *
2401
- * @param key - The identifier for the cached item.
2402
- * @returns Remaining seconds, or `null` if the item has no expiration or does not exist.
2403
- *
2404
- * @example
2405
- * ```typescript
2406
- * const secondsLeft = await store.ttl('token');
2407
- * ```
2408
- */
2409
1260
  async ttl(key) {
2410
1261
  const normalized = normalizeCacheKey(key);
2411
1262
  const entry = this.entries.peek(normalized);
@@ -2417,30 +1268,11 @@ var MemoryStore = class {
2417
1268
  await this.forget(normalized);
2418
1269
  return null;
2419
1270
  }
2420
- return Math.max(0, Math.ceil((entry.expiresAt - now) / 1e3));
2421
- }
2422
- /**
2423
- * Creates a lock instance for managing exclusive access to a resource.
2424
- *
2425
- * @param name - The name of the lock.
2426
- * @param seconds - The duration the lock should be held (defaults to 10).
2427
- * @returns A `CacheLock` instance.
2428
- *
2429
- * @example
2430
- * ```typescript
2431
- * const lock = store.lock('process-report', 30);
2432
- * if (await lock.acquire()) {
2433
- * try {
2434
- * // Critical section
2435
- * } finally {
2436
- * await lock.release();
2437
- * }
2438
- * }
2439
- * ```
2440
- */
1271
+ return Math.max(0, Math.ceil((entry.expiresAt - now) / 1000));
1272
+ }
2441
1273
  lock(name, seconds = 10) {
2442
1274
  const lockKey = `lock:${normalizeCacheKey(name)}`;
2443
- const ttlMillis = Math.max(1, seconds) * 1e3;
1275
+ const ttlMillis = Math.max(1, seconds) * 1000;
2444
1276
  const locks = this.locks;
2445
1277
  const acquire = async () => {
2446
1278
  const now = Date.now();
@@ -2448,17 +1280,12 @@ var MemoryStore = class {
2448
1280
  if (existing && existing.expiresAt > now) {
2449
1281
  return { ok: false };
2450
1282
  }
2451
- const owner2 = (0, import_node_crypto2.randomUUID)();
1283
+ const owner2 = crypto.randomUUID();
2452
1284
  locks.set(lockKey, { owner: owner2, expiresAt: now + ttlMillis });
2453
1285
  return { ok: true, owner: owner2 };
2454
1286
  };
2455
1287
  let owner;
2456
1288
  return {
2457
- /**
2458
- * Attempts to acquire the lock.
2459
- *
2460
- * @returns `true` if acquired, `false` if already held by another process.
2461
- */
2462
1289
  async acquire() {
2463
1290
  const result = await acquire();
2464
1291
  if (!result.ok) {
@@ -2467,9 +1294,6 @@ var MemoryStore = class {
2467
1294
  owner = result.owner;
2468
1295
  return true;
2469
1296
  },
2470
- /**
2471
- * Releases the lock if it is held by the current owner.
2472
- */
2473
1297
  async release() {
2474
1298
  if (!owner) {
2475
1299
  return;
@@ -2478,26 +1302,10 @@ var MemoryStore = class {
2478
1302
  if (existing?.owner === owner) {
2479
1303
  locks.delete(lockKey);
2480
1304
  }
2481
- owner = void 0;
1305
+ owner = undefined;
2482
1306
  },
2483
- /**
2484
- * Attempts to acquire the lock and execute a callback, waiting if necessary.
2485
- *
2486
- * @param secondsToWait - How long to wait for the lock before timing out.
2487
- * @param callback - The logic to execute while holding the lock.
2488
- * @param options - Polling configuration.
2489
- * @returns The result of the callback.
2490
- * @throws {LockTimeoutError} If the lock cannot be acquired within the wait time.
2491
- *
2492
- * @example
2493
- * ```typescript
2494
- * await lock.block(5, async () => {
2495
- * // This code runs exclusively
2496
- * });
2497
- * ```
2498
- */
2499
1307
  async block(secondsToWait, callback, options) {
2500
- const deadline = Date.now() + Math.max(0, secondsToWait) * 1e3;
1308
+ const deadline = Date.now() + Math.max(0, secondsToWait) * 1000;
2501
1309
  const sleepMillis = options?.sleepMillis ?? 150;
2502
1310
  while (Date.now() <= deadline) {
2503
1311
  if (await this.acquire()) {
@@ -2509,21 +1317,10 @@ var MemoryStore = class {
2509
1317
  }
2510
1318
  await sleep(sleepMillis);
2511
1319
  }
2512
- throw new LockTimeoutError(
2513
- `Failed to acquire lock '${name}' within ${secondsToWait} seconds.`
2514
- );
1320
+ throw new LockTimeoutError(`Failed to acquire lock '${name}' within ${secondsToWait} seconds.`);
2515
1321
  }
2516
1322
  };
2517
1323
  }
2518
- /**
2519
- * Generates a tagged key for storage.
2520
- *
2521
- * Used internally to prefix keys with their associated tags.
2522
- *
2523
- * @param key - The original cache key.
2524
- * @param tags - List of tags to associate with the key.
2525
- * @returns A formatted string containing tags and the key.
2526
- */
2527
1324
  tagKey(key, tags) {
2528
1325
  const normalizedKey = normalizeCacheKey(key);
2529
1326
  const normalizedTags = [...tags].map(String).filter(Boolean).sort();
@@ -2532,12 +1329,6 @@ var MemoryStore = class {
2532
1329
  }
2533
1330
  return `tags:${normalizedTags.join("|")}:${normalizedKey}`;
2534
1331
  }
2535
- /**
2536
- * Indexes a tagged key for bulk invalidation.
2537
- *
2538
- * @param tags - The tags to index.
2539
- * @param taggedKey - The full key (including tag prefix) to store.
2540
- */
2541
1332
  tagIndexAdd(tags, taggedKey) {
2542
1333
  const normalizedTags = [...tags].map(String).filter(Boolean);
2543
1334
  if (normalizedTags.length === 0) {
@@ -2546,25 +1337,20 @@ var MemoryStore = class {
2546
1337
  for (const tag of normalizedTags) {
2547
1338
  let keys = this.tagToKeys.get(tag);
2548
1339
  if (!keys) {
2549
- keys = /* @__PURE__ */ new Set();
1340
+ keys = new Set;
2550
1341
  this.tagToKeys.set(tag, keys);
2551
1342
  }
2552
1343
  keys.add(taggedKey);
2553
1344
  }
2554
1345
  let tagSet = this.keyToTags.get(taggedKey);
2555
1346
  if (!tagSet) {
2556
- tagSet = /* @__PURE__ */ new Set();
1347
+ tagSet = new Set;
2557
1348
  this.keyToTags.set(taggedKey, tagSet);
2558
1349
  }
2559
1350
  for (const tag of normalizedTags) {
2560
1351
  tagSet.add(tag);
2561
1352
  }
2562
1353
  }
2563
- /**
2564
- * Removes a key from the tag indexes.
2565
- *
2566
- * @param taggedKey - The key to remove from all tag sets.
2567
- */
2568
1354
  tagIndexRemove(taggedKey) {
2569
1355
  const tags = this.keyToTags.get(taggedKey);
2570
1356
  if (!tags) {
@@ -2582,22 +1368,12 @@ var MemoryStore = class {
2582
1368
  }
2583
1369
  this.keyToTags.delete(taggedKey);
2584
1370
  }
2585
- /**
2586
- * Invalidates all cache entries associated with any of the given tags.
2587
- *
2588
- * @param tags - The tags to flush.
2589
- *
2590
- * @example
2591
- * ```typescript
2592
- * await store.flushTags(['users', 'profiles']);
2593
- * ```
2594
- */
2595
1371
  async flushTags(tags) {
2596
1372
  const normalizedTags = [...tags].map(String).filter(Boolean);
2597
1373
  if (normalizedTags.length === 0) {
2598
1374
  return;
2599
1375
  }
2600
- const keysToDelete = /* @__PURE__ */ new Set();
1376
+ const keysToDelete = new Set;
2601
1377
  for (const tag of normalizedTags) {
2602
1378
  const keys = this.tagToKeys.get(tag);
2603
1379
  if (!keys) {
@@ -2611,131 +1387,42 @@ var MemoryStore = class {
2611
1387
  await this.forget(key);
2612
1388
  }
2613
1389
  }
2614
- };
1390
+ }
2615
1391
 
2616
1392
  // src/stores/NullStore.ts
2617
- var NullStore = class {
2618
- /**
2619
- * Simulates a cache miss for any given key.
2620
- *
2621
- * @param _key - Identifier for the cached item.
2622
- * @returns Always `null` regardless of requested key.
2623
- *
2624
- * @example
2625
- * ```typescript
2626
- * const value = await store.get('my-key');
2627
- * ```
2628
- */
1393
+ class NullStore {
2629
1394
  async get(_key) {
2630
1395
  return null;
2631
1396
  }
2632
- /**
2633
- * Discards the provided value instead of storing it.
2634
- *
2635
- * @param _key - The identifier for the item.
2636
- * @param _value - The data to be cached.
2637
- * @param _ttl - Time-to-live in seconds.
2638
- * @returns Resolves immediately after discarding the data.
2639
- *
2640
- * @example
2641
- * ```typescript
2642
- * await store.put('user:1', { id: 1 }, 3600);
2643
- * ```
2644
- */
2645
- async put(_key, _value, _ttl) {
2646
- }
2647
- /**
2648
- * Simulates a failed attempt to add an item to the cache.
2649
- *
2650
- * Since NullStore does not store data, this method always indicates that
2651
- * the item was not added.
2652
- *
2653
- * @param _key - The identifier for the item.
2654
- * @param _value - The data to be cached.
2655
- * @param _ttl - Time-to-live in seconds.
2656
- * @returns Always returns `false`.
2657
- *
2658
- * @example
2659
- * ```typescript
2660
- * const added = await store.add('key', 'value', 60); // false
2661
- * ```
2662
- */
1397
+ async put(_key, _value, _ttl) {}
2663
1398
  async add(_key, _value, _ttl) {
2664
1399
  return false;
2665
1400
  }
2666
- /**
2667
- * Simulates a failed attempt to remove an item from the cache.
2668
- *
2669
- * Since no data is ever stored, there is nothing to remove.
2670
- *
2671
- * @param _key - The identifier for the item to remove.
2672
- * @returns Always returns `false`.
2673
- *
2674
- * @example
2675
- * ```typescript
2676
- * const forgotten = await store.forget('key'); // false
2677
- * ```
2678
- */
2679
1401
  async forget(_key) {
2680
1402
  return false;
2681
1403
  }
2682
- /**
2683
- * Performs a no-op flush operation.
2684
- *
2685
- * @returns Resolves immediately as there is no data to clear.
2686
- *
2687
- * @example
2688
- * ```typescript
2689
- * await store.flush();
2690
- * ```
2691
- */
2692
- async flush() {
2693
- }
2694
- /**
2695
- * Simulates an increment operation on a non-existent key.
2696
- *
2697
- * @param _key - The identifier for the numeric item.
2698
- * @param _value - The amount to increment by.
2699
- * @returns Always returns `0`.
2700
- *
2701
- * @example
2702
- * ```typescript
2703
- * const newValue = await store.increment('counter', 1); // 0
2704
- * ```
2705
- */
1404
+ async flush() {}
2706
1405
  async increment(_key, _value = 1) {
2707
1406
  return 0;
2708
1407
  }
2709
- /**
2710
- * Simulates a decrement operation on a non-existent key.
2711
- *
2712
- * @param _key - The identifier for the numeric item.
2713
- * @param _value - The amount to decrement by.
2714
- * @returns Always returns `0`.
2715
- *
2716
- * @example
2717
- * ```typescript
2718
- * const newValue = await store.decrement('counter', 1); // 0
2719
- * ```
2720
- */
2721
1408
  async decrement(_key, _value = 1) {
2722
1409
  return 0;
2723
1410
  }
2724
- };
1411
+ }
2725
1412
 
2726
1413
  // src/stores/PredictiveStore.ts
2727
- var PredictiveStore = class {
1414
+ class PredictiveStore {
1415
+ store;
1416
+ predictor;
2728
1417
  constructor(store, options = {}) {
2729
1418
  this.store = store;
2730
- this.predictor = options.predictor ?? new MarkovPredictor();
1419
+ this.predictor = options.predictor ?? new MarkovPredictor;
2731
1420
  }
2732
- predictor;
2733
1421
  async get(key) {
2734
1422
  this.predictor.record(key);
2735
1423
  const candidates = this.predictor.predict(key);
2736
1424
  if (candidates.length > 0) {
2737
- void Promise.all(candidates.map((k) => this.store.get(k).catch(() => {
2738
- })));
1425
+ Promise.all(candidates.map((k) => this.store.get(k).catch(() => {})));
2739
1426
  }
2740
1427
  return this.store.get(key);
2741
1428
  }
@@ -2761,41 +1448,23 @@ var PredictiveStore = class {
2761
1448
  return this.store.decrement(key, value);
2762
1449
  }
2763
1450
  lock(name, seconds) {
2764
- return this.store.lock ? this.store.lock(name, seconds) : void 0;
1451
+ return this.store.lock ? this.store.lock(name, seconds) : undefined;
2765
1452
  }
2766
1453
  async ttl(key) {
2767
1454
  return this.store.ttl ? this.store.ttl(key) : null;
2768
1455
  }
2769
- };
1456
+ }
2770
1457
 
2771
1458
  // src/stores/RedisStore.ts
2772
- var import_node_crypto3 = require("crypto");
2773
1459
  var import_plasma = require("@gravito/plasma");
2774
- var RedisStore = class {
1460
+ class RedisStore {
2775
1461
  connectionName;
2776
- /**
2777
- * Initialize a new RedisStore instance.
2778
- *
2779
- * @param options - Redis connection and prefix settings.
2780
- *
2781
- * @example
2782
- * ```typescript
2783
- * const store = new RedisStore({ prefix: 'app:' });
2784
- * ```
2785
- */
2786
1462
  constructor(options = {}) {
2787
1463
  this.connectionName = options.connection;
2788
1464
  }
2789
1465
  get client() {
2790
1466
  return import_plasma.Redis.connection(this.connectionName);
2791
1467
  }
2792
- /**
2793
- * Retrieve an item from Redis.
2794
- *
2795
- * @param key - Unique cache key identifier.
2796
- * @returns Parsed JSON value or null if missing/expired.
2797
- * @throws {Error} If Redis connection fails or read errors occur.
2798
- */
2799
1468
  async get(key) {
2800
1469
  const normalized = normalizeCacheKey(key);
2801
1470
  const value = await this.client.get(normalized);
@@ -2808,14 +1477,6 @@ var RedisStore = class {
2808
1477
  return value;
2809
1478
  }
2810
1479
  }
2811
- /**
2812
- * Store an item in Redis.
2813
- *
2814
- * @param key - Unique cache key identifier.
2815
- * @param value - Value to serialize and store.
2816
- * @param ttl - Expiration duration.
2817
- * @throws {Error} If Redis connection fails or write errors occur.
2818
- */
2819
1480
  async put(key, value, ttl) {
2820
1481
  const normalized = normalizeCacheKey(key);
2821
1482
  const serialized = JSON.stringify(value);
@@ -2866,14 +1527,6 @@ var RedisStore = class {
2866
1527
  }
2867
1528
  return await this.client.incrby(normalized, value);
2868
1529
  }
2869
- /**
2870
- * Decrement a numeric value in Redis.
2871
- *
2872
- * @param key - Unique cache key identifier.
2873
- * @param value - Amount to subtract.
2874
- * @returns Updated numeric value.
2875
- * @throws {Error} If key is not numeric or Redis errors occur.
2876
- */
2877
1530
  async decrement(key, value = 1) {
2878
1531
  const normalized = normalizeCacheKey(key);
2879
1532
  if (value === 1) {
@@ -2881,9 +1534,6 @@ var RedisStore = class {
2881
1534
  }
2882
1535
  return await this.client.decrby(normalized, value);
2883
1536
  }
2884
- // ============================================================================
2885
- // Tags
2886
- // ============================================================================
2887
1537
  tagKey(key, _tags) {
2888
1538
  return key;
2889
1539
  }
@@ -2922,7 +1572,7 @@ var RedisStore = class {
2922
1572
  pipeline.smembers(tagKey);
2923
1573
  }
2924
1574
  const results = await pipeline.exec();
2925
- const keysToDelete = /* @__PURE__ */ new Set();
1575
+ const keysToDelete = new Set;
2926
1576
  for (const [err, keys] of results) {
2927
1577
  if (!err && Array.isArray(keys)) {
2928
1578
  for (const k of keys) {
@@ -2935,9 +1585,6 @@ var RedisStore = class {
2935
1585
  }
2936
1586
  await this.client.del(...tagKeys);
2937
1587
  }
2938
- // ============================================================================
2939
- // Locks
2940
- // ============================================================================
2941
1588
  async ttl(key) {
2942
1589
  const normalized = normalizeCacheKey(key);
2943
1590
  const result = await this.client.ttl(normalized);
@@ -2945,8 +1592,8 @@ var RedisStore = class {
2945
1592
  }
2946
1593
  lock(name, seconds = 10) {
2947
1594
  const lockKey = `lock:${normalizeCacheKey(name)}`;
2948
- const owner = (0, import_node_crypto3.randomUUID)();
2949
- const ttlMs = Math.max(1, seconds) * 1e3;
1595
+ const owner = crypto.randomUUID();
1596
+ const ttlMs = Math.max(1, seconds) * 1000;
2950
1597
  const client = this.client;
2951
1598
  return {
2952
1599
  async acquire() {
@@ -2975,13 +1622,7 @@ var RedisStore = class {
2975
1622
  end
2976
1623
  `;
2977
1624
  const evalClient = client;
2978
- const result = await evalClient.eval(
2979
- luaScript,
2980
- 1,
2981
- lockKey,
2982
- owner,
2983
- extensionSeconds.toString()
2984
- );
1625
+ const result = await evalClient.eval(luaScript, 1, lockKey, owner, extensionSeconds.toString());
2985
1626
  return result === 1;
2986
1627
  },
2987
1628
  async getRemainingTime() {
@@ -2991,7 +1632,7 @@ var RedisStore = class {
2991
1632
  const retryInterval = options?.retryInterval ?? options?.sleepMillis ?? 100;
2992
1633
  const maxRetries = options?.maxRetries ?? Number.POSITIVE_INFINITY;
2993
1634
  const signal = options?.signal;
2994
- const deadline = Date.now() + Math.max(0, secondsToWait) * 1e3;
1635
+ const deadline = Date.now() + Math.max(0, secondsToWait) * 1000;
2995
1636
  let attempt = 0;
2996
1637
  while (Date.now() <= deadline && attempt < maxRetries) {
2997
1638
  if (signal?.aborted) {
@@ -3005,25 +1646,19 @@ var RedisStore = class {
3005
1646
  }
3006
1647
  }
3007
1648
  attempt++;
3008
- 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);
3009
1650
  await sleep(delay);
3010
1651
  }
3011
- throw new LockTimeoutError(
3012
- `Failed to acquire lock '${name}' within ${secondsToWait} seconds.`
3013
- );
1652
+ throw new LockTimeoutError(`Failed to acquire lock '${name}' within ${secondsToWait} seconds.`);
3014
1653
  }
3015
1654
  };
3016
1655
  }
3017
- };
1656
+ }
3018
1657
 
3019
1658
  // src/stores/TieredStore.ts
3020
- var TieredStore = class {
3021
- /**
3022
- * Initializes a new TieredStore.
3023
- *
3024
- * @param local - The L1 cache store (usually MemoryStore).
3025
- * @param remote - The L2 cache store (usually RedisStore or FileStore).
3026
- */
1659
+ class TieredStore {
1660
+ local;
1661
+ remote;
3027
1662
  constructor(local, remote) {
3028
1663
  this.local = local;
3029
1664
  this.remote = remote;
@@ -3072,11 +1707,11 @@ var TieredStore = class {
3072
1707
  }
3073
1708
  return this.local.ttl ? this.local.ttl(key) : null;
3074
1709
  }
3075
- };
1710
+ }
3076
1711
 
3077
1712
  // src/index.ts
3078
- var MemoryCacheProvider = class {
3079
- store = new MemoryStore();
1713
+ class MemoryCacheProvider {
1714
+ store = new MemoryStore;
3080
1715
  async get(key) {
3081
1716
  return this.store.get(key);
3082
1717
  }
@@ -3089,7 +1724,7 @@ var MemoryCacheProvider = class {
3089
1724
  async clear() {
3090
1725
  await this.store.flush();
3091
1726
  }
3092
- };
1727
+ }
3093
1728
  function resolveStoreConfig(core, options) {
3094
1729
  if (options) {
3095
1730
  return options;
@@ -3107,15 +1742,15 @@ function createStoreFactory(config) {
3107
1742
  const hasExplicitStores = Object.keys(stores).length > 0;
3108
1743
  if (!storeConfig) {
3109
1744
  if (name === "memory") {
3110
- return new MemoryStore();
1745
+ return new MemoryStore;
3111
1746
  }
3112
1747
  if (name === "null") {
3113
- return new NullStore();
1748
+ return new NullStore;
3114
1749
  }
3115
1750
  if (hasExplicitStores) {
3116
1751
  throw new Error(`Cache store '${name}' is not defined.`);
3117
1752
  }
3118
- return new MemoryStore();
1753
+ return new MemoryStore;
3119
1754
  }
3120
1755
  if (storeConfig.driver === "memory") {
3121
1756
  return new MemoryStore({ maxItems: storeConfig.maxItems });
@@ -3127,7 +1762,7 @@ function createStoreFactory(config) {
3127
1762
  return new RedisStore({ connection: storeConfig.connection, prefix: storeConfig.prefix });
3128
1763
  }
3129
1764
  if (storeConfig.driver === "null") {
3130
- return new NullStore();
1765
+ return new NullStore;
3131
1766
  }
3132
1767
  if (storeConfig.driver === "custom") {
3133
1768
  return storeConfig.store;
@@ -3180,7 +1815,7 @@ function createStoreFactory(config) {
3180
1815
  if (storeConfig.driver === "circuit-breaker") {
3181
1816
  const factory = createStoreFactory(config);
3182
1817
  const primary = factory(storeConfig.primary);
3183
- const fallback = storeConfig.fallback ? factory(storeConfig.fallback) : void 0;
1818
+ const fallback = storeConfig.fallback ? factory(storeConfig.fallback) : undefined;
3184
1819
  return new CircuitBreakerStore(primary, {
3185
1820
  maxFailures: storeConfig.maxFailures,
3186
1821
  resetTimeout: storeConfig.resetTimeout,
@@ -3190,16 +1825,18 @@ function createStoreFactory(config) {
3190
1825
  throw new Error(`Unsupported cache driver '${storeConfig.driver}'.`);
3191
1826
  };
3192
1827
  }
3193
- var OrbitStasis = class {
1828
+
1829
+ class OrbitStasis {
1830
+ options;
1831
+ manager;
3194
1832
  constructor(options) {
3195
1833
  this.options = options;
3196
1834
  }
3197
- manager;
3198
1835
  install(core) {
3199
1836
  const resolvedConfig = resolveStoreConfig(core, this.options);
3200
1837
  const exposeAs = resolvedConfig.exposeAs ?? "cache";
3201
1838
  const defaultStore = resolvedConfig.default ?? (resolvedConfig.provider ? "default" : "memory");
3202
- 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;
3203
1840
  const prefix = resolvedConfig.prefix ?? "";
3204
1841
  const logger = core.logger;
3205
1842
  logger.info(`[OrbitCache] Initializing Cache (Exposed as: ${exposeAs})`);
@@ -3214,21 +1851,16 @@ var OrbitStasis = class {
3214
1851
  const key = payload.key ? ` (key: ${payload.key})` : "";
3215
1852
  logger.error(`[OrbitCache] cache event '${event}' failed${key}`, error);
3216
1853
  });
3217
- const stores = resolvedConfig.stores ?? (resolvedConfig.provider ? { default: { driver: "provider", provider: resolvedConfig.provider } } : void 0);
3218
- const manager = new CacheManager(
3219
- createStoreFactory({ ...resolvedConfig, stores }),
3220
- {
3221
- default: defaultStore,
3222
- prefix,
3223
- defaultTtl
3224
- },
3225
- events,
3226
- {
3227
- mode: resolvedConfig.eventsMode ?? "async",
3228
- throwOnError: resolvedConfig.throwOnEventError,
3229
- onError: onEventError
3230
- }
3231
- );
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
+ });
3232
1864
  this.manager = manager;
3233
1865
  core.adapter.use("*", async (c, next) => {
3234
1866
  c.set(exposeAs, manager);
@@ -3243,33 +1875,12 @@ var OrbitStasis = class {
3243
1875
  }
3244
1876
  return this.manager;
3245
1877
  }
3246
- };
1878
+ }
3247
1879
  function orbitCache(core, options = {}) {
3248
1880
  const orbit = new OrbitStasis(options);
3249
1881
  orbit.install(core);
3250
1882
  return orbit.getCache();
3251
1883
  }
3252
1884
  var OrbitCache = OrbitStasis;
3253
- // Annotate the CommonJS export names for ESM import in node:
3254
- 0 && (module.exports = {
3255
- CacheManager,
3256
- CacheRepository,
3257
- CircuitBreakerStore,
3258
- FileStore,
3259
- LockTimeoutError,
3260
- MarkovPredictor,
3261
- MemoryCacheProvider,
3262
- MemoryStore,
3263
- NullStore,
3264
- OrbitCache,
3265
- OrbitStasis,
3266
- PredictiveStore,
3267
- RateLimiter,
3268
- RedisStore,
3269
- TieredStore,
3270
- isExpired,
3271
- isTaggableStore,
3272
- normalizeCacheKey,
3273
- sleep,
3274
- ttlToExpiresAt
3275
- });
1885
+
1886
+ //# debugId=66831AF7E477960E64756E2164756E21