AFI parity: close all 35 gaps — every adapter wires every AFI-common capability
The conformance board (tests/afi/test_capability_parity.py) is now fully green: 90 capability cells + 4 meta-locks + 3 codegen byte-parity = 97 passed. The gaps the prose table used to launder as "Django-only" / "out of scope" are wired, against the pinned-spec model (single-authored spec, byte-identical conformance across languages) — never per-language reimplementation. FastAPI — edge_manifest + PSR (logic single-sourced in mizan_core.manifest), WebSocket RPC (/ws/ through the shared dispatch), SSR (the framework-agnostic SSRBridge relocated to mizan_core.ssr; Django rides it from there), Shapes (SQLAlchemy projection, same declaration surface as django-readers), Forms (Pydantic schema/validate/submit). Rust (Axum + Tauri + cores/mizan-rust) — X-Mizan-Invalidate header, auth= enforcement, origin HMAC cache, edge manifest + PSR, WebSocket handler / IPC subscription channel, multipart upload, SSR bridge, Shapes, Forms; JWT/MWT mint+verify and cache-key derivation byte-pinned to the Python reference (cache_keys_pin, token_pin, invalidate_header_pin). TypeScript — a KDL IR emitter byte-identical to the Python build_ir (so a TS backend can feed the codegen — the largest gap), multipart upload, session-init, WebSocket transport, SSR bridge, JWT/MWT mint (pinned to Python), Shapes, Forms. Verified in the merged tree: core 25, fastapi 74, django 353/21-skip, mizan-rust (incl. cross-language pins) green, axum 10, tauri 8, mizan-ts 103/2-skip. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
77
backends/mizan-fastapi/src/mizan_fastapi/forms/schemas.py
Normal file
77
backends/mizan-fastapi/src/mizan_fastapi/forms/schemas.py
Normal file
@@ -0,0 +1,77 @@
|
||||
"""
|
||||
Form role output schemas — the wire shapes the schema/validate/submit roles emit.
|
||||
|
||||
These mirror the Django adapter's `mizan.forms.schemas` field-for-field (FormMeta,
|
||||
FieldSchema, FormSchema, FormValidation, FormSubmitPass/Fail) so the generated
|
||||
client is identical regardless of which backend authored the form. The only
|
||||
difference is the source: Django builds these from `forms.Field` introspection;
|
||||
this builds them from Pydantic `FieldInfo`.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class FormMeta(BaseModel):
|
||||
"""Frontend behavior flags (parity with the Django adapter)."""
|
||||
|
||||
refetch_schema_on_validate: bool = False
|
||||
live_validation: bool = True
|
||||
live_form_errors: bool = False
|
||||
|
||||
|
||||
class FieldChoice(BaseModel):
|
||||
value: str
|
||||
label: str
|
||||
|
||||
|
||||
class FieldError(BaseModel):
|
||||
message: str
|
||||
code: Optional[str] = None
|
||||
|
||||
|
||||
class FieldErrorList(BaseModel):
|
||||
field: str
|
||||
errors: list[FieldError]
|
||||
|
||||
|
||||
class FieldSchema(BaseModel):
|
||||
name: str
|
||||
label: str
|
||||
type: str
|
||||
widget: str
|
||||
required: bool
|
||||
disabled: bool
|
||||
help_text: str
|
||||
initial: Any = None
|
||||
max_length: Optional[int] = None
|
||||
min_length: Optional[int] = None
|
||||
choices: Optional[list[FieldChoice]] = None
|
||||
|
||||
|
||||
class FormSchema(BaseModel):
|
||||
"""Schema returned by the `.schema` role: form metadata + field definitions."""
|
||||
|
||||
name: str
|
||||
title: str
|
||||
subtitle: Optional[str] = None
|
||||
submit_label: str
|
||||
fields: list[FieldSchema]
|
||||
meta: FormMeta = FormMeta()
|
||||
|
||||
|
||||
class FormValidation(BaseModel):
|
||||
errors: list[FieldErrorList]
|
||||
|
||||
|
||||
class FormSubmitPass(BaseModel):
|
||||
success: bool
|
||||
data: Optional[dict] = None
|
||||
|
||||
|
||||
class FormSubmitFail(BaseModel):
|
||||
success: bool
|
||||
errors: FormValidation
|
||||
Reference in New Issue
Block a user