feat: F5.3a the simulation rooms on server F

One room per project for everyone in it, held by a few worker_threads,
each room with a replica of the document that Hocuspocus' direct
connection keeps open: changes go in, what the room writes comes back.
/sim?project=... signs in as /sync does and says refusals and partings
as the socket's closing; only a write grant changes the room. The state
names who started the simulation. After the last person the room stays
60 s, then stops, writes back and ends. Freezing stops a running
simulation and refuses to start one, deleting ends the room without
writing back, revoking lets the person go from the room too; a failed
thread ends its rooms, stopping the service stops them all.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-04 14:29:11 +00:00
co-authored by Claude Opus 5.5
parent 5ded84cb33
commit 8cfc518f0c
17 changed files with 1033 additions and 43 deletions
+1 -1
View File
@@ -280,7 +280,7 @@ describe('server F keeps the shared documents', () => {
it('answers /healthz, refuses other ways in, and logs JSON', async () => {
const server = await start();
const health = await fetch(`http://127.0.0.1:${server.port}/healthz`);
expect([health.status, await health.json()]).toEqual([200, { status: 'ok', documents: 0, connections: 0 }]);
expect([health.status, await health.json()]).toEqual([200, { status: 'ok', documents: 0, connections: 0, rooms: 0 }]);
expect((await fetch(`http://127.0.0.1:${server.port}/nothing`)).status).toBe(404);
const refused = await new Promise<number>((resolve) => {
const socket = new WebSocket(`ws://127.0.0.1:${server.port}/elsewhere`);