@mrjasonroy/cache-components-cache-handler 16.0.0-alpha.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 +437 -0
- package/dist/data-cache/memory.d.ts +47 -0
- package/dist/data-cache/memory.d.ts.map +1 -0
- package/dist/data-cache/memory.js +310 -0
- package/dist/data-cache/memory.js.map +1 -0
- package/dist/data-cache/redis.d.ts +83 -0
- package/dist/data-cache/redis.d.ts.map +1 -0
- package/dist/data-cache/redis.js +240 -0
- package/dist/data-cache/redis.js.map +1 -0
- package/dist/data-cache/types.d.ts +85 -0
- package/dist/data-cache/types.d.ts.map +1 -0
- package/dist/data-cache/types.js +7 -0
- package/dist/data-cache/types.js.map +1 -0
- package/dist/handlers/composite.d.ts +70 -0
- package/dist/handlers/composite.d.ts.map +1 -0
- package/dist/handlers/composite.js +123 -0
- package/dist/handlers/composite.js.map +1 -0
- package/dist/handlers/memory.d.ts +77 -0
- package/dist/handlers/memory.d.ts.map +1 -0
- package/dist/handlers/memory.js +145 -0
- package/dist/handlers/memory.js.map +1 -0
- package/dist/handlers/redis.d.ts +80 -0
- package/dist/handlers/redis.d.ts.map +1 -0
- package/dist/handlers/redis.js +210 -0
- package/dist/handlers/redis.js.map +1 -0
- package/dist/helpers/buffer.d.ts +25 -0
- package/dist/helpers/buffer.d.ts.map +1 -0
- package/dist/helpers/buffer.js +45 -0
- package/dist/helpers/buffer.js.map +1 -0
- package/dist/helpers/is-implicit-tag.d.ts +9 -0
- package/dist/helpers/is-implicit-tag.d.ts.map +1 -0
- package/dist/helpers/is-implicit-tag.js +16 -0
- package/dist/helpers/is-implicit-tag.js.map +1 -0
- package/dist/helpers/lifespan.d.ts +18 -0
- package/dist/helpers/lifespan.d.ts.map +1 -0
- package/dist/helpers/lifespan.js +43 -0
- package/dist/helpers/lifespan.js.map +1 -0
- package/dist/helpers/next-config.d.ts +97 -0
- package/dist/helpers/next-config.d.ts.map +1 -0
- package/dist/helpers/next-config.js +96 -0
- package/dist/helpers/next-config.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +163 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Next.js Data Cache Handler Types
|
|
3
|
+
* For "use cache" directive support
|
|
4
|
+
* Source: next.js/packages/next/src/server/lib/cache-handlers/types.ts
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* A timestamp in milliseconds elapsed since the epoch
|
|
8
|
+
*/
|
|
9
|
+
export type Timestamp = number;
|
|
10
|
+
/**
|
|
11
|
+
* Cache entry for data cache (used by "use cache" directive)
|
|
12
|
+
* Uses ReadableStream for streaming responses
|
|
13
|
+
*/
|
|
14
|
+
export interface DataCacheEntry {
|
|
15
|
+
/**
|
|
16
|
+
* The ReadableStream can error and only have partial data so any cache
|
|
17
|
+
* handlers need to handle this case and decide to keep the partial cache
|
|
18
|
+
* around or not.
|
|
19
|
+
*/
|
|
20
|
+
value: ReadableStream<Uint8Array>;
|
|
21
|
+
/**
|
|
22
|
+
* The tags configured for the entry excluding soft tags
|
|
23
|
+
*/
|
|
24
|
+
tags: string[];
|
|
25
|
+
/**
|
|
26
|
+
* This is for the client, not used to calculate cache entry expiration
|
|
27
|
+
* [duration in seconds]
|
|
28
|
+
*/
|
|
29
|
+
stale: number;
|
|
30
|
+
/**
|
|
31
|
+
* When the cache entry was created [timestamp in milliseconds]
|
|
32
|
+
*/
|
|
33
|
+
timestamp: Timestamp;
|
|
34
|
+
/**
|
|
35
|
+
* How long the entry is allowed to be used (should be longer than revalidate)
|
|
36
|
+
* [duration in seconds]
|
|
37
|
+
*/
|
|
38
|
+
expire: number;
|
|
39
|
+
/**
|
|
40
|
+
* How long until the entry should be revalidated [duration in seconds]
|
|
41
|
+
*/
|
|
42
|
+
revalidate: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Data cache handler interface that must be implemented
|
|
46
|
+
* This is for "use cache" directive support
|
|
47
|
+
*/
|
|
48
|
+
export interface DataCacheHandler {
|
|
49
|
+
/**
|
|
50
|
+
* Retrieve a cache entry for the given cache key, if available. Will return
|
|
51
|
+
* undefined if there's no valid entry, or if the given soft tags are stale.
|
|
52
|
+
*/
|
|
53
|
+
get(cacheKey: string, softTags: string[]): Promise<undefined | DataCacheEntry>;
|
|
54
|
+
/**
|
|
55
|
+
* Store a cache entry for the given cache key. When this is called, the entry
|
|
56
|
+
* may still be pending, i.e. its value stream may still be written to. So it
|
|
57
|
+
* needs to be awaited first. If a `get` for the same cache key is called,
|
|
58
|
+
* before the pending entry is complete, the cache handler must wait for the
|
|
59
|
+
* `set` operation to finish, before returning the entry, instead of returning
|
|
60
|
+
* undefined.
|
|
61
|
+
*/
|
|
62
|
+
set(cacheKey: string, pendingEntry: Promise<DataCacheEntry>): Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* This function may be called periodically, but always before starting a new
|
|
65
|
+
* request. If applicable, it should communicate with the tags service to
|
|
66
|
+
* refresh the local tags manifest accordingly.
|
|
67
|
+
*/
|
|
68
|
+
refreshTags(): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* This function is called for each set of soft tags that are relevant at the
|
|
71
|
+
* start of a request. The result is the maximum timestamp of a revalidate
|
|
72
|
+
* event for the tags. Returns `0` if none of the tags were ever revalidated.
|
|
73
|
+
* Returns `Infinity` if the soft tags are supposed to be passed into the
|
|
74
|
+
* `get` method instead to be checked for expiration.
|
|
75
|
+
*/
|
|
76
|
+
getExpiration(tags: string[]): Promise<Timestamp>;
|
|
77
|
+
/**
|
|
78
|
+
* This function is called when tags are revalidated/expired. If applicable,
|
|
79
|
+
* it should update the tags manifest accordingly.
|
|
80
|
+
*/
|
|
81
|
+
updateTags(tags: string[], durations?: {
|
|
82
|
+
expire?: number;
|
|
83
|
+
}): Promise<void>;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/data-cache/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC;AAE/B;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;OAIG;IACH,KAAK,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;IAElC;;OAEG;IACH,IAAI,EAAE,MAAM,EAAE,CAAC;IAEf;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,SAAS,EAAE,SAAS,CAAC;IAErB;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,SAAS,GAAG,cAAc,CAAC,CAAC;IAE/E;;;;;;;OAOG;IACH,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE5E;;;;OAIG;IACH,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7B;;;;;;OAMG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAElD;;;OAGG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,SAAS,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5E"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/data-cache/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { CacheHandler, CacheHandlerContext, CacheHandlerGetMeta, CacheHandlerOptions, CacheHandlerValue, CacheValue } from "../types.js";
|
|
2
|
+
export interface CompositeHandlerOptions extends CacheHandlerOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Array of cache handlers to use (ordered by priority)
|
|
5
|
+
* First handler is checked first for reads
|
|
6
|
+
*/
|
|
7
|
+
handlers: CacheHandler[];
|
|
8
|
+
/**
|
|
9
|
+
* Strategy function to determine which handler to use for set operations
|
|
10
|
+
* Returns the index of the handler to use
|
|
11
|
+
*
|
|
12
|
+
* @default - Write to all handlers
|
|
13
|
+
*/
|
|
14
|
+
setStrategy?: (data: CacheHandlerValue) => number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Composite cache handler that orchestrates multiple cache handlers
|
|
18
|
+
* Enables multi-tier caching strategies (e.g., memory -> Redis)
|
|
19
|
+
*
|
|
20
|
+
* Features:
|
|
21
|
+
* - First-match read strategy (fast fallback)
|
|
22
|
+
* - Configurable write strategy
|
|
23
|
+
* - Fault tolerance with Promise.allSettled()
|
|
24
|
+
* - Parallel revalidation across all handlers
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* // Memory + Redis two-tier cache
|
|
29
|
+
* const handler = createCompositeHandler({
|
|
30
|
+
* handlers: [memoryHandler, redisHandler],
|
|
31
|
+
* // Only cache small items in memory, everything in Redis
|
|
32
|
+
* setStrategy: (data) => {
|
|
33
|
+
* const size = JSON.stringify(data).length;
|
|
34
|
+
* return size < 10000 ? 0 : 1; // 0 = memory, 1 = redis
|
|
35
|
+
* }
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare class CompositeHandler implements CacheHandler {
|
|
40
|
+
readonly name = "composite";
|
|
41
|
+
private readonly handlers;
|
|
42
|
+
private readonly setStrategy?;
|
|
43
|
+
constructor(options: CompositeHandlerOptions);
|
|
44
|
+
get(key: string, meta?: CacheHandlerGetMeta): Promise<CacheValue | null>;
|
|
45
|
+
set(key: string, value: CacheValue, context?: CacheHandlerContext): Promise<void>;
|
|
46
|
+
revalidateTag(tag: string, profile?: string | {
|
|
47
|
+
expire?: number;
|
|
48
|
+
}): Promise<void>;
|
|
49
|
+
delete(key: string): Promise<void>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Create a composite cache handler instance
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```typescript
|
|
56
|
+
* // Route based on tags
|
|
57
|
+
* const handler = createCompositeHandler({
|
|
58
|
+
* handlers: [memoryHandler, redisHandler],
|
|
59
|
+
* setStrategy: (data) => {
|
|
60
|
+
* // Use memory for items tagged with "memory-cache"
|
|
61
|
+
* if (data.tags.includes("memory-cache")) {
|
|
62
|
+
* return 0; // memory handler
|
|
63
|
+
* }
|
|
64
|
+
* return 1; // redis handler
|
|
65
|
+
* }
|
|
66
|
+
* });
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export declare function createCompositeHandler(options: CompositeHandlerOptions): CompositeHandler;
|
|
70
|
+
//# sourceMappingURL=composite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composite.d.ts","sourceRoot":"","sources":["../../src/handlers/composite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,UAAU,EACX,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,uBAAwB,SAAQ,mBAAmB;IAClE;;;OAGG;IACH,QAAQ,EAAE,YAAY,EAAE,CAAC;IAEzB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,iBAAiB,KAAK,MAAM,CAAC;CACnD;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,gBAAiB,YAAW,YAAY;IACnD,SAAgB,IAAI,eAAe;IAEnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiB;IAC1C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAsC;gBAEvD,OAAO,EAAE,uBAAuB;IAStC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAiBxE,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IA+BjF,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAcjF,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAezC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,uBAAuB,GAAG,gBAAgB,CAEzF"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composite cache handler that orchestrates multiple cache handlers
|
|
3
|
+
* Enables multi-tier caching strategies (e.g., memory -> Redis)
|
|
4
|
+
*
|
|
5
|
+
* Features:
|
|
6
|
+
* - First-match read strategy (fast fallback)
|
|
7
|
+
* - Configurable write strategy
|
|
8
|
+
* - Fault tolerance with Promise.allSettled()
|
|
9
|
+
* - Parallel revalidation across all handlers
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* // Memory + Redis two-tier cache
|
|
14
|
+
* const handler = createCompositeHandler({
|
|
15
|
+
* handlers: [memoryHandler, redisHandler],
|
|
16
|
+
* // Only cache small items in memory, everything in Redis
|
|
17
|
+
* setStrategy: (data) => {
|
|
18
|
+
* const size = JSON.stringify(data).length;
|
|
19
|
+
* return size < 10000 ? 0 : 1; // 0 = memory, 1 = redis
|
|
20
|
+
* }
|
|
21
|
+
* });
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export class CompositeHandler {
|
|
25
|
+
name = "composite";
|
|
26
|
+
handlers;
|
|
27
|
+
setStrategy;
|
|
28
|
+
constructor(options) {
|
|
29
|
+
if (!options.handlers || options.handlers.length === 0) {
|
|
30
|
+
throw new Error("CompositeHandler requires at least one handler");
|
|
31
|
+
}
|
|
32
|
+
this.handlers = options.handlers;
|
|
33
|
+
this.setStrategy = options.setStrategy;
|
|
34
|
+
}
|
|
35
|
+
async get(key, meta) {
|
|
36
|
+
// Try handlers in order, return first hit
|
|
37
|
+
for (const handler of this.handlers) {
|
|
38
|
+
try {
|
|
39
|
+
const value = await handler.get(key, meta);
|
|
40
|
+
if (value !== null) {
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
// Log error but continue to next handler
|
|
46
|
+
console.error(`Handler ${handler.name} failed:`, error);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
async set(key, value, context) {
|
|
52
|
+
// Build the full cache handler value for strategy function
|
|
53
|
+
const now = Date.now();
|
|
54
|
+
const cacheHandlerValue = {
|
|
55
|
+
lastModified: now,
|
|
56
|
+
lifespan: null, // Simplified - handlers will calculate their own
|
|
57
|
+
tags: context?.tags ?? [],
|
|
58
|
+
value,
|
|
59
|
+
};
|
|
60
|
+
// Determine which handler(s) to write to
|
|
61
|
+
if (this.setStrategy) {
|
|
62
|
+
const handlerIndex = this.setStrategy(cacheHandlerValue);
|
|
63
|
+
if (handlerIndex >= 0 && handlerIndex < this.handlers.length) {
|
|
64
|
+
await this.handlers[handlerIndex].set(key, value, context);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
// Default: write to all handlers with fault tolerance
|
|
69
|
+
const results = await Promise.allSettled(this.handlers.map((handler) => handler.set(key, value, context)));
|
|
70
|
+
// Log failures but don't throw (fault tolerance)
|
|
71
|
+
results.forEach((result, index) => {
|
|
72
|
+
if (result.status === "rejected") {
|
|
73
|
+
console.error(`Handler ${this.handlers[index].name} set failed:`, result.reason);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async revalidateTag(tag, profile) {
|
|
79
|
+
// Revalidate on all handlers in parallel with fault tolerance
|
|
80
|
+
const results = await Promise.allSettled(this.handlers.map((handler) => handler.revalidateTag(tag, profile)));
|
|
81
|
+
// Log failures but don't throw
|
|
82
|
+
results.forEach((result, index) => {
|
|
83
|
+
if (result.status === "rejected") {
|
|
84
|
+
console.error(`Handler ${this.handlers[index].name} revalidateTag failed:`, result.reason);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
async delete(key) {
|
|
89
|
+
// Delete from all handlers that support it
|
|
90
|
+
const deletePromises = this.handlers
|
|
91
|
+
.filter((handler) => typeof handler.delete === "function")
|
|
92
|
+
.map((handler) => handler.delete?.(key));
|
|
93
|
+
const results = await Promise.allSettled(deletePromises);
|
|
94
|
+
// Log failures but don't throw
|
|
95
|
+
results.forEach((result, index) => {
|
|
96
|
+
if (result.status === "rejected") {
|
|
97
|
+
console.error(`Handler ${this.handlers[index].name} delete failed:`, result.reason);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Create a composite cache handler instance
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```typescript
|
|
107
|
+
* // Route based on tags
|
|
108
|
+
* const handler = createCompositeHandler({
|
|
109
|
+
* handlers: [memoryHandler, redisHandler],
|
|
110
|
+
* setStrategy: (data) => {
|
|
111
|
+
* // Use memory for items tagged with "memory-cache"
|
|
112
|
+
* if (data.tags.includes("memory-cache")) {
|
|
113
|
+
* return 0; // memory handler
|
|
114
|
+
* }
|
|
115
|
+
* return 1; // redis handler
|
|
116
|
+
* }
|
|
117
|
+
* });
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
export function createCompositeHandler(options) {
|
|
121
|
+
return new CompositeHandler(options);
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=composite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composite.js","sourceRoot":"","sources":["../../src/handlers/composite.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { CacheHandler, CacheHandlerContext, CacheHandlerGetMeta, CacheHandlerOptions, CacheValue } from "../types.js";
|
|
2
|
+
export interface MemoryCacheHandlerOptions extends CacheHandlerOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Maximum number of items to store (LRU eviction when exceeded)
|
|
5
|
+
* @default 1000
|
|
6
|
+
*/
|
|
7
|
+
maxItemsNumber?: number;
|
|
8
|
+
/**
|
|
9
|
+
* Maximum size in bytes for a single item
|
|
10
|
+
* @default 104857600 (100MB)
|
|
11
|
+
*/
|
|
12
|
+
maxItemSizeBytes?: number;
|
|
13
|
+
/**
|
|
14
|
+
* Default TTL in seconds for entries without explicit revalidate
|
|
15
|
+
* @default undefined (no expiration)
|
|
16
|
+
*/
|
|
17
|
+
defaultTTL?: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* In-memory LRU cache handler for Next.js 16+
|
|
21
|
+
* Suitable for development and single-instance deployments
|
|
22
|
+
*
|
|
23
|
+
* Features:
|
|
24
|
+
* - LRU eviction based on item count and size
|
|
25
|
+
* - Implicit tag tracking for ISR
|
|
26
|
+
* - Proper expiration handling
|
|
27
|
+
* - Tag-based revalidation
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* const handler = createMemoryCacheHandler({
|
|
32
|
+
* maxItemsNumber: 1000,
|
|
33
|
+
* maxItemSizeBytes: 100 * 1024 * 1024, // 100MB
|
|
34
|
+
* defaultTTL: 3600 // 1 hour
|
|
35
|
+
* });
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export declare class MemoryCacheHandler implements CacheHandler {
|
|
39
|
+
readonly name = "local-lru";
|
|
40
|
+
private cache;
|
|
41
|
+
private revalidatedTags;
|
|
42
|
+
private readonly maxItemsNumber;
|
|
43
|
+
private readonly maxItemSizeBytes;
|
|
44
|
+
private readonly defaultTTL?;
|
|
45
|
+
constructor(options?: MemoryCacheHandlerOptions);
|
|
46
|
+
get(key: string, meta?: CacheHandlerGetMeta): Promise<CacheValue | null>;
|
|
47
|
+
set(key: string, value: CacheValue, context?: CacheHandlerContext): Promise<void>;
|
|
48
|
+
revalidateTag(tag: string, _profile?: string | {
|
|
49
|
+
expire?: number;
|
|
50
|
+
}): Promise<void>;
|
|
51
|
+
delete(key: string): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Clear all cache entries (useful for testing)
|
|
54
|
+
*/
|
|
55
|
+
clear(): Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Get cache statistics
|
|
58
|
+
*/
|
|
59
|
+
getStats(): {
|
|
60
|
+
size: number;
|
|
61
|
+
maxSize: number;
|
|
62
|
+
revalidatedTags: number;
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Create a memory cache handler instance
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```typescript
|
|
70
|
+
* const handler = createMemoryCacheHandler({
|
|
71
|
+
* maxItemsNumber: 500,
|
|
72
|
+
* defaultTTL: 3600
|
|
73
|
+
* });
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export declare function createMemoryCacheHandler(options?: MemoryCacheHandlerOptions): MemoryCacheHandler;
|
|
77
|
+
//# sourceMappingURL=memory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/handlers/memory.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,YAAY,EACZ,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EAEnB,UAAU,EACX,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,yBAA0B,SAAQ,mBAAmB;IACpE;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,kBAAmB,YAAW,YAAY;IACrD,SAAgB,IAAI,eAAe;IAEnC,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,eAAe,CAAsB;IAC7C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAS;gBAEzB,OAAO,GAAE,yBAA8B;IAQ7C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAiCxE,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAmCjF,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA0BlF,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5B;;OAEG;IACH,QAAQ;;;;;CAOT;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,CAAC,EAAE,yBAAyB,GAAG,kBAAkB,CAEhG"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { isImplicitTag } from "../helpers/is-implicit-tag.js";
|
|
2
|
+
import { calculateLifespan, isExpired } from "../helpers/lifespan.js";
|
|
3
|
+
/**
|
|
4
|
+
* In-memory LRU cache handler for Next.js 16+
|
|
5
|
+
* Suitable for development and single-instance deployments
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - LRU eviction based on item count and size
|
|
9
|
+
* - Implicit tag tracking for ISR
|
|
10
|
+
* - Proper expiration handling
|
|
11
|
+
* - Tag-based revalidation
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* const handler = createMemoryCacheHandler({
|
|
16
|
+
* maxItemsNumber: 1000,
|
|
17
|
+
* maxItemSizeBytes: 100 * 1024 * 1024, // 100MB
|
|
18
|
+
* defaultTTL: 3600 // 1 hour
|
|
19
|
+
* });
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export class MemoryCacheHandler {
|
|
23
|
+
name = "local-lru";
|
|
24
|
+
cache;
|
|
25
|
+
revalidatedTags; // tag -> timestamp (milliseconds)
|
|
26
|
+
maxItemsNumber;
|
|
27
|
+
maxItemSizeBytes;
|
|
28
|
+
defaultTTL;
|
|
29
|
+
constructor(options = {}) {
|
|
30
|
+
this.cache = new Map();
|
|
31
|
+
this.revalidatedTags = new Map();
|
|
32
|
+
this.maxItemsNumber = options.maxItemsNumber ?? 1000;
|
|
33
|
+
this.maxItemSizeBytes = options.maxItemSizeBytes ?? 104857600; // 100MB
|
|
34
|
+
this.defaultTTL = options.defaultTTL;
|
|
35
|
+
}
|
|
36
|
+
async get(key, meta) {
|
|
37
|
+
const entry = this.cache.get(key);
|
|
38
|
+
if (!entry) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
// Check if expired based on lifespan
|
|
42
|
+
if (entry.lifespan && isExpired(entry.lifespan)) {
|
|
43
|
+
await this.delete(key);
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
// Check if any tag (explicit or implicit) has been revalidated
|
|
47
|
+
const allTags = [...entry.tags, ...(meta?.implicitTags ?? [])];
|
|
48
|
+
for (const tag of allTags) {
|
|
49
|
+
const revalidatedAt = this.revalidatedTags.get(tag);
|
|
50
|
+
// If tag was revalidated after entry was last modified, entry is stale
|
|
51
|
+
if (revalidatedAt && revalidatedAt > entry.lastModified) {
|
|
52
|
+
await this.delete(key);
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Move to end for LRU (delete and re-add)
|
|
57
|
+
this.cache.delete(key);
|
|
58
|
+
this.cache.set(key, entry);
|
|
59
|
+
return entry.value;
|
|
60
|
+
}
|
|
61
|
+
async set(key, value, context) {
|
|
62
|
+
// Calculate size of the entry
|
|
63
|
+
const size = JSON.stringify(value).length;
|
|
64
|
+
// Check if entry exceeds max size
|
|
65
|
+
if (size > this.maxItemSizeBytes) {
|
|
66
|
+
// Skip storing oversized entries
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
// Enforce max items (LRU eviction)
|
|
70
|
+
if (this.cache.size >= this.maxItemsNumber) {
|
|
71
|
+
// Delete the oldest entry (first in Map)
|
|
72
|
+
const firstKey = this.cache.keys().next().value;
|
|
73
|
+
if (firstKey) {
|
|
74
|
+
await this.delete(firstKey);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Calculate lifespan
|
|
78
|
+
const lifespan = calculateLifespan(context?.revalidate, this.defaultTTL);
|
|
79
|
+
// Combine tags (explicit user tags only - implicit tags are passed in get())
|
|
80
|
+
const tags = context?.tags ?? [];
|
|
81
|
+
const entry = {
|
|
82
|
+
lastModified: Date.now(),
|
|
83
|
+
lifespan,
|
|
84
|
+
tags,
|
|
85
|
+
value,
|
|
86
|
+
};
|
|
87
|
+
this.cache.set(key, entry);
|
|
88
|
+
}
|
|
89
|
+
async revalidateTag(tag, _profile) {
|
|
90
|
+
// Store revalidation timestamp
|
|
91
|
+
// Note: profile parameter is used by Next.js for cache-life configuration
|
|
92
|
+
// but for memory handler we just invalidate immediately
|
|
93
|
+
this.revalidatedTags.set(tag, Date.now());
|
|
94
|
+
// For explicit tags (non-implicit), we can directly delete entries
|
|
95
|
+
if (!isImplicitTag(tag)) {
|
|
96
|
+
const keysToDelete = [];
|
|
97
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
98
|
+
if (entry.tags.includes(tag)) {
|
|
99
|
+
keysToDelete.push(key);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// Delete all entries with this tag
|
|
103
|
+
for (const key of keysToDelete) {
|
|
104
|
+
await this.delete(key);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// For implicit tags, we rely on the get() method to check revalidation
|
|
108
|
+
// This is because implicit tags are passed dynamically in meta.implicitTags
|
|
109
|
+
}
|
|
110
|
+
async delete(key) {
|
|
111
|
+
this.cache.delete(key);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Clear all cache entries (useful for testing)
|
|
115
|
+
*/
|
|
116
|
+
async clear() {
|
|
117
|
+
this.cache.clear();
|
|
118
|
+
this.revalidatedTags.clear();
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Get cache statistics
|
|
122
|
+
*/
|
|
123
|
+
getStats() {
|
|
124
|
+
return {
|
|
125
|
+
size: this.cache.size,
|
|
126
|
+
maxSize: this.maxItemsNumber,
|
|
127
|
+
revalidatedTags: this.revalidatedTags.size,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Create a memory cache handler instance
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```typescript
|
|
136
|
+
* const handler = createMemoryCacheHandler({
|
|
137
|
+
* maxItemsNumber: 500,
|
|
138
|
+
* defaultTTL: 3600
|
|
139
|
+
* });
|
|
140
|
+
* ```
|
|
141
|
+
*/
|
|
142
|
+
export function createMemoryCacheHandler(options) {
|
|
143
|
+
return new MemoryCacheHandler(options);
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/handlers/memory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { type RedisOptions } from "ioredis";
|
|
2
|
+
import type { CacheHandler, CacheHandlerContext, CacheHandlerGetMeta, CacheHandlerOptions, CacheValue } from "../types.js";
|
|
3
|
+
export interface RedisCacheHandlerOptions extends CacheHandlerOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Redis connection options (ioredis)
|
|
6
|
+
* Can be a URL string or RedisOptions object
|
|
7
|
+
*/
|
|
8
|
+
redis?: string | RedisOptions;
|
|
9
|
+
/**
|
|
10
|
+
* Key prefix for all cache entries
|
|
11
|
+
* @default "nextjs:cache:"
|
|
12
|
+
*/
|
|
13
|
+
keyPrefix?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Key prefix for tag tracking
|
|
16
|
+
* @default "nextjs:tags:"
|
|
17
|
+
*/
|
|
18
|
+
tagPrefix?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Default TTL in seconds for entries without explicit revalidate
|
|
21
|
+
* @default undefined (no expiration)
|
|
22
|
+
*/
|
|
23
|
+
defaultTTL?: number;
|
|
24
|
+
/**
|
|
25
|
+
* Enable debug logging
|
|
26
|
+
* @default false
|
|
27
|
+
*/
|
|
28
|
+
debug?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Redis cache handler for Next.js 16+ with Cache Components support
|
|
32
|
+
*
|
|
33
|
+
* Features:
|
|
34
|
+
* - Persistent caching across server restarts
|
|
35
|
+
* - Tag-based revalidation with Redis Sets
|
|
36
|
+
* - TTL support with automatic expiration
|
|
37
|
+
* - Compatible with Redis, ElastiCache, and Valkey
|
|
38
|
+
* - Connection pooling via ioredis
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* // In cache-handler.mjs or data-cache-handler.mjs
|
|
43
|
+
* import { RedisCacheHandler } from "@mrjasonroy/cache-components-cache-handler/handlers/redis";
|
|
44
|
+
*
|
|
45
|
+
* export default class NextCacheHandler extends RedisCacheHandler {
|
|
46
|
+
* constructor(options) {
|
|
47
|
+
* super({
|
|
48
|
+
* ...options,
|
|
49
|
+
* redis: process.env.REDIS_URL || "redis://localhost:6379",
|
|
50
|
+
* keyPrefix: "nextjs:cache:",
|
|
51
|
+
* defaultTTL: 3600
|
|
52
|
+
* });
|
|
53
|
+
* }
|
|
54
|
+
* }
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export declare class RedisCacheHandler implements CacheHandler {
|
|
58
|
+
readonly name = "redis";
|
|
59
|
+
private redis;
|
|
60
|
+
private readonly keyPrefix;
|
|
61
|
+
private readonly tagPrefix;
|
|
62
|
+
private readonly defaultTTL?;
|
|
63
|
+
private readonly debug;
|
|
64
|
+
constructor(options?: RedisCacheHandlerOptions);
|
|
65
|
+
private log;
|
|
66
|
+
private getCacheKey;
|
|
67
|
+
private getTagKey;
|
|
68
|
+
get(key: string, meta?: CacheHandlerGetMeta): Promise<CacheValue | null>;
|
|
69
|
+
set(key: string, value: CacheValue, context?: CacheHandlerContext): Promise<void>;
|
|
70
|
+
revalidateTag(tag: string, _profile?: string | {
|
|
71
|
+
expire?: number;
|
|
72
|
+
}): Promise<void>;
|
|
73
|
+
delete(key: string): Promise<void>;
|
|
74
|
+
/**
|
|
75
|
+
* Close the Redis connection
|
|
76
|
+
* Call this when shutting down your application
|
|
77
|
+
*/
|
|
78
|
+
close(): Promise<void>;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=redis.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis.d.ts","sourceRoot":"","sources":["../../src/handlers/redis.ts"],"names":[],"mappings":"AAAA,OAAc,EAAE,KAAK,YAAY,EAAE,MAAM,SAAS,CAAC;AAEnD,OAAO,KAAK,EACV,YAAY,EACZ,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EAEnB,UAAU,EACX,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,wBAAyB,SAAQ,mBAAmB;IACnE;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,YAAY,CAAC;IAE9B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBAAa,iBAAkB,YAAW,YAAY;IACpD,SAAgB,IAAI,WAAW;IAE/B,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAU;gBAEpB,OAAO,GAAE,wBAA6B;IA2BlD,OAAO,CAAC,GAAG;IAMX,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,SAAS;IAIX,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IA8CxE,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IA2DjF,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BlF,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUxC;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAQ7B"}
|