@@ -61,12 +61,7 @@ export async function invalidateByTag(
6161 // Clean up tag metadata after successful deletion
6262 if ( metadataResponse && metadata [ validatedTag ] ) {
6363 delete metadata [ validatedTag ] ;
64- await cache . put (
65- METADATA_KEY ,
66- new Response ( JSON . stringify ( metadata ) , {
67- headers : { "Content-Type" : "application/json" } ,
68- } ) ,
69- ) ;
64+ await cache . put ( METADATA_KEY , Response . json ( metadata ) ) ;
7065 }
7166
7267 return deletedCount ;
@@ -161,12 +156,7 @@ export async function invalidateByPath(
161156 }
162157 }
163158
164- await cache . put (
165- METADATA_KEY ,
166- new Response ( JSON . stringify ( updatedMetadata ) , {
167- headers : { "Content-Type" : "application/json" } ,
168- } ) ,
169- ) ;
159+ await cache . put ( METADATA_KEY , Response . json ( updatedMetadata ) ) ;
170160 }
171161
172162 return deletedCount ;
@@ -336,10 +326,75 @@ export async function getCacheStats(
336326export async function regenerateCacheStats (
337327 options : InvalidationOptions = { } ,
338328) : Promise < { totalEntries : number ; entriesByTag : Record < string , number > } > {
339- // In Deno, we can't enumerate cache keys, so this function cannot work
340- // without the ability to list all cache entries. Return empty stats.
341- console . warn (
342- "regenerateCacheStats: Cannot enumerate cache keys in Deno environment" ,
343- ) ;
344- return { totalEntries : 0 , entriesByTag : { } } ;
329+ const cache = await getCache ( options ) ;
330+ interface CacheWithKeys extends Cache {
331+ keys ( ) : Promise < Request [ ] > ;
332+ }
333+ if ( ! ( "keys" in cache ) ) {
334+ console . warn (
335+ "regenerateCacheStats: cache.keys() not supported in this runtime; returning empty stats" ,
336+ ) ;
337+ return { totalEntries : 0 , entriesByTag : { } } ;
338+ }
339+
340+ let requests : Request [ ] = [ ] ;
341+ try {
342+ requests = await ( cache as unknown as CacheWithKeys ) . keys ( ) ;
343+ } catch ( err ) {
344+ console . warn ( "regenerateCacheStats: failed to enumerate cache keys" , err ) ;
345+ return { totalEntries : 0 , entriesByTag : { } } ;
346+ }
347+
348+ const metadata : Record < string , string [ ] > = { } ;
349+ const uniqueKeys = new Set < string > ( ) ;
350+
351+ for ( const req of requests ) {
352+ const url = req . url ;
353+ if ( url === METADATA_KEY ) {
354+ continue ; // skip old metadata entry
355+ }
356+ uniqueKeys . add ( url ) ;
357+
358+ let response : Response | undefined ;
359+ try {
360+ response = await cache . match ( req ) as Response | undefined ;
361+ } catch {
362+ continue ;
363+ }
364+ if ( ! response ) {
365+ continue ;
366+ }
367+ const tagHeader = response . headers . get ( "cache-tag" ) ;
368+ if ( ! tagHeader ) {
369+ continue ; // cannot reconstruct tags if they were stripped
370+ }
371+ const tags = parseCacheTags ( tagHeader ) ;
372+ for ( const tag of tags ) {
373+ if ( ! metadata [ tag ] ) {
374+ metadata [ tag ] = [ ] ;
375+ }
376+ metadata [ tag ] . push ( url ) ;
377+ }
378+ }
379+
380+ // Write rebuilt metadata (best-effort)
381+ try {
382+ await cache . put ( METADATA_KEY , Response . json ( metadata ) ) ;
383+ } catch ( err ) {
384+ console . warn (
385+ "regenerateCacheStats: failed to persist rebuilt metadata" ,
386+ err ,
387+ ) ;
388+ }
389+
390+ const entriesByTag : Record < string , number > = { } ;
391+ for ( const tag in metadata ) {
392+ const list = metadata [ tag ] ;
393+ if ( ! Array . isArray ( list ) ) {
394+ continue ;
395+ }
396+ entriesByTag [ tag ] = list . length ;
397+ }
398+
399+ return { totalEntries : uniqueKeys . size , entriesByTag } ;
345400}
0 commit comments