UNPKG

tfl-ts

Version:

🚇 Fully-typed TypeScript client for Transport for London (TfL) API • Zero dependencies • Auto-generated types • Real-time arrivals • Journey planning • Universal compatibility

191 lines (190 loc) • 6.73 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TflErrorHandler = exports.TflConfigError = exports.TflTimeoutError = exports.TflValidationError = exports.TflNetworkError = exports.TflHttpError = exports.TflError = void 0; /** * Base error class for TfL API errors */ class TflError extends Error { constructor(message, statusCode, originalError, requestId) { super(message); this.statusCode = statusCode; this.originalError = originalError; this.isTflError = true; this.timestamp = new Date(); this.name = 'TflError'; this.requestId = requestId; // Maintain proper stack trace if (Error.captureStackTrace) { Error.captureStackTrace(this, TflError); } } } exports.TflError = TflError; /** * HTTP error class for API response errors */ class TflHttpError extends TflError { constructor(message, statusCode, statusText, responseBody, url, originalError, requestId) { super(message, statusCode, originalError, requestId); this.statusCode = statusCode; this.statusText = statusText; this.responseBody = responseBody; this.url = url; this.isHttpError = true; this.name = 'TflHttpError'; if (Error.captureStackTrace) { Error.captureStackTrace(this, TflHttpError); } } /** * Check if this is a client error (4xx) */ isClientError() { return this.statusCode >= 400 && this.statusCode < 500; } /** * Check if this is a server error (5xx) */ isServerError() { return this.statusCode >= 500 && this.statusCode < 600; } /** * Check if this is an authentication error (401) */ isAuthError() { return this.statusCode === 401; } /** * Check if this is a rate limit error (429) */ isRateLimitError() { return this.statusCode === 429; } } exports.TflHttpError = TflHttpError; /** * Network error class for connection issues */ class TflNetworkError extends TflError { constructor(message, code, url, originalError, requestId) { super(message, undefined, originalError, requestId); this.code = code; this.url = url; this.isNetworkError = true; this.name = 'TflNetworkError'; if (Error.captureStackTrace) { Error.captureStackTrace(this, TflNetworkError); } } } exports.TflNetworkError = TflNetworkError; /** * Validation error class for invalid parameters */ class TflValidationError extends TflError { constructor(message, field, value, originalError, requestId) { super(message, undefined, originalError, requestId); this.field = field; this.value = value; this.isValidationError = true; this.name = 'TflValidationError'; if (Error.captureStackTrace) { Error.captureStackTrace(this, TflValidationError); } } } exports.TflValidationError = TflValidationError; /** * Timeout error class for request timeouts */ class TflTimeoutError extends TflError { constructor(message, timeoutMs, url, originalError, requestId) { super(message, undefined, originalError, requestId); this.timeoutMs = timeoutMs; this.url = url; this.isTimeoutError = true; this.name = 'TflTimeoutError'; if (Error.captureStackTrace) { Error.captureStackTrace(this, TflTimeoutError); } } } exports.TflTimeoutError = TflTimeoutError; /** * Configuration error class for setup issues */ class TflConfigError extends TflError { constructor(message, configField, originalError, requestId) { super(message, undefined, originalError, requestId); this.configField = configField; this.isConfigError = true; this.name = 'TflConfigError'; if (Error.captureStackTrace) { Error.captureStackTrace(this, TflConfigError); } } } exports.TflConfigError = TflConfigError; /** * Error handler utility class */ class TflErrorHandler { /** * Handle API errors and convert them to appropriate TflError types */ static handleApiError(error, url, requestId) { // If it's already a TflError, return it if (error instanceof TflError) { return error; } // Handle fetch errors (network issues) if (error instanceof TypeError && error.message.includes('fetch')) { return new TflNetworkError(`Network error: ${error.message}`, 'NETWORK_ERROR', url, error, requestId); } // Handle timeout errors if (error.name === 'AbortError' || error.message.includes('timeout')) { return new TflTimeoutError(`Request timeout: ${error.message}`, 30000, // Default timeout url, error, requestId); } // Handle HTTP response errors if (error.status || error.statusCode) { const statusCode = error.status || error.statusCode; const statusText = error.statusText || 'Unknown Error'; const responseBody = error.data || error.body || error.message; return new TflHttpError(`HTTP ${statusCode}: ${statusText}`, statusCode, statusText, responseBody, url, error, requestId); } // Handle validation errors if (error.message && (error.message.includes('validation') || error.message.includes('invalid') || error.message.includes('required'))) { return new TflValidationError(`Validation error: ${error.message}`, undefined, undefined, error, requestId); } // Generic error fallback return new TflError(`Unexpected error: ${error.message || 'Unknown error occurred'}`, undefined, error, requestId); } /** * Check if an error is retryable */ static isRetryableError(error) { if (error instanceof TflHttpError) { // Retry on server errors (5xx) and rate limits (429) return error.isServerError() || error.isRateLimitError(); } if (error instanceof TflNetworkError || error instanceof TflTimeoutError) { return true; } return false; } /** * Get retry delay for an error */ static getRetryDelay(error, attempt, baseDelay = 1000) { if (error instanceof TflHttpError && error.isRateLimitError()) { // Exponential backoff for rate limits return Math.min(baseDelay * Math.pow(2, attempt), 30000); } // Linear backoff for other retryable errors return baseDelay * (attempt + 1); } } exports.TflErrorHandler = TflErrorHandler;