index.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. "use strict";
  2. var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
  3. if (k2 === undefined) k2 = k;
  4. Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
  5. }) : (function(o, m, k, k2) {
  6. if (k2 === undefined) k2 = k;
  7. o[k2] = m[k];
  8. }));
  9. var __exportStar = (this && this.__exportStar) || function(m, exports) {
  10. for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
  11. };
  12. Object.defineProperty(exports, "__esModule", { value: true });
  13. const events_1 = require("events");
  14. const is_1 = require("@sindresorhus/is");
  15. const PCancelable = require("p-cancelable");
  16. const types_1 = require("./types");
  17. const parse_body_1 = require("./parse-body");
  18. const core_1 = require("../core");
  19. const proxy_events_1 = require("../core/utils/proxy-events");
  20. const get_buffer_1 = require("../core/utils/get-buffer");
  21. const is_response_ok_1 = require("../core/utils/is-response-ok");
  22. const proxiedRequestEvents = [
  23. 'request',
  24. 'response',
  25. 'redirect',
  26. 'uploadProgress',
  27. 'downloadProgress'
  28. ];
  29. function asPromise(normalizedOptions) {
  30. let globalRequest;
  31. let globalResponse;
  32. const emitter = new events_1.EventEmitter();
  33. const promise = new PCancelable((resolve, reject, onCancel) => {
  34. const makeRequest = (retryCount) => {
  35. const request = new core_1.default(undefined, normalizedOptions);
  36. request.retryCount = retryCount;
  37. request._noPipe = true;
  38. onCancel(() => request.destroy());
  39. onCancel.shouldReject = false;
  40. onCancel(() => reject(new types_1.CancelError(request)));
  41. globalRequest = request;
  42. request.once('response', async (response) => {
  43. var _a;
  44. response.retryCount = retryCount;
  45. if (response.request.aborted) {
  46. // Canceled while downloading - will throw a `CancelError` or `TimeoutError` error
  47. return;
  48. }
  49. // Download body
  50. let rawBody;
  51. try {
  52. rawBody = await get_buffer_1.default(request);
  53. response.rawBody = rawBody;
  54. }
  55. catch (_b) {
  56. // The same error is caught below.
  57. // See request.once('error')
  58. return;
  59. }
  60. if (request._isAboutToError) {
  61. return;
  62. }
  63. // Parse body
  64. const contentEncoding = ((_a = response.headers['content-encoding']) !== null && _a !== void 0 ? _a : '').toLowerCase();
  65. const isCompressed = ['gzip', 'deflate', 'br'].includes(contentEncoding);
  66. const { options } = request;
  67. if (isCompressed && !options.decompress) {
  68. response.body = rawBody;
  69. }
  70. else {
  71. try {
  72. response.body = parse_body_1.default(response, options.responseType, options.parseJson, options.encoding);
  73. }
  74. catch (error) {
  75. // Fallback to `utf8`
  76. response.body = rawBody.toString();
  77. if (is_response_ok_1.isResponseOk(response)) {
  78. request._beforeError(error);
  79. return;
  80. }
  81. }
  82. }
  83. try {
  84. for (const [index, hook] of options.hooks.afterResponse.entries()) {
  85. // @ts-expect-error TS doesn't notice that CancelableRequest is a Promise
  86. // eslint-disable-next-line no-await-in-loop
  87. response = await hook(response, async (updatedOptions) => {
  88. const typedOptions = core_1.default.normalizeArguments(undefined, {
  89. ...updatedOptions,
  90. retry: {
  91. calculateDelay: () => 0
  92. },
  93. throwHttpErrors: false,
  94. resolveBodyOnly: false
  95. }, options);
  96. // Remove any further hooks for that request, because we'll call them anyway.
  97. // The loop continues. We don't want duplicates (asPromise recursion).
  98. typedOptions.hooks.afterResponse = typedOptions.hooks.afterResponse.slice(0, index);
  99. for (const hook of typedOptions.hooks.beforeRetry) {
  100. // eslint-disable-next-line no-await-in-loop
  101. await hook(typedOptions);
  102. }
  103. const promise = asPromise(typedOptions);
  104. onCancel(() => {
  105. promise.catch(() => { });
  106. promise.cancel();
  107. });
  108. return promise;
  109. });
  110. }
  111. }
  112. catch (error) {
  113. request._beforeError(new types_1.RequestError(error.message, error, request));
  114. return;
  115. }
  116. globalResponse = response;
  117. if (!is_response_ok_1.isResponseOk(response)) {
  118. request._beforeError(new types_1.HTTPError(response));
  119. return;
  120. }
  121. request.destroy();
  122. resolve(request.options.resolveBodyOnly ? response.body : response);
  123. });
  124. const onError = (error) => {
  125. if (promise.isCanceled) {
  126. return;
  127. }
  128. const { options } = request;
  129. if (error instanceof types_1.HTTPError && !options.throwHttpErrors) {
  130. const { response } = error;
  131. resolve(request.options.resolveBodyOnly ? response.body : response);
  132. return;
  133. }
  134. reject(error);
  135. };
  136. request.once('error', onError);
  137. const previousBody = request.options.body;
  138. request.once('retry', (newRetryCount, error) => {
  139. var _a, _b;
  140. if (previousBody === ((_a = error.request) === null || _a === void 0 ? void 0 : _a.options.body) && is_1.default.nodeStream((_b = error.request) === null || _b === void 0 ? void 0 : _b.options.body)) {
  141. onError(error);
  142. return;
  143. }
  144. makeRequest(newRetryCount);
  145. });
  146. proxy_events_1.default(request, emitter, proxiedRequestEvents);
  147. };
  148. makeRequest(0);
  149. });
  150. promise.on = (event, fn) => {
  151. emitter.on(event, fn);
  152. return promise;
  153. };
  154. const shortcut = (responseType) => {
  155. const newPromise = (async () => {
  156. // Wait until downloading has ended
  157. await promise;
  158. const { options } = globalResponse.request;
  159. return parse_body_1.default(globalResponse, responseType, options.parseJson, options.encoding);
  160. })();
  161. Object.defineProperties(newPromise, Object.getOwnPropertyDescriptors(promise));
  162. return newPromise;
  163. };
  164. promise.json = () => {
  165. const { headers } = globalRequest.options;
  166. if (!globalRequest.writableFinished && headers.accept === undefined) {
  167. headers.accept = 'application/json';
  168. }
  169. return shortcut('json');
  170. };
  171. promise.buffer = () => shortcut('buffer');
  172. promise.text = () => shortcut('text');
  173. return promise;
  174. }
  175. exports.default = asPromise;
  176. __exportStar(require("./types"), exports);