{"version":3,"sources":["../../src/utils/cache-helpers.ts","../../src/utils/hash-generator.ts","../../src/strategies/cache-first.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Utility functions for cache operations\n */\n\nimport { FILE_EXTENSION_TO_ASSET_TYPE } from \"../config/constants\";\nimport { AssetType, CacheEntry } from \"../config/types\";\n\n/**\n * Determine the asset type from a URL or content type\n * @param url URL of the asset\n * @param contentType Optional content type\n * @returns Asset type\n */\nexport function determineAssetType(\n  url: string,\n  contentType?: string\n): AssetType {\n  // If content type is provided, use it to determine asset type\n  if (contentType) {\n    if (contentType.startsWith(\"image/\")) {\n      return AssetType.IMAGE;\n    } else if (contentType.startsWith(\"video/\")) {\n      return AssetType.VIDEO;\n    } else if (\n      contentType.startsWith(\"font/\") ||\n      contentType === \"application/font-woff\" ||\n      contentType === \"application/font-woff2\"\n    ) {\n      return AssetType.FONT;\n    } else if (contentType.startsWith(\"audio/\")) {\n      return AssetType.AUDIO;\n    }\n  }\n\n  // Otherwise, determine from URL extension\n  const extension = url.split(\".\").pop()?.toLowerCase();\n  if (!extension) {\n    return AssetType.OTHER;\n  }\n\n  const mimeType = FILE_EXTENSION_TO_ASSET_TYPE[extension];\n  if (!mimeType) {\n    return AssetType.OTHER;\n  }\n\n  if (mimeType.startsWith(\"image/\")) {\n    return AssetType.IMAGE;\n  } else if (mimeType.startsWith(\"video/\")) {\n    return AssetType.VIDEO;\n  } else if (mimeType.startsWith(\"font/\")) {\n    return AssetType.FONT;\n  } else if (mimeType.startsWith(\"audio/\")) {\n    return AssetType.AUDIO;\n  }\n\n  return AssetType.OTHER;\n}\n\n/**\n * Check if an asset should be cached based on its type and size\n * @param url URL of the asset\n * @param size Size of the asset in bytes\n * @param contentType Optional content type\n * @param maxSize Maximum size for cacheable assets\n * @returns Whether the asset should be cached\n */\nexport function shouldCacheAsset(\n  url: string,\n  size: number,\n  contentType?: string,\n  maxSize = Infinity\n): boolean {\n  // Don't cache assets that are too large\n  if (size > maxSize) {\n    return false;\n  }\n\n  // Don't cache assets from external domains unless explicitly allowed\n  try {\n    const urlObj = new URL(url, window.location.origin);\n    if (urlObj.origin !== window.location.origin) {\n      // Check if the domain is in the allowed list\n      // This would be implemented based on your configuration\n      // For now, we'll allow all domains\n      // return isAllowedExternalDomain(urlObj.hostname);\n    }\n  } catch (error) {\n    return false;\n  }\n\n  // Determine asset type\n  const assetType = determineAssetType(url, contentType);\n\n  // Cache based on asset type\n  switch (assetType) {\n    case AssetType.IMAGE:\n    case AssetType.FONT:\n      return true;\n    case AssetType.VIDEO:\n    case AssetType.AUDIO:\n      // Only cache videos and audio if they're not too large\n      return size < maxSize / 2;\n    case AssetType.OTHER:\n    default:\n      // Don't cache other asset types by default\n      return false;\n  }\n}\n\n/**\n * Create a cache entry for an asset\n * @param data Asset data\n * @param url URL of the asset\n * @param contentType Optional content type\n * @param options Cache options\n * @returns Cache entry\n */\nexport function createCacheEntry<T>(\n  data: T,\n  url: string,\n  contentType?: string,\n  options?: {\n    ttl?: number;\n    tags?: string[];\n    version?: string;\n  }\n): CacheEntry<T> {\n  const now = Date.now();\n  const assetType = determineAssetType(url, contentType);\n\n  return {\n    data,\n    timestamp: now,\n    expires: options?.ttl ? now + options.ttl * 1000 : undefined,\n    type: assetType,\n    tags: options?.tags,\n    version: options?.version,\n  };\n}\n\n/**\n * Check if a cache entry has expired\n * @param entry Cache entry\n * @returns Whether the entry has expired\n */\nexport function hasExpired(entry: CacheEntry<any>): boolean {\n  return !!entry.expires && entry.expires < Date.now();\n}\n\n/**\n * Format a size in bytes to a human-readable string\n * @param bytes Size in bytes\n * @returns Human-readable size string\n */\nexport function formatSize(bytes: number): string {\n  if (bytes < 1024) {\n    return `${bytes} B`;\n  } else if (bytes < 1024 * 1024) {\n    return `${(bytes / 1024).toFixed(2)} KB`;\n  } else if (bytes < 1024 * 1024 * 1024) {\n    return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;\n  } else {\n    return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;\n  }\n}\n\n/**\n * Log a debug message if debug mode is enabled\n * @param message Message to log\n * @param args Additional arguments\n */\nexport function debugLog(message: string, ...args: any[]): void {\n  // Simple debug logging - can be controlled by the configuration\n  // In development, you can enable it via localStorage for debugging\n  if (\n    typeof localStorage !== \"undefined\" &&\n    localStorage.getItem(\"DEBUG_ASSET_CACHE\") === \"true\"\n  ) {\n    console.log(`[AssetCache] ${message}`, ...args);\n  }\n}\n","/**\n * Utility functions for generating content hashes\n */\n\n/**\n * Generate a simple hash from a string\n * @param str String to hash\n * @returns Hash string\n */\nexport function generateSimpleHash(str: string): string {\n  let hash = 0;\n  if (str.length === 0) return hash.toString(16);\n\n  for (let i = 0; i < str.length; i++) {\n    const char = str.charCodeAt(i);\n    hash = (hash << 5) - hash + char;\n    hash = hash & hash; // Convert to 32bit integer\n  }\n\n  return Math.abs(hash).toString(16);\n}\n\n/**\n * Generate a content hash from a Blob or File\n * @param content Blob or File to hash\n * @returns Promise resolving to hash string\n */\nexport async function generateContentHash(content: Blob): Promise<string> {\n  // Use the Web Crypto API if available\n  if (typeof crypto !== \"undefined\" && crypto.subtle) {\n    try {\n      const buffer = await content.arrayBuffer();\n      const hashBuffer = await crypto.subtle.digest(\"SHA-256\", buffer);\n      const hashArray = Array.from(new Uint8Array(hashBuffer));\n      const hashHex = hashArray\n        .map((b) => b.toString(16).padStart(2, \"0\"))\n        .join(\"\");\n      return hashHex;\n    } catch (error) {\n      console.error(\"Error generating content hash:\", error);\n      // Fall back to simple hash\n      return generateSimpleHashFromBlob(content);\n    }\n  }\n\n  // Fall back to simple hash if Web Crypto API is not available\n  return generateSimpleHashFromBlob(content);\n}\n\n/**\n * Generate a simple hash from a Blob or File\n * @param content Blob or File to hash\n * @returns Promise resolving to hash string\n */\nasync function generateSimpleHashFromBlob(content: Blob): Promise<string> {\n  return new Promise((resolve) => {\n    const reader = new FileReader();\n    reader.onload = () => {\n      const result = reader.result as string;\n      resolve(generateSimpleHash(result));\n    };\n    reader.readAsText(content);\n  });\n}\n\n/**\n * Generate a version string for a URL\n * @param url URL to version\n * @param version Version string\n * @returns Versioned URL\n */\nexport function addVersionToUrl(url: string, version: string): string {\n  const urlObj = new URL(url, window.location.origin);\n  urlObj.searchParams.set(\"v\", version);\n  return urlObj.toString();\n}\n\n/**\n * Extract version from a URL\n * @param url URL with version\n * @returns Version string or null if not found\n */\nexport function extractVersionFromUrl(url: string): string | null {\n  try {\n    const urlObj = new URL(url, window.location.origin);\n    return urlObj.searchParams.get(\"v\");\n  } catch (error) {\n    return null;\n  }\n}\n\n/**\n * Generate a cache key from a URL\n * @param url URL to generate key for\n * @returns Cache key\n */\nexport function generateCacheKey(url: string): string {\n  // Remove version parameter if present\n  const urlObj = new URL(url, window.location.origin);\n  urlObj.searchParams.delete(\"v\");\n  return urlObj.toString();\n}\n","/**\n * Cache-first strategy implementation\n *\n * This strategy:\n * 1. Tries to get the resource from the cache first\n * 2. If not in cache, fetches from network and caches the response\n * 3. Returns the cached or network response\n *\n * Best for: Static assets that rarely change (images, fonts)\n */\nimport { debugLog } from \"../utils/cache-helpers\";\nimport { generateCacheKey } from \"../utils/hash-generator\";\n\n/**\n * Cache-first strategy for fetch requests\n * @param request The request to handle\n * @param cacheName The name of the cache to use\n * @returns Promise resolving to the response\n */\nexport async function cacheFirstStrategy(\n  request: Request,\n  cacheName: string\n): Promise<Response> {\n  const cacheKey = generateCacheKey(request.url);\n\n  try {\n    // Try to get from cache first\n    const cache = await caches.open(cacheName);\n    const cachedResponse = await cache.match(cacheKey);\n\n    if (cachedResponse) {\n      debugLog(`Cache hit for ${request.url}`);\n      return cachedResponse;\n    }\n\n    // Not in cache, fetch from network\n    debugLog(`Cache miss for ${request.url}, fetching from network`);\n    const networkResponse = await fetch(request);\n\n    // Cache the response if it's valid\n    if (networkResponse.ok && networkResponse.status < 400) {\n      // Clone the response before caching it\n      const responseToCache = networkResponse.clone();\n      cache.put(cacheKey, responseToCache);\n      debugLog(`Cached response for ${request.url}`);\n    }\n\n    return networkResponse;\n  } catch (error) {\n    debugLog(`Error in cache-first strategy for ${request.url}:`, error);\n    // If both cache and network fail, throw an error\n    throw error;\n  }\n}\n\n/**\n * Cache-first strategy for the Cache API\n * @param cache The cache object\n * @param url The URL to fetch\n * @param fetchOptions Options for the fetch request\n * @returns Promise resolving to the response\n */\nexport async function cacheFirstWithCacheAPI(\n  cache: Cache,\n  url: string,\n  fetchOptions?: RequestInit\n): Promise<Response> {\n  const cacheKey = generateCacheKey(url);\n\n  try {\n    // Try to get from cache first\n    const cachedResponse = await cache.match(cacheKey);\n\n    if (cachedResponse) {\n      debugLog(`Cache hit for ${url}`);\n      return cachedResponse;\n    }\n\n    // Not in cache, fetch from network\n    debugLog(`Cache miss for ${url}, fetching from network`);\n    const networkResponse = await fetch(url, fetchOptions);\n\n    // Cache the response if it's valid\n    if (networkResponse.ok && networkResponse.status < 400) {\n      // Clone the response before caching it\n      const responseToCache = networkResponse.clone();\n      cache.put(cacheKey, responseToCache);\n      debugLog(`Cached response for ${url}`);\n    }\n\n    return networkResponse;\n  } catch (error) {\n    debugLog(`Error in cache-first strategy for ${url}:`, error);\n    // If both cache and network fail, throw an error\n    throw error;\n  }\n}\n"]}