Fixes #373.
Problem
Each dns-sd -P (or avahi-publish-address) child removes its hostname from activePublishers when it exits, without checking which child the entry belongs to:
child.on("exit", () => {
activePublishers.delete(hostname);
});Two paths start a replacement before the old child has exited:
- Port change in
reloadRoutes:unpublish(host)thenpublish(host). - LAN IP change in
updateLanIp:cleanupAll()thenpublishCachedRoutes(). This runs often on machines with Ethernet and Wi-Fi on the same LAN, on short DHCP leases, and after sleep/wake.
The old child's exit fires after the replacement is registered and deletes the replacement's entry. The replacement keeps running but nothing tracks it, so:
- After a LAN IP change, the next change can't kill it, and it keeps answering with the old IP. This is the symptom in #373.
- When its route is removed,
unpublish()finds nothing to kill. The hostname stays advertised and LAN clients land on the proxy's "No app registered" 404.
Fix
The exit and error handlers now remove the entry only if the map still points at the exiting child.
Tests
mdns.test.ts gets cases that replace spawn with a fake child and replay both sequences (port change, and cleanupAll plus republish) with a late exit/error from the old child. Two of the new tests fail on main and pass with this change. pnpm lint, pnpm type-check and Prettier pass.
Not covered here
If the proxy dies hard (crash or SIGKILL), its publishers outlive it, because they are never told to stop. That needs a separate change (for example, reaping leftover publishers on startup).
🤖 Generated with Claude Code
@akshitkrnagpal is attempting to deploy a commit to the Vercel Labs Team on Vercel.
A member of the Team first needs to authorize it.