pbtsdb 0.7.3 → 0.8.0

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
@@ -123,14 +123,19 @@ const queryClient = new QueryClient({
123
123
 
124
124
  // Create collections with automatic type inference
125
125
  const c = createCollection<BlogSchema>(pb, queryClient);
126
+ const users = c('users', {});
126
127
  export const { Provider, useStore } = createReactProvider({
128
+ users,
127
129
  posts: c('posts', {
128
- omitOnInsert: ['created', 'updated'] as const
130
+ omitOnInsert: ['created', 'updated'] as const,
131
+ relations: { author: users },
132
+ alwaysExpand: ['author'],
129
133
  }),
130
- users: c('users', {}),
131
134
  comments: c('comments', {
132
- omitOnInsert: ['created', 'updated'] as const
133
- })
135
+ omitOnInsert: ['created', 'updated'] as const,
136
+ relations: { author: users },
137
+ alwaysExpand: ['author'],
138
+ }),
134
139
  });
135
140
 
136
141
  export function App() {
@@ -320,7 +325,8 @@ const collection = c(collectionName: string, options?: CreateCollectionOptions);
320
325
  - `options` - Optional configuration
321
326
 
322
327
  **Options:**
323
- - `expand?: Record<string, Collection>` - Relations to auto-expand and auto-upsert on every fetch
328
+ - `relations?: Record<string, Collection>` - Collections that receive expanded records for each relation; declares what `alwaysExpand` and `collection.expand()` may name
329
+ - `alwaysExpand?: readonly string[]` - Expand paths applied on every fetch (e.g. `['author', 'book.author']`)
324
330
  - `omitOnInsert?: readonly string[]` - Fields to make optional during insert (e.g., `['created', 'updated'] as const`)
325
331
  - `syncMode?: 'eager' | 'on-demand'` - Data fetching strategy (default: `'eager'`)
326
332
  - `onInsert?: InsertMutationFn | false` - Custom insert handler or `false` to disable
@@ -339,40 +345,81 @@ const c = createCollection<MySchema>(pb, queryClient);
339
345
  const booksCollection = c('books', {});
340
346
  ```
341
347
 
342
- With auto-expand relations:
348
+ With always-expanded relations:
343
349
  ```typescript
344
350
  const c = createCollection<MySchema>(pb, queryClient);
345
351
  const authorsCollection = c('authors', {});
346
352
  const booksCollection = c('books', {
347
- expand: {
348
- author: authorsCollection // Auto-expand and auto-upsert
349
- }
353
+ relations: { author: authorsCollection }, // where expanded authors are upserted
354
+ alwaysExpand: ['author'], // expanded on every fetch
350
355
  });
351
356
 
352
- // Expand is automatic on every fetch
353
357
  const { data } = useLiveQuery((q) => q.from({ books: booksCollection }));
358
+ // data[0].expand?.author is typed and populated
359
+ ```
360
+
361
+ #### Per-query expand
362
+
363
+ Declare relations once, then ask for expansion per query with `collection.expand()`.
364
+ A view shares the collection's store, realtime subscription, and mutations; only
365
+ its fetches add the `expand` parameter.
354
366
 
355
- // Expanded records auto-inserted into authorsCollection
367
+ ```typescript
368
+ const tagsCollection = c('tags', {});
369
+ const booksCollection = c('books', {
370
+ relations: { author: authorsCollection, tags: tagsCollection },
371
+ });
372
+
373
+ function BookList() {
374
+ const [books] = useStore('books');
375
+ const { data } = useLiveQuery((q) =>
376
+ q.from({ books: books.expand('tags') })
377
+ .where(({ books }) => eq(books.genre, 'Fiction'))
378
+ );
379
+ // data[0].expand?.tags is Tags[] | undefined; data[0].expand?.author is a type error
380
+ }
356
381
  ```
357
382
 
383
+ Paths can be nested through a target collection's own `relations`:
384
+
385
+ ```typescript
386
+ const metadata = c('book_metadata', { relations: { book: booksCollection } });
387
+ const { data } = useLiveQuery((q) => q.from({ m: metadata.expand('book.author') }));
388
+ // data[0].expand?.book?.expand?.author?.name
389
+ ```
390
+
391
+ Rows fetched through a view keep their `expand` data in the shared store, and the
392
+ realtime subscription requests every relation in use, so echoes keep it populated.
393
+
394
+ Relation targets stay live too. While a query expands into `tags` (or through
395
+ it into `colors`), those collections keep their realtime subscriptions, and a
396
+ change to a tag or a color updates the embedded copy on affected rows in
397
+ place, so `book.expand?.tags?.[0].name` refreshes without a write to the book.
398
+ While an optimistic mutation is pending on the parent row, TanStack DB shows
399
+ the frozen optimistic snapshot, so a patched `expand` becomes visible once
400
+ that mutation settles. Deleting a related record clears the reference and the
401
+ copy, matching what PocketBase does server-side. Targets are released when
402
+ the last query on the parent unmounts.
403
+
358
404
  #### Collection Options Passthrough
359
405
 
360
406
  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:
361
407
 
362
408
  ```typescript
363
- import { BasicIndex } from 'pbtsdb';
364
-
365
409
  const c = createCollection<MySchema>(pb, queryClient);
366
410
  const booksCollection = c('books', {
367
411
  collectionOptions: {
368
- autoIndex: 'eager',
369
- defaultIndexType: BasicIndex,
370
412
  gcTime: 60000, // 1 minute GC
371
413
  startSync: true, // Start syncing immediately
372
414
  }
373
415
  });
374
416
  ```
375
417
 
418
+ pbtsdb defaults `autoIndex` to `'eager'` with `defaultIndexType: BTreeIndex`, so
419
+ `orderBy` + `limit` queries page lazily instead of loading the whole subset (and
420
+ TanStack DB does not warn about a missing index). Pass `autoIndex: 'off'` or a
421
+ different `defaultIndexType` in `collectionOptions` to change that per collection.
422
+
376
423
  The following fields are managed by pbtsdb and excluded from `collectionOptions`: `getKey`, `syncMode`, `onInsert`, `onUpdate`, `onDelete`, `schema`.
377
424
 
378
425
  ### React Integration
@@ -890,7 +937,8 @@ Use PocketBase's `expand` to auto-populate a related collection, then use includ
890
937
  const authorsCollection = c('authors', { syncMode: 'on-demand' });
891
938
  const booksCollection = c('books', {
892
939
  syncMode: 'on-demand',
893
- expand: { author: authorsCollection }, // Auto-populates authorsCollection
940
+ relations: { author: authorsCollection }, // Auto-populates authorsCollection
941
+ alwaysExpand: ['author'],
894
942
  });
895
943
 
896
944
  const { data } = useLiveQuery((q) =>
@@ -944,12 +992,11 @@ const books = c('books', {
944
992
  omitOnInsert: ['created', 'updated'] as const
945
993
  });
946
994
 
947
- // ✅ Good - with auto-expand relations
995
+ // ✅ Good - with always-expanded relations
948
996
  const authors = c('authors', {});
949
997
  const books = c('books', {
950
- expand: {
951
- author: authors
952
- }
998
+ relations: { author: authors },
999
+ alwaysExpand: ['author'],
953
1000
  });
954
1001
  ```
955
1002
 
@@ -979,16 +1026,16 @@ When using expand collections, create the target collection first:
979
1026
  const c = createCollection<MySchema>(pb, queryClient);
980
1027
  const authors = c('authors', {});
981
1028
  const books = c('books', {
982
- expand: {
983
- author: authors // authors is already created
984
- }
1029
+ relations: { author: authors }, // authors is already created
1030
+ alwaysExpand: ['author'],
985
1031
  });
986
1032
 
987
1033
  // ❌ Bad - can't reference what doesn't exist yet
988
1034
  const books = c('books', {
989
- expand: {
1035
+ relations: {
990
1036
  author: ??? // Where is authors?
991
- }
1037
+ },
1038
+ alwaysExpand: ['author'],
992
1039
  });
993
1040
  ```
994
1041
 
@@ -1030,9 +1077,8 @@ Use PocketBase's expand feature for better performance:
1030
1077
  const c = createCollection<MySchema>(pb, queryClient);
1031
1078
  const authors = c('authors', {});
1032
1079
  const posts = c('posts', {
1033
- expand: {
1034
- author: authors // Auto-expand on every fetch
1035
- }
1080
+ relations: { author: authors },
1081
+ alwaysExpand: ['author'], // Auto-expand on every fetch
1036
1082
  });
1037
1083
 
1038
1084
  const { data } = useLiveQuery((q) => q.from({ posts }));