Mutation→context merge primitive across the stack
The @client(merge=[context, ...]) decorator lets a mutation patch its
return value directly into the cached context bundle by matching the
mutation's Output type against each context-function's Output type
to identify the slot, then splicing server-side. Kernel runs
splice_slot on the response to apply locally — no refetch, no
invalidate-cascade.
Lands H14, H15, H16, M19, M20 from ISSUES.md.
Backends (Django + FastAPI):
_resolve_merges() in both executors walks @client(merge=...) targets,
resolves the per-context slot via types_match_for_merge, and emits
{context, slot, value, params?} entries on the response. Param
auto-scoping mirrors _resolve_invalidation's tier-1 logic.
Frontend kernel (mizan-base):
Response handler reads the merge[] array and applies splice_slot
for each entry — locates the cached context bundle by name+params,
overwrites the named slot with the new value, notifies subscribers.
Core (mizan-python):
@client decorator extended with merge= parameter. Schema export
threads merge metadata onto the OpenAPI x-mizan-functions entries.
Examples / fixtures:
fastapi-react-site harness exercises merge + Playwright spec covers
the end-to-end happy path (mutation → instant UI update without
network refetch). AFI fixture's rename_user function is the
canonical merge target.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
// AUTO-GENERATED by mizan — do not edit
|
||||
|
||||
import { mizanFetch } from '@mizan/base'
|
||||
|
||||
import type { morphGroupsOutput, morphLayersOutput } from '../types'
|
||||
|
||||
export interface MorphsContextData {
|
||||
morph_groups: morphGroupsOutput
|
||||
morph_layers: morphLayersOutput
|
||||
}
|
||||
|
||||
export type MorphsContextParams = Record<string, never>
|
||||
|
||||
export function fetchMorphsContext(params: MorphsContextParams): Promise<MorphsContextData> {
|
||||
return mizanFetch('morphs', params)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// AUTO-GENERATED by mizan — do not edit
|
||||
|
||||
import { mizanCall } from '@mizan/base'
|
||||
|
||||
import type { setMorphValueInput, setMorphValueOutput } from '../types'
|
||||
|
||||
export function callSetMorphValue(args: setMorphValueInput): Promise<setMorphValueOutput> {
|
||||
return mizanCall('set_morph_value', args)
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
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'
|
||||
@@ -14,6 +15,7 @@ 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'
|
||||
|
||||
// Stage 2 framework adapter
|
||||
export * from './react'
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
type ContextState,
|
||||
} from '@mizan/base'
|
||||
|
||||
import { fetchGlobalContext, type GlobalContextData, type GlobalContextParams, callEcho, callAdd, callMultiply, callWhoami, callStaffOnly, callSuperuserOnly, callVerifiedOnly, callNotImplementedFn, callBuggyFn, callPermissionCheckFn } from './index'
|
||||
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'
|
||||
|
||||
// Internal — runs inside a Provider, registers with the kernel exactly once.
|
||||
function useContextSubscription<T>(
|
||||
@@ -94,6 +94,29 @@ 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 }) {
|
||||
const state = useContextSubscription('morphs', {}, () => fetchMorphsContext({} as any))
|
||||
return <MorphsCtx.Provider value={state}>{children}</MorphsCtx.Provider>
|
||||
}
|
||||
|
||||
export function useMorphsContext(): ContextState<MorphsContextData> {
|
||||
const ctx = useContext(MorphsCtx)
|
||||
if (!ctx) throw new Error('useMorphsContext requires <MorphsContext>')
|
||||
return ctx
|
||||
}
|
||||
|
||||
export function useMorphGroups(): morphGroupsOutput | null {
|
||||
return useMorphsContext().data?.morph_groups ?? null
|
||||
}
|
||||
|
||||
export function useMorphLayers(): morphLayersOutput | null {
|
||||
return useMorphsContext().data?.morph_layers ?? null
|
||||
}
|
||||
|
||||
export function useEcho() {
|
||||
return useMutation<Parameters<typeof callEcho>[0], Awaited<ReturnType<typeof callEcho>>>(callEcho)
|
||||
}
|
||||
@@ -134,11 +157,17 @@ 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
|
||||
/** Set to `false` for backends without a `/session/` endpoint (e.g. FastAPI). */
|
||||
session?: boolean
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
@@ -146,10 +175,13 @@ export interface MizanContextProps {
|
||||
* Root provider — calls configure() once and mounts the global context (if defined).
|
||||
* Must wrap any component using Mizan-generated hooks.
|
||||
*/
|
||||
export function MizanContext({ baseUrl, children }: MizanContextProps) {
|
||||
export function MizanContext({ baseUrl, session, children }: MizanContextProps) {
|
||||
const configured = useRef(false)
|
||||
if (!configured.current) {
|
||||
if (baseUrl) configure({ baseUrl })
|
||||
const opts: Parameters<typeof configure>[0] = {}
|
||||
if (baseUrl !== undefined) opts.baseUrl = baseUrl
|
||||
if (session !== undefined) opts.session = session
|
||||
if (Object.keys(opts).length > 0) configure(opts)
|
||||
configured.current = true
|
||||
}
|
||||
return <GlobalContextProvider>{children}</GlobalContextProvider>
|
||||
|
||||
@@ -327,6 +327,92 @@
|
||||
"isContext": "global"
|
||||
}
|
||||
}
|
||||
},
|
||||
"/mizan/morph_groups": {
|
||||
"post": {
|
||||
"summary": "Summary-shape slot — server must route MorphLayer mutations away from here.",
|
||||
"operationId": "morphGroups",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/morphGroupsOutput"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-mizan": {
|
||||
"transport": "http",
|
||||
"isContext": "morphs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"/mizan/morph_layers": {
|
||||
"post": {
|
||||
"summary": "Detailed-shape slot — server routes MorphLayer mutations here.",
|
||||
"operationId": "morphLayers",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/morphLayersOutput"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-mizan": {
|
||||
"transport": "http",
|
||||
"isContext": "morphs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"/mizan/set_morph_value": {
|
||||
"post": {
|
||||
"summary": "Mutation that returns the changed row; kernel splices into morph_layers.",
|
||||
"operationId": "setMorphValue",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/setMorphValueInput"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/setMorphValueOutput"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-mizan": {
|
||||
"transport": "http",
|
||||
"isContext": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
@@ -344,6 +430,58 @@
|
||||
"type": "object",
|
||||
"title": "HTTPValidationError"
|
||||
},
|
||||
"MorphGroupMeta": {
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"title": "Id"
|
||||
},
|
||||
"label": {
|
||||
"type": "string",
|
||||
"title": "Label"
|
||||
},
|
||||
"count": {
|
||||
"type": "integer",
|
||||
"title": "Count"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"label",
|
||||
"count"
|
||||
],
|
||||
"title": "MorphGroupMeta",
|
||||
"description": "Group summary — narrower shape than MorphLayer. Listed alongside\nmorph_layers so the server's slot resolver has to discriminate by\nreturn-type rather than by bundle order."
|
||||
},
|
||||
"MorphLayer": {
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"title": "Id"
|
||||
},
|
||||
"group_id": {
|
||||
"type": "integer",
|
||||
"title": "Group Id"
|
||||
},
|
||||
"label": {
|
||||
"type": "string",
|
||||
"title": "Label"
|
||||
},
|
||||
"value": {
|
||||
"type": "number",
|
||||
"title": "Value"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"group_id",
|
||||
"label",
|
||||
"value"
|
||||
],
|
||||
"title": "MorphLayer"
|
||||
},
|
||||
"ValidationError": {
|
||||
"properties": {
|
||||
"loc": {
|
||||
@@ -477,6 +615,20 @@
|
||||
],
|
||||
"title": "echoOutput"
|
||||
},
|
||||
"morphGroupsOutput": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/MorphGroupMeta"
|
||||
},
|
||||
"type": "array",
|
||||
"title": "morphGroupsOutput"
|
||||
},
|
||||
"morphLayersOutput": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/MorphLayer"
|
||||
},
|
||||
"type": "array",
|
||||
"title": "morphLayersOutput"
|
||||
},
|
||||
"multiplyInput": {
|
||||
"properties": {
|
||||
"x": {
|
||||
@@ -547,6 +699,52 @@
|
||||
],
|
||||
"title": "permissionCheckFnOutput"
|
||||
},
|
||||
"setMorphValueInput": {
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"title": "Id"
|
||||
},
|
||||
"value": {
|
||||
"type": "number",
|
||||
"title": "Value"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"value"
|
||||
],
|
||||
"title": "setMorphValueInput"
|
||||
},
|
||||
"setMorphValueOutput": {
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"title": "Id"
|
||||
},
|
||||
"group_id": {
|
||||
"type": "integer",
|
||||
"title": "Group Id"
|
||||
},
|
||||
"label": {
|
||||
"type": "string",
|
||||
"title": "Label"
|
||||
},
|
||||
"value": {
|
||||
"type": "number",
|
||||
"title": "Value"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"group_id",
|
||||
"label",
|
||||
"value"
|
||||
],
|
||||
"title": "setMorphValueOutput"
|
||||
},
|
||||
"staffOnlyOutput": {
|
||||
"properties": {
|
||||
"message": {
|
||||
@@ -743,6 +941,45 @@
|
||||
"isForm": false,
|
||||
"formName": null,
|
||||
"formRole": null
|
||||
},
|
||||
{
|
||||
"name": "morph_groups",
|
||||
"camelName": "morphGroups",
|
||||
"hasInput": false,
|
||||
"inputType": null,
|
||||
"outputType": "morphGroupsOutput",
|
||||
"transport": "http",
|
||||
"isContext": "morphs",
|
||||
"isForm": false,
|
||||
"formName": null,
|
||||
"formRole": null
|
||||
},
|
||||
{
|
||||
"name": "morph_layers",
|
||||
"camelName": "morphLayers",
|
||||
"hasInput": false,
|
||||
"inputType": null,
|
||||
"outputType": "morphLayersOutput",
|
||||
"transport": "http",
|
||||
"isContext": "morphs",
|
||||
"isForm": false,
|
||||
"formName": null,
|
||||
"formRole": null
|
||||
},
|
||||
{
|
||||
"name": "set_morph_value",
|
||||
"camelName": "setMorphValue",
|
||||
"hasInput": true,
|
||||
"inputType": "setMorphValueInput",
|
||||
"outputType": "setMorphValueOutput",
|
||||
"transport": "http",
|
||||
"isContext": false,
|
||||
"isForm": false,
|
||||
"formName": null,
|
||||
"formRole": null,
|
||||
"merge": [
|
||||
"morphs"
|
||||
]
|
||||
}
|
||||
],
|
||||
"x-mizan-contexts": {
|
||||
@@ -751,6 +988,13 @@
|
||||
"current_user"
|
||||
],
|
||||
"params": {}
|
||||
},
|
||||
"morphs": {
|
||||
"functions": [
|
||||
"morph_groups",
|
||||
"morph_layers"
|
||||
],
|
||||
"params": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -188,6 +188,57 @@ export interface paths {
|
||||
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 {
|
||||
@@ -197,6 +248,31 @@ export interface components {
|
||||
/** 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 */
|
||||
@@ -249,6 +325,10 @@ export interface components {
|
||||
/** Message */
|
||||
message: string;
|
||||
};
|
||||
/** morphGroupsOutput */
|
||||
morphGroupsOutput: components["schemas"]["MorphGroupMeta"][];
|
||||
/** morphLayersOutput */
|
||||
morphLayersOutput: components["schemas"]["MorphLayer"][];
|
||||
/** multiplyInput */
|
||||
multiplyInput: {
|
||||
/** X */
|
||||
@@ -276,6 +356,24 @@ export interface components {
|
||||
/** 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 */
|
||||
@@ -584,11 +682,86 @@ export interface operations {
|
||||
};
|
||||
};
|
||||
};
|
||||
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"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// 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"]
|
||||
@@ -596,11 +769,15 @@ 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"]
|
||||
|
||||
@@ -11,6 +11,7 @@ import { useState, useEffect } from 'react'
|
||||
|
||||
import {
|
||||
MizanContext,
|
||||
MorphsContext,
|
||||
useEcho,
|
||||
useAdd,
|
||||
useMultiply,
|
||||
@@ -22,6 +23,9 @@ import {
|
||||
useBuggyFn,
|
||||
usePermissionCheckFn,
|
||||
useCurrentUser,
|
||||
useMorphGroups,
|
||||
useMorphLayers,
|
||||
useSetMorphValue,
|
||||
MizanError,
|
||||
useMizan,
|
||||
} from './api'
|
||||
@@ -51,6 +55,7 @@ export function Fixtures() {
|
||||
case 'permission-error': return <PermissionError_ />
|
||||
case 'permission-success': return <PermissionSuccess />
|
||||
case 'context-current-user': return <ContextCurrentUser />
|
||||
case 'merge-morph': return <MergeMorph />
|
||||
default: return <div data-testid="ready">Harness ready. Set #hash.</div>
|
||||
}
|
||||
}
|
||||
@@ -130,3 +135,29 @@ function ContextCurrentUser() {
|
||||
return <div>loading context...</div>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function MergeMorph() {
|
||||
return (
|
||||
<MorphsContext>
|
||||
<MergeMorphInner />
|
||||
</MorphsContext>
|
||||
)
|
||||
}
|
||||
|
||||
function MergeMorphInner() {
|
||||
const groups = useMorphGroups()
|
||||
const layers = useMorphLayers()
|
||||
const { mutate } = useSetMorphValue()
|
||||
const [fired, setFired] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (layers && groups && !fired) {
|
||||
setFired(true)
|
||||
mutate({ id: 1, value: 0.75 })
|
||||
}
|
||||
}, [layers, groups, fired, mutate])
|
||||
|
||||
if (layers === null || groups === null) return <div>loading...</div>
|
||||
return <pre data-testid="result">{JSON.stringify({ groups, layers })}</pre>
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Fixtures } from './fixtures'
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<MizanContext baseUrl="/api/mizan">
|
||||
<MizanContext baseUrl="/api/mizan" session={false}>
|
||||
<Fixtures />
|
||||
</MizanContext>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user