agent-orchestrator/ui-electrobun/src/mainview/rpc.ts
Hibryda ccbdc1b2b1 feat(e2e): Electrobun CEF E2E working — 13/18 specs pass!
Root cause: CEF views:// protocol can't serve ES modules.
Fix: navigate CEF to Vite dev server (http://localhost:9760/) via
ChromeDriver after launch. Graceful RPC degradation (no-ops when
RPC not initialized) allows app to mount without native bridge.

Results: 13 PASS, 5 FAIL (smoke, settings, theme, notifications,
diagnostics — selector differences, not infrastructure issues)
2026-03-22 07:46:47 +01:00

52 lines
2.3 KiB
TypeScript

/**
* RPC singleton — breaks the circular import chain.
*
* main.ts creates the Electroview and RPC, then sets it here.
* All other modules import from this file instead of main.ts.
*
* Fix #17: Typed RPC interface instead of `any`.
*/
import type { PtyRPCSchema, PtyRPCRequests, PtyRPCMessages } from '../shared/pty-rpc-schema.ts';
// ── Typed RPC interface ──────────────────────────────────────────────────────
type RequestFn<K extends keyof PtyRPCRequests> = (params: PtyRPCRequests[K]['params']) => Promise<PtyRPCRequests[K]['response']>;
type MessagePayload<K extends keyof PtyRPCMessages> = PtyRPCMessages[K];
type MessageListener<K extends keyof PtyRPCMessages> = (payload: MessagePayload<K>) => void;
export interface AppRpcHandle {
request: { [K in keyof PtyRPCRequests]: RequestFn<K> };
addMessageListener: <K extends keyof PtyRPCMessages>(event: K, handler: MessageListener<K>) => void;
removeMessageListener?: <K extends keyof PtyRPCMessages>(event: K, handler: MessageListener<K>) => void;
}
// ── Internal holder ──────────────────────────────────────────────────────────
let _rpc: AppRpcHandle | null = null;
/** Called once from main.ts after Electroview.defineRPC(). */
export function setAppRpc(rpc: AppRpcHandle): void {
_rpc = rpc;
}
/**
* The app-wide RPC handle.
* Safe to call after main.ts has executed (Svelte components mount after).
*/
export const appRpc: AppRpcHandle = new Proxy({} as AppRpcHandle, {
get(_target, prop) {
if (!_rpc) {
// Graceful degradation: return no-ops instead of throwing.
// This allows the app to mount even when RPC isn't available
// (e.g., E2E tests loading via http:// instead of views://).
if (prop === 'request') return new Proxy({}, { get: () => async () => null });
if (prop === 'addMessageListener') return () => {};
if (prop === 'removeMessageListener') return () => {};
console.warn(`[rpc] accessed before init — property "${String(prop)}" (returning no-op)`);
return () => {};
}
return (_rpc as Record<string | symbol, unknown>)[prop];
},
});