@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.
Files changed (50) hide show
  1. package/README.md +437 -0
  2. package/dist/data-cache/memory.d.ts +47 -0
  3. package/dist/data-cache/memory.d.ts.map +1 -0
  4. package/dist/data-cache/memory.js +310 -0
  5. package/dist/data-cache/memory.js.map +1 -0
  6. package/dist/data-cache/redis.d.ts +83 -0
  7. package/dist/data-cache/redis.d.ts.map +1 -0
  8. package/dist/data-cache/redis.js +240 -0
  9. package/dist/data-cache/redis.js.map +1 -0
  10. package/dist/data-cache/types.d.ts +85 -0
  11. package/dist/data-cache/types.d.ts.map +1 -0
  12. package/dist/data-cache/types.js +7 -0
  13. package/dist/data-cache/types.js.map +1 -0
  14. package/dist/handlers/composite.d.ts +70 -0
  15. package/dist/handlers/composite.d.ts.map +1 -0
  16. package/dist/handlers/composite.js +123 -0
  17. package/dist/handlers/composite.js.map +1 -0
  18. package/dist/handlers/memory.d.ts +77 -0
  19. package/dist/handlers/memory.d.ts.map +1 -0
  20. package/dist/handlers/memory.js +145 -0
  21. package/dist/handlers/memory.js.map +1 -0
  22. package/dist/handlers/redis.d.ts +80 -0
  23. package/dist/handlers/redis.d.ts.map +1 -0
  24. package/dist/handlers/redis.js +210 -0
  25. package/dist/handlers/redis.js.map +1 -0
  26. package/dist/helpers/buffer.d.ts +25 -0
  27. package/dist/helpers/buffer.d.ts.map +1 -0
  28. package/dist/helpers/buffer.js +45 -0
  29. package/dist/helpers/buffer.js.map +1 -0
  30. package/dist/helpers/is-implicit-tag.d.ts +9 -0
  31. package/dist/helpers/is-implicit-tag.d.ts.map +1 -0
  32. package/dist/helpers/is-implicit-tag.js +16 -0
  33. package/dist/helpers/is-implicit-tag.js.map +1 -0
  34. package/dist/helpers/lifespan.d.ts +18 -0
  35. package/dist/helpers/lifespan.d.ts.map +1 -0
  36. package/dist/helpers/lifespan.js +43 -0
  37. package/dist/helpers/lifespan.js.map +1 -0
  38. package/dist/helpers/next-config.d.ts +97 -0
  39. package/dist/helpers/next-config.d.ts.map +1 -0
  40. package/dist/helpers/next-config.js +96 -0
  41. package/dist/helpers/next-config.js.map +1 -0
  42. package/dist/index.d.ts +17 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +23 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/types.d.ts +163 -0
  47. package/dist/types.d.ts.map +1 -0
  48. package/dist/types.js +9 -0
  49. package/dist/types.js.map +1 -0
  50. package/package.json +67 -0
@@ -0,0 +1,310 @@
1
+ /**
2
+ * Memory-based Data Cache Handler for "use cache" directive
3
+ * Based on Next.js default handler but with customizable options
4
+ *
5
+ * In-memory caches are fragile and should not use stale-while-revalidate
6
+ * semantics because entries may be evicted before reuse. Stale entries
7
+ * are considered expired/missing.
8
+ */
9
+ /**
10
+ * Simple LRU cache implementation
11
+ */
12
+ class LRUCache {
13
+ cache = new Map();
14
+ maxSize;
15
+ sizeOf;
16
+ currentSize = 0;
17
+ constructor(maxSize, sizeOf) {
18
+ this.maxSize = maxSize;
19
+ this.sizeOf = sizeOf;
20
+ }
21
+ get(key) {
22
+ const value = this.cache.get(key);
23
+ if (value !== undefined) {
24
+ // Move to end (most recently used)
25
+ this.cache.delete(key);
26
+ this.cache.set(key, value);
27
+ }
28
+ return value;
29
+ }
30
+ set(key, value) {
31
+ // Remove existing if present
32
+ const existing = this.cache.get(key);
33
+ if (existing !== undefined) {
34
+ this.currentSize -= this.sizeOf(existing);
35
+ this.cache.delete(key);
36
+ }
37
+ const size = this.sizeOf(value);
38
+ // Evict oldest entries if needed
39
+ while (this.currentSize + size > this.maxSize && this.cache.size > 0) {
40
+ const firstKey = this.cache.keys().next().value;
41
+ if (firstKey !== undefined) {
42
+ const firstValue = this.cache.get(firstKey);
43
+ if (firstValue !== undefined) {
44
+ this.currentSize -= this.sizeOf(firstValue);
45
+ }
46
+ this.cache.delete(firstKey);
47
+ }
48
+ }
49
+ // Add new entry
50
+ this.cache.set(key, value);
51
+ this.currentSize += size;
52
+ }
53
+ delete(key) {
54
+ const value = this.cache.get(key);
55
+ if (value !== undefined) {
56
+ this.currentSize -= this.sizeOf(value);
57
+ this.cache.delete(key);
58
+ }
59
+ }
60
+ clear() {
61
+ this.cache.clear();
62
+ this.currentSize = 0;
63
+ }
64
+ }
65
+ /**
66
+ * Global tags manifest shared across all handler instances
67
+ *
68
+ * This is necessary because Next.js may create multiple handler instances
69
+ * (e.g., one for "default" profile and one for "remote" profile), but
70
+ * revalidateTag() needs to invalidate entries across ALL instances.
71
+ *
72
+ * ## Tag Revalidation Semantics
73
+ *
74
+ * When `updateTags()` is called, it can operate in two modes:
75
+ *
76
+ * ### 1. Immediate Expiration (deprecated, for testing)
77
+ * ```typescript
78
+ * updateTags(['my-tag'], undefined)
79
+ * // Sets: expired = now (entries expire immediately)
80
+ * ```
81
+ *
82
+ * ### 2. Stale-While-Revalidate (recommended for production)
83
+ * ```typescript
84
+ * updateTags(['my-tag'], { expire: 3600 })
85
+ * // Sets: stale = now, expired = now + 3600 * 1000
86
+ * // Entries are marked stale immediately but won't expire for 1 hour
87
+ * ```
88
+ *
89
+ * ## Entry Lifecycle
90
+ *
91
+ * When `get()` is called, entries go through these checks:
92
+ *
93
+ * 1. **TTL Check**: Entry expires if `now > timestamp + revalidate * 1000`
94
+ * 2. **Tag Expiration Check**: Entry expires if ANY tag was revalidated after entry creation
95
+ * - Skips tags with future expiration times (stale-while-revalidate)
96
+ * 3. **Tag Staleness Check**: Entry is marked stale (revalidate = -1) if ANY tag's stale
97
+ * timestamp is after entry creation
98
+ *
99
+ * This allows entries to be served with stale data while revalidating in the background.
100
+ */
101
+ const globalTagsManifest = new Map();
102
+ /**
103
+ * Check if tags are expired based on manifest
104
+ * Returns true if any tag was revalidated (invalidated) after the entry was created
105
+ */
106
+ function areTagsExpired(tags, timestamp, debug) {
107
+ const now = Math.round(performance.timeOrigin + performance.now());
108
+ for (const tag of tags) {
109
+ const entry = globalTagsManifest.get(tag);
110
+ const expiredAt = entry?.expired;
111
+ debug?.("areTagsExpired checking", {
112
+ tag,
113
+ entryTimestamp: timestamp,
114
+ expiredAt,
115
+ now,
116
+ });
117
+ if (typeof expiredAt === "number") {
118
+ // Match Next.js logic exactly:
119
+ // Entry is expired if:
120
+ // 1. expiredAt <= now (tag revalidation time has passed)
121
+ // 2. expiredAt > timestamp (tag was revalidated AFTER entry was created)
122
+ const isImmediatelyExpired = expiredAt <= now && expiredAt > timestamp;
123
+ debug?.("areTagsExpired result", {
124
+ tag,
125
+ isImmediatelyExpired,
126
+ check1: `expiredAt (${expiredAt}) <= now (${now})`,
127
+ check1Result: expiredAt <= now,
128
+ check2: `expiredAt (${expiredAt}) > timestamp (${timestamp})`,
129
+ check2Result: expiredAt > timestamp,
130
+ });
131
+ if (isImmediatelyExpired) {
132
+ return true;
133
+ }
134
+ }
135
+ }
136
+ return false;
137
+ }
138
+ /**
139
+ * Check if tags are stale based on manifest
140
+ * Returns true if any tag was marked stale AFTER the entry was created
141
+ */
142
+ function areTagsStale(tags, timestamp) {
143
+ for (const tag of tags) {
144
+ const entry = globalTagsManifest.get(tag);
145
+ const staleAt = entry?.stale ?? 0;
146
+ // Match Next.js logic: entry is stale if tag's stale timestamp > entry's creation timestamp
147
+ if (typeof staleAt === "number" && staleAt > timestamp) {
148
+ return true;
149
+ }
150
+ }
151
+ return false;
152
+ }
153
+ /**
154
+ * Create a memory-based data cache handler for "use cache" directive
155
+ *
156
+ * @example
157
+ * ```typescript
158
+ * // cache-handler.mjs
159
+ * import { createMemoryDataCacheHandler } from '@mrjasonroy/better-nextjs-cache-handler/data-cache';
160
+ *
161
+ * export default createMemoryDataCacheHandler({
162
+ * maxSize: 100 * 1024 * 1024, // 100MB
163
+ * debug: process.env.NODE_ENV === 'development'
164
+ * });
165
+ * ```
166
+ *
167
+ * Then in next.config.js:
168
+ * ```javascript
169
+ * module.exports = {
170
+ * cacheComponents: true,
171
+ * cacheHandlers: {
172
+ * default: require.resolve('./cache-handler.mjs')
173
+ * }
174
+ * }
175
+ * ```
176
+ */
177
+ export function createMemoryDataCacheHandler(options = {}) {
178
+ const maxSize = options.maxSize ?? 50 * 1024 * 1024; // 50MB default
179
+ // If max size is 0, return a no-op handler
180
+ if (maxSize === 0) {
181
+ return {
182
+ get: () => Promise.resolve(undefined),
183
+ set: () => Promise.resolve(),
184
+ refreshTags: () => Promise.resolve(),
185
+ getExpiration: () => Promise.resolve(0),
186
+ updateTags: () => Promise.resolve(),
187
+ };
188
+ }
189
+ const memoryCache = new LRUCache(maxSize, (entry) => entry.size);
190
+ const pendingSets = new Map();
191
+ const debug = options.debug ? console.debug.bind(console, "[MemoryDataCache]:") : undefined;
192
+ return {
193
+ async get(cacheKey, _softTags) {
194
+ const pendingPromise = pendingSets.get(cacheKey);
195
+ if (pendingPromise) {
196
+ debug?.("get", cacheKey, "pending");
197
+ await pendingPromise;
198
+ }
199
+ const privateEntry = memoryCache.get(cacheKey);
200
+ if (!privateEntry) {
201
+ debug?.("get", cacheKey, "not found");
202
+ return undefined;
203
+ }
204
+ const entry = privateEntry.entry;
205
+ const now = performance.timeOrigin + performance.now();
206
+ // In-memory caches should expire after revalidate time
207
+ if (now > entry.timestamp + entry.revalidate * 1000) {
208
+ debug?.("get", cacheKey, "expired");
209
+ return undefined;
210
+ }
211
+ let revalidate = entry.revalidate;
212
+ if (areTagsExpired(entry.tags, entry.timestamp, debug)) {
213
+ debug?.("get", cacheKey, "had expired tag");
214
+ return undefined;
215
+ }
216
+ if (areTagsStale(entry.tags, entry.timestamp)) {
217
+ debug?.("get", cacheKey, "had stale tag");
218
+ revalidate = -1;
219
+ }
220
+ // Tee the stream so we can return it and keep a copy
221
+ const [returnStream, newSaved] = entry.value.tee();
222
+ entry.value = newSaved;
223
+ debug?.("get", cacheKey, "found", {
224
+ tags: entry.tags,
225
+ timestamp: entry.timestamp,
226
+ expire: entry.expire,
227
+ revalidate,
228
+ });
229
+ return {
230
+ ...entry,
231
+ revalidate,
232
+ value: returnStream,
233
+ };
234
+ },
235
+ async set(cacheKey, pendingEntry) {
236
+ debug?.("set", cacheKey, "start");
237
+ let resolvePending = () => { };
238
+ const pendingPromise = new Promise((resolve) => {
239
+ resolvePending = resolve;
240
+ });
241
+ pendingSets.set(cacheKey, pendingPromise);
242
+ try {
243
+ const entry = await pendingEntry;
244
+ let size = 0;
245
+ // Tee the stream to read size and store value
246
+ const [value, clonedValue] = entry.value.tee();
247
+ entry.value = value;
248
+ const reader = clonedValue.getReader();
249
+ // Calculate total size by reading the stream
250
+ // biome-ignore lint/suspicious/noImplicitAnyLet: Chunk type inferred from reader
251
+ // biome-ignore lint/suspicious/noAssignInExpressions: Idiomatic pattern for stream reading
252
+ for (let chunk; !(chunk = await reader.read()).done;) {
253
+ size += Buffer.from(chunk.value).byteLength;
254
+ }
255
+ memoryCache.set(cacheKey, {
256
+ entry,
257
+ isErrored: false,
258
+ errorRetryCount: 0,
259
+ size,
260
+ });
261
+ debug?.("set", cacheKey, "done", { size });
262
+ }
263
+ catch (err) {
264
+ debug?.("set", cacheKey, "failed", err);
265
+ }
266
+ finally {
267
+ resolvePending();
268
+ pendingSets.delete(cacheKey);
269
+ }
270
+ },
271
+ async refreshTags() {
272
+ // Nothing to do for in-memory cache
273
+ debug?.("refreshTags", "no-op for memory cache");
274
+ },
275
+ async getExpiration(tags) {
276
+ const expirations = tags.map((tag) => {
277
+ const entry = globalTagsManifest.get(tag);
278
+ if (!entry)
279
+ return 0;
280
+ // Return the most recent timestamp (either expired or stale)
281
+ return entry.expired || 0;
282
+ });
283
+ const expiration = Math.max(...expirations, 0);
284
+ debug?.("getExpiration", { tags, expiration });
285
+ return expiration;
286
+ },
287
+ async updateTags(tags, durations) {
288
+ const now = Math.round(performance.timeOrigin + performance.now());
289
+ debug?.("updateTags", { tags, timestamp: now, durations });
290
+ for (const tag of tags) {
291
+ const existingEntry = globalTagsManifest.get(tag) || {};
292
+ if (durations) {
293
+ // Use provided durations directly
294
+ const updates = { ...existingEntry };
295
+ // mark as stale immediately
296
+ updates.stale = now;
297
+ if (durations.expire !== undefined) {
298
+ updates.expired = now + durations.expire * 1000; // Convert seconds to ms
299
+ }
300
+ globalTagsManifest.set(tag, updates);
301
+ }
302
+ else {
303
+ // Update expired field for immediate expiration (default behavior)
304
+ globalTagsManifest.set(tag, { ...existingEntry, expired: now });
305
+ }
306
+ }
307
+ },
308
+ };
309
+ }
310
+ //# sourceMappingURL=memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/data-cache/memory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Redis-based Data Cache Handler for "use cache" directive
3
+ * For production use with distributed Next.js deployments
4
+ *
5
+ * This handler stores cache entries in Redis, allowing multiple
6
+ * Next.js instances to share the same cache.
7
+ */
8
+ import type { DataCacheHandler } from "./types.js";
9
+ export interface RedisDataCacheHandlerOptions {
10
+ /**
11
+ * Redis client instance (ioredis or node-redis)
12
+ */
13
+ redis: RedisClient;
14
+ /**
15
+ * Key prefix for all cache entries
16
+ * @default "nextjs:data-cache:"
17
+ */
18
+ keyPrefix?: string;
19
+ /**
20
+ * Key prefix for tag manifest
21
+ * @default "nextjs:tags:"
22
+ */
23
+ tagPrefix?: string;
24
+ /**
25
+ * Enable debug logging
26
+ * @default false
27
+ */
28
+ debug?: boolean;
29
+ /**
30
+ * Default TTL for cache entries in seconds
31
+ * Used if entry doesn't specify expiration
32
+ * @default 86400 (24 hours)
33
+ */
34
+ defaultTTL?: number;
35
+ }
36
+ /**
37
+ * Redis client interface (compatible with ioredis and node-redis)
38
+ */
39
+ export interface RedisClient {
40
+ get(key: string): Promise<string | null>;
41
+ set(key: string, value: string, ...args: unknown[]): Promise<unknown>;
42
+ del(...keys: string[]): Promise<number>;
43
+ exists(...keys: string[]): Promise<number>;
44
+ ttl(key: string): Promise<number>;
45
+ hGet(key: string, field: string): Promise<string | null>;
46
+ hSet(key: string, field: string, value: string): Promise<unknown>;
47
+ hGetAll(key: string): Promise<Record<string, string>>;
48
+ }
49
+ /**
50
+ * Create a Redis-based data cache handler for "use cache" directive
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * // redis-cache-handler.mjs
55
+ * import { createClient } from 'redis';
56
+ * import { createRedisDataCacheHandler } from '@mrjasonroy/better-nextjs-cache-handler/data-cache';
57
+ *
58
+ * const redis = createClient({
59
+ * url: process.env.REDIS_URL
60
+ * });
61
+ *
62
+ * await redis.connect();
63
+ *
64
+ * export default createRedisDataCacheHandler({
65
+ * redis,
66
+ * keyPrefix: 'myapp:cache:',
67
+ * debug: process.env.NODE_ENV === 'development'
68
+ * });
69
+ * ```
70
+ *
71
+ * Then in next.config.js:
72
+ * ```javascript
73
+ * module.exports = {
74
+ * cacheComponents: true,
75
+ * cacheHandlers: {
76
+ * default: require.resolve('./redis-cache-handler.mjs'),
77
+ * remote: require.resolve('./redis-cache-handler.mjs') // Same handler for remote
78
+ * }
79
+ * }
80
+ * ```
81
+ */
82
+ export declare function createRedisDataCacheHandler(options: RedisDataCacheHandlerOptions): DataCacheHandler;
83
+ //# sourceMappingURL=redis.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis.d.ts","sourceRoot":"","sources":["../../src/data-cache/redis.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAkB,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnE,MAAM,WAAW,4BAA4B;IAC3C;;OAEG;IACH,KAAK,EAAE,WAAW,CAAC;IAEnB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtE,GAAG,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3C,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAClC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClE,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACvD;AAmDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,4BAA4B,GACpC,gBAAgB,CAwMlB"}
@@ -0,0 +1,240 @@
1
+ /**
2
+ * Redis-based Data Cache Handler for "use cache" directive
3
+ * For production use with distributed Next.js deployments
4
+ *
5
+ * This handler stores cache entries in Redis, allowing multiple
6
+ * Next.js instances to share the same cache.
7
+ */
8
+ /**
9
+ * Convert ReadableStream to Buffer
10
+ */
11
+ async function streamToBuffer(stream) {
12
+ const reader = stream.getReader();
13
+ const chunks = [];
14
+ try {
15
+ while (true) {
16
+ const { done, value } = await reader.read();
17
+ if (done)
18
+ break;
19
+ chunks.push(value);
20
+ }
21
+ }
22
+ finally {
23
+ reader.releaseLock();
24
+ }
25
+ const totalLength = chunks.reduce((acc, chunk) => acc + chunk.length, 0);
26
+ const result = new Uint8Array(totalLength);
27
+ let offset = 0;
28
+ for (const chunk of chunks) {
29
+ result.set(chunk, offset);
30
+ offset += chunk.length;
31
+ }
32
+ return Buffer.from(result);
33
+ }
34
+ /**
35
+ * Convert Buffer to ReadableStream
36
+ */
37
+ function bufferToStream(buffer) {
38
+ return new ReadableStream({
39
+ start(controller) {
40
+ controller.enqueue(new Uint8Array(buffer));
41
+ controller.close();
42
+ },
43
+ });
44
+ }
45
+ /**
46
+ * Create a Redis-based data cache handler for "use cache" directive
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * // redis-cache-handler.mjs
51
+ * import { createClient } from 'redis';
52
+ * import { createRedisDataCacheHandler } from '@mrjasonroy/better-nextjs-cache-handler/data-cache';
53
+ *
54
+ * const redis = createClient({
55
+ * url: process.env.REDIS_URL
56
+ * });
57
+ *
58
+ * await redis.connect();
59
+ *
60
+ * export default createRedisDataCacheHandler({
61
+ * redis,
62
+ * keyPrefix: 'myapp:cache:',
63
+ * debug: process.env.NODE_ENV === 'development'
64
+ * });
65
+ * ```
66
+ *
67
+ * Then in next.config.js:
68
+ * ```javascript
69
+ * module.exports = {
70
+ * cacheComponents: true,
71
+ * cacheHandlers: {
72
+ * default: require.resolve('./redis-cache-handler.mjs'),
73
+ * remote: require.resolve('./redis-cache-handler.mjs') // Same handler for remote
74
+ * }
75
+ * }
76
+ * ```
77
+ */
78
+ export function createRedisDataCacheHandler(options) {
79
+ const { redis, keyPrefix = "nextjs:data-cache:", tagPrefix = "nextjs:tags:", defaultTTL = 86400, debug = false, } = options;
80
+ const log = debug ? console.debug.bind(console, "[RedisDataCache]:") : undefined;
81
+ /**
82
+ * Get cache key with prefix
83
+ */
84
+ function getCacheKey(cacheKey) {
85
+ return `${keyPrefix}${cacheKey}`;
86
+ }
87
+ /**
88
+ * Get tag key with prefix
89
+ */
90
+ function getTagKey(tag) {
91
+ return `${tagPrefix}${tag}`;
92
+ }
93
+ /**
94
+ * Serialize cache entry for Redis storage
95
+ */
96
+ async function serializeEntry(entry) {
97
+ const buffer = await streamToBuffer(entry.value);
98
+ return {
99
+ value: buffer.toString("base64"),
100
+ tags: entry.tags,
101
+ stale: entry.stale,
102
+ timestamp: entry.timestamp,
103
+ expire: entry.expire,
104
+ revalidate: entry.revalidate,
105
+ };
106
+ }
107
+ /**
108
+ * Deserialize cache entry from Redis
109
+ */
110
+ function deserializeEntry(data) {
111
+ const buffer = Buffer.from(data.value, "base64");
112
+ return {
113
+ value: bufferToStream(buffer),
114
+ tags: data.tags,
115
+ stale: data.stale,
116
+ timestamp: data.timestamp,
117
+ expire: data.expire,
118
+ revalidate: data.revalidate,
119
+ };
120
+ }
121
+ return {
122
+ async get(cacheKey, _softTags) {
123
+ const key = getCacheKey(cacheKey);
124
+ try {
125
+ const data = await redis.get(key);
126
+ if (!data) {
127
+ log?.("get", cacheKey, "not found");
128
+ return undefined;
129
+ }
130
+ const entry = deserializeEntry(JSON.parse(data));
131
+ const now = Date.now();
132
+ // Check expiration
133
+ if (now > entry.timestamp + entry.revalidate * 1000) {
134
+ log?.("get", cacheKey, "expired");
135
+ await redis.del(key);
136
+ return undefined;
137
+ }
138
+ // Check tag expiration
139
+ let revalidate = entry.revalidate;
140
+ for (const tag of entry.tags) {
141
+ const tagData = await redis.hGetAll(getTagKey(tag));
142
+ if (tagData.expired) {
143
+ const expired = Number.parseInt(tagData.expired, 10);
144
+ if (expired > entry.timestamp) {
145
+ log?.("get", cacheKey, "had expired tag", tag);
146
+ await redis.del(key);
147
+ return undefined;
148
+ }
149
+ }
150
+ if (tagData.stale) {
151
+ const stale = Number.parseInt(tagData.stale, 10);
152
+ if (stale > entry.timestamp) {
153
+ log?.("get", cacheKey, "had stale tag", tag);
154
+ revalidate = -1;
155
+ }
156
+ }
157
+ }
158
+ // Tee the stream
159
+ const [returnStream, newSaved] = entry.value.tee();
160
+ entry.value = newSaved;
161
+ log?.("get", cacheKey, "found", {
162
+ tags: entry.tags,
163
+ timestamp: entry.timestamp,
164
+ revalidate,
165
+ });
166
+ return {
167
+ ...entry,
168
+ revalidate,
169
+ value: returnStream,
170
+ };
171
+ }
172
+ catch (error) {
173
+ log?.("get", cacheKey, "error", error);
174
+ return undefined;
175
+ }
176
+ },
177
+ async set(cacheKey, pendingEntry) {
178
+ const key = getCacheKey(cacheKey);
179
+ try {
180
+ log?.("set", cacheKey, "start");
181
+ const entry = await pendingEntry;
182
+ const serialized = await serializeEntry(entry);
183
+ // Calculate TTL (use expire time or default)
184
+ const ttl = entry.expire < 4294967294 ? entry.expire : defaultTTL;
185
+ // Store in Redis with TTL
186
+ await redis.set(key, JSON.stringify(serialized), "EX", Math.ceil(ttl));
187
+ log?.("set", cacheKey, "done", { ttl });
188
+ }
189
+ catch (error) {
190
+ log?.("set", cacheKey, "failed", error);
191
+ }
192
+ },
193
+ async refreshTags() {
194
+ // For Redis, tags are stored in a hash and don't need periodic refresh
195
+ // unless you're syncing with an external tags service
196
+ log?.("refreshTags", "no-op for Redis cache");
197
+ },
198
+ async getExpiration(tags) {
199
+ try {
200
+ const expirations = await Promise.all(tags.map(async (tag) => {
201
+ const data = await redis.hGet(getTagKey(tag), "expired");
202
+ return data ? Number.parseInt(data, 10) : 0;
203
+ }));
204
+ const expiration = Math.max(...expirations, 0);
205
+ log?.("getExpiration", { tags, expiration });
206
+ return expiration;
207
+ }
208
+ catch (error) {
209
+ log?.("getExpiration", "error", error);
210
+ return 0;
211
+ }
212
+ },
213
+ async updateTags(tags, durations) {
214
+ const now = Date.now();
215
+ try {
216
+ log?.("updateTags", { tags, timestamp: now, durations });
217
+ await Promise.all(tags.map(async (tag) => {
218
+ const key = getTagKey(tag);
219
+ if (durations) {
220
+ // Mark as stale immediately
221
+ await redis.hSet(key, "stale", now.toString());
222
+ // Set expiration if provided
223
+ if (durations.expire !== undefined) {
224
+ const expired = now + durations.expire * 1000;
225
+ await redis.hSet(key, "expired", expired.toString());
226
+ }
227
+ }
228
+ else {
229
+ // Immediate expiration (default behavior)
230
+ await redis.hSet(key, "expired", now.toString());
231
+ }
232
+ }));
233
+ }
234
+ catch (error) {
235
+ log?.("updateTags", "error", error);
236
+ }
237
+ },
238
+ };
239
+ }
240
+ //# sourceMappingURL=redis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis.js","sourceRoot":"","sources":["../../src/data-cache/redis.ts"],"names":[],"mappings":"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"}