UNPKG

got

Version:

Human-friendly and powerful HTTP request library for Node.js

153 lines (152 loc) 6.23 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const EventEmitter = require("events"); const getStream = require("get-stream"); const PCancelable = require("p-cancelable"); const is_1 = require("@sindresorhus/is"); const errors_1 = require("./errors"); const normalize_arguments_1 = require("./normalize-arguments"); const request_as_event_emitter_1 = require("./request-as-event-emitter"); const parseBody = (body, responseType, encoding) => { if (responseType === 'json') { return body.length === 0 ? '' : JSON.parse(body.toString()); } if (responseType === 'buffer') { return Buffer.from(body); } if (responseType === 'text') { return body.toString(encoding); } throw new TypeError(`Unknown body type '${responseType}'`); }; function createRejection(error) { const promise = Promise.reject(error); const returnPromise = () => promise; promise.json = returnPromise; promise.text = returnPromise; promise.buffer = returnPromise; promise.on = returnPromise; return promise; } exports.createRejection = createRejection; function asPromise(options) { const proxy = new EventEmitter(); let body; const promise = new PCancelable((resolve, reject, onCancel) => { const emitter = request_as_event_emitter_1.default(options); onCancel(emitter.abort); const emitError = async (error) => { try { for (const hook of options.hooks.beforeError) { // eslint-disable-next-line no-await-in-loop error = await hook(error); } reject(error); } catch (error_) { reject(error_); } }; emitter.on('response', async (response) => { var _a; proxy.emit('response', response); // Download body try { body = await getStream.buffer(response, { encoding: 'binary' }); } catch (error) { emitError(new errors_1.ReadError(error, options)); return; } if ((_a = response.req) === null || _a === void 0 ? void 0 : _a.aborted) { // Canceled while downloading - will throw a `CancelError` or `TimeoutError` error return; } const isOk = () => { const { statusCode } = response; const limitStatusCode = options.followRedirect ? 299 : 399; return (statusCode >= 200 && statusCode <= limitStatusCode) || statusCode === 304; }; // Parse body try { response.body = parseBody(body, options.responseType, options.encoding); } catch (error) { // Fall back to `utf8` response.body = body.toString(); if (isOk()) { const parseError = new errors_1.ParseError(error, response, options); emitError(parseError); return; } } try { for (const [index, hook] of options.hooks.afterResponse.entries()) { // @ts-ignore TS doesn't notice that CancelableRequest is a Promise // eslint-disable-next-line no-await-in-loop response = await hook(response, async (updatedOptions) => { const typedOptions = normalize_arguments_1.normalizeArguments(normalize_arguments_1.mergeOptions(options, { ...updatedOptions, retry: { calculateDelay: () => 0 }, throwHttpErrors: false, resolveBodyOnly: false })); // Remove any further hooks for that request, because we'll call them anyway. // The loop continues. We don't want duplicates (asPromise recursion). typedOptions.hooks.afterResponse = options.hooks.afterResponse.slice(0, index); for (const hook of options.hooks.beforeRetry) { // eslint-disable-next-line no-await-in-loop await hook(typedOptions); } const promise = asPromise(typedOptions); onCancel(() => { promise.catch(() => { }); promise.cancel(); }); return promise; }); } } catch (error) { emitError(error); return; } // Check for HTTP error codes if (!isOk()) { const error = new errors_1.HTTPError(response, options); if (emitter.retry(error)) { return; } if (options.throwHttpErrors) { emitError(error); return; } } resolve(options.resolveBodyOnly ? response.body : response); }); emitter.once('error', reject); request_as_event_emitter_1.proxyEvents(proxy, emitter); }); promise.on = (name, fn) => { proxy.on(name, fn); return promise; }; const shortcut = (responseType) => { // eslint-disable-next-line promise/prefer-await-to-then const newPromise = promise.then(() => parseBody(body, responseType, options.encoding)); Object.defineProperties(newPromise, Object.getOwnPropertyDescriptors(promise)); return newPromise; }; promise.json = () => { if (is_1.default.undefined(body) && is_1.default.undefined(options.headers.accept)) { options.headers.accept = 'application/json'; } return shortcut('json'); }; promise.buffer = () => shortcut('buffer'); promise.text = () => shortcut('text'); return promise; } exports.default = asPromise;