UNPKG

novel-reader-sdk

Version:

SDK for Novel Reader API

246 lines 6.88 kB
"use strict"; /** * SDK Utility Functions * * Helper functions for URL building, query parameters, and response handling */ Object.defineProperty(exports, "__esModule", { value: true }); exports.defaultRetryConfig = exports.defaultFetchOptions = void 0; exports.buildUrl = buildUrl; exports.buildQueryString = buildQueryString; exports.buildFullUrl = buildFullUrl; exports.fetchWithTimeout = fetchWithTimeout; exports.parseJsonResponse = parseJsonResponse; exports.validateDatabaseId = validateDatabaseId; exports.validateUrl = validateUrl; exports.extractHostname = extractHostname; exports.normalizeString = normalizeString; exports.arrayToString = arrayToString; exports.parseNumber = parseNumber; exports.withRetry = withRetry; exports.logRequest = logRequest; exports.logResponse = logResponse; const errors_1 = require("./errors"); // ===== URL Building Utilities ===== /** * Build URL with path parameters */ function buildUrl(baseUrl, path, params) { let url = `${baseUrl.replace(/\/$/, '')}${path}`; if (params) { for (const [key, value] of Object.entries(params)) { url = url.replace(`:${key}`, encodeURIComponent(value)); } } return url; } /** * Build query string from parameters */ function buildQueryString(params) { if (!params || Object.keys(params).length === 0) { return ''; } const searchParams = new URLSearchParams(); for (const [key, value] of Object.entries(params)) { if (value !== undefined && value !== null) { searchParams.append(key, value); } } const queryString = searchParams.toString(); return queryString ? `?${queryString}` : ''; } /** * Combine URL with query parameters */ function buildFullUrl(baseUrl, path, params, query) { const url = buildUrl(baseUrl, path, params); const queryString = buildQueryString(query); return `${url}${queryString}`; } // ===== HTTP Request Utilities ===== /** * Default fetch options for API requests */ exports.defaultFetchOptions = { headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, }; /** * Enhanced fetch with timeout and error handling */ async function fetchWithTimeout(url, options = {}) { const { timeout = 30000, ...fetchOptions } = options; const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); try { const response = await fetch(url, { ...exports.defaultFetchOptions, ...fetchOptions, signal: controller.signal, }); clearTimeout(timeoutId); return response; } catch (error) { clearTimeout(timeoutId); if (error instanceof Error && error.name === 'AbortError') { throw new errors_1.TimeoutError(`Request timeout after ${timeout}ms`); } throw new errors_1.NetworkError('Network request failed', error); } } /** * Parse JSON response with error handling */ async function parseJsonResponse(response) { const text = await response.text(); if (!text) { throw new errors_1.NetworkError('Empty response body'); } try { const data = JSON.parse(text); // Check if response is an error if (!response.ok) { if (isApiError(data)) { throw (0, errors_1.createErrorFromApiResponse)(data); } throw new errors_1.NetworkError(`HTTP ${response.status}: ${response.statusText}`); } return data; } catch (error) { if (error instanceof Error && error.name === 'SyntaxError') { throw new errors_1.NetworkError('Invalid JSON response', error); } throw error; } } /** * Type guard to check if response is an API error */ function isApiError(data) { if (typeof data === 'object' && data !== null && Object.prototype.hasOwnProperty.call(data, 'error')) { const error = data.error; if (typeof error === 'object' && error !== null && Object.prototype.hasOwnProperty.call(error, 'code') && Object.prototype.hasOwnProperty.call(error, 'message')) { return true; } } return false; } // ===== Validation Utilities ===== /** * Validate database ID */ function validateDatabaseId(id) { if (!id || typeof id !== 'string') { throw new Error('Database ID must be a non-empty string'); } } /** * Validate URL format */ function validateUrl(url) { try { new URL(url); return true; } catch { return false; } } /** * Extract hostname from URL */ function extractHostname(url) { try { return new URL(url).hostname; } catch { throw new Error(`Invalid URL: ${url}`); } } // ===== Data Transformation Utilities ===== /** * Clean and normalize string data */ function normalizeString(str) { return (str ?? '').trim(); } /** * Convert array to comma-separated string */ function arrayToString(arr) { return arr.filter(Boolean).join(', '); } /** * Safe number parsing */ function parseNumber(value) { if (typeof value === 'number') { return value; } if (typeof value === 'string') { const parsed = parseFloat(value); return isNaN(parsed) ? undefined : parsed; } return undefined; } /** * Default retry configuration */ exports.defaultRetryConfig = { maxAttempts: 3, baseDelay: 1000, maxDelay: 10000, backoffFactor: 2, }; /** * Retry function with exponential backoff */ async function withRetry(fn, config = {}) { const { maxAttempts, baseDelay, maxDelay, backoffFactor } = { ...exports.defaultRetryConfig, ...config, }; let lastError = new Error('No attempts made'); for (let attempt = 1; attempt <= maxAttempts; attempt++) { try { return await fn(); } catch (error) { lastError = error instanceof Error ? error : new Error(String(error)); if (attempt === maxAttempts) { break; } const delay = Math.min(baseDelay * Math.pow(backoffFactor, attempt - 1), maxDelay); await new Promise(resolve => setTimeout(resolve, delay)); } } throw lastError; } // ===== Debug Utilities ===== /** * Log request details for debugging */ function logRequest(method, url, data) { if (process.env.NODE_ENV === 'development') { console.log(`[ScrapingSDK] ${method} ${url}`, data ? { data } : ''); } } /** * Log response details for debugging */ function logResponse(url, status, data) { if (process.env.NODE_ENV === 'development') { console.log(`[ScrapingSDK] Response ${status} from ${url}`, data ? { data } : ''); } } //# sourceMappingURL=utils.js.map