Docs carry the shape of the system, not its status
MIZAN.md's phase-numbered implementation order, PRODUCT_ARCHITECTURE's "Deferred until Render revenue funds it" and "Shipped in", PSR_VS_EDGE's current-state section, and the READMEs' passing-test counts were all reporting where the work stood rather than what the system is. OWED_SURFACE keeps its subject — surface that is specified but unbuilt — stated as the shape each unit owes. The channel sections follow the renamed slots: Params / ClientMessage / ServerMessage, and Channel as the base class on both backends. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,11 +5,10 @@ function. Typed React client generated. Invalidation automatic.
|
||||
|
||||
## Scope
|
||||
|
||||
mizan-fastapi targets the **AFI-common subset** — RPC dispatch, context
|
||||
bundling, JSON-body invalidation, and auth gating. Forms, Channels, Shapes,
|
||||
and SSR are out of scope for the FastAPI adapter — FastAPI projects use
|
||||
native equivalents (Pydantic, native WebSockets, ORM-of-choice, FastAPI's
|
||||
own SSR ecosystem).
|
||||
mizan-fastapi's surface is RPC dispatch, context bundling, JSON-body
|
||||
invalidation, auth gating, and channels over a multiplexed WebSocket. Forms,
|
||||
Shapes, and SSR sit outside that surface — a FastAPI project reaches for its
|
||||
own native equivalents (Pydantic, ORM-of-choice, FastAPI's SSR ecosystem).
|
||||
|
||||
## Install
|
||||
|
||||
@@ -29,11 +28,13 @@ from mizan_fastapi import (
|
||||
mizan_exception_handler,
|
||||
mizan_validation_handler,
|
||||
router as mizan_router,
|
||||
ws_router,
|
||||
)
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
app.include_router(mizan_router, prefix="/api/mizan")
|
||||
app.include_router(ws_router, prefix="/api/mizan")
|
||||
app.add_exception_handler(MizanError, mizan_exception_handler)
|
||||
app.add_exception_handler(RequestValidationError, mizan_validation_handler)
|
||||
```
|
||||
@@ -82,9 +83,56 @@ a dedicated `clients.py` imported during startup.
|
||||
@client(rev=2) # cache revision (busts on bump)
|
||||
```
|
||||
|
||||
`websocket=True`, Forms, and Channels parameters are accepted by the
|
||||
decorator (they're a `mizan-core` primitive) but ignored by mizan-fastapi —
|
||||
those features only have effect when paired with mizan-django.
|
||||
Forms parameters are accepted by the decorator (they're a `mizan-core`
|
||||
primitive) and carry no meaning to this adapter.
|
||||
|
||||
## Channels
|
||||
|
||||
A channel is a named fan-out over the WebSocket `ws_router` serves. Subclass
|
||||
`Channel`, declare whichever payload models the channel carries, and register
|
||||
it. The three model names are read from the client's side: `Params` keys the
|
||||
fan-out, `ClientMessage` travels up, `ServerMessage` travels down.
|
||||
|
||||
```python
|
||||
from mizan_fastapi import Channel, register_channel
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Chat(Channel):
|
||||
|
||||
class Params(BaseModel):
|
||||
room: str
|
||||
|
||||
class ClientMessage(BaseModel):
|
||||
text: str
|
||||
|
||||
class ServerMessage(BaseModel):
|
||||
user: str
|
||||
text: str
|
||||
|
||||
def authorize(self, params: Params) -> bool:
|
||||
return True
|
||||
|
||||
def receive(self, params: Params, msg: ClientMessage) -> ServerMessage:
|
||||
return self.ServerMessage(user="anon", text=msg.text)
|
||||
|
||||
|
||||
register_channel(Chat, "chat")
|
||||
```
|
||||
|
||||
Server code pushes to a group from anywhere:
|
||||
|
||||
```python
|
||||
await Chat.push(Chat.ServerMessage(user="system", text="hello"), room="general")
|
||||
```
|
||||
|
||||
Registered channels contribute to the exported IR, so codegen emits the
|
||||
`<Pascal>Params` / `<Pascal>ClientMessage` / `<Pascal>ServerMessage` types and
|
||||
the matching frontend hook.
|
||||
|
||||
Group membership lives in the process that holds the socket, so a push reaches
|
||||
only the subscribers attached to that process. Fan-out that spans processes is
|
||||
a shared broker in front of `broadcast`.
|
||||
|
||||
## Auth integration
|
||||
|
||||
@@ -169,8 +217,6 @@ python -m mizan_fastapi.ir <module>
|
||||
|
||||
Imports the named module (which must register every `@client` function as
|
||||
import-time side effects), then prints the Mizan KDL IR to stdout.
|
||||
Mirrors mizan-django's `manage.py export_mizan_ir` so the codegen consumes
|
||||
either backend the same subprocess way.
|
||||
|
||||
## Architecture
|
||||
|
||||
@@ -183,4 +229,4 @@ emit equivalent schemas for the same registered functions. See
|
||||
|
||||
A live e2e harness exercises this adapter end-to-end at
|
||||
`examples/fastapi-react-site/` (real Chromium → React with generated hooks
|
||||
→ FastAPI server, 14/14 Playwright tests).
|
||||
→ FastAPI server, driven by Playwright).
|
||||
|
||||
Reference in New Issue
Block a user