({
response,
library,
isStreaming = false,
onAction,
onStateUpdate,
initialState,
onParseResult,
toolProvider,
queryLoader,
onError,
}: RendererProps)
| 199 | ); |
| 200 | |
| 201 | export function Renderer({ |
| 202 | response, |
| 203 | library, |
| 204 | isStreaming = false, |
| 205 | onAction, |
| 206 | onStateUpdate, |
| 207 | initialState, |
| 208 | onParseResult, |
| 209 | toolProvider, |
| 210 | queryLoader, |
| 211 | onError, |
| 212 | }: RendererProps) { |
| 213 | useInsertionEffect(() => { |
| 214 | ensureLoadingStyle(); |
| 215 | }, []); |
| 216 | |
| 217 | const onParseResultRef = useRef(onParseResult); |
| 218 | onParseResultRef.current = onParseResult; |
| 219 | |
| 220 | // Stable ToolProvider wrapper — identity never changes, so QueryManager |
| 221 | // is created once. callTool() reads the latest input from a ref on every |
| 222 | // call, so function map updates, closure changes, and provider swaps |
| 223 | // are always observed without triggering re-creation. |
| 224 | const toolProviderInputRef = useRef(toolProvider); |
| 225 | toolProviderInputRef.current = toolProvider; |
| 226 | |
| 227 | const stableToolProvider = useRef<ToolProvider>({ |
| 228 | async callTool(toolName: string, args: Record<string, unknown>): Promise<unknown> { |
| 229 | const current = toolProviderInputRef.current ?? null; |
| 230 | if (current == null) throw new Error("[openui] toolProvider is null"); |
| 231 | // MCP client — has callTool({ name, arguments }) returning MCP envelope |
| 232 | if (typeof (current as McpClientLike).callTool === "function") { |
| 233 | const result = await (current as McpClientLike).callTool({ |
| 234 | name: toolName, |
| 235 | arguments: args, |
| 236 | }); |
| 237 | return extractToolResult(result); |
| 238 | } |
| 239 | // Function map — plain object of async functions |
| 240 | const map = current as Record<string, (a: Record<string, unknown>) => Promise<unknown>>; |
| 241 | const fn = map[toolName]; |
| 242 | if (!fn) throw new ToolNotFoundError(toolName, Object.keys(map)); |
| 243 | return fn(args); |
| 244 | }, |
| 245 | }); |
| 246 | const resolvedToolProvider = toolProvider != null ? stableToolProvider.current : null; |
| 247 | |
| 248 | const { result, parseResult, contextValue, isQueryLoading } = useOpenUIState( |
| 249 | { |
| 250 | response, |
| 251 | library, |
| 252 | isStreaming, |
| 253 | onAction, |
| 254 | onStateUpdate, |
| 255 | initialState, |
| 256 | toolProvider: resolvedToolProvider, |
| 257 | onError, |
| 258 | }, |
nothing calls this directly
no test coverage detected