@korajs/react 1.0.0-beta.11 → 1.0.0-beta.13

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.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { KoraAppLike as KoraAppLike$1, KoraContextValue as KoraContextValue$1, UseMutationOptions as UseMutationOptions$1, UseMutationResultBase, UseQueryOptions as UseQueryOptions$1, AuthSyncBinding, AuthSyncState } from '@korajs/core/bindings';
1
+ import { KoraAppLike as KoraAppLike$1, KoraContextValue as KoraContextValue$1, UseQueryOptions as UseQueryOptions$1, UseMutationOptions as UseMutationOptions$1, UseMutationResultBase, AuthSyncBinding, AuthSyncState } from '@korajs/core/bindings';
2
2
  import { Store, QueryStoreCache, CollectionRecord, QueryBuilder, CollectionAccessor } from '@korajs/store';
3
3
  import { SyncEngine, CursorInfo, SyncStatusInfo, AwarenessUser, AwarenessState } from '@korajs/sync';
4
4
  import { ReactNode } from 'react';
@@ -6,7 +6,24 @@ 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 {@link useQuery} and {@link useQueryState}. */
10
+ interface UseQueryOptions extends UseQueryOptions$1 {
11
+ /**
12
+ * When true (default), `useQuery` throws a failed query to the nearest error
13
+ * boundary. When false it keeps returning the last good rows; read the error with
14
+ * `useQueryState`. Ignored by `useQueryState`, which always returns the error.
15
+ */
16
+ throwOnError?: boolean;
17
+ }
18
+ /** Result of `useQueryState`. */
19
+ interface UseQueryStateResult<T> {
20
+ /** The current rows (the last good rows while `error` is set). */
21
+ data: readonly T[];
22
+ /** The query's failure, or null. Cleared when results flow again. */
23
+ error: Error | null;
24
+ /** False until the first result for the current query arrived (always false on the server). */
25
+ ready: boolean;
26
+ }
10
27
  type UseMutationOptions<TData, TArgs extends unknown[], TContext = void> = UseMutationOptions$1<TData, TArgs, TContext>;
11
28
  /**
12
29
  * Props for the KoraProvider component.
@@ -53,15 +70,44 @@ interface AuthBoundKoraProviderProps {
53
70
  close(): Promise<void>;
54
71
  };
55
72
  signedOut?: ReactNode;
73
+ /**
74
+ * Rendered instead of the app while the session is locked (offline longer
75
+ * than the auth client's `maxOfflineGraceMs`, or the device clock moved
76
+ * backwards). The app and its local data stay open underneath; nothing is
77
+ * wiped. Defaults to `signedOut`.
78
+ */
79
+ locked?: ReactNode;
56
80
  fallback?: ReactNode;
81
+ error?: (context: AuthBoundKoraErrorContext) => ReactNode;
57
82
  children?: ReactNode;
58
83
  }
84
+ interface AuthBoundKoraSession {
85
+ /** The authenticated identity only. Access tokens are deliberately omitted. */
86
+ userId: string;
87
+ }
88
+ interface AuthBoundKoraInitializationError {
89
+ /** Stable Kora/DOM error code suitable for application-owned copy and recovery UI. */
90
+ code: string;
91
+ name: string;
92
+ message: string;
93
+ /** Sanitized primitive metadata. Credential-shaped keys are always removed. */
94
+ metadata: Readonly<Record<string, string | number | boolean | null>>;
95
+ /** Sanitized Error for logging; custom credential-bearing properties are omitted. */
96
+ cause: Error;
97
+ }
98
+ interface AuthBoundKoraErrorContext {
99
+ error: AuthBoundKoraInitializationError;
100
+ retry(): void;
101
+ session: AuthBoundKoraSession;
102
+ }
103
+ /** Classify initialization failures without requiring applications to parse copy. */
104
+ declare function classifyKoraInitializationError(error: unknown): AuthBoundKoraInitializationError;
59
105
  /**
60
106
  * Owns the security-sensitive app lifetime for shared-browser applications.
61
107
  * The previous app is fully closed before a different user's app is created,
62
108
  * and no stale provider tree is rendered across that boundary.
63
109
  */
64
- declare function AuthBoundKoraProvider({ authClient, createApp, signedOut, fallback, children, }: AuthBoundKoraProviderProps): ReactNode;
110
+ declare function AuthBoundKoraProvider({ authClient, createApp, signedOut, locked, fallback, error: renderError, children, }: AuthBoundKoraProviderProps): ReactNode;
65
111
 
66
112
  /**
67
113
  * React hook that returns the Kora app instance from context.
@@ -88,19 +134,137 @@ declare function useApp<T extends KoraAppLike = KoraAppLike>(): T;
88
134
 
89
135
  /**
90
136
  * React hook for reactive queries against the local Kora store.
137
+ *
138
+ * Returns the current rows synchronously (no loading state for local data) and
139
+ * re-renders only when the result set changes. The array keeps its identity while
140
+ * the result is unchanged. On the server (`renderToString`, Next.js App Router) and
141
+ * during hydration it returns `[]`; the rows arrive right after hydration.
142
+ *
143
+ * A query that fails (for example a where/orderBy on an unknown field) is thrown to
144
+ * the nearest React error boundary, so the failure is visible instead of an empty
145
+ * list. Pass `throwOnError: false`, or use {@link useQueryState}, to handle it inline.
146
+ *
147
+ * @param query - A query builder, for example `app.todos.where({ completed: false })`
148
+ * @param options - `enabled` (default true) and `throwOnError` (default true)
149
+ * @returns The current result rows
150
+ *
151
+ * @example
152
+ * ```tsx
153
+ * const todos = useQuery(app.todos.where({ completed: false }).orderBy('createdAt'))
154
+ * ```
91
155
  */
92
156
  declare function useQuery<T = CollectionRecord>(query: QueryBuilder<T>, options?: UseQueryOptions): readonly T[];
157
+ /**
158
+ * Like {@link useQuery}, but returns the error instead of throwing it.
159
+ *
160
+ * @param query - A query builder
161
+ * @param options - `enabled` (default true); `throwOnError` is ignored
162
+ * @returns `{ data, error, ready }`, the same object until one of them changes.
163
+ * `ready` is false until the first result for the current query has arrived.
164
+ *
165
+ * @example
166
+ * ```tsx
167
+ * const { data: todos, error } = useQueryState(app.todos.where({ completed: false }))
168
+ * if (error) return <p role="alert">{error.message}</p>
169
+ * ```
170
+ */
171
+ declare function useQueryState<T = CollectionRecord>(query: QueryBuilder<T>, options?: UseQueryOptions): UseQueryStateResult<T>;
93
172
 
94
173
  /**
95
174
  * React hook for performing mutations against the local Kora store.
175
+ *
176
+ * `mutate`, `mutateAsync` and `reset` keep their identity for the component's lifetime
177
+ * (safe in effect deps and as memoized props), and the result object only changes when
178
+ * `isLoading` or `error` does. The latest `mutationFn` and `options` are always used.
179
+ *
180
+ * @param mutationFn - The mutation to run, for example `app.todos.insert`
181
+ * @param options - Optional lifecycle callbacks (`onMutate`, `onSuccess`, `onError`, `onSettled`)
182
+ * @returns `{ mutate, mutateAsync, isLoading, error, reset }`
183
+ *
184
+ * @example
185
+ * ```tsx
186
+ * const { mutate: addTodo, isLoading } = useMutation(app.todos.insert)
187
+ * return <button disabled={isLoading} onClick={() => addTodo({ title: 'New' })}>Add</button>
188
+ * ```
96
189
  */
97
190
  declare function useMutation<TData, TArgs extends unknown[], TContext = void>(mutationFn: (...args: TArgs) => Promise<TData>, options?: UseMutationOptions<TData, TArgs, TContext>): UseMutationResult<TData, TArgs>;
98
191
 
99
192
  /**
100
193
  * React hook for monitoring the sync engine's connection status.
194
+ *
195
+ * Re-renders only when the status payload changes. The returned object, and its nested
196
+ * `heldNodes`, `initialSync` and `blockedFailure` values, keep their identity while
197
+ * unchanged, so they are safe as effect or memo dependencies. Beyond `status` and
198
+ * `pendingOperations` it reports `heldOperations` / `heldNodes` (writes of another
199
+ * user waiting on this device), `localDurability` (`'degraded'` when the local
200
+ * database cannot persist) and `serverProtocolVersion` / `protocolDeprecated`.
201
+ *
202
+ * @returns The current {@link SyncStatusInfo}
203
+ *
204
+ * @example
205
+ * ```tsx
206
+ * const { status, pendingOperations, localDurability } = useSyncStatus()
207
+ * ```
101
208
  */
102
209
  declare function useSyncStatus(): SyncStatusInfo;
103
210
 
211
+ /**
212
+ * The collections an app exposes, read from its `collections` namespace. Purely
213
+ * structural, so it follows whatever accessor types `createApp` infers from the schema.
214
+ */
215
+ type AppCollections<TApp> = TApp extends {
216
+ collections: infer C;
217
+ } ? C : never;
218
+ /** The collection names of an app, as string literals. */
219
+ type AppCollectionName<TApp> = Extract<keyof AppCollections<TApp>, string>;
220
+ /** The record type of a collection accessor, inferred from what its queries return. */
221
+ type AccessorRecord<TAccessor> = TAccessor extends {
222
+ where(...args: never[]): QueryBuilder<infer R>;
223
+ } ? R : CollectionRecord;
224
+ /** The record type of collection `N` of an app. */
225
+ type AppRecord<TApp, N extends AppCollectionName<TApp>> = AccessorRecord<AppCollections<TApp>[N]>;
226
+ /**
227
+ * Hooks bound to one app type, returned by {@link createKoraHooks}.
228
+ */
229
+ interface KoraHooks<TApp> {
230
+ /** The app from the nearest `<KoraProvider app={app}>`, typed as `TApp`. */
231
+ useApp: () => TApp;
232
+ /**
233
+ * The typed accessor of one collection: `insert`, `update` and `where` are checked
234
+ * against the schema. The accessor keeps its identity across renders.
235
+ */
236
+ useCollection: <N extends AppCollectionName<TApp>>(name: N) => AppCollections<TApp>[N];
237
+ /** {@link useQuery}: rows are typed from the query builder. */
238
+ useQuery: typeof useQuery;
239
+ /** {@link useQueryState}: rows are typed from the query builder. */
240
+ useQueryState: typeof useQueryState;
241
+ /** {@link useMutation}. */
242
+ useMutation: typeof useMutation;
243
+ /** {@link useSyncStatus}. */
244
+ useSyncStatus: typeof useSyncStatus;
245
+ }
246
+ /**
247
+ * Creates React hooks typed for one app, so components get schema-checked
248
+ * collection names, inserts, updates and rows without passing generics around.
249
+ * Call it once next to `createApp` and import the hooks from there. Nothing runs at
250
+ * call time; the hooks read the app from `<KoraProvider app={app}>`.
251
+ *
252
+ * @returns {@link KoraHooks} for `TApp`
253
+ *
254
+ * @example
255
+ * ```typescript
256
+ * // kora.ts
257
+ * export const app = createApp({ schema })
258
+ * export const { useCollection, useQuery, useMutation } = createKoraHooks<typeof app>()
259
+ *
260
+ * // TodoList.tsx
261
+ * const todos = useCollection('todos') // 'todoz' is a type error
262
+ * const rows = useQuery(todos.where({ completed: false }))
263
+ * const { mutate: add } = useMutation(todos.insert)
264
+ * ```
265
+ */
266
+ declare function createKoraHooks<TApp extends KoraAppLike>(): KoraHooks<TApp>;
267
+
104
268
  /**
105
269
  * React hook that returns a CollectionAccessor for the given collection name.
106
270
  * Convenience hook for accessing a collection without going through the store directly.
@@ -139,8 +303,11 @@ declare function usePresence(user: {
139
303
  /**
140
304
  * Returns all currently connected collaborators' awareness states.
141
305
  *
142
- * Excludes the local user — only returns remote peers.
306
+ * Excludes the local user — only returns remote peers. Safe under server rendering
307
+ * (`renderToString`, Next.js App Router): the server and the hydration pass see `[]`.
308
+ *
309
+ * @returns The remote peers' awareness states; the same array until they change
143
310
  */
144
311
  declare function useCollaborators(): AwarenessState[];
145
312
 
146
- export { AuthBoundKoraProvider, type AuthBoundKoraProviderProps, type KoraAppLike, type KoraContextValue, KoraProvider, type KoraProviderProps, type UseMutationOptions, type UseMutationResult, type UseQueryOptions, type UseRichTextOptions, type UseRichTextResult, useApp, useCollaborators, useCollection, useMutation, usePresence, useQuery, useRichText, useSyncStatus };
313
+ export { type AccessorRecord, type AppCollectionName, type AppCollections, type AppRecord, type AuthBoundKoraErrorContext, type AuthBoundKoraInitializationError, AuthBoundKoraProvider, type AuthBoundKoraProviderProps, type AuthBoundKoraSession, type KoraAppLike, type KoraContextValue, type KoraHooks, KoraProvider, type KoraProviderProps, type UseMutationOptions, type UseMutationResult, type UseQueryOptions, type UseQueryStateResult, type UseRichTextOptions, type UseRichTextResult, classifyKoraInitializationError, createKoraHooks, useApp, useCollaborators, useCollection, useMutation, usePresence, useQuery, useQueryState, useRichText, useSyncStatus };