diff --git a/README.md b/README.md index 5df982d..6050550 100644 --- a/README.md +++ b/README.md @@ -1199,6 +1199,7 @@ Companion runtime helper: - shared `publishHeartbeat()` helper for periodic `node.status.set` updates with safe defaults - `createHeartbeatLoop()` convenience helper that returns a bound `CompanionHeartbeatLoop` - optional `defaultSessionId` for canvas helper calls so `sessionId` can be omitted per call + - `dispose()` lifecycle helper for unified runtime teardown - `src/companion/heartbeatLoop.ts` provides `CompanionHeartbeatLoop` for periodic heartbeat scheduling (`publishHeartbeat`) with start/stop safety, optional interval jitter (`jitterRatio`) to spread load, `tickNow()` for manual sends, error hooks, failure observability (`failureCount`, `lastFailure`), and optional auto-stop after repeated failures. ## Canvas / A2UI Foundation diff --git a/docs/plans/state.json b/docs/plans/state.json index b1ea22c..47ff4a5 100644 --- a/docs/plans/state.json +++ b/docs/plans/state.json @@ -460,6 +460,19 @@ ], "test_status": "pnpm test:run src/companion/runtimeClient.test.ts src/companion/heartbeatLoop.test.ts src/companion/platformClients.test.ts src/companion/platformClients.integration.test.ts + pnpm typecheck passing" }, + "companion-platform-dispose-helper": { + "status": "completed", + "date": "2026-02-17", + "updated": "2026-02-17", + "summary": "Added `dispose()` lifecycle passthrough on platform clients (macOS/iOS/Android) for unified companion runtime teardown handling.", + "files_modified": [ + "src/companion/platformClients.ts", + "src/companion/platformClients.test.ts", + "README.md", + "docs/plans/state.json" + ], + "test_status": "pnpm test:run src/companion/platformClients.test.ts src/companion/runtimeClient.test.ts src/companion/heartbeatLoop.test.ts src/companion/platformClients.integration.test.ts + pnpm typecheck passing" + }, "browser-tools-activation-clarity": { "status": "completed", "date": "2026-02-17", diff --git a/src/companion/platformClients.test.ts b/src/companion/platformClients.test.ts index 8ebf21e..91df41a 100644 --- a/src/companion/platformClients.test.ts +++ b/src/companion/platformClients.test.ts @@ -10,6 +10,7 @@ function createRuntimeMock(): { runtime: CompanionRuntimeClient; connect: ReturnType; disconnect: ReturnType; + dispose: ReturnType; registerNode: ReturnType; getNodeCapabilities: ReturnType; setNodeStatus: ReturnType; @@ -26,6 +27,7 @@ function createRuntimeMock(): { } { const connect = vi.fn(async () => undefined); const disconnect = vi.fn(() => undefined); + const dispose = vi.fn(() => undefined); const registerNode = vi.fn(async () => ({ registered: true })); const getNodeCapabilities = vi.fn(async () => ({ node: { id: 'n1', role: 'companion', registeredAt: Date.now() }, protocol: { serverVersion: 1, nodeVersion: 1, negotiatedVersion: 1 }, capabilities: { declared: [], enabled: [], featureGates: {} } })); const setNodeStatus = vi.fn(async () => ({ updated: true })); @@ -43,6 +45,7 @@ function createRuntimeMock(): { const runtime = { connect, disconnect, + dispose, registerNode, getNodeCapabilities, setNodeStatus, @@ -62,6 +65,7 @@ function createRuntimeMock(): { runtime, connect, disconnect, + dispose, registerNode, getNodeCapabilities, setNodeStatus, @@ -127,6 +131,14 @@ describe('platform companion clients', () => { expect(mock.listSystemNodes).toHaveBeenCalledWith({ platform: 'android', role: 'companion' }); }); + it('platform dispose forwards to runtime dispose', async () => { + const mock = createRuntimeMock(); + const client = new MacOSCompanionClient({ runtime: mock.runtime, nodeId: 'mac-node' }); + + client.dispose(); + expect(mock.dispose).toHaveBeenCalledOnce(); + }); + it('macOS client forwards canvas methods to runtime client', async () => { const mock = createRuntimeMock(); const client = new MacOSCompanionClient({ runtime: mock.runtime, nodeId: 'mac-node' }); diff --git a/src/companion/platformClients.ts b/src/companion/platformClients.ts index a0c733b..5815010 100644 --- a/src/companion/platformClients.ts +++ b/src/companion/platformClients.ts @@ -95,6 +95,10 @@ export class MacOSCompanionClient { this.runtime.disconnect(); } + dispose(): void { + this.runtime.dispose(); + } + register(): Promise { return this.runtime.registerNode({ nodeId: this.nodeId, @@ -231,6 +235,10 @@ export class IOSCompanionClient { this.runtime.disconnect(); } + dispose(): void { + this.runtime.dispose(); + } + register(): Promise { return this.runtime.registerNode({ nodeId: this.nodeId, @@ -367,6 +375,10 @@ export class AndroidCompanionClient { this.runtime.disconnect(); } + dispose(): void { + this.runtime.dispose(); + } + register(): Promise { return this.runtime.registerNode({ nodeId: this.nodeId,