UNPKG

@signumjs/http

Version:
125 lines 4.46 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.HttpAdapterFetch = void 0; const httpResponse_1 = require("./httpResponse"); const httpError_1 = require("./httpError"); const defaultValidateStatus = (status) => status >= 200 && status < 300; /** * Http implementation of {@link Http} using the platform's native `fetch`. * * Works in browsers, Node.js >= 18, Deno, Bun and Cloudflare Workers without * any external dependency. * * Prefer {@link HttpClientFactory.createHttpClient} to create an instance. * * @module http */ class HttpAdapterFetch { _baseURL; _defaults; constructor(baseURL, options = {}) { this._baseURL = baseURL.replace(/\/+$/, ''); this._defaults = options; } async get(url, options) { return this.request('GET', url, undefined, options); } async post(url, payload, options) { return this.request('POST', url, payload, options); } async put(url, payload, options) { return this.request('PUT', url, payload, options); } async delete(url, options) { return this.request('DELETE', url, undefined, options); } buildUrl(url) { if (/^https?:\/\//i.test(url)) { return url; } if (!this._baseURL) { return url; } return url.startsWith('/') ? `${this._baseURL}${url}` : `${this._baseURL}/${url}`; } resolveOptions(perCall) { const merged = { ...this._defaults, ...perCall, headers: { ...(this._defaults.headers || {}), ...(perCall?.headers || {}) }, fetchOptions: { ...(this._defaults.fetchOptions || {}), ...(perCall?.fetchOptions || {}) } }; return { ...merged, validateStatus: merged.validateStatus || defaultValidateStatus }; } async request(method, url, payload, perCall) { const opts = this.resolveOptions(perCall); const fullUrl = this.buildUrl(url); const headers = { Accept: 'application/json', ...(opts.headers || {}) }; let body; if (payload !== undefined && payload !== null) { if (typeof payload === 'string' || payload instanceof ArrayBuffer || payload instanceof Uint8Array) { body = payload; } else if (typeof FormData !== 'undefined' && payload instanceof FormData) { body = payload; } else { body = JSON.stringify(payload); if (!headers['Content-Type'] && !headers['content-type']) { headers['Content-Type'] = 'application/json'; } } } const init = { ...(opts.fetchOptions || {}), method, headers, body }; if (opts.timeout && opts.timeout > 0) { init.signal = AbortSignal.timeout(opts.timeout); } let response; try { response = await fetch(fullUrl, init); } catch (error) { if (error?.name === 'TimeoutError' || error?.name === 'AbortError') { throw new httpError_1.HttpError(url, 0, 'Request timed out', error?.message || null); } throw new httpError_1.HttpError(url, 0, 'Request failed', error?.message || String(error)); } const data = await this.parseBody(response); if (!opts.validateStatus(response.status)) { throw new httpError_1.HttpError(url, response.status, response.statusText || 'Request failed', data); } return new httpResponse_1.HttpResponse(response.status, data); } // eslint-disable-next-line class-methods-use-this async parseBody(response) { if (response.status === 204 || response.status === 205) { return null; } const contentType = response.headers.get('content-type') || ''; if (contentType.includes('application/json')) { const text = await response.text(); return text ? JSON.parse(text) : null; } // Fall back to text; callers that need blobs/streams can use fetchOptions return response.text(); } } exports.HttpAdapterFetch = HttpAdapterFetch; //# sourceMappingURL=httpAdapterFetch.js.map