Skip to content

Commit 337f201

Browse files
committed
wip devtools - squashed
1 parent 1d98a77 commit 337f201

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+5743
-15
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ out
9090
# Nuxt.js build / generate output
9191
.nuxt
9292
dist
93+
build
9394

9495
# Gatsby files
9596
.cache/

examples/react/todo/package.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,27 @@
33
"private": true,
44
"version": "0.1.1",
55
"dependencies": {
6+
"@tanstack/db-devtools": "workspace:*",
67
"@tanstack/electric-db-collection": "^0.1.0",
78
"@tanstack/query-core": "^5.75.7",
89
"@tanstack/query-db-collection": "^0.2.0",
910
"@tanstack/react-db": "^0.1.0",
11+
"@tanstack/react-db-devtools": "workspace:*",
1012
"@tanstack/react-router": "^1.125.6",
13+
"@tanstack/react-router-devtools": "^1.130.2",
1114
"@tanstack/react-start": "^1.126.1",
12-
"@tanstack/trailbase-db-collection": "^0.1.0",
15+
"@tanstack/trailbase-db-collection": "^0.1.2",
1316
"cors": "^2.8.5",
1417
"drizzle-orm": "^0.40.1",
1518
"drizzle-zod": "^0.8.3",
16-
"zod": "^4.0.17",
1719
"express": "^4.19.2",
1820
"postgres": "^3.4.7",
1921
"react": "^19.1.0",
2022
"react-dom": "^19.1.0",
2123
"tailwindcss": "^4.1.11",
2224
"trailbase": "^0.7.1",
23-
"vite-tsconfig-paths": "^5.1.4"
25+
"vite-tsconfig-paths": "^5.1.4",
26+
"zod": "^4.0.17"
2427
},
2528
"devDependencies": {
2629
"@eslint/js": "^9.22.0",

examples/react/todo/src/lib/collections.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { createCollection } from "@tanstack/react-db"
2+
import { initializeDbDevtools } from "@tanstack/react-db-devtools"
23
import { electricCollectionOptions } from "@tanstack/electric-db-collection"
34
import { queryCollectionOptions } from "@tanstack/query-db-collection"
45
import { trailBaseCollectionOptions } from "@tanstack/trailbase-db-collection"
@@ -11,13 +12,16 @@ import type { SelectConfig, SelectTodo } from "../db/validation"
1112
// Create a query client for query collections
1213
const queryClient = new QueryClient()
1314

15+
// Initialize DB devtools early (idempotent - safe to call multiple times)
16+
initializeDbDevtools()
17+
1418
// Create a TrailBase client.
1519
const trailBaseClient = initClient(`http://localhost:4000`)
1620

1721
// Electric Todo Collection
1822
export const electricTodoCollection = createCollection(
1923
electricCollectionOptions({
20-
id: `todos`,
24+
id: `electric-todos`,
2125
shapeOptions: {
2226
url: `http://localhost:3003/v1/shape`,
2327
params: {
@@ -71,7 +75,7 @@ export const electricTodoCollection = createCollection(
7175
// Query Todo Collection
7276
export const queryTodoCollection = createCollection(
7377
queryCollectionOptions({
74-
id: `todos`,
78+
id: `query-todos`,
7579
queryKey: [`todos`],
7680
refetchInterval: 3000,
7781
queryFn: async () => {
@@ -130,7 +134,7 @@ type Todo = {
130134
// TrailBase Todo Collection
131135
export const trailBaseTodoCollection = createCollection(
132136
trailBaseCollectionOptions<SelectTodo, Todo>({
133-
id: `todos`,
137+
id: `trailbase-todos`,
134138
getKey: (item) => item.id,
135139
schema: selectTodoSchema,
136140
recordApi: trailBaseClient.records(`todos`),
@@ -149,7 +153,7 @@ export const trailBaseTodoCollection = createCollection(
149153
// Electric Config Collection
150154
export const electricConfigCollection = createCollection(
151155
electricCollectionOptions({
152-
id: `config`,
156+
id: `electric-config`,
153157
shapeOptions: {
154158
url: `http://localhost:3003/v1/shape`,
155159
params: {
@@ -185,7 +189,7 @@ export const electricConfigCollection = createCollection(
185189
// Query Config Collection
186190
export const queryConfigCollection = createCollection(
187191
queryCollectionOptions({
188-
id: `config`,
192+
id: `query-config`,
189193
queryKey: [`config`],
190194
refetchInterval: 3000,
191195
queryFn: async () => {
@@ -231,7 +235,7 @@ type Config = {
231235
// TrailBase Config Collection
232236
export const trailBaseConfigCollection = createCollection(
233237
trailBaseCollectionOptions<SelectConfig, Config>({
234-
id: `config`,
238+
id: `trailbase-config`,
235239
getKey: (item) => item.id,
236240
schema: selectConfigSchema,
237241
recordApi: trailBaseClient.records(`config`),

examples/react/todo/src/routes/__root.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import {
44
Scripts,
55
createRootRoute,
66
} from "@tanstack/react-router"
7+
import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
8+
import { TanStackReactDbDevtools } from "@tanstack/react-db-devtools"
79

810
import appCss from "../styles.css?url"
911

@@ -32,6 +34,8 @@ export const Route = createRootRoute({
3234
component: () => (
3335
<RootDocument>
3436
<Outlet />
37+
<TanStackRouterDevtools />
38+
<TanStackReactDbDevtools position="bottom-right" />
3539
</RootDocument>
3640
),
3741
})

packages/db-devtools/README.md

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
# @tanstack/db-devtools
2+
3+
Developer tools for TanStack DB that provide real-time insights into your collections, live queries, and transactions.
4+
5+
## Features
6+
7+
- **Collection Monitoring**: View all active collections with real-time status updates
8+
- **Live Query Insights**: Special handling for live queries with performance metrics
9+
- **Transaction Tracking**: Monitor all database transactions and their states
10+
- **WeakRef Architecture**: Collections are tracked without preventing garbage collection
11+
- **Framework Agnostic**: Core devtools built with Solid.js, with React and Vue wrappers
12+
- **Development Only**: Automatically tree-shaken in production builds
13+
14+
## Installation
15+
16+
```bash
17+
# Core devtools (built with Solid.js)
18+
npm install @tanstack/db-devtools
19+
20+
# React wrapper
21+
npm install @tanstack/react-db-devtools
22+
23+
# Vue wrapper
24+
npm install @tanstack/vue-db-devtools
25+
```
26+
27+
## Usage
28+
29+
### Core Devtools (Solid.js)
30+
31+
```typescript
32+
import { DbDevtools } from '@tanstack/db-devtools'
33+
34+
// Initialize devtools (must be called before creating collections)
35+
import { initializeDbDevtools } from '@tanstack/db-devtools'
36+
initializeDbDevtools()
37+
38+
// Use the devtools component
39+
function App() {
40+
return (
41+
<div>
42+
<h1>My App</h1>
43+
<DbDevtools />
44+
</div>
45+
)
46+
}
47+
```
48+
49+
### React
50+
51+
```tsx
52+
import { ReactDbDevtools } from '@tanstack/react-db-devtools'
53+
54+
function App() {
55+
return (
56+
<div>
57+
<h1>My App</h1>
58+
<ReactDbDevtools />
59+
</div>
60+
)
61+
}
62+
```
63+
64+
### Vue
65+
66+
```vue
67+
<template>
68+
<div>
69+
<h1>My App</h1>
70+
<VueDbDevtools />
71+
</div>
72+
</template>
73+
74+
<script setup>
75+
import { VueDbDevtools } from '@tanstack/vue-db-devtools'
76+
</script>
77+
```
78+
79+
## Configuration
80+
81+
The devtools accept the following configuration options:
82+
83+
```typescript
84+
interface DbDevtoolsConfig {
85+
initialIsOpen?: boolean
86+
position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'relative'
87+
panelProps?: Record<string, any>
88+
closeButtonProps?: Record<string, any>
89+
toggleButtonProps?: Record<string, any>
90+
storageKey?: string
91+
panelState?: 'open' | 'closed'
92+
onPanelStateChange?: (isOpen: boolean) => void
93+
}
94+
```
95+
96+
## Architecture
97+
98+
### Auto-Registration
99+
100+
Collections automatically register themselves with the devtools when created:
101+
102+
```typescript
103+
// When devtools are initialized, this creates a window global
104+
window.__TANSTACK_DB_DEVTOOLS__
105+
106+
// Collections check for this global and register themselves
107+
const collection = createCollection({
108+
// ... config
109+
})
110+
// Collection is now visible in devtools
111+
```
112+
113+
### WeakRef Design
114+
115+
The devtools use WeakRef to track collections without preventing garbage collection:
116+
117+
- **Metadata polling**: Basic info (size, status) is polled every second
118+
- **Hard references**: Only created when viewing collection details
119+
- **Automatic cleanup**: Dead references are garbage collected
120+
121+
### Live Query Detection
122+
123+
Live queries are automatically detected and shown separately:
124+
125+
```typescript
126+
// This will be marked as a live query in devtools
127+
const liveQuery = createLiveQueryCollection({
128+
query: (q) => q.from(collection).select()
129+
})
130+
```
131+
132+
## What You Can See
133+
134+
### Collections View
135+
- Collection ID and type (regular vs live query)
136+
- Status (idle, loading, ready, error, cleaned-up)
137+
- Current size and transaction count
138+
- Creation and last update timestamps
139+
- Garbage collection settings
140+
141+
### Live Queries View
142+
- All the above plus:
143+
- Initial run time
144+
- Total incremental runs
145+
- Average incremental run time
146+
- Last incremental run time
147+
148+
### Transactions View
149+
- Transaction ID and state
150+
- Associated collection
151+
- Mutation details (insert, update, delete)
152+
- Optimistic vs confirmed operations
153+
- Creation and update timestamps
154+
155+
### Collection Details
156+
- Full collection metadata
157+
- Live data with real-time updates
158+
- Individual item inspection
159+
- JSON view of all items
160+
161+
## Performance
162+
163+
The devtools are designed to have minimal impact on your application:
164+
165+
- **Development only**: Automatically removed in production
166+
- **WeakRef tracking**: No memory leaks from collection references
167+
- **Efficient polling**: Only basic metadata is polled, detailed data is fetched on-demand
168+
- **Lazy loading**: UI components are loaded only when needed
169+
170+
## Browser Support
171+
172+
Requires modern browsers that support WeakRef (Chrome 84+, Firefox 79+, Safari 14.1+).
173+
Since this is development-only, this should not be a concern for production applications.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import prettierPlugin from "eslint-plugin-prettier"
2+
import prettierConfig from "eslint-config-prettier"
3+
import stylisticPlugin from "@stylistic/eslint-plugin"
4+
import { tanstackConfig } from "@tanstack/config/eslint"
5+
6+
export default [
7+
...tanstackConfig,
8+
{ ignores: [`dist/`, 'build/**', 'coverage/**', 'eslint.config.js'] },
9+
{
10+
plugins: {
11+
stylistic: stylisticPlugin,
12+
prettier: prettierPlugin,
13+
},
14+
rules: {
15+
"prettier/prettier": `error`,
16+
"stylistic/quotes": [`error`, `backtick`],
17+
...prettierConfig.rules,
18+
"no-console": "warn",
19+
"@typescript-eslint/no-unused-vars": [
20+
`error`,
21+
{ argsIgnorePattern: `^_`, varsIgnorePattern: `^_` },
22+
],
23+
"@typescript-eslint/naming-convention": [
24+
"error",
25+
{
26+
selector: "typeParameter",
27+
format: ["PascalCase"],
28+
leadingUnderscore: `allow`,
29+
},
30+
],
31+
},
32+
},
33+
]

0 commit comments

Comments
 (0)