pbtsdb 0.11.0 → 2.0.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/README.md +146 -57
- package/dist/chunk-TTENGDS3.js +3142 -0
- package/dist/chunk-TTENGDS3.js.map +1 -0
- package/dist/core.d.ts +232 -86
- package/dist/core.js +1 -1
- package/dist/index.d.ts +25 -11
- package/dist/index.js +11 -3
- package/dist/index.js.map +1 -1
- package/llms.txt +29 -25
- package/package.json +6 -11
- package/dist/chunk-OBDOVDCI.js +0 -1596
- package/dist/chunk-OBDOVDCI.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
# pbtsdb: PocketBase TanStack Database Integration
|
|
2
2
|
|
|
3
|
-
> Type-safe PocketBase integration with TanStack
|
|
3
|
+
> Type-safe PocketBase integration with TanStack DB
|
|
4
4
|
|
|
5
|
-
A TypeScript library that seamlessly integrates [PocketBase](https://pocketbase.io) with [TanStack
|
|
5
|
+
A TypeScript library that seamlessly integrates [PocketBase](https://pocketbase.io) with [TanStack DB](https://tanstack.com/db), providing:
|
|
6
6
|
|
|
7
7
|
- 🔥 **Real-time subscriptions** with automatic synchronization
|
|
8
8
|
- 🎯 **Full TypeScript type safety** for queries and relations
|
|
9
9
|
- ⚡ **Reactive collections** with TanStack DB
|
|
10
|
-
- 🔄 **Automatic caching** via TanStack Query
|
|
11
10
|
- ✨ **Optimistic mutations** with insert/update/delete support
|
|
12
11
|
- 🎨 **React hooks** for easy component integration
|
|
13
12
|
- 🔗 **Type-safe joins** and relation fetching into their own collections
|
|
@@ -22,6 +21,7 @@ A TypeScript library that seamlessly integrates [PocketBase](https://pocketbase.
|
|
|
22
21
|
- [Related data](#related-data)
|
|
23
22
|
- [React Integration](#react-integration)
|
|
24
23
|
- [Subscriptions](#subscriptions)
|
|
24
|
+
- [Sync Status API](#sync-status-api)
|
|
25
25
|
- [Utility Functions](#utility-functions)
|
|
26
26
|
- [Usage Examples](#usage-examples)
|
|
27
27
|
- [Includes (Nested Subqueries)](#includes-nested-subqueries)
|
|
@@ -32,16 +32,14 @@ A TypeScript library that seamlessly integrates [PocketBase](https://pocketbase.
|
|
|
32
32
|
## Installation
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
|
-
npm install pbtsdb pocketbase @tanstack/db @tanstack/
|
|
35
|
+
npm install pbtsdb pocketbase @tanstack/db @tanstack/react-db
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
### Peer Dependencies
|
|
39
39
|
|
|
40
40
|
- `pocketbase` >= 0.22.0
|
|
41
|
-
- `@tanstack/db` >= 0.
|
|
42
|
-
- `@tanstack/
|
|
43
|
-
- `@tanstack/react-query` >= 5.0.0
|
|
44
|
-
- `@tanstack/react-db` >= 0.1.86 (optional; only for `createReactProvider`)
|
|
41
|
+
- `@tanstack/db` >= 0.12.1
|
|
42
|
+
- `@tanstack/react-db` >= 0.5.5 (optional; only for `createReactProvider`)
|
|
45
43
|
- `react` and `react-dom` >= 18.0.0 (optional)
|
|
46
44
|
|
|
47
45
|
All peer dependencies use minimum version constraints; newer versions should work. The
|
|
@@ -110,18 +108,12 @@ type BlogSchema = {
|
|
|
110
108
|
```typescript
|
|
111
109
|
// app.tsx
|
|
112
110
|
import PocketBase from 'pocketbase';
|
|
113
|
-
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
114
111
|
import { createCollection, createReactProvider } from 'pbtsdb';
|
|
115
112
|
|
|
116
113
|
const pb = new PocketBase('http://localhost:8090');
|
|
117
|
-
const queryClient = new QueryClient({
|
|
118
|
-
defaultOptions: {
|
|
119
|
-
queries: { staleTime: 60_000 } // Cache for 1 minute
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
114
|
|
|
123
115
|
// Create collections with automatic type inference
|
|
124
|
-
const c = createCollection<BlogSchema>(pb
|
|
116
|
+
const c = createCollection<BlogSchema>(pb);
|
|
125
117
|
const users = c('users', {});
|
|
126
118
|
export const { Provider, useStore } = createReactProvider({
|
|
127
119
|
users,
|
|
@@ -139,11 +131,9 @@ export const { Provider, useStore } = createReactProvider({
|
|
|
139
131
|
|
|
140
132
|
export function App() {
|
|
141
133
|
return (
|
|
142
|
-
<
|
|
143
|
-
<
|
|
144
|
-
|
|
145
|
-
</Provider>
|
|
146
|
-
</QueryClientProvider>
|
|
134
|
+
<Provider>
|
|
135
|
+
<BlogDashboard />
|
|
136
|
+
</Provider>
|
|
147
137
|
);
|
|
148
138
|
}
|
|
149
139
|
```
|
|
@@ -253,14 +243,13 @@ Collections are reactive data stores that automatically sync with PocketBase:
|
|
|
253
243
|
|
|
254
244
|
```typescript
|
|
255
245
|
// Create a collection using the curried API
|
|
256
|
-
const c = createCollection<MySchema>(pb
|
|
246
|
+
const c = createCollection<MySchema>(pb);
|
|
257
247
|
const booksCollection = c('books', {});
|
|
258
248
|
|
|
259
249
|
// Collections automatically:
|
|
260
250
|
// - Fetch data from PocketBase
|
|
261
251
|
// - Subscribe to real-time updates
|
|
262
252
|
// - Update React components when data changes
|
|
263
|
-
// - Cache data via TanStack Query
|
|
264
253
|
```
|
|
265
254
|
|
|
266
255
|
### Real-time Subscriptions
|
|
@@ -269,7 +258,7 @@ Collections manage subscriptions **automatically** based on query lifecycle:
|
|
|
269
258
|
|
|
270
259
|
```typescript
|
|
271
260
|
// Collections are lazy - no subscription until queried
|
|
272
|
-
const c = createCollection<MySchema>(pb
|
|
261
|
+
const c = createCollection<MySchema>(pb);
|
|
273
262
|
const booksCollection = c('books', {});
|
|
274
263
|
|
|
275
264
|
// Subscription starts automatically when query becomes active
|
|
@@ -286,6 +275,74 @@ const { data } = useLiveQuery((q) =>
|
|
|
286
275
|
- **Shared:** Multiple components using the same collection share one subscription
|
|
287
276
|
- **No manual control needed:** The collection handles all subscription management internally
|
|
288
277
|
|
|
278
|
+
### Reconnects
|
|
279
|
+
|
|
280
|
+
pbtsdb runs its own realtime connection. When the connection drops, it reconnects with backoff and refetches every live query, because PocketBase does not replay events missed during the gap. A server that supports pbtsdb's resume extension (`?resume=<clientId>&after=<seq>` on the SSE URL, `resumed: true` in `PB_CONNECT`, `seq` on each event) replays the gap instead, and no refetch runs.
|
|
281
|
+
|
|
282
|
+
pbtsdb's connection is independent of `pb.realtime`. When `pb.authStore` changes to another auth record (login, logout, switching users), pbtsdb forgets the connection's server-side session and reconnects at once, re-sending every subscribed topic under the new auth; every ready collection reloads once that POST succeeds, the same as a non-resumed reconnect. A token refresh for the same record changes nothing. PocketBase would otherwise answer the next subscriptions POST with a 403, or keep serving the previous user's data.
|
|
283
|
+
|
|
284
|
+
Call `resetRealtime(pb)` yourself for a change the auth store cannot see, such as pointing `pb` at another server:
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
import { disconnectRealtime, resetRealtime } from 'pbtsdb';
|
|
288
|
+
|
|
289
|
+
resetRealtime(pb); // forget the session and re-subscribe every open topic now
|
|
290
|
+
|
|
291
|
+
disconnectRealtime(pb); // close the connection and keep it closed
|
|
292
|
+
resetRealtime(pb); // open it again; every ready collection reloads
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
`disconnectRealtime(pb)` closes the connection and keeps it closed until `resetRealtime(pb)`. Collections keep working over REST and keep their subscriptions registered. Use it at logout, or at startup where realtime is not wanted, such as an embedded view.
|
|
296
|
+
|
|
297
|
+
### Sync Status
|
|
298
|
+
|
|
299
|
+
Two problems are visible only to pbtsdb: the realtime stream dropping while REST still works, so lists look fine but stop updating; and a query sleeping in its load retry backoff, so a list sits in "loading" with no reason shown. `getSyncStatus(pb)` reports both, for every collection of one client:
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
import { getSyncStatus, subscribeSyncStatus } from 'pbtsdb';
|
|
303
|
+
|
|
304
|
+
const status = getSyncStatus(pb);
|
|
305
|
+
// {
|
|
306
|
+
// realtime:
|
|
307
|
+
// | { state: 'disabled' } // disconnectRealtime(pb), or no collection has subscribed yet
|
|
308
|
+
// | { state: 'connecting' } // the first connection of a session is opening
|
|
309
|
+
// | { state: 'connected' } // stream open, PB_CONNECT received
|
|
310
|
+
// | { state: 'reconnecting'; attempt: number; nextRetryAt: number; since: number },
|
|
311
|
+
// loads: {
|
|
312
|
+
// retrying: number, // live queries sleeping in loadRetryDelays backoff
|
|
313
|
+
// failingSince?: number, // when the oldest of them first failed, epoch ms
|
|
314
|
+
// failed: number, // live queries whose load ended in a 4xx that is not retried
|
|
315
|
+
// },
|
|
316
|
+
// }
|
|
317
|
+
|
|
318
|
+
const stop = subscribeSyncStatus(pb, status => console.log(status));
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
`reconnecting` means the stream was up and dropped, or a retry never came up, and the next retry is scheduled; the first connection of a session reports `connecting` instead. `nextRetryAt` and `since` are epoch milliseconds, and `nextRetryAt` changes only when a new retry is scheduled. `disabled` is not a problem state: it is what `disconnectRealtime(pb)` asks for, and what an app sees before login.
|
|
322
|
+
|
|
323
|
+
`retrying` counts only demands a live query still holds; a parked subset or an unmounted query is never counted. `failed` is separate because a 403 or 404 is an answer, not an outage; the count drops when the query reloads or unmounts.
|
|
324
|
+
|
|
325
|
+
The snapshot is stable: `getSyncStatus(pb)` returns the same object, with the same nested objects, until a value changes. The React entry point wraps it in `useSyncStatus(pb)`:
|
|
326
|
+
|
|
327
|
+
```tsx
|
|
328
|
+
import { useSyncStatus } from 'pbtsdb';
|
|
329
|
+
|
|
330
|
+
function SyncNotice() {
|
|
331
|
+
const { realtime, loads } = useSyncStatus(pb);
|
|
332
|
+
if (realtime.state === 'reconnecting') return <p>Live updates paused, reconnecting…</p>;
|
|
333
|
+
if (loads.retrying > 0) return <p>Retrying {loads.retrying} queries…</p>;
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
The shape of `SyncStatus` is public and changes only in a major release.
|
|
339
|
+
|
|
340
|
+
### Subset Lifetime
|
|
341
|
+
|
|
342
|
+
In on-demand mode a live query's subset is released when the last subscriber unmounts, after a grace window of `subsetGcTime` milliseconds (default 5000). A query with an equal request that mounts within the window reuses the rows with no request. Realtime keeps the parked rows fresh, and rows a parent filed through a relation stay for the same window, so a panel that mounts and unmounts quickly costs no refetch. `collection.reload()` releases every parked subset. An auth change releases every waiting subset and every accepted row at once and reloads every live subset under the new auth, whether or not realtime is connected, so a load right after a logout never reuses the previous user's rows and the order of `authStore.clear()` and `disconnectRealtime(pb)` does not matter. Set `subsetGcTime: 0` to release a subset as soon as it unloads; `0` also keeps accepted rows until a reload or idle instead of expiring them.
|
|
343
|
+
|
|
344
|
+
A subset load that fails is retried with backoff (`loadRetryDelays`, default 1, 2, 4, 8, 15 and then every 30 seconds) until it succeeds, so a live query stays loading through an outage instead of entering an error state it cannot leave. A `collection.reload()` or a realtime reconnect retries at once, also while another reload is running. A response the server gave on purpose, a 4xx other than 401, 408 or 429, is not retried and reports its error. A 401 waits for the next auth change and then retries. Delays carry a little jitter so many queries do not retry in lockstep.
|
|
345
|
+
|
|
289
346
|
### Sync Modes
|
|
290
347
|
|
|
291
348
|
Every collection is either **eager** (the default) or **on-demand**:
|
|
@@ -331,9 +388,9 @@ builder still loads the first `n + limit` rows. Put a page boundary in the
|
|
|
331
388
|
|
|
332
389
|
### Mutations and Refetch
|
|
333
390
|
|
|
334
|
-
|
|
391
|
+
The built-in handlers write PocketBase's response into the collection before they return: server-assigned fields like `created` and `updated`, and any values rewritten by PocketBase hooks. A built-in delete removes the row the same way. TanStack DB drops a mutation's optimistic state when its handler settles, and rows written while the handler runs publish together with that drop, so the settled row is the server's row with no gap. The realtime echo that follows changes nothing.
|
|
335
392
|
|
|
336
|
-
Set `refetchOnMutation: true` to
|
|
393
|
+
By default, pbtsdb does **not** refetch after a successful insert, update, or delete. Set `refetchOnMutation: true` to refetch the collection's active queries before the handler settles — for example, when a server-side hook changes other rows you must read right after the mutation:
|
|
337
394
|
|
|
338
395
|
```typescript
|
|
339
396
|
const collection = c('books', {
|
|
@@ -341,7 +398,23 @@ const collection = c('books', {
|
|
|
341
398
|
});
|
|
342
399
|
```
|
|
343
400
|
|
|
344
|
-
The option only affects the built-in default handlers.
|
|
401
|
+
The option only affects the built-in default handlers. A custom `onInsert`, `onUpdate`, or `onDelete` controls write-back itself:
|
|
402
|
+
|
|
403
|
+
- TanStack DB drops the optimistic state when your handler returns. Until the realtime echo arrives, the row shows its previous server value: an updated row reverts, an inserted row disappears, and a deleted row comes back.
|
|
404
|
+
- For an insert or an update, `await collection.accept(serverRows)` before you return. This closes the gap.
|
|
405
|
+
- For a delete, `await collection.evict(ids)` before you return. Without it, the deleted row shows again until the delete echo arrives.
|
|
406
|
+
- In on-demand mode, when realtime goes idle (no live query on the collection), rows held only by an accepted write-back are released. A row written back while nothing is mounted is fetched again on the next mount.
|
|
407
|
+
- The handler returns `void`. Core no longer reads a `{ refetch }` result; call `collection.reload()` yourself if the handler needs a refetch.
|
|
408
|
+
|
|
409
|
+
### Writing server rows yourself
|
|
410
|
+
|
|
411
|
+
`collection.accept(rows)` lands rows the server returned, for example the response of a custom endpoint, as confirmed state. A row older than the stored one is ignored. Use it when the screen must update before the realtime echo arrives. It resolves when the rows are accepted, so a custom mutation handler can await it.
|
|
412
|
+
|
|
413
|
+
`collection.evict(ids)` removes rows the server deleted, for example after a custom endpoint deleted them. The rows leave every holder, and a fetch in flight does not put them back. It resolves when the removal is accepted, so a custom delete handler can await it.
|
|
414
|
+
|
|
415
|
+
`collection.reload()` refetches every live query's subset. Then it releases the realtime-topic and accepted holders of the rows the results do not confirm. A row that a parent filed through a relation stays. Use it when the server state changed with no realtime event, such as after a user loses access to rows.
|
|
416
|
+
|
|
417
|
+
All three are also on `collection.utils`.
|
|
345
418
|
|
|
346
419
|
### Type Safety
|
|
347
420
|
|
|
@@ -367,7 +440,6 @@ The main function for creating type-safe collections. Uses a curried API for bet
|
|
|
367
440
|
```typescript
|
|
368
441
|
const c = createCollection<Schema>(
|
|
369
442
|
pb: PocketBase,
|
|
370
|
-
queryClient: QueryClient,
|
|
371
443
|
factoryOptions?: CreateCollectionFactoryOptions
|
|
372
444
|
);
|
|
373
445
|
const collection = c(collectionName: string, options?: CreateCollectionOptions);
|
|
@@ -375,7 +447,6 @@ const collection = c(collectionName: string, options?: CreateCollectionOptions);
|
|
|
375
447
|
|
|
376
448
|
**Parameters:**
|
|
377
449
|
- `pb` - PocketBase instance
|
|
378
|
-
- `queryClient` - TanStack Query QueryClient instance
|
|
379
450
|
- `factoryOptions` - Optional configuration applied to every collection this factory builds (see [Subscription Options](#subscription-options))
|
|
380
451
|
- `collectionName` - Name of the PocketBase collection
|
|
381
452
|
- `options` - Optional configuration
|
|
@@ -390,7 +461,6 @@ const collection = c(collectionName: string, options?: CreateCollectionOptions);
|
|
|
390
461
|
- `onUpdate?: UpdateMutationFn | false` - Custom update handler or `false` to disable
|
|
391
462
|
- `onDelete?: DeleteMutationFn | false` - Custom delete handler or `false` to disable
|
|
392
463
|
- `refetchOnMutation?: boolean` - Refetch the collection after a built-in insert/update/delete succeeds (default: `false`; see [Mutations and Refetch](#mutations-and-refetch))
|
|
393
|
-
- `ignoreAutoCancellation?: boolean` - Ignore PocketBase auto-cancellation errors (default: `true`)
|
|
394
464
|
- `collectionOptions?: object` - Additional TanStack DB collection options passed through directly (see [Collection Options Passthrough](#collection-options-passthrough))
|
|
395
465
|
|
|
396
466
|
**Returns:** Fully-typed Collection instance with subscription capabilities
|
|
@@ -399,7 +469,7 @@ const collection = c(collectionName: string, options?: CreateCollectionOptions);
|
|
|
399
469
|
|
|
400
470
|
Basic collection (lazy, subscribes automatically on first query):
|
|
401
471
|
```typescript
|
|
402
|
-
const c = createCollection<MySchema>(pb
|
|
472
|
+
const c = createCollection<MySchema>(pb);
|
|
403
473
|
const booksCollection = c('books', {});
|
|
404
474
|
```
|
|
405
475
|
|
|
@@ -410,7 +480,7 @@ collections. Rows never carry `expand`; read related records from the target
|
|
|
410
480
|
collection.
|
|
411
481
|
|
|
412
482
|
```typescript
|
|
413
|
-
const c = createCollection<MySchema>(pb
|
|
483
|
+
const c = createCollection<MySchema>(pb);
|
|
414
484
|
const authors = c('authors', { syncMode: 'on-demand' });
|
|
415
485
|
const tags = c('tags', { syncMode: 'on-demand' });
|
|
416
486
|
const books = c('books', {
|
|
@@ -421,8 +491,8 @@ const books = c('books', {
|
|
|
421
491
|
|
|
422
492
|
Every books request expands `author`; the expanded authors are filed into
|
|
423
493
|
`authors` (an on-demand target has its sync started) and removed from the
|
|
424
|
-
book rows. Read them through `materialize()` in a query
|
|
425
|
-
`
|
|
494
|
+
book rows. Read them through `materialize()` in a query or through a join,
|
|
495
|
+
inside the `useLiveQuery` that needs them:
|
|
426
496
|
|
|
427
497
|
```typescript
|
|
428
498
|
import { eq } from '@tanstack/db';
|
|
@@ -446,6 +516,14 @@ reads through a `fetchRelations()` view, whose fetch goes to PocketBase so its
|
|
|
446
516
|
paths get filed; anything else is fetched in one batched request. An empty
|
|
447
517
|
`inArray(id, [])` yields no rows and no request.
|
|
448
518
|
|
|
519
|
+
Do not read a related row with `authors.get(book.author)` in a component. A
|
|
520
|
+
`get()` is a one-time read: the component does not re-render when the author
|
|
521
|
+
changes, and the row can leave the store while the component still shows it,
|
|
522
|
+
because a filed row stays only as long as a live query holds the parent row
|
|
523
|
+
that filed it. A `useLiveQuery` with `materialize()` or a join holds the rows
|
|
524
|
+
it reads and re-renders when they change. `get()` is for code outside React
|
|
525
|
+
that needs a value right now, such as a mutation handler or a test.
|
|
526
|
+
|
|
449
527
|
Fetch a relation for one query only with `fetchRelations()`; the view shares the
|
|
450
528
|
collection's store, realtime subscription, and mutations, and only its fetches
|
|
451
529
|
add the `expand` parameter:
|
|
@@ -490,7 +568,7 @@ never treated as complete.
|
|
|
490
568
|
Pass any [TanStack DB `BaseCollectionConfig`](https://tanstack.com/db/latest/docs/overview) option directly via `collectionOptions`. This is useful for configuring indexing, garbage collection, and other collection-level settings:
|
|
491
569
|
|
|
492
570
|
```typescript
|
|
493
|
-
const c = createCollection<MySchema>(pb
|
|
571
|
+
const c = createCollection<MySchema>(pb);
|
|
494
572
|
const booksCollection = c('books', {
|
|
495
573
|
collectionOptions: {
|
|
496
574
|
gcTime: 60000, // 1 minute GC
|
|
@@ -504,7 +582,7 @@ pbtsdb defaults `autoIndex` to `'eager'` with `defaultIndexType: BTreeIndex`, so
|
|
|
504
582
|
TanStack DB does not warn about a missing index). Pass `autoIndex: 'off'` or a
|
|
505
583
|
different `defaultIndexType` in `collectionOptions` to change that per collection.
|
|
506
584
|
|
|
507
|
-
The following fields are managed by pbtsdb and excluded from `collectionOptions`: `getKey`, `syncMode`, `onInsert`, `onUpdate`, `onDelete`, `schema`.
|
|
585
|
+
The following fields are managed by pbtsdb and excluded from `collectionOptions`: `getKey`, `syncMode`, `onInsert`, `onUpdate`, `onDelete`, `schema`, `utils`.
|
|
508
586
|
|
|
509
587
|
### React Integration
|
|
510
588
|
|
|
@@ -527,7 +605,7 @@ const { Provider, useStore } = createReactProvider(collections: CollectionsMap);
|
|
|
527
605
|
```typescript
|
|
528
606
|
import { createCollection, createReactProvider } from 'pbtsdb';
|
|
529
607
|
|
|
530
|
-
const c = createCollection<MySchema>(pb
|
|
608
|
+
const c = createCollection<MySchema>(pb);
|
|
531
609
|
const collections = {
|
|
532
610
|
authors: c('authors', {}),
|
|
533
611
|
books: c('books', {
|
|
@@ -685,7 +763,7 @@ options — `headers`, `filter`, `expand`, `fields` — to every real-time
|
|
|
685
763
|
subscription the factory creates.
|
|
686
764
|
|
|
687
765
|
```typescript
|
|
688
|
-
const c = createCollection<Schema>(pb,
|
|
766
|
+
const c = createCollection<Schema>(pb, {
|
|
689
767
|
subscribeOptions: () => {
|
|
690
768
|
const token = getShareToken();
|
|
691
769
|
return token ? { headers: { 'X-Share-Token': token } } : undefined;
|
|
@@ -717,6 +795,32 @@ An `expand` you add here is yours: pbtsdb strips only the paths it requested
|
|
|
717
795
|
through `alwaysFetchRelations` and `fetchRelations()`, so records expanded by
|
|
718
796
|
this option stay on the echoed rows, untyped.
|
|
719
797
|
|
|
798
|
+
### Sync Status API
|
|
799
|
+
|
|
800
|
+
#### getSyncStatus()
|
|
801
|
+
|
|
802
|
+
```typescript
|
|
803
|
+
getSyncStatus(pb: PocketBase): SyncStatus
|
|
804
|
+
```
|
|
805
|
+
|
|
806
|
+
The current [sync status](#sync-status) of `pb`'s collections. The returned object is stable until a value changes.
|
|
807
|
+
|
|
808
|
+
#### subscribeSyncStatus()
|
|
809
|
+
|
|
810
|
+
```typescript
|
|
811
|
+
subscribeSyncStatus(pb: PocketBase, listener: (status: SyncStatus) => void): () => void
|
|
812
|
+
```
|
|
813
|
+
|
|
814
|
+
Calls `listener` with each new snapshot. Returns the unsubscribe function.
|
|
815
|
+
|
|
816
|
+
#### useSyncStatus()
|
|
817
|
+
|
|
818
|
+
```typescript
|
|
819
|
+
useSyncStatus(pb: PocketBase): SyncStatus
|
|
820
|
+
```
|
|
821
|
+
|
|
822
|
+
React hook over the two functions above, built on `useSyncExternalStore`. Exported from `pbtsdb`, not from `pbtsdb/core`.
|
|
823
|
+
|
|
720
824
|
### Utility Functions
|
|
721
825
|
|
|
722
826
|
#### newRecordId()
|
|
@@ -968,7 +1072,7 @@ export function CreateBookForm() {
|
|
|
968
1072
|
try {
|
|
969
1073
|
// Optimistic insert - appears instantly
|
|
970
1074
|
const tx = books.insert({ id: newRecordId(), title, author: 'author_id' });
|
|
971
|
-
await tx.
|
|
1075
|
+
await tx.when('settled');
|
|
972
1076
|
|
|
973
1077
|
if (tx.state === 'completed') setTitle('');
|
|
974
1078
|
else setError('Failed to create book');
|
|
@@ -1143,7 +1247,7 @@ Always create collections with proper type parameters:
|
|
|
1143
1247
|
|
|
1144
1248
|
```typescript
|
|
1145
1249
|
// ✅ Good - full type safety
|
|
1146
|
-
const c = createCollection<MySchema>(pb
|
|
1250
|
+
const c = createCollection<MySchema>(pb);
|
|
1147
1251
|
const books = c('books', {
|
|
1148
1252
|
omitOnInsert: ['created', 'updated'] as const
|
|
1149
1253
|
});
|
|
@@ -1164,7 +1268,7 @@ Define all collections once at app initialization:
|
|
|
1164
1268
|
|
|
1165
1269
|
```typescript
|
|
1166
1270
|
// ✅ Do this - centralized, type-safe
|
|
1167
|
-
const c = createCollection<MySchema>(pb
|
|
1271
|
+
const c = createCollection<MySchema>(pb);
|
|
1168
1272
|
|
|
1169
1273
|
export const { Provider, useStore } = createReactProvider({
|
|
1170
1274
|
posts: c('posts', { omitOnInsert: ['created', 'updated'] as const }),
|
|
@@ -1179,7 +1283,7 @@ When declaring `relations`, create the target collection first:
|
|
|
1179
1283
|
|
|
1180
1284
|
```typescript
|
|
1181
1285
|
// ✅ Good - authors exists before books references it
|
|
1182
|
-
const c = createCollection<MySchema>(pb
|
|
1286
|
+
const c = createCollection<MySchema>(pb);
|
|
1183
1287
|
const authors = c('authors', {});
|
|
1184
1288
|
const books = c('books', {
|
|
1185
1289
|
relations: { author: authors }, // authors is already created
|
|
@@ -1230,7 +1334,7 @@ return <PostsList posts={data} />;
|
|
|
1230
1334
|
per parent row, on every fetch of the parent:
|
|
1231
1335
|
|
|
1232
1336
|
```typescript
|
|
1233
|
-
const c = createCollection<MySchema>(pb
|
|
1337
|
+
const c = createCollection<MySchema>(pb);
|
|
1234
1338
|
const authors = c('authors', {});
|
|
1235
1339
|
const posts = c('posts', {
|
|
1236
1340
|
relations: { author: authors },
|
|
@@ -1246,20 +1350,6 @@ it) and, once the rows are filed, subsequent queries make no request at all.
|
|
|
1246
1350
|
Prefer `alwaysFetchRelations` when the parent is the only path by which those
|
|
1247
1351
|
rows enter an on-demand collection; otherwise let the query load them.
|
|
1248
1352
|
|
|
1249
|
-
### 6. Configure QueryClient Defaults
|
|
1250
|
-
|
|
1251
|
-
```typescript
|
|
1252
|
-
const queryClient = new QueryClient({
|
|
1253
|
-
defaultOptions: {
|
|
1254
|
-
queries: {
|
|
1255
|
-
staleTime: 60_000, // 1 minute
|
|
1256
|
-
gcTime: 300_000, // 5 minutes
|
|
1257
|
-
refetchOnWindowFocus: false
|
|
1258
|
-
}
|
|
1259
|
-
}
|
|
1260
|
-
});
|
|
1261
|
-
```
|
|
1262
|
-
|
|
1263
1353
|
## Configuration
|
|
1264
1354
|
|
|
1265
1355
|
### Custom Logger Integration
|
|
@@ -1381,6 +1471,5 @@ npm run typecheck # TypeScript only
|
|
|
1381
1471
|
|
|
1382
1472
|
**Built with:**
|
|
1383
1473
|
- [PocketBase](https://pocketbase.io) - Backend-as-a-Service
|
|
1384
|
-
- [TanStack Query](https://tanstack.com/query) - Powerful data fetching
|
|
1385
1474
|
- [TanStack DB](https://tanstack.com/db) - Reactive database
|
|
1386
1475
|
- [TypeScript](https://www.typescriptlang.org) - Type safety
|