···11+// Copyright 2018-2026 the Deno authors. MIT license.
22+/*!
33+ * Adapted directly from negotiator at https://github.com/jshttp/negotiator/
44+ * which is licensed as follows:
55+ *
66+ * (The MIT License)
77+ *
88+ * Copyright (c) 2012-2014 Federico Romero
99+ * Copyright (c) 2012-2014 Isaac Z. Schlueter
1010+ * Copyright (c) 2014-2015 Douglas Christopher Wilson
1111+ *
1212+ * Permission is hereby granted, free of charge, to any person obtaining
1313+ * a copy of this software and associated documentation files (the
1414+ * 'Software'), to deal in the Software without restriction, including
1515+ * without limitation the rights to use, copy, modify, merge, publish,
1616+ * distribute, sublicense, and/or sell copies of the Software, and to
1717+ * permit persons to whom the Software is furnished to do so, subject to
1818+ * the following conditions:
1919+ *
2020+ * The above copyright notice and this permission notice shall be
2121+ * included in all copies or substantial portions of the Software.
2222+ *
2323+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
2424+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2525+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
2626+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
2727+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
2828+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
2929+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3030+ */
3131+3232+export interface Specificity {
3333+ i: number;
3434+ o: number | undefined;
3535+ q: number;
3636+ s: number | undefined;
3737+}
3838+3939+export function compareSpecs(a: Specificity, b: Specificity): number {
4040+ return (
4141+ b.q - a.q ||
4242+ (b.s ?? 0) - (a.s ?? 0) ||
4343+ (a.o ?? 0) - (b.o ?? 0) ||
4444+ a.i - b.i ||
4545+ 0
4646+ );
4747+}
4848+4949+export function isQuality(spec: Specificity): boolean {
5050+ return spec.q > 0;
5151+}
+164
src/lib/_negotiation/encoding.ts
···11+// Copyright 2018-2026 the Deno authors. MIT license.
22+/*!
33+ * Adapted directly from negotiator at https://github.com/jshttp/negotiator/
44+ * which is licensed as follows:
55+ *
66+ * (The MIT License)
77+ *
88+ * Copyright (c) 2012-2014 Federico Romero
99+ * Copyright (c) 2012-2014 Isaac Z. Schlueter
1010+ * Copyright (c) 2014-2015 Douglas Christopher Wilson
1111+ *
1212+ * Permission is hereby granted, free of charge, to any person obtaining
1313+ * a copy of this software and associated documentation files (the
1414+ * 'Software'), to deal in the Software without restriction, including
1515+ * without limitation the rights to use, copy, modify, merge, publish,
1616+ * distribute, sublicense, and/or sell copies of the Software, and to
1717+ * permit persons to whom the Software is furnished to do so, subject to
1818+ * the following conditions:
1919+ *
2020+ * The above copyright notice and this permission notice shall be
2121+ * included in all copies or substantial portions of the Software.
2222+ *
2323+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
2424+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2525+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
2626+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
2727+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
2828+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
2929+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3030+ */
3131+3232+import { compareSpecs, isQuality, type Specificity } from "./common.ts";
3333+3434+interface EncodingSpecificity extends Specificity {
3535+ encoding?: string;
3636+}
3737+3838+const simpleEncodingRegExp = /^\s*([^\s;]+)\s*(?:;(.*))?$/;
3939+4040+function parseEncoding(
4141+ str: string,
4242+ i: number,
4343+): EncodingSpecificity | undefined {
4444+ const match = simpleEncodingRegExp.exec(str);
4545+ if (!match) {
4646+ return undefined;
4747+ }
4848+4949+ const encoding = match[1]!;
5050+ let q = 1;
5151+ if (match[2]) {
5252+ const params = match[2].split(";");
5353+ for (const param of params) {
5454+ const p = param.trim().split("=");
5555+ if (p[0] === "q" && p[1]) {
5656+ q = parseFloat(p[1]);
5757+ break;
5858+ }
5959+ }
6060+ }
6161+6262+ return { encoding, o: undefined, q, i, s: undefined };
6363+}
6464+6565+function specify(
6666+ encoding: string,
6767+ spec: EncodingSpecificity,
6868+ i = -1,
6969+): Specificity | undefined {
7070+ if (!spec.encoding) {
7171+ return;
7272+ }
7373+ let s = 0;
7474+ if (spec.encoding.toLowerCase() === encoding.toLowerCase()) {
7575+ s = 1;
7676+ } else if (spec.encoding !== "*") {
7777+ return;
7878+ }
7979+8080+ return {
8181+ i,
8282+ o: spec.i,
8383+ q: spec.q,
8484+ s,
8585+ };
8686+}
8787+8888+function parseAcceptEncoding(accept: string): EncodingSpecificity[] {
8989+ const accepts = accept.split(",");
9090+ const parsedAccepts: EncodingSpecificity[] = [];
9191+ let hasIdentity = false;
9292+ let minQuality = 1;
9393+9494+ for (const [i, accept] of accepts.entries()) {
9595+ const encoding = parseEncoding(accept.trim(), i);
9696+9797+ if (encoding) {
9898+ parsedAccepts.push(encoding);
9999+ hasIdentity = hasIdentity || !!specify("identity", encoding);
100100+ minQuality = Math.min(minQuality, encoding.q || 1);
101101+ }
102102+ }
103103+104104+ if (!hasIdentity) {
105105+ parsedAccepts.push({
106106+ encoding: "identity",
107107+ o: undefined,
108108+ q: minQuality,
109109+ i: accepts.length - 1,
110110+ s: undefined,
111111+ });
112112+ }
113113+114114+ return parsedAccepts;
115115+}
116116+117117+function getEncodingPriority(
118118+ encoding: string,
119119+ accepted: Specificity[],
120120+ index: number,
121121+): Specificity {
122122+ let priority: Specificity = { o: -1, q: 0, s: 0, i: 0 };
123123+124124+ for (const s of accepted) {
125125+ const spec = specify(encoding, s, index);
126126+127127+ if (
128128+ spec &&
129129+ (priority.s! - spec.s! || priority.q - spec.q ||
130130+ priority.o! - spec.o!) <
131131+ 0
132132+ ) {
133133+ priority = spec;
134134+ }
135135+ }
136136+137137+ return priority;
138138+}
139139+140140+/** Given an `Accept-Encoding` string, parse out the encoding returning a
141141+ * negotiated encoding based on the `provided` encodings otherwise just a
142142+ * prioritized array of encodings. */
143143+export function preferredEncodings(
144144+ accept: string,
145145+ provided?: string[],
146146+): string[] {
147147+ const accepts = parseAcceptEncoding(accept);
148148+149149+ if (!provided) {
150150+ return accepts
151151+ .filter(isQuality)
152152+ .sort(compareSpecs)
153153+ .map((spec) => spec.encoding!);
154154+ }
155155+156156+ const priorities = provided.map((type, index) =>
157157+ getEncodingPriority(type, accepts, index)
158158+ );
159159+160160+ return priorities
161161+ .filter(isQuality)
162162+ .sort(compareSpecs)
163163+ .map((priority) => provided[priorities.indexOf(priority)]!);
164164+}
+148
src/lib/_negotiation/language.ts
···11+// Copyright 2018-2026 the Deno authors. MIT license.
22+/*!
33+ * Adapted directly from negotiator at https://github.com/jshttp/negotiator/
44+ * which is licensed as follows:
55+ *
66+ * (The MIT License)
77+ *
88+ * Copyright (c) 2012-2014 Federico Romero
99+ * Copyright (c) 2012-2014 Isaac Z. Schlueter
1010+ * Copyright (c) 2014-2015 Douglas Christopher Wilson
1111+ *
1212+ * Permission is hereby granted, free of charge, to any person obtaining
1313+ * a copy of this software and associated documentation files (the
1414+ * 'Software'), to deal in the Software without restriction, including
1515+ * without limitation the rights to use, copy, modify, merge, publish,
1616+ * distribute, sublicense, and/or sell copies of the Software, and to
1717+ * permit persons to whom the Software is furnished to do so, subject to
1818+ * the following conditions:
1919+ *
2020+ * The above copyright notice and this permission notice shall be
2121+ * included in all copies or substantial portions of the Software.
2222+ *
2323+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
2424+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2525+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
2626+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
2727+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
2828+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
2929+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3030+ */
3131+3232+import { compareSpecs, isQuality, type Specificity } from "./common.ts";
3333+3434+interface LanguageSpecificity extends Specificity {
3535+ prefix: string;
3636+ suffix: string | undefined;
3737+ full: string;
3838+}
3939+4040+const SIMPLE_LANGUAGE_REGEXP = /^\s*([^\s\-;]+)(?:-([^\s;]+))?\s*(?:;(.*))?$/;
4141+4242+function parseLanguage(
4343+ str: string,
4444+ i: number,
4545+): LanguageSpecificity | undefined {
4646+ const match = SIMPLE_LANGUAGE_REGEXP.exec(str);
4747+ if (!match) {
4848+ return undefined;
4949+ }
5050+5151+ const [, prefix, suffix] = match;
5252+ if (!prefix) {
5353+ return undefined;
5454+ }
5555+5656+ const full = suffix !== undefined ? `${prefix}-${suffix}` : prefix;
5757+5858+ let q = 1;
5959+ if (match[3]) {
6060+ const params = match[3].split(";");
6161+ for (const param of params) {
6262+ const [key, value] = param.trim().split("=");
6363+ if (key === "q" && value) {
6464+ q = parseFloat(value);
6565+ break;
6666+ }
6767+ }
6868+ }
6969+7070+ return { prefix, suffix, full, i, o: undefined, q, s: undefined };
7171+}
7272+7373+function parseAcceptLanguage(accept: string): LanguageSpecificity[] {
7474+ const accepts = accept.split(",");
7575+ const result: LanguageSpecificity[] = [];
7676+7777+ for (const [i, accept] of accepts.entries()) {
7878+ const language = parseLanguage(accept.trim(), i);
7979+ if (language) {
8080+ result.push(language);
8181+ }
8282+ }
8383+ return result;
8484+}
8585+8686+function specify(
8787+ language: string,
8888+ spec: LanguageSpecificity,
8989+ i: number,
9090+): Specificity | undefined {
9191+ const p = parseLanguage(language, i);
9292+ if (!p) {
9393+ return undefined;
9494+ }
9595+ let s = 0;
9696+ if (spec.full.toLowerCase() === p.full.toLowerCase()) {
9797+ s |= 4;
9898+ } else if (spec.prefix.toLowerCase() === p.prefix.toLowerCase()) {
9999+ s |= 2;
100100+ } else if (spec.full.toLowerCase() === p.prefix.toLowerCase()) {
101101+ s |= 1;
102102+ } else if (spec.full !== "*") {
103103+ return;
104104+ }
105105+106106+ return { i, o: spec.i, q: spec.q, s };
107107+}
108108+109109+function getLanguagePriority(
110110+ language: string,
111111+ accepted: LanguageSpecificity[],
112112+ index: number,
113113+): Specificity {
114114+ let priority: Specificity = { i: -1, o: -1, q: 0, s: 0 };
115115+ for (const accepts of accepted) {
116116+ const spec = specify(language, accepts, index);
117117+ if (
118118+ spec &&
119119+ ((priority.s ?? 0) - (spec.s ?? 0) || priority.q - spec.q ||
120120+ (priority.o ?? 0) - (spec.o ?? 0)) < 0
121121+ ) {
122122+ priority = spec;
123123+ }
124124+ }
125125+ return priority;
126126+}
127127+128128+export function preferredLanguages(
129129+ accept = "*",
130130+ provided?: string[],
131131+): string[] {
132132+ const accepts = parseAcceptLanguage(accept);
133133+134134+ if (!provided) {
135135+ return accepts
136136+ .filter(isQuality)
137137+ .sort(compareSpecs)
138138+ .map((spec) => spec.full);
139139+ }
140140+141141+ const priorities = provided
142142+ .map((type, index) => getLanguagePriority(type, accepts, index));
143143+144144+ return priorities
145145+ .filter(isQuality)
146146+ .sort(compareSpecs)
147147+ .map((priority) => provided[priorities.indexOf(priority)]!);
148148+}
+196
src/lib/_negotiation/media_type.ts
···11+// Copyright 2018-2026 the Deno authors. MIT license.
22+/*!
33+ * Adapted directly from negotiator at https://github.com/jshttp/negotiator/
44+ * which is licensed as follows:
55+ *
66+ * (The MIT License)
77+ *
88+ * Copyright (c) 2012-2014 Federico Romero
99+ * Copyright (c) 2012-2014 Isaac Z. Schlueter
1010+ * Copyright (c) 2014-2015 Douglas Christopher Wilson
1111+ *
1212+ * Permission is hereby granted, free of charge, to any person obtaining
1313+ * a copy of this software and associated documentation files (the
1414+ * 'Software'), to deal in the Software without restriction, including
1515+ * without limitation the rights to use, copy, modify, merge, publish,
1616+ * distribute, sublicense, and/or sell copies of the Software, and to
1717+ * permit persons to whom the Software is furnished to do so, subject to
1818+ * the following conditions:
1919+ *
2020+ * The above copyright notice and this permission notice shall be
2121+ * included in all copies or substantial portions of the Software.
2222+ *
2323+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
2424+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2525+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
2626+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
2727+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
2828+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
2929+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3030+ */
3131+3232+import { compareSpecs, isQuality, type Specificity } from "./common.ts";
3333+3434+interface MediaTypeSpecificity extends Specificity {
3535+ type: string;
3636+ subtype: string;
3737+ params: { [param: string]: string | undefined };
3838+}
3939+4040+const simpleMediaTypeRegExp = /^\s*([^\s\/;]+)\/([^;\s]+)\s*(?:;(.*))?$/;
4141+4242+function splitKeyValuePair(str: string): [string, string | undefined] {
4343+ const [key, value] = str.split("=");
4444+ return [key!.toLowerCase(), value];
4545+}
4646+4747+function parseMediaType(
4848+ str: string,
4949+ i: number,
5050+): MediaTypeSpecificity | undefined {
5151+ const match = simpleMediaTypeRegExp.exec(str);
5252+5353+ if (!match) {
5454+ return;
5555+ }
5656+5757+ const [, type, subtype, parameters] = match;
5858+ if (!type || !subtype) {
5959+ return;
6060+ }
6161+6262+ const params: { [param: string]: string | undefined } = Object.create(null);
6363+ let q = 1;
6464+ if (parameters) {
6565+ const kvps = parameters.split(";").map((p) => p.trim()).map(
6666+ splitKeyValuePair,
6767+ );
6868+6969+ for (const [key, val] of kvps) {
7070+ const value = val && val[0] === `"` && val[val.length - 1] === `"`
7171+ ? val.slice(1, val.length - 1)
7272+ : val;
7373+7474+ if (key === "q" && value) {
7575+ q = parseFloat(value);
7676+ break;
7777+ }
7878+7979+ params[key] = value;
8080+ }
8181+ }
8282+8383+ return { type, subtype, params, i, o: undefined, q, s: undefined };
8484+}
8585+8686+function parseAccept(accept: string): MediaTypeSpecificity[] {
8787+ const accepts = accept.split(",").map((p) => p.trim());
8888+8989+ const mediaTypes: MediaTypeSpecificity[] = [];
9090+ for (const [index, accept] of accepts.entries()) {
9191+ const mediaType = parseMediaType(accept.trim(), index);
9292+9393+ if (mediaType) {
9494+ mediaTypes.push(mediaType);
9595+ }
9696+ }
9797+9898+ return mediaTypes;
9999+}
100100+101101+function getFullType(spec: MediaTypeSpecificity) {
102102+ return `${spec.type}/${spec.subtype}`;
103103+}
104104+105105+function specify(
106106+ type: string,
107107+ spec: MediaTypeSpecificity,
108108+ index: number,
109109+): Specificity | undefined {
110110+ const p = parseMediaType(type, index);
111111+112112+ if (!p) {
113113+ return;
114114+ }
115115+116116+ let s = 0;
117117+118118+ if (spec.type.toLowerCase() === p.type.toLowerCase()) {
119119+ s |= 4;
120120+ } else if (spec.type !== "*") {
121121+ return;
122122+ }
123123+124124+ if (spec.subtype.toLowerCase() === p.subtype.toLowerCase()) {
125125+ s |= 2;
126126+ } else if (spec.subtype !== "*") {
127127+ return;
128128+ }
129129+130130+ const keys = Object.keys(spec.params);
131131+ if (keys.length) {
132132+ if (
133133+ keys.every((key) =>
134134+ (spec.params[key] ?? "").toLowerCase() ===
135135+ (p.params[key] ?? "").toLowerCase()
136136+ )
137137+ ) {
138138+ s |= 1;
139139+ } else {
140140+ return;
141141+ }
142142+ }
143143+144144+ return {
145145+ i: index,
146146+ o: spec.o,
147147+ q: spec.q,
148148+ s,
149149+ };
150150+}
151151+152152+function getMediaTypePriority(
153153+ type: string,
154154+ accepted: MediaTypeSpecificity[],
155155+ index: number,
156156+) {
157157+ let priority: Specificity = { o: -1, q: 0, s: 0, i: index };
158158+159159+ for (const accepts of accepted) {
160160+ const spec = specify(type, accepts, index);
161161+162162+ if (
163163+ spec &&
164164+ ((priority.s ?? 0) - (spec.s ?? 0) ||
165165+ (priority.q ?? 0) - (spec.q ?? 0) ||
166166+ (priority.o ?? 0) - (spec.o ?? 0)) < 0
167167+ ) {
168168+ priority = spec;
169169+ }
170170+ }
171171+172172+ return priority;
173173+}
174174+175175+export function preferredMediaTypes(
176176+ accept?: string | null,
177177+ provided?: string[],
178178+): string[] {
179179+ const accepts = parseAccept(accept === undefined ? "*/*" : accept ?? "");
180180+181181+ if (!provided) {
182182+ return accepts
183183+ .filter(isQuality)
184184+ .sort(compareSpecs)
185185+ .map(getFullType);
186186+ }
187187+188188+ const priorities = provided.map((type, index) => {
189189+ return getMediaTypePriority(type, accepts, index);
190190+ });
191191+192192+ return priorities
193193+ .filter(isQuality)
194194+ .sort(compareSpecs)
195195+ .map((priority) => provided[priorities.indexOf(priority)]!);
196196+}
+9-163
src/lib/negotiation.ts
···11// Copyright 2018-2026 the Deno authors. MIT license.
22-/*!
33- * Adapted directly from negotiator at https://github.com/jshttp/negotiator/
44- * which is licensed as follows:
55- *
66- * (The MIT License)
77- *
88- * Copyright (c) 2012-2014 Federico Romero
99- * Copyright (c) 2012-2014 Isaac Z. Schlueter
1010- * Copyright (c) 2014-2015 Douglas Christopher Wilson
22+// This module is browser compatible.
33+44+/**
55+ * Contains the functions {@linkcode accepts}, {@linkcode acceptsEncodings}, and
66+ * {@linkcode acceptsLanguages} to provide content negotiation capabilities.
117 *
1212- * Permission is hereby granted, free of charge, to any person obtaining
1313- * a copy of this software and associated documentation files (the
1414- * 'Software'), to deal in the Software without restriction, including
1515- * without limitation the rights to use, copy, modify, merge, publish,
1616- * distribute, sublicense, and/or sell copies of the Software, and to
1717- * permit persons to whom the Software is furnished to do so, subject to
1818- * the following conditions:
1919- *
2020- * The above copyright notice and this permission notice shall be
2121- * included in all copies or substantial portions of the Software.
2222- *
2323- * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
2424- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2525- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
2626- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
2727- * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
2828- * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
2929- * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
88+ * @module
309 */
31103232-export interface Specificity {
3333- i: number;
3434- o: number | undefined;
3535- q: number;
3636- s: number | undefined;
3737-}
3838-3939-export function compareSpecs(a: Specificity, b: Specificity): number {
4040- return (
4141- b.q - a.q ||
4242- (b.s ?? 0) - (a.s ?? 0) ||
4343- (a.o ?? 0) - (b.o ?? 0) ||
4444- a.i - b.i ||
4545- 0
4646- );
4747-}
4848-4949-export function isQuality(spec: Specificity): boolean {
5050- return spec.q > 0;
5151-}
5252-5353-interface LanguageSpecificity extends Specificity {
5454- prefix: string;
5555- suffix: string | undefined;
5656- full: string;
5757-}
5858-5959-const SIMPLE_LANGUAGE_REGEXP = /^\s*([^\s\-;]+)(?:-([^\s;]+))?\s*(?:;(.*))?$/;
6060-6161-function parseLanguage(
6262- str: string,
6363- i: number,
6464-): LanguageSpecificity | undefined {
6565- const match = SIMPLE_LANGUAGE_REGEXP.exec(str);
6666- if (!match) {
6767- return undefined;
6868- }
6969-7070- const [, prefix, suffix] = match;
7171- if (!prefix) {
7272- return undefined;
7373- }
7474-7575- const full = suffix !== undefined ? `${prefix}-${suffix}` : prefix;
7676-7777- let q = 1;
7878- if (match[3]) {
7979- const params = match[3].split(";");
8080- for (const param of params) {
8181- const [key, value] = param.trim().split("=");
8282- if (key === "q" && value) {
8383- q = parseFloat(value);
8484- break;
8585- }
8686- }
8787- }
8888-8989- return { prefix, suffix, full, i, o: undefined, q, s: undefined };
9090-}
9191-9292-function parseAcceptLanguage(accept: string): LanguageSpecificity[] {
9393- const accepts = accept.split(",");
9494- const result: LanguageSpecificity[] = [];
9595-9696- for (const [i, accept] of accepts.entries()) {
9797- const language = parseLanguage(accept.trim(), i);
9898- if (language) {
9999- result.push(language);
100100- }
101101- }
102102- return result;
103103-}
104104-105105-function specify(
106106- language: string,
107107- spec: LanguageSpecificity,
108108- i: number,
109109-): Specificity | undefined {
110110- const p = parseLanguage(language, i);
111111- if (!p) {
112112- return undefined;
113113- }
114114- let s = 0;
115115- if (spec.full.toLowerCase() === p.full.toLowerCase()) {
116116- s |= 4;
117117- } else if (spec.prefix.toLowerCase() === p.prefix.toLowerCase()) {
118118- s |= 2;
119119- } else if (spec.full.toLowerCase() === p.prefix.toLowerCase()) {
120120- s |= 1;
121121- } else if (spec.full !== "*") {
122122- return;
123123- }
124124-125125- return { i, o: spec.i, q: spec.q, s };
126126-}
127127-128128-function getLanguagePriority(
129129- language: string,
130130- accepted: LanguageSpecificity[],
131131- index: number,
132132-): Specificity {
133133- let priority: Specificity = { i: -1, o: -1, q: 0, s: 0 };
134134- for (const accepts of accepted) {
135135- const spec = specify(language, accepts, index);
136136- if (
137137- spec &&
138138- ((priority.s ?? 0) - (spec.s ?? 0) || priority.q - spec.q ||
139139- (priority.o ?? 0) - (spec.o ?? 0)) < 0
140140- ) {
141141- priority = spec;
142142- }
143143- }
144144- return priority;
145145-}
146146-147147-export function preferredLanguages(
148148- accept = "*",
149149- provided?: string[],
150150-): string[] {
151151- const accepts = parseAcceptLanguage(accept);
152152-153153- if (!provided) {
154154- return accepts
155155- .filter(isQuality)
156156- .sort(compareSpecs)
157157- .map((spec) => spec.full);
158158- }
159159-160160- const priorities = provided
161161- .map((type, index) => getLanguagePriority(type, accepts, index));
162162-163163- return priorities
164164- .filter(isQuality)
165165- .sort(compareSpecs)
166166- .map((priority) => provided[priorities.indexOf(priority)]!);
167167-}
1111+import { preferredEncodings } from "./_negotiation/encoding.ts";
1212+import { preferredLanguages } from "./_negotiation/language.ts";
1313+import { preferredMediaTypes } from "./_negotiation/media_type.ts";
1681416915/**
17016 * Returns an array of media types accepted by the request, in order of