{"version":3,"file":"index.mjs","names":[],"sources":["../../src/utils/config.ts","../../src/utils/lazyLoader.ts","../../src/utils/serviceWorker.ts"],"sourcesContent":["import type { CheckoutConfig } from '../types';\n\n/**\n * Default configuration values\n */\nexport const DEFAULT_CONFIG: Partial<CheckoutConfig> = {\n  environment: 'sandbox',\n};\n\n/**\n * Validate configuration object\n */\nexport function validateConfig(config: CheckoutConfig): { isValid: boolean; errors: string[] } {\n  const errors: string[] = [];\n\n  if (!config.merchantId) {\n    errors.push('Merchant ID is required');\n  }\n\n  if (!config.productId) {\n    errors.push('Product ID is required');\n  }\n\n  if (config.environment && !['sandbox', 'production'].includes(config.environment)) {\n    errors.push('Environment must be either \"sandbox\" or \"production\"');\n  }\n\n  if (config.theme) {\n    if (!['light', 'dark'].includes(config.theme.theme)) {\n      errors.push('Theme must be either \"light\" or \"dark\"');\n    }\n\n    if (typeof config.theme.show_product_info !== 'boolean') {\n      errors.push('show_product_info must be a boolean');\n    }\n\n    if (!['left', 'above'].includes(config.theme.product_layout)) {\n      errors.push('product_layout must be either \"left\" or \"above\"');\n    }\n\n    if (typeof config.theme.show_coupon_row !== 'boolean') {\n      errors.push('show_coupon_row must be a boolean');\n    }\n\n    if (\n      config.theme.coupon_row_disclaimer !== undefined &&\n      typeof config.theme.coupon_row_disclaimer !== 'string'\n    ) {\n      errors.push('coupon_row_disclaimer must be a string');\n    }\n\n    if (typeof config.theme.accent_color !== 'string') {\n      errors.push('accent_color must be a string');\n    }\n\n    // Validate hex color format\n    if (!/^#[0-9A-F]{6}$/i.test(config.theme.accent_color)) {\n      errors.push('accent_color must be a valid hex color (e.g., #007bff)');\n    }\n  }\n\n  return {\n    isValid: errors.length === 0,\n    errors\n  };\n}\n\n/**\n * Merge configuration with defaults\n */\nexport function mergeConfig(config: Partial<CheckoutConfig>): CheckoutConfig {\n  const merged = { ...DEFAULT_CONFIG, ...config };\n\n  // Ensure required fields are present\n  if (!merged.merchantId) {\n    throw new Error('Merchant ID is required');\n  }\n\n  if (!merged.productId) {\n    throw new Error('Product ID is required');\n  }\n\n  return merged as CheckoutConfig;\n}\n\n/**\n * Create a minimal configuration object\n */\nexport function createMinimalConfig(merchantId: string, productId: string, checkoutSessionSecret: string, environment?: 'sandbox' | 'production'): CheckoutConfig {\n  return {\n    merchantId,\n    productId,\n    checkoutSessionSecret,\n    environment: environment || 'sandbox'\n  };\n}\n\n/**\n * Check if configuration is complete\n */\nexport function isConfigComplete(config: Partial<CheckoutConfig>): config is CheckoutConfig {\n  return !!(config.merchantId && config.productId && config.checkoutSessionSecret);\n}\n\n/**\n * Get configuration summary for debugging\n */\nexport function getConfigSummary(config: CheckoutConfig): Record<string, any> {\n  return {\n    merchantId: config.merchantId,\n    productId: config.productId,\n    environment: config.environment,\n    hasTheme: !!config.theme,\n    themeDetails: config.theme ? {\n      theme: config.theme.theme,\n      showProductInfo: config.theme.show_product_info,\n      productLayout: config.theme.product_layout,\n      showCouponRow: config.theme.show_coupon_row,\n      couponRowDisclaimer: config.theme.coupon_row_disclaimer,\n      accentColor: config.theme.accent_color\n    } : undefined\n  };\n} ","/**\n * Lazy loading system for dynamic imports and progressive enhancement\n */\n\nexport interface LazyModuleConfig {\n  name: string;\n  importFn: () => Promise<any>;\n  fallback?: any;\n  timeout?: number;\n}\n\nexport class LazyLoader {\n  private modules = new Map<string, LazyModuleConfig>();\n  private loadedModules = new Map<string, any>();\n  private loadingPromises = new Map<string, Promise<any>>();\n\n  /**\n   * Register a lazy-loadable module\n   */\n  register(config: LazyModuleConfig): void {\n    this.modules.set(config.name, config);\n  }\n\n  /**\n   * Load a module dynamically\n   */\n  async load(moduleName: string): Promise<any> {\n    // Return cached module if already loaded\n    if (this.loadedModules.has(moduleName)) {\n      return this.loadedModules.get(moduleName);\n    }\n\n    // Return existing loading promise if already loading\n    if (this.loadingPromises.has(moduleName)) {\n      return this.loadingPromises.get(moduleName);\n    }\n\n    const config = this.modules.get(moduleName);\n    if (!config) {\n      throw new Error(`Module '${moduleName}' not registered`);\n    }\n\n    // Create loading promise with timeout\n    const loadingPromise = this.loadModuleWithTimeout(config);\n    this.loadingPromises.set(moduleName, loadingPromise);\n\n    try {\n      const module = await loadingPromise;\n      this.loadedModules.set(moduleName, module);\n      this.loadingPromises.delete(moduleName);\n      return module;\n    } catch (error) {\n      this.loadingPromises.delete(moduleName);\n      \n      // Return fallback if available\n      if (config.fallback) {\n        console.warn(`Failed to load module '${moduleName}', using fallback`, error);\n        return config.fallback;\n      }\n      \n      throw error;\n    }\n  }\n\n  /**\n   * Load module with timeout handling\n   */\n  private async loadModuleWithTimeout(config: LazyModuleConfig): Promise<any> {\n    const timeout = config.timeout || 10000; // 10 second default\n    \n    const timeoutPromise = new Promise((_, reject) => {\n      setTimeout(() => {\n        reject(new Error(`Module '${config.name}' load timeout after ${timeout}ms`));\n      }, timeout);\n    });\n\n    return Promise.race([config.importFn(), timeoutPromise]);\n  }\n\n  /**\n   * Preload a module in the background\n   */\n  preload(moduleName: string): void {\n    this.load(moduleName).catch(error => {\n      console.warn(`Preload failed for module '${moduleName}'`, error);\n    });\n  }\n\n  /**\n   * Check if a module is loaded\n   */\n  isLoaded(moduleName: string): boolean {\n    return this.loadedModules.has(moduleName);\n  }\n\n  /**\n   * Check if a module is currently loading\n   */\n  isLoading(moduleName: string): boolean {\n    return this.loadingPromises.has(moduleName);\n  }\n\n  /**\n   * Get all loaded module names\n   */\n  getLoadedModules(): string[] {\n    return Array.from(this.loadedModules.keys());\n  }\n\n  /**\n   * Clear all cached modules\n   */\n  clear(): void {\n    this.loadedModules.clear();\n    this.loadingPromises.clear();\n  }\n}\n\n// Global lazy loader instance\nexport const lazyLoader = new LazyLoader();\n\n// Predefined lazy modules\nexport const LAZY_MODULES = {\n  SECURITY: 'security',\n  EMBED: 'embed',\n} as const;\n\n// Register common lazy modules\n\nlazyLoader.register({\n  name: LAZY_MODULES.EMBED,\n  importFn: () => import('../embed/index'),\n  timeout: 3000,\n});\n","/**\n * Service worker for offline support and asset caching\n */\n\nexport interface ServiceWorkerConfig {\n  cacheName: string;\n  version: string;\n  assets: string[];\n  apiEndpoints: string[];\n  offlinePage?: string;\n}\n\nexport class ServiceWorkerManager {\n  private config: ServiceWorkerConfig;\n\n  constructor(config: ServiceWorkerConfig) {\n    this.config = config;\n  }\n\n  /**\n   * Generate service worker script\n   */\n  generateServiceWorkerScript(): string {\n    return `\n// Service Worker for Fanbasis Checkout SDK\n// Version: ${this.config.version}\n\nconst CACHE_NAME = '${this.config.cacheName}-${this.config.version}';\nconst ASSETS = ${JSON.stringify(this.config.assets)};\nconst API_ENDPOINTS = ${JSON.stringify(this.config.apiEndpoints)};\nconst OFFLINE_PAGE = ${JSON.stringify(this.config.offlinePage || '/offline.html')};\n\n// Install event - cache assets\nself.addEventListener('install', (event) => {\n  event.waitUntil(\n    caches.open(CACHE_NAME)\n      .then((cache) => {\n        console.log('Caching assets:', ASSETS);\n        return cache.addAll(ASSETS);\n      })\n      .then(() => {\n        console.log('Service worker installed');\n        return self.skipWaiting();\n      })\n      .catch((error) => {\n        console.error('Service worker install failed:', error);\n      })\n  );\n});\n\n// Activate event - clean up old caches\nself.addEventListener('activate', (event) => {\n  event.waitUntil(\n    caches.keys()\n      .then((cacheNames) => {\n        return Promise.all(\n          cacheNames.map((cacheName) => {\n            if (cacheName !== CACHE_NAME) {\n              console.log('Deleting old cache:', cacheName);\n              return caches.delete(cacheName);\n            }\n          })\n        );\n      })\n      .then(() => {\n        console.log('Service worker activated');\n        return self.clients.claim();\n      })\n  );\n});\n\n// Fetch event - handle requests\nself.addEventListener('fetch', (event) => {\n  const { request } = event;\n  const url = new URL(request.url);\n\n  // Handle API requests with network-first strategy\n  if (API_ENDPOINTS.some(endpoint => url.pathname.includes(endpoint))) {\n    event.respondWith(handleApiRequest(request));\n    return;\n  }\n\n  // Handle asset requests with cache-first strategy\n  if (ASSETS.some(asset => url.pathname.includes(asset))) {\n    event.respondWith(handleAssetRequest(request));\n    return;\n  }\n\n  // Handle other requests with network-first strategy\n  event.respondWith(handleOtherRequest(request));\n});\n\n// API request handler - network first, cache fallback\nasync function handleApiRequest(request) {\n  try {\n    const response = await fetch(request);\n    \n    // Cache successful API responses\n    if (response.ok) {\n      const cache = await caches.open(CACHE_NAME);\n      cache.put(request, response.clone());\n    }\n    \n    return response;\n  } catch (error) {\n    console.log('API request failed, trying cache:', request.url);\n    \n    // Try to get from cache\n    const cachedResponse = await caches.match(request);\n    if (cachedResponse) {\n      return cachedResponse;\n    }\n    \n    // Return offline page if available\n    const offlineResponse = await caches.match(OFFLINE_PAGE);\n    if (offlineResponse) {\n      return offlineResponse;\n    }\n    \n    throw error;\n  }\n}\n\n// Asset request handler - cache first, network fallback\nasync function handleAssetRequest(request) {\n  const cachedResponse = await caches.match(request);\n  if (cachedResponse) {\n    return cachedResponse;\n  }\n\n  try {\n    const response = await fetch(request);\n    if (response.ok) {\n      const cache = await caches.open(CACHE_NAME);\n      cache.put(request, response.clone());\n    }\n    return response;\n  } catch (error) {\n    console.error('Asset request failed:', request.url, error);\n    throw error;\n  }\n}\n\n// Other request handler - network first, cache fallback\nasync function handleOtherRequest(request) {\n  try {\n    const response = await fetch(request);\n    return response;\n  } catch (error) {\n    console.log('Request failed, trying cache:', request.url);\n    \n    const cachedResponse = await caches.match(request);\n    if (cachedResponse) {\n      return cachedResponse;\n    }\n    \n    throw error;\n  }\n}\n\n// Background sync for offline actions\nself.addEventListener('sync', (event) => {\n  if (event.tag === 'background-sync') {\n    event.waitUntil(handleBackgroundSync());\n  }\n});\n\nasync function handleBackgroundSync() {\n  try {\n    // Process any pending offline actions\n    console.log('Processing background sync');\n    \n    // Get pending actions from IndexedDB or other storage\n    // Process them and sync with server\n    \n  } catch (error) {\n    console.error('Background sync failed:', error);\n  }\n}\n\n// Push notification handler\nself.addEventListener('push', (event) => {\n  if (event.data) {\n    const data = event.data.json();\n    \n    event.waitUntil(\n      self.registration.showNotification(data.title, {\n        body: data.body,\n        icon: data.icon,\n        badge: data.badge,\n        data: data.data,\n      })\n    );\n  }\n});\n\n// Notification click handler\nself.addEventListener('notificationclick', (event) => {\n  event.notification.close();\n  \n  event.waitUntil(\n    clients.openWindow(event.notification.data?.url || '/')\n  );\n});\n`;\n  }\n\n  /**\n   * Register service worker\n   */\n  async register(swPath: string): Promise<ServiceWorkerRegistration | null> {\n    if (!('serviceWorker' in navigator)) {\n      console.warn('Service workers not supported');\n      return null;\n    }\n\n    try {\n      const registration = await navigator.serviceWorker.register(swPath);\n      console.log('Service worker registered:', registration);\n      return registration;\n    } catch (error) {\n      console.error('Service worker registration failed:', error);\n      return null;\n    }\n  }\n\n  /**\n   * Unregister service worker\n   */\n  async unregister(): Promise<boolean> {\n    if (!('serviceWorker' in navigator)) {\n      return false;\n    }\n\n    try {\n      const registration = await navigator.serviceWorker.getRegistration();\n      if (registration) {\n        await registration.unregister();\n        console.log('Service worker unregistered');\n        return true;\n      }\n      return false;\n    } catch (error) {\n      console.error('Service worker unregistration failed:', error);\n      return false;\n    }\n  }\n\n  /**\n   * Check if service worker is active\n   */\n  async isActive(): Promise<boolean> {\n    if (!('serviceWorker' in navigator)) {\n      return false;\n    }\n\n    try {\n      const registration = await navigator.serviceWorker.getRegistration();\n      return registration?.active !== undefined;\n    } catch (error) {\n      console.error('Service worker status check failed:', error);\n      return false;\n    }\n  }\n}\n\n/**\n * Default service worker configuration\n */\nexport const DEFAULT_SW_CONFIG: ServiceWorkerConfig = {\n  cacheName: 'fanbasis-checkout',\n  version: '1.0.0',\n  assets: [\n    '/checkout.js',\n    '/checkout.css',\n    '/assets/',\n  ],\n  apiEndpoints: [\n    '/api/checkout',\n    '/api/payments',\n  ],\n  offlinePage: '/offline.html',\n};\n\n// Global service worker manager instance\nexport const swManager = new ServiceWorkerManager(DEFAULT_SW_CONFIG); "],"mappings":"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"}