layercache 1.2.4 → 1.2.5
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 +6 -0
- package/dist/{chunk-KOYGHLVP.js → chunk-JC26W3KK.js} +27 -7
- package/dist/cli.cjs +2 -16
- package/dist/cli.js +2 -16
- package/dist/{edge-Dw97n89L.d.cts → edge-P07GCO2Y.d.cts} +1 -0
- package/dist/{edge-Dw97n89L.d.ts → edge-P07GCO2Y.d.ts} +1 -0
- package/dist/edge.cjs +27 -7
- package/dist/edge.d.cts +1 -1
- package/dist/edge.d.ts +1 -1
- package/dist/edge.js +1 -1
- package/dist/index.cjs +122 -30
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +96 -24
- package/package.json +1 -1
- package/packages/nestjs/dist/index.cjs +96 -23
- package/packages/nestjs/dist/index.d.cts +1 -0
- package/packages/nestjs/dist/index.d.ts +1 -0
- package/packages/nestjs/dist/index.js +96 -23
|
@@ -338,6 +338,7 @@ var CacheNamespace = class _CacheNamespace {
|
|
|
338
338
|
* ```
|
|
339
339
|
*/
|
|
340
340
|
namespace(childPrefix) {
|
|
341
|
+
validateNamespaceKey(childPrefix);
|
|
341
342
|
return new _CacheNamespace(this.cache, `${this.prefix}:${childPrefix}`);
|
|
342
343
|
}
|
|
343
344
|
qualify(key) {
|
|
@@ -467,6 +468,17 @@ function addMap(base, delta) {
|
|
|
467
468
|
}
|
|
468
469
|
return result;
|
|
469
470
|
}
|
|
471
|
+
function validateNamespaceKey(key) {
|
|
472
|
+
if (key.length === 0) {
|
|
473
|
+
throw new Error("Namespace prefix must not be empty.");
|
|
474
|
+
}
|
|
475
|
+
if (key.length > 256) {
|
|
476
|
+
throw new Error("Namespace prefix must be at most 256 characters.");
|
|
477
|
+
}
|
|
478
|
+
if (/[\u0000-\u001F\u007F]/.test(key)) {
|
|
479
|
+
throw new Error("Namespace prefix contains unsupported control characters.");
|
|
480
|
+
}
|
|
481
|
+
}
|
|
470
482
|
|
|
471
483
|
// ../../src/invalidation/PatternMatcher.ts
|
|
472
484
|
var PatternMatcher = class _PatternMatcher {
|
|
@@ -587,9 +599,7 @@ var CircuitBreakerManager = class {
|
|
|
587
599
|
}
|
|
588
600
|
const now = Date.now();
|
|
589
601
|
if (state.openUntil <= now) {
|
|
590
|
-
|
|
591
|
-
state.failures = 0;
|
|
592
|
-
this.breakers.set(key, state);
|
|
602
|
+
this.breakers.delete(key);
|
|
593
603
|
return;
|
|
594
604
|
}
|
|
595
605
|
const remainingMs = state.openUntil - now;
|
|
@@ -600,15 +610,15 @@ var CircuitBreakerManager = class {
|
|
|
600
610
|
if (!options) {
|
|
601
611
|
return;
|
|
602
612
|
}
|
|
613
|
+
this.pruneIfNeeded();
|
|
603
614
|
const failureThreshold = options.failureThreshold ?? 3;
|
|
604
615
|
const cooldownMs = options.cooldownMs ?? 3e4;
|
|
605
|
-
const state = this.breakers.get(key) ?? { failures: 0, openUntil: null };
|
|
616
|
+
const state = this.breakers.get(key) ?? { failures: 0, openUntil: null, createdAt: Date.now() };
|
|
606
617
|
state.failures += 1;
|
|
607
618
|
if (state.failures >= failureThreshold) {
|
|
608
619
|
state.openUntil = Date.now() + cooldownMs;
|
|
609
620
|
}
|
|
610
621
|
this.breakers.set(key, state);
|
|
611
|
-
this.pruneIfNeeded();
|
|
612
622
|
}
|
|
613
623
|
recordSuccess(key) {
|
|
614
624
|
this.breakers.delete(key);
|
|
@@ -619,8 +629,7 @@ var CircuitBreakerManager = class {
|
|
|
619
629
|
return false;
|
|
620
630
|
}
|
|
621
631
|
if (state.openUntil <= Date.now()) {
|
|
622
|
-
|
|
623
|
-
state.failures = 0;
|
|
632
|
+
this.breakers.delete(key);
|
|
624
633
|
return false;
|
|
625
634
|
}
|
|
626
635
|
return true;
|
|
@@ -644,15 +653,20 @@ var CircuitBreakerManager = class {
|
|
|
644
653
|
if (this.breakers.size <= this.maxEntries) {
|
|
645
654
|
return;
|
|
646
655
|
}
|
|
656
|
+
const now = Date.now();
|
|
647
657
|
for (const [key, state] of this.breakers.entries()) {
|
|
648
658
|
if (this.breakers.size <= this.maxEntries) {
|
|
649
|
-
|
|
659
|
+
return;
|
|
650
660
|
}
|
|
651
|
-
if (!state.openUntil || state.openUntil <=
|
|
661
|
+
if (!state.openUntil || state.openUntil <= now) {
|
|
652
662
|
this.breakers.delete(key);
|
|
653
663
|
}
|
|
654
664
|
}
|
|
655
|
-
|
|
665
|
+
if (this.breakers.size <= this.maxEntries) {
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
const sorted = [...this.breakers.entries()].sort((a, b) => a[1].createdAt - b[1].createdAt);
|
|
669
|
+
for (const [key] of sorted) {
|
|
656
670
|
if (this.breakers.size <= this.maxEntries) {
|
|
657
671
|
break;
|
|
658
672
|
}
|
|
@@ -662,6 +676,7 @@ var CircuitBreakerManager = class {
|
|
|
662
676
|
};
|
|
663
677
|
|
|
664
678
|
// ../../src/internal/FetchRateLimiter.ts
|
|
679
|
+
var MAX_BUCKETS = 1e4;
|
|
665
680
|
var FetchRateLimiter = class {
|
|
666
681
|
buckets = /* @__PURE__ */ new Map();
|
|
667
682
|
queuesByBucket = /* @__PURE__ */ new Map();
|
|
@@ -827,10 +842,25 @@ var FetchRateLimiter = class {
|
|
|
827
842
|
if (existing) {
|
|
828
843
|
return existing;
|
|
829
844
|
}
|
|
845
|
+
if (this.buckets.size >= MAX_BUCKETS) {
|
|
846
|
+
this.evictIdleBuckets();
|
|
847
|
+
}
|
|
830
848
|
const bucket = { active: 0, startedAt: [] };
|
|
831
849
|
this.buckets.set(bucketKey, bucket);
|
|
832
850
|
return bucket;
|
|
833
851
|
}
|
|
852
|
+
evictIdleBuckets() {
|
|
853
|
+
for (const [key, bucket] of this.buckets.entries()) {
|
|
854
|
+
if (this.buckets.size <= MAX_BUCKETS * 0.9) {
|
|
855
|
+
break;
|
|
856
|
+
}
|
|
857
|
+
if (bucket.active === 0 && bucket.startedAt.length === 0 && !this.queuesByBucket.has(key)) {
|
|
858
|
+
this.buckets.delete(key);
|
|
859
|
+
this.queuesByBucket.delete(key);
|
|
860
|
+
this.pendingBuckets.delete(key);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
834
864
|
cleanupBucket(bucketKey, bucket, intervalMs) {
|
|
835
865
|
const queued = this.queuesByBucket.get(bucketKey)?.length ?? 0;
|
|
836
866
|
if (queued === 0 && bucket.active === 0 && bucket.startedAt.length === 0) {
|
|
@@ -1157,18 +1187,18 @@ var TtlResolver = class {
|
|
|
1157
1187
|
return;
|
|
1158
1188
|
}
|
|
1159
1189
|
const toRemove = Math.ceil(this.maxProfileEntries * 0.1);
|
|
1160
|
-
|
|
1161
|
-
for (
|
|
1162
|
-
|
|
1163
|
-
|
|
1190
|
+
const sorted = [...this.accessProfiles.entries()].sort((a, b) => a[1].lastAccessAt - b[1].lastAccessAt);
|
|
1191
|
+
for (let i = 0; i < toRemove && i < sorted.length; i++) {
|
|
1192
|
+
const entry = sorted[i];
|
|
1193
|
+
if (entry) {
|
|
1194
|
+
this.accessProfiles.delete(entry[0]);
|
|
1164
1195
|
}
|
|
1165
|
-
this.accessProfiles.delete(key);
|
|
1166
|
-
removed += 1;
|
|
1167
1196
|
}
|
|
1168
1197
|
}
|
|
1169
1198
|
};
|
|
1170
1199
|
|
|
1171
1200
|
// ../../src/invalidation/TagIndex.ts
|
|
1201
|
+
var MAX_PATTERN_RECURSION_DEPTH = 500;
|
|
1172
1202
|
var TagIndex = class {
|
|
1173
1203
|
tagToKeys = /* @__PURE__ */ new Map();
|
|
1174
1204
|
keyToTags = /* @__PURE__ */ new Map();
|
|
@@ -1223,7 +1253,7 @@ var TagIndex = class {
|
|
|
1223
1253
|
}
|
|
1224
1254
|
async matchPattern(pattern) {
|
|
1225
1255
|
const matches = /* @__PURE__ */ new Set();
|
|
1226
|
-
this.collectPatternMatches(this.root, "", pattern, 0, matches, /* @__PURE__ */ new Set());
|
|
1256
|
+
this.collectPatternMatches(this.root, "", pattern, 0, matches, /* @__PURE__ */ new Set(), 0);
|
|
1227
1257
|
return [...matches];
|
|
1228
1258
|
}
|
|
1229
1259
|
async clear() {
|
|
@@ -1275,7 +1305,10 @@ var TagIndex = class {
|
|
|
1275
1305
|
this.collectFromNode(child, `${prefix}${character}`, matches);
|
|
1276
1306
|
}
|
|
1277
1307
|
}
|
|
1278
|
-
collectPatternMatches(node, prefix, pattern, patternIndex, matches, visited) {
|
|
1308
|
+
collectPatternMatches(node, prefix, pattern, patternIndex, matches, visited, depth) {
|
|
1309
|
+
if (depth > MAX_PATTERN_RECURSION_DEPTH) {
|
|
1310
|
+
return;
|
|
1311
|
+
}
|
|
1279
1312
|
const stateKey = `${node.id}:${patternIndex}`;
|
|
1280
1313
|
if (visited.has(stateKey)) {
|
|
1281
1314
|
return;
|
|
@@ -1292,21 +1325,37 @@ var TagIndex = class {
|
|
|
1292
1325
|
return;
|
|
1293
1326
|
}
|
|
1294
1327
|
if (patternChar === "*") {
|
|
1295
|
-
this.collectPatternMatches(node, prefix, pattern, patternIndex + 1, matches, visited);
|
|
1328
|
+
this.collectPatternMatches(node, prefix, pattern, patternIndex + 1, matches, visited, depth + 1);
|
|
1296
1329
|
for (const [character, child2] of node.children) {
|
|
1297
|
-
this.collectPatternMatches(child2, `${prefix}${character}`, pattern, patternIndex, matches, visited);
|
|
1330
|
+
this.collectPatternMatches(child2, `${prefix}${character}`, pattern, patternIndex, matches, visited, depth + 1);
|
|
1298
1331
|
}
|
|
1299
1332
|
return;
|
|
1300
1333
|
}
|
|
1301
1334
|
if (patternChar === "?") {
|
|
1302
1335
|
for (const [character, child2] of node.children) {
|
|
1303
|
-
this.collectPatternMatches(
|
|
1336
|
+
this.collectPatternMatches(
|
|
1337
|
+
child2,
|
|
1338
|
+
`${prefix}${character}`,
|
|
1339
|
+
pattern,
|
|
1340
|
+
patternIndex + 1,
|
|
1341
|
+
matches,
|
|
1342
|
+
visited,
|
|
1343
|
+
depth + 1
|
|
1344
|
+
);
|
|
1304
1345
|
}
|
|
1305
1346
|
return;
|
|
1306
1347
|
}
|
|
1307
1348
|
const child = node.children.get(patternChar);
|
|
1308
1349
|
if (child) {
|
|
1309
|
-
this.collectPatternMatches(
|
|
1350
|
+
this.collectPatternMatches(
|
|
1351
|
+
child,
|
|
1352
|
+
`${prefix}${patternChar}`,
|
|
1353
|
+
pattern,
|
|
1354
|
+
patternIndex + 1,
|
|
1355
|
+
matches,
|
|
1356
|
+
visited,
|
|
1357
|
+
depth + 1
|
|
1358
|
+
);
|
|
1310
1359
|
}
|
|
1311
1360
|
}
|
|
1312
1361
|
pruneKnownKeysIfNeeded() {
|
|
@@ -1448,6 +1497,7 @@ var DEFAULT_SINGLE_FLIGHT_TIMEOUT_MS = 5e3;
|
|
|
1448
1497
|
var DEFAULT_SINGLE_FLIGHT_POLL_MS = 50;
|
|
1449
1498
|
var DEFAULT_BACKGROUND_REFRESH_TIMEOUT_MS = 3e4;
|
|
1450
1499
|
var MAX_CACHE_KEY_LENGTH = 1024;
|
|
1500
|
+
var MAX_PATTERN_LENGTH = 1024;
|
|
1451
1501
|
var DEFAULT_MAX_PROFILE_ENTRIES = 1e5;
|
|
1452
1502
|
var DANGEROUS_OBJECT_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
|
|
1453
1503
|
var DebugLogger = class {
|
|
@@ -1881,6 +1931,7 @@ var CacheStack = class extends EventEmitter {
|
|
|
1881
1931
|
await this.publishInvalidation({ scope: "keys", keys, sourceId: this.instanceId, operation: "invalidate" });
|
|
1882
1932
|
}
|
|
1883
1933
|
async invalidateByPattern(pattern) {
|
|
1934
|
+
this.validatePattern(pattern);
|
|
1884
1935
|
await this.awaitStartup("invalidateByPattern");
|
|
1885
1936
|
const keys = await this.keyDiscovery.collectKeysMatchingPattern(this.qualifyPattern(pattern));
|
|
1886
1937
|
await this.deleteKeys(keys);
|
|
@@ -2774,6 +2825,17 @@ var CacheStack = class extends EventEmitter {
|
|
|
2774
2825
|
}
|
|
2775
2826
|
return key;
|
|
2776
2827
|
}
|
|
2828
|
+
validatePattern(pattern) {
|
|
2829
|
+
if (pattern.length === 0) {
|
|
2830
|
+
throw new Error("Pattern must not be empty.");
|
|
2831
|
+
}
|
|
2832
|
+
if (pattern.length > MAX_PATTERN_LENGTH) {
|
|
2833
|
+
throw new Error(`Pattern length must be at most ${MAX_PATTERN_LENGTH} characters.`);
|
|
2834
|
+
}
|
|
2835
|
+
if (/[\u0000-\u001F\u007F]/.test(pattern)) {
|
|
2836
|
+
throw new Error("Pattern contains unsupported control characters.");
|
|
2837
|
+
}
|
|
2838
|
+
}
|
|
2777
2839
|
validateTtlPolicy(name, policy) {
|
|
2778
2840
|
if (!policy || typeof policy === "function" || policy === "until-midnight" || policy === "next-hour") {
|
|
2779
2841
|
return;
|
|
@@ -2938,7 +3000,18 @@ var CacheStack = class extends EventEmitter {
|
|
|
2938
3000
|
}
|
|
2939
3001
|
};
|
|
2940
3002
|
function createInstanceId() {
|
|
2941
|
-
|
|
3003
|
+
if (globalThis.crypto?.randomUUID) {
|
|
3004
|
+
return globalThis.crypto.randomUUID();
|
|
3005
|
+
}
|
|
3006
|
+
const bytes = new Uint8Array(16);
|
|
3007
|
+
if (globalThis.crypto?.getRandomValues) {
|
|
3008
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
3009
|
+
} else {
|
|
3010
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
3011
|
+
bytes[i] = Math.floor(Math.random() * 256);
|
|
3012
|
+
}
|
|
3013
|
+
}
|
|
3014
|
+
return `layercache-${Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("")}`;
|
|
2942
3015
|
}
|
|
2943
3016
|
|
|
2944
3017
|
// src/module.ts
|