UNPKG

@segment/analytics-react-native

Version:

The hassle-free way to add Segment analytics to your React-Native app.

161 lines (153 loc) 5.94 kB
import { ErrorClassification } from './types'; /** * Error types reported through the errorHandler in the client */ export let ErrorType = /*#__PURE__*/function (ErrorType) { ErrorType[ErrorType["NetworkUnexpectedHTTPCode"] = 0] = "NetworkUnexpectedHTTPCode"; ErrorType[ErrorType["NetworkServerLimited"] = 1] = "NetworkServerLimited"; ErrorType[ErrorType["NetworkServerRejected"] = 2] = "NetworkServerRejected"; ErrorType[ErrorType["NetworkUnknown"] = 3] = "NetworkUnknown"; ErrorType[ErrorType["JsonUnableToSerialize"] = 4] = "JsonUnableToSerialize"; ErrorType[ErrorType["JsonUnableToDeserialize"] = 5] = "JsonUnableToDeserialize"; ErrorType[ErrorType["JsonUnknown"] = 6] = "JsonUnknown"; ErrorType[ErrorType["PluginError"] = 7] = "PluginError"; ErrorType[ErrorType["InitializationError"] = 8] = "InitializationError"; ErrorType[ErrorType["ResetError"] = 9] = "ResetError"; ErrorType[ErrorType["FlushError"] = 10] = "FlushError"; ErrorType[ErrorType["EventsDropped"] = 11] = "EventsDropped"; return ErrorType; }({}); /** * Segment Error object for ErrorHandler option */ export class SegmentError extends Error { constructor(type, message, innerError, metadata) { super(message); Object.setPrototypeOf(this, SegmentError.prototype); this.type = type; this.message = message; this.innerError = innerError; this.metadata = metadata; } } /** * Custom Error type for Segment HTTP Error responses */ export class NetworkError extends SegmentError { constructor(statusCode, message, innerError) { let type; if (statusCode === 429) { type = ErrorType.NetworkServerLimited; } else if (statusCode > 300 && statusCode < 400) { type = ErrorType.NetworkUnexpectedHTTPCode; } else if (statusCode >= 400) { type = ErrorType.NetworkServerRejected; } else { type = ErrorType.NetworkUnknown; } super(type, message, innerError); Object.setPrototypeOf(this, NetworkError.prototype); this.statusCode = statusCode; this.type = type; } } /** * Error type for JSON Serialization errors */ export class JSONError extends SegmentError { constructor(type, message, innerError) { super(type, message, innerError); Object.setPrototypeOf(this, JSONError.prototype); } } /** * Utility method for handling HTTP fetch errors * @param response Fetch Response * @returns response if status OK, throws NetworkError for everything else */ export const checkResponseForErrors = response => { if (!response.ok) { throw new NetworkError(response.status, response.statusText); } return response; }; /** * Converts a .fetch() error to a SegmentError object for reporting to the error handler * @param error any JS error instance * @returns a SegmentError object */ export const translateHTTPError = error => { // SegmentError already if (error instanceof SegmentError) { return error; // JSON Deserialization Errors } else if (error instanceof SyntaxError) { return new JSONError(ErrorType.JsonUnableToDeserialize, error.message, error); // HTTP Errors } else { const message = error instanceof Error ? error.message : typeof error === 'string' ? error : 'Unknown error'; return new NetworkError(-1, message, error); } }; /** * Classify an HTTP status code into rate_limit, transient, or permanent. * * Precedence: * 1. statusCodeOverrides — explicit per-code overrides * 2. 429 — rate limiting (if rateLimitEnabled !== false) * 3. default4xxBehavior / default5xxBehavior — range defaults * 4. Fallback — permanent (non-retryable) */ export const classifyError = (statusCode, config) => { const override = config?.statusCodeOverrides?.[statusCode.toString()]; if (override !== undefined) { if (override === 'retry') { // If the relevant config is disabled, treat retry overrides as permanent if (statusCode === 429 && config?.rateLimitEnabled === false) { return new ErrorClassification('permanent'); } if (statusCode !== 429 && config?.backoffEnabled === false) { return new ErrorClassification('permanent'); } return statusCode === 429 ? new ErrorClassification('rate_limit') : new ErrorClassification('transient'); } return new ErrorClassification('permanent'); } if (statusCode === 429) { return config?.rateLimitEnabled !== false ? new ErrorClassification('rate_limit') : new ErrorClassification('permanent'); } if (statusCode >= 400 && statusCode < 500) { const behavior = config?.default4xxBehavior ?? 'drop'; if (behavior === 'retry' && config?.backoffEnabled === false) { return new ErrorClassification('permanent'); } return new ErrorClassification(behavior === 'retry' ? 'transient' : 'permanent'); } if (statusCode >= 500 && statusCode < 600) { const behavior = config?.default5xxBehavior ?? 'retry'; if (behavior === 'retry' && config?.backoffEnabled === false) { return new ErrorClassification('permanent'); } return new ErrorClassification(behavior === 'retry' ? 'transient' : 'permanent'); } return new ErrorClassification('permanent'); }; /** * Parse Retry-After header value (seconds or HTTP-date format). * Returns delay in seconds clamped to maxRetryInterval, or undefined if invalid. */ export const parseRetryAfter = (retryAfterValue, maxRetryInterval = 300) => { if (retryAfterValue === null || retryAfterValue === '') return undefined; const asNumber = Number(retryAfterValue); if (Number.isFinite(asNumber)) { if (asNumber < 0) return undefined; return Math.min(asNumber, maxRetryInterval); } const retryDate = new Date(retryAfterValue); if (!isNaN(retryDate.getTime())) { const secondsUntil = Math.ceil((retryDate.getTime() - Date.now()) / 1000); return Math.min(Math.max(secondsUntil, 0), maxRetryInterval); } return undefined; }; //# sourceMappingURL=errors.js.map