{"version":3,"sources":["/Users/gwakko/Projects/shared-websocket/dist/vue.cjs","../src/adapters/vue/plugin.ts","../src/adapters/vue/auth.ts","../src/adapters/vue/events.ts","../src/adapters/vue/state.ts","../src/adapters/vue/status.ts","../src/adapters/vue/subscriptions.ts"],"names":["ref","onUnmounted","readonly"],"mappings":"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","file":"/Users/gwakko/Projects/shared-websocket/dist/vue.cjs","sourcesContent":[null,"import { inject, type InjectionKey, type App } from 'vue';\nimport { SharedWebSocket } from '../../SharedWebSocket';\nimport type { SharedWebSocketOptions } from '../../types';\n\n// ─── Plugin ──────────────────────────────────────────────\n\nexport const SharedWebSocketKey: InjectionKey<SharedWebSocket> = Symbol('SharedWebSocket');\n\n/**\n * Vue 3 plugin for SharedWebSocket.\n *\n * @example\n * const app = createApp(App);\n * app.use(createSharedWebSocketPlugin('wss://api.example.com/ws', {\n *   auth: () => localStorage.getItem('token')!,\n *   useWorker: true,\n * }));\n */\nexport function createSharedWebSocketPlugin(url: string, options?: SharedWebSocketOptions) {\n  return {\n    install(app: App) {\n      const socket = new SharedWebSocket(url, options);\n      void socket.connect();\n      app.provide(SharedWebSocketKey, socket);\n\n      const originalUnmount = app.unmount.bind(app);\n      app.unmount = () => {\n        socket[Symbol.dispose]();\n        originalUnmount();\n      };\n    },\n  };\n}\n\n/**\n * Access the SharedWebSocket instance from provided context.\n *\n * @example\n * const ws = useSharedWebSocket();\n * ws.send('chat.message', { text: 'Hello' });\n */\nexport function useSharedWebSocket(): SharedWebSocket {\n  const socket = inject(SharedWebSocketKey);\n  if (!socket) {\n    throw new Error('useSharedWebSocket: SharedWebSocket not provided. Did you install the plugin?');\n  }\n  return socket;\n}\n","import { ref, onUnmounted, readonly, type Ref } from 'vue';\nimport { useSharedWebSocket } from './plugin';\n\n/**\n * Reactive auth state with authenticate/deauthenticate actions.\n * Syncs across all tabs.\n *\n * @example\n * const { isAuthenticated, authenticate, deauthenticate } = useSocketAuth();\n *\n * async function login(email: string, password: string) {\n *   const { token } = await api.login(email, password);\n *   authenticate(token);\n * }\n *\n * @example\n * // In template: <button v-if=\"isAuthenticated\" @click=\"deauthenticate\">Logout</button>\n */\nexport function useSocketAuth(): {\n  isAuthenticated: Ref<boolean>;\n  authenticate: (token: string) => void;\n  deauthenticate: () => void;\n} {\n  const socket = useSharedWebSocket();\n  const isAuthenticated = ref(socket.isAuthenticated);\n\n  const unsub = socket.onAuthChange((authenticated: boolean) => {\n    isAuthenticated.value = authenticated;\n  });\n\n  onUnmounted(unsub);\n\n  return {\n    isAuthenticated: readonly(isAuthenticated) as Ref<boolean>,\n    authenticate: (token: string) => socket.authenticate(token),\n    deauthenticate: () => socket.deauthenticate(),\n  };\n}\n","import { ref, onUnmounted, readonly, type Ref } from 'vue';\nimport { useSharedWebSocket } from './plugin';\n\n/**\n * Subscribe to a WebSocket event.\n * - Without callback: returns reactive ref with latest value.\n * - With callback: calls your handler on each event.\n *\n * @example\n * // Reactive state\n * const order = useSocketEvent<Order>('order.created');\n *\n * @example\n * // Custom callback\n * useSocketEvent<Order>('order.created', (order) => {\n *   playSound('new-order');\n *   analytics.track('order_received', order);\n * });\n */\nexport function useSocketEvent<T>(event: string, callback?: (data: T, raw?: unknown) => void): Ref<T | undefined> {\n  const socket = useSharedWebSocket();\n  const value = ref<T | undefined>(undefined) as Ref<T | undefined>;\n\n  const handler = (data: unknown, raw?: unknown) => {\n    const typed = data as T;\n    if (callback) {\n      callback(typed, raw);\n    } else {\n      value.value = typed;\n    }\n  };\n  const unsub = socket.on(event, handler);\n\n  onUnmounted(unsub);\n  return readonly(value) as Ref<T | undefined>;\n}\n\n/**\n * Accumulate WebSocket events.\n * - Without callback: returns reactive array.\n * - With callback: calls your handler — manage your own state.\n *\n * @example\n * // Default accumulation\n * const messages = useSocketStream<ChatMessage>('chat.message');\n *\n * @example\n * // Custom — keep last 50\n * const messages = ref<ChatMessage[]>([]);\n * useSocketStream<ChatMessage>('chat.message', (msg) => {\n *   messages.value = [msg, ...messages.value].slice(0, 50);\n * });\n *\n * @example\n * // Custom — filter by type\n * const errors = ref<LogEntry[]>([]);\n * useSocketStream<LogEntry>('log.entry', (entry) => {\n *   if (entry.level === 'error') errors.value = [...errors.value, entry];\n * });\n */\nexport function useSocketStream<T>(event: string, callback?: (data: T, raw?: unknown) => void): Ref<T[]> {\n  const socket = useSharedWebSocket();\n  const items = ref<T[]>([]) as Ref<T[]>;\n\n  const handler = (data: unknown, raw?: unknown) => {\n    const typed = data as T;\n    if (callback) {\n      callback(typed, raw);\n    } else {\n      items.value = [...items.value, typed];\n    }\n  };\n  const unsub = socket.on(event, handler);\n\n  onUnmounted(unsub);\n  return readonly(items) as Ref<T[]>;\n}\n\n/**\n * Fire-and-forget event handler — no state, no ref.\n *\n * @example\n * useSocketCallback<Notification>('notification', (n) => {\n *   showToast(n.title);\n * });\n */\nexport function useSocketCallback<T>(event: string, callback: (data: T, raw?: unknown) => void): void {\n  const socket = useSharedWebSocket();\n\n  const unsub = socket.on(event, (data: unknown, raw?: unknown) => {\n    callback(data as T, raw);\n  });\n\n  onUnmounted(unsub);\n}\n","import { ref, onUnmounted, watch, type Ref } from 'vue';\nimport { useSharedWebSocket } from './plugin';\n\n/**\n * Two-way state sync across browser tabs.\n * - Without callback: reactive ref synced across tabs.\n * - With callback: called when any tab updates this key — side effects.\n *\n * @example\n * // Reactive two-way sync\n * const cart = useSocketSync<Cart>('cart', { items: [] });\n * cart.value = { items: [1, 2, 3] }; // syncs to all tabs\n *\n * @example\n * // With side effect callback\n * const cart = useSocketSync<Cart>('cart', { items: [] }, (cart) => {\n *   document.title = `Cart (${cart.items.length})`;\n *   analytics.track('cart_updated');\n * });\n */\nexport function useSocketSync<T>(key: string, initialValue: T, callback?: (value: T) => void): Ref<T> {\n  const socket = useSharedWebSocket();\n  const value = ref<T>(socket.getSync<T>(key) ?? initialValue) as Ref<T>;\n\n  const unsub = socket.onSync<T>(key, (v) => {\n    value.value = v;\n    callback?.(v);\n  });\n\n  watch(\n    value,\n    (newVal) => {\n      socket.sync(key, newVal);\n    },\n    { deep: true },\n  );\n\n  onUnmounted(unsub);\n  return value;\n}\n","import { ref, onUnmounted, readonly, type Ref } from 'vue';\nimport { useSharedWebSocket } from './plugin';\nimport type { TabRole, SocketLifecycleHandlers } from '../../types';\n\n/**\n * Reactive connection status.\n *\n * @example\n * const { connected, tabRole } = useSocketStatus();\n */\nexport function useSocketStatus(): {\n  connected: Ref<boolean>;\n  tabRole: Ref<TabRole>;\n  isAuthenticated: Ref<boolean>;\n} {\n  const socket = useSharedWebSocket();\n  const connected = ref(socket.connected);\n  const tabRole = ref<TabRole>(socket.tabRole);\n  const isAuthenticated = ref(socket.isAuthenticated);\n\n  const timer = setInterval(() => {\n    connected.value = socket.connected;\n    tabRole.value = socket.tabRole;\n    isAuthenticated.value = socket.isAuthenticated;\n  }, 1000);\n\n  onUnmounted(() => clearInterval(timer));\n\n  return {\n    connected: readonly(connected) as Ref<boolean>,\n    tabRole: readonly(tabRole) as Ref<TabRole>,\n    isAuthenticated: readonly(isAuthenticated) as Ref<boolean>,\n  };\n}\n\n/**\n * Lifecycle hooks — react to connection state changes.\n *\n * @example\n * useSocketLifecycle({\n *   onConnect: () => console.log('Connected!'),\n *   onDisconnect: () => showOfflineBanner(),\n *   onReconnecting: () => showSpinner(),\n *   onLeaderChange: (isLeader) => console.log('Leader:', isLeader),\n *   onError: (err) => reportError(err),\n * });\n */\nexport function useSocketLifecycle(handlers: SocketLifecycleHandlers): void {\n  const socket = useSharedWebSocket();\n  const unsubs: (() => void)[] = [];\n\n  if (handlers.onConnect) unsubs.push(socket.onConnect(handlers.onConnect));\n  if (handlers.onDisconnect) unsubs.push(socket.onDisconnect(handlers.onDisconnect));\n  if (handlers.onReconnecting) unsubs.push(socket.onReconnecting(handlers.onReconnecting));\n  if (handlers.onReconnectFailed) unsubs.push(socket.onReconnectFailed(handlers.onReconnectFailed));\n  if (handlers.onLeaderChange) unsubs.push(socket.onLeaderChange(handlers.onLeaderChange));\n  if (handlers.onError) unsubs.push(socket.onError(handlers.onError));\n  if (handlers.onActive) unsubs.push(socket.onActive(handlers.onActive));\n  if (handlers.onInactive) unsubs.push(socket.onInactive(handlers.onInactive));\n  if (handlers.onVisibilityChange) unsubs.push(socket.onVisibilityChange(handlers.onVisibilityChange));\n  if (handlers.onAuthChange) unsubs.push(socket.onAuthChange(handlers.onAuthChange));\n\n  onUnmounted(() => unsubs.forEach((u) => u()));\n}\n\n/**\n * Reactive reconnect state with a manual `reconnect` action. Use this to\n * power a \"Reconnect\" snackbar/banner after auto-reconnect gives up.\n *\n * `hasFailed` flips to `true` once `reconnectMaxRetries` are exhausted, and\n * back to `false` once the connection succeeds or the user calls `reconnect()`.\n *\n * @example\n * <script setup>\n * const { hasFailed, reconnect } = useSocketReconnect();\n * </script>\n *\n * <template>\n *   <div v-if=\"hasFailed\" class=\"snackbar\">\n *     Connection lost.\n *     <button @click=\"reconnect\">Reconnect</button>\n *   </div>\n * </template>\n */\nexport function useSocketReconnect(): {\n  hasFailed: Ref<boolean>;\n  reconnect: () => void;\n} {\n  const socket = useSharedWebSocket();\n  const hasFailed = ref(false);\n\n  const unsubs = [\n    socket.onReconnectFailed(() => {\n      hasFailed.value = true;\n    }),\n    socket.onConnect(() => {\n      hasFailed.value = false;\n    }),\n  ];\n\n  onUnmounted(() => unsubs.forEach((u) => u()));\n\n  return {\n    hasFailed: readonly(hasFailed) as Ref<boolean>,\n    reconnect: () => {\n      hasFailed.value = false;\n      socket.reconnect();\n    },\n  };\n}\n","import { onUnmounted } from 'vue';\nimport { useSharedWebSocket } from './plugin';\n\n/**\n * Subscribe to a private channel. Auto-joins on mount, leaves on unmount.\n *\n * @example\n * const chat = useChannel('chat:room_123');\n * // Listen via useSocketEvent('chat:room_123:message')\n * // Send via chat.send('message', { text: 'Hello' })\n */\nexport function useChannel(name: string, options?: { auth?: boolean }) {\n  const socket = useSharedWebSocket();\n  const channel = socket.channel(name, options);\n\n  onUnmounted(() => channel.leave());\n\n  return channel;\n}\n\n/**\n * Subscribe to server-side topics. Auto-unsubscribes on unmount.\n *\n * @example\n * useTopics(['notifications:orders', 'notifications:payments']);\n */\nexport function useTopics(topics: string[], options?: { auth?: boolean }): void {\n  const socket = useSharedWebSocket();\n\n  topics.forEach((t) => socket.subscribe(t, options));\n\n  onUnmounted(() => {\n    topics.forEach((t) => socket.unsubscribe(t));\n  });\n}\n\n/**\n * Enable browser push notifications for an event. Auto-cleanup on unmount.\n *\n * @example\n * usePush('notification', {\n *   title: (n) => n.title,\n *   body: (n) => n.body,\n *   icon: '/icon.png',\n * });\n */\nexport function usePush<T = unknown>(\n  event: string,\n  config: {\n    title: string | ((data: T) => string);\n    body?: string | ((data: T) => string);\n    icon?: string;\n    tag?: string | ((data: T) => string);\n    leaderOnly?: boolean;\n    onlyWhenHidden?: boolean;\n    onClick?: (data: T) => void;\n  },\n): void {\n  const socket = useSharedWebSocket();\n  const unsub = socket.push<T>(event, config);\n\n  onUnmounted(unsub);\n}\n"]}