Skip to content

Commit 8b7fb1a

Browse files
KyleAMathewsclaudeautofix-ci[bot]
authored
docs: Add db-core/persistence skill and update library version to 0.6.0 (#1421)
* docs: update react-db SKILL.md for virtual props and version bump Add virtual properties section ($synced, $origin, $key, $collectionId) from upstream live-queries guide and bump library_version to 0.6.0. https://claude.ai/code/session_01DTyMUCKyRTwuDSwEbhfW46 * docs: update all SKILL.md files for latest source changes - Bump library_version to 0.6.0 across all skills - live-queries: add virtual properties, includes subqueries, toArray/concat - collection-setup: add on-demand sync mode, opt-in indexing, queryKey prefix convention, Electric timeout fix - custom-adapter: add persisted sync metadata API (row + collection) - solid-db: add findOne single-result support - react-db was updated in prior commit https://claude.ai/code/session_01DTyMUCKyRTwuDSwEbhfW46 * docs: add queryOnce and createEffect to live-queries SKILL.md Both features are documented in the source doc (docs/guides/live-queries.md) but were missing from the SKILL. Found by cross-referencing with the 0.6 release blog post. https://claude.ai/code/session_01DTyMUCKyRTwuDSwEbhfW46 * docs: add persistence SKILL.md for SQLite-backed collection persistence New sub-skill covering persistedCollectionOptions, all 8 platform adapters (browser, React Native, Expo, Electron, Node, Capacitor, Tauri, Cloudflare DO), multi-tab coordination, schema versioning, and local-only vs synced modes. Also updates the db-core index to reference the new skill. https://claude.ai/code/session_01DTyMUCKyRTwuDSwEbhfW46 * ci: apply automated fixes * fix: correct documentation accuracy and add tanstack-intent keyword - Fix Electron persistence API examples to match actual function signatures - Fix missing id behavior: documents silent UUID generation instead of false error claim - Restore progressive syncMode (Electric-only) that was incorrectly removed - Clarify metadata availability: always provided, durable only with persistence - Add tanstack-intent keyword to all packages with skills - Fix stale version reference and align wording consistency Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: add changeset for skill docs update and tanstack-intent keyword Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * ci: apply automated fixes * docs: move adapter-specific content to reference docs Move PowerSync on-demand sync details to powersync-adapter.md reference and function-based queryKey mistake to query-adapter.md reference. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent e7637a8 commit 8b7fb1a

File tree

23 files changed

+605
-42
lines changed

23 files changed

+605
-42
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'@tanstack/db': patch
3+
'@tanstack/react-db': patch
4+
'@tanstack/angular-db': patch
5+
'@tanstack/solid-db': patch
6+
'@tanstack/svelte-db': patch
7+
'@tanstack/vue-db': patch
8+
'@tanstack/offline-transactions': patch
9+
---
10+
11+
Update all SKILL.md files to v0.6.0 with new documentation for persistence, virtual properties, queryOnce, createEffect, includes, indexing, and sync metadata. Add tanstack-intent keyword to all packages with skills.

packages/angular-db/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
"keywords": [
1414
"optimistic",
1515
"angular",
16-
"typescript"
16+
"typescript",
17+
"tanstack-intent"
1718
],
1819
"scripts": {
1920
"build": "vite build",

packages/angular-db/skills/angular-db/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ description: >
1010
type: framework
1111
library: db
1212
framework: angular
13-
library_version: '0.5.30'
13+
library_version: '0.6.0'
1414
requires:
1515
- db-core
1616
sources:

packages/db/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
"homepage": "https://tanstack.com/db",
1313
"keywords": [
1414
"optimistic",
15-
"typescript"
15+
"typescript",
16+
"tanstack-intent"
1617
],
1718
"scripts": {
1819
"build": "vite build",

packages/db/skills/db-core/SKILL.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ description: >
1010
createPacedMutations. Entry point for all TanStack DB skills.
1111
type: core
1212
library: db
13-
library_version: '0.5.30'
13+
library_version: '0.6.0'
1414
---
1515

1616
# TanStack DB — Core Concepts
@@ -33,6 +33,7 @@ hooks. In framework projects, import from the framework package directly.
3333
| Query data with where, join, groupBy, select | db-core/live-queries/SKILL.md |
3434
| Insert, update, delete with optimistic UI | db-core/mutations-optimistic/SKILL.md |
3535
| Build a custom sync adapter | db-core/custom-adapter/SKILL.md |
36+
| Persist collections to SQLite (offline cache) | db-core/persistence/SKILL.md |
3637
| Preload collections in route loaders | meta-framework/SKILL.md |
3738
| Add offline transaction queueing | offline/SKILL.md (in @tanstack/offline-transactions) |
3839

@@ -54,8 +55,9 @@ For framework-specific hooks:
5455
- Using React hooks? → react-db
5556
- Preloading in route loaders (Start, Next, Remix)? → meta-framework
5657
- Building an adapter for a new backend? → db-core/custom-adapter
58+
- Persisting collections to SQLite? → db-core/persistence
5759
- Need offline transaction persistence? → offline
5860

5961
## Version
6062

61-
Targets @tanstack/db v0.5.30.
63+
Targets @tanstack/db v0.6.0.

packages/db/skills/db-core/collection-setup/SKILL.md

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ description: >
66
(ElectricSQL real-time sync), powerSyncCollectionOptions (PowerSync SQLite),
77
rxdbCollectionOptions (RxDB), trailbaseCollectionOptions (TrailBase),
88
localOnlyCollectionOptions, localStorageCollectionOptions. CollectionConfig
9-
options: getKey, schema, sync, gcTime, autoIndex, syncMode (eager/on-demand/
10-
progressive). StandardSchema validation with Zod/Valibot/ArkType. Collection
11-
lifecycle (idle/loading/ready/error). Adapter-specific sync patterns including
12-
Electric txid tracking and Query direct writes.
9+
options: getKey, schema, sync, gcTime, autoIndex (default off), defaultIndexType,
10+
syncMode (eager/on-demand, plus progressive for Electric). StandardSchema validation
11+
with Zod/Valibot/ArkType. Collection lifecycle (idle/loading/ready/error).
12+
Adapter-specific sync patterns including Electric txid tracking, Query direct
13+
writes, and PowerSync query-driven sync with onLoad/onLoadSubset hooks.
1314
type: sub-skill
1415
library: db
15-
library_version: '0.5.30'
16+
library_version: '0.6.0'
1617
sources:
1718
- 'TanStack/db:docs/overview.md'
1819
- 'TanStack/db:docs/guides/schemas.md'
@@ -98,11 +99,29 @@ queryCollectionOptions({
9899
})
99100
```
100101

101-
| Mode | Best for | Data size |
102-
| ------------- | ---------------------------------------------- | --------- |
103-
| `eager` | Mostly-static datasets | <10k rows |
104-
| `on-demand` | Search, catalogs, large tables | >50k rows |
105-
| `progressive` | Collaborative apps needing instant first paint | Any |
102+
| Mode | Best for | Data size |
103+
| ------------- | -------------------------------------------------------------- | --------- |
104+
| `eager` | Mostly-static datasets | <10k rows |
105+
| `on-demand` | Search, catalogs, large tables | >50k rows |
106+
| `progressive` | Collaborative apps needing instant first paint (Electric only) | Any |
107+
108+
## Indexing
109+
110+
Indexing is opt-in. The `autoIndex` option defaults to `"off"`. To enable automatic indexing, set `autoIndex: "eager"` and provide a `defaultIndexType`:
111+
112+
```ts
113+
import { BasicIndex } from '@tanstack/db'
114+
115+
createCollection(
116+
queryCollectionOptions({
117+
autoIndex: 'eager',
118+
defaultIndexType: BasicIndex,
119+
// ...
120+
}),
121+
)
122+
```
123+
124+
Without `defaultIndexType`, setting `autoIndex: "eager"` throws a `CollectionConfigurationError`. You can also create indexes manually with `collection.createIndex()` and remove them with `collection.removeIndex()`.
106125

107126
## Core Patterns
108127

@@ -255,7 +274,7 @@ app.post('/api/todos', async (req, res) => {
255274
})
256275
```
257276

258-
`pg_current_xact_id()` must be queried inside the same SQL transaction as the mutation. Otherwise the txid doesn't match and `awaitTxId` stalls forever.
277+
`pg_current_xact_id()` must be queried inside the same SQL transaction as the mutation. Otherwise the txid doesn't match and `awaitTxId` times out (default 5 seconds).
259278

260279
Source: docs/collections/electric-collection.md
261280

packages/db/skills/db-core/collection-setup/references/powersync-adapter.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,10 @@ await tx.commit()
196196
await tx.isPersisted.promise
197197
```
198198

199+
## On-Demand Sync Mode
200+
201+
PowerSync supports `on-demand` sync mode (query-driven sync), where only rows matching active live query predicates are loaded from SQLite into the collection. This can be combined with Sync Streams via `onLoad` (eager) or `onLoadSubset` (on-demand) hooks to also control which data the PowerSync Service syncs to the device. Use `extractSimpleComparisons` or `parseWhereExpression` to derive Sync Stream parameters dynamically from live query predicates.
202+
199203
## Complete Example
200204

201205
```typescript

packages/db/skills/db-core/collection-setup/references/query-adapter.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,38 @@ const productsCollection = createCollection(
176176
)
177177
```
178178

179+
## Common Mistakes
180+
181+
### HIGH Function-based queryKey without shared prefix
182+
183+
Wrong:
184+
185+
```ts
186+
queryCollectionOptions({
187+
queryKey: (opts) => {
188+
if (opts.where) {
189+
return ['products-filtered', JSON.stringify(opts.where)]
190+
}
191+
return ['products-all']
192+
},
193+
})
194+
```
195+
196+
Correct:
197+
198+
```ts
199+
queryCollectionOptions({
200+
queryKey: (opts) => {
201+
if (opts.where) {
202+
return ['products', JSON.stringify(opts.where)]
203+
}
204+
return ['products']
205+
},
206+
})
207+
```
208+
209+
When using a function-based `queryKey`, all derived keys must share the base key (`queryKey({})`) as a prefix. TanStack Query uses prefix matching for cache operations; if derived keys don't share the base prefix, cache updates silently miss entries, leading to stale data.
210+
179211
## Key Behaviors
180212

181213
- `queryFn` result is treated as **complete state** -- missing items are deleted

packages/db/skills/db-core/custom-adapter/SKILL.md

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@
22
name: db-core/custom-adapter
33
description: >
44
Building custom collection adapters for new backends. SyncConfig interface:
5-
sync function receiving begin, write, commit, markReady, truncate primitives.
6-
ChangeMessage format (insert, update, delete). loadSubset for on-demand sync.
7-
LoadSubsetOptions (where, orderBy, limit, cursor). Expression parsing:
8-
parseWhereExpression, parseOrderByExpression, extractSimpleComparisons,
9-
parseLoadSubsetOptions. Collection options creator pattern. rowUpdateMode
10-
(partial vs full). Subscription lifecycle and cleanup functions.
5+
sync function receiving begin, write, commit, markReady, truncate, metadata
6+
primitives. ChangeMessage format (insert, update, delete). loadSubset for
7+
on-demand sync. LoadSubsetOptions (where, orderBy, limit, cursor). Expression
8+
parsing: parseWhereExpression, parseOrderByExpression,
9+
extractSimpleComparisons, parseLoadSubsetOptions. Collection options creator
10+
pattern. rowUpdateMode (partial vs full). Subscription lifecycle and cleanup
11+
functions. Persisted sync metadata API (metadata.row and metadata.collection)
12+
for storing per-row and per-collection adapter state.
1113
type: sub-skill
1214
library: db
13-
library_version: '0.5.30'
15+
library_version: '0.6.0'
1416
sources:
1517
- 'TanStack/db:docs/guides/collection-options-creator.md'
1618
- 'TanStack/db:packages/db/src/collection/sync.ts'
@@ -38,7 +40,7 @@ function myBackendCollectionOptions<T>(config: {
3840
return {
3941
getKey: config.getKey,
4042
sync: {
41-
sync: ({ begin, write, commit, markReady, collection }) => {
43+
sync: ({ begin, write, commit, markReady, metadata, collection }) => {
4244
let isInitialSyncComplete = false
4345
const bufferedEvents: Array<any> = []
4446

@@ -157,6 +159,53 @@ Mutation handlers must not resolve until server changes have synced back to the
157159
4. **Version/timestamp**: wait until sync stream catches up to mutation time
158160
5. **Provider method**: `await backend.waitForPendingWrites()`
159161

162+
### Persisted sync metadata
163+
164+
The `metadata` API on the sync config allows adapters to store per-row and per-collection metadata that persists across sync transactions. This is useful for tracking resume tokens, cursors, LSNs, or other adapter-specific state.
165+
166+
The `metadata` object is available as a property on the sync config argument alongside `begin`, `write`, `commit`, etc. It is always provided, but without persistence the metadata is in-memory only and does not survive reloads. With persistence, metadata is durable across sessions.
167+
168+
```ts
169+
sync: ({ begin, write, commit, markReady, metadata }) => {
170+
// Row metadata: store per-row state (e.g. server version, ETag)
171+
metadata.row.get(key) // => unknown | undefined
172+
metadata.row.set(key, { version: 3, etag: 'abc' })
173+
metadata.row.delete(key)
174+
175+
// Collection metadata: store per-collection state (e.g. resume cursor)
176+
metadata.collection.get('cursor') // => unknown | undefined
177+
metadata.collection.set('cursor', 'token_abc123')
178+
metadata.collection.delete('cursor')
179+
metadata.collection.list() // => [{ key: 'cursor', value: 'token_abc123' }]
180+
metadata.collection.list('resume') // filter by prefix
181+
}
182+
```
183+
184+
Row metadata writes are tied to the current transaction. When a row is deleted via `write({ type: 'delete', ... })`, its row metadata is automatically deleted. When a row is inserted, its metadata is set from `message.metadata` if provided, or deleted otherwise.
185+
186+
Collection metadata writes staged before `truncate()` are preserved and commit atomically with the truncate transaction.
187+
188+
**Typical usage — resume token:**
189+
190+
```ts
191+
sync: ({ begin, write, commit, markReady, metadata }) => {
192+
const lastCursor = metadata.collection.get('cursor') as string | undefined
193+
194+
const stream = subscribeFromCursor(lastCursor)
195+
stream.on('data', (batch) => {
196+
begin()
197+
for (const item of batch.items) {
198+
write({ type: item.type, key: item.id, value: item.data })
199+
}
200+
metadata.collection.set('cursor', batch.cursor)
201+
commit()
202+
})
203+
204+
stream.on('ready', () => markReady())
205+
return () => stream.close()
206+
}
207+
```
208+
160209
### Expression parsing for predicate push-down
161210

162211
```ts
@@ -282,4 +331,4 @@ Source: packages/db/src/collection/sync.ts:110
282331

283332
Getting-started simplicity (localOnly, eager mode) conflicts with production correctness (on-demand sync, race condition prevention, proper markReady handling). Agents optimizing for quick setup tend to skip buffering, markReady, and cleanup functions.
284333

285-
See also: db-core/collection-setup/SKILL.md -- for built-in adapter patterns to model after.
334+
See also: db-core/collection-setup/SKILL.md for built-in adapter patterns to model after.

0 commit comments

Comments
 (0)