{"version":3,"file":"broadcast-sync.mjs","sources":["../../../../src/lib/state/sync/broadcast-sync.ts"],"sourcesContent":["/**\n * @file BroadcastSync\n * @description Multi-tab state synchronization using the BroadcastChannel API\n *\n * This module enables real-time state synchronization across browser tabs/windows,\n * essential for enterprise applications where users may have multiple tabs open.\n *\n * Features:\n * - Selective state sync (only sync what you configure)\n * - Leader election for coordinated operations\n * - Conflict resolution with configurable strategies\n * - Throttled sync to prevent excessive messaging\n * - Graceful degradation for unsupported browsers\n * - DevTools integration for debugging sync events\n *\n * @example\n * ```typescript\n * import { createBroadcastSync } from './sync/broadcast-sync';\n * import { useStore } from './store';\n *\n * const sync = createBroadcastSync(useStore, {\n *   channelName: 'app-state-sync',\n *   syncKeys: ['settings', 'theme', 'locale'],\n *   throttleMs: 100,\n * });\n *\n * sync.start();\n * ```\n */\n\nimport type { StoreApi, UseBoundStore } from 'zustand';\nimport { useEffect, useState } from 'react';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Sync message types\n */\nexport type SyncMessageType =\n  | 'STATE_UPDATE'\n  | 'STATE_REQUEST'\n  | 'STATE_RESPONSE'\n  | 'LEADER_ELECTION'\n  | 'LEADER_ANNOUNCE'\n  | 'TAB_PING'\n  | 'TAB_PONG'\n  | 'HEARTBEAT';\n\n/**\n * Sync message structure\n */\nexport interface SyncMessage<TState = unknown> {\n  type: SyncMessageType;\n  tabId: string;\n  timestamp: number;\n  payload?: {\n    state?: Partial<TState>;\n    keys?: string[];\n    leaderId?: string;\n    priority?: number;\n  };\n}\n\n/**\n * Conflict resolution strategy\n */\nexport type ConflictStrategy = 'last-write-wins' | 'first-write-wins' | 'merge' | 'custom';\n\n/**\n * Sync configuration\n */\nexport interface BroadcastSyncConfig<TState> {\n  /** BroadcastChannel name (should be unique per app) */\n  channelName: string;\n\n  /** State keys to synchronize (empty = sync all) */\n  syncKeys?: (keyof TState)[];\n\n  /** Keys to never sync (e.g., sensitive data) */\n  excludeKeys?: (keyof TState)[];\n\n  /** Throttle sync messages (ms) */\n  throttleMs?: number;\n\n  /** Debounce sync messages (ms) */\n  debounceMs?: number;\n\n  /** Conflict resolution strategy */\n  conflictStrategy?: ConflictStrategy;\n\n  /** Custom conflict resolver */\n  customResolver?: (local: Partial<TState>, remote: Partial<TState>) => Partial<TState>;\n\n  /** Enable leader election */\n  enableLeaderElection?: boolean;\n\n  /** Leader heartbeat interval (ms) */\n  leaderHeartbeatMs?: number;\n\n  /** Log sync events to console (dev only) */\n  debug?: boolean;\n\n  /** Callback when sync state changes */\n  onSyncStateChange?: (state: 'connected' | 'disconnected' | 'leader' | 'follower') => void;\n\n  /** Callback when remote update received */\n  onRemoteUpdate?: (keys: string[], source: string) => void;\n\n  /** Filter function to determine if a state change should be synced */\n  shouldSync?: (prevState: TState, nextState: TState) => boolean;\n}\n\n/**\n * Sync instance interface\n */\nexport interface BroadcastSyncInstance<TState> {\n  /** Start synchronization */\n  start: () => void;\n  /** Stop synchronization */\n  stop: () => void;\n  /** Check if sync is active */\n  isActive: () => boolean;\n  /** Check if this tab is the leader */\n  isLeader: () => boolean;\n  /** Get current tab ID */\n  getTabId: () => string;\n  /** Request full state from other tabs */\n  requestState: () => void;\n  /** Broadcast current state to other tabs */\n  broadcastState: (keys?: (keyof TState)[]) => void;\n  /** Get connected tab count (approximate) */\n  getConnectedTabs: () => number;\n  /** Force this tab to become leader */\n  forceLeader: () => void;\n}\n\n// ============================================================================\n// Utilities\n// ============================================================================\n\n/**\n * Generate unique tab ID\n */\nfunction generateTabId(): string {\n  return `tab-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;\n}\n\n/**\n * Check if BroadcastChannel is supported\n */\nfunction isBroadcastChannelSupported(): boolean {\n  return typeof BroadcastChannel !== 'undefined';\n}\n\n/**\n * Throttle function\n */\nfunction throttle<T extends (...args: never[]) => void>(\n  fn: T,\n  wait: number\n): T & { cancel: () => void } {\n  let lastCall = 0;\n  let timeout: ReturnType<typeof setTimeout> | null = null;\n\n  const throttled = ((...args: Parameters<T>) => {\n    const now = Date.now();\n    const remaining = wait - (now - lastCall);\n\n    if (remaining <= 0) {\n      if (timeout !== null) {\n        clearTimeout(timeout);\n        timeout = null;\n      }\n      lastCall = now;\n      fn(...args);\n    } else {\n      timeout ??= setTimeout(() => {\n        lastCall = Date.now();\n        timeout = null;\n        fn(...args);\n      }, remaining);\n    }\n  }) as T & { cancel: () => void };\n\n  throttled.cancel = (): void => {\n    if (timeout !== null) {\n      clearTimeout(timeout);\n      timeout = null;\n    }\n  };\n\n  return throttled;\n}\n\n/**\n * Debounce function\n */\nfunction debounce<T extends (...args: never[]) => void>(\n  fn: T,\n  wait: number\n): T & { cancel: () => void } {\n  let timeout: ReturnType<typeof setTimeout> | null = null;\n\n  const debounced = ((...args: Parameters<T>) => {\n    if (timeout !== null) {\n      clearTimeout(timeout);\n    }\n    timeout = setTimeout(() => {\n      fn(...args);\n    }, wait);\n  }) as T & { cancel: () => void };\n\n  debounced.cancel = (): void => {\n    if (timeout !== null) {\n      clearTimeout(timeout);\n      timeout = null;\n    }\n  };\n\n  return debounced;\n}\n\n/**\n * Deep merge objects (simple implementation)\n */\nfunction deepMerge<T extends Record<string, unknown>>(target: T, source: Partial<T>): T {\n  const result = { ...target };\n\n  for (const key in source) {\n    if (Object.prototype.hasOwnProperty.call(source, key)) {\n      const sourceValue = source[key];\n      const targetValue = result[key];\n\n      if (\n        sourceValue !== null &&\n        sourceValue !== undefined &&\n        typeof sourceValue === 'object' &&\n        !Array.isArray(sourceValue) &&\n        targetValue !== null &&\n        targetValue !== undefined &&\n        typeof targetValue === 'object' &&\n        !Array.isArray(targetValue)\n      ) {\n        result[key] = deepMerge(\n          targetValue as Record<string, unknown>,\n          sourceValue as Partial<Record<string, unknown>>\n        ) as T[Extract<keyof T, string>];\n      } else {\n        result[key] = sourceValue as T[Extract<keyof T, string>];\n      }\n    }\n  }\n\n  return result;\n}\n\n/**\n * Pick specific keys from an object\n */\nfunction pickKeys<T extends object>(obj: T, keys: (keyof T)[]): Partial<T> {\n  const result: Partial<T> = {};\n  for (const key of keys) {\n    if (key in obj) {\n      result[key] = obj[key];\n    }\n  }\n  return result;\n}\n\n/**\n * Omit specific keys from an object\n */\nfunction omitKeys<T extends object>(obj: T, keys: (keyof T)[]): Partial<T> {\n  const result = { ...obj };\n  for (const key of keys) {\n    delete result[key];\n  }\n  return result;\n}\n\n// ============================================================================\n// Factory\n// ============================================================================\n\n/**\n * Create a BroadcastChannel-based state synchronizer\n *\n * @param store - Zustand store to synchronize\n * @param config - Sync configuration\n * @returns Sync instance with control methods\n *\n * @example\n * ```typescript\n * const sync = createBroadcastSync(useStore, {\n *   channelName: 'app-sync',\n *   syncKeys: ['theme', 'locale', 'settings'],\n *   throttleMs: 100,\n *   debug: process.env.NODE_ENV === 'development',\n * });\n *\n * // Start syncing\n * sync.start();\n *\n * // Check leadership\n * if (sync.isLeader()) {\n *   console.log('This tab is the leader');\n * }\n *\n * // Stop syncing (e.g., on logout)\n * sync.stop();\n * ```\n */\nexport function createBroadcastSync<TState extends object>(\n  store: UseBoundStore<StoreApi<TState>>,\n  config: BroadcastSyncConfig<TState>\n): BroadcastSyncInstance<TState> {\n  const {\n    channelName,\n    syncKeys,\n    excludeKeys = [],\n    throttleMs = 100,\n    debounceMs,\n    conflictStrategy = 'last-write-wins',\n    customResolver,\n    enableLeaderElection = false,\n    leaderHeartbeatMs = 5000,\n    debug = false,\n    onSyncStateChange,\n    onRemoteUpdate,\n    shouldSync,\n  } = config;\n\n  // Internal state\n  const tabId = generateTabId();\n  let channel: BroadcastChannel | null = null;\n  let isStarted = false;\n  let isLeaderTab = false;\n  let lastKnownLeader: string | null = null;\n  let leaderHeartbeatInterval: ReturnType<typeof setInterval> | null = null;\n  const connectedTabs = new Set<string>();\n  let unsubscribe: (() => void) | null = null;\n  // Use atomic counter instead of boolean to handle concurrent/nested updates safely\n  // This prevents the flag from getting stuck if an error occurs during setState\n  let applyingRemoteUpdateCount = 0;\n\n  // Logger - using console.info for debug mode since console.log is not allowed\n  const log = debug\n    ? (...args: unknown[]): void => { console.info(`[BroadcastSync:${tabId.slice(-6)}]`, ...args); }\n    : (): void => { /* noop */ };\n\n  // Get syncable state\n  const getSyncableState = (state: TState): Partial<TState> => {\n    let syncState: Partial<TState> = state;\n\n    if (syncKeys !== undefined && syncKeys.length > 0) {\n      syncState = pickKeys(state, syncKeys);\n    }\n\n    if (excludeKeys.length > 0) {\n      syncState = omitKeys(syncState as TState, excludeKeys);\n    }\n\n    // Filter out functions (actions)\n    const result: Partial<TState> = {};\n    for (const key in syncState) {\n      if (typeof syncState[key] !== 'function') {\n        result[key] = syncState[key];\n      }\n    }\n\n    return result;\n  };\n\n  // Send message\n  const sendMessage = (message: Omit<SyncMessage<TState>, 'tabId' | 'timestamp'>): void => {\n    if (!channel) return;\n\n    const fullMessage: SyncMessage<TState> = {\n      ...message,\n      tabId,\n      timestamp: Date.now(),\n    };\n\n    try {\n      channel.postMessage(fullMessage);\n      log('Sent:', message.type, message.payload);\n    } catch (err) {\n      log('Send error:', err);\n    }\n  };\n\n  // Throttled state broadcast\n  const broadcastStateThrottled = throttle((keys?: (keyof TState)[]): void => {\n    const state = store.getState();\n    let syncState = getSyncableState(state);\n\n    if (keys !== undefined && keys.length > 0) {\n      syncState = pickKeys(syncState as TState, keys);\n    }\n\n    sendMessage({\n      type: 'STATE_UPDATE',\n      payload: {\n        state: syncState,\n        keys: keys ? (keys as string[]) : Object.keys(syncState),\n      },\n    });\n  }, throttleMs);\n\n  // Debounced state broadcast (if configured)\n  const broadcastStateDebounced = debounceMs !== undefined && debounceMs > 0\n    ? debounce((keys?: (keyof TState)[]): void => { broadcastStateThrottled(keys); }, debounceMs)\n    : broadcastStateThrottled;\n\n  // Apply remote state update\n  const applyRemoteState = (remoteState: Partial<TState>, sourceTabId: string): void => {\n    if (Object.keys(remoteState).length === 0) return;\n\n    const currentState = store.getState();\n    let newState: Partial<TState>;\n\n    // Apply conflict resolution\n    switch (conflictStrategy) {\n      case 'first-write-wins':\n        // Only apply keys that don't exist in current state\n        newState = {};\n        for (const key in remoteState) {\n          if (currentState[key as keyof TState] === undefined) {\n            newState[key as keyof TState] = remoteState[key as keyof TState];\n          }\n        }\n        break;\n\n      case 'merge':\n        newState = deepMerge(\n          currentState as Record<string, unknown>,\n          remoteState as Partial<Record<string, unknown>>\n        ) as Partial<TState>;\n        break;\n\n      case 'custom':\n        if (customResolver) {\n          newState = customResolver(currentState, remoteState);\n        } else {\n          newState = remoteState;\n        }\n        break;\n\n      case 'last-write-wins':\n      default:\n        newState = remoteState;\n        break;\n    }\n\n    // Apply update using functional form to work with Immer middleware\n    // Counter prevents re-broadcasting our own changes and handles concurrent updates safely\n    applyingRemoteUpdateCount++;\n    try {\n      store.setState((state) => {\n        for (const key in newState) {\n          if (Object.prototype.hasOwnProperty.call(newState, key)) {\n            (state as Record<string, unknown>)[key] = newState[key as keyof typeof newState];\n          }\n        }\n        return state;\n      });\n      log('Applied remote state from', sourceTabId.slice(-6), Object.keys(newState));\n      onRemoteUpdate?.(Object.keys(newState), sourceTabId);\n    } finally {\n      // Always decrement counter, even if setState throws\n      applyingRemoteUpdateCount--;\n    }\n  };\n\n  // Handle incoming message\n  const handleMessage = (event: MessageEvent<SyncMessage<TState>>): void => {\n    const message = event.data;\n\n    // Ignore invalid messages or own messages\n    if (message === null || message === undefined || typeof message !== 'object' || !('type' in message) || !('tabId' in message)) return;\n    if (message.tabId === tabId) return;\n\n    log('Received:', message.type, 'from', String(message.tabId).slice(-6));\n\n    switch (message.type) {\n      case 'STATE_UPDATE':\n        if (message.payload?.state) {\n          applyRemoteState(message.payload.state, String(message.tabId));\n        }\n        break;\n\n      case 'STATE_REQUEST':\n        // Another tab is requesting state (e.g., just opened)\n        broadcastStateThrottled();\n        break;\n\n      case 'STATE_RESPONSE':\n        if (message.payload?.state) {\n          applyRemoteState(message.payload.state, String(message.tabId));\n        }\n        break;\n\n      case 'LEADER_ELECTION':\n        // Participate in election based on priority (timestamp)\n        if (enableLeaderElection && message.payload?.priority !== undefined && message.payload.priority !== null) {\n          const tabIdParts = tabId.split('-');\n          const myPriority = parseInt(tabIdParts[1] ?? '0', 10);\n          if (myPriority < message.payload.priority) {\n            // I have higher priority (lower timestamp = older tab)\n            sendMessage({\n              type: 'LEADER_ANNOUNCE',\n              payload: { leaderId: tabId },\n            });\n            becomeLeader();\n          }\n        }\n        break;\n\n      case 'LEADER_ANNOUNCE':\n        if (message.payload?.leaderId !== undefined && message.payload.leaderId !== null) {\n          if (message.payload.leaderId !== tabId) {\n            isLeaderTab = false;\n            lastKnownLeader = message.payload.leaderId;\n            onSyncStateChange?.('follower');\n            log('New leader:', String(message.payload.leaderId).slice(-6));\n          }\n        }\n        break;\n\n      case 'TAB_PING':\n        // Respond to tab discovery\n        sendMessage({ type: 'TAB_PONG' });\n        connectedTabs.add(String(message.tabId));\n        break;\n\n      case 'TAB_PONG':\n        connectedTabs.add(String(message.tabId));\n        break;\n\n      case 'HEARTBEAT':\n        connectedTabs.add(String(message.tabId));\n        if (enableLeaderElection && message.payload !== null && message.payload !== undefined && (message.payload as { leaderId?: string }).leaderId === lastKnownLeader) {\n          // Leader is still alive\n        }\n        break;\n    }\n  };\n\n  // Become leader\n  const becomeLeader = (): void => {\n    isLeaderTab = true;\n    lastKnownLeader = tabId;\n    onSyncStateChange?.('leader');\n    log('Became leader');\n\n    // Start heartbeat\n    if (leaderHeartbeatInterval) {\n      clearInterval(leaderHeartbeatInterval);\n    }\n    leaderHeartbeatInterval = setInterval(() => {\n      sendMessage({\n        type: 'HEARTBEAT',\n        payload: { leaderId: tabId },\n      });\n    }, leaderHeartbeatMs);\n  };\n\n  // Subscribe to store changes\n  const subscribeToStore = (): void => {\n    let prevState = store.getState();\n\n    unsubscribe = store.subscribe((state) => {\n      // Don't broadcast if we're applying a remote update (counter > 0 means in progress)\n      if (applyingRemoteUpdateCount > 0) {\n        prevState = state;\n        return;\n      }\n\n      // Check if should sync\n      if (shouldSync !== undefined && !shouldSync(prevState, state)) {\n        prevState = state;\n        return;\n      }\n\n      // Find changed keys\n      const changedKeys: (keyof TState)[] = [];\n      const syncableKeys = syncKeys ?? (Object.keys(state) as (keyof TState)[]);\n\n      for (const key of syncableKeys) {\n        if (excludeKeys.includes(key)) continue;\n        if (typeof state[key] === 'function') continue;\n        if (state[key] !== prevState[key]) {\n          changedKeys.push(key);\n        }\n      }\n\n      if (changedKeys.length > 0) {\n        broadcastStateDebounced(changedKeys);\n      }\n\n      prevState = state;\n    });\n  };\n\n  // ========================================================================\n  // Public API\n  // ========================================================================\n\n  return {\n    start: (): void => {\n      if (isStarted) {\n        log('Already started');\n        return;\n      }\n\n      if (!isBroadcastChannelSupported()) {\n        // BroadcastChannel not supported - silently degrade\n        return;\n      }\n\n      // Create channel\n      channel = new BroadcastChannel(channelName);\n      channel.onmessage = handleMessage;\n\n      // Subscribe to store\n      subscribeToStore();\n\n      isStarted = true;\n      onSyncStateChange?.('connected');\n      log('Started');\n\n      // Discover other tabs\n      sendMessage({ type: 'TAB_PING' });\n\n      // Request current state from other tabs\n      sendMessage({ type: 'STATE_REQUEST' });\n\n      // Start leader election if enabled\n      if (enableLeaderElection) {\n        setTimeout(() => {\n          const tabIdParts = tabId.split('-');\n          const priority = parseInt(tabIdParts[1] ?? '0', 10);\n          sendMessage({\n            type: 'LEADER_ELECTION',\n            payload: { priority },\n          });\n\n          // If no one responds, become leader\n          setTimeout(() => {\n            if (lastKnownLeader === null) {\n              becomeLeader();\n            }\n          }, 500);\n        }, 100);\n      }\n    },\n\n    stop: () => {\n      if (!isStarted) return;\n\n      // Cleanup\n      broadcastStateThrottled.cancel();\n      broadcastStateDebounced.cancel();\n\n      if (unsubscribe) {\n        unsubscribe();\n        unsubscribe = null;\n      }\n\n      if (leaderHeartbeatInterval) {\n        clearInterval(leaderHeartbeatInterval);\n        leaderHeartbeatInterval = null;\n      }\n\n      if (channel) {\n        channel.close();\n        channel = null;\n      }\n\n      isStarted = false;\n      isLeaderTab = false;\n      lastKnownLeader = null;\n      connectedTabs.clear();\n\n      onSyncStateChange?.('disconnected');\n      log('Stopped');\n    },\n\n    isActive: () => isStarted,\n\n    isLeader: () => isLeaderTab,\n\n    getTabId: () => tabId,\n\n    requestState: () => {\n      if (!isStarted) return;\n      sendMessage({ type: 'STATE_REQUEST' });\n    },\n\n    broadcastState: (keys) => {\n      if (!isStarted) return;\n      const state = store.getState();\n      let syncState = getSyncableState(state);\n\n      if (keys && keys.length > 0) {\n        syncState = pickKeys(syncState as TState, keys);\n      }\n\n      sendMessage({\n        type: 'STATE_UPDATE',\n        payload: {\n          state: syncState,\n          keys: keys ? (keys as string[]) : Object.keys(syncState),\n        },\n      });\n    },\n\n    getConnectedTabs: () => connectedTabs.size,\n\n    forceLeader: () => {\n      if (!isStarted || !enableLeaderElection) return;\n      sendMessage({\n        type: 'LEADER_ANNOUNCE',\n        payload: { leaderId: tabId },\n      });\n      becomeLeader();\n    },\n  };\n}\n\n// ============================================================================\n// React Hook\n// ============================================================================\n\n/**\n * React hook for BroadcastSync\n *\n * @example\n * ```typescript\n * function App() {\n *   const sync = useBroadcastSync(useStore, {\n *     channelName: 'app-sync',\n *     syncKeys: ['theme', 'locale'],\n *   });\n *\n *   return (\n *     <div>\n *       <p>Tab ID: {sync.getTabId()}</p>\n *       <p>Is Leader: {sync.isLeader() ? 'Yes' : 'No'}</p>\n *     </div>\n *   );\n * }\n * ```\n */\nexport function useBroadcastSync<TState extends object>(\n  store: UseBoundStore<StoreApi<TState>>,\n  config: BroadcastSyncConfig<TState>\n): BroadcastSyncInstance<TState> {\n  const [sync] = useState(() => createBroadcastSync(store, config));\n\n  useEffect(() => {\n    sync.start();\n\n    return () => {\n      sync.stop();\n    };\n  }, [sync]);\n\n  return sync;\n}\n\n// ============================================================================\n// Exports\n// ============================================================================"],"names":["generateTabId","isBroadcastChannelSupported","throttle","fn","wait","lastCall","timeout","throttled","args","now","remaining","debounce","debounced","deepMerge","target","source","result","key","sourceValue","targetValue","pickKeys","obj","keys","omitKeys","createBroadcastSync","store","config","channelName","syncKeys","excludeKeys","throttleMs","debounceMs","conflictStrategy","customResolver","enableLeaderElection","leaderHeartbeatMs","debug","onSyncStateChange","onRemoteUpdate","shouldSync","tabId","channel","isStarted","isLeaderTab","lastKnownLeader","leaderHeartbeatInterval","connectedTabs","unsubscribe","applyingRemoteUpdateCount","log","getSyncableState","state","syncState","sendMessage","message","fullMessage","err","broadcastStateThrottled","broadcastStateDebounced","applyRemoteState","remoteState","sourceTabId","currentState","newState","handleMessage","event","tabIdParts","becomeLeader","subscribeToStore","prevState","changedKeys","syncableKeys","priority","useBroadcastSync","sync","useState","useEffect"],"mappings":"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"}