153 lines
6.1 KiB
JavaScript
153 lines
6.1 KiB
JavaScript
"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;
|