@nahkies/typescript-fetch-runtime
Version:
Runtime package for code generated by @nahkies/openapi-code-generator using the typescript-fetch template
151 lines (150 loc) • 5.89 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.AbstractFetchClient = void 0;
const tslib_1 = require("tslib");
const qs_1 = tslib_1.__importDefault(require("qs"));
const url_search_params_1 = require("./request-bodies/url-search-params");
tslib_1.__exportStar(require("./types"), exports);
class AbstractFetchClient {
basePath;
defaultHeaders;
defaultTimeout;
constructor(config) {
this.basePath = config.basePath;
this.defaultHeaders = config.defaultHeaders ?? {};
this.defaultTimeout = config.defaultTimeout;
}
async _fetch(url, opts, timeout = this.defaultTimeout) {
// main abort controller that will be returned to the caller
const cancelRequest = new AbortController();
// if a timeout is provided, set a timeout signal and connect to main abort controller
if (timeout && timeout > 0) {
const timeoutSignal = AbortSignal.timeout(timeout);
timeoutSignal.addEventListener("abort", () => {
cancelRequest.abort(timeoutSignal.reason);
});
}
// if we were provided a signal connect it to the main abort controller
const userSignal = opts.signal;
if (userSignal) {
userSignal.addEventListener("abort", () => cancelRequest.abort(userSignal.reason));
}
const headers = opts.headers ?? this._headers();
return fetch(url, {
...opts,
headers,
signal: cancelRequest.signal,
}).catch((err) => {
// workaround bug where eg: TimeoutError gets converted to an AbortError
// https://stackoverflow.com/a/75973817/1445636
if (err?.name === "AbortError") {
cancelRequest.signal.throwIfAborted();
}
// if not aborted just throw
throw err;
});
}
_query(params) {
const definedParams = Object.entries(params).filter(([, v]) => v !== undefined);
if (!definedParams.length) {
return "";
}
return `?${qs_1.default.stringify(Object.fromEntries(definedParams), {
indices: false,
})}`;
}
/**
* Combines headers for a request, with precedence
* 1. default headers
* 2. route level header parameters
* 3. raw request config (escape hatch)
*
* following these rules:
* - header values of `undefined` are skipped
* - header values of `null` will remove/delete any previously set headers
*
* Eg:
* Passing `Authorization: null` as a parameter, will clear out any
* default `Authorization` header.
*
* But passing `Authorization: undefined` as parameter will fallthrough
* to the default `Authorization` header.
*
* @param paramHeaders
* @param optsHeaders
* @protected
*/
_headers(paramHeaders = {}, optsHeaders = {}) {
const headers = new Headers();
/*
This is pretty hideous, but basically we:
- Maintain a set of deleted headers, the nullSet
- Apply headers from most specific, to least
- Delete headers if we encounter a null value and note this in the nullSet
The primary reason is to enable the use of headers.append to support setting
the same header multiple times (aka `Set-Cookie`), whilst *also* allowing more
specific header sources to override all instances of the less specific source.
*/
const nullSet = new Set();
this.setHeaders(headers, optsHeaders, nullSet);
this.setHeaders(headers, paramHeaders, nullSet);
this.setHeaders(headers, this.defaultHeaders, nullSet);
return headers;
}
_requestBodyToUrlSearchParams(obj, encoding = {}) {
return (0, url_search_params_1.requestBodyToUrlSearchParams)(obj, encoding);
}
setHeaders(headers, headersInit, nullSet) {
const headersArray = this.headersAsArray(headersInit);
const filteredHeadersArray = headersArray.filter(([headerName, headerValue]) => !headers.has(headerName) &&
headerValue !== undefined &&
!nullSet.has(headerName));
for (const [headerName, headerValue] of filteredHeadersArray) {
if (headerValue === null) {
headers.delete(headerName);
nullSet.add(headerName);
}
else if (headerValue !== undefined) {
headers.append(headerName, headerValue.toString());
}
}
}
headersAsArray(headers) {
if (isMultiDimArray(headers)) {
return headers.flatMap((it) => isNonEmptyArray(it) ? headerArrayToTuples(it) : []);
}
if (headers instanceof Headers) {
const result = [];
headers.forEach((value, key) => {
result.push([key, value]);
});
return result;
}
if (headers && typeof headers === "object") {
const result = [];
for (const [headerName, values] of Object.entries(headers)) {
if (Array.isArray(values)) {
for (const value of values) {
result.push([headerName, value]);
}
}
else {
result.push([headerName, values]);
}
}
return result;
}
throw new Error(`couldn't process headers '${headers}'`);
}
}
exports.AbstractFetchClient = AbstractFetchClient;
function isMultiDimArray(arr) {
return Array.isArray(arr) && Array.isArray(arr[0]);
}
function isNonEmptyArray(it) {
return Array.isArray(it) && it.length > 0;
}
function headerArrayToTuples([head, ...rest]) {
return rest.map((value) => [head, value]);
}
//# sourceMappingURL=main.js.map
;