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>
150 lines
4.5 KiB
TypeScript
150 lines
4.5 KiB
TypeScript
/**
|
|
* The AFI fixture, TypeScript side — mirrors `tests/afi/fixture.py` 1:1.
|
|
*
|
|
* Each function declares the same IR type schema the Python fixture's Pydantic
|
|
* Input/Output models imply, so `buildIr()` here emits the same KDL the Python
|
|
* `build_ir()` emits from `fixture.py`. The byte-parity test (`ir.test.ts`)
|
|
* subprocesses the live Python emitter and asserts equality.
|
|
*
|
|
* Output structs are declared under their model name (`ProfileOutput`,
|
|
* `OrderOutput`, …) and referenced via `{ kind: 'ref' }`; the emitter renames
|
|
* them to the canonical `<camel>Output`, exactly as `_collect_named_types`
|
|
* renames the Pydantic models.
|
|
*/
|
|
|
|
import { client, ReactContext } from '../src'
|
|
import type { NamedType, StructField } from '../src'
|
|
|
|
const intField = (name: string): StructField => ({
|
|
name,
|
|
required: true,
|
|
shape: { kind: 'primitive', primitive: 'integer' },
|
|
})
|
|
const strField = (name: string): StructField => ({
|
|
name,
|
|
required: true,
|
|
shape: { kind: 'primitive', primitive: 'string' },
|
|
})
|
|
const boolField = (name: string): StructField => ({
|
|
name,
|
|
required: true,
|
|
shape: { kind: 'primitive', primitive: 'boolean' },
|
|
})
|
|
|
|
const ProfileOutput: NamedType = { kind: 'struct', fields: [intField('user_id'), strField('name')] }
|
|
const OrderOutput: NamedType = {
|
|
kind: 'struct',
|
|
fields: [intField('id'), intField('user_id'), intField('total')],
|
|
}
|
|
|
|
const UserCtx = new ReactContext('user')
|
|
|
|
/** Register the AFI fixture functions with the mizan-ts registry. */
|
|
export function registerFixture(): void {
|
|
// echo — plain function, typed input + struct output.
|
|
client(
|
|
{
|
|
ir: {
|
|
input: [strField('text')],
|
|
output: { kind: 'ref', name: 'EchoOutput' },
|
|
types: { EchoOutput: { kind: 'struct', fields: [strField('message')] } },
|
|
},
|
|
},
|
|
async function echo(text: string) {
|
|
return { message: `echo: ${text}` }
|
|
},
|
|
)
|
|
|
|
// whoami — no input.
|
|
client(
|
|
{
|
|
ir: {
|
|
output: { kind: 'ref', name: 'WhoamiOutput' },
|
|
types: {
|
|
WhoamiOutput: {
|
|
kind: 'struct',
|
|
fields: [strField('email'), boolField('authenticated')],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
async function whoami() {
|
|
return { email: 'anon@example.com', authenticated: false }
|
|
},
|
|
)
|
|
|
|
// user_profile — context member.
|
|
client(
|
|
{
|
|
context: UserCtx,
|
|
ir: {
|
|
input: [intField('user_id')],
|
|
output: { kind: 'ref', name: 'ProfileOutput' },
|
|
types: { ProfileOutput },
|
|
},
|
|
},
|
|
async function user_profile(user_id: number) {
|
|
return { user_id, name: 'placeholder' }
|
|
},
|
|
)
|
|
|
|
// user_orders — context member, list output, same param (param elevation).
|
|
client(
|
|
{
|
|
context: UserCtx,
|
|
ir: {
|
|
input: [intField('user_id')],
|
|
output: { kind: 'list', inner: { kind: 'ref', name: 'OrderOutput' } },
|
|
types: { OrderOutput },
|
|
},
|
|
},
|
|
async function user_orders(_user_id: number) {
|
|
return []
|
|
},
|
|
)
|
|
|
|
// update_profile — mutation affecting the user context.
|
|
client(
|
|
{
|
|
affects: UserCtx,
|
|
ir: {
|
|
input: [intField('user_id'), strField('name')],
|
|
output: { kind: 'ref', name: 'StatusOutput' },
|
|
types: { StatusOutput: { kind: 'struct', fields: [boolField('ok')] } },
|
|
},
|
|
},
|
|
async function update_profile(_user_id: number, _name: string) {
|
|
return { ok: true }
|
|
},
|
|
)
|
|
|
|
// find_user — optional return.
|
|
client(
|
|
{
|
|
ir: {
|
|
input: [intField('user_id')],
|
|
output: { kind: 'optional', inner: { kind: 'ref', name: 'ProfileOutput' } },
|
|
types: { ProfileOutput },
|
|
},
|
|
},
|
|
async function find_user(_user_id: number) {
|
|
return null
|
|
},
|
|
)
|
|
|
|
// rename_user — merge target.
|
|
client(
|
|
{
|
|
merge: UserCtx,
|
|
ir: {
|
|
input: [intField('user_id'), strField('name')],
|
|
output: { kind: 'ref', name: 'ProfileOutput' },
|
|
types: { ProfileOutput },
|
|
},
|
|
},
|
|
async function rename_user(user_id: number, name: string) {
|
|
return { user_id, name }
|
|
},
|
|
)
|
|
}
|