arangojs
Version:
The official ArangoDB JavaScript driver.
285 lines • 7.73 kB
JavaScript
/**
* ```ts
* import type { ArangoError, HttpError } from "arangojs/errors";
* ```
*
* The "errors" module provides types and interfaces for TypeScript related
* to arangojs error handling.
*
* @packageDocumentation
*/
import * as connection from "./connection.js";
import { ERROR_ARANGO_MAINTENANCE_MODE } from "./lib/codes.js";
/**
* Indicates whether the given value represents an {@link ArangoError}.
*
* @param error - A value that might be an `ArangoError`.
*/
export function isArangoError(error) {
return Boolean(error && error.isArangoError);
}
/**
* Indicates whether the given value represents a {@link NetworkError}.
*
* @param error - A value that might be a `NetworkError`.
*/
export function isNetworkError(error) {
return error instanceof NetworkError;
}
/**
* @internal
*
* Indicates whether the given value represents a Node.js `SystemError`.
*/
export function isSystemError(err) {
if (!err || !(err instanceof Error))
return false;
if (Object.getPrototypeOf(err) !== Error.prototype)
return false;
const error = err;
if (typeof error.code !== "string")
return false;
if (typeof error.syscall !== "string")
return false;
return typeof error.errno === "number" || typeof error.errno === "string";
}
/**
* @internal
*
* Indicates whether the given value represents a Node.js `UndiciError`.
*/
export function isUndiciError(err) {
if (!err || !(err instanceof Error))
return false;
const error = err;
if (typeof error.code !== "string")
return false;
return error.code.startsWith("UND_");
}
/**
* @internal
*
* Determines whether the given failed fetch error cause is safe to retry.
*/
function isSafeToRetryFailedFetch(error) {
if (!error || !error.cause)
return null;
let cause = error.cause;
if (isArangoError(cause) || isNetworkError(cause)) {
return cause.isSafeToRetry;
}
if (isSystemError(cause) &&
cause.syscall === "connect" &&
cause.code === "ECONNREFUSED") {
return true;
}
if (isUndiciError(cause) && cause.code === "UND_ERR_CONNECT_TIMEOUT") {
return true;
}
return isSafeToRetryFailedFetch(cause);
}
/**
* Represents an error from a deliberate timeout encountered while waiting
* for propagation.
*/
export class PropagationTimeoutError extends Error {
name = "PropagationTimeoutError";
constructor(message, options = {}) {
super(message ?? "Timed out while waiting for propagation", options);
}
}
/**
* Represents a network error or an error encountered while performing a network request.
*/
export class NetworkError extends Error {
name = "NetworkError";
/**
* Indicates whether the request that caused this error can be safely retried.
*/
isSafeToRetry;
/**
* Fetch request object.
*/
request;
constructor(message, request, options = {}) {
const { isSafeToRetry = null, ...opts } = options;
super(message, opts);
this.request = request;
this.isSafeToRetry = isSafeToRetry;
}
toJSON() {
return {
error: true,
errorMessage: this.message,
code: 0,
};
}
}
/**
* Represents an error from a deliberate timeout encountered while waiting
* for a server response.
*/
export class ResponseTimeoutError extends NetworkError {
name = "ResponseTimeoutError";
constructor(message, request, options = {}) {
super(message ?? "Timed out while waiting for server response", request, options);
}
}
/**
* Represents an error from a request that was aborted.
*/
export class RequestAbortedError extends NetworkError {
name = "RequestAbortedError";
constructor(message, request, options = {}) {
super(message ?? "Request aborted", request, options);
}
}
/**
* Represents an error from a failed fetch request.
*
* The root cause is often extremely difficult to determine.
*/
export class FetchFailedError extends NetworkError {
name = "FetchFailedError";
constructor(message, request, options = {}) {
let isSafeToRetry = options.isSafeToRetry ?? isSafeToRetryFailedFetch(options.cause);
if (options.cause?.cause instanceof Error && options.cause.cause.message) {
message = `Fetch failed: ${options.cause.cause.message}`;
}
super(message ?? "Fetch failed", request, { ...options, isSafeToRetry });
}
}
/**
* Represents a plain HTTP error response.
*/
export class HttpError extends NetworkError {
name = "HttpError";
/**
* HTTP status code of the server response.
*/
code;
/**
* Server response object.
*/
response;
/**
* @internal
*/
constructor(response, options = {}) {
super(connection.getStatusMessage(response), response.request, options);
this.response = response;
this.code = response.status;
}
toJSON() {
return {
error: true,
errorMessage: this.message,
code: this.code,
};
}
toString() {
return `${this.name} ${this.code}: ${this.message}`;
}
}
/**
* Represents an error returned by ArangoDB.
*/
export class ArangoError extends Error {
name = "ArangoError";
/**
* Indicates whether the request that caused this error can be safely retried.
*
* @internal
*/
isSafeToRetry = null;
/**
* @internal
*/
get error() {
return true;
}
/**
* ArangoDB error code.
*
* See [ArangoDB error documentation](https://www.arangodb.com/docs/stable/appendix-error-codes.html).
*/
errorNum;
/**
* Error message accompanying the error code.
*/
get errorMessage() {
return this.message;
}
/**
* HTTP status code included in the server error response object.
*/
code;
/**
* @internal
*
* Creates a new `ArangoError` from a response object.
*/
static from(response) {
return new ArangoError(response.parsedBody, {
cause: new HttpError(response),
});
}
/**
* Creates a new `ArangoError` from an ArangoDB error response.
*/
constructor(data, options = {}) {
const { isSafeToRetry, ...opts } = options;
super(data.errorMessage, opts);
this.errorNum = data.errorNum;
this.code = data.code;
if (isSafeToRetry !== undefined) {
this.isSafeToRetry = isSafeToRetry;
}
else if (this.errorNum === ERROR_ARANGO_MAINTENANCE_MODE) {
this.isSafeToRetry = true;
}
else if (this.cause instanceof NetworkError) {
this.isSafeToRetry = this.cause.isSafeToRetry;
}
}
/**
* Server response object.
*/
get response() {
const cause = this.cause;
if (cause instanceof HttpError) {
return cause.response;
}
return undefined;
}
/**
* Fetch request object.
*/
get request() {
const cause = this.cause;
if (cause instanceof NetworkError) {
return cause.request;
}
return undefined;
}
/**
* @internal
*
* Indicates that this object represents an ArangoDB error.
*/
get isArangoError() {
return true;
}
toJSON() {
return {
error: true,
errorMessage: this.errorMessage,
errorNum: this.errorNum,
code: this.code,
};
}
toString() {
return `${this.name} ${this.errorNum}: ${this.message}`;
}
}
//# sourceMappingURL=errors.js.map