fix(dev): treat a wildcard listener as holding the port - #3704
Conversation
`veryfront dev` probes 127.0.0.1 and ::1 to decide whether a port is free. A server that listens without naming a host binds `::` or `0.0.0.0`, and BSD and macOS let a more specific address bind over a wildcard holder - so both loopback probes came back free, `findAvailablePort` returned the busy port, and the dev server bound 127.0.0.1:3000 beside the existing listener instead of falling forward. Nothing failed loudly. Two processes ended up on port 3000, and the `http://localhost:3000` the CLI printed resolved to whichever family the resolver returned first - so the URL it had just printed served the other process. Probe the two wildcards alongside the two loopback addresses. A port counts as available only when nothing holds it on any of them.
📦 Client bundle boundary
A server module in a client graph aborts hydration in the browser. New leaks fail CI; known leaks are tracked in |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughPort availability probing now checks IPv4 and IPv6 loopback and wildcard addresses. Tests verify wildcard listener detection and skip unsupported address families. ChangesPort probing
Estimated code review effort: 2 (Simple) | ~10 minutes Merge Risk: ⚪ Minimal · up to The change makes wildcard listeners count as occupying a port and includes passing focused and full CLI checks; no actionable merge-blocking risk remains beyond normal review. Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Problem
veryfront devprintsReady — http://localhost:3000and then serves a different process's app on that URL.Reproduced on macOS with an unrelated dev server already running:
listen(3000))*:3000(::, dual-stack)veryfront dev127.0.0.1:3000http://localhost:3000resolves to::1first, so the browser reaches the other process.http://127.0.0.1:3000reaches the veryfront app. Neither the port fall-forward nor thePort N is in usewarning fired.Cause
PROBE_HOSTNAMESchecks only the two specific loopback addresses. BSD and macOS permit binding a more specific address over a wildcard holder, so both probes report free:#3562 added fall-forward; #3650 widened the probe from IPv4-only to both loopback families. Both addresses #3650 added are specific, so a wildcard holder — which is what any bare
listen(port)creates — is still invisible.Fix
Probe the two wildcards alongside the two loopback addresses. A port counts as available only when nothing holds it on any of them.
Testing
Test first, and it failed for the right reason before the change:
After:
ok | 1 passed (24 steps) | 0 failed. Full CLI suite:ok | 324 passed (3284 steps) | 0 failed.deno lintanddeno checkclean.Verified against the live collision that prompted this, with the real listeners still up:
Notes for review
192.168.1.5:3000). That falls forward and printsPort 3000 is in use, using 3001 instead, which is the safe direction — the alternative is the silent collision above.clearLocalCachesIfPortFree(cli/commands/dev/command.ts:148) shares this probe. The broadening only makes it skip clearing caches while another server holds the port — also the safe direction.cli/commands/dev/command.ts:258hardcodeshttp://localhost:${boundPort}rather than deriving the URL from the bound address. It stops being user-visible once fall-forward works; worth a separate change.PROBE_HOSTNAMESdoc comment is condensed rather than extended; the rationale from fix(dev): probe both loopback families for port availability, and de-flake its test #3650 is preserved in shorter form.Summary by CodeRabbit