Sync modal state with page state

This commit is contained in:
Jon Staab 2026-08-27 19:31:29 -07:00
parent 24a506ea85
commit ac7108c2c4
2 changed files with 44 additions and 0 deletions

View file

@ -1,4 +1,5 @@
import {execFile} from "node:child_process"
import {createConnection} from "node:net"
import {chmod, cp, readdir, rm} from "node:fs/promises"
import {tmpdir} from "node:os"
import {join} from "node:path"
@ -39,6 +40,32 @@ const execFileAsync = promisify(execFile)
// typed. Name the executable to drive with E2E_DOCKER in that case.
const dockerCommand = process.env.E2E_DOCKER ?? "docker"
// The loopback port the container publishes and transport.ts dials. It is fixed rather than
// configurable on purpose: the harness talks to whatever answers here, so a copy of the suite left
// running by anyone else on the machine — under another user, even — would be seeded, queried and
// reset out from under this run. Both places have to agree, so this must match the published port
// in docker/compose.yaml and `port` in transport.ts.
const relayHostPort = 3334
// Whether something already answers on the relay's loopback port. Used as a pre-flight: the port is
// the harness's alone for the length of a run, so anything holding it before this run brings its own
// container up is a foreign occupant we must refuse rather than quietly share.
const isRelayPortTaken = () =>
new Promise<boolean>(resolve => {
const socket = createConnection({port: relayHostPort, host: "127.0.0.1"})
socket.setTimeout(1000)
socket.once("connect", () => {
socket.destroy()
resolve(true)
})
socket.once("timeout", () => {
socket.destroy()
resolve(false)
})
socket.once("error", () => resolve(false))
})
// Every invocation carries ZOOID_CONFIG, `down` included: the compose file names it without a
// default, so a call that left it out would fail to interpolate rather than quietly mounting
// something else.
@ -177,6 +204,20 @@ export class Zooid {
)
}
// Nothing should answer on the relay port yet: this run has not brought its container up. If
// something does, it is a leftover container from an aborted run or another copy of the suite —
// and because the harness dials this fixed port, sharing it silently corrupts both runs. Refuse
// loudly instead. `compose down` clears a leftover of this project's own.
if (await isRelayPortTaken()) {
throw new Error(
`127.0.0.1:${relayHostPort} is already in use, but the zooid relay container publishes ` +
"exactly that port and the harness talks to whatever answers there. This is a leftover " +
"container from an interrupted run, or another copy of this suite running on the machine " +
`(under any user). Free it before running — \`${dockerCommand} compose -f ` +
"e2e/harness/zooid/docker/compose.yaml down\` clears one this project started.",
)
}
this.started = true
}

View file

@ -38,6 +38,9 @@ const modalHash = writable(typeof location === "undefined" ? "" : location.hash)
if (typeof window !== "undefined") {
window.addEventListener("popstate", () => modalHash.set(location.hash))
// Close modals on navigate
page.subscribe($page => modalHash.set($page.url?.hash ?? ""))
}
const setModalHash = (hash: string, replace: boolean) => {