layercache 2.1.0 → 3.1.0
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 +29 -9
- package/dist/{chunk-6X7NV5BG.js → chunk-L6L7QXYF.js} +95 -14
- package/dist/{chunk-IVX6ABFX.js → chunk-XMUT66SH.js} +116 -90
- package/dist/cli.cjs +153 -25
- package/dist/cli.js +69 -13
- package/dist/{edge-BCU8D-Yd.d.cts → edge-LBUuZAdr.d.cts} +61 -2
- package/dist/{edge-BCU8D-Yd.d.ts → edge-LBUuZAdr.d.ts} +61 -2
- package/dist/edge.cjs +114 -90
- package/dist/edge.d.cts +1 -1
- package/dist/edge.d.ts +1 -1
- package/dist/edge.js +1 -1
- package/dist/index.cjs +578 -220
- package/dist/index.d.cts +55 -3
- package/dist/index.d.ts +55 -3
- package/dist/index.js +366 -113
- package/package.json +1 -1
|
@@ -47,6 +47,8 @@ interface CacheContextOptionsContext {
|
|
|
47
47
|
interface CacheWriteOptions extends CacheEntryWriteOptions {
|
|
48
48
|
/** Cache `null` fetcher results using `negativeTtl` instead of treating them as misses. */
|
|
49
49
|
negativeCache?: boolean;
|
|
50
|
+
/** Cache `null` fetcher results as regular values instead of negative/empty entries. */
|
|
51
|
+
cacheNullValues?: boolean;
|
|
50
52
|
/** Extend a key's TTL on fresh reads. */
|
|
51
53
|
slidingTtl?: boolean;
|
|
52
54
|
/** Refresh in the background when the remaining TTL is at or below this threshold in milliseconds. */
|
|
@@ -71,6 +73,7 @@ interface CacheWriteOptions extends CacheEntryWriteOptions {
|
|
|
71
73
|
*
|
|
72
74
|
* Returned values override any static entry options already present on the
|
|
73
75
|
* same object. Fetch controls like `shouldCache`, `negativeCache`,
|
|
76
|
+
* `cacheNullValues`,
|
|
74
77
|
* `refreshAhead`, or `circuitBreaker` are not affected.
|
|
75
78
|
*
|
|
76
79
|
* @example
|
|
@@ -340,6 +343,8 @@ interface CacheStackOptions {
|
|
|
340
343
|
publishSetInvalidation?: boolean;
|
|
341
344
|
/** Cache null fetcher results as negative entries. */
|
|
342
345
|
negativeCaching?: boolean;
|
|
346
|
+
/** Cache null fetcher results as regular values instead of negative/empty entries. */
|
|
347
|
+
cacheNullValues?: boolean;
|
|
343
348
|
/** Default negative-cache TTL in milliseconds. */
|
|
344
349
|
negativeTtl?: number | LayerTtlMap;
|
|
345
350
|
/** Default stale-while-revalidate window in milliseconds. */
|
|
@@ -422,6 +427,13 @@ interface CacheCircuitBreakerOptions {
|
|
|
422
427
|
failureThreshold?: number;
|
|
423
428
|
/** Milliseconds before an open circuit allows another attempt. */
|
|
424
429
|
cooldownMs?: number;
|
|
430
|
+
/**
|
|
431
|
+
* Failure scope. `key` preserves the historical per-cache-key behavior.
|
|
432
|
+
* `shared` uses one breaker for all fetches using these options.
|
|
433
|
+
*/
|
|
434
|
+
scope?: 'key' | 'shared';
|
|
435
|
+
/** Custom breaker id for grouping related fetches, such as one backend dependency. */
|
|
436
|
+
breakerKey?: string;
|
|
425
437
|
}
|
|
426
438
|
/** Graceful degradation settings for temporarily unhealthy layers. */
|
|
427
439
|
interface CacheDegradationOptions {
|
|
@@ -440,6 +452,11 @@ interface CacheRateLimitOptions {
|
|
|
440
452
|
scope?: 'global' | 'key' | 'fetcher';
|
|
441
453
|
/** Custom bucket id used to group otherwise unrelated fetches. */
|
|
442
454
|
bucketKey?: string;
|
|
455
|
+
/**
|
|
456
|
+
* Behavior when a bucket queue reaches its internal safety limit.
|
|
457
|
+
* Defaults to `reject` so overflow is explicit instead of bypassing limits.
|
|
458
|
+
*/
|
|
459
|
+
queueOverflow?: 'reject' | 'bypass';
|
|
443
460
|
}
|
|
444
461
|
/** Queue controls for write-behind mode. */
|
|
445
462
|
interface CacheWriteBehindOptions {
|
|
@@ -539,6 +556,19 @@ interface CacheInspectResult {
|
|
|
539
556
|
/** Tags associated with this key (from the TagIndex). */
|
|
540
557
|
tags: string[];
|
|
541
558
|
}
|
|
559
|
+
/** Cached entry result that can distinguish a stored `null` from a miss. */
|
|
560
|
+
interface CacheEntryResult<T = unknown> {
|
|
561
|
+
/** User-facing cache key. */
|
|
562
|
+
key: string;
|
|
563
|
+
/** Unwrapped cached value. May be `null` when `kind` is `value` or `empty`. */
|
|
564
|
+
value: T | null;
|
|
565
|
+
/** Whether this entry stores a normal value or a negative-cache empty marker. */
|
|
566
|
+
kind: 'value' | 'empty';
|
|
567
|
+
/** Fresh/stale state currently available for this entry. */
|
|
568
|
+
state: 'fresh' | 'stale-while-revalidate' | 'stale-if-error';
|
|
569
|
+
/** First layer that supplied the entry. */
|
|
570
|
+
layer: string;
|
|
571
|
+
}
|
|
542
572
|
/** All events emitted by CacheStack and their payload shapes. */
|
|
543
573
|
interface CacheStackEvents {
|
|
544
574
|
/** Fired on every cache hit. */
|
|
@@ -746,12 +776,18 @@ interface TagIndexOptions {
|
|
|
746
776
|
* Defaults to 100,000.
|
|
747
777
|
*/
|
|
748
778
|
maxKnownKeys?: number;
|
|
779
|
+
/**
|
|
780
|
+
* Minimum age before an existing key touch refreshes LRU order.
|
|
781
|
+
* Defaults to 1000ms to avoid delete/set churn on cache-hit hot paths.
|
|
782
|
+
*/
|
|
783
|
+
touchRefreshIntervalMs?: number;
|
|
749
784
|
}
|
|
750
785
|
declare class TagIndex implements CacheTagIndex {
|
|
751
786
|
private readonly tagToKeys;
|
|
752
787
|
private readonly keyToTags;
|
|
753
788
|
private readonly knownKeys;
|
|
754
789
|
private readonly maxKnownKeys;
|
|
790
|
+
private readonly touchRefreshIntervalMs;
|
|
755
791
|
private nextNodeId;
|
|
756
792
|
private readonly root;
|
|
757
793
|
constructor(options?: TagIndexOptions);
|
|
@@ -804,7 +840,7 @@ declare class TagIndex implements CacheTagIndex {
|
|
|
804
840
|
private findNode;
|
|
805
841
|
private collectFromNode;
|
|
806
842
|
private visitFromNode;
|
|
807
|
-
private
|
|
843
|
+
private literalPrefix;
|
|
808
844
|
private pruneKnownKeysIfNeeded;
|
|
809
845
|
private removeKey;
|
|
810
846
|
private removeKnownKey;
|
|
@@ -834,6 +870,11 @@ declare class CacheNamespace {
|
|
|
834
870
|
* Alias for `get(key, fetcher, options)` that makes the get-or-set behavior explicit.
|
|
835
871
|
*/
|
|
836
872
|
getOrSet<T>(key: string, fetcher: CacheFetcher<T>, options?: CacheGetOptions): Promise<T | null>;
|
|
873
|
+
/**
|
|
874
|
+
* Returns a namespaced cache entry, or `null` on miss.
|
|
875
|
+
* Unlike `get()`, this distinguishes a stored `null` value from an absent key.
|
|
876
|
+
*/
|
|
877
|
+
getEntry<T>(key: string): Promise<CacheEntryResult<T> | null>;
|
|
837
878
|
/**
|
|
838
879
|
* Like `get()`, but throws `CacheMissError` instead of returning `null`.
|
|
839
880
|
*/
|
|
@@ -1024,6 +1065,11 @@ declare class CacheStack extends EventEmitter {
|
|
|
1024
1065
|
* Fetches and caches the value if not already present.
|
|
1025
1066
|
*/
|
|
1026
1067
|
getOrSet<T>(key: string, fetcher: CacheFetcher<T>, options?: CacheGetOptions): Promise<T | null>;
|
|
1068
|
+
/**
|
|
1069
|
+
* Returns a discriminated cache entry, or `null` on miss.
|
|
1070
|
+
* Unlike `get()`, this distinguishes a stored `null` value from an absent key.
|
|
1071
|
+
*/
|
|
1072
|
+
getEntry<T>(key: string): Promise<CacheEntryResult<T> | null>;
|
|
1027
1073
|
/**
|
|
1028
1074
|
* Like `get()`, but throws `CacheMissError` instead of returning `null`.
|
|
1029
1075
|
* Useful when the value is expected to exist or the fetcher is expected to
|
|
@@ -1139,6 +1185,14 @@ declare class CacheStack extends EventEmitter {
|
|
|
1139
1185
|
* Returns cumulative cache metrics since startup or the last `resetMetrics()`.
|
|
1140
1186
|
*/
|
|
1141
1187
|
getMetrics(): CacheMetricsSnapshot;
|
|
1188
|
+
/**
|
|
1189
|
+
* Runs an operation while collecting only the metrics emitted by its async context.
|
|
1190
|
+
* Used by namespaces so metrics tracking does not serialize the operation itself.
|
|
1191
|
+
*/
|
|
1192
|
+
captureMetrics<T>(operation: () => Promise<T>): Promise<{
|
|
1193
|
+
result: T;
|
|
1194
|
+
metrics: CacheMetricsSnapshot;
|
|
1195
|
+
}>;
|
|
1142
1196
|
/**
|
|
1143
1197
|
* Returns metrics plus layer degradation state and active background refresh count.
|
|
1144
1198
|
*/
|
|
@@ -1162,6 +1216,10 @@ declare class CacheStack extends EventEmitter {
|
|
|
1162
1216
|
* unless `generationCleanup` is enabled to prune them in the background.
|
|
1163
1217
|
*/
|
|
1164
1218
|
bumpGeneration(nextGeneration?: number): number;
|
|
1219
|
+
/**
|
|
1220
|
+
* Returns the active generation prefix number used for future cache keys.
|
|
1221
|
+
*/
|
|
1222
|
+
getGeneration(): number | undefined;
|
|
1165
1223
|
/**
|
|
1166
1224
|
* Returns detailed metadata about a single cache key: which layers contain it,
|
|
1167
1225
|
* remaining fresh/stale/error TTLs, and associated tags.
|
|
@@ -1246,6 +1304,7 @@ interface HonoLikeRequest {
|
|
|
1246
1304
|
interface HonoLikeContext {
|
|
1247
1305
|
req: HonoLikeRequest;
|
|
1248
1306
|
header?: (name: string, value: string) => void;
|
|
1307
|
+
status?: (status: number) => unknown;
|
|
1249
1308
|
json: (body: unknown, status?: number) => Response | Promise<Response> | unknown;
|
|
1250
1309
|
}
|
|
1251
1310
|
interface HonoCacheMiddlewareOptions extends CacheGetOptions {
|
|
@@ -1264,4 +1323,4 @@ interface HonoCacheMiddlewareOptions extends CacheGetOptions {
|
|
|
1264
1323
|
*/
|
|
1265
1324
|
declare function createHonoCacheMiddleware(cache: CacheStack, options?: HonoCacheMiddlewareOptions): (context: HonoLikeContext, next: () => Promise<void>) => Promise<unknown>;
|
|
1266
1325
|
|
|
1267
|
-
export {
|
|
1326
|
+
export { CacheMissError as A, CacheNamespace as B, type CacheLogger as C, type CacheRateLimitOptions as D, type CacheSnapshotEntry as E, type CacheStackEvents as F, type CacheStackOptions as G, type CacheStatsSnapshot as H, type InvalidationBus as I, type CacheTtlPolicy as J, type CacheTtlPolicyContext as K, type CacheWarmEntry as L, type CacheWarmOptions as M, type CacheWarmProgress as N, type CacheWriteBehindOptions as O, type CacheWriteOptions as P, type EvictionPolicy as Q, type LayerTtlMap as R, MemoryLayer as S, type MemoryLayerOptions as T, type MemoryLayerSnapshotEntry as U, PatternMatcher as V, TagIndex as W, createHonoCacheMiddleware as X, type InvalidationMessage as a, type CacheTagIndex as b, CacheStack as c, type CacheWrapOptions as d, type CacheGetOptions as e, type CacheLayer as f, type CacheSerializer as g, type CacheLayerSetManyEntry as h, type CacheSingleFlightCoordinator as i, type CacheSingleFlightExecutionOptions as j, type CacheAdaptiveTtlOptions as k, type CacheCircuitBreakerOptions as l, type CacheContextOptionsContext as m, type CacheDegradationOptions as n, type CacheEntryResult as o, type CacheEntryWriteKind as p, type CacheEntryWriteOptions as q, type CacheFetcher as r, type CacheFetcherContext as s, type CacheHealthCheckResult as t, type CacheHitRateSnapshot as u, type CacheInspectResult as v, type CacheLayerLatency as w, type CacheMGetEntry as x, type CacheMSetEntry as y, type CacheMetricsSnapshot as z };
|
|
@@ -47,6 +47,8 @@ interface CacheContextOptionsContext {
|
|
|
47
47
|
interface CacheWriteOptions extends CacheEntryWriteOptions {
|
|
48
48
|
/** Cache `null` fetcher results using `negativeTtl` instead of treating them as misses. */
|
|
49
49
|
negativeCache?: boolean;
|
|
50
|
+
/** Cache `null` fetcher results as regular values instead of negative/empty entries. */
|
|
51
|
+
cacheNullValues?: boolean;
|
|
50
52
|
/** Extend a key's TTL on fresh reads. */
|
|
51
53
|
slidingTtl?: boolean;
|
|
52
54
|
/** Refresh in the background when the remaining TTL is at or below this threshold in milliseconds. */
|
|
@@ -71,6 +73,7 @@ interface CacheWriteOptions extends CacheEntryWriteOptions {
|
|
|
71
73
|
*
|
|
72
74
|
* Returned values override any static entry options already present on the
|
|
73
75
|
* same object. Fetch controls like `shouldCache`, `negativeCache`,
|
|
76
|
+
* `cacheNullValues`,
|
|
74
77
|
* `refreshAhead`, or `circuitBreaker` are not affected.
|
|
75
78
|
*
|
|
76
79
|
* @example
|
|
@@ -340,6 +343,8 @@ interface CacheStackOptions {
|
|
|
340
343
|
publishSetInvalidation?: boolean;
|
|
341
344
|
/** Cache null fetcher results as negative entries. */
|
|
342
345
|
negativeCaching?: boolean;
|
|
346
|
+
/** Cache null fetcher results as regular values instead of negative/empty entries. */
|
|
347
|
+
cacheNullValues?: boolean;
|
|
343
348
|
/** Default negative-cache TTL in milliseconds. */
|
|
344
349
|
negativeTtl?: number | LayerTtlMap;
|
|
345
350
|
/** Default stale-while-revalidate window in milliseconds. */
|
|
@@ -422,6 +427,13 @@ interface CacheCircuitBreakerOptions {
|
|
|
422
427
|
failureThreshold?: number;
|
|
423
428
|
/** Milliseconds before an open circuit allows another attempt. */
|
|
424
429
|
cooldownMs?: number;
|
|
430
|
+
/**
|
|
431
|
+
* Failure scope. `key` preserves the historical per-cache-key behavior.
|
|
432
|
+
* `shared` uses one breaker for all fetches using these options.
|
|
433
|
+
*/
|
|
434
|
+
scope?: 'key' | 'shared';
|
|
435
|
+
/** Custom breaker id for grouping related fetches, such as one backend dependency. */
|
|
436
|
+
breakerKey?: string;
|
|
425
437
|
}
|
|
426
438
|
/** Graceful degradation settings for temporarily unhealthy layers. */
|
|
427
439
|
interface CacheDegradationOptions {
|
|
@@ -440,6 +452,11 @@ interface CacheRateLimitOptions {
|
|
|
440
452
|
scope?: 'global' | 'key' | 'fetcher';
|
|
441
453
|
/** Custom bucket id used to group otherwise unrelated fetches. */
|
|
442
454
|
bucketKey?: string;
|
|
455
|
+
/**
|
|
456
|
+
* Behavior when a bucket queue reaches its internal safety limit.
|
|
457
|
+
* Defaults to `reject` so overflow is explicit instead of bypassing limits.
|
|
458
|
+
*/
|
|
459
|
+
queueOverflow?: 'reject' | 'bypass';
|
|
443
460
|
}
|
|
444
461
|
/** Queue controls for write-behind mode. */
|
|
445
462
|
interface CacheWriteBehindOptions {
|
|
@@ -539,6 +556,19 @@ interface CacheInspectResult {
|
|
|
539
556
|
/** Tags associated with this key (from the TagIndex). */
|
|
540
557
|
tags: string[];
|
|
541
558
|
}
|
|
559
|
+
/** Cached entry result that can distinguish a stored `null` from a miss. */
|
|
560
|
+
interface CacheEntryResult<T = unknown> {
|
|
561
|
+
/** User-facing cache key. */
|
|
562
|
+
key: string;
|
|
563
|
+
/** Unwrapped cached value. May be `null` when `kind` is `value` or `empty`. */
|
|
564
|
+
value: T | null;
|
|
565
|
+
/** Whether this entry stores a normal value or a negative-cache empty marker. */
|
|
566
|
+
kind: 'value' | 'empty';
|
|
567
|
+
/** Fresh/stale state currently available for this entry. */
|
|
568
|
+
state: 'fresh' | 'stale-while-revalidate' | 'stale-if-error';
|
|
569
|
+
/** First layer that supplied the entry. */
|
|
570
|
+
layer: string;
|
|
571
|
+
}
|
|
542
572
|
/** All events emitted by CacheStack and their payload shapes. */
|
|
543
573
|
interface CacheStackEvents {
|
|
544
574
|
/** Fired on every cache hit. */
|
|
@@ -746,12 +776,18 @@ interface TagIndexOptions {
|
|
|
746
776
|
* Defaults to 100,000.
|
|
747
777
|
*/
|
|
748
778
|
maxKnownKeys?: number;
|
|
779
|
+
/**
|
|
780
|
+
* Minimum age before an existing key touch refreshes LRU order.
|
|
781
|
+
* Defaults to 1000ms to avoid delete/set churn on cache-hit hot paths.
|
|
782
|
+
*/
|
|
783
|
+
touchRefreshIntervalMs?: number;
|
|
749
784
|
}
|
|
750
785
|
declare class TagIndex implements CacheTagIndex {
|
|
751
786
|
private readonly tagToKeys;
|
|
752
787
|
private readonly keyToTags;
|
|
753
788
|
private readonly knownKeys;
|
|
754
789
|
private readonly maxKnownKeys;
|
|
790
|
+
private readonly touchRefreshIntervalMs;
|
|
755
791
|
private nextNodeId;
|
|
756
792
|
private readonly root;
|
|
757
793
|
constructor(options?: TagIndexOptions);
|
|
@@ -804,7 +840,7 @@ declare class TagIndex implements CacheTagIndex {
|
|
|
804
840
|
private findNode;
|
|
805
841
|
private collectFromNode;
|
|
806
842
|
private visitFromNode;
|
|
807
|
-
private
|
|
843
|
+
private literalPrefix;
|
|
808
844
|
private pruneKnownKeysIfNeeded;
|
|
809
845
|
private removeKey;
|
|
810
846
|
private removeKnownKey;
|
|
@@ -834,6 +870,11 @@ declare class CacheNamespace {
|
|
|
834
870
|
* Alias for `get(key, fetcher, options)` that makes the get-or-set behavior explicit.
|
|
835
871
|
*/
|
|
836
872
|
getOrSet<T>(key: string, fetcher: CacheFetcher<T>, options?: CacheGetOptions): Promise<T | null>;
|
|
873
|
+
/**
|
|
874
|
+
* Returns a namespaced cache entry, or `null` on miss.
|
|
875
|
+
* Unlike `get()`, this distinguishes a stored `null` value from an absent key.
|
|
876
|
+
*/
|
|
877
|
+
getEntry<T>(key: string): Promise<CacheEntryResult<T> | null>;
|
|
837
878
|
/**
|
|
838
879
|
* Like `get()`, but throws `CacheMissError` instead of returning `null`.
|
|
839
880
|
*/
|
|
@@ -1024,6 +1065,11 @@ declare class CacheStack extends EventEmitter {
|
|
|
1024
1065
|
* Fetches and caches the value if not already present.
|
|
1025
1066
|
*/
|
|
1026
1067
|
getOrSet<T>(key: string, fetcher: CacheFetcher<T>, options?: CacheGetOptions): Promise<T | null>;
|
|
1068
|
+
/**
|
|
1069
|
+
* Returns a discriminated cache entry, or `null` on miss.
|
|
1070
|
+
* Unlike `get()`, this distinguishes a stored `null` value from an absent key.
|
|
1071
|
+
*/
|
|
1072
|
+
getEntry<T>(key: string): Promise<CacheEntryResult<T> | null>;
|
|
1027
1073
|
/**
|
|
1028
1074
|
* Like `get()`, but throws `CacheMissError` instead of returning `null`.
|
|
1029
1075
|
* Useful when the value is expected to exist or the fetcher is expected to
|
|
@@ -1139,6 +1185,14 @@ declare class CacheStack extends EventEmitter {
|
|
|
1139
1185
|
* Returns cumulative cache metrics since startup or the last `resetMetrics()`.
|
|
1140
1186
|
*/
|
|
1141
1187
|
getMetrics(): CacheMetricsSnapshot;
|
|
1188
|
+
/**
|
|
1189
|
+
* Runs an operation while collecting only the metrics emitted by its async context.
|
|
1190
|
+
* Used by namespaces so metrics tracking does not serialize the operation itself.
|
|
1191
|
+
*/
|
|
1192
|
+
captureMetrics<T>(operation: () => Promise<T>): Promise<{
|
|
1193
|
+
result: T;
|
|
1194
|
+
metrics: CacheMetricsSnapshot;
|
|
1195
|
+
}>;
|
|
1142
1196
|
/**
|
|
1143
1197
|
* Returns metrics plus layer degradation state and active background refresh count.
|
|
1144
1198
|
*/
|
|
@@ -1162,6 +1216,10 @@ declare class CacheStack extends EventEmitter {
|
|
|
1162
1216
|
* unless `generationCleanup` is enabled to prune them in the background.
|
|
1163
1217
|
*/
|
|
1164
1218
|
bumpGeneration(nextGeneration?: number): number;
|
|
1219
|
+
/**
|
|
1220
|
+
* Returns the active generation prefix number used for future cache keys.
|
|
1221
|
+
*/
|
|
1222
|
+
getGeneration(): number | undefined;
|
|
1165
1223
|
/**
|
|
1166
1224
|
* Returns detailed metadata about a single cache key: which layers contain it,
|
|
1167
1225
|
* remaining fresh/stale/error TTLs, and associated tags.
|
|
@@ -1246,6 +1304,7 @@ interface HonoLikeRequest {
|
|
|
1246
1304
|
interface HonoLikeContext {
|
|
1247
1305
|
req: HonoLikeRequest;
|
|
1248
1306
|
header?: (name: string, value: string) => void;
|
|
1307
|
+
status?: (status: number) => unknown;
|
|
1249
1308
|
json: (body: unknown, status?: number) => Response | Promise<Response> | unknown;
|
|
1250
1309
|
}
|
|
1251
1310
|
interface HonoCacheMiddlewareOptions extends CacheGetOptions {
|
|
@@ -1264,4 +1323,4 @@ interface HonoCacheMiddlewareOptions extends CacheGetOptions {
|
|
|
1264
1323
|
*/
|
|
1265
1324
|
declare function createHonoCacheMiddleware(cache: CacheStack, options?: HonoCacheMiddlewareOptions): (context: HonoLikeContext, next: () => Promise<void>) => Promise<unknown>;
|
|
1266
1325
|
|
|
1267
|
-
export {
|
|
1326
|
+
export { CacheMissError as A, CacheNamespace as B, type CacheLogger as C, type CacheRateLimitOptions as D, type CacheSnapshotEntry as E, type CacheStackEvents as F, type CacheStackOptions as G, type CacheStatsSnapshot as H, type InvalidationBus as I, type CacheTtlPolicy as J, type CacheTtlPolicyContext as K, type CacheWarmEntry as L, type CacheWarmOptions as M, type CacheWarmProgress as N, type CacheWriteBehindOptions as O, type CacheWriteOptions as P, type EvictionPolicy as Q, type LayerTtlMap as R, MemoryLayer as S, type MemoryLayerOptions as T, type MemoryLayerSnapshotEntry as U, PatternMatcher as V, TagIndex as W, createHonoCacheMiddleware as X, type InvalidationMessage as a, type CacheTagIndex as b, CacheStack as c, type CacheWrapOptions as d, type CacheGetOptions as e, type CacheLayer as f, type CacheSerializer as g, type CacheLayerSetManyEntry as h, type CacheSingleFlightCoordinator as i, type CacheSingleFlightExecutionOptions as j, type CacheAdaptiveTtlOptions as k, type CacheCircuitBreakerOptions as l, type CacheContextOptionsContext as m, type CacheDegradationOptions as n, type CacheEntryResult as o, type CacheEntryWriteKind as p, type CacheEntryWriteOptions as q, type CacheFetcher as r, type CacheFetcherContext as s, type CacheHealthCheckResult as t, type CacheHitRateSnapshot as u, type CacheInspectResult as v, type CacheLayerLatency as w, type CacheMGetEntry as x, type CacheMSetEntry as y, type CacheMetricsSnapshot as z };
|
package/dist/edge.cjs
CHANGED
|
@@ -389,30 +389,34 @@ var PatternMatcher = class _PatternMatcher {
|
|
|
389
389
|
};
|
|
390
390
|
|
|
391
391
|
// src/invalidation/TagIndex.ts
|
|
392
|
-
var
|
|
392
|
+
var DEFAULT_TOUCH_REFRESH_INTERVAL_MS = 1e3;
|
|
393
393
|
var TagIndex = class {
|
|
394
394
|
tagToKeys = /* @__PURE__ */ new Map();
|
|
395
395
|
keyToTags = /* @__PURE__ */ new Map();
|
|
396
396
|
knownKeys = /* @__PURE__ */ new Map();
|
|
397
397
|
maxKnownKeys;
|
|
398
|
+
touchRefreshIntervalMs;
|
|
398
399
|
nextNodeId = 1;
|
|
399
400
|
root = this.createTrieNode();
|
|
400
401
|
constructor(options = {}) {
|
|
401
402
|
this.maxKnownKeys = options.maxKnownKeys ?? 1e5;
|
|
403
|
+
this.touchRefreshIntervalMs = options.touchRefreshIntervalMs ?? DEFAULT_TOUCH_REFRESH_INTERVAL_MS;
|
|
402
404
|
}
|
|
403
405
|
/**
|
|
404
406
|
* Records a key as known without changing tag assignments.
|
|
405
407
|
*/
|
|
406
408
|
async touch(key) {
|
|
407
|
-
this.insertKnownKey(key)
|
|
408
|
-
|
|
409
|
+
if (this.insertKnownKey(key)) {
|
|
410
|
+
this.pruneKnownKeysIfNeeded();
|
|
411
|
+
}
|
|
409
412
|
}
|
|
410
413
|
/**
|
|
411
414
|
* Replaces the tags associated with a key and records the key as known.
|
|
412
415
|
*/
|
|
413
416
|
async track(key, tags) {
|
|
414
|
-
this.insertKnownKey(key)
|
|
415
|
-
|
|
417
|
+
if (this.insertKnownKey(key)) {
|
|
418
|
+
this.pruneKnownKeysIfNeeded();
|
|
419
|
+
}
|
|
416
420
|
if (tags.length === 0) {
|
|
417
421
|
return;
|
|
418
422
|
}
|
|
@@ -482,9 +486,14 @@ var TagIndex = class {
|
|
|
482
486
|
* Returns known keys matching a wildcard pattern.
|
|
483
487
|
*/
|
|
484
488
|
async matchPattern(pattern) {
|
|
485
|
-
const
|
|
486
|
-
|
|
487
|
-
|
|
489
|
+
const literalPrefix = this.literalPrefix(pattern);
|
|
490
|
+
const node = this.findNode(literalPrefix);
|
|
491
|
+
if (!node) {
|
|
492
|
+
return [];
|
|
493
|
+
}
|
|
494
|
+
const candidates = [];
|
|
495
|
+
this.collectFromNode(node, literalPrefix, candidates);
|
|
496
|
+
return candidates.filter((key) => PatternMatcher.matches(pattern, key));
|
|
488
497
|
}
|
|
489
498
|
/**
|
|
490
499
|
* Visits known keys matching a wildcard pattern.
|
|
@@ -514,10 +523,18 @@ var TagIndex = class {
|
|
|
514
523
|
};
|
|
515
524
|
}
|
|
516
525
|
insertKnownKey(key) {
|
|
517
|
-
const
|
|
518
|
-
|
|
526
|
+
const previousTouch = this.knownKeys.get(key);
|
|
527
|
+
const isNew = previousTouch === void 0;
|
|
528
|
+
const now = Date.now();
|
|
529
|
+
if (!isNew && now - previousTouch < this.touchRefreshIntervalMs) {
|
|
530
|
+
return false;
|
|
531
|
+
}
|
|
519
532
|
if (!isNew) {
|
|
520
|
-
|
|
533
|
+
this.knownKeys.delete(key);
|
|
534
|
+
}
|
|
535
|
+
this.knownKeys.set(key, now);
|
|
536
|
+
if (!isNew) {
|
|
537
|
+
return true;
|
|
521
538
|
}
|
|
522
539
|
let node = this.root;
|
|
523
540
|
for (const character of key) {
|
|
@@ -529,6 +546,7 @@ var TagIndex = class {
|
|
|
529
546
|
node = child;
|
|
530
547
|
}
|
|
531
548
|
node.terminal = true;
|
|
549
|
+
return true;
|
|
532
550
|
}
|
|
533
551
|
findNode(prefix) {
|
|
534
552
|
let node = this.root;
|
|
@@ -541,85 +559,52 @@ var TagIndex = class {
|
|
|
541
559
|
return node;
|
|
542
560
|
}
|
|
543
561
|
collectFromNode(node, prefix, matches) {
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
562
|
+
const stack = [{ node, prefix }];
|
|
563
|
+
while (stack.length > 0) {
|
|
564
|
+
const current = stack.pop();
|
|
565
|
+
if (!current) {
|
|
566
|
+
continue;
|
|
567
|
+
}
|
|
568
|
+
if (current.node.terminal) {
|
|
569
|
+
matches.push(current.prefix);
|
|
570
|
+
}
|
|
571
|
+
const children = [...current.node.children].reverse();
|
|
572
|
+
for (const [character, child] of children) {
|
|
573
|
+
stack.push({ node: child, prefix: `${current.prefix}${character}` });
|
|
574
|
+
}
|
|
549
575
|
}
|
|
550
576
|
}
|
|
551
577
|
async visitFromNode(node, prefix, visitor) {
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
collectPatternMatches(node, prefix, pattern, patternIndex, matches, visited, depth) {
|
|
560
|
-
if (depth > MAX_PATTERN_RECURSION_DEPTH) {
|
|
561
|
-
return;
|
|
562
|
-
}
|
|
563
|
-
const stateKey = `${node.id}:${patternIndex}`;
|
|
564
|
-
if (visited.has(stateKey)) {
|
|
565
|
-
return;
|
|
566
|
-
}
|
|
567
|
-
visited.add(stateKey);
|
|
568
|
-
if (patternIndex === pattern.length) {
|
|
569
|
-
if (node.terminal) {
|
|
570
|
-
matches.add(prefix);
|
|
578
|
+
const stack = [{ node, prefix }];
|
|
579
|
+
while (stack.length > 0) {
|
|
580
|
+
const current = stack.pop();
|
|
581
|
+
if (!current) {
|
|
582
|
+
continue;
|
|
571
583
|
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
const patternChar = pattern[patternIndex];
|
|
575
|
-
if (patternChar === void 0) {
|
|
576
|
-
return;
|
|
577
|
-
}
|
|
578
|
-
if (patternChar === "*") {
|
|
579
|
-
this.collectPatternMatches(node, prefix, pattern, patternIndex + 1, matches, visited, depth + 1);
|
|
580
|
-
for (const [character, child2] of node.children) {
|
|
581
|
-
this.collectPatternMatches(child2, `${prefix}${character}`, pattern, patternIndex, matches, visited, depth + 1);
|
|
584
|
+
if (current.node.terminal) {
|
|
585
|
+
await visitor(current.prefix);
|
|
582
586
|
}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
for (const [character, child2] of node.children) {
|
|
587
|
-
this.collectPatternMatches(
|
|
588
|
-
child2,
|
|
589
|
-
`${prefix}${character}`,
|
|
590
|
-
pattern,
|
|
591
|
-
patternIndex + 1,
|
|
592
|
-
matches,
|
|
593
|
-
visited,
|
|
594
|
-
depth + 1
|
|
595
|
-
);
|
|
587
|
+
const children = [...current.node.children].reverse();
|
|
588
|
+
for (const [character, child] of children) {
|
|
589
|
+
stack.push({ node: child, prefix: `${current.prefix}${character}` });
|
|
596
590
|
}
|
|
597
|
-
return;
|
|
598
|
-
}
|
|
599
|
-
const child = node.children.get(patternChar);
|
|
600
|
-
if (child) {
|
|
601
|
-
this.collectPatternMatches(
|
|
602
|
-
child,
|
|
603
|
-
`${prefix}${patternChar}`,
|
|
604
|
-
pattern,
|
|
605
|
-
patternIndex + 1,
|
|
606
|
-
matches,
|
|
607
|
-
visited,
|
|
608
|
-
depth + 1
|
|
609
|
-
);
|
|
610
591
|
}
|
|
611
592
|
}
|
|
593
|
+
literalPrefix(pattern) {
|
|
594
|
+
const wildcardIndex = pattern.search(/[*?]/);
|
|
595
|
+
return wildcardIndex === -1 ? pattern : pattern.slice(0, wildcardIndex);
|
|
596
|
+
}
|
|
612
597
|
pruneKnownKeysIfNeeded() {
|
|
613
598
|
if (this.maxKnownKeys === void 0 || this.knownKeys.size <= this.maxKnownKeys) {
|
|
614
599
|
return;
|
|
615
600
|
}
|
|
616
|
-
const sorted = [...this.knownKeys.entries()].sort((a, b) => a[1] - b[1]);
|
|
617
601
|
const toRemove = Math.ceil(this.maxKnownKeys * 0.1);
|
|
618
|
-
for (let i = 0; i < toRemove
|
|
619
|
-
const
|
|
620
|
-
if (
|
|
621
|
-
|
|
602
|
+
for (let i = 0; i < toRemove; i += 1) {
|
|
603
|
+
const oldestKey = this.knownKeys.keys().next().value;
|
|
604
|
+
if (oldestKey === void 0) {
|
|
605
|
+
break;
|
|
622
606
|
}
|
|
607
|
+
this.removeKnownKey(oldestKey);
|
|
623
608
|
}
|
|
624
609
|
}
|
|
625
610
|
removeKey(key) {
|
|
@@ -670,6 +655,41 @@ var TagIndex = class {
|
|
|
670
655
|
}
|
|
671
656
|
};
|
|
672
657
|
|
|
658
|
+
// src/integrations/httpCacheKeys.ts
|
|
659
|
+
var SENSITIVE_QUERY_PARAMETERS = /* @__PURE__ */ new Set([
|
|
660
|
+
"access_token",
|
|
661
|
+
"api_key",
|
|
662
|
+
"apikey",
|
|
663
|
+
"auth",
|
|
664
|
+
"authorization",
|
|
665
|
+
"code",
|
|
666
|
+
"credentials",
|
|
667
|
+
"id_token",
|
|
668
|
+
"jwt",
|
|
669
|
+
"password",
|
|
670
|
+
"private_key",
|
|
671
|
+
"refresh_token",
|
|
672
|
+
"secret",
|
|
673
|
+
"session",
|
|
674
|
+
"sessionid",
|
|
675
|
+
"session_id",
|
|
676
|
+
"token"
|
|
677
|
+
]);
|
|
678
|
+
function normalizeHttpCacheUrl(url) {
|
|
679
|
+
try {
|
|
680
|
+
const parsed = new URL(url, "http://localhost");
|
|
681
|
+
for (const name of [...parsed.searchParams.keys()]) {
|
|
682
|
+
if (SENSITIVE_QUERY_PARAMETERS.has(name.toLowerCase())) {
|
|
683
|
+
parsed.searchParams.delete(name);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
parsed.searchParams.sort();
|
|
687
|
+
return parsed.pathname + parsed.search;
|
|
688
|
+
} catch {
|
|
689
|
+
return url;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
673
693
|
// src/integrations/hono.ts
|
|
674
694
|
function createHonoCacheMiddleware(cache, options = {}) {
|
|
675
695
|
const allowedMethods = new Set((options.methods ?? ["GET"]).map((method) => method.toUpperCase()));
|
|
@@ -684,35 +704,39 @@ function createHonoCacheMiddleware(cache, options = {}) {
|
|
|
684
704
|
return;
|
|
685
705
|
}
|
|
686
706
|
const rawPath = context.req.path ?? context.req.url ?? "/";
|
|
687
|
-
const key = options.keyResolver ? options.keyResolver(context.req) : `${method}:${
|
|
707
|
+
const key = options.keyResolver ? options.keyResolver(context.req) : `${method}:${normalizeHttpCacheUrl(rawPath)}`;
|
|
688
708
|
const cached = await cache.get(key, void 0, options);
|
|
689
709
|
if (cached !== null) {
|
|
690
710
|
context.header?.("x-cache", "HIT");
|
|
691
711
|
context.header?.("content-type", "application/json; charset=utf-8");
|
|
692
712
|
return context.json(cached);
|
|
693
713
|
}
|
|
714
|
+
let currentStatus;
|
|
715
|
+
const originalStatus = context.status?.bind(context);
|
|
716
|
+
if (originalStatus) {
|
|
717
|
+
context.status = (status) => {
|
|
718
|
+
currentStatus = status;
|
|
719
|
+
return originalStatus(status);
|
|
720
|
+
};
|
|
721
|
+
}
|
|
694
722
|
const originalJson = context.json.bind(context);
|
|
695
723
|
context.json = (body, status) => {
|
|
696
724
|
context.header?.("x-cache", "MISS");
|
|
697
|
-
|
|
698
|
-
cache.
|
|
699
|
-
|
|
700
|
-
|
|
725
|
+
if (isSuccessfulStatus(status ?? currentStatus)) {
|
|
726
|
+
cache.set(key, body, options).catch((err) => {
|
|
727
|
+
cache.emit("error", {
|
|
728
|
+
operation: "set",
|
|
729
|
+
error: err instanceof Error ? err.message : String(err)
|
|
730
|
+
});
|
|
701
731
|
});
|
|
702
|
-
}
|
|
732
|
+
}
|
|
703
733
|
return originalJson(body, status);
|
|
704
734
|
};
|
|
705
735
|
await next();
|
|
706
736
|
};
|
|
707
737
|
}
|
|
708
|
-
function
|
|
709
|
-
|
|
710
|
-
const parsed = new URL(url, "http://localhost");
|
|
711
|
-
parsed.searchParams.sort();
|
|
712
|
-
return parsed.pathname + parsed.search;
|
|
713
|
-
} catch {
|
|
714
|
-
return url;
|
|
715
|
-
}
|
|
738
|
+
function isSuccessfulStatus(statusCode) {
|
|
739
|
+
return statusCode === void 0 || statusCode >= 200 && statusCode < 300;
|
|
716
740
|
}
|
|
717
741
|
// Annotate the CommonJS export names for ESM import in node:
|
|
718
742
|
0 && (module.exports = {
|
package/dist/edge.d.cts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { m as CacheContextOptionsContext,
|
|
1
|
+
export { m as CacheContextOptionsContext, p as CacheEntryWriteKind, q as CacheEntryWriteOptions, e as CacheGetOptions, f as CacheLayer, h as CacheLayerSetManyEntry, z as CacheMetricsSnapshot, D as CacheRateLimitOptions, J as CacheTtlPolicy, K as CacheTtlPolicyContext, P as CacheWriteOptions, Q as EvictionPolicy, S as MemoryLayer, T as MemoryLayerOptions, U as MemoryLayerSnapshotEntry, V as PatternMatcher, W as TagIndex, X as createHonoCacheMiddleware } from './edge-LBUuZAdr.cjs';
|
|
2
2
|
import 'node:events';
|
package/dist/edge.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { m as CacheContextOptionsContext,
|
|
1
|
+
export { m as CacheContextOptionsContext, p as CacheEntryWriteKind, q as CacheEntryWriteOptions, e as CacheGetOptions, f as CacheLayer, h as CacheLayerSetManyEntry, z as CacheMetricsSnapshot, D as CacheRateLimitOptions, J as CacheTtlPolicy, K as CacheTtlPolicyContext, P as CacheWriteOptions, Q as EvictionPolicy, S as MemoryLayer, T as MemoryLayerOptions, U as MemoryLayerSnapshotEntry, V as PatternMatcher, W as TagIndex, X as createHonoCacheMiddleware } from './edge-LBUuZAdr.js';
|
|
2
2
|
import 'node:events';
|