Attic is a cozy space with lofty ambitions.
attic.social
1// Copyright 2018-2026 the Deno authors. MIT license.
2// This module is browser compatible.
3
4/**
5 * Contains the functions {@linkcode accepts}, {@linkcode acceptsEncodings}, and
6 * {@linkcode acceptsLanguages} to provide content negotiation capabilities.
7 *
8 * @module
9 */
10
11import { preferredEncodings } from "./_negotiation/encoding.ts";
12import { preferredLanguages } from "./_negotiation/language.ts";
13import { preferredMediaTypes } from "./_negotiation/media_type.ts";
14
15/**
16 * Returns an array of media types accepted by the request, in order of
17 * preference. If there are no media types supplied in the request, then any
18 * media type selector will be returned.
19 *
20 * @example Usage
21 * ```ts
22 * import { accepts } from "@std/http/negotiation";
23 * import { assertEquals } from "@std/assert";
24 *
25 * const request = new Request("https://example.com/", {
26 * headers: {
27 * accept:
28 * "text/html, application/xhtml+xml, application/xml;q=0.9, image/webp, *\/*;q=0.8",
29 * },
30 * });
31 *
32 * assertEquals(accepts(request), [
33 * "text/html",
34 * "application/xhtml+xml",
35 * "image/webp",
36 * "application/xml",
37 * "*\/*",
38 * ]);
39 * ```
40 *
41 * @param request The request to get the acceptable media types for.
42 * @returns An array of acceptable media types.
43 */
44export function accepts(request: Pick<Request, "headers">): string[];
45/**
46 * For a given set of media types, return the best match accepted in the
47 * request. If no media type matches, then the function returns `undefined`.
48 *
49 * @example Usage
50 * ```ts
51 * import { accepts } from "@std/http/negotiation";
52 * import { assertEquals } from "@std/assert";
53 *
54 * const request = new Request("https://example.com/", {
55 * headers: {
56 * accept:
57 * "text/html, application/xhtml+xml, application/xml;q=0.9, image/webp, *\/*;q=0.8",
58 * },
59 * });
60 *
61 * assertEquals(accepts(request, "text/html", "image/webp"), "text/html");
62 * ```
63 *
64 * @typeParam T The type of supported content-types (if provided).
65 * @param request The request to get the acceptable media types for.
66 * @param types An array of media types to find the best matching one from.
67 * @returns The best matching media type, if any match.
68 */
69export function accepts<T extends string = string>(
70 request: Pick<Request, "headers">,
71 ...types: T[]
72): T | undefined;
73export function accepts(
74 request: Pick<Request, "headers">,
75 ...types: string[]
76): string | string[] | undefined {
77 const accept = request.headers.get("accept");
78 return types.length
79 ? accept ? preferredMediaTypes(accept, types)[0] : types[0]
80 : accept
81 ? preferredMediaTypes(accept)
82 : ["*/*"];
83}
84
85/**
86 * Returns an array of content encodings accepted by the request, in order of
87 * preference. If there are no encoding supplied in the request, then `["*"]`
88 * is returned, implying any encoding is accepted.
89 *
90 * @example Usage
91 * ```ts
92 * import { acceptsEncodings } from "@std/http/negotiation";
93 * import { assertEquals } from "@std/assert";
94 *
95 * const request = new Request("https://example.com/", {
96 * headers: { "accept-encoding": "deflate, gzip;q=1.0, *;q=0.5" },
97 * });
98 *
99 * assertEquals(acceptsEncodings(request), ["deflate", "gzip", "*"]);
100 * ```
101 *
102 * @param request The request to get the acceptable content encodings for.
103 * @returns An array of content encodings this request accepts.
104 */
105export function acceptsEncodings(request: Pick<Request, "headers">): string[];
106/**
107 * For a given set of content encodings, return the best match accepted in the
108 * request. If no content encodings match, then the function returns
109 * `undefined`.
110 *
111 * **NOTE:** You should always supply `identity` as one of the encodings
112 * to ensure that there is a match when the `Accept-Encoding` header is part
113 * of the request.
114 *
115 * @example Usage
116 * ```ts
117 * import { acceptsEncodings } from "@std/http/negotiation";
118 * import { assertEquals } from "@std/assert";
119 *
120 * const request = new Request("https://example.com/", {
121 * headers: { "accept-encoding": "deflate, gzip;q=1.0, *;q=0.5" },
122 * });
123 *
124 * assertEquals(acceptsEncodings(request, "gzip", "identity"), "gzip");
125 * ```
126 *
127 * @typeParam T The type of supported encodings (if provided).
128 * @param request The request to get the acceptable content encodings for.
129 * @param encodings An array of encodings to find the best matching one from.
130 * @returns The best matching encoding, if any match.
131 */
132export function acceptsEncodings<T extends string = string>(
133 request: Pick<Request, "headers">,
134 ...encodings: T[]
135): T | undefined;
136export function acceptsEncodings(
137 request: Pick<Request, "headers">,
138 ...encodings: string[]
139): string | string[] | undefined {
140 const acceptEncoding = request.headers.get("accept-encoding");
141 return encodings.length
142 ? acceptEncoding
143 ? preferredEncodings(acceptEncoding, encodings)[0]
144 : encodings[0]
145 : acceptEncoding
146 ? preferredEncodings(acceptEncoding)
147 : ["*"];
148}
149
150/**
151 * Returns an array of languages accepted by the request, in order of
152 * preference. If there are no languages supplied in the request, then `["*"]`
153 * is returned, imply any language is accepted.
154 *
155 * @example Usage
156 * ```ts
157 * import { acceptsLanguages } from "@std/http/negotiation";
158 * import { assertEquals } from "@std/assert";
159 *
160 * const request = new Request("https://example.com/", {
161 * headers: {
162 * "accept-language": "fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5",
163 * },
164 * });
165 *
166 * assertEquals(acceptsLanguages(request), ["fr-CH", "fr", "en", "de", "*"]);
167 * ```
168 *
169 * @param request The request to get the acceptable languages for.
170 * @returns An array of languages this request accepts.
171 */
172export function acceptsLanguages(request: Pick<Request, "headers">): string[];
173/**
174 * For a given set of languages, return the best match accepted in the request.
175 * If no languages match, then the function returns `undefined`.
176 *
177 * @example Usage
178 * ```ts
179 * import { acceptsLanguages } from "@std/http/negotiation";
180 * import { assertEquals } from "@std/assert";
181 *
182 * const request = new Request("https://example.com/", {
183 * headers: {
184 * "accept-language": "fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5",
185 * },
186 * });
187 *
188 * assertEquals(acceptsLanguages(request, "en-gb", "en-us", "en"), "en");
189 * ```
190 *
191 * @typeParam T The type of supported languages (if provided).
192 * @param request The request to get the acceptable language for.
193 * @param langs An array of languages to find the best matching one from.
194 * @returns The best matching language, if any match.
195 */
196export function acceptsLanguages<T extends string = string>(
197 request: Pick<Request, "headers">,
198 ...langs: T[]
199): T | undefined;
200export function acceptsLanguages(
201 request: Pick<Request, "headers">,
202 ...langs: string[]
203): string | string[] | undefined {
204 const acceptLanguage = request.headers.get("accept-language");
205 return langs.length
206 ? acceptLanguage ? preferredLanguages(acceptLanguage, langs)[0] : langs[0]
207 : acceptLanguage
208 ? preferredLanguages(acceptLanguage)
209 : ["*"];
210}