koa-compress 5.1.1 → 5.2.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,5 +1,46 @@
1
1
  SEE https://github.com/koajs/compress/releases
2
2
 
3
+ 5.2.1 / 2026-03-12
4
+ ==================
5
+
6
+ * fixed TS typings and refactored them
7
+ * minor bug fix
8
+ * simplified code and removed unused code
9
+ * improved docs and typo fixes
10
+ * more tests
11
+ * updated dependencies
12
+
13
+ 5.2.0 / 2026-02-10
14
+ ==================
15
+
16
+ * add functional and local options
17
+ * updated dependencies
18
+ * copied the project setup from Koa
19
+ * add zstd
20
+ * use `negotiator` for content negotiation
21
+ * add TypeScript typings and tests
22
+
23
+ 5.1.1 / 2023-04-08
24
+ ==================
25
+
26
+ * minor refactoring + updated dependencies
27
+
28
+ 5.1.0 / 2021-08-17
29
+ ==================
30
+
31
+ * change brotli defaults (compression level to 4)
32
+ * updated dependencies
33
+
34
+ 5.0.1 / 2020-07-06
35
+ ==================
36
+
37
+ * updated to accomodate Node changes: zlib constants were moved to a different namespace
38
+
39
+ 5.0.0 / 2020-07-05
40
+ ==================
41
+
42
+ * add `defaultEncoding`
43
+
3
44
  4.0.1 / 2020-04-29
4
45
  ==================
5
46
 
package/README.md CHANGED
@@ -8,23 +8,28 @@ Compress middleware for Koa
8
8
  ## Example
9
9
 
10
10
  ```js
11
- const compress = require('koa-compress')
12
- const Koa = require('koa')
13
-
14
- const app = new Koa()
15
- app.use(compress({
16
- filter (content_type) {
17
- return /text/i.test(content_type)
18
- },
19
- threshold: 2048,
20
- gzip: {
21
- flush: require('zlib').constants.Z_SYNC_FLUSH
22
- },
23
- deflate: {
24
- flush: require('zlib').constants.Z_SYNC_FLUSH,
25
- },
26
- br: false // disable brotli
27
- }))
11
+ const compress = require("koa-compress");
12
+ const Koa = require("koa");
13
+
14
+ const app = new Koa();
15
+ app.use(
16
+ compress({
17
+ filter(content_type) {
18
+ return /text/i.test(content_type);
19
+ },
20
+ threshold: 2048,
21
+ gzip: {
22
+ flush: require("zlib").constants.Z_SYNC_FLUSH,
23
+ },
24
+ deflate: {
25
+ flush: require("zlib").constants.Z_SYNC_FLUSH,
26
+ },
27
+ zstd: {
28
+ flush: require("zlib").constants.Z_SYNC_FLUSH,
29
+ },
30
+ br: false, // disable brotli
31
+ }),
32
+ );
28
33
  ```
29
34
 
30
35
  ## Maintainers
@@ -37,49 +42,82 @@ app.use(compress({
37
42
  ### filter\<Function\>
38
43
 
39
44
  ```ts
40
- function (mimeType: string): Boolean {
41
-
42
- }
45
+ function (mimeType: string): boolean {}
43
46
  ```
44
47
 
45
- An optional function that checks the response content type to decide whether to compress.
46
- By default, it uses [compressible](https://github.com/jshttp/compressible).
48
+ A predicate that checks the response MIME type to decide whether to compress.
49
+ Default: [compressible](https://github.com/jshttp/compressible).
47
50
 
48
- ### options.threshold\<String|Number\>
51
+ ### options.threshold\<String|Number|Function\>
49
52
 
50
53
  Minimum response size in bytes to compress.
51
- Default `1024` bytes or `1kb`.
54
+ Can also be a string parsed by `bytes()` (e.g. `"1kb"`) or a function (see [Functional properties](#functional-properties)).
55
+ Default: `1024`.
56
+
57
+ ### options[encoding]\<Object|Function\>
58
+
59
+ Supported encodings in default preference order: `zstd`, `br`, `gzip`, `deflate`.
60
+ Setting `options[encoding] = {}` passes those options to the corresponding `zlib` compressor.
61
+ Setting `options[encoding] = false` disables that encoding.
62
+
63
+ Can also be a function (see [Functional properties](#functional-properties)).
52
64
 
53
- ### options[encoding]\<Object\>
65
+ #### options.br
54
66
 
55
- The current encodings are, in order of preference: `br`, `gzip`, `deflate`.
56
- Setting `options[encoding] = {}` will pass those options to the encoding function.
57
- Setting `options[encoding] = false` will disable that encoding.
67
+ [Brotli compression](https://en.wikipedia.org/wiki/Brotli) is available natively in all supported Node.js versions.
68
+ The default quality level is 4 for performance reasons.
58
69
 
59
- #### options<span></span>.br
70
+ #### options.zstd
60
71
 
61
- [Brotli compression](https://en.wikipedia.org/wiki/Brotli) is supported in node v11.7.0+, which includes it natively.
62
- As of v5.1.0, the default quality level is 4 for performance reasons.
72
+ [Zstandard compression](https://en.wikipedia.org/wiki/Zstandard) is natively supported starting from Node.js v22.15.0 (LTS) and v23.8.0 (Current). The middleware detects `zlib.createZstdCompress` at runtime; if available, zstd is enabled automatically, otherwise it is skipped.
63
73
 
64
74
  ### options.defaultEncoding\<String\>
65
75
 
66
- An optional string, which specifies what encoders to use for requests without
67
- [Accept-Encoding](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding).
68
- Default `identity`.
76
+ Encoding assumed when the client sends no
77
+ [Accept-Encoding](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding) header.
78
+ Default: `"identity"` (no compression).
69
79
 
70
- The standard dictates to treat such requests as `*` meaning that all compressions are permissible,
71
- yet it causes very practical problems when debugging servers with manual tools like `curl`, `wget`, and so on.
72
- If you want to enable the standard behavior, just set `defaultEncoding` to `*`.
80
+ The HTTP spec treats a missing header as `*` (any encoding is acceptable),
81
+ but that causes problems when debugging with tools like `curl` or `wget`.
82
+ Set `defaultEncoding` to `"*"` to restore spec-compliant behavior.
73
83
 
74
84
  ## Manually turning compression on and off
75
85
 
76
- You can always enable compression by setting `ctx.compress = true`.
77
- You can always disable compression by setting `ctx.compress = false`.
78
- This bypasses the filter check.
86
+ You can force compression by setting `ctx.compress = true` (bypasses the filter check).
87
+ You can disable compression by setting `ctx.compress = false`.
79
88
 
80
89
  ```js
81
90
  app.use((ctx, next) => {
82
- ctx.compress = true
83
- ctx.body = fs.createReadStream(file)
84
- })
91
+ ctx.compress = true;
92
+ ctx.body = fs.createReadStream(file);
93
+ });
94
+ ```
95
+
96
+ `ctx.compress` can also be an options object (same shape as the middleware options).
97
+ Its `threshold` and encoding properties override the global defaults for this response.
98
+ Note: an options object does **not** bypass the filter check — only `true` does.
99
+
100
+ ## Functional properties
101
+
102
+ The `threshold` and per-encoding options can be functions. They are called
103
+ for every response with three arguments:
104
+
105
+ - `type` &mdash; same as `ctx.response.type`
106
+ - `size` &mdash; same as `ctx.response.length`
107
+ - `ctx` &mdash; the full Koa context object
108
+
109
+ The function should return a valid value for that property.
110
+ Returning another function of the same shape is allowed — it will be called in turn.
111
+
112
+ Example:
113
+
114
+ ```js
115
+ app.use(
116
+ compress({
117
+ gzip: (type, size) => (size && size < 65536 ? { level: 9 } : false),
118
+ br: (type, size) => size && size >= 65536,
119
+ }),
120
+ );
85
121
  ```
122
+
123
+ See the Koa documentation for [`ctx`](https://koajs.com/#context) and [`ctx.response`](https://koajs.com/#response).
package/lib/index.d.ts ADDED
@@ -0,0 +1,162 @@
1
+ import type { BrotliOptions, ZlibOptions, ZstdOptions } from "node:zlib";
2
+ import type Koa = require("koa");
3
+
4
+ declare namespace compress {
5
+ /**
6
+ * Calculates a threshold value dynamically from the MIME type,
7
+ * the response size, and the current context.
8
+ */
9
+ type ThresholdFunction = (
10
+ /** MIME type of the response */
11
+ type: string,
12
+ /** Size of the response in bytes */
13
+ size: number,
14
+ /** Context of the request */
15
+ ctx: Koa.Context,
16
+ ) => number | string | ThresholdFunction;
17
+
18
+ /**
19
+ * Computes `deflate`/`gzip` compression options from the MIME type,
20
+ * the response size, and the current context.
21
+ */
22
+ type ZlibOptionsFunction = (
23
+ /** MIME type of the response */
24
+ type: string,
25
+ /** Size of the response in bytes */
26
+ size: number,
27
+ /** Context of the request */
28
+ ctx: Koa.Context,
29
+ ) => boolean | null | ZlibOptions | ZlibOptionsFunction;
30
+
31
+ /**
32
+ * Computes `brotli` compression options from the MIME type,
33
+ * the response size, and the current context.
34
+ */
35
+ type BrotliOptionsFunction = (
36
+ /** MIME type of the response */
37
+ type: string,
38
+ /** Size of the response in bytes */
39
+ size: number,
40
+ /** Context of the request */
41
+ ctx: Koa.Context,
42
+ ) => boolean | null | BrotliOptions | BrotliOptionsFunction;
43
+
44
+ /**
45
+ * Computes `zstd` compression options from the MIME type,
46
+ * the response size, and the current context.
47
+ */
48
+ type ZstdOptionsFunction = (
49
+ /** MIME type of the response */
50
+ type: string,
51
+ /** Size of the response in bytes */
52
+ size: number,
53
+ /** Context of the request */
54
+ ctx: Koa.Context,
55
+ ) => boolean | null | ZstdOptions | ZstdOptionsFunction;
56
+
57
+ /**
58
+ * Options for the `koa-compress` middleware.
59
+ */
60
+ type CompressOptions = {
61
+ /**
62
+ * Predicate that decides whether a given MIME type should be compressed.
63
+ * Default: `compressible()`.
64
+ * @param type - MIME type of the response
65
+ * @returns `true` to compress, `false` to skip
66
+ */
67
+ filter?: (type: string) => boolean;
68
+ /**
69
+ * Minimum response size to compress. A number (bytes), a string
70
+ * parsed by `bytes()` (e.g. `"1mb"`), or a {@link ThresholdFunction}.
71
+ * Default: `1024`.
72
+ */
73
+ threshold?: number | string | ThresholdFunction;
74
+ /**
75
+ * Encoding assumed when the client sends no `Accept-Encoding` header.
76
+ * Set to `"*"` for spec-compliant behavior. Default: `"identity"`.
77
+ */
78
+ defaultEncoding?: string;
79
+ /**
80
+ * Encoding to use when `Accept-Encoding` is `"*"`. Default: `"gzip"`.
81
+ */
82
+ wildcardAcceptEncoding?: string;
83
+ /**
84
+ * Preferred encoding order used to break ties among equally weighted
85
+ * encodings. Lower index = higher priority.
86
+ * Default: `['zstd', 'br', 'gzip', 'deflate', 'identity']`.
87
+ */
88
+ encodingPreference?: string[];
89
+ /**
90
+ * Options passed to `zlib.createDeflate()`, or `false`/`null` to disable.
91
+ * Can be a {@link ZlibOptionsFunction} for per-response values.
92
+ * Default: `{}`.
93
+ */
94
+ deflate?: boolean | null | ZlibOptions | ZlibOptionsFunction;
95
+ /**
96
+ * Options passed to `zlib.createGzip()`, or `false`/`null` to disable.
97
+ * Can be a {@link ZlibOptionsFunction} for per-response values.
98
+ * Default: `{}`.
99
+ */
100
+ gzip?: boolean | null | ZlibOptions | ZlibOptionsFunction;
101
+ /**
102
+ * Options passed to `zlib.createBrotliCompress()`, or `false`/`null` to disable.
103
+ * Can be a {@link BrotliOptionsFunction} for per-response values.
104
+ * Default: `{params: {[zlib.constants.BROTLI_PARAM_QUALITY]: 4}}`.
105
+ */
106
+ br?: boolean | null | BrotliOptions | BrotliOptionsFunction;
107
+ /**
108
+ * Options passed to `zlib.createZstdCompress()`, or `false`/`null` to disable.
109
+ * Can be a {@link ZstdOptionsFunction} for per-response values.
110
+ * Default: `{}`.
111
+ */
112
+ zstd?: boolean | null | ZstdOptions | ZstdOptionsFunction;
113
+ };
114
+
115
+ /**
116
+ * Per-encoding options: built-in defaults merged with user-supplied values.
117
+ */
118
+ type EncodingOptions = {
119
+ [encoding: string]: BrotliOptions | ZlibOptions | ZstdOptions | undefined;
120
+ };
121
+
122
+ /**
123
+ * Koa middleware with additional properties for introspection.
124
+ */
125
+ interface CompressMiddleware extends Koa.Middleware {
126
+ /**
127
+ * Supported content encodings available in the current Node.js runtime,
128
+ * ordered by preference.
129
+ */
130
+ preferredEncodings: string[];
131
+ /**
132
+ * Resolved options for each supported encoding (built-in defaults
133
+ * merged with user-provided values).
134
+ */
135
+ encodingOptions: EncodingOptions;
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Creates a Koa middleware that compresses response bodies via content
141
+ * negotiation. The returned function also exposes
142
+ * {@link compress.CompressMiddleware.preferredEncodings | preferredEncodings} and
143
+ * {@link compress.CompressMiddleware.encodingOptions | encodingOptions}.
144
+ *
145
+ * @param options - Compression options. See {@link compress.CompressOptions}.
146
+ * @returns Koa middleware with introspection properties.
147
+ */
148
+ declare function compress(options?: compress.CompressOptions): compress.CompressMiddleware;
149
+
150
+ declare module "koa" {
151
+ interface DefaultContext {
152
+ /**
153
+ * Per-response compression override.
154
+ * Set to `false` to disable compression, `true` to force it
155
+ * (bypassing the filter), or a `CompressOptions` object whose
156
+ * properties override the defaults for this response.
157
+ */
158
+ compress?: boolean | compress.CompressOptions;
159
+ }
160
+ }
161
+
162
+ export = compress;
package/lib/index.js CHANGED
@@ -1,3 +1,5 @@
1
+ // @ts-self-types="./index.d.ts"
2
+
1
3
  'use strict'
2
4
 
3
5
  /**
@@ -7,19 +9,43 @@
7
9
  const compressible = require('compressible')
8
10
  const isJSON = require('koa-is-json')
9
11
  const Stream = require('stream')
12
+ const zlib = require('zlib')
10
13
  const bytes = require('bytes')
14
+ const Negotiator = require('negotiator')
15
+
16
+ const encodingMethods = {
17
+ zstd: zlib.createZstdCompress,
18
+ br: zlib.createBrotliCompress,
19
+ gzip: zlib.createGzip,
20
+ deflate: zlib.createDeflate
21
+ }
22
+
23
+ const encodingDefaultPreference = ['zstd', 'br', 'gzip', 'deflate', 'identity']
24
+
25
+ const preferredEncodings = encodingDefaultPreference.filter(
26
+ (encoding) => encodingMethods[encoding] || encoding === 'identity'
27
+ )
11
28
 
12
- const Encodings = require('./encodings')
29
+ const encodingMethodDefaultOptions = {
30
+ gzip: {},
31
+ deflate: {},
32
+ br: {
33
+ params: {
34
+ [zlib.constants.BROTLI_PARAM_QUALITY]: 4
35
+ }
36
+ },
37
+ zstd: {}
38
+ }
13
39
 
14
40
  /**
15
- * Regex to match no-transform directive in a cache-control header
16
- */
41
+ * Regex to match no-transform directive in a cache-control header
42
+ */
17
43
  const NO_TRANSFORM_REGEX = /(?:^|,)\s*?no-transform\s*?(?:,|$)/
18
44
 
19
45
  /**
20
- * empty body statues.
46
+ * empty body statuses.
21
47
  */
22
- const emptyBodyStatues = new Set([204, 205, 304])
48
+ const emptyBodyStatuses = new Set([204, 205, 304])
23
49
 
24
50
  /**
25
51
  * Compress middleware.
@@ -30,18 +56,29 @@ const emptyBodyStatues = new Set([204, 205, 304])
30
56
  */
31
57
 
32
58
  module.exports = (options = {}) => {
33
- let { filter = compressible, threshold = 1024, defaultEncoding = 'identity' } = options
34
- if (typeof threshold === 'string') threshold = bytes(threshold)
59
+ let {
60
+ filter = compressible,
61
+ defaultEncoding,
62
+ wildcardAcceptEncoding,
63
+ encodingPreference = encodingDefaultPreference
64
+ } = options
65
+ if (!wildcardAcceptEncoding || wildcardAcceptEncoding === '*') {
66
+ wildcardAcceptEncoding = 'gzip'
67
+ }
68
+ defaultEncoding =
69
+ defaultEncoding === '*'
70
+ ? wildcardAcceptEncoding
71
+ : !defaultEncoding
72
+ ? 'identity'
73
+ : defaultEncoding
35
74
 
36
- // `options.br = false` would remove it as a preferred encoding
37
- const preferredEncodings = Encodings.preferredEncodings.filter((encoding) => options[encoding] !== false && options[encoding] !== null)
38
75
  const encodingOptions = {}
39
- preferredEncodings.forEach((encoding) => {
76
+ for (const encoding of preferredEncodings) {
40
77
  encodingOptions[encoding] = {
41
- ...Encodings.encodingMethodDefaultOptions[encoding],
42
- ...(options[encoding] || {})
78
+ ...encodingMethodDefaultOptions[encoding],
79
+ ...options[encoding]
43
80
  }
44
- })
81
+ }
45
82
 
46
83
  Object.assign(compressMiddleware, {
47
84
  preferredEncodings,
@@ -56,27 +93,66 @@ module.exports = (options = {}) => {
56
93
  await next()
57
94
 
58
95
  let { body } = ctx
96
+ const { type, length: size } = ctx.response
59
97
  if (
60
98
  // early exit if there's no content body or the body is already encoded
61
99
  !body ||
62
- ctx.res.headersSent || !ctx.writable ||
100
+ ctx.res.headersSent ||
101
+ !ctx.writable ||
63
102
  ctx.compress === false ||
64
103
  ctx.request.method === 'HEAD' ||
65
- emptyBodyStatues.has(+ctx.response.status) ||
104
+ emptyBodyStatuses.has(+ctx.response.status) ||
66
105
  ctx.response.get('Content-Encoding') ||
67
106
  // forced compression or implied
68
- !(ctx.compress === true || filter(ctx.response.type)) ||
107
+ !(ctx.compress === true || filter(type)) ||
69
108
  // don't compress for Cache-Control: no-transform
70
109
  // 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
110
+ NO_TRANSFORM_REGEX.test(ctx.response.get('Cache-Control'))
111
+ ) {
112
+ return
113
+ }
114
+
115
+ // calculate "local" compression options
116
+ const responseOptions = { ...options, ...ctx.compress }
117
+ let { threshold = 1024 } = responseOptions
118
+ while (typeof threshold === 'function') {
119
+ threshold = threshold(type, size, ctx)
120
+ }
121
+ if (typeof threshold === 'string') threshold = bytes(threshold)
122
+
123
+ // don't compress if the current response is below the threshold
124
+ if (threshold && size < threshold) return
75
125
 
76
126
  // get the preferred content encoding
77
- const encodings = new Encodings({ preferredEncodings })
78
- encodings.parseAcceptEncoding(ctx.request.headers['accept-encoding'] || defaultEncoding)
79
- const encoding = encodings.getPreferredContentEncoding()
127
+ for (const encoding of compressMiddleware.preferredEncodings) {
128
+ // calculate compressor options, if any
129
+ if (!(encoding in responseOptions)) continue
130
+ let compressor = responseOptions[encoding]
131
+ while (typeof compressor === 'function') {
132
+ compressor = compressor(type, size, ctx)
133
+ }
134
+ responseOptions[encoding] = compressor
135
+ }
136
+
137
+ const finalEncodings = compressMiddleware.preferredEncodings.filter(
138
+ (encoding) =>
139
+ responseOptions[encoding] !== false &&
140
+ responseOptions[encoding] !== null
141
+ )
142
+
143
+ const acceptEncoding = ctx.request.headers['accept-encoding']
144
+ let request = ctx.request
145
+ if (acceptEncoding === '*') {
146
+ request = { headers: { 'accept-encoding': wildcardAcceptEncoding } }
147
+ } else if (!acceptEncoding) {
148
+ request = { headers: { 'accept-encoding': defaultEncoding } }
149
+ }
150
+
151
+ const negotiator = new Negotiator(request)
152
+ // if no encodings are supported, negotiator returns 'identity'
153
+ const encoding = negotiator.encoding(finalEncodings, {
154
+ preferred: encodingPreference
155
+ })
80
156
 
81
157
  // identity === no compression
82
158
  if (encoding === 'identity') return
@@ -89,8 +165,9 @@ module.exports = (options = {}) => {
89
165
  ctx.set('Content-Encoding', encoding)
90
166
  ctx.res.removeHeader('Content-Length')
91
167
 
92
- const compress = Encodings.encodingMethods[encoding]
93
- const stream = ctx.body = compress(encodingOptions[encoding])
168
+ const compress = encodingMethods[encoding]
169
+ const compressOptions = { ...encodingMethodDefaultOptions[encoding], ...responseOptions[encoding] }
170
+ const stream = (ctx.body = compress(compressOptions))
94
171
 
95
172
  if (body instanceof Stream) return body.pipe(stream)
96
173
  stream.end(body)
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "koa-compress",
3
3
  "description": "Compress middleware for koa",
4
- "version": "5.1.1",
4
+ "version": "5.2.1",
5
+ "type": "commonjs",
5
6
  "author": {
6
7
  "name": "Jonathan Ong",
7
8
  "email": "me@jongleberry.com",
@@ -13,6 +14,11 @@
13
14
  "name": "Imed Jaberi",
14
15
  "email": "imed-jaberi@outlook.com",
15
16
  "url": "https://www.3imed-jaberi.com/"
17
+ },
18
+ {
19
+ "name": "Eugene Lazutkin",
20
+ "email": "eugene.lazutkin@gmail.com",
21
+ "url": "https://www.lazutkin.com/"
16
22
  }
17
23
  ],
18
24
  "license": "MIT",
@@ -20,23 +26,29 @@
20
26
  "dependencies": {
21
27
  "bytes": "^3.1.2",
22
28
  "compressible": "^2.0.18",
23
- "http-errors": "^1.8.1",
24
- "koa-is-json": "^1.0.0"
29
+ "http-errors": "^2.0.1",
30
+ "koa-is-json": "^1.0.0",
31
+ "negotiator": "^1.0.0"
25
32
  },
26
33
  "devDependencies": {
27
- "eslint": "^7.32.0",
28
- "eslint-config-standard": "^16.0.3",
29
- "eslint-plugin-import": "^2.26.0",
30
- "eslint-plugin-node": "^11.1.0",
31
- "eslint-plugin-promise": "^5.2.0",
32
- "eslint-plugin-standard": "^5.0.0",
33
- "jest": "^27.5.1",
34
- "koa": "^2.13.4",
35
- "supertest": "^6.2.4"
34
+ "@types/koa": "^3.0.1",
35
+ "@types/node": "^25.5.0",
36
+ "c8": "^11.0.0",
37
+ "jest": "^30.3.0",
38
+ "koa": "^3.1.2",
39
+ "snazzy": "^9.0.0",
40
+ "standard": "^17.1.2",
41
+ "supertest": "^7.2.2",
42
+ "typescript": "^5.9.3"
36
43
  },
37
44
  "scripts": {
38
- "eslint": "eslint --ignore-path .gitignore .",
39
- "test": "jest"
45
+ "test": "jest",
46
+ "test:coverage": "c8 --reporter=lcov --reporter=text-summary jest --coverage",
47
+ "lint": "standard",
48
+ "lint:fix": "standard --fix",
49
+ "lint:pretty": "standard | snazzy",
50
+ "ts-check": "tsc --noEmit",
51
+ "authors": "git log --format='%aN <%aE>' | sort -u > AUTHORS"
40
52
  },
41
53
  "files": [
42
54
  "lib",
@@ -44,7 +56,21 @@
44
56
  "HISTORY.md"
45
57
  ],
46
58
  "main": "lib",
59
+ "types": "lib/index.d.ts",
47
60
  "engines": {
48
61
  "node": ">= 12"
62
+ },
63
+ "standard": {
64
+ "globals": [
65
+ "afterAll",
66
+ "beforeAll",
67
+ "afterEach",
68
+ "jasmine",
69
+ "describe",
70
+ "test",
71
+ "jest",
72
+ "expect",
73
+ "it"
74
+ ]
49
75
  }
50
76
  }
package/lib/encodings.js DELETED
@@ -1,104 +0,0 @@
1
-
2
- // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding
3
- const errors = require('http-errors')
4
- const zlib = require('zlib')
5
-
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)
28
-
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
- }
36
-
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
- }
80
- }
81
-
82
- Encodings.encodingMethods = {
83
- gzip: zlib.createGzip,
84
- deflate: zlib.createDeflate,
85
- br: zlib.createBrotliCompress
86
- }
87
-
88
- Encodings.encodingMethodDefaultOptions = {
89
- gzip: {},
90
- deflate: {},
91
- br: {
92
- params: {
93
- [zlib.constants.BROTLI_PARAM_QUALITY]: 4
94
- }
95
- }
96
- }
97
-
98
- // how we treat `Accept-Encoding: *`
99
- Encodings.wildcardAcceptEncoding = ['gzip', 'deflate']
100
- // our preferred encodings
101
- Encodings.preferredEncodings = ['br', 'gzip', 'deflate']
102
- Encodings.reDirective = /^\s*(gzip|compress|deflate|br|identity|\*)\s*(?:;\s*q\s*=\s*(\d(?:\.\d)?))?\s*$/
103
-
104
- module.exports = Encodings