{"version":3,"file":"NetworkUtils.mjs","sources":["../src/NetworkUtils.ts"],"sourcesContent":["/**\n * @module NetworkUtils\n * @description Utility functions for network operations in the browser, including fetch helpers with authentication, caching, progress, retry, and timeout; network status listeners; and network information utilities. Designed to simplify robust HTTP requests and network state management in web applications.\n * @example\n * ```typescript\n * import { NetworkUtils } from 'houser-js-utils';\n *\n * // Add a network status listener\n * const remove = NetworkUtils.addNetworkStatusListener((online) => console.log('Online:', online));\n * // ...later\n * remove();\n *\n * // Fetch with authentication\n * const res = await NetworkUtils.fetchWithAuth('/api', 'token123');\n *\n * // Fetch with cache\n * const cachedRes = await NetworkUtils.fetchWithCache('/api/data');\n *\n * // Fetch JSON with error handling\n * const data = await NetworkUtils.fetchJson<{foo: string}>('/api/data');\n *\n * // Fetch with progress\n * await NetworkUtils.fetchWithProgress('/file', {}, (progress) => console.log('Progress:', progress));\n *\n * // Fetch with retry and timeout\n * const retryRes = await NetworkUtils.fetchWithRetry('/api', {}, 5, 500);\n * const timeoutRes = await NetworkUtils.fetchWithTimeout('/api', {}, 2000);\n *\n * // Get network speed and type\n * const speed = NetworkUtils.getNetworkSpeed();\n * const type = NetworkUtils.getNetworkType();\n *\n * // Check if online\n * if (NetworkUtils.isOnline()) {\n *   // Do something when online\n * }\n * ```\n */\nexport const NetworkUtils = {\n  /**\n   * Adds a listener for network status changes (online/offline).\n   * @param callback - Callback invoked with `true` if online, `false` if offline.\n   * @returns Function to remove the listener.\n   * @example\n   * ```typescript\n   * const remove = NetworkUtils.addNetworkStatusListener((online) => console.log('Online:', online));\n   * // ...later\n   * remove();\n   * ```\n   */\n  addNetworkStatusListener: (\n    callback: (online: boolean) => void\n  ): (() => void) => {\n    const handleOnline = () => callback(true);\n    const handleOffline = () => callback(false);\n\n    window.addEventListener(\"online\", handleOnline);\n    window.addEventListener(\"offline\", handleOffline);\n\n    return () => {\n      window.removeEventListener(\"online\", handleOnline);\n      window.removeEventListener(\"offline\", handleOffline);\n    };\n  },\n\n  /**\n   * Makes a fetch request with Bearer token authentication.\n   * @param url - The URL to fetch.\n   * @param token - The authentication token.\n   * @param options - Additional fetch options.\n   * @returns The fetch response.\n   * @example\n   * ```typescript\n   * const res = await NetworkUtils.fetchWithAuth('/api', 'token123');\n   * ```\n   */\n  fetchWithAuth: async (\n    url: string,\n    token: string,\n    options: RequestInit = {}\n  ): Promise<Response> => {\n    return fetch(url, {\n      ...options,\n      headers: {\n        ...options.headers,\n        Authorization: `Bearer ${token}`,\n      },\n    });\n  },\n\n  /**\n   * Makes a fetch request with caching in localStorage.\n   * @param url - The URL to fetch.\n   * @param options - Fetch options.\n   * @param cacheTime - Cache time in milliseconds (default 5 minutes).\n   * @returns The fetch response (from cache or network).\n   * @example\n   * ```typescript\n   * const res = await NetworkUtils.fetchWithCache('/api/data');\n   * ```\n   */\n  fetchWithCache: async (\n    url: string,\n    options: RequestInit = {},\n    cacheTime = 5 * 60 * 1000 // 5 minutes\n  ): Promise<Response> => {\n    const cacheKey = `fetch-cache-${url}`;\n    const cached = localStorage.getItem(cacheKey);\n\n    if (cached) {\n      const { data, timestamp } = JSON.parse(cached);\n      if (Date.now() - timestamp < cacheTime) {\n        return new Response(data);\n      }\n    }\n\n    const response = await fetch(url, options);\n    const data = await response.clone().text();\n\n    localStorage.setItem(\n      cacheKey,\n      JSON.stringify({\n        data,\n        timestamp: Date.now(),\n      })\n    );\n\n    return response;\n  },\n\n  /**\n   * Makes a fetch request and parses the response as JSON, with error handling.\n   * @template T\n   * @param url - The URL to fetch.\n   * @param options - Fetch options.\n   * @returns The parsed response data.\n   * @throws {Error} If the response is not OK.\n   * @example\n   * ```typescript\n   * const data = await NetworkUtils.fetchJson<{foo: string}>('/api/data');\n   * ```\n   */\n  fetchJson: async <T>(url: string, options: RequestInit = {}): Promise<T> => {\n    const response = await fetch(url, {\n      ...options,\n      headers: {\n        \"Content-Type\": \"application/json\",\n        ...options.headers,\n      },\n    });\n\n    if (!response.ok) {\n      throw new Error(`HTTP error! status: ${response.status}`);\n    }\n\n    return response.json();\n  },\n\n  /**\n   * Makes a fetch request and tracks download progress.\n   * @param url - The URL to fetch.\n   * @param options - Fetch options.\n   * @param onProgress - Progress callback (0-100).\n   * @returns The fetch response.\n   * @example\n   * ```typescript\n   * await NetworkUtils.fetchWithProgress('/file', {}, (progress) => console.log('Progress:', progress));\n   * ```\n   */\n  fetchWithProgress: async (\n    url: string,\n    options: RequestInit = {},\n    onProgress?: (progress: number) => void\n  ): Promise<Response> => {\n    const response = await fetch(url, options);\n    const contentLength = response.headers.get(\"content-length\");\n    const total = contentLength ? parseInt(contentLength, 10) : 0;\n    let loaded = 0;\n\n    const reader = response.body?.getReader();\n    if (!reader || !response.body) return response;\n\n    const stream = new ReadableStream({\n      start(controller) {\n        function push() {\n          reader!.read().then(({ done, value }) => {\n            if (done) {\n              controller.close();\n              return;\n            }\n\n            loaded += value.length;\n            if (total && onProgress) {\n              onProgress((loaded / total) * 100);\n            }\n\n            controller.enqueue(value);\n            push();\n          });\n        }\n\n        push();\n      },\n    });\n\n    return new Response(stream, {\n      headers: response.headers,\n      status: response.status,\n      statusText: response.statusText,\n    });\n  },\n\n  /**\n   * Makes a fetch request with retry logic on failure.\n   * @param url - The URL to fetch.\n   * @param options - Fetch options.\n   * @param retries - Number of retries (default 3).\n   * @param delay - Delay between retries in milliseconds (default 1000).\n   * @returns The fetch response.\n   * @throws {Error} If all retries fail.\n   * @example\n   * ```typescript\n   * const res = await NetworkUtils.fetchWithRetry('/api', {}, 5, 500);\n   * ```\n   */\n  fetchWithRetry: async (\n    url: string,\n    options: RequestInit = {},\n    retries = 3,\n    delay = 1000\n  ): Promise<Response> => {\n    let lastError: Error;\n\n    for (let i = 0; i < retries; i++) {\n      try {\n        return await fetch(url, options);\n      } catch (error) {\n        lastError = error as Error;\n        if (i < retries - 1) {\n          await new Promise((resolve) => setTimeout(resolve, delay));\n        }\n      }\n    }\n\n    throw lastError!;\n  },\n\n  /**\n   * Makes a fetch request with a timeout.\n   * @param url - The URL to fetch.\n   * @param options - Fetch options.\n   * @param timeout - Request timeout in milliseconds (default 5000).\n   * @returns The fetch response.\n   * @throws {Error} If the request times out.\n   * @example\n   * ```typescript\n   * const res = await NetworkUtils.fetchWithTimeout('/api', {}, 2000);\n   * ```\n   */\n  fetchWithTimeout: async (\n    url: string,\n    options: RequestInit = {},\n    timeout = 5000\n  ): Promise<Response> => {\n    const controller = new AbortController();\n    const { signal } = controller;\n\n    const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n    try {\n      const response = await fetch(url, { ...options, signal });\n      clearTimeout(timeoutId);\n      return response;\n    } catch (error) {\n      clearTimeout(timeoutId);\n      if (error instanceof Error && error.name === \"AbortError\") {\n        throw new Error(\"Request timeout\");\n      }\n      throw error;\n    }\n  },\n\n  /**\n   * Gets the current network speed (downlink) in Mbps, if available.\n   * @returns The network speed in Mbps, or null if not available.\n   * @example\n   * ```typescript\n   * const speed = NetworkUtils.getNetworkSpeed();\n   * console.log('Speed:', speed);\n   * ```\n   */\n  getNetworkSpeed: (): string | null => {\n    if (typeof navigator !== \"undefined\" && \"connection\" in navigator) {\n      const connection = (navigator as any).connection;\n      return connection ? connection.downlink + \" Mbps\" : null;\n    }\n    return null;\n  },\n\n  /**\n   * Gets the current network type (e.g., 'wifi', '4g'), if available.\n   * @returns The network type, or null if not available.\n   * @example\n   * ```typescript\n   * const type = NetworkUtils.getNetworkType();\n   * console.log('Type:', type);\n   * ```\n   */\n  getNetworkType: (): string | null => {\n    if (typeof navigator !== \"undefined\" && \"connection\" in navigator) {\n      const connection = (navigator as any).connection;\n      return connection ? connection.effectiveType : null;\n    }\n    return null;\n  },\n\n  /**\n   * Checks if the network is currently online.\n   * @returns True if the network is online, false otherwise.\n   * @example\n   * ```typescript\n   * if (NetworkUtils.isOnline()) {\n   *   // Do something when online\n   * }\n   * ```\n   */\n  isOnline: (): boolean => {\n    return typeof navigator !== \"undefined\" && navigator.onLine;\n  },\n};\n"],"names":["data"],"mappings":"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"}