@korajs/react 0.1.2 → 0.1.4
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 +43 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +50 -25
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -56,6 +56,7 @@ function KoraProvider({
|
|
|
56
56
|
syncEngine ?? null
|
|
57
57
|
);
|
|
58
58
|
const [ready, setReady] = (0, import_react.useState)(!app);
|
|
59
|
+
const [initError, setInitError] = (0, import_react.useState)(null);
|
|
59
60
|
(0, import_react.useEffect)(() => {
|
|
60
61
|
if (!app) return;
|
|
61
62
|
let cancelled = false;
|
|
@@ -64,11 +65,24 @@ function KoraProvider({
|
|
|
64
65
|
setResolvedStore(app.getStore());
|
|
65
66
|
setResolvedSync(app.getSyncEngine());
|
|
66
67
|
setReady(true);
|
|
68
|
+
}).catch((error) => {
|
|
69
|
+
if (cancelled) return;
|
|
70
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
71
|
+
console.error("[Kora] Initialization failed:", err);
|
|
72
|
+
setInitError(err);
|
|
67
73
|
});
|
|
68
74
|
return () => {
|
|
69
75
|
cancelled = true;
|
|
70
76
|
};
|
|
71
77
|
}, [app]);
|
|
78
|
+
if (initError) {
|
|
79
|
+
return (0, import_react.createElement)(
|
|
80
|
+
"div",
|
|
81
|
+
{ style: { color: "red", padding: "1rem", fontFamily: "monospace" } },
|
|
82
|
+
(0, import_react.createElement)("strong", null, "Kora initialization error: "),
|
|
83
|
+
initError.message
|
|
84
|
+
);
|
|
85
|
+
}
|
|
72
86
|
if (!ready) {
|
|
73
87
|
return fallback ?? null;
|
|
74
88
|
}
|
|
@@ -102,24 +116,30 @@ var QueryStore = class {
|
|
|
102
116
|
snapshot = EMPTY_ARRAY;
|
|
103
117
|
listeners = /* @__PURE__ */ new Set();
|
|
104
118
|
unsubscribeQuery = null;
|
|
105
|
-
|
|
119
|
+
active = false;
|
|
120
|
+
queryBuilder;
|
|
106
121
|
constructor(queryBuilder) {
|
|
107
|
-
this.
|
|
108
|
-
if (this.destroyed) return;
|
|
109
|
-
const newSnapshot = Object.freeze([...results]);
|
|
110
|
-
this.snapshot = newSnapshot;
|
|
111
|
-
this.notifyListeners();
|
|
112
|
-
});
|
|
122
|
+
this.queryBuilder = queryBuilder;
|
|
113
123
|
}
|
|
114
124
|
/**
|
|
115
125
|
* Subscribe to snapshot changes. Compatible with useSyncExternalStore.
|
|
116
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
|
+
*
|
|
117
131
|
* @returns Unsubscribe function
|
|
118
132
|
*/
|
|
119
133
|
subscribe = (onStoreChange) => {
|
|
120
134
|
this.listeners.add(onStoreChange);
|
|
135
|
+
if (!this.active) {
|
|
136
|
+
this.startSubscription();
|
|
137
|
+
}
|
|
121
138
|
return () => {
|
|
122
139
|
this.listeners.delete(onStoreChange);
|
|
140
|
+
if (this.listeners.size === 0) {
|
|
141
|
+
this.stopSubscription();
|
|
142
|
+
}
|
|
123
143
|
};
|
|
124
144
|
};
|
|
125
145
|
/**
|
|
@@ -131,15 +151,28 @@ var QueryStore = class {
|
|
|
131
151
|
};
|
|
132
152
|
/**
|
|
133
153
|
* Clean up the underlying subscription and release resources.
|
|
154
|
+
* Called by useMemo when the query descriptor changes.
|
|
134
155
|
*/
|
|
135
156
|
destroy() {
|
|
136
|
-
this.
|
|
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;
|
|
137
172
|
if (this.unsubscribeQuery) {
|
|
138
173
|
this.unsubscribeQuery();
|
|
139
174
|
this.unsubscribeQuery = null;
|
|
140
175
|
}
|
|
141
|
-
this.listeners.clear();
|
|
142
|
-
this.snapshot = EMPTY_ARRAY;
|
|
143
176
|
}
|
|
144
177
|
notifyListeners() {
|
|
145
178
|
for (const listener of this.listeners) {
|
|
@@ -179,14 +212,6 @@ function useQuery(query, options) {
|
|
|
179
212
|
}
|
|
180
213
|
return queryStoreRef.current;
|
|
181
214
|
}, [descriptorKey, enabled, query]);
|
|
182
|
-
(0, import_react2.useEffect)(() => {
|
|
183
|
-
return () => {
|
|
184
|
-
if (queryStoreRef.current) {
|
|
185
|
-
queryStoreRef.current.destroy();
|
|
186
|
-
queryStoreRef.current = null;
|
|
187
|
-
}
|
|
188
|
-
};
|
|
189
|
-
}, []);
|
|
190
215
|
const disabledGetSnapshot = () => EMPTY_ARRAY2;
|
|
191
216
|
return (0, import_react2.useSyncExternalStore)(
|
|
192
217
|
queryStore ? queryStore.subscribe : noopSubscribe,
|
package/dist/index.cjs.map
CHANGED
|
@@ -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\n\tuseEffect(() => {\n\t\tif (!app) return\n\t\tlet cancelled = false\n\t\tapp.ready.then(() => {\n\t\t\tif (cancelled) return\n\t\t\tsetResolvedStore(app.getStore())\n\t\t\tsetResolvedSync(app.getSyncEngine())\n\t\t\tsetReady(true)\n\t\t})\n\t\treturn () => {\n\t\t\tcancelled = true\n\t\t}\n\t}, [app])\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
|
@@ -15,6 +15,7 @@ function KoraProvider({
|
|
|
15
15
|
syncEngine ?? null
|
|
16
16
|
);
|
|
17
17
|
const [ready, setReady] = useState(!app);
|
|
18
|
+
const [initError, setInitError] = useState(null);
|
|
18
19
|
useEffect(() => {
|
|
19
20
|
if (!app) return;
|
|
20
21
|
let cancelled = false;
|
|
@@ -23,11 +24,24 @@ function KoraProvider({
|
|
|
23
24
|
setResolvedStore(app.getStore());
|
|
24
25
|
setResolvedSync(app.getSyncEngine());
|
|
25
26
|
setReady(true);
|
|
27
|
+
}).catch((error) => {
|
|
28
|
+
if (cancelled) return;
|
|
29
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
30
|
+
console.error("[Kora] Initialization failed:", err);
|
|
31
|
+
setInitError(err);
|
|
26
32
|
});
|
|
27
33
|
return () => {
|
|
28
34
|
cancelled = true;
|
|
29
35
|
};
|
|
30
36
|
}, [app]);
|
|
37
|
+
if (initError) {
|
|
38
|
+
return createElement(
|
|
39
|
+
"div",
|
|
40
|
+
{ style: { color: "red", padding: "1rem", fontFamily: "monospace" } },
|
|
41
|
+
createElement("strong", null, "Kora initialization error: "),
|
|
42
|
+
initError.message
|
|
43
|
+
);
|
|
44
|
+
}
|
|
31
45
|
if (!ready) {
|
|
32
46
|
return fallback ?? null;
|
|
33
47
|
}
|
|
@@ -53,7 +67,7 @@ function useKoraContext() {
|
|
|
53
67
|
}
|
|
54
68
|
|
|
55
69
|
// src/hooks/use-query.ts
|
|
56
|
-
import {
|
|
70
|
+
import { useMemo, useRef, useSyncExternalStore } from "react";
|
|
57
71
|
|
|
58
72
|
// src/query-store/query-store.ts
|
|
59
73
|
var EMPTY_ARRAY = Object.freeze([]);
|
|
@@ -61,24 +75,30 @@ var QueryStore = class {
|
|
|
61
75
|
snapshot = EMPTY_ARRAY;
|
|
62
76
|
listeners = /* @__PURE__ */ new Set();
|
|
63
77
|
unsubscribeQuery = null;
|
|
64
|
-
|
|
78
|
+
active = false;
|
|
79
|
+
queryBuilder;
|
|
65
80
|
constructor(queryBuilder) {
|
|
66
|
-
this.
|
|
67
|
-
if (this.destroyed) return;
|
|
68
|
-
const newSnapshot = Object.freeze([...results]);
|
|
69
|
-
this.snapshot = newSnapshot;
|
|
70
|
-
this.notifyListeners();
|
|
71
|
-
});
|
|
81
|
+
this.queryBuilder = queryBuilder;
|
|
72
82
|
}
|
|
73
83
|
/**
|
|
74
84
|
* Subscribe to snapshot changes. Compatible with useSyncExternalStore.
|
|
75
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
|
+
*
|
|
76
90
|
* @returns Unsubscribe function
|
|
77
91
|
*/
|
|
78
92
|
subscribe = (onStoreChange) => {
|
|
79
93
|
this.listeners.add(onStoreChange);
|
|
94
|
+
if (!this.active) {
|
|
95
|
+
this.startSubscription();
|
|
96
|
+
}
|
|
80
97
|
return () => {
|
|
81
98
|
this.listeners.delete(onStoreChange);
|
|
99
|
+
if (this.listeners.size === 0) {
|
|
100
|
+
this.stopSubscription();
|
|
101
|
+
}
|
|
82
102
|
};
|
|
83
103
|
};
|
|
84
104
|
/**
|
|
@@ -90,15 +110,28 @@ var QueryStore = class {
|
|
|
90
110
|
};
|
|
91
111
|
/**
|
|
92
112
|
* Clean up the underlying subscription and release resources.
|
|
113
|
+
* Called by useMemo when the query descriptor changes.
|
|
93
114
|
*/
|
|
94
115
|
destroy() {
|
|
95
|
-
this.
|
|
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;
|
|
96
131
|
if (this.unsubscribeQuery) {
|
|
97
132
|
this.unsubscribeQuery();
|
|
98
133
|
this.unsubscribeQuery = null;
|
|
99
134
|
}
|
|
100
|
-
this.listeners.clear();
|
|
101
|
-
this.snapshot = EMPTY_ARRAY;
|
|
102
135
|
}
|
|
103
136
|
notifyListeners() {
|
|
104
137
|
for (const listener of this.listeners) {
|
|
@@ -138,14 +171,6 @@ function useQuery(query, options) {
|
|
|
138
171
|
}
|
|
139
172
|
return queryStoreRef.current;
|
|
140
173
|
}, [descriptorKey, enabled, query]);
|
|
141
|
-
useEffect2(() => {
|
|
142
|
-
return () => {
|
|
143
|
-
if (queryStoreRef.current) {
|
|
144
|
-
queryStoreRef.current.destroy();
|
|
145
|
-
queryStoreRef.current = null;
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
}, []);
|
|
149
174
|
const disabledGetSnapshot = () => EMPTY_ARRAY2;
|
|
150
175
|
return useSyncExternalStore(
|
|
151
176
|
queryStore ? queryStore.subscribe : noopSubscribe,
|
|
@@ -154,14 +179,14 @@ function useQuery(query, options) {
|
|
|
154
179
|
}
|
|
155
180
|
|
|
156
181
|
// src/hooks/use-mutation.ts
|
|
157
|
-
import { useCallback, useEffect as
|
|
182
|
+
import { useCallback, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
158
183
|
function useMutation(mutationFn) {
|
|
159
184
|
const [state, setState] = useState2({
|
|
160
185
|
isLoading: false,
|
|
161
186
|
error: null
|
|
162
187
|
});
|
|
163
188
|
const mountedRef = useRef2(true);
|
|
164
|
-
|
|
189
|
+
useEffect2(() => {
|
|
165
190
|
mountedRef.current = true;
|
|
166
191
|
return () => {
|
|
167
192
|
mountedRef.current = false;
|
|
@@ -209,7 +234,7 @@ function useMutation(mutationFn) {
|
|
|
209
234
|
}
|
|
210
235
|
|
|
211
236
|
// src/hooks/use-sync-status.ts
|
|
212
|
-
import { useCallback as useCallback2, useEffect as
|
|
237
|
+
import { useCallback as useCallback2, useEffect as useEffect3, useRef as useRef3, useSyncExternalStore as useSyncExternalStore2 } from "react";
|
|
213
238
|
var POLL_INTERVAL_MS = 500;
|
|
214
239
|
var OFFLINE_STATUS = Object.freeze({
|
|
215
240
|
status: "offline",
|
|
@@ -249,7 +274,7 @@ function useSyncStatus() {
|
|
|
249
274
|
const getSnapshot = useCallback2(() => {
|
|
250
275
|
return snapshotRef.current;
|
|
251
276
|
}, []);
|
|
252
|
-
|
|
277
|
+
useEffect3(() => {
|
|
253
278
|
if (!syncEngine) {
|
|
254
279
|
snapshotRef.current = OFFLINE_STATUS;
|
|
255
280
|
serializedRef.current = JSON.stringify(OFFLINE_STATUS);
|
|
@@ -268,7 +293,7 @@ function useCollection(name) {
|
|
|
268
293
|
}
|
|
269
294
|
|
|
270
295
|
// src/hooks/use-rich-text.ts
|
|
271
|
-
import { useCallback as useCallback3, useEffect as
|
|
296
|
+
import { useCallback as useCallback3, useEffect as useEffect4, useMemo as useMemo3, useRef as useRef4, useState as useState3 } from "react";
|
|
272
297
|
import * as Y from "yjs";
|
|
273
298
|
var LOAD_ORIGIN = "kora-load";
|
|
274
299
|
var TEXT_KEY = "content";
|
|
@@ -297,7 +322,7 @@ function useRichText(collectionName, recordId, fieldName) {
|
|
|
297
322
|
undoManager.redo();
|
|
298
323
|
syncHistoryState();
|
|
299
324
|
}, [syncHistoryState, undoManager]);
|
|
300
|
-
|
|
325
|
+
useEffect4(() => {
|
|
301
326
|
let disposed = false;
|
|
302
327
|
const initialize = async () => {
|
|
303
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\n\tuseEffect(() => {\n\t\tif (!app) return\n\t\tlet cancelled = false\n\t\tapp.ready.then(() => {\n\t\t\tif (cancelled) return\n\t\t\tsetResolvedStore(app.getStore())\n\t\t\tsetResolvedSync(app.getSyncEngine())\n\t\t\tsetReady(true)\n\t\t})\n\t\treturn () => {\n\t\t\tcancelled = true\n\t\t}\n\t}, [app])\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":";AAEA,SAAS,eAAe,eAAe,YAAY,WAAW,gBAAgB;AAI9E,IAAM,cAAc,cAAuC,IAAI;AAqB/D,SAAS,aAAa;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,GAAiC;AAChC,QAAM,CAAC,eAAe,gBAAgB,IAAI;AAAA,IACzC,SAAS;AAAA,EACV;AACA,QAAM,CAAC,cAAc,eAAe,IAAI;AAAA,IACvC,cAAc;AAAA,EACf;AAEA,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,CAAC,GAAG;AAEvC,YAAU,MAAM;AACf,QAAI,CAAC,IAAK;AACV,QAAI,YAAY;AAChB,QAAI,MAAM,KAAK,MAAM;AACpB,UAAI,UAAW;AACf,uBAAiB,IAAI,SAAS,CAAC;AAC/B,sBAAgB,IAAI,cAAc,CAAC;AACnC,eAAS,IAAI;AAAA,IACd,CAAC;AACD,WAAO,MAAM;AACZ,kBAAY;AAAA,IACb;AAAA,EACD,GAAG,CAAC,GAAG,CAAC;AAER,MAAI,CAAC,OAAO;AACX,WAAQ,YAAY;AAAA,EACrB;AAEA,MAAI,CAAC,eAAe;AACnB,UAAM,IAAI;AAAA,MACT;AAAA,IAED;AAAA,EACD;AAEA,QAAM,QAA0B;AAAA,IAC/B,OAAO;AAAA,IACP,YAAY;AAAA,EACb;AACA,SAAO,cAAc,YAAY,UAAU,EAAE,MAAM,GAAG,QAAQ;AAC/D;AAMA,SAAS,iBAAmC;AAC3C,QAAM,UAAU,WAAW,WAAW;AACtC,MAAI,YAAY,MAAM;AACrB,UAAM,IAAI;AAAA,MACT;AAAA,IAED;AAAA,EACD;AACA,SAAO;AACR;;;ACvFA,SAAS,aAAAA,YAAW,SAAS,QAAQ,4BAA4B;;;ACKjE,IAAM,cAAkC,OAAO,OAAO,CAAC,CAAC;AAWjD,IAAM,aAAN,MAAuC;AAAA,EACrC,WAAyB;AAAA,EACzB,YAAY,oBAAI,IAAgB;AAAA,EAChC,mBAAwC;AAAA,EACxC,YAAY;AAAA,EAEpB,YAAY,cAA+B;AAE1C,SAAK,mBAAmB,aAAa,UAAU,CAAC,YAAY;AAC3D,UAAI,KAAK,UAAW;AAEpB,YAAM,cAAc,OAAO,OAAO,CAAC,GAAG,OAAO,CAAC;AAC9C,WAAK,WAAW;AAChB,WAAK,gBAAgB;AAAA,IACtB,CAAC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,YAAY,CAAC,kBAA4C;AACxD,SAAK,UAAU,IAAI,aAAa;AAEhC,WAAO,MAAM;AACZ,WAAK,UAAU,OAAO,aAAa;AAAA,IACpC;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,cAAc,MAAoB;AACjC,WAAO,KAAK;AAAA,EACb;AAAA;AAAA;AAAA;AAAA,EAKA,UAAgB;AACf,SAAK,YAAY;AACjB,QAAI,KAAK,kBAAkB;AAC1B,WAAK,iBAAiB;AACtB,WAAK,mBAAmB;AAAA,IACzB;AACA,SAAK,UAAU,MAAM;AACrB,SAAK,WAAW;AAAA,EACjB;AAAA,EAEQ,kBAAwB;AAC/B,eAAW,YAAY,KAAK,WAAW;AACtC,eAAS;AAAA,IACV;AAAA,EACD;AACD;;;ADhEA,IAAMC,eAAkC,OAAO,OAAO,CAAC,CAAC;AAExD,IAAM,gBAAgB,CAAC,mBAA6C;AACnE,SAAO,MAAM;AAAA,EAAC;AACf;AAsBO,SAAS,SACf,OACA,SACe;AACf,QAAM,UAAU,SAAS,YAAY;AAGrC,QAAM,gBAAgB,KAAK,UAAU,MAAM,cAAc,CAAC;AAG1D,QAAM,gBAAgB,OAA6B,IAAI;AACvD,QAAM,aAAa,OAAsB,IAAI;AAG7C,QAAM,aAAa,QAAQ,MAAM;AAChC,QAAI,CAAC,SAAS;AAEb,UAAI,cAAc,SAAS;AAC1B,sBAAc,QAAQ,QAAQ;AAC9B,sBAAc,UAAU;AAAA,MACzB;AACA,iBAAW,UAAU;AACrB,aAAO;AAAA,IACR;AAGA,QAAI,WAAW,YAAY,eAAe;AACzC,UAAI,cAAc,SAAS;AAC1B,sBAAc,QAAQ,QAAQ;AAAA,MAC/B;AACA,YAAM,WAAW,IAAI,WAAc,KAAK;AACxC,oBAAc,UAAU;AACxB,iBAAW,UAAU;AACrB,aAAO;AAAA,IACR;AAEA,WAAO,cAAc;AAAA,EACtB,GAAG,CAAC,eAAe,SAAS,KAAK,CAAC;AAGlC,EAAAC,WAAU,MAAM;AACf,WAAO,MAAM;AACZ,UAAI,cAAc,SAAS;AAC1B,sBAAc,QAAQ,QAAQ;AAC9B,sBAAc,UAAU;AAAA,MACzB;AAAA,IACD;AAAA,EACD,GAAG,CAAC,CAAC;AAEL,QAAM,sBAAsB,MAAoBD;AAEhD,SAAO;AAAA,IACN,aAAa,WAAW,YAAY;AAAA,IACpC,aAAa,WAAW,cAAc;AAAA,EACvC;AACD;;;AE1FA,SAAS,aAAa,aAAAE,YAAW,UAAAC,SAAQ,YAAAC,iBAAgB;AAkBlD,SAAS,YACf,YACkC;AAClC,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAsD;AAAA,IAC/E,WAAW;AAAA,IACX,OAAO;AAAA,EACR,CAAC;AAGD,QAAM,aAAaD,QAAO,IAAI;AAC9B,EAAAD,WAAU,MAAM;AACf,eAAW,UAAU;AACrB,WAAO,MAAM;AACZ,iBAAW,UAAU;AAAA,IACtB;AAAA,EACD,GAAG,CAAC,CAAC;AAGL,QAAM,QAAQC,QAAO,UAAU;AAC/B,QAAM,UAAU;AAEhB,QAAM,cAAc,YAAY,UAAU,SAAgC;AACzE,QAAI,WAAW,SAAS;AACvB,eAAS,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,IAC1C;AACA,QAAI;AACH,YAAM,SAAS,MAAM,MAAM,QAAQ,GAAG,IAAI;AAC1C,UAAI,WAAW,SAAS;AACvB,iBAAS,EAAE,WAAW,OAAO,OAAO,KAAK,CAAC;AAAA,MAC3C;AACA,aAAO;AAAA,IACR,SAAS,KAAK;AACb,YAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAChE,UAAI,WAAW,SAAS;AACvB,iBAAS,EAAE,WAAW,OAAO,MAAM,CAAC;AAAA,MACrC;AACA,YAAM;AAAA,IACP;AAAA,EACD,GAAG,CAAC,CAAC;AAEL,QAAM,SAAS;AAAA,IACd,IAAI,SAAsB;AACzB,kBAAY,GAAG,IAAI,EAAE,MAAM,MAAM;AAAA,MAEjC,CAAC;AAAA,IACF;AAAA,IACA,CAAC,WAAW;AAAA,EACb;AAEA,QAAM,QAAQ,YAAY,MAAY;AACrC,QAAI,WAAW,SAAS;AACvB,eAAS,EAAE,WAAW,OAAO,OAAO,KAAK,CAAC;AAAA,IAC3C;AAAA,EACD,GAAG,CAAC,CAAC;AAEL,SAAO;AAAA,IACN;AAAA,IACA;AAAA,IACA,WAAW,MAAM;AAAA,IACjB,OAAO,MAAM;AAAA,IACb;AAAA,EACD;AACD;;;AC/EA,SAAS,eAAAE,cAAa,aAAAC,YAAW,UAAAC,SAAQ,wBAAAC,6BAA4B;AAGrE,IAAM,mBAAmB;AAGzB,IAAM,iBAAiC,OAAO,OAAO;AAAA,EACpD,QAAQ;AAAA,EACR,mBAAmB;AAAA,EACnB,cAAc;AACf,CAAC;AAmBM,SAAS,gBAAgC;AAC/C,QAAM,EAAE,WAAW,IAAI,eAAe;AAGtC,QAAM,cAAcC,QAAuB,cAAc;AACzD,QAAM,gBAAgBA,QAAe,KAAK,UAAU,cAAc,CAAC;AAEnE,QAAM,YAAYC;AAAA,IACjB,CAAC,kBAA4C;AAC5C,UAAI,CAAC,WAAY,QAAO,MAAM;AAAA,MAAC;AAG/B,YAAM,aAAa,YAAY,MAAM;AACpC,cAAM,YAAY,WAAW,UAAU;AACvC,cAAM,gBAAgB,KAAK,UAAU,SAAS;AAG9C,YAAI,kBAAkB,cAAc,SAAS;AAC5C,sBAAY,UAAU;AACtB,wBAAc,UAAU;AACxB,wBAAc;AAAA,QACf;AAAA,MACD,GAAG,gBAAgB;AAGnB,YAAM,gBAAgB,WAAW,UAAU;AAC3C,YAAM,oBAAoB,KAAK,UAAU,aAAa;AACtD,UAAI,sBAAsB,cAAc,SAAS;AAChD,oBAAY,UAAU;AACtB,sBAAc,UAAU;AACxB,sBAAc;AAAA,MACf;AAEA,aAAO,MAAM;AACZ,sBAAc,UAAU;AAAA,MACzB;AAAA,IACD;AAAA,IACA,CAAC,UAAU;AAAA,EACZ;AAEA,QAAM,cAAcA,aAAY,MAAsB;AACrD,WAAO,YAAY;AAAA,EACpB,GAAG,CAAC,CAAC;AAGL,EAAAC,WAAU,MAAM;AACf,QAAI,CAAC,YAAY;AAChB,kBAAY,UAAU;AACtB,oBAAc,UAAU,KAAK,UAAU,cAAc;AAAA,IACtD;AAAA,EACD,GAAG,CAAC,UAAU,CAAC;AAEf,SAAOC,sBAAqB,WAAW,WAAW;AACnD;;;AClFA,SAAS,WAAAC,gBAAe;AAgBjB,SAAS,cAAc,MAAkC;AAC/D,QAAM,EAAE,MAAM,IAAI,eAAe;AAEjC,SAAOC,SAAQ,MAAM;AACpB,WAAO,MAAM,WAAW,IAAI;AAAA,EAC7B,GAAG,CAAC,OAAO,IAAI,CAAC;AACjB;;;ACtBA,SAAS,eAAAC,cAAa,aAAAC,YAAW,WAAAC,UAAS,UAAAC,SAAQ,YAAAC,iBAAgB;AAClE,YAAY,OAAO;AAInB,IAAM,cAAc;AACpB,IAAM,WAAW;AACjB,IAAM,uBAAuB;AAKtB,SAAS,YACf,gBACA,UACA,WACoB;AACpB,QAAM,EAAE,MAAM,IAAI,eAAe;AACjC,QAAM,aAAaC,SAA4B,MAAM,MAAM,WAAW,cAAc,GAAG,CAAC,OAAO,cAAc,CAAC;AAC9G,QAAM,CAAC,GAAG,IAAIC,UAAS,MAAM,IAAM,MAAI,CAAC;AACxC,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAS,KAAK;AACxC,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAuB,IAAI;AACrD,QAAM,CAAC,SAAS,UAAU,IAAIA,UAAS,KAAK;AAC5C,QAAM,CAAC,SAAS,UAAU,IAAIA,UAAS,KAAK;AAC5C,QAAM,gBAAgBC,QAA0B,IAAI;AACpD,QAAM,mBAAmBA,QAAqB,CAAC,CAAC;AAEhD,QAAM,OAAOF,SAAQ,MAAM,IAAI,QAAQ,QAAQ,GAAG,CAAC,GAAG,CAAC;AACvD,QAAM,cAAcA,SAAQ,MAAM,IAAM,cAAY,IAAI,GAAG,CAAC,IAAI,CAAC;AAEjE,QAAM,mBAAmBG,aAAY,MAAM;AAC1C,eAAW,YAAY,UAAU,SAAS,CAAC;AAC3C,eAAW,YAAY,UAAU,SAAS,CAAC;AAAA,EAC5C,GAAG,CAAC,WAAW,CAAC;AAEhB,QAAM,OAAOA,aAAY,MAAM;AAC9B,gBAAY,KAAK;AACjB,qBAAiB;AAAA,EAClB,GAAG,CAAC,kBAAkB,WAAW,CAAC;AAElC,QAAM,OAAOA,aAAY,MAAM;AAC9B,gBAAY,KAAK;AACjB,qBAAiB;AAAA,EAClB,GAAG,CAAC,kBAAkB,WAAW,CAAC;AAElC,EAAAC,WAAU,MAAM;AACf,QAAI,WAAW;AAEf,UAAM,aAAa,YAA2B;AAC7C,eAAS,KAAK;AACd,eAAS,IAAI;AAEb,UAAI;AACH,cAAM,SAAS,MAAM,WAAW,SAAS,QAAQ;AACjD,YAAI,SAAU;AAEd,YAAI,SAAS,MAAM;AAClB,gBAAM,SAAS,IAAI,QAAQ,QAAQ;AACnC,iBAAO,OAAO,GAAG,OAAO,MAAM;AAAA,QAC/B,GAAG,WAAW;AAEd,cAAM,UAAU,oBAAoB,SAAS,SAAS,CAAC;AACvD,sBAAc,UAAU;AACxB,yBAAiB,UAAU,CAAC;AAC5B,YAAI,SAAS;AACZ,UAAE,cAAY,KAAK,SAAS,WAAW;AAAA,QACxC;AAEA,iBAAS,IAAI;AAAA,MACd,SAAS,OAAO;AACf,YAAI,SAAU;AACd,iBAAS,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;AAAA,MACnE;AAAA,IACD;AAEA,UAAM,UAAU,OAAO,SAAqB,WAAmC;AAC9E,uBAAiB;AACjB,UAAI,WAAW,aAAa;AAC3B;AAAA,MACD;AAEA,uBAAiB,QAAQ,KAAK,OAAO;AACrC,YAAM,WAAW,wBAAwB,cAAc,SAAS,iBAAiB,OAAO;AAExF,UAAI,iBAAiB,QAAQ,UAAU,sBAAsB;AAC5D,sBAAc,UAAU;AACxB,yBAAiB,UAAU,CAAC;AAAA,MAC7B;AAEA,UAAI;AACH,cAAM,WAAW,OAAO,UAAU;AAAA,UACjC,CAAC,SAAS,GAAG;AAAA,QACd,CAAC;AAAA,MACF,SAAS,OAAO;AACf,YAAI,CAAC,UAAU;AACd,mBAAS,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;AAAA,QACnE;AAAA,MACD;AAAA,IACD;AAEA,QAAI,GAAG,UAAU,OAAO;AACxB,SAAK,WAAW;AAEhB,qBAAiB;AAEjB,WAAO,MAAM;AACZ,iBAAW;AACX,UAAI,IAAI,UAAU,OAAO;AACzB,kBAAY,QAAQ;AACpB,oBAAc,UAAU;AACxB,uBAAiB,UAAU,CAAC;AAAA,IAC7B;AAAA,EACD,GAAG,CAAC,YAAY,KAAK,WAAW,UAAU,kBAAkB,WAAW,CAAC;AAExE,SAAO,EAAE,KAAK,MAAM,MAAM,MAAM,SAAS,SAAS,OAAO,MAAM;AAChE;AAEA,SAAS,oBAAoB,OAAmC;AAC/D,MAAI,UAAU,QAAQ,UAAU,QAAW;AAC1C,WAAO;AAAA,EACR;AAEA,MAAI,OAAO,UAAU,UAAU;AAC9B,UAAM,MAAM,IAAM,MAAI;AACtB,QAAI,QAAQ,QAAQ,EAAE,OAAO,GAAG,KAAK;AACrC,WAAS,sBAAoB,GAAG;AAAA,EACjC;AAEA,MAAI,iBAAiB,YAAY;AAChC,WAAO;AAAA,EACR;AAEA,MAAI,iBAAiB,aAAa;AACjC,WAAO,IAAI,WAAW,KAAK;AAAA,EAC5B;AAGA,MAAI,OAAO,eAAe,eAAe,YAAY,YAAY;AAChE,UAAM,aAAc,WAAuC;AAC3D,QAAI,WAAW,SAAS,KAAK,GAAG;AAC/B,aAAO,IAAI,WAAW,MAAM,QAAQ,MAAM,YAAY,MAAM,UAAU;AAAA,IACvE;AAAA,EACD;AAEA,QAAM,IAAI,MAAM,2EAA2E;AAC5F;AAEA,SAAS,wBAAwB,MAAyB,QAAkC;AAC3F,QAAM,MAAM,IAAM,MAAI;AACtB,MAAI,MAAM;AACT,IAAE,cAAY,KAAK,IAAI;AAAA,EACxB;AAEA,aAAW,SAAS,QAAQ;AAC3B,IAAE,cAAY,KAAK,KAAK;AAAA,EACzB;AAEA,SAAS,sBAAoB,GAAG;AACjC;","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
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Kora.js React hooks and bindings for offline-first applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"yjs": "^13.6.30",
|
|
34
|
-
"@korajs/core": "0.1.
|
|
34
|
+
"@korajs/core": "0.1.3",
|
|
35
35
|
"@korajs/store": "0.1.2",
|
|
36
|
-
"@korajs/sync": "0.1.
|
|
36
|
+
"@korajs/sync": "0.1.3"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@testing-library/jest-dom": "^6.0.0",
|