@@ -3,7 +3,16 @@ import { existsDir, existsFile } from "./fs.ts";
3
3
import log from "./log.ts" ;
4
4
import util from "./util.ts" ;
5
5
6
- const memoryCache = new Map < string , [ Uint8Array , { headers : Record < string , string > } ] > ( ) ;
6
+ type Meta = {
7
+ url : string ;
8
+ headers : Record < string , string > ;
9
+ now : {
10
+ secs_since_epoch : number ;
11
+ nanos_since_epoch : number ;
12
+ } ;
13
+ } ;
14
+
15
+ const memoryCache = new Map < string , [ content : Uint8Array , meta : Meta ] > ( ) ;
7
16
8
17
/** fetch and cache remote contents */
9
18
export default async function cache (
@@ -27,20 +36,24 @@ export default async function cache(
27
36
if ( ! options ?. forceRefresh && ! isLocalhost ) {
28
37
if ( modulesCacheDir ) {
29
38
if ( await existsFile ( contentFilepath ) && await existsFile ( metaFilepath ) ) {
30
- const [ content , meta ] = await Promise . all ( [
39
+ const [ content , metaJSON ] = await Promise . all ( [
31
40
Deno . readFile ( contentFilepath ) ,
32
41
Deno . readTextFile ( metaFilepath ) ,
33
42
] ) ;
34
43
try {
35
- const { headers = { } } = JSON . parse ( meta ) ;
36
- return new Response ( content , { headers : { ...headers , "cache-hit" : "true" } } ) ;
44
+ const meta = JSON . parse ( metaJSON ) ;
45
+ if ( ! isExpired ( meta ) ) {
46
+ return new Response ( content , { headers : { ...meta . headers , "cache-hit" : "true" } } ) ;
47
+ }
37
48
} catch ( _e ) {
38
- return new Response ( content ) ;
49
+ log . debug ( `skip cache of ${ url } : invalid cache metadata file` ) ;
39
50
}
40
51
}
41
52
} else if ( memoryCache . has ( hashname ) ) {
42
- const [ content , { headers } ] = memoryCache . get ( hashname ) ! ;
43
- return new Response ( content , { headers : { ...headers , "cache-hit" : "true" } } ) ;
53
+ const [ content , meta ] = memoryCache . get ( hashname ) ! ;
54
+ if ( ! isExpired ( meta ) ) {
55
+ return new Response ( content , { headers : { ...meta . headers , "cache-hit" : "true" } } ) ;
56
+ }
44
57
}
45
58
}
46
59
@@ -64,27 +77,27 @@ export default async function cache(
64
77
if ( res . ok && ! isLocalhost ) {
65
78
const buffer = await res . arrayBuffer ( ) ;
66
79
const content = new Uint8Array ( buffer ) ;
67
- const headers : Record < string , string > = { } ;
80
+ const meta : Meta = {
81
+ url,
82
+ headers : { } ,
83
+ now : {
84
+ secs_since_epoch : Math . round ( Date . now ( ) / 1000 ) ,
85
+ nanos_since_epoch : 0 ,
86
+ } ,
87
+ } ;
68
88
res . headers . forEach ( ( val , key ) => {
69
- headers [ key ] = val ;
89
+ meta . headers [ key ] = val ;
70
90
} ) ;
71
91
if ( modulesCacheDir ) {
72
92
if ( ! ( await existsDir ( cacheDir ) ) ) {
73
93
await Deno . mkdir ( cacheDir , { recursive : true } ) ;
74
94
}
75
95
await Promise . all ( [
76
96
Deno . writeFile ( contentFilepath , content ) ,
77
- Deno . writeTextFile (
78
- metaFilepath ,
79
- JSON . stringify (
80
- { headers, url, now : { secs_since_epoch : Math . round ( Date . now ( ) / 1000 ) , nanos_since_epoch : 0 } } ,
81
- undefined ,
82
- 2 ,
83
- ) ,
84
- ) ,
97
+ Deno . writeTextFile ( metaFilepath , JSON . stringify ( meta , undefined , 2 ) ) ,
85
98
] ) ;
86
99
} else {
87
- memoryCache . set ( hashname , [ content , { headers } ] ) ;
100
+ memoryCache . set ( hashname , [ content , meta ] ) ;
88
101
}
89
102
return new Response ( content , { headers : res . headers } ) ;
90
103
}
@@ -94,3 +107,16 @@ export default async function cache(
94
107
95
108
return finalRes ;
96
109
}
110
+
111
+ function isExpired ( meta : Meta ) {
112
+ const cc = meta . headers [ "cache-control" ] ;
113
+ const dataCacheTtl = cc && cc . includes ( "max-age=" ) ? parseInt ( cc . split ( "max-age=" ) [ 1 ] ) : undefined ;
114
+ if ( dataCacheTtl ) {
115
+ const now = Date . now ( ) ;
116
+ const expireTime = ( meta . now . secs_since_epoch + dataCacheTtl ) * 1000 ;
117
+ if ( now > expireTime ) {
118
+ return true ;
119
+ }
120
+ }
121
+ return false ;
122
+ }
0 commit comments