@httpland/compression-middleware 1.0.0-beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +208 -0
  3. package/esm/deps/deno.land/std@0.180.0/http/_negotiation/common.js +40 -0
  4. package/esm/deps/deno.land/std@0.180.0/http/_negotiation/encoding.js +121 -0
  5. package/esm/deps/deno.land/std@0.180.0/http/_negotiation/language.js +110 -0
  6. package/esm/deps/deno.land/std@0.180.0/http/_negotiation/media_type.js +174 -0
  7. package/esm/deps/deno.land/std@0.180.0/http/negotiation.js +36 -0
  8. package/esm/deps/deno.land/std@0.180.0/media_types/_db.js +33 -0
  9. package/esm/deps/deno.land/std@0.180.0/media_types/_util.js +122 -0
  10. package/esm/deps/deno.land/std@0.180.0/media_types/content_type.js +54 -0
  11. package/esm/deps/deno.land/std@0.180.0/media_types/extension.js +25 -0
  12. package/esm/deps/deno.land/std@0.180.0/media_types/extensions_by_type.js +30 -0
  13. package/esm/deps/deno.land/std@0.180.0/media_types/format_media_type.js +60 -0
  14. package/esm/deps/deno.land/std@0.180.0/media_types/get_charset.js +36 -0
  15. package/esm/deps/deno.land/std@0.180.0/media_types/mod.js +22 -0
  16. package/esm/deps/deno.land/std@0.180.0/media_types/parse_media_type.js +121 -0
  17. package/esm/deps/deno.land/std@0.180.0/media_types/type_by_extension.js +23 -0
  18. package/esm/deps/deno.land/std@0.180.0/media_types/vendor/mime-db.v1.52.0.js +8555 -0
  19. package/esm/deps/deno.land/x/vary@1.0.0/mod.js +108 -0
  20. package/esm/deps.js +8 -0
  21. package/esm/encoders/deflate.js +11 -0
  22. package/esm/encoders/gzip.js +11 -0
  23. package/esm/encoders/utils.js +7 -0
  24. package/esm/middleware.js +55 -0
  25. package/esm/mod.js +3 -0
  26. package/esm/package.json +3 -0
  27. package/esm/transform.js +23 -0
  28. package/esm/types.js +3 -0
  29. package/esm/utils.js +19 -0
  30. package/package.json +55 -0
  31. package/script/deps/deno.land/std@0.180.0/http/_negotiation/common.js +45 -0
  32. package/script/deps/deno.land/std@0.180.0/http/_negotiation/encoding.js +125 -0
  33. package/script/deps/deno.land/std@0.180.0/http/_negotiation/language.js +114 -0
  34. package/script/deps/deno.land/std@0.180.0/http/_negotiation/media_type.js +178 -0
  35. package/script/deps/deno.land/std@0.180.0/http/negotiation.js +42 -0
  36. package/script/deps/deno.land/std@0.180.0/media_types/_db.js +39 -0
  37. package/script/deps/deno.land/std@0.180.0/media_types/_util.js +132 -0
  38. package/script/deps/deno.land/std@0.180.0/media_types/content_type.js +58 -0
  39. package/script/deps/deno.land/std@0.180.0/media_types/extension.js +29 -0
  40. package/script/deps/deno.land/std@0.180.0/media_types/extensions_by_type.js +34 -0
  41. package/script/deps/deno.land/std@0.180.0/media_types/format_media_type.js +64 -0
  42. package/script/deps/deno.land/std@0.180.0/media_types/get_charset.js +40 -0
  43. package/script/deps/deno.land/std@0.180.0/media_types/mod.js +38 -0
  44. package/script/deps/deno.land/std@0.180.0/media_types/parse_media_type.js +125 -0
  45. package/script/deps/deno.land/std@0.180.0/media_types/type_by_extension.js +27 -0
  46. package/script/deps/deno.land/std@0.180.0/media_types/vendor/mime-db.v1.52.0.js +8557 -0
  47. package/script/deps/deno.land/x/vary@1.0.0/mod.js +113 -0
  48. package/script/deps.js +22 -0
  49. package/script/encoders/deflate.js +14 -0
  50. package/script/encoders/gzip.js +14 -0
  51. package/script/encoders/utils.js +10 -0
  52. package/script/middleware.js +61 -0
  53. package/script/mod.js +7 -0
  54. package/script/package.json +3 -0
  55. package/script/transform.js +27 -0
  56. package/script/types.js +4 -0
  57. package/script/utils.js +24 -0
  58. package/types/deps/deno.land/std@0.180.0/http/_negotiation/common.d.ts +37 -0
  59. package/types/deps/deno.land/std@0.180.0/http/_negotiation/encoding.d.ts +33 -0
  60. package/types/deps/deno.land/std@0.180.0/http/_negotiation/language.d.ts +30 -0
  61. package/types/deps/deno.land/std@0.180.0/http/_negotiation/media_type.d.ts +30 -0
  62. package/types/deps/deno.land/std@0.180.0/http/negotiation.d.ts +126 -0
  63. package/types/deps/deno.land/std@0.180.0/media_types/_db.d.ts +5 -0
  64. package/types/deps/deno.land/std@0.180.0/media_types/_util.d.ts +21 -0
  65. package/types/deps/deno.land/std@0.180.0/media_types/content_type.d.ts +38 -0
  66. package/types/deps/deno.land/std@0.180.0/media_types/extension.d.ts +17 -0
  67. package/types/deps/deno.land/std@0.180.0/media_types/extensions_by_type.d.ts +20 -0
  68. package/types/deps/deno.land/std@0.180.0/media_types/format_media_type.d.ts +16 -0
  69. package/types/deps/deno.land/std@0.180.0/media_types/get_charset.d.ts +15 -0
  70. package/types/deps/deno.land/std@0.180.0/media_types/mod.d.ts +20 -0
  71. package/types/deps/deno.land/std@0.180.0/media_types/parse_media_type.d.ts +37 -0
  72. package/types/deps/deno.land/std@0.180.0/media_types/type_by_extension.d.ts +17 -0
  73. package/types/deps/deno.land/std@0.180.0/media_types/vendor/mime-db.v1.52.0.d.ts +8518 -0
  74. package/types/deps/deno.land/x/vary@1.0.0/mod.d.ts +23 -0
  75. package/types/deps.d.ts +7 -0
  76. package/types/encoders/deflate.d.ts +3 -0
  77. package/types/encoders/gzip.d.ts +3 -0
  78. package/types/encoders/utils.d.ts +4 -0
  79. package/types/middleware.d.ts +28 -0
  80. package/types/mod.d.ts +2 -0
  81. package/types/transform.d.ts +3 -0
  82. package/types/types.d.ts +15 -0
  83. package/types/utils.d.ts +2 -0
package/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 httpland
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,208 @@
1
+ # compression-middleware
2
+
3
+ [![deno land](http://img.shields.io/badge/available%20on-deno.land/x-lightgrey.svg?logo=deno)](https://deno.land/x/compression_middleware)
4
+ [![deno doc](https://doc.deno.land/badge.svg)](https://doc.deno.land/https/deno.land/x/compression_middleware/mod.ts)
5
+ [![GitHub release (latest by date)](https://img.shields.io/github/v/release/httpland/compression-middleware)](https://github.com/httpland/compression-middleware/releases)
6
+ [![codecov](https://codecov.io/github/httpland/compression-middleware/branch/main/graph/badge.svg)](https://codecov.io/gh/httpland/compression-middleware)
7
+ [![GitHub](https://img.shields.io/github/license/httpland/compression-middleware)](https://github.com/httpland/compression-middleware/blob/main/LICENSE)
8
+
9
+ [![test](https://github.com/httpland/compression-middleware/actions/workflows/test.yaml/badge.svg)](https://github.com/httpland/compression-middleware/actions/workflows/test.yaml)
10
+ [![NPM](https://nodei.co/npm/@httpland/compression-middleware.png?mini=true)](https://nodei.co/npm/@httpland/compression-middleware/)
11
+
12
+ HTTP compression middleware.
13
+
14
+ Compresses HTTP Content(body).
15
+
16
+ Compliant with
17
+ [RFC 9110, 8.4. Content-Encoding](https://www.rfc-editor.org/rfc/rfc9110.html#section-8.4)
18
+ and
19
+ [RFC 9110, 12.5.3. Accept-Encoding](https://www.rfc-editor.org/rfc/rfc9110.html#name-accept-encoding)
20
+ .
21
+
22
+ ## Middleware
23
+
24
+ For a definition of Universal HTTP middleware, see the
25
+ [http-middleware](https://github.com/httpland/http-middleware) project.
26
+
27
+ ## Usage
28
+
29
+ Middleware convert message body and adds the `Content-Encoding` header to the
30
+ response.
31
+
32
+ Also, safely add `Accept-Encoding` to the `vary` header in response.
33
+
34
+ ```ts
35
+ import { compression } from "https://deno.land/x/compression_middleware@$VERSION/mod.ts";
36
+ import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
37
+
38
+ const middleware = compression();
39
+ const request = new Request("test:", {
40
+ headers: {
41
+ "accept-encoding": "deflate;q=0.5, gzip;q=1.0",
42
+ },
43
+ });
44
+
45
+ const response = await middleware(
46
+ request,
47
+ () => new Response("<body>"),
48
+ );
49
+
50
+ assertEquals(await response.text(), "<gzip:body>");
51
+ assertEquals(response.headers.get("content-encoding"), "gzip");
52
+ ```
53
+
54
+ yield:
55
+
56
+ ```http
57
+ Content-Encoding: <encoding>
58
+ Vary: accept-encoding
59
+ ```
60
+
61
+ ## Built-in encoding
62
+
63
+ Middleware supports the following encodings by default:
64
+
65
+ - gzip
66
+ - deflate
67
+
68
+ ## Additional encoding
69
+
70
+ You can add supported encoding.
71
+
72
+ There are two ways to add encodings:
73
+
74
+ - Encoding map
75
+ - Encoder list
76
+
77
+ In either style, the result is the same.
78
+
79
+ When encoding is added, a shallow merge is performed in favor of user-defined.
80
+
81
+ ## Encoding map
82
+
83
+ Encoding map defines a map of encoding formats and
84
+ [encode functions](#encode-api) .
85
+
86
+ Example of adding `brotli` encoding:
87
+
88
+ ```ts
89
+ import {
90
+ compression,
91
+ type Encode,
92
+ type EncodingMap,
93
+ } from "https://deno.land/x/compression_middleware@$VERSION/mod.ts";
94
+
95
+ declare const encodeBr: Encode;
96
+ const encodingMap: EncodingMap = { br: encodeBr };
97
+
98
+ const middleware = compression(encodingMap);
99
+ ```
100
+
101
+ ## Encoder list
102
+
103
+ Encoder list defines a list of `Encoder`.
104
+
105
+ `Encoder` is a following structured object:
106
+
107
+ | Name | Type | Description |
108
+ | -------- | --------------------- | ---------------- |
109
+ | encoding | `string` | Encoding format. |
110
+ | encode | [Encode](#encode-api) | Encode stream. |
111
+
112
+ Example of adding `brotli` encoding:
113
+
114
+ ```ts
115
+ import {
116
+ compression,
117
+ type Encode,
118
+ type Encoder,
119
+ } from "https://deno.land/x/compression_middleware@$VERSION/mod.ts";
120
+
121
+ declare const encodeBr: Encode;
122
+ const Br: Encoder = { encoding: "br", encode: encodeBr };
123
+
124
+ const middleware = compression([Br]);
125
+ ```
126
+
127
+ ## Encode API
128
+
129
+ `Encode` is an API for converting content.
130
+
131
+ ```ts
132
+ interface Encode {
133
+ (stream: ReadableStream<Uint8Array>): BodyInit | Promise<BodyInit>;
134
+ }
135
+ ```
136
+
137
+ ## Compressible
138
+
139
+ Filter media types to be compressed according to the following
140
+ [specifications](https://www.rfc-editor.org/rfc/rfc9110.html#section-8.4-8):
141
+
142
+ > If the media type includes an inherent encoding, such as a data format that is
143
+ > always compressed, then that encoding would not be restated in
144
+ > Content-Encoding even if it happens to be the same algorithm as one of the
145
+ > content codings.
146
+
147
+ Determine from the `Content-Type` response header whether the representation is
148
+ compressible.
149
+
150
+ For example, image media such as `image/jpag` is not compressible because it is
151
+ already compressed.
152
+
153
+ ## Content-Length
154
+
155
+ If the Response has a `Content-Length` header, compression may cause the actual
156
+ content length to deviate.
157
+
158
+ In that case, the `Content-Length` is recalculated.
159
+
160
+ ```ts
161
+ import { compression } from "https://deno.land/x/compression_middleware@$VERSION/mod.ts";
162
+ import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
163
+
164
+ declare const request: Request;
165
+ const middleware = compression();
166
+
167
+ const response = await middleware(
168
+ request,
169
+ () =>
170
+ new Response("<body>", { headers: { "content-length": "<body:length>" } }),
171
+ );
172
+
173
+ assertEquals(await response.text(), "<gzip:body>");
174
+ assertEquals(response.headers.get("content-length"), "<gzip:body:length>");
175
+ ```
176
+
177
+ ## Effects
178
+
179
+ Middleware may make changes to the following elements of the HTTP message.
180
+
181
+ - HTTP Content
182
+ - HTTP Headers
183
+ - Content-Encoding
184
+ - Vary
185
+ - [Content-Length](#content-length)
186
+
187
+ ## Conditions
188
+
189
+ Middleware is executed if all of the following conditions are met:
190
+
191
+ - Encoding matches `Accept-Encoding` header in request
192
+ - `Content-Type` header exists in response
193
+ - `Content-Encoding` header does not exists in response
194
+ - `Cache-Control` header does not have `no-transform` directive in response
195
+ - Response body exists
196
+ - Response body is readable
197
+ - Response body is [compressible](#compressible)
198
+
199
+ ## API
200
+
201
+ All APIs can be found in the
202
+ [deno doc](https://doc.deno.land/https/deno.land/x/compression_middleware/mod.ts).
203
+
204
+ ## License
205
+
206
+ Copyright © 2023-present [httpland](https://github.com/httpland).
207
+
208
+ Released under the [MIT](./LICENSE) license
@@ -0,0 +1,40 @@
1
+ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
2
+ /*!
3
+ * Adapted directly from negotiator at https://github.com/jshttp/negotiator/
4
+ * which is licensed as follows:
5
+ *
6
+ * (The MIT License)
7
+ *
8
+ * Copyright (c) 2012-2014 Federico Romero
9
+ * Copyright (c) 2012-2014 Isaac Z. Schlueter
10
+ * Copyright (c) 2014-2015 Douglas Christopher Wilson
11
+ *
12
+ * Permission is hereby granted, free of charge, to any person obtaining
13
+ * a copy of this software and associated documentation files (the
14
+ * 'Software'), to deal in the Software without restriction, including
15
+ * without limitation the rights to use, copy, modify, merge, publish,
16
+ * distribute, sublicense, and/or sell copies of the Software, and to
17
+ * permit persons to whom the Software is furnished to do so, subject to
18
+ * the following conditions:
19
+ *
20
+ * The above copyright notice and this permission notice shall be
21
+ * included in all copies or substantial portions of the Software.
22
+ *
23
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
24
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
26
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
27
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
28
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
29
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30
+ */
31
+ export function compareSpecs(a, b) {
32
+ return (b.q - a.q ||
33
+ (b.s ?? 0) - (a.s ?? 0) ||
34
+ (a.o ?? 0) - (b.o ?? 0) ||
35
+ a.i - b.i ||
36
+ 0);
37
+ }
38
+ export function isQuality(spec) {
39
+ return spec.q > 0;
40
+ }
@@ -0,0 +1,121 @@
1
+ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
2
+ /*!
3
+ * Adapted directly from negotiator at https://github.com/jshttp/negotiator/
4
+ * which is licensed as follows:
5
+ *
6
+ * (The MIT License)
7
+ *
8
+ * Copyright (c) 2012-2014 Federico Romero
9
+ * Copyright (c) 2012-2014 Isaac Z. Schlueter
10
+ * Copyright (c) 2014-2015 Douglas Christopher Wilson
11
+ *
12
+ * Permission is hereby granted, free of charge, to any person obtaining
13
+ * a copy of this software and associated documentation files (the
14
+ * 'Software'), to deal in the Software without restriction, including
15
+ * without limitation the rights to use, copy, modify, merge, publish,
16
+ * distribute, sublicense, and/or sell copies of the Software, and to
17
+ * permit persons to whom the Software is furnished to do so, subject to
18
+ * the following conditions:
19
+ *
20
+ * The above copyright notice and this permission notice shall be
21
+ * included in all copies or substantial portions of the Software.
22
+ *
23
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
24
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
26
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
27
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
28
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
29
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30
+ */
31
+ import { compareSpecs, isQuality } from "./common.js";
32
+ const simpleEncodingRegExp = /^\s*([^\s;]+)\s*(?:;(.*))?$/;
33
+ function parseEncoding(str, i) {
34
+ const match = simpleEncodingRegExp.exec(str);
35
+ if (!match) {
36
+ return undefined;
37
+ }
38
+ const encoding = match[1];
39
+ let q = 1;
40
+ if (match[2]) {
41
+ const params = match[2].split(";");
42
+ for (const param of params) {
43
+ const p = param.trim().split("=");
44
+ if (p[0] === "q") {
45
+ q = parseFloat(p[1]);
46
+ break;
47
+ }
48
+ }
49
+ }
50
+ return { encoding, q, i };
51
+ }
52
+ function specify(encoding, spec, i = -1) {
53
+ if (!spec.encoding) {
54
+ return;
55
+ }
56
+ let s = 0;
57
+ if (spec.encoding.toLocaleLowerCase() === encoding.toLocaleLowerCase()) {
58
+ s = 1;
59
+ }
60
+ else if (spec.encoding !== "*") {
61
+ return;
62
+ }
63
+ return {
64
+ i,
65
+ o: spec.i,
66
+ q: spec.q,
67
+ s,
68
+ };
69
+ }
70
+ function parseAcceptEncoding(accept) {
71
+ const accepts = accept.split(",");
72
+ const parsedAccepts = [];
73
+ let hasIdentity = false;
74
+ let minQuality = 1;
75
+ for (let i = 0; i < accepts.length; i++) {
76
+ const encoding = parseEncoding(accepts[i].trim(), i);
77
+ if (encoding) {
78
+ parsedAccepts.push(encoding);
79
+ hasIdentity = hasIdentity || !!specify("identity", encoding);
80
+ minQuality = Math.min(minQuality, encoding.q || 1);
81
+ }
82
+ }
83
+ if (!hasIdentity) {
84
+ parsedAccepts.push({
85
+ encoding: "identity",
86
+ q: minQuality,
87
+ i: accepts.length - 1,
88
+ });
89
+ }
90
+ return parsedAccepts;
91
+ }
92
+ function getEncodingPriority(encoding, accepted, index) {
93
+ let priority = { o: -1, q: 0, s: 0, i: 0 };
94
+ for (const s of accepted) {
95
+ const spec = specify(encoding, s, index);
96
+ if (spec &&
97
+ (priority.s - spec.s || priority.q - spec.q ||
98
+ priority.o - spec.o) <
99
+ 0) {
100
+ priority = spec;
101
+ }
102
+ }
103
+ return priority;
104
+ }
105
+ /** Given an `Accept-Encoding` string, parse out the encoding returning a
106
+ * negotiated encoding based on the `provided` encodings otherwise just a
107
+ * prioritized array of encodings. */
108
+ export function preferredEncodings(accept, provided) {
109
+ const accepts = parseAcceptEncoding(accept);
110
+ if (!provided) {
111
+ return accepts
112
+ .filter(isQuality)
113
+ .sort(compareSpecs)
114
+ .map((spec) => spec.encoding);
115
+ }
116
+ const priorities = provided.map((type, index) => getEncodingPriority(type, accepts, index));
117
+ return priorities
118
+ .filter(isQuality)
119
+ .sort(compareSpecs)
120
+ .map((priority) => provided[priorities.indexOf(priority)]);
121
+ }
@@ -0,0 +1,110 @@
1
+ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
2
+ /*!
3
+ * Adapted directly from negotiator at https://github.com/jshttp/negotiator/
4
+ * which is licensed as follows:
5
+ *
6
+ * (The MIT License)
7
+ *
8
+ * Copyright (c) 2012-2014 Federico Romero
9
+ * Copyright (c) 2012-2014 Isaac Z. Schlueter
10
+ * Copyright (c) 2014-2015 Douglas Christopher Wilson
11
+ *
12
+ * Permission is hereby granted, free of charge, to any person obtaining
13
+ * a copy of this software and associated documentation files (the
14
+ * 'Software'), to deal in the Software without restriction, including
15
+ * without limitation the rights to use, copy, modify, merge, publish,
16
+ * distribute, sublicense, and/or sell copies of the Software, and to
17
+ * permit persons to whom the Software is furnished to do so, subject to
18
+ * the following conditions:
19
+ *
20
+ * The above copyright notice and this permission notice shall be
21
+ * included in all copies or substantial portions of the Software.
22
+ *
23
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
24
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
26
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
27
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
28
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
29
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30
+ */
31
+ import { compareSpecs, isQuality } from "./common.js";
32
+ const SIMPLE_LANGUAGE_REGEXP = /^\s*([^\s\-;]+)(?:-([^\s;]+))?\s*(?:;(.*))?$/;
33
+ function parseLanguage(str, i) {
34
+ const match = SIMPLE_LANGUAGE_REGEXP.exec(str);
35
+ if (!match) {
36
+ return undefined;
37
+ }
38
+ const [, prefix, suffix] = match;
39
+ const full = suffix ? `${prefix}-${suffix}` : prefix;
40
+ let q = 1;
41
+ if (match[3]) {
42
+ const params = match[3].split(";");
43
+ for (const param of params) {
44
+ const [key, value] = param.trim().split("=");
45
+ if (key === "q") {
46
+ q = parseFloat(value);
47
+ break;
48
+ }
49
+ }
50
+ }
51
+ return { prefix, suffix, full, q, i };
52
+ }
53
+ function parseAcceptLanguage(accept) {
54
+ const accepts = accept.split(",");
55
+ const result = [];
56
+ for (let i = 0; i < accepts.length; i++) {
57
+ const language = parseLanguage(accepts[i].trim(), i);
58
+ if (language) {
59
+ result.push(language);
60
+ }
61
+ }
62
+ return result;
63
+ }
64
+ function specify(language, spec, i) {
65
+ const p = parseLanguage(language, i);
66
+ if (!p) {
67
+ return undefined;
68
+ }
69
+ let s = 0;
70
+ if (spec.full.toLowerCase() === p.full.toLowerCase()) {
71
+ s |= 4;
72
+ }
73
+ else if (spec.prefix.toLowerCase() === p.prefix.toLowerCase()) {
74
+ s |= 2;
75
+ }
76
+ else if (spec.full.toLowerCase() === p.prefix.toLowerCase()) {
77
+ s |= 1;
78
+ }
79
+ else if (spec.full !== "*") {
80
+ return;
81
+ }
82
+ return { i, o: spec.i, q: spec.q, s };
83
+ }
84
+ function getLanguagePriority(language, accepted, index) {
85
+ let priority = { i: -1, o: -1, q: 0, s: 0 };
86
+ for (const accepts of accepted) {
87
+ const spec = specify(language, accepts, index);
88
+ if (spec &&
89
+ ((priority.s ?? 0) - (spec.s ?? 0) || priority.q - spec.q ||
90
+ (priority.o ?? 0) - (spec.o ?? 0)) < 0) {
91
+ priority = spec;
92
+ }
93
+ }
94
+ return priority;
95
+ }
96
+ export function preferredLanguages(accept = "*", provided) {
97
+ const accepts = parseAcceptLanguage(accept);
98
+ if (!provided) {
99
+ return accepts
100
+ .filter(isQuality)
101
+ .sort(compareSpecs)
102
+ .map((spec) => spec.full);
103
+ }
104
+ const priorities = provided
105
+ .map((type, index) => getLanguagePriority(type, accepts, index));
106
+ return priorities
107
+ .filter(isQuality)
108
+ .sort(compareSpecs)
109
+ .map((priority) => provided[priorities.indexOf(priority)]);
110
+ }
@@ -0,0 +1,174 @@
1
+ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
2
+ /*!
3
+ * Adapted directly from negotiator at https://github.com/jshttp/negotiator/
4
+ * which is licensed as follows:
5
+ *
6
+ * (The MIT License)
7
+ *
8
+ * Copyright (c) 2012-2014 Federico Romero
9
+ * Copyright (c) 2012-2014 Isaac Z. Schlueter
10
+ * Copyright (c) 2014-2015 Douglas Christopher Wilson
11
+ *
12
+ * Permission is hereby granted, free of charge, to any person obtaining
13
+ * a copy of this software and associated documentation files (the
14
+ * 'Software'), to deal in the Software without restriction, including
15
+ * without limitation the rights to use, copy, modify, merge, publish,
16
+ * distribute, sublicense, and/or sell copies of the Software, and to
17
+ * permit persons to whom the Software is furnished to do so, subject to
18
+ * the following conditions:
19
+ *
20
+ * The above copyright notice and this permission notice shall be
21
+ * included in all copies or substantial portions of the Software.
22
+ *
23
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
24
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
26
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
27
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
28
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
29
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30
+ */
31
+ import { compareSpecs, isQuality } from "./common.js";
32
+ const simpleMediaTypeRegExp = /^\s*([^\s\/;]+)\/([^;\s]+)\s*(?:;(.*))?$/;
33
+ function quoteCount(str) {
34
+ let count = 0;
35
+ let index = 0;
36
+ while ((index = str.indexOf(`"`, index)) !== -1) {
37
+ count++;
38
+ index++;
39
+ }
40
+ return count;
41
+ }
42
+ function splitMediaTypes(accept) {
43
+ const accepts = accept.split(",");
44
+ let j = 0;
45
+ for (let i = 1; i < accepts.length; i++) {
46
+ if (quoteCount(accepts[j]) % 2 === 0) {
47
+ accepts[++j] = accepts[i];
48
+ }
49
+ else {
50
+ accepts[j] += `,${accepts[i]}`;
51
+ }
52
+ }
53
+ accepts.length = j + 1;
54
+ return accepts;
55
+ }
56
+ function splitParameters(str) {
57
+ const parameters = str.split(";");
58
+ let j = 0;
59
+ for (let i = 1; i < parameters.length; i++) {
60
+ if (quoteCount(parameters[j]) % 2 === 0) {
61
+ parameters[++j] = parameters[i];
62
+ }
63
+ else {
64
+ parameters[j] += `;${parameters[i]}`;
65
+ }
66
+ }
67
+ parameters.length = j + 1;
68
+ return parameters.map((p) => p.trim());
69
+ }
70
+ function splitKeyValuePair(str) {
71
+ const [key, value] = str.split("=");
72
+ return [key.toLowerCase(), value];
73
+ }
74
+ function parseMediaType(str, i) {
75
+ const match = simpleMediaTypeRegExp.exec(str);
76
+ if (!match) {
77
+ return;
78
+ }
79
+ const params = Object.create(null);
80
+ let q = 1;
81
+ const [, type, subtype, parameters] = match;
82
+ if (parameters) {
83
+ const kvps = splitParameters(parameters).map(splitKeyValuePair);
84
+ for (const [key, val] of kvps) {
85
+ const value = val && val[0] === `"` && val[val.length - 1] === `"`
86
+ ? val.slice(1, val.length - 1)
87
+ : val;
88
+ if (key === "q" && value) {
89
+ q = parseFloat(value);
90
+ break;
91
+ }
92
+ params[key] = value;
93
+ }
94
+ }
95
+ return { type, subtype, params, q, i };
96
+ }
97
+ function parseAccept(accept) {
98
+ const accepts = splitMediaTypes(accept);
99
+ const mediaTypes = [];
100
+ for (let i = 0; i < accepts.length; i++) {
101
+ const mediaType = parseMediaType(accepts[i].trim(), i);
102
+ if (mediaType) {
103
+ mediaTypes.push(mediaType);
104
+ }
105
+ }
106
+ return mediaTypes;
107
+ }
108
+ function getFullType(spec) {
109
+ return `${spec.type}/${spec.subtype}`;
110
+ }
111
+ function specify(type, spec, index) {
112
+ const p = parseMediaType(type, index);
113
+ if (!p) {
114
+ return;
115
+ }
116
+ let s = 0;
117
+ if (spec.type.toLowerCase() === p.type.toLowerCase()) {
118
+ s |= 4;
119
+ }
120
+ else if (spec.type !== "*") {
121
+ return;
122
+ }
123
+ if (spec.subtype.toLowerCase() === p.subtype.toLowerCase()) {
124
+ s |= 2;
125
+ }
126
+ else if (spec.subtype !== "*") {
127
+ return;
128
+ }
129
+ const keys = Object.keys(spec.params);
130
+ if (keys.length) {
131
+ if (keys.every((key) => (spec.params[key] || "").toLowerCase() ===
132
+ (p.params[key] || "").toLowerCase())) {
133
+ s |= 1;
134
+ }
135
+ else {
136
+ return;
137
+ }
138
+ }
139
+ return {
140
+ i: index,
141
+ o: spec.o,
142
+ q: spec.q,
143
+ s,
144
+ };
145
+ }
146
+ function getMediaTypePriority(type, accepted, index) {
147
+ let priority = { o: -1, q: 0, s: 0, i: index };
148
+ for (const accepts of accepted) {
149
+ const spec = specify(type, accepts, index);
150
+ if (spec &&
151
+ ((priority.s || 0) - (spec.s || 0) ||
152
+ (priority.q || 0) - (spec.q || 0) ||
153
+ (priority.o || 0) - (spec.o || 0)) < 0) {
154
+ priority = spec;
155
+ }
156
+ }
157
+ return priority;
158
+ }
159
+ export function preferredMediaTypes(accept, provided) {
160
+ const accepts = parseAccept(accept === undefined ? "*/*" : accept || "");
161
+ if (!provided) {
162
+ return accepts
163
+ .filter(isQuality)
164
+ .sort(compareSpecs)
165
+ .map(getFullType);
166
+ }
167
+ const priorities = provided.map((type, index) => {
168
+ return getMediaTypePriority(type, accepts, index);
169
+ });
170
+ return priorities
171
+ .filter(isQuality)
172
+ .sort(compareSpecs)
173
+ .map((priority) => provided[priorities.indexOf(priority)]);
174
+ }