@vepler/http-client
Version:
A flexible and extensible API service library for making HTTP requests with built-in authentication support for bearer tokens and API keys.
318 lines • 14.5 kB
JavaScript
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createErrorFromResponse = exports.ValidationError = exports.RateLimitError = exports.TimeoutError = exports.AuthError = exports.NetworkError = exports.ServerError = exports.ClientError = exports.HttpError = void 0;
/**
* Base HTTP error class for all API errors
*/
var HttpError = /** @class */ (function (_super) {
__extends(HttpError, _super);
function HttpError(message, status, statusText, endpoint, method, url, data) {
if (status === void 0) { status = 500; }
if (statusText === void 0) { statusText = 'Internal Server Error'; }
if (endpoint === void 0) { endpoint = ''; }
if (method === void 0) { method = ''; }
if (url === void 0) { url = ''; }
if (data === void 0) { data = null; }
var _this = _super.call(this, message) || this;
_this.isHttpError = true;
_this.name = 'HttpError';
_this.status = status;
_this.statusText = statusText;
_this.endpoint = endpoint;
_this.method = method;
_this.url = url;
_this.data = data;
// This is needed due to extending a built-in class in TypeScript
Object.setPrototypeOf(_this, HttpError.prototype);
return _this;
}
/**
* Formats the error for developer debugging
*/
HttpError.prototype.toJSON = function () {
return {
name: this.name,
message: this.message,
status: this.status,
statusText: this.statusText,
endpoint: this.endpoint,
method: this.method,
url: this.url,
data: this.data,
};
};
/**
* Creates an HttpError from an AxiosError
*/
HttpError.fromAxiosError = function (error) {
var _a;
if (error.response) {
var _b = error.response, status_1 = _b.status, statusText = _b.statusText, config = _b.config, data = _b.data;
var method = ((_a = config === null || config === void 0 ? void 0 : config.method) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || 'UNKNOWN';
var endpoint = (config === null || config === void 0 ? void 0 : config.url) || '';
var baseUrl = (config === null || config === void 0 ? void 0 : config.baseURL) || '';
var url = baseUrl ? "".concat(baseUrl).concat(endpoint) : endpoint;
return new HttpError("".concat(status_1, " ").concat(statusText, " - ").concat(method, " ").concat(endpoint), status_1, statusText, endpoint, method, url, data);
}
else if (error.request) {
// The request was made but no response was received
return new NetworkError('No response received from server', error.request);
}
else {
// Something happened in setting up the request
return new ClientError(error.message || 'Request setup error');
}
};
return HttpError;
}(Error));
exports.HttpError = HttpError;
/**
* 400-499 level HTTP errors
*/
var ClientError = /** @class */ (function (_super) {
__extends(ClientError, _super);
function ClientError(message, status, statusText, endpoint, method, url, data) {
if (status === void 0) { status = 400; }
if (statusText === void 0) { statusText = 'Bad Request'; }
if (endpoint === void 0) { endpoint = ''; }
if (method === void 0) { method = ''; }
if (url === void 0) { url = ''; }
if (data === void 0) { data = null; }
var _this = _super.call(this, message, status, statusText, endpoint, method, url, data) || this;
_this.name = 'ClientError';
Object.setPrototypeOf(_this, ClientError.prototype);
return _this;
}
return ClientError;
}(HttpError));
exports.ClientError = ClientError;
/**
* 500-599 level HTTP errors
*/
var ServerError = /** @class */ (function (_super) {
__extends(ServerError, _super);
function ServerError(message, status, statusText, endpoint, method, url, data) {
if (status === void 0) { status = 500; }
if (statusText === void 0) { statusText = 'Internal Server Error'; }
if (endpoint === void 0) { endpoint = ''; }
if (method === void 0) { method = ''; }
if (url === void 0) { url = ''; }
if (data === void 0) { data = null; }
var _this = _super.call(this, message, status, statusText, endpoint, method, url, data) || this;
_this.name = 'ServerError';
Object.setPrototypeOf(_this, ServerError.prototype);
return _this;
}
return ServerError;
}(HttpError));
exports.ServerError = ServerError;
/**
* Network or connectivity errors
*/
var NetworkError = /** @class */ (function (_super) {
__extends(NetworkError, _super);
function NetworkError(message, request, endpoint, method, url) {
if (request === void 0) { request = null; }
if (endpoint === void 0) { endpoint = ''; }
if (method === void 0) { method = ''; }
if (url === void 0) { url = ''; }
var _this = _super.call(this, message, 0, 'Network Error', endpoint, method, url) || this;
_this.name = 'NetworkError';
_this.request = request;
Object.setPrototypeOf(_this, NetworkError.prototype);
return _this;
}
NetworkError.prototype.toJSON = function () {
return __assign(__assign({}, _super.prototype.toJSON.call(this)), { request: this.request ? '[Request Object]' : null });
};
return NetworkError;
}(HttpError));
exports.NetworkError = NetworkError;
/**
* Authentication errors (401, 403)
*/
var AuthError = /** @class */ (function (_super) {
__extends(AuthError, _super);
function AuthError(message, status, statusText, endpoint, method, url, data, credentials) {
if (status === void 0) { status = 401; }
if (statusText === void 0) { statusText = 'Unauthorized'; }
if (endpoint === void 0) { endpoint = ''; }
if (method === void 0) { method = ''; }
if (url === void 0) { url = ''; }
if (data === void 0) { data = null; }
if (credentials === void 0) { credentials = {}; }
var _this = _super.call(this, message, status, statusText, endpoint, method, url, data) || this;
_this.name = 'AuthError';
_this.credentials = AuthError.sanitizeCredentials(credentials);
Object.setPrototypeOf(_this, AuthError.prototype);
return _this;
}
AuthError.sanitizeCredentials = function (credentials) {
var sanitized = {};
Object.entries(credentials).forEach(function (_a) {
var key = _a[0], value = _a[1];
if (key.toLowerCase().includes('key') || key.toLowerCase().includes('token')) {
// Mask sensitive credentials but show the first and last 4 chars
if (value && value.length > 8) {
sanitized[key] = "".concat(value.substring(0, 4), "...").concat(value.substring(value.length - 4));
}
else if (value) {
sanitized[key] = '********';
}
}
else {
sanitized[key] = value;
}
});
return sanitized;
};
AuthError.prototype.toJSON = function () {
return __assign(__assign({}, _super.prototype.toJSON.call(this)), { credentials: this.credentials });
};
return AuthError;
}(ClientError));
exports.AuthError = AuthError;
/**
* Request timeout errors
*/
var TimeoutError = /** @class */ (function (_super) {
__extends(TimeoutError, _super);
function TimeoutError(message, request, endpoint, method, url) {
if (message === void 0) { message = 'Request timed out'; }
if (request === void 0) { request = null; }
if (endpoint === void 0) { endpoint = ''; }
if (method === void 0) { method = ''; }
if (url === void 0) { url = ''; }
var _this = _super.call(this, message, request, endpoint, method, url) || this;
_this.name = 'TimeoutError';
Object.setPrototypeOf(_this, TimeoutError.prototype);
return _this;
}
return TimeoutError;
}(NetworkError));
exports.TimeoutError = TimeoutError;
/**
* Rate limiting errors
*/
var RateLimitError = /** @class */ (function (_super) {
__extends(RateLimitError, _super);
function RateLimitError(message, retryAfter, status, statusText, endpoint, method, url, data) {
if (status === void 0) { status = 429; }
if (statusText === void 0) { statusText = 'Too Many Requests'; }
if (endpoint === void 0) { endpoint = ''; }
if (method === void 0) { method = ''; }
if (url === void 0) { url = ''; }
if (data === void 0) { data = null; }
var _this = _super.call(this, message, status, statusText, endpoint, method, url, data) || this;
_this.name = 'RateLimitError';
_this.retryAfter = retryAfter;
Object.setPrototypeOf(_this, RateLimitError.prototype);
return _this;
}
RateLimitError.prototype.toJSON = function () {
return __assign(__assign({}, _super.prototype.toJSON.call(this)), { retryAfter: this.retryAfter });
};
return RateLimitError;
}(ClientError));
exports.RateLimitError = RateLimitError;
/**
* Validation errors (400 with specific validation failures)
*/
var ValidationError = /** @class */ (function (_super) {
__extends(ValidationError, _super);
function ValidationError(message, validationErrors, status, statusText, endpoint, method, url, data) {
if (validationErrors === void 0) { validationErrors = {}; }
if (status === void 0) { status = 400; }
if (statusText === void 0) { statusText = 'Bad Request'; }
if (endpoint === void 0) { endpoint = ''; }
if (method === void 0) { method = ''; }
if (url === void 0) { url = ''; }
if (data === void 0) { data = null; }
var _this = _super.call(this, message, status, statusText, endpoint, method, url, data) || this;
_this.name = 'ValidationError';
_this.validationErrors = validationErrors;
Object.setPrototypeOf(_this, ValidationError.prototype);
return _this;
}
ValidationError.prototype.toJSON = function () {
return __assign(__assign({}, _super.prototype.toJSON.call(this)), { validationErrors: this.validationErrors });
};
return ValidationError;
}(ClientError));
exports.ValidationError = ValidationError;
/**
* Creates the appropriate error type based on HTTP status code
*/
function createErrorFromResponse(response) {
var _a, _b, _c;
var status = response.status, statusText = response.statusText, config = response.config, data = response.data;
var method = ((_a = config === null || config === void 0 ? void 0 : config.method) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || 'UNKNOWN';
var endpoint = (config === null || config === void 0 ? void 0 : config.url) || '';
var baseUrl = (config === null || config === void 0 ? void 0 : config.baseURL) || '';
var url = baseUrl ? "".concat(baseUrl).concat(endpoint) : endpoint;
var message = "".concat(status, " ").concat(statusText, " - ").concat(method, " ").concat(endpoint);
// Extract credentials for auth errors
var credentials = {};
if ((_b = config === null || config === void 0 ? void 0 : config.headers) === null || _b === void 0 ? void 0 : _b['Authorization']) {
credentials['Authorization'] = config.headers['Authorization'];
}
if ((_c = config === null || config === void 0 ? void 0 : config.headers) === null || _c === void 0 ? void 0 : _c['x-api-key']) {
credentials['x-api-key'] = config.headers['x-api-key'];
}
// Extract validation errors if present
var validationErrors = {};
if (status === 400 && (data === null || data === void 0 ? void 0 : data.errors) && typeof data.errors === 'object') {
Object.entries(data.errors).forEach(function (_a) {
var key = _a[0], values = _a[1];
validationErrors[key] = Array.isArray(values) ? values : [String(values)];
});
}
// Handle specific error types based on status code
if (status === 401 || status === 403) {
return new AuthError(message, status, statusText, endpoint, method, url, data, credentials);
}
else if (status === 429) {
var retryAfter = response.headers['retry-after']
? parseInt(response.headers['retry-after'], 10)
: undefined;
return new RateLimitError(message, retryAfter, status, statusText, endpoint, method, url, data);
}
else if (status === 400 && Object.keys(validationErrors).length > 0) {
return new ValidationError(message, validationErrors, status, statusText, endpoint, method, url, data);
}
else if (status >= 400 && status < 500) {
return new ClientError(message, status, statusText, endpoint, method, url, data);
}
else if (status >= 500) {
return new ServerError(message, status, statusText, endpoint, method, url, data);
}
return new HttpError(message, status, statusText, endpoint, method, url, data);
}
exports.createErrorFromResponse = createErrorFromResponse;
//# sourceMappingURL=http-error.js.map