Files
mizan/ROADMAP.md
Ryth Azhur 8aa20111b4 Add X-Mizan-Invalidate header (second invalidation transport)
Mutation responses now carry invalidation via two transports:

1. JSON body: {"result": ..., "invalidate": ["user"]}
2. HTTP header: X-Mizan-Invalidate: user, notifications

Both are set on every mutation response. The JSON body is consumed
by the client runtime (mizanCall). The header is consumed by Edge
for CDN cache purging and by XHR responses for htmx-style apps.

Header format: comma-separated contexts, semicolon-separated params.
  X-Mizan-Invalidate: user;user_id=5, notifications

Also: _resolve_invalidation and _format_invalidate_header extracted
as reusable helpers for when return-type branching adds HttpResponse
support (view-path mutations will only use the header transport).

Updated ROADMAP.md with full v1 plan including both transports,
return-type branching, affects_params, and Edge manifest.

270 Django tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 15:41:31 -04:00

5.9 KiB

Mizan Roadmap

v1 — Django + React

Done

  • @client decoratorcontext=, affects=, auth=, websocket=
  • ReactContext class — type-safe context/affects references with linting
  • Named contexts — functions sharing a context name are grouped into one provider and one fetch
  • Context bundling endpointGET /api/mizan/ctx/<name>/ returns all functions in one response
  • Server-driven invalidation (JSON body) — mutation responses carry {"result": ..., "invalidate": [...]}
  • Scoped invalidation — runtime supports invalidate: [{context: "user", params: {user_id: 5}}]
  • Param elevation — shared params become required provider props, non-shared become optional
  • Schema exportx-mizan-functions + x-mizan-contexts for codegen
  • Auth guardsauth=True, auth='staff', auth='superuser', auth=callable
  • JWT + session auth — auto-detected, CSRF handled
  • Shapes — Pydantic + django-readers for typed query projections
  • WebSocket channels — real-time bidirectional communication
  • Codegen — generates typed React providers, hooks, mutations from schema
  • CDN-ready headersCache-Control, Vary, deterministic JSON on context GETs, no-store on mutations

Next: X-Mizan-Invalidate Header

Second invalidation transport. For view responses (redirects, HTML), invalidation goes in an HTTP header instead of the JSON body. Both transports are first-class AFI spec.

  • Header format: X-Mizan-Invalidate: user;user_id=5, notifications
  • Comma-separated contexts, semicolon-separated params per context
  • Decorator auto-adds header to any HttpResponse with affects=
  • Edge reads this header to purge cached pages
  • Runtime also reads it on XHR/fetch responses (htmx path)

Next: Return-Type Branching

@client serves both RPC developers (React/SPA) and view developers (htmx/templates). Return type determines behavior:

  • Data return (dict, Shape, BaseModel) → RPC path. Generates typed hooks. Invalidation in JSON body.
  • HttpResponse return (render, redirect) → View path. No codegen. Invalidation in X-Mizan-Invalidate header.

Same decorator. Same affects=. Same invalidation graph. Two paths.

Next: affects_params

Scoped invalidation with a lambda that extracts which params were affected:

@client(affects='user', affects_params=lambda req: {'user_id': req.user.pk})
def update_name(request, name: str) -> dict:
    ...

Produces invalidate: [{context: "user", params: {user_id: 5}}] in JSON body or X-Mizan-Invalidate: user;user_id=5 in header.

Next: Edge Manifest

mizan-generate --manifest compiles the decorator registry + Django URL conf into static JSON for Edge:

{
    "contexts": {
        "user": {
            "endpoints": ["/api/mizan/ctx/user/"],
            "views": ["/profile/:user_id/"],
            "params": ["user_id"]
        }
    }
}

Edge reads the manifest at deploy time. When it receives X-Mizan-Invalidate: user;user_id=5, it resolves URL patterns with params and purges /profile/5/ and /api/mizan/ctx/user/?user_id=5.

Generated alongside React code. Covers both RPC and view-path functions.

Next: Codegen Rewrite

Generated code uses the runtime directly (mizanFetch, mizanCall, registerContext) instead of the legacy MizanProvider pattern. Mutations have zero invalidation knowledge — the runtime reads the server response.

Next: SSR Bridge

Django renders React components server-side via a persistent Bun subprocess.

  • Bun worker: stdin/stdout JSON-RPC, renderToString, component registry
  • Django bridge: subprocess management, IPC, request synthesis
  • Template tag: {% mizan_render "ProfilePage" user_profile=profile %}
  • Hydration: window.__MIZAN_SSR_DATA__ consumed by generated providers
  • Generated contexts check SSR data before first fetch

Mizan Cloud (closed-source)

Mizan Edge

Cloudflare Workers for automatic edge caching.

  • Reads the Edge manifest to configure cache rules
  • Context GETs cached at edge, keyed by context name + params
  • Reads X-Mizan-Invalidate header from mutation responses to purge caches
  • Reads JSON invalidate key from RPC responses for the same purpose
  • Resolves URL patterns from manifest to purge view pages
  • Zero configuration — the manifest IS the cache policy

Mizan Render

SSR at the edge via Cloudflare Workers.

  • The Bun SSR bridge, running on Cloudflare instead of colocated with Django
  • Context data fetched from Django (or edge cache), rendered at the edge
  • HTML response streamed to the user from the nearest PoP

Mizan Deploy

One-command deployment for Django + React apps.

  • Container orchestration (AWS/Azure)
  • Edge + Render auto-configured
  • mizan deploy from the CLI
  • The Vercel experience for Django

Protocol Spec (AFI)

The protocol is the product. Two invalidation transports. Every endpoint CDN-ready.

Context fetch

GET /api/mizan/ctx/<name>/?param=value

200 OK
Cache-Control: public, max-age=0, stale-while-revalidate=300
Vary: Authorization, Cookie

{
    "function_a": { ... },
    "function_b": [ ... ]
}

Mutation call (RPC path — JSON body transport)

POST /api/mizan/call/
Cache-Control: no-store

{
    "result": { ... },
    "invalidate": ["context_name"]
}

Mutation call (View path — header transport)

POST /profile/update/
302 Found
Location: /profile/5/
Cache-Control: no-store
X-Mizan-Invalidate: user;user_id=5, notifications

Scoped invalidation (JSON)

{
    "result": { ... },
    "invalidate": [
        "notifications",
        { "context": "user", "params": { "user_id": 5 } }
    ]
}

Scoped invalidation (Header)

X-Mizan-Invalidate: user;user_id=5, notifications

Edge manifest

{
    "contexts": {
        "user": {
            "endpoints": ["/api/mizan/ctx/user/"],
            "views": ["/profile/:user_id/"],
            "params": ["user_id"]
        }
    }
}