Mizan codegen substrate: Rust kernel + Rust codegen binary, JS generator deleted

The Mizan codegen substrate moves off JavaScript template-literal emission
onto a compiled Rust binary that consumes the same OpenAPI + x-mizan-* IR
the JS substrate consumed. Three structural wins fall out of one move:

1. Moat closes. The codegen logic (how `affects` becomes auto-invalidation,
   how named contexts collapse onto bundled fetches, how the registry-to-
   Provider mapping is shaped) ships compiled instead of as source bytes
   in every consumer's node_modules.

2. Pattern F (lines.push append-walls) becomes structurally unauthorable.
   The emit substrate is askama templates in templates/<target>/*.j2 —
   actual target-language files with {{ ... }} substitution markers,
   syntax-highlighted natively, type-checked against the render context
   structs at compile time. The Rust emit modules build typed render
   contexts and call .render(); no string-builder surface exists.

3. OpenAPI `default`-bearing fields now emit as non-optional in TS / Python
   / Rust — the server always populates them, so consumer code reads them
   without nullable checks. Surfaced by Blazr's typecheck on regeneration.

Layout:
  frontends/mizan-rust/        — Rust port of @mizan/base; #[cfg(feature="pyo3")]
                                 exposes PyMizanClient for the Python target.
  protocol/mizan-codegen/      — codegen binary source + askama templates.
  protocol/mizan-generate/     — npm-package shim. bin/launcher.mjs dispatches
                                 to the platform-appropriate prebuilt binary.
                                 Old generator/ JS tree deleted.
  tests/rust/                  — wire-parity drivers. drive_kernel exercises
                                 raw client.call() / fetch_context(); drive_emitted
                                 exercises the typed crate the codegen emits.
  tests/afi/afi_codegen_app.py — codegen entrypoint module (imports + registers).
  backends/mizan-fastapi/.../schema.py — adds outputNullable so the Rust
                                 codegen can wrap T | None responses in Option<T>.

Verification:
  - 20 mizan-codegen tests green (IR deserialization, byte-equivalent
    parity vs JS baseline for stage1/rust/python/react/vue/svelte,
    structural test for channels).
  - tests/rust/run_wire_parity.py — 12/12 probes green via the Rust binary
    driving the FastAPI fixture end-to-end.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-17 18:26:32 -04:00
parent c15c6f3e14
commit 43bcf3f26f
114 changed files with 11090 additions and 2342 deletions

View File

@@ -0,0 +1,685 @@
{
"openapi": "3.1.0",
"info": {
"title": "mizan Server Functions",
"description": "Auto-generated schema for mizan server functions",
"version": "1.0.0"
},
"paths": {
"/mizan/echo": {
"post": {
"summary": "Echoes the input back.",
"operationId": "echo",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/echoInput"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/echoOutput"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
},
"x-mizan": {
"transport": "http",
"isContext": false
}
}
},
"/mizan/whoami": {
"post": {
"summary": "Returns the current user identity.",
"operationId": "whoami",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/whoamiOutput"
}
}
}
}
},
"x-mizan": {
"transport": "http",
"isContext": false
}
}
},
"/mizan/user_profile": {
"post": {
"summary": "One half of the user context.",
"operationId": "userProfile",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/userProfileInput"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/userProfileOutput"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
},
"x-mizan": {
"transport": "http",
"isContext": "user"
}
}
},
"/mizan/user_orders": {
"post": {
"summary": "Other half of the user context \u2014 same param, proves param elevation.",
"operationId": "userOrders",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/userOrdersInput"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/userOrdersOutput"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
},
"x-mizan": {
"transport": "http",
"isContext": "user"
}
}
},
"/mizan/update_profile": {
"post": {
"summary": "Mutation declaring affects on the user context.",
"operationId": "updateProfile",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/updateProfileInput"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/updateProfileOutput"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
},
"x-mizan": {
"transport": "http",
"isContext": false
}
}
},
"/mizan/find_user": {
"post": {
"summary": "Optional return \u2014 exercises Pydantic `T | None` schema introspection.",
"operationId": "findUser",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/findUserInput"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/findUserOutput"
},
{
"type": "null"
}
],
"title": "Response Finduser"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
},
"x-mizan": {
"transport": "http",
"isContext": false
}
}
},
"/mizan/rename_user": {
"post": {
"summary": "Merge target \u2014 kernel splices return value into the user context.",
"operationId": "renameUser",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/renameUserInput"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/renameUserOutput"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
},
"x-mizan": {
"transport": "http",
"isContext": false
}
}
}
},
"components": {
"schemas": {
"HTTPValidationError": {
"properties": {
"detail": {
"items": {
"$ref": "#/components/schemas/ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
},
"OrderOutput": {
"properties": {
"id": {
"type": "integer",
"title": "Id"
},
"user_id": {
"type": "integer",
"title": "User Id"
},
"total": {
"type": "integer",
"title": "Total"
}
},
"type": "object",
"required": [
"id",
"user_id",
"total"
],
"title": "OrderOutput"
},
"ValidationError": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
},
"input": {
"title": "Input"
},
"ctx": {
"type": "object",
"title": "Context"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
},
"echoInput": {
"properties": {
"text": {
"type": "string",
"title": "Text"
}
},
"type": "object",
"required": [
"text"
],
"title": "echoInput"
},
"echoOutput": {
"properties": {
"message": {
"type": "string",
"title": "Message"
}
},
"type": "object",
"required": [
"message"
],
"title": "echoOutput"
},
"findUserInput": {
"properties": {
"user_id": {
"type": "integer",
"title": "User Id"
}
},
"type": "object",
"required": [
"user_id"
],
"title": "findUserInput"
},
"findUserOutput": {
"properties": {
"user_id": {
"type": "integer",
"title": "User Id"
},
"name": {
"type": "string",
"title": "Name"
}
},
"type": "object",
"required": [
"user_id",
"name"
],
"title": "findUserOutput"
},
"renameUserInput": {
"properties": {
"user_id": {
"type": "integer",
"title": "User Id"
},
"name": {
"type": "string",
"title": "Name"
}
},
"type": "object",
"required": [
"user_id",
"name"
],
"title": "renameUserInput"
},
"renameUserOutput": {
"properties": {
"user_id": {
"type": "integer",
"title": "User Id"
},
"name": {
"type": "string",
"title": "Name"
}
},
"type": "object",
"required": [
"user_id",
"name"
],
"title": "renameUserOutput"
},
"updateProfileInput": {
"properties": {
"user_id": {
"type": "integer",
"title": "User Id"
},
"name": {
"type": "string",
"title": "Name"
}
},
"type": "object",
"required": [
"user_id",
"name"
],
"title": "updateProfileInput"
},
"updateProfileOutput": {
"properties": {
"ok": {
"type": "boolean",
"title": "Ok"
}
},
"type": "object",
"required": [
"ok"
],
"title": "updateProfileOutput"
},
"userOrdersInput": {
"properties": {
"user_id": {
"type": "integer",
"title": "User Id"
}
},
"type": "object",
"required": [
"user_id"
],
"title": "userOrdersInput"
},
"userOrdersOutput": {
"items": {
"$ref": "#/components/schemas/OrderOutput"
},
"type": "array",
"title": "userOrdersOutput"
},
"userProfileInput": {
"properties": {
"user_id": {
"type": "integer",
"title": "User Id"
}
},
"type": "object",
"required": [
"user_id"
],
"title": "userProfileInput"
},
"userProfileOutput": {
"properties": {
"user_id": {
"type": "integer",
"title": "User Id"
},
"name": {
"type": "string",
"title": "Name"
}
},
"type": "object",
"required": [
"user_id",
"name"
],
"title": "userProfileOutput"
},
"whoamiOutput": {
"properties": {
"email": {
"type": "string",
"title": "Email"
},
"authenticated": {
"type": "boolean",
"title": "Authenticated"
}
},
"type": "object",
"required": [
"email",
"authenticated"
],
"title": "whoamiOutput"
}
}
},
"x-mizan-functions": [
{
"name": "echo",
"camelName": "echo",
"hasInput": true,
"inputType": "echoInput",
"outputType": "echoOutput",
"outputNullable": false,
"transport": "http",
"isContext": false,
"isForm": false,
"formName": null,
"formRole": null
},
{
"name": "whoami",
"camelName": "whoami",
"hasInput": false,
"inputType": null,
"outputType": "whoamiOutput",
"outputNullable": false,
"transport": "http",
"isContext": false,
"isForm": false,
"formName": null,
"formRole": null
},
{
"name": "user_profile",
"camelName": "userProfile",
"hasInput": true,
"inputType": "userProfileInput",
"outputType": "userProfileOutput",
"outputNullable": false,
"transport": "http",
"isContext": "user",
"isForm": false,
"formName": null,
"formRole": null
},
{
"name": "user_orders",
"camelName": "userOrders",
"hasInput": true,
"inputType": "userOrdersInput",
"outputType": "userOrdersOutput",
"outputNullable": false,
"transport": "http",
"isContext": "user",
"isForm": false,
"formName": null,
"formRole": null
},
{
"name": "update_profile",
"camelName": "updateProfile",
"hasInput": true,
"inputType": "updateProfileInput",
"outputType": "updateProfileOutput",
"outputNullable": false,
"transport": "http",
"isContext": false,
"isForm": false,
"formName": null,
"formRole": null,
"affects": [
{
"type": "context",
"name": "user"
}
]
},
{
"name": "find_user",
"camelName": "findUser",
"hasInput": true,
"inputType": "findUserInput",
"outputType": "findUserOutput",
"outputNullable": true,
"transport": "http",
"isContext": false,
"isForm": false,
"formName": null,
"formRole": null
},
{
"name": "rename_user",
"camelName": "renameUser",
"hasInput": true,
"inputType": "renameUserInput",
"outputType": "renameUserOutput",
"outputNullable": false,
"transport": "http",
"isContext": false,
"isForm": false,
"formName": null,
"formRole": null,
"merge": [
"user"
]
}
],
"x-mizan-contexts": {
"user": {
"functions": [
"user_profile",
"user_orders"
],
"params": {
"user_id": {
"type": "integer",
"sharedBy": [
"user_profile",
"user_orders"
],
"required": true
}
}
}
}
}

View File

@@ -0,0 +1,55 @@
{
"x-mizan-channels": [
{
"name": "chat",
"pascalName": "Chat",
"hasParams": true,
"hasReactMessage": true,
"hasDjangoMessage": true,
"paramsType": "ChatChannelParams",
"reactMessageType": "ChatReactMessage",
"djangoMessageType": "ChatDjangoMessage"
},
{
"name": "notifications",
"pascalName": "Notifications",
"hasParams": false,
"hasReactMessage": false,
"hasDjangoMessage": true,
"djangoMessageType": "NotificationsDjangoMessage"
}
],
"components": {
"schemas": {
"ChatChannelParams": {
"type": "object",
"properties": {
"room_id": { "type": "string" }
},
"required": ["room_id"]
},
"ChatReactMessage": {
"type": "object",
"properties": {
"text": { "type": "string" }
},
"required": ["text"]
},
"ChatDjangoMessage": {
"type": "object",
"properties": {
"text": { "type": "string" },
"from_user": { "type": "string" }
},
"required": ["text", "from_user"]
},
"NotificationsDjangoMessage": {
"type": "object",
"properties": {
"body": { "type": "string" }
},
"required": ["body"]
}
}
}
}

View File

@@ -0,0 +1,4 @@
# AUTO-GENERATED by mizan — do not edit
from .client import MizanClient # noqa: F401
from .types import * # noqa: F401, F403

View File

@@ -0,0 +1,67 @@
# AUTO-GENERATED by mizan — do not edit
from __future__ import annotations
from collections.abc import Callable
from typing import Any
# Built from frontends/mizan-rust with `maturin develop --features pyo3`.
from mizan_rust import PyMizanClient, PyContextSubscription
from .types import * # noqa: F401, F403
from .types import BaseModel # re-import for the synthesized ContextData classes
class MizanClient:
"""Typed Python facade over the PyO3 mizan-rust kernel."""
def __init__(self, base_url: str, *, session: bool = False,
csrf_cookie_name: str = "csrftoken",
csrf_header_name: str = "X-CSRFToken") -> None:
self._inner = PyMizanClient(
base_url,
session=session,
csrf_cookie_name=csrf_cookie_name,
csrf_header_name=csrf_header_name,
)
def fetch_user_context(self, user_id: int) -> "UserContextData":
raw = self._inner.fetch_context("user", {"user_id": user_id})
return UserContextData(**raw)
def subscribe_user_context(self, user_id: int,
callback: Callable[[dict[str, Any]], None]) -> PyContextSubscription:
return self._inner.subscribe_context("user", {"user_id": user_id}, callback)
def call_echo(self, args: EchoInput) -> EchoOutput:
raw = self._inner.call("echo", args.model_dump())
return EchoOutput(**raw)
def call_whoami(self) -> WhoamiOutput:
raw = self._inner.call("whoami", {})
return WhoamiOutput(**raw)
def call_update_profile(self, args: UpdateProfileInput) -> UpdateProfileOutput:
raw = self._inner.call("update_profile", args.model_dump())
return UpdateProfileOutput(**raw)
def call_find_user(self, args: FindUserInput) -> FindUserOutput | None:
raw = self._inner.call("find_user", args.model_dump())
return FindUserOutput(**raw) if raw is not None else None
def call_rename_user(self, args: RenameUserInput) -> RenameUserOutput:
raw = self._inner.call("rename_user", args.model_dump())
return RenameUserOutput(**raw)
def invalidate(self, context: str) -> None:
self._inner.invalidate(context)
def invalidate_scoped(self, context: str, params: dict[str, Any]) -> None:
self._inner.invalidate_scoped(context, params)
# ── Context data shapes (per-context bundle) ──────────────────────────────
class UserContextData(BaseModel):
"""Bundled return of fetch_user_context."""
user_profile: UserProfileOutput
user_orders: UserOrdersOutput

View File

@@ -0,0 +1,66 @@
# AUTO-GENERATED by mizan — do not edit
from __future__ import annotations
from typing import Any, Literal
from pydantic import BaseModel
class HTTPValidationError(BaseModel):
detail: list[ValidationError] | None = None
class OrderOutput(BaseModel):
id: int
user_id: int
total: int
class ValidationError(BaseModel):
loc: list[Any]
msg: str
r#type: str
input: Any | None = None
ctx: dict[str, Any] | None = None
class EchoInput(BaseModel):
text: str
class EchoOutput(BaseModel):
message: str
class FindUserInput(BaseModel):
user_id: int
class FindUserOutput(BaseModel):
user_id: int
name: str
class RenameUserInput(BaseModel):
user_id: int
name: str
class RenameUserOutput(BaseModel):
user_id: int
name: str
class UpdateProfileInput(BaseModel):
user_id: int
name: str
class UpdateProfileOutput(BaseModel):
ok: bool
class UserOrdersInput(BaseModel):
user_id: int
UserOrdersOutput = list[OrderOutput]
class UserProfileInput(BaseModel):
user_id: int
class UserProfileOutput(BaseModel):
user_id: int
name: str
class WhoamiOutput(BaseModel):
email: str
authenticated: bool

View File

@@ -0,0 +1,157 @@
'use client'
// AUTO-GENERATED by mizan — do not edit
import {
createContext,
useCallback,
useContext,
useEffect,
useRef,
useState,
useSyncExternalStore,
type ReactNode,
} from 'react'
import {
configure,
initSession,
mizanCall,
mizanFetch,
MizanError,
registerContext,
type ContextState,
} from '@mizan/base'
import { fetchUserContext, type UserContextData, type UserContextParams, callUpdateProfile, callEcho, callWhoami, callFindUser, callRenameUser, type userProfileOutput, type userOrdersOutput } from './index'
// Internal — runs inside a Provider, registers with the kernel exactly once.
function useContextSubscription<T>(
name: string,
params: Record<string, any>,
fetchFn: () => Promise<T>,
initialData?: T,
): ContextState<T> {
const ref = useRef<ReturnType<typeof registerContext> | null>(null)
if (!ref.current) {
ref.current = registerContext(name, params, fetchFn, initialData)
}
const handle = ref.current
useEffect(() => {
if (handle.getState().status === 'idle') handle.refetch()
return () => handle.unregister()
}, [handle])
return useSyncExternalStore(handle.subscribe, handle.getState, handle.getState)
}
// Internal — wraps an imperative call() with isPending / error state.
interface MutationHook<TArgs, TResult> {
mutate: (args: TArgs) => Promise<TResult>
isPending: boolean
error: Error | null
}
function useMutation<TArgs, TResult>(
callFn: (args: TArgs) => Promise<TResult>,
): MutationHook<TArgs, TResult> {
const [isPending, setIsPending] = useState(false)
const [error, setError] = useState<Error | null>(null)
const mutate = useCallback(async (args: TArgs) => {
setIsPending(true)
setError(null)
try {
return await callFn(args)
} catch (e) {
setError(e as Error)
throw e
} finally {
setIsPending(false)
}
}, [callFn])
return { mutate, isPending, error }
}
// ── User Context ──
const UserCtx = createContext<ContextState<UserContextData> | null>(null)
export function UserContext({ children, ...params }: UserContextParams & { children: ReactNode }) {
const state = useContextSubscription('user', params, () => fetchUserContext(params))
return <UserCtx.Provider value={state}>{children}</UserCtx.Provider>
}
export function useUserContext(): ContextState<UserContextData> {
const ctx = useContext(UserCtx)
if (!ctx) throw new Error('useUserContext requires <UserContext>')
return ctx
}
export function useUserProfile(): userProfileOutput | null {
return useUserContext().data?.user_profile ?? null
}
export function useUserOrders(): userOrdersOutput | null {
return useUserContext().data?.user_orders ?? null
}
export function useUpdateProfile() {
return useMutation<Parameters<typeof callUpdateProfile>[0], Awaited<ReturnType<typeof callUpdateProfile>>>(callUpdateProfile)
}
export function useEcho() {
return useMutation<Parameters<typeof callEcho>[0], Awaited<ReturnType<typeof callEcho>>>(callEcho)
}
export function useWhoami() {
return useMutation<void, Awaited<ReturnType<typeof callWhoami>>>(() => callWhoami() as any)
}
export function useFindUser() {
return useMutation<Parameters<typeof callFindUser>[0], Awaited<ReturnType<typeof callFindUser>>>(callFindUser)
}
export function useRenameUser() {
return useMutation<Parameters<typeof callRenameUser>[0], Awaited<ReturnType<typeof callRenameUser>>>(callRenameUser)
}
// ── MizanContext root provider ──
export interface MizanContextProps {
/** Base URL for protocol endpoints. Defaults to "/api/mizan". */
baseUrl?: string
/** Set to `false` for backends without a `/session/` endpoint (e.g. FastAPI). */
session?: boolean
children: ReactNode
}
/**
* Root provider — calls configure() once and mounts the global context (if defined).
* Must wrap any component using Mizan-generated hooks.
*/
export function MizanContext({ baseUrl, session, children }: MizanContextProps) {
const configured = useRef(false)
if (!configured.current) {
const opts: Parameters<typeof configure>[0] = {}
if (baseUrl !== undefined) opts.baseUrl = baseUrl
if (session !== undefined) opts.session = session
if (Object.keys(opts).length > 0) configure(opts)
configured.current = true
}
return <>{children}</>
}
// ── Imperative escape hatch ──
/**
* Returns the imperative kernel API. For test harnesses or rare cases where
* a typed generated hook does not fit. Most app code should use the typed hooks.
*/
export function useMizan() {
return { call: mizanCall, fetch: mizanFetch }
}
export type { ContextState } from '@mizan/base'
export { configure, initSession, MizanError } from '@mizan/base'

View File

@@ -0,0 +1,10 @@
[package]
name = "fixture_client"
version = "0.1.0"
edition = "2021"
[dependencies]
mizan-rust = { path = "../../../frontends/mizan-rust" }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tokio = { version = "1", features = ["rt", "macros"] }

View File

@@ -0,0 +1,3 @@
// AUTO-GENERATED by mizan — do not edit
pub mod user;

View File

@@ -0,0 +1,29 @@
// AUTO-GENERATED by mizan — do not edit
use serde::{Deserialize, Serialize};
use serde_json::Value;
use mizan_rust::{MizanClient, MizanError};
use crate::types::{UserProfileOutput, UserOrdersOutput};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserContextData {
pub user_profile: UserProfileOutput,
pub user_orders: UserOrdersOutput,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserContextParams {
pub user_id: i64,
}
pub async fn fetch_user_context(
client: &MizanClient,
params: &UserContextParams,
) -> Result<UserContextData, MizanError> {
let params_value = serde_json::to_value(params).unwrap_or(Value::Object(Default::default()));
let raw = client.fetch_context("user", &params_value).await?;
serde_json::from_value(raw)
.map_err(|e| MizanError::transport(format!("decode user context: {e}")))
}

View File

@@ -0,0 +1,14 @@
// AUTO-GENERATED by mizan — do not edit
use serde_json::Value;
use mizan_rust::{MizanClient, MizanError};
use crate::types::{EchoOutput, EchoInput};
pub async fn call_echo(client: &MizanClient, args: &EchoInput) -> Result<EchoOutput, MizanError> {
let args_value = serde_json::to_value(args).unwrap_or(Value::Object(Default::default()));
let raw = client.call("echo", args_value).await?;
serde_json::from_value(raw)
.map_err(|e| MizanError::transport(format!("decode echo result: {e}")))
}

View File

@@ -0,0 +1,14 @@
// AUTO-GENERATED by mizan — do not edit
use serde_json::Value;
use mizan_rust::{MizanClient, MizanError};
use crate::types::{FindUserOutput, FindUserInput};
pub async fn call_find_user(client: &MizanClient, args: &FindUserInput) -> Result<Option<FindUserOutput>, MizanError> {
let args_value = serde_json::to_value(args).unwrap_or(Value::Object(Default::default()));
let raw = client.call("find_user", args_value).await?;
serde_json::from_value(raw)
.map_err(|e| MizanError::transport(format!("decode find_user result: {e}")))
}

View File

@@ -0,0 +1,6 @@
// AUTO-GENERATED by mizan — do not edit
pub mod echo;
pub mod find_user;
pub mod rename_user;
pub mod whoami;

View File

@@ -0,0 +1,14 @@
// AUTO-GENERATED by mizan — do not edit
use serde_json::Value;
use mizan_rust::{MizanClient, MizanError};
use crate::types::{RenameUserOutput, RenameUserInput};
pub async fn call_rename_user(client: &MizanClient, args: &RenameUserInput) -> Result<RenameUserOutput, MizanError> {
let args_value = serde_json::to_value(args).unwrap_or(Value::Object(Default::default()));
let raw = client.call("rename_user", args_value).await?;
serde_json::from_value(raw)
.map_err(|e| MizanError::transport(format!("decode rename_user result: {e}")))
}

View File

@@ -0,0 +1,14 @@
// AUTO-GENERATED by mizan — do not edit
use serde_json::Value;
use mizan_rust::{MizanClient, MizanError};
use crate::types::{WhoamiOutput};
pub async fn call_whoami(client: &MizanClient) -> Result<WhoamiOutput, MizanError> {
let args_value = Value::Object(Default::default());
let raw = client.call("whoami", args_value).await?;
serde_json::from_value(raw)
.map_err(|e| MizanError::transport(format!("decode whoami result: {e}")))
}

View File

@@ -0,0 +1,8 @@
// AUTO-GENERATED by mizan — do not edit
pub mod types;
pub mod contexts;
pub mod mutations;
pub mod functions;
pub use mizan_rust::{MizanClient, MizanConfig, MizanError};

View File

@@ -0,0 +1,3 @@
// AUTO-GENERATED by mizan — do not edit
pub mod update_profile;

View File

@@ -0,0 +1,14 @@
// AUTO-GENERATED by mizan — do not edit
use serde_json::Value;
use mizan_rust::{MizanClient, MizanError};
use crate::types::{UpdateProfileOutput, UpdateProfileInput};
pub async fn call_update_profile(client: &MizanClient, args: &UpdateProfileInput) -> Result<UpdateProfileOutput, MizanError> {
let args_value = serde_json::to_value(args).unwrap_or(Value::Object(Default::default()));
let raw = client.call("update_profile", args_value).await?;
serde_json::from_value(raw)
.map_err(|e| MizanError::transport(format!("decode update_profile result: {e}")))
}

View File

@@ -0,0 +1,98 @@
// AUTO-GENERATED by mizan — do not edit
#![allow(non_camel_case_types)]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HTTPValidationError {
pub detail: Option<Vec<ValidationError>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OrderOutput {
pub id: i64,
pub user_id: i64,
pub total: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidationError {
pub loc: Vec<serde_json::Value>,
pub msg: String,
#[serde(rename = "type")]
pub r#type: String,
pub input: Option<serde_json::Value>,
pub ctx: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EchoInput {
pub text: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EchoOutput {
pub message: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FindUserInput {
pub user_id: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FindUserOutput {
pub user_id: i64,
pub name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RenameUserInput {
pub user_id: i64,
pub name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RenameUserOutput {
pub user_id: i64,
pub name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpdateProfileInput {
pub user_id: i64,
pub name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpdateProfileOutput {
pub ok: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserOrdersInput {
pub user_id: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(transparent)]
pub struct UserOrdersOutput(pub Vec<OrderOutput>);
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserProfileInput {
pub user_id: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserProfileOutput {
pub user_id: i64,
pub name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WhoamiOutput {
pub email: String,
pub authenticated: bool,
}

View File

@@ -0,0 +1,18 @@
// AUTO-GENERATED by mizan — do not edit
import { mizanFetch } from '@mizan/base'
import type { userProfileOutput, userOrdersOutput } from '../types'
export interface UserContextData {
user_profile: userProfileOutput
user_orders: userOrdersOutput
}
export interface UserContextParams {
user_id: number
}
export function fetchUserContext(params: UserContextParams): Promise<UserContextData> {
return mizanFetch('user', params)
}

View File

@@ -0,0 +1,9 @@
// AUTO-GENERATED by mizan — do not edit
import { mizanCall } from '@mizan/base'
import type { echoInput, echoOutput } from '../types'
export function callEcho(args: echoInput): Promise<echoOutput> {
return mizanCall('echo', args)
}

View File

@@ -0,0 +1,9 @@
// AUTO-GENERATED by mizan — do not edit
import { mizanCall } from '@mizan/base'
import type { findUserInput, findUserOutput } from '../types'
export function callFindUser(args: findUserInput): Promise<findUserOutput> {
return mizanCall('find_user', args)
}

View File

@@ -0,0 +1,9 @@
// AUTO-GENERATED by mizan — do not edit
import { mizanCall } from '@mizan/base'
import type { renameUserInput, renameUserOutput } from '../types'
export function callRenameUser(args: renameUserInput): Promise<renameUserOutput> {
return mizanCall('rename_user', args)
}

View File

@@ -0,0 +1,9 @@
// AUTO-GENERATED by mizan — do not edit
import { mizanCall } from '@mizan/base'
import type { whoamiOutput } from '../types'
export function callWhoami(): Promise<whoamiOutput> {
return mizanCall('whoami', {})
}

View File

@@ -0,0 +1,11 @@
// AUTO-GENERATED by mizan — do not edit
export * from './types'
export { fetchUserContext, type UserContextData, type UserContextParams } from './contexts/user'
export { callEcho } from './functions/echo'
export { callWhoami } from './functions/whoami'
export { callUpdateProfile } from './mutations/updateProfile'
export { callFindUser } from './functions/findUser'
export { callRenameUser } from './functions/renameUser'

View File

@@ -0,0 +1,9 @@
// AUTO-GENERATED by mizan — do not edit
import { mizanCall } from '@mizan/base'
import type { updateProfileInput, updateProfileOutput } from '../types'
export function callUpdateProfile(args: updateProfileInput): Promise<updateProfileOutput> {
return mizanCall('update_profile', args)
}

View File

@@ -0,0 +1,29 @@
// AUTO-GENERATED by mizan — do not edit
import { readable, type Readable } from 'svelte/store'
import { registerContext, type ContextState } from '@mizan/base'
import { fetchUserContext, type UserContextData, type UserContextParams, callUpdateProfile, callEcho, callWhoami, callFindUser, callRenameUser } from '../index'
export function createUserContext(params: UserContextParams) {
const store = readable<ContextState<UserContextData>>(
{ data: null, status: 'idle', error: null },
(set) => {
const handle = registerContext('user', params, () => fetchUserContext(params))
const unsub = handle.subscribe(() => set(handle.getState()))
handle.refetch()
return () => { unsub(); handle.unregister() }
},
)
return store
}
export { callUpdateProfile } from '../index'
export { callEcho } from '../index'
export { callWhoami } from '../index'
export { callFindUser } from '../index'
export { callRenameUser } from '../index'
export type { ContextState } from '@mizan/base'
export { configure, initSession, MizanError } from '@mizan/base'

View File

@@ -0,0 +1,96 @@
// AUTO-GENERATED by mizan — do not edit
import { ref, computed, onMounted, onUnmounted, onServerPrefetch, type ComputedRef } from 'vue'
import { registerContext, type ContextState } from '@mizan/base'
import { fetchUserContext, type UserContextData, type UserContextParams, callUpdateProfile, callEcho, callWhoami, callFindUser, callRenameUser } from '../index'
export function useUserContext(params: UserContextParams) {
const state = ref<ContextState<UserContextData>>({ data: null, status: 'idle', error: null })
let handle: ReturnType<typeof registerContext> | null = null
onMounted(() => {
handle = registerContext('user', params, () => fetchUserContext(params))
handle.subscribe(() => { state.value = handle!.getState() })
handle.refetch()
})
onServerPrefetch(async () => {
handle = registerContext('user', params, () => fetchUserContext(params))
await handle.refetch()
state.value = handle.getState()
})
onUnmounted(() => { handle?.unregister() })
return {
state,
userProfile: computed(() => state.value.data?.user_profile ?? null) as ComputedRef<userProfileOutput | null>,
userOrders: computed(() => state.value.data?.user_orders ?? null) as ComputedRef<userOrdersOutput | null>,
loading: computed(() => state.value.status === 'loading'),
error: computed(() => state.value.error),
}
}
export function useUpdateProfile() {
const isPending = ref(false)
const error = ref<Error | null>(null)
async function mutate(args: Parameters<typeof callUpdateProfile>[0]) {
isPending.value = true; error.value = null
try { return await callUpdateProfile(args) }
catch (e) { error.value = e as Error; throw e }
finally { isPending.value = false }
}
return { mutate, isPending, error }
}
export function useEcho() {
const isPending = ref(false)
const error = ref<Error | null>(null)
async function mutate(args: Parameters<typeof callEcho>[0]) {
isPending.value = true; error.value = null
try { return await callEcho(args) }
catch (e) { error.value = e as Error; throw e }
finally { isPending.value = false }
}
return { mutate, isPending, error }
}
export function useWhoami() {
const isPending = ref(false)
const error = ref<Error | null>(null)
async function mutate() {
isPending.value = true; error.value = null
try { return await callWhoami() }
catch (e) { error.value = e as Error; throw e }
finally { isPending.value = false }
}
return { mutate, isPending, error }
}
export function useFindUser() {
const isPending = ref(false)
const error = ref<Error | null>(null)
async function mutate(args: Parameters<typeof callFindUser>[0]) {
isPending.value = true; error.value = null
try { return await callFindUser(args) }
catch (e) { error.value = e as Error; throw e }
finally { isPending.value = false }
}
return { mutate, isPending, error }
}
export function useRenameUser() {
const isPending = ref(false)
const error = ref<Error | null>(null)
async function mutate(args: Parameters<typeof callRenameUser>[0]) {
isPending.value = true; error.value = null
try { return await callRenameUser(args) }
catch (e) { error.value = e as Error; throw e }
finally { isPending.value = false }
}
return { mutate, isPending, error }
}
export type { ContextState } from '@mizan/base'
export { configure, initSession, MizanError } from '@mizan/base'