@neezco/cache 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,10 +11,10 @@ declare const enum DELETE_REASON {
11
11
  */
12
12
  interface CacheConfigBase {
13
13
  /**
14
- * Callback invoked when a key expires naturally.
14
+ * Callback invoked when an entry expires naturally (not manually deleted).
15
15
  * @param key - The expired key.
16
16
  * @param value - The value associated with the expired key.
17
- * @param reason - The reason for deletion ('expired', or 'stale').
17
+ * @param reason - The reason for expiration: 'expired' (fully expired) or 'stale' (stale window expired).
18
18
  */
19
19
  onExpire?: (key: string, value: unknown, reason: Exclude<DELETE_REASON, DELETE_REASON.MANUAL>) => void;
20
20
  /**
@@ -30,16 +30,13 @@ interface CacheConfigBase {
30
30
  */
31
31
  defaultTtl: number;
32
32
  /**
33
- * Default stale window in milliseconds for entries that do not
34
- * specify their own `staleWindowMs`.
33
+ * Default stale window in milliseconds for entries without explicit `staleWindow`.
35
34
  *
36
- * This window determines how long an entry may continue to be
37
- * served as stale after it reaches its expiration time.
35
+ * Defines how long an entry can be served as stale after expiration.
36
+ * The window is relative to each entry's expiration moment, whether from
37
+ * explicit `ttl` or the cache's `defaultTtl`.
38
38
  *
39
- * The window is always relative to the entry’s own expiration
40
- * moment, regardless of whether that expiration comes from an
41
- * explicit `ttl` or from the cache’s default TTL.
42
- * @default null (No stale window)
39
+ * @default 0 (no stale window)
43
40
  */
44
41
  defaultStaleWindow: number;
45
42
  /**
@@ -55,283 +52,287 @@ interface CacheConfigBase {
55
52
  */
56
53
  maxMemorySize: number;
57
54
  /**
58
- * Controls how stale entries are handled when read from the cache.
59
- *
60
- * - true → stale entries are purged immediately after being returned.
61
- * - false → stale entries are retained after being returned.
62
- *
63
- * @default false
55
+ * Controls stale entry purging behavior on `get()` operations.
56
+ *
57
+ * Possible values:
58
+ * - `true`purge stale entries immediately after read.
59
+ * - `false` → retain stale entries after read.
60
+ * - `number (0-1)` → purge when `resourceUsage ≥ threshold` (uses `purgeResourceMetric`).
61
+ *
62
+ * Numeric threshold validation:
63
+ * - Must be 0 < value ≤ 1 (boolean fallback if invalid range)
64
+ * - Requires purgeResourceMetric to support thresholds (not 'fixed')
65
+ * - Requires matching configuration limits for the metric:
66
+ * * 'size' metric requires maxSize
67
+ * * 'memory' metric requires maxMemorySize
68
+ * * 'higher' metric requires both maxSize and maxMemorySize
69
+ * - Invalid numeric values fallback to default: 0.80 (with limits) or false (without)
70
+ *
71
+ * Environment notes:
72
+ * - Backend: `"memory"` and `"higher"` metrics available; frontend: only `"size"`.
73
+ * - Can be overridden per-read via `get(key, { purgeStale })`.
74
+ *
75
+ * Defaults:
76
+ * - With matching limits → `0.80` (80% resource usage).
77
+ * - Without matching limits → `false`.
64
78
  */
65
- purgeStaleOnGet: boolean;
79
+ purgeStaleOnGet: PurgeMode;
66
80
  /**
67
- * Controls how stale entries are handled during sweep operations.
81
+ * Controls stale entry purging behavior during sweep operations.
82
+ *
83
+ * Possible values:
84
+ * - `true` → purge stale entries during sweeps.
85
+ * - `false` → retain stale entries during sweeps.
86
+ * - `number (0-1)` → purge when `resourceUsage ≥ threshold` (uses `purgeResourceMetric`).
87
+ *
88
+ * Numeric threshold validation:
89
+ * - Must be 0 < value ≤ 1 (boolean fallback if invalid range)
90
+ * - Requires purgeResourceMetric to support thresholds (not 'fixed')
91
+ * - Requires matching configuration limits for the metric:
92
+ * * 'size' metric requires maxSize
93
+ * * 'memory' metric requires maxMemorySize
94
+ * * 'higher' metric requires both maxSize and maxMemorySize
95
+ * - Invalid numeric values fallback to default: 0.5 (with limits) or true (without)
96
+ *
97
+ * Prevents stale entry accumulation when enabled. Without limits, defaults to `true`
98
+ * to prevent unbounded growth.
99
+ *
100
+ * Environment notes:
101
+ * - Backend: `"memory"` and `"higher"` metrics available; frontend: only `"size"`.
102
+ *
103
+ * Defaults:
104
+ * - With matching limits → `0.5` (50% resource usage).
105
+ * - Without matching limits → `true` (prevent unbounded accumulation).
106
+ */
107
+ purgeStaleOnSweep: PurgeMode;
108
+ /**
109
+ * Metric used to evaluate resource usage for threshold-based stale purging.
68
110
  *
69
- * - true → stale entries are purged during sweeps.
70
- * - false → stale entries are retained during sweeps.
111
+ * Applies when `purgeStaleOnGet` or `purgeStaleOnSweep` are numeric (0-1).
71
112
  *
72
- * @default false
113
+ * Metric options:
114
+ * - `"size"` → normalized entry count (`current / maxSize`).
115
+ * - `"memory"` → normalized RAM (`currentMB / maxMemorySize`).
116
+ * - `"higher"` → max of both metrics (recommended for dual limits).
117
+ * - `"fixed"` → disable threshold purging; only bool values apply.
118
+ *
119
+ * Environment support:
120
+ * - Backend: all metrics available.
121
+ * - Frontend: only `"size"`; numeric thresholds fallback to `"fixed"`.
122
+ *
123
+ * Auto-selection (if not specified):
124
+ * - Backend: `"higher"` (both limits) → `"memory"` → `"size"` → `"fixed"`.
125
+ * - Frontend: `"size"` (if valid) → `"fixed"`.
126
+ *
127
+ * @default Depends on environment and valid limits.
73
128
  */
74
- purgeStaleOnSweep: boolean;
129
+ purgeResourceMetric?: "memory" | "size" | "higher" | "fixed";
75
130
  /**
76
- * Whether to automatically start the sweep process when the cache is created.
77
- *
78
- * - true → sweep starts automatically.
79
- * - false → sweep does not start automatically, allowing manual control.
131
+ * Auto-start sweep process on cache initialization.
80
132
  *
81
133
  * @internal
82
134
  * @default true
83
135
  */
84
136
  _autoStartSweep: boolean;
85
137
  /**
86
- * Allowed expired ratio for the cache instance.
138
+ * @internal Maximum allowed ratio of expired entries before aggressive sweep.
87
139
  */
88
140
  _maxAllowExpiredRatio: number;
89
141
  }
90
142
  /**
91
- * Public configuration options for the TTL cache.
143
+ * Purge mode: boolean for immediate/skip, or 0-1 for threshold-based purging.
144
+ */
145
+ type PurgeMode = boolean | number;
146
+ /**
147
+ * Public cache configuration (all fields optional).
92
148
  */
93
149
  type CacheOptions = Partial<CacheConfigBase>;
94
150
  /**
95
- * Options for `invalidateTag` operation. Kept intentionally extensible so
96
- * future flags can be added without breaking callers.
151
+ * Options for tag invalidation. Extensible for forward-compatibility.
97
152
  */
98
153
  interface InvalidateTagOptions {
99
- /** If true, mark affected entries as stale instead of fully expired. */
154
+ /**
155
+ * If true, mark entries as stale instead of fully expired.
156
+ * They remain accessible via stale window if configured.
157
+ */
100
158
  asStale?: boolean;
101
159
  [key: string]: unknown;
102
160
  }
103
161
  /**
104
- * Status of a cache entry.
162
+ * Entry status: fresh, stale, or expired.
105
163
  */
106
164
  declare enum ENTRY_STATUS {
107
- /** The entry is fresh and valid. */
165
+ /** Valid and within TTL. */
108
166
  FRESH = "fresh",
109
- /** The entry is stale but can still be served. */
167
+ /** Expired but within stale window; still served. */
110
168
  STALE = "stale",
111
- /** The entry has expired and is no longer valid. */
169
+ /** Beyond stale window; not served. */
112
170
  EXPIRED = "expired",
113
171
  }
114
172
  /**
115
- * Metadata returned when includeMetadata is enabled in get().
116
- * Contains complete information about a cache entry including
117
- * timing, status, and associated tags.
173
+ * Metadata returned from `get()` with `includeMetadata: true`.
174
+ * Provides complete entry state including timing, status, and tags.
118
175
  */
119
176
  interface EntryMetadata<T = unknown> {
120
177
  /** The cached value. */
121
178
  data: T;
122
- /** Absolute timestamp when this entry becomes fully expired (in milliseconds). */
179
+ /** Absolute timestamp (ms) when entry expires. */
123
180
  expirationTime: number;
124
- /** Absolute timestamp when the stale window expires (in milliseconds). */
181
+ /** Absolute timestamp (ms) when stale window ends. */
125
182
  staleWindowExpiration: number;
126
- /** Current status of the entry (fresh, stale, or expired). */
183
+ /** Current entry status. */
127
184
  status: ENTRY_STATUS;
128
- /** Tags associated with this entry, or null if no tags are set. */
185
+ /** Associated tags for group invalidation, or null. */
129
186
  tags: string[] | null;
130
187
  }
131
- //#endregion
132
- //#region src/index.d.ts
133
188
  /**
134
- * A TTL (Time-To-Live) cache implementation with support for expiration,
135
- * stale windows, tag-based invalidation, and automatic sweeping.
136
- *
137
- * Provides O(1) constant-time operations for all core methods.
138
- *
139
- * @example
140
- * ```typescript
141
- * const cache = new LocalTtlCache();
142
- * cache.set("user:123", { name: "Alice" }, { ttl: 5 * 60 * 1000 });
143
- * const user = cache.get("user:123"); // { name: "Alice" }
144
- * ```
189
+ * Options for `get()` without metadata (default).
190
+ * Returns only the cached value.
145
191
  */
146
- declare class LocalTtlCache {
147
- private state;
192
+ interface GetOptionsWithoutMetadata {
148
193
  /**
149
- * Creates a new cache instance.
150
- *
151
- * @param options - Configuration options for the cache (defaultTtl, defaultStaleWindow, maxSize, etc.)
152
- *
153
- * @example
154
- * ```typescript
155
- * const cache = new LocalTtlCache({
156
- * defaultTtl: 30 * 60 * 1000, // 30 minutes
157
- * defaultStaleWindow: 5 * 60 * 1000, // 5 minutes
158
- * maxSize: 500_000, // Maximum 500_000 entries
159
- * onExpire: (key, value) => console.log(`Expired: ${key}`),
160
- * onDelete: (key, value, reason) => console.log(`Deleted: ${key}, reason: ${reason}`),
161
- * });
162
- * ```
194
+ * If false (or omitted), returns value only without metadata.
195
+ * @default false
163
196
  */
164
- constructor(options?: CacheOptions);
197
+ includeMetadata?: false;
165
198
  /**
166
- * Gets the current number of entries tracked by the cache.
167
- *
168
- * This value may include entries that are already expired but have not yet been
169
- * removed by the lazy cleanup system. Expired keys are cleaned only when it is
170
- * efficient to do so, so the count can temporarily be higher than the number of
171
- * actually valid (non‑expired) entries.
199
+ * Controls stale entry purging on this read.
172
200
  *
173
- * @returns The number of entries currently stored (including entries pending cleanup)
201
+ * - `true` purge immediately after return.
202
+ * - `false` → keep stale entries.
203
+ * - `number (0-1)` → purge at resource usage threshold.
174
204
  *
175
- * @example
176
- * ```typescript
177
- * console.log(cache.size); // e.g., 42
178
- * ```
205
+ * Overrides global `purgeStaleOnGet` setting.
179
206
  */
180
- get size(): number;
207
+ purgeStale?: PurgeMode;
208
+ }
209
+ /**
210
+ * Options for `get()` with metadata.
211
+ * Returns value and complete entry state.
212
+ */
213
+ interface GetOptionsWithMetadata {
181
214
  /**
182
- * Retrieves a value from the cache.
183
- *
184
- * Returns the value if it exists and is not fully expired. If an entry is in the
185
- * stale window (expired but still within staleWindow), the stale value is returned.
186
- *
187
- * @param key - The key to retrieve
188
- * @param options - Optional configuration object
189
- * @param options.includeMetadata - If true, returns entry metadata (data, status, expirationTime, staleWindowExpiration, tags). Defaults to false
190
- * @returns The cached value, or entry metadata if includeMetadata is true. Returns undefined if not found or expired
191
- *
192
- * @example
193
- * ```typescript
194
- * // Get value
195
- * const user = cache.get("user:123");
196
- *
197
- * // Get with metadata
198
- * const entry = cache.get("user:123", { includeMetadata: true });
199
- * if (entry) {
200
- * console.log(entry.data);
201
- * console.log(entry.status);
202
- * }
203
- * ```
215
+ * If true, returns `EntryMetadata<T>` object with value, timing, and tags.
204
216
  */
205
- get<T = unknown>(key: string): T | undefined;
206
- get<T = unknown>(key: string, options: {
207
- includeMetadata: true;
208
- }): EntryMetadata<T> | undefined;
209
- get<T = unknown>(key: string, options: {
210
- includeMetadata: false;
211
- }): T | undefined;
217
+ includeMetadata: true;
212
218
  /**
213
- * Sets or updates a value in the cache.
219
+ * Controls stale entry purging on this read.
214
220
  *
215
- * If the key already exists, it will be completely replaced.
221
+ * - `true` purge immediately after return.
222
+ * - `false` → keep stale entries.
223
+ * - `number (0-1)` → purge at resource usage threshold.
216
224
  *
217
- * @param key - The key under which to store the value
218
- * @param value - The value to cache (any type)
219
- * @param options - Optional configuration for this specific entry
220
- * @param options.ttl - Time-To-Live in milliseconds. Defaults to `defaultTtl`
221
- * @param options.staleWindow - How long to serve stale data after expiration (milliseconds)
222
- * @param options.tags - One or more tags for group invalidation
223
- * @returns True if the entry was set or updated, false if rejected due to limits or invalid input
225
+ * Overrides global `purgeStaleOnGet` setting.
226
+ */
227
+ purgeStale?: PurgeMode;
228
+ }
229
+ /**
230
+ * Options for `set()` method.
231
+ * Controls TTL, stale window, and tagging per entry.
232
+ */
233
+ interface SetOptions {
234
+ /**
235
+ * Time-To-Live in milliseconds.
236
+ * Determines fresh period before expiration.
224
237
  *
225
- * @example
226
- * ```typescript
227
- * const success = cache.set("user:123", { name: "Alice" }, {
228
- * ttl: 5 * 60 * 1000,
229
- * staleWindow: 1 * 60 * 1000,
230
- * tags: "user:123",
231
- * });
238
+ * Special values:
239
+ * - `0` | `Infinity` → entry never expires
232
240
  *
233
- * if (!success) {
234
- * console.log("Entry was rejected due to size or memory limits");
235
- * }
236
- * ```
241
+ * Falls back to cache's `defaultTtl` if omitted.
242
+ */
243
+ ttl?: number;
244
+ /**
245
+ * Stale window duration in milliseconds.
237
246
  *
238
- * @edge-cases
239
- * - Overwriting an existing key replaces it completely
240
- * - If `ttl` is 0 or Infinite, the entry never expires
241
- * - If `staleWindow` is larger than `ttl`, the entry can be served as stale longer than it was fresh
242
- * - Tags are optional; only necessary for group invalidation via `invalidateTag()`
243
- * - Returns `false` if value is `undefined` (existing value remains untouched)
244
- * - Returns `false` if new key would exceed [`maxSize`](./docs/configuration.md#maxsize-number) limit
245
- * - Returns `false` if new key would exceed [`maxMemorySize`](./docs/configuration.md#maxmemorysize-number) limit
246
- * - Updating existing keys always succeeds, even at limit
247
+ * Determines how long entry serves stale after expiration.
248
+ * Falls back to cache's `defaultStaleWindow` if omitted.
247
249
  */
248
- set(key: string, value: unknown, options?: {
249
- ttl?: number;
250
- staleWindow?: number;
251
- tags?: string | string[];
252
- }): boolean;
250
+ staleWindow?: number;
253
251
  /**
254
- * Deletes a specific key from the cache.
252
+ * One or more tags for group-based invalidation.
255
253
  *
256
- * @param key - The key to delete
257
- * @returns True if the key was deleted, false if it didn't exist
254
+ * Tags enable batch invalidation via `invalidateTag()`.
255
+ * Invalidating ANY tag on an entry invalidates the whole entry.
258
256
  *
259
- * @example
260
- * ```typescript
261
- * const wasDeleted = cache.delete("user:123");
262
- * ```
257
+ * Falls back to cache's default if omitted.
258
+ */
259
+ tags?: string | string[];
260
+ }
261
+ /**
262
+ * TTL cache public interface.
263
+ * Implemented by `LocalTtlCache` class.
264
+ */
265
+ interface LocalTtlCacheInterface {
266
+ /**
267
+ * Current number of entries (may include expired entries pending cleanup).
268
+ */
269
+ readonly size: number;
270
+ /**
271
+ * Retrieves value from cache.
272
+ * Returns fresh, stale, or undefined (expired or not found).
263
273
  *
264
- * @edge-cases
265
- * - Triggers the `onDelete` callback with reason `'manual'`
266
- * - Does not trigger the `onExpire` callback
267
- * - Returns `false` if the key was already expired
268
- * - Deleting a non-existent key returns `false` without error
274
+ * @overload `get<T>(key)` → `T | undefined` (no metadata)
275
+ * @overload `get<T>(key, { includeMetadata: true })``EntryMetadata<T> | undefined` (with metadata)
276
+ */
277
+ get<T = unknown>(key: string): T | undefined;
278
+ get<T = unknown>(key: string, options: GetOptionsWithMetadata): EntryMetadata<T> | undefined;
279
+ get<T = unknown>(key: string, options: GetOptionsWithoutMetadata): T | undefined;
280
+ /**
281
+ * Sets or replaces a cache entry.
282
+ * @returns true if set/updated, false if rejected (limits/invalid).
283
+ */
284
+ set(key: string, value: unknown, options?: SetOptions): boolean;
285
+ /**
286
+ * Deletes a specific key from cache.
287
+ * @returns true if deleted, false if not found.
269
288
  */
270
289
  delete(key: string): boolean;
271
290
  /**
272
- * Checks if a key exists in the cache and is not fully expired.
273
- *
274
- * Returns true if the key exists and is either fresh or within the stale window.
275
- * Use this when you only need to check existence without retrieving the value.
276
- *
277
- * @param key - The key to check
278
- * @returns True if the key exists and is valid, false otherwise
279
- *
280
- * @example
281
- * ```typescript
282
- * if (cache.has("user:123")) {
283
- * // Key exists (either fresh or stale)
284
- * }
285
- * ```
286
- *
287
- * @edge-cases
288
- * - Returns `false` if the key doesn't exist
289
- * - Returns `false` if the key has expired beyond the stale window
290
- * - Returns `true` if the key is in the stale window (still being served)
291
- * - Both `has()` and `get()` have O(1) complexity; prefer `get()` if you need the value
291
+ * Checks if key exists (fresh or stale).
292
+ * @returns true if valid, false if not found or fully expired.
292
293
  */
293
294
  has(key: string): boolean;
294
295
  /**
295
- * Removes all entries from the cache at once.
296
- *
297
- * This is useful for resetting the cache or freeing memory when needed.
298
- * The `onDelete` callback is NOT invoked during clear (intentional optimization).
299
- *
300
- * @example
301
- * ```typescript
302
- * cache.clear(); // cache.size is now 0
303
- * ```
304
- *
305
- * @edge-cases
306
- * - The `onDelete` callback is NOT triggered during clear
307
- * - Clears both expired and fresh entries
308
- * - Resets `cache.size` to 0
296
+ * Removes all entries from cache.
297
+ * Does NOT trigger `onDelete` callbacks (optimization).
309
298
  */
310
299
  clear(): void;
311
300
  /**
312
- * Marks all entries with one or more tags as expired (or stale, if requested).
313
- *
314
- * If an entry has multiple tags, invalidating ANY of those tags will invalidate the entry.
315
- *
316
- * @param tags - A single tag (string) or array of tags to invalidate
317
- * @param asStale - If true, marks entries as stale instead of fully expired (still served from stale window)
318
- *
319
- * @example
320
- * ```typescript
321
- * // Invalidate a single tag
322
- * cache.invalidateTag("user:123");
301
+ * Marks entries with given tags as expired (or stale).
302
+ * Invalidating ANY tag on an entry invalidates it.
303
+ */
304
+ invalidateTag(tags: string | string[], options?: InvalidateTagOptions): void;
305
+ }
306
+ //#endregion
307
+ //#region src/index.d.ts
308
+ /**
309
+ * A TTL (Time-To-Live) cache implementation with support for expiration,
310
+ * stale windows, tag-based invalidation, and smart automatic sweeping.
311
+ *
312
+ * Provides O(1) constant-time operations for all core methods with support for:
313
+ * - Expiration and stale windows
314
+ * - Tag-based invalidation
315
+ * - Automatic sweeping
316
+ */
317
+ declare class LocalTtlCache implements LocalTtlCacheInterface {
318
+ private state;
319
+ /**
320
+ * Creates a new cache instance.
323
321
  *
324
- * // Invalidate multiple tags
325
- * cache.invalidateTag(["user:123", "posts:456"]);
326
- * ```
322
+ * @param options - Configuration options for the cache (defaultTtl, defaultStaleWindow, maxSize, etc.)
327
323
  *
328
- * @edge-cases
329
- * - Does not throw errors if a tag has no associated entries
330
- * - Invalidating a tag doesn't prevent new entries from being tagged with it later
331
- * - The `onDelete` callback is triggered with reason `'expired'` (even if `asStale` is true)
332
324
  */
325
+ constructor(options?: CacheOptions);
326
+ get size(): number;
327
+ get<T = unknown>(key: string): T | undefined;
328
+ get<T = unknown>(key: string, options: GetOptionsWithMetadata): EntryMetadata<T>;
329
+ get<T = unknown>(key: string, options: GetOptionsWithoutMetadata): T | undefined;
330
+ set(key: string, value: unknown, options?: SetOptions): boolean;
331
+ delete(key: string): boolean;
332
+ has(key: string): boolean;
333
+ clear(): void;
333
334
  invalidateTag(tags: string | string[], options?: InvalidateTagOptions): void;
334
335
  }
335
336
  //#endregion
336
- export { type CacheOptions, type EntryMetadata, type InvalidateTagOptions, LocalTtlCache };
337
+ export { type CacheOptions, ENTRY_STATUS, type EntryMetadata, type GetOptionsWithMetadata, type GetOptionsWithoutMetadata, type InvalidateTagOptions, LocalTtlCache, type LocalTtlCacheInterface, type PurgeMode, type SetOptions };
337
338
  //# sourceMappingURL=index.d.mts.map