This entire post was written by an AI
Let's be upfront about it. This blog post, every feature in v0.2.0, the PRs, the tests, the npm publish, the GitHub release — all of it was done by Claude Code.
The human's contribution? Saying "build this."
That's exactly what comwit's vibe coding philosophy is about. When your domain structure is clean and consistent, AI can understand the codebase precisely and extend it with the same patterns. Every feature in v0.2.0 is proof of that.
What's New
Suspense Support
query() and query.infinite() now accept a suspense: true option. No new hooks needed — it works directly with the existing useModel() / create() API.
const userModel = model({
profile: query<User>({
initialData: null,
queryFn: () => fetch('/api/user').then((r) => r.json()),
suspense: true,
}),
})
- Integrates naturally with React
<Suspense>and<ErrorBoundary> - Refetch does NOT suspend — shows stale data while fetching in the background
- TypeScript automatically narrows
datatoNonNullable<T> - Mix suspense and non-suspense queries in the same model
Real-time Subscriptions
query.realtime() brings WebSocket, SSE, and other real-time data sources into the query pattern.
const chatModel = model({
messages: query.realtime<Message[]>({
initialData: [],
queryFn: () => fetch('/api/messages').then((r) => r.json()),
subscribe: (callbacks) => {
const ws = new WebSocket('wss://api.example.com/chat')
ws.onmessage = (e) => {
callbacks.update((prev) => [...prev, JSON.parse(e.data)])
}
ws.onopen = () => callbacks.onStatus('connected')
ws.onclose = () => callbacks.onStatus('disconnected')
return () => ws.close()
},
}),
})
- Automatic
connectionStatus/isConnectedstate management update(),set(),refetch(),onStatus(),onError()callbacksunsubscribe()for manual cleanup; previous subscription auto-cleaned on re-query- Same API pattern as
query()/query.infinite()
Persist Plugin
persist() field descriptor automatically syncs model state to external storage.
const settingsModel = model({
theme: persist<'light' | 'dark'>({
key: 'app-theme',
defaultValue: 'light',
}),
sidebarOpen: persist<boolean>({
key: 'sidebar-open',
defaultValue: true,
storage: 'sessionStorage',
}),
})
- Built-in
localStorage/sessionStorageadapters - Custom
PersistAdapterinterface (IndexedDB, cookies, etc.) - Cross-tab sync (localStorage)
- Debounced write-back, SSR-safe
- Custom
serialize/deserialize
New Decorators
@Retry— automatic retry on failure with configurable max attempts and delay@Queue— prevents concurrent execution, ensures sequential processing@Log— method call logging@Validate— input validation
Also added global interceptors, derived state, and model validation support.
Architecture: Plugin System
The biggest structural change in v0.2.0.
Previously, adding a new feature (suspense, realtime, persist) required modifying model.ts, query.ts, action.ts, and provider.tsx — tight coupling across core files. Now, features register through the FieldPlugin interface without touching core code.
type FieldPlugin = {
name: string
detect(value, path, bag): { initialValue } | null
createRegistryState(defaults?): unknown
bindState(proxy, bag, state, defaults): object
onRender?(bag, state): void
}
query, persist, and realtime all run on top of this plugin system. Third-party plugins can extend comwit via registerPlugin().
We also completely removed the valtio dependency and implemented a custom proxy-based reactivity engine from scratch.
Bug Fixes
- Fixed query
isLoadingcalculation order — error-retry now correctly shows loading state - Fixed stale response race condition — rapid query arg changes only apply the latest response
set()now syncs with cache — reads withinstaleTimereturn set data, not stale cache- Removed
withCursorfrom infinite queries, addedhasMoreguard - Fixed
nextFetchcursor history — supports multi-step backward navigation
v0.2.0 by the Numbers
- 7 PRs merged since v0.0.2
- 195 tests passing
- 0 new runtime dependencies (still just
es-toolkit) - 100% Claude Code — code, tests, PRs, deployment, and this blog post
Upgrade
yarn add comwit@0.2.0
All changes are backward-compatible. Upgrade without modifying existing code.