Three bugs fixed:
1. MizanProvider.call() read data.data but server returns data.result.
Now reads data.result and processes data.invalidate for server-driven
invalidation (triggering refetch on mounted context providers).
2. GlobalContextLoader expected {error, data} wrapper but context GET
returns raw bundled data. Fixed to iterate response directly.
3. Named context providers had same wrapper assumption. Fixed to
setData(result) directly.
Two features added:
1. SSR hydration: GlobalContextLoader checks window.__MIZAN_SSR_DATA__
on mount. If present, populates contexts from it and skips fetch.
2. SSR hydration: Named context providers check __MIZAN_SSR_DATA__ in
useState initializer. If SSR data exists for their functions, they
render immediately without fetching.
3. Server-driven invalidation in MizanProvider.call(): reads the
invalidate array from mutation responses and triggers refetch on
mounted providers. Generated mutation hooks' hardcoded invalidation
is now redundant but idempotent — both paths coexist safely.
Also fixed FunctionSuccessResponse type to match new protocol:
{ result: T, invalidate?: [...] }
373 Django + 33 React tests pass.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@rythazhur/mizan (TypeScript)
React client for the mizan framework. See the monorepo root for full documentation.
Install
npm install @rythazhur/mizan@git+https://git.impactsoundworks.com/isw/mizan.git#workspace=react
Usage
You don't use this package directly. You use the generated hooks.
1. Configure
// django.config.mjs
export default {
source: {
django: {
managePath: '../backend/manage.py',
command: ['uv', 'run', 'python'],
},
},
output: 'src/api/generated.ts',
}
2. Generate
npx mizan-generate # once
npx mizan-generate --watch # dev mode
3. Wrap your app
import { DjangoContext } from '@/api'
<DjangoContext>
<App />
</DjangoContext>
DjangoContext is the only provider you need. It handles HTTP, WebSocket, CSRF, session init, context auto-fetching, and channel connections.
4. Use generated hooks
import { useCurrentUser, useEcho, useContactForm, useChatChannel } from '@/api'
// Context (SSR-hydrated, auto-refreshed)
const user = useCurrentUser()
// Server function
const echo = useEcho()
const result = await echo({ text: 'hello' })
// Form (Zod + server validation)
const form = useContactForm()
form.set('email', 'test@example.com')
await form.submit()
// Channel (WebSocket)
const chat = useChatChannel({ room: 'general' })
chat.send({ text: 'hello' })
chat.messages // typed, reactive
Generated Files
| File | Contents |
|---|---|
generated.django.tsx |
DjangoContext + typed hooks |
generated.mizan.ts |
Pydantic types |
generated.forms.ts |
Form hooks with Zod |
generated.channels.hooks.tsx |
Channel hooks |
index.ts |
Re-exports everything |
Sub-exports
| Import | When to use |
|---|---|
@rythazhur/mizan |
Core: mizanProvider, hooks, forms, errors |
@rythazhur/mizan/channels |
WebSocket channels |
@rythazhur/mizan/jwt |
JWT token management |
@rythazhur/mizan/client |
HTTP clients (CSR/SSR) |
@rythazhur/mizan/allauth |
Allauth UI components |
These are library internals used by the generated code. You should import from @/api (your generated index), not from the library directly.
Running Tests
# Unit tests (Vitest, jsdom)
npm test
# E2E tests (Playwright, real browser)
# Requires Docker backend running
npx playwright test