c15c6f3e146faef0c9ff750a5be4ef2dcc42790f
6 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| cc887fb1f6 |
Move codegen out of mizan-django: protocol/mizan-generate/
The codegen consumes a schema from any backend and emits typed client code for any frontend — it doesn't belong inside a backend adapter. That placement was historical sediment from when there was only a Django backend; it predates the AFI generalization. New top-level slot: `protocol/` for protocol-level tooling. Tree is now: backends/ server protocol adapters frontends/ client kernel + per-framework adapters cores/ shared language-level primitives protocol/ protocol-level tooling workers/ runtime workers / bridges Codegen moves to `protocol/mizan-generate/`. Same file layout under `generator/` (cli.mjs, lib/), preserved via git mv. Package metadata cleaned up: - name: "generate" (placeholder) → "mizan-generate" - description filled in - type: module (cli.mjs is .mjs ESM, was previously declared "commonjs") - bin entry added so `npx mizan-generate --config <config.mjs>` works once the package is published, instead of `node path/to/cli.mjs`. Path-reference fixups: - backends/mizan-django/README.md: `node path/to/...` → `npx mizan-generate` - backends/mizan-fastapi/README.md: same - ISSUES.md: file paths in three issue entries - CLAUDE.md: codegen description + Package Layout section refreshed (added protocol/, mizan-fastapi entry, mizan-python entry) - docs/AFI_ARCHITECTURE.md: Package Layout refreshed identically Verified codegen runs from new location: regenerated the FastAPI example harness's api/ output, identical to pre-move. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| f0f7a93ed2 |
Backend adapter READMEs — DX surface + codegen invocation
mizan-django/README.md:
- Updated install path (was pointing at the old `subdirectory=django` git
layout from before the backends/ restructure).
- Dropped the dead "monorepo root README" link (the root README was
removed earlier in the substrate-restoration work).
- Fixed the apps.py example — convention is `clients.py` per MIZAN.md,
not `mizan_clients.py`.
- Added the `mizan_clients()` auto-discovery pattern (it was missing).
- Added a Generate-the-frontend section: config shape + CLI invocation
+ the resulting <MizanContext>/use{Hook}() React surface.
- Tightened decorator-parameter overview to a single block covering the
full @client surface.
mizan-fastapi/README.md (new):
- Mirrors mizan-django's structure for consistency.
- Opens with the AFI-common scope: forms/channels/shapes/SSR are out of
scope on the FastAPI side; FastAPI projects use native equivalents.
- Setup shows app.add_exception_handler wiring for MizanError +
RequestValidationError so every error surface goes through the same
envelope the kernel parses.
- Calls out explicit register() (no AppConfig.ready() analog on FastAPI;
registrations live in main.py or an imported clients.py).
- Auth-integration section explains the request.state.user middleware
contract the executor expects.
- Codegen section shows the source.fastapi config shape that points at
the new `python -m mizan_fastapi.cli <module>` schema export.
- Closes with pointers to AFI conformance + the e2e harness so a reader
can verify the adapter's claims.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 255e10cb21 |
mizan-fastapi e2e — example app + Playwright harness, 14/14 green
Demonstration milestone. The substrate work earlier in the session established that mizan-fastapi can dispatch RPC, bundle context fetches, and emit invalidation envelopes via TestClient (in-process ASGI). This commit closes the demonstration gap: a real FastAPI server on port 8001 + a real React harness on port 5175 + Playwright in real Chromium, exercising generated hooks. What ships: backends/mizan-fastapi/src/mizan_fastapi/cli.py — schema-export CLI: - `python -m mizan_fastapi.cli <module>` imports the named module (triggering @client decorations + register() side effects), then prints the OpenAPI schema to stdout. Mirrors mizan-django's `manage.py export_mizan_schema` so the codegen consumes either backend the same subprocess way. backends/mizan-django/generate/generator/lib/fetch.mjs — codegen now dispatches on source.django vs source.fastapi. Refactored the subprocess plumbing into a shared runSubprocess helper. The codegen package is still named "mizan-django" by historical accident — it's the framework-agnostic CLI now (a rename for later). backends/mizan-fastapi/src/mizan_fastapi/executor.py — bug fix: mizan_core's @client decorator normalizes auth=True to meta['auth']='required'. The executor's match was only handling True, not 'required', so any auth-required endpoint failed with INTERNAL_ERROR. Now matches both. Caught when wiring up the FastAPI example backend's whoami fixture; would have surfaced first time any real FastAPI app used auth=True. backends/mizan-fastapi/tests/test_dispatch.py — added AuthTests covering the auth=True path so the bug fix has unit coverage. Suite now 12/12. examples/fastapi-react-site/ — parallel to examples/django-react-site/: - backend/main.py: FastAPI app with 11 @client fixtures matching the harness surface (echo, add, multiply, whoami, staff/superuser/ verified-only, notImplementedFn, buggyFn, permissionCheckFn, current_user context). Drops Django-only stuff (forms, channels, ws-whoami, session-bound JWT). - harness/: vite proxy → FastAPI on 8001; generated api/ produced by the codegen against fastapi.config.mjs. - mizan.spec.ts: Playwright suite, 14 tests covering the same axes as Django minus channel-chat. - ContextCurrentUser fixture renders 'loading' until data arrives rather than emitting <pre>null</pre> — fixes a race the Django harness has too (just doesn't trip in practice). Verified: - mizan-fastapi unit: 12/12 (incl. new auth=True coverage) - mizan-fastapi e2e: 14/14 (Playwright via real Chromium) - mizan-core unit: 15/15 - mizan-django unit: 348 pass, 21 skip - AFI conformance: 3/3 - mizan-django e2e: 14/15 (1 skip — channels, deferred) What remains for FastAPI side: - Dockerfile.test + docker-compose.test.yml so CI can run the e2e in the same containerized way as the Django example. - Makefile test-integration target for symmetry. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 0a95f3c860 |
AFI conformance test suite
Substrate-level gate: same @client fixture registered in both backends
emits equivalent schemas, therefore the codegen produces equivalent
TypeScript regardless of which backend the frontend is generated against.
Catches adapter symmetry problems (Pydantic→OpenAPI converter divergence,
metadata leakage, ordering non-determinism) without docker, browser, or
Playwright.
What ships:
backends/mizan-fastapi/src/mizan_fastapi/schema.py — build_schema():
- Builds OpenAPI 3.0 from registered Mizan functions, mirroring the
shape mizan-django's export emits.
- Drives FastAPI's native OpenAPI generation by registering a stub POST
endpoint per function with its Input/Output Pydantic models, then
appends x-mizan-functions and x-mizan-contexts extensions.
- Param-elevation logic mirrors mizan-django/src/mizan/export/__init__.py
exactly (sharedBy tracking, required iff every function in context has
the param).
- snake_to_camel and metadata field shapes match Django for byte-equality
on the AFI surface.
tests/afi/ — the conformance harness:
- fixture.py: 5 @client functions covering the protocol axes (plain,
context, mutation+affects). No channels/forms — those aren't AFI-common.
- django_app/: minimal Django project (settings, urls, AppConfig.ready
registers the fixture). manage.py adds tests/afi/ to sys.path so both
backends import the same fixture module.
- fastapi_app.py: thin make_app() that registers fixture and mounts router.
- schema_normalizer.py: drops backend-specific framing — Ninja-vs-FastAPI
envelope differences (info/servers/tags), Django-only function fields
(form metadata), x-mizan-channels. Plus afi_subset() and
function_io_schemas() helpers for narrower comparisons.
- test_codegen_parity.py: three gates
1. x-mizan-functions match across backends
2. x-mizan-contexts match across backends
3. Per-function Input/Output OpenAPI schemas match (what codegen feeds
to openapi-typescript for type generation)
The full normalized OpenAPI envelopes do diverge — FastAPI adds
HTTPValidationError, the two converters wrap things slightly differently
in non-AFI-essential ways. That's not in the test scope. The codegen
only consumes x-mizan-functions, x-mizan-contexts, and the per-function
type schemas; those are what the test gates.
Makefile: test-afi target added; rolls into the test aggregate.
Verified: 3/3 conformance tests pass. Other surfaces unaffected —
mizan-core 15/15, mizan-django 348 pass, mizan-fastapi 11/11.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 63c9a9c4ce |
mizan-fastapi: more Pythonic and declarative
Reworked the MVP code along the lines Ryth flagged. Same behavior
(11/11 tests still pass), tighter idiom.
executor.py:
- Replaced FunctionResult / FunctionError dataclasses with a MizanError
exception hierarchy (NotFound, BadRequest, ValidationFailed,
Unauthorized, Forbidden, NotImplementedYet, InternalError). Each
carries its own ErrorCode + HTTP status; the dispatcher path raises
rather than returning sentinel objects.
- Auth check uses match/case for the requirement (True / 'staff' /
'superuser' / callable / other) — single declarative dispatch instead
of an if/elif chain.
- Broke up the single 80-line execute_function into focused helpers:
_resolve_function, _enforce_auth, _validate_input, _serialize,
_invalidation_target. The execute_function body now reads as five
declarative steps.
- Input validation uses Pydantic's model_fields[name].is_required()
directly and a list comprehension for required-field reporting,
instead of round-tripping through model_json_schema().
router.py:
- POST /call/ now declares its body as a Pydantic CallBody model;
FastAPI handles parsing + envelope validation. No more manual
await request.json() + dict[get] dancing.
- Endpoint bodies shrink to 3-5 lines each. Context fetch uses a
dict comprehension over the function group.
- mizan_exception_handler renders MizanError to the protocol's
{error: {code, message, details}} envelope.
- mizan_validation_handler maps FastAPI's RequestValidationError to
the same envelope under BAD_REQUEST so the wire format is uniform
whether the failure is body-shape or business validation.
__init__.py: exposes the full exception hierarchy + both handlers
so consumers can wire them onto their FastAPI app declaratively:
app.add_exception_handler(MizanError, mizan_exception_handler)
app.add_exception_handler(RequestValidationError, mizan_validation_handler)
Verified: mizan-core 15/15, mizan-django 348 pass, mizan-fastapi 11/11.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 4e4d1bb6b1 |
Build mizan-fastapi MVP — HTTP RPC + context bundling
The Blazr-critical surface for FastAPI. Forms, Channels, Shapes, SSR,
and MWT are out of scope (Ryth's call: defer until Blazr exercises
them; FastAPI projects use native equivalents anyway).
What ships:
- POST /api/mizan/call/ RPC dispatch with Pydantic input validation
- GET /api/mizan/ctx/{name}/ bundled context fetch (all functions in
the named context, parallel-evaluated, single
JSON response)
- JSON-body invalidation transport (the 'invalidate' field on mutation
responses, with auto-scoping when mutation arg names match context params)
- Auth check infrastructure expecting request.state.user populated by
FastAPI middleware/deps (matches FastAPI idioms)
- Cache-Control: no-store on all responses
Built on existing mizan-core: registry (function lookup, context groups,
invalidation metadata), client.function (the @client decorator + ServerFunction
+ _FunctionWrapper). No code copied or duplicated from mizan-django — the
shared substrate is genuinely shared.
Package layout:
backends/mizan-fastapi/
pyproject.toml distribution=mizan-fastapi, module=mizan_fastapi
src/mizan_fastapi/
executor.py dispatch + auth + invalidation
router.py FastAPI APIRouter with the two endpoints
tests/test_dispatch.py 11 e2e tests against TestClient
Test fixture establishes the registration pattern: explicit
register(fn_class, "name") after each @client. mizan-fastapi doesn't
ship discovery — apps register their functions explicitly. (mizan-django
keeps its DjangoAppVisitor discovery; FastAPI's lack of an app system
makes auto-discovery less natural.)
Makefile: install + test targets now include mizan-fastapi alongside
the other packages. New test-core / test-fastapi targets added for
symmetry.
Verified:
- mizan-core: 15/15
- mizan-django: 348 pass, 21 skip, 0 fail
- mizan-fastapi: 11/11
- mizan-ts edge-compat: 34/34
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|