UNPKG

got-cjs

Version:

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

165 lines (164 loc) 7.46 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const events_1 = require("events"); const is_1 = __importDefault(require("@sindresorhus/is")); const p_cancelable_1 = __importDefault(require("p-cancelable")); const errors_js_1 = require("../core/errors.js"); const index_js_1 = __importDefault(require("../core/index.js")); const response_js_1 = require("../core/response.js"); const proxy_events_js_1 = __importDefault(require("../core/utils/proxy-events.js")); const types_js_1 = require("./types.js"); const proxiedRequestEvents = [ 'request', 'response', 'redirect', 'uploadProgress', 'downloadProgress', ]; function asPromise(firstRequest) { let globalRequest; let globalResponse; let normalizedOptions; const emitter = new events_1.EventEmitter(); const promise = new p_cancelable_1.default((resolve, reject, onCancel) => { onCancel(() => { globalRequest.destroy(); }); onCancel.shouldReject = false; onCancel(() => { reject(new types_js_1.CancelError(globalRequest)); }); const makeRequest = (retryCount) => { // Errors when a new request is made after the promise settles. // Used to detect a race condition. // See https://github.com/sindresorhus/got/issues/1489 onCancel(() => { }); const request = firstRequest ?? new index_js_1.default(undefined, undefined, normalizedOptions); request.retryCount = retryCount; request._noPipe = true; globalRequest = request; request.once('response', async (response) => { // Parse body const contentEncoding = (response.headers['content-encoding'] ?? '').toLowerCase(); const isCompressed = contentEncoding === 'gzip' || contentEncoding === 'deflate' || contentEncoding === 'br'; const { options } = request; if (isCompressed && !options.decompress) { response.body = response.rawBody; } else { try { response.body = (0, response_js_1.parseBody)(response, options.responseType, options.parseJson, options.encoding); } catch (error) { // Fall back to `utf8` response.body = response.rawBody.toString(); if ((0, response_js_1.isResponseOk)(response)) { request._beforeError(error); return; } } } try { const hooks = options.hooks.afterResponse; for (const [index, hook] of hooks.entries()) { // @ts-expect-error TS doesn't notice that CancelableRequest is a Promise // eslint-disable-next-line no-await-in-loop response = await hook(response, async (updatedOptions) => { options.merge(updatedOptions); options.prefixUrl = ''; if (updatedOptions.url) { options.url = updatedOptions.url; } // Remove any further hooks for that request, because we'll call them anyway. // The loop continues. We don't want duplicates (asPromise recursion). options.hooks.afterResponse = options.hooks.afterResponse.slice(0, index); throw new errors_js_1.RetryError(request); }); if (!(is_1.default.object(response) && is_1.default.number(response.statusCode) && !is_1.default.nullOrUndefined(response.body))) { throw new TypeError('The `afterResponse` hook returned an invalid value'); } } } catch (error) { request._beforeError(error); return; } globalResponse = response; if (!(0, response_js_1.isResponseOk)(response)) { request._beforeError(new errors_js_1.HTTPError(response)); return; } request.destroy(); resolve(request.options.resolveBodyOnly ? response.body : response); }); const onError = (error) => { if (promise.isCanceled) { return; } const { options } = request; if (error instanceof errors_js_1.HTTPError && !options.throwHttpErrors) { const { response } = error; request.destroy(); resolve(request.options.resolveBodyOnly ? response.body : response); return; } reject(error); }; request.once('error', onError); const previousBody = request.options?.body; request.once('retry', (newRetryCount, error) => { firstRequest = undefined; const newBody = request.options.body; if (previousBody === newBody && is_1.default.nodeStream(newBody)) { error.message = 'Cannot retry with consumed body stream'; onError(error); return; } // This is needed! We need to reuse `request.options` because they can get modified! // For example, by calling `promise.json()`. normalizedOptions = request.options; makeRequest(newRetryCount); }); (0, proxy_events_js_1.default)(request, emitter, proxiedRequestEvents); if (is_1.default.undefined(firstRequest)) { void request.flush(); } }; makeRequest(0); }); promise.on = (event, fn) => { emitter.on(event, fn); return promise; }; promise.off = (event, fn) => { emitter.off(event, fn); return promise; }; const shortcut = (responseType) => { const newPromise = (async () => { // Wait until downloading has ended await promise; const { options } = globalResponse.request; return (0, response_js_1.parseBody)(globalResponse, responseType, options.parseJson, options.encoding); })(); // eslint-disable-next-line @typescript-eslint/no-floating-promises Object.defineProperties(newPromise, Object.getOwnPropertyDescriptors(promise)); return newPromise; }; promise.json = () => { if (globalRequest.options) { const { headers } = globalRequest.options; if (!globalRequest.writableFinished && !('accept' in headers)) { headers.accept = 'application/json'; } } return shortcut('json'); }; promise.buffer = () => shortcut('buffer'); promise.text = () => shortcut('text'); return promise; } exports.default = asPromise;