@fastify/caching 9.0.2 → 9.0.4

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.
@@ -2,12 +2,52 @@ version: 2
2
2
  updates:
3
3
  - package-ecosystem: "github-actions"
4
4
  directory: "/"
5
+ commit-message:
6
+ # Prefix all commit messages with "chore: "
7
+ prefix: "chore"
5
8
  schedule:
6
- interval: "monthly"
9
+ interval: "weekly"
10
+ cooldown:
11
+ default-days: 7
12
+ allow:
13
+ - dependency-name: "*"
14
+ update-types:
15
+ - "version-update:semver-major"
7
16
  open-pull-requests-limit: 10
8
17
 
9
18
  - package-ecosystem: "npm"
10
19
  directory: "/"
20
+ commit-message:
21
+ # Prefix all commit messages with "chore: "
22
+ prefix: "chore"
11
23
  schedule:
12
24
  interval: "weekly"
25
+ cooldown:
26
+ default-days: 7
27
+ versioning-strategy: "increase-if-necessary"
28
+ allow:
29
+ - dependency-name: "*"
30
+ update-types:
31
+ - "version-update:semver-major"
32
+ ignore:
33
+ # TODO: remove ignore until neostandard support ESLint 10
34
+ - dependency-name: "eslint"
35
+ - dependency-name: "neostandard"
36
+ - dependency-name: "@stylistic/*"
13
37
  open-pull-requests-limit: 10
38
+ groups:
39
+ # Production dependencies with breaking changes
40
+ dependencies:
41
+ dependency-type: "production"
42
+ # ESLint related dependencies
43
+ dev-dependencies-eslint:
44
+ patterns:
45
+ - "eslint"
46
+ - "neostandard"
47
+ - "@stylistic/*"
48
+ # TypeScript related dependencies
49
+ dev-dependencies-typescript:
50
+ patterns:
51
+ - "@types/*"
52
+ - "tstyche"
53
+ - "typescript"
@@ -4,7 +4,6 @@ on:
4
4
  push:
5
5
  branches:
6
6
  - main
7
- - master
8
7
  - next
9
8
  - 'v*'
10
9
  paths-ignore:
@@ -15,9 +14,20 @@ on:
15
14
  - 'docs/**'
16
15
  - '*.md'
17
16
 
17
+ # This allows a subsequently queued workflow run to interrupt previous runs
18
+ concurrency:
19
+ group: "${{ github.workflow }}-${{ github.event.pull_request.head.label || github.head_ref || github.ref }}"
20
+ cancel-in-progress: true
21
+
22
+ permissions:
23
+ contents: read
24
+
18
25
  jobs:
19
26
  test:
20
- uses: fastify/workflows/.github/workflows/plugins-ci.yml@v5
27
+ permissions:
28
+ contents: write
29
+ pull-requests: write
30
+ uses: fastify/workflows/.github/workflows/plugins-ci.yml@ef591e2186785d5ab36b9fe6a79c7ce2f1d94e57 # v7.0.0
21
31
  with:
22
32
  license-check: true
23
33
  lint: true
@@ -0,0 +1,19 @@
1
+ name: Lock Threads
2
+
3
+ on:
4
+ schedule:
5
+ - cron: '0 0 1 * *'
6
+ workflow_dispatch:
7
+
8
+ concurrency:
9
+ group: lock
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ jobs:
15
+ lock-threads:
16
+ permissions:
17
+ issues: write
18
+ pull-requests: write
19
+ uses: fastify/workflows/.github/workflows/lock-threads.yml@ef591e2186785d5ab36b9fe6a79c7ce2f1d94e57 # v7.0.0
package/LICENSE CHANGED
@@ -1,6 +1,7 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2018 James Sumners <james.sumners@gmail.com>
3
+ Copyright (c) 2018-present James Sumners <james.sumners@gmail.com>
4
+ Copyright (c) 2018-present The Fastify team <https://github.com/fastify/fastify#team>
4
5
 
5
6
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
7
  of this software and associated documentation files (the "Software"), to deal
@@ -1,15 +1,12 @@
1
1
  # @fastify/caching
2
2
 
3
- [![CI](https://github.com/fastify/fastify-caching/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/fastify/fastify-caching/actions/workflows/ci.yml)
3
+ [![CI](https://github.com/fastify/fastify-caching/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/fastify/fastify-caching/actions/workflows/ci.yml)
4
4
  [![NPM version](https://img.shields.io/npm/v/@fastify/caching.svg?style=flat)](https://www.npmjs.com/package/@fastify/caching)
5
5
  [![neostandard javascript style](https://img.shields.io/badge/code_style-neostandard-brightgreen?style=flat)](https://github.com/neostandard/neostandard)
6
6
 
7
- *@fastify/caching* is a plugin for the [Fastify](http://fastify.dev/) framework
7
+ *@fastify/caching* is a plugin for the [Fastify](https://fastify.dev/) framework
8
8
  that provides server-side caching and mechanisms for manipulating HTTP cache headers according to
9
- [RFC 2616 §14.9](https://tools.ietf.org/html/rfc2616#section-14.9).
10
-
11
- Supports Fastify versions ^3.0.0. Version v5.x supports Fastify ^3.0.0 in
12
- the [`v5.x` branch](https://github.com/fastify/fastify-caching/tree/v5.x).
9
+ [RFC 2616 §14.9](https://datatracker.ietf.org/doc/html/rfc2616#section-14.9).
13
10
 
14
11
  This plugin fully supports Fastify's encapsulation. Therefore, routes that
15
12
  should have differing cache settings should be registered within different
@@ -19,14 +16,33 @@ In addition to providing header manipulation, the plugin also decorates the
19
16
  server instance with an object that can be used for caching items. **Note:**
20
17
  the default cache should not be used in a "production" environment. It is
21
18
  an LRU, in-memory cache that is capped at 100,000 items. It is *highly*
22
- recommended that a full featured cache object be supplied, e.g.
19
+ recommended that a full-featured cache object be supplied, e.g.
23
20
  [abstract-cache-redis][acache-redis].
24
21
 
25
22
  [acache-redis]: https://www.npmjs.com/package/abstract-cache-redis
26
23
 
24
+ ## Install
25
+ ```
26
+ npm i @fastify/caching
27
+ ```
28
+
29
+ ### Compatibility
30
+ | Plugin version | Fastify version |
31
+ | ---------------|-----------------|
32
+ | `>=9.x` | `^5.x` |
33
+ | `^8.x` | `^4.x` |
34
+ | `>=6.x <8.x` | `^3.x` |
35
+ | `>=4.x <6.x` | `^2.x` |
36
+ | `>=1.x <4.x` | `^1.x` |
37
+
38
+
39
+ Please note that if a Fastify version is out of support, then so are the corresponding versions of this plugin
40
+ in the table above.
41
+ See [Fastify's LTS policy](https://github.com/fastify/fastify/blob/main/docs/Reference/LTS.md) for more details.
42
+
27
43
  ## Example
28
44
 
29
- This example shows using the plugin to disable client side caching of all
45
+ This example shows using the plugin to disable client-side caching of all
30
46
  routes.
31
47
 
32
48
  ```js
@@ -107,7 +123,7 @@ for a *cache-response-directive* as defined by RFC 2616.
107
123
  resource may be cached. When this is set, and `privacy` is not set to `no-cache`,
108
124
  then `', max-age=<value>'` will be appended to the `cache-control` header.
109
125
  + `cache` (Default: `abstract-cache.memclient`): an [abstract-cache][acache]
110
- protocol compliant cache object. Note: the plugin requires a cache instance to
126
+ protocol-compliant cache object. Note: the plugin requires a cache instance to
111
127
  properly support the ETag mechanism. Therefore, if a falsy value is supplied
112
128
  the default will be used.
113
129
  + `cacheSegment` (Default: `'@fastify/caching'`): segment identifier to use when
@@ -128,7 +144,7 @@ recommended to always supply a value to this method to avoid this operation.
128
144
  All incoming requests to paths affected by this plugin will be inspected for
129
145
  the `if-none-match` header. If the header is present, the value will be used
130
146
  to lookup the tag within the cache associated with this plugin. If the tag is
131
- found, then the response will be ended with a 304 status code sent to
147
+ found, then the response will end with a 304 status code sent to
132
148
  the client.
133
149
 
134
150
  Tags will be cached according to the second parameter. If the second parameter
@@ -142,11 +158,11 @@ milliseconds. The default lifetime, when the parameter is not specified, is
142
158
  ### `reply.expires(date)`
143
159
 
144
160
  This method allows setting of the `expires` header. It accepts a regular `Date`
145
- object, or a string that is a valid date string according to
161
+ object or a string that is a valid date string according to
146
162
  [RFC 2616 §14.21][sec14.21].
147
163
 
148
- [sec14.21]: https://tools.ietf.org/html/rfc2616#section-14.21
164
+ [sec14.21]: https://datatracker.ietf.org/doc/html/rfc2616#section-14.21
149
165
 
150
166
  ## License
151
167
 
152
- [MIT License](https://jsumners.mit-license.org/)
168
+ Licensed under [MIT](./LICENSE).
package/index.js CHANGED
@@ -38,7 +38,7 @@ function cachingLoadByEtag (req, res, next) {
38
38
  })
39
39
  }
40
40
 
41
- function cachingStoreByEtag (req, res, payload, next) {
41
+ function cachingStoreByEtag (_req, res, payload, next) {
42
42
  const etag = res.getHeader('etag')
43
43
  if (!etag || !res._etagLife) return next()
44
44
  this.cache.set(
@@ -60,7 +60,7 @@ function fastifyCaching (instance, options, next) {
60
60
  if (!_options.cache) _options.cache = abstractCache()
61
61
 
62
62
  if (_options.privacy) {
63
- // https://tools.ietf.org/html/rfc2616#section-14.9.4
63
+ // https://datatracker.ietf.org/doc/html/rfc2616#section-14.9.4
64
64
  let value = _options.privacy
65
65
  if (_options.privacy.toLowerCase() !== 'no-cache' && _options.expiresIn) {
66
66
  value = `${_options.privacy}, max-age=${_options.expiresIn}`
@@ -70,9 +70,16 @@ function fastifyCaching (instance, options, next) {
70
70
  value += `, s-maxage=${_options.serverExpiresIn}`
71
71
  }
72
72
 
73
- instance.addHook('onRequest', function cachingSetCacheControlHeader (req, res, next) {
74
- if (!res.hasHeader('Cache-control')) {
75
- res.header('Cache-control', value)
73
+ instance.addHook('onSend', function cachingSetCacheControlHeader (_req, reply, _payload, next) {
74
+ if (!reply.hasHeader('Cache-control')) {
75
+ // Only apply the caching value to successful (2xx) responses.
76
+ // Error responses must not be cached, otherwise browsers may serve
77
+ // a stale 4xx/5xx for the duration of max-age.
78
+ if (reply.statusCode >= 200 && reply.statusCode < 300) {
79
+ reply.header('Cache-control', value)
80
+ } else {
81
+ reply.header('Cache-control', 'no-store')
82
+ }
76
83
  }
77
84
  next()
78
85
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fastify/caching",
3
- "version": "9.0.2",
3
+ "version": "9.0.4",
4
4
  "description": "A plugin for Fastify to enable management of cache control headers",
5
5
  "main": "index.js",
6
6
  "type": "commonjs",
@@ -9,18 +9,14 @@
9
9
  "lint": "eslint",
10
10
  "lint:fix": "eslint --fix",
11
11
  "test": "npm run test:unit && npm run test:typescript",
12
- "test:typescript": "tsd",
12
+ "test:typescript": "tstyche",
13
13
  "test:unit": "c8 --100 node --test",
14
14
  "test:unit:report": "npm run test:unit -- --coverage-report=html",
15
15
  "test:unit:verbose": "npm run test:unit -- -Rspec"
16
16
  },
17
- "precommit": [
18
- "lint",
19
- "test"
20
- ],
21
17
  "repository": {
22
18
  "type": "git",
23
- "url": "git+ssh://git@github.com/fastify/fastify-caching.git"
19
+ "url": "git+https://github.com/fastify/fastify-caching.git"
24
20
  },
25
21
  "keywords": [
26
22
  "fastify",
@@ -28,29 +24,54 @@
28
24
  "caching"
29
25
  ],
30
26
  "author": "James Sumners <james.sumners@gmail.com>",
27
+ "contributors": [
28
+ {
29
+ "name": "Matteo Collina",
30
+ "email": "hello@matteocollina.com"
31
+ },
32
+ {
33
+ "name": "Manuel Spigolon",
34
+ "email": "behemoth89@gmail.com"
35
+ },
36
+ {
37
+ "name": "Aras Abbasi",
38
+ "email": "aras.abbasi@gmail.com"
39
+ },
40
+ {
41
+ "name": "Frazer Smith",
42
+ "email": "frazer.dev@icloud.com",
43
+ "url": "https://github.com/fdawgs"
44
+ }
45
+ ],
31
46
  "license": "MIT",
32
47
  "bugs": {
33
48
  "url": "https://github.com/fastify/fastify-caching/issues"
34
49
  },
35
50
  "homepage": "https://github.com/fastify/fastify-caching#readme",
51
+ "funding": [
52
+ {
53
+ "type": "github",
54
+ "url": "https://github.com/sponsors/fastify"
55
+ },
56
+ {
57
+ "type": "opencollective",
58
+ "url": "https://opencollective.com/fastify"
59
+ }
60
+ ],
36
61
  "devDependencies": {
37
- "@fastify/pre-commit": "^2.1.0",
38
- "@types/node": "^22.0.0",
39
- "c8": "^10.1.2",
62
+ "@types/node": "^26.0.1",
63
+ "c8": "^12.0.0",
64
+ "eslint": "^9.17.0",
40
65
  "fastify": "^5.0.0",
41
- "neostandard": "^0.11.9",
42
- "tsd": "^0.31.0"
66
+ "neostandard": "^0.13.0",
67
+ "tstyche": "^7.0.0"
43
68
  },
44
69
  "dependencies": {
45
70
  "abstract-cache": "^1.0.1",
46
- "fastify-plugin": "^5.0.0",
71
+ "fastify-plugin": "^6.0.0",
47
72
  "uid-safe": "^2.1.5"
48
73
  },
49
74
  "publishConfig": {
50
75
  "access": "public"
51
- },
52
- "pre-commit": [
53
- "lint",
54
- "test"
55
- ]
76
+ }
56
77
  }
@@ -21,25 +21,25 @@ test('cache is usable', async (t) => {
21
21
  t.plan(4)
22
22
 
23
23
  const fastify = Fastify()
24
- await fastify.register(async (instance, options) => {
25
- instance.addHook('onRequest', async function checkCachingRegistered (req, reply) {
24
+ await fastify.register(async (instance) => {
25
+ instance.addHook('onRequest', async function checkCachingRegistered () {
26
26
  t.assert.ifError(instance[Symbol.for('fastify-caching.registered')])
27
27
  })
28
28
  })
29
29
  await fastify.register(plugin)
30
30
 
31
- fastify.addHook('onRequest', async function checkCachingRegistered (req, reply) {
31
+ fastify.addHook('onRequest', async function checkCachingRegistered () {
32
32
  t.assert.strictEqual(this[Symbol.for('fastify-caching.registered')], true)
33
33
  })
34
34
 
35
- fastify.get('/one', (req, reply) => {
35
+ fastify.get('/one', (_req, reply) => {
36
36
  fastify.cache.set('one', { one: true }, 1000, (err) => {
37
37
  if (err) return reply.send(err)
38
38
  reply.redirect('/two')
39
39
  })
40
40
  })
41
41
 
42
- fastify.get('/two', (req, reply) => {
42
+ fastify.get('/two', (_req, reply) => {
43
43
  fastify.cache.get('one', (err, obj) => {
44
44
  t.assert.ifError(err)
45
45
  t.assert.strictEqual(obj.item, { one: true })
@@ -70,25 +70,25 @@ test('cache is usable with function as plugin default options input', async (t)
70
70
  t.plan(4)
71
71
 
72
72
  const fastify = Fastify()
73
- await fastify.register(async (instance, options) => {
74
- instance.addHook('onRequest', async function checkCachingNotRegistered (req, reply) {
73
+ await fastify.register(async (instance) => {
74
+ instance.addHook('onRequest', async function checkCachingNotRegistered () {
75
75
  t.assert.failure(instance[Symbol.for('fastify-caching.registered')])
76
76
  })
77
77
  })
78
78
  await fastify.register(plugin, () => () => {})
79
79
 
80
- fastify.addHook('onRequest', async function checkCachingRegistered (req, reply) {
80
+ fastify.addHook('onRequest', async function checkCachingRegistered () {
81
81
  t.assert.strictEqual(this[Symbol.for('fastify-caching.registered')], true)
82
82
  })
83
83
 
84
- fastify.get('/one', (req, reply) => {
84
+ fastify.get('/one', (_req, reply) => {
85
85
  fastify.cache.set('one', { one: true }, 1000, (err) => {
86
86
  if (err) return reply.send(err)
87
87
  reply.redirect('/two')
88
88
  })
89
89
  })
90
90
 
91
- fastify.get('/two', (req, reply) => {
91
+ fastify.get('/two', (_req, reply) => {
92
92
  fastify.cache.get('one', (err, obj) => {
93
93
  t.assert.ifError(err)
94
94
  t.assert.strictEqual(obj.item, { one: true })
@@ -120,21 +120,21 @@ test('getting cache item with error returns error', async (t) => {
120
120
  t.plan(1)
121
121
 
122
122
  const mockCache = {
123
- get: (info, callback) => callback(new Error('cache.get always errors')),
124
- set: (key, value, ttl, callback) => callback()
123
+ get: (_info, callback) => callback(new Error('cache.get always errors')),
124
+ set: (_key, _value, _ttl, callback) => callback()
125
125
  }
126
126
 
127
127
  const fastify = Fastify()
128
128
  await fastify.register(plugin, { cache: mockCache })
129
129
 
130
- fastify.get('/one', (req, reply) => {
130
+ fastify.get('/one', (_req, reply) => {
131
131
  fastify.cache.set('one', { one: true }, 1000, (err) => {
132
132
  if (err) return reply.send(err)
133
133
  return reply.etag('123456').send({ hello: 'world' })
134
134
  })
135
135
  })
136
136
 
137
- fastify.get('/two', (req, reply) => {
137
+ fastify.get('/two', () => {
138
138
  fastify.cache.get('one', (err, obj) => {
139
139
  t.assert.failure(err)
140
140
  t.assert.failure(obj)
@@ -164,7 +164,7 @@ test('etags get stored in cache', async (t) => {
164
164
  const fastify = Fastify()
165
165
  await fastify.register(plugin)
166
166
 
167
- fastify.get('/one', (req, reply) => {
167
+ fastify.get('/one', (_req, reply) => {
168
168
  reply.etag('123456').send({ hello: 'world' })
169
169
  })
170
170
 
@@ -191,7 +191,7 @@ test('etag cache life is customizable', async (t) => {
191
191
  const fastify = Fastify()
192
192
  await fastify.register(plugin)
193
193
 
194
- fastify.get('/one', function (req, reply) {
194
+ fastify.get('/one', function (_req, reply) {
195
195
  reply
196
196
  // We set a cache lifetime of 50 milliseconds
197
197
  .etag('123456', 50)
@@ -237,7 +237,7 @@ test('returns response payload', async (t) => {
237
237
  const fastify = Fastify()
238
238
  await fastify.register(plugin)
239
239
 
240
- fastify.get('/one', (req, reply) => {
240
+ fastify.get('/one', (_req, reply) => {
241
241
  reply.etag('123456', 300).send({ hello: 'world' })
242
242
  })
243
243
 
@@ -10,7 +10,7 @@ test('decorators get added', async (t) => {
10
10
  const fastify = Fastify()
11
11
  await fastify.register(plugin)
12
12
 
13
- fastify.get('/', (req, reply) => {
13
+ fastify.get('/', (_req, reply) => {
14
14
  t.assert.ok(reply.etag)
15
15
  reply.send()
16
16
  })
@@ -31,7 +31,7 @@ test('decorators add headers', async (t) => {
31
31
  const fastify = Fastify()
32
32
  await fastify.register(plugin)
33
33
 
34
- fastify.get('/', (req, reply) => {
34
+ fastify.get('/', (_req, reply) => {
35
35
  reply.etag(tag).send()
36
36
  })
37
37
 
@@ -51,7 +51,7 @@ test('sets etag header for falsy argument', async (t) => {
51
51
  const fastify = Fastify()
52
52
  await fastify.register(plugin)
53
53
 
54
- fastify.get('/', (req, reply) => {
54
+ fastify.get('/', (_req, reply) => {
55
55
  reply.etag().send()
56
56
  })
57
57
 
@@ -70,7 +70,7 @@ test('sets no-cache header', async (t) => {
70
70
  const fastify = Fastify()
71
71
  await fastify.register(plugin, { privacy: plugin.privacy.NOCACHE })
72
72
 
73
- fastify.get('/', (req, reply) => {
73
+ fastify.get('/', (_req, reply) => {
74
74
  reply.send({ hello: 'world' })
75
75
  })
76
76
 
@@ -95,7 +95,7 @@ test('sets private with max-age header', async (t) => {
95
95
  const fastify = Fastify()
96
96
  await fastify.register(plugin, opts)
97
97
 
98
- fastify.get('/', (req, reply) => {
98
+ fastify.get('/', (_req, reply) => {
99
99
  reply.send({ hello: 'world' })
100
100
  })
101
101
 
@@ -124,7 +124,7 @@ test('sets public with max-age and s-maxage header', async (t) => {
124
124
  const fastify = Fastify()
125
125
  await fastify.register(plugin, opts)
126
126
 
127
- fastify.get('/', (req, reply) => {
127
+ fastify.get('/', (_req, reply) => {
128
128
  reply.send({ hello: 'world' })
129
129
  })
130
130
 
@@ -151,12 +151,12 @@ test('do not set headers if another upstream plugin already sets it', async (t)
151
151
  }
152
152
 
153
153
  const fastify = Fastify()
154
- fastify.addHook('onRequest', async function checkCachingDoesNotOverrideCacheControlHeader (req, reply) {
154
+ fastify.addHook('onRequest', async function checkCachingDoesNotOverrideCacheControlHeader (_req, reply) {
155
155
  reply.header('cache-control', 'do not override')
156
156
  })
157
157
  await fastify.register(plugin, opts)
158
158
 
159
- fastify.get('/', (req, reply) => {
159
+ fastify.get('/', (_req, reply) => {
160
160
  reply.send({ hello: 'world' })
161
161
  })
162
162
 
@@ -182,7 +182,7 @@ test('only sets max-age and ignores s-maxage with private header', async (t) =>
182
182
  const fastify = Fastify()
183
183
  await fastify.register(plugin, opts)
184
184
 
185
- fastify.get('/', (req, reply) => {
185
+ fastify.get('/', (_req, reply) => {
186
186
  reply.send({ hello: 'world' })
187
187
  })
188
188
 
@@ -210,7 +210,7 @@ test('s-maxage is optional with public header', async (t) => {
210
210
  const fastify = Fastify()
211
211
  await fastify.register(plugin, opts)
212
212
 
213
- fastify.get('/', (req, reply) => {
213
+ fastify.get('/', (_req, reply) => {
214
214
  reply.send({ hello: 'world' })
215
215
  })
216
216
 
@@ -230,7 +230,7 @@ test('sets no-store with max-age header', async (t) => {
230
230
  const fastify = Fastify()
231
231
  await fastify.register(plugin, { privacy: 'no-store', expiresIn: 300 })
232
232
 
233
- fastify.get('/', (req, reply) => {
233
+ fastify.get('/', (_req, reply) => {
234
234
  reply.send({ hello: 'world' })
235
235
  })
236
236
 
@@ -247,6 +247,105 @@ test('sets no-store with max-age header', async (t) => {
247
247
  )
248
248
  })
249
249
 
250
+ test('does not cache 5xx error responses', async (t) => {
251
+ t.plan(2)
252
+
253
+ const opts = {
254
+ privacy: plugin.privacy.PRIVATE,
255
+ expiresIn: 300
256
+ }
257
+
258
+ const fastify = Fastify()
259
+ await fastify.register(plugin, opts)
260
+
261
+ fastify.get('/', () => {
262
+ throw new Error('kaboom')
263
+ })
264
+
265
+ await fastify.ready()
266
+
267
+ const response = await fastify.inject({
268
+ method: 'GET',
269
+ path: '/'
270
+ })
271
+ t.assert.strictEqual(response.statusCode, 500)
272
+ t.assert.strictEqual(response.headers['cache-control'], 'no-store')
273
+ })
274
+
275
+ test('does not cache 4xx error responses', async (t) => {
276
+ t.plan(2)
277
+
278
+ const opts = {
279
+ privacy: plugin.privacy.PRIVATE,
280
+ expiresIn: 300
281
+ }
282
+
283
+ const fastify = Fastify()
284
+ await fastify.register(plugin, opts)
285
+
286
+ fastify.get('/', (_req, reply) => {
287
+ reply.code(400).send({ error: 'bad request' })
288
+ })
289
+
290
+ await fastify.ready()
291
+
292
+ const response = await fastify.inject({
293
+ method: 'GET',
294
+ path: '/'
295
+ })
296
+ t.assert.strictEqual(response.statusCode, 400)
297
+ t.assert.strictEqual(response.headers['cache-control'], 'no-store')
298
+ })
299
+
300
+ test('does not cache framework 404 responses', async (t) => {
301
+ t.plan(2)
302
+
303
+ const opts = {
304
+ privacy: plugin.privacy.PRIVATE,
305
+ expiresIn: 300
306
+ }
307
+
308
+ const fastify = Fastify()
309
+ await fastify.register(plugin, opts)
310
+
311
+ await fastify.ready()
312
+
313
+ const response = await fastify.inject({
314
+ method: 'GET',
315
+ path: '/does-not-exist'
316
+ })
317
+ t.assert.strictEqual(response.statusCode, 404)
318
+ t.assert.strictEqual(response.headers['cache-control'], 'no-store')
319
+ })
320
+
321
+ test('does not override a Cache-control header set upstream on error responses', async (t) => {
322
+ t.plan(2)
323
+
324
+ const opts = {
325
+ privacy: plugin.privacy.PRIVATE,
326
+ expiresIn: 300
327
+ }
328
+
329
+ const fastify = Fastify()
330
+ fastify.addHook('onRequest', async function presetCacheControl (_req, reply) {
331
+ reply.header('cache-control', 'private, max-age=10')
332
+ })
333
+ await fastify.register(plugin, opts)
334
+
335
+ fastify.get('/', () => {
336
+ throw new Error('kaboom')
337
+ })
338
+
339
+ await fastify.ready()
340
+
341
+ const response = await fastify.inject({
342
+ method: 'GET',
343
+ path: '/'
344
+ })
345
+ t.assert.strictEqual(response.statusCode, 500)
346
+ t.assert.strictEqual(response.headers['cache-control'], 'private, max-age=10')
347
+ })
348
+
250
349
  test('sets the expires header', async (t) => {
251
350
  t.plan(2)
252
351
 
@@ -255,7 +354,7 @@ test('sets the expires header', async (t) => {
255
354
  const fastify = Fastify()
256
355
  await fastify.register(plugin, { privacy: plugin.privacy.NOCACHE })
257
356
 
258
- fastify.get('/', (req, reply) => {
357
+ fastify.get('/', (_req, reply) => {
259
358
  reply.expires(now).send({ hello: 'world' })
260
359
  })
261
360
 
@@ -275,7 +374,7 @@ test('sets the expires header to a falsy value', async (t) => {
275
374
  const fastify = Fastify()
276
375
  await fastify.register(plugin, { privacy: plugin.privacy.NOCACHE })
277
376
 
278
- fastify.get('/', (req, reply) => {
377
+ fastify.get('/', (_req, reply) => {
279
378
  reply.expires().send({ hello: 'world' })
280
379
  })
281
380
 
@@ -294,7 +393,7 @@ test('sets the expires header to a custom value', async (t) => {
294
393
  const fastify = Fastify()
295
394
  await fastify.register(plugin, { privacy: plugin.privacy.NOCACHE })
296
395
 
297
- fastify.get('/', (req, reply) => {
396
+ fastify.get('/', (_req, reply) => {
298
397
  reply.expires('foobar').send({ hello: 'world' })
299
398
  })
300
399
 
package/types/index.d.ts CHANGED
@@ -1,5 +1,3 @@
1
- /// <reference types='node' />
2
-
3
1
  import { FastifyPluginCallback } from 'fastify'
4
2
 
5
3
  declare module 'fastify' {
@@ -0,0 +1,68 @@
1
+ import Fastify, { FastifyReply } from 'fastify'
2
+ import { expect } from 'tstyche'
3
+ import fastifyCaching, {
4
+ AbstractCacheCompliantObject,
5
+ FastifyCachingPluginOptions,
6
+ } from '..'
7
+
8
+ const fastify = Fastify({ logger: true })
9
+
10
+ const cachingOptions: FastifyCachingPluginOptions = {
11
+ privacy: fastifyCaching.privacy.PUBLIC,
12
+ expiresIn: 300,
13
+ cacheSegment: 'fastify-caching',
14
+ }
15
+ expect(cachingOptions).type.toBeAssignableTo<FastifyCachingPluginOptions>()
16
+
17
+ fastify.register(fastifyCaching, cachingOptions)
18
+
19
+ expect(fastify.cache).type.toBe<AbstractCacheCompliantObject>()
20
+ expect(fastify.cache.get).type.toBe<AbstractCacheCompliantObject['get']>()
21
+ expect(fastify.cache.set).type.toBe<AbstractCacheCompliantObject['set']>()
22
+ expect(fastify.cacheSegment).type.toBe<string>()
23
+
24
+ fastify.get('/one', async (_request, reply) => {
25
+ expect(reply.etag).type.toBe<(tag?: string, timeToLive?: number) => FastifyReply>()
26
+ expect(reply.expires).type.toBe<(date?: Date) => FastifyReply>()
27
+
28
+ expect(reply.etag('hello', 6000)).type.toBe<FastifyReply>()
29
+ expect(reply.expires(new Date(Date.now() + 6000))).type.toBe<FastifyReply>()
30
+
31
+ return { message: 'one' }
32
+ })
33
+
34
+ fastify.get('/two', async (_request, reply) => {
35
+ expect(
36
+ reply.etag('hello', 6000).expires(new Date(Date.now() + 6000))
37
+ ).type.toBe<FastifyReply>()
38
+
39
+ return { message: 'two' }
40
+ })
41
+
42
+ // We register a new instance that should trigger a typescript error.
43
+ const shouldErrorApp = Fastify({ logger: true })
44
+
45
+ const badCachingOptions = {
46
+ privacy: fastifyCaching.privacy.PRIVATE,
47
+ expiresIn: 'a string instead of a number of second',
48
+ cacheSegment: 'fastify-caching',
49
+ }
50
+
51
+ expect(shouldErrorApp.register).type.not.toBeCallableWith(fastifyCaching, badCachingOptions)
52
+
53
+ fastify.get('/three', async () => {
54
+ expect(fastify.cache.get('well-known')).type.toBeAssignableTo<Promise<unknown>>()
55
+
56
+ expect(fastify.cache.get<string>('well-known')).type.toBe<
57
+ Promise<{ item: string; stored: number; ttl: number; } | null>
58
+ >()
59
+
60
+ const result = fastify.cache.get<string>('well-known', (err, value) => {
61
+ expect(err).type.toBe<unknown>()
62
+ expect(value).type.toBe<{ item: string; stored: number; ttl: number; } | null>()
63
+ })
64
+
65
+ expect(result).type.toBe<void>()
66
+
67
+ return { message: 'two' }
68
+ })
package/.github/stale.yml DELETED
@@ -1,21 +0,0 @@
1
- # Number of days of inactivity before an issue becomes stale
2
- daysUntilStale: 15
3
- # Number of days of inactivity before a stale issue is closed
4
- daysUntilClose: 7
5
- # Issues with these labels will never be considered stale
6
- exemptLabels:
7
- - "discussion"
8
- - "feature request"
9
- - "bug"
10
- - "help wanted"
11
- - "plugin suggestion"
12
- - "good first issue"
13
- # Label to use when marking an issue as stale
14
- staleLabel: stale
15
- # Comment to post when marking an issue as stale. Set to `false` to disable
16
- markComment: >
17
- This issue has been automatically marked as stale because it has not had
18
- recent activity. It will be closed if no further activity occurs. Thank you
19
- for your contributions.
20
- # Comment to post when closing a stale issue. Set to `false` to disable
21
- closeComment: false
@@ -1,69 +0,0 @@
1
- import Fastify, { FastifyReply } from 'fastify'
2
- import { expectAssignable, expectError, expectType } from 'tsd'
3
- import fastifyCaching, {
4
- AbstractCacheCompliantObject,
5
- FastifyCachingPluginOptions,
6
- } from '..'
7
-
8
- const fastify = Fastify({ logger: true })
9
-
10
- const cachingOptions: FastifyCachingPluginOptions = {
11
- privacy: fastifyCaching.privacy.PUBLIC,
12
- expiresIn: 300,
13
- cacheSegment: 'fastify-caching',
14
- }
15
- expectAssignable<FastifyCachingPluginOptions>(cachingOptions)
16
-
17
- fastify.register(fastifyCaching, cachingOptions)
18
-
19
- expectType<AbstractCacheCompliantObject>(fastify.cache)
20
- expectType<AbstractCacheCompliantObject['get']>(fastify.cache.get)
21
- expectType<AbstractCacheCompliantObject['set']>(fastify.cache.set)
22
- expectType<string>(fastify.cacheSegment)
23
- // expectType<number>(fastify.etagMaxLife);
24
-
25
- fastify.get('/one', async (request, reply) => {
26
- expectType<(tag?: string, timeToLive?: number) => FastifyReply>(reply.etag)
27
- expectType<(date?: Date) => FastifyReply>(reply.expires)
28
-
29
- expectType<FastifyReply>(reply.etag('hello', 6000))
30
- expectType<FastifyReply>(reply.expires(new Date(Date.now() + 6000)))
31
-
32
- return { message: 'one' }
33
- })
34
-
35
- fastify.get('/two', async (request, reply) => {
36
- expectType<FastifyReply>(
37
- reply.etag('hello', 6000).expires(new Date(Date.now() + 6000))
38
- )
39
-
40
- return { message: 'two' }
41
- })
42
-
43
- // We register a new instance that should trigger a typescript error.
44
- const shouldErrorApp = Fastify({ logger: true })
45
-
46
- const badCachingOptions = {
47
- privacy: fastifyCaching.privacy.PRIVATE,
48
- expiresIn: 'a string instead of a number of second',
49
- cacheSegment: 'fastify-caching',
50
- }
51
-
52
- expectError(shouldErrorApp.register(fastifyCaching, badCachingOptions))
53
-
54
- fastify.get('/three', async (request, reply) => {
55
- expectAssignable<Promise<unknown>>(
56
- fastify.cache.get('well-known')
57
- )
58
- expectAssignable<Promise<{ item: string; stored: number; ttl: number; } | null>>(
59
- fastify.cache.get<string>('well-known')
60
- )
61
- expectType<void>(
62
- fastify.cache.get<string>('well-known', (err, value) => {
63
- expectType<unknown>(err)
64
- expectAssignable<{ item: string; stored: number; ttl: number; } | null>(value)
65
- })
66
- )
67
-
68
- return { message: 'two' }
69
- })