@alivelabs/expo-orchestrator-react-client
v0.2.1
Published
React client for Expo CI Orchestrator — streaming logs, live simulator video, and interactive controls.
Downloads
200
Readme
@alivelabs/expo-orchestrator-react-client
React client for the Expo CI Orchestrator. Renders a live build session in the browser: streaming logs, live iOS simulator video, session status, and interactive controls (tap, swipe, type, keypress).
Install
npm install @alivelabs/expo-orchestrator-react-client
# peer deps
npm install react react-domQuick start
import { SessionViewer } from "@alivelabs/expo-orchestrator-react-client";
function App() {
return (
<SessionViewer
sessionId="abc-123"
apiToken="your-api-token"
baseUrl="https://your-orchestrator.example.com"
/>
);
}Hook usage
The live screen is an H.264 stream decoded with WebCodecs onto a <canvas>.
The hook owns the decoder — pass attachCanvas to a <canvas ref>:
import { useExpoCiSession } from "@alivelabs/expo-orchestrator-react-client";
function BuildPage() {
const { status, logs, attachCanvas, videoSupported, connected, sendInput, reconnect } =
useExpoCiSession({
sessionId: "abc-123",
apiToken: "your-api-token",
baseUrl: "https://your-orchestrator.example.com",
autoConnect: true, // default
maxLogs: 2000, // default
});
return (
<div>
<p>Status: {status} — {connected ? "live" : "disconnected"}</p>
{videoSupported ? <canvas ref={attachCanvas} /> : <p>Video not supported in this browser.</p>}
<button onClick={() => sendInput({ type: "button", button: "home" })}>Home</button>
{!connected && <button onClick={reconnect}>Reconnect</button>}
</div>
);
}WebCodecs needs a modern browser (Chrome/Edge, Safari 16.4+, recent Firefox) and
a secure context (HTTPS or localhost); videoSupported is false otherwise. For
full control you can import SimulatorDecoder and drive your own canvas.
Exports reference
| Export | Kind | Description |
|---|---|---|
| SessionViewer | Component | All-in-one: status badge, simulator screen, log console, interaction controls |
| SimulatorScreen | Component | Renders the live H.264 stream to a canvas; click = tap, click-drag = swipe |
| LogConsole | Component | Scrollable, color-coded, auto-scrolling log viewer |
| StatusBadge | Component | Colored pill showing session status |
| useExpoCiSession | Hook | React hook wrapping ExpoCiClient + the WebCodecs decoder |
| ExpoCiClient | Class | Framework-agnostic client: REST + WebSocket with auto-reconnect |
| SimulatorDecoder | Class | WebCodecs H.264 decoder painting to a <canvas> (advanced) |
<SessionViewer> props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| sessionId | string | yes | — | The session to display |
| apiToken | string | yes | — | Bearer token for the API |
| baseUrl | string | no | http://localhost:3000 | Orchestrator base URL |
| className | string | no | — | CSS class applied to the root element |
<SimulatorScreen> props
| Prop | Type | Required | Description |
|---|---|---|---|
| canvasRef | (canvas: HTMLCanvasElement \| null) => void | yes | Ref callback wiring the canvas to the decoder (pass attachCanvas) |
| videoSupported | boolean | yes | Whether WebCodecs is available; shows a notice when false |
| hasFrame | boolean | yes | Whether a frame has painted yet (drives the placeholder) |
| onTap | (input: { type: "tap"; x: number; y: number }) => void | no | Called on click |
| onSwipe | (input: { type: "swipe"; startX; startY; endX; endY }) => void | no | Called on drag |
| onKeyInput | (input: { type: "type" \| "key"; … }) => void | no | Called for keystrokes while focused |
| maxHeight | number \| string | no | Cap on rendered height (default 80vh) |
<LogConsole> props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| logs | LogEntry[] | yes | — | Array of log entries |
| maxHeight | string \| number | no | 400 | CSS max-height of the scroll area |
<StatusBadge> props
| Prop | Type | Required | Description |
|---|---|---|---|
| status | SessionStatus \| null | yes | Session status to display |
useExpoCiSession options
| Option | Type | Default | Description |
|---|---|---|---|
| sessionId | string | — | Required |
| apiToken | string | — | Required |
| baseUrl | string | http://localhost:3000 | Orchestrator base URL |
| autoConnect | boolean | true | Open WebSocket immediately on mount |
| maxLogs | number | 2000 | Max log lines retained in state |
ExpoCiClient API
const client = new ExpoCiClient({ sessionId, apiToken, baseUrl });
client.getSession() // GET /api/sessions/:id → SessionDetail
client.getLogs() // GET /api/sessions/:id/logs → SessionLogs
client.sendInput(input) // POST /api/sessions/:id/simulator/input
client.getScreenshotObjectUrl() // GET /api/sessions/:id/screenshot → object URL (fetch + blob)
client.connect() // Open WebSocket (auto-reconnect with exponential backoff)
client.disconnect() // Close WebSocket and remove all listeners
// Typed event emitter. Video arrives as raw encoded chunks (ArrayBuffer) —
// feed them to a SimulatorDecoder, or let the hook do it for you.
const off = client.on("video-chunk", (chunk) => decoder.push(chunk));
off(); // unsubscribeEvents: open, close, connected, log, video-chunk, status, error.
