@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,210 @@
|
|
|
1
|
+
import Redis from "ioredis";
|
|
2
|
+
import { calculateLifespan, isExpired } from "../helpers/lifespan.js";
|
|
3
|
+
/**
|
|
4
|
+
* Redis cache handler for Next.js 16+ with Cache Components support
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - Persistent caching across server restarts
|
|
8
|
+
* - Tag-based revalidation with Redis Sets
|
|
9
|
+
* - TTL support with automatic expiration
|
|
10
|
+
* - Compatible with Redis, ElastiCache, and Valkey
|
|
11
|
+
* - Connection pooling via ioredis
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* // In cache-handler.mjs or data-cache-handler.mjs
|
|
16
|
+
* import { RedisCacheHandler } from "@mrjasonroy/cache-components-cache-handler/handlers/redis";
|
|
17
|
+
*
|
|
18
|
+
* export default class NextCacheHandler extends RedisCacheHandler {
|
|
19
|
+
* constructor(options) {
|
|
20
|
+
* super({
|
|
21
|
+
* ...options,
|
|
22
|
+
* redis: process.env.REDIS_URL || "redis://localhost:6379",
|
|
23
|
+
* keyPrefix: "nextjs:cache:",
|
|
24
|
+
* defaultTTL: 3600
|
|
25
|
+
* });
|
|
26
|
+
* }
|
|
27
|
+
* }
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export class RedisCacheHandler {
|
|
31
|
+
name = "redis";
|
|
32
|
+
redis;
|
|
33
|
+
keyPrefix;
|
|
34
|
+
tagPrefix;
|
|
35
|
+
defaultTTL;
|
|
36
|
+
debug;
|
|
37
|
+
constructor(options = {}) {
|
|
38
|
+
// Initialize Redis connection
|
|
39
|
+
if (typeof options.redis === "string") {
|
|
40
|
+
this.redis = new Redis(options.redis);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
this.redis = new Redis(options.redis || {});
|
|
44
|
+
}
|
|
45
|
+
this.keyPrefix = options.keyPrefix ?? "nextjs:cache:";
|
|
46
|
+
this.tagPrefix = options.tagPrefix ?? "nextjs:tags:";
|
|
47
|
+
this.defaultTTL = options.defaultTTL;
|
|
48
|
+
this.debug = options.debug ?? false;
|
|
49
|
+
// Handle Redis connection errors
|
|
50
|
+
this.redis.on("error", (err) => {
|
|
51
|
+
console.error("[RedisCacheHandler] Redis connection error:", err);
|
|
52
|
+
});
|
|
53
|
+
if (this.debug) {
|
|
54
|
+
console.log("[RedisCacheHandler] Initialized", {
|
|
55
|
+
keyPrefix: this.keyPrefix,
|
|
56
|
+
tagPrefix: this.tagPrefix,
|
|
57
|
+
defaultTTL: this.defaultTTL,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
log(...args) {
|
|
62
|
+
if (this.debug) {
|
|
63
|
+
console.log("[RedisCacheHandler]", ...args);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
getCacheKey(key) {
|
|
67
|
+
return `${this.keyPrefix}${key}`;
|
|
68
|
+
}
|
|
69
|
+
getTagKey(tag) {
|
|
70
|
+
return `${this.tagPrefix}${tag}`;
|
|
71
|
+
}
|
|
72
|
+
async get(key, meta) {
|
|
73
|
+
try {
|
|
74
|
+
const cacheKey = this.getCacheKey(key);
|
|
75
|
+
this.log("GET", cacheKey);
|
|
76
|
+
// Get the cached entry
|
|
77
|
+
const data = await this.redis.get(cacheKey);
|
|
78
|
+
if (!data) {
|
|
79
|
+
this.log("GET", cacheKey, "MISS");
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
// Parse the stored entry
|
|
83
|
+
const entry = JSON.parse(data);
|
|
84
|
+
// Check if expired based on lifespan
|
|
85
|
+
if (entry.lifespan && isExpired(entry.lifespan)) {
|
|
86
|
+
this.log("GET", cacheKey, "EXPIRED");
|
|
87
|
+
await this.delete(key);
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
// Check if any tag (explicit or implicit) has been revalidated
|
|
91
|
+
const allTags = [...entry.tags, ...(meta?.implicitTags ?? [])];
|
|
92
|
+
for (const tag of allTags) {
|
|
93
|
+
const tagKey = this.getTagKey(tag);
|
|
94
|
+
const revalidatedAt = await this.redis.get(tagKey);
|
|
95
|
+
// If tag was revalidated after entry was last modified, entry is stale
|
|
96
|
+
if (revalidatedAt && Number.parseInt(revalidatedAt) > entry.lastModified) {
|
|
97
|
+
this.log("GET", cacheKey, "STALE (tag revalidated)", tag);
|
|
98
|
+
await this.delete(key);
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
this.log("GET", cacheKey, "HIT");
|
|
103
|
+
return entry.value;
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
console.error("[RedisCacheHandler] GET error:", error);
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async set(key, value, context) {
|
|
111
|
+
try {
|
|
112
|
+
const cacheKey = this.getCacheKey(key);
|
|
113
|
+
// Calculate lifespan and TTL
|
|
114
|
+
const lifespan = calculateLifespan(context?.revalidate, this.defaultTTL);
|
|
115
|
+
const tags = context?.tags ?? [];
|
|
116
|
+
const entry = {
|
|
117
|
+
lastModified: Date.now(),
|
|
118
|
+
lifespan,
|
|
119
|
+
tags,
|
|
120
|
+
value,
|
|
121
|
+
};
|
|
122
|
+
const serialized = JSON.stringify(entry);
|
|
123
|
+
// Determine TTL for Redis
|
|
124
|
+
let ttl;
|
|
125
|
+
if (lifespan?.expireAt) {
|
|
126
|
+
// Use expire time as TTL
|
|
127
|
+
ttl = Math.ceil((lifespan.expireAt - Date.now()) / 1000);
|
|
128
|
+
if (ttl <= 0) {
|
|
129
|
+
this.log("SET", cacheKey, "SKIP (already expired)");
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// Store in Redis with optional TTL
|
|
134
|
+
if (ttl) {
|
|
135
|
+
await this.redis.setex(cacheKey, ttl, serialized);
|
|
136
|
+
this.log("SET", cacheKey, `TTL=${ttl}s`, `kind=${value.kind}`);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
await this.redis.set(cacheKey, serialized);
|
|
140
|
+
this.log("SET", cacheKey, "NO_TTL", `kind=${value.kind}`);
|
|
141
|
+
}
|
|
142
|
+
// Track tags -> keys mapping for revalidation
|
|
143
|
+
if (tags.length > 0) {
|
|
144
|
+
const pipeline = this.redis.pipeline();
|
|
145
|
+
for (const tag of tags) {
|
|
146
|
+
const tagKey = this.getTagKey(tag);
|
|
147
|
+
// Use a set to store all cache keys with this tag
|
|
148
|
+
pipeline.sadd(`${tagKey}:keys`, cacheKey);
|
|
149
|
+
// Set expiration on the tag's key set if we have a TTL
|
|
150
|
+
if (ttl) {
|
|
151
|
+
pipeline.expire(`${tagKey}:keys`, ttl);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
await pipeline.exec();
|
|
155
|
+
this.log("SET", cacheKey, "tags:", tags);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
console.error("[RedisCacheHandler] SET error:", error);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
async revalidateTag(tag, _profile) {
|
|
163
|
+
try {
|
|
164
|
+
const tagKey = this.getTagKey(tag);
|
|
165
|
+
// Store revalidation timestamp
|
|
166
|
+
await this.redis.set(tagKey, Date.now().toString());
|
|
167
|
+
this.log("revalidateTag", tag);
|
|
168
|
+
// Delete all cache keys associated with this tag
|
|
169
|
+
const keysSetKey = `${tagKey}:keys`;
|
|
170
|
+
const cacheKeys = await this.redis.smembers(keysSetKey);
|
|
171
|
+
if (cacheKeys.length > 0) {
|
|
172
|
+
const pipeline = this.redis.pipeline();
|
|
173
|
+
for (const cacheKey of cacheKeys) {
|
|
174
|
+
pipeline.del(cacheKey);
|
|
175
|
+
}
|
|
176
|
+
// Clear the tag's keys set
|
|
177
|
+
pipeline.del(keysSetKey);
|
|
178
|
+
await pipeline.exec();
|
|
179
|
+
this.log("revalidateTag", tag, `deleted ${cacheKeys.length} entries`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
console.error("[RedisCacheHandler] revalidateTag error:", error);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
async delete(key) {
|
|
187
|
+
try {
|
|
188
|
+
const cacheKey = this.getCacheKey(key);
|
|
189
|
+
await this.redis.del(cacheKey);
|
|
190
|
+
this.log("DELETE", cacheKey);
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
console.error("[RedisCacheHandler] DELETE error:", error);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Close the Redis connection
|
|
198
|
+
* Call this when shutting down your application
|
|
199
|
+
*/
|
|
200
|
+
async close() {
|
|
201
|
+
try {
|
|
202
|
+
await this.redis.quit();
|
|
203
|
+
this.log("Connection closed");
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
console.error("[RedisCacheHandler] close error:", error);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=redis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis.js","sourceRoot":"","sources":["../../src/handlers/redis.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Buffer conversion utilities for Next.js 15+ compatibility
|
|
3
|
+
* Next.js 15 changed body/rscData from strings to Buffers
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Convert a value to base64 string if it's a Buffer
|
|
7
|
+
* Used when storing cache values (e.g., in Redis)
|
|
8
|
+
*
|
|
9
|
+
* @param value - Value that might be a Buffer
|
|
10
|
+
* @returns Base64 string if Buffer, original value otherwise
|
|
11
|
+
*/
|
|
12
|
+
export declare function bufferToString(value: unknown): unknown;
|
|
13
|
+
/**
|
|
14
|
+
* Convert a base64 string back to Buffer if it was originally a Buffer
|
|
15
|
+
* Used when retrieving cache values (e.g., from Redis)
|
|
16
|
+
*
|
|
17
|
+
* This is a simplified version - in production you'd need to track
|
|
18
|
+
* which fields were originally Buffers
|
|
19
|
+
*
|
|
20
|
+
* @param value - Value that might be a base64 string
|
|
21
|
+
* @param isBuffer - Whether this value should be converted to Buffer
|
|
22
|
+
* @returns Buffer if isBuffer is true, original value otherwise
|
|
23
|
+
*/
|
|
24
|
+
export declare function stringToBuffer(value: string, isBuffer: boolean): Buffer | string;
|
|
25
|
+
//# sourceMappingURL=buffer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buffer.d.ts","sourceRoot":"","sources":["../../src/helpers/buffer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAkBtD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAKhF"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Buffer conversion utilities for Next.js 15+ compatibility
|
|
3
|
+
* Next.js 15 changed body/rscData from strings to Buffers
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Convert a value to base64 string if it's a Buffer
|
|
7
|
+
* Used when storing cache values (e.g., in Redis)
|
|
8
|
+
*
|
|
9
|
+
* @param value - Value that might be a Buffer
|
|
10
|
+
* @returns Base64 string if Buffer, original value otherwise
|
|
11
|
+
*/
|
|
12
|
+
export function bufferToString(value) {
|
|
13
|
+
if (Buffer.isBuffer(value)) {
|
|
14
|
+
return value.toString("base64");
|
|
15
|
+
}
|
|
16
|
+
if (typeof value === "object" && value !== null) {
|
|
17
|
+
const result = {};
|
|
18
|
+
for (const [key, val] of Object.entries(value)) {
|
|
19
|
+
result[key] = bufferToString(val);
|
|
20
|
+
}
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
if (Array.isArray(value)) {
|
|
24
|
+
return value.map(bufferToString);
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Convert a base64 string back to Buffer if it was originally a Buffer
|
|
30
|
+
* Used when retrieving cache values (e.g., from Redis)
|
|
31
|
+
*
|
|
32
|
+
* This is a simplified version - in production you'd need to track
|
|
33
|
+
* which fields were originally Buffers
|
|
34
|
+
*
|
|
35
|
+
* @param value - Value that might be a base64 string
|
|
36
|
+
* @param isBuffer - Whether this value should be converted to Buffer
|
|
37
|
+
* @returns Buffer if isBuffer is true, original value otherwise
|
|
38
|
+
*/
|
|
39
|
+
export function stringToBuffer(value, isBuffer) {
|
|
40
|
+
if (isBuffer && typeof value === "string") {
|
|
41
|
+
return Buffer.from(value, "base64");
|
|
42
|
+
}
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=buffer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buffer.js","sourceRoot":"","sources":["../../src/helpers/buffer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAClC,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAChD,MAAM,MAAM,GAA4B,EAAE,CAAC;QAC3C,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/C,MAAM,CAAC,GAAG,CAAC,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;QACpC,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IACnC,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,cAAc,CAAC,KAAa,EAAE,QAAiB;IAC7D,IAAI,QAAQ,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC1C,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check if a tag is an implicit (system-generated) tag
|
|
3
|
+
* Implicit tags are generated by Next.js with the _N_T_ prefix
|
|
4
|
+
*
|
|
5
|
+
* @param tag - The tag to check
|
|
6
|
+
* @returns True if the tag is implicit, false otherwise
|
|
7
|
+
*/
|
|
8
|
+
export declare function isImplicitTag(tag: string): boolean;
|
|
9
|
+
//# sourceMappingURL=is-implicit-tag.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"is-implicit-tag.d.ts","sourceRoot":"","sources":["../../src/helpers/is-implicit-tag.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAMlD"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { IMPLICIT_TAG_PREFIX } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Check if a tag is an implicit (system-generated) tag
|
|
4
|
+
* Implicit tags are generated by Next.js with the _N_T_ prefix
|
|
5
|
+
*
|
|
6
|
+
* @param tag - The tag to check
|
|
7
|
+
* @returns True if the tag is implicit, false otherwise
|
|
8
|
+
*/
|
|
9
|
+
export function isImplicitTag(tag) {
|
|
10
|
+
// Guard against non-string values that Next.js might pass
|
|
11
|
+
if (typeof tag !== "string") {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
return tag.startsWith(IMPLICIT_TAG_PREFIX);
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=is-implicit-tag.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"is-implicit-tag.js","sourceRoot":"","sources":["../../src/helpers/is-implicit-tag.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAElD;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,0DAA0D;IAC1D,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,GAAG,CAAC,UAAU,CAAC,mBAAmB,CAAC,CAAC;AAC7C,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { LifespanParameters } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Calculate lifespan parameters from revalidate value
|
|
4
|
+
* CRITICAL: All timestamps are in SECONDS, not milliseconds
|
|
5
|
+
*
|
|
6
|
+
* @param revalidate - Revalidation period in seconds, or false to disable
|
|
7
|
+
* @param defaultTTL - Default TTL in seconds if revalidate is undefined
|
|
8
|
+
* @returns Lifespan parameters or null if no expiration
|
|
9
|
+
*/
|
|
10
|
+
export declare function calculateLifespan(revalidate: number | false | undefined, defaultTTL?: number): LifespanParameters | null;
|
|
11
|
+
/**
|
|
12
|
+
* Check if a cache entry has expired based on its lifespan
|
|
13
|
+
*
|
|
14
|
+
* @param lifespan - Lifespan parameters (null means never expires)
|
|
15
|
+
* @returns True if expired, false otherwise
|
|
16
|
+
*/
|
|
17
|
+
export declare function isExpired(lifespan: LifespanParameters | null): boolean;
|
|
18
|
+
//# sourceMappingURL=lifespan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifespan.d.ts","sourceRoot":"","sources":["../../src/helpers/lifespan.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,EACtC,UAAU,CAAC,EAAE,MAAM,GAClB,kBAAkB,GAAG,IAAI,CAwB3B;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,kBAAkB,GAAG,IAAI,GAAG,OAAO,CAOtE"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Calculate lifespan parameters from revalidate value
|
|
3
|
+
* CRITICAL: All timestamps are in SECONDS, not milliseconds
|
|
4
|
+
*
|
|
5
|
+
* @param revalidate - Revalidation period in seconds, or false to disable
|
|
6
|
+
* @param defaultTTL - Default TTL in seconds if revalidate is undefined
|
|
7
|
+
* @returns Lifespan parameters or null if no expiration
|
|
8
|
+
*/
|
|
9
|
+
export function calculateLifespan(revalidate, defaultTTL) {
|
|
10
|
+
// If revalidate is explicitly false, no expiration
|
|
11
|
+
if (revalidate === false) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
// Use revalidate value or default TTL
|
|
15
|
+
const ttlSeconds = typeof revalidate === "number" ? revalidate : defaultTTL;
|
|
16
|
+
// If no TTL specified, no expiration
|
|
17
|
+
if (!ttlSeconds) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
const nowSeconds = Math.floor(Date.now() / 1000);
|
|
21
|
+
return {
|
|
22
|
+
lastModifiedAt: nowSeconds,
|
|
23
|
+
staleAt: nowSeconds + ttlSeconds,
|
|
24
|
+
expireAt: nowSeconds + ttlSeconds,
|
|
25
|
+
staleAge: ttlSeconds,
|
|
26
|
+
expireAge: ttlSeconds,
|
|
27
|
+
revalidate,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Check if a cache entry has expired based on its lifespan
|
|
32
|
+
*
|
|
33
|
+
* @param lifespan - Lifespan parameters (null means never expires)
|
|
34
|
+
* @returns True if expired, false otherwise
|
|
35
|
+
*/
|
|
36
|
+
export function isExpired(lifespan) {
|
|
37
|
+
if (!lifespan) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
const nowSeconds = Math.floor(Date.now() / 1000);
|
|
41
|
+
return nowSeconds >= lifespan.expireAt;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=lifespan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifespan.js","sourceRoot":"","sources":["../../src/helpers/lifespan.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAC/B,UAAsC,EACtC,UAAmB;IAEnB,mDAAmD;IACnD,IAAI,UAAU,KAAK,KAAK,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,sCAAsC;IACtC,MAAM,UAAU,GAAG,OAAO,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC;IAE5E,qCAAqC;IACrC,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAEjD,OAAO;QACL,cAAc,EAAE,UAAU;QAC1B,OAAO,EAAE,UAAU,GAAG,UAAU;QAChC,QAAQ,EAAE,UAAU,GAAG,UAAU;QACjC,QAAQ,EAAE,UAAU;QACpB,SAAS,EAAE,UAAU;QACrB,UAAU;KACX,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAC,QAAmC;IAC3D,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IACjD,OAAO,UAAU,IAAI,QAAQ,CAAC,QAAQ,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper functions for configuring Next.js cache handlers
|
|
3
|
+
*/
|
|
4
|
+
export interface CacheHandlerConfig {
|
|
5
|
+
/**
|
|
6
|
+
* Path to ISR cache handler (for cacheHandler in next.config)
|
|
7
|
+
*/
|
|
8
|
+
cacheHandler: string;
|
|
9
|
+
/**
|
|
10
|
+
* Paths to data cache handlers (for cacheHandlers in next.config)
|
|
11
|
+
*/
|
|
12
|
+
cacheHandlers: {
|
|
13
|
+
default: string;
|
|
14
|
+
remote: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Disable Next.js default in-memory cache for "use cache"
|
|
18
|
+
* Set to 0 to use your own handlers exclusively
|
|
19
|
+
*/
|
|
20
|
+
cacheMaxMemorySize?: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Create Next.js cache handler configuration for both ISR and Data Cache
|
|
24
|
+
*
|
|
25
|
+
* This generates the config needed for both:
|
|
26
|
+
* - `cacheHandler`: ISR cache (Incremental Static Regeneration)
|
|
27
|
+
* - `cacheHandlers`: Data cache ("use cache" and "use cache: remote")
|
|
28
|
+
*
|
|
29
|
+
* @param options - Configuration options
|
|
30
|
+
* @param options.isrHandlerPath - Path to ISR cache handler file (for cacheHandler)
|
|
31
|
+
* @param options.dataCacheHandlerPath - Path to data cache handler file (for cacheHandlers.default and cacheHandlers.remote)
|
|
32
|
+
* @param options.disableDefaultMemoryCache - Set to true to disable Next.js default in-memory cache (default: true)
|
|
33
|
+
*
|
|
34
|
+
* @returns Configuration object to spread into next.config
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* // next.config.mjs
|
|
39
|
+
* import { createCacheConfig } from "@mrjasonroy/better-nextjs-cache-handler";
|
|
40
|
+
* import { resolve } from "path";
|
|
41
|
+
*
|
|
42
|
+
* const cacheConfig = createCacheConfig({
|
|
43
|
+
* isrHandlerPath: resolve(process.cwd(), "./cache-handler.mjs"),
|
|
44
|
+
* dataCacheHandlerPath: resolve(process.cwd(), "./data-cache-handler.mjs"),
|
|
45
|
+
* });
|
|
46
|
+
*
|
|
47
|
+
* export default {
|
|
48
|
+
* ...cacheConfig,
|
|
49
|
+
* // other Next.js config
|
|
50
|
+
* };
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare function createCacheConfig(options: {
|
|
54
|
+
isrHandlerPath: string;
|
|
55
|
+
dataCacheHandlerPath: string;
|
|
56
|
+
disableDefaultMemoryCache?: boolean;
|
|
57
|
+
}): CacheHandlerConfig;
|
|
58
|
+
/**
|
|
59
|
+
* Create Next.js cache handler configuration with separate handlers for default and remote
|
|
60
|
+
*
|
|
61
|
+
* This allows you to use different cache handlers for:
|
|
62
|
+
* - `default`: Used by "use cache" (typically faster, in-memory or local)
|
|
63
|
+
* - `remote`: Used by "use cache: remote" (typically slower, database or API results)
|
|
64
|
+
*
|
|
65
|
+
* @param options - Configuration options
|
|
66
|
+
* @param options.isrHandlerPath - Path to ISR cache handler file (for cacheHandler)
|
|
67
|
+
* @param options.defaultDataCacheHandlerPath - Path to data cache handler for "use cache"
|
|
68
|
+
* @param options.remoteDataCacheHandlerPath - Path to data cache handler for "use cache: remote"
|
|
69
|
+
* @param options.disableDefaultMemoryCache - Set to true to disable Next.js default in-memory cache (default: true)
|
|
70
|
+
*
|
|
71
|
+
* @returns Configuration object to spread into next.config
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```ts
|
|
75
|
+
* // next.config.mjs
|
|
76
|
+
* import { createCacheConfigWithProfiles } from "@mrjasonroy/better-nextjs-cache-handler";
|
|
77
|
+
* import { resolve } from "path";
|
|
78
|
+
*
|
|
79
|
+
* const cacheConfig = createCacheConfigWithProfiles({
|
|
80
|
+
* isrHandlerPath: resolve(process.cwd(), "./cache-handler.mjs"),
|
|
81
|
+
* defaultDataCacheHandlerPath: resolve(process.cwd(), "./data-cache-memory.mjs"),
|
|
82
|
+
* remoteDataCacheHandlerPath: resolve(process.cwd(), "./data-cache-redis.mjs"),
|
|
83
|
+
* });
|
|
84
|
+
*
|
|
85
|
+
* export default {
|
|
86
|
+
* ...cacheConfig,
|
|
87
|
+
* // other Next.js config
|
|
88
|
+
* };
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
export declare function createCacheConfigWithProfiles(options: {
|
|
92
|
+
isrHandlerPath: string;
|
|
93
|
+
defaultDataCacheHandlerPath: string;
|
|
94
|
+
remoteDataCacheHandlerPath: string;
|
|
95
|
+
disableDefaultMemoryCache?: boolean;
|
|
96
|
+
}): CacheHandlerConfig;
|
|
97
|
+
//# sourceMappingURL=next-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"next-config.d.ts","sourceRoot":"","sources":["../../src/helpers/next-config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,aAAa,EAAE;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IAEF;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE;IACzC,cAAc,EAAE,MAAM,CAAC;IACvB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,yBAAyB,CAAC,EAAE,OAAO,CAAC;CACrC,GAAG,kBAAkB,CAcrB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,EAAE;IACrD,cAAc,EAAE,MAAM,CAAC;IACvB,2BAA2B,EAAE,MAAM,CAAC;IACpC,0BAA0B,EAAE,MAAM,CAAC;IACnC,yBAAyB,CAAC,EAAE,OAAO,CAAC;CACrC,GAAG,kBAAkB,CAmBrB"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper functions for configuring Next.js cache handlers
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Create Next.js cache handler configuration for both ISR and Data Cache
|
|
6
|
+
*
|
|
7
|
+
* This generates the config needed for both:
|
|
8
|
+
* - `cacheHandler`: ISR cache (Incremental Static Regeneration)
|
|
9
|
+
* - `cacheHandlers`: Data cache ("use cache" and "use cache: remote")
|
|
10
|
+
*
|
|
11
|
+
* @param options - Configuration options
|
|
12
|
+
* @param options.isrHandlerPath - Path to ISR cache handler file (for cacheHandler)
|
|
13
|
+
* @param options.dataCacheHandlerPath - Path to data cache handler file (for cacheHandlers.default and cacheHandlers.remote)
|
|
14
|
+
* @param options.disableDefaultMemoryCache - Set to true to disable Next.js default in-memory cache (default: true)
|
|
15
|
+
*
|
|
16
|
+
* @returns Configuration object to spread into next.config
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* // next.config.mjs
|
|
21
|
+
* import { createCacheConfig } from "@mrjasonroy/better-nextjs-cache-handler";
|
|
22
|
+
* import { resolve } from "path";
|
|
23
|
+
*
|
|
24
|
+
* const cacheConfig = createCacheConfig({
|
|
25
|
+
* isrHandlerPath: resolve(process.cwd(), "./cache-handler.mjs"),
|
|
26
|
+
* dataCacheHandlerPath: resolve(process.cwd(), "./data-cache-handler.mjs"),
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* export default {
|
|
30
|
+
* ...cacheConfig,
|
|
31
|
+
* // other Next.js config
|
|
32
|
+
* };
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export function createCacheConfig(options) {
|
|
36
|
+
const { isrHandlerPath, dataCacheHandlerPath, disableDefaultMemoryCache = true } = options;
|
|
37
|
+
return {
|
|
38
|
+
cacheHandler: isrHandlerPath,
|
|
39
|
+
cacheHandlers: {
|
|
40
|
+
// "use cache" uses this (default cache profile)
|
|
41
|
+
default: dataCacheHandlerPath,
|
|
42
|
+
// "use cache: remote" uses this (for DB queries, API calls)
|
|
43
|
+
remote: dataCacheHandlerPath,
|
|
44
|
+
},
|
|
45
|
+
// Disable Next.js default in-memory cache to use our handlers exclusively
|
|
46
|
+
cacheMaxMemorySize: disableDefaultMemoryCache ? 0 : undefined,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Create Next.js cache handler configuration with separate handlers for default and remote
|
|
51
|
+
*
|
|
52
|
+
* This allows you to use different cache handlers for:
|
|
53
|
+
* - `default`: Used by "use cache" (typically faster, in-memory or local)
|
|
54
|
+
* - `remote`: Used by "use cache: remote" (typically slower, database or API results)
|
|
55
|
+
*
|
|
56
|
+
* @param options - Configuration options
|
|
57
|
+
* @param options.isrHandlerPath - Path to ISR cache handler file (for cacheHandler)
|
|
58
|
+
* @param options.defaultDataCacheHandlerPath - Path to data cache handler for "use cache"
|
|
59
|
+
* @param options.remoteDataCacheHandlerPath - Path to data cache handler for "use cache: remote"
|
|
60
|
+
* @param options.disableDefaultMemoryCache - Set to true to disable Next.js default in-memory cache (default: true)
|
|
61
|
+
*
|
|
62
|
+
* @returns Configuration object to spread into next.config
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```ts
|
|
66
|
+
* // next.config.mjs
|
|
67
|
+
* import { createCacheConfigWithProfiles } from "@mrjasonroy/better-nextjs-cache-handler";
|
|
68
|
+
* import { resolve } from "path";
|
|
69
|
+
*
|
|
70
|
+
* const cacheConfig = createCacheConfigWithProfiles({
|
|
71
|
+
* isrHandlerPath: resolve(process.cwd(), "./cache-handler.mjs"),
|
|
72
|
+
* defaultDataCacheHandlerPath: resolve(process.cwd(), "./data-cache-memory.mjs"),
|
|
73
|
+
* remoteDataCacheHandlerPath: resolve(process.cwd(), "./data-cache-redis.mjs"),
|
|
74
|
+
* });
|
|
75
|
+
*
|
|
76
|
+
* export default {
|
|
77
|
+
* ...cacheConfig,
|
|
78
|
+
* // other Next.js config
|
|
79
|
+
* };
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
export function createCacheConfigWithProfiles(options) {
|
|
83
|
+
const { isrHandlerPath, defaultDataCacheHandlerPath, remoteDataCacheHandlerPath, disableDefaultMemoryCache = true, } = options;
|
|
84
|
+
return {
|
|
85
|
+
cacheHandler: isrHandlerPath,
|
|
86
|
+
cacheHandlers: {
|
|
87
|
+
// "use cache" uses this (default cache profile)
|
|
88
|
+
default: defaultDataCacheHandlerPath,
|
|
89
|
+
// "use cache: remote" uses this (for DB queries, API calls)
|
|
90
|
+
remote: remoteDataCacheHandlerPath,
|
|
91
|
+
},
|
|
92
|
+
// Disable Next.js default in-memory cache to use our handlers exclusively
|
|
93
|
+
cacheMaxMemorySize: disableDefaultMemoryCache ? 0 : undefined,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=next-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"next-config.js","sourceRoot":"","sources":["../../src/helpers/next-config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAuBH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAIjC;IACC,MAAM,EAAE,cAAc,EAAE,oBAAoB,EAAE,yBAAyB,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IAE3F,OAAO;QACL,YAAY,EAAE,cAAc;QAC5B,aAAa,EAAE;YACb,gDAAgD;YAChD,OAAO,EAAE,oBAAoB;YAC7B,4DAA4D;YAC5D,MAAM,EAAE,oBAAoB;SAC7B;QACD,0EAA0E;QAC1E,kBAAkB,EAAE,yBAAyB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS;KAC9D,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,UAAU,6BAA6B,CAAC,OAK7C;IACC,MAAM,EACJ,cAAc,EACd,2BAA2B,EAC3B,0BAA0B,EAC1B,yBAAyB,GAAG,IAAI,GACjC,GAAG,OAAO,CAAC;IAEZ,OAAO;QACL,YAAY,EAAE,cAAc;QAC5B,aAAa,EAAE;YACb,gDAAgD;YAChD,OAAO,EAAE,2BAA2B;YACpC,4DAA4D;YAC5D,MAAM,EAAE,0BAA0B;SACnC;QACD,0EAA0E;QAC1E,kBAAkB,EAAE,yBAAyB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS;KAC9D,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mrjasonroy/better-nextjs-cache-handler
|
|
3
|
+
* Modern cache handler for Next.js 16+ with support for ISR and 'use cache' directive
|
|
4
|
+
*/
|
|
5
|
+
export { CompositeHandler, type CompositeHandlerOptions, createCompositeHandler, } from "./handlers/composite.js";
|
|
6
|
+
export { createMemoryCacheHandler, MemoryCacheHandler, type MemoryCacheHandlerOptions, } from "./handlers/memory.js";
|
|
7
|
+
export { RedisCacheHandler, type RedisCacheHandlerOptions, } from "./handlers/redis.js";
|
|
8
|
+
export { bufferToString, stringToBuffer } from "./helpers/buffer.js";
|
|
9
|
+
export { isImplicitTag } from "./helpers/is-implicit-tag.js";
|
|
10
|
+
export { calculateLifespan, isExpired } from "./helpers/lifespan.js";
|
|
11
|
+
export { createCacheConfig, createCacheConfigWithProfiles, type CacheHandlerConfig, } from "./helpers/next-config.js";
|
|
12
|
+
export type { CacheHandler, CacheHandlerContext, CacheHandlerGetMeta, CacheHandlerOptions, CacheHandlerValue, CacheValue, LifespanParameters, } from "./types.js";
|
|
13
|
+
export { IMPLICIT_TAG_PREFIX } from "./types.js";
|
|
14
|
+
export { createMemoryDataCacheHandler, type MemoryDataCacheHandlerOptions, } from "./data-cache/memory.js";
|
|
15
|
+
export { createRedisDataCacheHandler, type RedisClient, type RedisDataCacheHandlerOptions, } from "./data-cache/redis.js";
|
|
16
|
+
export type { DataCacheEntry, DataCacheHandler, Timestamp, } from "./data-cache/types.js";
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,EACL,gBAAgB,EAChB,KAAK,uBAAuB,EAC5B,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,wBAAwB,EACxB,kBAAkB,EAClB,KAAK,yBAAyB,GAC/B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,iBAAiB,EACjB,KAAK,wBAAwB,GAC9B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErE,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EACL,iBAAiB,EACjB,6BAA6B,EAC7B,KAAK,kBAAkB,GACxB,MAAM,0BAA0B,CAAC;AAElC,YAAY,EACV,YAAY,EACZ,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,UAAU,EACV,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAMjD,OAAO,EACL,4BAA4B,EAC5B,KAAK,6BAA6B,GACnC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,2BAA2B,EAC3B,KAAK,WAAW,EAChB,KAAK,4BAA4B,GAClC,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,cAAc,EACd,gBAAgB,EAChB,SAAS,GACV,MAAM,uBAAuB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mrjasonroy/better-nextjs-cache-handler
|
|
3
|
+
* Modern cache handler for Next.js 16+ with support for ISR and 'use cache' directive
|
|
4
|
+
*/
|
|
5
|
+
// =============================================================================
|
|
6
|
+
// ISR Cache Handlers (for Incremental Static Regeneration)
|
|
7
|
+
// =============================================================================
|
|
8
|
+
export { CompositeHandler, createCompositeHandler, } from "./handlers/composite.js";
|
|
9
|
+
// Handlers
|
|
10
|
+
export { createMemoryCacheHandler, MemoryCacheHandler, } from "./handlers/memory.js";
|
|
11
|
+
export { RedisCacheHandler, } from "./handlers/redis.js";
|
|
12
|
+
export { bufferToString, stringToBuffer } from "./helpers/buffer.js";
|
|
13
|
+
// Helpers
|
|
14
|
+
export { isImplicitTag } from "./helpers/is-implicit-tag.js";
|
|
15
|
+
export { calculateLifespan, isExpired } from "./helpers/lifespan.js";
|
|
16
|
+
export { createCacheConfig, createCacheConfigWithProfiles, } from "./helpers/next-config.js";
|
|
17
|
+
export { IMPLICIT_TAG_PREFIX } from "./types.js";
|
|
18
|
+
// =============================================================================
|
|
19
|
+
// Data Cache Handlers (for "use cache" directive)
|
|
20
|
+
// =============================================================================
|
|
21
|
+
export { createMemoryDataCacheHandler, } from "./data-cache/memory.js";
|
|
22
|
+
export { createRedisDataCacheHandler, } from "./data-cache/redis.js";
|
|
23
|
+
//# sourceMappingURL=index.js.map
|