@enclave-vm/react
React hooks and components for building interactive code execution UIs with the EnclaveJS streaming runtime.Installation
npm install @enclave-vm/react
react>= 17.0.0
Quick Start
import { EnclaveProvider, useEnclaveSession } from '@enclave-vm/react';
function App() {
return (
<EnclaveProvider brokerUrl="http://localhost:3000">
<CodeEditor />
</EnclaveProvider>
);
}
function CodeEditor() {
const { execute, state, stdout, result, error } = useEnclaveSession();
return (
<div>
<button
onClick={() => execute('console.log("Hello!"); return 42;')}
disabled={state === 'running'}
>
Run
</button>
<pre>{stdout}</pre>
{result && <div>Result: {JSON.stringify(result)}</div>}
{error && <div className="error">{error.message}</div>}
</div>
);
}
EnclaveProvider
The provider sets up the EnclaveClient context for your application.import { EnclaveProvider, type EnclaveProviderProps } from '@enclave-vm/react';
const props: EnclaveProviderProps = {
// Required: Broker URL
brokerUrl: 'http://localhost:3000',
// Optional: Custom headers
headers: {
'Authorization': 'Bearer token',
},
// Optional: Timeout (ms)
timeout: 30000,
// Optional: Reconnection config
reconnect: {
enabled: true,
maxAttempts: 5,
},
};
function App() {
return (
<EnclaveProvider {...props}>
<MyApp />
</EnclaveProvider>
);
}
useEnclaveSession Hook
The main hook for executing code and managing session state.Basic Usage
import { useEnclaveSession } from '@enclave-vm/react';
function CodeRunner() {
const {
execute, // Function to execute code
state, // Current session state
stdout, // Accumulated stdout output
result, // Final result (when completed)
error, // Error (if any)
sessionId, // Current session ID
stats, // Execution stats
cancel, // Cancel execution
reset, // Reset state
} = useEnclaveSession();
return (
<div>
<button onClick={() => execute('return 1 + 1')}>
Run
</button>
</div>
);
}
Session States
import type { SessionState } from '@enclave-vm/react';
// Available states
type SessionState =
| 'idle' // No execution in progress
| 'running' // Code is executing
| 'completed' // Execution finished successfully
| 'error' // Execution failed
| 'cancelled'; // Execution was cancelled
function StatusIndicator() {
const { state } = useEnclaveSession();
switch (state) {
case 'idle':
return <span>Ready</span>;
case 'running':
return <span>Running...</span>;
case 'completed':
return <span>Done!</span>;
case 'error':
return <span>Failed</span>;
case 'cancelled':
return <span>Cancelled</span>;
}
}
Options
import { useEnclaveSession, type UseEnclaveSessionOptions } from '@enclave-vm/react';
function CodeRunner() {
const options: UseEnclaveSessionOptions = {
// Default limits for all executions
limits: {
timeout: 30000,
memoryLimit: 128 * 1024 * 1024,
},
// Event handlers
onSessionInit: (event) => {
console.log('Session started:', event.sessionId);
},
onStdout: (chunk) => {
// Custom stdout handling
},
onLog: (level, message, data) => {
console.log(`[${level}]`, message);
},
onToolCall: (callId, name, args) => {
console.log('Tool called:', name);
},
onFinal: (result, stats) => {
console.log('Completed:', result);
},
onError: (error) => {
console.error('Error:', error);
},
// Clear stdout on new execution
clearOnExecute: true,
};
const session = useEnclaveSession(options);
return <div>{/* ... */}</div>;
}
Return Value
import type { UseEnclaveSessionReturn } from '@enclave-vm/react';
const session: UseEnclaveSessionReturn = useEnclaveSession();
// Execute code
await session.execute(code);
await session.execute(code, { timeout: 5000 });
// Current state
session.state; // SessionState
session.sessionId; // SessionId | null
// Output
session.stdout; // string (accumulated)
session.logs; // LogEntry[]
session.result; // any | null
session.error; // Error | null
// Stats
session.stats; // SessionStats | null
// Actions
await session.cancel(); // Cancel current execution
session.reset(); // Reset to idle state
useEnclaveClient Hook
Access the underlying EnclaveClient directly.import { useEnclaveClient } from '@enclave-vm/react';
function CustomComponent() {
const client = useEnclaveClient();
// Use client directly for advanced scenarios
const handleExecute = async () => {
const handle = await client.execute(code, {
onStdout: console.log,
});
const result = await handle.result;
};
return <button onClick={handleExecute}>Execute</button>;
}
useEnclaveContext Hook
Access the full context value including client and configuration.import { useEnclaveContext, type EnclaveContextValue } from '@enclave-vm/react';
function DebugComponent() {
const context: EnclaveContextValue = useEnclaveContext();
return (
<div>
<p>Broker URL: {context.config.baseUrl}</p>
<p>Client ready: {context.client ? 'Yes' : 'No'}</p>
</div>
);
}
Examples
Code Editor with Output
import { useState } from 'react';
import { EnclaveProvider, useEnclaveSession } from '@enclave-vm/react';
function CodeEditor() {
const [code, setCode] = useState('console.log("Hello!");\nreturn 42;');
const { execute, state, stdout, result, error, reset } = useEnclaveSession();
return (
<div className="editor">
<textarea
value={code}
onChange={(e) => setCode(e.target.value)}
disabled={state === 'running'}
/>
<div className="buttons">
<button
onClick={() => execute(code)}
disabled={state === 'running'}
>
{state === 'running' ? 'Running...' : 'Run'}
</button>
<button onClick={reset}>Reset</button>
</div>
<div className="output">
<h3>Output</h3>
<pre>{stdout}</pre>
</div>
{result !== null && (
<div className="result">
<h3>Result</h3>
<pre>{JSON.stringify(result, null, 2)}</pre>
</div>
)}
{error && (
<div className="error">
<h3>Error</h3>
<pre>{error.message}</pre>
</div>
)}
</div>
);
}
function App() {
return (
<EnclaveProvider brokerUrl="http://localhost:3000">
<CodeEditor />
</EnclaveProvider>
);
}
With Tool Calls
function ToolDemo() {
const { execute, state, stdout, result } = useEnclaveSession({
onToolCall: (callId, name, args) => {
console.log(`Tool ${name} called with:`, args);
},
});
const runWithTool = () => {
execute(`
const time = await tools.getCurrentTime();
console.log('Current time:', time);
return time;
`);
};
return (
<div>
<button onClick={runWithTool} disabled={state === 'running'}>
Get Current Time
</button>
<pre>{stdout}</pre>
{result && <p>Time: {result}</p>}
</div>
);
}
With Cancellation
function CancellableExecution() {
const { execute, state, cancel, stdout } = useEnclaveSession();
const runLongTask = () => {
execute(`
for (let i = 0; i < 100; i++) {
console.log('Step', i);
await new Promise(r => setTimeout(r, 100));
}
return 'done';
`);
};
return (
<div>
{state === 'running' ? (
<button onClick={cancel}>Cancel</button>
) : (
<button onClick={runLongTask}>Start Long Task</button>
)}
<pre>{stdout}</pre>
</div>
);
}
Multiple Sessions
function MultiSession() {
const session1 = useEnclaveSession();
const session2 = useEnclaveSession();
return (
<div className="split">
<div>
<h3>Session 1</h3>
<button onClick={() => session1.execute('return 1')}>
Run
</button>
<pre>{session1.stdout}</pre>
</div>
<div>
<h3>Session 2</h3>
<button onClick={() => session2.execute('return 2')}>
Run
</button>
<pre>{session2.stdout}</pre>
</div>
</div>
);
}
TypeScript Types
// Re-exported from @enclave-vm/client
import type {
EnclaveClient,
EnclaveClientConfig,
SessionResult,
SessionHandle,
StreamEvent,
SessionId,
} from '@enclave-vm/react';