1// The TAB_HUB Durable Object namespace for tests: one hub per name (a 2// user's id, as the Worker names them), each holding the tabs a test opens 3// in it. A tab is a function answering commands as the page would 4// (website/src/jev/myTabs.mjs); what each hub was asked is recorded, so a 5// test can show a command reached only the hub it should. 6import type { TabCommand, TabReply } from '../src/tab-hub-core'; 7 8type Tab = (command: TabCommand) => TabReply | Promise<TabReply>; 9 10export function memoryTabHubs() { 11 const hubs = new Map<string, Map<string, Tab>>(); 12 const asked: { hub: string; session: string; command: TabCommand }[] = []; 13 const hub = (name: string) => { 14 let tabs = hubs.get(name); 15 if (!tabs) hubs.set(name, (tabs = new Map())); 16 return tabs; 17 }; 18 return { 19 asked, 20 // a tab open in `user`'s hub 21 open(user: string, session: string, tab: Tab) { 22 hub(user).set(session, tab); 23 }, 24 close(user: string, session: string) { 25 hub(user).delete(session); 26 }, 27 idFromName: (name: string) => ({ name }), 28 get: ({ name }: { name: string }) => ({ 29 sessions: async () => [...hub(name).keys()], 30 command: async (session: string, command: TabCommand) => { 31 const tab = hub(name).get(session); 32 if (!tab) throw new Error(`you have no open tab ${session}`); 33 asked.push({ hub: name, session, command }); 34 return tab(command); 35 }, 36 fetch: async (request: Request) => { 37 const session = new URL(request.url).searchParams.get('session_id') ?? ''; 38 return Response.json({ joined: name, session }); 39 }, 40 }), 41 }; 42}