A channel's message slots are named from the client, on every backend
The IR called them react-message and django-message, so a FastAPI channel had to declare a DjangoMessage. They are client-message and server-message now, and the direction words hold wherever a channel is declared: Params / ClientMessage / ServerMessage, with mizan-core deriving <Pascal>Params and friends so no backend names a type itself. Django's ReactChannel and FastAPI's ReactChannel are both Channel. mizan-fastapi never registered a channels extension, so build_ir() emitted no channel at all and every payload type was invisible to codegen. It registers one now. RegistryExtension is an ABC requiring all(), which is what the IR reads — an extension that cannot enumerate its registrations no longer exists. The gate that should have caught the rename could not: tests/afi registered no channel because mizan-rust had no channel registry to register one in, so a five-package rename of the wire contract passed byte-parity without a channel byte crossing it. mizan-rust grows ChannelSlotKind, a CHANNELS slice, a #[mizan::channel] macro, and KDL emission whose wire_to_pascal matches Python's split; the AFI fixture now carries a channel with every slot and one with a single slot, so all three backends prove the contract byte for byte. MizanChannel held three Option<String> beside three has_*() predicates and unwrapped them with defaults; it holds an ordered slot vector, so an absent slot is absent rather than defaulted. The channels target emitted a React hooks file that a stage1-only consumer could not compile — react emits that now. The codegen's parity tests byte-compared emitted source against baselines without ever compiling it: they compile the generated crate and run its tests, import the generated Python package and call every method, and typecheck each TypeScript target against a consumer. Also fixed at source: app_visitor printed its import diagnostic to stdout, the stream export_mizan_ir writes KDL to, so a failed import silently corrupted the IR; the apps root was hardcoded to "apps"; _default_literal crashed build_ir on any non-JSON-serializable field default; Django and mizan-core derived Pascal names two different ways, disagreeing on every dotted channel name. ir.py builds a document and renders templates/ir/document.kdl.j2 rather than appending KDL strings with hand-tracked indentation, and named types resolve to a fixed point — a model reachable only through a union branch was referenced by a ref that no type block ever defined. The rest is the write-gate's own classifiers run over the standing tree: relative imports, silent swallows, Protocol contracts that should be ABCs, emitters hand-rendering target source, catch-all arms over closed enums, and comments narrating the project rather than the code. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,19 +0,0 @@
|
||||
import path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
const root = path.resolve(__dirname, '../../..')
|
||||
|
||||
export default {
|
||||
projectId: 'mizan-fastapi-e2e',
|
||||
|
||||
source: {
|
||||
fastapi: {
|
||||
module: 'main',
|
||||
cwd: path.join(root, 'examples/fastapi-react-site/backend'),
|
||||
command: ['uv', 'run', 'python'],
|
||||
},
|
||||
},
|
||||
|
||||
output: 'src/api',
|
||||
}
|
||||
9
examples/fastapi-react-site/harness/mizan.toml
Normal file
9
examples/fastapi-react-site/harness/mizan.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
project_id = "mizan-fastapi-e2e"
|
||||
output = "src/api"
|
||||
targets = ["react"]
|
||||
|
||||
[source.fastapi]
|
||||
module = "main"
|
||||
# Resolved from this file's directory; the backend project is the subprocess cwd.
|
||||
cwd = "../backend"
|
||||
command = ["uv", "run", "python"]
|
||||
@@ -3,7 +3,10 @@
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"generate": "mizan-generate --config mizan.toml",
|
||||
"prebuild": "npm run generate",
|
||||
"build": "vite build",
|
||||
"predev": "npm run generate",
|
||||
"dev": "vite --port 5175"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
1
examples/fastapi-react-site/harness/src/api/.gitattributes
generated
vendored
Normal file
1
examples/fastapi-react-site/harness/src/api/.gitattributes
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
* linguist-generated=true
|
||||
@@ -1,5 +1,3 @@
|
||||
// AUTO-GENERATED by mizan — do not edit
|
||||
|
||||
import { mizanFetch } from '@mizan/base'
|
||||
|
||||
import type { currentUserOutput } from '../types'
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// AUTO-GENERATED by mizan — do not edit
|
||||
|
||||
import { mizanFetch } from '@mizan/base'
|
||||
|
||||
import type { morphGroupsOutput, morphLayersOutput } from '../types'
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// AUTO-GENERATED by mizan — do not edit
|
||||
|
||||
import { mizanCall } from '@mizan/base'
|
||||
|
||||
import type { addInput, addOutput } from '../types'
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
// AUTO-GENERATED by mizan — do not edit
|
||||
|
||||
import { mizanCall } from '@mizan/base'
|
||||
|
||||
import type { buggyFnOutput } from '../types'
|
||||
|
||||
export function callBuggyFn(): Promise<buggyFnOutput> {
|
||||
return mizanCall('buggy_fn', {})
|
||||
}
|
||||
7
examples/fastapi-react-site/harness/src/api/functions/divide.ts
generated
Normal file
7
examples/fastapi-react-site/harness/src/api/functions/divide.ts
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
import { mizanCall } from '@mizan/base'
|
||||
|
||||
import type { divideInput, divideOutput } from '../types'
|
||||
|
||||
export function callDivide(args: divideInput): Promise<divideOutput> {
|
||||
return mizanCall('divide', args)
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
// AUTO-GENERATED by mizan — do not edit
|
||||
|
||||
import { mizanCall } from '@mizan/base'
|
||||
|
||||
import type { echoInput, echoOutput } from '../types'
|
||||
|
||||
7
examples/fastapi-react-site/harness/src/api/functions/echoTransform.ts
generated
Normal file
7
examples/fastapi-react-site/harness/src/api/functions/echoTransform.ts
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
import { mizanCall } from '@mizan/base'
|
||||
|
||||
import type { echoTransformInput, echoTransformOutput } from '../types'
|
||||
|
||||
export function callEchoTransform(args: echoTransformInput): Promise<echoTransformOutput> {
|
||||
return mizanCall('echo_transform', args)
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
// AUTO-GENERATED by mizan — do not edit
|
||||
|
||||
import { mizanCall } from '@mizan/base'
|
||||
|
||||
import type { multiplyInput, multiplyOutput } from '../types'
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
// AUTO-GENERATED by mizan — do not edit
|
||||
|
||||
import { mizanCall } from '@mizan/base'
|
||||
|
||||
import type { notImplementedFnOutput } from '../types'
|
||||
|
||||
export function callNotImplementedFn(): Promise<notImplementedFnOutput> {
|
||||
return mizanCall('not_implemented_fn', {})
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
// AUTO-GENERATED by mizan — do not edit
|
||||
|
||||
import { mizanCall } from '@mizan/base'
|
||||
|
||||
import type { permissionCheckFnInput, permissionCheckFnOutput } from '../types'
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// AUTO-GENERATED by mizan — do not edit
|
||||
|
||||
import { mizanCall } from '@mizan/base'
|
||||
|
||||
import type { setMorphValueInput, setMorphValueOutput } from '../types'
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// AUTO-GENERATED by mizan — do not edit
|
||||
|
||||
import { mizanCall } from '@mizan/base'
|
||||
|
||||
import type { staffOnlyOutput } from '../types'
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// AUTO-GENERATED by mizan — do not edit
|
||||
|
||||
import { mizanCall } from '@mizan/base'
|
||||
|
||||
import type { superuserOnlyOutput } from '../types'
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// AUTO-GENERATED by mizan — do not edit
|
||||
|
||||
import { mizanCall } from '@mizan/base'
|
||||
|
||||
import type { verifiedOnlyOutput } from '../types'
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// AUTO-GENERATED by mizan — do not edit
|
||||
|
||||
import { mizanCall } from '@mizan/base'
|
||||
|
||||
import type { whoamiOutput } from '../types'
|
||||
|
||||
15
examples/fastapi-react-site/harness/src/api/index.ts
generated
15
examples/fastapi-react-site/harness/src/api/index.ts
generated
@@ -1,21 +1,18 @@
|
||||
// AUTO-GENERATED by mizan — do not edit
|
||||
|
||||
export * from './types'
|
||||
|
||||
export { fetchGlobalContext, type GlobalContextData, type GlobalContextParams } from './contexts/global'
|
||||
export { fetchMorphsContext, type MorphsContextData, type MorphsContextParams } from './contexts/morphs'
|
||||
|
||||
export { callEcho } from './functions/echo'
|
||||
export { callAdd } from './functions/add'
|
||||
export { callDivide } from './functions/divide'
|
||||
export { callEcho } from './functions/echo'
|
||||
export { callEchoTransform } from './functions/echoTransform'
|
||||
export { callMultiply } from './functions/multiply'
|
||||
export { callWhoami } from './functions/whoami'
|
||||
export { callPermissionCheckFn } from './functions/permissionCheckFn'
|
||||
export { callSetMorphValue } from './functions/setMorphValue'
|
||||
export { callStaffOnly } from './functions/staffOnly'
|
||||
export { callSuperuserOnly } from './functions/superuserOnly'
|
||||
export { callVerifiedOnly } from './functions/verifiedOnly'
|
||||
export { callNotImplementedFn } from './functions/notImplementedFn'
|
||||
export { callBuggyFn } from './functions/buggyFn'
|
||||
export { callPermissionCheckFn } from './functions/permissionCheckFn'
|
||||
export { callSetMorphValue } from './functions/setMorphValue'
|
||||
export { callWhoami } from './functions/whoami'
|
||||
|
||||
// Stage 2 framework adapter
|
||||
export * from './react'
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
'use client'
|
||||
|
||||
// AUTO-GENERATED by mizan — do not edit
|
||||
|
||||
import {
|
||||
createContext,
|
||||
useCallback,
|
||||
@@ -14,17 +12,15 @@ import {
|
||||
} from 'react'
|
||||
import {
|
||||
configure,
|
||||
initSession,
|
||||
mizanCall,
|
||||
mizanFetch,
|
||||
MizanError,
|
||||
registerContext,
|
||||
type ContextState,
|
||||
} from '@mizan/base'
|
||||
|
||||
import { fetchGlobalContext, type GlobalContextData, type GlobalContextParams, fetchMorphsContext, type MorphsContextData, type MorphsContextParams, callEcho, callAdd, callMultiply, callWhoami, callStaffOnly, callSuperuserOnly, callVerifiedOnly, callNotImplementedFn, callBuggyFn, callPermissionCheckFn, callSetMorphValue, type currentUserOutput, type morphGroupsOutput, type morphLayersOutput } from './index'
|
||||
import { fetchGlobalContext, type GlobalContextData, type GlobalContextParams, fetchMorphsContext, type MorphsContextData, type MorphsContextParams, callAdd, callDivide, callEcho, callEchoTransform, callMultiply, callPermissionCheckFn, callSetMorphValue, callStaffOnly, callSuperuserOnly, callVerifiedOnly, callWhoami, type currentUserOutput, type morphGroupsOutput, type morphLayersOutput, } from './index'
|
||||
|
||||
// Internal — runs inside a Provider, registers with the kernel exactly once.
|
||||
// Runs inside a Provider, registers with the kernel exactly once.
|
||||
function useContextSubscription<T>(
|
||||
name: string,
|
||||
params: Record<string, any>,
|
||||
@@ -45,7 +41,8 @@ function useContextSubscription<T>(
|
||||
return useSyncExternalStore(handle.subscribe, handle.getState, handle.getState)
|
||||
}
|
||||
|
||||
// Internal — wraps an imperative call() with isPending / error state.
|
||||
|
||||
// Wraps an imperative call() with isPending / error state.
|
||||
interface MutationHook<TArgs, TResult> {
|
||||
mutate: (args: TArgs) => Promise<TResult>
|
||||
isPending: boolean
|
||||
@@ -74,8 +71,6 @@ function useMutation<TArgs, TResult>(
|
||||
return { mutate, isPending, error }
|
||||
}
|
||||
|
||||
// ── Global Context ──
|
||||
|
||||
const GlobalCtx = createContext<ContextState<GlobalContextData> | null>(null)
|
||||
|
||||
export function GlobalContextProvider({ children }: { children: ReactNode }) {
|
||||
@@ -94,8 +89,6 @@ export function useCurrentUser(): currentUserOutput | null {
|
||||
return useGlobalContext().data?.current_user ?? null
|
||||
}
|
||||
|
||||
// ── Morphs Context ──
|
||||
|
||||
const MorphsCtx = createContext<ContextState<MorphsContextData> | null>(null)
|
||||
|
||||
export function MorphsContext({ children }: { children: ReactNode }) {
|
||||
@@ -117,20 +110,32 @@ export function useMorphLayers(): morphLayersOutput | null {
|
||||
return useMorphsContext().data?.morph_layers ?? null
|
||||
}
|
||||
|
||||
export function useAdd() {
|
||||
return useMutation<Parameters<typeof callAdd>[0], Awaited<ReturnType<typeof callAdd>>>(callAdd)
|
||||
}
|
||||
|
||||
export function useDivide() {
|
||||
return useMutation<Parameters<typeof callDivide>[0], Awaited<ReturnType<typeof callDivide>>>(callDivide)
|
||||
}
|
||||
|
||||
export function useEcho() {
|
||||
return useMutation<Parameters<typeof callEcho>[0], Awaited<ReturnType<typeof callEcho>>>(callEcho)
|
||||
}
|
||||
|
||||
export function useAdd() {
|
||||
return useMutation<Parameters<typeof callAdd>[0], Awaited<ReturnType<typeof callAdd>>>(callAdd)
|
||||
export function useEchoTransform() {
|
||||
return useMutation<Parameters<typeof callEchoTransform>[0], Awaited<ReturnType<typeof callEchoTransform>>>(callEchoTransform)
|
||||
}
|
||||
|
||||
export function useMultiply() {
|
||||
return useMutation<Parameters<typeof callMultiply>[0], Awaited<ReturnType<typeof callMultiply>>>(callMultiply)
|
||||
}
|
||||
|
||||
export function useWhoami() {
|
||||
return useMutation<void, Awaited<ReturnType<typeof callWhoami>>>(() => callWhoami() as any)
|
||||
export function usePermissionCheckFn() {
|
||||
return useMutation<Parameters<typeof callPermissionCheckFn>[0], Awaited<ReturnType<typeof callPermissionCheckFn>>>(callPermissionCheckFn)
|
||||
}
|
||||
|
||||
export function useSetMorphValue() {
|
||||
return useMutation<Parameters<typeof callSetMorphValue>[0], Awaited<ReturnType<typeof callSetMorphValue>>>(callSetMorphValue)
|
||||
}
|
||||
|
||||
export function useStaffOnly() {
|
||||
@@ -145,24 +150,10 @@ export function useVerifiedOnly() {
|
||||
return useMutation<void, Awaited<ReturnType<typeof callVerifiedOnly>>>(() => callVerifiedOnly() as any)
|
||||
}
|
||||
|
||||
export function useNotImplementedFn() {
|
||||
return useMutation<void, Awaited<ReturnType<typeof callNotImplementedFn>>>(() => callNotImplementedFn() as any)
|
||||
export function useWhoami() {
|
||||
return useMutation<void, Awaited<ReturnType<typeof callWhoami>>>(() => callWhoami() as any)
|
||||
}
|
||||
|
||||
export function useBuggyFn() {
|
||||
return useMutation<void, Awaited<ReturnType<typeof callBuggyFn>>>(() => callBuggyFn() as any)
|
||||
}
|
||||
|
||||
export function usePermissionCheckFn() {
|
||||
return useMutation<Parameters<typeof callPermissionCheckFn>[0], Awaited<ReturnType<typeof callPermissionCheckFn>>>(callPermissionCheckFn)
|
||||
}
|
||||
|
||||
export function useSetMorphValue() {
|
||||
return useMutation<Parameters<typeof callSetMorphValue>[0], Awaited<ReturnType<typeof callSetMorphValue>>>(callSetMorphValue)
|
||||
}
|
||||
|
||||
// ── MizanContext root provider ──
|
||||
|
||||
export interface MizanContextProps {
|
||||
/** Base URL for protocol endpoints. Defaults to "/api/mizan". */
|
||||
baseUrl?: string
|
||||
@@ -172,8 +163,8 @@ export interface MizanContextProps {
|
||||
}
|
||||
|
||||
/**
|
||||
* Root provider — calls configure() once and mounts the global context (if defined).
|
||||
* Must wrap any component using Mizan-generated hooks.
|
||||
* Calls configure() once and mounts the global context when one is defined.
|
||||
* Every component reading a generated hook resolves through this provider.
|
||||
*/
|
||||
export function MizanContext({ baseUrl, session, children }: MizanContextProps) {
|
||||
const configured = useRef(false)
|
||||
@@ -187,12 +178,7 @@ export function MizanContext({ baseUrl, session, children }: MizanContextProps)
|
||||
return <GlobalContextProvider>{children}</GlobalContextProvider>
|
||||
}
|
||||
|
||||
// ── Imperative escape hatch ──
|
||||
|
||||
/**
|
||||
* Returns the imperative kernel API. For test harnesses or rare cases where
|
||||
* a typed generated hook does not fit. Most app code should use the typed hooks.
|
||||
*/
|
||||
/** The untyped kernel entry points, bound to the configured client. */
|
||||
export function useMizan() {
|
||||
return { call: mizanCall, fetch: mizanFetch }
|
||||
}
|
||||
|
||||
1000
examples/fastapi-react-site/harness/src/api/schema.json
generated
1000
examples/fastapi-react-site/harness/src/api/schema.json
generated
File diff suppressed because it is too large
Load Diff
883
examples/fastapi-react-site/harness/src/api/types.ts
generated
883
examples/fastapi-react-site/harness/src/api/types.ts
generated
@@ -1,784 +1,105 @@
|
||||
// AUTO-GENERATED by mizan — do not edit
|
||||
|
||||
export interface paths {
|
||||
"/mizan/echo": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
/** Echoes the input text. */
|
||||
post: operations["echo"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/mizan/add": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
/** Returns a + b. */
|
||||
post: operations["add"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/mizan/multiply": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
/** Returns x * y. */
|
||||
post: operations["multiply"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/mizan/whoami": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
/** Returns the authenticated user's identity. Anonymous → UNAUTHORIZED. */
|
||||
post: operations["whoami"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/mizan/staff_only": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
/** Staff-only endpoint. */
|
||||
post: operations["staffOnly"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/mizan/superuser_only": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
/** Superuser-only endpoint. */
|
||||
post: operations["superuserOnly"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/mizan/verified_only": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
/** Verified-users-only endpoint. Anonymous → FORBIDDEN. */
|
||||
post: operations["verifiedOnly"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/mizan/not_implemented_fn": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
/** Always raises NotImplementedError → NOT_IMPLEMENTED. */
|
||||
post: operations["notImplementedFn"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/mizan/buggy_fn": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
/** Always raises a generic exception → INTERNAL_ERROR. */
|
||||
post: operations["buggyFn"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/mizan/permission_check_fn": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
/** Wrong secret → FORBIDDEN; correct secret → success. */
|
||||
post: operations["permissionCheckFn"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/mizan/current_user": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
/** The global context — auto-mounted at the React root. */
|
||||
post: operations["currentUser"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/mizan/morph_groups": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
/** Summary-shape slot — server must route MorphLayer mutations away from here. */
|
||||
post: operations["morphGroups"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/mizan/morph_layers": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
/** Detailed-shape slot — server routes MorphLayer mutations here. */
|
||||
post: operations["morphLayers"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/mizan/set_morph_value": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
/** Mutation that returns the changed row; kernel splices into morph_layers. */
|
||||
post: operations["setMorphValue"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
}
|
||||
export type webhooks = Record<string, never>;
|
||||
export interface components {
|
||||
schemas: {
|
||||
/** HTTPValidationError */
|
||||
HTTPValidationError: {
|
||||
/** Detail */
|
||||
detail?: components["schemas"]["ValidationError"][];
|
||||
};
|
||||
/**
|
||||
* MorphGroupMeta
|
||||
* @description Group summary — narrower shape than MorphLayer. Listed alongside
|
||||
* morph_layers so the server's slot resolver has to discriminate by
|
||||
* return-type rather than by bundle order.
|
||||
*/
|
||||
MorphGroupMeta: {
|
||||
/** Id */
|
||||
id: number;
|
||||
/** Label */
|
||||
label: string;
|
||||
/** Count */
|
||||
count: number;
|
||||
};
|
||||
/** MorphLayer */
|
||||
MorphLayer: {
|
||||
/** Id */
|
||||
id: number;
|
||||
/** Group Id */
|
||||
group_id: number;
|
||||
/** Label */
|
||||
label: string;
|
||||
/** Value */
|
||||
value: number;
|
||||
};
|
||||
/** ValidationError */
|
||||
ValidationError: {
|
||||
/** Location */
|
||||
loc: (string | number)[];
|
||||
/** Message */
|
||||
msg: string;
|
||||
/** Error Type */
|
||||
type: string;
|
||||
/** Input */
|
||||
input?: unknown;
|
||||
/** Context */
|
||||
ctx?: Record<string, never>;
|
||||
};
|
||||
/** addInput */
|
||||
addInput: {
|
||||
/** A */
|
||||
a: number;
|
||||
/** B */
|
||||
b: number;
|
||||
};
|
||||
/** addOutput */
|
||||
addOutput: {
|
||||
/** Result */
|
||||
result: number;
|
||||
};
|
||||
/** buggyFnOutput */
|
||||
buggyFnOutput: {
|
||||
/** Message */
|
||||
message: string;
|
||||
};
|
||||
/** currentUserOutput */
|
||||
currentUserOutput: {
|
||||
/** Email */
|
||||
email: string;
|
||||
/** Authenticated */
|
||||
authenticated: boolean;
|
||||
/**
|
||||
* Is Staff
|
||||
* @default false
|
||||
*/
|
||||
is_staff: boolean;
|
||||
};
|
||||
/** echoInput */
|
||||
echoInput: {
|
||||
/** Text */
|
||||
text: string;
|
||||
};
|
||||
/** echoOutput */
|
||||
echoOutput: {
|
||||
/** Message */
|
||||
message: string;
|
||||
};
|
||||
/** morphGroupsOutput */
|
||||
morphGroupsOutput: components["schemas"]["MorphGroupMeta"][];
|
||||
/** morphLayersOutput */
|
||||
morphLayersOutput: components["schemas"]["MorphLayer"][];
|
||||
/** multiplyInput */
|
||||
multiplyInput: {
|
||||
/** X */
|
||||
x: number;
|
||||
/** Y */
|
||||
y: number;
|
||||
};
|
||||
/** multiplyOutput */
|
||||
multiplyOutput: {
|
||||
/** Product */
|
||||
product: number;
|
||||
};
|
||||
/** notImplementedFnOutput */
|
||||
notImplementedFnOutput: {
|
||||
/** Message */
|
||||
message: string;
|
||||
};
|
||||
/** permissionCheckFnInput */
|
||||
permissionCheckFnInput: {
|
||||
/** Secret */
|
||||
secret: string;
|
||||
};
|
||||
/** permissionCheckFnOutput */
|
||||
permissionCheckFnOutput: {
|
||||
/** Message */
|
||||
message: string;
|
||||
};
|
||||
/** setMorphValueInput */
|
||||
setMorphValueInput: {
|
||||
/** Id */
|
||||
id: number;
|
||||
/** Value */
|
||||
value: number;
|
||||
};
|
||||
/** setMorphValueOutput */
|
||||
setMorphValueOutput: {
|
||||
/** Id */
|
||||
id: number;
|
||||
/** Group Id */
|
||||
group_id: number;
|
||||
/** Label */
|
||||
label: string;
|
||||
/** Value */
|
||||
value: number;
|
||||
};
|
||||
/** staffOnlyOutput */
|
||||
staffOnlyOutput: {
|
||||
/** Message */
|
||||
message: string;
|
||||
};
|
||||
/** superuserOnlyOutput */
|
||||
superuserOnlyOutput: {
|
||||
/** Message */
|
||||
message: string;
|
||||
};
|
||||
/** verifiedOnlyOutput */
|
||||
verifiedOnlyOutput: {
|
||||
/** Message */
|
||||
message: string;
|
||||
};
|
||||
/** whoamiOutput */
|
||||
whoamiOutput: {
|
||||
/** Email */
|
||||
email: string;
|
||||
/** Authenticated */
|
||||
authenticated: boolean;
|
||||
/**
|
||||
* Is Staff
|
||||
* @default false
|
||||
*/
|
||||
is_staff: boolean;
|
||||
};
|
||||
};
|
||||
responses: never;
|
||||
parameters: never;
|
||||
requestBodies: never;
|
||||
headers: never;
|
||||
pathItems: never;
|
||||
}
|
||||
export type $defs = Record<string, never>;
|
||||
export interface operations {
|
||||
echo: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["echoInput"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["echoOutput"];
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["HTTPValidationError"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
add: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["addInput"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["addOutput"];
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["HTTPValidationError"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
multiply: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["multiplyInput"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["multiplyOutput"];
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["HTTPValidationError"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
whoami: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["whoamiOutput"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
staffOnly: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["staffOnlyOutput"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
superuserOnly: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["superuserOnlyOutput"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
verifiedOnly: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["verifiedOnlyOutput"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
notImplementedFn: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["notImplementedFnOutput"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
buggyFn: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["buggyFnOutput"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
permissionCheckFn: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["permissionCheckFnInput"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["permissionCheckFnOutput"];
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["HTTPValidationError"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
currentUser: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["currentUserOutput"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
morphGroups: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["morphGroupsOutput"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
morphLayers: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["morphLayersOutput"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
setMorphValue: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["setMorphValueInput"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["setMorphValueOutput"];
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["HTTPValidationError"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
export interface MorphGroupMeta {
|
||||
id: number
|
||||
label: string
|
||||
count: number
|
||||
}
|
||||
|
||||
export interface MorphLayer {
|
||||
id: number
|
||||
group_id: number
|
||||
label: string
|
||||
value: number
|
||||
}
|
||||
|
||||
export interface addInput {
|
||||
a: number
|
||||
b: number
|
||||
}
|
||||
|
||||
export interface addOutput {
|
||||
result: number
|
||||
}
|
||||
|
||||
export interface currentUserOutput {
|
||||
email: string
|
||||
authenticated: boolean
|
||||
is_staff: boolean
|
||||
}
|
||||
|
||||
export interface divideInput {
|
||||
numerator: number
|
||||
denominator: number
|
||||
}
|
||||
|
||||
export interface divideOutput {
|
||||
quotient: number
|
||||
}
|
||||
|
||||
export interface echoInput {
|
||||
text: string
|
||||
}
|
||||
|
||||
export interface echoOutput {
|
||||
message: string
|
||||
}
|
||||
|
||||
export interface echoTransformInput {
|
||||
text: string
|
||||
mode: string
|
||||
}
|
||||
|
||||
export interface echoTransformOutput {
|
||||
message: string
|
||||
}
|
||||
|
||||
export type morphGroupsOutput = MorphGroupMeta[]
|
||||
|
||||
export type morphLayersOutput = MorphLayer[]
|
||||
|
||||
export interface multiplyInput {
|
||||
x: number
|
||||
y: number
|
||||
}
|
||||
|
||||
export interface multiplyOutput {
|
||||
product: number
|
||||
}
|
||||
|
||||
export interface permissionCheckFnInput {
|
||||
secret: string
|
||||
}
|
||||
|
||||
export interface permissionCheckFnOutput {
|
||||
message: string
|
||||
}
|
||||
|
||||
export interface setMorphValueInput {
|
||||
id: number
|
||||
value: number
|
||||
}
|
||||
|
||||
export interface setMorphValueOutput {
|
||||
id: number
|
||||
group_id: number
|
||||
label: string
|
||||
value: number
|
||||
}
|
||||
|
||||
export interface staffOnlyOutput {
|
||||
message: string
|
||||
}
|
||||
|
||||
export interface superuserOnlyOutput {
|
||||
message: string
|
||||
}
|
||||
|
||||
export interface verifiedOnlyOutput {
|
||||
message: string
|
||||
}
|
||||
|
||||
export interface whoamiOutput {
|
||||
email: string
|
||||
authenticated: boolean
|
||||
is_staff: boolean
|
||||
}
|
||||
|
||||
// Convenience type exports
|
||||
export type HTTPValidationError = components["schemas"]["HTTPValidationError"]
|
||||
export type MorphGroupMeta = components["schemas"]["MorphGroupMeta"]
|
||||
export type MorphLayer = components["schemas"]["MorphLayer"]
|
||||
export type ValidationError = components["schemas"]["ValidationError"]
|
||||
export type addInput = components["schemas"]["addInput"]
|
||||
export type addOutput = components["schemas"]["addOutput"]
|
||||
export type buggyFnOutput = components["schemas"]["buggyFnOutput"]
|
||||
export type currentUserOutput = components["schemas"]["currentUserOutput"]
|
||||
export type echoInput = components["schemas"]["echoInput"]
|
||||
export type echoOutput = components["schemas"]["echoOutput"]
|
||||
export type morphGroupsOutput = components["schemas"]["morphGroupsOutput"]
|
||||
export type morphLayersOutput = components["schemas"]["morphLayersOutput"]
|
||||
export type multiplyInput = components["schemas"]["multiplyInput"]
|
||||
export type multiplyOutput = components["schemas"]["multiplyOutput"]
|
||||
export type notImplementedFnOutput = components["schemas"]["notImplementedFnOutput"]
|
||||
export type permissionCheckFnInput = components["schemas"]["permissionCheckFnInput"]
|
||||
export type permissionCheckFnOutput = components["schemas"]["permissionCheckFnOutput"]
|
||||
export type setMorphValueInput = components["schemas"]["setMorphValueInput"]
|
||||
export type setMorphValueOutput = components["schemas"]["setMorphValueOutput"]
|
||||
export type staffOnlyOutput = components["schemas"]["staffOnlyOutput"]
|
||||
export type superuserOnlyOutput = components["schemas"]["superuserOnlyOutput"]
|
||||
export type verifiedOnlyOutput = components["schemas"]["verifiedOnlyOutput"]
|
||||
export type whoamiOutput = components["schemas"]["whoamiOutput"]
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
/**
|
||||
* E2E Test Fixtures — FastAPI flavor.
|
||||
*
|
||||
* Mirrors examples/django-react-site/harness/src/fixtures.tsx minus the
|
||||
* Django-only surfaces (channels, forms). Each fixture uses GENERATED
|
||||
* mizan hooks (not raw call()). Playwright reads the DOM to verify
|
||||
* behavior. URL hash selects the fixture: #echo, #add, etc.
|
||||
* The URL hash selects which component renders; each one drives one generated
|
||||
* hook and writes its outcome into data-testid nodes the Playwright spec reads.
|
||||
*/
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
|
||||
import {
|
||||
MizanContext,
|
||||
MorphsContext,
|
||||
useEcho,
|
||||
useAdd,
|
||||
@@ -19,8 +14,8 @@ import {
|
||||
useStaffOnly,
|
||||
useSuperuserOnly,
|
||||
useVerifiedOnly,
|
||||
useNotImplementedFn,
|
||||
useBuggyFn,
|
||||
useEchoTransform,
|
||||
useDivide,
|
||||
usePermissionCheckFn,
|
||||
useCurrentUser,
|
||||
useMorphGroups,
|
||||
@@ -50,8 +45,8 @@ export function Fixtures() {
|
||||
case 'staff-only': return <StaffOnly />
|
||||
case 'superuser-only': return <SuperuserOnly />
|
||||
case 'verified-only': return <VerifiedOnly />
|
||||
case 'not-implemented': return <NotImplemented />
|
||||
case 'internal-error': return <InternalError />
|
||||
case 'not-implemented': return <UnsupportedEchoMode />
|
||||
case 'internal-error': return <DivideByZero />
|
||||
case 'permission-error': return <PermissionError_ />
|
||||
case 'permission-success': return <PermissionSuccess />
|
||||
case 'context-current-user': return <ContextCurrentUser />
|
||||
@@ -105,7 +100,7 @@ function Multiply() { const r = useRun(useMultiply, { x: 6, y: 7 }); return <Res
|
||||
function NotFound() {
|
||||
const { call } = useMizan()
|
||||
const [error, setError] = useState<unknown>()
|
||||
useEffect(() => { call('does_not_exist').catch(setError) }, [call])
|
||||
useEffect(() => { call('does_not_exist', {}).catch(setError) }, [call])
|
||||
return <Result error={error} />
|
||||
}
|
||||
|
||||
@@ -120,20 +115,16 @@ function AuthRequired() { const r = useRun(useWhoami); return <Result {...r} />
|
||||
function StaffOnly() { const r = useRun(useStaffOnly); return <Result {...r} /> }
|
||||
function SuperuserOnly() { const r = useRun(useSuperuserOnly); return <Result {...r} /> }
|
||||
function VerifiedOnly() { const r = useRun(useVerifiedOnly); return <Result {...r} /> }
|
||||
function NotImplemented() { const r = useRun(useNotImplementedFn); return <Result {...r} /> }
|
||||
function InternalError() { const r = useRun(useBuggyFn); return <Result {...r} /> }
|
||||
function UnsupportedEchoMode() { const r = useRun(useEchoTransform, { text: 'e2e-test', mode: 'rot13' }); return <Result {...r} /> }
|
||||
function DivideByZero() { const r = useRun(useDivide, { numerator: 1, denominator: 0 }); return <Result {...r} /> }
|
||||
function PermissionError_() { const r = useRun(usePermissionCheckFn, { secret: 'wrong' }); return <Result {...r} /> }
|
||||
function PermissionSuccess() { const r = useRun(usePermissionCheckFn, { secret: 'open-sesame' }); return <Result {...r} /> }
|
||||
|
||||
|
||||
function ContextCurrentUser() {
|
||||
try {
|
||||
const user = useCurrentUser()
|
||||
if (user === null) return <div>loading context...</div>
|
||||
return <pre data-testid="result">{JSON.stringify(user)}</pre>
|
||||
} catch {
|
||||
return <div>loading context...</div>
|
||||
}
|
||||
const user = useCurrentUser()
|
||||
if (user === null) return <div>loading context...</div>
|
||||
return <pre data-testid="result">{JSON.stringify(user)}</pre>
|
||||
}
|
||||
|
||||
|
||||
@@ -150,14 +141,16 @@ function MergeMorphInner() {
|
||||
const layers = useMorphLayers()
|
||||
const { mutate } = useSetMorphValue()
|
||||
const [fired, setFired] = useState(false)
|
||||
const [error, setError] = useState<unknown>()
|
||||
|
||||
useEffect(() => {
|
||||
if (layers && groups && !fired) {
|
||||
setFired(true)
|
||||
mutate({ id: 1, value: 0.75 })
|
||||
mutate({ id: 1, value: 0.75 }).catch(setError)
|
||||
}
|
||||
}, [layers, groups, fired, mutate])
|
||||
|
||||
if (error !== undefined) return <Result error={error} />
|
||||
if (layers === null || groups === null) return <div>loading...</div>
|
||||
return <pre data-testid="result">{JSON.stringify({ groups, layers })}</pre>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user