UNPKG

@worker-tools/middleware

Version:

A suite of standalone HTTP server middlewares for Worker Runtimes.

134 lines 6.21 kB
// deno-lint-ignore-file import { notAcceptable, unsupportedMediaType } from "@worker-tools/response-creators"; import negotiated from 'negotiated'; const weightSortFn = (a, b) => a.weight >= b.weight ? a : b; const ACCEPT = 'Accept'; const ACCEPT_ENCODING = 'Accept-Encoding'; const ACCEPT_LANGUAGE = 'Accept-Language'; const CONTENT_TYPE = 'Content-Type'; const CONTENT_LANGUAGE = 'Content-Language'; const CONTENT_ENCODING = 'Content-Encoding'; const VARY = 'Vary'; /** * Performs content negotiation over the content type of the response. * @param types The content types _provided_ by this endpoint. */ export function contentTypes(types) { return async (ax) => { const ctx = await ax; const { headers } = ctx.request; const type = [...negotiated.mediaTypes(headers.get(ACCEPT))] .filter(t => !types || types.includes(t.type)) .reduce(weightSortFn, { weight: -1 }).type; if (headers.has(ACCEPT) && types && !type) throw notAcceptable(); ctx.effects.push(response => { var _a; if (!response.headers.has(CONTENT_TYPE)) response.headers.set(CONTENT_TYPE, type); // If the server accepts more than 1 option, we set the vary header for correct caching if (((_a = types === null || types === void 0 ? void 0 : types.length) !== null && _a !== void 0 ? _a : 0) > 1) response.headers.append(VARY, ACCEPT); return response; }); return Object.assign(ctx, { type }); }; } /** * Performs content negotiation over the content language of the response. * @param languages The languages _provided_ by this endpoint. */ export function contentLanguages(languages) { return async (ax) => { const ctx = await ax; const { headers } = ctx.request; const language = [...negotiated.languages(headers.get(ACCEPT_LANGUAGE))] .filter(l => !languages || languages.includes(l.language)) .reduce(weightSortFn, { weight: -1 }).language; if (headers.has(ACCEPT_LANGUAGE) && languages && !language) throw notAcceptable(); ctx.effects.push(response => { var _a; if (!response.headers.has(CONTENT_LANGUAGE)) response.headers.set(CONTENT_LANGUAGE, language); // If the server accepts more than 1 option, we set the vary header for correct caching if (((_a = languages === null || languages === void 0 ? void 0 : languages.length) !== null && _a !== void 0 ? _a : 0) > 1) response.headers.append(VARY, ACCEPT_LANGUAGE); return response; }); return Object.assign(ctx, { language }); }; } /** * Performs content negotiation over the content encoding of the response. * @param encodings The encodings _provided_ by this endpoint. */ export function contentEncodings(encodings) { return async (ax) => { const ctx = await ax; const { headers } = ctx.request; const encoding = [...negotiated.encodings(headers.get(ACCEPT_ENCODING))] .filter(e => !encodings || encodings.includes(e.encoding)) .reduce(weightSortFn, { weight: -1 }).encoding; // TODO: how to handle status errors in middleware?? if (headers.has(ACCEPT_ENCODING) && encodings && !encoding) throw notAcceptable(); ctx.effects.push(response => { var _a; if (!response.headers.has(CONTENT_ENCODING)) response.headers.set(CONTENT_ENCODING, encoding); // If the server accepts more than 1 option, we set the vary header for correct caching if (((_a = encodings === null || encodings === void 0 ? void 0 : encodings.length) !== null && _a !== void 0 ? _a : 0) > 1) response.headers.append(VARY, ACCEPT_ENCODING); return response; }); return Object.assign(ctx, { encoding }); }; } export { contentTypes as provides, contentLanguages as providesLanguages, contentEncodings as providesEncodings, }; /** * Determines if a request body content type is _acceptable_ to this endpoint. * @param types The content types _acceptable_ to this endpoint. */ export function accepts(types) { return async (ax) => { var _a; const ctx = await ax; const { headers } = ctx.request; const accepted = (_a = [...negotiated.mediaTypes(headers.get(CONTENT_TYPE))][0]) === null || _a === void 0 ? void 0 : _a.type; if ((types === null || types === void 0 ? void 0 : types.length) && !types.includes(accepted)) throw unsupportedMediaType(); return Object.assign(ctx, { accepted }); }; } /** * Determines if a request body content language is _acceptable_ to this endpoint. * @param languages The languages (of the request body) _acceptable_ to this endpoint. */ export function acceptsLanguages(languages) { return async (ax) => { var _a; const ctx = await ax; const { headers } = ctx.request; const acceptedLanguage = (_a = [...negotiated.languages(headers.get(CONTENT_LANGUAGE))][0]) === null || _a === void 0 ? void 0 : _a.language; if ((languages === null || languages === void 0 ? void 0 : languages.length) && !languages.includes(acceptedLanguage)) throw notAcceptable(); return Object.assign(ctx, { acceptedLanguage }); }; } /** * Determines if a request body content encoding is _acceptable_ to this endpoint. * @param encodings The body encodings _acceptable_ to this endpoint. */ export function acceptsEncodings(encodings) { return async (ax) => { var _a; const ctx = await ax; const { headers } = ctx.request; const acceptedEncoding = (_a = [...negotiated.encodings(headers.get(CONTENT_ENCODING))][0]) === null || _a === void 0 ? void 0 : _a.encoding; if ((encodings === null || encodings === void 0 ? void 0 : encodings.length) && !encodings.includes(acceptedEncoding)) throw notAcceptable(); return Object.assign(ctx, { acceptedEncoding }); }; } //# sourceMappingURL=content-negotiation.js.map