UNPKG

@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
"use strict"; 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