Regenerate example code + fix broken paths from repo restructure
- Fix testapp/apps.py: import djarea_clients (file was never renamed) - Fix fetch.mjs: command is export_djarea_schema not export_mizan_schema - Fix harness package.json: dependency path to mizan-react after restructure - Add package.json for generator (openapi-typescript dependency) - Regenerate all example code with new protocol format: - generated.provider.tsx uses raw context responses + SSR hydration - generated.server.ts uses GET /ctx/global/ with response.ok check - generated.forms.ts, channels.ts, channels.hooks.tsx refreshed - Remove stale generated.django.tsx and generated.django.server.ts - Update imports: fixtures.tsx and main.tsx import from ./api (index) - Use MizanContext instead of deprecated DjangoContext in examples Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,4 +6,4 @@ class TestAppConfig(AppConfig):
|
|||||||
default_auto_field = "django.db.models.BigAutoField"
|
default_auto_field = "django.db.models.BigAutoField"
|
||||||
|
|
||||||
def ready(self):
|
def ready(self):
|
||||||
import testapp.mizan_clients # noqa: F401
|
import testapp.djarea_clients # noqa: F401
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
"dev": "vite --port 5174"
|
"dev": "vite --port 5174"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@rythazhur/mizan": "file:../../react",
|
"@rythazhur/mizan": "file:../../../packages/mizan-react",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"zod": "^4.3.6"
|
"zod": "^4.3.6"
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
// AUTO-GENERATED by mizan - do not edit manually
|
|
||||||
// Regenerate with: npm run schemas
|
|
||||||
//
|
|
||||||
// Server-side functions for SSR hydration.
|
|
||||||
// These run in Next.js server components/layouts.
|
|
||||||
|
|
||||||
import type { currentUserOutput, greetOutput } from './generated.mizan'
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Hydration Types
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
/** Typed hydration data for SSR */
|
|
||||||
export interface DjangoHydration {
|
|
||||||
currentUser?: currentUserOutput
|
|
||||||
greet?: greetOutput
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// SSR Hydration Helper
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch hydration data for SSR.
|
|
||||||
*
|
|
||||||
* Call this in your server component:
|
|
||||||
* const hydration = await getDjangoHydration(client)
|
|
||||||
* return <DjangoContext hydration={hydration}>...</DjangoContext>
|
|
||||||
*/
|
|
||||||
export async function getDjangoHydration(
|
|
||||||
client: { request: (method: string, url: string, body?: unknown) => Promise<Response> }
|
|
||||||
): Promise<DjangoHydration> {
|
|
||||||
const hydration: DjangoHydration = {}
|
|
||||||
|
|
||||||
const results = await Promise.allSettled([
|
|
||||||
client.request('POST', '/api/mizan/call/', { fn: 'current_user', args: {} }),
|
|
||||||
client.request('POST', '/api/mizan/call/', { fn: 'greet', args: {} }),
|
|
||||||
])
|
|
||||||
|
|
||||||
if (results[0].status === 'fulfilled') {
|
|
||||||
const data = await (results[0] as PromiseFulfilledResult<Response>).value.json()
|
|
||||||
if (data.error) {
|
|
||||||
console.error('[getDjangoHydration] current_user failed:', data.code, data.message)
|
|
||||||
} else {
|
|
||||||
hydration.currentUser = data.data
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.error('[getDjangoHydration] current_user request failed:', (results[0] as PromiseRejectedResult).reason)
|
|
||||||
}
|
|
||||||
if (results[1].status === 'fulfilled') {
|
|
||||||
const data = await (results[1] as PromiseFulfilledResult<Response>).value.json()
|
|
||||||
if (data.error) {
|
|
||||||
console.error('[getDjangoHydration] greet failed:', data.code, data.message)
|
|
||||||
} else {
|
|
||||||
hydration.greet = data.data
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.error('[getDjangoHydration] greet request failed:', (results[1] as PromiseRejectedResult).reason)
|
|
||||||
}
|
|
||||||
|
|
||||||
return hydration
|
|
||||||
}
|
|
||||||
@@ -1,257 +0,0 @@
|
|||||||
'use client'
|
|
||||||
|
|
||||||
// AUTO-GENERATED by mizan - do not edit manually
|
|
||||||
// Regenerate with: npm run schemas
|
|
||||||
|
|
||||||
// This file provides typed wrappers around the mizan library.
|
|
||||||
// - DjangoContext: Typed provider wrapping mizanProvider
|
|
||||||
// - Typed hooks: useAuthStatus(), useUser(), etc.
|
|
||||||
|
|
||||||
import { type ReactNode, useCallback } from 'react'
|
|
||||||
import {
|
|
||||||
mizanProvider,
|
|
||||||
usemizan,
|
|
||||||
usemizanContext,
|
|
||||||
usemizanCall,
|
|
||||||
type mizanHydration,
|
|
||||||
type Transport,
|
|
||||||
} from 'mizan'
|
|
||||||
import { ChannelProvider, ChannelConnection } from 'mizan/channels'
|
|
||||||
import { useRef } from 'react'
|
|
||||||
|
|
||||||
import type { addEmailSchemaOutput, addEmailValidateInput, addEmailValidateOutput, addInput, addOutput, buggyFnOutput, contactSchemaInput, contactSchemaOutput, contactSubmitOutput, contactValidateInput, contactValidateOutput, currentUserOutput, echoInput, echoOutput, greetInput, greetOutput, httpOnlyEchoInput, httpOnlyEchoOutput, itemFormsetSchemaInput, itemFormsetSchemaOutput, itemFormsetSubmitInput, itemFormsetSubmitOutput, itemFormsetValidateInput, itemFormsetValidateOutput, itemSchemaInput, itemSchemaOutput, itemSubmitOutput, itemValidateInput, itemValidateOutput, jwtObtainOutput, jwtRefreshInput, jwtRefreshOutput, loginSchemaOutput, loginSubmitInput, loginSubmitOutput, loginValidateInput, loginValidateOutput, multiplyInput, multiplyOutput, notImplementedFnOutput, permissionCheckFnInput, permissionCheckFnOutput, signupSchemaOutput, signupSubmitInput, signupSubmitOutput, signupValidateInput, signupValidateOutput, staffOnlyOutput, superuserOnlyOutput, verifiedOnlyOutput, whoamiOutput, wsWhoamiOutput } from './generated.mizan'
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Hydration Types
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
/** Typed hydration data for SSR */
|
|
||||||
export interface DjangoHydration {
|
|
||||||
currentUser?: currentUserOutput
|
|
||||||
greet?: greetOutput
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Convert typed hydration to mizan format */
|
|
||||||
function tomizanHydration(hydration?: DjangoHydration): mizanHydration | undefined {
|
|
||||||
if (!hydration) return undefined
|
|
||||||
const result: mizanHydration = {}
|
|
||||||
if (hydration.currentUser !== undefined) result['current_user'] = hydration.currentUser
|
|
||||||
if (hydration.greet !== undefined) result['greet'] = hydration.greet
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Provider
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
export interface DjangoContextProps {
|
|
||||||
children: ReactNode
|
|
||||||
/** SSR hydration data */
|
|
||||||
hydration?: DjangoHydration
|
|
||||||
/** WebSocket URL for RPC calls (default: /ws/) */
|
|
||||||
wsUrl?: string
|
|
||||||
/** Base URL for HTTP fallback (default: /api/mizan) */
|
|
||||||
baseUrl?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Typed Django context provider.
|
|
||||||
*
|
|
||||||
* Wraps mizanProvider with:
|
|
||||||
* - Typed hydration
|
|
||||||
* - Auto-fetch for registered contexts
|
|
||||||
*
|
|
||||||
* Usage:
|
|
||||||
* <DjangoContext hydration={hydration}>
|
|
||||||
* <App />
|
|
||||||
* </DjangoContext>
|
|
||||||
*/
|
|
||||||
export function DjangoContext({
|
|
||||||
children,
|
|
||||||
hydration,
|
|
||||||
wsUrl,
|
|
||||||
baseUrl,
|
|
||||||
}: DjangoContextProps) {
|
|
||||||
const connectionRef = useRef<ChannelConnection | null>(null)
|
|
||||||
if (!connectionRef.current) {
|
|
||||||
connectionRef.current = new ChannelConnection({ url: wsUrl || '/ws/' })
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<mizanProvider
|
|
||||||
hydration={tomizanHydration(hydration)}
|
|
||||||
contexts={['current_user', 'greet']}
|
|
||||||
wsUrl={wsUrl}
|
|
||||||
baseUrl={baseUrl}
|
|
||||||
connection={connectionRef.current}
|
|
||||||
>
|
|
||||||
<ChannelProvider connection={connectionRef.current} autoConnect={true}>
|
|
||||||
{children}
|
|
||||||
</ChannelProvider>
|
|
||||||
</mizanProvider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Context Hooks (typed wrappers)
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get current_user context data.
|
|
||||||
* @throws if context not loaded yet
|
|
||||||
*/
|
|
||||||
export function useCurrentUser(): currentUserOutput {
|
|
||||||
const data = usemizanContext<currentUserOutput>('current_user')
|
|
||||||
if (data === undefined) {
|
|
||||||
throw new Error('useCurrentUser: context not loaded yet')
|
|
||||||
}
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get greet context data.
|
|
||||||
* @throws if context not loaded yet
|
|
||||||
*/
|
|
||||||
export function useGreet(): greetOutput {
|
|
||||||
const data = usemizanContext<greetOutput>('greet')
|
|
||||||
if (data === undefined) {
|
|
||||||
throw new Error('useGreet: context not loaded yet')
|
|
||||||
}
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get context refresh functions without subscribing to data changes.
|
|
||||||
* Use this in components that only need to trigger refreshes.
|
|
||||||
*/
|
|
||||||
export function useDjangoRefresh() {
|
|
||||||
const { refreshContext, refreshAllContexts } = usemizan()
|
|
||||||
return {
|
|
||||||
refreshCurrentUser: () => refreshContext('current_user'),
|
|
||||||
refreshGreet: () => refreshContext('greet'),
|
|
||||||
refreshAll: refreshAllContexts,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Function Hooks (typed wrappers)
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call echo server function.
|
|
||||||
* Transport: websocket
|
|
||||||
*/
|
|
||||||
export function useEcho() {
|
|
||||||
return usemizanCall<echoInput, echoOutput>('echo', 'websocket')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call add server function.
|
|
||||||
* Transport: websocket
|
|
||||||
*/
|
|
||||||
export function useAdd() {
|
|
||||||
return usemizanCall<addInput, addOutput>('add', 'websocket')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call whoami server function.
|
|
||||||
* Transport: http
|
|
||||||
*/
|
|
||||||
export function useWhoami() {
|
|
||||||
return usemizanCall<void, whoamiOutput>('whoami', 'http')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call http_only_echo server function.
|
|
||||||
* Transport: http
|
|
||||||
*/
|
|
||||||
export function useHttpOnlyEcho() {
|
|
||||||
return usemizanCall<httpOnlyEchoInput, httpOnlyEchoOutput>('http_only_echo', 'http')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call staff_only server function.
|
|
||||||
* Transport: http
|
|
||||||
*/
|
|
||||||
export function useStaffOnly() {
|
|
||||||
return usemizanCall<void, staffOnlyOutput>('staff_only', 'http')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call superuser_only server function.
|
|
||||||
* Transport: http
|
|
||||||
*/
|
|
||||||
export function useSuperuserOnly() {
|
|
||||||
return usemizanCall<void, superuserOnlyOutput>('superuser_only', 'http')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call verified_only server function.
|
|
||||||
* Transport: http
|
|
||||||
*/
|
|
||||||
export function useVerifiedOnly() {
|
|
||||||
return usemizanCall<void, verifiedOnlyOutput>('verified_only', 'http')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call multiply server function.
|
|
||||||
* Transport: http
|
|
||||||
*/
|
|
||||||
export function useMultiply() {
|
|
||||||
return usemizanCall<multiplyInput, multiplyOutput>('multiply', 'http')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call not_implemented_fn server function.
|
|
||||||
* Transport: http
|
|
||||||
*/
|
|
||||||
export function useNotImplementedFn() {
|
|
||||||
return usemizanCall<void, notImplementedFnOutput>('not_implemented_fn', 'http')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call buggy_fn server function.
|
|
||||||
* Transport: http
|
|
||||||
*/
|
|
||||||
export function useBuggyFn() {
|
|
||||||
return usemizanCall<void, buggyFnOutput>('buggy_fn', 'http')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call permission_check_fn server function.
|
|
||||||
* Transport: http
|
|
||||||
*/
|
|
||||||
export function usePermissionCheckFn() {
|
|
||||||
return usemizanCall<permissionCheckFnInput, permissionCheckFnOutput>('permission_check_fn', 'http')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call ws_whoami server function.
|
|
||||||
* Transport: websocket
|
|
||||||
*/
|
|
||||||
export function useWsWhoami() {
|
|
||||||
return usemizanCall<void, wsWhoamiOutput>('ws_whoami', 'websocket')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call jwt_obtain server function.
|
|
||||||
* Transport: http
|
|
||||||
*/
|
|
||||||
export function useJwtObtain() {
|
|
||||||
return usemizanCall<void, jwtObtainOutput>('jwt_obtain', 'http')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call jwt_refresh server function.
|
|
||||||
* Transport: http
|
|
||||||
*/
|
|
||||||
export function useJwtRefresh() {
|
|
||||||
return usemizanCall<jwtRefreshInput, jwtRefreshOutput>('jwt_refresh', 'http')
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Re-exports from mizan library
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
export { usemizan, usemizanStatus, usePush, DjangoError } from 'mizan'
|
|
||||||
export type { ConnectionStatus, PushMessage, PushListener } from 'mizan'
|
|
||||||
@@ -192,7 +192,7 @@ export function useItemFormset(
|
|||||||
// Form Registry
|
// Form Registry
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
export const DJANGO_FORMS = {
|
export const MIZAN_FORMS = {
|
||||||
login: {
|
login: {
|
||||||
name: 'login',
|
name: 'login',
|
||||||
schema: LoginSchema,
|
schema: LoginSchema,
|
||||||
|
|||||||
@@ -6,11 +6,10 @@
|
|||||||
* @example
|
* @example
|
||||||
* ```tsx
|
* ```tsx
|
||||||
* import {
|
* import {
|
||||||
* DjangoContext,
|
* MizanContext,
|
||||||
* useUser,
|
* useCurrentUser,
|
||||||
* useEcho,
|
* useEcho,
|
||||||
* useChatChannel,
|
* useChatChannel,
|
||||||
* DjangoError,
|
|
||||||
* } from '@/api'
|
* } from '@/api'
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
@@ -23,22 +22,30 @@
|
|||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
getMizanHydration,
|
||||||
getDjangoHydration,
|
getDjangoHydration,
|
||||||
|
type MizanHydrationData,
|
||||||
type DjangoHydration,
|
type DjangoHydration,
|
||||||
} from './generated.django.server'
|
} from './generated.server'
|
||||||
|
|
||||||
export {
|
export {
|
||||||
// Provider
|
// Provider
|
||||||
|
MizanContext,
|
||||||
|
type MizanContextProps,
|
||||||
DjangoContext,
|
DjangoContext,
|
||||||
type DjangoContextProps,
|
type DjangoContextProps,
|
||||||
|
|
||||||
// Context hooks
|
// Global context hooks
|
||||||
useCurrentUser,
|
useCurrentUser,
|
||||||
useGreet,
|
|
||||||
|
|
||||||
// Refresh hooks
|
// Refresh hooks
|
||||||
|
useMizanRefresh,
|
||||||
useDjangoRefresh,
|
useDjangoRefresh,
|
||||||
|
|
||||||
|
// Named context providers
|
||||||
|
LocalContext,
|
||||||
|
useGreet,
|
||||||
|
|
||||||
// Function hooks
|
// Function hooks
|
||||||
useEcho,
|
useEcho,
|
||||||
useAdd,
|
useAdd,
|
||||||
@@ -56,14 +63,14 @@ export {
|
|||||||
useJwtRefresh,
|
useJwtRefresh,
|
||||||
|
|
||||||
// Re-exports from mizan library
|
// Re-exports from mizan library
|
||||||
usemizan,
|
useMizan,
|
||||||
usemizanStatus,
|
useMizanStatus,
|
||||||
usePush,
|
usePush,
|
||||||
DjangoError,
|
DjangoError,
|
||||||
type ConnectionStatus,
|
type ConnectionStatus,
|
||||||
type PushMessage,
|
type PushMessage,
|
||||||
type PushListener,
|
type PushListener,
|
||||||
} from './generated.django'
|
} from './generated.provider'
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// Channel Hooks
|
// Channel Hooks
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { useState, useEffect, useRef } from 'react'
|
|||||||
|
|
||||||
// Generated typed hooks — the actual mizan API
|
// Generated typed hooks — the actual mizan API
|
||||||
import {
|
import {
|
||||||
DjangoContext,
|
MizanContext,
|
||||||
useEcho,
|
useEcho,
|
||||||
useAdd,
|
useAdd,
|
||||||
useMultiply,
|
useMultiply,
|
||||||
@@ -25,9 +25,9 @@ import {
|
|||||||
useCurrentUser,
|
useCurrentUser,
|
||||||
DjangoError,
|
DjangoError,
|
||||||
useMizan,
|
useMizan,
|
||||||
} from './api/generated.django'
|
useChatChannel,
|
||||||
|
} from './api'
|
||||||
import { useContactForm, useLoginForm } from './api/generated.forms'
|
import { useContactForm, useLoginForm } from './api/generated.forms'
|
||||||
import { useChatChannel } from './api/generated.channels.hooks'
|
|
||||||
|
|
||||||
// ─── Fixture router ─────────────────────────────────────────────────────────
|
// ─── Fixture router ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -228,7 +228,7 @@ function FormContactSubmit() {
|
|||||||
// ─── Channel fixtures ───────────────────────────────────────────────────────
|
// ─── Channel fixtures ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
function ChannelChatFixture() {
|
function ChannelChatFixture() {
|
||||||
// DjangoContext already includes ChannelProvider
|
// MizanContext already includes ChannelProvider
|
||||||
return <ChannelChat />
|
return <ChannelChat />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { createRoot } from 'react-dom/client'
|
import { createRoot } from 'react-dom/client'
|
||||||
import { DjangoContext } from './api/generated.django'
|
import { MizanContext } from './api'
|
||||||
import { Fixtures } from './fixtures'
|
import { Fixtures } from './fixtures'
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<DjangoContext baseUrl="/api/mizan">
|
<MizanContext baseUrl="/api/mizan">
|
||||||
<Fixtures />
|
<Fixtures />
|
||||||
</DjangoContext>
|
</MizanContext>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -84,5 +84,5 @@ export async function fetchMizanSchema(source, cwd) {
|
|||||||
if (!source.django) {
|
if (!source.django) {
|
||||||
throw new Error('mizan schema export requires django source configuration')
|
throw new Error('mizan schema export requires django source configuration')
|
||||||
}
|
}
|
||||||
return runDjangoCommand(source, cwd, 'export_mizan_schema')
|
return runDjangoCommand(source, cwd, 'export_djarea_schema')
|
||||||
}
|
}
|
||||||
|
|||||||
16
packages/mizan-django/generate/package.json
Normal file
16
packages/mizan-django/generate/package.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "generate",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"type": "commonjs",
|
||||||
|
"dependencies": {
|
||||||
|
"openapi-typescript": "^7.13.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user