@lodestar/api
Version:
A Typescript REST client for the Ethereum Consensus API
146 lines • 5.36 kB
JavaScript
import { toBase64 } from "@lodestar/utils";
export { HttpHeader };
var HttpHeader;
(function (HttpHeader) {
HttpHeader["ContentType"] = "content-type";
HttpHeader["Accept"] = "accept";
HttpHeader["Authorization"] = "authorization";
/**
* Used to indicate which response headers should be made available to
* scripts running in the browser, in response to a cross-origin request.
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers
*/
HttpHeader["ExposeHeaders"] = "access-control-expose-headers";
})(HttpHeader || (HttpHeader = {}));
export { MediaType };
var MediaType;
(function (MediaType) {
MediaType["json"] = "application/json";
MediaType["ssz"] = "application/octet-stream";
})(MediaType || (MediaType = {}));
export const SUPPORTED_MEDIA_TYPES = Object.values(MediaType);
function isSupportedMediaType(mediaType, supported) {
return mediaType !== null && supported.includes(mediaType);
}
export function parseContentTypeHeader(contentType) {
if (!contentType) {
return null;
}
const mediaType = contentType.split(";", 1)[0].trim().toLowerCase();
return isSupportedMediaType(mediaType, SUPPORTED_MEDIA_TYPES) ? mediaType : null;
}
export function parseAcceptHeader(accept, supported = SUPPORTED_MEDIA_TYPES) {
if (!accept) {
return null;
}
// Respect Quality Values per RFC-9110
// Acceptable mime-types are comma separated with optional whitespace
return accept
.toLowerCase()
.split(",")
.map((x) => x.trim())
.reduce((best, current) => {
// An optional `;` delimiter is used to separate the mime-type from the weight
// Normalize here, using 1 as the default qvalue
const quality = current.includes(";") ? current.split(";") : [current, "q=1"];
let mediaType = quality[0].trim();
if (mediaType === "*/*") {
// Default to json if all media types are accepted
mediaType = MediaType.json;
}
// If the mime type isn't acceptable, move on to the next entry
if (!isSupportedMediaType(mediaType, supported)) {
return best;
}
// Otherwise, the portion after the semicolon has optional whitespace and the constant prefix "q="
const weight = quality[1].trim();
if (!weight.startsWith("q=")) {
// If the format is invalid simply move on to the next entry
return best;
}
const qvalue = +weight.replace("q=", "");
if (Number.isNaN(qvalue) || qvalue > 1 || qvalue <= 0) {
// If we can't convert the qvalue to a valid number, move on
return best;
}
if (qvalue < best[0]) {
// This mime type is not preferred
return best;
}
// This mime type is preferred
return [qvalue, mediaType];
}, [0, null])[1];
}
export function setAuthorizationHeader(url, headers, { bearerToken }) {
if (bearerToken && !headers.has(HttpHeader.Authorization)) {
headers.set(HttpHeader.Authorization, `Bearer ${bearerToken}`);
}
if (url.username || url.password) {
if (!headers.has(HttpHeader.Authorization)) {
headers.set(HttpHeader.Authorization, `Basic ${toBase64(decodeURIComponent(`${url.username}:${url.password}`))}`);
}
// Remove the username and password from the URL
url.username = "";
url.password = "";
}
}
export function mergeHeaders(...headersList) {
const mergedHeaders = new Headers();
for (const headers of headersList) {
if (!headers) {
continue;
}
if (Array.isArray(headers)) {
for (const [key, value] of headers) {
mergedHeaders.set(key, value);
}
}
else if (headers instanceof Headers) {
for (const [key, value] of headers) {
mergedHeaders.set(key, value);
}
}
else {
for (const [key, value] of Object.entries(headers)) {
mergedHeaders.set(key, value);
}
}
}
return mergedHeaders;
}
/**
* Get header from request headers, by default an error will be thrown if the header
* is not present. The header can be marked as optional in which case the return value
* might be `undefined` but no error will be thrown if header is missing.
*/
export function fromHeaders(headers, name, required = true) {
// Fastify converts all headers to lower case
const header = headers[name.toLowerCase()];
if (header === undefined && required) {
throw Error(`${name} header is required`);
}
return header;
}
/**
* Extension of Headers object returned by Fetch API
*/
export class HeadersExtra extends Headers {
/**
* Get required header from response headers
*/
getRequired(name) {
const header = this.get(name);
if (header === null) {
throw Error(`${name} header is required in response`);
}
return header;
}
/**
* Get optional header from response headers.
* Return default value if it does not exist
*/
getOrDefault(name, defaultValue) {
return this.get(name) ?? defaultValue;
}
}
//# sourceMappingURL=headers.js.map