You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(pptx): serialize automation commands, and enforce require-atomic-updates (#70)
Every PowerPoint backend caches the slideshow position in module-level state
and refreshes it from PowerPoint across an `await` - an AppleScript round trip
on macOS, a PowerShell bridge command or a COM call on Windows. Nothing stopped
two of those from overlapping: the web remote can fire `next` from several
devices at once, a double-tap queues two `nextSlide()` calls, and the 500 ms
poll in server/socket.ts calls `getSlideInfo()` on top of both.
`require-atomic-updates` flagged seven sites where that state is read before an
await and written after it, and #64 had to land the rule as 'warn' to keep CI
green. This fixes the sites and turns it up to 'error'.
serializeAutomation() chains every call onto one promise, so a backend has at
most one command in flight - which is also what the PowerShell bridge's FIFO
response matching already assumed. Each backend exports its object already
wrapped, so main/index.ts and server/socket.ts share one queue rather than
getting a mutex each. Methods are listed explicitly instead of proxied, so
adding an unserialized method to the interface is a type error.
The mutex alone does not satisfy the lint rule, which is purely syntactic, so
each flagged site also re-reads its state after the await. That is deliberate
belt and braces: it keeps the invariant checkable instead of resting on the
wrapper still being there. nextSlide now commits slide and animation step in one
synchronous block (same result as before - the old code incremented the step
then zeroed it if the slide had moved), and localPresentationCopy is cleared
before the unlink is awaited rather than after.
The 500 ms poll needed a guard as a direct consequence: overlapping polls used
to just run concurrently, but under serialization a poll that outlives its tick
queues behind the next poll and every remote command, so a slow backend would
grow an unbounded backlog. It now skips a tick while one is outstanding.
Verified against a real PowerPoint 16.112.3 on macOS with a generated six-slide
deck (slide 3 hidden): openPresentation, gotoSlide, getSlideInfo,
closePresentation, concurrent and burst navigation, and the temp-copy cleanup.
With the serializer stubbed out, two concurrent gotoSlide calls interleave and
one is silently dropped - PowerPoint stays on slide 5 after a request for slide
2. With it in place the commands apply in order.
Not verified against a real install: startSlideshow, nextSlide, prevSlide and
stopSlideshow on macOS go through System Events keystrokes, and Accessibility
permission was not granted to the test process. Neither Windows backend was run
at all; their changes are the same three patterns applied identically.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
0 commit comments