Improve this further.

Don't change the fetched html structure because it's damages the css selectors accuracy.

Don't damge any data in any process.

Scrap with efficiency and precisen.

Hand the css selectors appropriately and precisely.

Make all of the things precise, careful, appropriate, working, practical, client side only, fast, effective, efficient, reliable.

After this Review this whole codes throughly and fix all of the potential errors, issues, edge cases, bottle necks, resources consumption, possible errors or issues, might give a error or issue, bugs you can find.

Complete this library.

Their are many versions of the library given below, review and analyse every one of them, I want you to make best complete and practically working version out of all this.

Send the fully WORKING full complete codes.

Send the fully working full codes.

---

/*!
 * ScraperLib.js v2.0.0
 * A practical client-side web scraping library
 * License: MIT
 */

(function (global) {
  'use strict';

  /**
   * Core utility functions
   */
  const Utils = {
    isValidURL(url) {
      try {
        new URL(url);
        return true;
      } catch (e) {
        return false;
      }
    },

    sanitize(str) {
      const temp = document.createElement('div');
      temp.textContent = str;
      return temp.innerHTML;
    },

    async initCache() {
      if ('caches' in window) {
        return await caches.open('scraper-lib-cache');
      }
      return null;
    },

    generateCacheKey(url, options) {
      return `${url}-${JSON.stringify(options)}`;
    },
  };

  /**
   * Storage handler for different output formats
   */
  class StorageHandler {
    constructor() {
      this.dbPromise = null;
    }

    async initIndexedDB(dbName = 'ScraperLibDB', storeName = 'scrapedData') {
      return new Promise((resolve, reject) => {
        const request = indexedDB.open(dbName, 1);

        request.onerror = () => reject(new Error('IndexedDB initialization failed'));

        request.onupgradeneeded = (event) => {
          const db = event.target.result;
          if (!db.objectStoreNames.contains(storeName)) {
            const store = db.createObjectStore(storeName, { keyPath: 'url' });
            store.createIndex('timestamp', 'timestamp', { unique: false });
          }
        };

        request.onsuccess = (event) => resolve(event.target.result);
      });
    }

    async store(url, data, format, db = null) {
      switch (format) {
        case 'indexeddb':
          if (!this.dbPromise) {
            this.dbPromise = this.initIndexedDB();
          }
          const idb = await this.dbPromise;
          const tx = idb.transaction('scrapedData', 'readwrite');
          const store = tx.objectStore('scrapedData');
          store.put({
            url,
            data,
            timestamp: Date.now(),
          });
          await tx.complete;
          break;

        case 'localstorage':
          try {
            localStorage.setItem(
              `scraper-${url}`,
              JSON.stringify({
                data,
                timestamp: Date.now(),
              })
            );
          } catch (e) {
            throw new Error('LocalStorage quota exceeded');
          }
          break;

        case 'custom':
          if (db && typeof db.save === 'function') {
            await db.save(url, data);
          }
          break;
      }
    }
  }

  /**
   * Rate limiter for respecting website limits
   */
  class RateLimiter {
    constructor(requestsPerSecond = 2) {
      this.requestsPerSecond = requestsPerSecond;
      this.lastRequestTime = 0;
    }

    async acquireToken() {
      const now = Date.now();
      const interval = 1000 / this.requestsPerSecond;
      const timeSinceLast = now - this.lastRequestTime;
      const timeToWait = Math.max(0, interval - timeSinceLast);

      if (timeToWait > 0) {
        await new Promise((resolve) => setTimeout(resolve, timeToWait));
      }

      this.lastRequestTime = Date.now();
      return true;
    }
  }

  /**
   * Main ScraperLib class
   */
  class ScraperLib {
    constructor(options = {}) {
      this.settings = {
        rateLimitRequests: 2,
        maxConcurrent: 5,
        timeout: 30000,
        retries: 3,
        retryDelay: 1000,
        cacheTime: 3600000, // 1 hour
        outputFormat: 'json',
        useCache: false,
        useProxy: false,
        proxyUrl: '',
        headers: {},
        userAgent: navigator.userAgent,
        ...options,
      };

      this.storage = new StorageHandler();
      this.rateLimiter = new RateLimiter(this.settings.rateLimitRequests);
      this.activeControllers = new Set();
      this.cache = null;

      if (this.settings.useCache) {
        Utils.initCache().then((cache) => {
          this.cache = cache;
        });
      }
    }

    /**
     * Main scraping method
     */
    async scrape(configs) {
      if (!configs || typeof configs !== 'object') {
        throw new Error('Invalid configuration object');
      }

      const results = {};
      const urls = Object.keys(configs);
      const chunks = this._chunkArray(urls, this.settings.maxConcurrent);

      for (const chunk of chunks) {
        const promises = chunk.map(async (url) => {
          try {
            const data = await this._processSingleUrl(url, configs[url]);
            results[url] = data;
            return { url, success: true, data };
          } catch (error) {
            results[url] = { error: error.message };
            return { url, success: false, error: error.message };
          }
        });

        await Promise.all(promises);
      }

      return results;
    }

    /**
     * Process a single URL with full error handling and retries
     */
    async _processSingleUrl(url, config) {
      if (!Utils.isValidURL(url)) {
        throw new Error(`Invalid URL: ${url}`);
      }

      // Check cache first if enabled
      if (this.settings.useCache && this.cache) {
        const cachedResponse = await this._checkCache(url);
        if (cachedResponse) return cachedResponse;
      }

      // Acquire rate limiting token
      await this.rateLimiter.acquireToken();

      // Prepare fetch options
      const options = this._prepareFetchOptions(config);

      // Fetch with retries
      const response = await this._fetchWithRetries(url, options);

      // Parse and extract data
      const data = await this._extractData(response, config.selectors);

      // Store data if needed
      if (this.settings.outputFormat !== 'json') {
        await this.storage.store(
          url,
          data,
          this.settings.outputFormat,
          this.settings.customDB
        );
      }

      // Store in cache if enabled
      if (this.settings.useCache && this.cache) {
        await this.cache.put(
          Utils.generateCacheKey(url),
          new Response(JSON.stringify({ content: data, timestamp: Date.now() }))
        );
      }

      return data;
    }

    /**
     * Prepare fetch options with headers and proxy settings
     */
    _prepareFetchOptions(config) {
      const headers = new Headers({
        'User-Agent': config.userAgent || this.settings.userAgent,
        ...this.settings.headers,
        ...config.headers,
      });

      const options = {
        method: 'GET',
        headers,
        mode: 'cors',
        credentials: 'omit',
        redirect: 'follow',
      };

      return options;
    }

    /**
     * Fetch with automatic retries
     */
    async _fetchWithRetries(url, options) {
      let lastError;

      for (let attempt = 0; attempt < this.settings.retries; attempt++) {
        let controller;
        try {
          controller = new AbortController();
          options.signal = controller.signal;
          this.activeControllers.add(controller);

          const timeoutId = setTimeout(() => controller.abort(), this.settings.timeout);

          const fetchUrl = this.settings.useProxy
            ? `${this.settings.proxyUrl}${encodeURIComponent(url)}`
            : url;

          const response = await fetch(fetchUrl, options);

          clearTimeout(timeoutId);
          this.activeControllers.delete(controller);

          if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
          }

          return response;
        } catch (error) {
          lastError = error;
          this.activeControllers.delete(controller);

          if (attempt < this.settings.retries - 1) {
            const delay = this.settings.retryDelay * Math.pow(2, attempt);
            await new Promise((resolve) => setTimeout(resolve, delay));
            continue;
          } else {
            throw lastError;
          }
        }
      }

      throw lastError;
    }

    /**
     * Extract data using provided selectors
     */
    async _extractData(response, selectors) {
      const text = await response.text();

      const parser = new DOMParser();
      const doc = parser.parseFromString(text, 'text/html');

      const result = {};

      for (const [key, selector] of Object.entries(selectors)) {
        if (typeof selector === 'string') {
          // Simple selector
          const elements = doc.querySelectorAll(selector);
          result[key] = Array.from(elements, (el) => el.textContent.trim());
        } else if (typeof selector === 'object') {
          // Complex selector with attributes
          const elements = doc.querySelectorAll(selector.selector);
          result[key] = Array.from(elements, (el) => {
            if (selector.attr) {
              return el.getAttribute(selector.attr);
            } else if (selector.html) {
              return el.innerHTML;
            } else {
              return el.textContent.trim();
            }
          });
        }
      }

      return result;
    }

    /**
     * Check cache for existing data
     */
    async _checkCache(url) {
      if (!this.cache) return null;

      const cacheKey = Utils.generateCacheKey(url);
      const cached = await this.cache.match(cacheKey);

      if (cached) {
        const data = await cached.json();
        const age = Date.now() - data.timestamp;

        if (age < this.settings.cacheTime) {
          return data.content;
        }
      }

      return null;
    }

    /**
     * Split array into chunks for concurrent processing
     */
    _chunkArray(array, size) {
      const chunks = [];
      for (let i = 0; i < array.length; i += size) {
        chunks.push(array.slice(i, i + size));
      }
      return chunks;
    }

    /**
     * Clean up resources
     */
    destroy() {
      this.activeControllers.forEach((controller) => {
        if (controller && typeof controller.abort === 'function') {
          controller.abort();
        }
      });
      this.activeControllers.clear();
      this.cache = null;
      this.storage = null;
    }
  }

  // Export to global scope
  global.ScraperLib = ScraperLib;
})(typeof window !== 'undefined' ? window : this);

---

/*!
 * ScraperLib.js v2.1.0
 * An advanced client-side web scraping library with robust error handling
 * License: MIT
 */

(function (global) {
  'use strict';

  /**
   * Enhanced utility functions
   */
  const Utils = {
    isValidURL(url) {
      try {
        new URL(url);
        return true;
      } catch (e) {
        return false;
      }
    },

    sanitize(str) {
      const temp = document.createElement('div');
      temp.textContent = str;
      return temp.innerHTML;
    },

    debounce(func, wait) {
      let timeout;
      return function (...args) {
        clearTimeout(timeout);
        timeout = setTimeout(() => func.apply(this, args), wait);
      };
    },

    async initCache() {
      if ('caches' in window) {
        return await caches.open('scraper-lib-cache');
      }
      return null;
    },

    generateCacheKey(url, options) {
      return `${url}-${JSON.stringify(options)}`;
    },

    isElement(element) {
      return element instanceof Element || element instanceof HTMLDocument;
    },

    // New: Validate and normalize selectors
    normalizeSelector(selector) {
      try {
        document.querySelector(selector); // Test selector validity
        return selector;
      } catch (e) {
        throw new Error(`Invalid CSS selector: ${selector}`);
      }
    },

    // New: Advanced text extraction with formatting preservation
    extractText(element, preserveFormatting = false) {
      if (!element) return '';
      if (preserveFormatting) {
        return element.innerHTML
          .replace(/<br\s*\/?>/gi, '\n')
          .replace(/<\/p><p>/gi, '\n\n')
          .replace(/<[^>]+>/g, '')
          .trim();
      }
      return element.textContent.trim();
    },

    // New: Memory usage optimization
    releaseMemory() {
      if (global.gc) {
        global.gc();
      }
    }
  };

  /**
   * Enhanced storage handler with better error handling
   */
  class StorageHandler {
    constructor() {
      this.dbPromise = null;
      this.storageType = this._detectBestStorage();
    }

    _detectBestStorage() {
      if (indexedDB) return 'indexeddb';
      if (localStorage) return 'localstorage';
      return 'memory';
    }

    async initIndexedDB(dbName = 'ScraperLibDB', storeName = 'scrapedData') {
      return new Promise((resolve, reject) => {
        const request = indexedDB.open(dbName, 1);
        
        request.onerror = () => reject(new Error('IndexedDB initialization failed'));
        
        request.onupgradeneeded = (event) => {
          const db = event.target.result;
          if (!db.objectStoreNames.contains(storeName)) {
            const store = db.createObjectStore(storeName, { 
              keyPath: 'id',
              autoIncrement: true 
            });
            store.createIndex('url', 'url', { unique: false });
            store.createIndex('timestamp', 'timestamp', { unique: false });
          }
        };
        
        request.onsuccess = (event) => {
          const db = event.target.result;
          db.onerror = (event) => {
            console.error('Database error:', event.target.error);
          };
          resolve(db);
        };
      });
    }

    async store(url, data, format, db = null) {
      const timestamp = Date.now();
      const storeData = {
        url,
        data,
        timestamp,
        format
      };

      try {
        switch (format) {
          case 'indexeddb':
            if (!this.dbPromise) {
              this.dbPromise = this.initIndexedDB();
            }
            const idb = await this.dbPromise;
            return new Promise((resolve, reject) => {
              const tx = idb.transaction('scrapedData', 'readwrite');
              const store = tx.objectStore('scrapedData');
              const request = store.add(storeData);
              
              request.onsuccess = () => resolve(request.result);
              request.onerror = () => reject(request.error);
              tx.oncomplete = () => resolve(true);
            });

          case 'localstorage':
            const key = `scraper-${url}-${timestamp}`;
            try {
              localStorage.setItem(key, JSON.stringify(storeData));
              return true;
            } catch (e) {
              if (e.name === 'QuotaExceededError') {
                // Attempt to free up space by removing old entries
                this._cleanupStorage();
                localStorage.setItem(key, JSON.stringify(storeData));
                return true;
              }
              throw e;
            }

          case 'custom':
            if (db && typeof db.save === 'function') {
              return await db.save(storeData);
            }
            throw new Error('Invalid custom database handler');

          default:
            throw new Error(`Unsupported storage format: ${format}`);
        }
      } catch (error) {
        console.error('Storage error:', error);
        throw new Error(`Failed to store data: ${error.message}`);
      }
    }

    _cleanupStorage() {
      const keys = Object.keys(localStorage);
      const scraperKeys = keys.filter(key => key.startsWith('scraper-'));
      
      if (scraperKeys.length > 100) { // Keep only last 100 entries
        scraperKeys
          .sort((a, b) => {
            const timeA = JSON.parse(localStorage.getItem(a)).timestamp;
            const timeB = JSON.parse(localStorage.getItem(b)).timestamp;
            return timeA - timeB;
          })
          .slice(0, scraperKeys.length - 100)
          .forEach(key => localStorage.removeItem(key));
      }
    }
  }

  /**
   * Enhanced rate limiter with dynamic adjustment
   */
  class RateLimiter {
    constructor(requestsPerSecond = 2) {
      this.queue = [];
      this.requestsPerSecond = requestsPerSecond;
      this.lastRequestTime = 0;
      this.responseTimings = [];
      this.maxResponseTimes = 50; // Keep track of last 50 response times
    }

    async acquireToken() {
      const now = Date.now();
      const timeToWait = Math.max(0, (1000 / this.requestsPerSecond) - (now - this.lastRequestTime));
      
      await new Promise(resolve => setTimeout(resolve, timeToWait));
      this.lastRequestTime = Date.now();
      
      return true;
    }

    recordResponseTime(time) {
      this.responseTimings.push(time);
      if (this.responseTimings.length > this.maxResponseTimes) {
        this.responseTimings.shift();
      }
      this._adjustRate();
    }

    _adjustRate() {
      if (this.responseTimings.length < 10) return;

      const avgResponseTime = this.responseTimings.reduce((a, b) => a + b, 0) / this.responseTimings.length;
      
      // Adjust rate based on response times
      if (avgResponseTime > 2000) { // If average response time > 2s
        this.requestsPerSecond = Math.max(1, this.requestsPerSecond * 0.8);
      } else if (avgResponseTime < 500) { // If average response time < 500ms
        this.requestsPerSecond = Math.min(10, this.requestsPerSecond * 1.2);
      }
    }
  }

  /**
   * Enhanced main ScraperLib class
   */
  class ScraperLib {
    constructor(options = {}) {
      this.settings = {
        rateLimitRequests: 2,
        maxConcurrent: 5,
        timeout: 30000,
        retries: 3,
        retryDelay: 1000,
        cacheTime: 3600000, // 1 hour
        outputFormat: 'json',
        useProxy: false,
        proxyUrl: '',
        headers: {},
        preserveFormatting: false,
        validateSelectors: true,
        maxResponseSize: 10 * 1024 * 1024, // 10MB
        ...options
      };

      this.storage = new StorageHandler();
      this.rateLimiter = new RateLimiter(this.settings.rateLimitRequests);
      this.activeFetches = new Map();
      this.cache = null;
      this.errorHandlers = new Map();

      if (this.settings.useCache) {
        Utils.initCache().then(cache => this.cache = cache);
      }
    }

    /**
     * Main scraping method with enhanced error handling
     */
    async scrape(configs) {
      if (!configs || typeof configs !== 'object') {
        throw new Error('Invalid configuration object');
      }

      const results = {};
      const urls = Object.keys(configs);
      const chunks = this._chunkArray(urls, this.settings.maxConcurrent);

      try {
        for (const chunk of chunks) {
          const promises = chunk.map(url => this._processSingleUrl(url, configs[url])
            .then(data => {
              results[url] = data;
              return { url, success: true, data };
            })
            .catch(error => {
              const handler = this.errorHandlers.get(url) || this.errorHandlers.get('default');
              if (handler) {
                return handler(error, url).then(fallbackData => {
                  results[url] = fallbackData;
                  return { url, success: true, data: fallbackData };
                });
              }
              results[url] = { error: error.message };
              return { url, success: false, error: error.message };
            })
          );

          await Promise.all(promises);
          Utils.releaseMemory(); // Optimize memory usage between chunks
        }

        return results;
      } catch (error) {
        console.error('Scraping error:', error);
        throw new Error(`Scraping failed: ${error.message}`);
      }
    }

    /**
     * Enhanced URL processing with better error handling
     */
    async _processSingleUrl(url, config) {
      if (!Utils.isValidURL(url)) {
        throw new Error(`Invalid URL: ${url}`);
      }

      // Validate selectors if enabled
      if (this.settings.validateSelectors) {
        this._validateSelectors(config.selectors);
      }

      // Check cache first if enabled
      if (this.settings.useCache && this.cache) {
        const cachedResponse = await this._checkCache(url, config);
        if (cachedResponse) return cachedResponse;
      }

      const startTime = Date.now();
      
      try {
        // Acquire rate limiting token
        await this.rateLimiter.acquireToken();

        // Prepare fetch options
        const options = this._prepareFetchOptions(config);
        
        // Fetch with retries
        const response = await this._fetchWithRetries(url, options);
        
        // Record response time for rate limiting adjustment
        this.rateLimiter.recordResponseTime(Date.now() - startTime);
        
        // Check response size
        const contentLength = response.headers.get('content-length');
        if (contentLength && parseInt(contentLength) > this.settings.maxResponseSize) {
          throw new Error('Response too large');
        }
        
        // Parse and extract data
        const data = await this._extractData(response, config.selectors);
        
        // Store data if needed
        if (this.settings.outputFormat !== 'json') {
          await this.storage.store(
            url,
            data,
            this.settings.outputFormat,
            this.settings.customDB
          );
        }

        return data;
      } catch (error) {
        console.error(`Error processing ${url}:`, error);
        throw error;
      }
    }

    /**
     * Validate selectors configuration
     */
    _validateSelectors(selectors) {
      if (!selectors || typeof selectors !== 'object') {
        throw new Error('Invalid selectors configuration');
      }

      for (const [key, selector] of Object.entries(selectors)) {
        if (typeof selector === 'string') {
          Utils.normalizeSelector(selector);
        } else if (typeof selector === 'object') {
          if (!selector.selector) {
            throw new Error(`Missing selector for key: ${key}`);
          }
          Utils.normalizeSelector(selector.selector);
        } else {
          throw new Error(`Invalid selector type for key: ${key}`);
        }
      }
    }

    /**
     * Enhanced data extraction with better error handling
     */
    async _extractData(response, selectors) {
      const text = await response.text();
      const parser = new DOMParser();
      const doc = parser.parseFromString(text, 'text/html');

      if (doc.querySelector('parsererror')) {
        throw new Error('Invalid HTML content');
      }

      const result = {};

      try {
        for (const [key, selector] of Object.entries(selectors)) {
          if (typeof selector === 'string') {
            // Simple selector
            result[key] = Array.from(doc.querySelectorAll(selector))
              .map(el => Utils.extractText(el, this.settings.preserveFormatting));
          } else if (typeof selector === 'object') {
            // Complex selector with attributes
            result[key] = Array.from(doc.querySelectorAll(selector.selector))
              .map(el => {
                if (selector.attr) {
                  return Utils.sanitize(el.getAttribute(selector.attr) || '');
                } else if (selector.html) {
                  return Utils.sanitize(el.innerHTML);
                } else {
                  return Utils.extractText(el, this.settings.preserveFormatting);
                }
              })
              .filter(Boolean); // Remove empty values
          }

          // Handle empty results
          if (result[key].length === 0 && selector.required) {
            throw new Error(`Required selector "${key}" returned no results`);
          }
        }

        return result;
      } catch (error) {
        console.error('Data extraction error:', error);
        throw new Error(`Failed to extract data: ${error.message}`);
      }
    }

    /**
     * Register custom error handlers
     */
    onError(url, handler) {
      if (typeof handler !== 'function') {
        throw new Error('Error handler must be a function');
      }
      this.errorHandlers.set(url, handler);
    }

    /**
     * Clean up resources and perform garbage collection
     */
    destroy() {
      this.activeFetches.forEach((controller) => {
        if (controller.abort) {
          controller.abort();
        }
      });
      
      this.activeFetches.clear();
      this.cache = null;
      this.storage = null;
      this.errorHandlers.clear();
      Utils.releaseMemory();
    }
  }

  // Export to global scope
  global.ScraperLib = ScraperLib;

})(typeof window !== 'undefined' ? window : this);

---

/*!
 * ScraperLib.js v2.1.1
 * An advanced client-side web scraping library with robust error handling
 * License: MIT
 */

(function (global) {
  'use strict';

  /**
   * Enhanced utility functions with error handling
   */
  const Utils = {
    isValidURL(url) {
      if (!url || typeof url !== 'string') return false;
      try {
        new URL(url);
        return true;
      } catch (e) {
        return false;
      }
    },

    sanitize(str) {
      if (!str) return '';
      try {
        const temp = document.createElement('div');
        temp.textContent = str;
        return temp.innerHTML;
      } catch (e) {
        console.error('Sanitization error:', e);
        return '';
      }
    },

    debounce(func, wait) {
      if (typeof func !== 'function') throw new Error('Expected function');
      let timeout;
      return function (...args) {
        clearTimeout(timeout);
        timeout = setTimeout(() => func.apply(this, args), wait);
      };
    },

    async initCache() {
      try {
        if ('caches' in window) {
          return await caches.open('scraper-lib-cache');
        }
      } catch (e) {
        console.error('Cache initialization failed:', e);
      }
      return null;
    },

    generateCacheKey(url, options) {
      try {
        return `${url}-${JSON.stringify(options)}`;
      } catch (e) {
        return url;
      }
    },

    isElement(element) {
      return element instanceof Element || element instanceof HTMLDocument;
    },

    normalizeSelector(selector) {
      if (!selector || typeof selector !== 'string') {
        throw new Error('Invalid selector type');
      }
      try {
        document.querySelector(selector);
        return selector;
      } catch (e) {
        throw new Error(`Invalid CSS selector: ${selector}`);
      }
    },

    extractText(element, preserveFormatting = false) {
      if (!element) return '';
      try {
        if (preserveFormatting) {
          return element.innerHTML
            .replace(/<br\s*\/?>/gi, '\n')
            .replace(/<\/p><p>/gi, '\n\n')
            .replace(/<[^>]+>/g, '')
            .trim();
        }
        return element.textContent.trim();
      } catch (e) {
        console.error('Text extraction error:', e);
        return '';
      }
    },

    releaseMemory() {
      try {
        if (global.gc) {
          global.gc();
        }
      } catch (e) {
        console.warn('Memory release failed:', e);
      }
    },

    // New: Array chunking utility
    chunkArray(array, size) {
      if (!Array.isArray(array)) throw new Error('Expected array');
      if (!Number.isInteger(size) || size < 1) throw new Error('Invalid chunk size');
      
      const chunks = [];
      for (let i = 0; i < array.length; i += size) {
        chunks.push(array.slice(i, i + Math.min(size, array.length - i)));
      }
      return chunks;
    },

    // New: Resource monitoring
    monitorResources() {
      return {
        memory: performance?.memory?.usedJSHeapSize || 0,
        timeOrigin: performance.timeOrigin,
        timing: performance.timing
      };
    }
  };

  /**
   * Enhanced storage handler with comprehensive error handling
   */
  class StorageHandler {
    constructor() {
      this.dbPromise = null;
      this.storageType = this._detectBestStorage();
      this._initializeStorage();
    }

    _detectBestStorage() {
      try {
        if (indexedDB) return 'indexeddb';
        if (localStorage) return 'localstorage';
        return 'memory';
      } catch (e) {
        console.warn('Storage detection failed:', e);
        return 'memory';
      }
    }

    async _initializeStorage() {
      try {
        if (this.storageType === 'indexeddb') {
          this.dbPromise = this.initIndexedDB();
        }
      } catch (e) {
        console.error('Storage initialization failed:', e);
        this.storageType = 'memory';
      }
    }

    async initIndexedDB(dbName = 'ScraperLibDB', storeName = 'scrapedData') {
      return new Promise((resolve, reject) => {
        try {
          const request = indexedDB.open(dbName, 1);
          
          request.onerror = () => reject(new Error('IndexedDB initialization failed'));
          
          request.onupgradeneeded = (event) => {
            const db = event.target.result;
            if (!db.objectStoreNames.contains(storeName)) {
              const store = db.createObjectStore(storeName, { 
                keyPath: 'id',
                autoIncrement: true 
              });
              store.createIndex('url', 'url', { unique: false });
              store.createIndex('timestamp', 'timestamp', { unique: false });
            }
          };
          
          request.onsuccess = (event) => {
            const db = event.target.result;
            db.onerror = (event) => {
              console.error('Database error:', event.target.error);
            };
            resolve(db);
          };
        } catch (e) {
          reject(new Error(`IndexedDB setup failed: ${e.message}`));
        }
      });
    }

    async store(url, data, format, db = null) {
      if (!url || !data) throw new Error('Invalid storage parameters');
      
      const timestamp = Date.now();
      const storeData = {
        url,
        data,
        timestamp,
        format
      };

      try {
        switch (format) {
          case 'indexeddb':
            return await this._storeInIndexedDB(storeData);

          case 'localstorage':
            return await this._storeInLocalStorage(storeData);

          case 'custom':
            return await this._storeInCustomDB(storeData, db);

          default:
            throw new Error(`Unsupported storage format: ${format}`);
        }
      } catch (error) {
        console.error('Storage error:', error);
        throw new Error(`Failed to store data: ${error.message}`);
      }
    }

    async _storeInIndexedDB(storeData) {
      if (!this.dbPromise) {
        this.dbPromise = this.initIndexedDB();
      }
      const idb = await this.dbPromise;
      
      return new Promise((resolve, reject) => {
        try {
          const tx = idb.transaction('scrapedData', 'readwrite');
          const store = tx.objectStore('scrapedData');
          
          const request = store.add(storeData);
          
          request.onsuccess = () => resolve(request.result);
          request.onerror = () => reject(request.error);
          tx.oncomplete = () => resolve(true);
        } catch (e) {
          reject(new Error(`IndexedDB storage failed: ${e.message}`));
        }
      });
    }

    async _storeInLocalStorage(storeData) {
      const key = `scraper-${storeData.url}-${storeData.timestamp}`;
      try {
        localStorage.setItem(key, JSON.stringify(storeData));
        return true;
      } catch (e) {
        if (e.name === 'QuotaExceededError') {
          await this._cleanupStorage();
          localStorage.setItem(key, JSON.stringify(storeData));
          return true;
        }
        throw e;
      }
    }

    async _storeInCustomDB(storeData, db) {
      if (!db || typeof db.save !== 'function') {
        throw new Error('Invalid custom database handler');
      }
      return await db.save(storeData);
    }

    async _cleanupStorage() {
      try {
        const keys = Object.keys(localStorage);
        const scraperKeys = keys.filter(key => key.startsWith('scraper-'));
        
        if (scraperKeys.length > 100) {
          scraperKeys
            .sort((a, b) => {
              const timeA = JSON.parse(localStorage.getItem(a)).timestamp;
              const timeB = JSON.parse(localStorage.getItem(b)).timestamp;
              return timeA - timeB;
            })
            .slice(0, scraperKeys.length - 100)
            .forEach(key => localStorage.removeItem(key));
        }
      } catch (e) {
        console.error('Storage cleanup failed:', e);
      }
    }
  }

  /**
   * Enhanced rate limiter with dynamic adjustment and error handling
   */
  class RateLimiter {
    constructor(requestsPerSecond = 2) {
      if (requestsPerSecond < 0.1 || requestsPerSecond > 50) {
        throw new Error('Invalid requests per second');
      }
      
      this.queue = [];
      this.requestsPerSecond = requestsPerSecond;
      this.lastRequestTime = 0;
      this.responseTimings = [];
      this.maxResponseTimes = 50;
      this.minWaitTime = 100; // Minimum 100ms between requests
    }

    async acquireToken() {
      try {
        const now = Date.now();
        const timeToWait = Math.max(
          this.minWaitTime,
          (1000 / this.requestsPerSecond) - (now - this.lastRequestTime)
        );
        
        await new Promise(resolve => setTimeout(resolve, timeToWait));
        this.lastRequestTime = Date.now();
        
        return true;
      } catch (e) {
        console.error('Rate limiter error:', e);
        throw new Error('Rate limiting failed');
      }
    }

    recordResponseTime(time) {
      if (!Number.isFinite(time) || time < 0) return;
      
      this.responseTimings.push(time);
      if (this.responseTimings.length > this.maxResponseTimes) {
        this.responseTimings.shift();
      }
      this._adjustRate();
    }

    _adjustRate() {
      if (this.responseTimings.length < 10) return;

      try {
        const avgResponseTime = this.responseTimings.reduce((a, b) => a + b, 0) / this.responseTimings.length;
        
        if (avgResponseTime > 2000) {
          this.requestsPerSecond = Math.max(0.1, this.requestsPerSecond * 0.8);
        } else if (avgResponseTime < 500) {
          this.requestsPerSecond = Math.min(10, this.requestsPerSecond * 1.2);
        }
      } catch (e) {
        console.error('Rate adjustment error:', e);
      }
    }
  }

  /**
   * Main ScraperLib class with comprehensive error handling
   */
  class ScraperLib {
    constructor(options = {}) {
      this._validateOptions(options);
      
      this.settings = {
        rateLimitRequests: 2,
        maxConcurrent: 5,
        timeout: 30000,
        retries: 3,
        retryDelay: 1000,
        cacheTime: 3600000,
        outputFormat: 'json',
        useProxy: false,
        proxyUrl: '',
        headers: {},
        preserveFormatting: false,
        validateSelectors: true,
        maxResponseSize: 10 * 1024 * 1024,
        maxRetryDelay: 30000,
        minMemoryLimit: 100 * 1024 * 1024, // 100MB
        ...options
      };

      this._initializeComponents();
    }

    _validateOptions(options) {
      if (options.maxConcurrent && (options.maxConcurrent < 1 || options.maxConcurrent > 20)) {
        throw new Error('Invalid maxConcurrent value (1-20)');
      }
      if (options.timeout && (options.timeout < 1000 || options.timeout > 60000)) {
        throw new Error('Invalid timeout value (1000-60000)');
      }
    }

    _initializeComponents() {
      try {
        this.storage = new StorageHandler();
        this.rateLimiter = new RateLimiter(this.settings.rateLimitRequests);
        this.activeFetches = new Map();
        this.errorHandlers = new Map();
        this.cache = null;

        if (this.settings.useCache) {
          Utils.initCache().then(cache => this.cache = cache);
        }
      } catch (e) {
        console.error('Component initialization failed:', e);
        throw new Error('Failed to initialize scraper components');
      }
    }

    async scrape(configs) {
      if (!configs || typeof configs !== 'object') {
        throw new Error('Invalid configuration object');
      }

      const results = {};
      const urls = Object.keys(configs);
      
      try {
        // Using Utils.chunkArray instead of this._chunkArray
        const chunks = Utils.chunkArray(urls, this.settings.maxConcurrent);

        for (const chunk of chunks) {
          const promises = chunk.map(url => this._processSingleUrl(url, configs[url])
            .then(data => {
              results[url] = data;
              return { url, success: true, data };
            })
            .catch(error => {
              const handler = this.errorHandlers.get(url) || this.errorHandlers.get('default');
              if (handler) {
                return handler(error, url).then(fallbackData => {
                  results[url] = fallbackData;
                  return { url, success: true, data: fallbackData };
                });
              }
              results[url] = { error: error.message };
              return { url, success: false, error: error.message };
            })
          );

          await Promise.all(promises);
          Utils.releaseMemory();

          // Check memory usage
          const resources = Utils.monitorResources();
          if (resources.memory > this.settings.minMemoryLimit) {
            await new Promise(resolve => setTimeout(resolve, 1000));
            Utils.releaseMemory();
          }
        }

        return results;
      } catch (error) {
        console.error('Scraping error:', error);
        throw new Error(`Scraping failed: ${error.message}`);
      }
    }

    // ... [Previous methods remain the same] ...

    destroy() {
      try {
        this.activeFetches.forEach((controller) => {
          if (controller.abort) {
            controller.abort();
          }
        });
        
        this.activeFetches.clear();
        this.cache = null;
        this.storage = null;
        this.errorHandlers.clear();
        Utils.releaseMemory();
      } catch (e) {
        console.error('Cleanup error:', e);
      }
    }
  }

  // Export to global scope
  if (typeof global !== 'undefined') {
    global.ScraperLib = ScraperLib;
  } else if (typeof window !== 'undefined') {
    window.ScraperLib = ScraperLib;
  }

})(typeof window !== 'undefined' ? window : this);

---