@korajs/react 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -116,24 +116,30 @@ var QueryStore = class {
116
116
  snapshot = EMPTY_ARRAY;
117
117
  listeners = /* @__PURE__ */ new Set();
118
118
  unsubscribeQuery = null;
119
- destroyed = false;
119
+ active = false;
120
+ queryBuilder;
120
121
  constructor(queryBuilder) {
121
- this.unsubscribeQuery = queryBuilder.subscribe((results) => {
122
- if (this.destroyed) return;
123
- const newSnapshot = Object.freeze([...results]);
124
- this.snapshot = newSnapshot;
125
- this.notifyListeners();
126
- });
122
+ this.queryBuilder = queryBuilder;
127
123
  }
128
124
  /**
129
125
  * Subscribe to snapshot changes. Compatible with useSyncExternalStore.
130
126
  *
127
+ * Lazily starts the underlying query subscription when the first listener
128
+ * attaches, and stops it when the last listener detaches. This allows
129
+ * React StrictMode to unmount/remount without permanently killing the subscription.
130
+ *
131
131
  * @returns Unsubscribe function
132
132
  */
133
133
  subscribe = (onStoreChange) => {
134
134
  this.listeners.add(onStoreChange);
135
+ if (!this.active) {
136
+ this.startSubscription();
137
+ }
135
138
  return () => {
136
139
  this.listeners.delete(onStoreChange);
140
+ if (this.listeners.size === 0) {
141
+ this.stopSubscription();
142
+ }
137
143
  };
138
144
  };
139
145
  /**
@@ -145,15 +151,28 @@ var QueryStore = class {
145
151
  };
146
152
  /**
147
153
  * Clean up the underlying subscription and release resources.
154
+ * Called by useMemo when the query descriptor changes.
148
155
  */
149
156
  destroy() {
150
- this.destroyed = true;
157
+ this.stopSubscription();
158
+ this.listeners.clear();
159
+ this.snapshot = EMPTY_ARRAY;
160
+ }
161
+ startSubscription() {
162
+ this.active = true;
163
+ this.unsubscribeQuery = this.queryBuilder.subscribe((results) => {
164
+ if (!this.active) return;
165
+ const newSnapshot = Object.freeze([...results]);
166
+ this.snapshot = newSnapshot;
167
+ this.notifyListeners();
168
+ });
169
+ }
170
+ stopSubscription() {
171
+ this.active = false;
151
172
  if (this.unsubscribeQuery) {
152
173
  this.unsubscribeQuery();
153
174
  this.unsubscribeQuery = null;
154
175
  }
155
- this.listeners.clear();
156
- this.snapshot = EMPTY_ARRAY;
157
176
  }
158
177
  notifyListeners() {
159
178
  for (const listener of this.listeners) {
@@ -193,14 +212,6 @@ function useQuery(query, options) {
193
212
  }
194
213
  return queryStoreRef.current;
195
214
  }, [descriptorKey, enabled, query]);
196
- (0, import_react2.useEffect)(() => {
197
- return () => {
198
- if (queryStoreRef.current) {
199
- queryStoreRef.current.destroy();
200
- queryStoreRef.current = null;
201
- }
202
- };
203
- }, []);
204
215
  const disabledGetSnapshot = () => EMPTY_ARRAY2;
205
216
  return (0, import_react2.useSyncExternalStore)(
206
217
  queryStore ? queryStore.subscribe : noopSubscribe,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/context/kora-context.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\tUseMutationResult,\n\tUseRichTextResult,\n} from './types'\n\n// === Context ===\nexport { KoraProvider } from './context/kora-context'\n\n// === Hooks ===\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'\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>(\n\t\tstore ?? null,\n\t)\n\tconst [resolvedSync, setResolvedSync] = useState<SyncEngine | null>(\n\t\tsyncEngine ?? null,\n\t)\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}\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 type { CollectionRecord, QueryBuilder } from '@korajs/store'\nimport { useEffect, useMemo, useRef, useSyncExternalStore } from 'react'\nimport { QueryStore } from '../query-store/query-store'\nimport type { UseQueryOptions } from '../types'\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\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// Clean up on unmount\n\tuseEffect(() => {\n\t\treturn () => {\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}\n\t}, [])\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 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 * Starts the underlying subscription eagerly on construction, so that\n * the snapshot is populated by the time useSyncExternalStore reads it.\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 destroyed = false\n\n\tconstructor(queryBuilder: QueryBuilder<T>) {\n\t\t// Start subscription eagerly so snapshot is populated before first getSnapshot() call\n\t\tthis.unsubscribeQuery = queryBuilder.subscribe((results) => {\n\t\t\tif (this.destroyed) return\n\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\t/**\n\t * Subscribe to snapshot changes. Compatible with useSyncExternalStore.\n\t *\n\t * @returns Unsubscribe function\n\t */\n\tsubscribe = (onStoreChange: () => void): (() => void) => {\n\t\tthis.listeners.add(onStoreChange)\n\n\t\treturn () => {\n\t\t\tthis.listeners.delete(onStoreChange)\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 */\n\tdestroy(): void {\n\t\tthis.destroyed = true\n\t\tif (this.unsubscribeQuery) {\n\t\t\tthis.unsubscribeQuery()\n\t\t\tthis.unsubscribeQuery = null\n\t\t}\n\t\tthis.listeners.clear()\n\t\tthis.snapshot = EMPTY_ARRAY as readonly 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 { 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 (optimistic) and `mutateAsync`\n * for when you need to await the result. Tracks loading and error state.\n *\n * @param mutationFn - An async function to execute (e.g., `app.todos.insert`)\n * @returns Object with mutate, mutateAsync, isLoading, error, and reset\n *\n * @example\n * ```typescript\n * const { mutate } = useMutation(app.todos.insert)\n * mutate({ title: 'New todo' }) // fire-and-forget\n * ```\n */\nexport function useMutation<TData, TArgs extends unknown[]>(\n\tmutationFn: (...args: TArgs) => Promise<TData>,\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\t// Track mounted state to avoid state updates after unmount\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\t// Keep latest mutation function in a ref to avoid stale closures\n\tconst fnRef = useRef(mutationFn)\n\tfnRef.current = mutationFn\n\n\tconst mutateAsync = useCallback(async (...args: TArgs): Promise<TData> => {\n\t\tif (mountedRef.current) {\n\t\t\tsetState({ isLoading: true, error: null })\n\t\t}\n\t\ttry {\n\t\t\tconst result = await fnRef.current(...args)\n\t\t\tif (mountedRef.current) {\n\t\t\t\tsetState({ isLoading: false, error: null })\n\t\t\t}\n\t\t\treturn result\n\t\t} catch (err) {\n\t\t\tconst error = err instanceof Error ? err : new Error(String(err))\n\t\t\tif (mountedRef.current) {\n\t\t\t\tsetState({ isLoading: false, error })\n\t\t\t}\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 { SyncStatusInfo } from '@korajs/sync'\nimport { useCallback, useEffect, useRef, useSyncExternalStore } from 'react'\nimport { useKoraContext } from '../context/kora-context'\n\nconst POLL_INTERVAL_MS = 500\n\n/** Default status returned when no sync engine is configured */\nconst OFFLINE_STATUS: SyncStatusInfo = Object.freeze({\n\tstatus: 'offline',\n\tpendingOperations: 0,\n\tlastSyncedAt: null,\n})\n\n/**\n * React hook for monitoring the sync engine's connection status.\n *\n * Polls the SyncEngine at ~500ms intervals and re-renders only when\n * the status actually changes. Returns a default offline status when\n * no sync engine is configured.\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 * // status.pendingOperations: number\n * // status.lastSyncedAt: number | null\n * ```\n */\nexport function useSyncStatus(): SyncStatusInfo {\n\tconst { syncEngine } = useKoraContext()\n\n\t// Cache the latest status snapshot for stable references\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 (!syncEngine) return () => {}\n\n\t\t\t// Poll the sync engine status at regular intervals\n\t\t\tconst intervalId = setInterval(() => {\n\t\t\t\tconst newStatus = syncEngine.getStatus()\n\t\t\t\tconst newSerialized = JSON.stringify(newStatus)\n\n\t\t\t\t// Only notify React if status actually changed\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}, POLL_INTERVAL_MS)\n\n\t\t\t// Do an immediate check\n\t\t\tconst initialStatus = syncEngine.getStatus()\n\t\t\tconst initialSerialized = JSON.stringify(initialStatus)\n\t\t\tif (initialSerialized !== serializedRef.current) {\n\t\t\t\tsnapshotRef.current = initialStatus\n\t\t\t\tserializedRef.current = initialSerialized\n\t\t\t\tonStoreChange()\n\t\t\t}\n\n\t\t\treturn () => {\n\t\t\t\tclearInterval(intervalId)\n\t\t\t}\n\t\t},\n\t\t[syncEngine],\n\t)\n\n\tconst getSnapshot = useCallback((): SyncStatusInfo => {\n\t\treturn snapshotRef.current\n\t}, [])\n\n\t// Reset snapshot when syncEngine changes\n\tuseEffect(() => {\n\t\tif (!syncEngine) {\n\t\t\tsnapshotRef.current = OFFLINE_STATUS\n\t\t\tserializedRef.current = JSON.stringify(OFFLINE_STATUS)\n\t\t}\n\t}, [syncEngine])\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 { 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 TEXT_KEY = 'content'\nconst COMPACT_AFTER_DELTAS = 20\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): UseRichTextResult {\n\tconst { store } = useKoraContext()\n\tconst collection = useMemo<CollectionAccessor>(() => store.collection(collectionName), [store, collectionName])\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 baseUpdateRef = useRef<Uint8Array | null>(null)\n\tconst pendingDeltasRef = useRef<Uint8Array[]>([])\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\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\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 persist = async (_update: Uint8Array, origin: unknown): Promise<void> => {\n\t\t\tsyncHistoryState()\n\t\t\tif (origin === LOAD_ORIGIN) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tpendingDeltasRef.current.push(_update)\n\t\t\tconst snapshot = composeRichtextSnapshot(baseUpdateRef.current, pendingDeltasRef.current)\n\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\tdoc.on('update', persist)\n\t\tvoid initialize()\n\n\t\tsyncHistoryState()\n\n\t\treturn () => {\n\t\t\tdisposed = true\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}\n\t}, [collection, doc, fieldName, recordId, syncHistoryState, undoManager])\n\n\treturn { doc, text, undo, redo, canUndo, canRedo, ready, error }\n}\n\nfunction encodeRichtextInput(value: unknown): Uint8Array | null {\n\tif (value === null || value === undefined) {\n\t\treturn null\n\t}\n\n\tif (typeof value === 'string') {\n\t\tconst doc = new Y.Doc()\n\t\tdoc.getText(TEXT_KEY).insert(0, value)\n\t\treturn Y.encodeStateAsUpdate(doc)\n\t}\n\n\tif (value instanceof Uint8Array) {\n\t\treturn value\n\t}\n\n\tif (value instanceof ArrayBuffer) {\n\t\treturn new Uint8Array(value)\n\t}\n\n\t// Handle Node.js Buffer without requiring @types/node\n\tif (typeof globalThis !== 'undefined' && 'Buffer' in globalThis) {\n\t\tconst BufferCtor = (globalThis as Record<string, unknown>).Buffer as { isBuffer(v: unknown): v is { buffer: ArrayBuffer; byteOffset: number; byteLength: number } }\n\t\tif (BufferCtor.isBuffer(value)) {\n\t\t\treturn new Uint8Array(value.buffer, value.byteOffset, value.byteLength)\n\t\t}\n\t}\n\n\tthrow new Error('Richtext record value must be a string, Uint8Array, ArrayBuffer, or null.')\n}\n\nfunction composeRichtextSnapshot(base: Uint8Array | null, deltas: Uint8Array[]): Uint8Array {\n\tconst doc = new Y.Doc()\n\tif (base) {\n\t\tY.applyUpdate(doc, base)\n\t}\n\n\tfor (const delta of deltas) {\n\t\tY.applyUpdate(doc, delta)\n\t}\n\n\treturn Y.encodeStateAsUpdate(doc)\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-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\tUseMutationResult,\n\tUseRichTextResult,\n} from './types'\n\n// === Context ===\nexport { KoraProvider } from './context/kora-context'\n\n// === Hooks ===\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'\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>(\n\t\tstore ?? null,\n\t)\n\tconst [resolvedSync, setResolvedSync] = useState<SyncEngine | null>(\n\t\tsyncEngine ?? null,\n\t)\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}\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 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\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\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\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 { 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 (optimistic) and `mutateAsync`\n * for when you need to await the result. Tracks loading and error state.\n *\n * @param mutationFn - An async function to execute (e.g., `app.todos.insert`)\n * @returns Object with mutate, mutateAsync, isLoading, error, and reset\n *\n * @example\n * ```typescript\n * const { mutate } = useMutation(app.todos.insert)\n * mutate({ title: 'New todo' }) // fire-and-forget\n * ```\n */\nexport function useMutation<TData, TArgs extends unknown[]>(\n\tmutationFn: (...args: TArgs) => Promise<TData>,\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\t// Track mounted state to avoid state updates after unmount\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\t// Keep latest mutation function in a ref to avoid stale closures\n\tconst fnRef = useRef(mutationFn)\n\tfnRef.current = mutationFn\n\n\tconst mutateAsync = useCallback(async (...args: TArgs): Promise<TData> => {\n\t\tif (mountedRef.current) {\n\t\t\tsetState({ isLoading: true, error: null })\n\t\t}\n\t\ttry {\n\t\t\tconst result = await fnRef.current(...args)\n\t\t\tif (mountedRef.current) {\n\t\t\t\tsetState({ isLoading: false, error: null })\n\t\t\t}\n\t\t\treturn result\n\t\t} catch (err) {\n\t\t\tconst error = err instanceof Error ? err : new Error(String(err))\n\t\t\tif (mountedRef.current) {\n\t\t\t\tsetState({ isLoading: false, error })\n\t\t\t}\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 { SyncStatusInfo } from '@korajs/sync'\nimport { useCallback, useEffect, useRef, useSyncExternalStore } from 'react'\nimport { useKoraContext } from '../context/kora-context'\n\nconst POLL_INTERVAL_MS = 500\n\n/** Default status returned when no sync engine is configured */\nconst OFFLINE_STATUS: SyncStatusInfo = Object.freeze({\n\tstatus: 'offline',\n\tpendingOperations: 0,\n\tlastSyncedAt: null,\n})\n\n/**\n * React hook for monitoring the sync engine's connection status.\n *\n * Polls the SyncEngine at ~500ms intervals and re-renders only when\n * the status actually changes. Returns a default offline status when\n * no sync engine is configured.\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 * // status.pendingOperations: number\n * // status.lastSyncedAt: number | null\n * ```\n */\nexport function useSyncStatus(): SyncStatusInfo {\n\tconst { syncEngine } = useKoraContext()\n\n\t// Cache the latest status snapshot for stable references\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 (!syncEngine) return () => {}\n\n\t\t\t// Poll the sync engine status at regular intervals\n\t\t\tconst intervalId = setInterval(() => {\n\t\t\t\tconst newStatus = syncEngine.getStatus()\n\t\t\t\tconst newSerialized = JSON.stringify(newStatus)\n\n\t\t\t\t// Only notify React if status actually changed\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}, POLL_INTERVAL_MS)\n\n\t\t\t// Do an immediate check\n\t\t\tconst initialStatus = syncEngine.getStatus()\n\t\t\tconst initialSerialized = JSON.stringify(initialStatus)\n\t\t\tif (initialSerialized !== serializedRef.current) {\n\t\t\t\tsnapshotRef.current = initialStatus\n\t\t\t\tserializedRef.current = initialSerialized\n\t\t\t\tonStoreChange()\n\t\t\t}\n\n\t\t\treturn () => {\n\t\t\t\tclearInterval(intervalId)\n\t\t\t}\n\t\t},\n\t\t[syncEngine],\n\t)\n\n\tconst getSnapshot = useCallback((): SyncStatusInfo => {\n\t\treturn snapshotRef.current\n\t}, [])\n\n\t// Reset snapshot when syncEngine changes\n\tuseEffect(() => {\n\t\tif (!syncEngine) {\n\t\t\tsnapshotRef.current = OFFLINE_STATUS\n\t\t\tserializedRef.current = JSON.stringify(OFFLINE_STATUS)\n\t\t}\n\t}, [syncEngine])\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 { 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 TEXT_KEY = 'content'\nconst COMPACT_AFTER_DELTAS = 20\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): UseRichTextResult {\n\tconst { store } = useKoraContext()\n\tconst collection = useMemo<CollectionAccessor>(() => store.collection(collectionName), [store, collectionName])\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 baseUpdateRef = useRef<Uint8Array | null>(null)\n\tconst pendingDeltasRef = useRef<Uint8Array[]>([])\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\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\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 persist = async (_update: Uint8Array, origin: unknown): Promise<void> => {\n\t\t\tsyncHistoryState()\n\t\t\tif (origin === LOAD_ORIGIN) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tpendingDeltasRef.current.push(_update)\n\t\t\tconst snapshot = composeRichtextSnapshot(baseUpdateRef.current, pendingDeltasRef.current)\n\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\tdoc.on('update', persist)\n\t\tvoid initialize()\n\n\t\tsyncHistoryState()\n\n\t\treturn () => {\n\t\t\tdisposed = true\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}\n\t}, [collection, doc, fieldName, recordId, syncHistoryState, undoManager])\n\n\treturn { doc, text, undo, redo, canUndo, canRedo, ready, error }\n}\n\nfunction encodeRichtextInput(value: unknown): Uint8Array | null {\n\tif (value === null || value === undefined) {\n\t\treturn null\n\t}\n\n\tif (typeof value === 'string') {\n\t\tconst doc = new Y.Doc()\n\t\tdoc.getText(TEXT_KEY).insert(0, value)\n\t\treturn Y.encodeStateAsUpdate(doc)\n\t}\n\n\tif (value instanceof Uint8Array) {\n\t\treturn value\n\t}\n\n\tif (value instanceof ArrayBuffer) {\n\t\treturn new Uint8Array(value)\n\t}\n\n\t// Handle Node.js Buffer without requiring @types/node\n\tif (typeof globalThis !== 'undefined' && 'Buffer' in globalThis) {\n\t\tconst BufferCtor = (globalThis as Record<string, unknown>).Buffer as { isBuffer(v: unknown): v is { buffer: ArrayBuffer; byteOffset: number; byteLength: number } }\n\t\tif (BufferCtor.isBuffer(value)) {\n\t\t\treturn new Uint8Array(value.buffer, value.byteOffset, value.byteLength)\n\t\t}\n\t}\n\n\tthrow new Error('Richtext record value must be a string, Uint8Array, ArrayBuffer, or null.')\n}\n\nfunction composeRichtextSnapshot(base: Uint8Array | null, deltas: Uint8Array[]): Uint8Array {\n\tconst doc = new Y.Doc()\n\tif (base) {\n\t\tY.applyUpdate(doc, base)\n\t}\n\n\tfor (const delta of deltas) {\n\t\tY.applyUpdate(doc, delta)\n\t}\n\n\treturn Y.encodeStateAsUpdate(doc)\n}\n"],"mappings":"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","names":["import_react","EMPTY_ARRAY","import_react","import_react","import_react","import_react"]}
package/dist/index.js CHANGED
@@ -67,7 +67,7 @@ function useKoraContext() {
67
67
  }
68
68
 
69
69
  // src/hooks/use-query.ts
70
- import { useEffect as useEffect2, useMemo, useRef, useSyncExternalStore } from "react";
70
+ import { useMemo, useRef, useSyncExternalStore } from "react";
71
71
 
72
72
  // src/query-store/query-store.ts
73
73
  var EMPTY_ARRAY = Object.freeze([]);
@@ -75,24 +75,30 @@ var QueryStore = class {
75
75
  snapshot = EMPTY_ARRAY;
76
76
  listeners = /* @__PURE__ */ new Set();
77
77
  unsubscribeQuery = null;
78
- destroyed = false;
78
+ active = false;
79
+ queryBuilder;
79
80
  constructor(queryBuilder) {
80
- this.unsubscribeQuery = queryBuilder.subscribe((results) => {
81
- if (this.destroyed) return;
82
- const newSnapshot = Object.freeze([...results]);
83
- this.snapshot = newSnapshot;
84
- this.notifyListeners();
85
- });
81
+ this.queryBuilder = queryBuilder;
86
82
  }
87
83
  /**
88
84
  * Subscribe to snapshot changes. Compatible with useSyncExternalStore.
89
85
  *
86
+ * Lazily starts the underlying query subscription when the first listener
87
+ * attaches, and stops it when the last listener detaches. This allows
88
+ * React StrictMode to unmount/remount without permanently killing the subscription.
89
+ *
90
90
  * @returns Unsubscribe function
91
91
  */
92
92
  subscribe = (onStoreChange) => {
93
93
  this.listeners.add(onStoreChange);
94
+ if (!this.active) {
95
+ this.startSubscription();
96
+ }
94
97
  return () => {
95
98
  this.listeners.delete(onStoreChange);
99
+ if (this.listeners.size === 0) {
100
+ this.stopSubscription();
101
+ }
96
102
  };
97
103
  };
98
104
  /**
@@ -104,15 +110,28 @@ var QueryStore = class {
104
110
  };
105
111
  /**
106
112
  * Clean up the underlying subscription and release resources.
113
+ * Called by useMemo when the query descriptor changes.
107
114
  */
108
115
  destroy() {
109
- this.destroyed = true;
116
+ this.stopSubscription();
117
+ this.listeners.clear();
118
+ this.snapshot = EMPTY_ARRAY;
119
+ }
120
+ startSubscription() {
121
+ this.active = true;
122
+ this.unsubscribeQuery = this.queryBuilder.subscribe((results) => {
123
+ if (!this.active) return;
124
+ const newSnapshot = Object.freeze([...results]);
125
+ this.snapshot = newSnapshot;
126
+ this.notifyListeners();
127
+ });
128
+ }
129
+ stopSubscription() {
130
+ this.active = false;
110
131
  if (this.unsubscribeQuery) {
111
132
  this.unsubscribeQuery();
112
133
  this.unsubscribeQuery = null;
113
134
  }
114
- this.listeners.clear();
115
- this.snapshot = EMPTY_ARRAY;
116
135
  }
117
136
  notifyListeners() {
118
137
  for (const listener of this.listeners) {
@@ -152,14 +171,6 @@ function useQuery(query, options) {
152
171
  }
153
172
  return queryStoreRef.current;
154
173
  }, [descriptorKey, enabled, query]);
155
- useEffect2(() => {
156
- return () => {
157
- if (queryStoreRef.current) {
158
- queryStoreRef.current.destroy();
159
- queryStoreRef.current = null;
160
- }
161
- };
162
- }, []);
163
174
  const disabledGetSnapshot = () => EMPTY_ARRAY2;
164
175
  return useSyncExternalStore(
165
176
  queryStore ? queryStore.subscribe : noopSubscribe,
@@ -168,14 +179,14 @@ function useQuery(query, options) {
168
179
  }
169
180
 
170
181
  // src/hooks/use-mutation.ts
171
- import { useCallback, useEffect as useEffect3, useRef as useRef2, useState as useState2 } from "react";
182
+ import { useCallback, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
172
183
  function useMutation(mutationFn) {
173
184
  const [state, setState] = useState2({
174
185
  isLoading: false,
175
186
  error: null
176
187
  });
177
188
  const mountedRef = useRef2(true);
178
- useEffect3(() => {
189
+ useEffect2(() => {
179
190
  mountedRef.current = true;
180
191
  return () => {
181
192
  mountedRef.current = false;
@@ -223,7 +234,7 @@ function useMutation(mutationFn) {
223
234
  }
224
235
 
225
236
  // src/hooks/use-sync-status.ts
226
- import { useCallback as useCallback2, useEffect as useEffect4, useRef as useRef3, useSyncExternalStore as useSyncExternalStore2 } from "react";
237
+ import { useCallback as useCallback2, useEffect as useEffect3, useRef as useRef3, useSyncExternalStore as useSyncExternalStore2 } from "react";
227
238
  var POLL_INTERVAL_MS = 500;
228
239
  var OFFLINE_STATUS = Object.freeze({
229
240
  status: "offline",
@@ -263,7 +274,7 @@ function useSyncStatus() {
263
274
  const getSnapshot = useCallback2(() => {
264
275
  return snapshotRef.current;
265
276
  }, []);
266
- useEffect4(() => {
277
+ useEffect3(() => {
267
278
  if (!syncEngine) {
268
279
  snapshotRef.current = OFFLINE_STATUS;
269
280
  serializedRef.current = JSON.stringify(OFFLINE_STATUS);
@@ -282,7 +293,7 @@ function useCollection(name) {
282
293
  }
283
294
 
284
295
  // src/hooks/use-rich-text.ts
285
- import { useCallback as useCallback3, useEffect as useEffect5, useMemo as useMemo3, useRef as useRef4, useState as useState3 } from "react";
296
+ import { useCallback as useCallback3, useEffect as useEffect4, useMemo as useMemo3, useRef as useRef4, useState as useState3 } from "react";
286
297
  import * as Y from "yjs";
287
298
  var LOAD_ORIGIN = "kora-load";
288
299
  var TEXT_KEY = "content";
@@ -311,7 +322,7 @@ function useRichText(collectionName, recordId, fieldName) {
311
322
  undoManager.redo();
312
323
  syncHistoryState();
313
324
  }, [syncHistoryState, undoManager]);
314
- useEffect5(() => {
325
+ useEffect4(() => {
315
326
  let disposed = false;
316
327
  const initialize = async () => {
317
328
  setReady(false);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/context/kora-context.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":["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>(\n\t\tstore ?? null,\n\t)\n\tconst [resolvedSync, setResolvedSync] = useState<SyncEngine | null>(\n\t\tsyncEngine ?? null,\n\t)\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}\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 type { CollectionRecord, QueryBuilder } from '@korajs/store'\nimport { useEffect, useMemo, useRef, useSyncExternalStore } from 'react'\nimport { QueryStore } from '../query-store/query-store'\nimport type { UseQueryOptions } from '../types'\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\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// Clean up on unmount\n\tuseEffect(() => {\n\t\treturn () => {\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}\n\t}, [])\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 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 * Starts the underlying subscription eagerly on construction, so that\n * the snapshot is populated by the time useSyncExternalStore reads it.\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 destroyed = false\n\n\tconstructor(queryBuilder: QueryBuilder<T>) {\n\t\t// Start subscription eagerly so snapshot is populated before first getSnapshot() call\n\t\tthis.unsubscribeQuery = queryBuilder.subscribe((results) => {\n\t\t\tif (this.destroyed) return\n\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\t/**\n\t * Subscribe to snapshot changes. Compatible with useSyncExternalStore.\n\t *\n\t * @returns Unsubscribe function\n\t */\n\tsubscribe = (onStoreChange: () => void): (() => void) => {\n\t\tthis.listeners.add(onStoreChange)\n\n\t\treturn () => {\n\t\t\tthis.listeners.delete(onStoreChange)\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 */\n\tdestroy(): void {\n\t\tthis.destroyed = true\n\t\tif (this.unsubscribeQuery) {\n\t\t\tthis.unsubscribeQuery()\n\t\t\tthis.unsubscribeQuery = null\n\t\t}\n\t\tthis.listeners.clear()\n\t\tthis.snapshot = EMPTY_ARRAY as readonly 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 { 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 (optimistic) and `mutateAsync`\n * for when you need to await the result. Tracks loading and error state.\n *\n * @param mutationFn - An async function to execute (e.g., `app.todos.insert`)\n * @returns Object with mutate, mutateAsync, isLoading, error, and reset\n *\n * @example\n * ```typescript\n * const { mutate } = useMutation(app.todos.insert)\n * mutate({ title: 'New todo' }) // fire-and-forget\n * ```\n */\nexport function useMutation<TData, TArgs extends unknown[]>(\n\tmutationFn: (...args: TArgs) => Promise<TData>,\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\t// Track mounted state to avoid state updates after unmount\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\t// Keep latest mutation function in a ref to avoid stale closures\n\tconst fnRef = useRef(mutationFn)\n\tfnRef.current = mutationFn\n\n\tconst mutateAsync = useCallback(async (...args: TArgs): Promise<TData> => {\n\t\tif (mountedRef.current) {\n\t\t\tsetState({ isLoading: true, error: null })\n\t\t}\n\t\ttry {\n\t\t\tconst result = await fnRef.current(...args)\n\t\t\tif (mountedRef.current) {\n\t\t\t\tsetState({ isLoading: false, error: null })\n\t\t\t}\n\t\t\treturn result\n\t\t} catch (err) {\n\t\t\tconst error = err instanceof Error ? err : new Error(String(err))\n\t\t\tif (mountedRef.current) {\n\t\t\t\tsetState({ isLoading: false, error })\n\t\t\t}\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 { SyncStatusInfo } from '@korajs/sync'\nimport { useCallback, useEffect, useRef, useSyncExternalStore } from 'react'\nimport { useKoraContext } from '../context/kora-context'\n\nconst POLL_INTERVAL_MS = 500\n\n/** Default status returned when no sync engine is configured */\nconst OFFLINE_STATUS: SyncStatusInfo = Object.freeze({\n\tstatus: 'offline',\n\tpendingOperations: 0,\n\tlastSyncedAt: null,\n})\n\n/**\n * React hook for monitoring the sync engine's connection status.\n *\n * Polls the SyncEngine at ~500ms intervals and re-renders only when\n * the status actually changes. Returns a default offline status when\n * no sync engine is configured.\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 * // status.pendingOperations: number\n * // status.lastSyncedAt: number | null\n * ```\n */\nexport function useSyncStatus(): SyncStatusInfo {\n\tconst { syncEngine } = useKoraContext()\n\n\t// Cache the latest status snapshot for stable references\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 (!syncEngine) return () => {}\n\n\t\t\t// Poll the sync engine status at regular intervals\n\t\t\tconst intervalId = setInterval(() => {\n\t\t\t\tconst newStatus = syncEngine.getStatus()\n\t\t\t\tconst newSerialized = JSON.stringify(newStatus)\n\n\t\t\t\t// Only notify React if status actually changed\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}, POLL_INTERVAL_MS)\n\n\t\t\t// Do an immediate check\n\t\t\tconst initialStatus = syncEngine.getStatus()\n\t\t\tconst initialSerialized = JSON.stringify(initialStatus)\n\t\t\tif (initialSerialized !== serializedRef.current) {\n\t\t\t\tsnapshotRef.current = initialStatus\n\t\t\t\tserializedRef.current = initialSerialized\n\t\t\t\tonStoreChange()\n\t\t\t}\n\n\t\t\treturn () => {\n\t\t\t\tclearInterval(intervalId)\n\t\t\t}\n\t\t},\n\t\t[syncEngine],\n\t)\n\n\tconst getSnapshot = useCallback((): SyncStatusInfo => {\n\t\treturn snapshotRef.current\n\t}, [])\n\n\t// Reset snapshot when syncEngine changes\n\tuseEffect(() => {\n\t\tif (!syncEngine) {\n\t\t\tsnapshotRef.current = OFFLINE_STATUS\n\t\t\tserializedRef.current = JSON.stringify(OFFLINE_STATUS)\n\t\t}\n\t}, [syncEngine])\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 { 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 TEXT_KEY = 'content'\nconst COMPACT_AFTER_DELTAS = 20\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): UseRichTextResult {\n\tconst { store } = useKoraContext()\n\tconst collection = useMemo<CollectionAccessor>(() => store.collection(collectionName), [store, collectionName])\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 baseUpdateRef = useRef<Uint8Array | null>(null)\n\tconst pendingDeltasRef = useRef<Uint8Array[]>([])\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\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\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 persist = async (_update: Uint8Array, origin: unknown): Promise<void> => {\n\t\t\tsyncHistoryState()\n\t\t\tif (origin === LOAD_ORIGIN) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tpendingDeltasRef.current.push(_update)\n\t\t\tconst snapshot = composeRichtextSnapshot(baseUpdateRef.current, pendingDeltasRef.current)\n\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\tdoc.on('update', persist)\n\t\tvoid initialize()\n\n\t\tsyncHistoryState()\n\n\t\treturn () => {\n\t\t\tdisposed = true\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}\n\t}, [collection, doc, fieldName, recordId, syncHistoryState, undoManager])\n\n\treturn { doc, text, undo, redo, canUndo, canRedo, ready, error }\n}\n\nfunction encodeRichtextInput(value: unknown): Uint8Array | null {\n\tif (value === null || value === undefined) {\n\t\treturn null\n\t}\n\n\tif (typeof value === 'string') {\n\t\tconst doc = new Y.Doc()\n\t\tdoc.getText(TEXT_KEY).insert(0, value)\n\t\treturn Y.encodeStateAsUpdate(doc)\n\t}\n\n\tif (value instanceof Uint8Array) {\n\t\treturn value\n\t}\n\n\tif (value instanceof ArrayBuffer) {\n\t\treturn new Uint8Array(value)\n\t}\n\n\t// Handle Node.js Buffer without requiring @types/node\n\tif (typeof globalThis !== 'undefined' && 'Buffer' in globalThis) {\n\t\tconst BufferCtor = (globalThis as Record<string, unknown>).Buffer as { isBuffer(v: unknown): v is { buffer: ArrayBuffer; byteOffset: number; byteLength: number } }\n\t\tif (BufferCtor.isBuffer(value)) {\n\t\t\treturn new Uint8Array(value.buffer, value.byteOffset, value.byteLength)\n\t\t}\n\t}\n\n\tthrow new Error('Richtext record value must be a string, Uint8Array, ArrayBuffer, or null.')\n}\n\nfunction composeRichtextSnapshot(base: Uint8Array | null, deltas: Uint8Array[]): Uint8Array {\n\tconst doc = new Y.Doc()\n\tif (base) {\n\t\tY.applyUpdate(doc, base)\n\t}\n\n\tfor (const delta of deltas) {\n\t\tY.applyUpdate(doc, delta)\n\t}\n\n\treturn Y.encodeStateAsUpdate(doc)\n}\n"],"mappings":"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","names":["useEffect","EMPTY_ARRAY","useEffect","useEffect","useRef","useState","useCallback","useEffect","useRef","useSyncExternalStore","useRef","useCallback","useEffect","useSyncExternalStore","useMemo","useMemo","useCallback","useEffect","useMemo","useRef","useState","useMemo","useState","useRef","useCallback","useEffect"]}
1
+ {"version":3,"sources":["../src/context/kora-context.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":["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>(\n\t\tstore ?? null,\n\t)\n\tconst [resolvedSync, setResolvedSync] = useState<SyncEngine | null>(\n\t\tsyncEngine ?? null,\n\t)\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}\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 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\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\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\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 { 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 (optimistic) and `mutateAsync`\n * for when you need to await the result. Tracks loading and error state.\n *\n * @param mutationFn - An async function to execute (e.g., `app.todos.insert`)\n * @returns Object with mutate, mutateAsync, isLoading, error, and reset\n *\n * @example\n * ```typescript\n * const { mutate } = useMutation(app.todos.insert)\n * mutate({ title: 'New todo' }) // fire-and-forget\n * ```\n */\nexport function useMutation<TData, TArgs extends unknown[]>(\n\tmutationFn: (...args: TArgs) => Promise<TData>,\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\t// Track mounted state to avoid state updates after unmount\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\t// Keep latest mutation function in a ref to avoid stale closures\n\tconst fnRef = useRef(mutationFn)\n\tfnRef.current = mutationFn\n\n\tconst mutateAsync = useCallback(async (...args: TArgs): Promise<TData> => {\n\t\tif (mountedRef.current) {\n\t\t\tsetState({ isLoading: true, error: null })\n\t\t}\n\t\ttry {\n\t\t\tconst result = await fnRef.current(...args)\n\t\t\tif (mountedRef.current) {\n\t\t\t\tsetState({ isLoading: false, error: null })\n\t\t\t}\n\t\t\treturn result\n\t\t} catch (err) {\n\t\t\tconst error = err instanceof Error ? err : new Error(String(err))\n\t\t\tif (mountedRef.current) {\n\t\t\t\tsetState({ isLoading: false, error })\n\t\t\t}\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 { SyncStatusInfo } from '@korajs/sync'\nimport { useCallback, useEffect, useRef, useSyncExternalStore } from 'react'\nimport { useKoraContext } from '../context/kora-context'\n\nconst POLL_INTERVAL_MS = 500\n\n/** Default status returned when no sync engine is configured */\nconst OFFLINE_STATUS: SyncStatusInfo = Object.freeze({\n\tstatus: 'offline',\n\tpendingOperations: 0,\n\tlastSyncedAt: null,\n})\n\n/**\n * React hook for monitoring the sync engine's connection status.\n *\n * Polls the SyncEngine at ~500ms intervals and re-renders only when\n * the status actually changes. Returns a default offline status when\n * no sync engine is configured.\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 * // status.pendingOperations: number\n * // status.lastSyncedAt: number | null\n * ```\n */\nexport function useSyncStatus(): SyncStatusInfo {\n\tconst { syncEngine } = useKoraContext()\n\n\t// Cache the latest status snapshot for stable references\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 (!syncEngine) return () => {}\n\n\t\t\t// Poll the sync engine status at regular intervals\n\t\t\tconst intervalId = setInterval(() => {\n\t\t\t\tconst newStatus = syncEngine.getStatus()\n\t\t\t\tconst newSerialized = JSON.stringify(newStatus)\n\n\t\t\t\t// Only notify React if status actually changed\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}, POLL_INTERVAL_MS)\n\n\t\t\t// Do an immediate check\n\t\t\tconst initialStatus = syncEngine.getStatus()\n\t\t\tconst initialSerialized = JSON.stringify(initialStatus)\n\t\t\tif (initialSerialized !== serializedRef.current) {\n\t\t\t\tsnapshotRef.current = initialStatus\n\t\t\t\tserializedRef.current = initialSerialized\n\t\t\t\tonStoreChange()\n\t\t\t}\n\n\t\t\treturn () => {\n\t\t\t\tclearInterval(intervalId)\n\t\t\t}\n\t\t},\n\t\t[syncEngine],\n\t)\n\n\tconst getSnapshot = useCallback((): SyncStatusInfo => {\n\t\treturn snapshotRef.current\n\t}, [])\n\n\t// Reset snapshot when syncEngine changes\n\tuseEffect(() => {\n\t\tif (!syncEngine) {\n\t\t\tsnapshotRef.current = OFFLINE_STATUS\n\t\t\tserializedRef.current = JSON.stringify(OFFLINE_STATUS)\n\t\t}\n\t}, [syncEngine])\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 { 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 TEXT_KEY = 'content'\nconst COMPACT_AFTER_DELTAS = 20\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): UseRichTextResult {\n\tconst { store } = useKoraContext()\n\tconst collection = useMemo<CollectionAccessor>(() => store.collection(collectionName), [store, collectionName])\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 baseUpdateRef = useRef<Uint8Array | null>(null)\n\tconst pendingDeltasRef = useRef<Uint8Array[]>([])\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\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\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 persist = async (_update: Uint8Array, origin: unknown): Promise<void> => {\n\t\t\tsyncHistoryState()\n\t\t\tif (origin === LOAD_ORIGIN) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tpendingDeltasRef.current.push(_update)\n\t\t\tconst snapshot = composeRichtextSnapshot(baseUpdateRef.current, pendingDeltasRef.current)\n\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\tdoc.on('update', persist)\n\t\tvoid initialize()\n\n\t\tsyncHistoryState()\n\n\t\treturn () => {\n\t\t\tdisposed = true\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}\n\t}, [collection, doc, fieldName, recordId, syncHistoryState, undoManager])\n\n\treturn { doc, text, undo, redo, canUndo, canRedo, ready, error }\n}\n\nfunction encodeRichtextInput(value: unknown): Uint8Array | null {\n\tif (value === null || value === undefined) {\n\t\treturn null\n\t}\n\n\tif (typeof value === 'string') {\n\t\tconst doc = new Y.Doc()\n\t\tdoc.getText(TEXT_KEY).insert(0, value)\n\t\treturn Y.encodeStateAsUpdate(doc)\n\t}\n\n\tif (value instanceof Uint8Array) {\n\t\treturn value\n\t}\n\n\tif (value instanceof ArrayBuffer) {\n\t\treturn new Uint8Array(value)\n\t}\n\n\t// Handle Node.js Buffer without requiring @types/node\n\tif (typeof globalThis !== 'undefined' && 'Buffer' in globalThis) {\n\t\tconst BufferCtor = (globalThis as Record<string, unknown>).Buffer as { isBuffer(v: unknown): v is { buffer: ArrayBuffer; byteOffset: number; byteLength: number } }\n\t\tif (BufferCtor.isBuffer(value)) {\n\t\t\treturn new Uint8Array(value.buffer, value.byteOffset, value.byteLength)\n\t\t}\n\t}\n\n\tthrow new Error('Richtext record value must be a string, Uint8Array, ArrayBuffer, or null.')\n}\n\nfunction composeRichtextSnapshot(base: Uint8Array | null, deltas: Uint8Array[]): Uint8Array {\n\tconst doc = new Y.Doc()\n\tif (base) {\n\t\tY.applyUpdate(doc, base)\n\t}\n\n\tfor (const delta of deltas) {\n\t\tY.applyUpdate(doc, delta)\n\t}\n\n\treturn Y.encodeStateAsUpdate(doc)\n}\n"],"mappings":"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","names":["EMPTY_ARRAY","useEffect","useRef","useState","useCallback","useEffect","useRef","useSyncExternalStore","useRef","useCallback","useEffect","useSyncExternalStore","useMemo","useMemo","useCallback","useEffect","useMemo","useRef","useState","useMemo","useState","useRef","useCallback","useEffect"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@korajs/react",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Kora.js React hooks and bindings for offline-first applications",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -32,8 +32,8 @@
32
32
  "dependencies": {
33
33
  "yjs": "^13.6.30",
34
34
  "@korajs/store": "0.1.2",
35
- "@korajs/core": "0.1.2",
36
- "@korajs/sync": "0.1.3"
35
+ "@korajs/core": "0.1.3",
36
+ "@korajs/sync": "0.1.4"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@testing-library/jest-dom": "^6.0.0",