koa-compress 5.1.0 → 5.1.1

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/HISTORY.md CHANGED
@@ -1,3 +1,4 @@
1
+ SEE https://github.com/koajs/compress/releases
1
2
 
2
3
  4.0.1 / 2020-04-29
3
4
  ==================
package/lib/encodings.js CHANGED
@@ -3,16 +3,80 @@
3
3
  const errors = require('http-errors')
4
4
  const zlib = require('zlib')
5
5
 
6
- module.exports = Encodings
6
+ class Encodings {
7
+ constructor (options = {}) {
8
+ this.wildcardAcceptEncoding = options.wildcardAcceptEncoding || Encodings.wildcardAcceptEncoding
9
+ this.preferredEncodings = options.preferredEncodings || Encodings.preferredEncodings
10
+ this.reDirective = options.reDirective || Encodings.reDirective
11
+
12
+ this.encodingWeights = new Map()
13
+ }
14
+
15
+ parseAcceptEncoding (acceptEncoding = '*') {
16
+ const { encodingWeights, reDirective } = this
17
+
18
+ acceptEncoding.split(',').forEach((directive) => {
19
+ const match = reDirective.exec(directive)
20
+ if (!match) return // not a supported encoding above
21
+
22
+ const encoding = match[1]
23
+
24
+ // weight must be in [0, 1]
25
+ let weight = match[2] && !isNaN(match[2]) ? parseFloat(match[2], 10) : 1
26
+ weight = Math.max(weight, 0)
27
+ weight = Math.min(weight, 1)
7
28
 
8
- // TODO: convert this to a Class when we drop node@10 support
9
- // because node@10 does not support class fields.
10
- function Encodings (options = {}) {
11
- this.wildcardAcceptEncoding = options.wildcardAcceptEncoding || Encodings.wildcardAcceptEncoding
12
- this.preferredEncodings = options.preferredEncodings || Encodings.preferredEncodings
13
- this.reDirective = options.reDirective || Encodings.reDirective
29
+ if (encoding === '*') {
30
+ // set the weights for the default encodings
31
+ this.wildcardAcceptEncoding.forEach((enc) => {
32
+ if (!encodingWeights.has(enc)) encodingWeights.set(enc, weight)
33
+ })
34
+ return
35
+ }
14
36
 
15
- this.encodingWeights = new Map()
37
+ encodingWeights.set(encoding, weight)
38
+ })
39
+ }
40
+
41
+ getPreferredContentEncoding () {
42
+ const {
43
+ encodingWeights,
44
+ preferredEncodings
45
+ } = this
46
+
47
+ // get ordered list of accepted encodings
48
+ const acceptedEncodings = Array.from(encodingWeights.keys())
49
+ // sort by weight
50
+ .sort((a, b) => encodingWeights.get(b) - encodingWeights.get(a))
51
+ // filter by supported encodings
52
+ .filter((encoding) => encoding === 'identity' || typeof Encodings.encodingMethods[encoding] === 'function')
53
+
54
+ // group them by weights
55
+ const weightClasses = new Map()
56
+ acceptedEncodings.forEach((encoding) => {
57
+ const weight = encodingWeights.get(encoding)
58
+ if (!weightClasses.has(weight)) weightClasses.set(weight, new Set())
59
+ weightClasses.get(weight).add(encoding)
60
+ })
61
+
62
+ // search by weight, descending
63
+ const weights = Array.from(weightClasses.keys()).sort((a, b) => b - a)
64
+ for (let i = 0; i < weights.length; i++) {
65
+ // encodings at this weight
66
+ const encodings = weightClasses.get(weights[i])
67
+ // return the first encoding in the preferred list
68
+ for (let j = 0; j < preferredEncodings.length; j++) {
69
+ const preferredEncoding = preferredEncodings[j]
70
+ if (encodings.has(preferredEncoding)) return preferredEncoding
71
+ }
72
+ }
73
+
74
+ // no encoding matches, check to see if the client set identity, q=0
75
+ if (encodingWeights.get('identity') === 0) throw errors(406, 'Please accept br, gzip, deflate, or identity.')
76
+
77
+ // by default, return nothing
78
+ return 'identity'
79
+ }
16
80
  }
17
81
 
18
82
  Encodings.encodingMethods = {
@@ -25,7 +89,9 @@ Encodings.encodingMethodDefaultOptions = {
25
89
  gzip: {},
26
90
  deflate: {},
27
91
  br: {
28
- [zlib.constants.BROTLI_PARAM_QUALITY]: 4
92
+ params: {
93
+ [zlib.constants.BROTLI_PARAM_QUALITY]: 4
94
+ }
29
95
  }
30
96
  }
31
97
 
@@ -35,68 +101,4 @@ Encodings.wildcardAcceptEncoding = ['gzip', 'deflate']
35
101
  Encodings.preferredEncodings = ['br', 'gzip', 'deflate']
36
102
  Encodings.reDirective = /^\s*(gzip|compress|deflate|br|identity|\*)\s*(?:;\s*q\s*=\s*(\d(?:\.\d)?))?\s*$/
37
103
 
38
- Encodings.prototype.parseAcceptEncoding = function (acceptEncoding = '*') {
39
- const { encodingWeights, reDirective } = this
40
-
41
- acceptEncoding.split(',').forEach((directive) => {
42
- const match = reDirective.exec(directive)
43
- if (!match) return // not a supported encoding above
44
-
45
- const encoding = match[1]
46
-
47
- // weight must be in [0, 1]
48
- let weight = match[2] && !isNaN(match[2]) ? parseFloat(match[2], 10) : 1
49
- weight = Math.max(weight, 0)
50
- weight = Math.min(weight, 1)
51
-
52
- if (encoding === '*') {
53
- // set the weights for the default encodings
54
- this.wildcardAcceptEncoding.forEach((enc) => {
55
- if (!encodingWeights.has(enc)) encodingWeights.set(enc, weight)
56
- })
57
- return
58
- }
59
-
60
- encodingWeights.set(encoding, weight)
61
- })
62
- }
63
-
64
- Encodings.prototype.getPreferredContentEncoding = function () {
65
- const {
66
- encodingWeights,
67
- preferredEncodings
68
- } = this
69
-
70
- // get ordered list of accepted encodings
71
- const acceptedEncodings = Array.from(encodingWeights.keys())
72
- // sort by weight
73
- .sort((a, b) => encodingWeights.get(b) - encodingWeights.get(a))
74
- // filter by supported encodings
75
- .filter((encoding) => encoding === 'identity' || typeof Encodings.encodingMethods[encoding] === 'function')
76
-
77
- // group them by weights
78
- const weightClasses = new Map()
79
- acceptedEncodings.forEach((encoding) => {
80
- const weight = encodingWeights.get(encoding)
81
- if (!weightClasses.has(weight)) weightClasses.set(weight, new Set())
82
- weightClasses.get(weight).add(encoding)
83
- })
84
-
85
- // search by weight, descending
86
- const weights = Array.from(weightClasses.keys()).sort((a, b) => b - a)
87
- for (let i = 0; i < weights.length; i++) {
88
- // encodings at this weight
89
- const encodings = weightClasses.get(weights[i])
90
- // return the first encoding in the preferred list
91
- for (let j = 0; j < preferredEncodings.length; j++) {
92
- const preferredEncoding = preferredEncodings[j]
93
- if (encodings.has(preferredEncoding)) return preferredEncoding
94
- }
95
- }
96
-
97
- // no encoding matches, check to see if the client set identity, q=0
98
- if (encodingWeights.get('identity') === 0) throw errors(406, 'Please accept br, gzip, deflate, or identity.')
99
-
100
- // by default, return nothing
101
- return 'identity'
102
- }
104
+ module.exports = Encodings
package/lib/index.js CHANGED
@@ -6,7 +6,6 @@
6
6
 
7
7
  const compressible = require('compressible')
8
8
  const isJSON = require('koa-is-json')
9
- const status = require('statuses')
10
9
  const Stream = require('stream')
11
10
  const bytes = require('bytes')
12
11
 
@@ -17,6 +16,11 @@ const Encodings = require('./encodings')
17
16
  */
18
17
  const NO_TRANSFORM_REGEX = /(?:^|,)\s*?no-transform\s*?(?:,|$)/
19
18
 
19
+ /**
20
+ * empty body statues.
21
+ */
22
+ const emptyBodyStatues = new Set([204, 205, 304])
23
+
20
24
  /**
21
25
  * Compress middleware.
22
26
  *
@@ -51,30 +55,26 @@ module.exports = (options = {}) => {
51
55
 
52
56
  await next()
53
57
 
54
- // early exit if there's no content body or the body is already encoded
55
58
  let { body } = ctx
56
- if (!body) return
57
- if (ctx.res.headersSent || !ctx.writable) return
58
- if (ctx.compress === false) return
59
- if (ctx.request.method === 'HEAD') return
60
- if (status.empty[ctx.response.status]) return
61
- if (ctx.response.get('Content-Encoding')) return
62
-
63
- // forced compression or implied
64
- if (!(ctx.compress === true || filter(ctx.response.type))) return
65
-
66
- // don't compress for Cache-Control: no-transform
67
- // https://tools.ietf.org/html/rfc7234#section-5.2.1.6
68
- const cacheControl = ctx.response.get('Cache-Control')
69
- if (cacheControl && NO_TRANSFORM_REGEX.test(cacheControl)) return
70
-
71
- // don't compress if the current response is below the threshold
72
- if (threshold && ctx.response.length < threshold) return
59
+ if (
60
+ // early exit if there's no content body or the body is already encoded
61
+ !body ||
62
+ ctx.res.headersSent || !ctx.writable ||
63
+ ctx.compress === false ||
64
+ ctx.request.method === 'HEAD' ||
65
+ emptyBodyStatues.has(+ctx.response.status) ||
66
+ ctx.response.get('Content-Encoding') ||
67
+ // forced compression or implied
68
+ !(ctx.compress === true || filter(ctx.response.type)) ||
69
+ // don't compress for Cache-Control: no-transform
70
+ // https://tools.ietf.org/html/rfc7234#section-5.2.1.6
71
+ NO_TRANSFORM_REGEX.test(ctx.response.get('Cache-Control')) ||
72
+ // don't compress if the current response is below the threshold
73
+ (threshold && ctx.response.length < threshold)
74
+ ) return
73
75
 
74
76
  // get the preferred content encoding
75
- const encodings = new Encodings({
76
- preferredEncodings
77
- })
77
+ const encodings = new Encodings({ preferredEncodings })
78
78
  encodings.parseAcceptEncoding(ctx.request.headers['accept-encoding'] || defaultEncoding)
79
79
  const encoding = encodings.getPreferredContentEncoding()
80
80
 
@@ -92,10 +92,7 @@ module.exports = (options = {}) => {
92
92
  const compress = Encodings.encodingMethods[encoding]
93
93
  const stream = ctx.body = compress(encodingOptions[encoding])
94
94
 
95
- if (body instanceof Stream) {
96
- body.pipe(stream)
97
- } else {
98
- stream.end(body)
99
- }
95
+ if (body instanceof Stream) return body.pipe(stream)
96
+ stream.end(body)
100
97
  }
101
98
  }
package/package.json CHANGED
@@ -1,32 +1,38 @@
1
1
  {
2
2
  "name": "koa-compress",
3
3
  "description": "Compress middleware for koa",
4
- "version": "5.1.0",
4
+ "version": "5.1.1",
5
5
  "author": {
6
6
  "name": "Jonathan Ong",
7
7
  "email": "me@jongleberry.com",
8
8
  "url": "http://jongleberry.com",
9
9
  "twitter": "https://twitter.com/jongleberry"
10
10
  },
11
+ "contributors": [
12
+ {
13
+ "name": "Imed Jaberi",
14
+ "email": "imed-jaberi@outlook.com",
15
+ "url": "https://www.3imed-jaberi.com/"
16
+ }
17
+ ],
11
18
  "license": "MIT",
12
19
  "repository": "koajs/compress",
13
20
  "dependencies": {
14
- "bytes": "^3.0.0",
15
- "compressible": "^2.0.0",
16
- "http-errors": "^1.8.0",
17
- "koa-is-json": "^1.0.0",
18
- "statuses": "^2.0.1"
21
+ "bytes": "^3.1.2",
22
+ "compressible": "^2.0.18",
23
+ "http-errors": "^1.8.1",
24
+ "koa-is-json": "^1.0.0"
19
25
  },
20
26
  "devDependencies": {
21
27
  "eslint": "^7.32.0",
22
28
  "eslint-config-standard": "^16.0.3",
23
- "eslint-plugin-import": "^2.24.0",
24
- "eslint-plugin-node": "^11.0.0",
25
- "eslint-plugin-promise": "^5.1.0",
29
+ "eslint-plugin-import": "^2.26.0",
30
+ "eslint-plugin-node": "^11.1.0",
31
+ "eslint-plugin-promise": "^5.2.0",
26
32
  "eslint-plugin-standard": "^5.0.0",
27
- "jest": "^27.0.6",
28
- "koa": "^2.13.1",
29
- "supertest": "^6.1.6"
33
+ "jest": "^27.5.1",
34
+ "koa": "^2.13.4",
35
+ "supertest": "^6.2.4"
30
36
  },
31
37
  "scripts": {
32
38
  "eslint": "eslint --ignore-path .gitignore .",
@@ -38,10 +44,7 @@
38
44
  "HISTORY.md"
39
45
  ],
40
46
  "main": "lib",
41
- "jest": {
42
- "testEnvironment": "node"
43
- },
44
47
  "engines": {
45
- "node": ">= 8.0.0"
48
+ "node": ">= 12"
46
49
  }
47
50
  }