feat(companion): support runtime client autoConnect mode

This commit is contained in:
William Valentin
2026-02-16 18:36:50 -08:00
parent 5db7beeb53
commit 8d123cf859
5 changed files with 67 additions and 2 deletions
+27
View File
@@ -268,4 +268,31 @@ describe('CompanionRuntimeClient', () => {
client.disconnect();
}
});
it('supports autoConnect mode for one-shot RPC usage', async () => {
if (!LISTEN_ALLOWED) {
return;
}
const client = new CompanionRuntimeClient({
url: `ws://127.0.0.1:${TEST_PORT}`,
token: TEST_TOKEN,
autoConnect: true,
});
expect(client.connected).toBe(false);
try {
const register = await client.registerNode({
nodeId: 'auto-connect-node',
role: 'companion',
capabilities: ['ui.canvas'],
});
expect(register.registered).toBe(true);
expect(client.connected).toBe(true);
} finally {
client.disconnect();
}
});
});
+24
View File
@@ -37,6 +37,7 @@ export interface CompanionRuntimeClientOptions {
url: string;
token?: string;
requestTimeoutMs?: number;
autoConnect?: boolean;
websocketFactory?: (url: string) => WebSocket;
}
@@ -249,9 +250,11 @@ export class CompanionRuntimeClient {
private readonly url: string;
private readonly token?: string;
private readonly requestTimeoutMs: number;
private readonly autoConnect: boolean;
private readonly websocketFactory: (url: string) => WebSocket;
private ws: WebSocket | null = null;
private connectPromise: Promise<void> | null = null;
private nextId = 1;
private pending = new Map<number, PendingRequest>();
@@ -259,6 +262,7 @@ export class CompanionRuntimeClient {
this.url = options.url;
this.token = options.token;
this.requestTimeoutMs = options.requestTimeoutMs ?? 15_000;
this.autoConnect = options.autoConnect ?? false;
this.websocketFactory = options.websocketFactory ?? ((url) => new WebSocket(url));
}
@@ -271,6 +275,19 @@ export class CompanionRuntimeClient {
return;
}
if (this.connectPromise) {
return this.connectPromise;
}
this.connectPromise = this.openConnection();
try {
await this.connectPromise;
} finally {
this.connectPromise = null;
}
}
private async openConnection(): Promise<void> {
const ws = this.websocketFactory(withToken(this.url, this.token));
await new Promise<void>((resolve, reject) => {
@@ -326,6 +343,13 @@ export class CompanionRuntimeClient {
}
async call<T>(method: string, params?: Record<string, unknown>): Promise<T> {
if (!this.connected) {
if (!this.autoConnect) {
throw new Error('WebSocket is not connected');
}
await this.connect();
}
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
throw new Error('WebSocket is not connected');
}