@pantheon-systems/nextjs-cache-handler 0.1.0 → 0.2.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 +110 -0
- package/dist/handlers/base.d.ts +9 -0
- package/dist/handlers/base.d.ts.map +1 -1
- package/dist/handlers/base.js +22 -0
- package/dist/handlers/base.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/dist/middleware/index.d.ts +2 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +2 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/surrogate-key.d.ts +29 -0
- package/dist/middleware/surrogate-key.d.ts.map +1 -0
- package/dist/middleware/surrogate-key.js +66 -0
- package/dist/middleware/surrogate-key.js.map +1 -0
- package/dist/use-cache/file-handler.d.ts +71 -0
- package/dist/use-cache/file-handler.d.ts.map +1 -0
- package/dist/use-cache/file-handler.js +366 -0
- package/dist/use-cache/file-handler.js.map +1 -0
- package/dist/use-cache/gcs-handler.d.ts +64 -0
- package/dist/use-cache/gcs-handler.d.ts.map +1 -0
- package/dist/use-cache/gcs-handler.js +406 -0
- package/dist/use-cache/gcs-handler.js.map +1 -0
- package/dist/use-cache/index.d.ts +58 -0
- package/dist/use-cache/index.d.ts.map +1 -0
- package/dist/use-cache/index.js +87 -0
- package/dist/use-cache/index.js.map +1 -0
- package/dist/use-cache/stream-serialization.d.ts +34 -0
- package/dist/use-cache/stream-serialization.d.ts.map +1 -0
- package/dist/use-cache/stream-serialization.js +96 -0
- package/dist/use-cache/stream-serialization.js.map +1 -0
- package/dist/use-cache/types.d.ts +202 -0
- package/dist/use-cache/types.d.ts.map +1 -0
- package/dist/use-cache/types.js +9 -0
- package/dist/use-cache/types.js.map +1 -0
- package/dist/utils/cache-tag-context.d.ts +66 -0
- package/dist/utils/cache-tag-context.d.ts.map +1 -0
- package/dist/utils/cache-tag-context.js +126 -0
- package/dist/utils/cache-tag-context.js.map +1 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +3 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/request-context.d.ts +28 -0
- package/dist/utils/request-context.d.ts.map +1 -0
- package/dist/utils/request-context.js +54 -0
- package/dist/utils/request-context.js.map +1 -0
- package/dist/utils/request-context.test.d.ts +2 -0
- package/dist/utils/request-context.test.d.ts.map +1 -0
- package/dist/utils/request-context.test.js +65 -0
- package/dist/utils/request-context.test.js.map +1 -0
- package/dist/utils/with-surrogate-key.d.ts +32 -0
- package/dist/utils/with-surrogate-key.d.ts.map +1 -0
- package/dist/utils/with-surrogate-key.js +113 -0
- package/dist/utils/with-surrogate-key.js.map +1 -0
- package/package.json +15 -1
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
import { Storage } from '@google-cloud/storage';
|
|
2
|
+
import { serializeUseCacheEntry, deserializeUseCacheEntry, } from './stream-serialization.js';
|
|
3
|
+
import { createLogger } from '../utils/logger.js';
|
|
4
|
+
import { createEdgeCacheClearer } from '../edge/edge-cache-clear.js';
|
|
5
|
+
const log = createLogger('UseCacheGcsHandler');
|
|
6
|
+
/**
|
|
7
|
+
* Next.js internal prefix for path-based cache tags.
|
|
8
|
+
* When revalidatePath('/api/foo') is called, Next.js internally calls
|
|
9
|
+
* revalidateTag('_N_T_/api/foo'). This prefix identifies path tags.
|
|
10
|
+
*/
|
|
11
|
+
const NEXTJS_PATH_TAG_PREFIX = '_N_T_';
|
|
12
|
+
/**
|
|
13
|
+
* Symbol used to access CacheTagContext from globalThis.
|
|
14
|
+
* This matches the Symbol.for pattern used by Next.js for @next/request-context.
|
|
15
|
+
*/
|
|
16
|
+
const CACHE_TAG_CONTEXT_SYMBOL = Symbol.for('@nextjs-cache-handler/tag-context');
|
|
17
|
+
/**
|
|
18
|
+
* Symbol used to expose registerPathTags on globalThis.
|
|
19
|
+
* This allows withSurrogateKey (which knows the request path and captured tags)
|
|
20
|
+
* to register path→surrogate-key mappings without a direct module import.
|
|
21
|
+
*/
|
|
22
|
+
const PATH_TAGS_REGISTRY_SYMBOL = Symbol.for('@nextjs-cache-handler/path-tags-registry');
|
|
23
|
+
/**
|
|
24
|
+
* Access the CacheTagContext via globalThis using Symbol.for.
|
|
25
|
+
* This allows cross-context access without direct module imports.
|
|
26
|
+
*/
|
|
27
|
+
function getCacheTagContext() {
|
|
28
|
+
const accessor = globalThis[CACHE_TAG_CONTEXT_SYMBOL];
|
|
29
|
+
return accessor?.get();
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Add tags to the CacheTagContext if available.
|
|
33
|
+
* Falls back to global store if CacheTagContext is not active.
|
|
34
|
+
*/
|
|
35
|
+
function captureTags(tags) {
|
|
36
|
+
if (tags.length === 0) {
|
|
37
|
+
return { captured: false, source: 'none' };
|
|
38
|
+
}
|
|
39
|
+
// Primary: Try CacheTagContext (Symbol.for pattern)
|
|
40
|
+
const context = getCacheTagContext();
|
|
41
|
+
if (context) {
|
|
42
|
+
context.tags.push(...tags);
|
|
43
|
+
log.debug(`Captured ${tags.length} tags via CacheTagContext: ${tags.join(', ')}`);
|
|
44
|
+
return { captured: true, source: 'CacheTagContext' };
|
|
45
|
+
}
|
|
46
|
+
// Fallback: Use global store (for environments where Symbol.for doesn't propagate)
|
|
47
|
+
let globalTags = globalThis.__pantheonSurrogateKeyTags;
|
|
48
|
+
if (!globalTags) {
|
|
49
|
+
globalTags = [];
|
|
50
|
+
globalThis.__pantheonSurrogateKeyTags = globalTags;
|
|
51
|
+
}
|
|
52
|
+
globalTags.push(...tags);
|
|
53
|
+
log.debug(`Captured ${tags.length} tags via globalStore fallback: ${tags.join(', ')}`);
|
|
54
|
+
return { captured: true, source: 'globalStore' };
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Google Cloud Storage cache handler for Next.js 16 'use cache' directive.
|
|
58
|
+
* Implements the cacheHandlers (plural) interface.
|
|
59
|
+
*
|
|
60
|
+
* Suitable for:
|
|
61
|
+
* - Production/Pantheon environments
|
|
62
|
+
* - Multi-instance deployments requiring shared cache
|
|
63
|
+
*/
|
|
64
|
+
export class UseCacheGcsHandler {
|
|
65
|
+
constructor() {
|
|
66
|
+
this.tagTimestamps = new Map();
|
|
67
|
+
this.pathToSurrogateKeys = new Map();
|
|
68
|
+
this.initialized = false;
|
|
69
|
+
this.initPromise = null;
|
|
70
|
+
const bucketName = process.env.CACHE_BUCKET;
|
|
71
|
+
if (!bucketName) {
|
|
72
|
+
throw new Error('CACHE_BUCKET environment variable is required for GCS cache handler');
|
|
73
|
+
}
|
|
74
|
+
const storage = new Storage();
|
|
75
|
+
this.bucket = storage.bucket(bucketName);
|
|
76
|
+
this.cachePrefix = 'use-cache/';
|
|
77
|
+
this.tagsKey = `${this.cachePrefix}_tags.json`;
|
|
78
|
+
this.edgeCacheClearer = createEdgeCacheClearer();
|
|
79
|
+
// Expose registerPathTags on globalThis so withSurrogateKey can register
|
|
80
|
+
// path→surrogate-key mappings without direct module coupling
|
|
81
|
+
globalThis[PATH_TAGS_REGISTRY_SYMBOL] =
|
|
82
|
+
(path, tags) => this.registerPathTags(path, tags);
|
|
83
|
+
// Initialize asynchronously but track the promise
|
|
84
|
+
this.initPromise = this.initialize().catch(() => { });
|
|
85
|
+
log.info('Initialized GCS use-cache handler');
|
|
86
|
+
}
|
|
87
|
+
async initialize() {
|
|
88
|
+
if (this.initialized)
|
|
89
|
+
return;
|
|
90
|
+
await this.loadTagTimestamps();
|
|
91
|
+
this.initialized = true;
|
|
92
|
+
}
|
|
93
|
+
async ensureInitialized() {
|
|
94
|
+
if (this.initPromise) {
|
|
95
|
+
await this.initPromise;
|
|
96
|
+
this.initPromise = null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async loadTagTimestamps() {
|
|
100
|
+
try {
|
|
101
|
+
const file = this.bucket.file(this.tagsKey);
|
|
102
|
+
const [exists] = await file.exists();
|
|
103
|
+
if (!exists) {
|
|
104
|
+
// Only reset if not already populated by local operations
|
|
105
|
+
if (this.tagTimestamps.size === 0) {
|
|
106
|
+
this.tagTimestamps = new Map();
|
|
107
|
+
}
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const [data] = await file.download();
|
|
111
|
+
const parsed = JSON.parse(data.toString());
|
|
112
|
+
// Merge with existing in-memory state (local updates take precedence)
|
|
113
|
+
const loadedTimestamps = new Map(Object.entries(parsed));
|
|
114
|
+
for (const [tag, timestamp] of loadedTimestamps) {
|
|
115
|
+
const existing = this.tagTimestamps.get(tag);
|
|
116
|
+
if (!existing || timestamp > existing) {
|
|
117
|
+
this.tagTimestamps.set(tag, timestamp);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
log.warn('Error loading tag timestamps:', error);
|
|
123
|
+
// Don't reset - keep existing in-memory state
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async saveTagTimestamps() {
|
|
127
|
+
try {
|
|
128
|
+
const obj = Object.fromEntries(this.tagTimestamps);
|
|
129
|
+
const file = this.bucket.file(this.tagsKey);
|
|
130
|
+
await file.save(JSON.stringify(obj, null, 2), {
|
|
131
|
+
metadata: { contentType: 'application/json' },
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
log.error('Error saving tag timestamps:', error);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
getCacheKey(cacheKey) {
|
|
139
|
+
// Sanitize cache key for GCS object naming
|
|
140
|
+
const safeKey = cacheKey.replace(/[^a-zA-Z0-9-]/g, '_');
|
|
141
|
+
return `${this.cachePrefix}${safeKey}.json`;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Check if an entry is expired based on revalidate time.
|
|
145
|
+
*/
|
|
146
|
+
isExpired(entry) {
|
|
147
|
+
const now = Date.now();
|
|
148
|
+
const age = now - entry.timestamp;
|
|
149
|
+
const revalidateMs = entry.revalidate * 1000;
|
|
150
|
+
// Entry is expired if it's older than revalidate time
|
|
151
|
+
if (age > revalidateMs) {
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
// Also check if any of the entry's tags have been invalidated
|
|
155
|
+
for (const tag of entry.tags) {
|
|
156
|
+
const tagTimestamp = this.tagTimestamps.get(tag);
|
|
157
|
+
if (tagTimestamp && tagTimestamp > entry.timestamp) {
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Retrieve a cache entry.
|
|
165
|
+
*/
|
|
166
|
+
async get(cacheKey, softTags) {
|
|
167
|
+
log.debug(`GET: ${cacheKey}`);
|
|
168
|
+
try {
|
|
169
|
+
const gcsKey = this.getCacheKey(cacheKey);
|
|
170
|
+
const file = this.bucket.file(gcsKey);
|
|
171
|
+
const [exists] = await file.exists();
|
|
172
|
+
if (!exists) {
|
|
173
|
+
log.debug(`MISS: ${cacheKey} (not found)`);
|
|
174
|
+
return undefined;
|
|
175
|
+
}
|
|
176
|
+
const [data] = await file.download();
|
|
177
|
+
const stored = JSON.parse(data.toString());
|
|
178
|
+
const entry = deserializeUseCacheEntry(stored);
|
|
179
|
+
// Check expiration
|
|
180
|
+
if (this.isExpired(entry)) {
|
|
181
|
+
log.debug(`MISS: ${cacheKey} (expired)`);
|
|
182
|
+
// Optionally delete expired entry
|
|
183
|
+
try {
|
|
184
|
+
await file.delete();
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
// Ignore deletion errors
|
|
188
|
+
}
|
|
189
|
+
return undefined;
|
|
190
|
+
}
|
|
191
|
+
log.debug(`HIT: ${cacheKey}`);
|
|
192
|
+
// Capture tags for Surrogate-Key header propagation
|
|
193
|
+
// Uses Symbol.for pattern to access PantheonContext across async boundaries
|
|
194
|
+
const storedTags = entry.tags ?? [];
|
|
195
|
+
const { captured, source } = captureTags(storedTags);
|
|
196
|
+
if (captured) {
|
|
197
|
+
log.debug(`HIT ${cacheKey}: Captured ${storedTags.length} tags via ${source}`);
|
|
198
|
+
}
|
|
199
|
+
else if (storedTags.length === 0) {
|
|
200
|
+
// TODO: Remove this diagnostic logging once Next.js fixes the empty tags bug
|
|
201
|
+
// See: https://github.com/vercel/next.js/issues/78864
|
|
202
|
+
log.info(`HIT ${cacheKey}: No tags to capture (Next.js empty tags bug)`);
|
|
203
|
+
}
|
|
204
|
+
return entry;
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
log.error(`Error reading cache for key ${cacheKey}:`, error);
|
|
208
|
+
return undefined;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Store a cache entry.
|
|
213
|
+
* CRITICAL: Must await pendingEntry before storing.
|
|
214
|
+
*/
|
|
215
|
+
async set(cacheKey, pendingEntry) {
|
|
216
|
+
log.debug(`SET: ${cacheKey}`);
|
|
217
|
+
try {
|
|
218
|
+
// CRITICAL: Await the pending entry
|
|
219
|
+
const entry = await pendingEntry;
|
|
220
|
+
// TODO: Remove this diagnostic logging once Next.js fixes the empty tags bug
|
|
221
|
+
// Diagnostic logging for empty tags issue
|
|
222
|
+
// See: https://github.com/vercel/next.js/issues/78864
|
|
223
|
+
// See: docs/known-issues-nextjs16.md
|
|
224
|
+
const tagsLength = entry.tags?.length ?? 0;
|
|
225
|
+
if (tagsLength === 0) {
|
|
226
|
+
// Use info level so this is gated behind CACHE_DEBUG
|
|
227
|
+
log.info(`[EMPTY_TAGS_BUG] SET ${cacheKey}: Next.js passed empty tags array. ` +
|
|
228
|
+
`This is a known Next.js bug - cacheTag() values are not propagated to cacheHandlers.set(). ` +
|
|
229
|
+
`See: https://github.com/vercel/next.js/issues/78864`);
|
|
230
|
+
}
|
|
231
|
+
log.info(`SET entry structure for ${cacheKey}:`, {
|
|
232
|
+
hasTags: !!entry.tags,
|
|
233
|
+
tags: entry.tags,
|
|
234
|
+
tagsLength,
|
|
235
|
+
stale: entry.stale,
|
|
236
|
+
revalidate: entry.revalidate,
|
|
237
|
+
expire: entry.expire,
|
|
238
|
+
timestamp: entry.timestamp,
|
|
239
|
+
hasValue: !!entry.value,
|
|
240
|
+
entryKeys: Object.keys(entry),
|
|
241
|
+
});
|
|
242
|
+
const serialized = await serializeUseCacheEntry(entry);
|
|
243
|
+
const gcsKey = this.getCacheKey(cacheKey);
|
|
244
|
+
const file = this.bucket.file(gcsKey);
|
|
245
|
+
await file.save(JSON.stringify(serialized, null, 2), {
|
|
246
|
+
metadata: { contentType: 'application/json' },
|
|
247
|
+
});
|
|
248
|
+
log.debug(`Cached ${cacheKey}`);
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
log.error(`Error setting cache for key ${cacheKey}:`, error);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Synchronize tag state from external source.
|
|
256
|
+
* Reloads tag timestamps from GCS.
|
|
257
|
+
*/
|
|
258
|
+
async refreshTags() {
|
|
259
|
+
log.debug('REFRESH TAGS');
|
|
260
|
+
await this.loadTagTimestamps();
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Return maximum revalidation timestamp for given tags.
|
|
264
|
+
*/
|
|
265
|
+
async getExpiration(tags) {
|
|
266
|
+
let maxTimestamp = 0;
|
|
267
|
+
for (const tag of tags) {
|
|
268
|
+
const timestamp = this.tagTimestamps.get(tag) ?? 0;
|
|
269
|
+
if (timestamp > maxTimestamp) {
|
|
270
|
+
maxTimestamp = timestamp;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
log.debug(`GET EXPIRATION for [${tags.join(', ')}]: ${maxTimestamp}`);
|
|
274
|
+
return maxTimestamp;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Register the surrogate keys (explicit cacheTag values) associated with a path.
|
|
278
|
+
* Called by withSurrogateKey when tags are captured during a cache HIT,
|
|
279
|
+
* enabling revalidatePath to resolve the correct CDN surrogate keys.
|
|
280
|
+
*/
|
|
281
|
+
registerPathTags(path, surrogateKeys) {
|
|
282
|
+
this.pathToSurrogateKeys.set(path, surrogateKeys);
|
|
283
|
+
log.debug(`Registered path tags: ${path} → [${surrogateKeys.join(', ')}]`);
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Invalidate cache entries with matching tags.
|
|
287
|
+
*/
|
|
288
|
+
async updateTags(tags, durations) {
|
|
289
|
+
log.debug(`UPDATE TAGS: [${tags.join(', ')}]`);
|
|
290
|
+
if (tags.length === 0) {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
const now = Date.now();
|
|
294
|
+
for (const tag of tags) {
|
|
295
|
+
this.tagTimestamps.set(tag, now);
|
|
296
|
+
}
|
|
297
|
+
await this.saveTagTimestamps();
|
|
298
|
+
// Clear edge cache if configured
|
|
299
|
+
if (this.edgeCacheClearer) {
|
|
300
|
+
// Separate explicit tags from _N_T_ path tags
|
|
301
|
+
const explicitTags = [];
|
|
302
|
+
const pathTags = [];
|
|
303
|
+
for (const tag of tags) {
|
|
304
|
+
if (tag.startsWith(NEXTJS_PATH_TAG_PREFIX)) {
|
|
305
|
+
pathTags.push(tag);
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
explicitTags.push(tag);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
// Purge explicit tags as surrogate keys (these match CDN Surrogate-Key headers directly)
|
|
312
|
+
if (explicitTags.length > 0) {
|
|
313
|
+
this.edgeCacheClearer.clearKeysInBackground(explicitTags, `use-cache tag invalidation: ${explicitTags.join(', ')}`);
|
|
314
|
+
}
|
|
315
|
+
// Resolve _N_T_ path tags to surrogate keys via the registered mapping
|
|
316
|
+
if (pathTags.length > 0) {
|
|
317
|
+
const resolvedKeys = [];
|
|
318
|
+
const unresolvedPaths = [];
|
|
319
|
+
for (const pathTag of pathTags) {
|
|
320
|
+
const path = pathTag.substring(NEXTJS_PATH_TAG_PREFIX.length);
|
|
321
|
+
const surrogateKeys = this.pathToSurrogateKeys.get(path);
|
|
322
|
+
if (surrogateKeys && surrogateKeys.length > 0) {
|
|
323
|
+
resolvedKeys.push(...surrogateKeys);
|
|
324
|
+
log.debug(`Resolved path tag ${pathTag} → surrogate keys: [${surrogateKeys.join(', ')}]`);
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
unresolvedPaths.push(path);
|
|
328
|
+
log.debug(`No surrogate key mapping for path tag ${pathTag}, falling back to path purge`);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
// Purge resolved surrogate keys
|
|
332
|
+
if (resolvedKeys.length > 0) {
|
|
333
|
+
this.edgeCacheClearer.clearKeysInBackground(resolvedKeys, `path revalidation (resolved): ${resolvedKeys.join(', ')}`);
|
|
334
|
+
}
|
|
335
|
+
// Fallback: attempt path-based purge for unmapped paths
|
|
336
|
+
if (unresolvedPaths.length > 0) {
|
|
337
|
+
this.edgeCacheClearer.clearPathsInBackground(unresolvedPaths, `path revalidation (fallback): ${unresolvedPaths.join(', ')}`);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Get cache statistics for the use-cache entries in GCS.
|
|
344
|
+
* Returns information about all valid (non-expired) cache entries.
|
|
345
|
+
*/
|
|
346
|
+
async getStats() {
|
|
347
|
+
log.debug('GET STATS');
|
|
348
|
+
const entries = [];
|
|
349
|
+
const keys = [];
|
|
350
|
+
try {
|
|
351
|
+
await this.ensureInitialized();
|
|
352
|
+
// List all files in the use-cache prefix
|
|
353
|
+
const [files] = await this.bucket.getFiles({ prefix: this.cachePrefix });
|
|
354
|
+
for (const file of files) {
|
|
355
|
+
// Skip tags file
|
|
356
|
+
if (file.name === this.tagsKey) {
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
// Only process .json files
|
|
360
|
+
if (!file.name.endsWith('.json')) {
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
try {
|
|
364
|
+
const [data] = await file.download();
|
|
365
|
+
const stored = JSON.parse(data.toString());
|
|
366
|
+
// Extract key from filename (remove prefix and .json suffix)
|
|
367
|
+
const key = file.name.replace(this.cachePrefix, '').replace('.json', '');
|
|
368
|
+
// Deserialize to check expiration
|
|
369
|
+
const entry = deserializeUseCacheEntry(stored);
|
|
370
|
+
// Skip expired entries
|
|
371
|
+
if (this.isExpired(entry)) {
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
// Get file metadata for size
|
|
375
|
+
const [metadata] = await file.getMetadata();
|
|
376
|
+
const entryInfo = {
|
|
377
|
+
key,
|
|
378
|
+
tags: stored.tags || [],
|
|
379
|
+
type: 'use-cache',
|
|
380
|
+
lastModified: new Date(entry.timestamp).toISOString(),
|
|
381
|
+
size: Number(metadata.size) || 0,
|
|
382
|
+
revalidate: entry.revalidate,
|
|
383
|
+
stale: entry.stale,
|
|
384
|
+
expire: entry.expire,
|
|
385
|
+
};
|
|
386
|
+
entries.push(entryInfo);
|
|
387
|
+
keys.push(key);
|
|
388
|
+
}
|
|
389
|
+
catch (error) {
|
|
390
|
+
log.warn(`Error reading cache file ${file.name}:`, error);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
catch (error) {
|
|
395
|
+
log.error('Error getting cache stats:', error);
|
|
396
|
+
}
|
|
397
|
+
log.debug(`Found ${entries.length} valid cache entries`);
|
|
398
|
+
return {
|
|
399
|
+
size: entries.length,
|
|
400
|
+
entries,
|
|
401
|
+
keys,
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
export default UseCacheGcsHandler;
|
|
406
|
+
//# sourceMappingURL=gcs-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gcs-handler.js","sourceRoot":"","sources":["../../src/use-cache/gcs-handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Next.js 16 'use cache' Handler Module
|
|
3
|
+
*
|
|
4
|
+
* This module provides cache handlers for the new `cacheHandlers` (plural)
|
|
5
|
+
* configuration in Next.js 16, which supports the `'use cache'` directive.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```javascript
|
|
9
|
+
* // next.config.mjs
|
|
10
|
+
* import { createUseCacheHandler } from '@pantheon-systems/nextjs-cache-handler/use-cache';
|
|
11
|
+
*
|
|
12
|
+
* const nextConfig = {
|
|
13
|
+
* // Existing handler for ISR, routes, fetch cache
|
|
14
|
+
* cacheHandler: require.resolve('./cache-handler.mjs'),
|
|
15
|
+
*
|
|
16
|
+
* // NEW handler for 'use cache' directive
|
|
17
|
+
* cacheHandlers: {
|
|
18
|
+
* default: require.resolve('./use-cache-handler.mjs'),
|
|
19
|
+
* },
|
|
20
|
+
*
|
|
21
|
+
* cacheComponents: true,
|
|
22
|
+
* };
|
|
23
|
+
*
|
|
24
|
+
* export default nextConfig;
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
import type { UseCacheHandlerConfig } from './types.js';
|
|
28
|
+
import { UseCacheFileHandler } from './file-handler.js';
|
|
29
|
+
import { UseCacheGcsHandler } from './gcs-handler.js';
|
|
30
|
+
/**
|
|
31
|
+
* Factory function to create a use cache handler based on configuration.
|
|
32
|
+
*
|
|
33
|
+
* @param config - Configuration options for the cache handler
|
|
34
|
+
* @returns A cache handler class that implements the cacheHandlers interface
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* // In your use-cache-handler.ts file:
|
|
39
|
+
* import { createUseCacheHandler } from '@pantheon-systems/nextjs-cache-handler/use-cache';
|
|
40
|
+
*
|
|
41
|
+
* const UseCacheHandler = createUseCacheHandler({
|
|
42
|
+
* type: 'auto', // Auto-detect: GCS if CACHE_BUCKET exists, else file-based
|
|
43
|
+
* });
|
|
44
|
+
*
|
|
45
|
+
* export default UseCacheHandler;
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export declare function createUseCacheHandler(config?: UseCacheHandlerConfig): typeof UseCacheFileHandler | typeof UseCacheGcsHandler;
|
|
49
|
+
export { UseCacheFileHandler } from './file-handler.js';
|
|
50
|
+
export { UseCacheGcsHandler } from './gcs-handler.js';
|
|
51
|
+
export type { UseCacheEntry, UseCacheHandler, UseCacheHandlerConfig, SerializedUseCacheEntry, UseCacheStats, UseCacheEntryInfo, } from './types.js';
|
|
52
|
+
/**
|
|
53
|
+
* Get cache statistics for use-cache entries.
|
|
54
|
+
* Automatically detects whether to use file-based or GCS cache stats.
|
|
55
|
+
*/
|
|
56
|
+
export declare function getUseCacheStats(): Promise<import('./types.js').UseCacheStats>;
|
|
57
|
+
export { streamToBytes, bytesToStream, serializeUseCacheEntry, deserializeUseCacheEntry, } from './stream-serialization.js';
|
|
58
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/use-cache/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEtD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,CAAC,EAAE,qBAAqB,GAC7B,OAAO,mBAAmB,GAAG,OAAO,kBAAkB,CAQxD;AAkBD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAMtD,YAAY,EACV,aAAa,EACb,eAAe,EACf,qBAAqB,EACrB,uBAAuB,EACvB,aAAa,EACb,iBAAiB,GAClB,MAAM,YAAY,CAAC;AAMpB;;;GAGG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,OAAO,YAAY,EAAE,aAAa,CAAC,CAOpF;AAMD,OAAO,EACL,aAAa,EACb,aAAa,EACb,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,2BAA2B,CAAC"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Next.js 16 'use cache' Handler Module
|
|
3
|
+
*
|
|
4
|
+
* This module provides cache handlers for the new `cacheHandlers` (plural)
|
|
5
|
+
* configuration in Next.js 16, which supports the `'use cache'` directive.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```javascript
|
|
9
|
+
* // next.config.mjs
|
|
10
|
+
* import { createUseCacheHandler } from '@pantheon-systems/nextjs-cache-handler/use-cache';
|
|
11
|
+
*
|
|
12
|
+
* const nextConfig = {
|
|
13
|
+
* // Existing handler for ISR, routes, fetch cache
|
|
14
|
+
* cacheHandler: require.resolve('./cache-handler.mjs'),
|
|
15
|
+
*
|
|
16
|
+
* // NEW handler for 'use cache' directive
|
|
17
|
+
* cacheHandlers: {
|
|
18
|
+
* default: require.resolve('./use-cache-handler.mjs'),
|
|
19
|
+
* },
|
|
20
|
+
*
|
|
21
|
+
* cacheComponents: true,
|
|
22
|
+
* };
|
|
23
|
+
*
|
|
24
|
+
* export default nextConfig;
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
import { UseCacheFileHandler } from './file-handler.js';
|
|
28
|
+
import { UseCacheGcsHandler } from './gcs-handler.js';
|
|
29
|
+
/**
|
|
30
|
+
* Factory function to create a use cache handler based on configuration.
|
|
31
|
+
*
|
|
32
|
+
* @param config - Configuration options for the cache handler
|
|
33
|
+
* @returns A cache handler class that implements the cacheHandlers interface
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```typescript
|
|
37
|
+
* // In your use-cache-handler.ts file:
|
|
38
|
+
* import { createUseCacheHandler } from '@pantheon-systems/nextjs-cache-handler/use-cache';
|
|
39
|
+
*
|
|
40
|
+
* const UseCacheHandler = createUseCacheHandler({
|
|
41
|
+
* type: 'auto', // Auto-detect: GCS if CACHE_BUCKET exists, else file-based
|
|
42
|
+
* });
|
|
43
|
+
*
|
|
44
|
+
* export default UseCacheHandler;
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export function createUseCacheHandler(config) {
|
|
48
|
+
const type = config?.type ?? 'auto';
|
|
49
|
+
if (shouldUseGcs(type)) {
|
|
50
|
+
return UseCacheGcsHandler;
|
|
51
|
+
}
|
|
52
|
+
return UseCacheFileHandler;
|
|
53
|
+
}
|
|
54
|
+
function shouldUseGcs(type) {
|
|
55
|
+
if (type === 'gcs') {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
if (type === 'auto') {
|
|
59
|
+
return !!process.env.CACHE_BUCKET;
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
// ============================================================================
|
|
64
|
+
// Handler exports
|
|
65
|
+
// ============================================================================
|
|
66
|
+
export { UseCacheFileHandler } from './file-handler.js';
|
|
67
|
+
export { UseCacheGcsHandler } from './gcs-handler.js';
|
|
68
|
+
// ============================================================================
|
|
69
|
+
// Stats function
|
|
70
|
+
// ============================================================================
|
|
71
|
+
/**
|
|
72
|
+
* Get cache statistics for use-cache entries.
|
|
73
|
+
* Automatically detects whether to use file-based or GCS cache stats.
|
|
74
|
+
*/
|
|
75
|
+
export async function getUseCacheStats() {
|
|
76
|
+
if (process.env.CACHE_BUCKET) {
|
|
77
|
+
const handler = new UseCacheGcsHandler();
|
|
78
|
+
return handler.getStats();
|
|
79
|
+
}
|
|
80
|
+
const handler = new UseCacheFileHandler();
|
|
81
|
+
return handler.getStats();
|
|
82
|
+
}
|
|
83
|
+
// ============================================================================
|
|
84
|
+
// Utility exports
|
|
85
|
+
// ============================================================================
|
|
86
|
+
export { streamToBytes, bytesToStream, serializeUseCacheEntry, deserializeUseCacheEntry, } from './stream-serialization.js';
|
|
87
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/use-cache/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAGH,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEtD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,qBAAqB,CACnC,MAA8B;IAE9B,MAAM,IAAI,GAAG,MAAM,EAAE,IAAI,IAAI,MAAM,CAAC;IAEpC,IAAI,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;QACvB,OAAO,kBAAkB,CAAC;IAC5B,CAAC;IAED,OAAO,mBAAmB,CAAC;AAC7B,CAAC;AAED,SAAS,YAAY,CAAC,IAA6B;IACjD,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QACnB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACpB,OAAO,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IACpC,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,+EAA+E;AAC/E,kBAAkB;AAClB,+EAA+E;AAE/E,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAetD,+EAA+E;AAC/E,iBAAiB;AACjB,+EAA+E;AAE/E;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,IAAI,kBAAkB,EAAE,CAAC;QACzC,OAAO,OAAO,CAAC,QAAQ,EAAE,CAAC;IAC5B,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,mBAAmB,EAAE,CAAC;IAC1C,OAAO,OAAO,CAAC,QAAQ,EAAE,CAAC;AAC5B,CAAC;AAED,+EAA+E;AAC/E,kBAAkB;AAClB,+EAA+E;AAE/E,OAAO,EACL,aAAa,EACb,aAAa,EACb,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,2BAA2B,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { UseCacheEntry, SerializedUseCacheEntry } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Convert a ReadableStream<Uint8Array> to a Uint8Array.
|
|
4
|
+
* Consumes the entire stream and concatenates all chunks.
|
|
5
|
+
*
|
|
6
|
+
* @param stream - The stream to consume
|
|
7
|
+
* @returns A single Uint8Array containing all the stream data
|
|
8
|
+
*/
|
|
9
|
+
export declare function streamToBytes(stream: ReadableStream<Uint8Array>): Promise<Uint8Array>;
|
|
10
|
+
/**
|
|
11
|
+
* Convert a Uint8Array to a ReadableStream<Uint8Array>.
|
|
12
|
+
* Creates a stream that emits the entire array as a single chunk.
|
|
13
|
+
*
|
|
14
|
+
* @param bytes - The bytes to convert to a stream
|
|
15
|
+
* @returns A ReadableStream that emits the bytes
|
|
16
|
+
*/
|
|
17
|
+
export declare function bytesToStream(bytes: Uint8Array): ReadableStream<Uint8Array>;
|
|
18
|
+
/**
|
|
19
|
+
* Serialize a UseCacheEntry for persistent storage.
|
|
20
|
+
* Converts the ReadableStream value to a base64-encoded string.
|
|
21
|
+
*
|
|
22
|
+
* @param entry - The cache entry to serialize
|
|
23
|
+
* @returns A serialized entry suitable for JSON storage
|
|
24
|
+
*/
|
|
25
|
+
export declare function serializeUseCacheEntry(entry: UseCacheEntry): Promise<SerializedUseCacheEntry>;
|
|
26
|
+
/**
|
|
27
|
+
* Deserialize a stored cache entry back to UseCacheEntry.
|
|
28
|
+
* Converts the base64-encoded value back to a ReadableStream.
|
|
29
|
+
*
|
|
30
|
+
* @param stored - The serialized entry from storage
|
|
31
|
+
* @returns A UseCacheEntry with a ReadableStream value
|
|
32
|
+
*/
|
|
33
|
+
export declare function deserializeUseCacheEntry(stored: SerializedUseCacheEntry): UseCacheEntry;
|
|
34
|
+
//# sourceMappingURL=stream-serialization.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-serialization.d.ts","sourceRoot":"","sources":["../../src/use-cache/stream-serialization.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAEzE;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC,CAgC3F;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,cAAc,CAAC,UAAU,CAAC,CAO3E;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAenG;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,uBAAuB,GAAG,aAAa,CAkBvF"}
|