@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.
- package/LICENSE +20 -0
- package/README.md +208 -0
- package/esm/deps/deno.land/std@0.180.0/http/_negotiation/common.js +40 -0
- package/esm/deps/deno.land/std@0.180.0/http/_negotiation/encoding.js +121 -0
- package/esm/deps/deno.land/std@0.180.0/http/_negotiation/language.js +110 -0
- package/esm/deps/deno.land/std@0.180.0/http/_negotiation/media_type.js +174 -0
- package/esm/deps/deno.land/std@0.180.0/http/negotiation.js +36 -0
- package/esm/deps/deno.land/std@0.180.0/media_types/_db.js +33 -0
- package/esm/deps/deno.land/std@0.180.0/media_types/_util.js +122 -0
- package/esm/deps/deno.land/std@0.180.0/media_types/content_type.js +54 -0
- package/esm/deps/deno.land/std@0.180.0/media_types/extension.js +25 -0
- package/esm/deps/deno.land/std@0.180.0/media_types/extensions_by_type.js +30 -0
- package/esm/deps/deno.land/std@0.180.0/media_types/format_media_type.js +60 -0
- package/esm/deps/deno.land/std@0.180.0/media_types/get_charset.js +36 -0
- package/esm/deps/deno.land/std@0.180.0/media_types/mod.js +22 -0
- package/esm/deps/deno.land/std@0.180.0/media_types/parse_media_type.js +121 -0
- package/esm/deps/deno.land/std@0.180.0/media_types/type_by_extension.js +23 -0
- package/esm/deps/deno.land/std@0.180.0/media_types/vendor/mime-db.v1.52.0.js +8555 -0
- package/esm/deps/deno.land/x/vary@1.0.0/mod.js +108 -0
- package/esm/deps.js +8 -0
- package/esm/encoders/deflate.js +11 -0
- package/esm/encoders/gzip.js +11 -0
- package/esm/encoders/utils.js +7 -0
- package/esm/middleware.js +55 -0
- package/esm/mod.js +3 -0
- package/esm/package.json +3 -0
- package/esm/transform.js +23 -0
- package/esm/types.js +3 -0
- package/esm/utils.js +19 -0
- package/package.json +55 -0
- package/script/deps/deno.land/std@0.180.0/http/_negotiation/common.js +45 -0
- package/script/deps/deno.land/std@0.180.0/http/_negotiation/encoding.js +125 -0
- package/script/deps/deno.land/std@0.180.0/http/_negotiation/language.js +114 -0
- package/script/deps/deno.land/std@0.180.0/http/_negotiation/media_type.js +178 -0
- package/script/deps/deno.land/std@0.180.0/http/negotiation.js +42 -0
- package/script/deps/deno.land/std@0.180.0/media_types/_db.js +39 -0
- package/script/deps/deno.land/std@0.180.0/media_types/_util.js +132 -0
- package/script/deps/deno.land/std@0.180.0/media_types/content_type.js +58 -0
- package/script/deps/deno.land/std@0.180.0/media_types/extension.js +29 -0
- package/script/deps/deno.land/std@0.180.0/media_types/extensions_by_type.js +34 -0
- package/script/deps/deno.land/std@0.180.0/media_types/format_media_type.js +64 -0
- package/script/deps/deno.land/std@0.180.0/media_types/get_charset.js +40 -0
- package/script/deps/deno.land/std@0.180.0/media_types/mod.js +38 -0
- package/script/deps/deno.land/std@0.180.0/media_types/parse_media_type.js +125 -0
- package/script/deps/deno.land/std@0.180.0/media_types/type_by_extension.js +27 -0
- package/script/deps/deno.land/std@0.180.0/media_types/vendor/mime-db.v1.52.0.js +8557 -0
- package/script/deps/deno.land/x/vary@1.0.0/mod.js +113 -0
- package/script/deps.js +22 -0
- package/script/encoders/deflate.js +14 -0
- package/script/encoders/gzip.js +14 -0
- package/script/encoders/utils.js +10 -0
- package/script/middleware.js +61 -0
- package/script/mod.js +7 -0
- package/script/package.json +3 -0
- package/script/transform.js +27 -0
- package/script/types.js +4 -0
- package/script/utils.js +24 -0
- package/types/deps/deno.land/std@0.180.0/http/_negotiation/common.d.ts +37 -0
- package/types/deps/deno.land/std@0.180.0/http/_negotiation/encoding.d.ts +33 -0
- package/types/deps/deno.land/std@0.180.0/http/_negotiation/language.d.ts +30 -0
- package/types/deps/deno.land/std@0.180.0/http/_negotiation/media_type.d.ts +30 -0
- package/types/deps/deno.land/std@0.180.0/http/negotiation.d.ts +126 -0
- package/types/deps/deno.land/std@0.180.0/media_types/_db.d.ts +5 -0
- package/types/deps/deno.land/std@0.180.0/media_types/_util.d.ts +21 -0
- package/types/deps/deno.land/std@0.180.0/media_types/content_type.d.ts +38 -0
- package/types/deps/deno.land/std@0.180.0/media_types/extension.d.ts +17 -0
- package/types/deps/deno.land/std@0.180.0/media_types/extensions_by_type.d.ts +20 -0
- package/types/deps/deno.land/std@0.180.0/media_types/format_media_type.d.ts +16 -0
- package/types/deps/deno.land/std@0.180.0/media_types/get_charset.d.ts +15 -0
- package/types/deps/deno.land/std@0.180.0/media_types/mod.d.ts +20 -0
- package/types/deps/deno.land/std@0.180.0/media_types/parse_media_type.d.ts +37 -0
- package/types/deps/deno.land/std@0.180.0/media_types/type_by_extension.d.ts +17 -0
- package/types/deps/deno.land/std@0.180.0/media_types/vendor/mime-db.v1.52.0.d.ts +8518 -0
- package/types/deps/deno.land/x/vary@1.0.0/mod.d.ts +23 -0
- package/types/deps.d.ts +7 -0
- package/types/encoders/deflate.d.ts +3 -0
- package/types/encoders/gzip.d.ts +3 -0
- package/types/encoders/utils.d.ts +4 -0
- package/types/middleware.d.ts +28 -0
- package/types/mod.d.ts +2 -0
- package/types/transform.d.ts +3 -0
- package/types/types.d.ts +15 -0
- package/types/utils.d.ts +2 -0
@@ -0,0 +1,108 @@
|
|
1
|
+
/*!
|
2
|
+
* Based on https://github.com/jshttp/vary/blob/master/index.js
|
3
|
+
* Copyright(c) 2014-2017 Douglas Christopher Wilson
|
4
|
+
* Copyright(c) 2020 Henry Zhuang
|
5
|
+
* MIT Licensed
|
6
|
+
*/
|
7
|
+
/**
|
8
|
+
* RegExp to match field-name in RFC 7230 sec 3.2
|
9
|
+
*
|
10
|
+
* field-name = token
|
11
|
+
* token = 1*tchar
|
12
|
+
* tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
|
13
|
+
* / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
|
14
|
+
* / DIGIT / ALPHA
|
15
|
+
* ; any VCHAR, except delimiters
|
16
|
+
*/
|
17
|
+
const FIELD_NAME_REGEXP = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
|
18
|
+
/**
|
19
|
+
* Append a field to a vary header.
|
20
|
+
*
|
21
|
+
* @param {String} header
|
22
|
+
* @param {String|Array} field
|
23
|
+
* @return {String}
|
24
|
+
* @public
|
25
|
+
*/
|
26
|
+
export function append(header, field) {
|
27
|
+
// if (typeof header !== 'string') {
|
28
|
+
// throw new TypeError('header argument is required')
|
29
|
+
// }
|
30
|
+
// if (!field) {
|
31
|
+
// throw new TypeError('field argument is required')
|
32
|
+
// }
|
33
|
+
// existing, unspecified vary
|
34
|
+
if (header === "*") {
|
35
|
+
return header;
|
36
|
+
}
|
37
|
+
// get fields array
|
38
|
+
const fields = !Array.isArray(field) ? parse(String(field)) : field;
|
39
|
+
// assert on invalid field names
|
40
|
+
for (let j = 0; j < fields.length; j++) {
|
41
|
+
if (!FIELD_NAME_REGEXP.test(fields[j])) {
|
42
|
+
throw new TypeError(`field argument contains an invalid header name \`${fields[j]}\``);
|
43
|
+
}
|
44
|
+
}
|
45
|
+
// enumerate current values
|
46
|
+
let val = header;
|
47
|
+
const vals = parse(header.toLowerCase());
|
48
|
+
// unspecified vary
|
49
|
+
if (fields.indexOf("*") !== -1 || vals.indexOf("*") !== -1) {
|
50
|
+
return "*";
|
51
|
+
}
|
52
|
+
for (let i = 0; i < fields.length; i++) {
|
53
|
+
const fld = fields[i].toLowerCase();
|
54
|
+
// append value (case-preserving)
|
55
|
+
if (vals.indexOf(fld) === -1) {
|
56
|
+
vals.push(fld);
|
57
|
+
val = val ? val + ", " + fields[i] : fields[i];
|
58
|
+
}
|
59
|
+
}
|
60
|
+
return val;
|
61
|
+
}
|
62
|
+
/**
|
63
|
+
* Parse a vary header into an array.
|
64
|
+
*
|
65
|
+
* @param {String} header
|
66
|
+
* @return {Array}
|
67
|
+
* @private
|
68
|
+
*/
|
69
|
+
function parse(header) {
|
70
|
+
let end = 0;
|
71
|
+
const list = [];
|
72
|
+
let start = 0;
|
73
|
+
// gather tokens
|
74
|
+
for (let i = 0, len = header.length; i < len; i++) {
|
75
|
+
switch (header.charCodeAt(i)) {
|
76
|
+
case 0x20: /* */
|
77
|
+
if (start === end) {
|
78
|
+
start = end = i + 1;
|
79
|
+
}
|
80
|
+
break;
|
81
|
+
case 0x2c: /* , */
|
82
|
+
list.push(header.substring(start, end));
|
83
|
+
start = end = i + 1;
|
84
|
+
break;
|
85
|
+
default:
|
86
|
+
end = i + 1;
|
87
|
+
break;
|
88
|
+
}
|
89
|
+
}
|
90
|
+
// final token
|
91
|
+
list.push(header.substring(start, end));
|
92
|
+
return list;
|
93
|
+
}
|
94
|
+
/**
|
95
|
+
* Mark that a request is varied on a header field.
|
96
|
+
*
|
97
|
+
* @param {Headers} header
|
98
|
+
* @param {String|Array} field
|
99
|
+
* @public
|
100
|
+
*/
|
101
|
+
export function vary(header, field) {
|
102
|
+
// get existing header
|
103
|
+
let val = header.get("vary") || "";
|
104
|
+
// set new header
|
105
|
+
if ((val = append(val, field))) {
|
106
|
+
header.set("vary", val);
|
107
|
+
}
|
108
|
+
}
|
package/esm/deps.js
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
// Copyright 2023-latest the httpland authors. All rights reserved. MIT license.
|
2
|
+
// This module is browser compatible.
|
3
|
+
export { CachingHeader, ContentNegotiationHeader, RepresentationHeader, } from "@httpland/http-utils";
|
4
|
+
export { acceptsEncodings } from "./deps/deno.land/std@0.180.0/http/negotiation.js";
|
5
|
+
export { parseMediaType } from "./deps/deno.land/std@0.180.0/media_types/mod.js";
|
6
|
+
export { isNull } from "isxx";
|
7
|
+
export { default as compressible } from "compressible";
|
8
|
+
export { vary } from "./deps/deno.land/x/vary@1.0.0/mod.js";
|
@@ -0,0 +1,11 @@
|
|
1
|
+
// Copyright 2023-latest the httpland authors. All rights reserved. MIT license.
|
2
|
+
// This module is browser compatible.
|
3
|
+
import { Format } from "./utils.js";
|
4
|
+
function encodeDeflate(stream) {
|
5
|
+
return stream.pipeThrough(new CompressionStream(Format.Deflate));
|
6
|
+
}
|
7
|
+
/** Encoder for `deflate` */
|
8
|
+
export const Deflate = {
|
9
|
+
encoding: Format.Deflate,
|
10
|
+
encode: encodeDeflate,
|
11
|
+
};
|
@@ -0,0 +1,11 @@
|
|
1
|
+
// Copyright 2023-latest the httpland authors. All rights reserved. MIT license.
|
2
|
+
// This module is browser compatible.
|
3
|
+
import { Format } from "./utils.js";
|
4
|
+
function encodeGzip(stream) {
|
5
|
+
return stream.pipeThrough(new CompressionStream(Format.Gzip));
|
6
|
+
}
|
7
|
+
/** Encoder for `gzip` */
|
8
|
+
export const Gzip = {
|
9
|
+
encoding: Format.Gzip,
|
10
|
+
encode: encodeGzip,
|
11
|
+
};
|
@@ -0,0 +1,55 @@
|
|
1
|
+
// Copyright 2023-latest the httpland authors. All rights reserved. MIT license.
|
2
|
+
// This module is browser compatible.
|
3
|
+
import { acceptsEncodings, ContentNegotiationHeader, vary, } from "./deps.js";
|
4
|
+
import { withContentEncoding } from "./transform.js";
|
5
|
+
import { Gzip } from "./encoders/gzip.js";
|
6
|
+
import { Deflate } from "./encoders/deflate.js";
|
7
|
+
const DefaultEncoders = [Gzip, Deflate];
|
8
|
+
/** Create HTTP content compression middleware.
|
9
|
+
*
|
10
|
+
* @example
|
11
|
+
* ```ts
|
12
|
+
* import { compression } from "https://deno.land/x/compression_middleware@$VERSION/mod.ts";
|
13
|
+
* import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
|
14
|
+
*
|
15
|
+
* const middleware = compression();
|
16
|
+
* const request = new Request("test:", {
|
17
|
+
* headers: {
|
18
|
+
* "accept-encoding": "deflate;q=0.5, gzip;q=1.0, deflate-raw;q=0.3",
|
19
|
+
* },
|
20
|
+
* });
|
21
|
+
*
|
22
|
+
* const response = await middleware(
|
23
|
+
* request,
|
24
|
+
* () => new Response("<body>"),
|
25
|
+
* );
|
26
|
+
*
|
27
|
+
* assertEquals(await response.text(), "<gzip:body>");
|
28
|
+
* assertEquals(response.headers.get("content-encoding"), "gzip");
|
29
|
+
* ```
|
30
|
+
*/
|
31
|
+
export function compression(encoders) {
|
32
|
+
const encodingMap = {
|
33
|
+
...fromEncoders(DefaultEncoders),
|
34
|
+
...Array.isArray(encoders) ? fromEncoders(encoders) : encoders,
|
35
|
+
};
|
36
|
+
const encodings = Object.keys(encodingMap);
|
37
|
+
return async (request, next) => {
|
38
|
+
const encoding = acceptsEncodings(request, ...encodings, IDENTITY);
|
39
|
+
const response = await next(request);
|
40
|
+
vary(response.headers, ContentNegotiationHeader.AcceptEncoding);
|
41
|
+
if (!encoding || encoding === IDENTITY)
|
42
|
+
return response;
|
43
|
+
const encode = encodingMap[encoding];
|
44
|
+
if (!encode)
|
45
|
+
return response;
|
46
|
+
return withContentEncoding(response, { encode, encoding });
|
47
|
+
};
|
48
|
+
}
|
49
|
+
export function flat(encoder) {
|
50
|
+
return [encoder.encoding, encoder.encode];
|
51
|
+
}
|
52
|
+
export function fromEncoders(encoders) {
|
53
|
+
return Object.fromEntries(encoders.map(flat));
|
54
|
+
}
|
55
|
+
const IDENTITY = "identity";
|
package/esm/mod.js
ADDED
package/esm/package.json
ADDED
package/esm/transform.js
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
// Copyright 2023-latest the httpland authors. All rights reserved. MIT license.
|
2
|
+
// This module is browser compatible.
|
3
|
+
import { CachingHeader, compressible, isNull, parseMediaType, RepresentationHeader, } from "./deps.js";
|
4
|
+
import { isNoTransform, reCalcContentLength } from "./utils.js";
|
5
|
+
/** Response with `Content-Encoding` header. */
|
6
|
+
export async function withContentEncoding(response, context) {
|
7
|
+
const contentType = response.headers.get(RepresentationHeader.ContentType);
|
8
|
+
const cacheControl = response.headers.get(CachingHeader.CacheControl);
|
9
|
+
if (isNull(contentType) ||
|
10
|
+
response.headers.has(RepresentationHeader.ContentEncoding) ||
|
11
|
+
!response.body ||
|
12
|
+
response.bodyUsed ||
|
13
|
+
(!isNull(cacheControl) && isNoTransform(cacheControl)))
|
14
|
+
return response;
|
15
|
+
const [mediaType] = parseMediaType(contentType);
|
16
|
+
const isCompressible = compressible(mediaType);
|
17
|
+
if (!isCompressible)
|
18
|
+
return response;
|
19
|
+
const bodyInit = await context.encode(response.body);
|
20
|
+
const newResponse = await reCalcContentLength(new Response(bodyInit, response));
|
21
|
+
newResponse.headers.set(RepresentationHeader.ContentEncoding, context.encoding);
|
22
|
+
return newResponse;
|
23
|
+
}
|
package/esm/types.js
ADDED
package/esm/utils.js
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
// Copyright 2023-latest the httpland authors. All rights reserved. MIT license.
|
2
|
+
// This module is browser compatible.
|
3
|
+
import { RepresentationHeader } from "./deps.js";
|
4
|
+
const ReNoTransform = /(?:^|,)\s*?no-transform\s*?(?:,|$)/;
|
5
|
+
export function isNoTransform(input) {
|
6
|
+
return ReNoTransform.test(input);
|
7
|
+
}
|
8
|
+
export async function reCalcContentLength(response) {
|
9
|
+
if (response.bodyUsed ||
|
10
|
+
!response.headers.has(RepresentationHeader.ContentLength))
|
11
|
+
return response;
|
12
|
+
const length = await response
|
13
|
+
.clone()
|
14
|
+
.arrayBuffer()
|
15
|
+
.then((buffer) => buffer.byteLength)
|
16
|
+
.then(String);
|
17
|
+
response.headers.set(RepresentationHeader.ContentLength, length);
|
18
|
+
return response;
|
19
|
+
}
|
package/package.json
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
{
|
2
|
+
"module": "./esm/mod.js",
|
3
|
+
"main": "./script/mod.js",
|
4
|
+
"types": "./types/mod.d.ts",
|
5
|
+
"name": "@httpland/compression-middleware",
|
6
|
+
"version": "1.0.0-beta.1",
|
7
|
+
"description": "HTTP compression middleware",
|
8
|
+
"keywords": [
|
9
|
+
"http",
|
10
|
+
"middleware",
|
11
|
+
"header",
|
12
|
+
"compression",
|
13
|
+
"compress",
|
14
|
+
"gzip",
|
15
|
+
"deflate",
|
16
|
+
"encoding",
|
17
|
+
"encoder",
|
18
|
+
"fetch-api"
|
19
|
+
],
|
20
|
+
"engines": {
|
21
|
+
"node": "^18.x"
|
22
|
+
},
|
23
|
+
"license": "MIT",
|
24
|
+
"homepage": "https://github.com/httpland/compression-middleware",
|
25
|
+
"repository": {
|
26
|
+
"type": "git",
|
27
|
+
"url": "git+https://github.com/httpland/compression-middleware.git"
|
28
|
+
},
|
29
|
+
"bugs": {
|
30
|
+
"url": "https://github.com/httpland/compression-middleware/issues"
|
31
|
+
},
|
32
|
+
"sideEffects": false,
|
33
|
+
"type": "module",
|
34
|
+
"publishConfig": {
|
35
|
+
"access": "public"
|
36
|
+
},
|
37
|
+
"exports": {
|
38
|
+
".": {
|
39
|
+
"import": {
|
40
|
+
"types": "./types/mod.d.ts",
|
41
|
+
"default": "./esm/mod.js"
|
42
|
+
},
|
43
|
+
"require": {
|
44
|
+
"types": "./types/mod.d.ts",
|
45
|
+
"default": "./script/mod.js"
|
46
|
+
}
|
47
|
+
}
|
48
|
+
},
|
49
|
+
"dependencies": {
|
50
|
+
"@httpland/http-middleware": "1.0.0",
|
51
|
+
"@httpland/http-utils": "1.0.0-beta.13",
|
52
|
+
"compressible": "2.0.18",
|
53
|
+
"isxx": "1.0.0-beta.24"
|
54
|
+
}
|
55
|
+
}
|
@@ -0,0 +1,45 @@
|
|
1
|
+
"use strict";
|
2
|
+
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
3
|
+
/*!
|
4
|
+
* Adapted directly from negotiator at https://github.com/jshttp/negotiator/
|
5
|
+
* which is licensed as follows:
|
6
|
+
*
|
7
|
+
* (The MIT License)
|
8
|
+
*
|
9
|
+
* Copyright (c) 2012-2014 Federico Romero
|
10
|
+
* Copyright (c) 2012-2014 Isaac Z. Schlueter
|
11
|
+
* Copyright (c) 2014-2015 Douglas Christopher Wilson
|
12
|
+
*
|
13
|
+
* Permission is hereby granted, free of charge, to any person obtaining
|
14
|
+
* a copy of this software and associated documentation files (the
|
15
|
+
* 'Software'), to deal in the Software without restriction, including
|
16
|
+
* without limitation the rights to use, copy, modify, merge, publish,
|
17
|
+
* distribute, sublicense, and/or sell copies of the Software, and to
|
18
|
+
* permit persons to whom the Software is furnished to do so, subject to
|
19
|
+
* the following conditions:
|
20
|
+
*
|
21
|
+
* The above copyright notice and this permission notice shall be
|
22
|
+
* included in all copies or substantial portions of the Software.
|
23
|
+
*
|
24
|
+
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
25
|
+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
26
|
+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
27
|
+
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
28
|
+
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
29
|
+
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
30
|
+
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
31
|
+
*/
|
32
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
33
|
+
exports.isQuality = exports.compareSpecs = void 0;
|
34
|
+
function compareSpecs(a, b) {
|
35
|
+
return (b.q - a.q ||
|
36
|
+
(b.s ?? 0) - (a.s ?? 0) ||
|
37
|
+
(a.o ?? 0) - (b.o ?? 0) ||
|
38
|
+
a.i - b.i ||
|
39
|
+
0);
|
40
|
+
}
|
41
|
+
exports.compareSpecs = compareSpecs;
|
42
|
+
function isQuality(spec) {
|
43
|
+
return spec.q > 0;
|
44
|
+
}
|
45
|
+
exports.isQuality = isQuality;
|
@@ -0,0 +1,125 @@
|
|
1
|
+
"use strict";
|
2
|
+
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
3
|
+
/*!
|
4
|
+
* Adapted directly from negotiator at https://github.com/jshttp/negotiator/
|
5
|
+
* which is licensed as follows:
|
6
|
+
*
|
7
|
+
* (The MIT License)
|
8
|
+
*
|
9
|
+
* Copyright (c) 2012-2014 Federico Romero
|
10
|
+
* Copyright (c) 2012-2014 Isaac Z. Schlueter
|
11
|
+
* Copyright (c) 2014-2015 Douglas Christopher Wilson
|
12
|
+
*
|
13
|
+
* Permission is hereby granted, free of charge, to any person obtaining
|
14
|
+
* a copy of this software and associated documentation files (the
|
15
|
+
* 'Software'), to deal in the Software without restriction, including
|
16
|
+
* without limitation the rights to use, copy, modify, merge, publish,
|
17
|
+
* distribute, sublicense, and/or sell copies of the Software, and to
|
18
|
+
* permit persons to whom the Software is furnished to do so, subject to
|
19
|
+
* the following conditions:
|
20
|
+
*
|
21
|
+
* The above copyright notice and this permission notice shall be
|
22
|
+
* included in all copies or substantial portions of the Software.
|
23
|
+
*
|
24
|
+
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
25
|
+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
26
|
+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
27
|
+
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
28
|
+
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
29
|
+
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
30
|
+
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
31
|
+
*/
|
32
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
33
|
+
exports.preferredEncodings = void 0;
|
34
|
+
const common_js_1 = require("./common.js");
|
35
|
+
const simpleEncodingRegExp = /^\s*([^\s;]+)\s*(?:;(.*))?$/;
|
36
|
+
function parseEncoding(str, i) {
|
37
|
+
const match = simpleEncodingRegExp.exec(str);
|
38
|
+
if (!match) {
|
39
|
+
return undefined;
|
40
|
+
}
|
41
|
+
const encoding = match[1];
|
42
|
+
let q = 1;
|
43
|
+
if (match[2]) {
|
44
|
+
const params = match[2].split(";");
|
45
|
+
for (const param of params) {
|
46
|
+
const p = param.trim().split("=");
|
47
|
+
if (p[0] === "q") {
|
48
|
+
q = parseFloat(p[1]);
|
49
|
+
break;
|
50
|
+
}
|
51
|
+
}
|
52
|
+
}
|
53
|
+
return { encoding, q, i };
|
54
|
+
}
|
55
|
+
function specify(encoding, spec, i = -1) {
|
56
|
+
if (!spec.encoding) {
|
57
|
+
return;
|
58
|
+
}
|
59
|
+
let s = 0;
|
60
|
+
if (spec.encoding.toLocaleLowerCase() === encoding.toLocaleLowerCase()) {
|
61
|
+
s = 1;
|
62
|
+
}
|
63
|
+
else if (spec.encoding !== "*") {
|
64
|
+
return;
|
65
|
+
}
|
66
|
+
return {
|
67
|
+
i,
|
68
|
+
o: spec.i,
|
69
|
+
q: spec.q,
|
70
|
+
s,
|
71
|
+
};
|
72
|
+
}
|
73
|
+
function parseAcceptEncoding(accept) {
|
74
|
+
const accepts = accept.split(",");
|
75
|
+
const parsedAccepts = [];
|
76
|
+
let hasIdentity = false;
|
77
|
+
let minQuality = 1;
|
78
|
+
for (let i = 0; i < accepts.length; i++) {
|
79
|
+
const encoding = parseEncoding(accepts[i].trim(), i);
|
80
|
+
if (encoding) {
|
81
|
+
parsedAccepts.push(encoding);
|
82
|
+
hasIdentity = hasIdentity || !!specify("identity", encoding);
|
83
|
+
minQuality = Math.min(minQuality, encoding.q || 1);
|
84
|
+
}
|
85
|
+
}
|
86
|
+
if (!hasIdentity) {
|
87
|
+
parsedAccepts.push({
|
88
|
+
encoding: "identity",
|
89
|
+
q: minQuality,
|
90
|
+
i: accepts.length - 1,
|
91
|
+
});
|
92
|
+
}
|
93
|
+
return parsedAccepts;
|
94
|
+
}
|
95
|
+
function getEncodingPriority(encoding, accepted, index) {
|
96
|
+
let priority = { o: -1, q: 0, s: 0, i: 0 };
|
97
|
+
for (const s of accepted) {
|
98
|
+
const spec = specify(encoding, s, index);
|
99
|
+
if (spec &&
|
100
|
+
(priority.s - spec.s || priority.q - spec.q ||
|
101
|
+
priority.o - spec.o) <
|
102
|
+
0) {
|
103
|
+
priority = spec;
|
104
|
+
}
|
105
|
+
}
|
106
|
+
return priority;
|
107
|
+
}
|
108
|
+
/** Given an `Accept-Encoding` string, parse out the encoding returning a
|
109
|
+
* negotiated encoding based on the `provided` encodings otherwise just a
|
110
|
+
* prioritized array of encodings. */
|
111
|
+
function preferredEncodings(accept, provided) {
|
112
|
+
const accepts = parseAcceptEncoding(accept);
|
113
|
+
if (!provided) {
|
114
|
+
return accepts
|
115
|
+
.filter(common_js_1.isQuality)
|
116
|
+
.sort(common_js_1.compareSpecs)
|
117
|
+
.map((spec) => spec.encoding);
|
118
|
+
}
|
119
|
+
const priorities = provided.map((type, index) => getEncodingPriority(type, accepts, index));
|
120
|
+
return priorities
|
121
|
+
.filter(common_js_1.isQuality)
|
122
|
+
.sort(common_js_1.compareSpecs)
|
123
|
+
.map((priority) => provided[priorities.indexOf(priority)]);
|
124
|
+
}
|
125
|
+
exports.preferredEncodings = preferredEncodings;
|
@@ -0,0 +1,114 @@
|
|
1
|
+
"use strict";
|
2
|
+
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
3
|
+
/*!
|
4
|
+
* Adapted directly from negotiator at https://github.com/jshttp/negotiator/
|
5
|
+
* which is licensed as follows:
|
6
|
+
*
|
7
|
+
* (The MIT License)
|
8
|
+
*
|
9
|
+
* Copyright (c) 2012-2014 Federico Romero
|
10
|
+
* Copyright (c) 2012-2014 Isaac Z. Schlueter
|
11
|
+
* Copyright (c) 2014-2015 Douglas Christopher Wilson
|
12
|
+
*
|
13
|
+
* Permission is hereby granted, free of charge, to any person obtaining
|
14
|
+
* a copy of this software and associated documentation files (the
|
15
|
+
* 'Software'), to deal in the Software without restriction, including
|
16
|
+
* without limitation the rights to use, copy, modify, merge, publish,
|
17
|
+
* distribute, sublicense, and/or sell copies of the Software, and to
|
18
|
+
* permit persons to whom the Software is furnished to do so, subject to
|
19
|
+
* the following conditions:
|
20
|
+
*
|
21
|
+
* The above copyright notice and this permission notice shall be
|
22
|
+
* included in all copies or substantial portions of the Software.
|
23
|
+
*
|
24
|
+
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
25
|
+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
26
|
+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
27
|
+
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
28
|
+
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
29
|
+
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
30
|
+
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
31
|
+
*/
|
32
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
33
|
+
exports.preferredLanguages = void 0;
|
34
|
+
const common_js_1 = require("./common.js");
|
35
|
+
const SIMPLE_LANGUAGE_REGEXP = /^\s*([^\s\-;]+)(?:-([^\s;]+))?\s*(?:;(.*))?$/;
|
36
|
+
function parseLanguage(str, i) {
|
37
|
+
const match = SIMPLE_LANGUAGE_REGEXP.exec(str);
|
38
|
+
if (!match) {
|
39
|
+
return undefined;
|
40
|
+
}
|
41
|
+
const [, prefix, suffix] = match;
|
42
|
+
const full = suffix ? `${prefix}-${suffix}` : prefix;
|
43
|
+
let q = 1;
|
44
|
+
if (match[3]) {
|
45
|
+
const params = match[3].split(";");
|
46
|
+
for (const param of params) {
|
47
|
+
const [key, value] = param.trim().split("=");
|
48
|
+
if (key === "q") {
|
49
|
+
q = parseFloat(value);
|
50
|
+
break;
|
51
|
+
}
|
52
|
+
}
|
53
|
+
}
|
54
|
+
return { prefix, suffix, full, q, i };
|
55
|
+
}
|
56
|
+
function parseAcceptLanguage(accept) {
|
57
|
+
const accepts = accept.split(",");
|
58
|
+
const result = [];
|
59
|
+
for (let i = 0; i < accepts.length; i++) {
|
60
|
+
const language = parseLanguage(accepts[i].trim(), i);
|
61
|
+
if (language) {
|
62
|
+
result.push(language);
|
63
|
+
}
|
64
|
+
}
|
65
|
+
return result;
|
66
|
+
}
|
67
|
+
function specify(language, spec, i) {
|
68
|
+
const p = parseLanguage(language, i);
|
69
|
+
if (!p) {
|
70
|
+
return undefined;
|
71
|
+
}
|
72
|
+
let s = 0;
|
73
|
+
if (spec.full.toLowerCase() === p.full.toLowerCase()) {
|
74
|
+
s |= 4;
|
75
|
+
}
|
76
|
+
else if (spec.prefix.toLowerCase() === p.prefix.toLowerCase()) {
|
77
|
+
s |= 2;
|
78
|
+
}
|
79
|
+
else if (spec.full.toLowerCase() === p.prefix.toLowerCase()) {
|
80
|
+
s |= 1;
|
81
|
+
}
|
82
|
+
else if (spec.full !== "*") {
|
83
|
+
return;
|
84
|
+
}
|
85
|
+
return { i, o: spec.i, q: spec.q, s };
|
86
|
+
}
|
87
|
+
function getLanguagePriority(language, accepted, index) {
|
88
|
+
let priority = { i: -1, o: -1, q: 0, s: 0 };
|
89
|
+
for (const accepts of accepted) {
|
90
|
+
const spec = specify(language, accepts, index);
|
91
|
+
if (spec &&
|
92
|
+
((priority.s ?? 0) - (spec.s ?? 0) || priority.q - spec.q ||
|
93
|
+
(priority.o ?? 0) - (spec.o ?? 0)) < 0) {
|
94
|
+
priority = spec;
|
95
|
+
}
|
96
|
+
}
|
97
|
+
return priority;
|
98
|
+
}
|
99
|
+
function preferredLanguages(accept = "*", provided) {
|
100
|
+
const accepts = parseAcceptLanguage(accept);
|
101
|
+
if (!provided) {
|
102
|
+
return accepts
|
103
|
+
.filter(common_js_1.isQuality)
|
104
|
+
.sort(common_js_1.compareSpecs)
|
105
|
+
.map((spec) => spec.full);
|
106
|
+
}
|
107
|
+
const priorities = provided
|
108
|
+
.map((type, index) => getLanguagePriority(type, accepts, index));
|
109
|
+
return priorities
|
110
|
+
.filter(common_js_1.isQuality)
|
111
|
+
.sort(common_js_1.compareSpecs)
|
112
|
+
.map((priority) => provided[priorities.indexOf(priority)]);
|
113
|
+
}
|
114
|
+
exports.preferredLanguages = preferredLanguages;
|