UNPKG

6.21 kBJavaScriptView Raw
1// deno-lint-ignore-file
2import { notAcceptable, unsupportedMediaType } from "@worker-tools/response-creators";
3import negotiated from 'negotiated';
4const weightSortFn = (a, b) => a.weight >= b.weight ? a : b;
5const ACCEPT = 'Accept';
6const ACCEPT_ENCODING = 'Accept-Encoding';
7const ACCEPT_LANGUAGE = 'Accept-Language';
8const CONTENT_TYPE = 'Content-Type';
9const CONTENT_LANGUAGE = 'Content-Language';
10const CONTENT_ENCODING = 'Content-Encoding';
11const VARY = 'Vary';
12/**
13 * Performs content negotiation over the content type of the response.
14 * @param types The content types _provided_ by this endpoint.
15 */
16export function contentTypes(types) {
17 return async (ax) => {
18 const ctx = await ax;
19 const { headers } = ctx.request;
20 const type = [...negotiated.mediaTypes(headers.get(ACCEPT))]
21 .filter(t => !types || types.includes(t.type))
22 .reduce(weightSortFn, { weight: -1 }).type;
23 if (headers.has(ACCEPT) && types && !type)
24 throw notAcceptable();
25 ctx.effects.push(response => {
26 var _a;
27 if (!response.headers.has(CONTENT_TYPE))
28 response.headers.set(CONTENT_TYPE, type);
29 // If the server accepts more than 1 option, we set the vary header for correct caching
30 if (((_a = types === null || types === void 0 ? void 0 : types.length) !== null && _a !== void 0 ? _a : 0) > 1)
31 response.headers.append(VARY, ACCEPT);
32 return response;
33 });
34 return Object.assign(ctx, { type });
35 };
36}
37/**
38 * Performs content negotiation over the content language of the response.
39 * @param languages The languages _provided_ by this endpoint.
40 */
41export function contentLanguages(languages) {
42 return async (ax) => {
43 const ctx = await ax;
44 const { headers } = ctx.request;
45 const language = [...negotiated.languages(headers.get(ACCEPT_LANGUAGE))]
46 .filter(l => !languages || languages.includes(l.language))
47 .reduce(weightSortFn, { weight: -1 }).language;
48 if (headers.has(ACCEPT_LANGUAGE) && languages && !language)
49 throw notAcceptable();
50 ctx.effects.push(response => {
51 var _a;
52 if (!response.headers.has(CONTENT_LANGUAGE))
53 response.headers.set(CONTENT_LANGUAGE, language);
54 // If the server accepts more than 1 option, we set the vary header for correct caching
55 if (((_a = languages === null || languages === void 0 ? void 0 : languages.length) !== null && _a !== void 0 ? _a : 0) > 1)
56 response.headers.append(VARY, ACCEPT_LANGUAGE);
57 return response;
58 });
59 return Object.assign(ctx, { language });
60 };
61}
62/**
63 * Performs content negotiation over the content encoding of the response.
64 * @param encodings The encodings _provided_ by this endpoint.
65 */
66export function contentEncodings(encodings) {
67 return async (ax) => {
68 const ctx = await ax;
69 const { headers } = ctx.request;
70 const encoding = [...negotiated.encodings(headers.get(ACCEPT_ENCODING))]
71 .filter(e => !encodings || encodings.includes(e.encoding))
72 .reduce(weightSortFn, { weight: -1 }).encoding;
73 // TODO: how to handle status errors in middleware??
74 if (headers.has(ACCEPT_ENCODING) && encodings && !encoding)
75 throw notAcceptable();
76 ctx.effects.push(response => {
77 var _a;
78 if (!response.headers.has(CONTENT_ENCODING))
79 response.headers.set(CONTENT_ENCODING, encoding);
80 // If the server accepts more than 1 option, we set the vary header for correct caching
81 if (((_a = encodings === null || encodings === void 0 ? void 0 : encodings.length) !== null && _a !== void 0 ? _a : 0) > 1)
82 response.headers.append(VARY, ACCEPT_ENCODING);
83 return response;
84 });
85 return Object.assign(ctx, { encoding });
86 };
87}
88export { contentTypes as provides, contentLanguages as providesLanguages, contentEncodings as providesEncodings, };
89/**
90 * Determines if a request body content type is _acceptable_ to this endpoint.
91 * @param types The content types _acceptable_ to this endpoint.
92 */
93export function accepts(types) {
94 return async (ax) => {
95 var _a;
96 const ctx = await ax;
97 const { headers } = ctx.request;
98 const accepted = (_a = [...negotiated.mediaTypes(headers.get(CONTENT_TYPE))][0]) === null || _a === void 0 ? void 0 : _a.type;
99 if ((types === null || types === void 0 ? void 0 : types.length) && !types.includes(accepted))
100 throw unsupportedMediaType();
101 return Object.assign(ctx, { accepted });
102 };
103}
104/**
105 * Determines if a request body content language is _acceptable_ to this endpoint.
106 * @param languages The languages (of the request body) _acceptable_ to this endpoint.
107 */
108export function acceptsLanguages(languages) {
109 return async (ax) => {
110 var _a;
111 const ctx = await ax;
112 const { headers } = ctx.request;
113 const acceptedLanguage = (_a = [...negotiated.languages(headers.get(CONTENT_LANGUAGE))][0]) === null || _a === void 0 ? void 0 : _a.language;
114 if ((languages === null || languages === void 0 ? void 0 : languages.length) && !languages.includes(acceptedLanguage))
115 throw notAcceptable();
116 return Object.assign(ctx, { acceptedLanguage });
117 };
118}
119/**
120 * Determines if a request body content encoding is _acceptable_ to this endpoint.
121 * @param encodings The body encodings _acceptable_ to this endpoint.
122 */
123export function acceptsEncodings(encodings) {
124 return async (ax) => {
125 var _a;
126 const ctx = await ax;
127 const { headers } = ctx.request;
128 const acceptedEncoding = (_a = [...negotiated.encodings(headers.get(CONTENT_ENCODING))][0]) === null || _a === void 0 ? void 0 : _a.encoding;
129 if ((encodings === null || encodings === void 0 ? void 0 : encodings.length) && !encodings.includes(acceptedEncoding))
130 throw notAcceptable();
131 return Object.assign(ctx, { acceptedEncoding });
132 };
133}
134//# sourceMappingURL=content-negotiation.js.map
\No newline at end of file