bugsnag-source-maps-fork
Version:
CLI and JS library for uploading source maps to Bugsnag
199 lines • 8.93 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = request;
exports.send = send;
exports.isRetryable = isRetryable;
exports.fetch = fetch;
const https_1 = __importDefault(require("https"));
const http_1 = __importDefault(require("http"));
const concat_stream_1 = __importDefault(require("concat-stream"));
const url_1 = __importDefault(require("url"));
const form_data_1 = __importDefault(require("form-data"));
const NetworkError_1 = require("./NetworkError");
const MAX_ATTEMPTS = 5;
const RETRY_INTERVAL_MS = parseInt(process.env.BUGSNAG_RETRY_INTERVAL_MS) || 1000;
const DEFAULT_TIMEOUT_MS = parseInt(process.env.BUGSNAG_TIMEOUT_MS) || 60000;
function request(endpoint_1, payload_1, requestOpts_1) {
return __awaiter(this, arguments, void 0, function* (endpoint, payload, requestOpts, options = {}) {
let attempts = 0;
const go = () => __awaiter(this, void 0, void 0, function* () {
try {
attempts++;
yield send(endpoint, payload, requestOpts, options);
}
catch (err) {
if (err && err.isRetryable !== false && attempts < MAX_ATTEMPTS) {
yield new Promise((resolve) => setTimeout(resolve, RETRY_INTERVAL_MS));
return yield go();
}
throw err;
}
});
yield go();
});
}
function createFormData(payload) {
const formData = new form_data_1.default();
formData.append('apiKey', payload.apiKey);
switch (payload.type) {
case 0 /* PayloadType.Browser */:
case 2 /* PayloadType.Node */:
return appendJsFormData(formData, payload);
case 1 /* PayloadType.ReactNative */:
return appendReactNativeFormData(formData, payload);
}
}
function appendJsFormData(formData, payload) {
if (payload.appVersion)
formData.append('appVersion', payload.appVersion);
if (payload.codeBundleId)
formData.append('codeBundleId', payload.codeBundleId);
formData.append('minifiedUrl', payload.minifiedUrl);
formData.append('sourceMap', payload.sourceMap.data, { filepath: payload.sourceMap.filepath });
if (payload.minifiedFile)
formData.append('minifiedFile', payload.minifiedFile.data, { filepath: payload.minifiedFile.filepath });
if (payload.overwrite)
formData.append('overwrite', payload.overwrite.toString());
return formData;
}
function appendReactNativeFormData(formData, payload) {
formData.append('platform', payload.platform);
formData.append('overwrite', payload.overwrite.toString());
formData.append('dev', payload.dev.toString());
formData.append('sourceMap', payload.sourceMap.data, { filepath: payload.sourceMap.filepath });
formData.append('bundle', payload.bundle.data, { filepath: payload.bundle.filepath });
if (payload.appVersion) {
formData.append('appVersion', payload.appVersion);
}
if (payload.codeBundleId) {
formData.append('codeBundleId', payload.codeBundleId);
}
if (payload.appBundleVersion) {
formData.append('appBundleVersion', payload.appBundleVersion);
}
if (payload.appVersionCode) {
formData.append('appVersionCode', payload.appVersionCode);
}
return formData;
}
function send(endpoint_1, payload_1, requestOpts_1) {
return __awaiter(this, arguments, void 0, function* (endpoint, payload, requestOpts, options = {}) {
return new Promise((resolve, reject) => {
const formData = createFormData(payload);
const parsedUrl = url_1.default.parse(endpoint);
const req = (parsedUrl.protocol === 'https:' ? https_1.default : http_1.default).request({
method: 'POST',
hostname: parsedUrl.hostname,
path: parsedUrl.path || '/',
headers: formData.getHeaders(),
port: parsedUrl.port || undefined,
agent: requestOpts && requestOpts.agent
}, res => {
res.pipe((0, concat_stream_1.default)((bodyBuffer) => {
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300)
return resolve();
const err = new NetworkError_1.NetworkError(`HTTP status ${res.statusCode} received from upload API`);
err.responseText = bodyBuffer.toString();
if (!isRetryable(res.statusCode)) {
err.isRetryable = false;
}
if (res.statusCode && (res.statusCode >= 400 && res.statusCode < 500)) {
switch (res.statusCode) {
case 401:
err.code = NetworkError_1.NetworkErrorCode.INVALID_API_KEY;
break;
case 409:
err.code = NetworkError_1.NetworkErrorCode.DUPLICATE;
break;
case 422:
err.code = NetworkError_1.NetworkErrorCode.EMPTY_FILE;
break;
default:
err.code = NetworkError_1.NetworkErrorCode.MISC_BAD_REQUEST;
}
}
else {
err.code = NetworkError_1.NetworkErrorCode.SERVER_ERROR;
}
return reject(err);
}));
});
formData.pipe(req);
addErrorHandler(req, reject);
addTimeout(req, reject, options);
});
});
}
function isRetryable(status) {
return (!status || (status < 400 ||
status > 499 ||
[
408, // timeout
429 // too many requests
].indexOf(status) !== -1));
}
function fetch(endpoint, options = {}) {
return new Promise((resolve, reject) => {
const parsedUrl = url_1.default.parse(endpoint);
const req = (parsedUrl.protocol === 'https:' ? https_1.default : http_1.default).get(endpoint, res => {
res.pipe((0, concat_stream_1.default)((bodyBuffer) => {
if (res.statusCode === 200) {
return resolve(bodyBuffer.toString());
}
const err = new NetworkError_1.NetworkError(`HTTP status ${res.statusCode} received from bundle server`);
err.responseText = bodyBuffer.toString();
if (!isRetryable(res.statusCode)) {
err.isRetryable = false;
}
if (res.statusCode && (res.statusCode >= 400 && res.statusCode < 500)) {
err.code = NetworkError_1.NetworkErrorCode.MISC_BAD_REQUEST;
}
else {
err.code = NetworkError_1.NetworkErrorCode.SERVER_ERROR;
}
return reject(err);
}));
});
addErrorHandler(req, reject);
addTimeout(req, reject, options);
});
}
function addErrorHandler(req, reject) {
req.on('error', e => {
const err = new NetworkError_1.NetworkError('Unknown connection error');
err.cause = e;
const failureReason = e.code;
if (failureReason === 'ECONNREFUSED') {
err.code = NetworkError_1.NetworkErrorCode.CONNECTION_REFUSED;
}
else {
err.code = NetworkError_1.NetworkErrorCode.UNKNOWN;
}
reject(err);
});
}
const minutesToMilliseconds = (minutes) => minutes * 60 * 1000;
function addTimeout(req, reject, options) {
const timeout = options.idleTimeout
? minutesToMilliseconds(options.idleTimeout)
: DEFAULT_TIMEOUT_MS;
req.setTimeout(timeout, () => {
const err = new NetworkError_1.NetworkError('Connection timed out');
err.code = NetworkError_1.NetworkErrorCode.TIMEOUT;
reject(err);
req.abort();
});
}
//# sourceMappingURL=Request.js.map