fast-map-cache 1.0.1 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,7 +13,7 @@ A high-performance in-memory LRU cache for Node.js and browsers, implemented wit
13
13
 
14
14
  In real-world scenarios, `fast-map-cache` demonstrates significant performance gains by preventing expensive operations:
15
15
 
16
- - **🚀 Proven Performance**: Delivers a **2x to 3x** performance boost in real-world I/O and CPU-bound scenarios by preventing expensive operations.
16
+ - **🚀 Measured Benefits**: In the simulated API and CPU-bound workloads included in this repo, caching reduced end-to-end work by roughly **2x to 6x** when it could avoid expensive operations. Results depend on hit rate and workload cost.
17
17
 
18
18
  For a detailed analysis, see the full [**Performance Report**](./docs/performance-report.md).
19
19
 
@@ -78,11 +78,13 @@ setTimeout(() => {
78
78
  }, 6000)
79
79
  ```
80
80
 
81
+ > Note: TTL is based on the timestamp of the last `set()` operation. A `get()` does not refresh TTL (i.e., it is not access-renewal).
82
+
81
83
  ### ❗ Important Note for Node.js Users
82
84
 
83
- When using `FastCacheWithTTL` with the `autoCleanup: true` option in a Node.js environment, a `setInterval` is used to periodically clean up expired items. This timer will prevent the Node.js process from exiting gracefully on its own.
85
+ When using `FastCacheWithTTL` with the `autoCleanup: true` option in Node.js, an internal `setInterval` is used for periodic cleanup. The timer is created with `unref()` when available, so it will not keep the process alive on its own.
84
86
 
85
- **You must manually call the `destroy()` method before your application exits to clear the timer.**
87
+ **It is still recommended to call `destroy()` before your application exits** to proactively clear resources and avoid potential hangs in long-running tasks or test environments.
86
88
 
87
89
  ```typescript
88
90
  const cache = createCacheWithTTL({ maxSize: 100, autoCleanup: true, ttl: 60000 })
@@ -97,7 +99,7 @@ For more advanced use cases, including batch operations and presets, please see
97
99
 
98
100
  ## API Reference
99
101
 
100
- The cache instance, created by `createCache` or `createCacheWithTTL`, implements the `IFastCache` interface.
102
+ The cache instance created by `createCache` implements `IFastCache`. `FastCacheWithTTL` additionally satisfies `IFastTTLCache` and exposes `cleanup()` and `destroy()`.
101
103
 
102
104
  ### `get(key: K): V | undefined`
103
105
 
@@ -123,11 +125,11 @@ Clears all items from the cache.
123
125
 
124
126
  ### `getMany(keys: K[]): Map<K, V>`
125
127
 
126
- Retrieves multiple values for an array of keys. Returns a `Map` containing the found key-value pairs.
128
+ Retrieves multiple values for an array of keys. Returns a `Map` containing the found key-value pairs. This is a convenience API, not a guaranteed performance optimization.
127
129
 
128
130
  ### `setMany(entries: [K, V][]): void`
129
131
 
130
- Adds or updates multiple key-value pairs from an array of entries.
132
+ Adds or updates multiple key-value pairs from an array of entries. This is a convenience API, not a guaranteed performance optimization.
131
133
 
132
134
  ### `size: number` (getter)
133
135
 
@@ -158,13 +160,13 @@ Manually triggers the cleanup of expired items. Returns the number of items that
158
160
 
159
161
  ### `destroy(): void` (For `FastCacheWithTTL` only)
160
162
 
161
- Clears the automatic cleanup timer if `autoCleanup` was enabled. **Crucial for graceful shutdown in Node.js.**
163
+ Clears the automatic cleanup timer if `autoCleanup` was enabled. Useful for proactively releasing timer resources in long-lived processes or tests.
162
164
 
163
165
  ## Benchmark
164
166
 
165
167
  This library is designed for high performance in real-world scenarios. The core value of a cache is not just the raw speed of its `get`/`set` operations, but its ability to prevent expensive computations or network requests.
166
168
 
167
- Our benchmarks show that in realistic I/O-bound and CPU-bound scenarios, `fast-map-cache` provides a **2x to 3x performance boost on average**. The actual improvement depends heavily on the cost of the operation being cached.
169
+ The included benchmarks show clear gains when cache hits avoid expensive work such as simulated API calls or CPU-heavy computations. Actual results depend on hit rate, cache size, and the real cost of the work being skipped.
168
170
 
169
171
  ## Contributing
170
172
 
package/README_zh-CN.md CHANGED
@@ -13,7 +13,7 @@
13
13
 
14
14
  在真实业务场景中,`fast-map-cache` 通过有效避免高成本操作,展现出显著的性能优势:
15
15
 
16
- - **🚀 可靠性能**: 在真实的 I/O CPU 密集型场景中,通过避免高成本操作,带来 **2-3 倍** 的性能提升。
16
+ - **🚀 可复核收益**: 在仓库自带的模拟 API CPU 密集场景里,当缓存命中能够避开昂贵操作时,端到端收益大致在 **2 6 倍**。实际效果仍取决于命中率和被缓存工作的成本。
17
17
 
18
18
  详细分析请参阅完整的 [**性能报告**](./docs/performance-report.md)。
19
19
 
@@ -80,11 +80,13 @@ setTimeout(() => {
80
80
  }, 6000)
81
81
  ```
82
82
 
83
+ > 提示:TTL 以最近一次 `set()` 的时间为基准;`get()` 不会刷新 TTL(非“访问续期”)。
84
+
83
85
  ### ❗ Node.js 用户重要提示
84
86
 
85
- 在 Node.js 环境下使用 `FastCacheWithTTL` 并开启 `autoCleanup: true` 选项时,系统会使用 `setInterval` 定时清理过期缓存。这个定时器会阻止 Node.js 进程正常退出。
87
+ 在 Node.js 下使用 `autoCleanup: true` 时,内部会使用 `setInterval` 做周期清理;当环境支持时定时器会调用 `unref()`,因此不会单独阻止进程退出。
86
88
 
87
- **您必须在程序退出前手动调用 `destroy()` 方法来清除该定时器。**
89
+ **仍然建议在应用退出前调用 `destroy()` 主动释放资源**,可避免长生命周期任务或测试环境中的潜在挂起。
88
90
 
89
91
  ```typescript
90
92
  const cache = createCacheWithTTL({ maxSize: 100, autoCleanup: true, ttl: 60000 })
@@ -97,7 +99,7 @@ cache.destroy()
97
99
 
98
100
  ## API 参考
99
101
 
100
- `createCache` `createCacheWithTTL` 创建的缓存实例均实现了 `IFastCache` 接口。
102
+ `createCache` 返回实现 `IFastCache` 的实例。`FastCacheWithTTL` 进一步满足 `IFastTTLCache`,并额外提供 `cleanup()` 和 `destroy()`。
101
103
 
102
104
  ### `get(key: K): V | undefined`
103
105
 
@@ -123,11 +125,11 @@ cache.destroy()
123
125
 
124
126
  ### `getMany(keys: K[]): Map<K, V>`
125
127
 
126
- 根据键数组批量获取值。返回一个包含已找到的键值对的 `Map`。
128
+ 根据键数组批量获取值。返回一个包含已找到的键值对的 `Map`。这是便捷 API,不保证一定比逐个调用更快。
127
129
 
128
130
  ### `setMany(entries: [K, V][]): void`
129
131
 
130
- 通过一个条目数组批量添加或更新键值对。
132
+ 通过一个条目数组批量添加或更新键值对。这是便捷 API,不保证一定比逐个调用更快。
131
133
 
132
134
  ### `size: number` (getter)
133
135
 
@@ -158,13 +160,13 @@ cache.destroy()
158
160
 
159
161
  ### `destroy(): void` (仅 `FastCacheWithTTL` 可用)
160
162
 
161
- 如果开启了 `autoCleanup`,此方法用于清除自动清理定时器。**这对于在 Node.js 中实现优雅停机至关重要。**
163
+ 如果开启了 `autoCleanup`,此方法用于清除自动清理定时器,适合在长生命周期进程或测试中主动释放资源。
162
164
 
163
165
  ## 性能基准
164
166
 
165
167
  本库为真实场景下的高性能而设计。缓存的核心价值不仅在于 `get`/`set` 操作的原始速度,更在于它能够有效避免昂贵的计算或网络请求。
166
168
 
167
- 我们的基准测试显示,在模拟真实世界的 I/O 密集和 CPU 密集场景下,`fast-map-cache` 平均能带来 **2 到 3 倍的性能提升**。实际提升效果取决于被缓存操作的成本。
169
+ 仓库内自带的 benchmark 显示:当缓存命中可以避开昂贵的 I/O CPU 计算时,收益会很明显。实际效果主要取决于命中率、缓存容量是否覆盖热点集,以及被缓存工作的真实成本。
168
170
 
169
171
  ## 贡献
170
172
 
package/dist/main.d.mts CHANGED
@@ -30,6 +30,10 @@ interface IFastCache<K extends CacheKey, V> {
30
30
  getStats(): CacheStats;
31
31
  cleanup?(): number;
32
32
  }
33
+ interface IFastTTLCache<K extends CacheKey, V> extends IFastCache<K, V> {
34
+ cleanup(): number;
35
+ destroy(): void;
36
+ }
33
37
  interface CacheNode<K extends CacheKey, V> {
34
38
  key: K;
35
39
  value: V;
@@ -66,6 +70,11 @@ declare class FastCache<K extends CacheKey, V> implements IFastCache<K, V> {
66
70
  protected addToHead(node: CacheNode<K, V>): void;
67
71
  protected removeNode(node: CacheNode<K, V>): void;
68
72
  protected moveToHead(node: CacheNode<K, V>): void;
73
+ /**
74
+ * 统一删除入口:删除 Map 记录并从 LRU 链表移除节点。
75
+ * 子类(如 TTL 版本)可覆写该方法以保证多链表同步删除。
76
+ */
77
+ protected deleteNode(node: CacheNode<K, V>): void;
69
78
  protected removeTail(): void;
70
79
  }
71
80
  //#endregion
@@ -76,12 +85,11 @@ declare class FastCache<K extends CacheKey, V> implements IFastCache<K, V> {
76
85
  * 以实现 O(M) 复杂度的过期项目清理(M 为过期数量)。
77
86
  *
78
87
  * @important 在 Node.js 环境下使用 `autoCleanup: true` 时,
79
- * 必须在程序退出前手动调用 `destroy()` 方法来清理定时器,
80
- * 否则定时器会阻止 Node.js 进程正常退出。
88
+ * 内部定时器在支持的运行时会调用 `unref()`,因此通常不会单独阻止进程退出。
89
+ * 如果缓存生命周期明确,仍建议调用 `destroy()` 主动清理定时器。
81
90
  */
82
- declare class FastCacheWithTTL<K extends CacheKey, V> extends FastCache<K, V> {
91
+ declare class FastCacheWithTTL<K extends CacheKey, V> extends FastCache<K, V> implements IFastTTLCache<K, V> {
83
92
  private readonly ttl;
84
- private readonly autoCleanup;
85
93
  private cleanupTimer;
86
94
  private readonly timeHead;
87
95
  private readonly timeTail;
@@ -92,17 +100,16 @@ declare class FastCacheWithTTL<K extends CacheKey, V> extends FastCache<K, V> {
92
100
  delete(key: K): boolean;
93
101
  clear(): void;
94
102
  has(key: K): boolean;
95
- get size(): number;
96
- get capacity(): number;
97
- setMany(entries: [K, V][]): void;
98
- getMany(keys: K[]): Map<K, V>;
99
103
  getStats(): CacheStats;
100
104
  cleanup(): number;
101
105
  /**
102
- * 清理定时器,防止在 Node.js 环境中进程无法正常退出。
103
- * 如果开启了 `autoCleanup`,你应当在应用关闭前调用此方法。
106
+ * 清理自动清理定时器,适合在可控生命周期中主动释放资源。
104
107
  */
105
108
  destroy(): void;
109
+ /**
110
+ * 覆写统一删除入口:先维护 TTL 链表,再调用父类删除 LRU/Map。
111
+ */
112
+ protected deleteNode(node: CacheNode<K, V>): void;
106
113
  private isExpired;
107
114
  private _addToTimeListTail;
108
115
  private _removeFromTimeList;
@@ -119,4 +126,4 @@ declare const CachePresets: {
119
126
  readonly tempCache: <T>(maxSize?: number, ttl?: number) => FastCacheWithTTL<string, T>;
120
127
  };
121
128
  //#endregion
122
- export { CacheKey, CacheNode, CacheOptions, CachePresets, CacheStats, FastCache, FastCacheWithTTL, IFastCache, createCache, createCacheWithTTL };
129
+ export { type CacheKey, type CacheNode, type CacheOptions, CachePresets, type CacheStats, FastCache, FastCacheWithTTL, type IFastCache, type IFastTTLCache, createCache, createCacheWithTTL };
package/dist/main.d.ts CHANGED
@@ -30,6 +30,10 @@ interface IFastCache<K extends CacheKey, V> {
30
30
  getStats(): CacheStats;
31
31
  cleanup?(): number;
32
32
  }
33
+ interface IFastTTLCache<K extends CacheKey, V> extends IFastCache<K, V> {
34
+ cleanup(): number;
35
+ destroy(): void;
36
+ }
33
37
  interface CacheNode<K extends CacheKey, V> {
34
38
  key: K;
35
39
  value: V;
@@ -66,6 +70,11 @@ declare class FastCache<K extends CacheKey, V> implements IFastCache<K, V> {
66
70
  protected addToHead(node: CacheNode<K, V>): void;
67
71
  protected removeNode(node: CacheNode<K, V>): void;
68
72
  protected moveToHead(node: CacheNode<K, V>): void;
73
+ /**
74
+ * 统一删除入口:删除 Map 记录并从 LRU 链表移除节点。
75
+ * 子类(如 TTL 版本)可覆写该方法以保证多链表同步删除。
76
+ */
77
+ protected deleteNode(node: CacheNode<K, V>): void;
69
78
  protected removeTail(): void;
70
79
  }
71
80
  //#endregion
@@ -76,12 +85,11 @@ declare class FastCache<K extends CacheKey, V> implements IFastCache<K, V> {
76
85
  * 以实现 O(M) 复杂度的过期项目清理(M 为过期数量)。
77
86
  *
78
87
  * @important 在 Node.js 环境下使用 `autoCleanup: true` 时,
79
- * 必须在程序退出前手动调用 `destroy()` 方法来清理定时器,
80
- * 否则定时器会阻止 Node.js 进程正常退出。
88
+ * 内部定时器在支持的运行时会调用 `unref()`,因此通常不会单独阻止进程退出。
89
+ * 如果缓存生命周期明确,仍建议调用 `destroy()` 主动清理定时器。
81
90
  */
82
- declare class FastCacheWithTTL<K extends CacheKey, V> extends FastCache<K, V> {
91
+ declare class FastCacheWithTTL<K extends CacheKey, V> extends FastCache<K, V> implements IFastTTLCache<K, V> {
83
92
  private readonly ttl;
84
- private readonly autoCleanup;
85
93
  private cleanupTimer;
86
94
  private readonly timeHead;
87
95
  private readonly timeTail;
@@ -92,17 +100,16 @@ declare class FastCacheWithTTL<K extends CacheKey, V> extends FastCache<K, V> {
92
100
  delete(key: K): boolean;
93
101
  clear(): void;
94
102
  has(key: K): boolean;
95
- get size(): number;
96
- get capacity(): number;
97
- setMany(entries: [K, V][]): void;
98
- getMany(keys: K[]): Map<K, V>;
99
103
  getStats(): CacheStats;
100
104
  cleanup(): number;
101
105
  /**
102
- * 清理定时器,防止在 Node.js 环境中进程无法正常退出。
103
- * 如果开启了 `autoCleanup`,你应当在应用关闭前调用此方法。
106
+ * 清理自动清理定时器,适合在可控生命周期中主动释放资源。
104
107
  */
105
108
  destroy(): void;
109
+ /**
110
+ * 覆写统一删除入口:先维护 TTL 链表,再调用父类删除 LRU/Map。
111
+ */
112
+ protected deleteNode(node: CacheNode<K, V>): void;
106
113
  private isExpired;
107
114
  private _addToTimeListTail;
108
115
  private _removeFromTimeList;
@@ -119,4 +126,4 @@ declare const CachePresets: {
119
126
  readonly tempCache: <T>(maxSize?: number, ttl?: number) => FastCacheWithTTL<string, T>;
120
127
  };
121
128
  //#endregion
122
- export { CacheKey, CacheNode, CacheOptions, CachePresets, CacheStats, FastCache, FastCacheWithTTL, IFastCache, createCache, createCacheWithTTL };
129
+ export { type CacheKey, type CacheNode, type CacheOptions, CachePresets, type CacheStats, FastCache, FastCacheWithTTL, type IFastCache, type IFastTTLCache, createCache, createCacheWithTTL };
package/dist/main.js CHANGED
@@ -1,102 +1,55 @@
1
- //#region rolldown:runtime
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __commonJS = (cb, mod) => function() {
9
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
10
- };
11
- var __copyProps = (to, from, except, desc) => {
12
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
13
- key = keys[i];
14
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
15
- get: ((k) => from[k]).bind(null, key),
16
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
17
- });
18
- }
19
- return to;
20
- };
21
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
22
- value: mod,
23
- enumerable: true
24
- }) : target, mod));
25
-
26
- //#endregion
27
-
28
- //#region node_modules/.pnpm/@oxc-project+runtime@0.72.3/node_modules/@oxc-project/runtime/src/helpers/typeof.js
29
- var require_typeof = __commonJS({ "node_modules/.pnpm/@oxc-project+runtime@0.72.3/node_modules/@oxc-project/runtime/src/helpers/typeof.js"(exports, module) {
30
- function _typeof$2(o) {
31
- "@babel/helpers - typeof";
32
- return module.exports = _typeof$2 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o$1) {
33
- return typeof o$1;
34
- } : function(o$1) {
35
- return o$1 && "function" == typeof Symbol && o$1.constructor === Symbol && o$1 !== Symbol.prototype ? "symbol" : typeof o$1;
36
- }, module.exports.__esModule = true, module.exports["default"] = module.exports, _typeof$2(o);
37
- }
38
- module.exports = _typeof$2, module.exports.__esModule = true, module.exports["default"] = module.exports;
39
- } });
40
-
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region \0@oxc-project+runtime@0.115.0/helpers/typeof.js
3
+ function _typeof(o) {
4
+ "@babel/helpers - typeof";
5
+ return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
6
+ return typeof o;
7
+ } : function(o) {
8
+ return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
9
+ }, _typeof(o);
10
+ }
41
11
  //#endregion
42
- //#region node_modules/.pnpm/@oxc-project+runtime@0.72.3/node_modules/@oxc-project/runtime/src/helpers/toPrimitive.js
43
- var require_toPrimitive = __commonJS({ "node_modules/.pnpm/@oxc-project+runtime@0.72.3/node_modules/@oxc-project/runtime/src/helpers/toPrimitive.js"(exports, module) {
44
- var _typeof$1 = require_typeof()["default"];
45
- function toPrimitive$1(t, r) {
46
- if ("object" != _typeof$1(t) || !t) return t;
47
- var e = t[Symbol.toPrimitive];
48
- if (void 0 !== e) {
49
- var i = e.call(t, r || "default");
50
- if ("object" != _typeof$1(i)) return i;
51
- throw new TypeError("@@toPrimitive must return a primitive value.");
52
- }
53
- return ("string" === r ? String : Number)(t);
54
- }
55
- module.exports = toPrimitive$1, module.exports.__esModule = true, module.exports["default"] = module.exports;
56
- } });
57
-
12
+ //#region \0@oxc-project+runtime@0.115.0/helpers/toPrimitive.js
13
+ function toPrimitive(t, r) {
14
+ if ("object" != _typeof(t) || !t) return t;
15
+ var e = t[Symbol.toPrimitive];
16
+ if (void 0 !== e) {
17
+ var i = e.call(t, r || "default");
18
+ if ("object" != _typeof(i)) return i;
19
+ throw new TypeError("@@toPrimitive must return a primitive value.");
20
+ }
21
+ return ("string" === r ? String : Number)(t);
22
+ }
58
23
  //#endregion
59
- //#region node_modules/.pnpm/@oxc-project+runtime@0.72.3/node_modules/@oxc-project/runtime/src/helpers/toPropertyKey.js
60
- var require_toPropertyKey = __commonJS({ "node_modules/.pnpm/@oxc-project+runtime@0.72.3/node_modules/@oxc-project/runtime/src/helpers/toPropertyKey.js"(exports, module) {
61
- var _typeof = require_typeof()["default"];
62
- var toPrimitive = require_toPrimitive();
63
- function toPropertyKey$1(t) {
64
- var i = toPrimitive(t, "string");
65
- return "symbol" == _typeof(i) ? i : i + "";
66
- }
67
- module.exports = toPropertyKey$1, module.exports.__esModule = true, module.exports["default"] = module.exports;
68
- } });
69
-
24
+ //#region \0@oxc-project+runtime@0.115.0/helpers/toPropertyKey.js
25
+ function toPropertyKey(t) {
26
+ var i = toPrimitive(t, "string");
27
+ return "symbol" == _typeof(i) ? i : i + "";
28
+ }
70
29
  //#endregion
71
- //#region node_modules/.pnpm/@oxc-project+runtime@0.72.3/node_modules/@oxc-project/runtime/src/helpers/defineProperty.js
72
- var require_defineProperty = __commonJS({ "node_modules/.pnpm/@oxc-project+runtime@0.72.3/node_modules/@oxc-project/runtime/src/helpers/defineProperty.js"(exports, module) {
73
- var toPropertyKey = require_toPropertyKey();
74
- function _defineProperty$2(e, r, t) {
75
- return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
76
- value: t,
77
- enumerable: !0,
78
- configurable: !0,
79
- writable: !0
80
- }) : e[r] = t, e;
81
- }
82
- module.exports = _defineProperty$2, module.exports.__esModule = true, module.exports["default"] = module.exports;
83
- } });
84
-
30
+ //#region \0@oxc-project+runtime@0.115.0/helpers/defineProperty.js
31
+ function _defineProperty(e, r, t) {
32
+ return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
33
+ value: t,
34
+ enumerable: !0,
35
+ configurable: !0,
36
+ writable: !0
37
+ }) : e[r] = t, e;
38
+ }
85
39
  //#endregion
86
40
  //#region src/fast-cache.ts
87
- var import_defineProperty$1 = __toESM(require_defineProperty());
88
41
  /**
89
42
  * 高性能 LRU 缓存实现
90
43
  * 使用 Map + 双向链表实现 O(1) 复杂度的所有操作
91
44
  */
92
45
  var FastCache = class {
93
46
  constructor(maxSize) {
94
- (0, import_defineProperty$1.default)(this, "cache", void 0);
95
- (0, import_defineProperty$1.default)(this, "head", void 0);
96
- (0, import_defineProperty$1.default)(this, "tail", void 0);
97
- (0, import_defineProperty$1.default)(this, "maxSize", void 0);
98
- (0, import_defineProperty$1.default)(this, "hits", 0);
99
- (0, import_defineProperty$1.default)(this, "misses", 0);
47
+ _defineProperty(this, "cache", void 0);
48
+ _defineProperty(this, "head", void 0);
49
+ _defineProperty(this, "tail", void 0);
50
+ _defineProperty(this, "maxSize", void 0);
51
+ _defineProperty(this, "hits", 0);
52
+ _defineProperty(this, "misses", 0);
100
53
  if (maxSize <= 0) throw new Error("Cache size must be positive");
101
54
  this.maxSize = maxSize;
102
55
  this.cache = /* @__PURE__ */ new Map();
@@ -109,7 +62,7 @@ var FastCache = class {
109
62
  const node = this.cache.get(key);
110
63
  if (node === void 0) {
111
64
  this.misses++;
112
- return void 0;
65
+ return;
113
66
  }
114
67
  this.moveToHead(node);
115
68
  this.hits++;
@@ -135,8 +88,7 @@ var FastCache = class {
135
88
  delete(key) {
136
89
  const node = this.cache.get(key);
137
90
  if (node === void 0) return false;
138
- this.cache.delete(key);
139
- this.removeNode(node);
91
+ this.deleteNode(node);
140
92
  return true;
141
93
  }
142
94
  clear() {
@@ -190,59 +142,64 @@ var FastCache = class {
190
142
  this.removeNode(node);
191
143
  this.addToHead(node);
192
144
  }
145
+ /**
146
+ * 统一删除入口:删除 Map 记录并从 LRU 链表移除节点。
147
+ * 子类(如 TTL 版本)可覆写该方法以保证多链表同步删除。
148
+ */
149
+ deleteNode(node) {
150
+ this.cache.delete(node.key);
151
+ this.removeNode(node);
152
+ }
193
153
  removeTail() {
194
154
  const lastNode = this.tail.prev;
195
- if (lastNode && lastNode !== this.head) {
196
- this.cache.delete(lastNode.key);
197
- this.removeNode(lastNode);
198
- }
155
+ if (lastNode && lastNode !== this.head) this.deleteNode(lastNode);
199
156
  }
200
157
  };
201
-
202
158
  //#endregion
203
159
  //#region src/fast-cache-ttl.ts
204
- var import_defineProperty = __toESM(require_defineProperty());
205
160
  /**
206
161
  * 带 TTL (Time To Live) 支持的高性能缓存实现。
207
162
  * 继承自 FastCache,并增加了一个按时间排序的独立链表,
208
163
  * 以实现 O(M) 复杂度的过期项目清理(M 为过期数量)。
209
164
  *
210
165
  * @important 在 Node.js 环境下使用 `autoCleanup: true` 时,
211
- * 必须在程序退出前手动调用 `destroy()` 方法来清理定时器,
212
- * 否则定时器会阻止 Node.js 进程正常退出。
166
+ * 内部定时器在支持的运行时会调用 `unref()`,因此通常不会单独阻止进程退出。
167
+ * 如果缓存生命周期明确,仍建议调用 `destroy()` 主动清理定时器。
213
168
  */
214
169
  var FastCacheWithTTL = class extends FastCache {
215
170
  constructor(options) {
216
171
  super(options.maxSize);
217
- (0, import_defineProperty.default)(this, "ttl", void 0);
218
- (0, import_defineProperty.default)(this, "autoCleanup", void 0);
219
- (0, import_defineProperty.default)(this, "cleanupTimer", void 0);
220
- (0, import_defineProperty.default)(this, "timeHead", void 0);
221
- (0, import_defineProperty.default)(this, "timeTail", void 0);
222
- (0, import_defineProperty.default)(this, "expired", 0);
172
+ _defineProperty(this, "ttl", void 0);
173
+ _defineProperty(this, "cleanupTimer", void 0);
174
+ _defineProperty(this, "timeHead", void 0);
175
+ _defineProperty(this, "timeTail", void 0);
176
+ _defineProperty(this, "expired", 0);
223
177
  if (options.ttl !== void 0 && options.ttl <= 0) throw new Error("TTL must be positive");
224
178
  this.ttl = options.ttl ?? 0;
225
- this.autoCleanup = options.autoCleanup ?? false;
179
+ const autoCleanup = options.autoCleanup ?? false;
226
180
  this.timeHead = {};
227
181
  this.timeTail = {};
228
182
  this.timeHead.timeNext = this.timeTail;
229
183
  this.timeTail.timePrev = this.timeHead;
230
- if (this.autoCleanup && this.ttl > 0) {
184
+ if (autoCleanup && this.ttl > 0) {
231
185
  const cleanupInterval = options.cleanupInterval ?? this.ttl;
186
+ if (cleanupInterval <= 0) throw new Error("cleanupInterval must be positive");
232
187
  this.cleanupTimer = setInterval(() => this.cleanup(), cleanupInterval);
188
+ const anyTimer = this.cleanupTimer;
189
+ if (typeof anyTimer.unref === "function") anyTimer.unref();
233
190
  }
234
191
  }
235
192
  get(key) {
236
193
  const node = this.cache.get(key);
237
194
  if (node === void 0) {
238
195
  this.misses++;
239
- return void 0;
196
+ return;
240
197
  }
241
198
  if (this.isExpired(node)) {
242
199
  this.delete(key);
243
200
  this.expired++;
244
201
  this.misses++;
245
- return void 0;
202
+ return;
246
203
  }
247
204
  return super.get(key);
248
205
  }
@@ -277,27 +234,9 @@ var FastCacheWithTTL = class extends FastCache {
277
234
  }
278
235
  return true;
279
236
  }
280
- get size() {
281
- return this.cache.size;
282
- }
283
- get capacity() {
284
- return this.maxSize;
285
- }
286
- setMany(entries) {
287
- for (const [key, value] of entries) this.set(key, value);
288
- }
289
- getMany(keys) {
290
- const result = /* @__PURE__ */ new Map();
291
- for (const key of keys) {
292
- const value = this.get(key);
293
- if (value !== void 0) result.set(key, value);
294
- }
295
- return result;
296
- }
297
237
  getStats() {
298
- const stats = super.getStats();
299
238
  return {
300
- ...stats,
239
+ ...super.getStats(),
301
240
  expired: this.expired
302
241
  };
303
242
  }
@@ -315,8 +254,7 @@ var FastCacheWithTTL = class extends FastCache {
315
254
  return removedCount;
316
255
  }
317
256
  /**
318
- * 清理定时器,防止在 Node.js 环境中进程无法正常退出。
319
- * 如果开启了 `autoCleanup`,你应当在应用关闭前调用此方法。
257
+ * 清理自动清理定时器,适合在可控生命周期中主动释放资源。
320
258
  */
321
259
  destroy() {
322
260
  if (this.cleanupTimer) {
@@ -324,6 +262,13 @@ var FastCacheWithTTL = class extends FastCache {
324
262
  this.cleanupTimer = void 0;
325
263
  }
326
264
  }
265
+ /**
266
+ * 覆写统一删除入口:先维护 TTL 链表,再调用父类删除 LRU/Map。
267
+ */
268
+ deleteNode(node) {
269
+ this._removeFromTimeList(node);
270
+ super.deleteNode(node);
271
+ }
327
272
  isExpired(node, now) {
328
273
  if (this.ttl === 0 || node.timestamp === void 0) return false;
329
274
  return (now ?? Date.now()) - node.timestamp > this.ttl;
@@ -344,7 +289,6 @@ var FastCacheWithTTL = class extends FastCache {
344
289
  this._addToTimeListTail(node);
345
290
  }
346
291
  };
347
-
348
292
  //#endregion
349
293
  //#region src/index.ts
350
294
  function createCache(maxSize) {
@@ -356,21 +300,20 @@ function createCacheWithTTL(options) {
356
300
  const CachePresets = {
357
301
  apiCache: (maxSize = 1e3) => createCache(maxSize),
358
302
  computeCache: (maxSize = 500) => createCache(maxSize),
359
- sessionCache: (maxSize = 100, ttl = 30 * 60 * 1e3) => createCacheWithTTL({
303
+ sessionCache: (maxSize = 100, ttl = 1800 * 1e3) => createCacheWithTTL({
360
304
  maxSize,
361
305
  ttl,
362
306
  autoCleanup: true
363
307
  }),
364
- tempCache: (maxSize = 200, ttl = 5 * 60 * 1e3) => createCacheWithTTL({
308
+ tempCache: (maxSize = 200, ttl = 300 * 1e3) => createCacheWithTTL({
365
309
  maxSize,
366
310
  ttl,
367
311
  autoCleanup: true
368
312
  })
369
313
  };
370
-
371
314
  //#endregion
372
315
  exports.CachePresets = CachePresets;
373
316
  exports.FastCache = FastCache;
374
317
  exports.FastCacheWithTTL = FastCacheWithTTL;
375
318
  exports.createCache = createCache;
376
- exports.createCacheWithTTL = createCacheWithTTL;
319
+ exports.createCacheWithTTL = createCacheWithTTL;
package/dist/main.mjs CHANGED
@@ -1,15 +1,14 @@
1
- //#region node_modules/.pnpm/@oxc-project+runtime@0.72.3/node_modules/@oxc-project/runtime/src/helpers/esm/typeof.js
1
+ //#region \0@oxc-project+runtime@0.115.0/helpers/typeof.js
2
2
  function _typeof(o) {
3
3
  "@babel/helpers - typeof";
4
- return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o$1) {
5
- return typeof o$1;
6
- } : function(o$1) {
7
- return o$1 && "function" == typeof Symbol && o$1.constructor === Symbol && o$1 !== Symbol.prototype ? "symbol" : typeof o$1;
4
+ return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
5
+ return typeof o;
6
+ } : function(o) {
7
+ return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
8
8
  }, _typeof(o);
9
9
  }
10
-
11
10
  //#endregion
12
- //#region node_modules/.pnpm/@oxc-project+runtime@0.72.3/node_modules/@oxc-project/runtime/src/helpers/esm/toPrimitive.js
11
+ //#region \0@oxc-project+runtime@0.115.0/helpers/toPrimitive.js
13
12
  function toPrimitive(t, r) {
14
13
  if ("object" != _typeof(t) || !t) return t;
15
14
  var e = t[Symbol.toPrimitive];
@@ -20,16 +19,14 @@ function toPrimitive(t, r) {
20
19
  }
21
20
  return ("string" === r ? String : Number)(t);
22
21
  }
23
-
24
22
  //#endregion
25
- //#region node_modules/.pnpm/@oxc-project+runtime@0.72.3/node_modules/@oxc-project/runtime/src/helpers/esm/toPropertyKey.js
23
+ //#region \0@oxc-project+runtime@0.115.0/helpers/toPropertyKey.js
26
24
  function toPropertyKey(t) {
27
25
  var i = toPrimitive(t, "string");
28
26
  return "symbol" == _typeof(i) ? i : i + "";
29
27
  }
30
-
31
28
  //#endregion
32
- //#region node_modules/.pnpm/@oxc-project+runtime@0.72.3/node_modules/@oxc-project/runtime/src/helpers/esm/defineProperty.js
29
+ //#region \0@oxc-project+runtime@0.115.0/helpers/defineProperty.js
33
30
  function _defineProperty(e, r, t) {
34
31
  return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
35
32
  value: t,
@@ -38,7 +35,6 @@ function _defineProperty(e, r, t) {
38
35
  writable: !0
39
36
  }) : e[r] = t, e;
40
37
  }
41
-
42
38
  //#endregion
43
39
  //#region src/fast-cache.ts
44
40
  /**
@@ -65,7 +61,7 @@ var FastCache = class {
65
61
  const node = this.cache.get(key);
66
62
  if (node === void 0) {
67
63
  this.misses++;
68
- return void 0;
64
+ return;
69
65
  }
70
66
  this.moveToHead(node);
71
67
  this.hits++;
@@ -91,8 +87,7 @@ var FastCache = class {
91
87
  delete(key) {
92
88
  const node = this.cache.get(key);
93
89
  if (node === void 0) return false;
94
- this.cache.delete(key);
95
- this.removeNode(node);
90
+ this.deleteNode(node);
96
91
  return true;
97
92
  }
98
93
  clear() {
@@ -146,15 +141,19 @@ var FastCache = class {
146
141
  this.removeNode(node);
147
142
  this.addToHead(node);
148
143
  }
144
+ /**
145
+ * 统一删除入口:删除 Map 记录并从 LRU 链表移除节点。
146
+ * 子类(如 TTL 版本)可覆写该方法以保证多链表同步删除。
147
+ */
148
+ deleteNode(node) {
149
+ this.cache.delete(node.key);
150
+ this.removeNode(node);
151
+ }
149
152
  removeTail() {
150
153
  const lastNode = this.tail.prev;
151
- if (lastNode && lastNode !== this.head) {
152
- this.cache.delete(lastNode.key);
153
- this.removeNode(lastNode);
154
- }
154
+ if (lastNode && lastNode !== this.head) this.deleteNode(lastNode);
155
155
  }
156
156
  };
157
-
158
157
  //#endregion
159
158
  //#region src/fast-cache-ttl.ts
160
159
  /**
@@ -163,41 +162,43 @@ var FastCache = class {
163
162
  * 以实现 O(M) 复杂度的过期项目清理(M 为过期数量)。
164
163
  *
165
164
  * @important 在 Node.js 环境下使用 `autoCleanup: true` 时,
166
- * 必须在程序退出前手动调用 `destroy()` 方法来清理定时器,
167
- * 否则定时器会阻止 Node.js 进程正常退出。
165
+ * 内部定时器在支持的运行时会调用 `unref()`,因此通常不会单独阻止进程退出。
166
+ * 如果缓存生命周期明确,仍建议调用 `destroy()` 主动清理定时器。
168
167
  */
169
168
  var FastCacheWithTTL = class extends FastCache {
170
169
  constructor(options) {
171
170
  super(options.maxSize);
172
171
  _defineProperty(this, "ttl", void 0);
173
- _defineProperty(this, "autoCleanup", void 0);
174
172
  _defineProperty(this, "cleanupTimer", void 0);
175
173
  _defineProperty(this, "timeHead", void 0);
176
174
  _defineProperty(this, "timeTail", void 0);
177
175
  _defineProperty(this, "expired", 0);
178
176
  if (options.ttl !== void 0 && options.ttl <= 0) throw new Error("TTL must be positive");
179
177
  this.ttl = options.ttl ?? 0;
180
- this.autoCleanup = options.autoCleanup ?? false;
178
+ const autoCleanup = options.autoCleanup ?? false;
181
179
  this.timeHead = {};
182
180
  this.timeTail = {};
183
181
  this.timeHead.timeNext = this.timeTail;
184
182
  this.timeTail.timePrev = this.timeHead;
185
- if (this.autoCleanup && this.ttl > 0) {
183
+ if (autoCleanup && this.ttl > 0) {
186
184
  const cleanupInterval = options.cleanupInterval ?? this.ttl;
185
+ if (cleanupInterval <= 0) throw new Error("cleanupInterval must be positive");
187
186
  this.cleanupTimer = setInterval(() => this.cleanup(), cleanupInterval);
187
+ const anyTimer = this.cleanupTimer;
188
+ if (typeof anyTimer.unref === "function") anyTimer.unref();
188
189
  }
189
190
  }
190
191
  get(key) {
191
192
  const node = this.cache.get(key);
192
193
  if (node === void 0) {
193
194
  this.misses++;
194
- return void 0;
195
+ return;
195
196
  }
196
197
  if (this.isExpired(node)) {
197
198
  this.delete(key);
198
199
  this.expired++;
199
200
  this.misses++;
200
- return void 0;
201
+ return;
201
202
  }
202
203
  return super.get(key);
203
204
  }
@@ -232,27 +233,9 @@ var FastCacheWithTTL = class extends FastCache {
232
233
  }
233
234
  return true;
234
235
  }
235
- get size() {
236
- return this.cache.size;
237
- }
238
- get capacity() {
239
- return this.maxSize;
240
- }
241
- setMany(entries) {
242
- for (const [key, value] of entries) this.set(key, value);
243
- }
244
- getMany(keys) {
245
- const result = /* @__PURE__ */ new Map();
246
- for (const key of keys) {
247
- const value = this.get(key);
248
- if (value !== void 0) result.set(key, value);
249
- }
250
- return result;
251
- }
252
236
  getStats() {
253
- const stats = super.getStats();
254
237
  return {
255
- ...stats,
238
+ ...super.getStats(),
256
239
  expired: this.expired
257
240
  };
258
241
  }
@@ -270,8 +253,7 @@ var FastCacheWithTTL = class extends FastCache {
270
253
  return removedCount;
271
254
  }
272
255
  /**
273
- * 清理定时器,防止在 Node.js 环境中进程无法正常退出。
274
- * 如果开启了 `autoCleanup`,你应当在应用关闭前调用此方法。
256
+ * 清理自动清理定时器,适合在可控生命周期中主动释放资源。
275
257
  */
276
258
  destroy() {
277
259
  if (this.cleanupTimer) {
@@ -279,6 +261,13 @@ var FastCacheWithTTL = class extends FastCache {
279
261
  this.cleanupTimer = void 0;
280
262
  }
281
263
  }
264
+ /**
265
+ * 覆写统一删除入口:先维护 TTL 链表,再调用父类删除 LRU/Map。
266
+ */
267
+ deleteNode(node) {
268
+ this._removeFromTimeList(node);
269
+ super.deleteNode(node);
270
+ }
282
271
  isExpired(node, now) {
283
272
  if (this.ttl === 0 || node.timestamp === void 0) return false;
284
273
  return (now ?? Date.now()) - node.timestamp > this.ttl;
@@ -299,7 +288,6 @@ var FastCacheWithTTL = class extends FastCache {
299
288
  this._addToTimeListTail(node);
300
289
  }
301
290
  };
302
-
303
291
  //#endregion
304
292
  //#region src/index.ts
305
293
  function createCache(maxSize) {
@@ -311,17 +299,16 @@ function createCacheWithTTL(options) {
311
299
  const CachePresets = {
312
300
  apiCache: (maxSize = 1e3) => createCache(maxSize),
313
301
  computeCache: (maxSize = 500) => createCache(maxSize),
314
- sessionCache: (maxSize = 100, ttl = 30 * 60 * 1e3) => createCacheWithTTL({
302
+ sessionCache: (maxSize = 100, ttl = 1800 * 1e3) => createCacheWithTTL({
315
303
  maxSize,
316
304
  ttl,
317
305
  autoCleanup: true
318
306
  }),
319
- tempCache: (maxSize = 200, ttl = 5 * 60 * 1e3) => createCacheWithTTL({
307
+ tempCache: (maxSize = 200, ttl = 300 * 1e3) => createCacheWithTTL({
320
308
  maxSize,
321
309
  ttl,
322
310
  autoCleanup: true
323
311
  })
324
312
  };
325
-
326
313
  //#endregion
327
- export { CachePresets, FastCache, FastCacheWithTTL, createCache, createCacheWithTTL };
314
+ export { CachePresets, FastCache, FastCacheWithTTL, createCache, createCacheWithTTL };
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "fast-map-cache",
3
- "version": "1.0.1",
3
+ "version": "1.1.1",
4
4
  "description": "High-performance TypeScript LRU cache (with optional TTL) for Node.js & browsers.",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
8
+ "type": "commonjs",
8
9
  "main": "./dist/main.js",
9
10
  "module": "./dist/main.mjs",
10
11
  "types": "./dist/main.d.ts",
@@ -20,26 +21,17 @@
20
21
  },
21
22
  "exports": {
22
23
  ".": {
23
- "import": "./dist/main.mjs",
24
- "require": "./dist/main.js"
24
+ "import": {
25
+ "types": "./dist/main.d.mts",
26
+ "default": "./dist/main.mjs"
27
+ },
28
+ "require": {
29
+ "types": "./dist/main.d.ts",
30
+ "default": "./dist/main.js"
31
+ }
25
32
  },
26
33
  "./package.json": "./package.json"
27
34
  },
28
- "scripts": {
29
- "test": "vitest run",
30
- "test:coverage": "vitest run --coverage",
31
- "lint": "oxlint",
32
- "build": "pnpm lint && pnpm type-check && tsdown",
33
- "bench": "vitest bench --run",
34
- "prepare": "simple-git-hooks install",
35
- "run:example": "npx tsx examples/01-basic-example.ts",
36
- "type-check": "tsc --noEmit",
37
- "format": "prettier --write .",
38
- "gen-report": "pnpm bench && tsx scripts/generate-performance-report.ts",
39
- "changeset": "changeset",
40
- "version:packages": "changeset version",
41
- "release": "pnpm build && changeset publish"
42
- },
43
35
  "keywords": [
44
36
  "cache",
45
37
  "lru",
@@ -59,19 +51,21 @@
59
51
  },
60
52
  "license": "MIT",
61
53
  "devDependencies": {
62
- "@changesets/cli": "^2.29.5",
63
- "@commitlint/cli": "^19.8.1",
64
- "@commitlint/config-conventional": "^19.8.1",
65
- "@vitest/coverage-v8": "^3.2.4",
66
- "lint-staged": "^16.1.2",
67
- "oxlint": "^1.2.0",
68
- "pnpm": "^10.12.2",
69
- "prettier": "^3.6.0",
54
+ "@changesets/cli": "^2.30.0",
55
+ "@commitlint/cli": "^20.4.4",
56
+ "@commitlint/config-conventional": "^20.4.4",
57
+ "@vitest/coverage-v8": "^4.1.0",
58
+ "lint-staged": "^16.3.3",
59
+ "oxlint": "^1.55.0",
60
+ "pnpm": "^10.32.1",
61
+ "prettier": "^3.8.1",
62
+ "publint": "^0.3.15",
70
63
  "simple-git-hooks": "^2.13.0",
71
- "tsdown": "^0.12.8",
72
- "tsx": "^4.20.3",
73
- "typescript": "^5.8.3",
74
- "vitest": "^3.2.4"
64
+ "tsdown": "^0.21.2",
65
+ "tsx": "^4.21.0",
66
+ "typescript": "^5.9.3",
67
+ "vite": "^8.0.0",
68
+ "vitest": "^4.1.0"
75
69
  },
76
70
  "simple-git-hooks": {
77
71
  "pre-commit": "npx lint-staged",
@@ -86,10 +80,24 @@
86
80
  },
87
81
  "repository": {
88
82
  "type": "git",
89
- "url": "https://github.com/crper/fast-map-cache.git"
83
+ "url": "git+https://github.com/crper/fast-map-cache.git"
90
84
  },
91
85
  "bugs": {
92
86
  "url": "https://github.com/crper/fast-map-cache/issues"
93
87
  },
94
- "homepage": "https://github.com/crper/fast-map-cache#readme"
95
- }
88
+ "homepage": "https://github.com/crper/fast-map-cache#readme",
89
+ "scripts": {
90
+ "test": "vitest run",
91
+ "test:coverage": "vitest run --coverage",
92
+ "lint": "oxlint",
93
+ "build": "pnpm lint && pnpm type-check && tsdown",
94
+ "bench": "vitest bench --run",
95
+ "run:example": "npx tsx examples/01-basic-example.ts",
96
+ "type-check": "tsc --noEmit",
97
+ "format": "prettier --write .",
98
+ "gen-report": "pnpm bench && tsx scripts/generate-performance-report.ts",
99
+ "changeset": "changeset",
100
+ "version:packages": "changeset version",
101
+ "release": "pnpm build && changeset publish"
102
+ }
103
+ }