@korajs/react 0.5.0 → 0.6.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/index.ts","../src/context/kora-context.ts","../src/hooks/use-app.ts","../src/hooks/use-query.ts","../src/query-store/query-store.ts","../src/hooks/use-mutation.ts","../src/hooks/use-sync-status.ts","../src/hooks/use-collection.ts","../src/hooks/use-rich-text.ts"],"sourcesContent":["// @korajs/react — public API\n// Every export here is a public API commitment. Be explicit.\n\n// === Types ===\nexport type {\n\tKoraAppLike,\n\tKoraContextValue,\n\tKoraProviderProps,\n\tUseQueryOptions,\n\tUseMutationOptions,\n\tUseMutationResult,\n\tUseRichTextResult,\n} from './types'\n\n// === Context ===\nexport { KoraProvider } from './context/kora-context'\n\n// === Hooks ===\nexport { useApp } from './hooks/use-app'\nexport { useQuery } from './hooks/use-query'\nexport { useMutation } from './hooks/use-mutation'\nexport { useSyncStatus } from './hooks/use-sync-status'\nexport { useCollection } from './hooks/use-collection'\nexport { useRichText } from './hooks/use-rich-text'\nexport type { UseRichTextOptions } from './hooks/use-rich-text'\n","import type { Store } from '@korajs/store'\nimport type { SyncEngine } from '@korajs/sync'\nimport { createContext, createElement, useContext, useEffect, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport type { KoraContextValue, KoraProviderProps } from '../types'\n\nconst KoraContext = createContext<KoraContextValue | null>(null)\n\n/**\n * Provides Kora store and optional sync engine to all child components.\n * Must wrap any component that uses Kora hooks (useQuery, useMutation, etc.).\n *\n * Accepts either an `app` prop (recommended) or explicit `store` + `syncEngine` props.\n *\n * When using the `app` prop, KoraProvider waits for `app.ready` before rendering\n * children. A `fallback` prop can be provided to show content while initializing.\n *\n * @example\n * ```typescript\n * // Recommended: pass the app object directly\n * const app = createApp({ schema })\n * <KoraProvider app={app}><App /></KoraProvider>\n *\n * // Advanced: pass store and syncEngine explicitly\n * <KoraProvider store={store} syncEngine={syncEngine}><App /></KoraProvider>\n * ```\n */\nfunction KoraProvider({\n\tapp,\n\tstore,\n\tsyncEngine,\n\tfallback,\n\tchildren,\n}: KoraProviderProps): ReactNode {\n\tconst [resolvedStore, setResolvedStore] = useState<Store | null>(store ?? null)\n\tconst [resolvedSync, setResolvedSync] = useState<SyncEngine | null>(syncEngine ?? null)\n\t// If no app prop, we're using the store prop and are ready immediately\n\tconst [ready, setReady] = useState(!app)\n\tconst [initError, setInitError] = useState<Error | null>(null)\n\n\tuseEffect(() => {\n\t\tif (!app) return\n\t\tlet cancelled = false\n\t\tapp.ready\n\t\t\t.then(() => {\n\t\t\t\tif (cancelled) return\n\t\t\t\tsetResolvedStore(app.getStore())\n\t\t\t\tsetResolvedSync(app.getSyncEngine())\n\t\t\t\tsetReady(true)\n\t\t\t})\n\t\t\t.catch((error: unknown) => {\n\t\t\t\tif (cancelled) return\n\t\t\t\tconst err = error instanceof Error ? error : new Error(String(error))\n\t\t\t\tconsole.error('[Kora] Initialization failed:', err)\n\t\t\t\tsetInitError(err)\n\t\t\t})\n\t\treturn () => {\n\t\t\tcancelled = true\n\t\t}\n\t}, [app])\n\n\tif (initError) {\n\t\treturn createElement(\n\t\t\t'div',\n\t\t\t{ style: { color: 'red', padding: '1rem', fontFamily: 'monospace' } },\n\t\t\tcreateElement('strong', null, 'Kora initialization error: '),\n\t\t\tinitError.message,\n\t\t)\n\t}\n\n\tif (!ready) {\n\t\treturn (fallback ?? null) as ReactNode\n\t}\n\n\tif (!resolvedStore) {\n\t\tthrow new Error(\n\t\t\t'KoraProvider requires either an \"app\" or \"store\" prop. ' +\n\t\t\t\t'Pass a KoraApp from createApp() or a Store instance.',\n\t\t)\n\t}\n\n\tconst value: KoraContextValue = {\n\t\tstore: resolvedStore,\n\t\tsyncEngine: resolvedSync,\n\t\tapp: app ?? null,\n\t\tevents: app?.events ?? null,\n\t\tsubscribeSyncStatus: app?.sync?.subscribeStatus ?? null,\n\t}\n\treturn createElement(KoraContext.Provider, { value }, children)\n}\n\n/**\n * Internal hook to access the Kora context.\n * Throws if used outside of a KoraProvider.\n */\nfunction useKoraContext(): KoraContextValue {\n\tconst context = useContext(KoraContext)\n\tif (context === null) {\n\t\tthrow new Error(\n\t\t\t'useKoraContext must be used within a <KoraProvider>. ' +\n\t\t\t\t'Wrap your component tree with <KoraProvider store={store}>.',\n\t\t)\n\t}\n\treturn context\n}\n\nexport { KoraProvider, useKoraContext }\n","import { useKoraContext } from '../context/kora-context'\nimport type { KoraAppLike } from '../types'\n\n/**\n * React hook that returns the Kora app instance from context.\n *\n * Use the generic parameter to cast to your typed app for full type inference:\n *\n * ```typescript\n * // In your app setup:\n * export const app = createApp({ schema: mySchema })\n * export type App = typeof app\n *\n * // In components:\n * const app = useApp<App>()\n * app.todos.insert({ title: 'Hello' }) // fully typed\n * const todos = useQuery(app.todos.where({ completed: false }))\n * ```\n *\n * Requires `KoraProvider` to be initialized with the `app` prop.\n * Throws if used outside of `KoraProvider` or without an `app` prop.\n *\n * @returns The KoraApp instance, typed as `T`\n */\nexport function useApp<T extends KoraAppLike = KoraAppLike>(): T {\n\tconst { app } = useKoraContext()\n\tif (!app) {\n\t\tthrow new Error(\n\t\t\t'useApp() requires KoraProvider to be initialized with an \"app\" prop. ' +\n\t\t\t\t'Pass your createApp() result to <KoraProvider app={app}>.',\n\t\t)\n\t}\n\treturn app as T\n}\n","import type { CollectionRecord, QueryBuilder } from '@korajs/store'\nimport { useMemo, useRef, useSyncExternalStore } from 'react'\nimport { QueryStore } from '../query-store/query-store'\nimport type { UseQueryOptions } from '../types'\n\nconst APP_NOT_READY_CODE = 'APP_NOT_READY'\n\nfunction assertQueryReady(query: QueryBuilder<unknown>): void {\n\tconst descriptor = query.getDescriptor()\n\tif (descriptor.collection === '__pending__') {\n\t\tconst err = new Error(\n\t\t\t'Cannot use useQuery() before app.ready. Await app.ready or wrap your UI in <KoraProvider app={app}>.',\n\t\t)\n\t\terr.name = 'AppNotReadyError'\n\t\tObject.assign(err, { code: APP_NOT_READY_CODE })\n\t\tthrow err\n\t}\n}\n\n/**\n * Frozen empty array returned when the query is disabled or before data loads.\n * Same reference prevents unnecessary re-renders.\n */\nconst EMPTY_ARRAY: readonly unknown[] = Object.freeze([])\n\nconst noopSubscribe = (_onStoreChange: () => void): (() => void) => {\n\treturn () => {}\n}\n\n/**\n * React hook for reactive queries against the local Kora store.\n *\n * Returns data synchronously from the local store — no loading spinners needed.\n * Re-renders automatically when the query results change due to mutations.\n * Uses `useSyncExternalStore` for React 18+ concurrent mode safety.\n *\n * The generic parameter `T` is inferred from the QueryBuilder, providing\n * full type safety when used with typed collection accessors.\n *\n * @param query - A QueryBuilder instance (e.g., `app.todos.where({ done: false })`)\n * @param options - Optional configuration (e.g., `{ enabled: false }` to skip the query)\n * @returns Readonly array of matching records\n *\n * @example\n * ```typescript\n * const todos = useQuery(app.todos.where({ completed: false }).orderBy('createdAt'))\n * // todos is typed as readonly InferRecord<typeof todoFields>[]\n * ```\n */\nexport function useQuery<T = CollectionRecord>(\n\tquery: QueryBuilder<T>,\n\toptions?: UseQueryOptions,\n): readonly T[] {\n\tconst enabled = options?.enabled !== false\n\n\tif (enabled) {\n\t\tassertQueryReady(query)\n\t}\n\n\t// Compute a stable key from the query descriptor\n\tconst descriptorKey = JSON.stringify(query.getDescriptor())\n\n\t// Track the current QueryStore instance\n\tconst queryStoreRef = useRef<QueryStore<T> | null>(null)\n\tconst prevKeyRef = useRef<string | null>(null)\n\n\t// Create or reuse a QueryStore when the descriptor changes\n\tconst queryStore = useMemo(() => {\n\t\tif (!enabled) {\n\t\t\t// Destroy previous if it exists\n\t\t\tif (queryStoreRef.current) {\n\t\t\t\tqueryStoreRef.current.destroy()\n\t\t\t\tqueryStoreRef.current = null\n\t\t\t}\n\t\t\tprevKeyRef.current = null\n\t\t\treturn null\n\t\t}\n\n\t\t// If descriptor changed, destroy previous and create new\n\t\tif (prevKeyRef.current !== descriptorKey) {\n\t\t\tif (queryStoreRef.current) {\n\t\t\t\tqueryStoreRef.current.destroy()\n\t\t\t}\n\t\t\tconst newStore = new QueryStore<T>(query)\n\t\t\tqueryStoreRef.current = newStore\n\t\t\tprevKeyRef.current = descriptorKey\n\t\t\treturn newStore\n\t\t}\n\n\t\treturn queryStoreRef.current\n\t}, [descriptorKey, enabled, query])\n\n\t// No useEffect cleanup needed: QueryStore's subscribe/unsubscribe cycle\n\t// manages the underlying subscription lifetime. When the last listener\n\t// detaches (on unmount), the subscription auto-stops. This also makes\n\t// the hook resilient to React StrictMode's double-mount cycle.\n\n\tconst disabledGetSnapshot = (): readonly T[] => EMPTY_ARRAY as readonly T[]\n\n\t// The query object is deliberately excluded from the dependency array.\n\t// QueryBuilder instances are ephemeral (created on every render when\n\t// developers use inline `app.todos.where({...})`). Using it as a dep\n\t// would destroy and recreate the QueryStore on every render, causing\n\t// subscription storms. Instead, we only react to the stable descriptor\n\t// JSON key, which captures the actual query parameters.\n\treturn useSyncExternalStore(\n\t\tqueryStore ? queryStore.subscribe : noopSubscribe,\n\t\tqueryStore ? queryStore.getSnapshot : disabledGetSnapshot,\n\t)\n}\n","import type { CollectionRecord, QueryBuilder } from '@korajs/store'\n\n/**\n * Frozen empty array returned as the initial snapshot before any data loads.\n * Same reference every time prevents infinite re-render loops with useSyncExternalStore.\n */\nconst EMPTY_ARRAY: readonly unknown[] = Object.freeze([])\n\n/**\n * Bridges the async QueryBuilder.subscribe() API with the synchronous\n * getSnapshot() required by React's useSyncExternalStore.\n *\n * Uses a lazy subscription model: the underlying query subscription starts\n * when the first listener attaches (via useSyncExternalStore's subscribe)\n * and stops when the last listener detaches. This makes QueryStore resilient\n * to React StrictMode's double-mount cycle, where useEffect cleanup fires\n * between mount and remount.\n *\n * Generic parameter `T` defaults to `CollectionRecord` for backward compatibility.\n */\nexport class QueryStore<T = CollectionRecord> {\n\tprivate snapshot: readonly T[] = EMPTY_ARRAY as readonly T[]\n\tprivate listeners = new Set<() => void>()\n\tprivate unsubscribeQuery: (() => void) | null = null\n\tprivate active = false\n\tprivate readonly queryBuilder: QueryBuilder<T>\n\n\tconstructor(queryBuilder: QueryBuilder<T>) {\n\t\tthis.queryBuilder = queryBuilder\n\t}\n\n\t/**\n\t * Subscribe to snapshot changes. Compatible with useSyncExternalStore.\n\t *\n\t * Lazily starts the underlying query subscription when the first listener\n\t * attaches, and stops it when the last listener detaches. This allows\n\t * React StrictMode to unmount/remount without permanently killing the subscription.\n\t *\n\t * @returns Unsubscribe function\n\t */\n\tsubscribe = (onStoreChange: () => void): (() => void) => {\n\t\tthis.listeners.add(onStoreChange)\n\n\t\t// Start the underlying query subscription when the first listener attaches\n\t\tif (!this.active) {\n\t\t\tthis.startSubscription()\n\t\t}\n\n\t\treturn () => {\n\t\t\tthis.listeners.delete(onStoreChange)\n\t\t\t// Stop the underlying subscription when the last listener detaches.\n\t\t\t// This ensures cleanup on unmount without needing a useEffect.\n\t\t\tif (this.listeners.size === 0) {\n\t\t\t\tthis.stopSubscription()\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Get the current snapshot synchronously. Compatible with useSyncExternalStore.\n\t * Returns EMPTY_ARRAY before the first async fetch completes.\n\t */\n\tgetSnapshot = (): readonly T[] => {\n\t\treturn this.snapshot\n\t}\n\n\t/**\n\t * Clean up the underlying subscription and release resources.\n\t * Called by useMemo when the query descriptor changes.\n\t */\n\tdestroy(): void {\n\t\tthis.stopSubscription()\n\t\tthis.listeners.clear()\n\t\tthis.snapshot = EMPTY_ARRAY as readonly T[]\n\t}\n\n\tprivate startSubscription(): void {\n\t\tthis.active = true\n\t\tthis.unsubscribeQuery = this.queryBuilder.subscribe((results) => {\n\t\t\tif (!this.active) return\n\t\t\tconst newSnapshot = Object.freeze([...results])\n\t\t\tthis.snapshot = newSnapshot\n\t\t\tthis.notifyListeners()\n\t\t})\n\t}\n\n\tprivate stopSubscription(): void {\n\t\tthis.active = false\n\t\tif (this.unsubscribeQuery) {\n\t\t\tthis.unsubscribeQuery()\n\t\t\tthis.unsubscribeQuery = null\n\t\t}\n\t}\n\n\tprivate notifyListeners(): void {\n\t\tfor (const listener of this.listeners) {\n\t\t\tlistener()\n\t\t}\n\t}\n}\n","import { useCallback, useEffect, useRef, useState } from 'react'\nimport type { UseMutationOptions, UseMutationResult } from '../types'\n\n/**\n * React hook for performing mutations against the local Kora store.\n *\n * Returns `mutate` for fire-and-forget usage and `mutateAsync` when you need\n * to await the result. Optional `onMutate` / `onRollback` support optimistic\n * UI updates that revert if the mutation throws.\n *\n * @param mutationFn - An async function to execute (e.g., `app.todos.insert`)\n * @param options - Optional optimistic/rollback and lifecycle callbacks\n * @returns Object with mutate, mutateAsync, isLoading, error, and reset\n *\n * @example\n * ```typescript\n * const { mutate } = useMutation(app.todos.insert, {\n * onMutate: (data) => {\n * const previous = todos\n * setTodos((list) => [...list, { id: 'temp', ...data }])\n * return previous\n * },\n * onRollback: (previous) => setTodos(previous),\n * })\n * ```\n */\nexport function useMutation<TData, TArgs extends unknown[], TContext = void>(\n\tmutationFn: (...args: TArgs) => Promise<TData>,\n\toptions?: UseMutationOptions<TData, TArgs, TContext>,\n): UseMutationResult<TData, TArgs> {\n\tconst [state, setState] = useState<{ isLoading: boolean; error: Error | null }>({\n\t\tisLoading: false,\n\t\terror: null,\n\t})\n\n\tconst mountedRef = useRef(true)\n\tuseEffect(() => {\n\t\tmountedRef.current = true\n\t\treturn () => {\n\t\t\tmountedRef.current = false\n\t\t}\n\t}, [])\n\n\tconst fnRef = useRef(mutationFn)\n\tfnRef.current = mutationFn\n\n\tconst optionsRef = useRef(options)\n\toptionsRef.current = options\n\n\tconst mutateAsync = useCallback(async (...args: TArgs): Promise<TData> => {\n\t\tconst opts = optionsRef.current\n\t\tlet context: TContext | undefined\n\n\t\tif (mountedRef.current) {\n\t\t\tsetState({ isLoading: true, error: null })\n\t\t}\n\n\t\ttry {\n\t\t\tif (opts?.onMutate) {\n\t\t\t\tcontext = await opts.onMutate(...args)\n\t\t\t}\n\n\t\t\tconst result = await fnRef.current(...args)\n\n\t\t\tif (mountedRef.current) {\n\t\t\t\tsetState({ isLoading: false, error: null })\n\t\t\t}\n\t\t\topts?.onSuccess?.(result, ...args)\n\t\t\topts?.onSettled?.(result, null, ...args)\n\t\t\treturn result\n\t\t} catch (err) {\n\t\t\tconst error = err instanceof Error ? err : new Error(String(err))\n\n\t\t\tif (context !== undefined && opts?.onRollback) {\n\t\t\t\tawait opts.onRollback(context, ...args)\n\t\t\t}\n\n\t\t\tif (mountedRef.current) {\n\t\t\t\tsetState({ isLoading: false, error })\n\t\t\t}\n\t\t\topts?.onError?.(error, ...args)\n\t\t\topts?.onSettled?.(undefined, error, ...args)\n\t\t\tthrow error\n\t\t}\n\t}, [])\n\n\tconst mutate = useCallback(\n\t\t(...args: TArgs): void => {\n\t\t\tmutateAsync(...args).catch(() => {\n\t\t\t\t// Fire-and-forget: error is captured in state, no unhandled rejection\n\t\t\t})\n\t\t},\n\t\t[mutateAsync],\n\t)\n\n\tconst reset = useCallback((): void => {\n\t\tif (mountedRef.current) {\n\t\t\tsetState({ isLoading: false, error: null })\n\t\t}\n\t}, [])\n\n\treturn {\n\t\tmutate,\n\t\tmutateAsync,\n\t\tisLoading: state.isLoading,\n\t\terror: state.error,\n\t\treset,\n\t}\n}\n","import type { KoraEventType } from '@korajs/core'\nimport type { SyncStatusInfo } from '@korajs/sync'\nimport { useCallback, useRef, useSyncExternalStore } from 'react'\nimport { useKoraContext } from '../context/kora-context'\n\nconst OFFLINE_STATUS: SyncStatusInfo = Object.freeze({\n\tstatus: 'offline',\n\tpendingOperations: 0,\n\tlastSyncedAt: null,\n\tlastSuccessfulPush: null,\n\tlastSuccessfulPull: null,\n\tconflicts: 0,\n})\n\nconst SYNC_STATUS_EVENT_TYPES = [\n\t'sync:connected',\n\t'sync:disconnected',\n\t'sync:schema-mismatch',\n\t'sync:auth-failed',\n\t'sync:sent',\n\t'sync:received',\n\t'sync:acknowledged',\n\t'sync:apply-failed',\n\t'sync:diagnostics',\n\t'sync:initial-sync-progress',\n] as const satisfies readonly KoraEventType[]\n\n/**\n * React hook for monitoring the sync engine's connection status.\n *\n * Subscribes to sync events via `app.sync.subscribeStatus` (or the sync engine\n * emitter when using store-only mode) and re-renders only when status changes.\n *\n * @returns Current sync status information\n *\n * @example\n * ```typescript\n * const status = useSyncStatus()\n * // status.status: 'connected' | 'syncing' | 'synced' | 'offline' | 'error'\n * ```\n */\nexport function useSyncStatus(): SyncStatusInfo {\n\tconst { syncEngine, subscribeSyncStatus, events } = useKoraContext()\n\tconst snapshotRef = useRef<SyncStatusInfo>(OFFLINE_STATUS)\n\tconst serializedRef = useRef<string>(JSON.stringify(OFFLINE_STATUS))\n\n\tconst subscribe = useCallback(\n\t\t(onStoreChange: () => void): (() => void) => {\n\t\t\tif (subscribeSyncStatus) {\n\t\t\t\treturn subscribeSyncStatus((status) => {\n\t\t\t\t\tsnapshotRef.current = status\n\t\t\t\t\tserializedRef.current = JSON.stringify(status)\n\t\t\t\t\tonStoreChange()\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tif (!syncEngine) {\n\t\t\t\treturn () => {}\n\t\t\t}\n\n\t\t\tconst refresh = (): void => {\n\t\t\t\tconst newStatus = syncEngine.getStatus()\n\t\t\t\tconst newSerialized = JSON.stringify(newStatus)\n\t\t\t\tif (newSerialized !== serializedRef.current) {\n\t\t\t\t\tsnapshotRef.current = newStatus\n\t\t\t\t\tserializedRef.current = newSerialized\n\t\t\t\t\tonStoreChange()\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (events) {\n\t\t\t\tconst unsubs = SYNC_STATUS_EVENT_TYPES.map((type) => events.on(type, refresh))\n\t\t\t\trefresh()\n\t\t\t\treturn () => {\n\t\t\t\t\tfor (const unsub of unsubs) {\n\t\t\t\t\t\tunsub()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\trefresh()\n\t\t\treturn () => {}\n\t\t},\n\t\t[syncEngine, subscribeSyncStatus, events],\n\t)\n\n\tconst getSnapshot = useCallback((): SyncStatusInfo => {\n\t\tif (subscribeSyncStatus) {\n\t\t\treturn snapshotRef.current\n\t\t}\n\t\treturn syncEngine ? syncEngine.getStatus() : OFFLINE_STATUS\n\t}, [syncEngine, subscribeSyncStatus])\n\n\treturn useSyncExternalStore(subscribe, getSnapshot)\n}\n","import type { CollectionAccessor } from '@korajs/store'\nimport { useMemo } from 'react'\nimport { useKoraContext } from '../context/kora-context'\n\n/**\n * React hook that returns a CollectionAccessor for the given collection name.\n * Convenience hook for accessing a collection without going through the store directly.\n *\n * @param name - The collection name (must match a collection in the schema)\n * @returns CollectionAccessor with insert, findById, update, delete, and where methods\n *\n * @example\n * ```typescript\n * const todos = useCollection('todos')\n * await todos.insert({ title: 'New todo' })\n * ```\n */\nexport function useCollection(name: string): CollectionAccessor {\n\tconst { store } = useKoraContext()\n\n\treturn useMemo(() => {\n\t\treturn store.collection(name)\n\t}, [store, name])\n}\n","import type { CollectionAccessor } from '@korajs/store'\nimport { encodeRichtext } from '@korajs/store'\nimport type { AwarenessState, AwarenessUser, CursorInfo } from '@korajs/sync'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport * as Y from 'yjs'\nimport { useKoraContext } from '../context/kora-context'\nimport type { UseRichTextResult } from '../types'\n\nconst LOAD_ORIGIN = 'kora-load'\nconst REMOTE_ORIGIN = 'kora-remote'\nconst DOC_CHANNEL_ORIGIN = 'kora-doc-channel'\nconst TEXT_KEY = 'content'\nconst COMPACT_AFTER_DELTAS = 20\nconst PERSIST_DEBOUNCE_MS = 400\n\nexport interface UseRichTextOptions {\n\t/** Presence identity broadcast with cursor updates. */\n\tuser?: AwarenessUser\n\t/**\n\t * Use the incremental Yjs doc channel for live edits (recommended for large documents).\n\t * When omitted, the channel activates automatically once the snapshot exceeds the sync threshold.\n\t */\n\tuseDocChannel?: boolean\n}\n\n/**\n * Binds a richtext field to a shared Yjs document for editor integration.\n */\nexport function useRichText(\n\tcollectionName: string,\n\trecordId: string,\n\tfieldName: string,\n\toptions?: UseRichTextOptions,\n): UseRichTextResult {\n\tconst { store, syncEngine } = useKoraContext()\n\tconst collection = useMemo<CollectionAccessor>(\n\t\t() => store.collection(collectionName),\n\t\t[store, collectionName],\n\t)\n\tconst [doc] = useState(() => new Y.Doc())\n\tconst [ready, setReady] = useState(false)\n\tconst [error, setError] = useState<Error | null>(null)\n\tconst [canUndo, setCanUndo] = useState(false)\n\tconst [canRedo, setCanRedo] = useState(false)\n\tconst [cursors, setCursors] = useState<CursorInfo[]>([])\n\tconst baseUpdateRef = useRef<Uint8Array | null>(null)\n\tconst pendingDeltasRef = useRef<Uint8Array[]>([])\n\tconst docChannelActiveRef = useRef(false)\n\tconst persistTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n\tconst userRef = useRef<AwarenessUser | undefined>(options?.user)\n\n\tuseEffect(() => {\n\t\tuserRef.current = options?.user\n\t}, [options?.user])\n\n\tconst text = useMemo(() => doc.getText(TEXT_KEY), [doc])\n\tconst undoManager = useMemo(() => new Y.UndoManager(text), [text])\n\n\tconst syncHistoryState = useCallback(() => {\n\t\tsetCanUndo(undoManager.undoStack.length > 0)\n\t\tsetCanRedo(undoManager.redoStack.length > 0)\n\t}, [undoManager])\n\n\tconst undo = useCallback(() => {\n\t\tundoManager.undo()\n\t\tsyncHistoryState()\n\t}, [syncHistoryState, undoManager])\n\n\tconst redo = useCallback(() => {\n\t\tundoManager.redo()\n\t\tsyncHistoryState()\n\t}, [syncHistoryState, undoManager])\n\n\tconst resolveAwarenessUser = useCallback((): AwarenessUser => {\n\t\tif (userRef.current) {\n\t\t\treturn userRef.current\n\t\t}\n\t\tif (syncEngine) {\n\t\t\tconst existing = syncEngine.getAwarenessManager().getLocalState()?.user\n\t\t\tif (existing) {\n\t\t\t\treturn existing\n\t\t\t}\n\t\t}\n\t\treturn {\n\t\t\tname: 'Anonymous',\n\t\t\tcolor: '#6366f1',\n\t\t}\n\t}, [syncEngine])\n\n\tconst setCursor = useCallback(\n\t\t(anchor: number, head: number) => {\n\t\t\tif (!syncEngine) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst awareness = syncEngine.getAwarenessManager()\n\t\t\tconst state: AwarenessState = {\n\t\t\t\tuser: resolveAwarenessUser(),\n\t\t\t\tcursor: {\n\t\t\t\t\tcollection: collectionName,\n\t\t\t\t\trecordId,\n\t\t\t\t\tfield: fieldName,\n\t\t\t\t\tanchor,\n\t\t\t\t\thead,\n\t\t\t\t},\n\t\t\t}\n\t\t\tawareness.setLocalState(state)\n\t\t},\n\t\t[collectionName, fieldName, recordId, resolveAwarenessUser, syncEngine],\n\t)\n\n\tconst clearCursor = useCallback(() => {\n\t\tif (!syncEngine) {\n\t\t\treturn\n\t\t}\n\n\t\tconst awareness = syncEngine.getAwarenessManager()\n\t\tconst current = awareness.getLocalState()\n\t\tif (!current?.cursor) {\n\t\t\treturn\n\t\t}\n\t\tif (\n\t\t\tcurrent.cursor.collection !== collectionName ||\n\t\t\tcurrent.cursor.recordId !== recordId ||\n\t\t\tcurrent.cursor.field !== fieldName\n\t\t) {\n\t\t\treturn\n\t\t}\n\n\t\tawareness.setLocalState({ user: current.user })\n\t}, [collectionName, fieldName, recordId, syncEngine])\n\n\tconst applyRemoteSnapshot = useCallback(\n\t\t(value: unknown) => {\n\t\t\tconst encoded = encodeRichtextInput(value)\n\t\t\tif (!encoded) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst currentSnapshot = Y.encodeStateAsUpdate(doc)\n\t\t\tif (updatesEqual(currentSnapshot, encoded)) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tY.applyUpdate(doc, encoded, REMOTE_ORIGIN)\n\t\t\tbaseUpdateRef.current = encoded\n\t\t\tpendingDeltasRef.current = []\n\t\t\tsyncHistoryState()\n\t\t},\n\t\t[doc, syncHistoryState],\n\t)\n\n\tuseEffect(() => {\n\t\tlet disposed = false\n\n\t\tconst initialize = async (): Promise<void> => {\n\t\t\tsetReady(false)\n\t\t\tsetError(null)\n\n\t\t\ttry {\n\t\t\t\tconst record = await collection.findById(recordId)\n\t\t\t\tif (disposed) return\n\n\t\t\t\tdoc.transact(() => {\n\t\t\t\t\tconst target = doc.getText(TEXT_KEY)\n\t\t\t\t\ttarget.delete(0, target.length)\n\t\t\t\t}, LOAD_ORIGIN)\n\n\t\t\t\tconst encoded = encodeRichtextInput(record?.[fieldName])\n\t\t\t\tbaseUpdateRef.current = encoded\n\t\t\t\tpendingDeltasRef.current = []\n\t\t\t\tif (encoded) {\n\t\t\t\t\tY.applyUpdate(doc, encoded, LOAD_ORIGIN)\n\t\t\t\t}\n\n\t\t\t\tconst channel = syncEngine?.getRichtextDocChannel?.()\n\t\t\t\tdocChannelActiveRef.current =\n\t\t\t\t\tchannel?.shouldUseChannel(encoded?.length ?? 0, options?.useDocChannel) ?? false\n\n\t\t\t\tsetReady(true)\n\t\t\t} catch (cause) {\n\t\t\t\tif (disposed) return\n\t\t\t\tsetError(cause instanceof Error ? cause : new Error(String(cause)))\n\t\t\t}\n\t\t}\n\n\t\tconst flushPersist = async (): Promise<void> => {\n\t\t\tconst snapshot = composeRichtextSnapshot(baseUpdateRef.current, pendingDeltasRef.current)\n\t\t\tif (pendingDeltasRef.current.length >= COMPACT_AFTER_DELTAS) {\n\t\t\t\tbaseUpdateRef.current = snapshot\n\t\t\t\tpendingDeltasRef.current = []\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tawait collection.update(recordId, {\n\t\t\t\t\t[fieldName]: snapshot,\n\t\t\t\t})\n\t\t\t} catch (cause) {\n\t\t\t\tif (!disposed) {\n\t\t\t\t\tsetError(cause instanceof Error ? cause : new Error(String(cause)))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst schedulePersist = (): void => {\n\t\t\tif (persistTimerRef.current) {\n\t\t\t\tclearTimeout(persistTimerRef.current)\n\t\t\t}\n\t\t\tpersistTimerRef.current = setTimeout(() => {\n\t\t\t\tpersistTimerRef.current = null\n\t\t\t\tvoid flushPersist()\n\t\t\t}, PERSIST_DEBOUNCE_MS)\n\t\t}\n\n\t\tconst persist = (_update: Uint8Array, origin: unknown): void => {\n\t\t\tsyncHistoryState()\n\t\t\tif (origin === LOAD_ORIGIN || origin === REMOTE_ORIGIN || origin === DOC_CHANNEL_ORIGIN) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tpendingDeltasRef.current.push(_update)\n\n\t\t\tif (docChannelActiveRef.current && syncEngine) {\n\t\t\t\tsyncEngine.getRichtextDocChannel().send(collectionName, recordId, fieldName, _update)\n\t\t\t\tschedulePersist()\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tvoid flushPersist()\n\t\t}\n\n\t\tdoc.on('update', persist)\n\t\tvoid initialize()\n\n\t\tsyncHistoryState()\n\n\t\treturn () => {\n\t\t\tdisposed = true\n\t\t\tif (persistTimerRef.current) {\n\t\t\t\tclearTimeout(persistTimerRef.current)\n\t\t\t\tpersistTimerRef.current = null\n\t\t\t}\n\t\t\tdoc.off('update', persist)\n\t\t\tundoManager.destroy()\n\t\t\tbaseUpdateRef.current = null\n\t\t\tpendingDeltasRef.current = []\n\t\t\tdocChannelActiveRef.current = false\n\t\t}\n\t}, [\n\t\tcollection,\n\t\tcollectionName,\n\t\tdoc,\n\t\tfieldName,\n\t\toptions?.useDocChannel,\n\t\trecordId,\n\t\tsyncEngine,\n\t\tsyncHistoryState,\n\t\tundoManager,\n\t])\n\n\t// Incremental Yjs updates from the doc channel (large-document side path).\n\tuseEffect(() => {\n\t\tif (!ready || !syncEngine || !docChannelActiveRef.current) {\n\t\t\treturn\n\t\t}\n\n\t\tconst channel = syncEngine.getRichtextDocChannel()\n\t\treturn channel.subscribe(collectionName, recordId, fieldName, (update) => {\n\t\t\tY.applyUpdate(doc, update, DOC_CHANNEL_ORIGIN)\n\t\t\tsyncHistoryState()\n\t\t})\n\t}, [collectionName, doc, fieldName, ready, recordId, syncEngine, syncHistoryState])\n\n\t// Merge remote richtext writes from sync into the live Y.Doc.\n\tuseEffect(() => {\n\t\tif (!ready) {\n\t\t\treturn\n\t\t}\n\n\t\tconst unsubscribe = store\n\t\t\t.collection(collectionName)\n\t\t\t.where({ id: recordId })\n\t\t\t.subscribe((results) => {\n\t\t\t\tconst record = results[0]\n\t\t\t\tif (!record) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tapplyRemoteSnapshot(record[fieldName])\n\t\t\t})\n\n\t\treturn unsubscribe\n\t}, [applyRemoteSnapshot, collectionName, fieldName, ready, recordId, store])\n\n\t// Track remote collaborators' cursors for this specific field.\n\tuseEffect(() => {\n\t\tif (!syncEngine) return\n\n\t\tconst awareness = syncEngine.getAwarenessManager()\n\t\tconst localClientId = awareness.clientId\n\n\t\tconst updateCursors = (): void => {\n\t\t\tconst states = awareness.getStates()\n\t\t\tconst fieldCursors: CursorInfo[] = []\n\n\t\t\tfor (const [clientId, state] of states) {\n\t\t\t\tif (clientId === localClientId) continue\n\t\t\t\tif (!state.cursor) continue\n\t\t\t\tif (\n\t\t\t\t\tstate.cursor.collection !== collectionName ||\n\t\t\t\t\tstate.cursor.recordId !== recordId ||\n\t\t\t\t\tstate.cursor.field !== fieldName\n\t\t\t\t) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tfieldCursors.push({\n\t\t\t\t\tclientId,\n\t\t\t\t\tuserName: state.user.name,\n\t\t\t\t\tcolor: state.user.color,\n\t\t\t\t\tanchor: state.cursor.anchor,\n\t\t\t\t\thead: state.cursor.head,\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tsetCursors(fieldCursors)\n\t\t}\n\n\t\tconst unsubscribe = awareness.on('change', () => {\n\t\t\tupdateCursors()\n\t\t})\n\n\t\tupdateCursors()\n\n\t\treturn () => {\n\t\t\tunsubscribe()\n\t\t\tclearCursor()\n\t\t}\n\t}, [clearCursor, collectionName, fieldName, recordId, syncEngine])\n\n\treturn { doc, text, undo, redo, canUndo, canRedo, ready, error, cursors, setCursor, clearCursor }\n}\n\nfunction encodeRichtextInput(value: unknown): Uint8Array | null {\n\tif (value === null || value === undefined) {\n\t\treturn null\n\t}\n\n\ttry {\n\t\treturn encodeRichtext(value as Parameters<typeof encodeRichtext>[0])\n\t} catch {\n\t\tif (typeof value === 'string') {\n\t\t\tconst fallbackDoc = new Y.Doc()\n\t\t\tfallbackDoc.getText(TEXT_KEY).insert(0, value)\n\t\t\treturn Y.encodeStateAsUpdate(fallbackDoc)\n\t\t}\n\t\tthrow new Error('Richtext record value must be a string, Uint8Array, ArrayBuffer, or null.')\n\t}\n}\n\nfunction composeRichtextSnapshot(base: Uint8Array | null, deltas: Uint8Array[]): Uint8Array {\n\tconst mergedDoc = new Y.Doc()\n\tif (base) {\n\t\tY.applyUpdate(mergedDoc, base)\n\t}\n\n\tfor (const delta of deltas) {\n\t\tY.applyUpdate(mergedDoc, delta)\n\t}\n\n\treturn Y.encodeStateAsUpdate(mergedDoc)\n}\n\nfunction updatesEqual(left: Uint8Array, right: Uint8Array): boolean {\n\tif (left.length !== right.length) {\n\t\treturn false\n\t}\n\tfor (let index = 0; index < left.length; index++) {\n\t\tif (left[index] !== right[index]) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n"],"mappings":"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","names":["import_react","EMPTY_ARRAY","import_react","import_react","import_react","import_react"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/context/kora-context.ts","../src/hooks/use-app.ts","../src/hooks/use-query.ts","../src/hooks/use-mutation.ts","../src/hooks/use-controller.ts","../src/hooks/use-sync-status.ts","../src/hooks/use-collection.ts","../src/hooks/use-rich-text.ts","../src/hooks/use-presence.ts","../src/hooks/use-collaborators.ts"],"sourcesContent":["// @korajs/react — public API\n// Every export here is a public API commitment. Be explicit.\n\n// === Types ===\nexport type {\n\tKoraAppLike,\n\tKoraContextValue,\n\tKoraProviderProps,\n\tUseQueryOptions,\n\tUseMutationOptions,\n\tUseMutationResult,\n\tUseRichTextResult,\n} from './types'\n\n// === Context ===\nexport { KoraProvider } from './context/kora-context'\n\n// === Hooks ===\nexport { useApp } from './hooks/use-app'\nexport { useQuery } from './hooks/use-query'\nexport { useMutation } from './hooks/use-mutation'\nexport { useSyncStatus } from './hooks/use-sync-status'\nexport { useCollection } from './hooks/use-collection'\nexport { useRichText } from './hooks/use-rich-text'\nexport type { UseRichTextOptions } from './hooks/use-rich-text'\nexport { usePresence } from './hooks/use-presence'\nexport { useCollaborators } from './hooks/use-collaborators'\n","import type { Store } from '@korajs/store'\nimport { QueryStoreCache } from '@korajs/store'\nimport type { SyncEngine } from '@korajs/sync'\nimport { createContext, createElement, useContext, useEffect, useMemo, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport type { KoraContextValue, KoraProviderProps } from '../types'\n\nconst KoraContext = createContext<KoraContextValue | null>(null)\n\n/**\n * Provides Kora store and optional sync engine to all child components.\n * Must wrap any component that uses Kora hooks (useQuery, useMutation, etc.).\n */\nfunction KoraProvider({\n\tapp,\n\tstore,\n\tsyncEngine,\n\tfallback,\n\tchildren,\n}: KoraProviderProps): ReactNode {\n\tconst [resolvedStore, setResolvedStore] = useState<Store | null>(store ?? null)\n\tconst [resolvedSync, setResolvedSync] = useState<SyncEngine | null>(syncEngine ?? null)\n\tconst [ready, setReady] = useState(!app)\n\tconst [initError, setInitError] = useState<Error | null>(null)\n\tconst fallbackQueryStoreCache = useRef<QueryStoreCache | null>(null)\n\n\tif (!fallbackQueryStoreCache.current) {\n\t\tfallbackQueryStoreCache.current = new QueryStoreCache()\n\t}\n\n\tuseEffect(() => {\n\t\tif (!app) return\n\t\tlet cancelled = false\n\t\tapp.ready\n\t\t\t.then(() => {\n\t\t\t\tif (cancelled) return\n\t\t\t\tsetResolvedStore(app.getStore())\n\t\t\t\tsetResolvedSync(app.getSyncEngine())\n\t\t\t\tsetReady(true)\n\t\t\t})\n\t\t\t.catch((error: unknown) => {\n\t\t\t\tif (cancelled) return\n\t\t\t\tconst err = error instanceof Error ? error : new Error(String(error))\n\t\t\t\tconsole.error('[Kora] Initialization failed:', err)\n\t\t\t\tsetInitError(err)\n\t\t\t})\n\t\treturn () => {\n\t\t\tcancelled = true\n\t\t}\n\t}, [app])\n\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tif (!app) {\n\t\t\t\tfallbackQueryStoreCache.current?.clear()\n\t\t\t}\n\t\t}\n\t}, [app])\n\n\tconst contextValue = useMemo<KoraContextValue | null>(() => {\n\t\tif (!resolvedStore) {\n\t\t\treturn null\n\t\t}\n\t\treturn {\n\t\t\tstore: resolvedStore,\n\t\t\tsyncEngine: resolvedSync,\n\t\t\tapp: app ?? null,\n\t\t\tevents: app?.events ?? null,\n\t\t\tsubscribeSyncStatus: app?.sync?.subscribeStatus ?? null,\n\t\t\tqueryStoreCache:\n\t\t\t\tapp && typeof app.getQueryStoreCache === 'function'\n\t\t\t\t\t? app.getQueryStoreCache()\n\t\t\t\t\t: fallbackQueryStoreCache.current!,\n\t\t}\n\t}, [resolvedStore, resolvedSync, app])\n\n\tif (initError) {\n\t\treturn createElement(\n\t\t\t'div',\n\t\t\t{ style: { color: 'red', padding: '1rem', fontFamily: 'monospace' } },\n\t\t\tcreateElement('strong', null, 'Kora initialization error: '),\n\t\t\tinitError.message,\n\t\t)\n\t}\n\n\tif (!ready) {\n\t\treturn (fallback ?? null) as ReactNode\n\t}\n\n\tif (!contextValue) {\n\t\tthrow new Error(\n\t\t\t'KoraProvider requires either an \"app\" or \"store\" prop. ' +\n\t\t\t\t'Pass a KoraApp from createApp() or a Store instance.',\n\t\t)\n\t}\n\n\treturn createElement(KoraContext.Provider, { value: contextValue }, children)\n}\n\nfunction useKoraContext(): KoraContextValue {\n\tconst context = useContext(KoraContext)\n\tif (context === null) {\n\t\tthrow new Error(\n\t\t\t'useKoraContext must be used within a <KoraProvider>. ' +\n\t\t\t\t'Wrap your component tree with <KoraProvider store={store}>.',\n\t\t)\n\t}\n\treturn context\n}\n\nexport { KoraProvider, useKoraContext }\n","import { useKoraContext } from '../context/kora-context'\nimport type { KoraAppLike } from '../types'\n\n/**\n * React hook that returns the Kora app instance from context.\n *\n * Use the generic parameter to cast to your typed app for full type inference:\n *\n * ```typescript\n * // In your app setup:\n * export const app = createApp({ schema: mySchema })\n * export type App = typeof app\n *\n * // In components:\n * const app = useApp<App>()\n * app.todos.insert({ title: 'Hello' }) // fully typed\n * const todos = useQuery(app.todos.where({ completed: false }))\n * ```\n *\n * Requires `KoraProvider` to be initialized with the `app` prop.\n * Throws if used outside of `KoraProvider` or without an `app` prop.\n *\n * @returns The KoraApp instance, typed as `T`\n */\nexport function useApp<T extends KoraAppLike = KoraAppLike>(): T {\n\tconst { app } = useKoraContext()\n\tif (!app) {\n\t\tthrow new Error(\n\t\t\t'useApp() requires KoraProvider to be initialized with an \"app\" prop. ' +\n\t\t\t\t'Pass your createApp() result to <KoraProvider app={app}>.',\n\t\t)\n\t}\n\treturn app as T\n}\n","import type { CollectionRecord, QueryBuilder } from '@korajs/store'\nimport { assertQueryReady } from '@korajs/store'\nimport { useEffect, useRef, useState, useSyncExternalStore } from 'react'\nimport { useKoraContext } from '../context/kora-context'\nimport type { UseQueryOptions } from '../types'\n\nconst EMPTY_ARRAY: readonly unknown[] = Object.freeze([])\n\nconst noopSubscribe = (_onStoreChange: () => void): (() => void) => {\n\treturn () => {}\n}\n\n/**\n * React hook for reactive queries against the local Kora store.\n */\nexport function useQuery<T = CollectionRecord>(\n\tquery: QueryBuilder<T>,\n\toptions?: UseQueryOptions,\n): readonly T[] {\n\tconst { queryStoreCache } = useKoraContext()\n\tconst enabled = options?.enabled !== false\n\tconst descriptorKey = JSON.stringify(query.getDescriptor())\n\tconst queryRef = useRef(query)\n\tqueryRef.current = query\n\n\tconst [queryStore, setQueryStore] = useState<import('@korajs/store').QueryStore<T> | null>(null)\n\n\tuseEffect(() => {\n\t\tif (!enabled) {\n\t\t\tsetQueryStore(null)\n\t\t\treturn\n\t\t}\n\n\t\tconst currentQuery = queryRef.current\n\t\tassertQueryReady(currentQuery)\n\t\tconst store = queryStoreCache.getOrCreate(currentQuery)\n\t\tsetQueryStore(store)\n\n\t\treturn () => {\n\t\t\tqueryStoreCache.release(currentQuery as QueryBuilder<unknown>)\n\t\t\tsetQueryStore(null)\n\t\t}\n\t}, [descriptorKey, enabled, queryStoreCache])\n\n\tconst disabledGetSnapshot = (): readonly T[] => EMPTY_ARRAY as readonly T[]\n\n\treturn useSyncExternalStore(\n\t\tqueryStore ? queryStore.subscribe : noopSubscribe,\n\t\tqueryStore ? queryStore.getSnapshot : disabledGetSnapshot,\n\t)\n}\n","import { createMutationController } from '@korajs/core/bindings'\nimport { useRef, useSyncExternalStore } from 'react'\nimport type { UseMutationOptions, UseMutationResult } from '../types'\nimport { useController } from './use-controller'\n\n/**\n * React hook for performing mutations against the local Kora store.\n */\nexport function useMutation<TData, TArgs extends unknown[], TContext = void>(\n\tmutationFn: (...args: TArgs) => Promise<TData>,\n\toptions?: UseMutationOptions<TData, TArgs, TContext>,\n): UseMutationResult<TData, TArgs> {\n\tconst fnRef = useRef(mutationFn)\n\tfnRef.current = mutationFn\n\n\tconst optionsRef = useRef(options)\n\toptionsRef.current = options\n\n\tconst getController = useController(\n\t\t() =>\n\t\t\tcreateMutationController<TData, TArgs, TContext>({\n\t\t\t\tmutationFn: (...args) => fnRef.current(...args),\n\t\t\t\tresolveOptions: () => optionsRef.current,\n\t\t\t}),\n\t\t(controller) => controller.destroy(),\n\t\t[],\n\t)\n\n\tconst state = useSyncExternalStore(\n\t\t(onStoreChange) => getController().subscribe(onStoreChange),\n\t\t() => getController().getSnapshot(),\n\t\t() => getController().getSnapshot(),\n\t)\n\n\treturn {\n\t\tmutate: (...args: TArgs) => getController().mutate(...args),\n\t\tmutateAsync: (...args: TArgs) => getController().mutateAsync(...args),\n\t\tisLoading: state.isLoading,\n\t\terror: state.error,\n\t\treset: () => getController().reset(),\n\t}\n}\n","import { useEffect, useReducer, useRef } from 'react'\n\n/**\n * Manages the lifecycle of an external controller in a StrictMode-safe way.\n *\n * React 18+ StrictMode mounts, unmounts, and remounts components in development.\n * The previous pattern (create in `useMemo`, destroy in effect cleanup) permanently\n * destroyed the memoized controller on the simulated unmount, leaving every\n * subsequent interaction against a disposed controller.\n *\n * This hook instead keeps the controller in a ref and recreates it lazily\n * whenever it has been destroyed, so the effect cleanup / re-run cycle is safe.\n *\n * Returns a getter so subscribe/getSnapshot closures always reach a live controller.\n */\nexport function useController<C>(\n\tcreate: () => C,\n\tdestroy: (controller: C) => void,\n\tdeps: readonly unknown[],\n): () => C {\n\tconst controllerRef = useRef<C | null>(null)\n\tconst createRef = useRef(create)\n\tcreateRef.current = create\n\n\tconst [, forceRender] = useReducer((count: number) => count + 1, 0)\n\n\tconst getController = useRef((): C => {\n\t\tif (controllerRef.current === null) {\n\t\t\tcontrollerRef.current = createRef.current()\n\t\t}\n\t\treturn controllerRef.current\n\t}).current\n\n\t// biome-ignore lint/correctness/useExhaustiveDependencies: deps are the caller's create() inputs\n\tuseEffect(() => {\n\t\tif (controllerRef.current === null) {\n\t\t\tcontrollerRef.current = createRef.current()\n\t\t\t// A destroyed controller was recreated (StrictMode remount or deps\n\t\t\t// change): re-render so useSyncExternalStore rebinds to the new one.\n\t\t\tforceRender()\n\t\t}\n\t\treturn () => {\n\t\t\tconst controller = controllerRef.current\n\t\t\tcontrollerRef.current = null\n\t\t\tif (controller !== null) {\n\t\t\t\tdestroy(controller)\n\t\t\t}\n\t\t}\n\t}, deps)\n\n\treturn getController\n}\n","import type { SyncStatusInfo } from '@korajs/sync'\nimport { createSyncStatusController } from '@korajs/sync'\nimport { useSyncExternalStore } from 'react'\nimport { useKoraContext } from '../context/kora-context'\nimport { useController } from './use-controller'\n\n/**\n * React hook for monitoring the sync engine's connection status.\n */\nexport function useSyncStatus(): SyncStatusInfo {\n\tconst { syncEngine, subscribeSyncStatus, events } = useKoraContext()\n\n\tconst getController = useController(\n\t\t() =>\n\t\t\tcreateSyncStatusController({\n\t\t\t\tsyncEngine,\n\t\t\t\tsubscribeSyncStatus,\n\t\t\t\tevents: subscribeSyncStatus ? null : events,\n\t\t\t}),\n\t\t(controller) => controller.destroy(),\n\t\t[syncEngine, subscribeSyncStatus, events],\n\t)\n\n\treturn useSyncExternalStore(\n\t\t(onStoreChange) => getController().subscribe(onStoreChange),\n\t\t() => getController().getSnapshot(),\n\t\t() => getController().getSnapshot(),\n\t)\n}\n","import type { CollectionAccessor } from '@korajs/store'\nimport { useMemo } from 'react'\nimport { useKoraContext } from '../context/kora-context'\n\n/**\n * React hook that returns a CollectionAccessor for the given collection name.\n * Convenience hook for accessing a collection without going through the store directly.\n *\n * @param name - The collection name (must match a collection in the schema)\n * @returns CollectionAccessor with insert, findById, update, delete, and where methods\n *\n * @example\n * ```typescript\n * const todos = useCollection('todos')\n * await todos.insert({ title: 'New todo' })\n * ```\n */\nexport function useCollection(name: string): CollectionAccessor {\n\tconst { store } = useKoraContext()\n\n\treturn useMemo(() => {\n\t\treturn store.collection(name)\n\t}, [store, name])\n}\n","import { asRichTextSyncEngine, createRichTextController } from '@korajs/store'\nimport type { RichTextControllerSnapshot } from '@korajs/store'\nimport type { AwarenessUser } from '@korajs/sync'\nimport { useCallback, useEffect, useMemo, useSyncExternalStore } from 'react'\nimport { useKoraContext } from '../context/kora-context'\nimport type { UseRichTextResult } from '../types'\nimport { useController } from './use-controller'\n\nexport interface UseRichTextOptions {\n\tuser?: AwarenessUser\n\tuseDocChannel?: boolean\n}\n\n/**\n * Binds a richtext field to a shared Yjs document for editor integration.\n */\nexport function useRichText(\n\tcollectionName: string,\n\trecordId: string,\n\tfieldName: string,\n\toptions?: UseRichTextOptions,\n): UseRichTextResult {\n\tconst { store, syncEngine } = useKoraContext()\n\tconst collection = useMemo(() => store.collection(collectionName), [store, collectionName])\n\n\tconst getController = useController(\n\t\t() =>\n\t\t\tcreateRichTextController({\n\t\t\t\tcollection,\n\t\t\t\tcollectionName,\n\t\t\t\trecordId,\n\t\t\t\tfieldName,\n\t\t\t\tstore,\n\t\t\t\tsyncEngine: asRichTextSyncEngine(syncEngine),\n\t\t\t\tuseDocChannel: options?.useDocChannel,\n\t\t\t\tuser: options?.user,\n\t\t\t}),\n\t\t(controller) => controller.destroy(),\n\t\t[collection, collectionName, fieldName, options?.useDocChannel, recordId, store, syncEngine],\n\t)\n\n\tuseEffect(() => {\n\t\tgetController().setUser(options?.user)\n\t}, [getController, options?.user])\n\n\tconst snapshot = useSyncExternalStore(\n\t\t(onStoreChange) => getController().subscribe(onStoreChange),\n\t\t() => getController().getSnapshot(),\n\t\t() => getController().getSnapshot(),\n\t)\n\n\tconst undo = useCallback(() => {\n\t\tgetController().undo()\n\t}, [getController])\n\tconst redo = useCallback(() => {\n\t\tgetController().redo()\n\t}, [getController])\n\tconst setCursor = useCallback(\n\t\t(anchor: number, head: number) => getController().setCursor(anchor, head),\n\t\t[getController],\n\t)\n\tconst clearCursor = useCallback(() => getController().clearCursor(), [getController])\n\n\treturn buildResult(getController(), snapshot, undo, redo, setCursor, clearCursor)\n}\n\nfunction buildResult(\n\tcontroller: ReturnType<typeof createRichTextController>,\n\tsnapshot: RichTextControllerSnapshot,\n\tundo: () => void,\n\tredo: () => void,\n\tsetCursor: (anchor: number, head: number) => void,\n\tclearCursor: () => void,\n): UseRichTextResult {\n\treturn {\n\t\tdoc: controller.doc,\n\t\ttext: controller.text,\n\t\tundo,\n\t\tredo,\n\t\tcanUndo: snapshot.canUndo,\n\t\tcanRedo: snapshot.canRedo,\n\t\tready: snapshot.ready,\n\t\terror: snapshot.error,\n\t\tcursors: [...snapshot.cursors],\n\t\tsetCursor,\n\t\tclearCursor,\n\t}\n}\n","import { useEffect } from 'react'\nimport { useKoraContext } from '../context/kora-context'\n\n/**\n * Sets the local user's collaborative presence state.\n *\n * Automatically cleans up presence on unmount.\n */\nexport function usePresence(user: { name: string; color: string; avatar?: string } | null): void {\n\tconst { syncEngine } = useKoraContext()\n\tconst name = user?.name ?? null\n\tconst color = user?.color ?? null\n\tconst avatar = user?.avatar ?? null\n\n\tuseEffect(() => {\n\t\tif (!syncEngine || !name || !color) return\n\n\t\tconst awareness = syncEngine.getAwarenessManager()\n\t\tawareness.setLocalState({\n\t\t\tuser: { name, color, avatar: avatar ?? undefined },\n\t\t})\n\n\t\treturn () => {\n\t\t\tawareness.setLocalState(null)\n\t\t}\n\t}, [syncEngine, name, color, avatar])\n}\n","import type { AwarenessState } from '@korajs/sync'\nimport { subscribeRemoteAwarenessStates } from '@korajs/sync'\nimport { useCallback, useEffect, useRef, useSyncExternalStore } from 'react'\nimport { useKoraContext } from '../context/kora-context'\n\nconst EMPTY_ARRAY: AwarenessState[] = []\n\n/**\n * Returns all currently connected collaborators' awareness states.\n *\n * Excludes the local user — only returns remote peers.\n */\nexport function useCollaborators(): AwarenessState[] {\n\tconst { syncEngine } = useKoraContext()\n\n\tconst snapshotRef = useRef<AwarenessState[]>(EMPTY_ARRAY)\n\n\tconst subscribe = useCallback(\n\t\t(onStoreChange: () => void): (() => void) => {\n\t\t\tif (!syncEngine) {\n\t\t\t\tsnapshotRef.current = EMPTY_ARRAY\n\t\t\t\treturn () => {}\n\t\t\t}\n\n\t\t\tconst awareness = syncEngine.getAwarenessManager()\n\t\t\treturn subscribeRemoteAwarenessStates(awareness, (states) => {\n\t\t\t\tsnapshotRef.current = states\n\t\t\t\tonStoreChange()\n\t\t\t})\n\t\t},\n\t\t[syncEngine],\n\t)\n\n\tconst getSnapshot = useCallback((): AwarenessState[] => snapshotRef.current, [])\n\n\tuseEffect(() => {\n\t\tif (!syncEngine) {\n\t\t\tsnapshotRef.current = EMPTY_ARRAY\n\t\t}\n\t}, [syncEngine])\n\n\treturn useSyncExternalStore(subscribe, getSnapshot)\n}\n"],"mappings":"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","names":["import_store","import_react","import_react","import_react","import_react","import_react","import_store","import_react","import_react","import_sync","import_react","EMPTY_ARRAY"]}
package/dist/index.d.cts CHANGED
@@ -1,42 +1,13 @@
1
- import { KoraEventEmitter } from '@korajs/core';
2
- import { Store, CollectionRecord, QueryBuilder, CollectionAccessor } from '@korajs/store';
3
- import { SyncStatusInfo, SyncEngine, CursorInfo, AwarenessUser } from '@korajs/sync';
1
+ import { KoraAppLike as KoraAppLike$1, KoraContextValue as KoraContextValue$1, UseMutationOptions as UseMutationOptions$1, UseMutationResultBase, UseQueryOptions as UseQueryOptions$1 } from '@korajs/core/bindings';
2
+ import { Store, QueryStoreCache, CollectionRecord, QueryBuilder, CollectionAccessor } from '@korajs/store';
3
+ import { SyncEngine, CursorInfo, SyncStatusInfo, AwarenessUser, AwarenessState } from '@korajs/sync';
4
4
  import { ReactNode } from 'react';
5
5
  import * as Y from 'yjs';
6
6
 
7
- /**
8
- * A Kora application instance returned by createApp().
9
- * Defined here to avoid a hard dependency on the kora meta-package.
10
- */
11
- interface KoraAppLike {
12
- /** Resolves when the store is open and collections are ready. */
13
- ready: Promise<void>;
14
- /** Framework event emitter (sync, store, merge, query). */
15
- events?: KoraEventEmitter;
16
- /** Sync control when sync is configured. */
17
- sync?: {
18
- subscribeStatus(listener: (status: SyncStatusInfo) => void): () => void;
19
- } | null;
20
- /** Get the underlying Store instance. */
21
- getStore(): Store;
22
- /** Get the underlying SyncEngine instance. Null if sync not configured. */
23
- getSyncEngine(): SyncEngine | null;
24
- }
25
- /**
26
- * Value provided by KoraProvider via React context.
27
- */
28
- interface KoraContextValue {
29
- /** The local Kora store instance */
30
- store: Store;
31
- /** Optional sync engine for remote synchronization */
32
- syncEngine: SyncEngine | null;
33
- /** The KoraApp instance (when provided via app prop). */
34
- app: KoraAppLike | null;
35
- /** App event emitter (when using app prop). */
36
- events: KoraEventEmitter | null;
37
- /** Event-driven sync status subscription from app.sync. */
38
- subscribeSyncStatus: ((listener: (status: SyncStatusInfo) => void) => () => void) | null;
39
- }
7
+ type KoraAppLike = KoraAppLike$1<Store, SyncEngine, QueryStoreCache>;
8
+ type KoraContextValue = KoraContextValue$1<Store, SyncEngine, QueryStoreCache>;
9
+ type UseQueryOptions = UseQueryOptions$1;
10
+ type UseMutationOptions<TData, TArgs extends unknown[], TContext = void> = UseMutationOptions$1<TData, TArgs, TContext>;
40
11
  /**
41
12
  * Props for the KoraProvider component.
42
13
  *
@@ -44,104 +15,33 @@ interface KoraContextValue {
44
15
  * explicit `store` + `syncEngine` props (advanced use case).
45
16
  */
46
17
  interface KoraProviderProps {
47
- /** A KoraApp instance from createApp(). Extracts store and syncEngine automatically. */
48
18
  app?: KoraAppLike;
49
- /** The local Kora store instance (alternative to app prop). */
50
19
  store?: Store;
51
- /** Optional sync engine for remote synchronization (used with store prop). */
52
20
  syncEngine?: SyncEngine | null;
53
- /** Fallback content to render while app.ready is resolving. Defaults to null. */
54
21
  fallback?: ReactNode;
55
- /** Child components */
56
22
  children?: ReactNode;
57
23
  }
58
- /**
59
- * Options for the useQuery hook.
60
- */
61
- interface UseQueryOptions {
62
- /** Set to false to disable the subscription (query won't execute). Defaults to true. */
63
- enabled?: boolean;
64
- }
65
- /**
66
- * Options for optimistic updates and rollback in {@link useMutation}.
67
- */
68
- interface UseMutationOptions<TData, TArgs extends unknown[], TContext = void> {
69
- /**
70
- * Runs before the mutation. Return a context value for rollback.
71
- */
72
- onMutate?: (...args: TArgs) => TContext | Promise<TContext>;
73
- /**
74
- * Reverts optimistic changes when the mutation fails.
75
- */
76
- onRollback?: (context: TContext, ...args: TArgs) => void | Promise<void>;
77
- /** Called when the mutation succeeds. */
78
- onSuccess?: (data: TData, ...args: TArgs) => void;
79
- /** Called when the mutation fails (after optional rollback). */
80
- onError?: (error: Error, ...args: TArgs) => void;
81
- /** Called after success or failure. */
82
- onSettled?: (data: TData | undefined, error: Error | null, ...args: TArgs) => void;
83
- }
84
- /**
85
- * Result from the useMutation hook.
86
- */
87
- interface UseMutationResult<TData, TArgs extends unknown[]> {
88
- /** Fire-and-forget mutation. Catches errors silently (sets error state). */
89
- mutate: (...args: TArgs) => void;
90
- /** Promise-returning mutation. Throws on error. */
91
- mutateAsync: (...args: TArgs) => Promise<TData>;
92
- /** Whether a mutation is currently in progress */
24
+ interface UseMutationResult<TData, TArgs extends unknown[]> extends UseMutationResultBase<TData, TArgs> {
93
25
  isLoading: boolean;
94
- /** The last error that occurred, or null */
95
26
  error: Error | null;
96
- /** Reset isLoading and error state */
97
- reset: () => void;
98
27
  }
99
- /**
100
- * Result from the useRichText hook.
101
- */
102
28
  interface UseRichTextResult {
103
- /** Shared Yjs document backing this field. */
104
29
  doc: Y.Doc;
105
- /** Y.Text instance to bind to editor integrations. */
106
30
  text: Y.Text;
107
- /** Undo local changes made to this richtext field. */
108
31
  undo: () => void;
109
- /** Redo previously undone local changes. */
110
32
  redo: () => void;
111
- /** True when undo can be applied. */
112
33
  canUndo: boolean;
113
- /** True when redo can be applied. */
114
34
  canRedo: boolean;
115
- /** True once the record field has been loaded into the Y.Doc. */
116
35
  ready: boolean;
117
- /** Last hook error (load/persist), if any. */
118
36
  error: Error | null;
119
- /** Remote collaborators' cursor positions in this field. Empty if no sync engine. */
120
37
  cursors: CursorInfo[];
121
- /** Publish local cursor/selection to connected collaborators. No-op without sync. */
122
38
  setCursor: (anchor: number, head: number) => void;
123
- /** Clear local cursor presence for this field. */
124
39
  clearCursor: () => void;
125
40
  }
126
41
 
127
42
  /**
128
43
  * Provides Kora store and optional sync engine to all child components.
129
44
  * Must wrap any component that uses Kora hooks (useQuery, useMutation, etc.).
130
- *
131
- * Accepts either an `app` prop (recommended) or explicit `store` + `syncEngine` props.
132
- *
133
- * When using the `app` prop, KoraProvider waits for `app.ready` before rendering
134
- * children. A `fallback` prop can be provided to show content while initializing.
135
- *
136
- * @example
137
- * ```typescript
138
- * // Recommended: pass the app object directly
139
- * const app = createApp({ schema })
140
- * <KoraProvider app={app}><App /></KoraProvider>
141
- *
142
- * // Advanced: pass store and syncEngine explicitly
143
- * <KoraProvider store={store} syncEngine={syncEngine}><App /></KoraProvider>
144
- * ```
145
45
  */
146
46
  declare function KoraProvider({ app, store, syncEngine, fallback, children, }: KoraProviderProps): ReactNode;
147
47
 
@@ -170,64 +70,16 @@ declare function useApp<T extends KoraAppLike = KoraAppLike>(): T;
170
70
 
171
71
  /**
172
72
  * React hook for reactive queries against the local Kora store.
173
- *
174
- * Returns data synchronously from the local store — no loading spinners needed.
175
- * Re-renders automatically when the query results change due to mutations.
176
- * Uses `useSyncExternalStore` for React 18+ concurrent mode safety.
177
- *
178
- * The generic parameter `T` is inferred from the QueryBuilder, providing
179
- * full type safety when used with typed collection accessors.
180
- *
181
- * @param query - A QueryBuilder instance (e.g., `app.todos.where({ done: false })`)
182
- * @param options - Optional configuration (e.g., `{ enabled: false }` to skip the query)
183
- * @returns Readonly array of matching records
184
- *
185
- * @example
186
- * ```typescript
187
- * const todos = useQuery(app.todos.where({ completed: false }).orderBy('createdAt'))
188
- * // todos is typed as readonly InferRecord<typeof todoFields>[]
189
- * ```
190
73
  */
191
74
  declare function useQuery<T = CollectionRecord>(query: QueryBuilder<T>, options?: UseQueryOptions): readonly T[];
192
75
 
193
76
  /**
194
77
  * React hook for performing mutations against the local Kora store.
195
- *
196
- * Returns `mutate` for fire-and-forget usage and `mutateAsync` when you need
197
- * to await the result. Optional `onMutate` / `onRollback` support optimistic
198
- * UI updates that revert if the mutation throws.
199
- *
200
- * @param mutationFn - An async function to execute (e.g., `app.todos.insert`)
201
- * @param options - Optional optimistic/rollback and lifecycle callbacks
202
- * @returns Object with mutate, mutateAsync, isLoading, error, and reset
203
- *
204
- * @example
205
- * ```typescript
206
- * const { mutate } = useMutation(app.todos.insert, {
207
- * onMutate: (data) => {
208
- * const previous = todos
209
- * setTodos((list) => [...list, { id: 'temp', ...data }])
210
- * return previous
211
- * },
212
- * onRollback: (previous) => setTodos(previous),
213
- * })
214
- * ```
215
78
  */
216
79
  declare function useMutation<TData, TArgs extends unknown[], TContext = void>(mutationFn: (...args: TArgs) => Promise<TData>, options?: UseMutationOptions<TData, TArgs, TContext>): UseMutationResult<TData, TArgs>;
217
80
 
218
81
  /**
219
82
  * React hook for monitoring the sync engine's connection status.
220
- *
221
- * Subscribes to sync events via `app.sync.subscribeStatus` (or the sync engine
222
- * emitter when using store-only mode) and re-renders only when status changes.
223
- *
224
- * @returns Current sync status information
225
- *
226
- * @example
227
- * ```typescript
228
- * const status = useSyncStatus()
229
- * // status.status: 'connected' | 'syncing' | 'synced' | 'offline' | 'error'
230
- * ```
231
83
  */
232
84
  declare function useSyncStatus(): SyncStatusInfo;
233
85
 
@@ -247,12 +99,7 @@ declare function useSyncStatus(): SyncStatusInfo;
247
99
  declare function useCollection(name: string): CollectionAccessor;
248
100
 
249
101
  interface UseRichTextOptions {
250
- /** Presence identity broadcast with cursor updates. */
251
102
  user?: AwarenessUser;
252
- /**
253
- * Use the incremental Yjs doc channel for live edits (recommended for large documents).
254
- * When omitted, the channel activates automatically once the snapshot exceeds the sync threshold.
255
- */
256
103
  useDocChannel?: boolean;
257
104
  }
258
105
  /**
@@ -260,4 +107,22 @@ interface UseRichTextOptions {
260
107
  */
261
108
  declare function useRichText(collectionName: string, recordId: string, fieldName: string, options?: UseRichTextOptions): UseRichTextResult;
262
109
 
263
- export { type KoraAppLike, type KoraContextValue, KoraProvider, type KoraProviderProps, type UseMutationOptions, type UseMutationResult, type UseQueryOptions, type UseRichTextOptions, type UseRichTextResult, useApp, useCollection, useMutation, useQuery, useRichText, useSyncStatus };
110
+ /**
111
+ * Sets the local user's collaborative presence state.
112
+ *
113
+ * Automatically cleans up presence on unmount.
114
+ */
115
+ declare function usePresence(user: {
116
+ name: string;
117
+ color: string;
118
+ avatar?: string;
119
+ } | null): void;
120
+
121
+ /**
122
+ * Returns all currently connected collaborators' awareness states.
123
+ *
124
+ * Excludes the local user — only returns remote peers.
125
+ */
126
+ declare function useCollaborators(): AwarenessState[];
127
+
128
+ export { type KoraAppLike, type KoraContextValue, KoraProvider, type KoraProviderProps, type UseMutationOptions, type UseMutationResult, type UseQueryOptions, type UseRichTextOptions, type UseRichTextResult, useApp, useCollaborators, useCollection, useMutation, usePresence, useQuery, useRichText, useSyncStatus };