layercache 1.1.0 → 1.2.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 +165 -7
- package/dist/chunk-46UH7LNM.js +312 -0
- package/dist/{chunk-QUB5VZFZ.js → chunk-GF47Y3XR.js} +16 -38
- package/dist/chunk-ZMDB5KOK.js +159 -0
- package/dist/cli.cjs +133 -23
- package/dist/cli.js +66 -4
- package/dist/edge-C1sBhTfv.d.cts +667 -0
- package/dist/edge-C1sBhTfv.d.ts +667 -0
- package/dist/edge.cjs +399 -0
- package/dist/edge.d.cts +2 -0
- package/dist/edge.d.ts +2 -0
- package/dist/edge.js +14 -0
- package/dist/index.cjs +1259 -192
- package/dist/index.d.cts +132 -480
- package/dist/index.d.ts +132 -480
- package/dist/index.js +1115 -474
- package/package.json +7 -2
- package/packages/nestjs/dist/index.cjs +1025 -327
- package/packages/nestjs/dist/index.d.cts +167 -1
- package/packages/nestjs/dist/index.d.ts +167 -1
- package/packages/nestjs/dist/index.js +1013 -325
package/dist/edge.cjs
ADDED
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/edge.ts
|
|
21
|
+
var edge_exports = {};
|
|
22
|
+
__export(edge_exports, {
|
|
23
|
+
MemoryLayer: () => MemoryLayer,
|
|
24
|
+
PatternMatcher: () => PatternMatcher,
|
|
25
|
+
TagIndex: () => TagIndex,
|
|
26
|
+
createHonoCacheMiddleware: () => createHonoCacheMiddleware
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(edge_exports);
|
|
29
|
+
|
|
30
|
+
// src/internal/StoredValue.ts
|
|
31
|
+
function isStoredValueEnvelope(value) {
|
|
32
|
+
return typeof value === "object" && value !== null && "__layercache" in value && value.__layercache === 1 && "kind" in value;
|
|
33
|
+
}
|
|
34
|
+
function unwrapStoredValue(stored) {
|
|
35
|
+
if (!isStoredValueEnvelope(stored)) {
|
|
36
|
+
return stored;
|
|
37
|
+
}
|
|
38
|
+
if (stored.kind === "empty") {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
return stored.value ?? null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// src/layers/MemoryLayer.ts
|
|
45
|
+
var MemoryLayer = class {
|
|
46
|
+
name;
|
|
47
|
+
defaultTtl;
|
|
48
|
+
isLocal = true;
|
|
49
|
+
maxSize;
|
|
50
|
+
evictionPolicy;
|
|
51
|
+
onEvict;
|
|
52
|
+
entries = /* @__PURE__ */ new Map();
|
|
53
|
+
cleanupTimer;
|
|
54
|
+
constructor(options = {}) {
|
|
55
|
+
this.name = options.name ?? "memory";
|
|
56
|
+
this.defaultTtl = options.ttl;
|
|
57
|
+
this.maxSize = options.maxSize ?? 1e3;
|
|
58
|
+
this.evictionPolicy = options.evictionPolicy ?? "lru";
|
|
59
|
+
this.onEvict = options.onEvict;
|
|
60
|
+
if (options.cleanupIntervalMs && options.cleanupIntervalMs > 0) {
|
|
61
|
+
this.cleanupTimer = setInterval(() => {
|
|
62
|
+
this.pruneExpired();
|
|
63
|
+
}, options.cleanupIntervalMs);
|
|
64
|
+
this.cleanupTimer.unref?.();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async get(key) {
|
|
68
|
+
const value = await this.getEntry(key);
|
|
69
|
+
return unwrapStoredValue(value);
|
|
70
|
+
}
|
|
71
|
+
async getEntry(key) {
|
|
72
|
+
const entry = this.entries.get(key);
|
|
73
|
+
if (!entry) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
if (this.isExpired(entry)) {
|
|
77
|
+
this.entries.delete(key);
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
if (this.evictionPolicy === "lru") {
|
|
81
|
+
this.entries.delete(key);
|
|
82
|
+
entry.accessCount += 1;
|
|
83
|
+
this.entries.set(key, entry);
|
|
84
|
+
} else if (this.evictionPolicy === "lfu") {
|
|
85
|
+
entry.accessCount += 1;
|
|
86
|
+
}
|
|
87
|
+
return entry.value;
|
|
88
|
+
}
|
|
89
|
+
async getMany(keys) {
|
|
90
|
+
const values = [];
|
|
91
|
+
for (const key of keys) {
|
|
92
|
+
values.push(await this.getEntry(key));
|
|
93
|
+
}
|
|
94
|
+
return values;
|
|
95
|
+
}
|
|
96
|
+
async setMany(entries) {
|
|
97
|
+
for (const entry of entries) {
|
|
98
|
+
await this.set(entry.key, entry.value, entry.ttl);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async set(key, value, ttl = this.defaultTtl) {
|
|
102
|
+
this.entries.delete(key);
|
|
103
|
+
this.entries.set(key, {
|
|
104
|
+
value,
|
|
105
|
+
expiresAt: ttl && ttl > 0 ? Date.now() + ttl * 1e3 : null,
|
|
106
|
+
accessCount: 0,
|
|
107
|
+
insertedAt: Date.now()
|
|
108
|
+
});
|
|
109
|
+
while (this.entries.size > this.maxSize) {
|
|
110
|
+
this.evict();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async has(key) {
|
|
114
|
+
const entry = this.entries.get(key);
|
|
115
|
+
if (!entry) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
if (this.isExpired(entry)) {
|
|
119
|
+
this.entries.delete(key);
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
async ttl(key) {
|
|
125
|
+
const entry = this.entries.get(key);
|
|
126
|
+
if (!entry) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
if (this.isExpired(entry)) {
|
|
130
|
+
this.entries.delete(key);
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
if (entry.expiresAt === null) {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
return Math.max(0, Math.ceil((entry.expiresAt - Date.now()) / 1e3));
|
|
137
|
+
}
|
|
138
|
+
async size() {
|
|
139
|
+
this.pruneExpired();
|
|
140
|
+
return this.entries.size;
|
|
141
|
+
}
|
|
142
|
+
async delete(key) {
|
|
143
|
+
this.entries.delete(key);
|
|
144
|
+
}
|
|
145
|
+
async deleteMany(keys) {
|
|
146
|
+
for (const key of keys) {
|
|
147
|
+
this.entries.delete(key);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
async clear() {
|
|
151
|
+
this.entries.clear();
|
|
152
|
+
}
|
|
153
|
+
async ping() {
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
async dispose() {
|
|
157
|
+
if (this.cleanupTimer) {
|
|
158
|
+
clearInterval(this.cleanupTimer);
|
|
159
|
+
this.cleanupTimer = void 0;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
async keys() {
|
|
163
|
+
this.pruneExpired();
|
|
164
|
+
return [...this.entries.keys()];
|
|
165
|
+
}
|
|
166
|
+
exportState() {
|
|
167
|
+
this.pruneExpired();
|
|
168
|
+
return [...this.entries.entries()].map(([key, entry]) => ({
|
|
169
|
+
key,
|
|
170
|
+
value: entry.value,
|
|
171
|
+
expiresAt: entry.expiresAt
|
|
172
|
+
}));
|
|
173
|
+
}
|
|
174
|
+
importState(entries) {
|
|
175
|
+
for (const entry of entries) {
|
|
176
|
+
if (entry.expiresAt !== null && entry.expiresAt <= Date.now()) {
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
this.entries.set(entry.key, {
|
|
180
|
+
value: entry.value,
|
|
181
|
+
expiresAt: entry.expiresAt,
|
|
182
|
+
accessCount: 0,
|
|
183
|
+
insertedAt: Date.now()
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
while (this.entries.size > this.maxSize) {
|
|
187
|
+
this.evict();
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
evict() {
|
|
191
|
+
if (this.evictionPolicy === "lru" || this.evictionPolicy === "fifo") {
|
|
192
|
+
const oldestKey = this.entries.keys().next().value;
|
|
193
|
+
if (oldestKey !== void 0) {
|
|
194
|
+
const entry = this.entries.get(oldestKey);
|
|
195
|
+
this.entries.delete(oldestKey);
|
|
196
|
+
if (entry) {
|
|
197
|
+
this.onEvict?.(oldestKey, unwrapStoredValue(entry.value));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
let victimKey;
|
|
203
|
+
let minCount = Number.POSITIVE_INFINITY;
|
|
204
|
+
let minInsertedAt = Number.POSITIVE_INFINITY;
|
|
205
|
+
for (const [key, entry] of this.entries.entries()) {
|
|
206
|
+
if (entry.accessCount < minCount || entry.accessCount === minCount && entry.insertedAt < minInsertedAt) {
|
|
207
|
+
minCount = entry.accessCount;
|
|
208
|
+
minInsertedAt = entry.insertedAt;
|
|
209
|
+
victimKey = key;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (victimKey !== void 0) {
|
|
213
|
+
const victim = this.entries.get(victimKey);
|
|
214
|
+
this.entries.delete(victimKey);
|
|
215
|
+
if (victim) {
|
|
216
|
+
this.onEvict?.(victimKey, unwrapStoredValue(victim.value));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
pruneExpired() {
|
|
221
|
+
for (const [key, entry] of this.entries.entries()) {
|
|
222
|
+
if (this.isExpired(entry)) {
|
|
223
|
+
this.entries.delete(key);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
isExpired(entry) {
|
|
228
|
+
return entry.expiresAt !== null && entry.expiresAt <= Date.now();
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
// src/invalidation/PatternMatcher.ts
|
|
233
|
+
var PatternMatcher = class _PatternMatcher {
|
|
234
|
+
/**
|
|
235
|
+
* Tests whether a glob-style pattern matches a value.
|
|
236
|
+
* Supports `*` (any sequence of characters) and `?` (any single character).
|
|
237
|
+
* Uses a two-pointer algorithm to avoid ReDoS vulnerabilities and
|
|
238
|
+
* quadratic memory usage on long patterns/keys.
|
|
239
|
+
*/
|
|
240
|
+
static matches(pattern, value) {
|
|
241
|
+
return _PatternMatcher.matchLinear(pattern, value);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Linear-time glob matching with O(1) extra memory.
|
|
245
|
+
*/
|
|
246
|
+
static matchLinear(pattern, value) {
|
|
247
|
+
let patternIndex = 0;
|
|
248
|
+
let valueIndex = 0;
|
|
249
|
+
let starIndex = -1;
|
|
250
|
+
let backtrackValueIndex = 0;
|
|
251
|
+
while (valueIndex < value.length) {
|
|
252
|
+
const patternChar = pattern[patternIndex];
|
|
253
|
+
const valueChar = value[valueIndex];
|
|
254
|
+
if (patternChar === "*" && patternIndex < pattern.length) {
|
|
255
|
+
starIndex = patternIndex;
|
|
256
|
+
patternIndex += 1;
|
|
257
|
+
backtrackValueIndex = valueIndex;
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
if (patternChar === "?" || patternChar === valueChar) {
|
|
261
|
+
patternIndex += 1;
|
|
262
|
+
valueIndex += 1;
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
if (starIndex !== -1) {
|
|
266
|
+
patternIndex = starIndex + 1;
|
|
267
|
+
backtrackValueIndex += 1;
|
|
268
|
+
valueIndex = backtrackValueIndex;
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
while (patternIndex < pattern.length && pattern[patternIndex] === "*") {
|
|
274
|
+
patternIndex += 1;
|
|
275
|
+
}
|
|
276
|
+
return patternIndex === pattern.length;
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
// src/invalidation/TagIndex.ts
|
|
281
|
+
var TagIndex = class {
|
|
282
|
+
tagToKeys = /* @__PURE__ */ new Map();
|
|
283
|
+
keyToTags = /* @__PURE__ */ new Map();
|
|
284
|
+
knownKeys = /* @__PURE__ */ new Set();
|
|
285
|
+
maxKnownKeys;
|
|
286
|
+
constructor(options = {}) {
|
|
287
|
+
this.maxKnownKeys = options.maxKnownKeys;
|
|
288
|
+
}
|
|
289
|
+
async touch(key) {
|
|
290
|
+
this.knownKeys.add(key);
|
|
291
|
+
this.pruneKnownKeysIfNeeded();
|
|
292
|
+
}
|
|
293
|
+
async track(key, tags) {
|
|
294
|
+
this.knownKeys.add(key);
|
|
295
|
+
this.pruneKnownKeysIfNeeded();
|
|
296
|
+
if (tags.length === 0) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const existingTags = this.keyToTags.get(key);
|
|
300
|
+
if (existingTags) {
|
|
301
|
+
for (const tag of existingTags) {
|
|
302
|
+
this.tagToKeys.get(tag)?.delete(key);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
const tagSet = new Set(tags);
|
|
306
|
+
this.keyToTags.set(key, tagSet);
|
|
307
|
+
for (const tag of tagSet) {
|
|
308
|
+
const keys = this.tagToKeys.get(tag) ?? /* @__PURE__ */ new Set();
|
|
309
|
+
keys.add(key);
|
|
310
|
+
this.tagToKeys.set(tag, keys);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
async remove(key) {
|
|
314
|
+
this.removeKey(key);
|
|
315
|
+
}
|
|
316
|
+
async keysForTag(tag) {
|
|
317
|
+
return [...this.tagToKeys.get(tag) ?? /* @__PURE__ */ new Set()];
|
|
318
|
+
}
|
|
319
|
+
async keysForPrefix(prefix) {
|
|
320
|
+
return [...this.knownKeys].filter((key) => key.startsWith(prefix));
|
|
321
|
+
}
|
|
322
|
+
async tagsForKey(key) {
|
|
323
|
+
return [...this.keyToTags.get(key) ?? /* @__PURE__ */ new Set()];
|
|
324
|
+
}
|
|
325
|
+
async matchPattern(pattern) {
|
|
326
|
+
return [...this.knownKeys].filter((key) => PatternMatcher.matches(pattern, key));
|
|
327
|
+
}
|
|
328
|
+
async clear() {
|
|
329
|
+
this.tagToKeys.clear();
|
|
330
|
+
this.keyToTags.clear();
|
|
331
|
+
this.knownKeys.clear();
|
|
332
|
+
}
|
|
333
|
+
pruneKnownKeysIfNeeded() {
|
|
334
|
+
if (this.maxKnownKeys === void 0 || this.knownKeys.size <= this.maxKnownKeys) {
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
const toRemove = Math.ceil(this.maxKnownKeys * 0.1);
|
|
338
|
+
let removed = 0;
|
|
339
|
+
for (const key of this.knownKeys) {
|
|
340
|
+
if (removed >= toRemove) {
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
this.removeKey(key);
|
|
344
|
+
removed += 1;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
removeKey(key) {
|
|
348
|
+
this.knownKeys.delete(key);
|
|
349
|
+
const tags = this.keyToTags.get(key);
|
|
350
|
+
if (!tags) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
for (const tag of tags) {
|
|
354
|
+
const keys = this.tagToKeys.get(tag);
|
|
355
|
+
if (!keys) {
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
keys.delete(key);
|
|
359
|
+
if (keys.size === 0) {
|
|
360
|
+
this.tagToKeys.delete(tag);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
this.keyToTags.delete(key);
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
// src/integrations/hono.ts
|
|
368
|
+
function createHonoCacheMiddleware(cache, options = {}) {
|
|
369
|
+
const allowedMethods = new Set((options.methods ?? ["GET"]).map((method) => method.toUpperCase()));
|
|
370
|
+
return async (context, next) => {
|
|
371
|
+
const method = (context.req.method ?? "GET").toUpperCase();
|
|
372
|
+
if (!allowedMethods.has(method)) {
|
|
373
|
+
await next();
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
const key = options.keyResolver ? options.keyResolver(context.req) : `${method}:${context.req.path ?? context.req.url ?? "/"}`;
|
|
377
|
+
const cached = await cache.get(key, void 0, options);
|
|
378
|
+
if (cached !== null) {
|
|
379
|
+
context.header?.("x-cache", "HIT");
|
|
380
|
+
context.header?.("content-type", "application/json; charset=utf-8");
|
|
381
|
+
context.json(cached);
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
const originalJson = context.json.bind(context);
|
|
385
|
+
context.json = (body, status) => {
|
|
386
|
+
context.header?.("x-cache", "MISS");
|
|
387
|
+
void cache.set(key, body, options);
|
|
388
|
+
return originalJson(body, status);
|
|
389
|
+
};
|
|
390
|
+
await next();
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
394
|
+
0 && (module.exports = {
|
|
395
|
+
MemoryLayer,
|
|
396
|
+
PatternMatcher,
|
|
397
|
+
TagIndex,
|
|
398
|
+
createHonoCacheMiddleware
|
|
399
|
+
});
|
package/dist/edge.d.cts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { e as CacheGetOptions, f as CacheLayer, h as CacheLayerSetManyEntry, t as CacheMetricsSnapshot, w as CacheRateLimitOptions, B as CacheTtlPolicy, D as CacheTtlPolicyContext, J as CacheWriteOptions, K as EvictionPolicy, M as MemoryLayer, N as MemoryLayerOptions, O as MemoryLayerSnapshotEntry, P as PatternMatcher, T as TagIndex, Q as createHonoCacheMiddleware } from './edge-C1sBhTfv.cjs';
|
|
2
|
+
import 'node:events';
|
package/dist/edge.d.ts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { e as CacheGetOptions, f as CacheLayer, h as CacheLayerSetManyEntry, t as CacheMetricsSnapshot, w as CacheRateLimitOptions, B as CacheTtlPolicy, D as CacheTtlPolicyContext, J as CacheWriteOptions, K as EvictionPolicy, M as MemoryLayer, N as MemoryLayerOptions, O as MemoryLayerSnapshotEntry, P as PatternMatcher, T as TagIndex, Q as createHonoCacheMiddleware } from './edge-C1sBhTfv.js';
|
|
2
|
+
import 'node:events';
|
package/dist/edge.js
ADDED