Which listening parties are live now, for the data tab (data.ts): the rules of the site's one PartyDirectory Durable Object (party.ts), as plain code the tests run in Node.
A party room cannot be listed (each is its own object, named by a room id
only its people know), so every room tells the directory when its people
or its song change (party-room.ts's changed), and the directory keeps
one entry per live room: its song, how many are in it, whether the host
is, and since when. Keyed by the room's Durable Object id, a hash of the
room's name that cannot be turned back into it, so the directory never
holds the join link: a party is counted and described. A room whose host
chose to be seen (the lobby's "show me
here", website/src/jev/party.mjs) is listed: its entry then carries its
object id, which /jev/parties/<id> (party.ts) turns into the room to join
by asking the room itself, which answers only while it is listed. An
unlisted party is counted and described, never joinable from here.
An empty room removes its entry; one that stops reporting (a
room lost in a crash) is dropped once its last report is STALE_MS old.
Live coordination only, like the rooms: nothing here outlives a party.
The part of a Durable Object's storage the directory uses.
A room's report: its entry kept or updated, or removed when empty.
37export async function note(storage: Storage, room: string, live: Live | null, now: number): Promise<void> { 38 if (!KEY.test(room)) return; 39 const key = `p:${room}`; 40 if (!live || live.people < 1) { 41 await storage.delete(key); 42 return; 43 } 44 const before = await storage.get<Entry>(key); 45 const entry: Entry = { 46 people: Math.min(Math.max(Math.floor(live.people), 0), 1000), 47 host: Boolean(live.host), 48 song: typeof live.song === 'string' ? live.song.slice(0, 129) : null, 49 listed: live.listed === true, 50 since: before?.since ?? now, 51 seen: now, 52 }; 53 await storage.put(key, entry); 54}
Every live party, the longest-running first; stale entries are removed.
57export type Listed = { people: number; host: boolean; song: string | null; since: number; listed: boolean; id?: string }; 58export async function live(storage: Storage, now: number): Promise<Listed[]> { 59 const entries = await storage.list<Entry>({ prefix: 'p:', limit: MAX_ENTRIES }); 60 const out: Listed[] = []; 61 for (const [key, e] of entries) { 62 if (now - e.seen > STALE_MS) { 63 await storage.delete(key); 64 continue; 65 } 66 const listed = e.listed === true; 67 out.push({ people: e.people, host: e.host, song: e.song, since: e.since, listed, ...(listed ? { id: key.slice(2) } : {}) }); 68 } 69 return out.sort((a, b) => a.since - b.since); 70}