@vira-ui/react 13.0.0 → 14.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -11,7 +11,7 @@ interface UseViraStateOptions<T = any> {
11
11
  onError?: (error: Error) => void;
12
12
  /** Use deep merge for diff patches (default: true) */
13
13
  deepMerge?: boolean;
14
- /** API URL (defaults to VITE_API_URL env or 'http://45.90.35.155') */
14
+ /** API URL (defaults to VITE_API_URL env or current origin with /ws path) */
15
15
  apiUrl?: string;
16
16
  /** Auth token for handshake */
17
17
  authToken?: string;
package/dist/index.d.ts CHANGED
@@ -11,7 +11,7 @@ interface UseViraStateOptions<T = any> {
11
11
  onError?: (error: Error) => void;
12
12
  /** Use deep merge for diff patches (default: true) */
13
13
  deepMerge?: boolean;
14
- /** API URL (defaults to VITE_API_URL env or 'http://45.90.35.155') */
14
+ /** API URL (defaults to VITE_API_URL env or current origin with /ws path) */
15
15
  apiUrl?: string;
16
16
  /** Auth token for handshake */
17
17
  authToken?: string;
package/dist/index.js CHANGED
@@ -70,23 +70,11 @@ function useViraState(channel, initialOrOptions) {
70
70
  }
71
71
  }
72
72
  if (!url) {
73
- url = "http://45.90.35.155";
74
- }
75
- if (typeof window !== "undefined" && window.location) {
76
- const isHttps = window.location.protocol === "https:";
77
- if (isHttps) {
78
- if (url.startsWith("ws://")) {
79
- url = url.replace("ws://", "wss://");
80
- } else if (url.startsWith("http://")) {
81
- url = url.replace("http://", "https://");
82
- }
83
- } else {
84
- if (url.startsWith("wss://")) {
85
- url = url.replace("wss://", "ws://");
86
- } else if (url.startsWith("https://")) {
87
- url = url.replace("https://", "http://");
88
- }
73
+ if (typeof window !== "undefined" && window.location) {
74
+ const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
75
+ return `${protocol}//${window.location.host}/ws`;
89
76
  }
77
+ return "/ws";
90
78
  }
91
79
  return url;
92
80
  }, [apiUrlOption]);
@@ -317,40 +305,33 @@ function useViraState(channel, initialOrOptions) {
317
305
  (patch, msgId) => {
318
306
  if (patch && typeof patch === "object") {
319
307
  const patchObj = patch;
320
- const keysToDelete = [];
321
- for (const key in patchObj) {
322
- if (patchObj[key] === null || patchObj[key] === void 0) {
323
- keysToDelete.push(key);
324
- }
325
- }
326
- if (keysToDelete.length > 0) {
308
+ const patchKeys = Object.keys(patchObj);
309
+ if (patchKeys.length > 0) {
327
310
  if (debug) {
328
- console.log("[VRP_CLIENT] Optimistic deletion:", { channel, keysToDelete });
311
+ console.log("[VRP_CLIENT] Optimistic sendDiff:", { channel, patchKeys });
329
312
  }
330
313
  setData((prev) => {
331
- if (!prev || typeof prev !== "object") return prev;
332
- const prevObj = prev;
333
- const result = {};
334
- for (const key in prevObj) {
335
- if (!keysToDelete.includes(key)) {
336
- result[key] = prevObj[key];
337
- }
338
- }
314
+ const prevObj = prev && typeof prev === "object" ? prev : {};
315
+ const result = { ...prevObj };
339
316
  for (const key in patchObj) {
340
- if (!keysToDelete.includes(key) && patchObj[key] !== null && patchObj[key] !== void 0) {
317
+ if (patchObj[key] === null || patchObj[key] === void 0) {
318
+ delete result[key];
319
+ } else if (result[key] && typeof result[key] === "object" && !Array.isArray(result[key]) && typeof patchObj[key] === "object" && !Array.isArray(patchObj[key])) {
320
+ result[key] = { ...result[key], ...patchObj[key] };
321
+ } else {
341
322
  result[key] = patchObj[key];
342
323
  }
343
324
  }
344
325
  if (debug) {
345
- console.log("[VRP_CLIENT] Optimistic deletion result:", { channel, resultCount: Object.keys(result).length, prevCount: Object.keys(prevObj).length });
326
+ console.log("[VRP_CLIENT] Optimistic sendDiff result:", { channel, resultCount: Object.keys(result).length });
346
327
  }
347
- return { ...result };
328
+ return result;
348
329
  });
349
330
  }
350
331
  }
351
332
  transportRef.current?.sendDiff(patch, msgId ?? generateMsgId());
352
333
  },
353
- [generateMsgId, channel]
334
+ [generateMsgId, channel, debug]
354
335
  );
355
336
  return {
356
337
  data,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/useViraState.ts"],"sourcesContent":["/**\n * @vira-ui/react\n * \n * Vira Framework - React hooks for Vira Reactive Protocol\n * \n * This package provides React hooks for VRP, built on top of @vira-ui/core.\n */\n\nexport {\n useViraState,\n useViraStream,\n} from './useViraState';\nexport type {\n UseViraStateOptions,\n} from './useViraState';\n\n","import { useEffect, useMemo, useRef, useState, useCallback } from 'react';\nimport {\n createViraClient,\n deepMerge,\n getViraConnectionPool,\n type ViraClient,\n type Message,\n type ViraConnectionPool,\n type ViraPoolStatus,\n} from '@vira-ui/core';\n\nexport interface UseViraStateOptions<T = any> {\n /** Initial value for the state */\n initial?: T | null;\n /** Enable msgId support for idempotency */\n enableMsgId?: boolean;\n /** Callback when connection opens */\n onOpen?: () => void;\n /** Callback when connection closes */\n onClose?: (event: CloseEvent) => void;\n /** Callback when connection error occurs */\n onError?: (error: Error) => void;\n /** Use deep merge for diff patches (default: true) */\n deepMerge?: boolean;\n /** API URL (defaults to VITE_API_URL env or 'http://45.90.35.155') */\n apiUrl?: string;\n /** Auth token for handshake */\n authToken?: string;\n /** Additional data to send in handshake (e.g., company_id, location_id) */\n handshakeData?: Record<string, any>;\n /** Disable connection pooling (fallback to 1 WS per channel). Default: false */\n disablePooling?: boolean;\n /** Enable debug logs for VRP (console.debug). Default: env VITE_VRP_DEBUG === 'true' */\n debug?: boolean;\n}\n\n/**\n * Unified hook for Vira Reactive Protocol state management.\n * Replaces both useViraState and useViraStream.\n *\n * @example\n * ```tsx\n * // Basic usage\n * const { data, sendUpdate } = useViraState<MyType>('my-channel');\n *\n * // With options\n * const { data, sendUpdate, sendDiff } = useViraState<User>('user:123', {\n * initial: { name: 'Guest' },\n * enableMsgId: true,\n * onOpen: () => console.log('Connected'),\n * deepMerge: true\n * });\n * ```\n */\nexport function useViraState<T = any, C extends string = string>(\n channel: C,\n initialOrOptions?: T | null | UseViraStateOptions<T>\n): {\n /** Current state data */\n data: T | null;\n /** Send an event to the server */\n sendEvent: (name: string, payload: any, msgId?: string) => void;\n /** Send a full update (replaces state) */\n sendUpdate: (payload: T, msgId?: string) => void;\n /** Send a partial diff (merges with current state) */\n sendDiff: (patch: Partial<T>, msgId?: string) => void;\n /** Connection status */\n isConnected: boolean;\n /** Connection error, if any */\n error: Error | null;\n} {\n // Parse options (backward compatibility: second param can be initial value or options)\n const options: UseViraStateOptions<T> = useMemo(() => {\n if (initialOrOptions === null || initialOrOptions === undefined) {\n return {};\n }\n // If it's an object with known option keys, treat as options\n if (\n typeof initialOrOptions === 'object' &&\n !Array.isArray(initialOrOptions) &&\n ('enableMsgId' in initialOrOptions ||\n 'onOpen' in initialOrOptions ||\n 'onClose' in initialOrOptions ||\n 'onError' in initialOrOptions ||\n 'deepMerge' in initialOrOptions ||\n 'initial' in initialOrOptions ||\n 'apiUrl' in initialOrOptions ||\n 'authToken' in initialOrOptions ||\n 'handshakeData' in initialOrOptions ||\n 'disablePooling' in initialOrOptions ||\n 'debug' in initialOrOptions)\n ) {\n return initialOrOptions as UseViraStateOptions<T>;\n }\n // Otherwise, treat as initial value (backward compatibility)\n return { initial: initialOrOptions as T | null };\n }, [initialOrOptions]);\n\n const {\n initial = null,\n enableMsgId = false,\n onOpen,\n onClose,\n onError,\n deepMerge: useDeepMerge = true,\n apiUrl: apiUrlOption,\n authToken: authTokenOption,\n handshakeData: handshakeDataOption,\n disablePooling = false,\n debug: debugOption,\n } = options;\n\n const [data, setData] = useState<T | null>(initial);\n const [isConnected, setIsConnected] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n type Transport = {\n sendEvent: (name: string, payload: any, msgId?: string) => void;\n sendUpdate: (payload: T, msgId?: string) => void;\n sendDiff: (patch: Partial<T>, msgId?: string) => void;\n };\n\n const transportRef = useRef<Transport | null>(null);\n const clientRef = useRef<ViraClient | null>(null); // legacy non-pooled mode only\n const sessionRef = useRef<string | null>(null);\n const msgIdCounterRef = useRef(0);\n const wasConnectedRef = useRef(false);\n const lastErrorRef = useRef<Error | null>(null);\n\n // Use provided apiUrl or fallback to env or default\n // Note: import.meta is only available in ESM, so we check safely\n // IMPORTANT: Auto-detect protocol based on current page (wss:// for HTTPS, ws:// for HTTP)\n const apiUrl = useMemo(() => {\n let url = apiUrlOption;\n \n // Try to get from env if not provided\n if (!url) {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const env = (globalThis as any).import?.meta?.env || (globalThis as any).process?.env;\n url = env?.VITE_API_URL;\n } catch {\n // Ignore if import.meta is not available\n }\n }\n \n // If still no URL, use default\n if (!url) {\n url = 'http://45.90.35.155';\n }\n \n // Auto-detect protocol based on current page\n // For HTTPS pages, use wss:// for WebSocket and https:// for HTTP\n // For HTTP pages, use ws:// for WebSocket and http:// for HTTP\n if (typeof window !== 'undefined' && window.location) {\n const isHttps = window.location.protocol === 'https:';\n \n if (isHttps) {\n // Replace ws:// with wss:// and http:// with https://\n if (url.startsWith('ws://')) {\n url = url.replace('ws://', 'wss://');\n } else if (url.startsWith('http://')) {\n url = url.replace('http://', 'https://');\n }\n } else {\n // Replace wss:// with ws:// and https:// with http://\n if (url.startsWith('wss://')) {\n url = url.replace('wss://', 'ws://');\n } else if (url.startsWith('https://')) {\n url = url.replace('https://', 'http://');\n }\n }\n }\n \n return url;\n }, [apiUrlOption]);\n\n // Get authToken from options or try to get from env\n const authToken = useMemo(() => {\n if (authTokenOption !== undefined) return authTokenOption;\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const env = (globalThis as any).import?.meta?.env || (globalThis as any).process?.env;\n return env?.VITE_AUTH_TOKEN || '';\n } catch {\n return '';\n }\n }, [authTokenOption]);\n\n const debug = useMemo(() => {\n if (debugOption !== undefined) return Boolean(debugOption);\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const env = (globalThis as any).import?.meta?.env || (globalThis as any).process?.env;\n return String(env?.VITE_VRP_DEBUG || '').toLowerCase() === 'true';\n } catch {\n return false;\n }\n }, [debugOption]);\n\n // Create a stable key for handshakeData to avoid unnecessary pool recreation\n // Use values from handshakeData instead of the object reference\n const handshakeDataKey = useMemo(() => {\n if (!handshakeDataOption) return '';\n try {\n return JSON.stringify(handshakeDataOption);\n } catch {\n return '';\n }\n }, [handshakeDataOption]);\n \n const pool: ViraConnectionPool | null = useMemo(() => {\n if (disablePooling) return null;\n return getViraConnectionPool({ url: apiUrl, authToken, handshakeData: handshakeDataOption, debug });\n }, [disablePooling, apiUrl, authToken, handshakeDataKey, debug]);\n\n\n\n\n useEffect(() => {\n if (!channel) return;\n\n const handleMessage = (msg: Message) => {\n const msgChannel = 'channel' in msg ? msg.channel : undefined;\n if (debug) {\n if (msg.type === 'diff' && msgChannel === channel) {\n const patchObj = msg.patch as any;\n const patchKeys = patchObj && typeof patchObj === 'object' ? Object.keys(patchObj) : [];\n console.log('[VRP_CLIENT] Diff message received:', { channel, patchKeys });\n } else {\n console.log('[VRP_CLIENT] Message received:', { type: msg.type, channel: msgChannel, myChannel: channel, match: msgChannel === channel });\n }\n }\n switch (msg.type) {\n case 'update':\n case 'event':\n if (msg.channel === channel) {\n setData((prev) => {\n if (!prev) {\n return msg.data as T;\n }\n\n if (typeof prev === 'object' && typeof msg.data === 'object') {\n let mergedData;\n if (useDeepMerge) {\n mergedData = deepMerge(prev as Record<string, any>, msg.data as Record<string, any>) as T;\n } else {\n mergedData = { ...(prev as any), ...(msg.data as any) };\n }\n return mergedData;\n }\n\n return msg.data as T;\n });\n }\n break;\n\n case 'diff':\n if (msg.channel === channel && msg.patch) {\n setData((prev) => {\n if (debug) {\n console.log('[VRP_CLIENT] Applying diff:', { channel, patch: msg.patch, prevKeys: prev && typeof prev === 'object' ? Object.keys(prev as any) : [] });\n }\n if (!prev) {\n return msg.patch as T;\n }\n\n if (typeof prev === 'object' && typeof msg.patch === 'object') {\n const patchObj = msg.patch as Record<string, any>;\n const prevObj = prev as Record<string, any>;\n \n // If patch is empty, ignore it (might be a backend issue)\n const patchKeys = Object.keys(patchObj);\n if (patchKeys.length === 0) {\n if (debug) {\n console.log('[VRP_CLIENT] Empty patch received, ignoring:', { channel });\n }\n return prev;\n }\n \n // Check for deletions first (null values in patch)\n const keysToDelete: string[] = [];\n for (const key in patchObj) {\n if (patchObj[key] === null || patchObj[key] === undefined) {\n keysToDelete.push(key);\n }\n }\n \n // If we have deletions, create new object without deleted keys\n if (keysToDelete.length > 0) {\n if (debug) {\n console.log('[VRP_CLIENT] Processing deletions:', { channel, keysToDelete, prevCount: Object.keys(prevObj).length });\n }\n \n const result: Record<string, any> = {};\n // Copy all keys from prevObj except deleted ones\n for (const key in prevObj) {\n if (!keysToDelete.includes(key)) {\n result[key] = prevObj[key];\n }\n }\n // Apply non-null updates from patch\n for (const key in patchObj) {\n if (!keysToDelete.includes(key) && patchObj[key] !== null && patchObj[key] !== undefined) {\n result[key] = patchObj[key];\n }\n }\n \n if (debug) {\n console.log('[VRP_CLIENT] After deletion:', { channel, resultCount: Object.keys(result).length, deletedCount: keysToDelete.length });\n }\n \n // Force new object reference to trigger React re-render\n return { ...result } as T;\n }\n \n // No deletions, just merge updates\n let mergedData;\n if (useDeepMerge) {\n mergedData = deepMerge(prev as Record<string, any>, patchObj) as T;\n } else {\n mergedData = { ...(prev as any), ...patchObj };\n }\n \n return mergedData;\n }\n\n return prev;\n });\n }\n break;\n }\n };\n\n // --- Pooled mode (default) ---\n if (pool) {\n if (debug) {\n console.log('[VRP_CLIENT] Subscribing to channel:', channel);\n }\n // Subscribe to messages for this channel\n const unsubChannel = pool.subscribe(channel, handleMessage);\n\n // Track shared connection status\n const unsubStatus = pool.onStatus((status: ViraPoolStatus) => {\n setError(status.error);\n setIsConnected(status.connected);\n\n // Fire callbacks only on transitions\n if (status.connected && !wasConnectedRef.current) {\n wasConnectedRef.current = true;\n onOpen?.();\n }\n if (!status.connected && wasConnectedRef.current) {\n wasConnectedRef.current = false;\n // We don't get a real CloseEvent from pooled status, so we pass a lightweight synthetic object\n const synthetic = (typeof CloseEvent !== 'undefined'\n ? new CloseEvent('close', { code: 1001, reason: 'pooled disconnect' })\n : ({ code: 1001, reason: 'pooled disconnect' } as any)) as any;\n onClose?.(synthetic);\n }\n if (status.error) {\n if (lastErrorRef.current !== status.error) {\n lastErrorRef.current = status.error;\n onError?.(status.error);\n }\n } else {\n lastErrorRef.current = null;\n }\n });\n\n // Wire transport for send* APIs\n transportRef.current = {\n sendEvent: (name, payload, msgId) => pool.sendEvent(channel, name, payload, msgId),\n sendUpdate: (payload, msgId) => pool.sendUpdate(channel, payload, msgId),\n sendDiff: (patch, msgId) => pool.sendDiff(channel, patch, msgId),\n };\n\n return () => {\n unsubStatus();\n unsubChannel();\n transportRef.current = null;\n setIsConnected(false);\n setError(null);\n };\n }\n\n // --- Legacy mode (1 WS per channel) ---\n const handleConnect = () => {\n setIsConnected(true);\n setError(null);\n if (!wasConnectedRef.current) {\n wasConnectedRef.current = true;\n }\n onOpen?.();\n };\n\n const handleDisconnect = (event?: CloseEvent) => {\n setIsConnected(false);\n wasConnectedRef.current = false;\n onClose?.(event!);\n };\n\n const handleError = (err: Error) => {\n setError(err);\n onError?.(err);\n };\n\n const client = createViraClient({\n url: apiUrl,\n channel,\n onMessage: handleMessage,\n onConnect: handleConnect,\n onDisconnect: handleDisconnect,\n onError: handleError,\n session: sessionRef.current,\n authToken,\n onSessionChange: (newSession: string | null) => {\n sessionRef.current = newSession;\n },\n });\n\n clientRef.current = client;\n transportRef.current = {\n sendEvent: (name, payload, msgId) => client.sendEvent(name, payload, msgId),\n sendUpdate: (payload, msgId) => client.sendUpdate(payload, msgId),\n sendDiff: (patch, msgId) => client.sendDiff(patch, msgId),\n };\n\n return () => {\n client.close();\n clientRef.current = null;\n transportRef.current = null;\n setIsConnected(false);\n setError(null);\n };\n }, [channel, apiUrl, authToken, onOpen, onClose, onError, useDeepMerge, pool]);\n\n // Generate msgId if enabled\n const generateMsgId = useCallback((): string | undefined => {\n if (!enableMsgId) return undefined;\n msgIdCounterRef.current++;\n return `${channel}:${Date.now()}:${msgIdCounterRef.current}`;\n }, [channel, enableMsgId]);\n\n const sendEvent = useCallback(\n (name: string, payload: any, msgId?: string) => {\n transportRef.current?.sendEvent(name, payload, msgId ?? generateMsgId());\n },\n [generateMsgId]\n );\n\n const sendUpdate = useCallback(\n (payload: T, msgId?: string) => {\n transportRef.current?.sendUpdate(payload, msgId ?? generateMsgId());\n },\n [generateMsgId]\n );\n\n const sendDiff = useCallback(\n (patch: Partial<T>, msgId?: string) => {\n // Apply optimistic update immediately for deletions\n if (patch && typeof patch === 'object') {\n const patchObj = patch as Record<string, any>;\n const keysToDelete: string[] = [];\n for (const key in patchObj) {\n if (patchObj[key] === null || patchObj[key] === undefined) {\n keysToDelete.push(key);\n }\n }\n if (keysToDelete.length > 0) {\n if (debug) {\n console.log('[VRP_CLIENT] Optimistic deletion:', { channel, keysToDelete });\n }\n setData((prev) => {\n if (!prev || typeof prev !== 'object') return prev;\n const prevObj = prev as Record<string, any>;\n const result: Record<string, any> = {};\n // Copy all keys except deleted ones\n for (const key in prevObj) {\n if (!keysToDelete.includes(key)) {\n result[key] = prevObj[key];\n }\n }\n // Apply non-null updates from patch\n for (const key in patchObj) {\n if (!keysToDelete.includes(key) && patchObj[key] !== null && patchObj[key] !== undefined) {\n result[key] = patchObj[key];\n }\n }\n if (debug) {\n console.log('[VRP_CLIENT] Optimistic deletion result:', { channel, resultCount: Object.keys(result).length, prevCount: Object.keys(prevObj).length });\n }\n // Force new object reference\n return { ...result } as T;\n });\n }\n }\n transportRef.current?.sendDiff(patch, msgId ?? generateMsgId());\n },\n [generateMsgId, channel]\n );\n\n return {\n data,\n sendEvent,\n sendUpdate,\n sendDiff,\n isConnected,\n error,\n };\n}\n\n/**\n * Legacy hook - use useViraState instead.\n * @deprecated Use useViraState with options instead\n */\nexport function useViraStream<T = any, C extends string = string>(\n channel: C,\n options?: UseViraStateOptions<T>\n) {\n return useViraState<T, C>(channel, { ...options, initial: null });\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/useViraState.ts"],"sourcesContent":["/**\n * @vira-ui/react\n * \n * Vira Framework - React hooks for Vira Reactive Protocol\n * \n * This package provides React hooks for VRP, built on top of @vira-ui/core.\n */\n\nexport {\n useViraState,\n useViraStream,\n} from './useViraState';\nexport type {\n UseViraStateOptions,\n} from './useViraState';\n\n","import { useEffect, useMemo, useRef, useState, useCallback } from 'react';\nimport {\n createViraClient,\n deepMerge,\n getViraConnectionPool,\n type ViraClient,\n type Message,\n type ViraConnectionPool,\n type ViraPoolStatus,\n} from '@vira-ui/core';\n\nexport interface UseViraStateOptions<T = any> {\n /** Initial value for the state */\n initial?: T | null;\n /** Enable msgId support for idempotency */\n enableMsgId?: boolean;\n /** Callback when connection opens */\n onOpen?: () => void;\n /** Callback when connection closes */\n onClose?: (event: CloseEvent) => void;\n /** Callback when connection error occurs */\n onError?: (error: Error) => void;\n /** Use deep merge for diff patches (default: true) */\n deepMerge?: boolean;\n /** API URL (defaults to VITE_API_URL env or current origin with /ws path) */\n apiUrl?: string;\n /** Auth token for handshake */\n authToken?: string;\n /** Additional data to send in handshake (e.g., company_id, location_id) */\n handshakeData?: Record<string, any>;\n /** Disable connection pooling (fallback to 1 WS per channel). Default: false */\n disablePooling?: boolean;\n /** Enable debug logs for VRP (console.debug). Default: env VITE_VRP_DEBUG === 'true' */\n debug?: boolean;\n}\n\n/**\n * Unified hook for Vira Reactive Protocol state management.\n * Replaces both useViraState and useViraStream.\n *\n * @example\n * ```tsx\n * // Basic usage\n * const { data, sendUpdate } = useViraState<MyType>('my-channel');\n *\n * // With options\n * const { data, sendUpdate, sendDiff } = useViraState<User>('user:123', {\n * initial: { name: 'Guest' },\n * enableMsgId: true,\n * onOpen: () => console.log('Connected'),\n * deepMerge: true\n * });\n * ```\n */\nexport function useViraState<T = any, C extends string = string>(\n channel: C,\n initialOrOptions?: T | null | UseViraStateOptions<T>\n): {\n /** Current state data */\n data: T | null;\n /** Send an event to the server */\n sendEvent: (name: string, payload: any, msgId?: string) => void;\n /** Send a full update (replaces state) */\n sendUpdate: (payload: T, msgId?: string) => void;\n /** Send a partial diff (merges with current state) */\n sendDiff: (patch: Partial<T>, msgId?: string) => void;\n /** Connection status */\n isConnected: boolean;\n /** Connection error, if any */\n error: Error | null;\n} {\n // Parse options (backward compatibility: second param can be initial value or options)\n const options: UseViraStateOptions<T> = useMemo(() => {\n if (initialOrOptions === null || initialOrOptions === undefined) {\n return {};\n }\n // If it's an object with known option keys, treat as options\n if (\n typeof initialOrOptions === 'object' &&\n !Array.isArray(initialOrOptions) &&\n ('enableMsgId' in initialOrOptions ||\n 'onOpen' in initialOrOptions ||\n 'onClose' in initialOrOptions ||\n 'onError' in initialOrOptions ||\n 'deepMerge' in initialOrOptions ||\n 'initial' in initialOrOptions ||\n 'apiUrl' in initialOrOptions ||\n 'authToken' in initialOrOptions ||\n 'handshakeData' in initialOrOptions ||\n 'disablePooling' in initialOrOptions ||\n 'debug' in initialOrOptions)\n ) {\n return initialOrOptions as UseViraStateOptions<T>;\n }\n // Otherwise, treat as initial value (backward compatibility)\n return { initial: initialOrOptions as T | null };\n }, [initialOrOptions]);\n\n const {\n initial = null,\n enableMsgId = false,\n onOpen,\n onClose,\n onError,\n deepMerge: useDeepMerge = true,\n apiUrl: apiUrlOption,\n authToken: authTokenOption,\n handshakeData: handshakeDataOption,\n disablePooling = false,\n debug: debugOption,\n } = options;\n\n const [data, setData] = useState<T | null>(initial);\n const [isConnected, setIsConnected] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n type Transport = {\n sendEvent: (name: string, payload: any, msgId?: string) => void;\n sendUpdate: (payload: T, msgId?: string) => void;\n sendDiff: (patch: Partial<T>, msgId?: string) => void;\n };\n\n const transportRef = useRef<Transport | null>(null);\n const clientRef = useRef<ViraClient | null>(null); // legacy non-pooled mode only\n const sessionRef = useRef<string | null>(null);\n const msgIdCounterRef = useRef(0);\n const wasConnectedRef = useRef(false);\n const lastErrorRef = useRef<Error | null>(null);\n\n // Use provided apiUrl or fallback to env or relative path\n // Note: import.meta is only available in ESM, so we check safely\n // IMPORTANT: Auto-detect protocol based on current page (wss:// for HTTPS, ws:// for HTTP)\n const apiUrl = useMemo(() => {\n let url = apiUrlOption;\n \n // Try to get from env if not provided\n if (!url) {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const env = (globalThis as any).import?.meta?.env || (globalThis as any).process?.env;\n url = env?.VITE_API_URL;\n } catch {\n // Ignore if import.meta is not available\n }\n }\n // Default: use current origin with /ws path (works for same-origin deployments)\n if (!url) {\n if (typeof window !== 'undefined' && window.location) {\n const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';\n return `${protocol}//${window.location.host}/ws`;\n }\n // Server-side or unknown environment: use relative path\n return '/ws';\n }\n return url;\n }, [apiUrlOption]);\n\n // Get authToken from options or try to get from env\n const authToken = useMemo(() => {\n if (authTokenOption !== undefined) return authTokenOption;\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const env = (globalThis as any).import?.meta?.env || (globalThis as any).process?.env;\n return env?.VITE_AUTH_TOKEN || '';\n } catch {\n return '';\n }\n }, [authTokenOption]);\n\n const debug = useMemo(() => {\n if (debugOption !== undefined) return Boolean(debugOption);\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const env = (globalThis as any).import?.meta?.env || (globalThis as any).process?.env;\n return String(env?.VITE_VRP_DEBUG || '').toLowerCase() === 'true';\n } catch {\n return false;\n }\n }, [debugOption]);\n\n // Create a stable key for handshakeData to avoid unnecessary pool recreation\n // Use values from handshakeData instead of the object reference\n const handshakeDataKey = useMemo(() => {\n if (!handshakeDataOption) return '';\n try {\n return JSON.stringify(handshakeDataOption);\n } catch {\n return '';\n }\n }, [handshakeDataOption]);\n \n const pool: ViraConnectionPool | null = useMemo(() => {\n if (disablePooling) return null;\n return getViraConnectionPool({ url: apiUrl, authToken, handshakeData: handshakeDataOption, debug });\n }, [disablePooling, apiUrl, authToken, handshakeDataKey, debug]);\n\n\n\n\n useEffect(() => {\n if (!channel) return;\n\n const handleMessage = (msg: Message) => {\n const msgChannel = 'channel' in msg ? msg.channel : undefined;\n if (debug) {\n if (msg.type === 'diff' && msgChannel === channel) {\n const patchObj = msg.patch as any;\n const patchKeys = patchObj && typeof patchObj === 'object' ? Object.keys(patchObj) : [];\n console.log('[VRP_CLIENT] Diff message received:', { channel, patchKeys });\n } else {\n console.log('[VRP_CLIENT] Message received:', { type: msg.type, channel: msgChannel, myChannel: channel, match: msgChannel === channel });\n }\n }\n switch (msg.type) {\n case 'update':\n case 'event':\n if (msg.channel === channel) {\n setData((prev) => {\n if (!prev) {\n return msg.data as T;\n }\n\n if (typeof prev === 'object' && typeof msg.data === 'object') {\n let mergedData;\n if (useDeepMerge) {\n mergedData = deepMerge(prev as Record<string, any>, msg.data as Record<string, any>) as T;\n } else {\n mergedData = { ...(prev as any), ...(msg.data as any) };\n }\n return mergedData;\n }\n\n return msg.data as T;\n });\n }\n break;\n\n case 'diff':\n if (msg.channel === channel && msg.patch) {\n setData((prev) => {\n if (debug) {\n console.log('[VRP_CLIENT] Applying diff:', { channel, patch: msg.patch, prevKeys: prev && typeof prev === 'object' ? Object.keys(prev as any) : [] });\n }\n if (!prev) {\n return msg.patch as T;\n }\n\n if (typeof prev === 'object' && typeof msg.patch === 'object') {\n const patchObj = msg.patch as Record<string, any>;\n const prevObj = prev as Record<string, any>;\n \n // If patch is empty, ignore it (might be a backend issue)\n const patchKeys = Object.keys(patchObj);\n if (patchKeys.length === 0) {\n if (debug) {\n console.log('[VRP_CLIENT] Empty patch received, ignoring:', { channel });\n }\n return prev;\n }\n \n // Check for deletions first (null values in patch)\n const keysToDelete: string[] = [];\n for (const key in patchObj) {\n if (patchObj[key] === null || patchObj[key] === undefined) {\n keysToDelete.push(key);\n }\n }\n \n // If we have deletions, create new object without deleted keys\n if (keysToDelete.length > 0) {\n if (debug) {\n console.log('[VRP_CLIENT] Processing deletions:', { channel, keysToDelete, prevCount: Object.keys(prevObj).length });\n }\n \n const result: Record<string, any> = {};\n // Copy all keys from prevObj except deleted ones\n for (const key in prevObj) {\n if (!keysToDelete.includes(key)) {\n result[key] = prevObj[key];\n }\n }\n // Apply non-null updates from patch\n for (const key in patchObj) {\n if (!keysToDelete.includes(key) && patchObj[key] !== null && patchObj[key] !== undefined) {\n result[key] = patchObj[key];\n }\n }\n \n if (debug) {\n console.log('[VRP_CLIENT] After deletion:', { channel, resultCount: Object.keys(result).length, deletedCount: keysToDelete.length });\n }\n \n // Force new object reference to trigger React re-render\n return { ...result } as T;\n }\n \n // No deletions, just merge updates\n let mergedData;\n if (useDeepMerge) {\n mergedData = deepMerge(prev as Record<string, any>, patchObj) as T;\n } else {\n mergedData = { ...(prev as any), ...patchObj };\n }\n \n return mergedData;\n }\n\n return prev;\n });\n }\n break;\n }\n };\n\n // --- Pooled mode (default) ---\n if (pool) {\n if (debug) {\n console.log('[VRP_CLIENT] Subscribing to channel:', channel);\n }\n // Subscribe to messages for this channel\n const unsubChannel = pool.subscribe(channel, handleMessage);\n\n // Track shared connection status\n const unsubStatus = pool.onStatus((status: ViraPoolStatus) => {\n setError(status.error);\n setIsConnected(status.connected);\n\n // Fire callbacks only on transitions\n if (status.connected && !wasConnectedRef.current) {\n wasConnectedRef.current = true;\n onOpen?.();\n }\n if (!status.connected && wasConnectedRef.current) {\n wasConnectedRef.current = false;\n // We don't get a real CloseEvent from pooled status, so we pass a lightweight synthetic object\n const synthetic = (typeof CloseEvent !== 'undefined'\n ? new CloseEvent('close', { code: 1001, reason: 'pooled disconnect' })\n : ({ code: 1001, reason: 'pooled disconnect' } as any)) as any;\n onClose?.(synthetic);\n }\n if (status.error) {\n if (lastErrorRef.current !== status.error) {\n lastErrorRef.current = status.error;\n onError?.(status.error);\n }\n } else {\n lastErrorRef.current = null;\n }\n });\n\n // Wire transport for send* APIs\n transportRef.current = {\n sendEvent: (name, payload, msgId) => pool.sendEvent(channel, name, payload, msgId),\n sendUpdate: (payload, msgId) => pool.sendUpdate(channel, payload, msgId),\n sendDiff: (patch, msgId) => pool.sendDiff(channel, patch, msgId),\n };\n\n return () => {\n unsubStatus();\n unsubChannel();\n transportRef.current = null;\n setIsConnected(false);\n setError(null);\n };\n }\n\n // --- Legacy mode (1 WS per channel) ---\n const handleConnect = () => {\n setIsConnected(true);\n setError(null);\n if (!wasConnectedRef.current) {\n wasConnectedRef.current = true;\n }\n onOpen?.();\n };\n\n const handleDisconnect = (event?: CloseEvent) => {\n setIsConnected(false);\n wasConnectedRef.current = false;\n onClose?.(event!);\n };\n\n const handleError = (err: Error) => {\n setError(err);\n onError?.(err);\n };\n\n const client = createViraClient({\n url: apiUrl,\n channel,\n onMessage: handleMessage,\n onConnect: handleConnect,\n onDisconnect: handleDisconnect,\n onError: handleError,\n session: sessionRef.current,\n authToken,\n onSessionChange: (newSession: string | null) => {\n sessionRef.current = newSession;\n },\n });\n\n clientRef.current = client;\n transportRef.current = {\n sendEvent: (name, payload, msgId) => client.sendEvent(name, payload, msgId),\n sendUpdate: (payload, msgId) => client.sendUpdate(payload, msgId),\n sendDiff: (patch, msgId) => client.sendDiff(patch, msgId),\n };\n\n return () => {\n client.close();\n clientRef.current = null;\n transportRef.current = null;\n setIsConnected(false);\n setError(null);\n };\n }, [channel, apiUrl, authToken, onOpen, onClose, onError, useDeepMerge, pool]);\n\n // Generate msgId if enabled\n const generateMsgId = useCallback((): string | undefined => {\n if (!enableMsgId) return undefined;\n msgIdCounterRef.current++;\n return `${channel}:${Date.now()}:${msgIdCounterRef.current}`;\n }, [channel, enableMsgId]);\n\n const sendEvent = useCallback(\n (name: string, payload: any, msgId?: string) => {\n transportRef.current?.sendEvent(name, payload, msgId ?? generateMsgId());\n },\n [generateMsgId]\n );\n\n const sendUpdate = useCallback(\n (payload: T, msgId?: string) => {\n transportRef.current?.sendUpdate(payload, msgId ?? generateMsgId());\n },\n [generateMsgId]\n );\n\n const sendDiff = useCallback(\n (patch: Partial<T>, msgId?: string) => {\n // Apply optimistic update immediately for all changes (creates, updates, deletes)\n if (patch && typeof patch === 'object') {\n const patchObj = patch as Record<string, any>;\n const patchKeys = Object.keys(patchObj);\n if (patchKeys.length > 0) {\n if (debug) {\n console.log('[VRP_CLIENT] Optimistic sendDiff:', { channel, patchKeys });\n }\n setData((prev) => {\n const prevObj = (prev && typeof prev === 'object' ? prev : {}) as Record<string, any>;\n const result: Record<string, any> = { ...prevObj };\n for (const key in patchObj) {\n if (patchObj[key] === null || patchObj[key] === undefined) {\n delete result[key];\n } else if (\n result[key] &&\n typeof result[key] === 'object' &&\n !Array.isArray(result[key]) &&\n typeof patchObj[key] === 'object' &&\n !Array.isArray(patchObj[key])\n ) {\n // Shallow-merge nested objects so partial patches don't wipe existing fields\n // (e.g. list patch { orderId: { status_id } } keeps all other order fields)\n result[key] = { ...result[key], ...patchObj[key] };\n } else {\n result[key] = patchObj[key];\n }\n }\n if (debug) {\n console.log('[VRP_CLIENT] Optimistic sendDiff result:', { channel, resultCount: Object.keys(result).length });\n }\n return result as T;\n });\n }\n }\n transportRef.current?.sendDiff(patch, msgId ?? generateMsgId());\n },\n [generateMsgId, channel, debug]\n );\n\n return {\n data,\n sendEvent,\n sendUpdate,\n sendDiff,\n isConnected,\n error,\n };\n}\n\n/**\n * Legacy hook - use useViraState instead.\n * @deprecated Use useViraState with options instead\n */\nexport function useViraStream<T = any, C extends string = string>(\n channel: C,\n options?: UseViraStateOptions<T>\n) {\n return useViraState<T, C>(channel, { ...options, initial: null });\n}\n"],"mappings":"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","names":[]}
package/dist/index.mjs CHANGED
@@ -47,23 +47,11 @@ function useViraState(channel, initialOrOptions) {
47
47
  }
48
48
  }
49
49
  if (!url) {
50
- url = "http://45.90.35.155";
51
- }
52
- if (typeof window !== "undefined" && window.location) {
53
- const isHttps = window.location.protocol === "https:";
54
- if (isHttps) {
55
- if (url.startsWith("ws://")) {
56
- url = url.replace("ws://", "wss://");
57
- } else if (url.startsWith("http://")) {
58
- url = url.replace("http://", "https://");
59
- }
60
- } else {
61
- if (url.startsWith("wss://")) {
62
- url = url.replace("wss://", "ws://");
63
- } else if (url.startsWith("https://")) {
64
- url = url.replace("https://", "http://");
65
- }
50
+ if (typeof window !== "undefined" && window.location) {
51
+ const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
52
+ return `${protocol}//${window.location.host}/ws`;
66
53
  }
54
+ return "/ws";
67
55
  }
68
56
  return url;
69
57
  }, [apiUrlOption]);
@@ -294,40 +282,33 @@ function useViraState(channel, initialOrOptions) {
294
282
  (patch, msgId) => {
295
283
  if (patch && typeof patch === "object") {
296
284
  const patchObj = patch;
297
- const keysToDelete = [];
298
- for (const key in patchObj) {
299
- if (patchObj[key] === null || patchObj[key] === void 0) {
300
- keysToDelete.push(key);
301
- }
302
- }
303
- if (keysToDelete.length > 0) {
285
+ const patchKeys = Object.keys(patchObj);
286
+ if (patchKeys.length > 0) {
304
287
  if (debug) {
305
- console.log("[VRP_CLIENT] Optimistic deletion:", { channel, keysToDelete });
288
+ console.log("[VRP_CLIENT] Optimistic sendDiff:", { channel, patchKeys });
306
289
  }
307
290
  setData((prev) => {
308
- if (!prev || typeof prev !== "object") return prev;
309
- const prevObj = prev;
310
- const result = {};
311
- for (const key in prevObj) {
312
- if (!keysToDelete.includes(key)) {
313
- result[key] = prevObj[key];
314
- }
315
- }
291
+ const prevObj = prev && typeof prev === "object" ? prev : {};
292
+ const result = { ...prevObj };
316
293
  for (const key in patchObj) {
317
- if (!keysToDelete.includes(key) && patchObj[key] !== null && patchObj[key] !== void 0) {
294
+ if (patchObj[key] === null || patchObj[key] === void 0) {
295
+ delete result[key];
296
+ } else if (result[key] && typeof result[key] === "object" && !Array.isArray(result[key]) && typeof patchObj[key] === "object" && !Array.isArray(patchObj[key])) {
297
+ result[key] = { ...result[key], ...patchObj[key] };
298
+ } else {
318
299
  result[key] = patchObj[key];
319
300
  }
320
301
  }
321
302
  if (debug) {
322
- console.log("[VRP_CLIENT] Optimistic deletion result:", { channel, resultCount: Object.keys(result).length, prevCount: Object.keys(prevObj).length });
303
+ console.log("[VRP_CLIENT] Optimistic sendDiff result:", { channel, resultCount: Object.keys(result).length });
323
304
  }
324
- return { ...result };
305
+ return result;
325
306
  });
326
307
  }
327
308
  }
328
309
  transportRef.current?.sendDiff(patch, msgId ?? generateMsgId());
329
310
  },
330
- [generateMsgId, channel]
311
+ [generateMsgId, channel, debug]
331
312
  );
332
313
  return {
333
314
  data,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/useViraState.ts"],"sourcesContent":["import { useEffect, useMemo, useRef, useState, useCallback } from 'react';\nimport {\n createViraClient,\n deepMerge,\n getViraConnectionPool,\n type ViraClient,\n type Message,\n type ViraConnectionPool,\n type ViraPoolStatus,\n} from '@vira-ui/core';\n\nexport interface UseViraStateOptions<T = any> {\n /** Initial value for the state */\n initial?: T | null;\n /** Enable msgId support for idempotency */\n enableMsgId?: boolean;\n /** Callback when connection opens */\n onOpen?: () => void;\n /** Callback when connection closes */\n onClose?: (event: CloseEvent) => void;\n /** Callback when connection error occurs */\n onError?: (error: Error) => void;\n /** Use deep merge for diff patches (default: true) */\n deepMerge?: boolean;\n /** API URL (defaults to VITE_API_URL env or 'http://45.90.35.155') */\n apiUrl?: string;\n /** Auth token for handshake */\n authToken?: string;\n /** Additional data to send in handshake (e.g., company_id, location_id) */\n handshakeData?: Record<string, any>;\n /** Disable connection pooling (fallback to 1 WS per channel). Default: false */\n disablePooling?: boolean;\n /** Enable debug logs for VRP (console.debug). Default: env VITE_VRP_DEBUG === 'true' */\n debug?: boolean;\n}\n\n/**\n * Unified hook for Vira Reactive Protocol state management.\n * Replaces both useViraState and useViraStream.\n *\n * @example\n * ```tsx\n * // Basic usage\n * const { data, sendUpdate } = useViraState<MyType>('my-channel');\n *\n * // With options\n * const { data, sendUpdate, sendDiff } = useViraState<User>('user:123', {\n * initial: { name: 'Guest' },\n * enableMsgId: true,\n * onOpen: () => console.log('Connected'),\n * deepMerge: true\n * });\n * ```\n */\nexport function useViraState<T = any, C extends string = string>(\n channel: C,\n initialOrOptions?: T | null | UseViraStateOptions<T>\n): {\n /** Current state data */\n data: T | null;\n /** Send an event to the server */\n sendEvent: (name: string, payload: any, msgId?: string) => void;\n /** Send a full update (replaces state) */\n sendUpdate: (payload: T, msgId?: string) => void;\n /** Send a partial diff (merges with current state) */\n sendDiff: (patch: Partial<T>, msgId?: string) => void;\n /** Connection status */\n isConnected: boolean;\n /** Connection error, if any */\n error: Error | null;\n} {\n // Parse options (backward compatibility: second param can be initial value or options)\n const options: UseViraStateOptions<T> = useMemo(() => {\n if (initialOrOptions === null || initialOrOptions === undefined) {\n return {};\n }\n // If it's an object with known option keys, treat as options\n if (\n typeof initialOrOptions === 'object' &&\n !Array.isArray(initialOrOptions) &&\n ('enableMsgId' in initialOrOptions ||\n 'onOpen' in initialOrOptions ||\n 'onClose' in initialOrOptions ||\n 'onError' in initialOrOptions ||\n 'deepMerge' in initialOrOptions ||\n 'initial' in initialOrOptions ||\n 'apiUrl' in initialOrOptions ||\n 'authToken' in initialOrOptions ||\n 'handshakeData' in initialOrOptions ||\n 'disablePooling' in initialOrOptions ||\n 'debug' in initialOrOptions)\n ) {\n return initialOrOptions as UseViraStateOptions<T>;\n }\n // Otherwise, treat as initial value (backward compatibility)\n return { initial: initialOrOptions as T | null };\n }, [initialOrOptions]);\n\n const {\n initial = null,\n enableMsgId = false,\n onOpen,\n onClose,\n onError,\n deepMerge: useDeepMerge = true,\n apiUrl: apiUrlOption,\n authToken: authTokenOption,\n handshakeData: handshakeDataOption,\n disablePooling = false,\n debug: debugOption,\n } = options;\n\n const [data, setData] = useState<T | null>(initial);\n const [isConnected, setIsConnected] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n type Transport = {\n sendEvent: (name: string, payload: any, msgId?: string) => void;\n sendUpdate: (payload: T, msgId?: string) => void;\n sendDiff: (patch: Partial<T>, msgId?: string) => void;\n };\n\n const transportRef = useRef<Transport | null>(null);\n const clientRef = useRef<ViraClient | null>(null); // legacy non-pooled mode only\n const sessionRef = useRef<string | null>(null);\n const msgIdCounterRef = useRef(0);\n const wasConnectedRef = useRef(false);\n const lastErrorRef = useRef<Error | null>(null);\n\n // Use provided apiUrl or fallback to env or default\n // Note: import.meta is only available in ESM, so we check safely\n // IMPORTANT: Auto-detect protocol based on current page (wss:// for HTTPS, ws:// for HTTP)\n const apiUrl = useMemo(() => {\n let url = apiUrlOption;\n \n // Try to get from env if not provided\n if (!url) {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const env = (globalThis as any).import?.meta?.env || (globalThis as any).process?.env;\n url = env?.VITE_API_URL;\n } catch {\n // Ignore if import.meta is not available\n }\n }\n \n // If still no URL, use default\n if (!url) {\n url = 'http://45.90.35.155';\n }\n \n // Auto-detect protocol based on current page\n // For HTTPS pages, use wss:// for WebSocket and https:// for HTTP\n // For HTTP pages, use ws:// for WebSocket and http:// for HTTP\n if (typeof window !== 'undefined' && window.location) {\n const isHttps = window.location.protocol === 'https:';\n \n if (isHttps) {\n // Replace ws:// with wss:// and http:// with https://\n if (url.startsWith('ws://')) {\n url = url.replace('ws://', 'wss://');\n } else if (url.startsWith('http://')) {\n url = url.replace('http://', 'https://');\n }\n } else {\n // Replace wss:// with ws:// and https:// with http://\n if (url.startsWith('wss://')) {\n url = url.replace('wss://', 'ws://');\n } else if (url.startsWith('https://')) {\n url = url.replace('https://', 'http://');\n }\n }\n }\n \n return url;\n }, [apiUrlOption]);\n\n // Get authToken from options or try to get from env\n const authToken = useMemo(() => {\n if (authTokenOption !== undefined) return authTokenOption;\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const env = (globalThis as any).import?.meta?.env || (globalThis as any).process?.env;\n return env?.VITE_AUTH_TOKEN || '';\n } catch {\n return '';\n }\n }, [authTokenOption]);\n\n const debug = useMemo(() => {\n if (debugOption !== undefined) return Boolean(debugOption);\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const env = (globalThis as any).import?.meta?.env || (globalThis as any).process?.env;\n return String(env?.VITE_VRP_DEBUG || '').toLowerCase() === 'true';\n } catch {\n return false;\n }\n }, [debugOption]);\n\n // Create a stable key for handshakeData to avoid unnecessary pool recreation\n // Use values from handshakeData instead of the object reference\n const handshakeDataKey = useMemo(() => {\n if (!handshakeDataOption) return '';\n try {\n return JSON.stringify(handshakeDataOption);\n } catch {\n return '';\n }\n }, [handshakeDataOption]);\n \n const pool: ViraConnectionPool | null = useMemo(() => {\n if (disablePooling) return null;\n return getViraConnectionPool({ url: apiUrl, authToken, handshakeData: handshakeDataOption, debug });\n }, [disablePooling, apiUrl, authToken, handshakeDataKey, debug]);\n\n\n\n\n useEffect(() => {\n if (!channel) return;\n\n const handleMessage = (msg: Message) => {\n const msgChannel = 'channel' in msg ? msg.channel : undefined;\n if (debug) {\n if (msg.type === 'diff' && msgChannel === channel) {\n const patchObj = msg.patch as any;\n const patchKeys = patchObj && typeof patchObj === 'object' ? Object.keys(patchObj) : [];\n console.log('[VRP_CLIENT] Diff message received:', { channel, patchKeys });\n } else {\n console.log('[VRP_CLIENT] Message received:', { type: msg.type, channel: msgChannel, myChannel: channel, match: msgChannel === channel });\n }\n }\n switch (msg.type) {\n case 'update':\n case 'event':\n if (msg.channel === channel) {\n setData((prev) => {\n if (!prev) {\n return msg.data as T;\n }\n\n if (typeof prev === 'object' && typeof msg.data === 'object') {\n let mergedData;\n if (useDeepMerge) {\n mergedData = deepMerge(prev as Record<string, any>, msg.data as Record<string, any>) as T;\n } else {\n mergedData = { ...(prev as any), ...(msg.data as any) };\n }\n return mergedData;\n }\n\n return msg.data as T;\n });\n }\n break;\n\n case 'diff':\n if (msg.channel === channel && msg.patch) {\n setData((prev) => {\n if (debug) {\n console.log('[VRP_CLIENT] Applying diff:', { channel, patch: msg.patch, prevKeys: prev && typeof prev === 'object' ? Object.keys(prev as any) : [] });\n }\n if (!prev) {\n return msg.patch as T;\n }\n\n if (typeof prev === 'object' && typeof msg.patch === 'object') {\n const patchObj = msg.patch as Record<string, any>;\n const prevObj = prev as Record<string, any>;\n \n // If patch is empty, ignore it (might be a backend issue)\n const patchKeys = Object.keys(patchObj);\n if (patchKeys.length === 0) {\n if (debug) {\n console.log('[VRP_CLIENT] Empty patch received, ignoring:', { channel });\n }\n return prev;\n }\n \n // Check for deletions first (null values in patch)\n const keysToDelete: string[] = [];\n for (const key in patchObj) {\n if (patchObj[key] === null || patchObj[key] === undefined) {\n keysToDelete.push(key);\n }\n }\n \n // If we have deletions, create new object without deleted keys\n if (keysToDelete.length > 0) {\n if (debug) {\n console.log('[VRP_CLIENT] Processing deletions:', { channel, keysToDelete, prevCount: Object.keys(prevObj).length });\n }\n \n const result: Record<string, any> = {};\n // Copy all keys from prevObj except deleted ones\n for (const key in prevObj) {\n if (!keysToDelete.includes(key)) {\n result[key] = prevObj[key];\n }\n }\n // Apply non-null updates from patch\n for (const key in patchObj) {\n if (!keysToDelete.includes(key) && patchObj[key] !== null && patchObj[key] !== undefined) {\n result[key] = patchObj[key];\n }\n }\n \n if (debug) {\n console.log('[VRP_CLIENT] After deletion:', { channel, resultCount: Object.keys(result).length, deletedCount: keysToDelete.length });\n }\n \n // Force new object reference to trigger React re-render\n return { ...result } as T;\n }\n \n // No deletions, just merge updates\n let mergedData;\n if (useDeepMerge) {\n mergedData = deepMerge(prev as Record<string, any>, patchObj) as T;\n } else {\n mergedData = { ...(prev as any), ...patchObj };\n }\n \n return mergedData;\n }\n\n return prev;\n });\n }\n break;\n }\n };\n\n // --- Pooled mode (default) ---\n if (pool) {\n if (debug) {\n console.log('[VRP_CLIENT] Subscribing to channel:', channel);\n }\n // Subscribe to messages for this channel\n const unsubChannel = pool.subscribe(channel, handleMessage);\n\n // Track shared connection status\n const unsubStatus = pool.onStatus((status: ViraPoolStatus) => {\n setError(status.error);\n setIsConnected(status.connected);\n\n // Fire callbacks only on transitions\n if (status.connected && !wasConnectedRef.current) {\n wasConnectedRef.current = true;\n onOpen?.();\n }\n if (!status.connected && wasConnectedRef.current) {\n wasConnectedRef.current = false;\n // We don't get a real CloseEvent from pooled status, so we pass a lightweight synthetic object\n const synthetic = (typeof CloseEvent !== 'undefined'\n ? new CloseEvent('close', { code: 1001, reason: 'pooled disconnect' })\n : ({ code: 1001, reason: 'pooled disconnect' } as any)) as any;\n onClose?.(synthetic);\n }\n if (status.error) {\n if (lastErrorRef.current !== status.error) {\n lastErrorRef.current = status.error;\n onError?.(status.error);\n }\n } else {\n lastErrorRef.current = null;\n }\n });\n\n // Wire transport for send* APIs\n transportRef.current = {\n sendEvent: (name, payload, msgId) => pool.sendEvent(channel, name, payload, msgId),\n sendUpdate: (payload, msgId) => pool.sendUpdate(channel, payload, msgId),\n sendDiff: (patch, msgId) => pool.sendDiff(channel, patch, msgId),\n };\n\n return () => {\n unsubStatus();\n unsubChannel();\n transportRef.current = null;\n setIsConnected(false);\n setError(null);\n };\n }\n\n // --- Legacy mode (1 WS per channel) ---\n const handleConnect = () => {\n setIsConnected(true);\n setError(null);\n if (!wasConnectedRef.current) {\n wasConnectedRef.current = true;\n }\n onOpen?.();\n };\n\n const handleDisconnect = (event?: CloseEvent) => {\n setIsConnected(false);\n wasConnectedRef.current = false;\n onClose?.(event!);\n };\n\n const handleError = (err: Error) => {\n setError(err);\n onError?.(err);\n };\n\n const client = createViraClient({\n url: apiUrl,\n channel,\n onMessage: handleMessage,\n onConnect: handleConnect,\n onDisconnect: handleDisconnect,\n onError: handleError,\n session: sessionRef.current,\n authToken,\n onSessionChange: (newSession: string | null) => {\n sessionRef.current = newSession;\n },\n });\n\n clientRef.current = client;\n transportRef.current = {\n sendEvent: (name, payload, msgId) => client.sendEvent(name, payload, msgId),\n sendUpdate: (payload, msgId) => client.sendUpdate(payload, msgId),\n sendDiff: (patch, msgId) => client.sendDiff(patch, msgId),\n };\n\n return () => {\n client.close();\n clientRef.current = null;\n transportRef.current = null;\n setIsConnected(false);\n setError(null);\n };\n }, [channel, apiUrl, authToken, onOpen, onClose, onError, useDeepMerge, pool]);\n\n // Generate msgId if enabled\n const generateMsgId = useCallback((): string | undefined => {\n if (!enableMsgId) return undefined;\n msgIdCounterRef.current++;\n return `${channel}:${Date.now()}:${msgIdCounterRef.current}`;\n }, [channel, enableMsgId]);\n\n const sendEvent = useCallback(\n (name: string, payload: any, msgId?: string) => {\n transportRef.current?.sendEvent(name, payload, msgId ?? generateMsgId());\n },\n [generateMsgId]\n );\n\n const sendUpdate = useCallback(\n (payload: T, msgId?: string) => {\n transportRef.current?.sendUpdate(payload, msgId ?? generateMsgId());\n },\n [generateMsgId]\n );\n\n const sendDiff = useCallback(\n (patch: Partial<T>, msgId?: string) => {\n // Apply optimistic update immediately for deletions\n if (patch && typeof patch === 'object') {\n const patchObj = patch as Record<string, any>;\n const keysToDelete: string[] = [];\n for (const key in patchObj) {\n if (patchObj[key] === null || patchObj[key] === undefined) {\n keysToDelete.push(key);\n }\n }\n if (keysToDelete.length > 0) {\n if (debug) {\n console.log('[VRP_CLIENT] Optimistic deletion:', { channel, keysToDelete });\n }\n setData((prev) => {\n if (!prev || typeof prev !== 'object') return prev;\n const prevObj = prev as Record<string, any>;\n const result: Record<string, any> = {};\n // Copy all keys except deleted ones\n for (const key in prevObj) {\n if (!keysToDelete.includes(key)) {\n result[key] = prevObj[key];\n }\n }\n // Apply non-null updates from patch\n for (const key in patchObj) {\n if (!keysToDelete.includes(key) && patchObj[key] !== null && patchObj[key] !== undefined) {\n result[key] = patchObj[key];\n }\n }\n if (debug) {\n console.log('[VRP_CLIENT] Optimistic deletion result:', { channel, resultCount: Object.keys(result).length, prevCount: Object.keys(prevObj).length });\n }\n // Force new object reference\n return { ...result } as T;\n });\n }\n }\n transportRef.current?.sendDiff(patch, msgId ?? generateMsgId());\n },\n [generateMsgId, channel]\n );\n\n return {\n data,\n sendEvent,\n sendUpdate,\n sendDiff,\n isConnected,\n error,\n };\n}\n\n/**\n * Legacy hook - use useViraState instead.\n * @deprecated Use useViraState with options instead\n */\nexport function useViraStream<T = any, C extends string = string>(\n channel: C,\n options?: UseViraStateOptions<T>\n) {\n return useViraState<T, C>(channel, { ...options, initial: null });\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/useViraState.ts"],"sourcesContent":["import { useEffect, useMemo, useRef, useState, useCallback } from 'react';\nimport {\n createViraClient,\n deepMerge,\n getViraConnectionPool,\n type ViraClient,\n type Message,\n type ViraConnectionPool,\n type ViraPoolStatus,\n} from '@vira-ui/core';\n\nexport interface UseViraStateOptions<T = any> {\n /** Initial value for the state */\n initial?: T | null;\n /** Enable msgId support for idempotency */\n enableMsgId?: boolean;\n /** Callback when connection opens */\n onOpen?: () => void;\n /** Callback when connection closes */\n onClose?: (event: CloseEvent) => void;\n /** Callback when connection error occurs */\n onError?: (error: Error) => void;\n /** Use deep merge for diff patches (default: true) */\n deepMerge?: boolean;\n /** API URL (defaults to VITE_API_URL env or current origin with /ws path) */\n apiUrl?: string;\n /** Auth token for handshake */\n authToken?: string;\n /** Additional data to send in handshake (e.g., company_id, location_id) */\n handshakeData?: Record<string, any>;\n /** Disable connection pooling (fallback to 1 WS per channel). Default: false */\n disablePooling?: boolean;\n /** Enable debug logs for VRP (console.debug). Default: env VITE_VRP_DEBUG === 'true' */\n debug?: boolean;\n}\n\n/**\n * Unified hook for Vira Reactive Protocol state management.\n * Replaces both useViraState and useViraStream.\n *\n * @example\n * ```tsx\n * // Basic usage\n * const { data, sendUpdate } = useViraState<MyType>('my-channel');\n *\n * // With options\n * const { data, sendUpdate, sendDiff } = useViraState<User>('user:123', {\n * initial: { name: 'Guest' },\n * enableMsgId: true,\n * onOpen: () => console.log('Connected'),\n * deepMerge: true\n * });\n * ```\n */\nexport function useViraState<T = any, C extends string = string>(\n channel: C,\n initialOrOptions?: T | null | UseViraStateOptions<T>\n): {\n /** Current state data */\n data: T | null;\n /** Send an event to the server */\n sendEvent: (name: string, payload: any, msgId?: string) => void;\n /** Send a full update (replaces state) */\n sendUpdate: (payload: T, msgId?: string) => void;\n /** Send a partial diff (merges with current state) */\n sendDiff: (patch: Partial<T>, msgId?: string) => void;\n /** Connection status */\n isConnected: boolean;\n /** Connection error, if any */\n error: Error | null;\n} {\n // Parse options (backward compatibility: second param can be initial value or options)\n const options: UseViraStateOptions<T> = useMemo(() => {\n if (initialOrOptions === null || initialOrOptions === undefined) {\n return {};\n }\n // If it's an object with known option keys, treat as options\n if (\n typeof initialOrOptions === 'object' &&\n !Array.isArray(initialOrOptions) &&\n ('enableMsgId' in initialOrOptions ||\n 'onOpen' in initialOrOptions ||\n 'onClose' in initialOrOptions ||\n 'onError' in initialOrOptions ||\n 'deepMerge' in initialOrOptions ||\n 'initial' in initialOrOptions ||\n 'apiUrl' in initialOrOptions ||\n 'authToken' in initialOrOptions ||\n 'handshakeData' in initialOrOptions ||\n 'disablePooling' in initialOrOptions ||\n 'debug' in initialOrOptions)\n ) {\n return initialOrOptions as UseViraStateOptions<T>;\n }\n // Otherwise, treat as initial value (backward compatibility)\n return { initial: initialOrOptions as T | null };\n }, [initialOrOptions]);\n\n const {\n initial = null,\n enableMsgId = false,\n onOpen,\n onClose,\n onError,\n deepMerge: useDeepMerge = true,\n apiUrl: apiUrlOption,\n authToken: authTokenOption,\n handshakeData: handshakeDataOption,\n disablePooling = false,\n debug: debugOption,\n } = options;\n\n const [data, setData] = useState<T | null>(initial);\n const [isConnected, setIsConnected] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n type Transport = {\n sendEvent: (name: string, payload: any, msgId?: string) => void;\n sendUpdate: (payload: T, msgId?: string) => void;\n sendDiff: (patch: Partial<T>, msgId?: string) => void;\n };\n\n const transportRef = useRef<Transport | null>(null);\n const clientRef = useRef<ViraClient | null>(null); // legacy non-pooled mode only\n const sessionRef = useRef<string | null>(null);\n const msgIdCounterRef = useRef(0);\n const wasConnectedRef = useRef(false);\n const lastErrorRef = useRef<Error | null>(null);\n\n // Use provided apiUrl or fallback to env or relative path\n // Note: import.meta is only available in ESM, so we check safely\n // IMPORTANT: Auto-detect protocol based on current page (wss:// for HTTPS, ws:// for HTTP)\n const apiUrl = useMemo(() => {\n let url = apiUrlOption;\n \n // Try to get from env if not provided\n if (!url) {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const env = (globalThis as any).import?.meta?.env || (globalThis as any).process?.env;\n url = env?.VITE_API_URL;\n } catch {\n // Ignore if import.meta is not available\n }\n }\n // Default: use current origin with /ws path (works for same-origin deployments)\n if (!url) {\n if (typeof window !== 'undefined' && window.location) {\n const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';\n return `${protocol}//${window.location.host}/ws`;\n }\n // Server-side or unknown environment: use relative path\n return '/ws';\n }\n return url;\n }, [apiUrlOption]);\n\n // Get authToken from options or try to get from env\n const authToken = useMemo(() => {\n if (authTokenOption !== undefined) return authTokenOption;\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const env = (globalThis as any).import?.meta?.env || (globalThis as any).process?.env;\n return env?.VITE_AUTH_TOKEN || '';\n } catch {\n return '';\n }\n }, [authTokenOption]);\n\n const debug = useMemo(() => {\n if (debugOption !== undefined) return Boolean(debugOption);\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const env = (globalThis as any).import?.meta?.env || (globalThis as any).process?.env;\n return String(env?.VITE_VRP_DEBUG || '').toLowerCase() === 'true';\n } catch {\n return false;\n }\n }, [debugOption]);\n\n // Create a stable key for handshakeData to avoid unnecessary pool recreation\n // Use values from handshakeData instead of the object reference\n const handshakeDataKey = useMemo(() => {\n if (!handshakeDataOption) return '';\n try {\n return JSON.stringify(handshakeDataOption);\n } catch {\n return '';\n }\n }, [handshakeDataOption]);\n \n const pool: ViraConnectionPool | null = useMemo(() => {\n if (disablePooling) return null;\n return getViraConnectionPool({ url: apiUrl, authToken, handshakeData: handshakeDataOption, debug });\n }, [disablePooling, apiUrl, authToken, handshakeDataKey, debug]);\n\n\n\n\n useEffect(() => {\n if (!channel) return;\n\n const handleMessage = (msg: Message) => {\n const msgChannel = 'channel' in msg ? msg.channel : undefined;\n if (debug) {\n if (msg.type === 'diff' && msgChannel === channel) {\n const patchObj = msg.patch as any;\n const patchKeys = patchObj && typeof patchObj === 'object' ? Object.keys(patchObj) : [];\n console.log('[VRP_CLIENT] Diff message received:', { channel, patchKeys });\n } else {\n console.log('[VRP_CLIENT] Message received:', { type: msg.type, channel: msgChannel, myChannel: channel, match: msgChannel === channel });\n }\n }\n switch (msg.type) {\n case 'update':\n case 'event':\n if (msg.channel === channel) {\n setData((prev) => {\n if (!prev) {\n return msg.data as T;\n }\n\n if (typeof prev === 'object' && typeof msg.data === 'object') {\n let mergedData;\n if (useDeepMerge) {\n mergedData = deepMerge(prev as Record<string, any>, msg.data as Record<string, any>) as T;\n } else {\n mergedData = { ...(prev as any), ...(msg.data as any) };\n }\n return mergedData;\n }\n\n return msg.data as T;\n });\n }\n break;\n\n case 'diff':\n if (msg.channel === channel && msg.patch) {\n setData((prev) => {\n if (debug) {\n console.log('[VRP_CLIENT] Applying diff:', { channel, patch: msg.patch, prevKeys: prev && typeof prev === 'object' ? Object.keys(prev as any) : [] });\n }\n if (!prev) {\n return msg.patch as T;\n }\n\n if (typeof prev === 'object' && typeof msg.patch === 'object') {\n const patchObj = msg.patch as Record<string, any>;\n const prevObj = prev as Record<string, any>;\n \n // If patch is empty, ignore it (might be a backend issue)\n const patchKeys = Object.keys(patchObj);\n if (patchKeys.length === 0) {\n if (debug) {\n console.log('[VRP_CLIENT] Empty patch received, ignoring:', { channel });\n }\n return prev;\n }\n \n // Check for deletions first (null values in patch)\n const keysToDelete: string[] = [];\n for (const key in patchObj) {\n if (patchObj[key] === null || patchObj[key] === undefined) {\n keysToDelete.push(key);\n }\n }\n \n // If we have deletions, create new object without deleted keys\n if (keysToDelete.length > 0) {\n if (debug) {\n console.log('[VRP_CLIENT] Processing deletions:', { channel, keysToDelete, prevCount: Object.keys(prevObj).length });\n }\n \n const result: Record<string, any> = {};\n // Copy all keys from prevObj except deleted ones\n for (const key in prevObj) {\n if (!keysToDelete.includes(key)) {\n result[key] = prevObj[key];\n }\n }\n // Apply non-null updates from patch\n for (const key in patchObj) {\n if (!keysToDelete.includes(key) && patchObj[key] !== null && patchObj[key] !== undefined) {\n result[key] = patchObj[key];\n }\n }\n \n if (debug) {\n console.log('[VRP_CLIENT] After deletion:', { channel, resultCount: Object.keys(result).length, deletedCount: keysToDelete.length });\n }\n \n // Force new object reference to trigger React re-render\n return { ...result } as T;\n }\n \n // No deletions, just merge updates\n let mergedData;\n if (useDeepMerge) {\n mergedData = deepMerge(prev as Record<string, any>, patchObj) as T;\n } else {\n mergedData = { ...(prev as any), ...patchObj };\n }\n \n return mergedData;\n }\n\n return prev;\n });\n }\n break;\n }\n };\n\n // --- Pooled mode (default) ---\n if (pool) {\n if (debug) {\n console.log('[VRP_CLIENT] Subscribing to channel:', channel);\n }\n // Subscribe to messages for this channel\n const unsubChannel = pool.subscribe(channel, handleMessage);\n\n // Track shared connection status\n const unsubStatus = pool.onStatus((status: ViraPoolStatus) => {\n setError(status.error);\n setIsConnected(status.connected);\n\n // Fire callbacks only on transitions\n if (status.connected && !wasConnectedRef.current) {\n wasConnectedRef.current = true;\n onOpen?.();\n }\n if (!status.connected && wasConnectedRef.current) {\n wasConnectedRef.current = false;\n // We don't get a real CloseEvent from pooled status, so we pass a lightweight synthetic object\n const synthetic = (typeof CloseEvent !== 'undefined'\n ? new CloseEvent('close', { code: 1001, reason: 'pooled disconnect' })\n : ({ code: 1001, reason: 'pooled disconnect' } as any)) as any;\n onClose?.(synthetic);\n }\n if (status.error) {\n if (lastErrorRef.current !== status.error) {\n lastErrorRef.current = status.error;\n onError?.(status.error);\n }\n } else {\n lastErrorRef.current = null;\n }\n });\n\n // Wire transport for send* APIs\n transportRef.current = {\n sendEvent: (name, payload, msgId) => pool.sendEvent(channel, name, payload, msgId),\n sendUpdate: (payload, msgId) => pool.sendUpdate(channel, payload, msgId),\n sendDiff: (patch, msgId) => pool.sendDiff(channel, patch, msgId),\n };\n\n return () => {\n unsubStatus();\n unsubChannel();\n transportRef.current = null;\n setIsConnected(false);\n setError(null);\n };\n }\n\n // --- Legacy mode (1 WS per channel) ---\n const handleConnect = () => {\n setIsConnected(true);\n setError(null);\n if (!wasConnectedRef.current) {\n wasConnectedRef.current = true;\n }\n onOpen?.();\n };\n\n const handleDisconnect = (event?: CloseEvent) => {\n setIsConnected(false);\n wasConnectedRef.current = false;\n onClose?.(event!);\n };\n\n const handleError = (err: Error) => {\n setError(err);\n onError?.(err);\n };\n\n const client = createViraClient({\n url: apiUrl,\n channel,\n onMessage: handleMessage,\n onConnect: handleConnect,\n onDisconnect: handleDisconnect,\n onError: handleError,\n session: sessionRef.current,\n authToken,\n onSessionChange: (newSession: string | null) => {\n sessionRef.current = newSession;\n },\n });\n\n clientRef.current = client;\n transportRef.current = {\n sendEvent: (name, payload, msgId) => client.sendEvent(name, payload, msgId),\n sendUpdate: (payload, msgId) => client.sendUpdate(payload, msgId),\n sendDiff: (patch, msgId) => client.sendDiff(patch, msgId),\n };\n\n return () => {\n client.close();\n clientRef.current = null;\n transportRef.current = null;\n setIsConnected(false);\n setError(null);\n };\n }, [channel, apiUrl, authToken, onOpen, onClose, onError, useDeepMerge, pool]);\n\n // Generate msgId if enabled\n const generateMsgId = useCallback((): string | undefined => {\n if (!enableMsgId) return undefined;\n msgIdCounterRef.current++;\n return `${channel}:${Date.now()}:${msgIdCounterRef.current}`;\n }, [channel, enableMsgId]);\n\n const sendEvent = useCallback(\n (name: string, payload: any, msgId?: string) => {\n transportRef.current?.sendEvent(name, payload, msgId ?? generateMsgId());\n },\n [generateMsgId]\n );\n\n const sendUpdate = useCallback(\n (payload: T, msgId?: string) => {\n transportRef.current?.sendUpdate(payload, msgId ?? generateMsgId());\n },\n [generateMsgId]\n );\n\n const sendDiff = useCallback(\n (patch: Partial<T>, msgId?: string) => {\n // Apply optimistic update immediately for all changes (creates, updates, deletes)\n if (patch && typeof patch === 'object') {\n const patchObj = patch as Record<string, any>;\n const patchKeys = Object.keys(patchObj);\n if (patchKeys.length > 0) {\n if (debug) {\n console.log('[VRP_CLIENT] Optimistic sendDiff:', { channel, patchKeys });\n }\n setData((prev) => {\n const prevObj = (prev && typeof prev === 'object' ? prev : {}) as Record<string, any>;\n const result: Record<string, any> = { ...prevObj };\n for (const key in patchObj) {\n if (patchObj[key] === null || patchObj[key] === undefined) {\n delete result[key];\n } else if (\n result[key] &&\n typeof result[key] === 'object' &&\n !Array.isArray(result[key]) &&\n typeof patchObj[key] === 'object' &&\n !Array.isArray(patchObj[key])\n ) {\n // Shallow-merge nested objects so partial patches don't wipe existing fields\n // (e.g. list patch { orderId: { status_id } } keeps all other order fields)\n result[key] = { ...result[key], ...patchObj[key] };\n } else {\n result[key] = patchObj[key];\n }\n }\n if (debug) {\n console.log('[VRP_CLIENT] Optimistic sendDiff result:', { channel, resultCount: Object.keys(result).length });\n }\n return result as T;\n });\n }\n }\n transportRef.current?.sendDiff(patch, msgId ?? generateMsgId());\n },\n [generateMsgId, channel, debug]\n );\n\n return {\n data,\n sendEvent,\n sendUpdate,\n sendDiff,\n isConnected,\n error,\n };\n}\n\n/**\n * Legacy hook - use useViraState instead.\n * @deprecated Use useViraState with options instead\n */\nexport function useViraStream<T = any, C extends string = string>(\n channel: C,\n options?: UseViraStateOptions<T>\n) {\n return useViraState<T, C>(channel, { ...options, initial: null });\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vira-ui/react",
3
- "version": "13.0.0",
3
+ "version": "14.0.0",
4
4
  "description": "Vira Framework - React hooks for Vira Reactive Protocol",
5
5
  "author": "Vira Team",
6
6
  "license": "MIT",
@@ -28,7 +28,7 @@
28
28
  },
29
29
  "dependencies": {
30
30
  "react": "^18.2.0",
31
- "@vira-ui/core": "15.0.0"
31
+ "@vira-ui/core": "16.0.0"
32
32
  },
33
33
  "peerDependencies": {
34
34
  "react": "^18.2.0"