text: "use strict";
  Object.defineProperty(exports, "__esModule", { value: true });
  exports.UnsupportedProtocolError = exports.ReadError = exports.TimeoutError = exports.UploadError = exports.CacheError = exports.HTTPError = exports.MaxRedirectsError = exports.RequestError = exports.setNonEnumerableProperties = exports.knownHookEvents = exports.withoutBody = exports.kIsNormalizedAlready = void 0;
  const util_1 = require("util");
  const stream_1 = require("stream");
  const fs_1 = require("fs");
  const url_1 = require("url");
  const http = require("http");
  const http_1 = require("http");
  const https = require("https");
  const http_timer_1 = require("@szmarczak/http-timer");
  const cacheable_lookup_1 = require("cacheable-lookup");
  const CacheableRequest = require("cacheable-request");
  const decompressResponse = require("decompress-response");
  // @ts-expect-error Missing types
  const http2wrapper = require("http2-wrapper");
  const lowercaseKeys = require("lowercase-keys");
  const is_1 = require("@sindresorhus/is");
  const get_body_size_1 = require("./utils/get-body-size");
  const is_form_data_1 = require("./utils/is-form-data");
  const proxy_events_1 = require("./utils/proxy-events");
  const timed_out_1 = require("./utils/timed-out");
  const url_to_options_1 = require("./utils/url-to-options");
  const options_to_url_1 = require("./utils/options-to-url");
  const weakable_map_1 = require("./utils/weakable-map");
  const get_buffer_1 = require("./utils/get-buffer");
  const dns_ip_version_1 = require("./utils/dns-ip-version");
  const is_response_ok_1 = require("./utils/is-response-ok");
  const deprecation_warning_1 = require("../utils/deprecation-warning");
  const normalize_arguments_1 = require("../as-promise/normalize-arguments");
  const calculate_retry_delay_1 = require("./calculate-retry-delay");
  const globalDnsCache = new cacheable_lookup_1.default();
  const kRequest = Symbol('request');
  const kResponse = Symbol('response');
  const kResponseSize = Symbol('responseSize');
  const kDownloadedSize = Symbol('downloadedSize');
  const kBodySize = Symbol('bodySize');
  const kUploadedSize = Symbol('uploadedSize');
  const kServerResponsesPiped = Symbol('serverResponsesPiped');
  const kUnproxyEvents = Symbol('unproxyEvents');
  const kIsFromCache = Symbol('isFromCache');
  const kCancelTimeouts = Symbol('cancelTimeouts');
  const kStartedReading = Symbol('startedReading');
  const kStopReading = Symbol('stopReading');
  const kTriggerRead = Symbol('triggerRead');
  const kBody = Symbol('body');
  const kJobs = Symbol('jobs');
  const kOriginalResponse = Symbol('originalResponse');
  const kRetryTimeout = Symbol('retryTimeout');
  exports.kIsNormalizedAlready = Symbol('isNormalizedAlready');
  const supportsBrotli = is_1.default.string(process.versions.brotli);
  exports.withoutBody = new Set(['GET', 'HEAD']);
  exports.knownHookEvents = [
      'init',
      'beforeRequest',
      'beforeRedirect',
      'beforeError',
      'beforeRetry',
      // Promise-Only
      'afterResponse'
  ];
  function validateSearchParameters(searchParameters) {
      // eslint-disable-next-line guard-for-in
      for (const key in searchParameters) {
          const value = searchParameters[key];
          if (!is_1.default.string(value) && !is_1.default.number(value) && !is_1.default.boolean(value) && !is_1.default.null_(value) && !is_1.default.undefined(value)) {
              throw new TypeError(`The \`searchParams\` value '${String(value)}' must be a string, number, boolean or null`);
          }
      }
  }
  function isClientRequest(clientRequest) {
      return is_1.default.object(clientRequest) && !('statusCode' in clientRequest);
  }
  const cacheableStore = new weakable_map_1.default();
  const waitForOpenFile = async (file) => new Promise((resolve, reject) => {
      const onError = (error) => {
          reject(error);
      };
      // Node.js 12 has incomplete types
      if (!file.pending) {
          resolve();
      }
      file.once('error', onError);
      file.once('ready', () => {
          file.off('error', onError);
          resolve();
      });
  });
  const redirectCodes = new Set([300, 301, 302, 303, 304, 307, 308]);
  const nonEnumerableProperties = [
      'context',
      'body',
      'json',
      'form'
  ];
  exports.setNonEnumerableProperties = (sources, to) => {
      // Non enumerable properties shall not be merged
      const properties = {};
      for (const source of sources) {
          if (!source) {
              continue;
          }
          for (const name of nonEnumerableProperties) {
              if (!(name in source)) {
                  continue;
              }
              properties[name] = {
                  writable: true,
                  configurable: true,
                  enumerable: false,
                  // @ts-expect-error TS doesn't see the check above
                  value: source[name]
              };
          }
      }
      Object.defineProperties(to, properties);
  };
  /**
  An error to be thrown when a request fails.
  Contains a `code` property with error class code, like `ECONNREFUSED`.
  */
  class RequestError extends Error {
      constructor(message, error, self) {
          var _a;
          super(message);
          Error.captureStackTrace(this, this.constructor);
          this.name = 'RequestError';
          this.code = error.code;
          if (self instanceof Request) {
              Object.defineProperty(this, 'request', {
                  enumerable: false,
                  value: self
              });
              Object.defineProperty(this, 'response', {
                  enumerable: false,
                  value: self[kResponse]
              });
              Object.defineProperty(this, 'options', {
                  // This fails because of TS 3.7.2 useDefineForClassFields
                  // Ref: https://github.com/microsoft/TypeScript/issues/34972
                  enumerable: false,
                  value: self.options
              });
          }
          else {
              Object.defineProperty(this, 'options', {
                  // This fails because of TS 3.7.2 useDefineForClassFields
                  // Ref: https://github.com/microsoft/TypeScript/issues/34972
                  enumerable: false,
                  value: self
              });
          }
          this.timings = (_a = this.request) === null || _a === void 0 ? void 0 : _a.timings;
          // Recover the original stacktrace
          if (!is_1.default.undefined(error.stack)) {
              const indexOfMessage = this.stack.indexOf(this.message) + this.message.length;
              const thisStackTrace = this.stack.slice(indexOfMessage).split('\n').reverse();
              const errorStackTrace = error.stack.slice(error.stack.indexOf(error.message) + error.message.length).split('\n').reverse();
              // Remove duplicated traces
              while (errorStackTrace.length !== 0 && errorStackTrace[0] === thisStackTrace[0]) {
                  thisStackTrace.shift();
              }
              this.stack = `${this.stack.slice(0, indexOfMessage)}${thisStackTrace.reverse().join('\n')}${errorStackTrace.reverse().join('\n')}`;
          }
      }
  }
  exports.RequestError = RequestError;
  /**
  An error to be thrown when the server redirects you more than ten times.
  Includes a `response` property.
  */
  class MaxRedirectsError extends RequestError {
      constructor(request) {
          super(`Redirected ${request.options.maxRedirects} times. Aborting.`, {}, request);
          this.name = 'MaxRedirectsError';
      }
  }
  exports.MaxRedirectsError = MaxRedirectsError;
  /**
  An error to be thrown when the server response code is not 2xx nor 3xx if `options.followRedirect` is `true`, but always except for 304.
  Includes a `response` property.
  */
  class HTTPError extends RequestError {
      constructor(response) {
          super(`Response code ${response.statusCode} (${response.statusMessage})`, {}, response.request);
          this.name = 'HTTPError';
      }
  }
  exports.HTTPError = HTTPError;
  /**
  An error to be thrown when a cache method fails.
  For example, if the database goes down or there's a filesystem error.
  */
  class CacheError extends RequestError {
      constructor(error, request) {
          super(error.message, error, request);
          this.name = 'CacheError';
      }
  }
  exports.CacheError = CacheError;
  /**
  An error to be thrown when the request body is a stream and an error occurs while reading from that stream.
  */
  class UploadError extends RequestError {
      constructor(error, request) {
          super(error.message, error, request);
          this.name = 'UploadError';
      }
  }
  exports.UploadError = UploadError;
  /**
  An error to be thrown when the request is aborted due to a timeout.
  Includes an `event` and `timings` property.
  */
  class TimeoutError extends RequestError {
      constructor(error, timings, request) {
          super(error.message, error, request);
          this.name = 'TimeoutError';
          this.event = error.event;
          this.timings = timings;
      }
  }
  exports.TimeoutError = TimeoutError;
  /**
  An error to be thrown when reading from response stream fails.
  */
  class ReadError extends RequestError {
      constructor(error, request) {
          super(error.message, error, request);
          this.name = 'ReadError';
      }
  }
  exports.ReadError = ReadError;
  /**
  An error to be thrown when given an unsupported protocol.
  */
  class UnsupportedProtocolError extends RequestError {
      constructor(options) {
          super(`Unsupported protocol "${options.url.protocol}"`, {}, options);
          this.name = 'UnsupportedProtocolError';
      }
  }
  exports.UnsupportedProtocolError = UnsupportedProtocolError;
  const proxiedRequestEvents = [
      'socket',
      'connect',
      'continue',
      'information',
      'upgrade',
      'timeout'
  ];
  class Request extends stream_1.Duplex {
      constructor(url, options = {}, defaults) {
          super({
              // This must be false, to enable throwing after destroy
              // It is used for retry logic in Promise API
              autoDestroy: false,
              // It needs to be zero because we're just proxying the data to another stream
              highWaterMark: 0
          });
          this[kDownloadedSize] = 0;
          this[kUploadedSize] = 0;
          this.requestInitialized = false;
          this[kServerResponsesPiped] = new Set();
          this.redirects = [];
          this[kStopReading] = false;
          this[kTriggerRead] = false;
          this[kJobs] = [];
          this.retryCount = 0;
          // TODO: Remove this when targeting Node.js >= 12
          this._progressCallbacks = [];
          const unlockWrite = () => this._unlockWrite();
          const lockWrite = () => this._lockWrite();
          this.on('pipe', (source) => {
              source.prependListener('data', unlockWrite);
              source.on('data', lockWrite);
              source.prependListener('end', unlockWrite);
              source.on('end', lockWrite);
          });
          this.on('unpipe', (source) => {
              source.off('data', unlockWrite);
              source.off('data', lockWrite);
              source.off('end', unlockWrite);
              source.off('end', lockWrite);
          });
          this.on('pipe', source => {
              if (source instanceof http_1.IncomingMessage) {
                  this.options.headers = {
                      ...source.headers,
                      ...this.options.headers
                  };
              }
          });
          const { json, body, form } = options;
          if (json || body || form) {
              this._lockWrite();
          }
          (async (nonNormalizedOptions) => {
              var _a;
              try {
                  if (nonNormalizedOptions.body instanceof fs_1.ReadStream) {
                      await waitForOpenFile(nonNormalizedOptions.body);
                  }
                  if (exports.kIsNormalizedAlready in nonNormalizedOptions) {
                      this.options = nonNormalizedOptions;
                  }
                  else {
                      // @ts-expect-error Common TypeScript bug saying that `this.constructor` is not accessible
                      this.options = this.constructor.normalizeArguments(url, nonNormalizedOptions, defaults);
                  }
                  const { url: normalizedURL } = this.options;
                  if (!normalizedURL) {
                      throw new TypeError('Missing `url` property');
                  }
                  this.requestUrl = normalizedURL.toString();
                  decodeURI(this.requestUrl);
                  await this._finalizeBody();
                  await this._makeRequest();
                  if (this.destroyed) {
                      (_a = this[kRequest]) === null || _a === void 0 ? void 0 : _a.destroy();
                      return;
                  }
                  // Queued writes etc.
                  for (const job of this[kJobs]) {
                      job();
                  }
                  // Prevent memory leak
                  this[kJobs].length = 0;
                  this.requestInitialized = true;
              }
              catch (error) {
                  if (error instanceof RequestError) {
                      this._beforeError(error);
                      return;
                  }
                  // This is a workaround for https://github.com/nodejs/node/issues/33335
                  if (!this.destroyed) {
                      this.destroy(error);
                  }
              }
          })(options);
      }
      static normalizeArguments(url, options, defaults) {
          var _a, _b, _c, _d;
          const rawOptions = options;
          if (is_1.default.object(url) && !is_1.default.urlInstance(url)) {
              options = { ...defaults, ...url, ...options };
          }
          else {
              if (url && options && options.url !== undefined) {
                  throw new TypeError('The `url` option is mutually exclusive with the `input` argument');
              }
              options = { ...defaults, ...options };
              if (url !== undefined) {
                  options.url = url;
              }
              if (is_1.default.urlInstance(options.url)) {
                  options.url = new url_1.URL(options.url.toString());
              }
          }
          // TODO: Deprecate URL options in Got 12.
          // Support extend-specific options
          if (options.cache === false) {
              options.cache = undefined;
          }
          if (options.dnsCache === false) {
              options.dnsCache = undefined;
          }
          // Nice type assertions
          is_1.assert.any([is_1.default.string, is_1.default.undefined], options.method);
          is_1.assert.any([is_1.default.object, is_1.default.undefined], options.headers);
          is_1.assert.any([is_1.default.string, is_1.default.urlInstance, is_1.default.undefined], options.prefixUrl);
          is_1.assert.any([is_1.default.object, is_1.default.undefined], options.cookieJar);
          is_1.assert.any([is_1.default.object, is_1.default.string, is_1.default.undefined], options.searchParams);
          is_1.assert.any([is_1.default.object, is_1.default.string, is_1.default.undefined], options.cache);
          is_1.assert.any([is_1.default.object, is_1.default.number, is_1.default.undefined], options.timeout);
          is_1.assert.any([is_1.default.object, is_1.default.undefined], options.context);
          is_1.assert.any([is_1.default.object, is_1.default.undefined], options.hooks);
          is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.decompress);
          is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.ignoreInvalidCookies);
          is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.followRedirect);
          is_1.assert.any([is_1.default.number, is_1.default.undefined], options.maxRedirects);
          is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.throwHttpErrors);
          is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.http2);
          is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.allowGetBody);
          is_1.assert.any([is_1.default.string, is_1.default.undefined], options.localAddress);
          is_1.assert.any([dns_ip_version_1.isDnsLookupIpVersion, is_1.default.undefined], options.dnsLookupIpVersion);
          is_1.assert.any([is_1.default.object, is_1.default.undefined], options.https);
          is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.rejectUnauthorized);
          if (options.https) {
              is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.https.rejectUnauthorized);
              is_1.assert.any([is_1.default.function_, is_1.default.undefined], options.https.checkServerIdentity);
              is_1.assert.any([is_1.default.string, is_1.default.object, is_1.default.array, is_1.default.undefined], options.https.certificateAuthority);
              is_1.assert.any([is_1.default.string, is_1.default.object, is_1.default.array, is_1.default.undefined], options.https.key);
              is_1.assert.any([is_1.default.string, is_1.default.object, is_1.default.array, is_1.default.undefined], options.https.certificate);
              is_1.assert.any([is_1.default.string, is_1.default.undefined], options.https.passphrase);
          }
          is_1.assert.any([is_1.default.object, is_1.default.undefined], options.cacheOptions);
          // `options.method`
          if (is_1.default.string(options.method)) {
              options.method = options.method.toUpperCase();
          }
          else {
              options.method = 'GET';
          }
          // `options.headers`
          if (options.headers === (defaults === null || defaults === void 0 ? void 0 : defaults.headers)) {
              options.headers = { ...options.headers };
          }
          else {
              options.headers = lowercaseKeys({ ...(defaults === null || defaults === void 0 ? void 0 : defaults.headers), ...options.headers });
          }
          // Disallow legacy `url.Url`
          if ('slashes' in options) {
              throw new TypeError('The legacy `url.Url` has been deprecated. Use `URL` instead.');
          }
          // `options.auth`
          if ('auth' in options) {
              throw new TypeError('Parameter `auth` is deprecated. Use `username` / `password` instead.');
          }
          // `options.searchParams`
          if ('searchParams' in options) {
              if (options.searchParams && options.searchParams !== (defaults === null || defaults === void 0 ? void 0 : defaults.searchParams)) {
                  let searchParameters;
                  if (is_1.default.string(options.searchParams) || (options.searchParams instanceof url_1.URLSearchParams)) {
                      searchParameters = new url_1.URLSearchParams(options.searchParams);
                  }
                  else {
                      validateSearchParameters(options.searchParams);
                      searchParameters = new url_1.URLSearchParams();
                      // eslint-disable-next-line guard-for-in
                      for (const key in options.searchParams) {
                          const value = options.searchParams[key];
                          if (value === null) {
                              searchParameters.append(key, '');
                          }
                          else if (value !== undefined) {
                              searchParameters.append(key, value);
                          }
                      }
                  }
                  // `normalizeArguments()` is also used to merge options
                  (_a = defaults === null || defaults === void 0 ? void 0 : defaults.searchParams) === null || _a === void 0 ? void 0 : _a.forEach((value, key) => {
                      // Only use default if one isn't already defined
                      if (!searchParameters.has(key)) {
                          searchParameters.append(key, value);
                      }
                  });
                  options.searchParams = searchParameters;
              }
          }
          // `options.username` & `options.password`
          options.username = (_b = options.username) !== null && _b !== void 0 ? _b : '';
          options.password = (_c = options.password) !== null && _c !== void 0 ? _c : '';
          // `options.prefixUrl` & `options.url`
          if (options.prefixUrl) {
              options.prefixUrl = options.prefixUrl.toString();
              if (options.prefixUrl !== '' && !options.prefixUrl.endsWith('/')) {
                  options.prefixUrl += '/';
              }
          }
          else {
              options.prefixUrl = '';
          }
          if (is_1.default.string(options.url)) {
              if (options.url.startsWith('/')) {
                  throw new Error('`input` must not start with a slash when using `prefixUrl`');
              }
              options.url = options_to_url_1.default(options.prefixUrl + options.url, options);
          }
          else if ((is_1.default.undefined(options.url) && options.prefixUrl !== '') || options.protocol) {
              options.url = options_to_url_1.default(options.prefixUrl, options);
          }
          if (options.url) {
              // Make it possible to change `options.prefixUrl`
              let { prefixUrl } = options;
              Object.defineProperty(options, 'prefixUrl', {
                  set: (value) => {
                      const url = options.url;
                      if (!url.href.startsWith(value)) {
                          throw new Error(`Cannot change \`prefixUrl\` from ${prefixUrl} to ${value}: ${url.href}`);
                      }
                      options.url = new url_1.URL(value + url.href.slice(prefixUrl.length));
                      prefixUrl = value;
                  },
                  get: () => prefixUrl
              });
              // Support UNIX sockets
              let { protocol } = options.url;
              if (protocol === 'unix:') {
                  protocol = 'http:';
                  options.url = new url_1.URL(`http://unix${options.url.pathname}${options.url.search}`);
              }
              // Set search params
              if (options.searchParams) {
                  // eslint-disable-next-line @typescript-eslint/no-base-to-string
                  options.url.search = options.searchParams.toString();
              }
              // Protocol check
              if (protocol !== 'http:' && protocol !== 'https:') {
                  throw new UnsupportedProtocolError(options);
              }
              // Update `username`
              if (options.username === '') {
                  options.username = options.url.username;
              }
              else {
                  options.url.username = options.username;
              }
              // Update `password`
              if (options.password === '') {
                  options.password = options.url.password;
              }
              else {
                  options.url.password = options.password;
              }
          }
          // `options.cookieJar`
          const { cookieJar } = options;
          if (cookieJar) {
              let { setCookie, getCookieString } = cookieJar;
              is_1.assert.function_(setCookie);
              is_1.assert.function_(getCookieString);
              /* istanbul ignore next: Horrible `tough-cookie` v3 check */
              if (setCookie.length === 4 && getCookieString.length === 0) {
                  setCookie = util_1.promisify(setCookie.bind(options.cookieJar));
                  getCookieString = util_1.promisify(getCookieString.bind(options.cookieJar));
                  options.cookieJar = {
                      setCookie,
                      getCookieString: getCookieString
                  };
              }
          }
          // `options.cache`
          const { cache } = options;
          if (cache) {
              if (!cacheableStore.has(cache)) {
                  cacheableStore.set(cache, new CacheableRequest(((requestOptions, handler) => {
                      const result = requestOptions[kRequest](requestOptions, handler);
                      // TODO: remove this when `cacheable-request` supports async request functions.
                      if (is_1.default.promise(result)) {
                          // @ts-expect-error
                          // We only need to implement the error handler in order to support HTTP2 caching.
                          // The result will be a promise anyway.
                          result.once = (event, handler) => {
                              if (event === 'error') {
                                  result.catch(handler);
                              }
                              else if (event === 'abort') {
                                  // The empty catch is needed here in case when
                                  // it rejects before it's `await`ed in `_makeRequest`.
                                  (async () => {
                                      try {
                                          const request = (await result);
                                          request.once('abort', handler);
                                      }
                                      catch (_a) { }
                                  })();
                              }
                              else {
                                  /* istanbul ignore next: safety check */
                                  throw new Error(`Unknown HTTP2 promise event: ${event}`);
                              }
                              return result;
                          };
                      }
                      return result;
                  }), cache));
              }
          }
          // `options.cacheOptions`
          options.cacheOptions = { ...options.cacheOptions };
          // `options.dnsCache`
          if (options.dnsCache === true) {
              options.dnsCache = globalDnsCache;
          }
          else if (!is_1.default.undefined(options.dnsCache) && !options.dnsCache.lookup) {
              throw new TypeError(`Parameter \`dnsCache\` must be a CacheableLookup instance or a boolean, got ${is_1.default(options.dnsCache)}`);
          }
          // `options.timeout`
          if (is_1.default.number(options.timeout)) {
              options.timeout = { request: options.timeout };
          }
          else if (defaults && options.timeout !== defaults.timeout) {
              options.timeout = {
                  ...defaults.timeout,
                  ...options.timeout
              };
          }
          else {
              options.timeout = { ...options.timeout };
          }
          // `options.context`
          if (!options.context) {
              options.context = {};
          }
          // `options.hooks`
          const areHooksDefault = options.hooks === (defaults === null || defaults === void 0 ? void 0 : defaults.hooks);
          options.hooks = { ...options.hooks };
          for (const event of exports.knownHookEvents) {
              if (event in options.hooks) {
                  if (is_1.default.array(options.hooks[event])) {
                      // See https://github.com/microsoft/TypeScript/issues/31445#issuecomment-576929044
                      options.hooks[event] = [...options.hooks[event]];
                  }
                  else {
                      throw new TypeError(`Parameter \`${event}\` must be an Array, got ${is_1.default(options.hooks[event])}`);
                  }
              }
              else {
                  options.hooks[event] = [];
              }
          }
          if (defaults && !areHooksDefault) {
              for (const event of exports.knownHookEvents) {
                  const defaultHooks = defaults.hooks[event];
                  if (defaultHooks.length !== 0) {
                      // See https://github.com/microsoft/TypeScript/issues/31445#issuecomment-576929044
                      options.hooks[event] = [
                          ...defaults.hooks[event],
                          ...options.hooks[event]
                      ];
                  }
              }
          }
          // DNS options
          if ('family' in options) {
              deprecation_warning_1.default('"options.family" was never documented, please use "options.dnsLookupIpVersion"');
          }
          // HTTPS options
          if (defaults === null || defaults === void 0 ? void 0 : defaults.https) {
              options.https = { ...defaults.https, ...options.https };
          }
          if ('rejectUnauthorized' in options) {
              deprecation_warning_1.default('"options.rejectUnauthorized" is now deprecated, please use "options.https.rejectUnauthorized"');
          }
          if ('checkServerIdentity' in options) {
              deprecation_warning_1.default('"options.checkServerIdentity" was never documented, please use "options.https.checkServerIdentity"');
          }
          if ('ca' in options) {
              deprecation_warning_1.default('"options.ca" was never documented, please use "options.https.certificateAuthority"');
          }
          if ('key' in options) {
              deprecation_warning_1.default('"options.key" was never documented, please use "options.https.key"');
          }
          if ('cert' in options) {
              deprecation_warning_1.default('"options.cert" was never documented, please use "options.https.certificate"');
          }
          if ('passphrase' in options) {
              deprecation_warning_1.default('"options.passphrase" was never documented, please use "options.https.passphrase"');
          }
          // Other options
          if ('followRedirects' in options) {
              throw new TypeError('The `followRedirects` option does not exist. Use `followRedirect` instead.');
          }
          if (options.agent) {
              for (const key in options.agent) {
                  if (key !== 'http' && key !== 'https' && key !== 'http2') {
                      throw new TypeError(`Expected the \`options.agent\` properties to be \`http\`, \`https\` or \`http2\`, got \`${key}\``);
                  }
              }
          }
          options.maxRedirects = (_d = options.maxRedirects) !== null && _d !== void 0 ? _d : 0;
          // Set non-enumerable properties
          exports.setNonEnumerableProperties([defaults, rawOptions], options);
          return normalize_arguments_1.default(options, defaults);
      }
      _lockWrite() {
          const onLockedWrite = () => {
              throw new TypeError('The payload has been already provided');
          };
          this.write = onLockedWrite;
          this.end = onLockedWrite;
      }
      _unlockWrite() {
          this.write = super.write;
          this.end = super.end;
      }
      async _finalizeBody() {
          const { options } = this;
          const { headers } = options;
          const isForm = !is_1.default.undefined(options.form);
          const isJSON = !is_1.default.undefined(options.json);
          const isBody = !is_1.default.undefined(options.body);
          const hasPayload = isForm || isJSON || isBody;
          const cannotHaveBody = exports.withoutBody.has(options.method) && !(options.method === 'GET' && options.allowGetBody);
          this._cannotHaveBody = cannotHaveBody;
          if (hasPayload) {
              if (cannotHaveBody) {
                  throw new TypeError(`The \`${options.method}\` method cannot be used with a body`);
              }
              if ([isBody, isForm, isJSON].filter(isTrue => isTrue).length > 1) {
                  throw new TypeError('The `body`, `json` and `form` options are mutually exclusive');
              }
              if (isBody &&
                  !(options.body instanceof stream_1.Readable) &&
                  !is_1.default.string(options.body) &&
                  !is_1.default.buffer(options.body) &&
                  !is_form_data_1.default(options.body)) {
                  throw new TypeError('The `body` option must be a stream.Readable, string or Buffer');
              }
              if (isForm && !is_1.default.object(options.form)) {
                  throw new TypeError('The `form` option must be an Object');
              }
              {
                  // Serialize body
                  const noContentType = !is_1.default.string(headers['content-type']);
                  if (isBody) {
                      // Special case for https://github.com/form-data/form-data
                      if (is_form_data_1.default(options.body) && noContentType) {
                          headers['content-type'] = `multipart/form-data; boundary=${options.body.getBoundary()}`;
                      }
                      this[kBody] = options.body;
                  }
                  else if (isForm) {
                      if (noContentType) {
                          headers['content-type'] = 'application/x-www-form-urlencoded';
                      }
                      this[kBody] = (new url_1.URLSearchParams(options.form)).toString();
                  }
                  else {
                      if (noContentType) {
                          headers['content-type'] = 'application/json';
                      }
                      this[kBody] = options.stringifyJson(options.json);
                  }
                  const uploadBodySize = await get_body_size_1.default(this[kBody], options.headers);
                  // See https://tools.ietf.org/html/rfc7230#section-3.3.2
                  // A user agent SHOULD send a Content-Length in a request message when
                  // no Transfer-Encoding is sent and the request method defines a meaning
                  // for an enclosed payload body.  For example, a Content-Length header
                  // field is normally sent in a POST request even when the value is 0
                  // (indicating an empty payload body).  A user agent SHOULD NOT send a
                  // Content-Length header field when the request message does not contain
                  // a payload body and the method semantics do not anticipate such a
                  // body.
                  if (is_1.default.undefined(headers['content-length']) && is_1.default.undefined(headers['transfer-encoding'])) {
                      if (!cannotHaveBody && !is_1.default.undefined(uploadBodySize)) {
                          headers['content-length'] = String(uploadBodySize);
                      }
                  }
              }
          }
          else if (cannotHaveBody) {
              this._lockWrite();
          }
          else {
              this._unlockWrite();
          }
          this[kBodySize] = Number(headers['content-length']) || undefined;
      }
      async _onResponseBase(response) {
          const { options } = this;
          const { url } = options;
          this[kOriginalResponse] = response;
          if (options.decompress) {
              response = decompressResponse(response);
          }
          const statusCode = response.statusCode;
          const typedResponse = response;
          typedResponse.statusMessage = typedResponse.statusMessage ? typedResponse.statusMessage : http.STATUS_CODES[statusCode];
          typedResponse.url = options.url.toString();
          typedResponse.requestUrl = this.requestUrl;
          typedResponse.redirectUrls = this.redirects;
          typedResponse.request = this;
          typedResponse.isFromCache = response.fromCache || false;
          typedResponse.ip = this.ip;
          typedResponse.retryCount = this.retryCount;
          this[kIsFromCache] = typedResponse.isFromCache;
          this[kResponseSize] = Number(response.headers['content-length']) || undefined;
          this[kResponse] = response;
          response.once('end', () => {
              this[kResponseSize] = this[kDownloadedSize];
              this.emit('downloadProgress', this.downloadProgress);
          });
          response.once('error', (error) => {
              // Force clean-up, because some packages don't do this.
              // TODO: Fix decompress-response
              response.destroy();
              this._beforeError(new ReadError(error, this));
          });
          response.once('aborted', () => {
              this._beforeError(new ReadError({
                  name: 'Error',
                  message: 'The server aborted pending request',
                  code: 'ECONNRESET'
              }, this));
          });
          this.emit('downloadProgress', this.downloadProgress);
          const rawCookies = response.headers['set-cookie'];
          if (is_1.default.object(options.cookieJar) && rawCookies) {
              let promises = rawCookies.map(async (rawCookie) => options.cookieJar.setCookie(rawCookie, url.toString()));
              if (options.ignoreInvalidCookies) {
                  promises = promises.map(async (p) => p.catch(() => { }));
              }
              try {
                  await Promise.all(promises);
              }
              catch (error) {
                  this._beforeError(error);
                  return;
              }
          }
          if (options.followRedirect && response.headers.location && redirectCodes.has(statusCode)) {
              // We're being redirected, we don't care about the response.
              // It'd be best to abort the request, but we can't because
              // we would have to sacrifice the TCP connection. We don't want that.
              response.resume();
              if (this[kRequest]) {
                  this[kCancelTimeouts]();
                  // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
                  delete this[kRequest];
                  this[kUnproxyEvents]();
              }
              const shouldBeGet = statusCode === 303 && options.method !== 'GET' && options.method !== 'HEAD';
              if (shouldBeGet || !options.methodRewriting) {
                  // Server responded with "see other", indicating that the resource exists at another location,
                  // and the client should request it from that location via GET or HEAD.
                  options.method = 'GET';
                  if ('body' in options) {
                      delete options.body;
                  }
                  if ('json' in options) {
                      delete options.json;
                  }
                  if ('form' in options) {
                      delete options.form;
                  }
              }
              if (this.redirects.length >= options.maxRedirects) {
                  this._beforeError(new MaxRedirectsError(this));
                  return;
              }
              try {
                  // Do not remove. See https://github.com/sindresorhus/got/pull/214
                  const redirectBuffer = Buffer.from(response.headers.location, 'binary').toString();
                  // Handles invalid URLs. See https://github.com/sindresorhus/got/issues/604
                  const redirectUrl = new url_1.URL(redirectBuffer, url);
                  const redirectString = redirectUrl.toString();
                  decodeURI(redirectString);
                  // Redirecting to a different site, clear sensitive data.
                  if (redirectUrl.hostname !== url.hostname) {
                      if ('host' in options.headers) {
                          delete options.headers.host;
                      }
                      if ('cookie' in options.headers) {
                          delete options.headers.cookie;
                      }
                      if ('authorization' in options.headers) {
                          delete options.headers.authorization;
                      }
                      if (options.username || options.password) {
                          // TODO: Fix this ignore.
                          // @ts-expect-error
                          delete options.username;
                          // @ts-expect-error
                          delete options.password;
                      }
                  }
                  this.redirects.push(redirectString);
                  options.url = redirectUrl;
                  for (const hook of options.hooks.beforeRedirect) {
                      // eslint-disable-next-line no-await-in-loop
                      await hook(options, typedResponse);
                  }
                  this.emit('redirect', typedResponse, options);
                  await this._makeRequest();
              }
              catch (error) {
                  this._beforeError(error);
                  return;
              }
              return;
          }
          if (options.isStream && options.throwHttpErrors && !is_response_ok_1.isResponseOk(typedResponse)) {
              this._beforeError(new HTTPError(typedResponse));
              return;
          }
          response.on('readable', () => {
              if (this[kTriggerRead]) {
                  this._read();
              }
          });
          this.on('resume', () => {
              response.resume();
          });
          this.on('pause', () => {
              response.pause();
          });
          response.once('end', () => {
              this.push(null);
          });
          this.emit('response', response);
          for (const destination of this[kServerResponsesPiped]) {
              if (destination.headersSent) {
                  continue;
              }
              // eslint-disable-next-line guard-for-in
              for (const key in response.headers) {
                  const isAllowed = options.decompress ? key !== 'content-encoding' : true;
                  const value = response.headers[key];
                  if (isAllowed) {
                      destination.setHeader(key, value);
                  }
              }
              destination.statusCode = statusCode;
          }
      }
      async _onResponse(response) {
          try {
              await this._onResponseBase(response);
          }
          catch (error) {
              /* istanbul ignore next: better safe than sorry */
              this._beforeError(error);
          }
      }
      _onRequest(request) {
          const { options } = this;
          const { timeout, url } = options;
          http_timer_1.default(request);
          this[kCancelTimeouts] = timed_out_1.default(request, timeout, url);
          const responseEventName = options.cache ? 'cacheableResponse' : 'response';
          request.once(responseEventName, (response) => {
              void this._onResponse(response);
          });
          request.once('error', (error) => {
              var _a;
              // Force clean-up, because some packages (e.g. nock) don't do this.
              request.destroy();
              // Node.js <= 12.18.2 mistakenly emits the response `end` first.
              (_a = request.res) === null || _a === void 0 ? void 0 : _a.removeAllListeners('end');
              if (error instanceof timed_out_1.TimeoutError) {
                  error = new TimeoutError(error, this.timings, this);
              }
              else {
                  error = new RequestError(error.message, error, this);
              }
              this._beforeError(error);
          });
          this[kUnproxyEvents] = proxy_events_1.default(request, this, proxiedRequestEvents);
          this[kRequest] = request;
          this.emit('uploadProgress', this.uploadProgress);
          // Send body
          const body = this[kBody];
          const currentRequest = this.redirects.length === 0 ? this : request;
          if (is_1.default.nodeStream(body)) {
              body.pipe(currentRequest);
              body.once('error', (error) => {
                  this._beforeError(new UploadError(error, this));
              });
          }
          else {
              this._unlockWrite();
              if (!is_1.default.undefined(body)) {
                  this._writeRequest(body, undefined, () => { });
                  currentRequest.end();
                  this._lockWrite();
              }
              else if (this._cannotHaveBody || this._noPipe) {
                  currentRequest.end();
                  this._lockWrite();
              }
          }
          this.emit('request', request);
      }
      async _createCacheableRequest(url, options) {
          return new Promise((resolve, reject) => {
              // TODO: Remove `utils/url-to-options.ts` when `cacheable-request` is fixed
              Object.assign(options, url_to_options_1.default(url));
              // `http-cache-semantics` checks this
              // TODO: Fix this ignore.
              // @ts-expect-error
              delete options.url;
              let request;
              // This is ugly
              const cacheRequest = cacheableStore.get(options.cache)(options, async (response) => {
                  // TODO: Fix `cacheable-response`
                  response._readableState.autoDestroy = false;
                  if (request) {
                      (await request).emit('cacheableResponse', response);
                  }
                  resolve(response);
              });
              // Restore options
              options.url = url;
              cacheRequest.once('error', reject);
              cacheRequest.once('request', async (requestOrPromise) => {
                  request = requestOrPromise;
                  resolve(request);
              });
          });
      }
      async _makeRequest() {
          var _a, _b, _c, _d, _e;
          const { options } = this;
          const { headers } = options;
          for (const key in headers) {
              if (is_1.default.undefined(headers[key])) {
                  // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
                  delete headers[key];
              }
              else if (is_1.default.null_(headers[key])) {
                  throw new TypeError(`Use \`undefined\` instead of \`null\` to delete the \`${key}\` header`);
              }
          }
          if (options.decompress && is_1.default.undefined(headers['accept-encoding'])) {
              headers['accept-encoding'] = supportsBrotli ? 'gzip, deflate, br' : 'gzip, deflate';
          }
          // Set cookies
          if (options.cookieJar) {
              const cookieString = await options.cookieJar.getCookieString(options.url.toString());
              if (is_1.default.nonEmptyString(cookieString)) {
                  options.headers.cookie = cookieString;
              }
          }
          for (const hook of options.hooks.beforeRequest) {
              // eslint-disable-next-line no-await-in-loop
              const result = await hook(options);
              if (!is_1.default.undefined(result)) {
                  // @ts-expect-error Skip the type mismatch to support abstract responses
                  options.request = () => result;
                  break;
              }
          }
          const { agent, request, timeout, url } = options;
          if (options.dnsCache && !('lookup' in options)) {
              options.lookup = options.dnsCache.lookup;
          }
          // UNIX sockets
          if (url.hostname === 'unix') {
              const matches = /(?<socketPath>.+?):(?<path>.+)/.exec(`${url.pathname}${url.search}`);
              if (matches === null || matches === void 0 ? void 0 : matches.groups) {
                  const { socketPath, path } = matches.groups;
                  Object.assign(options, {
                      socketPath,
                      path,
                      host: ''
                  });
              }
          }
          const isHttps = url.protocol === 'https:';
          // Fallback function
          let fallbackFn;
          if (options.http2) {
              fallbackFn = http2wrapper.auto;
          }
          else {
              fallbackFn = isHttps ? https.request : http.request;
          }
          const realFn = (_a = options.request) !== null && _a !== void 0 ? _a : fallbackFn;
          // Cache support
          const fn = options.cache ? this._createCacheableRequest : realFn;
          // Pass an agent directly when HTTP2 is disabled
          if (agent && !options.http2) {
              options.agent = agent[isHttps ? 'https' : 'http'];
          }
          // Prepare plain HTTP request options
          options[kRequest] = realFn;
          delete options.request;
          // TODO: Fix this ignore.
          // @ts-expect-error
          delete options.timeout;
          const requestOptions = options;
          requestOptions.shared = (_b = options.cacheOptions) === null || _b === void 0 ? void 0 : _b.shared;
          requestOptions.cacheHeuristic = (_c = options.cacheOptions) === null || _c === void 0 ? void 0 : _c.cacheHeuristic;
          requestOptions.immutableMinTimeToLive = (_d = options.cacheOptions) === null || _d === void 0 ? void 0 : _d.immutableMinTimeToLive;
          requestOptions.ignoreCargoCult = (_e = options.cacheOptions) === null || _e === void 0 ? void 0 : _e.ignoreCargoCult;
          // If `dnsLookupIpVersion` is not present do not override `family`
          if (options.dnsLookupIpVersion !== undefined) {
              try {
                  requestOptions.family = dns_ip_version_1.dnsLookupIpVersionToFamily(options.dnsLookupIpVersion);
              }
              catch (_f) {
                  throw new Error('Invalid `dnsLookupIpVersion` option value');
              }
          }
          // HTTPS options remapping
          if (options.https) {
              if ('rejectUnauthorized' in options.https) {
                  requestOptions.rejectUnauthorized = options.https.rejectUnauthorized;
              }
              if (options.https.checkServerIdentity) {
                  requestOptions.checkServerIdentity = options.https.checkServerIdentity;
              }
              if (options.https.certificateAuthority) {
                  requestOptions.ca = options.https.certificateAuthority;
              }
              if (options.https.certificate) {
                  requestOptions.cert = options.https.certificate;
              }
              if (options.https.key) {
                  requestOptions.key = options.https.key;
              }
              if (options.https.passphrase) {
                  requestOptions.passphrase = options.https.passphrase;
              }
          }
          try {
              let requestOrResponse = await fn(url, requestOptions);
              if (is_1.default.undefined(requestOrResponse)) {
                  requestOrResponse = fallbackFn(url, requestOptions);
              }
              // Restore options
              options.request = request;
              options.timeout = timeout;
              options.agent = agent;
              // HTTPS options restore
              if (options.https) {
                  if ('rejectUnauthorized' in options.https) {
                      delete requestOptions.rejectUnauthorized;
                  }
                  if (options.https.checkServerIdentity) {
                      // @ts-expect-error - This one will be removed when we remove the alias.
                      delete requestOptions.checkServerIdentity;
                  }
                  if (options.https.certificateAuthority) {
                      delete requestOptions.ca;
                  }
                  if (options.https.certificate) {
                      delete requestOptions.cert;
                  }
                  if (options.https.key) {
                      delete requestOptions.key;
                  }
                  if (options.https.passphrase) {
                      delete requestOptions.passphrase;
                  }
              }
              if (isClientRequest(requestOrResponse)) {
                  this._onRequest(requestOrResponse);
                  // Emit the response after the stream has been ended
              }
              else if (this.writable) {
                  this.once('finish', () => {
                      void this._onResponse(requestOrResponse);
                  });
                  this._unlockWrite();
                  this.end();
                  this._lockWrite();
              }
              else {
                  void this._onResponse(requestOrResponse);
              }
          }
          catch (error) {
              if (error instanceof CacheableRequest.CacheError) {
                  throw new CacheError(error, this);
              }
              throw new RequestError(error.message, error, this);
          }
      }
      async _error(error) {
          try {
              for (const hook of this.options.hooks.beforeError) {
                  // eslint-disable-next-line no-await-in-loop
                  error = await hook(error);
              }
          }
          catch (error_) {
              error = new RequestError(error_.message, error_, this);
          }
          this.destroy(error);
      }
      _beforeError(error) {
          if (this[kStopReading]) {
              return;
          }
          const { options } = this;
          const retryCount = this.retryCount + 1;
          this[kStopReading] = true;
          if (!(error instanceof RequestError)) {
              error = new RequestError(error.message, error, this);
          }
          const typedError = error;
          const { response } = typedError;
          void (async () => {
              if (response && !response.body) {
                  response.setEncoding(this._readableState.encoding);
                  try {
                      response.rawBody = await get_buffer_1.default(response);
                  }
                  catch (_a) { }
                  response.body = response.rawBody.toString();
              }
              if (this.listenerCount('retry') !== 0) {
                  let backoff;
                  try {
                      let retryAfter;
                      if (response && 'retry-after' in response.headers) {
                          retryAfter = Number(response.headers['retry-after']);
                          if (Number.isNaN(retryAfter)) {
                              retryAfter = Date.parse(response.headers['retry-after']) - Date.now();
                              if (retryAfter <= 0) {
                                  retryAfter = 1;
                              }
                          }
                          else {
                              retryAfter *= 1000;
                          }
                      }
                      backoff = await options.retry.calculateDelay({
                          attemptCount: retryCount,
                          retryOptions: options.retry,
                          error: typedError,
                          retryAfter,
                          computedValue: calculate_retry_delay_1.default({
                              attemptCount: retryCount,
                              retryOptions: options.retry,
                              error: typedError,
                              retryAfter,
                              computedValue: 0
                          })
                      });
                  }
                  catch (error_) {
                      void this._error(new RequestError(error_.message, error_, this));
                      return;
                  }
                  if (backoff) {
                      const retry = async () => {
                          try {
                              for (const hook of this.options.hooks.beforeRetry) {
                                  // eslint-disable-next-line no-await-in-loop
                                  await hook(this.options, typedError, retryCount);
                              }
                          }
                          catch (error_) {
                              void this._error(new RequestError(error_.message, error, this));
                              return;
                          }
                          // Something forced us to abort the retry
                          if (this.destroyed) {
                              return;
                          }
                          this.destroy();
                          this.emit('retry', retryCount, error);
                      };
                      this[kRetryTimeout] = setTimeout(retry, backoff);
                      return;
                  }
              }
              void this._error(typedError);
          })();
      }
      _read() {
          this[kTriggerRead] = true;
          const response = this[kResponse];
          if (response && !this[kStopReading]) {
              // We cannot put this in the `if` above
              // because `.read()` also triggers the `end` event
              if (response.readableLength) {
                  this[kTriggerRead] = false;
              }
              let data;
              while ((data = response.read()) !== null) {
                  this[kDownloadedSize] += data.length;
                  this[kStartedReading] = true;
                  const progress = this.downloadProgress;
                  if (progress.percent < 1) {
                      this.emit('downloadProgress', progress);
                  }
                  this.push(data);
              }
          }
      }
      // Node.js 12 has incorrect types, so the encoding must be a string
      _write(chunk, encoding, callback) {
          const write = () => {
              this._writeRequest(chunk, encoding, callback);
          };
          if (this.requestInitialized) {
              write();
          }
          else {
              this[kJobs].push(write);
          }
      }
      _writeRequest(chunk, encoding, callback) {
          this._progressCallbacks.push(() => {
              this[kUploadedSize] += Buffer.byteLength(chunk, encoding);
              const progress = this.uploadProgress;
              if (progress.percent < 1) {
                  this.emit('uploadProgress', progress);
              }
          });
          // TODO: What happens if it's from cache? Then this[kRequest] won't be defined.
          this[kRequest].write(chunk, encoding, (error) => {
              if (!error && this._progressCallbacks.length !== 0) {
                  this._progressCallbacks.shift()();
              }
              callback(error);
          });
      }
      _final(callback) {
          const endRequest = () => {
              // FIX: Node.js 10 calls the write callback AFTER the end callback!
              while (this._progressCallbacks.length !== 0) {
                  this._progressCallbacks.shift()();
              }
              // We need to check if `this[kRequest]` is present,
              // because it isn't when we use cache.
              if (!(kRequest in this)) {
                  callback();
                  return;
              }
              if (this[kRequest].destroyed) {
                  callback();
                  return;
              }
              this[kRequest].end((error) => {
                  if (!error) {
                      this[kBodySize] = this[kUploadedSize];
                      this.emit('uploadProgress', this.uploadProgress);
                      this[kRequest].emit('upload-complete');
                  }
                  callback(error);
              });
          };
          if (this.requestInitialized) {
              endRequest();
          }
          else {
              this[kJobs].push(endRequest);
          }
      }
      _destroy(error, callback) {
          var _a;
          this[kStopReading] = true;
          // Prevent further retries
          clearTimeout(this[kRetryTimeout]);
          if (kRequest in this) {
              this[kCancelTimeouts]();
              // TODO: Remove the next `if` when these get fixed:
              // - https://github.com/nodejs/node/issues/32851
              if (!((_a = this[kResponse]) === null || _a === void 0 ? void 0 : _a.complete)) {
                  this[kRequest].destroy();
              }
          }
          if (error !== null && !is_1.default.undefined(error) && !(error instanceof RequestError)) {
              error = new RequestError(error.message, error, this);
          }
          callback(error);
      }
      get _isAboutToError() {
          return this[kStopReading];
      }
      /**
      The remote IP address.
      */
      get ip() {
          var _a;
          return (_a = this[kRequest]) === null || _a === void 0 ? void 0 : _a.socket.remoteAddress;
      }
      /**
      Indicates whether the request has been aborted or not.
      */
      get aborted() {
          var _a, _b, _c;
          return ((_b = (_a = this[kRequest]) === null || _a === void 0 ? void 0 : _a.destroyed) !== null && _b !== void 0 ? _b : this.destroyed) && !((_c = this[kOriginalResponse]) === null || _c === void 0 ? void 0 : _c.complete);
      }
      get socket() {
          var _a;
          return (_a = this[kRequest]) === null || _a === void 0 ? void 0 : _a.socket;
      }
      /**
      Progress event for downloading (receiving a response).
      */
      get downloadProgress() {
          let percent;
          if (this[kResponseSize]) {
              percent = this[kDownloadedSize] / this[kResponseSize];
          }
          else if (this[kResponseSize] === this[kDownloadedSize]) {
              percent = 1;
          }
          else {
              percent = 0;
          }
          return {
              percent,
              transferred: this[kDownloadedSize],
              total: this[kResponseSize]
          };
      }
      /**
      Progress event for uploading (sending a request).
      */
      get uploadProgress() {
          let percent;
          if (this[kBodySize]) {
              percent = this[kUploadedSize] / this[kBodySize];
          }
          else if (this[kBodySize] === this[kUploadedSize]) {
              percent = 1;
          }
          else {
              percent = 0;
          }
          return {
              percent,
              transferred: this[kUploadedSize],
              total: this[kBodySize]
          };
      }
      /**
      The object contains the following properties:
  
      - `start` - Time when the request started.
      - `socket` - Time when a socket was assigned to the request.
      - `lookup` - Time when the DNS lookup finished.
      - `connect` - Time when the socket successfully connected.
      - `secureConnect` - Time when the socket securely connected.
      - `upload` - Time when the request finished uploading.
      - `response` - Time when the request fired `response` event.
      - `end` - Time when the response fired `end` event.
      - `error` - Time when the request fired `error` event.
      - `abort` - Time when the request fired `abort` event.
      - `phases`
          - `wait` - `timings.socket - timings.start`
          - `dns` - `timings.lookup - timings.socket`
          - `tcp` - `timings.connect - timings.lookup`
          - `tls` - `timings.secureConnect - timings.connect`
          - `request` - `timings.upload - (timings.secureConnect || timings.connect)`
          - `firstByte` - `timings.response - timings.upload`
          - `download` - `timings.end - timings.response`
          - `total` - `(timings.end || timings.error || timings.abort) - timings.start`
  
      If something has not been measured yet, it will be `undefined`.
  
      __Note__: The time is a `number` representing the milliseconds elapsed since the UNIX epoch.
      */
      get timings() {
          var _a;
          return (_a = this[kRequest]) === null || _a === void 0 ? void 0 : _a.timings;
      }
      /**
      Whether the response was retrieved from the cache.
      */
      get isFromCache() {
          return this[kIsFromCache];
      }
      pipe(destination, options) {
          if (this[kStartedReading]) {
              throw new Error('Failed to pipe. The response has been emitted already.');
          }
          if (destination instanceof http_1.ServerResponse) {
              this[kServerResponsesPiped].add(destination);
          }
          return super.pipe(destination, options);
      }
      unpipe(destination) {
          if (destination instanceof http_1.ServerResponse) {
              this[kServerResponsesPiped].delete(destination);
          }
          super.unpipe(destination);
          return this;
      }
  }
  exports.default = Request;
  