@korajs/svelte 1.0.0-beta.12 → 1.0.0-beta.14

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/README.md CHANGED
@@ -1,17 +1,18 @@
1
1
  # @korajs/svelte
2
2
 
3
- Svelte bindings for [Kora.js](https://github.com/ehoneahobed/kora) offline-first applications (Svelte 4 stores + Svelte 5 components).
3
+ Svelte bindings for [Kora.js](https://korajs.dev) offline-first applications (Svelte 4 stores + Svelte 5 components).
4
4
 
5
5
  ## Install
6
6
 
7
7
  ```bash
8
- pnpm add korajs @korajs/svelte
8
+ pnpm add korajs@beta @korajs/svelte@beta
9
9
  ```
10
10
 
11
11
  ## Setup
12
12
 
13
13
  Create the Kora app at module scope, connect sync after `ready`, then mount a root layout with provider components (matches the CLI `svelte-sync` template):
14
14
 
15
+ <!-- docs-check: skip template excerpt: imports the project's own auth and schema modules -->
15
16
  ```typescript
16
17
  // main.ts
17
18
  import { createKoraAuthSync } from '@korajs/auth'
@@ -24,7 +25,7 @@ import koraWorkerUrl from './kora-worker.ts?worker&url'
24
25
 
25
26
  const kora = createKoraApp({
26
27
  schema,
27
- sync: { url: 'ws://localhost:3000/kora-sync', authClient: createKoraAuthSync({ authClient, schema }) },
28
+ sync: { url: 'ws://localhost:3001/kora-sync', authClient: createKoraAuthSync({ authClient, schema }) },
28
29
  store: { workerUrl: koraWorkerUrl },
29
30
  })
30
31
 
@@ -54,7 +55,7 @@ Precompiled component JS is also published under `dist/components/` for non-Vite
54
55
 
55
56
  ## Queries
56
57
 
57
- ### Static filters — readable store
58
+ ### Static filters: a readable store
58
59
 
59
60
  ```svelte
60
61
  <script lang="ts">
@@ -69,7 +70,7 @@ Precompiled component JS is also published under `dist/components/` for non-Vite
69
70
  {/each}
70
71
  ```
71
72
 
72
- ### Reactive filters — `KoraQuery` component
73
+ ### Reactive filters: the `KoraQuery` component
73
74
 
74
75
  ```svelte
75
76
  <script lang="ts">
@@ -98,7 +99,7 @@ Precompiled component JS is also published under `dist/components/` for non-Vite
98
99
  | `createSyncStatusStore()` | Sync status readable store |
99
100
  | `getApp()` / `getCollection()` | Context accessors |
100
101
  | `createRichTextBinding()` / `useRichText()` | Yjs richtext editor binding |
101
- | `applyPresence(user)` | Set local presence — use in `$effect(() => applyPresence(user))` |
102
+ | `applyPresence(user)` | Set local presence; use it in `$effect(() => applyPresence(user))` |
102
103
  | `createCollaboratorsStore()` | Remote collaborator awareness states |
103
104
  | `KoraQuery.svelte` | Descriptor-reactive query snippet |
104
105
 
package/dist/index.cjs CHANGED
@@ -23,6 +23,7 @@ __export(index_exports, {
23
23
  applyPresence: () => applyPresence,
24
24
  createCollaboratorsStore: () => createCollaboratorsStore,
25
25
  createMutation: () => createMutation,
26
+ createQueryStateStore: () => createQueryStateStore,
26
27
  createQueryStore: () => createQueryStore,
27
28
  createRichTextBinding: () => createRichTextBinding,
28
29
  createSyncStatusStore: () => createSyncStatusStore,
@@ -39,6 +40,7 @@ __export(index_exports, {
39
40
  useMutation: () => useMutation,
40
41
  usePresence: () => usePresence,
41
42
  useQuery: () => useQuery,
43
+ useQueryState: () => useQueryState,
42
44
  useRichText: () => useRichText,
43
45
  useSyncStatus: () => useSyncStatus
44
46
  });
@@ -48,8 +50,8 @@ module.exports = __toCommonJS(index_exports);
48
50
  var import_core = require("@korajs/core");
49
51
  var import_store = require("@korajs/store");
50
52
  var import_svelte = require("svelte");
51
- var koraContextKey = /* @__PURE__ */ Symbol("korajs-context");
52
- var koraAppContextKey = /* @__PURE__ */ Symbol("korajs-app");
53
+ var koraContextKey = /* @__PURE__ */ Symbol.for("@korajs/svelte:context");
54
+ var koraAppContextKey = /* @__PURE__ */ Symbol.for("@korajs/svelte:app");
53
55
  function setKoraContext(value) {
54
56
  (0, import_svelte.setContext)(koraContextKey, value);
55
57
  }
@@ -97,28 +99,116 @@ async function initKoraProvider(app) {
97
99
  var import_store2 = require("@korajs/store");
98
100
  var import_store3 = require("svelte/store");
99
101
  var EMPTY_ARRAY = Object.freeze([]);
100
- function createQueryStore(query, options) {
101
- const { queryStoreCache } = getKoraContext();
102
- const enabled = options?.enabled !== false;
103
- return (0, import_store3.readable)(EMPTY_ARRAY, (set) => {
104
- if (!enabled) {
105
- set(EMPTY_ARRAY);
106
- return () => {
107
- };
102
+ function isQueryBuilder(input) {
103
+ return typeof input.getDescriptor === "function";
104
+ }
105
+ function isReadable(input) {
106
+ return typeof input === "object" && input !== null && typeof input.subscribe === "function";
107
+ }
108
+ function readQueryError(queryStore) {
109
+ const channel = queryStore;
110
+ return typeof channel.getError === "function" ? channel.getError() : null;
111
+ }
112
+ function followQuery(cache, query, options, emit) {
113
+ let current = isQueryBuilder(query) ? query : null;
114
+ const enabledInput = options?.enabled;
115
+ let enabled = isReadable(enabledInput) ? true : enabledInput !== false;
116
+ let activeKey = null;
117
+ let release = null;
118
+ let state = {
119
+ data: EMPTY_ARRAY,
120
+ error: null,
121
+ ready: false
122
+ };
123
+ const publish = (next) => {
124
+ if (next.data === state.data && next.error === state.error && next.ready === state.ready) {
125
+ return;
108
126
  }
109
- (0, import_store2.assertQueryReady)(query);
110
- const queryStore = queryStoreCache.getOrCreate(query);
111
- const unsubscribe = queryStore.subscribe(() => {
112
- set(queryStore.getSnapshot());
113
- });
114
- set(queryStore.getSnapshot());
115
- return () => {
127
+ if (next.error && next.error !== state.error) options?.onError?.(next.error);
128
+ state = next;
129
+ emit(state);
130
+ };
131
+ const apply = () => {
132
+ const key = enabled && current ? (0, import_store2.queryKey)(current.getDescriptor()) : null;
133
+ if (key === activeKey) return;
134
+ release?.();
135
+ release = null;
136
+ activeKey = key;
137
+ if (!key || !current) {
138
+ publish({ data: EMPTY_ARRAY, error: null, ready: false });
139
+ return;
140
+ }
141
+ const builder = current;
142
+ (0, import_store2.assertQueryReady)(builder);
143
+ const queryStore = cache.getOrCreate(builder);
144
+ const sync = () => {
145
+ const loaded = queryStore.hasSnapshot();
146
+ publish({
147
+ // A descriptor switch keeps the previous rows until the new query answers.
148
+ data: loaded ? queryStore.getSnapshot() : state.data,
149
+ error: readQueryError(queryStore),
150
+ ready: loaded
151
+ });
152
+ };
153
+ const unsubscribe = queryStore.subscribe(sync);
154
+ sync();
155
+ release = () => {
116
156
  unsubscribe();
117
- queryStoreCache.release(query);
157
+ cache.release(builder);
118
158
  };
119
- });
159
+ };
160
+ const stops = [];
161
+ if (!isQueryBuilder(query)) {
162
+ stops.push(
163
+ query.subscribe((next) => {
164
+ current = next ?? null;
165
+ apply();
166
+ })
167
+ );
168
+ }
169
+ if (isReadable(enabledInput)) {
170
+ stops.push(
171
+ enabledInput.subscribe((next) => {
172
+ enabled = next !== false;
173
+ apply();
174
+ })
175
+ );
176
+ }
177
+ apply();
178
+ return () => {
179
+ for (const stop of stops) stop();
180
+ release?.();
181
+ release = null;
182
+ activeKey = null;
183
+ };
184
+ }
185
+ function createQueryStore(query, options) {
186
+ const { queryStoreCache } = getKoraContext();
187
+ const reporting = {
188
+ ...options,
189
+ onError: options?.onError ?? ((failure) => {
190
+ reportUnhandled(failure);
191
+ })
192
+ };
193
+ return (0, import_store3.readable)(
194
+ EMPTY_ARRAY,
195
+ (set) => followQuery(queryStoreCache, query, reporting, (state) => set(state.data))
196
+ );
197
+ }
198
+ function createQueryStateStore(query, options) {
199
+ const { queryStoreCache } = getKoraContext();
200
+ const initial = { data: EMPTY_ARRAY, error: null, ready: false };
201
+ return (0, import_store3.readable)(
202
+ initial,
203
+ (set) => followQuery(queryStoreCache, query, options, set)
204
+ );
205
+ }
206
+ function reportUnhandled(failure) {
207
+ const host = globalThis;
208
+ host.console?.error("[Kora] createQueryStore failed:", failure);
120
209
  }
121
210
  var useQuery = createQueryStore;
211
+ var useQueryState = createQueryStateStore;
122
212
 
123
213
  // src/composables/use-mutation.ts
124
214
  var import_bindings = require("@korajs/core/bindings");
@@ -271,6 +361,11 @@ function createRichTextBinding(collectionName, recordId, fieldName, options) {
271
361
  },
272
362
  setCursor: (anchor, head) => controller.setCursor(anchor, head),
273
363
  clearCursor: () => controller.clearCursor(),
364
+ get hasUnsavedChanges() {
365
+ return (0, import_store6.get)(resultStore).hasUnsavedChanges;
366
+ },
367
+ retrySave: () => controller.retrySave(),
368
+ getUnsavedState: () => controller.getUnsavedState(),
274
369
  subscribe: resultStore.subscribe
275
370
  };
276
371
  }
@@ -288,7 +383,10 @@ function buildResult(controller) {
288
383
  canRedo: snapshot.canRedo,
289
384
  cursors: [...snapshot.cursors],
290
385
  setCursor: (anchor, head) => controller.setCursor(anchor, head),
291
- clearCursor: () => controller.clearCursor()
386
+ clearCursor: () => controller.clearCursor(),
387
+ hasUnsavedChanges: snapshot.hasUnsavedChanges,
388
+ retrySave: () => controller.retrySave(),
389
+ getUnsavedState: () => controller.getUnsavedState()
292
390
  };
293
391
  }
294
392
 
@@ -334,6 +432,7 @@ var useCollaborators = createCollaboratorsStore;
334
432
  applyPresence,
335
433
  createCollaboratorsStore,
336
434
  createMutation,
435
+ createQueryStateStore,
337
436
  createQueryStore,
338
437
  createRichTextBinding,
339
438
  createSyncStatusStore,
@@ -350,6 +449,7 @@ var useCollaborators = createCollaboratorsStore;
350
449
  useMutation,
351
450
  usePresence,
352
451
  useQuery,
452
+ useQueryState,
353
453
  useRichText,
354
454
  useSyncStatus
355
455
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/context.ts","../src/stores/query-store.ts","../src/composables/use-mutation.ts","../src/composables/use-sync-status.ts","../src/composables/use-app.ts","../src/composables/use-collection.ts","../src/composables/use-rich-text.ts","../src/composables/use-presence.ts","../src/composables/use-collaborators.ts"],"sourcesContent":["export type {\n\tKoraAppHandle,\n\tKoraAppLike,\n\tKoraContextValue,\n\tUseMutationOptions,\n\tUseMutationResult,\n\tUseQueryOptions,\n\tUseRichTextResult,\n} from './types'\n\nexport {\n\tgetKoraApp,\n\tgetKoraContext,\n\tinitKoraProvider,\n\tsetKoraAppContext,\n\tsetKoraContext,\n} from './context'\n\nexport { createQueryStore, useQuery } from './stores/query-store'\nexport { createMutation, useMutation } from './composables/use-mutation'\nexport { createSyncStatusStore, useSyncStatus } from './composables/use-sync-status'\nexport { getApp, useApp } from './composables/use-app'\nexport { getCollection, useCollection } from './composables/use-collection'\nexport { createRichTextBinding, useRichText } from './composables/use-rich-text'\nexport type { UseRichTextOptions } from './composables/use-rich-text'\nexport { applyPresence, usePresence } from './composables/use-presence'\nexport { createCollaboratorsStore, useCollaborators } from './composables/use-collaborators'\n","import { KoraError } from '@korajs/core'\nimport { QueryStoreCache } from '@korajs/store'\nimport { getContext, setContext } from 'svelte'\nimport type { KoraAppLike, KoraContextValue } from './types'\n\nconst koraContextKey = Symbol('korajs-context')\nconst koraAppContextKey = Symbol('korajs-app')\n\nexport function setKoraContext(value: KoraContextValue): void {\n\tsetContext(koraContextKey, value)\n}\n\nexport function getKoraContext(): KoraContextValue {\n\tconst context = getContext<KoraContextValue | undefined>(koraContextKey)\n\tif (!context) {\n\t\tthrow new KoraError(\n\t\t\t'Kora context missing. Wrap your app with <KoraProvider app={kora}> or <KoraStoreProvider store={store}>.',\n\t\t\t'KORA_NOT_PROVIDED',\n\t\t\t{ fix: 'Wrap your app with <KoraProvider app={kora}> or <KoraStoreProvider store={store}>.' },\n\t\t)\n\t}\n\treturn context\n}\n\n/** @deprecated Use {@link setKoraContext} via {@link KoraProvider} or {@link KoraStoreProvider}. */\nexport function setKoraAppContext(koraApp: KoraAppLike): void {\n\tsetContext(koraAppContextKey, koraApp)\n}\n\n/** @deprecated Use {@link getKoraContext} and {@link getApp}. */\nexport function getKoraApp(): KoraAppLike {\n\tconst app = getContext<KoraAppLike | undefined>(koraAppContextKey)\n\tif (!app) {\n\t\tthrow new KoraError(\n\t\t\t'getKoraApp() requires setKoraAppContext(koraApp) on an ancestor.',\n\t\t\t'KORA_NOT_PROVIDED',\n\t\t)\n\t}\n\treturn app\n}\n\n/**\n * Initialize Kora provider context after `app.ready`.\n *\n * @deprecated Prefer {@link KoraProvider} or {@link KoraStoreProvider} in root layout.\n */\nexport async function initKoraProvider(app: KoraAppLike): Promise<KoraContextValue> {\n\tawait app.ready\n\tconst queryStoreCache =\n\t\ttypeof app.getQueryStoreCache === 'function' ? app.getQueryStoreCache() : new QueryStoreCache()\n\tconst value: KoraContextValue = {\n\t\tstore: app.getStore(),\n\t\tsyncEngine: app.getSyncEngine(),\n\t\tapp,\n\t\tevents: app.events ?? null,\n\t\tsubscribeSyncStatus: app.sync?.subscribeStatus ?? null,\n\t\tqueryStoreCache,\n\t}\n\tsetKoraContext(value)\n\tsetKoraAppContext(app)\n\treturn value\n}\n","import type { CollectionRecord, QueryBuilder } from '@korajs/store'\nimport { assertQueryReady } from '@korajs/store'\nimport { type Readable, readable } from 'svelte/store'\nimport { getKoraContext } from '../context'\nimport type { UseQueryOptions } from '../types'\n\nconst EMPTY_ARRAY: readonly unknown[] = Object.freeze([])\n\n/**\n * Create a Svelte readable store for a static Kora query.\n *\n * For reactive filter changes, use {@link KoraQuery} which re-subscribes when the\n * query descriptor changes.\n */\nexport function createQueryStore<T = CollectionRecord>(\n\tquery: QueryBuilder<T>,\n\toptions?: UseQueryOptions,\n): Readable<readonly T[]> {\n\tconst { queryStoreCache } = getKoraContext()\n\tconst enabled = options?.enabled !== false\n\n\treturn readable<readonly T[]>(EMPTY_ARRAY as readonly T[], (set) => {\n\t\tif (!enabled) {\n\t\t\tset(EMPTY_ARRAY as readonly T[])\n\t\t\treturn () => {}\n\t\t}\n\n\t\tassertQueryReady(query as QueryBuilder<unknown>)\n\t\tconst queryStore = queryStoreCache.getOrCreate(query)\n\t\tconst unsubscribe = queryStore.subscribe(() => {\n\t\t\tset(queryStore.getSnapshot())\n\t\t})\n\t\tset(queryStore.getSnapshot())\n\n\t\treturn () => {\n\t\t\tunsubscribe()\n\t\t\tqueryStoreCache.release(query as QueryBuilder<unknown>)\n\t\t}\n\t})\n}\n\n/** Alias for {@link createQueryStore}. */\nexport const useQuery = createQueryStore\n","import { createMutationController } from '@korajs/core/bindings'\nimport { onDestroy } from 'svelte'\nimport type { UseMutationOptions, UseMutationResult } from '../types'\n\n/**\n * Create a mutation controller with optimistic update hooks.\n */\nexport function createMutation<TData, TArgs extends unknown[], TContext = void>(\n\tmutationFn: (...args: TArgs) => Promise<TData>,\n\toptions?: UseMutationOptions<TData, TArgs, TContext>,\n): UseMutationResult<TData, TArgs> {\n\tconst optionsRef = { current: options }\n\toptionsRef.current = options\n\n\tconst fnRef = { current: mutationFn }\n\tfnRef.current = mutationFn\n\n\tconst controller = createMutationController<TData, TArgs, TContext>({\n\t\tmutationFn: (...args) => fnRef.current(...args),\n\t\tresolveOptions: () => optionsRef.current,\n\t})\n\n\tconst loadingListeners = new Set<(value: boolean) => void>()\n\tconst errorListeners = new Set<(value: Error | null) => void>()\n\n\tconst unsubscribe = controller.subscribe(() => {\n\t\tconst snapshot = controller.getSnapshot()\n\t\tfor (const listener of loadingListeners) {\n\t\t\tlistener(snapshot.isLoading)\n\t\t}\n\t\tfor (const listener of errorListeners) {\n\t\t\tlistener(snapshot.error)\n\t\t}\n\t})\n\n\t// Dispose the controller when the owning component unmounts, matching the\n\t// React (`useController`) and Vue (`onScopeDispose`) lifecycles. Guarded so\n\t// `createMutation()` can still be called outside a component (manual usage),\n\t// where `onDestroy` is unavailable and the caller owns disposal.\n\ttry {\n\t\tonDestroy(() => {\n\t\t\tunsubscribe()\n\t\t\tcontroller.destroy()\n\t\t})\n\t} catch {\n\t\t// Not inside a component initialization — no lifecycle to hook into.\n\t}\n\n\treturn {\n\t\tmutate: (...args: TArgs) => controller.mutate(...args),\n\t\tmutateAsync: (...args: TArgs) => controller.mutateAsync(...args),\n\t\tsubscribeLoading: (fn) => {\n\t\t\tloadingListeners.add(fn)\n\t\t\tfn(controller.getSnapshot().isLoading)\n\t\t\treturn () => loadingListeners.delete(fn)\n\t\t},\n\t\tsubscribeError: (fn) => {\n\t\t\terrorListeners.add(fn)\n\t\t\tfn(controller.getSnapshot().error)\n\t\t\treturn () => errorListeners.delete(fn)\n\t\t},\n\t\tget loading() {\n\t\t\treturn controller.getSnapshot().isLoading\n\t\t},\n\t\tget isLoading() {\n\t\t\treturn controller.getSnapshot().isLoading\n\t\t},\n\t\tget error() {\n\t\t\treturn controller.getSnapshot().error\n\t\t},\n\t\treset: () => controller.reset(),\n\t}\n}\n\n/** Alias for {@link createMutation}. */\nexport const useMutation = createMutation\n","import { OFFLINE_SYNC_STATUS, createSyncStatusController } from '@korajs/sync'\nimport type { SyncStatusInfo } from '@korajs/sync'\nimport { type Readable, readable } from 'svelte/store'\nimport { getKoraContext } from '../context'\n\n/**\n * Readable store of sync engine status.\n */\nexport function createSyncStatusStore(): Readable<SyncStatusInfo> {\n\tconst { syncEngine, subscribeSyncStatus, events } = getKoraContext()\n\n\treturn readable<SyncStatusInfo>(OFFLINE_SYNC_STATUS, (set) => {\n\t\tconst controller = createSyncStatusController({\n\t\t\tsyncEngine,\n\t\t\tsubscribeSyncStatus,\n\t\t\tevents: subscribeSyncStatus ? null : events,\n\t\t})\n\t\tset(controller.getSnapshot())\n\t\tconst unsubscribe = controller.subscribe(() => {\n\t\t\tset(controller.getSnapshot())\n\t\t})\n\t\treturn () => {\n\t\t\tunsubscribe()\n\t\t\tcontroller.destroy()\n\t\t}\n\t})\n}\n\n/** Alias for {@link createSyncStatusStore}. */\nexport const useSyncStatus = createSyncStatusStore\n","import { getKoraContext } from '../context'\nimport type { KoraAppLike } from '../types'\n\nexport function getApp<T extends KoraAppLike = KoraAppLike>(): T {\n\tconst { app } = getKoraContext()\n\tif (!app) {\n\t\tthrow new Error(\n\t\t\t'getApp() requires <KoraProvider app={kora}> or <KoraStoreProvider store={store}> with a createApp() instance.',\n\t\t)\n\t}\n\treturn app as T\n}\n\n/** Alias for {@link getApp}. */\nexport const useApp = getApp\n","import type { CollectionAccessor } from '@korajs/store'\nimport { getKoraContext } from '../context'\n\nexport function getCollection(name: string): CollectionAccessor {\n\tconst { store } = getKoraContext()\n\treturn store.collection(name)\n}\n\n/** Alias for {@link getCollection}. */\nexport const useCollection = getCollection\n","import { asRichTextSyncEngine, createRichTextController } from '@korajs/store'\nimport type { AwarenessUser } from '@korajs/sync'\nimport { onDestroy } from 'svelte'\nimport { type Readable, get, readable } from 'svelte/store'\nimport { getKoraContext } from '../context'\nimport type { UseRichTextResult } from '../types'\n\nexport interface UseRichTextOptions {\n\tuser?: AwarenessUser\n\tuseDocChannel?: boolean\n}\n\n/**\n * Binds a richtext field to a shared Yjs document for editor integration.\n *\n * Must be called during component initialization. For reactive target changes,\n * use {@link KoraRichText} instead.\n */\nexport function createRichTextBinding(\n\tcollectionName: string,\n\trecordId: string,\n\tfieldName: string,\n\toptions?: UseRichTextOptions,\n): UseRichTextResult & { subscribe: Readable<UseRichTextResult>['subscribe'] } {\n\tconst { store, syncEngine } = getKoraContext()\n\n\tconst controller = createRichTextController({\n\t\tcollection: store.collection(collectionName),\n\t\tcollectionName,\n\t\trecordId,\n\t\tfieldName,\n\t\tstore,\n\t\tsyncEngine: asRichTextSyncEngine(syncEngine),\n\t\tuseDocChannel: options?.useDocChannel,\n\t\tuser: options?.user,\n\t})\n\n\tonDestroy(() => {\n\t\tcontroller.destroy()\n\t})\n\n\tconst resultStore = readable<UseRichTextResult>(buildResult(controller), (set) => {\n\t\tconst sync = (): void => {\n\t\t\tset(buildResult(controller))\n\t\t}\n\t\tsync()\n\t\treturn controller.subscribe(sync)\n\t})\n\n\treturn {\n\t\tget doc() {\n\t\t\treturn controller.doc\n\t\t},\n\t\tget text() {\n\t\t\treturn controller.text\n\t\t},\n\t\tundo: () => controller.undo(),\n\t\tredo: () => controller.redo(),\n\t\tget ready() {\n\t\t\treturn get(resultStore).ready\n\t\t},\n\t\tget error() {\n\t\t\treturn get(resultStore).error\n\t\t},\n\t\tget canUndo() {\n\t\t\treturn get(resultStore).canUndo\n\t\t},\n\t\tget canRedo() {\n\t\t\treturn get(resultStore).canRedo\n\t\t},\n\t\tget cursors() {\n\t\t\treturn get(resultStore).cursors\n\t\t},\n\t\tsetCursor: (anchor: number, head: number) => controller.setCursor(anchor, head),\n\t\tclearCursor: () => controller.clearCursor(),\n\t\tsubscribe: resultStore.subscribe,\n\t}\n}\n\n/** @alias createRichTextBinding */\nexport const useRichText = createRichTextBinding\n\nfunction buildResult(controller: ReturnType<typeof createRichTextController>): UseRichTextResult {\n\tconst snapshot = controller.getSnapshot()\n\treturn {\n\t\tdoc: controller.doc,\n\t\ttext: controller.text,\n\t\tundo: () => controller.undo(),\n\t\tredo: () => controller.redo(),\n\t\tready: snapshot.ready,\n\t\terror: snapshot.error,\n\t\tcanUndo: snapshot.canUndo,\n\t\tcanRedo: snapshot.canRedo,\n\t\tcursors: [...snapshot.cursors],\n\t\tsetCursor: (anchor, head) => controller.setCursor(anchor, head),\n\t\tclearCursor: () => controller.clearCursor(),\n\t}\n}\n","import { getKoraContext } from '../context'\n\n/**\n * Applies local collaborative presence. Returns a cleanup function.\n *\n * In Svelte components, call from an effect:\n * `$effect(() => applyPresence(user))`\n */\nexport function applyPresence(\n\tuser: { name: string; color: string; avatar?: string } | null,\n): () => void {\n\tconst { syncEngine } = getKoraContext()\n\tif (!syncEngine || !user?.name || !user?.color) {\n\t\treturn () => {}\n\t}\n\n\tconst awareness = syncEngine.getAwarenessManager()\n\tawareness.setLocalState({\n\t\tuser: {\n\t\t\tname: user.name,\n\t\t\tcolor: user.color,\n\t\t\tavatar: user.avatar,\n\t\t},\n\t})\n\n\treturn () => {\n\t\tawareness.setLocalState(null)\n\t}\n}\n\n/** Alias for {@link applyPresence} — use inside `$effect()` in `.svelte` files. */\nexport const usePresence = applyPresence\n","import type { AwarenessState } from '@korajs/sync'\nimport { subscribeRemoteAwarenessStates } from '@korajs/sync'\nimport { type Readable, readable } from 'svelte/store'\nimport { getKoraContext } from '../context'\n\n/**\n * Readable store of remote collaborators' awareness states.\n */\nexport function createCollaboratorsStore(): Readable<AwarenessState[]> {\n\tconst { syncEngine } = getKoraContext()\n\n\treturn readable<AwarenessState[]>([], (set) => {\n\t\tif (!syncEngine) {\n\t\t\tset([])\n\t\t\treturn () => {}\n\t\t}\n\n\t\tconst awareness = syncEngine.getAwarenessManager()\n\t\treturn subscribeRemoteAwarenessStates(awareness, set)\n\t})\n}\n\n/** Alias for {@link createCollaboratorsStore}. */\nexport const useCollaborators = createCollaboratorsStore\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,kBAA0B;AAC1B,mBAAgC;AAChC,oBAAuC;AAGvC,IAAM,iBAAiB,uBAAO,gBAAgB;AAC9C,IAAM,oBAAoB,uBAAO,YAAY;AAEtC,SAAS,eAAe,OAA+B;AAC7D,gCAAW,gBAAgB,KAAK;AACjC;AAEO,SAAS,iBAAmC;AAClD,QAAM,cAAU,0BAAyC,cAAc;AACvE,MAAI,CAAC,SAAS;AACb,UAAM,IAAI;AAAA,MACT;AAAA,MACA;AAAA,MACA,EAAE,KAAK,qFAAqF;AAAA,IAC7F;AAAA,EACD;AACA,SAAO;AACR;AAGO,SAAS,kBAAkB,SAA4B;AAC7D,gCAAW,mBAAmB,OAAO;AACtC;AAGO,SAAS,aAA0B;AACzC,QAAM,UAAM,0BAAoC,iBAAiB;AACjE,MAAI,CAAC,KAAK;AACT,UAAM,IAAI;AAAA,MACT;AAAA,MACA;AAAA,IACD;AAAA,EACD;AACA,SAAO;AACR;AAOA,eAAsB,iBAAiB,KAA6C;AACnF,QAAM,IAAI;AACV,QAAM,kBACL,OAAO,IAAI,uBAAuB,aAAa,IAAI,mBAAmB,IAAI,IAAI,6BAAgB;AAC/F,QAAM,QAA0B;AAAA,IAC/B,OAAO,IAAI,SAAS;AAAA,IACpB,YAAY,IAAI,cAAc;AAAA,IAC9B;AAAA,IACA,QAAQ,IAAI,UAAU;AAAA,IACtB,qBAAqB,IAAI,MAAM,mBAAmB;AAAA,IAClD;AAAA,EACD;AACA,iBAAe,KAAK;AACpB,oBAAkB,GAAG;AACrB,SAAO;AACR;;;AC5DA,IAAAA,gBAAiC;AACjC,IAAAA,gBAAwC;AAIxC,IAAM,cAAkC,OAAO,OAAO,CAAC,CAAC;AAQjD,SAAS,iBACf,OACA,SACyB;AACzB,QAAM,EAAE,gBAAgB,IAAI,eAAe;AAC3C,QAAM,UAAU,SAAS,YAAY;AAErC,aAAO,wBAAuB,aAA6B,CAAC,QAAQ;AACnE,QAAI,CAAC,SAAS;AACb,UAAI,WAA2B;AAC/B,aAAO,MAAM;AAAA,MAAC;AAAA,IACf;AAEA,wCAAiB,KAA8B;AAC/C,UAAM,aAAa,gBAAgB,YAAY,KAAK;AACpD,UAAM,cAAc,WAAW,UAAU,MAAM;AAC9C,UAAI,WAAW,YAAY,CAAC;AAAA,IAC7B,CAAC;AACD,QAAI,WAAW,YAAY,CAAC;AAE5B,WAAO,MAAM;AACZ,kBAAY;AACZ,sBAAgB,QAAQ,KAA8B;AAAA,IACvD;AAAA,EACD,CAAC;AACF;AAGO,IAAM,WAAW;;;AC1CxB,sBAAyC;AACzC,IAAAC,iBAA0B;AAMnB,SAAS,eACf,YACA,SACkC;AAClC,QAAM,aAAa,EAAE,SAAS,QAAQ;AACtC,aAAW,UAAU;AAErB,QAAM,QAAQ,EAAE,SAAS,WAAW;AACpC,QAAM,UAAU;AAEhB,QAAM,iBAAa,0CAAiD;AAAA,IACnE,YAAY,IAAI,SAAS,MAAM,QAAQ,GAAG,IAAI;AAAA,IAC9C,gBAAgB,MAAM,WAAW;AAAA,EAClC,CAAC;AAED,QAAM,mBAAmB,oBAAI,IAA8B;AAC3D,QAAM,iBAAiB,oBAAI,IAAmC;AAE9D,QAAM,cAAc,WAAW,UAAU,MAAM;AAC9C,UAAM,WAAW,WAAW,YAAY;AACxC,eAAW,YAAY,kBAAkB;AACxC,eAAS,SAAS,SAAS;AAAA,IAC5B;AACA,eAAW,YAAY,gBAAgB;AACtC,eAAS,SAAS,KAAK;AAAA,IACxB;AAAA,EACD,CAAC;AAMD,MAAI;AACH,kCAAU,MAAM;AACf,kBAAY;AACZ,iBAAW,QAAQ;AAAA,IACpB,CAAC;AAAA,EACF,QAAQ;AAAA,EAER;AAEA,SAAO;AAAA,IACN,QAAQ,IAAI,SAAgB,WAAW,OAAO,GAAG,IAAI;AAAA,IACrD,aAAa,IAAI,SAAgB,WAAW,YAAY,GAAG,IAAI;AAAA,IAC/D,kBAAkB,CAAC,OAAO;AACzB,uBAAiB,IAAI,EAAE;AACvB,SAAG,WAAW,YAAY,EAAE,SAAS;AACrC,aAAO,MAAM,iBAAiB,OAAO,EAAE;AAAA,IACxC;AAAA,IACA,gBAAgB,CAAC,OAAO;AACvB,qBAAe,IAAI,EAAE;AACrB,SAAG,WAAW,YAAY,EAAE,KAAK;AACjC,aAAO,MAAM,eAAe,OAAO,EAAE;AAAA,IACtC;AAAA,IACA,IAAI,UAAU;AACb,aAAO,WAAW,YAAY,EAAE;AAAA,IACjC;AAAA,IACA,IAAI,YAAY;AACf,aAAO,WAAW,YAAY,EAAE;AAAA,IACjC;AAAA,IACA,IAAI,QAAQ;AACX,aAAO,WAAW,YAAY,EAAE;AAAA,IACjC;AAAA,IACA,OAAO,MAAM,WAAW,MAAM;AAAA,EAC/B;AACD;AAGO,IAAM,cAAc;;;AC3E3B,kBAAgE;AAEhE,IAAAC,gBAAwC;AAMjC,SAAS,wBAAkD;AACjE,QAAM,EAAE,YAAY,qBAAqB,OAAO,IAAI,eAAe;AAEnE,aAAO,wBAAyB,iCAAqB,CAAC,QAAQ;AAC7D,UAAM,iBAAa,wCAA2B;AAAA,MAC7C;AAAA,MACA;AAAA,MACA,QAAQ,sBAAsB,OAAO;AAAA,IACtC,CAAC;AACD,QAAI,WAAW,YAAY,CAAC;AAC5B,UAAM,cAAc,WAAW,UAAU,MAAM;AAC9C,UAAI,WAAW,YAAY,CAAC;AAAA,IAC7B,CAAC;AACD,WAAO,MAAM;AACZ,kBAAY;AACZ,iBAAW,QAAQ;AAAA,IACpB;AAAA,EACD,CAAC;AACF;AAGO,IAAM,gBAAgB;;;AC1BtB,SAAS,SAAiD;AAChE,QAAM,EAAE,IAAI,IAAI,eAAe;AAC/B,MAAI,CAAC,KAAK;AACT,UAAM,IAAI;AAAA,MACT;AAAA,IACD;AAAA,EACD;AACA,SAAO;AACR;AAGO,IAAM,SAAS;;;ACXf,SAAS,cAAc,MAAkC;AAC/D,QAAM,EAAE,MAAM,IAAI,eAAe;AACjC,SAAO,MAAM,WAAW,IAAI;AAC7B;AAGO,IAAM,gBAAgB;;;ACT7B,IAAAC,gBAA+D;AAE/D,IAAAC,iBAA0B;AAC1B,IAAAD,gBAA6C;AAetC,SAAS,sBACf,gBACA,UACA,WACA,SAC8E;AAC9E,QAAM,EAAE,OAAO,WAAW,IAAI,eAAe;AAE7C,QAAM,iBAAa,wCAAyB;AAAA,IAC3C,YAAY,MAAM,WAAW,cAAc;AAAA,IAC3C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,gBAAY,oCAAqB,UAAU;AAAA,IAC3C,eAAe,SAAS;AAAA,IACxB,MAAM,SAAS;AAAA,EAChB,CAAC;AAED,gCAAU,MAAM;AACf,eAAW,QAAQ;AAAA,EACpB,CAAC;AAED,QAAM,kBAAc,wBAA4B,YAAY,UAAU,GAAG,CAAC,QAAQ;AACjF,UAAM,OAAO,MAAY;AACxB,UAAI,YAAY,UAAU,CAAC;AAAA,IAC5B;AACA,SAAK;AACL,WAAO,WAAW,UAAU,IAAI;AAAA,EACjC,CAAC;AAED,SAAO;AAAA,IACN,IAAI,MAAM;AACT,aAAO,WAAW;AAAA,IACnB;AAAA,IACA,IAAI,OAAO;AACV,aAAO,WAAW;AAAA,IACnB;AAAA,IACA,MAAM,MAAM,WAAW,KAAK;AAAA,IAC5B,MAAM,MAAM,WAAW,KAAK;AAAA,IAC5B,IAAI,QAAQ;AACX,iBAAO,mBAAI,WAAW,EAAE;AAAA,IACzB;AAAA,IACA,IAAI,QAAQ;AACX,iBAAO,mBAAI,WAAW,EAAE;AAAA,IACzB;AAAA,IACA,IAAI,UAAU;AACb,iBAAO,mBAAI,WAAW,EAAE;AAAA,IACzB;AAAA,IACA,IAAI,UAAU;AACb,iBAAO,mBAAI,WAAW,EAAE;AAAA,IACzB;AAAA,IACA,IAAI,UAAU;AACb,iBAAO,mBAAI,WAAW,EAAE;AAAA,IACzB;AAAA,IACA,WAAW,CAAC,QAAgB,SAAiB,WAAW,UAAU,QAAQ,IAAI;AAAA,IAC9E,aAAa,MAAM,WAAW,YAAY;AAAA,IAC1C,WAAW,YAAY;AAAA,EACxB;AACD;AAGO,IAAM,cAAc;AAE3B,SAAS,YAAY,YAA4E;AAChG,QAAM,WAAW,WAAW,YAAY;AACxC,SAAO;AAAA,IACN,KAAK,WAAW;AAAA,IAChB,MAAM,WAAW;AAAA,IACjB,MAAM,MAAM,WAAW,KAAK;AAAA,IAC5B,MAAM,MAAM,WAAW,KAAK;AAAA,IAC5B,OAAO,SAAS;AAAA,IAChB,OAAO,SAAS;AAAA,IAChB,SAAS,SAAS;AAAA,IAClB,SAAS,SAAS;AAAA,IAClB,SAAS,CAAC,GAAG,SAAS,OAAO;AAAA,IAC7B,WAAW,CAAC,QAAQ,SAAS,WAAW,UAAU,QAAQ,IAAI;AAAA,IAC9D,aAAa,MAAM,WAAW,YAAY;AAAA,EAC3C;AACD;;;ACzFO,SAAS,cACf,MACa;AACb,QAAM,EAAE,WAAW,IAAI,eAAe;AACtC,MAAI,CAAC,cAAc,CAAC,MAAM,QAAQ,CAAC,MAAM,OAAO;AAC/C,WAAO,MAAM;AAAA,IAAC;AAAA,EACf;AAEA,QAAM,YAAY,WAAW,oBAAoB;AACjD,YAAU,cAAc;AAAA,IACvB,MAAM;AAAA,MACL,MAAM,KAAK;AAAA,MACX,OAAO,KAAK;AAAA,MACZ,QAAQ,KAAK;AAAA,IACd;AAAA,EACD,CAAC;AAED,SAAO,MAAM;AACZ,cAAU,cAAc,IAAI;AAAA,EAC7B;AACD;AAGO,IAAM,cAAc;;;AC9B3B,IAAAE,eAA+C;AAC/C,IAAAC,gBAAwC;AAMjC,SAAS,2BAAuD;AACtE,QAAM,EAAE,WAAW,IAAI,eAAe;AAEtC,aAAO,wBAA2B,CAAC,GAAG,CAAC,QAAQ;AAC9C,QAAI,CAAC,YAAY;AAChB,UAAI,CAAC,CAAC;AACN,aAAO,MAAM;AAAA,MAAC;AAAA,IACf;AAEA,UAAM,YAAY,WAAW,oBAAoB;AACjD,eAAO,6CAA+B,WAAW,GAAG;AAAA,EACrD,CAAC;AACF;AAGO,IAAM,mBAAmB;","names":["import_store","import_svelte","import_store","import_store","import_svelte","import_sync","import_store"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/context.ts","../src/stores/query-store.ts","../src/composables/use-mutation.ts","../src/composables/use-sync-status.ts","../src/composables/use-app.ts","../src/composables/use-collection.ts","../src/composables/use-rich-text.ts","../src/composables/use-presence.ts","../src/composables/use-collaborators.ts"],"sourcesContent":["export type {\n\tKoraAppHandle,\n\tKoraAppLike,\n\tKoraContextValue,\n\tUseMutationOptions,\n\tUseMutationResult,\n\tQueryState,\n\tUseQueryOptions,\n\tUseRichTextResult,\n} from './types'\n\nexport {\n\tgetKoraApp,\n\tgetKoraContext,\n\tinitKoraProvider,\n\tsetKoraAppContext,\n\tsetKoraContext,\n} from './context'\n\nexport {\n\tcreateQueryStateStore,\n\tcreateQueryStore,\n\tuseQuery,\n\tuseQueryState,\n} from './stores/query-store'\nexport type { QueryInput } from './stores/query-store'\nexport { createMutation, useMutation } from './composables/use-mutation'\nexport { createSyncStatusStore, useSyncStatus } from './composables/use-sync-status'\nexport { getApp, useApp } from './composables/use-app'\nexport { getCollection, useCollection } from './composables/use-collection'\nexport { createRichTextBinding, useRichText } from './composables/use-rich-text'\nexport type { UseRichTextOptions } from './composables/use-rich-text'\nexport { applyPresence, usePresence } from './composables/use-presence'\nexport { createCollaboratorsStore, useCollaborators } from './composables/use-collaborators'\n","import { KoraError } from '@korajs/core'\nimport { QueryStoreCache } from '@korajs/store'\nimport { getContext, setContext } from 'svelte'\nimport type { KoraAppLike, KoraContextValue } from './types'\n\n// Registered (Symbol.for) keys: the .svelte components are compiled from src/ by the app\n// (the \"svelte\" export condition) while helpers such as getCollection come from dist/, so\n// two copies of this module exist in every app. Module-local Symbol() keys made the\n// provider's context invisible to the helpers (\"Kora context missing\").\nconst koraContextKey = Symbol.for('@korajs/svelte:context')\nconst koraAppContextKey = Symbol.for('@korajs/svelte:app')\n\nexport function setKoraContext(value: KoraContextValue): void {\n\tsetContext(koraContextKey, value)\n}\n\nexport function getKoraContext(): KoraContextValue {\n\tconst context = getContext<KoraContextValue | undefined>(koraContextKey)\n\tif (!context) {\n\t\tthrow new KoraError(\n\t\t\t'Kora context missing. Wrap your app with <KoraProvider app={kora}> or <KoraStoreProvider store={store}>.',\n\t\t\t'KORA_NOT_PROVIDED',\n\t\t\t{ fix: 'Wrap your app with <KoraProvider app={kora}> or <KoraStoreProvider store={store}>.' },\n\t\t)\n\t}\n\treturn context\n}\n\n/** @deprecated Use {@link setKoraContext} via {@link KoraProvider} or {@link KoraStoreProvider}. */\nexport function setKoraAppContext(koraApp: KoraAppLike): void {\n\tsetContext(koraAppContextKey, koraApp)\n}\n\n/** @deprecated Use {@link getKoraContext} and {@link getApp}. */\nexport function getKoraApp(): KoraAppLike {\n\tconst app = getContext<KoraAppLike | undefined>(koraAppContextKey)\n\tif (!app) {\n\t\tthrow new KoraError(\n\t\t\t'getKoraApp() requires setKoraAppContext(koraApp) on an ancestor.',\n\t\t\t'KORA_NOT_PROVIDED',\n\t\t)\n\t}\n\treturn app\n}\n\n/**\n * Initialize Kora provider context after `app.ready`.\n *\n * @deprecated Prefer {@link KoraProvider} or {@link KoraStoreProvider} in root layout.\n */\nexport async function initKoraProvider(app: KoraAppLike): Promise<KoraContextValue> {\n\tawait app.ready\n\tconst queryStoreCache =\n\t\ttypeof app.getQueryStoreCache === 'function' ? app.getQueryStoreCache() : new QueryStoreCache()\n\tconst value: KoraContextValue = {\n\t\tstore: app.getStore(),\n\t\tsyncEngine: app.getSyncEngine(),\n\t\tapp,\n\t\tevents: app.events ?? null,\n\t\tsubscribeSyncStatus: app.sync?.subscribeStatus ?? null,\n\t\tqueryStoreCache,\n\t}\n\tsetKoraContext(value)\n\tsetKoraAppContext(app)\n\treturn value\n}\n","import type { CollectionRecord, QueryBuilder, QueryStore, QueryStoreCache } from '@korajs/store'\nimport { assertQueryReady, queryKey } from '@korajs/store'\nimport { type Readable, readable } from 'svelte/store'\nimport { getKoraContext } from '../context'\nimport type { QueryState, UseQueryOptions } from '../types'\n\nconst EMPTY_ARRAY: readonly unknown[] = Object.freeze([])\n\n/** A query input: a builder, or a readable store of one (null disables). */\nexport type QueryInput<T> = QueryBuilder<T> | Readable<QueryBuilder<T> | null | undefined>\n\nfunction isQueryBuilder<T>(input: QueryInput<T>): input is QueryBuilder<T> {\n\treturn typeof (input as { getDescriptor?: unknown }).getDescriptor === 'function'\n}\n\nfunction isReadable<V>(input: V | Readable<V>): input is Readable<V> {\n\treturn (\n\t\ttypeof input === 'object' &&\n\t\tinput !== null &&\n\t\ttypeof (input as { subscribe?: unknown }).subscribe === 'function'\n\t)\n}\n\n/** The STORE-12 error channel of a QueryStore, read structurally (older store builds lack it). */\nfunction readQueryError(queryStore: QueryStore<unknown>): Error | null {\n\tconst channel = queryStore as unknown as { getError?: () => Error | null }\n\treturn typeof channel.getError === 'function' ? channel.getError() : null\n}\n\n/**\n * Follows the query and `enabled` inputs (values or readable stores), holding one\n * ref-counted QueryStore per descriptor and switching when the descriptor changes\n * (DX-7). Returns the teardown.\n */\nfunction followQuery<T>(\n\tcache: QueryStoreCache,\n\tquery: QueryInput<T>,\n\toptions: UseQueryOptions | undefined,\n\temit: (state: QueryState<T>) => void,\n): () => void {\n\tlet current: QueryBuilder<T> | null = isQueryBuilder(query) ? query : null\n\tconst enabledInput = options?.enabled\n\tlet enabled = isReadable(enabledInput) ? true : enabledInput !== false\n\tlet activeKey: string | null = null\n\tlet release: (() => void) | null = null\n\tlet state: QueryState<T> = {\n\t\tdata: EMPTY_ARRAY as readonly T[],\n\t\terror: null,\n\t\tready: false,\n\t}\n\n\tconst publish = (next: QueryState<T>): void => {\n\t\tif (next.data === state.data && next.error === state.error && next.ready === state.ready) {\n\t\t\treturn\n\t\t}\n\t\tif (next.error && next.error !== state.error) options?.onError?.(next.error)\n\t\tstate = next\n\t\temit(state)\n\t}\n\n\tconst apply = (): void => {\n\t\tconst key = enabled && current ? queryKey(current.getDescriptor()) : null\n\t\tif (key === activeKey) return\n\t\trelease?.()\n\t\trelease = null\n\t\tactiveKey = key\n\t\tif (!key || !current) {\n\t\t\tpublish({ data: EMPTY_ARRAY as readonly T[], error: null, ready: false })\n\t\t\treturn\n\t\t}\n\t\tconst builder = current\n\t\tassertQueryReady(builder as QueryBuilder<unknown>)\n\t\tconst queryStore = cache.getOrCreate(builder)\n\t\tconst sync = (): void => {\n\t\t\tconst loaded = queryStore.hasSnapshot()\n\t\t\tpublish({\n\t\t\t\t// A descriptor switch keeps the previous rows until the new query answers.\n\t\t\t\tdata: loaded ? queryStore.getSnapshot() : state.data,\n\t\t\t\terror: readQueryError(queryStore as QueryStore<unknown>),\n\t\t\t\tready: loaded,\n\t\t\t})\n\t\t}\n\t\tconst unsubscribe = queryStore.subscribe(sync)\n\t\tsync()\n\t\trelease = () => {\n\t\t\tunsubscribe()\n\t\t\tcache.release(builder as QueryBuilder<unknown>)\n\t\t}\n\t}\n\n\tconst stops: Array<() => void> = []\n\tif (!isQueryBuilder(query)) {\n\t\tstops.push(\n\t\t\tquery.subscribe((next) => {\n\t\t\t\tcurrent = next ?? null\n\t\t\t\tapply()\n\t\t\t}),\n\t\t)\n\t}\n\tif (isReadable(enabledInput)) {\n\t\tstops.push(\n\t\t\tenabledInput.subscribe((next) => {\n\t\t\t\tenabled = next !== false\n\t\t\t\tapply()\n\t\t\t}),\n\t\t)\n\t}\n\tapply()\n\n\treturn () => {\n\t\tfor (const stop of stops) stop()\n\t\trelease?.()\n\t\trelease = null\n\t\tactiveKey = null\n\t}\n}\n\n/**\n * Create a Svelte readable store of a Kora query's rows.\n *\n * The query and `enabled` may be plain values or readable stores: pass a store (for\n * example a `derived` of your filter) and the rows follow it, re-subscribing when the\n * query's descriptor changes. A failed query is reported through `options.onError`,\n * or `console.error` without one; use {@link createQueryStateStore} to render it.\n *\n * @param query - A query builder, or a readable store of one (null disables)\n * @param options - `enabled` (value or readable store; default true) and `onError`\n * @returns A readable store of the current rows\n *\n * @example\n * ```ts\n * const filter = writable(false)\n * const todos = createQueryStore(derived(filter, (done) => app.todos.where({ completed: done })))\n * ```\n */\nexport function createQueryStore<T = CollectionRecord>(\n\tquery: QueryInput<T>,\n\toptions?: UseQueryOptions,\n): Readable<readonly T[]> {\n\tconst { queryStoreCache } = getKoraContext()\n\tconst reporting: UseQueryOptions = {\n\t\t...options,\n\t\tonError:\n\t\t\toptions?.onError ??\n\t\t\t((failure: Error) => {\n\t\t\t\t// Never swallowed: without a handler the failure is at least logged.\n\t\t\t\treportUnhandled(failure)\n\t\t\t}),\n\t}\n\treturn readable<readonly T[]>(EMPTY_ARRAY as readonly T[], (set) =>\n\t\tfollowQuery(queryStoreCache, query, reporting, (state) => set(state.data)),\n\t)\n}\n\n/**\n * Like {@link createQueryStore}, but the store holds `{ data, error, ready }`.\n *\n * @param query - A query builder, or a readable store of one\n * @param options - `enabled` and `onError`\n * @returns A readable store of the query state\n */\nexport function createQueryStateStore<T = CollectionRecord>(\n\tquery: QueryInput<T>,\n\toptions?: UseQueryOptions,\n): Readable<QueryState<T>> {\n\tconst { queryStoreCache } = getKoraContext()\n\tconst initial: QueryState<T> = { data: EMPTY_ARRAY as readonly T[], error: null, ready: false }\n\treturn readable<QueryState<T>>(initial, (set) =>\n\t\tfollowQuery(queryStoreCache, query, options, set),\n\t)\n}\n\n/**\n * Logs a query failure nobody handles. Read through globalThis because this package\n * builds without DOM or Node typings.\n */\nfunction reportUnhandled(failure: Error): void {\n\tconst host = globalThis as { console?: { error(...args: unknown[]): void } }\n\thost.console?.error('[Kora] createQueryStore failed:', failure)\n}\n\n/** Alias for {@link createQueryStore}. */\nexport const useQuery = createQueryStore\n\n/** Alias for {@link createQueryStateStore}. */\nexport const useQueryState = createQueryStateStore\n","import { createMutationController } from '@korajs/core/bindings'\nimport { onDestroy } from 'svelte'\nimport type { UseMutationOptions, UseMutationResult } from '../types'\n\n/**\n * Create a mutation controller with optimistic update hooks.\n */\nexport function createMutation<TData, TArgs extends unknown[], TContext = void>(\n\tmutationFn: (...args: TArgs) => Promise<TData>,\n\toptions?: UseMutationOptions<TData, TArgs, TContext>,\n): UseMutationResult<TData, TArgs> {\n\tconst optionsRef = { current: options }\n\toptionsRef.current = options\n\n\tconst fnRef = { current: mutationFn }\n\tfnRef.current = mutationFn\n\n\tconst controller = createMutationController<TData, TArgs, TContext>({\n\t\tmutationFn: (...args) => fnRef.current(...args),\n\t\tresolveOptions: () => optionsRef.current,\n\t})\n\n\tconst loadingListeners = new Set<(value: boolean) => void>()\n\tconst errorListeners = new Set<(value: Error | null) => void>()\n\n\tconst unsubscribe = controller.subscribe(() => {\n\t\tconst snapshot = controller.getSnapshot()\n\t\tfor (const listener of loadingListeners) {\n\t\t\tlistener(snapshot.isLoading)\n\t\t}\n\t\tfor (const listener of errorListeners) {\n\t\t\tlistener(snapshot.error)\n\t\t}\n\t})\n\n\t// Dispose the controller when the owning component unmounts, matching the\n\t// React (`useController`) and Vue (`onScopeDispose`) lifecycles. Guarded so\n\t// `createMutation()` can still be called outside a component (manual usage),\n\t// where `onDestroy` is unavailable and the caller owns disposal.\n\ttry {\n\t\tonDestroy(() => {\n\t\t\tunsubscribe()\n\t\t\tcontroller.destroy()\n\t\t})\n\t} catch {\n\t\t// Not inside a component initialization — no lifecycle to hook into.\n\t}\n\n\treturn {\n\t\tmutate: (...args: TArgs) => controller.mutate(...args),\n\t\tmutateAsync: (...args: TArgs) => controller.mutateAsync(...args),\n\t\tsubscribeLoading: (fn) => {\n\t\t\tloadingListeners.add(fn)\n\t\t\tfn(controller.getSnapshot().isLoading)\n\t\t\treturn () => loadingListeners.delete(fn)\n\t\t},\n\t\tsubscribeError: (fn) => {\n\t\t\terrorListeners.add(fn)\n\t\t\tfn(controller.getSnapshot().error)\n\t\t\treturn () => errorListeners.delete(fn)\n\t\t},\n\t\tget loading() {\n\t\t\treturn controller.getSnapshot().isLoading\n\t\t},\n\t\tget isLoading() {\n\t\t\treturn controller.getSnapshot().isLoading\n\t\t},\n\t\tget error() {\n\t\t\treturn controller.getSnapshot().error\n\t\t},\n\t\treset: () => controller.reset(),\n\t}\n}\n\n/** Alias for {@link createMutation}. */\nexport const useMutation = createMutation\n","import { OFFLINE_SYNC_STATUS, createSyncStatusController } from '@korajs/sync'\nimport type { SyncStatusInfo } from '@korajs/sync'\nimport { type Readable, readable } from 'svelte/store'\nimport { getKoraContext } from '../context'\n\n/**\n * Readable store of sync engine status.\n */\nexport function createSyncStatusStore(): Readable<SyncStatusInfo> {\n\tconst { syncEngine, subscribeSyncStatus, events } = getKoraContext()\n\n\treturn readable<SyncStatusInfo>(OFFLINE_SYNC_STATUS, (set) => {\n\t\tconst controller = createSyncStatusController({\n\t\t\tsyncEngine,\n\t\t\tsubscribeSyncStatus,\n\t\t\tevents: subscribeSyncStatus ? null : events,\n\t\t})\n\t\tset(controller.getSnapshot())\n\t\tconst unsubscribe = controller.subscribe(() => {\n\t\t\tset(controller.getSnapshot())\n\t\t})\n\t\treturn () => {\n\t\t\tunsubscribe()\n\t\t\tcontroller.destroy()\n\t\t}\n\t})\n}\n\n/** Alias for {@link createSyncStatusStore}. */\nexport const useSyncStatus = createSyncStatusStore\n","import { getKoraContext } from '../context'\nimport type { KoraAppLike } from '../types'\n\nexport function getApp<T extends KoraAppLike = KoraAppLike>(): T {\n\tconst { app } = getKoraContext()\n\tif (!app) {\n\t\tthrow new Error(\n\t\t\t'getApp() requires <KoraProvider app={kora}> or <KoraStoreProvider store={store}> with a createApp() instance.',\n\t\t)\n\t}\n\treturn app as T\n}\n\n/** Alias for {@link getApp}. */\nexport const useApp = getApp\n","import type { CollectionAccessor } from '@korajs/store'\nimport { getKoraContext } from '../context'\n\nexport function getCollection(name: string): CollectionAccessor {\n\tconst { store } = getKoraContext()\n\treturn store.collection(name)\n}\n\n/** Alias for {@link getCollection}. */\nexport const useCollection = getCollection\n","import { asRichTextSyncEngine, createRichTextController } from '@korajs/store'\nimport type { AwarenessUser } from '@korajs/sync'\nimport { onDestroy } from 'svelte'\nimport { type Readable, get, readable } from 'svelte/store'\nimport { getKoraContext } from '../context'\nimport type { UseRichTextResult } from '../types'\n\nexport interface UseRichTextOptions {\n\tuser?: AwarenessUser\n\tuseDocChannel?: boolean\n}\n\n/**\n * Binds a richtext field to a shared Yjs document for editor integration.\n *\n * Must be called during component initialization. For reactive target changes,\n * use {@link KoraRichText} instead.\n */\nexport function createRichTextBinding(\n\tcollectionName: string,\n\trecordId: string,\n\tfieldName: string,\n\toptions?: UseRichTextOptions,\n): UseRichTextResult & { subscribe: Readable<UseRichTextResult>['subscribe'] } {\n\tconst { store, syncEngine } = getKoraContext()\n\n\tconst controller = createRichTextController({\n\t\tcollection: store.collection(collectionName),\n\t\tcollectionName,\n\t\trecordId,\n\t\tfieldName,\n\t\tstore,\n\t\tsyncEngine: asRichTextSyncEngine(syncEngine),\n\t\tuseDocChannel: options?.useDocChannel,\n\t\tuser: options?.user,\n\t})\n\n\tonDestroy(() => {\n\t\tcontroller.destroy()\n\t})\n\n\tconst resultStore = readable<UseRichTextResult>(buildResult(controller), (set) => {\n\t\tconst sync = (): void => {\n\t\t\tset(buildResult(controller))\n\t\t}\n\t\tsync()\n\t\treturn controller.subscribe(sync)\n\t})\n\n\treturn {\n\t\tget doc() {\n\t\t\treturn controller.doc\n\t\t},\n\t\tget text() {\n\t\t\treturn controller.text\n\t\t},\n\t\tundo: () => controller.undo(),\n\t\tredo: () => controller.redo(),\n\t\tget ready() {\n\t\t\treturn get(resultStore).ready\n\t\t},\n\t\tget error() {\n\t\t\treturn get(resultStore).error\n\t\t},\n\t\tget canUndo() {\n\t\t\treturn get(resultStore).canUndo\n\t\t},\n\t\tget canRedo() {\n\t\t\treturn get(resultStore).canRedo\n\t\t},\n\t\tget cursors() {\n\t\t\treturn get(resultStore).cursors\n\t\t},\n\t\tsetCursor: (anchor: number, head: number) => controller.setCursor(anchor, head),\n\t\tclearCursor: () => controller.clearCursor(),\n\t\tget hasUnsavedChanges() {\n\t\t\treturn get(resultStore).hasUnsavedChanges\n\t\t},\n\t\tretrySave: () => controller.retrySave(),\n\t\tgetUnsavedState: () => controller.getUnsavedState(),\n\t\tsubscribe: resultStore.subscribe,\n\t}\n}\n\n/** @alias createRichTextBinding */\nexport const useRichText = createRichTextBinding\n\nfunction buildResult(controller: ReturnType<typeof createRichTextController>): UseRichTextResult {\n\tconst snapshot = controller.getSnapshot()\n\treturn {\n\t\tdoc: controller.doc,\n\t\ttext: controller.text,\n\t\tundo: () => controller.undo(),\n\t\tredo: () => controller.redo(),\n\t\tready: snapshot.ready,\n\t\terror: snapshot.error,\n\t\tcanUndo: snapshot.canUndo,\n\t\tcanRedo: snapshot.canRedo,\n\t\tcursors: [...snapshot.cursors],\n\t\tsetCursor: (anchor, head) => controller.setCursor(anchor, head),\n\t\tclearCursor: () => controller.clearCursor(),\n\t\thasUnsavedChanges: snapshot.hasUnsavedChanges,\n\t\tretrySave: () => controller.retrySave(),\n\t\tgetUnsavedState: () => controller.getUnsavedState(),\n\t}\n}\n","import { getKoraContext } from '../context'\n\n/**\n * Applies local collaborative presence. Returns a cleanup function.\n *\n * In Svelte components, call from an effect:\n * `$effect(() => applyPresence(user))`\n */\nexport function applyPresence(\n\tuser: { name: string; color: string; avatar?: string } | null,\n): () => void {\n\tconst { syncEngine } = getKoraContext()\n\tif (!syncEngine || !user?.name || !user?.color) {\n\t\treturn () => {}\n\t}\n\n\tconst awareness = syncEngine.getAwarenessManager()\n\tawareness.setLocalState({\n\t\tuser: {\n\t\t\tname: user.name,\n\t\t\tcolor: user.color,\n\t\t\tavatar: user.avatar,\n\t\t},\n\t})\n\n\treturn () => {\n\t\tawareness.setLocalState(null)\n\t}\n}\n\n/** Alias for {@link applyPresence} — use inside `$effect()` in `.svelte` files. */\nexport const usePresence = applyPresence\n","import type { AwarenessState } from '@korajs/sync'\nimport { subscribeRemoteAwarenessStates } from '@korajs/sync'\nimport { type Readable, readable } from 'svelte/store'\nimport { getKoraContext } from '../context'\n\n/**\n * Readable store of remote collaborators' awareness states.\n */\nexport function createCollaboratorsStore(): Readable<AwarenessState[]> {\n\tconst { syncEngine } = getKoraContext()\n\n\treturn readable<AwarenessState[]>([], (set) => {\n\t\tif (!syncEngine) {\n\t\t\tset([])\n\t\t\treturn () => {}\n\t\t}\n\n\t\tconst awareness = syncEngine.getAwarenessManager()\n\t\treturn subscribeRemoteAwarenessStates(awareness, set)\n\t})\n}\n\n/** Alias for {@link createCollaboratorsStore}. */\nexport const useCollaborators = createCollaboratorsStore\n"],"mappings":"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","names":["import_store","import_svelte","import_store","import_store","import_svelte","import_sync","import_store"]}
package/dist/index.d.cts CHANGED
@@ -1,12 +1,27 @@
1
1
  import { KoraAppLike as KoraAppLike$1, KoraContextValue as KoraContextValue$1, UseMutationOptions as UseMutationOptions$1, UseMutationResultBase, UseQueryOptions as UseQueryOptions$1 } from '@korajs/core/bindings';
2
- import { Store, QueryStoreCache, CollectionRecord, QueryBuilder, CollectionAccessor } from '@korajs/store';
2
+ import { Store, QueryStoreCache, QueryBuilder, CollectionRecord, CollectionAccessor } from '@korajs/store';
3
3
  import { SyncEngine, CursorInfo, SyncStatusInfo, AwarenessUser, AwarenessState } from '@korajs/sync';
4
- import * as Y from 'yjs';
5
4
  import { Readable } from 'svelte/store';
5
+ import * as Y from 'yjs';
6
6
 
7
7
  type KoraAppLike = KoraAppLike$1<Store, SyncEngine, QueryStoreCache>;
8
8
  type KoraContextValue = KoraContextValue$1<Store, SyncEngine, QueryStoreCache>;
9
- type UseQueryOptions = UseQueryOptions$1;
9
+ /** Options for `createQueryStore` and `createQueryStateStore`. */
10
+ interface UseQueryOptions extends Omit<UseQueryOptions$1, 'enabled'> {
11
+ /** When false, the query subscription is disabled. A value or a readable store. Defaults to true. */
12
+ enabled?: boolean | Readable<boolean>;
13
+ /** Called when the query fails (STORE-12). `createQueryStore` logs to `console.error` without it. */
14
+ onError?: (error: Error) => void;
15
+ }
16
+ /** Value of `createQueryStateStore`. */
17
+ interface QueryState<T> {
18
+ /** The current rows (the last good rows while `error` is set). */
19
+ data: readonly T[];
20
+ /** The query's failure, or null. Cleared when results flow again. */
21
+ error: Error | null;
22
+ /** False until the first result for the current query arrived. */
23
+ ready: boolean;
24
+ }
10
25
  type UseMutationOptions<TData, TArgs extends unknown[], TContext = void> = UseMutationOptions$1<TData, TArgs, TContext>;
11
26
  interface UseMutationResult<TData, TArgs extends unknown[]> extends UseMutationResultBase<TData, TArgs> {
12
27
  subscribeLoading: (fn: (value: boolean) => void) => () => void;
@@ -29,6 +44,15 @@ interface UseRichTextResult {
29
44
  cursors: CursorInfo[];
30
45
  setCursor: (anchor: number, head: number) => void;
31
46
  clearCursor: () => void;
47
+ /**
48
+ * True while local edits are not saved yet (a save pending or refused; see
49
+ * `error`). The edits stay in the document and the next edit saves them again.
50
+ */
51
+ hasUnsavedChanges: boolean;
52
+ /** Save the document now, for example after a refused save. */
53
+ retrySave: () => Promise<void>;
54
+ /** The full document state while edits are unsaved (for a recovery copy), else null. */
55
+ getUnsavedState: () => Uint8Array | null;
32
56
  }
33
57
 
34
58
  declare function setKoraContext(value: KoraContextValue): void;
@@ -44,15 +68,39 @@ declare function getKoraApp(): KoraAppLike;
44
68
  */
45
69
  declare function initKoraProvider(app: KoraAppLike): Promise<KoraContextValue>;
46
70
 
71
+ /** A query input: a builder, or a readable store of one (null disables). */
72
+ type QueryInput<T> = QueryBuilder<T> | Readable<QueryBuilder<T> | null | undefined>;
73
+ /**
74
+ * Create a Svelte readable store of a Kora query's rows.
75
+ *
76
+ * The query and `enabled` may be plain values or readable stores: pass a store (for
77
+ * example a `derived` of your filter) and the rows follow it, re-subscribing when the
78
+ * query's descriptor changes. A failed query is reported through `options.onError`,
79
+ * or `console.error` without one; use {@link createQueryStateStore} to render it.
80
+ *
81
+ * @param query - A query builder, or a readable store of one (null disables)
82
+ * @param options - `enabled` (value or readable store; default true) and `onError`
83
+ * @returns A readable store of the current rows
84
+ *
85
+ * @example
86
+ * ```ts
87
+ * const filter = writable(false)
88
+ * const todos = createQueryStore(derived(filter, (done) => app.todos.where({ completed: done })))
89
+ * ```
90
+ */
91
+ declare function createQueryStore<T = CollectionRecord>(query: QueryInput<T>, options?: UseQueryOptions): Readable<readonly T[]>;
47
92
  /**
48
- * Create a Svelte readable store for a static Kora query.
93
+ * Like {@link createQueryStore}, but the store holds `{ data, error, ready }`.
49
94
  *
50
- * For reactive filter changes, use {@link KoraQuery} which re-subscribes when the
51
- * query descriptor changes.
95
+ * @param query - A query builder, or a readable store of one
96
+ * @param options - `enabled` and `onError`
97
+ * @returns A readable store of the query state
52
98
  */
53
- declare function createQueryStore<T = CollectionRecord>(query: QueryBuilder<T>, options?: UseQueryOptions): Readable<readonly T[]>;
99
+ declare function createQueryStateStore<T = CollectionRecord>(query: QueryInput<T>, options?: UseQueryOptions): Readable<QueryState<T>>;
54
100
  /** Alias for {@link createQueryStore}. */
55
101
  declare const useQuery: typeof createQueryStore;
102
+ /** Alias for {@link createQueryStateStore}. */
103
+ declare const useQueryState: typeof createQueryStateStore;
56
104
 
57
105
  /**
58
106
  * Create a mutation controller with optimistic update hooks.
@@ -113,4 +161,4 @@ declare function createCollaboratorsStore(): Readable<AwarenessState[]>;
113
161
  /** Alias for {@link createCollaboratorsStore}. */
114
162
  declare const useCollaborators: typeof createCollaboratorsStore;
115
163
 
116
- export { type KoraAppHandle, type KoraAppLike, type KoraContextValue, type UseMutationOptions, type UseMutationResult, type UseQueryOptions, type UseRichTextOptions, type UseRichTextResult, applyPresence, createCollaboratorsStore, createMutation, createQueryStore, createRichTextBinding, createSyncStatusStore, getApp, getCollection, getKoraApp, getKoraContext, initKoraProvider, setKoraAppContext, setKoraContext, useApp, useCollaborators, useCollection, useMutation, usePresence, useQuery, useRichText, useSyncStatus };
164
+ export { type KoraAppHandle, type KoraAppLike, type KoraContextValue, type QueryInput, type QueryState, type UseMutationOptions, type UseMutationResult, type UseQueryOptions, type UseRichTextOptions, type UseRichTextResult, applyPresence, createCollaboratorsStore, createMutation, createQueryStateStore, createQueryStore, createRichTextBinding, createSyncStatusStore, getApp, getCollection, getKoraApp, getKoraContext, initKoraProvider, setKoraAppContext, setKoraContext, useApp, useCollaborators, useCollection, useMutation, usePresence, useQuery, useQueryState, useRichText, useSyncStatus };
package/dist/index.d.ts CHANGED
@@ -1,12 +1,27 @@
1
1
  import { KoraAppLike as KoraAppLike$1, KoraContextValue as KoraContextValue$1, UseMutationOptions as UseMutationOptions$1, UseMutationResultBase, UseQueryOptions as UseQueryOptions$1 } from '@korajs/core/bindings';
2
- import { Store, QueryStoreCache, CollectionRecord, QueryBuilder, CollectionAccessor } from '@korajs/store';
2
+ import { Store, QueryStoreCache, QueryBuilder, CollectionRecord, CollectionAccessor } from '@korajs/store';
3
3
  import { SyncEngine, CursorInfo, SyncStatusInfo, AwarenessUser, AwarenessState } from '@korajs/sync';
4
- import * as Y from 'yjs';
5
4
  import { Readable } from 'svelte/store';
5
+ import * as Y from 'yjs';
6
6
 
7
7
  type KoraAppLike = KoraAppLike$1<Store, SyncEngine, QueryStoreCache>;
8
8
  type KoraContextValue = KoraContextValue$1<Store, SyncEngine, QueryStoreCache>;
9
- type UseQueryOptions = UseQueryOptions$1;
9
+ /** Options for `createQueryStore` and `createQueryStateStore`. */
10
+ interface UseQueryOptions extends Omit<UseQueryOptions$1, 'enabled'> {
11
+ /** When false, the query subscription is disabled. A value or a readable store. Defaults to true. */
12
+ enabled?: boolean | Readable<boolean>;
13
+ /** Called when the query fails (STORE-12). `createQueryStore` logs to `console.error` without it. */
14
+ onError?: (error: Error) => void;
15
+ }
16
+ /** Value of `createQueryStateStore`. */
17
+ interface QueryState<T> {
18
+ /** The current rows (the last good rows while `error` is set). */
19
+ data: readonly T[];
20
+ /** The query's failure, or null. Cleared when results flow again. */
21
+ error: Error | null;
22
+ /** False until the first result for the current query arrived. */
23
+ ready: boolean;
24
+ }
10
25
  type UseMutationOptions<TData, TArgs extends unknown[], TContext = void> = UseMutationOptions$1<TData, TArgs, TContext>;
11
26
  interface UseMutationResult<TData, TArgs extends unknown[]> extends UseMutationResultBase<TData, TArgs> {
12
27
  subscribeLoading: (fn: (value: boolean) => void) => () => void;
@@ -29,6 +44,15 @@ interface UseRichTextResult {
29
44
  cursors: CursorInfo[];
30
45
  setCursor: (anchor: number, head: number) => void;
31
46
  clearCursor: () => void;
47
+ /**
48
+ * True while local edits are not saved yet (a save pending or refused; see
49
+ * `error`). The edits stay in the document and the next edit saves them again.
50
+ */
51
+ hasUnsavedChanges: boolean;
52
+ /** Save the document now, for example after a refused save. */
53
+ retrySave: () => Promise<void>;
54
+ /** The full document state while edits are unsaved (for a recovery copy), else null. */
55
+ getUnsavedState: () => Uint8Array | null;
32
56
  }
33
57
 
34
58
  declare function setKoraContext(value: KoraContextValue): void;
@@ -44,15 +68,39 @@ declare function getKoraApp(): KoraAppLike;
44
68
  */
45
69
  declare function initKoraProvider(app: KoraAppLike): Promise<KoraContextValue>;
46
70
 
71
+ /** A query input: a builder, or a readable store of one (null disables). */
72
+ type QueryInput<T> = QueryBuilder<T> | Readable<QueryBuilder<T> | null | undefined>;
73
+ /**
74
+ * Create a Svelte readable store of a Kora query's rows.
75
+ *
76
+ * The query and `enabled` may be plain values or readable stores: pass a store (for
77
+ * example a `derived` of your filter) and the rows follow it, re-subscribing when the
78
+ * query's descriptor changes. A failed query is reported through `options.onError`,
79
+ * or `console.error` without one; use {@link createQueryStateStore} to render it.
80
+ *
81
+ * @param query - A query builder, or a readable store of one (null disables)
82
+ * @param options - `enabled` (value or readable store; default true) and `onError`
83
+ * @returns A readable store of the current rows
84
+ *
85
+ * @example
86
+ * ```ts
87
+ * const filter = writable(false)
88
+ * const todos = createQueryStore(derived(filter, (done) => app.todos.where({ completed: done })))
89
+ * ```
90
+ */
91
+ declare function createQueryStore<T = CollectionRecord>(query: QueryInput<T>, options?: UseQueryOptions): Readable<readonly T[]>;
47
92
  /**
48
- * Create a Svelte readable store for a static Kora query.
93
+ * Like {@link createQueryStore}, but the store holds `{ data, error, ready }`.
49
94
  *
50
- * For reactive filter changes, use {@link KoraQuery} which re-subscribes when the
51
- * query descriptor changes.
95
+ * @param query - A query builder, or a readable store of one
96
+ * @param options - `enabled` and `onError`
97
+ * @returns A readable store of the query state
52
98
  */
53
- declare function createQueryStore<T = CollectionRecord>(query: QueryBuilder<T>, options?: UseQueryOptions): Readable<readonly T[]>;
99
+ declare function createQueryStateStore<T = CollectionRecord>(query: QueryInput<T>, options?: UseQueryOptions): Readable<QueryState<T>>;
54
100
  /** Alias for {@link createQueryStore}. */
55
101
  declare const useQuery: typeof createQueryStore;
102
+ /** Alias for {@link createQueryStateStore}. */
103
+ declare const useQueryState: typeof createQueryStateStore;
56
104
 
57
105
  /**
58
106
  * Create a mutation controller with optimistic update hooks.
@@ -113,4 +161,4 @@ declare function createCollaboratorsStore(): Readable<AwarenessState[]>;
113
161
  /** Alias for {@link createCollaboratorsStore}. */
114
162
  declare const useCollaborators: typeof createCollaboratorsStore;
115
163
 
116
- export { type KoraAppHandle, type KoraAppLike, type KoraContextValue, type UseMutationOptions, type UseMutationResult, type UseQueryOptions, type UseRichTextOptions, type UseRichTextResult, applyPresence, createCollaboratorsStore, createMutation, createQueryStore, createRichTextBinding, createSyncStatusStore, getApp, getCollection, getKoraApp, getKoraContext, initKoraProvider, setKoraAppContext, setKoraContext, useApp, useCollaborators, useCollection, useMutation, usePresence, useQuery, useRichText, useSyncStatus };
164
+ export { type KoraAppHandle, type KoraAppLike, type KoraContextValue, type QueryInput, type QueryState, type UseMutationOptions, type UseMutationResult, type UseQueryOptions, type UseRichTextOptions, type UseRichTextResult, applyPresence, createCollaboratorsStore, createMutation, createQueryStateStore, createQueryStore, createRichTextBinding, createSyncStatusStore, getApp, getCollection, getKoraApp, getKoraContext, initKoraProvider, setKoraAppContext, setKoraContext, useApp, useCollaborators, useCollection, useMutation, usePresence, useQuery, useQueryState, useRichText, useSyncStatus };