Skip to content

Commit 026a27b

Browse files
committed
feat: be possible to set staleTtl from data
1 parent 631768f commit 026a27b

File tree

3 files changed

+45
-29
lines changed

3 files changed

+45
-29
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
],
5656
"dependencies": {
5757
"@keyvhq/core": "~1.1.1",
58-
"@keyvhq/memoize": "~1.2.0",
58+
"@keyvhq/memoize": "~1.2.2",
5959
"compress-brotli": "~1.3.0",
6060
"debug-logfmt": "~1.0.4",
6161
"etag": "~1.8.1",

src/index.js

+17-8
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const Keyv = require('@keyvhq/core')
77
const assert = require('assert')
88
const getEtag = require('etag')
99

10-
const { hasQueryParameter, createSetHeaders } = require('./util')
10+
const { hasQueryParameter, setHeaders } = require('./util')
1111

1212
const cacheableResponse = ({
1313
bypassQueryParameter = 'force',
@@ -17,29 +17,36 @@ const cacheableResponse = ({
1717
getKey = require('./util').getKey,
1818
send,
1919
staleTtl: rawStaleTtl = 3600000,
20-
ttl: defaultTtl = 86400000,
20+
ttl: rawTtl = 86400000,
2121
...compressOpts
2222
} = {}) => {
2323
assert(get, '.get required')
2424
assert(send, '.send required')
2525

26-
const staleTtl = typeof rawStaleTtl === 'number' ? rawStaleTtl : undefined
26+
const isStaleEnabled = rawStaleTtl !== false
27+
28+
const staleTtl = isStaleEnabled
29+
? typeof rawStaleTtl === 'function'
30+
? rawStaleTtl
31+
: ({ staleTtl = rawStaleTtl } = {}) => staleTtl
32+
: undefined
33+
34+
const ttl =
35+
typeof rawTtl === 'function' ? rawTtl : ({ ttl = rawTtl } = {}) => ttl
2736

2837
const { serialize, compress, decompress } = createCompress({
2938
enable: enableCompression,
3039
...compressOpts
3140
})
3241

3342
const memoGet = memoize(get, cache, {
34-
ttl: ({ ttl = defaultTtl } = {}) => ttl,
43+
ttl,
3544
staleTtl,
3645
objectMode: true,
3746
key: opts => getKey(opts, { bypassQueryParameter }),
3847
value: compress
3948
})
4049

41-
const setHeaders = createSetHeaders({ staleTtl })
42-
4350
return async opts => {
4451
const { req, res } = opts
4552
const hasForce = hasQueryParameter(req, bypassQueryParameter)
@@ -50,10 +57,11 @@ const cacheableResponse = ({
5057
const isHit = !hasForce && !isExpired && hasValue
5158

5259
const {
53-
etag: cachedEtag,
54-
ttl = defaultTtl,
5560
createdAt = Date.now(),
5661
data = null,
62+
etag: cachedEtag,
63+
staleTtl = isStaleEnabled ? memoGet.staleTtl(result) : undefined,
64+
ttl = memoGet.ttl(result),
5765
...props
5866
} = result
5967

@@ -79,6 +87,7 @@ const cacheableResponse = ({
7987
isHit,
8088
isStale,
8189
ttl,
90+
staleTtl,
8291
hasForce
8392
})
8493

src/util.js

+27-20
Original file line numberDiff line numberDiff line change
@@ -28,29 +28,36 @@ const toSeconds = ms => Math.floor(ms / 1000)
2828
const getStatus = ({ isHit, isStale, hasForce }) =>
2929
isHit ? (isStale ? 'STALE' : 'HIT') : hasForce ? 'BYPASS' : 'MISS'
3030

31-
const createSetHeaders = ({ staleTtl }) => {
32-
return ({ res, createdAt, isHit, isStale, ttl, hasForce, etag }) => {
33-
// Specifies the maximum amount of time a resource
34-
// will be considered fresh in seconds
35-
const diff = hasForce ? 0 : createdAt + ttl - Date.now()
36-
const maxAge = toSeconds(diff)
37-
const revalidation = staleTtl ? toSeconds(staleTtl) : 0
38-
39-
let cacheControl = `public, must-revalidate, max-age=${maxAge}`
40-
41-
if (revalidation) {
42-
cacheControl = `${cacheControl}, stale-while-revalidate=${revalidation}, stale-if-error=${revalidation}`
43-
}
44-
45-
res.setHeader('Cache-Control', cacheControl)
46-
res.setHeader('X-Cache-Status', getStatus({ isHit, isStale, hasForce }))
47-
res.setHeader('X-Cache-Expired-At', prettyMs(diff))
48-
res.setHeader('ETag', etag)
31+
const setHeaders = ({
32+
createdAt,
33+
etag,
34+
hasForce,
35+
isHit,
36+
isStale,
37+
res,
38+
staleTtl,
39+
ttl
40+
}) => {
41+
// Specifies the maximum amount of time a resource
42+
// will be considered fresh in seconds
43+
const diff = hasForce ? 0 : createdAt + ttl - Date.now()
44+
const maxAge = toSeconds(diff)
45+
const revalidation = staleTtl ? toSeconds(staleTtl) : 0
46+
47+
let cacheControl = `public, must-revalidate, max-age=${maxAge}`
48+
49+
if (revalidation) {
50+
cacheControl = `${cacheControl}, stale-while-revalidate=${revalidation}, stale-if-error=${revalidation}`
4951
}
52+
53+
res.setHeader('Cache-Control', cacheControl)
54+
res.setHeader('X-Cache-Status', getStatus({ isHit, isStale, hasForce }))
55+
res.setHeader('X-Cache-Expired-At', prettyMs(diff))
56+
res.setHeader('ETag', etag)
5057
}
5158

5259
module.exports = {
53-
hasQueryParameter,
5460
getKey,
55-
createSetHeaders
61+
hasQueryParameter,
62+
setHeaders
5663
}

0 commit comments

Comments
 (0)