fast-map-cache 1.0.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/LICENSE +21 -0
- package/README.md +175 -0
- package/README_zh-CN.md +175 -0
- package/dist/main.d.mts +122 -0
- package/dist/main.d.ts +122 -0
- package/dist/main.js +376 -0
- package/dist/main.mjs +327 -0
- package/package.json +95 -0
package/dist/main.js
ADDED
|
@@ -0,0 +1,376 @@
|
|
|
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
|
+
|
|
41
|
+
//#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
|
+
|
|
58
|
+
//#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
|
+
|
|
70
|
+
//#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
|
+
|
|
85
|
+
//#endregion
|
|
86
|
+
//#region src/fast-cache.ts
|
|
87
|
+
var import_defineProperty$1 = __toESM(require_defineProperty());
|
|
88
|
+
/**
|
|
89
|
+
* 高性能 LRU 缓存实现
|
|
90
|
+
* 使用 Map + 双向链表实现 O(1) 复杂度的所有操作
|
|
91
|
+
*/
|
|
92
|
+
var FastCache = class {
|
|
93
|
+
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);
|
|
100
|
+
if (maxSize <= 0) throw new Error("Cache size must be positive");
|
|
101
|
+
this.maxSize = maxSize;
|
|
102
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
103
|
+
this.head = {};
|
|
104
|
+
this.tail = {};
|
|
105
|
+
this.head.next = this.tail;
|
|
106
|
+
this.tail.prev = this.head;
|
|
107
|
+
}
|
|
108
|
+
get(key) {
|
|
109
|
+
const node = this.cache.get(key);
|
|
110
|
+
if (node === void 0) {
|
|
111
|
+
this.misses++;
|
|
112
|
+
return void 0;
|
|
113
|
+
}
|
|
114
|
+
this.moveToHead(node);
|
|
115
|
+
this.hits++;
|
|
116
|
+
return node.value;
|
|
117
|
+
}
|
|
118
|
+
set(key, value) {
|
|
119
|
+
const existingNode = this.cache.get(key);
|
|
120
|
+
if (existingNode !== void 0) {
|
|
121
|
+
existingNode.value = value;
|
|
122
|
+
this.moveToHead(existingNode);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const newNode = {
|
|
126
|
+
key,
|
|
127
|
+
value,
|
|
128
|
+
prev: null,
|
|
129
|
+
next: null
|
|
130
|
+
};
|
|
131
|
+
if (this.cache.size >= this.maxSize) this.removeTail();
|
|
132
|
+
this.cache.set(key, newNode);
|
|
133
|
+
this.addToHead(newNode);
|
|
134
|
+
}
|
|
135
|
+
delete(key) {
|
|
136
|
+
const node = this.cache.get(key);
|
|
137
|
+
if (node === void 0) return false;
|
|
138
|
+
this.cache.delete(key);
|
|
139
|
+
this.removeNode(node);
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
clear() {
|
|
143
|
+
this.cache.clear();
|
|
144
|
+
this.head.next = this.tail;
|
|
145
|
+
this.tail.prev = this.head;
|
|
146
|
+
this.hits = 0;
|
|
147
|
+
this.misses = 0;
|
|
148
|
+
}
|
|
149
|
+
has(key) {
|
|
150
|
+
return this.cache.has(key);
|
|
151
|
+
}
|
|
152
|
+
get size() {
|
|
153
|
+
return this.cache.size;
|
|
154
|
+
}
|
|
155
|
+
get capacity() {
|
|
156
|
+
return this.maxSize;
|
|
157
|
+
}
|
|
158
|
+
setMany(entries) {
|
|
159
|
+
for (const [key, value] of entries) this.set(key, value);
|
|
160
|
+
}
|
|
161
|
+
getMany(keys) {
|
|
162
|
+
const result = /* @__PURE__ */ new Map();
|
|
163
|
+
for (const key of keys) {
|
|
164
|
+
const value = this.get(key);
|
|
165
|
+
if (value !== void 0) result.set(key, value);
|
|
166
|
+
}
|
|
167
|
+
return result;
|
|
168
|
+
}
|
|
169
|
+
getStats() {
|
|
170
|
+
const total = this.hits + this.misses;
|
|
171
|
+
return {
|
|
172
|
+
hits: this.hits,
|
|
173
|
+
misses: this.misses,
|
|
174
|
+
hitRate: total > 0 ? this.hits / total : 0,
|
|
175
|
+
size: this.size,
|
|
176
|
+
capacity: this.capacity
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
addToHead(node) {
|
|
180
|
+
node.prev = this.head;
|
|
181
|
+
node.next = this.head.next;
|
|
182
|
+
this.head.next.prev = node;
|
|
183
|
+
this.head.next = node;
|
|
184
|
+
}
|
|
185
|
+
removeNode(node) {
|
|
186
|
+
if (node.prev) node.prev.next = node.next;
|
|
187
|
+
if (node.next) node.next.prev = node.prev;
|
|
188
|
+
}
|
|
189
|
+
moveToHead(node) {
|
|
190
|
+
this.removeNode(node);
|
|
191
|
+
this.addToHead(node);
|
|
192
|
+
}
|
|
193
|
+
removeTail() {
|
|
194
|
+
const lastNode = this.tail.prev;
|
|
195
|
+
if (lastNode && lastNode !== this.head) {
|
|
196
|
+
this.cache.delete(lastNode.key);
|
|
197
|
+
this.removeNode(lastNode);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
//#endregion
|
|
203
|
+
//#region src/fast-cache-ttl.ts
|
|
204
|
+
var import_defineProperty = __toESM(require_defineProperty());
|
|
205
|
+
/**
|
|
206
|
+
* 带 TTL (Time To Live) 支持的高性能缓存实现。
|
|
207
|
+
* 继承自 FastCache,并增加了一个按时间排序的独立链表,
|
|
208
|
+
* 以实现 O(M) 复杂度的过期项目清理(M 为过期数量)。
|
|
209
|
+
*
|
|
210
|
+
* @important 在 Node.js 环境下使用 `autoCleanup: true` 时,
|
|
211
|
+
* 必须在程序退出前手动调用 `destroy()` 方法来清理定时器,
|
|
212
|
+
* 否则定时器会阻止 Node.js 进程正常退出。
|
|
213
|
+
*/
|
|
214
|
+
var FastCacheWithTTL = class extends FastCache {
|
|
215
|
+
constructor(options) {
|
|
216
|
+
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);
|
|
223
|
+
if (options.ttl !== void 0 && options.ttl <= 0) throw new Error("TTL must be positive");
|
|
224
|
+
this.ttl = options.ttl ?? 0;
|
|
225
|
+
this.autoCleanup = options.autoCleanup ?? false;
|
|
226
|
+
this.timeHead = {};
|
|
227
|
+
this.timeTail = {};
|
|
228
|
+
this.timeHead.timeNext = this.timeTail;
|
|
229
|
+
this.timeTail.timePrev = this.timeHead;
|
|
230
|
+
if (this.autoCleanup && this.ttl > 0) {
|
|
231
|
+
const cleanupInterval = options.cleanupInterval ?? this.ttl;
|
|
232
|
+
this.cleanupTimer = setInterval(() => this.cleanup(), cleanupInterval);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
get(key) {
|
|
236
|
+
const node = this.cache.get(key);
|
|
237
|
+
if (node === void 0) {
|
|
238
|
+
this.misses++;
|
|
239
|
+
return void 0;
|
|
240
|
+
}
|
|
241
|
+
if (this.isExpired(node)) {
|
|
242
|
+
this.delete(key);
|
|
243
|
+
this.expired++;
|
|
244
|
+
this.misses++;
|
|
245
|
+
return void 0;
|
|
246
|
+
}
|
|
247
|
+
return super.get(key);
|
|
248
|
+
}
|
|
249
|
+
set(key, value) {
|
|
250
|
+
const existingNode = this.cache.get(key);
|
|
251
|
+
const now = this.ttl > 0 ? Date.now() : 0;
|
|
252
|
+
super.set(key, value);
|
|
253
|
+
const node = this.cache.get(key);
|
|
254
|
+
node.timestamp = now;
|
|
255
|
+
if (existingNode) this._moveToTimeListTail(node);
|
|
256
|
+
else this._addToTimeListTail(node);
|
|
257
|
+
}
|
|
258
|
+
delete(key) {
|
|
259
|
+
const node = this.cache.get(key);
|
|
260
|
+
if (node === void 0) return false;
|
|
261
|
+
this._removeFromTimeList(node);
|
|
262
|
+
return super.delete(key);
|
|
263
|
+
}
|
|
264
|
+
clear() {
|
|
265
|
+
super.clear();
|
|
266
|
+
this.timeHead.timeNext = this.timeTail;
|
|
267
|
+
this.timeTail.timePrev = this.timeHead;
|
|
268
|
+
this.expired = 0;
|
|
269
|
+
}
|
|
270
|
+
has(key) {
|
|
271
|
+
const node = this.cache.get(key);
|
|
272
|
+
if (node === void 0) return false;
|
|
273
|
+
if (this.isExpired(node)) {
|
|
274
|
+
this.delete(key);
|
|
275
|
+
this.expired++;
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
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
|
+
getStats() {
|
|
298
|
+
const stats = super.getStats();
|
|
299
|
+
return {
|
|
300
|
+
...stats,
|
|
301
|
+
expired: this.expired
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
cleanup() {
|
|
305
|
+
if (this.ttl === 0) return 0;
|
|
306
|
+
let removedCount = 0;
|
|
307
|
+
const now = Date.now();
|
|
308
|
+
let currentNode = this.timeHead.timeNext;
|
|
309
|
+
while (currentNode && currentNode !== this.timeTail) if (this.isExpired(currentNode, now)) {
|
|
310
|
+
this.delete(currentNode.key);
|
|
311
|
+
removedCount++;
|
|
312
|
+
this.expired++;
|
|
313
|
+
currentNode = this.timeHead.timeNext;
|
|
314
|
+
} else break;
|
|
315
|
+
return removedCount;
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* 清理定时器,防止在 Node.js 环境中进程无法正常退出。
|
|
319
|
+
* 如果开启了 `autoCleanup`,你应当在应用关闭前调用此方法。
|
|
320
|
+
*/
|
|
321
|
+
destroy() {
|
|
322
|
+
if (this.cleanupTimer) {
|
|
323
|
+
clearInterval(this.cleanupTimer);
|
|
324
|
+
this.cleanupTimer = void 0;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
isExpired(node, now) {
|
|
328
|
+
if (this.ttl === 0 || node.timestamp === void 0) return false;
|
|
329
|
+
return (now ?? Date.now()) - node.timestamp > this.ttl;
|
|
330
|
+
}
|
|
331
|
+
_addToTimeListTail(node) {
|
|
332
|
+
const prev = this.timeTail.timePrev;
|
|
333
|
+
prev.timeNext = node;
|
|
334
|
+
node.timePrev = prev;
|
|
335
|
+
node.timeNext = this.timeTail;
|
|
336
|
+
this.timeTail.timePrev = node;
|
|
337
|
+
}
|
|
338
|
+
_removeFromTimeList(node) {
|
|
339
|
+
if (node.timePrev) node.timePrev.timeNext = node.timeNext ?? null;
|
|
340
|
+
if (node.timeNext) node.timeNext.timePrev = node.timePrev ?? null;
|
|
341
|
+
}
|
|
342
|
+
_moveToTimeListTail(node) {
|
|
343
|
+
this._removeFromTimeList(node);
|
|
344
|
+
this._addToTimeListTail(node);
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
//#endregion
|
|
349
|
+
//#region src/index.ts
|
|
350
|
+
function createCache(maxSize) {
|
|
351
|
+
return new FastCache(maxSize);
|
|
352
|
+
}
|
|
353
|
+
function createCacheWithTTL(options) {
|
|
354
|
+
return new FastCacheWithTTL(options);
|
|
355
|
+
}
|
|
356
|
+
const CachePresets = {
|
|
357
|
+
apiCache: (maxSize = 1e3) => createCache(maxSize),
|
|
358
|
+
computeCache: (maxSize = 500) => createCache(maxSize),
|
|
359
|
+
sessionCache: (maxSize = 100, ttl = 30 * 60 * 1e3) => createCacheWithTTL({
|
|
360
|
+
maxSize,
|
|
361
|
+
ttl,
|
|
362
|
+
autoCleanup: true
|
|
363
|
+
}),
|
|
364
|
+
tempCache: (maxSize = 200, ttl = 5 * 60 * 1e3) => createCacheWithTTL({
|
|
365
|
+
maxSize,
|
|
366
|
+
ttl,
|
|
367
|
+
autoCleanup: true
|
|
368
|
+
})
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
//#endregion
|
|
372
|
+
exports.CachePresets = CachePresets;
|
|
373
|
+
exports.FastCache = FastCache;
|
|
374
|
+
exports.FastCacheWithTTL = FastCacheWithTTL;
|
|
375
|
+
exports.createCache = createCache;
|
|
376
|
+
exports.createCacheWithTTL = createCacheWithTTL;
|
package/dist/main.mjs
ADDED
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
//#region node_modules/.pnpm/@oxc-project+runtime@0.72.3/node_modules/@oxc-project/runtime/src/helpers/esm/typeof.js
|
|
2
|
+
function _typeof(o) {
|
|
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;
|
|
8
|
+
}, _typeof(o);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region node_modules/.pnpm/@oxc-project+runtime@0.72.3/node_modules/@oxc-project/runtime/src/helpers/esm/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
|
+
}
|
|
23
|
+
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region node_modules/.pnpm/@oxc-project+runtime@0.72.3/node_modules/@oxc-project/runtime/src/helpers/esm/toPropertyKey.js
|
|
26
|
+
function toPropertyKey(t) {
|
|
27
|
+
var i = toPrimitive(t, "string");
|
|
28
|
+
return "symbol" == _typeof(i) ? i : i + "";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region node_modules/.pnpm/@oxc-project+runtime@0.72.3/node_modules/@oxc-project/runtime/src/helpers/esm/defineProperty.js
|
|
33
|
+
function _defineProperty(e, r, t) {
|
|
34
|
+
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
35
|
+
value: t,
|
|
36
|
+
enumerable: !0,
|
|
37
|
+
configurable: !0,
|
|
38
|
+
writable: !0
|
|
39
|
+
}) : e[r] = t, e;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region src/fast-cache.ts
|
|
44
|
+
/**
|
|
45
|
+
* 高性能 LRU 缓存实现
|
|
46
|
+
* 使用 Map + 双向链表实现 O(1) 复杂度的所有操作
|
|
47
|
+
*/
|
|
48
|
+
var FastCache = class {
|
|
49
|
+
constructor(maxSize) {
|
|
50
|
+
_defineProperty(this, "cache", void 0);
|
|
51
|
+
_defineProperty(this, "head", void 0);
|
|
52
|
+
_defineProperty(this, "tail", void 0);
|
|
53
|
+
_defineProperty(this, "maxSize", void 0);
|
|
54
|
+
_defineProperty(this, "hits", 0);
|
|
55
|
+
_defineProperty(this, "misses", 0);
|
|
56
|
+
if (maxSize <= 0) throw new Error("Cache size must be positive");
|
|
57
|
+
this.maxSize = maxSize;
|
|
58
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
59
|
+
this.head = {};
|
|
60
|
+
this.tail = {};
|
|
61
|
+
this.head.next = this.tail;
|
|
62
|
+
this.tail.prev = this.head;
|
|
63
|
+
}
|
|
64
|
+
get(key) {
|
|
65
|
+
const node = this.cache.get(key);
|
|
66
|
+
if (node === void 0) {
|
|
67
|
+
this.misses++;
|
|
68
|
+
return void 0;
|
|
69
|
+
}
|
|
70
|
+
this.moveToHead(node);
|
|
71
|
+
this.hits++;
|
|
72
|
+
return node.value;
|
|
73
|
+
}
|
|
74
|
+
set(key, value) {
|
|
75
|
+
const existingNode = this.cache.get(key);
|
|
76
|
+
if (existingNode !== void 0) {
|
|
77
|
+
existingNode.value = value;
|
|
78
|
+
this.moveToHead(existingNode);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const newNode = {
|
|
82
|
+
key,
|
|
83
|
+
value,
|
|
84
|
+
prev: null,
|
|
85
|
+
next: null
|
|
86
|
+
};
|
|
87
|
+
if (this.cache.size >= this.maxSize) this.removeTail();
|
|
88
|
+
this.cache.set(key, newNode);
|
|
89
|
+
this.addToHead(newNode);
|
|
90
|
+
}
|
|
91
|
+
delete(key) {
|
|
92
|
+
const node = this.cache.get(key);
|
|
93
|
+
if (node === void 0) return false;
|
|
94
|
+
this.cache.delete(key);
|
|
95
|
+
this.removeNode(node);
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
clear() {
|
|
99
|
+
this.cache.clear();
|
|
100
|
+
this.head.next = this.tail;
|
|
101
|
+
this.tail.prev = this.head;
|
|
102
|
+
this.hits = 0;
|
|
103
|
+
this.misses = 0;
|
|
104
|
+
}
|
|
105
|
+
has(key) {
|
|
106
|
+
return this.cache.has(key);
|
|
107
|
+
}
|
|
108
|
+
get size() {
|
|
109
|
+
return this.cache.size;
|
|
110
|
+
}
|
|
111
|
+
get capacity() {
|
|
112
|
+
return this.maxSize;
|
|
113
|
+
}
|
|
114
|
+
setMany(entries) {
|
|
115
|
+
for (const [key, value] of entries) this.set(key, value);
|
|
116
|
+
}
|
|
117
|
+
getMany(keys) {
|
|
118
|
+
const result = /* @__PURE__ */ new Map();
|
|
119
|
+
for (const key of keys) {
|
|
120
|
+
const value = this.get(key);
|
|
121
|
+
if (value !== void 0) result.set(key, value);
|
|
122
|
+
}
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
125
|
+
getStats() {
|
|
126
|
+
const total = this.hits + this.misses;
|
|
127
|
+
return {
|
|
128
|
+
hits: this.hits,
|
|
129
|
+
misses: this.misses,
|
|
130
|
+
hitRate: total > 0 ? this.hits / total : 0,
|
|
131
|
+
size: this.size,
|
|
132
|
+
capacity: this.capacity
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
addToHead(node) {
|
|
136
|
+
node.prev = this.head;
|
|
137
|
+
node.next = this.head.next;
|
|
138
|
+
this.head.next.prev = node;
|
|
139
|
+
this.head.next = node;
|
|
140
|
+
}
|
|
141
|
+
removeNode(node) {
|
|
142
|
+
if (node.prev) node.prev.next = node.next;
|
|
143
|
+
if (node.next) node.next.prev = node.prev;
|
|
144
|
+
}
|
|
145
|
+
moveToHead(node) {
|
|
146
|
+
this.removeNode(node);
|
|
147
|
+
this.addToHead(node);
|
|
148
|
+
}
|
|
149
|
+
removeTail() {
|
|
150
|
+
const lastNode = this.tail.prev;
|
|
151
|
+
if (lastNode && lastNode !== this.head) {
|
|
152
|
+
this.cache.delete(lastNode.key);
|
|
153
|
+
this.removeNode(lastNode);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
//#endregion
|
|
159
|
+
//#region src/fast-cache-ttl.ts
|
|
160
|
+
/**
|
|
161
|
+
* 带 TTL (Time To Live) 支持的高性能缓存实现。
|
|
162
|
+
* 继承自 FastCache,并增加了一个按时间排序的独立链表,
|
|
163
|
+
* 以实现 O(M) 复杂度的过期项目清理(M 为过期数量)。
|
|
164
|
+
*
|
|
165
|
+
* @important 在 Node.js 环境下使用 `autoCleanup: true` 时,
|
|
166
|
+
* 必须在程序退出前手动调用 `destroy()` 方法来清理定时器,
|
|
167
|
+
* 否则定时器会阻止 Node.js 进程正常退出。
|
|
168
|
+
*/
|
|
169
|
+
var FastCacheWithTTL = class extends FastCache {
|
|
170
|
+
constructor(options) {
|
|
171
|
+
super(options.maxSize);
|
|
172
|
+
_defineProperty(this, "ttl", void 0);
|
|
173
|
+
_defineProperty(this, "autoCleanup", void 0);
|
|
174
|
+
_defineProperty(this, "cleanupTimer", void 0);
|
|
175
|
+
_defineProperty(this, "timeHead", void 0);
|
|
176
|
+
_defineProperty(this, "timeTail", void 0);
|
|
177
|
+
_defineProperty(this, "expired", 0);
|
|
178
|
+
if (options.ttl !== void 0 && options.ttl <= 0) throw new Error("TTL must be positive");
|
|
179
|
+
this.ttl = options.ttl ?? 0;
|
|
180
|
+
this.autoCleanup = options.autoCleanup ?? false;
|
|
181
|
+
this.timeHead = {};
|
|
182
|
+
this.timeTail = {};
|
|
183
|
+
this.timeHead.timeNext = this.timeTail;
|
|
184
|
+
this.timeTail.timePrev = this.timeHead;
|
|
185
|
+
if (this.autoCleanup && this.ttl > 0) {
|
|
186
|
+
const cleanupInterval = options.cleanupInterval ?? this.ttl;
|
|
187
|
+
this.cleanupTimer = setInterval(() => this.cleanup(), cleanupInterval);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
get(key) {
|
|
191
|
+
const node = this.cache.get(key);
|
|
192
|
+
if (node === void 0) {
|
|
193
|
+
this.misses++;
|
|
194
|
+
return void 0;
|
|
195
|
+
}
|
|
196
|
+
if (this.isExpired(node)) {
|
|
197
|
+
this.delete(key);
|
|
198
|
+
this.expired++;
|
|
199
|
+
this.misses++;
|
|
200
|
+
return void 0;
|
|
201
|
+
}
|
|
202
|
+
return super.get(key);
|
|
203
|
+
}
|
|
204
|
+
set(key, value) {
|
|
205
|
+
const existingNode = this.cache.get(key);
|
|
206
|
+
const now = this.ttl > 0 ? Date.now() : 0;
|
|
207
|
+
super.set(key, value);
|
|
208
|
+
const node = this.cache.get(key);
|
|
209
|
+
node.timestamp = now;
|
|
210
|
+
if (existingNode) this._moveToTimeListTail(node);
|
|
211
|
+
else this._addToTimeListTail(node);
|
|
212
|
+
}
|
|
213
|
+
delete(key) {
|
|
214
|
+
const node = this.cache.get(key);
|
|
215
|
+
if (node === void 0) return false;
|
|
216
|
+
this._removeFromTimeList(node);
|
|
217
|
+
return super.delete(key);
|
|
218
|
+
}
|
|
219
|
+
clear() {
|
|
220
|
+
super.clear();
|
|
221
|
+
this.timeHead.timeNext = this.timeTail;
|
|
222
|
+
this.timeTail.timePrev = this.timeHead;
|
|
223
|
+
this.expired = 0;
|
|
224
|
+
}
|
|
225
|
+
has(key) {
|
|
226
|
+
const node = this.cache.get(key);
|
|
227
|
+
if (node === void 0) return false;
|
|
228
|
+
if (this.isExpired(node)) {
|
|
229
|
+
this.delete(key);
|
|
230
|
+
this.expired++;
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
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
|
+
getStats() {
|
|
253
|
+
const stats = super.getStats();
|
|
254
|
+
return {
|
|
255
|
+
...stats,
|
|
256
|
+
expired: this.expired
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
cleanup() {
|
|
260
|
+
if (this.ttl === 0) return 0;
|
|
261
|
+
let removedCount = 0;
|
|
262
|
+
const now = Date.now();
|
|
263
|
+
let currentNode = this.timeHead.timeNext;
|
|
264
|
+
while (currentNode && currentNode !== this.timeTail) if (this.isExpired(currentNode, now)) {
|
|
265
|
+
this.delete(currentNode.key);
|
|
266
|
+
removedCount++;
|
|
267
|
+
this.expired++;
|
|
268
|
+
currentNode = this.timeHead.timeNext;
|
|
269
|
+
} else break;
|
|
270
|
+
return removedCount;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* 清理定时器,防止在 Node.js 环境中进程无法正常退出。
|
|
274
|
+
* 如果开启了 `autoCleanup`,你应当在应用关闭前调用此方法。
|
|
275
|
+
*/
|
|
276
|
+
destroy() {
|
|
277
|
+
if (this.cleanupTimer) {
|
|
278
|
+
clearInterval(this.cleanupTimer);
|
|
279
|
+
this.cleanupTimer = void 0;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
isExpired(node, now) {
|
|
283
|
+
if (this.ttl === 0 || node.timestamp === void 0) return false;
|
|
284
|
+
return (now ?? Date.now()) - node.timestamp > this.ttl;
|
|
285
|
+
}
|
|
286
|
+
_addToTimeListTail(node) {
|
|
287
|
+
const prev = this.timeTail.timePrev;
|
|
288
|
+
prev.timeNext = node;
|
|
289
|
+
node.timePrev = prev;
|
|
290
|
+
node.timeNext = this.timeTail;
|
|
291
|
+
this.timeTail.timePrev = node;
|
|
292
|
+
}
|
|
293
|
+
_removeFromTimeList(node) {
|
|
294
|
+
if (node.timePrev) node.timePrev.timeNext = node.timeNext ?? null;
|
|
295
|
+
if (node.timeNext) node.timeNext.timePrev = node.timePrev ?? null;
|
|
296
|
+
}
|
|
297
|
+
_moveToTimeListTail(node) {
|
|
298
|
+
this._removeFromTimeList(node);
|
|
299
|
+
this._addToTimeListTail(node);
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
//#endregion
|
|
304
|
+
//#region src/index.ts
|
|
305
|
+
function createCache(maxSize) {
|
|
306
|
+
return new FastCache(maxSize);
|
|
307
|
+
}
|
|
308
|
+
function createCacheWithTTL(options) {
|
|
309
|
+
return new FastCacheWithTTL(options);
|
|
310
|
+
}
|
|
311
|
+
const CachePresets = {
|
|
312
|
+
apiCache: (maxSize = 1e3) => createCache(maxSize),
|
|
313
|
+
computeCache: (maxSize = 500) => createCache(maxSize),
|
|
314
|
+
sessionCache: (maxSize = 100, ttl = 30 * 60 * 1e3) => createCacheWithTTL({
|
|
315
|
+
maxSize,
|
|
316
|
+
ttl,
|
|
317
|
+
autoCleanup: true
|
|
318
|
+
}),
|
|
319
|
+
tempCache: (maxSize = 200, ttl = 5 * 60 * 1e3) => createCacheWithTTL({
|
|
320
|
+
maxSize,
|
|
321
|
+
ttl,
|
|
322
|
+
autoCleanup: true
|
|
323
|
+
})
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
//#endregion
|
|
327
|
+
export { CachePresets, FastCache, FastCacheWithTTL, createCache, createCacheWithTTL };
|