@topgunbuild/react 0.7.0 → 0.8.1

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/TopGunProvider.tsx","../src/hooks/useQuery.ts","../src/hooks/useMutation.ts","../src/hooks/useMap.ts","../src/hooks/useORMap.ts","../src/hooks/useTopic.ts","../src/hooks/usePNCounter.ts","../src/hooks/useEntryProcessor.ts","../src/hooks/useEventJournal.ts","../src/hooks/useMergeRejections.ts","../src/hooks/useConflictResolver.ts"],"sourcesContent":["import React, { createContext, useContext, ReactNode } from 'react';\nimport { TopGunClient } from '@topgunbuild/client';\n\nconst TopGunContext = createContext<TopGunClient | null>(null);\n\nexport interface TopGunProviderProps {\n client: TopGunClient;\n children: ReactNode;\n}\n\nexport const TopGunProvider: React.FC<TopGunProviderProps> = ({ client, children }) => {\n return (\n <TopGunContext.Provider value={client}>\n {children}\n </TopGunContext.Provider>\n );\n};\n\nexport function useClient(): TopGunClient {\n const client = useContext(TopGunContext);\n if (!client) {\n throw new Error('useClient must be used within a TopGunProvider');\n }\n return client;\n}\n\n","import { useState, useEffect, useRef, useCallback, useMemo } from 'react';\nimport { QueryFilter, QueryResultItem, ChangeEvent, QueryHandle } from '@topgunbuild/client';\nimport { useClient } from './useClient';\n\n/**\n * Options for useQuery change callbacks (Phase 5.1)\n */\nexport interface UseQueryOptions<T> {\n /** Called for any change event */\n onChange?: (change: ChangeEvent<T>) => void;\n /** Called when an item is added */\n onAdd?: (key: string, value: T) => void;\n /** Called when an item is updated */\n onUpdate?: (key: string, value: T, previous: T) => void;\n /** Called when an item is removed */\n onRemove?: (key: string, previous: T) => void;\n /**\n * Maximum number of changes to accumulate before auto-rotating.\n * When exceeded, oldest changes are removed to prevent memory leaks.\n * Default: 1000\n */\n maxChanges?: number;\n}\n\n/**\n * Result type for useQuery hook with change tracking (Phase 5.1)\n */\nexport interface UseQueryResult<T> {\n /** Current data array */\n data: QueryResultItem<T>[];\n /** Loading state */\n loading: boolean;\n /** Error if query failed */\n error: Error | null;\n /** Last change event (Phase 5.1) */\n lastChange: ChangeEvent<T> | null;\n /** All changes since last clearChanges() call (Phase 5.1) */\n changes: ChangeEvent<T>[];\n /** Clear accumulated changes (Phase 5.1) */\n clearChanges: () => void;\n}\n\n/**\n * React hook for querying data with real-time updates and change tracking.\n *\n * @example Basic usage with change tracking\n * ```tsx\n * function TodoList() {\n * const { data, lastChange } = useQuery<Todo>('todos');\n *\n * useEffect(() => {\n * if (lastChange?.type === 'add') {\n * toast.success(`New todo: ${lastChange.value.title}`);\n * }\n * }, [lastChange]);\n *\n * return <ul>{data.map(todo => <TodoItem key={todo._key} {...todo} />)}</ul>;\n * }\n * ```\n *\n * @example With callback-based notifications\n * ```tsx\n * function NotifyingTodoList() {\n * const { data } = useQuery<Todo>('todos', undefined, {\n * onAdd: (key, todo) => showNotification(`New: ${todo.title}`),\n * onRemove: (key, todo) => showNotification(`Removed: ${todo.title}`)\n * });\n *\n * return <ul>{data.map(todo => <TodoItem key={todo._key} {...todo} />)}</ul>;\n * }\n * ```\n *\n * @example With framer-motion animations\n * ```tsx\n * import { AnimatePresence, motion } from 'framer-motion';\n *\n * function AnimatedTodoList() {\n * const { data } = useQuery<Todo>('todos');\n *\n * return (\n * <AnimatePresence>\n * {data.map(todo => (\n * <motion.li\n * key={todo._key}\n * initial={{ opacity: 0, x: -20 }}\n * animate={{ opacity: 1, x: 0 }}\n * exit={{ opacity: 0, x: 20 }}\n * >\n * {todo.title}\n * </motion.li>\n * ))}\n * </AnimatePresence>\n * );\n * }\n * ```\n */\nexport function useQuery<T = any>(\n mapName: string,\n query: QueryFilter = {},\n options?: UseQueryOptions<T>\n): UseQueryResult<T> {\n const client = useClient();\n const [data, setData] = useState<QueryResultItem<T>[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n // Phase 5.1: Change tracking state\n const [changes, setChanges] = useState<ChangeEvent<T>[]>([]);\n const [lastChange, setLastChange] = useState<ChangeEvent<T> | null>(null);\n\n // Use a ref to track if the component is mounted to avoid state updates on unmounted components\n const isMounted = useRef(true);\n\n // Store handle ref for cleanup\n const handleRef = useRef<QueryHandle<T> | null>(null);\n\n // We serialize the query to use it as a stable dependency for the effect\n const queryJson = JSON.stringify(query);\n\n // Phase 5.1: Clear changes callback\n const clearChanges = useCallback(() => {\n setChanges([]);\n setLastChange(null);\n }, []);\n\n // Memoize options callbacks to avoid unnecessary effect runs\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n useEffect(() => {\n isMounted.current = true;\n setLoading(true);\n\n // Reset changes when query changes\n setChanges([]);\n setLastChange(null);\n\n try {\n const handle = client.query<T>(mapName, query);\n handleRef.current = handle;\n\n // Subscribe to data updates\n const unsubscribeData = handle.subscribe((results) => {\n if (isMounted.current) {\n setData(results);\n setLoading(false);\n }\n });\n\n // Phase 5.1: Subscribe to change events\n const unsubscribeChanges = handle.onChanges((newChanges) => {\n if (!isMounted.current) return;\n\n const maxChanges = optionsRef.current?.maxChanges ?? 1000;\n\n // Accumulate changes with rotation to prevent memory leaks\n setChanges((prev) => {\n const combined = [...prev, ...newChanges];\n // Rotate oldest changes if exceeding limit\n if (combined.length > maxChanges) {\n return combined.slice(-maxChanges);\n }\n return combined;\n });\n\n // Track last change\n if (newChanges.length > 0) {\n setLastChange(newChanges[newChanges.length - 1]);\n }\n\n // Invoke callbacks from options\n const opts = optionsRef.current;\n if (opts) {\n for (const change of newChanges) {\n opts.onChange?.(change);\n\n switch (change.type) {\n case 'add':\n if (change.value !== undefined) {\n opts.onAdd?.(change.key, change.value);\n }\n break;\n case 'update':\n if (change.value !== undefined && change.previousValue !== undefined) {\n opts.onUpdate?.(change.key, change.value, change.previousValue);\n }\n break;\n case 'remove':\n if (change.previousValue !== undefined) {\n opts.onRemove?.(change.key, change.previousValue);\n }\n break;\n }\n }\n }\n });\n\n return () => {\n isMounted.current = false;\n unsubscribeData();\n unsubscribeChanges();\n handleRef.current = null;\n };\n } catch (err) {\n if (isMounted.current) {\n setError(err instanceof Error ? err : new Error(String(err)));\n setLoading(false);\n }\n return () => {\n isMounted.current = false;\n handleRef.current = null;\n };\n }\n }, [client, mapName, queryJson]);\n\n return useMemo(\n () => ({ data, loading, error, lastChange, changes, clearChanges }),\n [data, loading, error, lastChange, changes, clearChanges]\n );\n}\n","import { useCallback } from 'react';\nimport { useClient } from './useClient';\n\nexport interface UseMutationResult<T, K = string> {\n create: (key: K, value: T) => void;\n update: (key: K, value: T) => void;\n remove: (key: K) => void;\n map: any; // Expose map instance if needed\n}\n\nexport function useMutation<T = any, K = string>(mapName: string): UseMutationResult<T, K> {\n const client = useClient();\n // We get the map instance. Note: getMap is synchronous but might trigger async restore.\n // LWWMap is the default assumption for simple mutations.\n const map = client.getMap<K, T>(mapName);\n\n const create = useCallback((key: K, value: T) => {\n map.set(key, value);\n }, [map]);\n\n const update = useCallback((key: K, value: T) => {\n map.set(key, value);\n }, [map]);\n\n const remove = useCallback((key: K) => {\n map.remove(key);\n }, [map]);\n\n return { create, update, remove, map };\n}\n\n","import { useState, useEffect, useRef } from 'react';\nimport { LWWMap } from '@topgunbuild/core';\nimport { useClient } from './useClient';\n\nexport function useMap<K = string, V = any>(mapName: string): LWWMap<K, V> {\n const client = useClient();\n // Get the map instance. This is stable for the same mapName.\n const map = client.getMap<K, V>(mapName);\n\n // We use a dummy state to trigger re-renders when the map changes\n const [, setTick] = useState(0);\n const isMounted = useRef(true);\n\n useEffect(() => {\n isMounted.current = true;\n\n // Subscribe to map changes\n const unsubscribe = map.onChange(() => {\n if (isMounted.current) {\n setTick(t => t + 1);\n }\n });\n\n return () => {\n isMounted.current = false;\n unsubscribe();\n };\n }, [map]);\n\n return map;\n}\n","import { useState, useEffect, useRef } from 'react';\nimport { ORMap } from '@topgunbuild/core';\nimport { useClient } from './useClient';\n\nexport function useORMap<K = string, V = any>(mapName: string): ORMap<K, V> {\n const client = useClient();\n const map = client.getORMap<K, V>(mapName);\n\n const [, setTick] = useState(0);\n const isMounted = useRef(true);\n\n useEffect(() => {\n isMounted.current = true;\n\n const unsubscribe = map.onChange(() => {\n if (isMounted.current) {\n setTick(t => t + 1);\n }\n });\n\n return () => {\n isMounted.current = false;\n unsubscribe();\n };\n }, [map]);\n\n return map;\n}\n","import { useEffect, useRef } from 'react';\nimport { TopicCallback } from '@topgunbuild/client';\nimport { useClient } from './useClient';\n\nexport function useTopic(topicName: string, callback?: TopicCallback) {\n const client = useClient();\n const topic = client.topic(topicName);\n const isMounted = useRef(true);\n\n // Keep callback ref stable to avoid re-subscribing if callback function identity changes\n const callbackRef = useRef(callback);\n useEffect(() => {\n callbackRef.current = callback;\n }, [callback]);\n\n useEffect(() => {\n isMounted.current = true;\n\n if (!callback) return;\n\n const unsubscribe = topic.subscribe((data, context) => {\n if (isMounted.current && callbackRef.current) {\n callbackRef.current(data, context);\n }\n });\n\n return () => {\n isMounted.current = false;\n unsubscribe();\n };\n }, [topic, callback]); // Re-subscribe if topic handle changes (rare) or if callback presence toggles\n\n return topic;\n}\n","import { useState, useEffect, useCallback, useMemo } from 'react';\nimport { useClient } from './useClient';\n\n/**\n * Result type for usePNCounter hook.\n */\nexport interface UsePNCounterResult {\n /** Current counter value */\n value: number;\n /** Increment the counter by 1 */\n increment: () => void;\n /** Decrement the counter by 1 */\n decrement: () => void;\n /** Add delta (positive or negative) to the counter */\n add: (delta: number) => void;\n /** Loading state (true until first value received) */\n loading: boolean;\n}\n\n/**\n * React hook for using a PN Counter with real-time updates.\n *\n * PN Counters support increment and decrement operations that work offline\n * and sync to server when connected. They guarantee convergence across\n * distributed nodes without coordination.\n *\n * @param name The counter name (e.g., 'likes:post-123')\n * @returns Counter value and methods\n *\n * @example Basic usage\n * ```tsx\n * function LikeButton({ postId }: { postId: string }) {\n * const { value, increment } = usePNCounter(`likes:${postId}`);\n *\n * return (\n * <button onClick={increment}>\n * ❤️ {value}\n * </button>\n * );\n * }\n * ```\n *\n * @example Inventory control\n * ```tsx\n * function InventoryControl({ productId }: { productId: string }) {\n * const { value, increment, decrement } = usePNCounter(`inventory:${productId}`);\n *\n * return (\n * <div>\n * <span>Stock: {value}</span>\n * <button onClick={decrement} disabled={value <= 0}>-</button>\n * <button onClick={increment}>+</button>\n * </div>\n * );\n * }\n * ```\n *\n * @example Bulk operations\n * ```tsx\n * function BulkAdd({ counterId }: { counterId: string }) {\n * const { value, add } = usePNCounter(counterId);\n * const [amount, setAmount] = useState(10);\n *\n * return (\n * <div>\n * <span>Value: {value}</span>\n * <input\n * type=\"number\"\n * value={amount}\n * onChange={(e) => setAmount(parseInt(e.target.value))}\n * />\n * <button onClick={() => add(amount)}>Add {amount}</button>\n * <button onClick={() => add(-amount)}>Subtract {amount}</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function usePNCounter(name: string): UsePNCounterResult {\n const client = useClient();\n const [value, setValue] = useState(0);\n const [loading, setLoading] = useState(true);\n\n // Get or create counter handle - memoized by name\n const counter = useMemo(() => {\n return client.getPNCounter(name);\n }, [client, name]);\n\n useEffect(() => {\n // Reset state when counter changes\n setLoading(true);\n\n const unsubscribe = counter.subscribe((newValue) => {\n setValue(newValue);\n setLoading(false);\n });\n\n return unsubscribe;\n }, [counter]);\n\n const increment = useCallback(() => {\n counter.increment();\n }, [counter]);\n\n const decrement = useCallback(() => {\n counter.decrement();\n }, [counter]);\n\n const add = useCallback((delta: number) => {\n counter.addAndGet(delta);\n }, [counter]);\n\n return useMemo(\n () => ({ value, increment, decrement, add, loading }),\n [value, increment, decrement, add, loading]\n );\n}\n","import { useState, useCallback, useMemo } from 'react';\nimport { useClient } from './useClient';\nimport type { EntryProcessorDef, EntryProcessorResult } from '@topgunbuild/core';\n\n/**\n * Options for the useEntryProcessor hook.\n */\nexport interface UseEntryProcessorOptions {\n /**\n * Number of retry attempts on failure.\n * Default: 0 (no retries)\n */\n retries?: number;\n\n /**\n * Delay between retries in milliseconds.\n * Default: 100ms, doubles with each retry (exponential backoff)\n */\n retryDelayMs?: number;\n}\n\n/**\n * Result type for useEntryProcessor hook.\n */\nexport interface UseEntryProcessorResult<R> {\n /**\n * Execute the processor on a key.\n * @param key The key to process\n * @param args Optional arguments to pass to the processor\n */\n execute: (key: string, args?: unknown) => Promise<EntryProcessorResult<R>>;\n\n /**\n * Execute the processor on multiple keys.\n * @param keys The keys to process\n * @param args Optional arguments to pass to the processor\n */\n executeMany: (keys: string[], args?: unknown) => Promise<Map<string, EntryProcessorResult<R>>>;\n\n /** True while a processor is executing */\n executing: boolean;\n\n /** Last execution result (single key) */\n lastResult: EntryProcessorResult<R> | null;\n\n /** Last error encountered */\n error: Error | null;\n\n /** Reset the hook state (clears lastResult and error) */\n reset: () => void;\n}\n\n/**\n * React hook for executing entry processors with loading and error states.\n *\n * Entry processors execute user-defined logic atomically on the server,\n * solving the read-modify-write race condition.\n *\n * @param mapName Name of the map to operate on\n * @param processorDef Processor definition (without args - args are passed per-execution)\n * @param options Optional configuration\n * @returns Execute function and state\n *\n * @example Basic increment\n * ```tsx\n * function LikeButton({ postId }: { postId: string }) {\n * const { execute, executing } = useEntryProcessor<number>('likes', {\n * name: 'increment',\n * code: `\n * const current = value ?? 0;\n * return { value: current + 1, result: current + 1 };\n * `,\n * });\n *\n * const handleLike = async () => {\n * const result = await execute(postId);\n * if (result.success) {\n * console.log('New like count:', result.result);\n * }\n * };\n *\n * return (\n * <button onClick={handleLike} disabled={executing}>\n * {executing ? '...' : 'Like'}\n * </button>\n * );\n * }\n * ```\n *\n * @example Inventory reservation with args\n * ```tsx\n * function ReserveButton({ productId }: { productId: string }) {\n * const { execute, executing, error } = useEntryProcessor<\n * { stock: number; reserved: string[] },\n * { success: boolean; remaining: number }\n * >('inventory', {\n * name: 'reserve_item',\n * code: `\n * if (!value || value.stock <= 0) {\n * return { value, result: { success: false, remaining: 0 } };\n * }\n * const newValue = {\n * ...value,\n * stock: value.stock - 1,\n * reserved: [...value.reserved, args.userId],\n * };\n * return {\n * value: newValue,\n * result: { success: true, remaining: newValue.stock }\n * };\n * `,\n * });\n *\n * const handleReserve = async () => {\n * const result = await execute(productId, { userId: currentUser.id });\n * if (result.success && result.result?.success) {\n * toast.success(`Reserved! ${result.result.remaining} left`);\n * } else {\n * toast.error('Out of stock');\n * }\n * };\n *\n * return (\n * <button onClick={handleReserve} disabled={executing}>\n * {executing ? 'Reserving...' : 'Reserve'}\n * </button>\n * );\n * }\n * ```\n *\n * @example Using built-in processor\n * ```tsx\n * import { BuiltInProcessors } from '@topgunbuild/core';\n *\n * function DecrementStock({ productId }: { productId: string }) {\n * const processorDef = useMemo(\n * () => BuiltInProcessors.DECREMENT_FLOOR(1),\n * []\n * );\n *\n * const { execute, executing, lastResult } = useEntryProcessor<\n * number,\n * { newValue: number; wasFloored: boolean }\n * >('stock', processorDef);\n *\n * const handleDecrement = async () => {\n * const result = await execute(productId);\n * if (result.result?.wasFloored) {\n * alert('Stock is now at zero!');\n * }\n * };\n *\n * return (\n * <button onClick={handleDecrement} disabled={executing}>\n * Decrease Stock\n * </button>\n * );\n * }\n * ```\n */\nexport function useEntryProcessor<V = unknown, R = V>(\n mapName: string,\n processorDef: Omit<EntryProcessorDef<V, R>, 'args'>,\n options: UseEntryProcessorOptions = {},\n): UseEntryProcessorResult<R> {\n const client = useClient();\n const [executing, setExecuting] = useState(false);\n const [lastResult, setLastResult] = useState<EntryProcessorResult<R> | null>(null);\n const [error, setError] = useState<Error | null>(null);\n\n const { retries = 0, retryDelayMs = 100 } = options;\n\n const execute = useCallback(\n async (key: string, args?: unknown): Promise<EntryProcessorResult<R>> => {\n setExecuting(true);\n setError(null);\n\n const processor: EntryProcessorDef<V, R> = {\n ...processorDef,\n args,\n } as EntryProcessorDef<V, R>;\n\n let attempts = 0;\n let lastError: Error | null = null;\n\n while (attempts <= retries) {\n try {\n const result = await client.executeOnKey<V, R>(mapName, key, processor);\n setLastResult(result);\n setExecuting(false);\n return result;\n } catch (err) {\n lastError = err instanceof Error ? err : new Error(String(err));\n attempts++;\n\n if (attempts <= retries) {\n // Exponential backoff\n const delay = retryDelayMs * Math.pow(2, attempts - 1);\n await new Promise((r) => setTimeout(r, delay));\n }\n }\n }\n\n // All retries exhausted\n setError(lastError);\n setExecuting(false);\n throw lastError;\n },\n [client, mapName, processorDef, retries, retryDelayMs],\n );\n\n const executeMany = useCallback(\n async (keys: string[], args?: unknown): Promise<Map<string, EntryProcessorResult<R>>> => {\n setExecuting(true);\n setError(null);\n\n const processor: EntryProcessorDef<V, R> = {\n ...processorDef,\n args,\n } as EntryProcessorDef<V, R>;\n\n try {\n const results = await client.executeOnKeys<V, R>(mapName, keys, processor);\n setExecuting(false);\n return results;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setError(error);\n setExecuting(false);\n throw error;\n }\n },\n [client, mapName, processorDef],\n );\n\n const reset = useCallback(() => {\n setLastResult(null);\n setError(null);\n }, []);\n\n return useMemo(\n () => ({ execute, executeMany, executing, lastResult, error, reset }),\n [execute, executeMany, executing, lastResult, error, reset],\n );\n}\n","import { useState, useEffect, useRef, useCallback, useMemo } from 'react';\nimport type { JournalEvent, JournalEventType } from '@topgunbuild/core';\nimport type { EventJournalReader, JournalSubscribeOptions } from '@topgunbuild/client';\nimport { useClient } from './useClient';\n\n/**\n * Options for useEventJournal hook.\n */\nexport interface UseEventJournalOptions {\n /** Start from specific sequence */\n fromSequence?: bigint;\n /** Filter by map name */\n mapName?: string;\n /** Filter by event types */\n types?: JournalEventType[];\n /** Maximum events to keep in state (default: 100) */\n maxEvents?: number;\n /** Called when new event is received */\n onEvent?: (event: JournalEvent) => void;\n /** Pause subscription */\n paused?: boolean;\n}\n\n/**\n * Result type for useEventJournal hook.\n */\nexport interface UseEventJournalResult {\n /** Array of recent events (newest last) */\n events: JournalEvent[];\n /** Last received event */\n lastEvent: JournalEvent | null;\n /** Clear accumulated events */\n clearEvents: () => void;\n /** Read historical events from sequence */\n readFrom: (sequence: bigint, limit?: number) => Promise<JournalEvent[]>;\n /** Get latest sequence number */\n getLatestSequence: () => Promise<bigint>;\n /** Whether subscription is active */\n isSubscribed: boolean;\n}\n\n/**\n * React hook for subscribing to Event Journal changes.\n *\n * The Event Journal captures all map changes (PUT, UPDATE, DELETE) as an\n * append-only log, useful for:\n * - Real-time activity feeds\n * - Audit trails\n * - Change notifications\n * - Debugging and monitoring\n *\n * @example Basic usage - show all changes\n * ```tsx\n * function ActivityFeed() {\n * const { events, lastEvent } = useEventJournal();\n *\n * return (\n * <ul>\n * {events.map((e) => (\n * <li key={e.sequence.toString()}>\n * {e.type} {e.mapName}:{e.key}\n * </li>\n * ))}\n * </ul>\n * );\n * }\n * ```\n *\n * @example Filter by map name\n * ```tsx\n * function UserActivityFeed() {\n * const { events } = useEventJournal({ mapName: 'users' });\n *\n * return (\n * <ul>\n * {events.map((e) => (\n * <li key={e.sequence.toString()}>\n * User {e.key}: {e.type}\n * </li>\n * ))}\n * </ul>\n * );\n * }\n * ```\n *\n * @example With event callback\n * ```tsx\n * function NotifyingComponent() {\n * const { events } = useEventJournal({\n * mapName: 'orders',\n * types: ['PUT'],\n * onEvent: (event) => {\n * toast.success(`New order: ${event.key}`);\n * },\n * });\n *\n * return <OrderList events={events} />;\n * }\n * ```\n */\nexport function useEventJournal(\n options: UseEventJournalOptions = {}\n): UseEventJournalResult {\n const client = useClient();\n const [events, setEvents] = useState<JournalEvent[]>([]);\n const [lastEvent, setLastEvent] = useState<JournalEvent | null>(null);\n const [isSubscribed, setIsSubscribed] = useState(false);\n\n const isMounted = useRef(true);\n const journalRef = useRef<EventJournalReader | null>(null);\n\n const maxEvents = options.maxEvents ?? 100;\n\n // Store options in ref to avoid re-subscription on every render\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n // Clear events callback\n const clearEvents = useCallback(() => {\n setEvents([]);\n setLastEvent(null);\n }, []);\n\n // Read historical events\n const readFrom = useCallback(\n async (sequence: bigint, limit?: number): Promise<JournalEvent[]> => {\n if (!journalRef.current) {\n journalRef.current = client.getEventJournal();\n }\n return journalRef.current.readFrom(sequence, limit);\n },\n [client]\n );\n\n // Get latest sequence\n const getLatestSequence = useCallback(async (): Promise<bigint> => {\n if (!journalRef.current) {\n journalRef.current = client.getEventJournal();\n }\n return journalRef.current.getLatestSequence();\n }, [client]);\n\n // Serialize filter options for dependency comparison\n const filterKey = useMemo(\n () =>\n JSON.stringify({\n mapName: options.mapName,\n types: options.types,\n fromSequence: options.fromSequence?.toString(),\n paused: options.paused,\n }),\n [options.mapName, options.types, options.fromSequence, options.paused]\n );\n\n useEffect(() => {\n isMounted.current = true;\n\n // Don't subscribe if paused\n if (options.paused) {\n setIsSubscribed(false);\n return;\n }\n\n const journal = client.getEventJournal();\n journalRef.current = journal;\n\n const subscribeOptions: JournalSubscribeOptions = {\n fromSequence: options.fromSequence,\n mapName: options.mapName,\n types: options.types,\n };\n\n const unsubscribe = journal.subscribe((event) => {\n if (!isMounted.current) return;\n\n // Add to events with rotation\n setEvents((prev) => {\n const newEvents = [...prev, event];\n if (newEvents.length > maxEvents) {\n return newEvents.slice(-maxEvents);\n }\n return newEvents;\n });\n\n setLastEvent(event);\n\n // Call event callback\n optionsRef.current.onEvent?.(event);\n }, subscribeOptions);\n\n setIsSubscribed(true);\n\n return () => {\n isMounted.current = false;\n setIsSubscribed(false);\n unsubscribe();\n };\n }, [client, filterKey, maxEvents]);\n\n return useMemo(\n () => ({\n events,\n lastEvent,\n clearEvents,\n readFrom,\n getLatestSequence,\n isSubscribed,\n }),\n [events, lastEvent, clearEvents, readFrom, getLatestSequence, isSubscribed]\n );\n}\n","import { useState, useEffect, useCallback } from 'react';\nimport { useClient } from './useClient';\nimport type { MergeRejection } from '@topgunbuild/core';\n\n/**\n * Options for useMergeRejections hook.\n */\nexport interface UseMergeRejectionsOptions {\n /** Filter rejections by map name (optional) */\n mapName?: string;\n\n /** Maximum number of rejections to keep in history */\n maxHistory?: number;\n}\n\n/**\n * Result type for useMergeRejections hook.\n */\nexport interface UseMergeRejectionsResult {\n /** List of recent merge rejections */\n rejections: MergeRejection[];\n\n /** Last rejection received */\n lastRejection: MergeRejection | null;\n\n /** Clear rejection history */\n clear: () => void;\n}\n\n/**\n * React hook for subscribing to merge rejection events.\n *\n * Merge rejections occur when a custom conflict resolver rejects\n * a client's write operation. This hook allows you to:\n * - Display rejection notifications to users\n * - Refresh local state after rejection\n * - Log conflicts for debugging\n *\n * @param options Optional filtering and configuration\n * @returns Rejection list and utilities\n *\n * @example Show rejection notifications\n * ```tsx\n * function BookingForm() {\n * const { lastRejection, clear } = useMergeRejections({\n * mapName: 'bookings'\n * });\n *\n * useEffect(() => {\n * if (lastRejection) {\n * toast.error(`Booking failed: ${lastRejection.reason}`);\n * clear(); // Clear after showing notification\n * }\n * }, [lastRejection]);\n *\n * return <form>...</form>;\n * }\n * ```\n *\n * @example Track all rejections\n * ```tsx\n * function ConflictLog() {\n * const { rejections } = useMergeRejections({ maxHistory: 50 });\n *\n * return (\n * <ul>\n * {rejections.map((r, i) => (\n * <li key={i}>\n * {r.mapName}/{r.key}: {r.reason}\n * </li>\n * ))}\n * </ul>\n * );\n * }\n * ```\n */\nexport function useMergeRejections(\n options: UseMergeRejectionsOptions = {},\n): UseMergeRejectionsResult {\n const client = useClient();\n const { mapName, maxHistory = 100 } = options;\n\n const [rejections, setRejections] = useState<MergeRejection[]>([]);\n const [lastRejection, setLastRejection] = useState<MergeRejection | null>(null);\n\n useEffect(() => {\n const resolvers = client.getConflictResolvers();\n\n const unsubscribe = resolvers.onRejection((rejection) => {\n // Filter by map name if specified\n if (mapName && rejection.mapName !== mapName) {\n return;\n }\n\n setLastRejection(rejection);\n setRejections((prev) => {\n const next = [...prev, rejection];\n // Limit history size\n if (next.length > maxHistory) {\n return next.slice(-maxHistory);\n }\n return next;\n });\n });\n\n return unsubscribe;\n }, [client, mapName, maxHistory]);\n\n const clear = useCallback(() => {\n setRejections([]);\n setLastRejection(null);\n }, []);\n\n return {\n rejections,\n lastRejection,\n clear,\n };\n}\n","import { useState, useCallback, useEffect } from 'react';\nimport { useClient } from './useClient';\nimport type { ConflictResolverDef } from '@topgunbuild/core';\nimport type { ResolverInfo, RegisterResult } from '@topgunbuild/client';\n\n/**\n * Options for useConflictResolver hook.\n */\nexport interface UseConflictResolverOptions {\n /** Auto-unregister resolver on unmount (default: true) */\n autoUnregister?: boolean;\n}\n\n/**\n * Result type for useConflictResolver hook.\n */\nexport interface UseConflictResolverResult {\n /**\n * Register a conflict resolver on the server.\n * @param resolver The resolver definition\n */\n register: (resolver: Omit<ConflictResolverDef, 'fn'>) => Promise<RegisterResult>;\n\n /**\n * Unregister a resolver by name.\n * @param resolverName Name of the resolver to unregister\n */\n unregister: (resolverName: string) => Promise<RegisterResult>;\n\n /**\n * List all registered resolvers for this map.\n */\n list: () => Promise<ResolverInfo[]>;\n\n /** True while a registration/unregistration is in progress */\n loading: boolean;\n\n /** Last error encountered */\n error: Error | null;\n\n /** List of resolvers registered by this hook instance */\n registered: string[];\n}\n\n/**\n * React hook for managing conflict resolvers on a specific map.\n *\n * Conflict resolvers allow you to customize how merge conflicts are handled\n * on the server. This hook provides a convenient way to:\n * - Register custom resolvers\n * - Auto-unregister on component unmount\n * - Track registration state\n *\n * @param mapName Name of the map to manage resolvers for\n * @param options Optional configuration\n * @returns Resolver management functions and state\n *\n * @example First-write-wins for bookings\n * ```tsx\n * function BookingManager() {\n * const { register, registered, loading, error } = useConflictResolver('bookings');\n *\n * useEffect(() => {\n * // Register resolver on mount\n * register({\n * name: 'first-write-wins',\n * code: `\n * if (context.localValue !== undefined) {\n * return { action: 'reject', reason: 'Already booked' };\n * }\n * return { action: 'accept', value: context.remoteValue };\n * `,\n * priority: 100,\n * });\n * }, []);\n *\n * return (\n * <div>\n * {loading && <span>Registering...</span>}\n * {error && <span>Error: {error.message}</span>}\n * <ul>\n * {registered.map(name => <li key={name}>{name}</li>)}\n * </ul>\n * </div>\n * );\n * }\n * ```\n *\n * @example Numeric constraints\n * ```tsx\n * function InventorySettings() {\n * const { register } = useConflictResolver('inventory');\n *\n * const enableNonNegative = async () => {\n * await register({\n * name: 'non-negative',\n * code: `\n * if (context.remoteValue < 0) {\n * return { action: 'reject', reason: 'Stock cannot be negative' };\n * }\n * return { action: 'accept', value: context.remoteValue };\n * `,\n * priority: 90,\n * keyPattern: 'stock:*',\n * });\n * };\n *\n * return <button onClick={enableNonNegative}>Enable Stock Protection</button>;\n * }\n * ```\n */\nexport function useConflictResolver(\n mapName: string,\n options: UseConflictResolverOptions = {},\n): UseConflictResolverResult {\n const client = useClient();\n const { autoUnregister = true } = options;\n\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [registered, setRegistered] = useState<string[]>([]);\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n if (autoUnregister && registered.length > 0) {\n const resolvers = client.getConflictResolvers();\n // Fire-and-forget unregistration\n for (const name of registered) {\n resolvers.unregister(mapName, name).catch(() => {\n // Ignore errors on cleanup\n });\n }\n }\n };\n }, [client, mapName, autoUnregister, registered]);\n\n const register = useCallback(\n async (resolver: Omit<ConflictResolverDef, 'fn'>): Promise<RegisterResult> => {\n setLoading(true);\n setError(null);\n\n try {\n const resolvers = client.getConflictResolvers();\n const result = await resolvers.register(mapName, resolver);\n\n if (result.success) {\n setRegistered((prev) => {\n if (prev.includes(resolver.name)) {\n return prev;\n }\n return [...prev, resolver.name];\n });\n } else if (result.error) {\n setError(new Error(result.error));\n }\n\n return result;\n } catch (e) {\n const err = e instanceof Error ? e : new Error(String(e));\n setError(err);\n return { success: false, error: err.message };\n } finally {\n setLoading(false);\n }\n },\n [client, mapName],\n );\n\n const unregister = useCallback(\n async (resolverName: string): Promise<RegisterResult> => {\n setLoading(true);\n setError(null);\n\n try {\n const resolvers = client.getConflictResolvers();\n const result = await resolvers.unregister(mapName, resolverName);\n\n if (result.success) {\n setRegistered((prev) => prev.filter((n) => n !== resolverName));\n } else if (result.error) {\n setError(new Error(result.error));\n }\n\n return result;\n } catch (e) {\n const err = e instanceof Error ? e : new Error(String(e));\n setError(err);\n return { success: false, error: err.message };\n } finally {\n setLoading(false);\n }\n },\n [client, mapName],\n );\n\n const list = useCallback(async (): Promise<ResolverInfo[]> => {\n try {\n const resolvers = client.getConflictResolvers();\n return await resolvers.list(mapName);\n } catch (e) {\n const err = e instanceof Error ? e : new Error(String(e));\n setError(err);\n return [];\n }\n }, [client, mapName]);\n\n return {\n register,\n unregister,\n list,\n loading,\n error,\n registered,\n };\n}\n"],"mappings":"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","names":["useCallback","useCallback","useState","useEffect","useRef","useState","useRef","useEffect","useState","useEffect","useRef","useState","useRef","useEffect","useEffect","useRef","useRef","useEffect","useState","useEffect","useCallback","useMemo","useState","useMemo","useEffect","useCallback","useState","useCallback","useMemo","useState","useCallback","error","useMemo","useState","useEffect","useRef","useCallback","useMemo","useState","useRef","useCallback","useMemo","useEffect","useState","useEffect","useCallback","useState","useEffect","useCallback","useState","useCallback","useEffect","useState","useEffect","useCallback"]}
1
+ {"version":3,"sources":["../src/TopGunProvider.tsx","../src/hooks/useQuery.ts","../src/hooks/useMutation.ts","../src/hooks/useMap.ts","../src/hooks/useORMap.ts","../src/hooks/useTopic.ts","../src/hooks/usePNCounter.ts","../src/hooks/useEntryProcessor.ts","../src/hooks/useEventJournal.ts","../src/hooks/useMergeRejections.ts","../src/hooks/useConflictResolver.ts","../src/hooks/useSearch.ts","../src/hooks/useHybridQuery.ts"],"sourcesContent":["import React, { createContext, useContext, ReactNode } from 'react';\nimport { TopGunClient } from '@topgunbuild/client';\n\nconst TopGunContext = createContext<TopGunClient | null>(null);\n\nexport interface TopGunProviderProps {\n client: TopGunClient;\n children: ReactNode;\n}\n\nexport const TopGunProvider: React.FC<TopGunProviderProps> = ({ client, children }) => {\n return (\n <TopGunContext.Provider value={client}>\n {children}\n </TopGunContext.Provider>\n );\n};\n\nexport function useClient(): TopGunClient {\n const client = useContext(TopGunContext);\n if (!client) {\n throw new Error('useClient must be used within a TopGunProvider');\n }\n return client;\n}\n\n","import { useState, useEffect, useRef, useCallback, useMemo } from 'react';\nimport { QueryFilter, QueryResultItem, ChangeEvent, QueryHandle } from '@topgunbuild/client';\nimport { useClient } from './useClient';\n\n/**\n * Options for useQuery change callbacks (Phase 5.1)\n */\nexport interface UseQueryOptions<T> {\n /** Called for any change event */\n onChange?: (change: ChangeEvent<T>) => void;\n /** Called when an item is added */\n onAdd?: (key: string, value: T) => void;\n /** Called when an item is updated */\n onUpdate?: (key: string, value: T, previous: T) => void;\n /** Called when an item is removed */\n onRemove?: (key: string, previous: T) => void;\n /**\n * Maximum number of changes to accumulate before auto-rotating.\n * When exceeded, oldest changes are removed to prevent memory leaks.\n * Default: 1000\n */\n maxChanges?: number;\n}\n\n/**\n * Result type for useQuery hook with change tracking (Phase 5.1)\n */\nexport interface UseQueryResult<T> {\n /** Current data array */\n data: QueryResultItem<T>[];\n /** Loading state */\n loading: boolean;\n /** Error if query failed */\n error: Error | null;\n /** Last change event (Phase 5.1) */\n lastChange: ChangeEvent<T> | null;\n /** All changes since last clearChanges() call (Phase 5.1) */\n changes: ChangeEvent<T>[];\n /** Clear accumulated changes (Phase 5.1) */\n clearChanges: () => void;\n}\n\n/**\n * React hook for querying data with real-time updates and change tracking.\n *\n * @example Basic usage with change tracking\n * ```tsx\n * function TodoList() {\n * const { data, lastChange } = useQuery<Todo>('todos');\n *\n * useEffect(() => {\n * if (lastChange?.type === 'add') {\n * toast.success(`New todo: ${lastChange.value.title}`);\n * }\n * }, [lastChange]);\n *\n * return <ul>{data.map(todo => <TodoItem key={todo._key} {...todo} />)}</ul>;\n * }\n * ```\n *\n * @example With callback-based notifications\n * ```tsx\n * function NotifyingTodoList() {\n * const { data } = useQuery<Todo>('todos', undefined, {\n * onAdd: (key, todo) => showNotification(`New: ${todo.title}`),\n * onRemove: (key, todo) => showNotification(`Removed: ${todo.title}`)\n * });\n *\n * return <ul>{data.map(todo => <TodoItem key={todo._key} {...todo} />)}</ul>;\n * }\n * ```\n *\n * @example With framer-motion animations\n * ```tsx\n * import { AnimatePresence, motion } from 'framer-motion';\n *\n * function AnimatedTodoList() {\n * const { data } = useQuery<Todo>('todos');\n *\n * return (\n * <AnimatePresence>\n * {data.map(todo => (\n * <motion.li\n * key={todo._key}\n * initial={{ opacity: 0, x: -20 }}\n * animate={{ opacity: 1, x: 0 }}\n * exit={{ opacity: 0, x: 20 }}\n * >\n * {todo.title}\n * </motion.li>\n * ))}\n * </AnimatePresence>\n * );\n * }\n * ```\n */\nexport function useQuery<T = any>(\n mapName: string,\n query: QueryFilter = {},\n options?: UseQueryOptions<T>\n): UseQueryResult<T> {\n const client = useClient();\n const [data, setData] = useState<QueryResultItem<T>[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n // Phase 5.1: Change tracking state\n const [changes, setChanges] = useState<ChangeEvent<T>[]>([]);\n const [lastChange, setLastChange] = useState<ChangeEvent<T> | null>(null);\n\n // Use a ref to track if the component is mounted to avoid state updates on unmounted components\n const isMounted = useRef(true);\n\n // Store handle ref for cleanup\n const handleRef = useRef<QueryHandle<T> | null>(null);\n\n // We serialize the query to use it as a stable dependency for the effect\n const queryJson = JSON.stringify(query);\n\n // Phase 5.1: Clear changes callback\n const clearChanges = useCallback(() => {\n setChanges([]);\n setLastChange(null);\n }, []);\n\n // Memoize options callbacks to avoid unnecessary effect runs\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n useEffect(() => {\n isMounted.current = true;\n setLoading(true);\n\n // Reset changes when query changes\n setChanges([]);\n setLastChange(null);\n\n try {\n const handle = client.query<T>(mapName, query);\n handleRef.current = handle;\n\n // Subscribe to data updates\n const unsubscribeData = handle.subscribe((results) => {\n if (isMounted.current) {\n setData(results);\n setLoading(false);\n }\n });\n\n // Phase 5.1: Subscribe to change events\n const unsubscribeChanges = handle.onChanges((newChanges) => {\n if (!isMounted.current) return;\n\n const maxChanges = optionsRef.current?.maxChanges ?? 1000;\n\n // Accumulate changes with rotation to prevent memory leaks\n setChanges((prev) => {\n const combined = [...prev, ...newChanges];\n // Rotate oldest changes if exceeding limit\n if (combined.length > maxChanges) {\n return combined.slice(-maxChanges);\n }\n return combined;\n });\n\n // Track last change\n if (newChanges.length > 0) {\n setLastChange(newChanges[newChanges.length - 1]);\n }\n\n // Invoke callbacks from options\n const opts = optionsRef.current;\n if (opts) {\n for (const change of newChanges) {\n opts.onChange?.(change);\n\n switch (change.type) {\n case 'add':\n if (change.value !== undefined) {\n opts.onAdd?.(change.key, change.value);\n }\n break;\n case 'update':\n if (change.value !== undefined && change.previousValue !== undefined) {\n opts.onUpdate?.(change.key, change.value, change.previousValue);\n }\n break;\n case 'remove':\n if (change.previousValue !== undefined) {\n opts.onRemove?.(change.key, change.previousValue);\n }\n break;\n }\n }\n }\n });\n\n return () => {\n isMounted.current = false;\n unsubscribeData();\n unsubscribeChanges();\n handleRef.current = null;\n };\n } catch (err) {\n if (isMounted.current) {\n setError(err instanceof Error ? err : new Error(String(err)));\n setLoading(false);\n }\n return () => {\n isMounted.current = false;\n handleRef.current = null;\n };\n }\n }, [client, mapName, queryJson]);\n\n return useMemo(\n () => ({ data, loading, error, lastChange, changes, clearChanges }),\n [data, loading, error, lastChange, changes, clearChanges]\n );\n}\n","import { useCallback } from 'react';\nimport { useClient } from './useClient';\n\nexport interface UseMutationResult<T, K = string> {\n create: (key: K, value: T) => void;\n update: (key: K, value: T) => void;\n remove: (key: K) => void;\n map: any; // Expose map instance if needed\n}\n\nexport function useMutation<T = any, K = string>(mapName: string): UseMutationResult<T, K> {\n const client = useClient();\n // We get the map instance. Note: getMap is synchronous but might trigger async restore.\n // LWWMap is the default assumption for simple mutations.\n const map = client.getMap<K, T>(mapName);\n\n const create = useCallback((key: K, value: T) => {\n map.set(key, value);\n }, [map]);\n\n const update = useCallback((key: K, value: T) => {\n map.set(key, value);\n }, [map]);\n\n const remove = useCallback((key: K) => {\n map.remove(key);\n }, [map]);\n\n return { create, update, remove, map };\n}\n\n","import { useState, useEffect, useRef } from 'react';\nimport { LWWMap } from '@topgunbuild/core';\nimport { useClient } from './useClient';\n\nexport function useMap<K = string, V = any>(mapName: string): LWWMap<K, V> {\n const client = useClient();\n // Get the map instance. This is stable for the same mapName.\n const map = client.getMap<K, V>(mapName);\n\n // We use a dummy state to trigger re-renders when the map changes\n const [, setTick] = useState(0);\n const isMounted = useRef(true);\n\n useEffect(() => {\n isMounted.current = true;\n\n // Subscribe to map changes\n const unsubscribe = map.onChange(() => {\n if (isMounted.current) {\n setTick(t => t + 1);\n }\n });\n\n return () => {\n isMounted.current = false;\n unsubscribe();\n };\n }, [map]);\n\n return map;\n}\n","import { useState, useEffect, useRef } from 'react';\nimport { ORMap } from '@topgunbuild/core';\nimport { useClient } from './useClient';\n\nexport function useORMap<K = string, V = any>(mapName: string): ORMap<K, V> {\n const client = useClient();\n const map = client.getORMap<K, V>(mapName);\n\n const [, setTick] = useState(0);\n const isMounted = useRef(true);\n\n useEffect(() => {\n isMounted.current = true;\n\n const unsubscribe = map.onChange(() => {\n if (isMounted.current) {\n setTick(t => t + 1);\n }\n });\n\n return () => {\n isMounted.current = false;\n unsubscribe();\n };\n }, [map]);\n\n return map;\n}\n","import { useEffect, useRef } from 'react';\nimport { TopicCallback } from '@topgunbuild/client';\nimport { useClient } from './useClient';\n\nexport function useTopic(topicName: string, callback?: TopicCallback) {\n const client = useClient();\n const topic = client.topic(topicName);\n const isMounted = useRef(true);\n\n // Keep callback ref stable to avoid re-subscribing if callback function identity changes\n const callbackRef = useRef(callback);\n useEffect(() => {\n callbackRef.current = callback;\n }, [callback]);\n\n useEffect(() => {\n isMounted.current = true;\n\n if (!callback) return;\n\n const unsubscribe = topic.subscribe((data, context) => {\n if (isMounted.current && callbackRef.current) {\n callbackRef.current(data, context);\n }\n });\n\n return () => {\n isMounted.current = false;\n unsubscribe();\n };\n }, [topic, callback]); // Re-subscribe if topic handle changes (rare) or if callback presence toggles\n\n return topic;\n}\n","import { useState, useEffect, useCallback, useMemo } from 'react';\nimport { useClient } from './useClient';\n\n/**\n * Result type for usePNCounter hook.\n */\nexport interface UsePNCounterResult {\n /** Current counter value */\n value: number;\n /** Increment the counter by 1 */\n increment: () => void;\n /** Decrement the counter by 1 */\n decrement: () => void;\n /** Add delta (positive or negative) to the counter */\n add: (delta: number) => void;\n /** Loading state (true until first value received) */\n loading: boolean;\n}\n\n/**\n * React hook for using a PN Counter with real-time updates.\n *\n * PN Counters support increment and decrement operations that work offline\n * and sync to server when connected. They guarantee convergence across\n * distributed nodes without coordination.\n *\n * @param name The counter name (e.g., 'likes:post-123')\n * @returns Counter value and methods\n *\n * @example Basic usage\n * ```tsx\n * function LikeButton({ postId }: { postId: string }) {\n * const { value, increment } = usePNCounter(`likes:${postId}`);\n *\n * return (\n * <button onClick={increment}>\n * ❤️ {value}\n * </button>\n * );\n * }\n * ```\n *\n * @example Inventory control\n * ```tsx\n * function InventoryControl({ productId }: { productId: string }) {\n * const { value, increment, decrement } = usePNCounter(`inventory:${productId}`);\n *\n * return (\n * <div>\n * <span>Stock: {value}</span>\n * <button onClick={decrement} disabled={value <= 0}>-</button>\n * <button onClick={increment}>+</button>\n * </div>\n * );\n * }\n * ```\n *\n * @example Bulk operations\n * ```tsx\n * function BulkAdd({ counterId }: { counterId: string }) {\n * const { value, add } = usePNCounter(counterId);\n * const [amount, setAmount] = useState(10);\n *\n * return (\n * <div>\n * <span>Value: {value}</span>\n * <input\n * type=\"number\"\n * value={amount}\n * onChange={(e) => setAmount(parseInt(e.target.value))}\n * />\n * <button onClick={() => add(amount)}>Add {amount}</button>\n * <button onClick={() => add(-amount)}>Subtract {amount}</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function usePNCounter(name: string): UsePNCounterResult {\n const client = useClient();\n const [value, setValue] = useState(0);\n const [loading, setLoading] = useState(true);\n\n // Get or create counter handle - memoized by name\n const counter = useMemo(() => {\n return client.getPNCounter(name);\n }, [client, name]);\n\n useEffect(() => {\n // Reset state when counter changes\n setLoading(true);\n\n const unsubscribe = counter.subscribe((newValue) => {\n setValue(newValue);\n setLoading(false);\n });\n\n return unsubscribe;\n }, [counter]);\n\n const increment = useCallback(() => {\n counter.increment();\n }, [counter]);\n\n const decrement = useCallback(() => {\n counter.decrement();\n }, [counter]);\n\n const add = useCallback((delta: number) => {\n counter.addAndGet(delta);\n }, [counter]);\n\n return useMemo(\n () => ({ value, increment, decrement, add, loading }),\n [value, increment, decrement, add, loading]\n );\n}\n","import { useState, useCallback, useMemo } from 'react';\nimport { useClient } from './useClient';\nimport type { EntryProcessorDef, EntryProcessorResult } from '@topgunbuild/core';\n\n/**\n * Options for the useEntryProcessor hook.\n */\nexport interface UseEntryProcessorOptions {\n /**\n * Number of retry attempts on failure.\n * Default: 0 (no retries)\n */\n retries?: number;\n\n /**\n * Delay between retries in milliseconds.\n * Default: 100ms, doubles with each retry (exponential backoff)\n */\n retryDelayMs?: number;\n}\n\n/**\n * Result type for useEntryProcessor hook.\n */\nexport interface UseEntryProcessorResult<R> {\n /**\n * Execute the processor on a key.\n * @param key The key to process\n * @param args Optional arguments to pass to the processor\n */\n execute: (key: string, args?: unknown) => Promise<EntryProcessorResult<R>>;\n\n /**\n * Execute the processor on multiple keys.\n * @param keys The keys to process\n * @param args Optional arguments to pass to the processor\n */\n executeMany: (keys: string[], args?: unknown) => Promise<Map<string, EntryProcessorResult<R>>>;\n\n /** True while a processor is executing */\n executing: boolean;\n\n /** Last execution result (single key) */\n lastResult: EntryProcessorResult<R> | null;\n\n /** Last error encountered */\n error: Error | null;\n\n /** Reset the hook state (clears lastResult and error) */\n reset: () => void;\n}\n\n/**\n * React hook for executing entry processors with loading and error states.\n *\n * Entry processors execute user-defined logic atomically on the server,\n * solving the read-modify-write race condition.\n *\n * @param mapName Name of the map to operate on\n * @param processorDef Processor definition (without args - args are passed per-execution)\n * @param options Optional configuration\n * @returns Execute function and state\n *\n * @example Basic increment\n * ```tsx\n * function LikeButton({ postId }: { postId: string }) {\n * const { execute, executing } = useEntryProcessor<number>('likes', {\n * name: 'increment',\n * code: `\n * const current = value ?? 0;\n * return { value: current + 1, result: current + 1 };\n * `,\n * });\n *\n * const handleLike = async () => {\n * const result = await execute(postId);\n * if (result.success) {\n * console.log('New like count:', result.result);\n * }\n * };\n *\n * return (\n * <button onClick={handleLike} disabled={executing}>\n * {executing ? '...' : 'Like'}\n * </button>\n * );\n * }\n * ```\n *\n * @example Inventory reservation with args\n * ```tsx\n * function ReserveButton({ productId }: { productId: string }) {\n * const { execute, executing, error } = useEntryProcessor<\n * { stock: number; reserved: string[] },\n * { success: boolean; remaining: number }\n * >('inventory', {\n * name: 'reserve_item',\n * code: `\n * if (!value || value.stock <= 0) {\n * return { value, result: { success: false, remaining: 0 } };\n * }\n * const newValue = {\n * ...value,\n * stock: value.stock - 1,\n * reserved: [...value.reserved, args.userId],\n * };\n * return {\n * value: newValue,\n * result: { success: true, remaining: newValue.stock }\n * };\n * `,\n * });\n *\n * const handleReserve = async () => {\n * const result = await execute(productId, { userId: currentUser.id });\n * if (result.success && result.result?.success) {\n * toast.success(`Reserved! ${result.result.remaining} left`);\n * } else {\n * toast.error('Out of stock');\n * }\n * };\n *\n * return (\n * <button onClick={handleReserve} disabled={executing}>\n * {executing ? 'Reserving...' : 'Reserve'}\n * </button>\n * );\n * }\n * ```\n *\n * @example Using built-in processor\n * ```tsx\n * import { BuiltInProcessors } from '@topgunbuild/core';\n *\n * function DecrementStock({ productId }: { productId: string }) {\n * const processorDef = useMemo(\n * () => BuiltInProcessors.DECREMENT_FLOOR(1),\n * []\n * );\n *\n * const { execute, executing, lastResult } = useEntryProcessor<\n * number,\n * { newValue: number; wasFloored: boolean }\n * >('stock', processorDef);\n *\n * const handleDecrement = async () => {\n * const result = await execute(productId);\n * if (result.result?.wasFloored) {\n * alert('Stock is now at zero!');\n * }\n * };\n *\n * return (\n * <button onClick={handleDecrement} disabled={executing}>\n * Decrease Stock\n * </button>\n * );\n * }\n * ```\n */\nexport function useEntryProcessor<V = unknown, R = V>(\n mapName: string,\n processorDef: Omit<EntryProcessorDef<V, R>, 'args'>,\n options: UseEntryProcessorOptions = {},\n): UseEntryProcessorResult<R> {\n const client = useClient();\n const [executing, setExecuting] = useState(false);\n const [lastResult, setLastResult] = useState<EntryProcessorResult<R> | null>(null);\n const [error, setError] = useState<Error | null>(null);\n\n const { retries = 0, retryDelayMs = 100 } = options;\n\n const execute = useCallback(\n async (key: string, args?: unknown): Promise<EntryProcessorResult<R>> => {\n setExecuting(true);\n setError(null);\n\n const processor: EntryProcessorDef<V, R> = {\n ...processorDef,\n args,\n } as EntryProcessorDef<V, R>;\n\n let attempts = 0;\n let lastError: Error | null = null;\n\n while (attempts <= retries) {\n try {\n const result = await client.executeOnKey<V, R>(mapName, key, processor);\n setLastResult(result);\n setExecuting(false);\n return result;\n } catch (err) {\n lastError = err instanceof Error ? err : new Error(String(err));\n attempts++;\n\n if (attempts <= retries) {\n // Exponential backoff\n const delay = retryDelayMs * Math.pow(2, attempts - 1);\n await new Promise((r) => setTimeout(r, delay));\n }\n }\n }\n\n // All retries exhausted\n setError(lastError);\n setExecuting(false);\n throw lastError;\n },\n [client, mapName, processorDef, retries, retryDelayMs],\n );\n\n const executeMany = useCallback(\n async (keys: string[], args?: unknown): Promise<Map<string, EntryProcessorResult<R>>> => {\n setExecuting(true);\n setError(null);\n\n const processor: EntryProcessorDef<V, R> = {\n ...processorDef,\n args,\n } as EntryProcessorDef<V, R>;\n\n try {\n const results = await client.executeOnKeys<V, R>(mapName, keys, processor);\n setExecuting(false);\n return results;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setError(error);\n setExecuting(false);\n throw error;\n }\n },\n [client, mapName, processorDef],\n );\n\n const reset = useCallback(() => {\n setLastResult(null);\n setError(null);\n }, []);\n\n return useMemo(\n () => ({ execute, executeMany, executing, lastResult, error, reset }),\n [execute, executeMany, executing, lastResult, error, reset],\n );\n}\n","import { useState, useEffect, useRef, useCallback, useMemo } from 'react';\nimport type { JournalEvent, JournalEventType } from '@topgunbuild/core';\nimport type { EventJournalReader, JournalSubscribeOptions } from '@topgunbuild/client';\nimport { useClient } from './useClient';\n\n/**\n * Options for useEventJournal hook.\n */\nexport interface UseEventJournalOptions {\n /** Start from specific sequence */\n fromSequence?: bigint;\n /** Filter by map name */\n mapName?: string;\n /** Filter by event types */\n types?: JournalEventType[];\n /** Maximum events to keep in state (default: 100) */\n maxEvents?: number;\n /** Called when new event is received */\n onEvent?: (event: JournalEvent) => void;\n /** Pause subscription */\n paused?: boolean;\n}\n\n/**\n * Result type for useEventJournal hook.\n */\nexport interface UseEventJournalResult {\n /** Array of recent events (newest last) */\n events: JournalEvent[];\n /** Last received event */\n lastEvent: JournalEvent | null;\n /** Clear accumulated events */\n clearEvents: () => void;\n /** Read historical events from sequence */\n readFrom: (sequence: bigint, limit?: number) => Promise<JournalEvent[]>;\n /** Get latest sequence number */\n getLatestSequence: () => Promise<bigint>;\n /** Whether subscription is active */\n isSubscribed: boolean;\n}\n\n/**\n * React hook for subscribing to Event Journal changes.\n *\n * The Event Journal captures all map changes (PUT, UPDATE, DELETE) as an\n * append-only log, useful for:\n * - Real-time activity feeds\n * - Audit trails\n * - Change notifications\n * - Debugging and monitoring\n *\n * @example Basic usage - show all changes\n * ```tsx\n * function ActivityFeed() {\n * const { events, lastEvent } = useEventJournal();\n *\n * return (\n * <ul>\n * {events.map((e) => (\n * <li key={e.sequence.toString()}>\n * {e.type} {e.mapName}:{e.key}\n * </li>\n * ))}\n * </ul>\n * );\n * }\n * ```\n *\n * @example Filter by map name\n * ```tsx\n * function UserActivityFeed() {\n * const { events } = useEventJournal({ mapName: 'users' });\n *\n * return (\n * <ul>\n * {events.map((e) => (\n * <li key={e.sequence.toString()}>\n * User {e.key}: {e.type}\n * </li>\n * ))}\n * </ul>\n * );\n * }\n * ```\n *\n * @example With event callback\n * ```tsx\n * function NotifyingComponent() {\n * const { events } = useEventJournal({\n * mapName: 'orders',\n * types: ['PUT'],\n * onEvent: (event) => {\n * toast.success(`New order: ${event.key}`);\n * },\n * });\n *\n * return <OrderList events={events} />;\n * }\n * ```\n */\nexport function useEventJournal(\n options: UseEventJournalOptions = {}\n): UseEventJournalResult {\n const client = useClient();\n const [events, setEvents] = useState<JournalEvent[]>([]);\n const [lastEvent, setLastEvent] = useState<JournalEvent | null>(null);\n const [isSubscribed, setIsSubscribed] = useState(false);\n\n const isMounted = useRef(true);\n const journalRef = useRef<EventJournalReader | null>(null);\n\n const maxEvents = options.maxEvents ?? 100;\n\n // Store options in ref to avoid re-subscription on every render\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n // Clear events callback\n const clearEvents = useCallback(() => {\n setEvents([]);\n setLastEvent(null);\n }, []);\n\n // Read historical events\n const readFrom = useCallback(\n async (sequence: bigint, limit?: number): Promise<JournalEvent[]> => {\n if (!journalRef.current) {\n journalRef.current = client.getEventJournal();\n }\n return journalRef.current.readFrom(sequence, limit);\n },\n [client]\n );\n\n // Get latest sequence\n const getLatestSequence = useCallback(async (): Promise<bigint> => {\n if (!journalRef.current) {\n journalRef.current = client.getEventJournal();\n }\n return journalRef.current.getLatestSequence();\n }, [client]);\n\n // Serialize filter options for dependency comparison\n const filterKey = useMemo(\n () =>\n JSON.stringify({\n mapName: options.mapName,\n types: options.types,\n fromSequence: options.fromSequence?.toString(),\n paused: options.paused,\n }),\n [options.mapName, options.types, options.fromSequence, options.paused]\n );\n\n useEffect(() => {\n isMounted.current = true;\n\n // Don't subscribe if paused\n if (options.paused) {\n setIsSubscribed(false);\n return;\n }\n\n const journal = client.getEventJournal();\n journalRef.current = journal;\n\n const subscribeOptions: JournalSubscribeOptions = {\n fromSequence: options.fromSequence,\n mapName: options.mapName,\n types: options.types,\n };\n\n const unsubscribe = journal.subscribe((event) => {\n if (!isMounted.current) return;\n\n // Add to events with rotation\n setEvents((prev) => {\n const newEvents = [...prev, event];\n if (newEvents.length > maxEvents) {\n return newEvents.slice(-maxEvents);\n }\n return newEvents;\n });\n\n setLastEvent(event);\n\n // Call event callback\n optionsRef.current.onEvent?.(event);\n }, subscribeOptions);\n\n setIsSubscribed(true);\n\n return () => {\n isMounted.current = false;\n setIsSubscribed(false);\n unsubscribe();\n };\n }, [client, filterKey, maxEvents]);\n\n return useMemo(\n () => ({\n events,\n lastEvent,\n clearEvents,\n readFrom,\n getLatestSequence,\n isSubscribed,\n }),\n [events, lastEvent, clearEvents, readFrom, getLatestSequence, isSubscribed]\n );\n}\n","import { useState, useEffect, useCallback } from 'react';\nimport { useClient } from './useClient';\nimport type { MergeRejection } from '@topgunbuild/core';\n\n/**\n * Options for useMergeRejections hook.\n */\nexport interface UseMergeRejectionsOptions {\n /** Filter rejections by map name (optional) */\n mapName?: string;\n\n /** Maximum number of rejections to keep in history */\n maxHistory?: number;\n}\n\n/**\n * Result type for useMergeRejections hook.\n */\nexport interface UseMergeRejectionsResult {\n /** List of recent merge rejections */\n rejections: MergeRejection[];\n\n /** Last rejection received */\n lastRejection: MergeRejection | null;\n\n /** Clear rejection history */\n clear: () => void;\n}\n\n/**\n * React hook for subscribing to merge rejection events.\n *\n * Merge rejections occur when a custom conflict resolver rejects\n * a client's write operation. This hook allows you to:\n * - Display rejection notifications to users\n * - Refresh local state after rejection\n * - Log conflicts for debugging\n *\n * @param options Optional filtering and configuration\n * @returns Rejection list and utilities\n *\n * @example Show rejection notifications\n * ```tsx\n * function BookingForm() {\n * const { lastRejection, clear } = useMergeRejections({\n * mapName: 'bookings'\n * });\n *\n * useEffect(() => {\n * if (lastRejection) {\n * toast.error(`Booking failed: ${lastRejection.reason}`);\n * clear(); // Clear after showing notification\n * }\n * }, [lastRejection]);\n *\n * return <form>...</form>;\n * }\n * ```\n *\n * @example Track all rejections\n * ```tsx\n * function ConflictLog() {\n * const { rejections } = useMergeRejections({ maxHistory: 50 });\n *\n * return (\n * <ul>\n * {rejections.map((r, i) => (\n * <li key={i}>\n * {r.mapName}/{r.key}: {r.reason}\n * </li>\n * ))}\n * </ul>\n * );\n * }\n * ```\n */\nexport function useMergeRejections(\n options: UseMergeRejectionsOptions = {},\n): UseMergeRejectionsResult {\n const client = useClient();\n const { mapName, maxHistory = 100 } = options;\n\n const [rejections, setRejections] = useState<MergeRejection[]>([]);\n const [lastRejection, setLastRejection] = useState<MergeRejection | null>(null);\n\n useEffect(() => {\n const resolvers = client.getConflictResolvers();\n\n const unsubscribe = resolvers.onRejection((rejection) => {\n // Filter by map name if specified\n if (mapName && rejection.mapName !== mapName) {\n return;\n }\n\n setLastRejection(rejection);\n setRejections((prev) => {\n const next = [...prev, rejection];\n // Limit history size\n if (next.length > maxHistory) {\n return next.slice(-maxHistory);\n }\n return next;\n });\n });\n\n return unsubscribe;\n }, [client, mapName, maxHistory]);\n\n const clear = useCallback(() => {\n setRejections([]);\n setLastRejection(null);\n }, []);\n\n return {\n rejections,\n lastRejection,\n clear,\n };\n}\n","import { useState, useCallback, useEffect } from 'react';\nimport { useClient } from './useClient';\nimport type { ConflictResolverDef } from '@topgunbuild/core';\nimport type { ResolverInfo, RegisterResult } from '@topgunbuild/client';\n\n/**\n * Options for useConflictResolver hook.\n */\nexport interface UseConflictResolverOptions {\n /** Auto-unregister resolver on unmount (default: true) */\n autoUnregister?: boolean;\n}\n\n/**\n * Result type for useConflictResolver hook.\n */\nexport interface UseConflictResolverResult {\n /**\n * Register a conflict resolver on the server.\n * @param resolver The resolver definition\n */\n register: (resolver: Omit<ConflictResolverDef, 'fn'>) => Promise<RegisterResult>;\n\n /**\n * Unregister a resolver by name.\n * @param resolverName Name of the resolver to unregister\n */\n unregister: (resolverName: string) => Promise<RegisterResult>;\n\n /**\n * List all registered resolvers for this map.\n */\n list: () => Promise<ResolverInfo[]>;\n\n /** True while a registration/unregistration is in progress */\n loading: boolean;\n\n /** Last error encountered */\n error: Error | null;\n\n /** List of resolvers registered by this hook instance */\n registered: string[];\n}\n\n/**\n * React hook for managing conflict resolvers on a specific map.\n *\n * Conflict resolvers allow you to customize how merge conflicts are handled\n * on the server. This hook provides a convenient way to:\n * - Register custom resolvers\n * - Auto-unregister on component unmount\n * - Track registration state\n *\n * @param mapName Name of the map to manage resolvers for\n * @param options Optional configuration\n * @returns Resolver management functions and state\n *\n * @example First-write-wins for bookings\n * ```tsx\n * function BookingManager() {\n * const { register, registered, loading, error } = useConflictResolver('bookings');\n *\n * useEffect(() => {\n * // Register resolver on mount\n * register({\n * name: 'first-write-wins',\n * code: `\n * if (context.localValue !== undefined) {\n * return { action: 'reject', reason: 'Already booked' };\n * }\n * return { action: 'accept', value: context.remoteValue };\n * `,\n * priority: 100,\n * });\n * }, []);\n *\n * return (\n * <div>\n * {loading && <span>Registering...</span>}\n * {error && <span>Error: {error.message}</span>}\n * <ul>\n * {registered.map(name => <li key={name}>{name}</li>)}\n * </ul>\n * </div>\n * );\n * }\n * ```\n *\n * @example Numeric constraints\n * ```tsx\n * function InventorySettings() {\n * const { register } = useConflictResolver('inventory');\n *\n * const enableNonNegative = async () => {\n * await register({\n * name: 'non-negative',\n * code: `\n * if (context.remoteValue < 0) {\n * return { action: 'reject', reason: 'Stock cannot be negative' };\n * }\n * return { action: 'accept', value: context.remoteValue };\n * `,\n * priority: 90,\n * keyPattern: 'stock:*',\n * });\n * };\n *\n * return <button onClick={enableNonNegative}>Enable Stock Protection</button>;\n * }\n * ```\n */\nexport function useConflictResolver(\n mapName: string,\n options: UseConflictResolverOptions = {},\n): UseConflictResolverResult {\n const client = useClient();\n const { autoUnregister = true } = options;\n\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [registered, setRegistered] = useState<string[]>([]);\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n if (autoUnregister && registered.length > 0) {\n const resolvers = client.getConflictResolvers();\n // Fire-and-forget unregistration\n for (const name of registered) {\n resolvers.unregister(mapName, name).catch(() => {\n // Ignore errors on cleanup\n });\n }\n }\n };\n }, [client, mapName, autoUnregister, registered]);\n\n const register = useCallback(\n async (resolver: Omit<ConflictResolverDef, 'fn'>): Promise<RegisterResult> => {\n setLoading(true);\n setError(null);\n\n try {\n const resolvers = client.getConflictResolvers();\n const result = await resolvers.register(mapName, resolver);\n\n if (result.success) {\n setRegistered((prev) => {\n if (prev.includes(resolver.name)) {\n return prev;\n }\n return [...prev, resolver.name];\n });\n } else if (result.error) {\n setError(new Error(result.error));\n }\n\n return result;\n } catch (e) {\n const err = e instanceof Error ? e : new Error(String(e));\n setError(err);\n return { success: false, error: err.message };\n } finally {\n setLoading(false);\n }\n },\n [client, mapName],\n );\n\n const unregister = useCallback(\n async (resolverName: string): Promise<RegisterResult> => {\n setLoading(true);\n setError(null);\n\n try {\n const resolvers = client.getConflictResolvers();\n const result = await resolvers.unregister(mapName, resolverName);\n\n if (result.success) {\n setRegistered((prev) => prev.filter((n) => n !== resolverName));\n } else if (result.error) {\n setError(new Error(result.error));\n }\n\n return result;\n } catch (e) {\n const err = e instanceof Error ? e : new Error(String(e));\n setError(err);\n return { success: false, error: err.message };\n } finally {\n setLoading(false);\n }\n },\n [client, mapName],\n );\n\n const list = useCallback(async (): Promise<ResolverInfo[]> => {\n try {\n const resolvers = client.getConflictResolvers();\n return await resolvers.list(mapName);\n } catch (e) {\n const err = e instanceof Error ? e : new Error(String(e));\n setError(err);\n return [];\n }\n }, [client, mapName]);\n\n return {\n register,\n unregister,\n list,\n loading,\n error,\n registered,\n };\n}\n","import { useState, useEffect, useRef, useMemo } from 'react';\nimport type { SearchHandle, SearchResult } from '@topgunbuild/client';\nimport type { SearchOptions } from '@topgunbuild/core';\nimport { useClient } from './useClient';\n\n/**\n * Extended search options for useSearch hook.\n */\nexport interface UseSearchOptions extends SearchOptions {\n /**\n * Debounce delay in milliseconds.\n * If specified, query changes will be debounced before sending to the server.\n * Useful for search-as-you-type interfaces.\n */\n debounceMs?: number;\n}\n\n/**\n * Result type for the useSearch hook.\n */\nexport interface UseSearchResult<T> {\n /** Current search results sorted by relevance */\n results: SearchResult<T>[];\n /** True while waiting for initial results */\n loading: boolean;\n /** Error if search failed */\n error: Error | null;\n}\n\n/**\n * React hook for live full-text search with real-time updates.\n *\n * Creates a search subscription that receives delta updates when documents\n * matching the query are added, updated, or removed. Results are automatically\n * sorted by BM25 relevance score.\n *\n * @param mapName - Name of the map to search\n * @param query - Search query text\n * @param options - Search options (limit, minScore, boost, debounceMs)\n * @returns Object containing results, loading state, and error\n *\n * @example Basic usage\n * ```tsx\n * function SearchResults() {\n * const [searchTerm, setSearchTerm] = useState('');\n * const { results, loading } = useSearch<Article>('articles', searchTerm, {\n * limit: 20,\n * boost: { title: 2.0 }\n * });\n *\n * if (loading) return <Spinner />;\n *\n * return (\n * <ul>\n * {results.map(r => (\n * <li key={r.key}>\n * [{r.score.toFixed(2)}] {r.value.title}\n * </li>\n * ))}\n * </ul>\n * );\n * }\n * ```\n *\n * @example With debounce for search-as-you-type\n * ```tsx\n * function SearchInput() {\n * const [input, setInput] = useState('');\n * const { results, loading, error } = useSearch<Product>('products', input, {\n * debounceMs: 300,\n * limit: 10\n * });\n *\n * return (\n * <div>\n * <input\n * value={input}\n * onChange={(e) => setInput(e.target.value)}\n * placeholder=\"Search products...\"\n * />\n * {loading && <span>Searching...</span>}\n * {error && <span className=\"error\">{error.message}</span>}\n * <ul>\n * {results.map(r => (\n * <li key={r.key}>{r.value.name}</li>\n * ))}\n * </ul>\n * </div>\n * );\n * }\n * ```\n */\nexport function useSearch<T = unknown>(\n mapName: string,\n query: string,\n options?: UseSearchOptions\n): UseSearchResult<T> {\n const client = useClient();\n const [results, setResults] = useState<SearchResult<T>[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n // Track if component is mounted\n const isMounted = useRef(true);\n\n // Store handle ref for reuse\n const handleRef = useRef<SearchHandle<T> | null>(null);\n\n // Store unsubscribe function\n const unsubscribeRef = useRef<(() => void) | null>(null);\n\n // Store timeout ref for debounce\n const debounceTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n // Extract debounceMs from options\n const debounceMs = options?.debounceMs;\n\n // Memoize search options (without debounceMs) to avoid unnecessary re-renders\n const searchOptions = useMemo<SearchOptions>(() => {\n if (!options) return {};\n const { debounceMs: _, ...opts } = options;\n return opts;\n }, [options?.limit, options?.minScore, options?.boost]);\n\n // Track the debounced query\n const [debouncedQuery, setDebouncedQuery] = useState(query);\n\n // Track if this is the first query (for initial handle creation)\n const isFirstQuery = useRef(true);\n\n // Debounce the query if debounceMs is set\n useEffect(() => {\n if (debounceMs != null && debounceMs > 0) {\n // Clear any pending timeout\n if (debounceTimeoutRef.current) {\n clearTimeout(debounceTimeoutRef.current);\n }\n\n debounceTimeoutRef.current = setTimeout(() => {\n if (isMounted.current) {\n setDebouncedQuery(query);\n }\n }, debounceMs);\n\n return () => {\n if (debounceTimeoutRef.current) {\n clearTimeout(debounceTimeoutRef.current);\n }\n };\n } else {\n // No debounce, update immediately\n setDebouncedQuery(query);\n }\n }, [query, debounceMs]);\n\n // Effect for creating/disposing handle when mapName changes\n useEffect(() => {\n isMounted.current = true;\n isFirstQuery.current = true;\n\n // Cleanup on mapName change or unmount\n return () => {\n isMounted.current = false;\n if (unsubscribeRef.current) {\n unsubscribeRef.current();\n unsubscribeRef.current = null;\n }\n if (handleRef.current) {\n handleRef.current.dispose();\n handleRef.current = null;\n }\n };\n }, [client, mapName]);\n\n // Effect for handling query changes\n useEffect(() => {\n // Don't subscribe for empty queries\n if (!debouncedQuery.trim()) {\n setResults([]);\n setLoading(false);\n setError(null);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n // If we have an existing handle, use setQuery() for efficiency\n if (handleRef.current && !isFirstQuery.current) {\n handleRef.current.setQuery(debouncedQuery);\n } else {\n // First query or no handle - create new subscription\n if (handleRef.current) {\n // Cleanup old handle if exists\n if (unsubscribeRef.current) {\n unsubscribeRef.current();\n }\n handleRef.current.dispose();\n }\n\n const handle = client.searchSubscribe<T>(mapName, debouncedQuery, searchOptions);\n handleRef.current = handle;\n isFirstQuery.current = false;\n\n // Flag to track if we've received initial results\n let hasReceivedResults = false;\n\n // Subscribe to result updates\n unsubscribeRef.current = handle.subscribe((newResults) => {\n if (isMounted.current) {\n setResults(newResults);\n if (!hasReceivedResults) {\n hasReceivedResults = true;\n setLoading(false);\n }\n }\n });\n }\n } catch (err) {\n if (isMounted.current) {\n setError(err instanceof Error ? err : new Error(String(err)));\n setLoading(false);\n }\n }\n }, [client, mapName, debouncedQuery, searchOptions]);\n\n // Effect for handling options changes (use setOptions)\n useEffect(() => {\n // Skip if no handle or if this is the initial render\n if (!handleRef.current || isFirstQuery.current) {\n return;\n }\n\n // Use setOptions() for efficient options update\n handleRef.current.setOptions(searchOptions);\n }, [searchOptions]);\n\n return useMemo(\n () => ({ results, loading, error }),\n [results, loading, error]\n );\n}\n","import { useState, useEffect, useRef, useMemo } from 'react';\nimport type { HybridQueryHandle, HybridResultItem, HybridQueryFilter } from '@topgunbuild/client';\nimport { useClient } from './useClient';\n\n/**\n * Extended options for useHybridQuery hook.\n */\nexport interface UseHybridQueryOptions {\n /**\n * Whether to skip the query (don't execute).\n * Useful for conditional queries.\n */\n skip?: boolean;\n}\n\n/**\n * Result type for the useHybridQuery hook.\n */\nexport interface UseHybridQueryResult<T> {\n /** Current query results with _key, value, _score, _matchedTerms */\n results: HybridResultItem<T>[];\n /** True while waiting for initial results */\n loading: boolean;\n /** Error if query failed */\n error: Error | null;\n}\n\n/**\n * React hook for hybrid queries combining FTS with traditional filters.\n *\n * Creates a subscription that receives live updates when documents\n * matching the query change. Results include relevance scores for FTS predicates.\n *\n * @param mapName - Name of the map to query\n * @param filter - Hybrid query filter with predicate, where, sort, limit, offset\n * @param options - Hook options (skip)\n * @returns Object containing results, loading state, and error\n *\n * @example Basic hybrid query (FTS + filter)\n * ```tsx\n * import { Predicates } from '@topgunbuild/core';\n *\n * function TechArticles() {\n * const { results, loading } = useHybridQuery<Article>('articles', {\n * predicate: Predicates.and(\n * Predicates.match('body', 'machine learning'),\n * Predicates.equal('category', 'tech')\n * ),\n * sort: { _score: 'desc' },\n * limit: 20\n * });\n *\n * if (loading) return <Spinner />;\n *\n * return (\n * <ul>\n * {results.map(r => (\n * <li key={r._key}>\n * [{r._score?.toFixed(2)}] {r.value.title}\n * </li>\n * ))}\n * </ul>\n * );\n * }\n * ```\n *\n * @example With dynamic filter\n * ```tsx\n * function SearchWithFilters() {\n * const [searchTerm, setSearchTerm] = useState('');\n * const [category, setCategory] = useState('all');\n *\n * const filter = useMemo(() => ({\n * predicate: searchTerm\n * ? category !== 'all'\n * ? Predicates.and(\n * Predicates.match('body', searchTerm),\n * Predicates.equal('category', category)\n * )\n * : Predicates.match('body', searchTerm)\n * : category !== 'all'\n * ? Predicates.equal('category', category)\n * : undefined,\n * sort: searchTerm ? { _score: 'desc' } : { createdAt: 'desc' },\n * limit: 20\n * }), [searchTerm, category]);\n *\n * const { results, loading, error } = useHybridQuery<Article>('articles', filter);\n *\n * return (\n * <div>\n * <input\n * value={searchTerm}\n * onChange={(e) => setSearchTerm(e.target.value)}\n * placeholder=\"Search...\"\n * />\n * <select value={category} onChange={(e) => setCategory(e.target.value)}>\n * <option value=\"all\">All</option>\n * <option value=\"tech\">Tech</option>\n * <option value=\"science\">Science</option>\n * </select>\n * {loading && <span>Loading...</span>}\n * {error && <span className=\"error\">{error.message}</span>}\n * <ul>\n * {results.map(r => (\n * <li key={r._key}>{r.value.title}</li>\n * ))}\n * </ul>\n * </div>\n * );\n * }\n * ```\n */\nexport function useHybridQuery<T = unknown>(\n mapName: string,\n filter: HybridQueryFilter = {},\n options?: UseHybridQueryOptions\n): UseHybridQueryResult<T> {\n const client = useClient();\n const [results, setResults] = useState<HybridResultItem<T>[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n // Track if component is mounted\n const isMounted = useRef(true);\n\n // Store handle ref\n const handleRef = useRef<HybridQueryHandle<T> | null>(null);\n\n // Store unsubscribe function\n const unsubscribeRef = useRef<(() => void) | null>(null);\n\n // Memoize filter to avoid unnecessary re-renders\n const memoizedFilter = useMemo(() => filter, [\n JSON.stringify(filter.predicate),\n JSON.stringify(filter.where),\n JSON.stringify(filter.sort),\n filter.limit,\n filter.offset,\n ]);\n\n // Skip option\n const skip = options?.skip ?? false;\n\n // Effect for creating/disposing handle\n useEffect(() => {\n isMounted.current = true;\n\n // Don't subscribe if skip is true\n if (skip) {\n setResults([]);\n setLoading(false);\n setError(null);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n // Cleanup old handle if exists\n if (handleRef.current) {\n if (unsubscribeRef.current) {\n unsubscribeRef.current();\n unsubscribeRef.current = null;\n }\n }\n\n // Create new handle\n const handle = client.hybridQuery<T>(mapName, memoizedFilter);\n handleRef.current = handle;\n\n // Flag to track if we've received initial results\n let hasReceivedResults = false;\n\n // Subscribe to result updates\n unsubscribeRef.current = handle.subscribe((newResults) => {\n if (isMounted.current) {\n setResults(newResults);\n if (!hasReceivedResults) {\n hasReceivedResults = true;\n setLoading(false);\n }\n }\n });\n } catch (err) {\n if (isMounted.current) {\n setError(err instanceof Error ? err : new Error(String(err)));\n setLoading(false);\n }\n }\n\n // Cleanup on unmount or dependency change\n return () => {\n isMounted.current = false;\n if (unsubscribeRef.current) {\n unsubscribeRef.current();\n unsubscribeRef.current = null;\n }\n handleRef.current = null;\n };\n }, [client, mapName, memoizedFilter, skip]);\n\n return useMemo(\n () => ({ results, loading, error }),\n [results, loading, error]\n );\n}\n"],"mappings":"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","names":["useCallback","useCallback","useState","useEffect","useRef","useState","useRef","useEffect","useState","useEffect","useRef","useState","useRef","useEffect","useEffect","useRef","useRef","useEffect","useState","useEffect","useCallback","useMemo","useState","useMemo","useEffect","useCallback","useState","useCallback","useMemo","useState","useCallback","error","useMemo","useState","useEffect","useRef","useCallback","useMemo","useState","useRef","useCallback","useMemo","useEffect","useState","useEffect","useCallback","useState","useEffect","useCallback","useState","useCallback","useEffect","useState","useEffect","useCallback","useState","useEffect","useRef","useMemo","useState","useRef","useMemo","useEffect","useState","useEffect","useRef","useMemo","useState","useRef","useMemo","useEffect"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topgunbuild/react",
3
- "version": "0.7.0",
3
+ "version": "0.8.1",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",
@@ -18,13 +18,18 @@
18
18
  "publishConfig": {
19
19
  "access": "public"
20
20
  },
21
+ "scripts": {
22
+ "build": "tsup",
23
+ "test": "jest",
24
+ "test:coverage": "jest --coverage"
25
+ },
21
26
  "peerDependencies": {
22
27
  "react": ">=16.8.0",
23
28
  "react-dom": ">=16.8.0"
24
29
  },
25
30
  "dependencies": {
26
- "@topgunbuild/client": "0.7.0",
27
- "@topgunbuild/core": "0.7.0"
31
+ "@topgunbuild/client": "workspace:*",
32
+ "@topgunbuild/core": "workspace:*"
28
33
  },
29
34
  "devDependencies": {
30
35
  "@testing-library/react": "^14.0.0",
@@ -42,10 +47,5 @@
42
47
  "engines": {
43
48
  "node": ">=18"
44
49
  },
45
- "license": "BSL-1.1",
46
- "scripts": {
47
- "build": "tsup",
48
- "test": "jest",
49
- "test:coverage": "jest --coverage"
50
- }
51
- }
50
+ "license": "BSL-1.1"
51
+ }
package/LICENSE DELETED
@@ -1,97 +0,0 @@
1
- Business Source License 1.1
2
-
3
- Parameters
4
-
5
- Licensor: TopGun Contributors
6
- Licensed Work: TopGun
7
- The Licensed Work is (c) 2024 TopGun Contributors.
8
- Additional Use Grant: You may make production use of the Licensed Work,
9
- provided Your use does not include offering the
10
- Licensed Work to third parties as a commercial
11
- managed database service, database-as-a-service,
12
- or similar hosted database offering that competes
13
- with TopGun products or services.
14
-
15
- For purposes of this license:
16
- - "Managed database service" means a service that
17
- allows third parties to create, manage, or operate
18
- databases using TopGun as the underlying technology.
19
- - Internal use within your organization is permitted.
20
- - Using TopGun as part of your application's backend
21
- (not exposed as a database service) is permitted.
22
- - Consulting and professional services around TopGun
23
- are permitted.
24
-
25
- Change Date: Four years from the date of each version release
26
- Change License: Apache License, Version 2.0
27
-
28
- For information about alternative licensing arrangements for the Licensed Work,
29
- please contact the Licensor.
30
-
31
- Notice
32
-
33
- Business Source License 1.1
34
-
35
- Terms
36
-
37
- The Licensor hereby grants you the right to copy, modify, create derivative
38
- works, redistribute, and make non-production use of the Licensed Work. The
39
- Licensor may make an Additional Use Grant, above, permitting limited production use.
40
-
41
- Effective on the Change Date, or the fourth anniversary of the first publicly
42
- available distribution of a specific version of the Licensed Work under this
43
- License, whichever comes first, the Licensor hereby grants you rights under
44
- the terms of the Change License, and the rights granted in the paragraph
45
- above terminate.
46
-
47
- If your use of the Licensed Work does not comply with the requirements
48
- currently in effect as described in this License, you must purchase a
49
- commercial license from the Licensor, its affiliated entities, or authorized
50
- resellers, or you must refrain from using the Licensed Work.
51
-
52
- All copies of the original and modified Licensed Work, and derivative works
53
- of the Licensed Work, are subject to this License. This License applies
54
- separately for each version of the Licensed Work and the Change Date may vary
55
- for each version of the Licensed Work released by Licensor.
56
-
57
- You must conspicuously display this License on each original or modified copy
58
- of the Licensed Work. If you receive the Licensed Work in original or
59
- modified form from a third party, the terms and conditions set forth in this
60
- License apply to your use of that work.
61
-
62
- Any use of the Licensed Work in violation of this License will automatically
63
- terminate your rights under this License for the current and all other
64
- versions of the Licensed Work.
65
-
66
- This License does not grant you any right in any trademark or logo of
67
- Licensor or its affiliates (provided that you may use a trademark or logo of
68
- Licensor as expressly required by this License).
69
-
70
- TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
71
- AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
72
- EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
73
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
74
- TITLE.
75
-
76
- MariaDB hereby grants you permission to use this License's text to license
77
- your works, and to refer to it using the trademark "Business Source License",
78
- as long as you comply with the Covenants of Licensor below.
79
-
80
- Covenants of Licensor
81
-
82
- In consideration of the right to use this License's text and the "Business
83
- Source License" name and trademark, Licensor covenants to MariaDB, and to all
84
- other recipients of the licensed work to be provided by Licensor:
85
-
86
- 1. To specify as the Change License the GPL Version 2.0 or any later version,
87
- or a license that is compatible with GPL Version 2.0 or a later version,
88
- where "compatible" means that software provided under the Change License can
89
- be included in a program with software provided under GPL Version 2.0 or a
90
- later version. Licensor may specify additional Change Licenses without
91
- limitation.
92
-
93
- 2. To either: (a) specify an Additional Use Grant (above) that does not impose
94
- any additional restriction on the right granted in this License, as the
95
- Additional Use Grant; or (b) insert the text "None" to specify a Change Date.
96
-
97
- 3. Not to modify this License in any other way.