@taladb/react 0.8.4 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +273 -15
- package/dist/index.d.ts +273 -15
- package/dist/index.js +459 -8
- package/dist/index.mjs +453 -10
- package/package.json +3 -3
package/dist/index.d.mts
CHANGED
|
@@ -1,27 +1,52 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import { TalaDB, Document, Collection, Filter } from 'taladb';
|
|
3
|
+
import { TalaDB, OpenDBOptions, Document, Collection, Filter } from 'taladb';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
/** The TalaDB instance returned by `openDB()`. */
|
|
7
|
-
db: TalaDB;
|
|
5
|
+
type TalaDBProviderProps = {
|
|
8
6
|
children: ReactNode;
|
|
9
|
-
}
|
|
7
|
+
} & ({
|
|
8
|
+
/** A TalaDB instance you opened yourself with `openDB()`. */
|
|
9
|
+
db: TalaDB;
|
|
10
|
+
name?: never;
|
|
11
|
+
options?: never;
|
|
12
|
+
fallback?: never;
|
|
13
|
+
} | {
|
|
14
|
+
/**
|
|
15
|
+
* Database name — the provider owns the `openDB(name)` lifecycle:
|
|
16
|
+
* it opens lazily on the client (never during SSR), provides the handle
|
|
17
|
+
* once ready, and closes it on unmount. The natural form for Next.js,
|
|
18
|
+
* where `openDB` cannot run during server rendering.
|
|
19
|
+
*/
|
|
20
|
+
name: string;
|
|
21
|
+
/** Options forwarded to `openDB(name, options)` (e.g. inline sync config). */
|
|
22
|
+
options?: OpenDBOptions;
|
|
23
|
+
/**
|
|
24
|
+
* Rendered while the database is opening (and during SSR).
|
|
25
|
+
* Defaults to `null`. Children only render once the db is ready, so
|
|
26
|
+
* `useTalaDB()` never observes a missing instance.
|
|
27
|
+
*/
|
|
28
|
+
fallback?: ReactNode;
|
|
29
|
+
db?: never;
|
|
30
|
+
});
|
|
10
31
|
/**
|
|
11
32
|
* Provides a TalaDB instance to all child hooks.
|
|
12
33
|
*
|
|
13
|
-
*
|
|
34
|
+
* Two forms:
|
|
35
|
+
*
|
|
36
|
+
* **Instance form** — you own the lifecycle (plain React, React Native):
|
|
37
|
+
* ```tsx
|
|
14
38
|
* const db = await openDB('myapp.db')
|
|
39
|
+
* <TalaDBProvider db={db}>…</TalaDBProvider>
|
|
40
|
+
* ```
|
|
15
41
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* }
|
|
42
|
+
* **Name form** — the provider owns the lifecycle (recommended for Next.js):
|
|
43
|
+
* ```tsx
|
|
44
|
+
* <TalaDBProvider name="myapp.db" fallback={<Splash />}>…</TalaDBProvider>
|
|
45
|
+
* ```
|
|
46
|
+
* The database opens client-side only; during SSR (and while opening) the
|
|
47
|
+
* `fallback` renders instead of children, so hooks always see a ready db.
|
|
23
48
|
*/
|
|
24
|
-
declare function TalaDBProvider(
|
|
49
|
+
declare function TalaDBProvider(props: TalaDBProviderProps): react_jsx_runtime.JSX.Element;
|
|
25
50
|
/**
|
|
26
51
|
* Returns the TalaDB instance from the nearest `<TalaDBProvider>`.
|
|
27
52
|
*
|
|
@@ -49,6 +74,8 @@ interface FindResult<T> {
|
|
|
49
74
|
data: T[];
|
|
50
75
|
/** True until the first snapshot has been delivered from the database. */
|
|
51
76
|
loading: boolean;
|
|
77
|
+
/** Most recent subscription error, cleared by the next successful snapshot. */
|
|
78
|
+
error: unknown | null;
|
|
52
79
|
}
|
|
53
80
|
/**
|
|
54
81
|
* Subscribe to a live query. Re-renders whenever the matching documents change.
|
|
@@ -72,6 +99,8 @@ interface FindOneResult<T> {
|
|
|
72
99
|
data: T | null;
|
|
73
100
|
/** True until the first snapshot has been delivered from the database. */
|
|
74
101
|
loading: boolean;
|
|
102
|
+
/** Most recent subscription error, cleared by the next successful snapshot. */
|
|
103
|
+
error: unknown | null;
|
|
75
104
|
}
|
|
76
105
|
/**
|
|
77
106
|
* Subscribe to a single document live query. Re-renders when the matching
|
|
@@ -89,4 +118,233 @@ interface FindOneResult<T> {
|
|
|
89
118
|
*/
|
|
90
119
|
declare function useFindOne<T extends Document>(collection: Collection<T>, filter: Filter<T>): FindOneResult<T>;
|
|
91
120
|
|
|
92
|
-
|
|
121
|
+
/** Resolved network configuration for one replicated slice. */
|
|
122
|
+
interface ResolvedReplicationConfig {
|
|
123
|
+
/** Base URL; `/push` and `/pull` are appended by {@link HttpSyncAdapter}. */
|
|
124
|
+
endpoint: string;
|
|
125
|
+
/**
|
|
126
|
+
* Async (or sync) resolver for per-request headers — typically the
|
|
127
|
+
* `Authorization` bearer. Called **once per pass, at send time**, so a token
|
|
128
|
+
* that refreshed while a write sat in the local database is picked up when the
|
|
129
|
+
* write finally flushes.
|
|
130
|
+
*/
|
|
131
|
+
getAuth?: () => Promise<Record<string, string>> | Record<string, string>;
|
|
132
|
+
/** `fetch` implementation. Defaults to the global `fetch`. */
|
|
133
|
+
fetch?: typeof fetch;
|
|
134
|
+
/** Override the `/push` and `/pull` sub-paths to match an existing API. */
|
|
135
|
+
paths?: {
|
|
136
|
+
push?: string;
|
|
137
|
+
pull?: string;
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** A slice to warm on first run — a collection, optionally on a specific endpoint. */
|
|
142
|
+
type PrefetchSlice = {
|
|
143
|
+
collection: string;
|
|
144
|
+
endpoint?: string;
|
|
145
|
+
};
|
|
146
|
+
/** A prefetch entry: a collection name (shorthand) or a {@link PrefetchSlice}. */
|
|
147
|
+
type PrefetchEntry = string | PrefetchSlice;
|
|
148
|
+
/** `'once'` warms a slice only if it has never synced; `'always'` on every mount. */
|
|
149
|
+
type PrefetchMode = 'once' | 'always';
|
|
150
|
+
/**
|
|
151
|
+
* Replication settings shared by `useQuery` / `useMutation`, supplied once by
|
|
152
|
+
* `<ReplicationProvider>` and overridable per hook.
|
|
153
|
+
*
|
|
154
|
+
* The origin is *your* API — never a database credential. It authorizes the
|
|
155
|
+
* session token from {@link ReplicationConfig.getAuth} and returns only that
|
|
156
|
+
* user's slice, so the auth header doubles as the per-user scope.
|
|
157
|
+
*/
|
|
158
|
+
interface ReplicationConfig {
|
|
159
|
+
/** Base sync URL, e.g. `/api/sync`. `/push` and `/pull` are appended. */
|
|
160
|
+
endpoint: string;
|
|
161
|
+
/**
|
|
162
|
+
* Per-request header resolver — typically `{ Authorization: 'Bearer …' }`.
|
|
163
|
+
* Async so it can await a token refresh. Resolved at **send time**, once per
|
|
164
|
+
* pass, so an offline write flushed later carries a current token.
|
|
165
|
+
*/
|
|
166
|
+
getAuth?: () => Promise<Record<string, string>> | Record<string, string>;
|
|
167
|
+
/** `fetch` implementation. Defaults to the global `fetch`. */
|
|
168
|
+
fetch?: typeof fetch;
|
|
169
|
+
/** Override the `/push` and `/pull` sub-paths to match an existing API. */
|
|
170
|
+
paths?: {
|
|
171
|
+
push?: string;
|
|
172
|
+
pull?: string;
|
|
173
|
+
};
|
|
174
|
+
/**
|
|
175
|
+
* Default background refresh interval (ms) for `useQuery`. A replication
|
|
176
|
+
* *interval*, not a cache TTL — the local data is never evicted, only
|
|
177
|
+
* refreshed. Omit or set `0` to disable polling by default; a hook can still
|
|
178
|
+
* opt in per query. `30_000` matches the guide's own example cadence.
|
|
179
|
+
*/
|
|
180
|
+
pollMs?: number;
|
|
181
|
+
/**
|
|
182
|
+
* Slices to warm into the local replica in the background on first run, so a
|
|
183
|
+
* later `useQuery` for that collection reads local instead of waiting on the
|
|
184
|
+
* network. Best-effort and non-blocking: deferred to browser idle, run in the
|
|
185
|
+
* sync Worker on web, and silently skipped on failure. Each entry is a
|
|
186
|
+
* collection name or a {@link PrefetchSlice}.
|
|
187
|
+
*/
|
|
188
|
+
prefetch?: PrefetchEntry[];
|
|
189
|
+
/** How prefetch decides to warm a slice. Default `'once'`. */
|
|
190
|
+
prefetchMode?: PrefetchMode;
|
|
191
|
+
/** Max concurrent prefetch pulls — keeps the active page from starving. Default `2`. */
|
|
192
|
+
prefetchConcurrency?: number;
|
|
193
|
+
}
|
|
194
|
+
interface ReplicationProviderProps extends ReplicationConfig {
|
|
195
|
+
children: ReactNode;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Supplies replication defaults (endpoint, auth, poll interval, prefetch) to the
|
|
199
|
+
* `useQuery` / `useMutation` hooks below it. Compose it inside a
|
|
200
|
+
* `<TalaDBProvider>`:
|
|
201
|
+
*
|
|
202
|
+
* ```tsx
|
|
203
|
+
* <TalaDBProvider name="app.db" fallback={<Splash />}>
|
|
204
|
+
* <ReplicationProvider
|
|
205
|
+
* endpoint="/api/sync"
|
|
206
|
+
* getAuth={async () => ({ Authorization: `Bearer ${await session.token()}` })}
|
|
207
|
+
* pollMs={30_000}
|
|
208
|
+
* prefetch={['products', 'categories']}
|
|
209
|
+
* >
|
|
210
|
+
* <App />
|
|
211
|
+
* </ReplicationProvider>
|
|
212
|
+
* </TalaDBProvider>
|
|
213
|
+
* ```
|
|
214
|
+
*/
|
|
215
|
+
declare function ReplicationProvider({ children, ...config }: ReplicationProviderProps): react_jsx_runtime.JSX.Element;
|
|
216
|
+
/**
|
|
217
|
+
* Read the nearest replication config, merged with per-hook overrides.
|
|
218
|
+
* Non-throwing: `config` is `null` when no endpoint is resolvable (valid for
|
|
219
|
+
* `source: 'local-only'`); the caller decides whether that's an error.
|
|
220
|
+
*/
|
|
221
|
+
declare function useReplicationConfig(overrides?: Partial<ReplicationConfig>): {
|
|
222
|
+
config: ResolvedReplicationConfig | null;
|
|
223
|
+
pollMs: number;
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* How a `useQuery` combines the local replica with the remote origin.
|
|
228
|
+
* *(`remote-only` is a planned addition; the durable-replica model makes it a
|
|
229
|
+
* rare escape hatch.)*
|
|
230
|
+
*/
|
|
231
|
+
type ReadSource = 'local-first' | 'remote-first' | 'local-only';
|
|
232
|
+
interface UseQueryOptions<T extends Document> extends Partial<Pick<ReplicationConfig, 'endpoint' | 'getAuth' | 'fetch' | 'paths' | 'pollMs'>> {
|
|
233
|
+
/** Collection name — replicated as a whole; the `filter` narrows locally. */
|
|
234
|
+
collection: string;
|
|
235
|
+
/** Live filter over the local collection. Inline objects are safe. */
|
|
236
|
+
filter?: Filter<T>;
|
|
237
|
+
/**
|
|
238
|
+
* - `local-first` *(default)* — serve local immediately; refresh in the
|
|
239
|
+
* background, and the live query re-renders when the pull lands.
|
|
240
|
+
* - `remote-first` — stay `loading` until the first pull completes, then serve.
|
|
241
|
+
* - `local-only` — never touch the network (no endpoint required).
|
|
242
|
+
*/
|
|
243
|
+
source?: ReadSource;
|
|
244
|
+
}
|
|
245
|
+
interface QueryResult<T> {
|
|
246
|
+
/** Current matching documents from the local replica. Reactive. */
|
|
247
|
+
data: T[];
|
|
248
|
+
/** First local snapshot pending — plus the first pull, for `remote-first`. */
|
|
249
|
+
loading: boolean;
|
|
250
|
+
/** Most recent local-read error. */
|
|
251
|
+
error: unknown | null;
|
|
252
|
+
/** A background replication pass is in flight. */
|
|
253
|
+
syncing: boolean;
|
|
254
|
+
/** Most recent replication error (the local data is still served). */
|
|
255
|
+
syncError: unknown | null;
|
|
256
|
+
/** Trigger a pull now. No-op for `local-only`. */
|
|
257
|
+
refetch: () => Promise<void>;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Bind a component to a slice of a remote origin, backed by the local replica.
|
|
261
|
+
*
|
|
262
|
+
* The read is a live query over the local collection (`useFind`); the network
|
|
263
|
+
* pull writes into that same collection, so the live query re-renders on its
|
|
264
|
+
* own — one-way data flow, no `queryKey`, no `invalidateQueries`. See
|
|
265
|
+
* `docs/scoped-replication.md`.
|
|
266
|
+
*
|
|
267
|
+
* @example
|
|
268
|
+
* const { data, loading, syncing } = useQuery<Product>({
|
|
269
|
+
* collection: 'products',
|
|
270
|
+
* filter: { category: 'kitchen' },
|
|
271
|
+
* pollMs: 30_000,
|
|
272
|
+
* })
|
|
273
|
+
*/
|
|
274
|
+
declare function useQuery<T extends Document>(options: UseQueryOptions<T>): QueryResult<T>;
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Run several scoped queries at once — one replication + live query per entry,
|
|
278
|
+
* in parallel. The result array is index-aligned with `queries`.
|
|
279
|
+
*
|
|
280
|
+
* Each entry is an independent `useQuery`: its own collection, filter, source,
|
|
281
|
+
* and (optionally) endpoint. This is the multi-slice page case — e.g. a
|
|
282
|
+
* dashboard that needs `orders` and `products` from the same origin. There are
|
|
283
|
+
* no cross-collection joins (TalaDB is a document store); compose in the
|
|
284
|
+
* component.
|
|
285
|
+
*
|
|
286
|
+
* v1 note: entries are typed as `Document`. For a strictly-typed single slice
|
|
287
|
+
* use `useQuery<T>`; per-entry generics (tuple typing) are a follow-up.
|
|
288
|
+
*
|
|
289
|
+
* @example
|
|
290
|
+
* const [orders, products] = useQueries([
|
|
291
|
+
* { collection: 'orders', filter: { open: true } },
|
|
292
|
+
* { collection: 'products' },
|
|
293
|
+
* ])
|
|
294
|
+
*/
|
|
295
|
+
declare function useQueries(queries: UseQueryOptions<Document>[]): QueryResult<Document>[];
|
|
296
|
+
|
|
297
|
+
/** A single write intent against the local replica. Discriminated on `type`. */
|
|
298
|
+
type WriteOp<T extends Document> = {
|
|
299
|
+
type: 'insert';
|
|
300
|
+
doc: Omit<T, '_id'>;
|
|
301
|
+
} | {
|
|
302
|
+
type: 'update';
|
|
303
|
+
where: Filter<T>;
|
|
304
|
+
set: Partial<Omit<T, '_id'>>;
|
|
305
|
+
} | {
|
|
306
|
+
type: 'delete';
|
|
307
|
+
where: Filter<T>;
|
|
308
|
+
};
|
|
309
|
+
interface UseMutationOptions extends Partial<Pick<ReplicationConfig, 'endpoint' | 'getAuth' | 'fetch' | 'paths'>> {
|
|
310
|
+
/** Collection the write targets. */
|
|
311
|
+
collection: string;
|
|
312
|
+
/**
|
|
313
|
+
* Replication direction for the drain. `push` *(default)* sends the write;
|
|
314
|
+
* read hooks reconcile the authoritative value on their next pull. `both`
|
|
315
|
+
* also pulls the authoritative echo inline (heavier — replays the collection).
|
|
316
|
+
*/
|
|
317
|
+
direction?: 'push' | 'both';
|
|
318
|
+
/**
|
|
319
|
+
* On mount, attempt to flush any local writes left unsent from a previous
|
|
320
|
+
* (offline) session for this collection. Default `true`.
|
|
321
|
+
*/
|
|
322
|
+
drainOnMount?: boolean;
|
|
323
|
+
}
|
|
324
|
+
interface MutationResult<T extends Document> {
|
|
325
|
+
/** Fire-and-forget write. Errors surface on `error`, never thrown to render. */
|
|
326
|
+
mutate: (op: WriteOp<T>) => void;
|
|
327
|
+
/** Awaitable write. Resolves once the local write and drain settle; rejects on error. */
|
|
328
|
+
mutateAsync: (op: WriteOp<T>) => Promise<void>;
|
|
329
|
+
/** A write (local + drain) is in flight. */
|
|
330
|
+
pending: boolean;
|
|
331
|
+
/** Most recent write/drain error. The local write is durable regardless. */
|
|
332
|
+
error: unknown | null;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Local-first write hook. A mutation writes the local replica **first**
|
|
336
|
+
* (immediate, durable, reactive — every `useQuery`/`useFind` on the collection
|
|
337
|
+
* re-renders) and then replicates the change outward over the sync-contract with
|
|
338
|
+
* bounded retry. The network step never rolls the local write back: it is
|
|
339
|
+
* already committed, and a later drain still delivers it (write-behind).
|
|
340
|
+
*
|
|
341
|
+
* Write-authority is origin-authoritative by default — the push sends the
|
|
342
|
+
* change and the server is the arbiter; read hooks pull the authoritative value.
|
|
343
|
+
*
|
|
344
|
+
* @example
|
|
345
|
+
* const { mutate, pending } = useMutation<Order>({ collection: 'orders' })
|
|
346
|
+
* mutate({ type: 'update', where: { _id }, set: { status: 'shipped' } })
|
|
347
|
+
*/
|
|
348
|
+
declare function useMutation<T extends Document>(options: UseMutationOptions): MutationResult<T>;
|
|
349
|
+
|
|
350
|
+
export { type FindOneResult, type FindResult, type MutationResult, type PrefetchEntry, type PrefetchMode, type PrefetchSlice, type QueryResult, type ReadSource, type ReplicationConfig, ReplicationProvider, type ReplicationProviderProps, TalaDBProvider, type TalaDBProviderProps, type UseMutationOptions, type UseQueryOptions, type WriteOp, useCollection, useFind, useFindOne, useMutation, useQueries, useQuery, useReplicationConfig, useTalaDB };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,27 +1,52 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import { TalaDB, Document, Collection, Filter } from 'taladb';
|
|
3
|
+
import { TalaDB, OpenDBOptions, Document, Collection, Filter } from 'taladb';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
/** The TalaDB instance returned by `openDB()`. */
|
|
7
|
-
db: TalaDB;
|
|
5
|
+
type TalaDBProviderProps = {
|
|
8
6
|
children: ReactNode;
|
|
9
|
-
}
|
|
7
|
+
} & ({
|
|
8
|
+
/** A TalaDB instance you opened yourself with `openDB()`. */
|
|
9
|
+
db: TalaDB;
|
|
10
|
+
name?: never;
|
|
11
|
+
options?: never;
|
|
12
|
+
fallback?: never;
|
|
13
|
+
} | {
|
|
14
|
+
/**
|
|
15
|
+
* Database name — the provider owns the `openDB(name)` lifecycle:
|
|
16
|
+
* it opens lazily on the client (never during SSR), provides the handle
|
|
17
|
+
* once ready, and closes it on unmount. The natural form for Next.js,
|
|
18
|
+
* where `openDB` cannot run during server rendering.
|
|
19
|
+
*/
|
|
20
|
+
name: string;
|
|
21
|
+
/** Options forwarded to `openDB(name, options)` (e.g. inline sync config). */
|
|
22
|
+
options?: OpenDBOptions;
|
|
23
|
+
/**
|
|
24
|
+
* Rendered while the database is opening (and during SSR).
|
|
25
|
+
* Defaults to `null`. Children only render once the db is ready, so
|
|
26
|
+
* `useTalaDB()` never observes a missing instance.
|
|
27
|
+
*/
|
|
28
|
+
fallback?: ReactNode;
|
|
29
|
+
db?: never;
|
|
30
|
+
});
|
|
10
31
|
/**
|
|
11
32
|
* Provides a TalaDB instance to all child hooks.
|
|
12
33
|
*
|
|
13
|
-
*
|
|
34
|
+
* Two forms:
|
|
35
|
+
*
|
|
36
|
+
* **Instance form** — you own the lifecycle (plain React, React Native):
|
|
37
|
+
* ```tsx
|
|
14
38
|
* const db = await openDB('myapp.db')
|
|
39
|
+
* <TalaDBProvider db={db}>…</TalaDBProvider>
|
|
40
|
+
* ```
|
|
15
41
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* }
|
|
42
|
+
* **Name form** — the provider owns the lifecycle (recommended for Next.js):
|
|
43
|
+
* ```tsx
|
|
44
|
+
* <TalaDBProvider name="myapp.db" fallback={<Splash />}>…</TalaDBProvider>
|
|
45
|
+
* ```
|
|
46
|
+
* The database opens client-side only; during SSR (and while opening) the
|
|
47
|
+
* `fallback` renders instead of children, so hooks always see a ready db.
|
|
23
48
|
*/
|
|
24
|
-
declare function TalaDBProvider(
|
|
49
|
+
declare function TalaDBProvider(props: TalaDBProviderProps): react_jsx_runtime.JSX.Element;
|
|
25
50
|
/**
|
|
26
51
|
* Returns the TalaDB instance from the nearest `<TalaDBProvider>`.
|
|
27
52
|
*
|
|
@@ -49,6 +74,8 @@ interface FindResult<T> {
|
|
|
49
74
|
data: T[];
|
|
50
75
|
/** True until the first snapshot has been delivered from the database. */
|
|
51
76
|
loading: boolean;
|
|
77
|
+
/** Most recent subscription error, cleared by the next successful snapshot. */
|
|
78
|
+
error: unknown | null;
|
|
52
79
|
}
|
|
53
80
|
/**
|
|
54
81
|
* Subscribe to a live query. Re-renders whenever the matching documents change.
|
|
@@ -72,6 +99,8 @@ interface FindOneResult<T> {
|
|
|
72
99
|
data: T | null;
|
|
73
100
|
/** True until the first snapshot has been delivered from the database. */
|
|
74
101
|
loading: boolean;
|
|
102
|
+
/** Most recent subscription error, cleared by the next successful snapshot. */
|
|
103
|
+
error: unknown | null;
|
|
75
104
|
}
|
|
76
105
|
/**
|
|
77
106
|
* Subscribe to a single document live query. Re-renders when the matching
|
|
@@ -89,4 +118,233 @@ interface FindOneResult<T> {
|
|
|
89
118
|
*/
|
|
90
119
|
declare function useFindOne<T extends Document>(collection: Collection<T>, filter: Filter<T>): FindOneResult<T>;
|
|
91
120
|
|
|
92
|
-
|
|
121
|
+
/** Resolved network configuration for one replicated slice. */
|
|
122
|
+
interface ResolvedReplicationConfig {
|
|
123
|
+
/** Base URL; `/push` and `/pull` are appended by {@link HttpSyncAdapter}. */
|
|
124
|
+
endpoint: string;
|
|
125
|
+
/**
|
|
126
|
+
* Async (or sync) resolver for per-request headers — typically the
|
|
127
|
+
* `Authorization` bearer. Called **once per pass, at send time**, so a token
|
|
128
|
+
* that refreshed while a write sat in the local database is picked up when the
|
|
129
|
+
* write finally flushes.
|
|
130
|
+
*/
|
|
131
|
+
getAuth?: () => Promise<Record<string, string>> | Record<string, string>;
|
|
132
|
+
/** `fetch` implementation. Defaults to the global `fetch`. */
|
|
133
|
+
fetch?: typeof fetch;
|
|
134
|
+
/** Override the `/push` and `/pull` sub-paths to match an existing API. */
|
|
135
|
+
paths?: {
|
|
136
|
+
push?: string;
|
|
137
|
+
pull?: string;
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** A slice to warm on first run — a collection, optionally on a specific endpoint. */
|
|
142
|
+
type PrefetchSlice = {
|
|
143
|
+
collection: string;
|
|
144
|
+
endpoint?: string;
|
|
145
|
+
};
|
|
146
|
+
/** A prefetch entry: a collection name (shorthand) or a {@link PrefetchSlice}. */
|
|
147
|
+
type PrefetchEntry = string | PrefetchSlice;
|
|
148
|
+
/** `'once'` warms a slice only if it has never synced; `'always'` on every mount. */
|
|
149
|
+
type PrefetchMode = 'once' | 'always';
|
|
150
|
+
/**
|
|
151
|
+
* Replication settings shared by `useQuery` / `useMutation`, supplied once by
|
|
152
|
+
* `<ReplicationProvider>` and overridable per hook.
|
|
153
|
+
*
|
|
154
|
+
* The origin is *your* API — never a database credential. It authorizes the
|
|
155
|
+
* session token from {@link ReplicationConfig.getAuth} and returns only that
|
|
156
|
+
* user's slice, so the auth header doubles as the per-user scope.
|
|
157
|
+
*/
|
|
158
|
+
interface ReplicationConfig {
|
|
159
|
+
/** Base sync URL, e.g. `/api/sync`. `/push` and `/pull` are appended. */
|
|
160
|
+
endpoint: string;
|
|
161
|
+
/**
|
|
162
|
+
* Per-request header resolver — typically `{ Authorization: 'Bearer …' }`.
|
|
163
|
+
* Async so it can await a token refresh. Resolved at **send time**, once per
|
|
164
|
+
* pass, so an offline write flushed later carries a current token.
|
|
165
|
+
*/
|
|
166
|
+
getAuth?: () => Promise<Record<string, string>> | Record<string, string>;
|
|
167
|
+
/** `fetch` implementation. Defaults to the global `fetch`. */
|
|
168
|
+
fetch?: typeof fetch;
|
|
169
|
+
/** Override the `/push` and `/pull` sub-paths to match an existing API. */
|
|
170
|
+
paths?: {
|
|
171
|
+
push?: string;
|
|
172
|
+
pull?: string;
|
|
173
|
+
};
|
|
174
|
+
/**
|
|
175
|
+
* Default background refresh interval (ms) for `useQuery`. A replication
|
|
176
|
+
* *interval*, not a cache TTL — the local data is never evicted, only
|
|
177
|
+
* refreshed. Omit or set `0` to disable polling by default; a hook can still
|
|
178
|
+
* opt in per query. `30_000` matches the guide's own example cadence.
|
|
179
|
+
*/
|
|
180
|
+
pollMs?: number;
|
|
181
|
+
/**
|
|
182
|
+
* Slices to warm into the local replica in the background on first run, so a
|
|
183
|
+
* later `useQuery` for that collection reads local instead of waiting on the
|
|
184
|
+
* network. Best-effort and non-blocking: deferred to browser idle, run in the
|
|
185
|
+
* sync Worker on web, and silently skipped on failure. Each entry is a
|
|
186
|
+
* collection name or a {@link PrefetchSlice}.
|
|
187
|
+
*/
|
|
188
|
+
prefetch?: PrefetchEntry[];
|
|
189
|
+
/** How prefetch decides to warm a slice. Default `'once'`. */
|
|
190
|
+
prefetchMode?: PrefetchMode;
|
|
191
|
+
/** Max concurrent prefetch pulls — keeps the active page from starving. Default `2`. */
|
|
192
|
+
prefetchConcurrency?: number;
|
|
193
|
+
}
|
|
194
|
+
interface ReplicationProviderProps extends ReplicationConfig {
|
|
195
|
+
children: ReactNode;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Supplies replication defaults (endpoint, auth, poll interval, prefetch) to the
|
|
199
|
+
* `useQuery` / `useMutation` hooks below it. Compose it inside a
|
|
200
|
+
* `<TalaDBProvider>`:
|
|
201
|
+
*
|
|
202
|
+
* ```tsx
|
|
203
|
+
* <TalaDBProvider name="app.db" fallback={<Splash />}>
|
|
204
|
+
* <ReplicationProvider
|
|
205
|
+
* endpoint="/api/sync"
|
|
206
|
+
* getAuth={async () => ({ Authorization: `Bearer ${await session.token()}` })}
|
|
207
|
+
* pollMs={30_000}
|
|
208
|
+
* prefetch={['products', 'categories']}
|
|
209
|
+
* >
|
|
210
|
+
* <App />
|
|
211
|
+
* </ReplicationProvider>
|
|
212
|
+
* </TalaDBProvider>
|
|
213
|
+
* ```
|
|
214
|
+
*/
|
|
215
|
+
declare function ReplicationProvider({ children, ...config }: ReplicationProviderProps): react_jsx_runtime.JSX.Element;
|
|
216
|
+
/**
|
|
217
|
+
* Read the nearest replication config, merged with per-hook overrides.
|
|
218
|
+
* Non-throwing: `config` is `null` when no endpoint is resolvable (valid for
|
|
219
|
+
* `source: 'local-only'`); the caller decides whether that's an error.
|
|
220
|
+
*/
|
|
221
|
+
declare function useReplicationConfig(overrides?: Partial<ReplicationConfig>): {
|
|
222
|
+
config: ResolvedReplicationConfig | null;
|
|
223
|
+
pollMs: number;
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* How a `useQuery` combines the local replica with the remote origin.
|
|
228
|
+
* *(`remote-only` is a planned addition; the durable-replica model makes it a
|
|
229
|
+
* rare escape hatch.)*
|
|
230
|
+
*/
|
|
231
|
+
type ReadSource = 'local-first' | 'remote-first' | 'local-only';
|
|
232
|
+
interface UseQueryOptions<T extends Document> extends Partial<Pick<ReplicationConfig, 'endpoint' | 'getAuth' | 'fetch' | 'paths' | 'pollMs'>> {
|
|
233
|
+
/** Collection name — replicated as a whole; the `filter` narrows locally. */
|
|
234
|
+
collection: string;
|
|
235
|
+
/** Live filter over the local collection. Inline objects are safe. */
|
|
236
|
+
filter?: Filter<T>;
|
|
237
|
+
/**
|
|
238
|
+
* - `local-first` *(default)* — serve local immediately; refresh in the
|
|
239
|
+
* background, and the live query re-renders when the pull lands.
|
|
240
|
+
* - `remote-first` — stay `loading` until the first pull completes, then serve.
|
|
241
|
+
* - `local-only` — never touch the network (no endpoint required).
|
|
242
|
+
*/
|
|
243
|
+
source?: ReadSource;
|
|
244
|
+
}
|
|
245
|
+
interface QueryResult<T> {
|
|
246
|
+
/** Current matching documents from the local replica. Reactive. */
|
|
247
|
+
data: T[];
|
|
248
|
+
/** First local snapshot pending — plus the first pull, for `remote-first`. */
|
|
249
|
+
loading: boolean;
|
|
250
|
+
/** Most recent local-read error. */
|
|
251
|
+
error: unknown | null;
|
|
252
|
+
/** A background replication pass is in flight. */
|
|
253
|
+
syncing: boolean;
|
|
254
|
+
/** Most recent replication error (the local data is still served). */
|
|
255
|
+
syncError: unknown | null;
|
|
256
|
+
/** Trigger a pull now. No-op for `local-only`. */
|
|
257
|
+
refetch: () => Promise<void>;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Bind a component to a slice of a remote origin, backed by the local replica.
|
|
261
|
+
*
|
|
262
|
+
* The read is a live query over the local collection (`useFind`); the network
|
|
263
|
+
* pull writes into that same collection, so the live query re-renders on its
|
|
264
|
+
* own — one-way data flow, no `queryKey`, no `invalidateQueries`. See
|
|
265
|
+
* `docs/scoped-replication.md`.
|
|
266
|
+
*
|
|
267
|
+
* @example
|
|
268
|
+
* const { data, loading, syncing } = useQuery<Product>({
|
|
269
|
+
* collection: 'products',
|
|
270
|
+
* filter: { category: 'kitchen' },
|
|
271
|
+
* pollMs: 30_000,
|
|
272
|
+
* })
|
|
273
|
+
*/
|
|
274
|
+
declare function useQuery<T extends Document>(options: UseQueryOptions<T>): QueryResult<T>;
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Run several scoped queries at once — one replication + live query per entry,
|
|
278
|
+
* in parallel. The result array is index-aligned with `queries`.
|
|
279
|
+
*
|
|
280
|
+
* Each entry is an independent `useQuery`: its own collection, filter, source,
|
|
281
|
+
* and (optionally) endpoint. This is the multi-slice page case — e.g. a
|
|
282
|
+
* dashboard that needs `orders` and `products` from the same origin. There are
|
|
283
|
+
* no cross-collection joins (TalaDB is a document store); compose in the
|
|
284
|
+
* component.
|
|
285
|
+
*
|
|
286
|
+
* v1 note: entries are typed as `Document`. For a strictly-typed single slice
|
|
287
|
+
* use `useQuery<T>`; per-entry generics (tuple typing) are a follow-up.
|
|
288
|
+
*
|
|
289
|
+
* @example
|
|
290
|
+
* const [orders, products] = useQueries([
|
|
291
|
+
* { collection: 'orders', filter: { open: true } },
|
|
292
|
+
* { collection: 'products' },
|
|
293
|
+
* ])
|
|
294
|
+
*/
|
|
295
|
+
declare function useQueries(queries: UseQueryOptions<Document>[]): QueryResult<Document>[];
|
|
296
|
+
|
|
297
|
+
/** A single write intent against the local replica. Discriminated on `type`. */
|
|
298
|
+
type WriteOp<T extends Document> = {
|
|
299
|
+
type: 'insert';
|
|
300
|
+
doc: Omit<T, '_id'>;
|
|
301
|
+
} | {
|
|
302
|
+
type: 'update';
|
|
303
|
+
where: Filter<T>;
|
|
304
|
+
set: Partial<Omit<T, '_id'>>;
|
|
305
|
+
} | {
|
|
306
|
+
type: 'delete';
|
|
307
|
+
where: Filter<T>;
|
|
308
|
+
};
|
|
309
|
+
interface UseMutationOptions extends Partial<Pick<ReplicationConfig, 'endpoint' | 'getAuth' | 'fetch' | 'paths'>> {
|
|
310
|
+
/** Collection the write targets. */
|
|
311
|
+
collection: string;
|
|
312
|
+
/**
|
|
313
|
+
* Replication direction for the drain. `push` *(default)* sends the write;
|
|
314
|
+
* read hooks reconcile the authoritative value on their next pull. `both`
|
|
315
|
+
* also pulls the authoritative echo inline (heavier — replays the collection).
|
|
316
|
+
*/
|
|
317
|
+
direction?: 'push' | 'both';
|
|
318
|
+
/**
|
|
319
|
+
* On mount, attempt to flush any local writes left unsent from a previous
|
|
320
|
+
* (offline) session for this collection. Default `true`.
|
|
321
|
+
*/
|
|
322
|
+
drainOnMount?: boolean;
|
|
323
|
+
}
|
|
324
|
+
interface MutationResult<T extends Document> {
|
|
325
|
+
/** Fire-and-forget write. Errors surface on `error`, never thrown to render. */
|
|
326
|
+
mutate: (op: WriteOp<T>) => void;
|
|
327
|
+
/** Awaitable write. Resolves once the local write and drain settle; rejects on error. */
|
|
328
|
+
mutateAsync: (op: WriteOp<T>) => Promise<void>;
|
|
329
|
+
/** A write (local + drain) is in flight. */
|
|
330
|
+
pending: boolean;
|
|
331
|
+
/** Most recent write/drain error. The local write is durable regardless. */
|
|
332
|
+
error: unknown | null;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Local-first write hook. A mutation writes the local replica **first**
|
|
336
|
+
* (immediate, durable, reactive — every `useQuery`/`useFind` on the collection
|
|
337
|
+
* re-renders) and then replicates the change outward over the sync-contract with
|
|
338
|
+
* bounded retry. The network step never rolls the local write back: it is
|
|
339
|
+
* already committed, and a later drain still delivers it (write-behind).
|
|
340
|
+
*
|
|
341
|
+
* Write-authority is origin-authoritative by default — the push sends the
|
|
342
|
+
* change and the server is the arbiter; read hooks pull the authoritative value.
|
|
343
|
+
*
|
|
344
|
+
* @example
|
|
345
|
+
* const { mutate, pending } = useMutation<Order>({ collection: 'orders' })
|
|
346
|
+
* mutate({ type: 'update', where: { _id }, set: { status: 'shipped' } })
|
|
347
|
+
*/
|
|
348
|
+
declare function useMutation<T extends Document>(options: UseMutationOptions): MutationResult<T>;
|
|
349
|
+
|
|
350
|
+
export { type FindOneResult, type FindResult, type MutationResult, type PrefetchEntry, type PrefetchMode, type PrefetchSlice, type QueryResult, type ReadSource, type ReplicationConfig, ReplicationProvider, type ReplicationProviderProps, TalaDBProvider, type TalaDBProviderProps, type UseMutationOptions, type UseQueryOptions, type WriteOp, useCollection, useFind, useFindOne, useMutation, useQueries, useQuery, useReplicationConfig, useTalaDB };
|