March 17, 2026 — v1.3.0 (BoundCamera + lag fixes)
Major client-SDK ergonomics refactor and three independent fixes for UI lag that accumulated under long-running sessions.
cam(id)bound accessor —manager.camera(id)returns aBoundCamerawith every API method pre-bound to the camera ID. Callers no longer repeatcameraIdon every invocation:const cam = manager.camera(id);await cam.setProperty("iso", { value: "0x320" });await cam.afShutter();CameraManagernow drives discovery polling with auto-reconnect,autoConnect, and event-driven updates. Replaces the older ad-hocCameraClient+ manual polling pattern.
- Poll stacking under idle —
setIntervalhad no in-flight guard, so polls queued after the browser woke and saturated the 6-connection pool. AddedisPollingflag inCameraManagerCore.poll(). - Property-refresh connection exhaustion —
refreshPropertiesfired 12 parallelgetPropertyGETs; after ~2 minutes all connections were held. Replaced with a singlegetAllPropertiescall plus a mutex (isRefreshingRef) against concurrent bursts. - Ghost SSE callbacks after reconnect —
EventStream.off()removed handlers from an internal map but not from the nativeEventSource. AddednativeListenerMapsooff()reaches both layers. - ISO duplicate entries in
getAllProperties— SDK returns two ISO rows (one read-only, no values; one writable). Server-side dedup now prefers the entry with values/writable.