onelobby 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/blueprint.schema.json +8 -1
- package/dist/blueprint.d.ts +4 -0
- package/dist/blueprint.js +5 -0
- package/dist/blueprint.js.map +1 -1
- package/dist/codec.d.ts +23 -0
- package/dist/codec.js +28 -0
- package/dist/codec.js.map +1 -1
- package/dist/direct/browser-host.d.ts +46 -0
- package/dist/direct/browser-host.js +93 -0
- package/dist/direct/browser-host.js.map +1 -0
- package/dist/direct/hub-host.d.ts +34 -23
- package/dist/direct/hub-host.js +75 -59
- package/dist/direct/hub-host.js.map +1 -1
- package/dist/direct/hub-local.d.ts +22 -0
- package/dist/direct/hub-local.js +46 -0
- package/dist/direct/hub-local.js.map +1 -0
- package/dist/direct/hub.d.ts +37 -10
- package/dist/direct/hub.js +114 -23
- package/dist/direct/hub.js.map +1 -1
- package/dist/direct/index.d.ts +36 -3
- package/dist/direct/index.js +146 -20
- package/dist/direct/index.js.map +1 -1
- package/dist/direct/metrics.d.ts +12 -0
- package/dist/direct/metrics.js +45 -0
- package/dist/direct/metrics.js.map +1 -0
- package/dist/direct/portmap.d.ts +9 -2
- package/dist/direct/portmap.js +7 -2
- package/dist/direct/portmap.js.map +1 -1
- package/dist/direct/sha1.d.ts +16 -0
- package/dist/direct/sha1.js +111 -0
- package/dist/direct/sha1.js.map +1 -0
- package/dist/direct-address.d.ts +2 -2
- package/dist/direct-address.js.map +1 -1
- package/dist/friendly.d.ts +23 -0
- package/dist/friendly.js +28 -0
- package/dist/friendly.js.map +1 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/internals.d.ts +9 -0
- package/dist/internals.js +9 -0
- package/dist/internals.js.map +1 -1
- package/dist/join-code-kind.d.ts +10 -0
- package/dist/join-code-kind.js +10 -0
- package/dist/join-code-kind.js.map +1 -0
- package/dist/joincode.d.ts +10 -1
- package/dist/joincode.js +23 -3
- package/dist/joincode.js.map +1 -1
- package/dist/lobby.d.ts +65 -4
- package/dist/lobby.js +151 -12
- package/dist/lobby.js.map +1 -1
- package/dist/lockstep.d.ts +8 -0
- package/dist/lockstep.js +23 -2
- package/dist/lockstep.js.map +1 -1
- package/dist/nostr/crypto.d.ts +20 -0
- package/dist/nostr/crypto.js +78 -0
- package/dist/nostr/crypto.js.map +1 -0
- package/dist/nostr/host.d.ts +66 -0
- package/dist/nostr/host.js +196 -0
- package/dist/nostr/host.js.map +1 -0
- package/dist/nostr/limits.d.ts +31 -0
- package/dist/nostr/limits.js +36 -0
- package/dist/nostr/limits.js.map +1 -0
- package/dist/nostr/link.d.ts +70 -0
- package/dist/nostr/link.js +180 -0
- package/dist/nostr/link.js.map +1 -0
- package/dist/nostr/relays.d.ts +79 -0
- package/dist/nostr/relays.js +315 -0
- package/dist/nostr/relays.js.map +1 -0
- package/dist/nostr/room.d.ts +43 -0
- package/dist/nostr/room.js +104 -0
- package/dist/nostr/room.js.map +1 -0
- package/dist/nostr/secp256k1.d.ts +10 -0
- package/dist/nostr/secp256k1.js +171 -0
- package/dist/nostr/secp256k1.js.map +1 -0
- package/dist/nostr/socket.d.ts +48 -0
- package/dist/nostr/socket.js +141 -0
- package/dist/nostr/socket.js.map +1 -0
- package/dist/onelobby.bundle.js +146 -0
- package/dist/onelobby.bundle.js.map +7 -0
- package/dist/onelobby.bundle.mjs +11 -5
- package/dist/onelobby.bundle.mjs.map +4 -4
- package/dist/onelobby.d.ts +31 -3
- package/dist/onelobby.global.js +2599 -46
- package/dist/onelobby.global.js.map +4 -4
- package/dist/onelobby.global.min.js +11 -5
- package/dist/onelobby.global.min.js.map +4 -4
- package/dist/onelobby.js +68 -14
- package/dist/onelobby.js.map +1 -1
- package/dist/pause.d.ts +85 -0
- package/dist/pause.js +160 -0
- package/dist/pause.js.map +1 -0
- package/dist/transport.d.ts +3 -1
- package/dist/transport.js.map +1 -1
- package/dist/webrtc.d.ts +1 -0
- package/dist/webrtc.js +6 -1
- package/dist/webrtc.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/clock.ts", "../src/emitter.ts", "../src/crockford.ts", "../src/codec.ts", "../src/chat.ts", "../src/chunking.ts", "../src/deflate.ts", "../src/lockstep.ts", "../src/migration.ts", "../src/signaling.ts", "../src/pause.ts", "../src/voice-core.ts", "../src/voice.ts", "../src/socket.ts", "../src/lobby.ts", "../src/transport.ts", "../src/voice-browser.ts", "../src/webrtc.ts", "../src/joincode.ts", "../src/nostr/link.ts", "../src/nostr/crypto.ts", "../src/nostr/secp256k1.ts", "../src/nostr/relays.ts", "../src/nostr/room.ts", "../src/nostr/socket.ts", "../src/direct-address.ts", "../src/blueprint.ts", "../src/nostr/host.ts", "../src/direct/metrics.ts", "../src/direct/sha1.ts", "../src/direct/hub.ts", "../src/direct/hub-local.ts", "../src/direct/browser-host.ts", "../src/onelobby.ts", "../src/friendly.ts", "../src/deprecated.ts", "../src/ui/controller.ts", "../src/ui/styles.ts", "../src/ui/server-url.ts", "../src/ui/element.ts", "../src/ui/index.ts"],
|
|
4
|
+
"sourcesContent": ["/** Time source. Lockstep timing never uses requestAnimationFrame (DESIGN s.4). */\nexport interface Clock {\n now(): number;\n /** Repeating timer; returns a cancel function. */\n setInterval(fn: () => void, ms: number): () => void;\n setTimeout(fn: () => void, ms: number): () => void;\n}\n\n/**\n * Interval driven from a dedicated Worker when available (hidden tabs clamp main-thread timers to 1 Hz),\n * main-thread setInterval otherwise.\n */\nfunction workerInterval(fn: () => void, ms: number): (() => void) | null {\n if (typeof Worker === \"undefined\" || typeof Blob === \"undefined\" || typeof URL === \"undefined\" || !URL.createObjectURL) {\n return null;\n }\n try {\n const src = `let h=0;onmessage=e=>{if(e.data&&e.data.start){h=setInterval(()=>postMessage(0),e.data.ms)}else{clearInterval(h);close()}}`;\n const url = URL.createObjectURL(new Blob([src], { type: \"text/javascript\" }));\n const w = new Worker(url);\n URL.revokeObjectURL(url);\n w.onmessage = () => fn();\n w.postMessage({ start: true, ms });\n return () => {\n w.postMessage({ stop: true });\n w.terminate();\n };\n } catch {\n return null;\n }\n}\n\nexport const systemClock: Clock = {\n now: () => (typeof performance !== \"undefined\" ? performance.now() : Date.now()),\n setInterval(fn, ms) {\n const w = workerInterval(fn, ms);\n if (w) return w;\n const h = setInterval(fn, ms);\n return () => clearInterval(h);\n },\n setTimeout(fn, ms) {\n const h = setTimeout(fn, ms);\n return () => clearTimeout(h);\n },\n};\n", "/** Minimal typed event emitter (zero deps). */\nexport class Emitter<E extends { [k: string]: unknown[] }> {\n private handlers = new Map<keyof E, Array<(...a: any[]) => void>>();\n\n on<K extends keyof E>(event: K, fn: (...args: E[K]) => void): () => void {\n let list = this.handlers.get(event);\n if (!list) this.handlers.set(event, (list = []));\n list.push(fn as (...a: any[]) => void);\n return () => this.off(event, fn);\n }\n\n off<K extends keyof E>(event: K, fn: (...args: E[K]) => void): void {\n const list = this.handlers.get(event);\n if (!list) return;\n const i = list.indexOf(fn as (...a: any[]) => void);\n if (i >= 0) list.splice(i, 1);\n }\n\n protected emit<K extends keyof E>(event: K, ...args: E[K]): void {\n const list = this.handlers.get(event);\n if (!list) return;\n for (const fn of [...list]) {\n try {\n fn(...args);\n } catch (e) {\n // A throwing listener must never break the protocol state machine.\n queueMicrotask(() => {\n throw e;\n });\n }\n }\n }\n}\n", "/** Crockford Base32 (DESIGN s.6): digits + 22 letters, no I L O U. */\nexport const CROCKFORD_ALPHABET = \"0123456789ABCDEFGHJKMNPQRSTVWXYZ\";\nexport const CODE_LENGTH = 6;\n\n/** ASCII-only uppercasing: `\u00DF` must not fold to `SS`. */\nfunction asciiUpper(s: string): string {\n return s.replace(/[a-z]/g, (c) => c.toUpperCase());\n}\n\n/**\n * Decode user input to the canonical code form (upper case, no separators, `i`/`l` -> `1`, `o` -> `0`).\n * Returns null when the input is not a valid 6-character code.\n */\nexport function normalizeCode(input: string): string | null {\n const s = asciiUpper(input)\n .replace(/[ \\-]/g, \"\")\n .replace(/[IL]/g, \"1\")\n .replace(/O/g, \"0\");\n if (s.length !== CODE_LENGTH) return null;\n for (const ch of s) if (CROCKFORD_ALPHABET.indexOf(ch) < 0) return null;\n return s;\n}\n\n/** Canonical code -> `7HQ 4K2` display form. */\nexport function formatCode(code: string): string {\n const c = normalizeCode(code) ?? code.toUpperCase();\n return c.length === CODE_LENGTH ? `${c.slice(0, 3)} ${c.slice(3)}` : c;\n}\n\n/** Numeric value of a Crockford string (same alias rules). Throws on invalid characters. */\nexport function decodeCrockford(input: string): bigint {\n const s = asciiUpper(input).replace(/[ \\-]/g, \"\").replace(/[IL]/g, \"1\").replace(/O/g, \"0\");\n let v = 0n;\n for (const ch of s) {\n const i = CROCKFORD_ALPHABET.indexOf(ch);\n if (i < 0) throw new Error(`invalid Crockford character '${ch}'`);\n v = v * 32n + BigInt(i);\n }\n return v;\n}\n\n/** Inverse of decodeCrockford, left-padded to `length` if given. */\nexport function encodeCrockford(value: bigint, length = 0): string {\n let s = \"\";\n let v = value;\n if (v < 0n) throw new Error(\"negative value\");\n while (v > 0n) {\n s = CROCKFORD_ALPHABET[Number(v % 32n)] + s;\n v /= 32n;\n }\n if (s === \"\") s = \"0\";\n return s.padStart(length, \"0\");\n}\n", "/**\n * Binary codec for the peer data channels (DESIGN s.8). Little-endian, one message per frame,\n * 3-byte header `type:u8, route:u16`. Pure functions; no I/O.\n */\n\nexport const MAX_FRAME_BYTES = 16 * 1024;\n/** Cap on one reassembled chunk set (spec/binary.md \"Snapshot and chunks\"). */\nexport const MAX_REASSEMBLED_BYTES = 64 * 1024 * 1024;\nexport const HEADER_BYTES = 3;\nexport const PROTO = 1;\n\nexport const MsgType = {\n hi: 0x01,\n welcome_dc: 0x02,\n cmd: 0x10,\n frame: 0x11,\n hash: 0x12,\n desync: 0x13,\n snapshot_req: 0x14,\n snapshot: 0x15,\n chunk: 0x16,\n raw: 0x20,\n ping: 0x30,\n pong: 0x31,\n host_claim: 0x40,\n voice: 0x50,\n chat: 0x60,\n chat_history: 0x61,\n chat_reject: 0x62,\n pause_request: 0x70,\n pause_state: 0x71,\n pause_refuse: 0x72,\n} as const;\n\n/** welcome_dc.mode: 1 = lockstep, 2 = raw (0 reserved) per spec/binary.md. */\nexport const MODE_LOCKSTEP = 1;\nexport const MODE_RAW = 2;\n\nexport type CodecErrorCode = \"too_short\" | \"trailing\" | \"too_long\" | \"bad_utf8\" | \"bad_value\";\nexport class CodecError extends Error {\n constructor(public code: CodecErrorCode, detail = \"\") {\n super(detail ? `${code}: ${detail}` : code);\n this.name = \"CodecError\";\n }\n}\n\nexport interface FrameCommand {\n peerId: number;\n bytes: Uint8Array;\n}\nexport interface ChatLine {\n peerId: number;\n id: number;\n ts: number;\n text: string;\n}\n\nexport type Message =\n | { type: \"hi\"; proto: number; peerId: number; lastTick: number }\n | { type: \"welcome_dc\"; tick: number; tickRate: number; mode: number; hostId: number }\n | { type: \"cmd\"; seq: number; bytes: Uint8Array }\n | { type: \"frame\"; tick: number; commands: FrameCommand[] }\n | { type: \"hash\"; tick: number; hash: bigint }\n | { type: \"desync\"; tick: number; hostHash: bigint; yourHash: bigint }\n | { type: \"snapshot_req\"; sinceTick: number }\n | { type: \"snapshot\"; tick: number; total: number; flags: number }\n | { type: \"chunk\"; id: number; index: number; count: number; bytes: Uint8Array }\n | { type: \"raw\"; bytes: Uint8Array }\n | { type: \"ping\"; n: number; t: number }\n | { type: \"pong\"; n: number; t: number }\n | { type: \"host_claim\"; epoch: number; hostId: number; tick: number }\n | { type: \"voice\"; seq: number; flags: number; frames: Uint8Array[] }\n | { type: \"chat\"; id: number; ts: number; text: string }\n | { type: \"chat_history\"; lines: ChatLine[] }\n | { type: \"chat_reject\"; id: number; reason: number }\n | { type: \"pause_request\"; paused: number }\n | { type: \"pause_state\"; paused: number; byPeer: number; flags: number }\n | { type: \"pause_refuse\"; paused: number; reason: number };\n\nexport interface Decoded {\n route: number;\n msg: Message;\n}\n\nconst enc = new TextEncoder();\nconst dec = new TextDecoder(\"utf-8\", { fatal: true });\nfunction decodeText(b: Uint8Array): string {\n try {\n return dec.decode(b);\n } catch {\n throw new CodecError(\"bad_utf8\");\n }\n}\n\nclass Writer {\n buf: Uint8Array;\n dv: DataView;\n pos = 0;\n constructor(size: number) {\n this.buf = new Uint8Array(size);\n this.dv = new DataView(this.buf.buffer);\n }\n ensure(n: number) {\n if (this.pos + n <= this.buf.length) return;\n const nb = new Uint8Array(Math.max(this.buf.length * 2, this.pos + n));\n nb.set(this.buf);\n this.buf = nb;\n this.dv = new DataView(nb.buffer);\n }\n u8(v: number) { this.ensure(1); this.dv.setUint8(this.pos, v); this.pos += 1; }\n u16(v: number) { this.ensure(2); this.dv.setUint16(this.pos, v, true); this.pos += 2; }\n u32(v: number) { this.ensure(4); this.dv.setUint32(this.pos, v >>> 0, true); this.pos += 4; }\n i64(v: number) { this.ensure(8); this.dv.setBigInt64(this.pos, BigInt(v), true); this.pos += 8; }\n u64(v: bigint) { this.ensure(8); this.dv.setBigUint64(this.pos, BigInt.asUintN(64, v), true); this.pos += 8; }\n bytes(b: Uint8Array) { this.ensure(b.length); this.buf.set(b, this.pos); this.pos += b.length; }\n done(): Uint8Array { return this.buf.slice(0, this.pos); }\n}\n\nclass Reader {\n dv: DataView;\n pos = 0;\n constructor(public buf: Uint8Array) {\n this.dv = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);\n }\n need(n: number) { if (this.pos + n > this.buf.length) throw new CodecError(\"too_short\", \"truncated frame\"); }\n u8() { this.need(1); const v = this.dv.getUint8(this.pos); this.pos += 1; return v; }\n u16() { this.need(2); const v = this.dv.getUint16(this.pos, true); this.pos += 2; return v; }\n u32() { this.need(4); const v = this.dv.getUint32(this.pos, true); this.pos += 4; return v; }\n i64() { this.need(8); const v = this.dv.getBigInt64(this.pos, true); this.pos += 8; return Number(v); }\n u64() { this.need(8); const v = this.dv.getBigUint64(this.pos, true); this.pos += 8; return v; }\n bytes(n: number) { this.need(n); const v = this.buf.slice(this.pos, this.pos + n); this.pos += n; return v; }\n rest() { const v = this.buf.slice(this.pos); this.pos = this.buf.length; return v; }\n}\n\nexport function encodeMessage(msg: Message, route = 0, allowOversize = false): Uint8Array {\n const w = new Writer(64);\n w.u8(MsgType[msg.type]);\n w.u16(route);\n switch (msg.type) {\n case \"hi\": w.u8(msg.proto); w.u16(msg.peerId); w.i64(msg.lastTick); break;\n case \"welcome_dc\": w.i64(msg.tick); w.u8(msg.tickRate); w.u8(msg.mode); w.u16(msg.hostId); break;\n case \"cmd\": w.u32(msg.seq); w.u16(msg.bytes.length); w.bytes(msg.bytes); break;\n case \"frame\":\n w.i64(msg.tick); w.u16(msg.commands.length);\n for (const c of msg.commands) { w.u16(c.peerId); w.u16(c.bytes.length); w.bytes(c.bytes); }\n break;\n case \"hash\": w.i64(msg.tick); w.u64(msg.hash); break;\n case \"desync\": w.i64(msg.tick); w.u64(msg.hostHash); w.u64(msg.yourHash); break;\n case \"snapshot_req\": w.i64(msg.sinceTick); break;\n case \"snapshot\": w.i64(msg.tick); w.u32(msg.total); w.u8(msg.flags); break;\n case \"chunk\": w.u32(msg.id); w.u16(msg.index); w.u16(msg.count); w.bytes(msg.bytes); break;\n case \"raw\": w.bytes(msg.bytes); break;\n case \"ping\": case \"pong\": w.u32(msg.n); w.u32(msg.t); break;\n case \"host_claim\": w.u16(msg.epoch); w.u16(msg.hostId); w.i64(msg.tick); break;\n case \"voice\":\n w.u16(msg.seq); w.u8(msg.flags); w.u8(msg.frames.length);\n for (const f of msg.frames) { w.u16(f.length); w.bytes(f); }\n break;\n case \"chat\": { const t = enc.encode(msg.text); w.u32(msg.id); w.i64(msg.ts); w.u16(t.length); w.bytes(t); break; }\n case \"chat_history\":\n w.u16(msg.lines.length);\n for (const l of msg.lines) {\n const t = enc.encode(l.text);\n w.u16(l.peerId); w.u32(l.id); w.i64(l.ts); w.u16(t.length); w.bytes(t);\n }\n break;\n case \"chat_reject\": w.u32(msg.id); w.u8(msg.reason); break;\n case \"pause_request\": w.u8(msg.paused); break;\n case \"pause_state\": w.u8(msg.paused); w.u16(msg.byPeer); w.u8(msg.flags); break;\n case \"pause_refuse\": w.u8(msg.paused); w.u8(msg.reason); break;\n }\n const out = w.done();\n if (!allowOversize && out.length > MAX_FRAME_BYTES) throw new CodecError(\"too_long\", `${out.length} > ${MAX_FRAME_BYTES}`);\n return out;\n}\n\nconst TYPE_BY_ID = new Map<number, keyof typeof MsgType>(\n (Object.keys(MsgType) as Array<keyof typeof MsgType>).map((k) => [MsgType[k], k]),\n);\n\n/**\n * Decode one frame. Returns null for unknown types (ignored for forward compatibility); throws on a truncated body.\n * A frame reassembled from chunks passes `reassembled` so the cap is MAX_REASSEMBLED_BYTES instead of one frame's.\n */\nexport function decodeMessage(buf: Uint8Array, reassembled = false): Decoded | null {\n const max = reassembled ? MAX_REASSEMBLED_BYTES : MAX_FRAME_BYTES;\n if (buf.length > max) throw new CodecError(\"too_long\", `${buf.length} > ${max}`);\n if (buf.length < HEADER_BYTES) throw new CodecError(\"too_short\", \"header\");\n const r = new Reader(buf);\n const type = TYPE_BY_ID.get(r.u8());\n const route = r.u16();\n if (!type) return null;\n let msg: Message;\n switch (type) {\n case \"hi\": msg = { type, proto: r.u8(), peerId: r.u16(), lastTick: r.i64() }; break;\n case \"welcome_dc\": msg = { type, tick: r.i64(), tickRate: r.u8(), mode: r.u8(), hostId: r.u16() }; break;\n case \"cmd\": { const seq = r.u32(); msg = { type, seq, bytes: r.bytes(r.u16()) }; break; }\n case \"frame\": {\n const tick = r.i64(); const n = r.u16(); const commands: FrameCommand[] = [];\n for (let i = 0; i < n; i++) { const peerId = r.u16(); commands.push({ peerId, bytes: r.bytes(r.u16()) }); }\n msg = { type, tick, commands }; break;\n }\n case \"hash\": msg = { type, tick: r.i64(), hash: r.u64() }; break;\n case \"desync\": msg = { type, tick: r.i64(), hostHash: r.u64(), yourHash: r.u64() }; break;\n case \"snapshot_req\": msg = { type, sinceTick: r.i64() }; break;\n case \"snapshot\": msg = { type, tick: r.i64(), total: r.u32(), flags: r.u8() }; break;\n case \"chunk\": msg = { type, id: r.u32(), index: r.u16(), count: r.u16(), bytes: r.rest() }; break;\n case \"raw\": msg = { type, bytes: r.rest() }; break;\n case \"ping\": case \"pong\": msg = { type, n: r.u32(), t: r.u32() }; break;\n case \"host_claim\": msg = { type, epoch: r.u16(), hostId: r.u16(), tick: r.i64() }; break;\n case \"voice\": {\n const seq = r.u16(); const flags = r.u8(); const n = r.u8(); const frames: Uint8Array[] = [];\n for (let i = 0; i < n; i++) frames.push(r.bytes(r.u16()));\n msg = { type, seq, flags, frames }; break;\n }\n case \"chat\": { const id = r.u32(); const ts = r.i64(); msg = { type, id, ts, text: decodeText(r.bytes(r.u16())) }; break; }\n case \"chat_history\": {\n const n = r.u16(); const lines: ChatLine[] = [];\n for (let i = 0; i < n; i++) {\n const peerId = r.u16(); const id = r.u32(); const ts = r.i64();\n lines.push({ peerId, id, ts, text: decodeText(r.bytes(r.u16())) });\n }\n msg = { type, lines }; break;\n }\n case \"chat_reject\": msg = { type, id: r.u32(), reason: r.u8() }; break;\n case \"pause_request\": msg = { type, paused: r.u8() }; break;\n case \"pause_state\": msg = { type, paused: r.u8(), byPeer: r.u16(), flags: r.u8() }; break;\n case \"pause_refuse\": msg = { type, paused: r.u8(), reason: r.u8() }; break;\n }\n if (r.pos !== buf.length) throw new CodecError(\"trailing\", `${buf.length - r.pos} extra bytes`);\n return { route, msg };\n}\n\n/** Rewrite the route field on a copy (the host does this when relaying). */\nexport function withRoute(buf: Uint8Array, route: number): Uint8Array {\n const out = buf.slice();\n out[1] = route & 0xff;\n out[2] = (route >> 8) & 0xff;\n return out;\n}\n\nexport const CHAT_REJECT = { rate: 1, length: 2, mutedByHost: 3 } as const;\nexport type ChatRejectReason = \"rate\" | \"length\" | \"muted_by_host\" | \"unknown\";\nexport function chatRejectName(code: number): ChatRejectReason {\n return code === 1 ? \"rate\" : code === 2 ? \"length\" : code === 3 ? \"muted_by_host\" : \"unknown\";\n}\n\n/** `pause_state.flags` bit 0: the pauser left and the host holds the pause. */\nexport const PAUSE_FLAG_PAUSER_LEFT = 1;\n/** `pause_refuse.reason`: 1 not_in_game, 2 rate_limited, 3 not_allowed. */\nexport const PAUSE_REFUSE = { notInGame: 1, rateLimited: 2, notAllowed: 3 } as const;\n", "import { CHAT_REJECT, chatRejectName, type ChatLine, type ChatRejectReason, type Message } from \"./codec.js\";\nimport { Emitter } from \"./emitter.js\";\nimport type { Hub } from \"./hub.js\";\n\nexport const CHAT_MAX_BYTES = 256;\nexport const CHAT_BURST = 3;\nexport const CHAT_WINDOW_MS = 5000;\nexport const CHAT_PER_WINDOW = 5;\nexport const CHAT_HISTORY_LINES = 100;\n\n/** Control characters (C0, DEL, C1) and bidi overrides/isolates/marks are removed at the host. */\n// eslint-disable-next-line no-control-regex\nconst STRIP = /[\\u0000-\\u001f\\u007f-\\u009f\\u202a-\\u202e\\u2066-\\u2069]/g;\n\nexport function sanitizeChat(text: string): string {\n return text.replace(STRIP, \"\");\n}\n\nexport function utf8Length(text: string): number {\n return new TextEncoder().encode(text).length;\n}\n\n/**\n * Token bucket per peer: `CHAT_PER_WINDOW` messages per `CHAT_WINDOW_MS`, burst `CHAT_BURST`\n * (capacity 3, refilled one token per second).\n */\nexport class ChatLimiter {\n private buckets = new Map<number, { tokens: number; at: number }>();\n private refillMs = CHAT_WINDOW_MS / CHAT_PER_WINDOW;\n\n tryTake(peerId: number, now: number): boolean {\n let b = this.buckets.get(peerId);\n if (!b) this.buckets.set(peerId, (b = { tokens: CHAT_BURST, at: now }));\n b.tokens = Math.min(CHAT_BURST, b.tokens + (now - b.at) / this.refillMs);\n b.at = now;\n if (b.tokens < 1) return false;\n b.tokens -= 1;\n return true;\n }\n\n forget(peerId: number): void {\n this.buckets.delete(peerId);\n }\n}\n\nexport interface ChatMessage {\n peerId: number;\n id: number;\n ts: number;\n text: string;\n}\n\nexport type ChatEvents = {\n message: [ChatMessage];\n rejected: [id: number, reason: ChatRejectReason];\n /** The host's `chat_history` arrived (joiners, right after the handshake). */\n history: [readonly ChatMessage[]];\n};\n\n/** Text chat (DESIGN s.9b): sender -> host (validates, stamps) -> everyone, over the reliable channel. */\nexport class Chat extends Emitter<ChatEvents> {\n /** Game-provided profanity hook; applied to every line a player is shown. */\n filter: (text: string) => string = (t) => t;\n\n private lines: ChatMessage[] = [];\n private limiter = new ChatLimiter();\n private hostMuted = new Set<number>();\n private localId = 0;\n private stampId = 0;\n\n constructor(\n private hub: Hub,\n private wall: () => number,\n private isLocallyMuted: (peerId: number) => boolean,\n ) {\n super();\n }\n\n get history(): readonly ChatMessage[] {\n return this.lines;\n }\n\n /** Returns the local message id (what `rejected` will refer to). */\n send(text: string): number {\n const id = ++this.localId >>> 0;\n if (utf8Length(text) > CHAT_MAX_BYTES * 4) {\n // Do not even ship absurd payloads; the host would reject on length anyway.\n queueMicrotask(() => this.emit(\"rejected\", id, \"length\"));\n return id;\n }\n if (this.hub.isHost) this.hostProcess(this.hub.localPeerId, id, text);\n else this.hub.sendToHost({ type: \"chat\", id, ts: 0, text });\n return id;\n }\n\n /** Host only: drop a peer's chat for everyone (`chat_reject` reason 3). */\n hostMute(peerId: number, muted: boolean): void {\n if (!this.hub.isHost) throw new Error(\"only the host can mute chat\");\n if (muted) this.hostMuted.add(peerId);\n else this.hostMuted.delete(peerId);\n }\n\n isHostMuted(peerId: number): boolean {\n return this.hostMuted.has(peerId);\n }\n\n onPeerLeft(peerId: number): void {\n this.limiter.forget(peerId);\n this.hostMuted.delete(peerId);\n }\n\n /** Host: send recent lines to a freshly connected peer. */\n sendHistory(peerId: number): void {\n const lines: ChatLine[] = this.lines.slice(-CHAT_HISTORY_LINES).map((l) => ({ ...l }));\n this.hub.sendTo(peerId, { type: \"chat_history\", lines }, 0);\n }\n\n handle(from: number, route: number, msg: Message): void {\n switch (msg.type) {\n case \"chat\":\n if (this.hub.isHost) this.hostProcess(from, msg.id, msg.text);\n else this.deliver({ peerId: route, id: msg.id, ts: msg.ts, text: msg.text });\n break;\n case \"chat_history\":\n if (!this.hub.isHost) {\n this.lines = [];\n for (const l of msg.lines.slice(-CHAT_HISTORY_LINES)) this.push({ ...l, text: this.safeFilter(l.text) });\n this.emit(\"history\", this.lines);\n }\n break;\n case \"chat_reject\":\n this.emit(\"rejected\", msg.id, chatRejectName(msg.reason));\n break;\n }\n }\n\n // ---- host path ----------------------------------------------------------------------------------------------\n\n private hostProcess(from: number, id: number, rawText: string): void {\n const reject = (code: number) => {\n if (from === this.hub.localPeerId) this.emit(\"rejected\", id, chatRejectName(code));\n else this.hub.sendTo(from, { type: \"chat_reject\", id, reason: code }, 0);\n };\n if (this.hostMuted.has(from)) return reject(CHAT_REJECT.mutedByHost);\n // spec order: muted, length (before stripping), rate (a reject spends no token), strip, drop if empty\n if (utf8Length(rawText) > CHAT_MAX_BYTES) return reject(CHAT_REJECT.length);\n if (!this.limiter.tryTake(from, this.hub.clock.now())) return reject(CHAT_REJECT.rate);\n const text = sanitizeChat(rawText);\n if (text.length === 0) return;\n const line: ChatMessage = { peerId: from, id: ++this.stampId >>> 0, ts: this.wall(), text };\n const out: Message = { type: \"chat\", id: line.id, ts: line.ts, text: line.text };\n for (const p of this.hub.connectedPeers()) this.hub.sendTo(p, out, from);\n this.deliver(line);\n }\n\n private deliver(line: ChatMessage): void {\n const shown: ChatMessage = { ...line, text: this.safeFilter(line.text) };\n this.push(shown);\n if (this.isLocallyMuted(line.peerId)) return; // local mute drops the peer's chat, history included\n this.emit(\"message\", shown);\n }\n\n private push(line: ChatMessage): void {\n if (this.isLocallyMuted(line.peerId)) return;\n this.lines.push(line);\n if (this.lines.length > CHAT_HISTORY_LINES) this.lines.splice(0, this.lines.length - CHAT_HISTORY_LINES);\n }\n\n private safeFilter(text: string): string {\n try {\n return this.filter(text);\n } catch {\n return text;\n }\n }\n}\n", "import { CodecError, MAX_REASSEMBLED_BYTES, type Message } from \"./codec.js\";\n\n/** Chunk body capacity: 16 KiB frame - 3 header - 8 chunk header, rounded down for headroom. */\nexport const CHUNK_PAYLOAD = 16000;\n\nexport function splitChunks(id: number, bytes: Uint8Array, size = CHUNK_PAYLOAD): Message[] {\n const count = Math.max(1, Math.ceil(bytes.length / size));\n if (count > 0xffff) throw new RangeError(\"payload needs more than 65535 chunks\");\n const out: Message[] = [];\n for (let i = 0; i < count; i++) {\n out.push({\n type: \"chunk\",\n id,\n index: i,\n count,\n bytes: bytes.subarray(i * size, Math.min(bytes.length, (i + 1) * size)),\n });\n }\n return out;\n}\n\ninterface Partial {\n count: number;\n got: number;\n parts: (Uint8Array | undefined)[];\n bytes: number;\n}\n\n/** Most incomplete chunk sets one reassembler (one peer) holds at once; a new id past this evicts the oldest. */\nexport const MAX_PARTIAL_SETS = 8;\n\n/** Reassembles chunked payloads by id. Duplicates are ignored; out-of-order chunks are fine. */\nexport class Reassembler {\n private parts = new Map<number, Partial>(); // insertion order = oldest first\n\n /** Returns the full payload when the last chunk arrives, otherwise null. */\n push(chunk: { id: number; index: number; count: number; bytes: Uint8Array }): Uint8Array | null {\n if (chunk.count === 0 || chunk.index >= chunk.count) return null;\n let e = this.parts.get(chunk.id);\n if (!e || e.count !== chunk.count) {\n this.parts.delete(chunk.id);\n while (this.parts.size >= MAX_PARTIAL_SETS) this.parts.delete(this.parts.keys().next().value!);\n e = { count: chunk.count, got: 0, parts: new Array(chunk.count), bytes: 0 };\n this.parts.set(chunk.id, e);\n }\n if (e.parts[chunk.index]) return null;\n e.parts[chunk.index] = chunk.bytes;\n e.got++;\n e.bytes += chunk.bytes.length;\n if (e.bytes > MAX_REASSEMBLED_BYTES) {\n this.parts.delete(chunk.id);\n throw new CodecError(\"too_long\", `chunk set over ${MAX_REASSEMBLED_BYTES} bytes`);\n }\n if (e.got < e.count) {\n // Many half-sent sets must not add up to more than one whole message: the oldest others go first.\n while (this.pendingBytes > MAX_REASSEMBLED_BYTES && this.parts.size > 1) {\n for (const id of this.parts.keys()) if (id !== chunk.id) { this.parts.delete(id); break; }\n }\n return null;\n }\n this.parts.delete(chunk.id);\n const out = new Uint8Array(e.bytes);\n let pos = 0;\n for (const p of e.parts) {\n out.set(p!, pos);\n pos += p!.length;\n }\n return out;\n }\n\n get pending(): number {\n return this.parts.size;\n }\n /** Bytes held across incomplete sets. */\n get pendingBytes(): number {\n let n = 0;\n for (const p of this.parts.values()) n += p.bytes;\n return n;\n }\n clear(): void {\n this.parts.clear();\n }\n}\n", "import { CodecError, MAX_REASSEMBLED_BYTES } from \"./codec.js\";\n\n/** Optional zlib deflate for snapshots (CompressionStream where present; .NET ZLibStream is the matching side). */\n\nexport function deflateAvailable(): boolean {\n return typeof CompressionStream !== \"undefined\" && typeof DecompressionStream !== \"undefined\";\n}\n\nasync function pipe(\n bytes: Uint8Array,\n stream: { readable: ReadableStream<Uint8Array>; writable: WritableStream<Uint8Array> },\n maxBytes = Infinity,\n) {\n const w = stream.writable.getWriter();\n void w.write(bytes as unknown as Uint8Array<ArrayBuffer>).then(() => w.close()).catch(() => {});\n const chunks: Uint8Array[] = [];\n const r = stream.readable.getReader();\n let total = 0;\n for (;;) {\n const { done, value } = await r.read();\n if (done) break;\n chunks.push(value);\n total += value.length;\n if (total > maxBytes) {\n void r.cancel().catch(() => {});\n throw new CodecError(\"too_long\", `snapshot inflates to more than ${maxBytes} bytes`);\n }\n }\n const out = new Uint8Array(total);\n let pos = 0;\n for (const c of chunks) {\n out.set(c, pos);\n pos += c.length;\n }\n return out;\n}\n\nexport function deflate(bytes: Uint8Array): Promise<Uint8Array> {\n return pipe(bytes, new CompressionStream(\"deflate\") as never);\n}\n/** Inflates at most `maxBytes` (default 64 MiB): a few KB can inflate to gigabytes. Over it rejects with CodecError too_long. */\nexport function inflate(bytes: Uint8Array, maxBytes = MAX_REASSEMBLED_BYTES): Promise<Uint8Array> {\n return pipe(bytes, new DecompressionStream(\"deflate\") as never, maxBytes);\n}\n", "import { CHUNK_PAYLOAD, splitChunks } from \"./chunking.js\";\nimport { PROTO, type FrameCommand, type Message } from \"./codec.js\";\nimport { deflate, deflateAvailable, inflate } from \"./deflate.js\";\nimport { Emitter } from \"./emitter.js\";\nimport type { Hub } from \"./hub.js\";\n\nexport interface Frame {\n tick: number;\n commands: FrameCommand[];\n}\n\nexport interface DesyncInfo {\n tick: number;\n hostHash: bigint;\n yourHash: bigint;\n}\n\nexport type LockstepEvents = {\n desync: [DesyncInfo];\n /** A snapshot was applied (late join, desync, falling too far behind). */\n restored: [tick: number];\n /** First frame (or snapshot) of the game reached this peer: the session is in game. */\n started: [];\n error: [Error];\n};\n\nexport const MAX_DESYNCS = 3;\nexport const MAX_RESTORE_RETRIES = 3;\nexport const HOST_SILENCE_MS = 3000;\nexport const PEER_HASH_SILENCE_MS = 10_000;\n/** Commands one remote peer may have waiting for the host's next frame; the excess is dropped. */\nexport const MAX_PENDING_PER_PEER = 256;\nconst SNAPSHOT_COMPRESS_MIN = 1024;\n\nexport interface LockstepOptions {\n hashEveryTicks: number;\n /** Frames kept for replays (late join, migration). Default 30 s of ticks. */\n historyFrames?: number;\n}\n\n/**\n * Host-ordered lockstep (spec/binary.md, DESIGN s.8). One class for both roles: the host emits `frame(T)` on a fixed\n * cadence and never waits; joiners queue frames and hand them out through `nextFrame()`.\n *\n * Ticks: the initial state is tick 0 and has no frame; `frame(T)` produces tick T. `reportHash(tick, h)` is called\n * after applying `frame.tick` (the library only sends when `tick % hashEveryTicks == 0`, tick > 0). `snapshot()` must\n * return the state after the last frame returned by `nextFrame()`; `restore(tick, bytes)` receives that tick, and the\n * next frame applied is `tick + 1`.\n */\nexport class Lockstep extends Emitter<LockstepEvents> {\n snapshot: (() => Uint8Array) | null = null;\n restore: ((tick: number, bytes: Uint8Array) => void) | null = null;\n\n readonly hashEveryTicks: number;\n /** Chunk body size used when serving snapshots. */\n chunkSize = CHUNK_PAYLOAD;\n private historyMax: number;\n\n private queue: Frame[] = [];\n private history: Frame[] = [];\n /** Highest frame tick received/emitted; -1 = no state (a fresh joiner). */\n private lastFrameTick = -1;\n /** Tick of the state the game holds (last frame returned by nextFrame, or a restored snapshot). */\n private consumedTick = 0;\n private cmdSeq = 1;\n private awaitingSnapshot = false;\n private lastFrameRx = 0;\n private lastSnapshotReq = -Infinity;\n private restoreFailures = 0;\n\n // host side\n private hosting = false;\n private _started = false;\n private pending: FrameCommand[] = [];\n private _droppedCommands = 0;\n private ready = new Set<number>();\n private snapshotting = new Set<number>();\n private cancelTicker: (() => void) | null = null;\n private baseTime = 0;\n private emitted = 0;\n private held = false;\n private ownHashes = new Map<number, bigint>();\n private peerHashes = new Map<number, Map<number, bigint>>();\n private desyncs = new Map<number, number>();\n private lastHashRx = new Map<number, number>();\n /** Hashes a peer sent for ticks up to this were computed before it restored a snapshot: stale, not evidence. */\n private staleUpTo = new Map<number, { from: number; to: number }>();\n\n // joiner side\n private pendingSnapshot: { tick: number; total: number; flags: number } | null = null;\n\n constructor(private hub: Hub, opts: LockstepOptions) {\n super();\n this.hashEveryTicks = Math.max(1, opts.hashEveryTicks);\n this.historyMax = opts.historyFrames ?? Math.max(60, hub.tickRate * 30);\n this.lastFrameRx = hub.clock.now();\n }\n\n // ---- public game-facing surface -------------------------------------------------------------------------------\n\n /** Remote commands the host dropped because that peer already had MAX_PENDING_PER_PEER waiting. */\n get droppedCommands(): number {\n return this._droppedCommands;\n }\n get started(): boolean {\n return this._started;\n }\n get framesBuffered(): number {\n return this.queue.length;\n }\n /** True when the game should run ticks without rendering (> 2 frames behind). */\n get catchingUp(): boolean {\n return this.queue.length > 2;\n }\n /** Tick of the game's current state. */\n get currentTick(): number {\n return this.consumedTick;\n }\n /** Highest frame tick known to this peer (0 when none yet; the host's `welcome_dc.tick`). */\n get lastTick(): number {\n return Math.max(0, this.lastFrameTick);\n }\n /** What `hi.lastTick` carries: -1 until the first frame/snapshot. */\n get hiTick(): number {\n return this.lastFrameTick;\n }\n\n /** Queue a command. Host: into its own next frame. Joiner: to the host (reliable). */\n submit(bytes: Uint8Array): void {\n if (this.hosting) {\n this.pending.push({ peerId: this.hub.localPeerId, bytes: bytes.slice() });\n } else {\n this.hub.sendToHost({ type: \"cmd\", seq: this.cmdSeq++ >>> 0, bytes });\n }\n }\n\n nextFrame(): Frame | null {\n const f = this.queue.shift() ?? null;\n if (f) this.consumedTick = f.tick;\n return f;\n }\n\n reportHash(tick: number, hash: bigint): void {\n if (this.awaitingSnapshot || tick <= 0 || tick % this.hashEveryTicks !== 0) return;\n if (this.hosting) {\n this.ownHashes.set(tick, hash);\n this.prune(this.ownHashes);\n const theirs = this.peerHashes.get(tick);\n if (theirs) {\n this.peerHashes.delete(tick);\n for (const [peer, h] of [...theirs].sort((a, b) => a[0] - b[0])) this.compare(peer, tick, hash, h);\n }\n } else {\n this.hub.sendToHost({ type: \"hash\", tick, hash });\n }\n }\n\n // ---- role control (called by Lobby) ---------------------------------------------------------------------------\n\n /** Host role without emitting yet (lobby phase): peers may already say hi. */\n hostIdle(): void {\n this.hosting = true;\n if (this.lastFrameTick < 0) this.lastFrameTick = 0;\n }\n\n /** Host begins emitting frames (game start, or after winning a migration), continuing after the last known tick. */\n hostStart(): void {\n this.hostIdle();\n this._started = true;\n this.emitted = 0;\n this.baseTime = this.hub.clock.now();\n this.cancelTicker?.();\n const interval = 1000 / this.hub.tickRate;\n this.cancelTicker = this.hub.clock.setInterval(() => this.pump(interval), Math.max(1, Math.floor(interval / 2)));\n }\n\n /** Winning a migration: continue from our last received frame. Pending commands are lost by design. */\n becomeHost(): void {\n this.pending = [];\n this.ready.clear();\n this.snapshotting.clear();\n this.ownHashes.clear();\n this.peerHashes.clear();\n this.desyncs.clear();\n this.awaitingSnapshot = false;\n this.hostIdle();\n if (this._started) this.hostStart();\n }\n\n /** Losing a claim, or a new host appeared: stop emitting. Keep history; the new host decides replay vs snapshot. */\n becomeJoiner(): void {\n this.hosting = false;\n this.cancelTicker?.();\n this.cancelTicker = null;\n this.pending = [];\n this.ready.clear();\n this.lastFrameRx = this.hub.clock.now();\n }\n\n /** A joiner just learned the session is in game (first frame, snapshot, or phase from the server). */\n markStarted(): void {\n if (this._started) return;\n this._started = true;\n this.emit(\"started\");\n }\n\n /** The shared pause is on: the host emits no frames and a joiner expects none (no silent-host check). */\n get isHeld(): boolean {\n return this.held;\n }\n\n /**\n * Pause (true) or resume (false) frame emission (the shared pause calls this; not for games). Resuming rebases the\n * host's tick clock to now, so the next frame is simply tick+1 (no gap, no burst) and nobody counts as silent.\n */\n hold(held: boolean): void {\n if (this.held === held) return;\n this.held = held;\n if (held) return;\n const now = this.hub.clock.now();\n this.baseTime = now - this.emitted * (1000 / this.hub.tickRate);\n this.lastFrameRx = now;\n for (const p of this.ready) this.lastHashRx.set(p, now);\n }\n\n stop(): void {\n this.cancelTicker?.();\n this.cancelTicker = null;\n }\n\n onPeerLeft(peerId: number): void {\n this.ready.delete(peerId);\n this.snapshotting.delete(peerId);\n this.desyncs.delete(peerId);\n this.lastHashRx.delete(peerId);\n this.staleUpTo.delete(peerId);\n for (const m of this.peerHashes.values()) m.delete(peerId);\n this.pending = this.pending.filter((c) => c.peerId !== peerId);\n }\n\n // ---- host: frame emission ---------------------------------------------------------------------------------------\n\n private pump(interval: number): void {\n if (!this.hosting || this.held) return;\n const now = this.hub.clock.now();\n let due = Math.floor((now - this.baseTime + 1) / interval) - this.emitted;\n if (due > 10) {\n // Starved (e.g. a throttled tab): do not flood. Rebase rather than emit a burst of empty frames.\n this.baseTime = now - this.emitted * interval;\n due = 1;\n }\n for (let i = 0; i < due; i++) this.emitFrame();\n }\n\n private emitFrame(): void {\n // sorted by peerId, stable: one peer's commands keep their arrival order\n const commands = this.pending.splice(0).sort((a, b) => a.peerId - b.peerId);\n const frame: Frame = { tick: this.lastFrameTick + 1, commands };\n this.emitted++;\n this.lastFrameTick = frame.tick;\n this.record(frame);\n this.queue.push(frame);\n const msg: Message = { type: \"frame\", tick: frame.tick, commands };\n for (const p of this.ready) this.hub.sendTo(p, msg, 0);\n this.checkPeerSilence();\n }\n\n private record(f: Frame): void {\n this.history.push(f);\n if (this.history.length > this.historyMax) this.history.splice(0, this.history.length - this.historyMax);\n }\n\n /** The host drops a peer it has had no `hash` from for 10 s (scaled up when hashes are rare). */\n private checkPeerSilence(): void {\n if (this.ownHashes.size === 0) return;\n const now = this.hub.clock.now();\n const limit = Math.max(PEER_HASH_SILENCE_MS, (this.hashEveryTicks / this.hub.tickRate) * 1000 * 3);\n for (const p of [...this.ready]) {\n const last = this.lastHashRx.get(p) ?? now;\n if (now - last > limit) this.hub.dropPeer(p, \"timeout\");\n }\n }\n\n // ---- message handling -------------------------------------------------------------------------------------------\n\n handle(from: number, msg: Message): void {\n switch (msg.type) {\n case \"cmd\":\n if (this.hosting) {\n let waiting = 0;\n for (const c of this.pending) if (c.peerId === from) waiting++;\n if (waiting >= MAX_PENDING_PER_PEER) this._droppedCommands++;\n else this.pending.push({ peerId: from, bytes: msg.bytes });\n }\n break;\n case \"frame\":\n if (!this.hosting) this.onFrame(msg.tick, msg.commands);\n break;\n case \"hash\":\n if (this.hosting) this.onPeerHash(from, msg.tick, msg.hash);\n break;\n case \"desync\":\n if (!this.hosting) {\n this.awaitingSnapshot = true;\n this.queue = [];\n this.emit(\"desync\", { tick: msg.tick, hostHash: msg.hostHash, yourHash: msg.yourHash });\n }\n break;\n case \"snapshot_req\":\n if (this.hosting) void this.sendSnapshot(from);\n break;\n case \"snapshot\":\n if (!this.hosting) {\n this.markStarted();\n this.pendingSnapshot = { tick: msg.tick, total: msg.total, flags: msg.flags };\n }\n break;\n }\n }\n\n /** True when a `snapshot` header was seen and its chunk set has not completed yet. */\n get snapshotPending(): boolean {\n return this.pendingSnapshot !== null;\n }\n\n /** The reassembled chunk set that follows a `snapshot` header. */\n onSnapshotPayload(payload: Uint8Array): void {\n const ps = this.pendingSnapshot;\n this.pendingSnapshot = null;\n if (!ps) return;\n if (payload.length !== ps.total) {\n // spec: a receiver that gets a different sum drops the snapshot and re-requests\n this.awaitingSnapshot = false;\n this.requestSnapshot();\n return;\n }\n void this.applySnapshot(ps, payload);\n }\n\n private onFrame(tick: number, commands: FrameCommand[]): void {\n this.lastFrameRx = this.hub.clock.now();\n this.markStarted();\n if (this.awaitingSnapshot) return;\n const expected = Math.max(0, this.lastFrameTick) + 1;\n if (tick < expected) return; // duplicate (replay overlap)\n if (tick > expected) {\n this.requestSnapshot(); // gap: the host changed under us\n return;\n }\n const f: Frame = { tick, commands };\n this.lastFrameTick = tick;\n this.record(f);\n this.queue.push(f);\n if (this.queue.length > this.hub.tickRate * 3) this.requestSnapshot();\n }\n\n /** Joiner asks the host for a snapshot (far behind, gap, silent host). Throttled while one is outstanding. */\n requestSnapshot(): void {\n const now = this.hub.clock.now();\n if (this.awaitingSnapshot && now - this.lastSnapshotReq < 2000) return;\n this.lastSnapshotReq = now;\n this.awaitingSnapshot = true;\n this.queue = [];\n this.hub.sendToHost({ type: \"snapshot_req\", sinceTick: this.lastFrameTick });\n }\n\n /** 3 s without frames: ask the host for a snapshot without discarding anything (a live host answers). */\n pokeHost(): void {\n this.hub.sendToHost({ type: \"snapshot_req\", sinceTick: this.lastFrameTick });\n }\n\n private async applySnapshot(ps: { tick: number; total: number; flags: number }, payload: Uint8Array): Promise<void> {\n try {\n let bytes = payload;\n if (ps.flags & 1) bytes = await inflate(payload);\n if (!this.restore) throw new Error(\"lockstep.restore is not set; cannot apply a snapshot\");\n // Restore first: if the game's restore() throws, our bookkeeping must not have moved.\n this.restore(ps.tick, bytes);\n this.queue = [];\n this.history = [];\n this.lastFrameTick = ps.tick;\n this.consumedTick = ps.tick;\n this.awaitingSnapshot = false;\n this.restoreFailures = 0;\n this.markStarted();\n this.lastFrameRx = this.hub.clock.now();\n this.emit(\"restored\", ps.tick);\n } catch (e) {\n this.hub.reportError(e instanceof Error ? e : new Error(String(e)));\n // The game's state is unknown after a failed restore: keep our tick where it was, drop frames, ask again.\n if (++this.restoreFailures <= MAX_RESTORE_RETRIES) {\n this.awaitingSnapshot = false;\n this.lastSnapshotReq = -Infinity;\n this.requestSnapshot();\n } else {\n this.hub.reportError(new Error(`snapshot restore failed ${this.restoreFailures} times; giving up`));\n }\n }\n }\n\n // ---- host: hello, hashes, snapshots -----------------------------------------------------------------------------\n\n /** Host: a peer said hi (welcome_dc and chat_history already sent). Decide: nothing, replay, or snapshot. */\n onPeerHi(peerId: number, lastTick: number): void {\n this.lastHashRx.set(peerId, this.hub.clock.now());\n const hostTick = this.lastFrameTick;\n const first = this.history.length ? this.history[0].tick : hostTick + 1;\n if (lastTick < 0) {\n // fresh joiner: initial state if no game is running, otherwise a snapshot\n if (this._started && hostTick > 0) void this.sendSnapshot(peerId);\n else this.ready.add(peerId);\n return;\n }\n if (lastTick <= hostTick && lastTick + 1 >= first) {\n // The peer's timeline is a prefix of ours: replay what it lacks, then go live.\n for (const f of this.history) {\n if (f.tick > lastTick) this.hub.sendTo(peerId, { type: \"frame\", tick: f.tick, commands: f.commands }, 0);\n }\n this.ready.add(peerId);\n } else {\n void this.sendSnapshot(peerId);\n }\n }\n\n private onPeerHash(peerId: number, tick: number, hash: bigint): void {\n this.lastHashRx.set(peerId, this.hub.clock.now());\n if (this.isStale(peerId, tick)) return;\n const own = this.ownHashes.get(tick);\n if (own === undefined) {\n let m = this.peerHashes.get(tick);\n if (!m) this.peerHashes.set(tick, (m = new Map()));\n m.set(peerId, hash);\n this.prune(this.peerHashes);\n return;\n }\n this.compare(peerId, tick, own, hash);\n }\n\n private compare(peerId: number, tick: number, hostHash: bigint, yourHash: bigint): void {\n if (hostHash === yourHash) return;\n if (this.isStale(peerId, tick)) return;\n if (!this.ready.has(peerId) || this.snapshotting.has(peerId)) return; // already being resynced\n const n = (this.desyncs.get(peerId) ?? 0) + 1;\n this.desyncs.set(peerId, n);\n if (n >= MAX_DESYNCS) {\n this.hub.dropPeer(peerId, \"desync\");\n return;\n }\n this.hub.sendTo(peerId, { type: \"desync\", tick, hostHash, yourHash }, 0);\n void this.sendSnapshot(peerId, tick);\n }\n\n /** Hashes computed before a peer restored its snapshot (ticks after the strike, up to what we had emitted) are not evidence. */\n private isStale(peerId: number, tick: number): boolean {\n const w = this.staleUpTo.get(peerId);\n return !!w && tick > w.from && tick <= w.to;\n }\n\n private async sendSnapshot(peerId: number, strikeTick = Infinity): Promise<void> {\n if (this.snapshotting.has(peerId)) return;\n if (!this.snapshot) {\n this.hub.reportError(new Error(\"lockstep.snapshot is not set; cannot serve a snapshot\"));\n return;\n }\n this.snapshotting.add(peerId);\n this.ready.delete(peerId); // live frames now flow only through the replay below\n try {\n const S = this.consumedTick;\n const raw = this.snapshot();\n let payload = raw;\n let flags = 0;\n if (raw.length >= SNAPSHOT_COMPRESS_MIN && deflateAvailable()) {\n payload = await deflate(raw);\n flags = 1;\n }\n if (!this.snapshotting.has(peerId)) return; // peer left while compressing\n // header + one contiguous chunk set + frames S+1... with nothing else in between\n this.hub.sendTo(peerId, { type: \"snapshot\", tick: S, total: payload.length, flags }, 0);\n for (const c of splitChunks(this.hub.nextChunkId(), payload, this.chunkSize)) this.hub.sendTo(peerId, c, 0);\n for (const f of this.history) {\n if (f.tick > S) this.hub.sendTo(peerId, { type: \"frame\", tick: f.tick, commands: f.commands }, 0);\n }\n if (strikeTick !== Infinity) this.staleUpTo.set(peerId, { from: strikeTick, to: this.lastFrameTick });\n else this.staleUpTo.delete(peerId);\n for (const m of this.peerHashes.values()) m.delete(peerId);\n this.ready.add(peerId);\n this.lastHashRx.set(peerId, this.hub.clock.now());\n } catch (e) {\n this.hub.reportError(e instanceof Error ? e : new Error(String(e)));\n } finally {\n this.snapshotting.delete(peerId);\n }\n }\n\n private prune<T>(m: Map<number, T>): void {\n if (m.size <= 64) return;\n const keys = [...m.keys()].sort((a, b) => a - b);\n for (const k of keys.slice(0, keys.length - 64)) m.delete(k);\n }\n\n // ---- joiner watchdog --------------------------------------------------------------------------------------------\n\n /** Joiner: ms since the last frame from the host (only meaningful once in game). */\n get msSinceFrame(): number {\n return this.hub.clock.now() - this.lastFrameRx;\n }\n\n /** True when a started joiner has heard no frames for 3 s: the Lobby then runs the host-loss rule (s.9). */\n hostSilent(): boolean {\n return !this.hosting && !this.held && this._started && this.msSinceFrame > HOST_SILENCE_MS;\n }\n\n /** Reset the silence timer, e.g. after the link to a new host opened. */\n touch(): void {\n this.lastFrameRx = this.hub.clock.now();\n }\n\n hiMessage(peerId: number): Message {\n return { type: \"hi\", proto: PROTO, peerId, lastTick: this.lastFrameTick };\n }\n\n get resyncCount(): Map<number, number> {\n return this.desyncs;\n }\n}\n", "/**\n * Host migration rule (DESIGN s.9). Pure functions so the rule is testable without any network.\n *\n * - New host = lowest peerId still connected.\n * - Two claims: higher epoch wins; equal epoch -> lower peerId wins; the loser demotes itself.\n */\n\nexport interface Claim {\n epoch: number;\n hostId: number;\n}\n\n/** Lowest peerId among `connected`, skipping the failed host. Null when nobody is left. */\nexport function pickNewHost(connected: Iterable<number>, failedHost: number): number | null {\n let best: number | null = null;\n for (const id of connected) {\n if (id === failedHost) continue;\n if (best === null || id < best) best = id;\n }\n return best;\n}\n\n/** True when claim `a` beats claim `b`. */\nexport function claimWins(a: Claim, b: Claim): boolean {\n if (a.epoch !== b.epoch) return a.epoch > b.epoch;\n return a.hostId < b.hostId;\n}\n\n/** Epochs are u16 on the wire; compare with wraparound so a long-lived lobby never sticks. */\nexport function epochAfter(a: number, b: number): boolean {\n const d = (a - b) & 0xffff;\n return d !== 0 && d < 0x8000;\n}\n\nexport function claimWinsWrapped(a: Claim, b: Claim): boolean {\n if (a.epoch !== b.epoch) return epochAfter(a.epoch, b.epoch);\n return a.hostId < b.hostId;\n}\n", "import type { Clock } from \"./clock.js\";\nimport type { IceServer, SocketFactory, SocketLike } from \"./transport.js\";\n\n/** Signaling wire types (DESIGN s.7). Unknown fields are kept; unknown types are ignored by the Lobby. */\nexport interface PeerInfo {\n peerId: number;\n name: string;\n joinedAt?: number;\n}\n\nexport interface LobbyState {\n t: \"lobby\";\n code: string;\n gameId: string;\n netVersion: string;\n hostId: number;\n you: number;\n mode: \"lockstep\" | \"raw\";\n tickRate: number;\n lateJoin: boolean;\n phase: \"lobby\" | \"in_game\";\n meta: Record<string, string>;\n peers: PeerInfo[];\n resumeToken: string;\n name?: string;\n maxPlayers?: number;\n visibility?: string;\n relayOnly?: boolean;\n}\n\nexport interface LobbySummary {\n code: string;\n gameId: string;\n netVersion: string;\n name: string;\n hostName: string;\n players: number;\n maxPlayers: number;\n phase: string;\n meta: Record<string, string>;\n region?: string;\n createdAt?: number;\n}\n\nexport interface WelcomeMsg {\n t: \"welcome\";\n peerId?: number;\n ice: IceServer[];\n serverTime?: number;\n limits?: { maxMsg: number; maxPlayers: number };\n}\n\nexport interface ServerError {\n t: \"error\";\n code: string;\n msg?: string;\n data?: Record<string, unknown>;\n}\n\nexport type ServerMsg = { t: string; [k: string]: unknown };\n\nexport class OneLobbyError extends Error {\n constructor(public code: string, message?: string, public data?: Record<string, unknown>) {\n super(message ? `${code}: ${message}` : code);\n this.name = \"OneLobbyError\";\n }\n}\n\nexport const PROTO_ID = \"lk1\";\nconst PING_EVERY_MS = 10_000;\nconst SILENCE_MS = 30_000;\n\ninterface Waiter {\n types: string[];\n resolve: (m: ServerMsg) => void;\n reject: (e: Error) => void;\n}\n\n/** Thin JSON signaling client: one socket, request/response helpers, keep-alive pings. */\nexport class SignalingClient {\n private socket: SocketLike | null = null;\n private waiters: Waiter[] = [];\n private lastRx = 0;\n private cancelPing: (() => void) | null = null;\n private pingN = 0;\n closed = false;\n\n private handler: ((m: ServerMsg) => void) | null = null;\n // Messages with no waiter and no handler yet. The reply that resolves join/create can arrive in the same socket\n // read as the host's first offer; both are dispatched before the awaiting code builds its Lobby and sets a handler.\n private backlog: ServerMsg[] = [];\n onClose: (() => void) | null = null;\n\n get onMessage(): ((m: ServerMsg) => void) | null {\n return this.handler;\n }\n\n /** Setting a handler first delivers, in order, what arrived while there was none. */\n set onMessage(h: ((m: ServerMsg) => void) | null) {\n this.handler = h;\n while (h && this.handler === h && this.backlog.length) h(this.backlog.shift()!);\n }\n\n constructor(private url: string, private factory: SocketFactory, private clock: Clock) {}\n\n connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n let s: SocketLike;\n try {\n s = this.factory(this.url);\n } catch (e) {\n reject(new OneLobbyError(\"connect_failed\", String(e)));\n return;\n }\n this.socket = s;\n s.onopen = () => {\n this.lastRx = this.clock.now();\n this.cancelPing = this.clock.setInterval(() => this.tick(), PING_EVERY_MS);\n resolve();\n };\n s.onerror = () => {\n if (!this.lastRx) reject(new OneLobbyError(\"connect_failed\", `cannot reach ${this.url}`));\n };\n s.onmessage = (ev) => {\n this.lastRx = this.clock.now();\n let m: ServerMsg;\n try {\n m = JSON.parse(String(ev.data)) as ServerMsg;\n } catch {\n return;\n }\n if (!m || typeof m.t !== \"string\") return;\n if (m.t === \"ping\") {\n this.send({ t: \"pong\", n: m.n });\n return;\n }\n if (m.t === \"pong\") return;\n const i = this.waiters.findIndex((w) => w.types.includes(m.t) || m.t === \"error\");\n if (i >= 0) {\n const [w] = this.waiters.splice(i, 1);\n if (m.t === \"error\") w.reject(errorFrom(m));\n else w.resolve(m);\n return;\n }\n if (this.handler) this.handler(m);\n else if (this.backlog.length < 256) this.backlog.push(m);\n };\n s.onclose = () => {\n if (!this.lastRx) reject(new OneLobbyError(\"connect_failed\", `cannot reach ${this.url}`));\n this.shutdown();\n };\n });\n }\n\n private tick() {\n if (this.clock.now() - this.lastRx > SILENCE_MS) {\n this.socket?.close(4000, \"silence\");\n this.shutdown();\n return;\n }\n this.send({ t: \"ping\", n: ++this.pingN });\n }\n\n private shutdown() {\n if (this.closed) return;\n this.closed = true;\n this.cancelPing?.();\n const err = new OneLobbyError(\"closed\", \"signaling connection closed\");\n for (const w of this.waiters.splice(0)) w.reject(err);\n this.onClose?.();\n }\n\n get isOpen(): boolean {\n return !this.closed && this.socket !== null && this.socket.readyState === 1;\n }\n\n send(m: Record<string, unknown>): void {\n if (!this.isOpen) return;\n this.socket!.send(JSON.stringify(m));\n }\n\n /** Wait for the next message of one of `types`, or reject on `error`/close. */\n expect<T extends { t: string } = ServerMsg>(...types: string[]): Promise<T> {\n if (this.closed) return Promise.reject(new OneLobbyError(\"closed\", \"signaling connection closed\"));\n return new Promise<T>((resolve, reject) => {\n this.waiters.push({ types, resolve: resolve as (m: ServerMsg) => void, reject });\n });\n }\n\n async request<T extends { t: string } = ServerMsg>(m: Record<string, unknown>, ...types: string[]): Promise<T> {\n const p = this.expect<T>(...types);\n this.send(m);\n return p;\n }\n\n close(): void {\n this.socket?.close(1000, \"bye\");\n this.shutdown();\n }\n}\n\nfunction errorFrom(m: ServerMsg): OneLobbyError {\n return new OneLobbyError(String(m.code ?? \"error\"), m.msg === undefined ? undefined : String(m.msg), m.data as Record<string, unknown> | undefined);\n}\n", "import { PAUSE_FLAG_PAUSER_LEFT, PAUSE_REFUSE, type Message } from \"./codec.js\";\nimport { Emitter } from \"./emitter.js\";\nimport type { Hub } from \"./hub.js\";\nimport { OneLobbyError } from \"./signaling.js\";\n\n/** The least time between two accepted pause requests from one peer. */\nexport const PAUSE_MIN_REQUEST_GAP_MS = 500;\n\n/**\n * The shared pause: `paused` is on while someone holds the match; `byPeer` is the peer that paused (while paused) or\n * that last resumed it; `pauserLeft` says the pauser left and the host now holds the pause (`byPeer` is then the host).\n */\nexport interface PauseState {\n readonly paused: boolean;\n readonly byPeer: number;\n readonly pauserLeft: boolean;\n}\n\n/** Why a request was refused: `not_in_game`, `rate_limited` or `not_allowed` (the `ErrorCodes` constants). */\nexport type PauseRefusal = \"not_in_game\" | \"rate_limited\" | \"not_allowed\" | \"unknown\";\n\nexport type PauseEvents = {\n /** The state changed (paused, resumed, pauser left, cleared). Fires on every peer. */\n changed: [PauseState];\n /** A request of this peer was refused; `code` is a `PauseRefusal`. */\n refused: [code: PauseRefusal];\n};\n\n/** `session.pause` of a blueprint: \"off\" makes `request` throw `pause_disabled`, \"host\" makes the default policy host-only. */\nexport type PauseMode = \"off\" | \"host\" | \"anyone\";\n\nexport interface PauseDeps {\n /** The lobby's phase now (\"lobby\" | \"in_game\"). */\n phase: () => string;\n /** Lockstep: stop (true) or resume (false) the host's frame emission. */\n hold: (held: boolean) => void;\n /** Whether `peerId` is still in the roster. */\n hasPeer: (peerId: number) => boolean;\n}\n\nconst NONE: PauseState = Object.freeze({ paused: false, byPeer: 0, pauserLeft: false });\n\nfunction refusalName(code: number): PauseRefusal {\n return code === PAUSE_REFUSE.notInGame ? \"not_in_game\" : code === PAUSE_REFUSE.rateLimited ? \"rate_limited\" : code === PAUSE_REFUSE.notAllowed ? \"not_allowed\" : \"unknown\";\n}\n\n/**\n * Shared co-op pause (docs/recipes/pause.md): any player asks the host to pause or resume the match for everyone.\n * The host validates (phase, rate, policy), keeps the state and broadcasts it over the reliable channel. OneLobby moves\n * the state and, in lockstep, stops the host's frames; scaling time, menus and banners stay in the game. It is outside\n * the sim: never put it in a lockstep command or hash.\n */\nexport class Pause extends Emitter<PauseEvents> {\n /** Set by a blueprint (`session.pause`); the default is \"anyone\". */\n mode: PauseMode = \"anyone\";\n /**\n * Host: replaces the default policy. Called with (requesting peer, true to pause / false to resume); return true to\n * allow. Default: anyone pauses, the pauser or the host resumes (host only in \"host\" mode). Phase and rate run first.\n */\n policy: ((peerId: number, paused: boolean) => boolean) | null = null;\n\n private current: PauseState = NONE;\n private lastAccepted = new Map<number, number>();\n\n constructor(private hub: Hub, private deps: PauseDeps) {\n super();\n }\n\n /** The current shared state (the same on every peer once the host's broadcast has arrived). */\n get state(): PauseState {\n return this.current;\n }\n\n /**\n * True when the local peer may resume now under the default policy: the pauser or the host (in \"host\" mode, the\n * host). A custom `policy` lives on the host, so on a joiner this is a hint.\n */\n get canResume(): boolean {\n return this.current.paused && this.defaultAllows(this.hub.localPeerId, false);\n }\n\n /**\n * Ask to pause (true) or resume (false) the match for everyone. Throws `pause_disabled` when the blueprint says\n * `session.pause` is \"off\" (the default; set it to \"anyone\" or \"host\" in onelobby.json). Works only while the lobby\n * is in game (phase \"in_game\", after `start()`): before that the host refuses with `not_in_game`. The result arrives\n * as `changed` or `refused`; asking for the state that already holds does nothing.\n */\n request(paused: boolean): void {\n if (this.mode === \"off\") throw new OneLobbyError(\"pause_disabled\", \"pause is off in onelobby.json (session.pause is \\\"off\\\"); set session.pause to \\\"anyone\\\" or \\\"host\\\" to turn it on\");\n if (this.hub.isHost) this.hostHandle(this.hub.localPeerId, paused);\n else this.hub.sendToHost({ type: \"pause_request\", paused: paused ? 1 : 0 });\n }\n\n /**\n * Host only: unpause at once, skipping the policy and the rate limit. Call it before a restart or game over so the\n * next round does not start frozen. Does nothing when not paused.\n */\n clear(): void {\n if (!this.hub.isHost) throw new Error(\"only the host can clear the pause\");\n if (!this.current.paused) return;\n this.set({ paused: false, byPeer: this.hub.localPeerId, pauserLeft: false }, true);\n }\n\n // ---- internals (called by Lobby) ------------------------------------------------------------------------------\n\n handle(from: number, msg: Message): void {\n switch (msg.type) {\n case \"pause_request\":\n if (this.hub.isHost && this.hub.connectedPeers().includes(from)) this.hostHandle(from, msg.paused !== 0);\n break;\n case \"pause_state\":\n if (!this.hub.isHost && from === this.hub.hostPeerId) {\n this.set({ paused: msg.paused !== 0, byPeer: msg.byPeer, pauserLeft: (msg.flags & PAUSE_FLAG_PAUSER_LEFT) !== 0 }, false);\n }\n break;\n case \"pause_refuse\":\n if (!this.hub.isHost && from === this.hub.hostPeerId) this.emit(\"refused\", refusalName(msg.reason));\n break;\n }\n }\n\n /** Host: a late joiner's link is up; it gets the state when there is one to tell. */\n sendState(peerId: number): void {\n if (this.current.paused) this.hub.sendTo(peerId, this.stateMessage(), 0);\n }\n\n /** A peer is gone: the host takes over the pause its leaver held. */\n onPeerLeft(peerId: number): void {\n this.lastAccepted.delete(peerId);\n const s = this.current;\n if (this.hub.isHost && s.paused && !s.pauserLeft && s.byPeer === peerId) {\n this.set({ paused: true, byPeer: this.hub.localPeerId, pauserLeft: true }, true);\n }\n }\n\n /** This peer just became the host (migration): it keeps the state as is, and takes over a pause whose holder is gone. */\n onBecameHost(): void {\n this.deps.hold(this.current.paused);\n const s = this.current;\n if (s.paused && !s.pauserLeft && !this.deps.hasPeer(s.byPeer)) {\n this.set({ paused: true, byPeer: this.hub.localPeerId, pauserLeft: true }, true);\n }\n }\n\n /** The lobby went back to the lobby phase: nothing stays paused. */\n onLobbyPhase(): void {\n if (!this.current.paused) return;\n this.set({ paused: false, byPeer: this.hub.hostPeerId, pauserLeft: false }, this.hub.isHost);\n }\n\n // ---- host path --------------------------------------------------------------------------------------------------\n\n private defaultAllows(peer: number, wantPaused: boolean): boolean {\n if (this.mode === \"host\") return peer === this.hub.hostPeerId;\n return wantPaused || peer === this.current.byPeer || peer === this.hub.hostPeerId;\n }\n\n private hostHandle(from: number, want: boolean): void {\n if (this.mode === \"off\") return this.refuse(from, want, PAUSE_REFUSE.notAllowed);\n if (this.deps.phase() !== \"in_game\") return this.refuse(from, want, PAUSE_REFUSE.notInGame);\n if (want === this.current.paused) return;\n const now = this.hub.clock.now();\n const last = this.lastAccepted.get(from);\n if (last !== undefined && now - last < PAUSE_MIN_REQUEST_GAP_MS) return this.refuse(from, want, PAUSE_REFUSE.rateLimited);\n const ok = this.policy ? this.policy(from, want) : this.defaultAllows(from, want);\n if (!ok) return this.refuse(from, want, PAUSE_REFUSE.notAllowed);\n this.lastAccepted.set(from, now);\n this.set({ paused: want, byPeer: from, pauserLeft: false }, true);\n }\n\n private refuse(to: number, want: boolean, reason: number): void {\n if (to === this.hub.localPeerId) this.emit(\"refused\", refusalName(reason));\n else this.hub.sendTo(to, { type: \"pause_refuse\", paused: want ? 1 : 0, reason }, 0);\n }\n\n private stateMessage(): Message {\n const s = this.current;\n return { type: \"pause_state\", paused: s.paused ? 1 : 0, byPeer: s.byPeer, flags: s.pauserLeft ? PAUSE_FLAG_PAUSER_LEFT : 0 };\n }\n\n private set(next: PauseState, broadcast: boolean): void {\n const prev = this.current;\n this.current = Object.freeze(next);\n this.deps.hold(next.paused);\n if (broadcast) {\n const msg = this.stateMessage();\n for (const p of this.hub.connectedPeers()) this.hub.sendTo(p, msg, 0);\n }\n if (prev.paused !== next.paused || prev.byPeer !== next.byPeer || prev.pauserLeft !== next.pauserLeft) this.emit(\"changed\", next);\n }\n}\n", "/**\n * Pure voice building blocks (DESIGN s.9a): packetizer, PTT/VAD gate, jitter buffer, host relay policy.\n * No audio APIs here, so all of it is unit-testable.\n */\n\nexport const SAMPLE_RATE = 48000;\nexport const FRAME_MS = 20;\nexport const FRAME_SAMPLES = (SAMPLE_RATE * FRAME_MS) / 1000; // 960\nexport const FRAMES_PER_PACKET = 2;\nexport const MAX_VOICE_PACKET_BYTES = 1024;\nexport const MAX_SPEAKERS = 8;\nexport const MAX_PACKETS_PER_SEC = 30;\nexport const JITTER_MS = 60;\nexport const VAD_HANGOVER_MS = 300;\nexport const FLAG_END_OF_BURST = 1;\n\nexport interface VoicePacket {\n seq: number;\n flags: number;\n frames: Uint8Array[];\n}\n\n/** Batches encoded 20 ms frames into 40 ms packets; `endBurst()` flushes the remainder with the end flag. */\nexport class VoicePacketizer {\n private seq = 0;\n private buf: Uint8Array[] = [];\n constructor(private framesPerPacket = FRAMES_PER_PACKET) {}\n\n push(frame: Uint8Array): VoicePacket | null {\n this.buf.push(frame);\n if (this.buf.length < this.framesPerPacket) return null;\n return this.take(0);\n }\n\n /** Wrap already-encoded frames in the next packet (no batching). */\n packet(frames: Uint8Array[], flags: number): VoicePacket {\n const p: VoicePacket = { seq: this.seq, flags, frames };\n this.seq = (this.seq + 1) & 0xffff;\n return p;\n }\n\n /** Always emits a packet (possibly with zero frames) carrying the end-of-burst flag. */\n endBurst(): VoicePacket {\n return this.take(FLAG_END_OF_BURST);\n }\n\n get buffered(): number {\n return this.buf.length;\n }\n\n private take(flags: number): VoicePacket {\n const p: VoicePacket = { seq: this.seq, flags, frames: this.buf };\n this.seq = (this.seq + 1) & 0xffff;\n this.buf = [];\n return p;\n }\n}\n\nexport function rms(pcm: Float32Array): number {\n if (pcm.length === 0) return 0;\n let s = 0;\n for (let i = 0; i < pcm.length; i++) s += pcm[i] * pcm[i];\n return Math.sqrt(s / pcm.length);\n}\n\nexport type VoiceMode = \"pushToTalk\" | \"voiceActivity\";\n\n/** Decides, per captured 20 ms frame, whether it is transmitted. VAD uses an RMS gate with a 300 ms hangover. */\nexport class VoiceGate {\n mode: VoiceMode = \"pushToTalk\";\n ptt = false;\n vadThreshold = 0.02;\n private lastLoud = -Infinity;\n private open = false;\n\n /** Returns { send, edge } where edge is 'open' | 'close' | null on a transition. */\n update(level: number, nowMs: number): { send: boolean; edge: \"open\" | \"close\" | null } {\n let want: boolean;\n if (this.mode === \"pushToTalk\") {\n want = this.ptt;\n } else {\n if (level >= this.vadThreshold) this.lastLoud = nowMs;\n want = nowMs - this.lastLoud <= VAD_HANGOVER_MS;\n }\n const edge = want === this.open ? null : want ? \"open\" : \"close\";\n this.open = want;\n return { send: want, edge };\n }\n\n get isOpen(): boolean {\n return this.open;\n }\n}\n\n/** u16 sequence comparison with wraparound: positive when a is after b. */\nexport function seqDiff(a: number, b: number): number {\n const d = (a - b) & 0xffff;\n return d >= 0x8000 ? d - 0x10000 : d;\n}\n\nexport type PullResult =\n | { kind: \"frame\"; opus: Uint8Array }\n | { kind: \"plc\" }\n | { kind: \"wait\" }\n | { kind: \"idle\" };\n\n/**\n * Per-speaker jitter buffer. Reorders by `seq`, prefills 60 ms (3 frames) before playing, reports a lost packet as\n * PLC frames when a later packet is already here, and flushes on the end-of-burst flag.\n * `pull()` is called once per 20 ms by the playout clock.\n */\nexport class JitterBuffer {\n private packets = new Map<number, VoicePacket>();\n private expected: number | null = null;\n private ready: Uint8Array[] = [];\n private playing = false;\n private endSeq: number | null = null;\n private waitTicks = 0;\n readonly prefillFrames: number;\n\n constructor(jitterMs = JITTER_MS, private framesPerPacket = FRAMES_PER_PACKET) {\n this.prefillFrames = Math.max(1, Math.round(jitterMs / FRAME_MS));\n }\n\n push(p: VoicePacket): void {\n if (this.expected === null) {\n this.expected = p.seq;\n } else if (seqDiff(p.seq, this.expected) < 0) {\n return; // late: already played or skipped\n }\n this.packets.set(p.seq, p);\n if (p.flags & FLAG_END_OF_BURST) this.endSeq = p.seq;\n else if (this.endSeq !== null && seqDiff(p.seq, this.endSeq) > 0) this.endSeq = null; // a new burst began\n }\n\n private get ended(): boolean {\n return this.endSeq !== null;\n }\n\n private bufferedFrames(): number {\n let n = this.ready.length;\n for (const p of this.packets.values()) n += p.frames.length;\n return n;\n }\n\n private newerThanExpected(): boolean {\n for (const s of this.packets.keys()) if (this.expected !== null && seqDiff(s, this.expected) > 0) return true;\n return false;\n }\n\n pull(): PullResult {\n if (this.expected === null) return { kind: \"idle\" };\n if (!this.playing) {\n if (this.bufferedFrames() >= this.prefillFrames || (this.ended && this.packets.size > 0)) this.playing = true;\n else return { kind: \"wait\" };\n }\n if (this.ready.length === 0) this.refill();\n const f = this.ready.shift();\n if (f) {\n this.waitTicks = 0;\n return f.length === 0 ? { kind: \"plc\" } : { kind: \"frame\", opus: f };\n }\n // nothing playable\n if (this.ended && this.packets.size === 0) {\n this.reset();\n return { kind: \"idle\" };\n }\n if (++this.waitTicks > 12) {\n // 240 ms of silence without an end flag: the burst is over (flag lost). Flush.\n this.reset();\n return { kind: \"idle\" };\n }\n return { kind: \"wait\" };\n }\n\n private refill(): void {\n if (this.expected === null) return;\n const p = this.packets.get(this.expected);\n if (p) {\n this.packets.delete(this.expected);\n this.expected = (this.expected + 1) & 0xffff;\n for (const fr of p.frames) this.ready.push(fr);\n return;\n }\n if (this.newerThanExpected()) {\n // The packet never arrived but later ones did: conceal it (empty array = PLC marker).\n this.expected = (this.expected + 1) & 0xffff;\n for (let i = 0; i < this.framesPerPacket; i++) this.ready.push(new Uint8Array(0));\n }\n }\n\n reset(): void {\n this.packets.clear();\n this.ready = [];\n this.expected = null;\n this.playing = false;\n this.endSeq = null;\n this.waitTicks = 0;\n }\n\n get active(): boolean {\n return this.expected !== null;\n }\n}\n\n/**\n * Host-side policy for forwarding voice (s.9a): <= 8 concurrent speakers (9th burst silently dropped), packets <= 1 KiB,\n * <= 30 packets/s per peer.\n */\nexport class VoiceRelayPolicy {\n private speakers = new Map<number, number>(); // peerId -> last packet time\n private windows = new Map<number, number[]>();\n /** A burst with no packet for this long counts as over even if its end flag was lost. */\n burstTimeoutMs = 500;\n\n accept(from: number, size: number, flags: number, now: number): boolean {\n if (size > MAX_VOICE_PACKET_BYTES) return false;\n // rate cap\n let w = this.windows.get(from);\n if (!w) this.windows.set(from, (w = []));\n while (w.length && now - w[0] >= 1000) w.shift();\n if (w.length >= MAX_PACKETS_PER_SEC) return false;\n // speaker cap\n for (const [p, t] of this.speakers) if (now - t > this.burstTimeoutMs) this.speakers.delete(p);\n if (!this.speakers.has(from) && this.speakers.size >= MAX_SPEAKERS) return false;\n w.push(now);\n if (flags & FLAG_END_OF_BURST) this.speakers.delete(from);\n else this.speakers.set(from, now);\n return true;\n }\n\n get activeSpeakers(): number {\n return this.speakers.size;\n }\n\n forget(peerId: number): void {\n this.speakers.delete(peerId);\n this.windows.delete(peerId);\n }\n}\n", "import type { VoiceError } from \"./voice-types.js\";\nimport { Emitter } from \"./emitter.js\";\nimport type { Hub } from \"./hub.js\";\nimport type { Message } from \"./codec.js\";\nimport {\n FLAG_END_OF_BURST,\n FRAME_MS,\n JitterBuffer,\n VoiceGate,\n VoicePacketizer,\n VoiceRelayPolicy,\n rms,\n type VoiceMode,\n type VoicePacket,\n} from \"./voice-core.js\";\nimport type { AudioSink, FrameSource, VoiceCodec, VoiceDecoderHandle, VoiceEncoderHandle } from \"./voice-types.js\";\n\nexport type VoiceEvents = {\n speaking: [peerId: number, talking: boolean];\n error: [VoiceError];\n /** Every voice packet that reached this peer (after the host's validity checks), before mute/enable filtering. */\n packet: [speaker: number, seq: number, flags: number, frames: Uint8Array[]];\n};\n\ninterface Speaker {\n jitter: JitterBuffer;\n decoder: VoiceDecoderHandle | null;\n talking: boolean;\n lastHeard: number;\n}\n\nexport interface VoiceDeps {\n codec: VoiceCodec | null;\n /** Creates the mic capture; resolved on `enabled = true`. */\n createSource: (() => FrameSource) | null;\n createSink: (() => AudioSink) | null;\n}\n\nconst SPEAKING_TIMEOUT_MS = 300;\n\n/** Voice chat (DESIGN s.9a): Opus frames on the `voice` channel, star-routed through the host, never in a frame. */\nexport class Voice extends Emitter<VoiceEvents> {\n mode: VoiceMode = \"pushToTalk\";\n /** Master mute for capture (self). */\n muted = false;\n /** 0..1 RMS of the last captured frame. */\n inputLevel = 0;\n bitrate = 24000;\n\n private _enabled = false;\n private gate = new VoiceGate();\n private packetizer = new VoicePacketizer();\n private relay = new VoiceRelayPolicy();\n private speakers = new Map<number, Speaker>();\n private peerMuted = new Set<number>();\n private gains = new Map<number, number>();\n private encoder: VoiceEncoderHandle | null = null;\n private source: FrameSource | null = null;\n private sink: AudioSink | null = null;\n private cancelPlayout: (() => void) | null = null;\n private endTimer: (() => void) | null = null;\n private sending = false;\n\n constructor(private hub: Hub, private deps: VoiceDeps) {\n super();\n }\n\n get enabled(): boolean {\n return this._enabled;\n }\n set enabled(v: boolean) {\n if (v === this._enabled) return;\n this._enabled = v;\n if (v) void this.start();\n else this.stop();\n }\n\n pushToTalk(down: boolean): void {\n this.gate.ptt = down;\n }\n\n isMutedPeer(peerId: number): boolean {\n return this.peerMuted.has(peerId);\n }\n setMuted(peerId: number, muted: boolean): void {\n if (muted) this.peerMuted.add(peerId);\n else this.peerMuted.delete(peerId);\n }\n setVolume(peerId: number, gain: number): void {\n this.gains.set(peerId, Math.min(2, Math.max(0, gain)));\n }\n volumeOf(peerId: number): number {\n return this.gains.get(peerId) ?? 1;\n }\n\n // ---- capture -------------------------------------------------------------------------------------------------\n\n private async start(): Promise<void> {\n try {\n if (!this.deps.codec) throw new Error(\"no Opus codec available: supply one through the voice.codec option (WASM fallback)\");\n if (!this.deps.createSink) throw new Error(\"no audio output available\");\n this.sink = this.deps.createSink();\n this.cancelPlayout = this.hub.clock.setInterval(() => this.playoutTick(), FRAME_MS);\n if (this.deps.createSource) {\n this.encoder = this.deps.codec.createEncoder({ bitrate: this.bitrate }, (opus) => this.onEncoded(opus));\n this.source = this.deps.createSource();\n await this.source.start((pcm) => this.onCapturedFrame(pcm, this.hub.clock.now()));\n }\n } catch (e) {\n this._enabled = false;\n this.stop();\n this.emit(\"error\", { message: e instanceof Error ? e.message : String(e), cause: e });\n }\n }\n\n private stop(): void {\n if (this.sending) this.closeBurst();\n this.source?.stop();\n this.source = null;\n this.encoder?.close();\n this.encoder = null;\n this.cancelPlayout?.();\n this.cancelPlayout = null;\n this.sink?.close();\n this.sink = null;\n for (const [id, s] of this.speakers) {\n s.decoder?.close();\n if (s.talking) this.emit(\"speaking\", id, false);\n }\n this.speakers.clear();\n }\n\n /** Feed one 20 ms mono 48 kHz frame (called by the mic source; exposed so tests and custom sources can drive it). */\n onCapturedFrame(pcm: Float32Array, nowMs: number): void {\n const level = rms(pcm);\n this.inputLevel = level;\n if (!this._enabled || this.muted) {\n if (this.sending) this.closeBurst();\n return;\n }\n const g = this.gate.update(level, nowMs);\n if (g.send) {\n this.sending = true;\n this.endTimer?.();\n this.endTimer = null;\n this.encoder?.encode(pcm);\n } else if (g.edge === \"close\" && this.sending) {\n this.closeBurst();\n }\n }\n\n private closeBurst(): void {\n this.sending = false;\n // Encoders can emit asynchronously (WebCodecs); give them one packet's worth of time before the end flag.\n this.endTimer?.();\n this.endTimer = this.hub.clock.setTimeout(() => {\n this.endTimer = null;\n this.sendPacket(this.packetizer.endBurst());\n }, FRAME_MS * 2);\n }\n\n /** Send already-encoded Opus frames as one packet (no codec involved); used by tools and conformance. */\n sendEncoded(frames: Uint8Array[], endOfBurst = false): void {\n this.sendPacket(this.packetizer.packet(frames, endOfBurst ? FLAG_END_OF_BURST : 0));\n }\n\n /** Encoded frame from the encoder (also the test entry point for packetization). */\n onEncoded(opus: Uint8Array): void {\n const p = this.packetizer.push(opus);\n if (p) this.sendPacket(p);\n }\n\n private sendPacket(p: VoicePacket): void {\n const msg: Message = { type: \"voice\", seq: p.seq, flags: p.flags, frames: p.frames };\n if (this.hub.isHost) {\n for (const peer of this.hub.connectedPeers()) this.hub.sendTo(peer, msg, this.hub.localPeerId, \"voice\");\n } else {\n this.hub.sendToHost(msg, 0, \"voice\");\n }\n }\n\n // ---- receive -------------------------------------------------------------------------------------------------\n\n handle(from: number, route: number, msg: Message): void {\n if (msg.type !== \"voice\") return;\n const size = msg.frames.reduce((a, f) => a + f.length + 2, 0) + 7; // whole wire frame: header 3 + seq/flags/n 4\n const now = this.hub.clock.now();\n let speaker = route;\n if (this.hub.isHost) {\n speaker = from;\n if (!this.relay.accept(from, size, msg.flags, now)) return;\n for (const peer of this.hub.connectedPeers()) {\n if (peer !== from) this.hub.sendTo(peer, msg, from, \"voice\");\n }\n }\n this.emit(\"packet\", speaker, msg.seq, msg.flags, msg.frames);\n if (!this._enabled || speaker === this.hub.localPeerId || this.peerMuted.has(speaker)) return;\n this.receivePacket(speaker, { seq: msg.seq, flags: msg.flags, frames: msg.frames }, now);\n }\n\n /** Local playback path for one speaker's packet (after host forwarding). */\n receivePacket(speaker: number, p: VoicePacket, now: number): void {\n let s = this.speakers.get(speaker);\n if (!s) {\n s = { jitter: new JitterBuffer(), decoder: null, talking: false, lastHeard: now };\n this.speakers.set(speaker, s);\n }\n s.lastHeard = now;\n s.jitter.push(p);\n if (!s.talking && p.frames.length > 0) {\n s.talking = true;\n this.emit(\"speaking\", speaker, true);\n }\n }\n\n private playoutTick(): void {\n const now = this.hub.clock.now();\n for (const [id, s] of this.speakers) {\n const r = s.jitter.pull();\n if (r.kind === \"frame\" || r.kind === \"plc\") {\n if (!s.decoder && this.deps.codec) {\n s.decoder = this.deps.codec.createDecoder((pcm) => this.sink?.play(id, pcm, this.volumeOf(id)));\n }\n s.decoder?.decode(r.kind === \"frame\" ? r.opus : null);\n } else if (r.kind === \"idle\" && s.talking) {\n s.talking = false;\n this.emit(\"speaking\", id, false);\n } else if (s.talking && now - s.lastHeard > SPEAKING_TIMEOUT_MS + 5 * FRAME_MS && !s.jitter.active) {\n s.talking = false;\n this.emit(\"speaking\", id, false);\n }\n }\n }\n\n onPeerLeft(peerId: number): void {\n this.relay.forget(peerId);\n const s = this.speakers.get(peerId);\n if (s) {\n s.decoder?.close();\n if (s.talking) this.emit(\"speaking\", peerId, false);\n this.speakers.delete(peerId);\n }\n }\n\n close(): void {\n this._enabled = false;\n this.stop();\n this.endTimer?.();\n }\n\n /** For tests: expose the end-of-burst flag constant. */\n static readonly END = FLAG_END_OF_BURST;\n}\n", "import type { CloseInfo, Lobby, Peer } from \"./lobby.js\";\n\n/**\n * A WebSocket-shaped view of one peer link, so code written against `WebSocket` runs on a lobby unchanged:\n * `lobby.asWebSocket()` on a joiner (the link to the host), `lobby.onConnection(conn => ...)` on the host (one per\n * joiner). Not a real WebSocket: no URL, no subprotocols, no `addEventListener`; `bufferedAmount` is always 0.\n */\nexport interface LobbySocket {\n /** The peer at the other end (the host for a joiner's socket, the joiner for a host's connection). */\n readonly peerId: number;\n /** 0 CONNECTING (joiner, before `lobby.ready`), 1 OPEN, 3 CLOSED (there is no CLOSING). */\n readonly readyState: 0 | 1 | 3;\n readonly bufferedAmount: 0;\n /** A string is sent as UTF-8; bytes as they are. Ignored unless OPEN. */\n send(data: string | Uint8Array): void;\n /**\n * Closes this socket locally and fires `onclose` with `code` (default 1000). On a joiner it also leaves the lobby.\n * On the host it only stops this connection's events: to end the player's connection too (its socket gets your code\n * and reason), call `lobby.disconnect(conn.peerId, code, reason)`; `lobby.kick()` also bans it from the lobby.\n */\n close(code?: number, reason?: string): void;\n /**\n * Fires once, on a later task than the one that created the socket (like a real WebSocket). Assigned after it already\n * fired (the socket came out of an `await` chain)? It runs for the new handler once; `readyState === 1` also tells.\n */\n onopen: ((ev: { type: \"open\" }) => void) | null;\n /** `data` is a string (UTF-8 decoded), or the bytes with `{ binary: true }`. */\n onmessage: ((ev: { type: \"message\"; data: string | Uint8Array }) => void) | null;\n /** `reason` is the lobby's reason (\"host_left\", \"timeout\", \"kicked\", \"disconnected\", ...) unless `close()` or the host gave one. */\n onclose: ((ev: { type: \"close\"; code: number; reason: string; wasClean: boolean }) => void) | null;\n /** Never called (errors surface as `onclose` with code 1006); present so WebSocket code that assigns it works. */\n onerror: ((ev: { type: \"error\" }) => void) | null;\n}\n\nexport interface LobbySocketOptions {\n /** Deliver `onmessage` data as bytes (Uint8Array) instead of UTF-8 text. Default false. */\n binary?: boolean;\n /** Channel for `send` (default \"reliable\"). \"fast\" is unordered and may drop, like UDP. */\n channel?: \"reliable\" | \"fast\";\n}\n\n/**\n * Close codes: 1000 a normal leave, `close()` or the host's `disconnect()`, 1001 the other end went away (`host_left`,\n * the host closing, a player who left), 4000 kicked, 1006 anything abnormal (`timeout`, `ice_failed`, `server_lost`,\n * ...). A code and reason the host passed to `kick()` / `disconnect()` win over these.\n */\nexport function closeCodeFor(reason: string): number {\n if (reason === \"left\" || reason === \"disconnected\") return 1000;\n if (reason === \"host_left\" || reason === \"closed\") return 1001;\n if (reason === \"kicked\") return 4000;\n return 1006;\n}\n\n/** A later task, like a real WebSocket's events: setImmediate in Node, setTimeout(0) in browsers. */\nconst nextTask: (f: () => void) => void =\n (globalThis as { setImmediate?: (f: () => void) => void }).setImmediate ?? ((f) => void setTimeout(f, 0));\n\nconst enc = new TextEncoder();\nconst dec = new TextDecoder();\n\nclass PeerSocket implements LobbySocket {\n readyState: 0 | 1 | 3 = 0;\n readonly bufferedAmount = 0 as const;\n private openHandler: LobbySocket[\"onopen\"] = null;\n /** OPEN, but `onopen` has not run yet for any handler. */\n private openPending = false;\n onmessage: LobbySocket[\"onmessage\"] = null;\n onclose: LobbySocket[\"onclose\"] = null;\n onerror: LobbySocket[\"onerror\"] = null;\n /** Unsubscribes from the lobby; set by the factory. */\n detach: () => void = () => {};\n\n constructor(\n readonly peerId: number,\n private readonly out: (bytes: Uint8Array) => void,\n private readonly binary: boolean,\n private readonly onLocalClose: () => void,\n ) {}\n\n send(data: string | Uint8Array): void {\n if (this.readyState !== 1) return;\n this.out(typeof data === \"string\" ? enc.encode(data) : data);\n }\n\n close(code = 1000, reason = \"left\"): void {\n if (this.readyState === 3) return;\n this.closed(code, reason);\n this.onLocalClose();\n }\n\n get onopen(): LobbySocket[\"onopen\"] {\n return this.openHandler;\n }\n\n set onopen(h: LobbySocket[\"onopen\"]) {\n this.openHandler = h;\n // assigned after the open event went by with no handler: deliver it once, still on a later task\n if (h && this.openPending) nextTask(() => this.fireOpen());\n }\n\n /**\n * @internal OPEN now; `onopen` now, or on a later task (like a real WebSocket) so a handler assigned\n * after any number of awaits in the same task still runs.\n */\n open(later = false): void {\n if (this.readyState !== 0) return;\n this.readyState = 1;\n this.openPending = true;\n if (!later) this.fireOpen();\n else nextTask(() => this.fireOpen());\n }\n\n private fireOpen(): void {\n if (this.readyState !== 1 || !this.openPending || !this.openHandler) return;\n this.openPending = false;\n this.openHandler({ type: \"open\" });\n }\n\n /** @internal */\n deliver(bytes: Uint8Array): void {\n if (this.readyState === 1) this.onmessage?.({ type: \"message\", data: this.binary ? bytes : dec.decode(bytes) });\n }\n\n /** @internal */\n closed(code: number, reason: string): void {\n if (this.readyState === 3) return;\n this.readyState = 3;\n this.detach();\n this.onclose?.({ type: \"close\", code, reason, wasClean: code === 1000 || code === 1001 });\n }\n}\n\n/**\n * Joiner: the link to the host as a socket. OPEN once `lobby.ready` resolves (at once if it already has), with\n * `onopen` on a later task;\n * closed when the session ends, or with 1001 \"host_changed\" when a server-backed lobby migrates to a new host.\n */\nexport function joinerSocket(lobby: Lobby, o: LobbySocketOptions = {}): LobbySocket {\n if (lobby.isHost) throw new Error(\"asWebSocket() is for joiners; the host uses lobby.onConnection()\");\n const channel = o.channel ?? \"reliable\";\n const s = new PeerSocket(lobby.hostPeerId, (b) => lobby.send(lobby.hostPeerId, b, { channel }), !!o.binary, () => void lobby.leave());\n const offs = [\n lobby.on(\"message\", (from, bytes) => {\n if (from === lobby.hostPeerId) s.deliver(bytes);\n }),\n lobby.on(\"sessionEnded\", (reason, close?: CloseInfo) => s.closed(close?.code ?? closeCodeFor(reason), close?.reason ?? reason)),\n // The socket's far end is the host it opened to; after a migration that server is gone (direct lobbies end instead).\n lobby.on(\"hostChanged\", () => s.closed(1001, \"host_changed\")),\n ];\n s.detach = () => offs.forEach((f) => f());\n // Already connected (made after `await lobby.ready`): OPEN at once, so a send() right away goes out.\n if (lobby.connectedPeers.includes(lobby.hostPeerId)) s.open(true);\n else lobby.ready.then(() => s.open(true), () => {});\n return s;\n}\n\n/**\n * Host: calls `handler(conn)` once per joiner, as soon as its link is ready (and right away for joiners already\n * connected). A message from a joiner the handler has not seen yet creates its connection first, so nothing is lost.\n * `conn.onclose` fires when that peer leaves (code from closeCodeFor) or the host's session ends. Returns a function\n * that stops new connections (existing ones keep working).\n */\nexport function hostConnections(lobby: Lobby, handler: (conn: LobbySocket) => void, o: LobbySocketOptions = {}): () => void {\n if (!lobby.isHost) throw new Error(\"onConnection() is for the host; a joiner uses lobby.asWebSocket()\");\n const channel = o.channel ?? \"reliable\";\n const conns = new Map<number, PeerSocket>();\n let stopped = false;\n const ensure = (peerId: number): PeerSocket | undefined => {\n if (peerId === lobby.localPeerId) return undefined;\n let c = conns.get(peerId);\n if (c || stopped) return c;\n c = new PeerSocket(peerId, (b) => lobby.send(peerId, b, { channel }), !!o.binary, () => {});\n const conn = c;\n conn.detach = () => {\n if (conns.get(peerId) === conn) conns.delete(peerId);\n };\n conns.set(peerId, conn);\n conn.open();\n handler(conn);\n return conn;\n };\n const offs = [\n lobby.on(\"peerReady\", (p: Peer) => void ensure(p.peerId)),\n lobby.on(\"message\", (from, bytes) => ensure(from)?.deliver(bytes)),\n lobby.on(\"peerLeft\", (p: Peer, reason, close?: CloseInfo) =>\n conns.get(p.peerId)?.closed(close?.code ?? (reason === \"left\" ? 1001 : closeCodeFor(reason)), close?.reason ?? reason),\n ),\n lobby.on(\"sessionEnded\", (reason) => {\n for (const c of [...conns.values()]) c.closed(reason === \"left\" ? 1001 : closeCodeFor(reason), reason);\n offs.forEach((f) => f());\n }),\n ];\n for (const id of lobby.connectedPeers) ensure(id);\n return () => {\n stopped = true;\n };\n}\n", "import { Chat } from \"./chat.js\";\nimport type { IpCodeKind, JoinCodeKind } from \"./join-code-kind.js\";\nimport type { Clock } from \"./clock.js\";\nimport {\n CodecError,\n MAX_FRAME_BYTES,\n MODE_LOCKSTEP,\n MODE_RAW,\n PROTO,\n decodeMessage,\n encodeMessage,\n type Message,\n} from \"./codec.js\";\nimport { Reassembler, splitChunks } from \"./chunking.js\";\nimport { Emitter } from \"./emitter.js\";\nimport type { Hub } from \"./hub.js\";\nimport { Lockstep } from \"./lockstep.js\";\nimport { claimWinsWrapped, pickNewHost } from \"./migration.js\";\nimport { Pause } from \"./pause.js\";\nimport { OneLobbyError, type LobbyState, type PeerInfo, type SignalingClient, type ServerMsg } from \"./signaling.js\";\nimport type { ChannelName, IceServer, LinkFactory, PeerLink, SignalPayload } from \"./transport.js\";\nimport { Voice, type VoiceDeps } from \"./voice.js\";\nimport { hostConnections, joinerSocket, type LobbySocket, type LobbySocketOptions } from \"./socket.js\";\n\nexport type Phase = \"lobby\" | \"in_game\";\n/** The close code and reason a host chose with `kick()` / `disconnect()`, passed to the removed player's sockets. */\nexport interface CloseInfo {\n code?: number;\n reason?: string;\n}\n/** Options for `lobby.kick()`. */\nexport interface KickOptions {\n /** Default true: the player's address cannot rejoin this lobby while it lasts. false = `disconnect()`. */\n ban?: boolean;\n /** Close code for the player's `asWebSocket()` onclose: 1000 or 3000-4999. */\n code?: number;\n /** Close reason for that onclose, at most 120 characters. */\n reason?: string;\n}\nexport type Peer = PeerInfo;\n\n/** What a direct host's `joinCode` is: see {@link Lobby.joinCodeKind}. */\nexport type { JoinCodeKind };\n\nexport type LobbyEvents = {\n peerJoined: [Peer];\n /** The peer's data channels are open and the handshake is done: reliable sends reach it now (earlier ones were queued and flushed). */\n peerReady: [Peer];\n /** Joiner: the link to the host is open (the host itself is ready from the start). Also available as `lobby.ready`. */\n ready: [];\n /** reason \"left\", \"kicked\", \"disconnected\" (kick with ban false), \"timeout\", ...; `close` when the host gave one. */\n peerLeft: [Peer, reason: string, close?: CloseInfo];\n /**\n * Host only: a joiner never got its link to the host open (\"a friend could not connect\"). reason \"timeout\" when it is\n * not ready `readyTimeoutMs` after joining (it may still connect: `peerReady` then fires as usual), else the\n * `peerLeft` reason when it leaves before it was ready (a joiner's own `ice_failed` ends that way). Fires at most once\n * per peer, just before that `peerLeft`; not for peers the host kicks or disconnects.\n */\n peerFailed: [Peer, reason: string];\n joinRequested: [Peer];\n hostChanged: [hostId: number];\n phase: [Phase];\n message: [from: number, bytes: Uint8Array, channel: ChannelName];\n /** `close` is the code and reason the host gave when it removed this player (`kick()` / `disconnect()`). */\n sessionEnded: [reason: string, close?: CloseInfo];\n /** The signaling server went away. The game continues on the data channels (s.9). */\n serverLost: [];\n metaChanged: [Record<string, string>];\n /**\n * Direct-IP host only: the join code changed after `hostDirect` resolved, e.g. the router answered a later port-mapping\n * retry and the code went from \"same_network\" to \"internet\". Show the new code (and `joinUrl`).\n */\n joinCodeChanged: [code: string, kind: JoinCodeKind];\n /**\n * Browser host only (`kit.hostNostr`): the host tab went hidden (`true`) or visible again (`false`). Browsers throttle\n * timers in hidden tabs, so joiners may see lag or drop; ask the host to keep the tab visible.\n */\n hostHiddenChanged: [hidden: boolean];\n};\n\nexport interface LobbyConfig {\n signaling: SignalingClient;\n state: LobbyState;\n ice: IceServer[];\n links: LinkFactory;\n clock: Clock;\n wall: () => number;\n hashEveryTicks: number;\n relayOnly: boolean;\n voice: VoiceDeps;\n displayName: string;\n /**\n * Joiner: give up on reaching the host after this many ms (0 or absent: wait forever). When the ICE list has a\n * TURN server and the lobby is not relay-only already, the second half of the budget is a relay-only retry.\n * On failure `ready` rejects with OneLobbyError(\"ice_failed\"), the error is reported and the lobby is left.\n */\n readyTimeoutMs?: number;\n /**\n * Direct IP (docs/design/direct-ip.md): the hub lives in the host, so there is no migration. A joiner that loses the host\n * (link, peer_left, a host_changed from the hub as it shuts down, or the signaling socket) ends with \"host_left\".\n */\n direct?: boolean;\n /**\n * Treat a peer as gone after this many ms without hearing anything from it (0 or absent: off, and a silent peer\n * surfaces only when its link or signaling socket times out, about 30 s). Joiners ping the host every 2 s and the\n * host answers, so the host drops a silent joiner (`peerLeft`, \"timeout\") and a joiner handles a silent host as\n * lost (a direct lobby ends with \"host_left\"). Values under 6000 are raised to 6000.\n */\n peerTimeoutMs?: number;\n /** Host: per-peer cap on incoming raw messages; the excess is dropped before `message` fires or is relayed. */\n rateLimit?: RateLimit;\n}\n\n/** At most `messages` raw messages per peer in any `perMs` window (a fixed window, reset every `perMs`). */\nexport interface RateLimit {\n messages: number;\n perMs: number;\n}\n\n/** The smallest `peerTimeoutMs` honoured: three missed pings. */\nexport const MIN_PEER_TIMEOUT_MS = 6000;\n/** Host: how long a kicked / disconnected peer's link stays open for the hub's notice to reach it first. */\nexport const KICK_LINGER_MS = 3000;\n/** How long an offer held during a possible migration stays answerable. */\nconst HELD_OFFER_MS = 20_000;\n\n/** The name shown for a peer that chose none: \"Player N\", N = its seat (peer id, join order). */\nexport function seatName(p: PeerInfo): PeerInfo {\n const n = typeof p.name === \"string\" ? p.name.trim() : \"\";\n // \"Player\" is onelobby-server's stand-in for an empty name\n if (n && n !== \"Player\") return p;\n return { ...p, name: `Player ${p.peerId}` };\n}\n\nfunction hasRelay(ice: IceServer[]): boolean {\n return ice.some((s) => (Array.isArray(s.urls) ? s.urls : [s.urls]).some((u) => /^turns?:/i.test(String(u))));\n}\n\nconst HOST_WATCHDOG_MS = 250;\nconst PING_EVERY_MS = 2000;\n\n/** One joined lobby: roster, star of peer links, and the lockstep/chat/voice engines riding on them. */\nexport class Lobby extends Emitter<LobbyEvents> {\n readonly code: string;\n readonly mode: \"lockstep\" | \"raw\";\n readonly tickRate: number;\n readonly lockstep: Lockstep;\n readonly chat: Chat;\n /** The shared co-op pause (docs/recipes/pause.md). */\n readonly pause: Pause;\n readonly voice: Voice;\n\n localPeerId: number;\n hostPeerId: number;\n phase: Phase;\n meta: Record<string, string>;\n /** The lobby's name as the server keeps it (host()'s `name`, sanitised); \"\" if the server sends none. */\n name: string;\n resumeToken: string;\n /** Last measured round trip to the host in ms (joiners), or 0. */\n rtt = 0;\n /**\n * Direct-IP host only (onelobby/direct): the code friends paste to join, e.g. \"6B01-RGFE-P2B\" (docs/design/direct-ip.md\n * s.2a). Show it with joinCodeKind. Empty when the machine has no IPv4 address, and on joiners.\n */\n joinCode = \"\";\n /**\n * \"nostr\": a version 1 code that works through public Nostr relays (spec/nostr-signaling.md; the default for a direct\n * host). \"internet\": the router forwarded the port and the code carries its public address; \"same_network\": the LAN\n * address. \"none\": no code.\n */\n joinCodeKind: JoinCodeKind = \"none\";\n /**\n * Direct-IP host only: the IP code (address and port, version 0), with `directCodeKind` \"internet\" or \"same_network\".\n * Equal to `joinCode` when Nostr is off; otherwise the code to hand out for LAN play or when the router port is open.\n */\n directCode = \"\";\n directCodeKind: IpCodeKind = \"none\";\n /** Direct-IP host only: this machine's addresses with the port, for an \"Advanced\" fold-out. */\n directAddresses: string[] = [];\n /** Direct-IP host only: how the router port-open went (\"UPnP\", \"no UPnP gateway answered; ...\", \"off\"). */\n portMapping = \"\";\n /** Direct-IP host only: the one startup line (bound port, join code, port mapping); see `formatHostLog` in onelobby/direct. */\n directHostLog = \"\";\n\n /** Browser host only (`kit.hostNostr`): true while the host tab is hidden (see the `hostHiddenChanged` event). */\n hostHidden = false;\n\n /** @internal kit.hostNostr: the tab's visibility changed. */\n setHostHidden(hidden: boolean): void {\n if (this.hostHidden === hidden) return;\n this.hostHidden = hidden;\n this.emit(\"hostHiddenChanged\", hidden);\n }\n\n /** @internal onelobby/direct: fires `joinCodeChanged` with the current code. */\n notifyJoinCodeChanged(): void {\n this.emit(\"joinCodeChanged\", this.joinCode, this.joinCodeKind);\n }\n\n private signaling: SignalingClient;\n private cfg: LobbyConfig;\n private roster = new Map<number, Peer>();\n private links = new Map<number, PeerLink>();\n /** ICE from a peer that has no link yet: its candidates can overtake its offer (node-datachannel). */\n private earlyIce = new Map<number, SignalPayload[]>();\n /** Offers from roster members that arrived while the host link was open (see offerVerdict), by sender. */\n private heldOffers = new Map<number, { payload: SignalPayload; at: number }>();\n private connected = new Set<number>(); // host: peers that said hi\n private hiSentTo = new Set<PeerLink>();\n private epoch = 0;\n private ended = false;\n private serverUp = true;\n private cancelWatchdog: (() => void) | null = null;\n private cancelPing: (() => void) | null = null;\n private pingN = 0;\n private chunkId = 1;\n private reassemblers = new Map<number, Reassembler>();\n private hub: Hub;\n private outbox = new Map<number, { msg: Message; route: number; channel?: ChannelName }[]>();\n private markReady: () => void = () => {};\n private failReady: (e: Error) => void = () => {};\n private isReady = false;\n private cancelReadyTimer: (() => void) | null = null;\n private relayRetry = false;\n /** clock.now() of the last frame from each peer (peerTimeoutMs). */\n private lastHeard = new Map<number, number>();\n /** Host: links of kicked / disconnected peers, kept open briefly (removePeer `linger`). */\n private readonly lingering = new Set<PeerLink>();\n /** Host: joiners that joined but are not ready yet, with the cancel of their readyTimeoutMs timer (peerFailed). */\n private notReady = new Map<number, () => void>();\n /** Host: per-peer fixed window for rateLimit. */\n private rate = new Map<number, { start: number; n: number }>();\n /**\n * Resolves when this peer can exchange data: immediately for the host, once the link to the host is open for a\n * joiner. Rejects with OneLobbyError(\"ice_failed\") when the joiner cannot reach the host in `readyTimeoutMs`\n * (or with \"closed\"/\"kicked\" when the lobby ends first; \"closed\" also when the host disconnected it).\n */\n readonly ready: Promise<void> = new Promise<void>((res, rej) => {\n this.markReady = () => {\n if (this.isReady) return;\n this.isReady = true;\n this.cancelReadyTimer?.();\n this.cancelReadyTimer = null;\n res();\n };\n this.failReady = rej;\n });\n\n constructor(cfg: LobbyConfig) {\n super();\n this.cfg = cfg;\n this.signaling = cfg.signaling;\n const st = cfg.state;\n this.code = st.code;\n this.mode = st.mode;\n this.tickRate = st.tickRate;\n this.localPeerId = st.you;\n this.hostPeerId = st.hostId;\n this.phase = st.phase;\n this.meta = { ...st.meta };\n this.name = st.name ?? \"\";\n this.resumeToken = st.resumeToken;\n for (const p of st.peers) this.roster.set(p.peerId, seatName(p));\n this.ready.catch(() => {}); // a rejection is also reported through errorSink; awaiting `ready` stays optional\n\n const self = this;\n this.hub = {\n get clock() { return self.cfg.clock; },\n get localPeerId() { return self.localPeerId; },\n get hostPeerId() { return self.hostPeerId; },\n get isHost() { return self.isHost; },\n get tickRate() { return self.tickRate; },\n connectedPeers: () => self.connectedPeerIds(),\n sendTo: (peerId, msg, route, channel) => self.sendTo(peerId, msg, route ?? 0, channel),\n sendToHost: (msg, route, channel) => {\n if (!self.isHost) self.sendTo(self.hostPeerId, msg, route ?? 0, channel);\n },\n dropPeer: (peerId, reason) => self.dropPeer(peerId, reason),\n reportError: (e) => self.reportError(e),\n nextChunkId: () => self.chunkId++ >>> 0,\n };\n\n this.lockstep = new Lockstep(this.hub, { hashEveryTicks: cfg.hashEveryTicks });\n this.chat = new Chat(this.hub, cfg.wall, (id) => this.voice.isMutedPeer(id));\n this.voice = new Voice(this.hub, cfg.voice);\n this.pause = new Pause(this.hub, {\n phase: () => this.phase,\n hold: (held) => this.lockstep.hold(held),\n hasPeer: (id) => this.roster.has(id),\n });\n this.lockstep.on(\"started\", () => this.setPhase(\"in_game\"));\n this.lockstep.on(\"error\", (e) => this.reportError(e));\n\n this.signaling.onMessage = (m) => this.onSignaling(m);\n this.signaling.onClose = () => this.onServerClosed();\n\n if (this.isHost) this.markReady();\n else this.armReadyTimer();\n if (this.isHost && this.mode === \"lockstep\") {\n if (this.phase === \"in_game\") this.lockstep.hostStart();\n else this.lockstep.hostIdle();\n }\n this.cancelWatchdog = cfg.clock.setInterval(() => this.watchdog(), HOST_WATCHDOG_MS);\n this.cancelPing = cfg.clock.setInterval(() => this.pingHost(), PING_EVERY_MS);\n }\n\n // ---- state -----------------------------------------------------------------------------------------------------\n\n get isHost(): boolean {\n return this.localPeerId === this.hostPeerId;\n }\n get peers(): Peer[] {\n return [...this.roster.values()].sort((a, b) => a.peerId - b.peerId);\n }\n peer(peerId: number): Peer | undefined {\n return this.roster.get(peerId);\n }\n /** Peers whose link is open and handshake done (host: the ready joiners; joiner: the host once ready). */\n get connectedPeers(): number[] {\n return this.connectedPeerIds();\n }\n\n /**\n * Joiner: the link to the host as a WebSocket-shaped object (text by default), so WebSocket client code runs\n * unchanged. It opens when `ready` resolves and closes when the session ends (`host_left` gives code 1001).\n */\n asWebSocket(opts?: LobbySocketOptions): LobbySocket {\n return joinerSocket(this, opts);\n }\n\n /** Host: `handler(conn)` once per joiner (a WebSocket-shaped connection), the server-side mirror of asWebSocket(). */\n onConnection(handler: (conn: LobbySocket) => void, opts?: LobbySocketOptions): () => void {\n return hostConnections(this, handler, opts);\n }\n\n set snapshotChunkSize(n: number) {\n this.lockstep.chunkSize = n;\n }\n\n /** Current migration epoch (0 until the first host change). */\n get hostEpoch(): number {\n return this.epoch;\n }\n\n /** @internal Host role: create the link to `peerId` and start negotiating (adapters and tests). */\n _offer(peerId: number): PeerLink {\n this.offerTo(peerId);\n return this.links.get(peerId)!;\n }\n\n /** @internal Joiner role: create the answering link to `peerId` (adapters and tests). */\n _answer(peerId: number): PeerLink {\n return this.makeLink(peerId);\n }\n\n /** @internal Called by OneLobby right after construction: the host waits for joiners, a joiner waits for the host's offer. */\n _begin(): void {\n /* joiners are passive until the host offers; hosts create links on peer_joined */\n }\n\n // ---- game-facing actions ---------------------------------------------------------------------------------------\n\n async start(): Promise<void> {\n if (!this.isHost) throw new Error(\"only the host can start the game\");\n if (this.phase === \"in_game\") return;\n this.signaling.send({ t: \"phase\", phase: \"in_game\" });\n this.setPhase(\"in_game\");\n if (this.mode === \"lockstep\") this.lockstep.hostStart();\n }\n\n async setMeta(meta: Record<string, string>): Promise<void> {\n if (!this.isHost) throw new Error(\"only the host can set metadata\");\n this.meta = { ...meta };\n this.signaling.send({ t: \"meta\", meta });\n this.emit(\"metaChanged\", this.meta);\n }\n\n /**\n * Host: removes a player. By default it is a ban: the hub refuses that player's address for the rest of this lobby\n * (a new tab or a reload included). `{ ban: false }` is `disconnect()`. `code` / `reason` reach the player's\n * `asWebSocket()` onclose (and `sessionEnded`'s second argument); without them its socket closes with 4000 \"kicked\".\n */\n async kick(peerId: number, opts: KickOptions = {}): Promise<void> {\n if (!this.isHost) throw new Error(\"only the host can kick\");\n const { ban = true, code, reason } = opts;\n if (code !== undefined && !(code === 1000 || (Number.isInteger(code) && code >= 3000 && code <= 4999)))\n throw new RangeError(\"close code must be 1000 or 3000-4999\");\n if (reason !== undefined && reason.length > 120) throw new RangeError(\"close reason must be at most 120 characters\");\n const msg: Record<string, unknown> = { t: \"kick\", peerId };\n if (!ban) msg.ban = false;\n if (code !== undefined) msg.closeCode = code;\n if (reason !== undefined) msg.closeReason = reason;\n this.signaling.send(msg as { t: \"kick\"; peerId: number });\n const close: CloseInfo | undefined = code !== undefined || reason !== undefined ? { code, reason } : undefined;\n this.removePeer(peerId, ban ? \"kicked\" : \"disconnected\", close, true);\n }\n\n /**\n * Host: ends one player's connection without a ban, like a server closing one WebSocket. The player's\n * `asWebSocket()` gets onclose with `code` (default 1000) and `reason` (default \"disconnected\"), its lobby ends with\n * `sessionEnded(\"disconnected\")`, and it may join again (a new tab, a reload). Same as `kick(peerId, { ban: false, code, reason })`.\n */\n disconnect(peerId: number, code?: number, reason?: string): Promise<void> {\n return this.kick(peerId, { ban: false, code, reason });\n }\n\n async approve(peerId: number, ok: boolean): Promise<void> {\n if (!this.isHost) throw new Error(\"only the host can approve joins\");\n this.signaling.send({ t: \"approve\", peerId, ok });\n }\n\n async leave(): Promise<void> {\n if (this.ended) return;\n this.signaling.send({ t: \"leave\" });\n this.teardown(\"left\");\n }\n\n /** raw mode (and any extra game data): to one peer. */\n send(peerId: number, bytes: Uint8Array, opts: { channel?: \"reliable\" | \"fast\" } = {}): void {\n const channel = opts.channel ?? \"reliable\";\n if (this.isHost) this.sendTo(peerId, { type: \"raw\", bytes }, this.localPeerId, channel);\n else this.sendTo(this.hostPeerId, { type: \"raw\", bytes }, peerId, channel);\n }\n\n /** To everyone else in the lobby. */\n broadcast(bytes: Uint8Array, opts: { channel?: \"reliable\" | \"fast\" } = {}): void {\n const channel = opts.channel ?? \"reliable\";\n if (this.isHost) {\n // peers whose channels are not open yet get reliable broadcasts queued and flushed in order\n for (const p of this.roster.keys()) if (p !== this.localPeerId) this.sendTo(p, { type: \"raw\", bytes }, this.localPeerId, channel);\n } else {\n this.sendTo(this.hostPeerId, { type: \"raw\", bytes }, 0, channel);\n }\n }\n\n // ---- sending ---------------------------------------------------------------------------------------------------\n\n private channelFor(msg: Message): ChannelName {\n if (msg.type === \"voice\") return \"voice\";\n if (msg.type === \"ping\" || msg.type === \"pong\") return \"fast\";\n return \"reliable\";\n }\n\n private sendTo(peerId: number, msg: Message, route: number, channel?: ChannelName): void {\n const link = this.links.get(peerId);\n const ch0 = channel ?? this.channelFor(msg);\n const queueable = msg.type === \"raw\" || msg.type === \"chat\" || msg.type === \"cmd\";\n // game data to a host-side peer waits for its `hi`; protocol messages only need the link\n const ready = !!link?.isOpen && (!queueable || !this.isHost || this.connected.has(peerId));\n if (!ready) {\n // Reliable game data for a peer that is in the roster (or is the host) but not connected yet waits for its handshake.\n const known = this.roster.has(peerId) || peerId === this.hostPeerId;\n if (queueable && ch0 === \"reliable\" && known && !this.ended) {\n let q = this.outbox.get(peerId);\n if (!q) this.outbox.set(peerId, (q = []));\n if (q.length >= 1000) q.shift();\n q.push({ msg, route, channel });\n }\n return;\n }\n if (!link) return;\n try {\n const ch = ch0;\n let bytes: Uint8Array;\n try {\n bytes = encodeMessage(msg, route);\n } catch (e) {\n if (!(e instanceof CodecError) || e.code !== \"too_long\") throw e;\n bytes = encodeMessage(msg, route, true);\n }\n if (bytes.length > MAX_FRAME_BYTES) {\n // Oversized frame: send the whole inner frame as a chunk set (spec/binary.md \"Large non-snapshot frames\").\n for (const c of splitChunks(this.chunkId++ >>> 0, bytes)) link.send(ch, encodeMessage(c, route));\n } else {\n link.send(ch, bytes);\n }\n } catch (e) {\n this.reportError(e instanceof Error ? e : new Error(String(e)));\n }\n }\n\n private connectedPeerIds(): number[] {\n if (this.isHost) return [...this.connected].filter((id) => this.links.get(id)?.isOpen).sort((a, b) => a - b);\n const l = this.links.get(this.hostPeerId);\n return l?.isOpen ? [this.hostPeerId] : [];\n }\n\n private reportError(e: Error): void {\n this.errorSink?.(e);\n }\n /** @internal set by OneLobby so lobby errors reach `kit.on(\"error\")`. */\n errorSink: ((e: Error) => void) | null = null;\n /** @internal set by OneLobby: the server issued a new resume token (rememberSeat). */\n seatSink: ((token: string) => void) | null = null;\n\n // ---- joiner connect timeout -------------------------------------------------------------------------------------\n\n private armReadyTimer(): void {\n const total = this.cfg.readyTimeoutMs ?? 0;\n if (!(total > 0)) return;\n const canRelay = !this.cfg.relayOnly && hasRelay(this.cfg.ice);\n const first = canRelay ? Math.ceil(total / 2) : total;\n this.cancelReadyTimer = this.cfg.clock.setTimeout(() => {\n if (this.isReady || this.ended) return;\n if (canRelay) this.retryRelayed(total - first);\n else this.giveUpConnecting();\n }, first);\n }\n\n /** The direct attempt stalled: dial the host ourselves through the TURN relay (a host answers any offer). */\n private retryRelayed(budget: number): void {\n this.relayRetry = true;\n const link = this.makeLink(this.hostPeerId, true);\n link.startOffer();\n this.cancelReadyTimer = this.cfg.clock.setTimeout(() => {\n if (!this.isReady && !this.ended) this.giveUpConnecting();\n }, budget);\n }\n\n private giveUpConnecting(): void {\n const err = new OneLobbyError(\"ice_failed\", `could not open a connection to the host${this.relayRetry ? \" (direct or relayed)\" : \"\"}`);\n this.failReady(err);\n this.reportError(err);\n if (this.ended) return;\n this.signaling.send({ t: \"leave\" });\n this.teardown(\"ice_failed\");\n }\n\n // ---- links -----------------------------------------------------------------------------------------------------\n\n /** @internal A direct host's hub learns where each joiner's link comes from (Nostr kick bans the address too). */\n linkAddressSink: ((peerId: number, ip: string) => void) | null = null;\n\n private makeLink(peerId: number, relayOnly = this.cfg.relayOnly): PeerLink {\n const old = this.links.get(peerId);\n if (old) {\n old.onClose = null;\n old.onMessage = null;\n old.close();\n }\n const link = this.cfg.links.create({ remoteId: peerId, iceServers: this.cfg.ice, relayOnly });\n this.links.set(peerId, link);\n link.onSignal = (s) => this.signaling.send({ t: \"signal\", to: peerId, ...s });\n link.onOpen = () => this.onLinkOpen(peerId, link);\n link.onClose = (reason) => this.onLinkClosed(peerId, link, reason);\n link.onMessage = (ch, bytes) => this.onData(peerId, ch, bytes);\n link.onPath = (path) => this.signaling.send({ t: \"path\", path });\n link.onRemoteAddress = (ip) => this.linkAddressSink?.(peerId, ip);\n return link;\n }\n\n /** Host side: offer to a joiner. */\n private offerTo(peerId: number): void {\n const link = this.makeLink(peerId);\n link.startOffer();\n }\n\n private onLinkOpen(peerId: number, link: PeerLink): void {\n if (this.isHost) {\n // Joiner speaks first (`hi`). After a migration the new host announces itself so the joiner knows who won.\n if (this.epoch > 0) this.sendTo(peerId, { type: \"host_claim\", epoch: this.epoch, hostId: this.localPeerId, tick: this.lockstep.lastTick }, 0);\n return;\n }\n if (peerId === this.hostPeerId) this.sendHi(link);\n // else: an unexpected offerer; wait for its host_claim before trusting it\n }\n\n private sendHi(link: PeerLink): void {\n if (this.hiSentTo.has(link)) return;\n this.hiSentTo.add(link);\n this.lockstep.touch();\n link.send(\"reliable\", encodeMessage(this.lockstep.hiMessage(this.localPeerId)));\n this.markReady();\n this.emit(\"ready\");\n this.emit(\"peerReady\", this.roster.get(link.remoteId) ?? { peerId: link.remoteId, name: \"\" });\n this.flush(link.remoteId);\n }\n\n private onLinkClosed(peerId: number, link: PeerLink, _reason: string): void {\n if (this.links.get(peerId) !== link) return; // replaced\n this.links.delete(peerId);\n this.hiSentTo.delete(link);\n if (this.ended) return;\n if (this.isHost) {\n this.connected.delete(peerId);\n this.lockstep.onPeerLeft(peerId);\n this.voice.onPeerLeft(peerId);\n // The server announces the departure; if it is gone, drop the peer ourselves.\n if (!this.serverUp && this.roster.has(peerId)) this.removePeer(peerId, \"timeout\");\n } else if (peerId === this.hostPeerId) {\n // Never connected yet (ICE failed): not a host loss. The ready timer retries over the relay or gives up.\n if (!this.isReady && this.cancelReadyTimer) return;\n this.handleHostLost(\"link closed\");\n }\n }\n\n // ---- data channel dispatch -------------------------------------------------------------------------------------\n\n private onData(from: number, channel: ChannelName, bytes: Uint8Array, reassembled = false): void {\n let d;\n try {\n d = decodeMessage(bytes, reassembled);\n } catch (e) {\n this.reportError(e instanceof Error ? e : new Error(String(e)));\n return;\n }\n if (!d) return;\n // The topology is a star: a joiner hears only its host. Anything else over some other link (a peer that offered to\n // us, or a member that dialled us) is not host traffic and must not reach raw / lockstep / pause / chat handling.\n if (!this.isHost && from !== this.hostPeerId && d.msg.type !== \"host_claim\") return;\n if (!reassembled) this.lastHeard.set(from, this.cfg.clock.now());\n const { msg, route } = d;\n switch (msg.type) {\n case \"hi\":\n if (this.isHost) this.onHi(from, msg.lastTick);\n break;\n case \"welcome_dc\":\n if (!this.isHost) {\n if (msg.hostId !== this.hostPeerId) this.setHost(msg.hostId);\n this.lockstep.touch();\n }\n break;\n case \"cmd\": case \"frame\": case \"hash\": case \"desync\":\n case \"snapshot_req\": case \"snapshot\":\n if (from === this.hostPeerId || this.isHost) this.lockstep.handle(from, msg);\n break;\n case \"chunk\": {\n if (reassembled) break; // a chunk set never nests another\n let r = this.reassemblers.get(from);\n if (!r) this.reassemblers.set(from, (r = new Reassembler()));\n let whole;\n try {\n whole = r.push(msg);\n } catch (e) {\n this.reportError(e instanceof Error ? e : new Error(String(e)));\n break;\n }\n if (!whole) break;\n if (this.lockstep.snapshotPending && from === this.hostPeerId) this.lockstep.onSnapshotPayload(whole);\n else this.onData(from, channel, whole, true); // an oversized inner frame\n break;\n }\n case \"chat\": case \"chat_history\": case \"chat_reject\":\n this.chat.handle(from, route, msg);\n break;\n case \"pause_request\": case \"pause_state\": case \"pause_refuse\":\n this.pause.handle(from, msg);\n break;\n case \"voice\":\n if (channel === \"voice\") this.voice.handle(from, route, msg); // a voice frame on another channel is ignored\n break;\n case \"raw\":\n this.onRaw(from, route, msg.bytes, channel);\n break;\n case \"ping\":\n this.sendTo(from, { type: \"pong\", n: msg.n, t: msg.t }, 0, \"fast\");\n break;\n case \"pong\":\n this.rtt = Math.max(0, (this.cfg.clock.now() >>> 0) - msg.t);\n break;\n case \"host_claim\":\n this.onHostClaim(from, msg.epoch, msg.hostId);\n break;\n }\n }\n\n private onHi(from: number, lastTick: number): void {\n this.connected.add(from);\n this.notReady.get(from)?.();\n this.notReady.delete(from);\n this.sendTo(from, {\n type: \"welcome_dc\",\n // the tick the snapshot will carry (the game's applied tick), so welcome_dc.tick == snapshot.tick even when\n // the game loop has not yet consumed every emitted frame; 0 before the game starts\n tick: this.mode === \"lockstep\" ? this.lockstep.currentTick : 0,\n tickRate: this.tickRate,\n mode: this.mode === \"lockstep\" ? MODE_LOCKSTEP : MODE_RAW,\n hostId: this.localPeerId,\n }, 0);\n this.chat.sendHistory(from);\n this.pause.sendState(from);\n if (this.mode === \"lockstep\") this.lockstep.onPeerHi(from, lastTick);\n this.emit(\"peerReady\", this.roster.get(from) ?? { peerId: from, name: \"\" });\n this.flush(from);\n }\n\n private flush(peerId: number): void {\n const q = this.outbox.get(peerId);\n if (!q) return;\n this.outbox.delete(peerId);\n for (const m of q) this.sendTo(peerId, m.msg, m.route, m.channel);\n }\n\n private onRaw(from: number, route: number, bytes: Uint8Array, channel: ChannelName): void {\n if (!this.isHost) {\n this.emit(\"message\", route, bytes, channel);\n return;\n }\n if (!this.allowRaw(from)) return;\n if (route === 0 || route === this.localPeerId) {\n this.emit(\"message\", from, bytes, channel);\n }\n if (route === 0) {\n for (const p of this.connectedPeerIds()) if (p !== from) this.sendTo(p, { type: \"raw\", bytes }, from, channel);\n } else if (route !== this.localPeerId) {\n this.sendTo(route, { type: \"raw\", bytes }, from, channel);\n }\n }\n\n private allowRaw(from: number): boolean {\n const lim = this.cfg.rateLimit;\n if (!lim || !(lim.messages > 0) || !(lim.perMs > 0)) return true;\n const now = this.cfg.clock.now();\n let w = this.rate.get(from);\n if (!w || now - w.start >= lim.perMs) this.rate.set(from, (w = { start: now, n: 0 }));\n return ++w.n <= lim.messages;\n }\n\n // ---- migration -------------------------------------------------------------------------------------------------\n\n private setHost(hostId: number): void {\n if (hostId === this.hostPeerId) return;\n this.hostPeerId = hostId;\n this.emit(\"hostChanged\", hostId);\n const held = this.heldOffers.get(hostId);\n if (held) {\n this.heldOffers.delete(hostId);\n if (this.cfg.clock.now() - held.at <= HELD_OFFER_MS) void this.acceptOffer(hostId, held.payload);\n }\n }\n\n /** The host is gone (link closed, no frames for 3 s, or the server said so): run the s.9 rule. */\n private handleHostLost(reason: string): void {\n if (this.isHost || this.ended) return;\n if (this.cfg.direct) {\n this.teardown(\"host_left\");\n return;\n }\n const failed = this.hostPeerId;\n const gone = this.roster.get(failed);\n if (gone) {\n this.roster.delete(failed);\n this.emit(\"peerLeft\", gone, reason === \"link closed\" ? \"timeout\" : reason);\n }\n const old = this.links.get(failed);\n if (old) {\n old.onClose = null;\n old.onMessage = null;\n old.close();\n this.links.delete(failed);\n this.hiSentTo.delete(old);\n }\n const candidate = pickNewHost([...this.roster.keys(), this.localPeerId], failed) ?? this.localPeerId;\n if (candidate === this.localPeerId) this.becomeHost();\n // else: wait for the candidate's host_claim (or the server's host_changed); it offers to us and we answer with hi\n this.replayHeldOffers();\n }\n\n /** Offers that came from roster members while the host link was still open: now the host is lost, answer them. */\n private replayHeldOffers(): void {\n const now = this.cfg.clock.now();\n for (const [from, held] of [...this.heldOffers]) {\n this.heldOffers.delete(from);\n if (now - held.at <= HELD_OFFER_MS && this.roster.has(from)) void this.acceptOffer(from, held.payload);\n }\n }\n\n private becomeHost(): void {\n this.epoch = (this.epoch + 1) & 0xffff;\n this.hostPeerId = this.localPeerId;\n this.markReady();\n this.connected.clear();\n this.hiSentTo.clear();\n this.lastHeard.clear(); // joiners spoke to the old host; their clocks start when they say hi to us\n this.lockstep.becomeHost();\n this.pause.onBecameHost();\n this.emit(\"hostChanged\", this.localPeerId);\n if (this.serverUp) this.signaling.send({ t: \"host_changed\", hostId: this.localPeerId, epoch: this.epoch });\n for (const p of this.roster.keys()) {\n if (p !== this.localPeerId) this.offerTo(p);\n }\n }\n\n private onHostClaim(from: number, epoch: number, hostId: number): void {\n if (hostId !== from) return; // a claim is a peer claiming itself; nobody claims for someone else\n const mine = { epoch: this.epoch, hostId: this.hostPeerId };\n const theirs = { epoch, hostId };\n if (hostId === this.hostPeerId && epoch === this.epoch && !this.isHost) {\n const l = this.links.get(from);\n if (l) this.sendHi(l);\n return;\n }\n if (!claimWinsWrapped(theirs, mine)) return; // we hold the better claim\n const wasHost = this.isHost;\n this.epoch = epoch;\n if (wasHost) {\n // We lose: demote. Everyone else will be offered to by the winner.\n for (const [id, l] of this.links) {\n if (id === from) continue;\n l.onClose = null;\n l.onMessage = null;\n l.close();\n this.links.delete(id);\n }\n this.connected.clear();\n this.lockstep.becomeJoiner();\n }\n this.setHost(hostId);\n const link = this.links.get(from);\n if (link && from === hostId) this.sendHi(link);\n }\n\n // ---- signaling -------------------------------------------------------------------------------------------------\n\n private onSignaling(m: ServerMsg): void {\n switch (m.t) {\n case \"peer_joined\": {\n if (!m.peer || this.roster.has((m.peer as Peer).peerId)) return;\n const peer = seatName(m.peer as Peer);\n this.roster.set(peer.peerId, peer);\n this.emit(\"peerJoined\", peer);\n if (this.isHost) {\n this.watchReady(peer.peerId);\n this.offerTo(peer.peerId);\n }\n break;\n }\n case \"peer_left\":\n this.removePeer(Number(m.peerId), String(m.reason ?? \"left\"));\n break;\n case \"host_changed\": {\n if (this.cfg.direct) {\n // the hub promotes a joiner as the host leaves; in direct mode the hub is going with it\n if (!this.isHost) this.teardown(\"host_left\");\n break;\n }\n const id = Number(m.hostId);\n if (typeof m.epoch === \"number\" && m.epoch > this.epoch) this.epoch = m.epoch & 0xffff;\n if (id === this.hostPeerId) break;\n if (id === this.localPeerId && !this.isHost) {\n const failed = this.hostPeerId;\n this.roster.delete(failed);\n this.becomeHost();\n } else {\n this.setHost(id);\n }\n break;\n }\n case \"join_request\":\n this.emit(\"joinRequested\", seatName(m.peer as Peer));\n break;\n case \"lobby\": {\n const st = m as unknown as LobbyState;\n this.meta = { ...st.meta };\n if (st.name) this.name = st.name;\n if (st.resumeToken && st.resumeToken !== this.resumeToken) {\n this.resumeToken = st.resumeToken;\n this.seatSink?.(this.resumeToken);\n }\n this.roster = new Map(st.peers.map((p) => [p.peerId, seatName(p)]));\n if (st.hostId !== this.hostPeerId && !this.isHost) this.setHost(st.hostId);\n this.setPhase(st.phase);\n break;\n }\n case \"meta\":\n this.meta = { ...(m.meta as Record<string, string>) };\n this.emit(\"metaChanged\", this.meta);\n break;\n case \"phase\":\n this.setPhase(m.phase as Phase);\n break;\n case \"signal\":\n void this.onSignal(Number(m.from), m as unknown as SignalPayload);\n break;\n case \"error\":\n if (m.code === \"kicked\") {\n const d = (m.data ?? {}) as { ban?: unknown; closeCode?: unknown; closeReason?: unknown };\n const close: CloseInfo = {};\n if (typeof d.closeCode === \"number\") close.code = d.closeCode;\n if (typeof d.closeReason === \"string\") close.reason = d.closeReason;\n this.teardown(d.ban === false ? \"disconnected\" : \"kicked\", close.code !== undefined || close.reason !== undefined ? close : undefined);\n }\n else this.reportError(new OneLobbyError(String(m.code), String(m.msg ?? \"\"), m.data as Record<string, unknown> | undefined));\n break;\n }\n }\n\n private async onSignal(from: number, s: SignalPayload): Promise<void> {\n let link = this.links.get(from);\n const payload: SignalPayload = {};\n if (s.sdp) payload.sdp = s.sdp;\n if (s.ice) payload.ice = s.ice;\n if (s.sdp?.type === \"offer\") {\n // We are the answerer. An offer comes from the (new) host, so a peer may dial us only as that rule allows.\n const verdict = this.offerVerdict(from);\n if (verdict === \"hold\") {\n if (this.heldOffers.size < 64 || this.heldOffers.has(from)) this.heldOffers.set(from, { payload, at: this.cfg.clock.now() });\n } else if (verdict === \"accept\") await this.acceptOffer(from, payload);\n return;\n }\n if (!link) {\n if (!payload.ice || this.ended || !this.roster.has(from)) return;\n if (this.cfg.direct && from !== this.hostPeerId) return;\n const q = this.earlyIce.get(from) ?? [];\n if (q.length < 64) q.push(payload);\n this.earlyIce.set(from, q);\n return;\n }\n await link.handleSignal(payload);\n }\n\n /**\n * Whether an SDP offer from `from` may be answered. The host (or a joiner's host) always may. Direct lobbies have no\n * migration, so nobody else. In a server lobby a roster member may dial us when we are the host (a claim race decides\n * who wins) or when our own link to the host is gone (the migration window); while that link is open the offer is\n * held, to be answered if the host turns out to be lost a moment later.\n */\n private offerVerdict(from: number): \"accept\" | \"hold\" | \"drop\" {\n if (this.ended || from === this.localPeerId) return \"drop\";\n if (!this.isHost && from === this.hostPeerId) return \"accept\";\n if (this.cfg.direct || !this.roster.has(from)) return \"drop\";\n if (this.isHost) return \"accept\";\n if (!this.isReady) return \"drop\"; // still joining: only the host's own offer is expected\n return this.links.get(this.hostPeerId)?.isOpen ? \"hold\" : \"accept\";\n }\n\n private async acceptOffer(from: number, payload: SignalPayload): Promise<void> {\n const link = this.makeLink(from);\n const early = this.earlyIce.get(from) ?? [];\n this.earlyIce.delete(from);\n // the link holds candidates until the offer is set, so hand them over right behind it\n const done = link.handleSignal(payload);\n for (const e of early) void link.handleSignal(e);\n await done;\n }\n\n /**\n * `linger` (host kick / disconnect): stop using the peer's link at once but leave it open until that peer closes it\n * or KICK_LINGER_MS pass. The hub tells the peer over its WebSocket; closing the data link first would make it look\n * like the host vanished (a migration and a 1001 instead of the host's close code).\n */\n private removePeer(peerId: number, reason: string, close?: CloseInfo, linger = false): void {\n const peer = this.roster.get(peerId);\n if (peerId === this.localPeerId) {\n this.teardown(reason === \"kicked\" ? \"kicked\" : reason);\n return;\n }\n if (peer) this.roster.delete(peerId);\n this.earlyIce.delete(peerId);\n const cancelWatch = this.notReady.get(peerId);\n if (cancelWatch) {\n cancelWatch();\n this.notReady.delete(peerId);\n if (peer && reason !== \"kicked\" && reason !== \"disconnected\") this.emit(\"peerFailed\", peer, reason);\n }\n const link = this.links.get(peerId);\n if (link && peerId !== this.hostPeerId) {\n link.onMessage = null;\n this.links.delete(peerId);\n this.hiSentTo.delete(link);\n if (linger) {\n this.lingering.add(link);\n const done = () => {\n if (!this.lingering.delete(link)) return;\n cancel();\n link.onClose = null;\n link.close();\n };\n const cancel = this.cfg.clock.setTimeout(done, KICK_LINGER_MS);\n link.onClose = done;\n } else {\n link.onClose = null;\n link.close();\n }\n }\n this.connected.delete(peerId);\n this.outbox.delete(peerId);\n this.lastHeard.delete(peerId);\n this.rate.delete(peerId);\n this.lockstep.onPeerLeft(peerId);\n this.chat.onPeerLeft(peerId);\n this.voice.onPeerLeft(peerId);\n this.pause.onPeerLeft(peerId);\n if (peer) this.emit(\"peerLeft\", peer, reason, close);\n if (peerId === this.hostPeerId && !this.isHost) {\n this.handleHostLost(reason);\n }\n }\n\n /** Lockstep drops (stalled, desynced): removed without a ban, so the player can come back (a rejoin resyncs). */\n private dropPeer(peerId: number, reason: string): void {\n this.signaling.send({ t: \"kick\", peerId, ban: false, closeReason: reason } as { t: \"kick\"; peerId: number });\n this.removePeer(peerId, reason, undefined, true);\n }\n\n private setPhase(p: Phase): void {\n if (p !== \"lobby\" && p !== \"in_game\") return;\n if (this.phase === p) return;\n this.phase = p;\n if (p === \"lobby\") this.pause.onLobbyPhase();\n if (p === \"in_game\" && !this.lockstep.started) this.lockstep.markStarted();\n this.emit(\"phase\", p);\n }\n\n private onServerClosed(): void {\n if (this.ended) return;\n if (this.cfg.direct && !this.isHost) {\n this.teardown(\"host_left\");\n return;\n }\n this.serverUp = false;\n this.emit(\"serverLost\");\n // Alone with no peers: nothing keeps this session alive.\n if (this.links.size === 0 && this.roster.size <= 1) this.teardown(\"server_lost\");\n }\n\n // ---- housekeeping ----------------------------------------------------------------------------------------------\n\n private watchdog(): void {\n if (this.ended) return;\n const timeout = this.cfg.peerTimeoutMs ?? 0;\n if (timeout > 0) this.dropSilent(Math.max(timeout, MIN_PEER_TIMEOUT_MS));\n if (this.ended || this.isHost) return;\n if (this.mode === \"lockstep\" && this.lockstep.hostSilent()) {\n this.lockstep.touch();\n this.lockstep.pokeHost(); // a live-but-stalled host answers with a snapshot; a dead one is replaced below\n this.handleHostLost(\"timeout\");\n }\n }\n\n /** peerTimeoutMs: the host drops joiners it has not heard from; a joiner handles a silent host as lost. */\n private dropSilent(timeout: number): void {\n const now = this.cfg.clock.now();\n if (this.isHost) {\n for (const id of [...this.connected]) {\n const last = this.lastHeard.get(id);\n if (last !== undefined && now - last > timeout) this.removePeer(id, \"timeout\");\n }\n } else if (this.isReady) {\n const last = this.lastHeard.get(this.hostPeerId);\n if (last !== undefined && now - last > timeout) {\n this.lastHeard.delete(this.hostPeerId);\n this.handleHostLost(\"timeout\");\n }\n }\n }\n\n private pingHost(): void {\n if (this.ended || this.isHost) return;\n this.sendTo(this.hostPeerId, { type: \"ping\", n: ++this.pingN, t: this.cfg.clock.now() >>> 0 }, 0, \"fast\");\n }\n\n /** Host: report a joiner that is not ready `readyTimeoutMs` after joining as peerFailed(\"timeout\"), once. */\n private watchReady(peerId: number): void {\n const ms = this.cfg.readyTimeoutMs ?? 0;\n let cancel = () => {};\n if (ms > 0) {\n cancel = this.cfg.clock.setTimeout(() => {\n if (!this.notReady.delete(peerId)) return;\n const peer = this.roster.get(peerId);\n if (peer) this.emit(\"peerFailed\", peer, \"timeout\");\n }, ms);\n }\n this.notReady.set(peerId, cancel);\n }\n\n private teardown(reason: string, close?: CloseInfo): void {\n if (this.ended) return;\n this.ended = true;\n this.cancelReadyTimer?.();\n for (const cancel of this.notReady.values()) cancel();\n this.notReady.clear();\n if (!this.isReady) {\n const code = reason === \"ice_failed\" || reason === \"kicked\" ? reason : \"closed\";\n this.failReady(new OneLobbyError(code, `the lobby ended before connecting (${reason})`));\n }\n this.cancelWatchdog?.();\n this.cancelPing?.();\n this.lockstep.stop();\n this.voice.close();\n for (const l of [...this.links.values(), ...this.lingering]) {\n l.onClose = null;\n l.onMessage = null;\n l.close();\n }\n this.links.clear();\n this.earlyIce.clear();\n this.heldOffers.clear();\n this.lingering.clear();\n this.signaling.onClose = null;\n this.signaling.onMessage = null;\n this.signaling.close();\n this.emit(\"sessionEnded\", reason, close);\n }\n\n /** Total in-flight links (diagnostics and tests). */\n get linkCount(): number {\n return this.links.size;\n }\n}\n\nexport { PROTO };\n", "/**\n * Transport seams. The protocol logic never touches RTCPeerConnection or WebSocket directly, so the whole\n * lobby/lockstep/chat/voice stack runs under vitest against `MemoryNetwork` / `MemoryServer` (see testing.ts).\n */\n\nexport type ChannelName = \"reliable\" | \"fast\" | \"voice\";\n\nexport const CHANNEL_IDS: Record<ChannelName, number> = { reliable: 0, fast: 1, voice: 2 };\n\nexport interface IceServer {\n urls: string | string[];\n username?: string;\n credential?: string;\n ttl?: number;\n}\n\nexport interface SdpInit {\n type: \"offer\" | \"answer\";\n sdp: string;\n}\nexport interface IceInit {\n candidate: string;\n sdpMid?: string | null;\n sdpMLineIndex?: number | null;\n}\n\nexport interface SignalPayload {\n sdp?: SdpInit;\n ice?: IceInit;\n}\n\n/** One peer connection with the three negotiated channels (s.8). */\nexport interface PeerLink {\n readonly remoteId: number;\n readonly isOpen: boolean;\n /** Fired for each local SDP/ICE blob that must reach the remote through signaling. */\n onSignal: ((s: SignalPayload) => void) | null;\n /** Fired once, when all three channels are open. */\n onOpen: (() => void) | null;\n /** Fired once when the link dies (after open or while connecting). */\n onClose: ((reason: string) => void) | null;\n onMessage: ((channel: ChannelName, bytes: Uint8Array) => void) | null;\n /** Fired once after ICE completes: whether the selected candidate pair uses a TURN relay (reported to the server). */\n onPath?: ((path: \"direct\" | \"relayed\") => void) | null;\n /** Fired when the selected candidate pair's remote address is known and is neither relayed nor an mDNS name. */\n onRemoteAddress?: ((ip: string) => void) | null;\n /** The offerer calls this to start negotiation. The answerer just feeds handleSignal. */\n startOffer(): void;\n handleSignal(s: SignalPayload): void | Promise<void>;\n send(channel: ChannelName, bytes: Uint8Array): void;\n close(): void;\n}\n\nexport interface LinkOptions {\n remoteId: number;\n iceServers: IceServer[];\n relayOnly: boolean;\n /**\n * Direct-IP host (docs/design/direct-ip.md s.2): bind ICE to these local UDP ports. One port (begin = end) also turns on\n * ICE UDP mux so every peer shares it. Honoured by libdatachannel-based RTCPeerConnection (node-datachannel);\n * browsers ignore it.\n */\n portRange?: { begin: number; end: number };\n /**\n * Direct-IP joiner: remote ICE candidate lines (`candidate:1 1 UDP ... typ host`) added after the remote\n * description, for the address the player typed or pasted as a code.\n */\n extraRemoteCandidates?: string[];\n}\n\nexport interface LinkFactory {\n create(opts: LinkOptions): PeerLink;\n}\n\n/** Subset of the WebSocket API the signaling client needs. */\nexport interface SocketLike {\n readonly readyState: number;\n onopen: ((ev?: unknown) => void) | null;\n onmessage: ((ev: { data: unknown }) => void) | null;\n onclose: ((ev?: unknown) => void) | null;\n onerror: ((ev?: unknown) => void) | null;\n send(data: string): void;\n close(code?: number, reason?: string): void;\n}\nexport type SocketFactory = (url: string) => SocketLike;\n\nexport const defaultSocketFactory: SocketFactory = (url) => new WebSocket(url) as unknown as SocketLike;\n", "/* Browser implementations of the voice seams: WebCodecs Opus, AudioWorklet mic capture, WebAudio playback. */\nimport { FRAME_SAMPLES, SAMPLE_RATE } from \"./voice-core.js\";\nimport type { AudioSink, FrameSource, VoiceCodec, VoiceDecoderHandle, VoiceEncoderHandle } from \"./voice-types.js\";\n\nexport function webCodecsOpusAvailable(): boolean {\n return typeof AudioEncoder !== \"undefined\" && typeof AudioDecoder !== \"undefined\" && typeof AudioData !== \"undefined\";\n}\n\n/** Opus through WebCodecs (not Baseline: Chromium/Edge/Safari; check `webCodecsOpusAvailable()`). */\nexport const webCodecsOpus: VoiceCodec = {\n createEncoder(opts, onFrame): VoiceEncoderHandle {\n let ts = 0;\n const enc = new AudioEncoder({\n output: (chunk) => {\n const out = new Uint8Array(chunk.byteLength);\n chunk.copyTo(out);\n onFrame(out);\n },\n error: (e) => console.error(\"onelobby: AudioEncoder\", e),\n });\n enc.configure({\n codec: \"opus\",\n sampleRate: SAMPLE_RATE,\n numberOfChannels: 1,\n bitrate: opts.bitrate,\n opus: { frameDuration: 20000, application: \"voip\", useinbandfec: true, usedtx: false, packetlossperc: 10 },\n } as AudioEncoderConfig);\n return {\n encode(pcm) {\n const data = new AudioData({\n format: \"f32\",\n sampleRate: SAMPLE_RATE,\n numberOfFrames: pcm.length,\n numberOfChannels: 1,\n timestamp: ts,\n data: pcm as unknown as Float32Array<ArrayBuffer>,\n });\n ts += 20000;\n enc.encode(data);\n data.close();\n },\n close() {\n if (enc.state !== \"closed\") enc.close();\n },\n };\n },\n createDecoder(onPcm): VoiceDecoderHandle {\n let ts = 0;\n const dec = new AudioDecoder({\n output: (audio) => {\n const pcm = new Float32Array(audio.numberOfFrames);\n audio.copyTo(pcm, { planeIndex: 0, format: \"f32-planar\" } as AudioDataCopyToOptions);\n audio.close();\n onPcm(pcm);\n },\n error: (e) => console.error(\"onelobby: AudioDecoder\", e),\n });\n dec.configure({ codec: \"opus\", sampleRate: SAMPLE_RATE, numberOfChannels: 1 });\n return {\n decode(opus) {\n if (opus === null) {\n onPcm(new Float32Array(FRAME_SAMPLES)); // WebCodecs has no PLC hook: conceal with silence\n return;\n }\n dec.decode(new EncodedAudioChunk({ type: \"key\", timestamp: ts, data: opus }));\n ts += 20000;\n },\n close() {\n if (dec.state !== \"closed\") dec.close();\n },\n };\n },\n};\n\nconst WORKLET_SRC = `\nclass OneLobbyCapture extends AudioWorkletProcessor {\n constructor() { super(); this.buf = new Float32Array(${FRAME_SAMPLES}); this.n = 0; }\n process(inputs) {\n const ch = inputs[0] && inputs[0][0];\n if (ch) {\n for (let i = 0; i < ch.length; i++) {\n this.buf[this.n++] = ch[i];\n if (this.n === ${FRAME_SAMPLES}) {\n const out = this.buf; this.buf = new Float32Array(${FRAME_SAMPLES}); this.n = 0;\n this.port.postMessage(out, [out.buffer]);\n }\n }\n }\n return true;\n }\n}\nregisterProcessor('onelobby-capture', OneLobbyCapture);\n`;\n\n/** getUserMedia + AudioWorklet producing 960-sample frames at 48 kHz. Must be started from a user gesture. */\nexport class WorkletMic implements FrameSource {\n private ctx: AudioContext | null = null;\n private stream: MediaStream | null = null;\n\n async start(onFrame: (pcm: Float32Array) => void): Promise<void> {\n this.stream = await navigator.mediaDevices.getUserMedia({\n audio: { channelCount: 1, echoCancellation: true, noiseSuppression: true, autoGainControl: true },\n });\n this.ctx = new AudioContext({ sampleRate: SAMPLE_RATE });\n const url = URL.createObjectURL(new Blob([WORKLET_SRC], { type: \"text/javascript\" }));\n try {\n await this.ctx.audioWorklet.addModule(url);\n } finally {\n URL.revokeObjectURL(url);\n }\n const src = this.ctx.createMediaStreamSource(this.stream);\n const node = new AudioWorkletNode(this.ctx, \"onelobby-capture\", { numberOfInputs: 1, numberOfOutputs: 0 });\n node.port.onmessage = (e) => onFrame(e.data as Float32Array);\n src.connect(node);\n if (this.ctx.state === \"suspended\") await this.ctx.resume();\n }\n\n stop(): void {\n for (const t of this.stream?.getTracks() ?? []) t.stop();\n void this.ctx?.close();\n this.stream = null;\n this.ctx = null;\n }\n}\n\n/** Plays decoded PCM per speaker, scheduled back to back on an AudioContext. */\nexport class WebAudioSink implements AudioSink {\n private ctx = new AudioContext({ sampleRate: SAMPLE_RATE });\n private next = new Map<number, number>();\n\n constructor() {\n void this.ctx.resume();\n }\n\n play(peerId: number, pcm: Float32Array, gain: number): void {\n if (pcm.length === 0) return;\n const buf = this.ctx.createBuffer(1, pcm.length, SAMPLE_RATE);\n buf.copyToChannel(pcm as unknown as Float32Array<ArrayBuffer>, 0);\n const src = this.ctx.createBufferSource();\n src.buffer = buf;\n const g = this.ctx.createGain();\n g.gain.value = gain;\n src.connect(g).connect(this.ctx.destination);\n const now = this.ctx.currentTime;\n let at = this.next.get(peerId) ?? 0;\n if (at < now + 0.01) at = now + 0.02; // (re)started: small lead so scheduling never lands in the past\n src.start(at);\n this.next.set(peerId, at + pcm.length / SAMPLE_RATE);\n }\n\n close(): void {\n void this.ctx.close();\n }\n}\n", "import {\n CHANNEL_IDS,\n type ChannelName,\n type LinkFactory,\n type LinkOptions,\n type PeerLink,\n type SignalPayload,\n} from \"./transport.js\";\n\nconst CHANNEL_INIT: Record<ChannelName, RTCDataChannelInit> = {\n reliable: { negotiated: true, id: CHANNEL_IDS.reliable, ordered: true },\n fast: { negotiated: true, id: CHANNEL_IDS.fast, ordered: false, maxRetransmits: 0 },\n voice: { negotiated: true, id: CHANNEL_IDS.voice, ordered: false, maxRetransmits: 0 },\n};\n\n/** Browser (or any WebRTC-capable runtime) PeerLink over RTCPeerConnection with three negotiated channels. */\nexport class WebRtcLink implements PeerLink {\n readonly remoteId: number;\n isOpen = false;\n onSignal: PeerLink[\"onSignal\"] = null;\n onOpen: PeerLink[\"onOpen\"] = null;\n onClose: PeerLink[\"onClose\"] = null;\n onMessage: PeerLink[\"onMessage\"] = null;\n onPath: PeerLink[\"onPath\"] = null;\n onRemoteAddress: PeerLink[\"onRemoteAddress\"] = null;\n\n private pc: RTCPeerConnection;\n private channels = {} as Record<ChannelName, RTCDataChannel>;\n private pendingIce: RTCIceCandidateInit[] = [];\n /**\n * Set once setRemoteDescription resolves. Not `pc.remoteDescription`: node-datachannel's polyfill returns an object\n * there before any is set, and its addIceCandidate throws until one is.\n */\n private haveRemote = false;\n private closed = false;\n private opened = 0;\n private early: [ChannelName, Uint8Array][] = [];\n private extra: string[];\n\n /** `rtc`: the RTCPeerConnection class to use (default the global one; in Node, node-datachannel's polyfill). */\n constructor(opts: LinkOptions, rtc: typeof RTCPeerConnection = globalThis.RTCPeerConnection) {\n this.remoteId = opts.remoteId;\n this.extra = opts.extraRemoteCandidates ?? [];\n const config: RTCConfiguration & Record<string, unknown> = {\n iceServers: opts.iceServers as RTCIceServer[],\n iceTransportPolicy: opts.relayOnly ? \"relay\" : \"all\",\n };\n if (opts.portRange) {\n // libdatachannel names (node-datachannel passes its config through); one pinned port = every peer on it\n config.portRangeBegin = opts.portRange.begin;\n config.portRangeEnd = opts.portRange.end;\n if (opts.portRange.begin === opts.portRange.end) config.enableIceUdpMux = true;\n }\n this.pc = new rtc(config);\n this.pc.onicecandidate = (e) => {\n if (e.candidate) this.onSignal?.({ ice: e.candidate.toJSON() as { candidate: string } });\n };\n this.pc.onconnectionstatechange = () => {\n const s = this.pc.connectionState;\n if (s === \"failed\" || s === \"closed\" || s === \"disconnected\") {\n // \"disconnected\" can recover; give ICE a moment before declaring the link dead.\n if (s === \"disconnected\") setTimeout(() => this.pc.connectionState === \"disconnected\" && this.fail(\"disconnected\"), 4000);\n else this.fail(s);\n }\n };\n }\n\n /**\n * The offerer creates the channels just before its offer, the answerer once the remote offer is set. Creating them\n * any earlier makes libdatachannel (node-datachannel) start an offer of its own on the answering side, which then\n * collides with the real one (both ICE agents controlling, the link never opens).\n */\n private openChannels(): void {\n if (this.channels.reliable) return;\n for (const name of [\"reliable\", \"fast\", \"voice\"] as ChannelName[]) {\n const ch = this.pc.createDataChannel(name, CHANNEL_INIT[name]);\n ch.binaryType = \"arraybuffer\";\n ch.onopen = () => {\n if (++this.opened === 3 && !this.closed) {\n this.isOpen = true;\n this.onOpen?.();\n for (const [n, data] of this.early.splice(0)) if (!this.closed) this.onMessage?.(n, data);\n void this.reportPath();\n }\n };\n ch.onclose = () => this.fail(`channel ${name} closed`);\n ch.onmessage = (e) => {\n const d = e.data as unknown;\n const data = d instanceof ArrayBuffer ? new Uint8Array(d) : ArrayBuffer.isView(d) ? new Uint8Array(d.buffer, d.byteOffset, d.byteLength) : new Uint8Array(d as never);\n // The far side can open first and speak at once; hold its data until this side is open too, so whoever\n // reads it can already answer (otherwise the host queues or drops its replies to a link it thinks is down).\n if (!this.isOpen) {\n if (this.early.length < 1000) this.early.push([name, data]);\n return;\n }\n this.onMessage?.(name, data);\n };\n this.channels[name] = ch;\n }\n }\n\n startOffer(): void {\n void (async () => {\n try {\n this.openChannels();\n const offer = await this.pc.createOffer();\n await this.pc.setLocalDescription(offer);\n this.onSignal?.({ sdp: { type: \"offer\", sdp: offer.sdp ?? \"\" } });\n } catch (e) {\n this.fail(`offer failed: ${String(e)}`);\n }\n })();\n }\n\n async handleSignal(s: SignalPayload): Promise<void> {\n try {\n if (s.sdp) {\n await this.pc.setRemoteDescription({ type: s.sdp.type, sdp: s.sdp.sdp });\n this.haveRemote = true;\n for (const c of this.pendingIce.splice(0)) await this.pc.addIceCandidate(c);\n if (this.extra.length) {\n // docs/design/direct-ip.md s.2: the address the joiner typed is the host's reachable candidate\n const mid = /^a=mid:(\\S+)/m.exec(s.sdp.sdp)?.[1] ?? \"0\";\n for (const candidate of this.extra.splice(0)) await this.pc.addIceCandidate({ candidate, sdpMid: mid, sdpMLineIndex: 0 });\n }\n if (s.sdp.type === \"offer\") {\n this.openChannels();\n const answer = await this.pc.createAnswer();\n await this.pc.setLocalDescription(answer);\n this.onSignal?.({ sdp: { type: \"answer\", sdp: answer.sdp ?? \"\" } });\n }\n }\n if (s.ice) {\n // a candidate can come before the description it belongs to (the far side's native callbacks are unordered)\n if (this.haveRemote) await this.pc.addIceCandidate(s.ice as RTCIceCandidateInit);\n else this.pendingIce.push(s.ice as RTCIceCandidateInit);\n }\n } catch (e) {\n this.fail(`signal failed: ${String(e)}`);\n }\n }\n\n /** Selected candidate pair has a relay candidate -> \"relayed\", else \"direct\". Retries while stats settle. */\n private async reportPath(attempt = 0): Promise<void> {\n if (this.closed || attempt > 10) return;\n try {\n const stats = await this.pc.getStats();\n const byId = new Map<string, any>();\n stats.forEach((v) => byId.set(v.id, v));\n let pair: any;\n stats.forEach((v) => {\n if (v.type === \"transport\" && v.selectedCandidatePairId) pair = byId.get(v.selectedCandidatePairId);\n });\n if (!pair) stats.forEach((v) => { if (v.type === \"candidate-pair\" && (v.selected || (v.nominated && v.state === \"succeeded\"))) pair = v; });\n const l = pair && byId.get(pair.localCandidateId);\n const r = pair && byId.get(pair.remoteCandidateId);\n if (l && r) {\n // the remote address (not a relay's, not an mDNS name) lets a Nostr host ban the IP behind a kicked key\n const addr = String(r.address ?? r.ip ?? \"\");\n if (r.candidateType !== \"relay\" && l.candidateType !== \"relay\" && addr && !addr.endsWith(\".local\")) this.onRemoteAddress?.(addr.replace(/^::ffff:/i, \"\"));\n this.onPath?.(l.candidateType === \"relay\" || r.candidateType === \"relay\" ? \"relayed\" : \"direct\");\n return;\n }\n } catch {\n /* stats unavailable: retry */\n }\n setTimeout(() => void this.reportPath(attempt + 1), 300);\n }\n\n send(channel: ChannelName, bytes: Uint8Array): void {\n const ch = this.channels[channel];\n if (ch && ch.readyState === \"open\") ch.send(bytes as unknown as ArrayBuffer);\n }\n\n close(): void {\n this.fail(\"closed locally\");\n }\n\n private fail(reason: string): void {\n if (this.closed) return;\n this.closed = true;\n const wasOpen = this.isOpen;\n this.isOpen = false;\n try {\n this.pc.close();\n } catch {\n /* ignore */\n }\n void wasOpen;\n this.onClose?.(reason);\n }\n}\n\nexport const webRtcLinkFactory: LinkFactory = { create: (opts) => new WebRtcLink(opts) };\n\n/** A link factory over a given RTCPeerConnection class, e.g. `import { RTCPeerConnection } from \"node-datachannel/polyfill\"`. */\nexport function webRtcLinkFactoryFor(rtc: typeof RTCPeerConnection): LinkFactory {\n return { create: (opts) => new WebRtcLink(opts, rtc) };\n}\n", "import { CROCKFORD_ALPHABET } from \"./crockford.js\";\n\n/**\n * `dc1` join codes (docs/design/direct-ip.md s.2a, spec/reference/joincode.mjs): a direct-IP host's IPv4 address and port in\n * 11 Crockford characters, shown `6B01-RGFE-P2B`. The code is the address in disguise, so decoding asks nobody.\n * Bits, most significant first: version:2 (0 = IPv4) | ipv4:32 | port:16 | crc:5 (CRC-5, polynomial 0x05, over the 50\n * bits before it). One mistyped character always fails the check.\n */\nexport const JOIN_CODE_LENGTH = 11;\n\nexport type JoinCodeError = \"bad_code\" | \"unknown_version\";\nexport type DecodedJoinCode = { ip: string; port: number } | { room: string } | { error: JoinCodeError };\n\nfunction crc5(value: bigint, bits: number): number {\n let crc = 0;\n for (let i = bits - 1; i >= 0; i--) {\n const bit = Number((value >> BigInt(i)) & 1n);\n const top = (crc >> 4) & 1;\n crc = (crc << 1) & 0x1f;\n if (top ^ bit) crc ^= 0x05;\n }\n return crc;\n}\n\nfunction parseIPv4(ip: string): number | null {\n const parts = ip.split(\".\");\n if (parts.length !== 4) return null;\n let n = 0;\n for (const p of parts) {\n if (!/^\\d{1,3}$/.test(p) || Number(p) > 255) return null;\n n = n * 256 + Number(p);\n }\n return n;\n}\n\n/** Display form (`6B01-RGFE-P2B`) for an IPv4 address and port 1-65535. Throws on anything else. */\nexport function encodeJoinCode(ipv4: string, port: number): string {\n const ip = parseIPv4(ipv4);\n if (ip === null) throw new Error(`not an IPv4 address: ${ipv4}`);\n if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error(`bad port ${port}`);\n return encodeData((BigInt(ip) << 16n) | BigInt(port)); // version 0\n}\n\nfunction encodeData(data: bigint): string {\n let v = (data << 5n) | BigInt(crc5(data, 50));\n let s = \"\";\n for (let i = 0; i < JOIN_CODE_LENGTH; i++) {\n s = CROCKFORD_ALPHABET[Number(v & 31n)] + s;\n v >>= 5n;\n }\n return `${s.slice(0, 4)}-${s.slice(4, 8)}-${s.slice(8)}`;\n}\n\n/**\n * Version 1 code (a Nostr room, spec/nostr-signaling.md s.1): version:2 = 1 | room:48 | crc:5. `roomHex` is 12 hex\n * digits. The room is only a secret both sides derive keys from.\n */\nexport function encodeRoomCodeFor(roomHex: string): string {\n if (!/^[0-9a-fA-F]{12}$/.test(roomHex)) throw new Error(`room must be 12 hex digits: ${roomHex}`);\n return encodeData((1n << 48n) | BigInt(\"0x\" + roomHex));\n}\n\n/** A new version 1 code with a random 48-bit room from the platform's CSPRNG. */\nexport function encodeRoomCode(): string {\n const b = new Uint8Array(6);\n globalThis.crypto.getRandomValues(b);\n return encodeRoomCodeFor([...b].map((x) => x.toString(16).padStart(2, \"0\")).join(\"\"));\n}\n\n/** Upper case, spaces and hyphens dropped, i/l -> 1, o -> 0; null unless 11 alphabet characters. */\nexport function normalizeJoinCode(input: string): string | null {\n const s = input\n .replace(/[ \\-]/g, \"\")\n .replace(/[a-z]/g, (c) => c.toUpperCase())\n .replace(/[IL]/g, \"1\")\n .replace(/O/g, \"0\");\n if (s.length !== JOIN_CODE_LENGTH) return null;\n for (const c of s) if (CROCKFORD_ALPHABET.indexOf(c) < 0) return null;\n return s;\n}\n\nexport function decodeJoinCode(input: string): DecodedJoinCode {\n const s = normalizeJoinCode(input);\n if (!s) return { error: \"bad_code\" };\n let v = 0n;\n for (const c of s) v = (v << 5n) | BigInt(CROCKFORD_ALPHABET.indexOf(c));\n const data = v >> 5n;\n if (crc5(data, 50) !== Number(v & 31n)) return { error: \"bad_code\" };\n const version = data >> 48n;\n if (version === 1n) return { room: (data & 0xffffffffffffn).toString(16).padStart(12, \"0\") };\n if (version !== 0n) return { error: \"unknown_version\" };\n const port = Number(data & 0xffffn);\n if (port === 0) return { error: \"bad_code\" };\n const a = Number((data >> 16n) & 0xffffffffn);\n return { ip: [a >>> 24, (a >>> 16) & 255, (a >>> 8) & 255, a & 255].join(\".\"), port };\n}\n\n/** Shaped like a join code (no dots or colons, 11 alphabet characters): a failed decode is `bad_code`, not a host name. */\nexport function looksLikeJoinCode(input: string): boolean {\n return !/[.:]/.test(input) && normalizeJoinCode(input) !== null;\n}\n", "/**\n * The frame layer of Nostr signaling (spec/nostr-signaling.md s.4): ordered, de-duplicated, acknowledged delivery of\n * hub protocol texts between two pubkeys over lossy relays. One {@link ReliableLink} per peer; it knows nothing about\n * relays, keys or encryption: the owner stamps and ships the frames it emits.\n */\nimport type { Clock } from \"../clock.js\";\nimport { BATCH_MS, RESEND_MS, MAX_RESENDS, BEAT_MS, SILENCE_MS, MAX_HELD, MAX_OUT_FRAMES, MAX_OUT_BYTES, MAX_FRAME_BYTES, TICK_MS } from \"./limits.js\";\n\nexport type FrameType = \"open\" | \"data\" | \"beat\" | \"close\";\n\n/** The wire frame (JSON in the encrypted event content). */\nexport interface Frame {\n v: 1;\n type: FrameType;\n from: string;\n to: string;\n seq: number;\n ack: number;\n msgs?: string[];\n reason?: string;\n}\n\n/** What a link emits; the owner adds v / from / to. */\nexport type OutFrame = Pick<Frame, \"type\" | \"seq\" | \"ack\"> & { msgs?: string[]; reason?: string };\n\nexport { BATCH_MS, RESEND_MS, MAX_RESENDS, BEAT_MS, SILENCE_MS, MAX_HELD, MAX_OUT_FRAMES, MAX_OUT_BYTES, MAX_FRAME_BYTES };\n\nconst utf8 = new TextEncoder();\nconst byteLen = (s: string) => utf8.encode(s).length;\n\nexport interface LinkOptions {\n clock: Clock;\n /** Ships a frame to the peer. */\n send: (f: OutFrame) => void;\n /** An in-order hub protocol text from the peer. */\n deliver: (text: string) => void;\n /** The peer sent `close`. */\n onPeerClose: (reason: string) => void;\n /** Gave up: five resends unanswered, or 30 s of silence (reason \"timeout\"). */\n onDead: (reason: string) => void;\n}\n\ninterface Pending {\n bytes: number;\n frame: OutFrame;\n lastSent: number;\n resends: number;\n}\n\nexport class ReliableLink {\n private readonly o: LinkOptions;\n private queue: string[] = [];\n private queueBytes = 0;\n private unackedBytes = 0;\n private nextSeq = 1;\n private readonly unacked = new Map<number, Pending>();\n private rcvNext = 1; // next seq to deliver\n private readonly held = new Map<number, string[]>();\n private ackOwed = false;\n private lastSent: number;\n private lastHeard: number;\n private cancelFlush: (() => void) | null = null;\n private cancelTick: (() => void) | null = null;\n private done = false;\n\n constructor(o: LinkOptions) {\n this.o = o;\n this.lastSent = this.lastHeard = o.clock.now();\n this.cancelTick = o.clock.setInterval(() => this.tick(), TICK_MS);\n }\n\n get closed(): boolean {\n return this.done;\n }\n\n /** Queues a hub text; sent within 30 ms, packed with its neighbours. */\n send(text: string): void {\n if (this.done) return;\n // relays refuse high publish rates: past the cap the text is dropped, the connection is not\n const n = byteLen(text);\n if (this.queue.length + this.unacked.size >= MAX_OUT_FRAMES || this.queueBytes + this.unackedBytes + n > MAX_OUT_BYTES) return;\n this.queue.push(text);\n this.queueBytes += n;\n this.scheduleFlush();\n }\n\n /** Any frame arrived from the peer (including `open`, which only proves it is alive). */\n heard(): void {\n this.lastHeard = this.o.clock.now();\n }\n\n /** `data`, `beat` and `close` frames from the peer. */\n receive(f: Frame): void {\n if (this.done) return;\n this.heard();\n if (f.type === \"close\") {\n const reason = typeof f.reason === \"string\" ? f.reason : \"\";\n this.stop();\n this.o.onPeerClose(reason);\n return;\n }\n if (f.type !== \"data\" && f.type !== \"beat\") return;\n if (Number.isInteger(f.ack)) for (const s of [...this.unacked.keys()]) if (s <= f.ack) this.dropUnacked(s);\n if (f.type === \"beat\") return;\n const msgs = Array.isArray(f.msgs) ? f.msgs.filter((m) => typeof m === \"string\") : [];\n if (!Number.isInteger(f.seq) || f.seq < 1) return;\n this.ackOwed = true;\n if (f.seq === this.rcvNext) {\n this.rcvNext++;\n for (const m of msgs) this.o.deliver(m);\n for (let h = this.held.get(this.rcvNext); h && !this.done; h = this.held.get(this.rcvNext)) {\n this.held.delete(this.rcvNext);\n this.rcvNext++;\n for (const m of h) this.o.deliver(m);\n }\n } else if (f.seq > this.rcvNext && f.seq <= this.rcvNext + MAX_HELD) this.held.set(f.seq, msgs);\n // f.seq < rcvNext: a duplicate; the ack below tells the sender to stop resending\n this.scheduleFlush();\n }\n\n /** Sends what is queued, then `close` (once), and stops. */\n close(reason = \"\"): void {\n if (this.done) return;\n this.flush();\n this.o.send({ type: \"close\", seq: 0, ack: this.rcvNext - 1, ...(reason ? { reason } : {}) });\n this.stop();\n }\n\n /** Stops the timers without telling the peer. */\n stop(): void {\n this.done = true;\n this.cancelFlush?.();\n this.cancelTick?.();\n this.cancelFlush = this.cancelTick = null;\n this.unacked.clear();\n this.unackedBytes = 0;\n this.held.clear();\n this.queue = [];\n this.queueBytes = 0;\n }\n\n private scheduleFlush(): void {\n if (this.cancelFlush || this.done) return;\n this.cancelFlush = this.o.clock.setTimeout(() => {\n this.cancelFlush = null;\n this.flush();\n }, BATCH_MS);\n }\n\n private flush(): void {\n if (this.done) return;\n this.cancelFlush?.();\n this.cancelFlush = null;\n const now = this.o.clock.now();\n if (this.queue.length === 0) {\n if (this.ackOwed) this.emit({ type: \"beat\", seq: 0, ack: this.rcvNext - 1 }, now);\n return;\n }\n let batch: string[] = [];\n let bytes = 0;\n const ship = () => {\n if (batch.length === 0) return;\n const frame: OutFrame = { type: \"data\", seq: this.nextSeq++, ack: this.rcvNext - 1, msgs: batch };\n this.unacked.set(frame.seq, { bytes, frame, lastSent: now, resends: 0 });\n this.unackedBytes += bytes;\n this.emit(frame, now);\n batch = [];\n bytes = 0;\n };\n this.queueBytes = 0;\n for (const m of this.queue.splice(0)) {\n const n = byteLen(m);\n if (batch.length > 0 && bytes + n > MAX_FRAME_BYTES) ship();\n batch.push(m);\n bytes += n;\n }\n ship();\n }\n\n private dropUnacked(seq: number): void {\n const p = this.unacked.get(seq);\n if (!p) return;\n this.unackedBytes -= p.bytes;\n this.unacked.delete(seq);\n }\n\n private emit(f: OutFrame, now: number): void {\n this.lastSent = now;\n this.ackOwed = false;\n this.o.send(f);\n }\n\n private tick(): void {\n if (this.done) return;\n const now = this.o.clock.now();\n if (now - this.lastHeard >= SILENCE_MS) return this.die();\n for (const p of this.unacked.values()) {\n if (now - p.lastSent < RESEND_MS) continue;\n if (p.resends >= MAX_RESENDS) return this.die();\n p.resends++;\n p.lastSent = now;\n this.o.send({ ...p.frame, ack: this.rcvNext - 1 });\n this.lastSent = now;\n }\n if (now - this.lastSent >= BEAT_MS) this.emit({ type: \"beat\", seq: 0, ack: this.rcvNext - 1 }, now);\n }\n\n private die(): void {\n this.stop();\n this.o.onDead(\"timeout\");\n }\n}\n", "/** WebCrypto helpers for Nostr signaling (spec/nostr-signaling.md s.2-3); Node 18+ and every browser have it. */\n\nconst subtle = (): SubtleCrypto => {\n const s = globalThis.crypto?.subtle;\n if (!s) throw new Error(\"WebCrypto (crypto.subtle) is not available; Nostr signaling needs a secure context or Node 18+\");\n return s;\n};\n\nconst buf = (b: Uint8Array): BufferSource => b as unknown as BufferSource;\nconst utf8 = new TextEncoder();\n\nexport function concatBytes(...parts: Uint8Array[]): Uint8Array {\n const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));\n let o = 0;\n for (const p of parts) {\n out.set(p, o);\n o += p.length;\n }\n return out;\n}\n\nexport const toHex = (b: Uint8Array): string => Array.from(b, (x) => x.toString(16).padStart(2, \"0\")).join(\"\");\n\nexport function fromHex(s: string): Uint8Array {\n if (s.length % 2 || /[^0-9a-fA-F]/.test(s)) throw new Error(\"bad hex\");\n const out = new Uint8Array(s.length / 2);\n for (let i = 0; i < out.length; i++) out[i] = parseInt(s.slice(i * 2, i * 2 + 2), 16);\n return out;\n}\n\nexport function toBase64(b: Uint8Array): string {\n let s = \"\";\n for (let i = 0; i < b.length; i += 0x8000) s += String.fromCharCode(...b.subarray(i, i + 0x8000));\n return btoa(s);\n}\n\nexport function fromBase64(s: string): Uint8Array {\n const bin = atob(s);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n}\n\nexport async function sha256(data: Uint8Array): Promise<Uint8Array> {\n return new Uint8Array(await subtle().digest(\"SHA-256\", buf(data)));\n}\n\n/** HKDF-SHA256 (RFC 5869) with an ASCII salt and info, `length` bytes. */\nexport async function hkdf(ikm: Uint8Array, salt: string, info: string, length = 32): Promise<Uint8Array> {\n const key = await subtle().importKey(\"raw\", buf(ikm), \"HKDF\", false, [\"deriveBits\"]);\n const bits = await subtle().deriveBits({ name: \"HKDF\", hash: \"SHA-256\", salt: buf(utf8.encode(salt)), info: buf(utf8.encode(info)) }, key, length * 8);\n return new Uint8Array(bits);\n}\n\n/** Keys of one room (s.2): `key` encrypts frames, `topic` is the relay-visible tag. */\nexport interface RoomKeys {\n key: Uint8Array;\n topic: string;\n}\n\n/** `normalizedCode` is the 11 normalised code characters (no hyphens, upper case). */\nexport async function deriveRoomKeys(normalizedCode: string): Promise<RoomKeys> {\n const ikm = utf8.encode(normalizedCode);\n const salt = \"onelobby-nostr-v1\";\n const [key, topic] = await Promise.all([hkdf(ikm, salt, \"key\"), hkdf(ikm, salt, \"topic\")]);\n return { key, topic: toHex(topic) };\n}\n\n/** base64( iv(12) || ciphertext || tag(16) ), no associated data. `iv` is fresh random unless a test pins it. */\nexport async function encryptContent(key: Uint8Array, plaintext: string, iv?: Uint8Array): Promise<string> {\n const k = await subtle().importKey(\"raw\", buf(key), \"AES-GCM\", false, [\"encrypt\"]);\n const v = iv ?? globalThis.crypto.getRandomValues(new Uint8Array(12));\n const ct = new Uint8Array(await subtle().encrypt({ name: \"AES-GCM\", iv: buf(v) }, k, buf(utf8.encode(plaintext))));\n return toBase64(concatBytes(v, ct));\n}\n\n/** Plain text, or null when the content is not valid base64, too short or fails the GCM tag. */\nexport async function decryptContent(key: Uint8Array, content: string): Promise<string | null> {\n try {\n const raw = fromBase64(content);\n if (raw.length < 12 + 16) return null;\n const k = await subtle().importKey(\"raw\", buf(key), \"AES-GCM\", false, [\"decrypt\"]);\n const pt = await subtle().decrypt({ name: \"AES-GCM\", iv: buf(raw.subarray(0, 12)) }, k, buf(raw.subarray(12)));\n return new TextDecoder().decode(pt);\n } catch {\n return null;\n }\n}\n", "/**\n * BIP340 Schnorr signatures over secp256k1 with BigInt, so the package keeps zero runtime dependencies. Nostr needs\n * nothing else from the curve: a fresh key pair per session and one signature per event (spec/nostr-signaling.md s.3).\n * Not constant-time; the keys are throw-away session keys and never leave the process.\n */\nimport { concatBytes, sha256 } from \"./crypto.js\";\n\nconst P = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2fn;\nconst N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n;\nconst GX = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798n;\nconst GY = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8n;\n\ntype Jac = [bigint, bigint, bigint]; // Jacobian (X, Y, Z); Z = 0 is the point at infinity\nconst INF: Jac = [0n, 1n, 0n];\nconst G: Jac = [GX, GY, 1n];\n\nconst mod = (a: bigint, m = P): bigint => {\n const r = a % m;\n return r >= 0n ? r : r + m;\n};\n\nfunction pow(b: bigint, e: bigint, m: bigint): bigint {\n let r = 1n;\n b = mod(b, m);\n while (e > 0n) {\n if (e & 1n) r = (r * b) % m;\n b = (b * b) % m;\n e >>= 1n;\n }\n return r;\n}\n\nconst inv = (a: bigint, m = P): bigint => pow(a, m - 2n, m);\n\nfunction dbl(p: Jac): Jac {\n const [X, Y, Z] = p;\n if (Z === 0n || Y === 0n) return INF;\n const A = (X * X) % P;\n const B = (Y * Y) % P;\n const C = (B * B) % P;\n const D = mod(2n * ((X + B) * (X + B) - A - C));\n const E = 3n * A;\n const F = (E * E) % P;\n const X3 = mod(F - 2n * D);\n return [X3, mod(E * (D - X3) - 8n * C), mod(2n * Y * Z)];\n}\n\nfunction add(p: Jac, q: Jac): Jac {\n if (p[2] === 0n) return q;\n if (q[2] === 0n) return p;\n const [X1, Y1, Z1] = p;\n const [X2, Y2, Z2] = q;\n const Z1Z1 = (Z1 * Z1) % P;\n const Z2Z2 = (Z2 * Z2) % P;\n const U1 = (X1 * Z2Z2) % P;\n const U2 = (X2 * Z1Z1) % P;\n const S1 = (Y1 * Z2 * Z2Z2) % P;\n const S2 = (Y2 * Z1 * Z1Z1) % P;\n if (U1 === U2) return S1 === S2 ? dbl(p) : INF;\n const H = mod(U2 - U1);\n const I = (4n * H * H) % P;\n const J = (H * I) % P;\n const r = mod(2n * (S2 - S1));\n const V = (U1 * I) % P;\n const X3 = mod(r * r - J - 2n * V);\n const Y3 = mod(r * (V - X3) - 2n * S1 * J);\n const Z3 = mod(((Z1 + Z2) * (Z1 + Z2) - Z1Z1 - Z2Z2) * H);\n return [X3, Y3, Z3];\n}\n\nfunction mul(p: Jac, k: bigint): Jac {\n let r = INF;\n let q = p;\n while (k > 0n) {\n if (k & 1n) r = add(r, q);\n q = dbl(q);\n k >>= 1n;\n }\n return r;\n}\n\nfunction affine(p: Jac): [bigint, bigint] | null {\n if (p[2] === 0n) return null;\n const zi = inv(p[2]);\n const zi2 = (zi * zi) % P;\n return [(p[0] * zi2) % P, (p[1] * zi2 * zi) % P];\n}\n\nexport const bytesToInt = (b: Uint8Array): bigint => {\n let n = 0n;\n for (const x of b) n = (n << 8n) | BigInt(x);\n return n;\n};\n\nexport function intToBytes(n: bigint, len = 32): Uint8Array {\n const out = new Uint8Array(len);\n for (let i = len - 1; i >= 0; i--) {\n out[i] = Number(n & 0xffn);\n n >>= 8n;\n }\n return out;\n}\n\nasync function taggedHash(tag: string, ...msgs: Uint8Array[]): Promise<Uint8Array> {\n const t = await sha256(new TextEncoder().encode(tag));\n return sha256(concatBytes(t, t, ...msgs));\n}\n\nfunction liftX(x: bigint): [bigint, bigint] | null {\n if (x >= P) return null;\n const y2 = mod(x * x * x + 7n);\n let y = pow(y2, (P + 1n) / 4n, P);\n if (mod(y * y) !== y2) return null;\n if (y & 1n) y = P - y;\n return [x, y];\n}\n\n/** The 32-byte x-only public key for a 32-byte secret key. */\nexport function schnorrPublicKey(secret: Uint8Array): Uint8Array {\n const d = bytesToInt(secret);\n if (d < 1n || d >= N) throw new Error(\"secret key out of range\");\n const pt = affine(mul(G, d))!;\n return intToBytes(pt[0]);\n}\n\n/** A fresh random secret key (valid scalar) from the platform CSPRNG. */\nexport function randomSecretKey(): Uint8Array {\n for (;;) {\n const b = new Uint8Array(32);\n globalThis.crypto.getRandomValues(b);\n const d = bytesToInt(b);\n if (d >= 1n && d < N) return b;\n }\n}\n\n/** BIP340 signature (64 bytes) of a 32-byte message. `aux` is 32 bytes of randomness (default: fresh). */\nexport async function schnorrSign(msg: Uint8Array, secret: Uint8Array, aux?: Uint8Array): Promise<Uint8Array> {\n const dPrime = bytesToInt(secret);\n if (dPrime < 1n || dPrime >= N) throw new Error(\"secret key out of range\");\n const pt = affine(mul(G, dPrime))!;\n const d = pt[1] & 1n ? N - dPrime : dPrime;\n const px = intToBytes(pt[0]);\n const a = aux ?? globalThis.crypto.getRandomValues(new Uint8Array(32));\n const th = await taggedHash(\"BIP0340/aux\", a);\n const dBytes = intToBytes(d);\n const t = dBytes.map((x, i) => x ^ th[i]);\n const rand = await taggedHash(\"BIP0340/nonce\", t, px, msg);\n const kPrime = bytesToInt(rand) % N;\n if (kPrime === 0n) throw new Error(\"bad nonce\");\n const r = affine(mul(G, kPrime))!;\n const k = r[1] & 1n ? N - kPrime : kPrime;\n const rx = intToBytes(r[0]);\n const e = bytesToInt(await taggedHash(\"BIP0340/challenge\", rx, px, msg)) % N;\n return concatBytes(rx, intToBytes((k + e * d) % N));\n}\n\n/** BIP340 verification. */\nexport async function schnorrVerify(sig: Uint8Array, msg: Uint8Array, pub: Uint8Array): Promise<boolean> {\n if (sig.length !== 64 || pub.length !== 32) return false;\n const pt = liftX(bytesToInt(pub));\n if (!pt) return false;\n const r = bytesToInt(sig.subarray(0, 32));\n const s = bytesToInt(sig.subarray(32));\n if (r >= P || s >= N) return false;\n const e = bytesToInt(await taggedHash(\"BIP0340/challenge\", sig.subarray(0, 32), pub, msg)) % N;\n const sG = mul(G, s);\n const eP = mul([pt[0], pt[1], 1n], N - e);\n const R = affine(add(sG, eP));\n return !!R && !(R[1] & 1n) && R[0] === r;\n}\n", "/**\n * Nostr events and the relay pool (spec/nostr-signaling.md s.3 and s.6). Anonymous: every connection uses a throw-away\n * key and carries encrypted frames only.\n */\nimport type { Clock } from \"../clock.js\";\nimport type { SocketLike } from \"../transport.js\";\nimport { sha256, toHex, fromHex } from \"./crypto.js\";\nimport { schnorrPublicKey, schnorrSign } from \"./secp256k1.js\";\n\nexport const NOSTR_KIND = 25050;\n\nexport interface NostrEvent {\n id: string;\n pubkey: string;\n created_at: number;\n kind: number;\n tags: string[][];\n content: string;\n sig: string;\n}\n\n/** NIP-01 event id: SHA-256 of `[0, pubkey, created_at, kind, tags, content]` serialised without whitespace. */\nexport async function eventId(pubkey: string, createdAt: number, kind: number, tags: string[][], content: string): Promise<string> {\n return toHex(await sha256(new TextEncoder().encode(JSON.stringify([0, pubkey, createdAt, kind, tags, content]))));\n}\n\n/** A signed kind-25050 event (BIP340 Schnorr over the id). */\nexport async function makeEvent(secret: Uint8Array, topic: string, content: string, createdAt: number): Promise<NostrEvent> {\n const pubkey = toHex(schnorrPublicKey(secret));\n const tags = [[\"t\", topic]];\n const id = await eventId(pubkey, createdAt, NOSTR_KIND, tags, content);\n const sig = toHex(await schnorrSign(fromHex(id), secret));\n return { id, pubkey, created_at: createdAt, kind: NOSTR_KIND, tags, content, sig };\n}\n\n/** The WebSocket constructor to a relay; the browser's, or Node 22+'s global one. Tests pass their own. */\nexport type RelayConnect = (url: string) => SocketLike;\n\nexport const defaultRelayConnect: RelayConnect = (url) => {\n const WS = (globalThis as { WebSocket?: new (url: string) => unknown }).WebSocket;\n if (!WS) throw new Error(\"no WebSocket in this runtime (Node 22+ has one; older Node needs advanced.nostrConnect)\");\n return new WS(url) as SocketLike;\n};\n\nexport interface RelayPoolOptions {\n urls: readonly string[];\n topic: string;\n connect: RelayConnect;\n clock: Clock;\n /** Unix ms. */\n wall: () => number;\n onEvent: (ev: NostrEvent) => void;\n /** Called when the number of connected relays changes. */\n onChange?: () => void;\n}\n\ninterface Relay {\n url: string;\n ws: SocketLike | null;\n open: boolean;\n /** The first connect to this relay has succeeded or failed (a dead relay is known to be dead). */\n attempted: boolean;\n skipped: boolean;\n backoff: number;\n cancelRetry: (() => void) | null;\n}\n\nconst SEEN_MAX = 4096;\nconst SINCE_SLACK_S = 30;\n\n/** Every relay at once, each reconnected with backoff (1 s doubling to 30 s); events deduped by id. */\nexport class RelayPool {\n private readonly relays: Relay[];\n private readonly seen = new Set<string>();\n private readonly sub = \"ol\" + Math.random().toString(16).slice(2, 10);\n private closed = false;\n private waiters: (() => void)[] = [];\n\n constructor(private readonly o: RelayPoolOptions) {\n this.relays = o.urls.map((url) => ({ url, ws: null, open: false, attempted: false, skipped: false, backoff: 1000, cancelRetry: null }));\n }\n\n get total(): number {\n return this.relays.length;\n }\n\n /** Relays connected right now (and not skipped). */\n get connected(): number {\n return this.relays.filter((r) => r.open && !r.skipped).length;\n }\n\n /** Every relay has either connected, failed a connect, or been skipped: waiting longer for the rest is pointless. */\n get settled(): boolean {\n return this.relays.every((r) => r.attempted || r.skipped);\n }\n\n start(): void {\n for (const r of this.relays) this.dial(r);\n }\n\n /** Resolves true once one relay is connected, false after `timeoutMs` (or when closed). */\n whenUp(timeoutMs: number): Promise<boolean> {\n if (this.connected > 0) return Promise.resolve(true);\n return new Promise((resolve) => {\n const cancel = this.o.clock.setTimeout(() => done(false), timeoutMs);\n const done = (v: boolean) => {\n cancel();\n this.waiters = this.waiters.filter((w) => w !== check);\n resolve(v);\n };\n const check = () => {\n if (this.connected > 0) done(true);\n else if (this.closed) done(false);\n };\n this.waiters.push(check);\n });\n }\n\n publish(ev: NostrEvent): void {\n const msg = JSON.stringify([\"EVENT\", ev]);\n for (const r of this.relays) if (r.open && !r.skipped) this.trySend(r, msg);\n }\n\n close(): void {\n this.closed = true;\n for (const r of this.relays) {\n r.cancelRetry?.();\n r.cancelRetry = null;\n this.drop(r, false);\n }\n this.changed();\n }\n\n private changed(): void {\n for (const w of [...this.waiters]) w();\n this.o.onChange?.();\n }\n\n private trySend(r: Relay, msg: string): void {\n try {\n r.ws?.send(msg);\n } catch {\n /* a dying socket: its onclose reconnects */\n }\n }\n\n private dial(r: Relay): void {\n if (this.closed || r.skipped) return;\n let ws: SocketLike;\n try {\n ws = this.o.connect(r.url);\n } catch {\n r.attempted = true;\n this.retryLater(r);\n return;\n }\n r.ws = ws;\n ws.onopen = () => {\n if (r.ws !== ws) return;\n r.open = true;\n r.attempted = true;\n r.backoff = 1000;\n const since = Math.floor(this.o.wall() / 1000) - SINCE_SLACK_S;\n this.trySend(r, JSON.stringify([\"REQ\", this.sub, { kinds: [NOSTR_KIND], \"#t\": [this.o.topic], since }]));\n this.changed();\n };\n ws.onmessage = (e) => {\n if (r.ws === ws) this.onMessage(r, String(e.data));\n };\n const lost = () => {\n if (r.ws !== ws) return;\n const wasOpen = r.open;\n r.ws = null;\n r.open = false;\n r.attempted = true;\n if (wasOpen) this.changed();\n this.retryLater(r);\n };\n ws.onclose = lost;\n ws.onerror = () => {\n /* onclose follows; Node's WebSocket may not fire it for a failed connect, so retry from here too */\n lost();\n };\n }\n\n private drop(r: Relay, retry: boolean): void {\n const ws = r.ws;\n r.ws = null;\n r.open = false;\n try {\n ws?.close();\n } catch {\n /* ignore */\n }\n if (retry) this.retryLater(r);\n }\n\n private retryLater(r: Relay): void {\n if (this.closed || r.skipped || r.cancelRetry) return;\n const wait = r.backoff;\n r.backoff = Math.min(30_000, r.backoff * 2);\n r.cancelRetry = this.o.clock.setTimeout(() => {\n r.cancelRetry = null;\n this.dial(r);\n }, wait);\n }\n\n private skip(r: Relay): void {\n if (r.skipped) return;\n r.skipped = true;\n r.cancelRetry?.();\n r.cancelRetry = null;\n this.drop(r, false);\n this.changed();\n }\n\n private onMessage(r: Relay, text: string): void {\n let m: unknown;\n try {\n m = JSON.parse(text);\n } catch {\n return;\n }\n if (!Array.isArray(m)) return;\n switch (m[0]) {\n case \"EVENT\": {\n const ev = m[2] as NostrEvent | undefined;\n if (!ev || typeof ev.id !== \"string\" || typeof ev.pubkey !== \"string\" || typeof ev.content !== \"string\" || typeof ev.created_at !== \"number\") return;\n if (ev.kind !== NOSTR_KIND || !Array.isArray(ev.tags) || !ev.tags.some((t) => t[0] === \"t\" && t[1] === this.o.topic)) return;\n if (this.seen.has(ev.id)) return;\n this.seen.add(ev.id);\n if (this.seen.size > SEEN_MAX) this.seen.delete(this.seen.values().next().value as string);\n this.o.onEvent(ev);\n return;\n }\n case \"OK\":\n // refused (not a plain duplicate): this relay does not take our events, skip it for the session\n if (m[2] === false && !String(m[3] ?? \"\").startsWith(\"duplicate\")) this.skip(r);\n return;\n case \"CLOSED\":\n this.skip(r);\n return;\n }\n }\n}\n\n/** Default public relays (probed 2026-10-26, docs/design/no-server-fallback.md): all accept and echo ephemeral kind 25050. */\nexport const DEFAULT_NOSTR_RELAYS: readonly string[] = [\n \"wss://nos.lol\",\n \"wss://relay.primal.net\",\n \"wss://nostr-pub.wellorder.net\",\n \"wss://nostr.oxtr.dev\",\n \"wss://nostr.bitcoiner.social\",\n \"wss://relay.nostr.net\",\n \"wss://nostr.sathoarder.com\",\n];\n\n/** Public STUN servers for Nostr joiners (spec s.5). */\nexport const DEFAULT_NOSTR_STUN: readonly string[] = [\"stun:stun.l.google.com:19302\", \"stun:stun.cloudflare.com:3478\"];\n\nexport interface RelayCheck {\n url: string;\n ok: boolean;\n ms: number;\n error?: string;\n}\n\n/**\n * Probes each relay: connect, subscribe, publish one kind-25050 event to itself under a random topic with a random\n * key, and see it echoed. Anonymous (nothing about the player is sent). Use it to pick `hosting.nostrRelays`.\n */\nexport async function checkNostrRelays(\n urls: readonly string[],\n timeoutMs = 8000,\n opts: { connect?: RelayConnect; clock?: Clock } = {},\n): Promise<RelayCheck[]> {\n const connect = opts.connect ?? defaultRelayConnect;\n const setT = (fn: () => void, ms: number) => {\n const h = setTimeout(fn, ms);\n return () => clearTimeout(h);\n };\n return Promise.all(urls.map((url) => checkOne(url, timeoutMs, connect, opts.clock?.setTimeout.bind(opts.clock) ?? setT)));\n}\n\nasync function checkOne(url: string, timeoutMs: number, connect: RelayConnect, setT: (fn: () => void, ms: number) => () => void): Promise<RelayCheck> {\n const t0 = Date.now();\n const secret = new Uint8Array(32);\n globalThis.crypto.getRandomValues(secret);\n secret[0] &= 0x7f; // far below the curve order\n if (secret.every((b) => b === 0)) secret[31] = 1;\n const topic = toHex(globalThis.crypto.getRandomValues(new Uint8Array(32)));\n let ev: NostrEvent;\n try {\n ev = await makeEvent(secret, topic, \"probe\", Math.floor(Date.now() / 1000));\n } catch (e) {\n return { url, ok: false, ms: 0, error: String(e) };\n }\n return new Promise<RelayCheck>((resolve) => {\n let ws: SocketLike | null = null;\n let finished = false;\n let accepted = false;\n const finish = (ok: boolean, error?: string) => {\n if (finished) return;\n finished = true;\n cancel();\n try {\n ws?.close();\n } catch {\n /* ignore */\n }\n resolve({ url, ok, ms: Date.now() - t0, ...(error ? { error } : {}) });\n };\n const cancel = setT(() => finish(false, accepted ? \"accepted but not echoed\" : \"timeout\"), timeoutMs);\n try {\n ws = connect(url);\n } catch (e) {\n finish(false, String(e));\n return;\n }\n const sub = \"chk\" + topic.slice(0, 8);\n let published = false;\n const publish = () => {\n if (published) return;\n published = true;\n ws!.send(JSON.stringify([\"EVENT\", ev]));\n };\n ws.onopen = () => {\n ws!.send(JSON.stringify([\"REQ\", sub, { kinds: [NOSTR_KIND], \"#t\": [topic], since: Math.floor(Date.now() / 1000) - 30 }]));\n // some relays never send EOSE for a subscription with no stored events: publish shortly anyway\n setT(publish, 1500);\n };\n ws.onerror = () => finish(false, \"connection failed\");\n ws.onclose = () => finish(false, \"closed\");\n ws.onmessage = (e) => {\n let m: unknown;\n try {\n m = JSON.parse(String(e.data));\n } catch {\n return;\n }\n if (!Array.isArray(m)) return;\n if (m[0] === \"EOSE\") publish();\n else if (m[0] === \"OK\" && m[1] === ev.id) {\n if (m[2] === true) accepted = true;\n else finish(false, `refused: ${String(m[3] ?? \"\")}`.slice(0, 120));\n } else if (m[0] === \"CLOSED\") finish(false, `closed: ${String(m[2] ?? \"\")}`.slice(0, 120));\n else if (m[0] === \"EVENT\" && (m[2] as NostrEvent | undefined)?.id === ev.id) finish(true);\n };\n });\n}\n", "/**\n * One side's connection to a Nostr room: a fresh session key pair, the relay pool, and encrypted frames in and out\n * (spec/nostr-signaling.md s.2-4). {@link NostrSocket} (joiner) and {@link NostrHostBridge} (host) sit on top.\n */\nimport type { Clock } from \"../clock.js\";\nimport { normalizeJoinCode } from \"../joincode.js\";\nimport { decryptContent, deriveRoomKeys, encryptContent, toHex } from \"./crypto.js\";\nimport type { Frame, OutFrame } from \"./link.js\";\nimport { DEFAULT_NOSTR_RELAYS, RelayPool, defaultRelayConnect, makeEvent, type NostrEvent, type RelayConnect } from \"./relays.js\";\nimport { randomSecretKey, schnorrPublicKey } from \"./secp256k1.js\";\n\nexport interface NostrRoomOptions {\n /** The version 1 join code, any display form. */\n code: string;\n relays?: readonly string[];\n connect?: RelayConnect;\n clock: Clock;\n /** Unix ms; default Date.now. */\n wall?: () => number;\n /** The number of connected relays changed. */\n onChange?: () => void;\n}\n\nconst MAX_AGE_S = 120;\n\nexport class NostrRoom {\n readonly pub: string;\n /** Called for each valid frame addressed to nobody in particular or to us (`to` empty or our pubkey). */\n onFrame: ((f: Frame) => void) | null = null;\n private readonly pool: RelayPool;\n private closed = false;\n private readonly pending = new Set<Promise<void>>();\n\n private constructor(private readonly secret: Uint8Array, private readonly keys: { key: Uint8Array; topic: string }, o: NostrRoomOptions, private readonly wall: () => number) {\n this.pub = toHex(schnorrPublicKey(secret));\n this.pool = new RelayPool({\n urls: o.relays && o.relays.length > 0 ? o.relays : DEFAULT_NOSTR_RELAYS,\n topic: keys.topic,\n connect: o.connect ?? defaultRelayConnect,\n clock: o.clock,\n wall,\n onEvent: (ev) => void this.onEvent(ev),\n onChange: o.onChange,\n });\n }\n\n /** Derives the keys and starts connecting. Throws on a code that is not 11 alphabet characters. */\n static async open(o: NostrRoomOptions): Promise<NostrRoom> {\n const norm = normalizeJoinCode(o.code);\n if (!norm) throw new Error(`not a join code: ${o.code}`);\n const keys = await deriveRoomKeys(norm);\n const room = new NostrRoom(randomSecretKey(), keys, o, o.wall ?? (() => Date.now()));\n room.pool.start();\n return room;\n }\n\n get relaysConnected(): number {\n return this.pool.connected;\n }\n\n get relaysTotal(): number {\n return this.pool.total;\n }\n\n /** Every relay has connected, failed or been skipped. */\n get relaysSettled(): boolean {\n return this.pool.settled;\n }\n\n whenUp(timeoutMs: number): Promise<boolean> {\n return this.pool.whenUp(timeoutMs);\n }\n\n /** Stamps, encrypts, signs and publishes a frame to every relay. */\n send(to: string, f: OutFrame): Promise<void> {\n const p = this.sendNow(to, f);\n this.pending.add(p);\n void p.finally(() => this.pending.delete(p));\n return p;\n }\n\n private async sendNow(to: string, f: OutFrame): Promise<void> {\n if (this.closed) return;\n const frame: Frame = { v: 1, type: f.type, from: this.pub, to, seq: f.seq, ack: f.ack, ...(f.msgs ? { msgs: f.msgs } : {}), ...(f.reason ? { reason: f.reason } : {}) };\n const content = await encryptContent(this.keys.key, JSON.stringify(frame));\n const ev = await makeEvent(this.secret, this.keys.topic, content, Math.floor(this.wall() / 1000));\n if (!this.closed) this.pool.publish(ev);\n }\n\n /** Closes now, or (`flush`) once the frames already handed to `send` are published (a final `close` frame). */\n close(flush = false): void {\n if (flush && this.pending.size > 0) {\n void Promise.allSettled([...this.pending]).then(() => this.close());\n return;\n }\n this.closed = true;\n this.onFrame = null;\n this.pool.close();\n }\n\n private async onEvent(ev: NostrEvent): Promise<void> {\n if (this.closed || ev.pubkey === this.pub) return;\n if (this.wall() / 1000 - ev.created_at > MAX_AGE_S) return;\n const text = await decryptContent(this.keys.key, ev.content);\n if (text === null || this.closed) return;\n let f: Frame;\n try {\n f = JSON.parse(text) as Frame;\n } catch {\n return;\n }\n if (!f || f.v !== 1 || f.from !== ev.pubkey || f.from === this.pub) return;\n if (f.type !== \"open\" && f.type !== \"data\" && f.type !== \"beat\" && f.type !== \"close\") return;\n if (typeof f.to !== \"string\" || (f.to !== \"\" && f.to !== this.pub)) return;\n this.onFrame?.(f);\n }\n}\n", "/**\n * The joiner's signaling socket over Nostr (spec/nostr-signaling.md s.4): a {@link SocketLike} that carries the hub\n * protocol to the host through a {@link NostrRoom}. `SignalingClient` cannot tell it from a TCP WebSocket.\n */\nimport type { Clock } from \"../clock.js\";\nimport type { SocketLike } from \"../transport.js\";\nimport { ReliableLink, type Frame } from \"./link.js\";\nimport { NostrRoom } from \"./room.js\";\nimport { GIVE_UP_MS, JOINER_FIRST_RELAY_MS, OPEN_EVERY_MS } from \"./limits.js\";\nimport type { RelayConnect } from \"./relays.js\";\n\nexport interface NostrSocketOptions {\n code: string;\n relays?: readonly string[];\n connect?: RelayConnect;\n clock: Clock;\n wall?: () => number;\n /** No relay connected within this long: the socket fails (default 10000). */\n relayTimeoutMs?: number;\n /** The host's first `open` did not arrive within this long: the socket fails (default 15000). */\n openTimeoutMs?: number;\n /** Resend interval of `open` (default 2000). */\n openEveryMs?: number;\n}\n\nexport class NostrSocket implements SocketLike {\n readyState = 0;\n onopen: SocketLike[\"onopen\"] = null;\n onmessage: SocketLike[\"onmessage\"] = null;\n onclose: SocketLike[\"onclose\"] = null;\n onerror: SocketLike[\"onerror\"] = null;\n /** Why the socket failed or closed, for messages (\"no Nostr relay reachable\", \"host did not answer\"). */\n failure = \"\";\n\n private room: NostrRoom | null = null;\n private link: ReliableLink | null = null;\n private hostPub = \"\";\n private cancelOpen: (() => void) | null = null;\n private cancelDeadline: (() => void) | null = null;\n private sendOpen: (() => void) | null = null;\n private relaysSeen = 0;\n\n constructor(private readonly o: NostrSocketOptions) {\n void this.start();\n }\n\n /** Relays connected, for a progress line. */\n get relaysConnected(): number {\n return this.room?.relaysConnected ?? 0;\n }\n\n get relaysTotal(): number {\n return this.room?.relaysTotal ?? 0;\n }\n\n send(data: string): void {\n if (this.readyState !== 1 || !this.link) return;\n // The keep-alive ping never crosses Nostr: answer it here (spec s.4)\n if (data.length < 64 && data.startsWith('{\"t\":\"ping\"')) {\n try {\n const m = JSON.parse(data) as { n?: unknown };\n queueMicrotask(() => this.readyState === 1 && this.onmessage?.({ data: JSON.stringify({ t: \"pong\", n: m.n }) }));\n return;\n } catch {\n /* send it on */\n }\n }\n this.link.send(data);\n }\n\n close(_code?: number, reason?: string): void {\n if (this.readyState === 3) return;\n const wasOpen = this.readyState === 1;\n if (wasOpen) this.link?.close(reason ?? \"\");\n this.finish(1000, reason ?? \"\", false);\n }\n\n private async start(): Promise<void> {\n const { clock } = this.o;\n try {\n // a relay that comes up after the first `open` gets one at once, not at the next resend (spec s.4)\n this.room = await NostrRoom.open({ ...this.o, onChange: () => this.relaysChanged() });\n } catch (e) {\n this.fail(String(e));\n return;\n }\n const room = this.room;\n if (this.readyState === 3) {\n room.close();\n return;\n }\n room.onFrame = (f) => this.onFrame(f);\n this.cancelDeadline = clock.setTimeout(() => this.fail(\"the host did not answer through the Nostr relays\"), this.o.openTimeoutMs ?? GIVE_UP_MS);\n if (!(await room.whenUp(this.o.relayTimeoutMs ?? JOINER_FIRST_RELAY_MS))) {\n this.fail(\"no Nostr relay reachable\");\n return;\n }\n if (this.readyState !== 0) return;\n const open = () => void room.send(\"\", { type: \"open\", seq: 0, ack: 0 });\n this.relaysSeen = room.relaysConnected;\n this.sendOpen = open;\n open();\n this.cancelOpen = clock.setInterval(open, this.o.openEveryMs ?? OPEN_EVERY_MS);\n }\n\n private relaysChanged(): void {\n const up = this.room?.relaysConnected ?? 0;\n if (up > this.relaysSeen && this.readyState === 0) this.sendOpen?.();\n this.relaysSeen = up;\n }\n\n private onFrame(f: Frame): void {\n if (this.readyState === 3 || f.to !== this.room?.pub) return;\n if (this.readyState === 0) {\n if (f.type !== \"open\") return;\n this.hostPub = f.from;\n this.sendOpen = null;\n this.cancelOpen?.();\n this.cancelDeadline?.();\n this.cancelOpen = this.cancelDeadline = null;\n const room = this.room!;\n this.link = new ReliableLink({\n clock: this.o.clock,\n send: (out) => void room.send(this.hostPub, out),\n deliver: (text) => this.readyState === 1 && this.onmessage?.({ data: text }),\n onPeerClose: (reason) => this.finish(1000, reason, true),\n onDead: (reason) => this.finish(1006, reason, false),\n });\n this.readyState = 1;\n this.onopen?.();\n return;\n }\n if (f.from !== this.hostPub) return;\n if (f.type === \"open\") {\n this.link?.heard();\n return;\n }\n this.link?.receive(f);\n }\n\n private fail(why: string): void {\n if (this.readyState === 3) return;\n this.failure = why;\n if (this.readyState === 0) this.onerror?.({ message: why });\n this.finish(1006, why, false);\n }\n\n private finish(code: number, reason: string, clean: boolean): void {\n if (this.readyState === 3) return;\n this.readyState = 3;\n this.cancelOpen?.();\n this.cancelDeadline?.();\n this.cancelOpen = this.cancelDeadline = null;\n this.link?.stop();\n this.room?.close(true);\n queueMicrotask(() => this.onclose?.({ code, reason, wasClean: clean }));\n }\n}\n", "/**\n * A direct-IP host address as a player types it (docs/design/direct-ip.md s.3): `ip`, `ip:port`, `[v6]:port`, a bare IPv6\n * address or a host name (a virtual-LAN name, say). Same rules as OneLobby.Direct's `DirectAddress` in C#.\n */\nexport interface DirectAddress {\n host: string;\n port: number;\n isIPv6: boolean;\n /** `host:port`, with brackets for IPv6. */\n display: string;\n /** The joiner's signaling URL: `ws://host:port/`. */\n url: string;\n /**\n * The address as a remote ICE host candidate (UDP, same port number as the TCP listener). Without it a joiner on the\n * internet only learns the host's private LAN addresses (docs/design/direct-ip.md s.2).\n */\n candidate: string;\n}\n\nexport const DEFAULT_DIRECT_PORT = 47810;\n\nconst IPV4 = /^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/;\nconst HOSTNAME = /^(?=.{1,253}$)([A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)(\\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$/;\n\nfunction isIPv4(s: string): boolean {\n const m = IPV4.exec(s);\n return !!m && m.slice(1).every((x) => Number(x) <= 255);\n}\n\nfunction isIPv6(s: string): boolean {\n if (!/^[0-9A-Fa-f:.]+$/.test(s) || s.split(\"::\").length > 2 || !s.includes(\":\")) return false;\n const tail = s.split(\":\").pop() ?? \"\";\n const groups = s.split(\":\").filter((g, i, a) => g !== \"\" || (i > 0 && i < a.length - 1));\n if (tail.includes(\".\") && !isIPv4(tail)) return false;\n return groups.every((g) => g === \"\" || g.includes(\".\") || /^[0-9A-Fa-f]{1,4}$/.test(g));\n}\n\nexport function parseDirectAddress(input: string, defaultPort = DEFAULT_DIRECT_PORT): DirectAddress | null {\n let s = input.trim();\n if (/^ws:\\/\\//i.test(s)) s = s.slice(5);\n s = s.replace(/\\/+$/, \"\");\n if (!s) return null;\n let host: string;\n let portText: string | null = null;\n if (s[0] === \"[\") {\n const close = s.indexOf(\"]\");\n if (close < 0) return null;\n host = s.slice(1, close);\n const rest = s.slice(close + 1);\n if (rest) {\n if (rest[0] !== \":\") return null;\n portText = rest.slice(1);\n }\n } else {\n const first = s.indexOf(\":\");\n if (first >= 0 && first === s.lastIndexOf(\":\")) {\n host = s.slice(0, first);\n portText = s.slice(first + 1);\n } else host = s; // no colon, or several (a bare IPv6 address)\n }\n let port = defaultPort;\n if (portText !== null) {\n if (!/^\\d{1,5}$/.test(portText)) return null;\n port = Number(portText);\n if (port < 1 || port > 65535) return null;\n }\n const v6 = isIPv6(host);\n if (!v6 && !isIPv4(host) && !HOSTNAME.test(host)) return null;\n const display = (v6 ? `[${host}]` : host) + `:${port}`;\n return { host, port, isIPv6: v6, display, url: `ws://${display}/`, candidate: `candidate:1 1 UDP 2122317823 ${host} ${port} typ host` };\n}\n", "/**\n * Blueprint v1: the `onelobby.json` settings file (docs/design/blueprint.md s.4, spec/blueprint.schema.json).\n * `parseBlueprint` validates by hand (the package has no dependencies), fills the defaults and returns a frozen\n * object. Errors carry the dotted `path` and the `rule` id the shared vectors (spec/vectors/blueprint.json) expect.\n */\nimport { OneLobbyError } from \"./signaling.js\";\nimport { DEFAULT_NOSTR_RELAYS } from \"./nostr/relays.js\";\n\nexport type BlueprintEngine = \"web\" | \"node-browser\" | \"godot-csharp\" | \"unity\" | \"dotnet\";\n\nexport interface Blueprint {\n readonly version: 1;\n readonly game: { readonly id: string; readonly netVersion: string; readonly title?: string };\n readonly engine?: BlueprintEngine;\n readonly hosting: {\n readonly direct: boolean;\n readonly server: boolean;\n readonly lobbyServer: string;\n readonly directPort: number;\n readonly openPort: boolean;\n readonly nostr: boolean;\n readonly nostrRelays: readonly string[];\n readonly relayOnly: boolean;\n readonly allowedOrigins: readonly string[];\n };\n readonly lobby: {\n readonly maxPlayers: number;\n readonly listed: boolean;\n readonly approveJoins: boolean;\n readonly password: boolean;\n readonly lateJoin: boolean;\n readonly rejoin: boolean;\n readonly joinLink: boolean;\n readonly meta: Readonly<Record<string, string>>;\n };\n readonly sync: {\n readonly mode: \"raw\" | \"lockstep\" | \"replication\";\n readonly tickRate: number;\n readonly hashEveryTicks: number;\n };\n readonly players: { readonly identity: \"name\" | \"anonymous\" | \"platform\"; readonly maxNameLength: number };\n readonly chat: { readonly text: boolean; readonly voice: boolean; readonly voiceMode: \"pushToTalk\" | \"voiceActivity\" };\n readonly session: {\n readonly hostLeaves: \"end\" | \"migrate\";\n readonly pause: \"off\" | \"host\" | \"anyone\";\n readonly readyTimeoutMs: number;\n readonly peerTimeoutMs: number;\n readonly rateLimit?: { readonly messages: number; readonly perMs: number };\n readonly runInBackground: boolean;\n };\n readonly platforms: {\n readonly steam: boolean | { readonly appId?: number };\n readonly epic: boolean | { readonly [k: string]: string };\n readonly discord: boolean;\n };\n readonly ui: { readonly kind: \"drop-in\" | \"generated\" | \"none\"; readonly entry?: string; readonly theme: \"light\" | \"dark\" | \"auto\" };\n}\n\n/** What you may write: only `game.id` is required. */\nexport interface BlueprintInput {\n $schema?: string;\n $comment?: string;\n version?: 1;\n game: { $comment?: string; id: string; netVersion?: string; title?: string };\n engine?: BlueprintEngine;\n hosting?: {\n $comment?: string;\n direct?: boolean;\n server?: boolean;\n lobbyServer?: string;\n directPort?: number;\n openPort?: boolean;\n nostr?: boolean;\n nostrRelays?: string[];\n relayOnly?: boolean;\n allowedOrigins?: string[];\n };\n lobby?: {\n $comment?: string;\n maxPlayers?: number;\n listed?: boolean;\n approveJoins?: boolean;\n password?: boolean;\n lateJoin?: boolean;\n rejoin?: boolean;\n joinLink?: boolean;\n meta?: Record<string, string>;\n };\n sync?: { $comment?: string; mode?: \"raw\" | \"lockstep\" | \"replication\"; tickRate?: number; hashEveryTicks?: number };\n players?: { $comment?: string; identity?: \"name\" | \"anonymous\" | \"platform\"; maxNameLength?: number };\n chat?: { $comment?: string; text?: boolean; voice?: boolean; voiceMode?: \"pushToTalk\" | \"voiceActivity\" };\n session?: {\n $comment?: string;\n hostLeaves?: \"end\" | \"migrate\";\n pause?: \"off\" | \"host\" | \"anyone\";\n readyTimeoutMs?: number;\n peerTimeoutMs?: number;\n rateLimit?: { messages: number; perMs: number };\n runInBackground?: boolean;\n };\n platforms?: {\n $comment?: string;\n steam?: boolean | { appId?: number };\n epic?: boolean | { [k: string]: string };\n discord?: boolean;\n };\n ui?: { $comment?: string; kind?: \"drop-in\" | \"generated\" | \"none\"; entry?: string; theme?: \"light\" | \"dark\" | \"auto\" };\n}\n\n/** Identity function for TypeScript completion in a `.ts` config (like Vite's `defineConfig`). */\nexport function defineBlueprint<T extends BlueprintInput>(x: T): T {\n return x;\n}\n\n/** `bad_config`: the file is not a valid blueprint. `path` is dotted (\"\" = root), `rule` a vector rule id. */\nexport class BlueprintError extends OneLobbyError {\n constructor(public path: string, public rule: string, detail: string) {\n super(\"bad_config\", `onelobby.json: ${path ? path + \": \" : \"\"}${detail}`);\n }\n}\n\n// ---- schema as data (key order = spec/blueprint.schema.json) ----------------------------------------------------\n\ntype Node =\n | { k: \"obj\"; req?: string[]; props: Record<string, Node> }\n | { k: \"str\"; pattern?: RegExp; min?: number; max?: number }\n | { k: \"int\"; min?: number; max?: number; zeroOr?: boolean }\n | { k: \"bool\" }\n | { k: \"enum\"; vals: string[] }\n | { k: \"const\"; val: number }\n | { k: \"strarr\" }\n | { k: \"meta\" }\n | { k: \"rate\" }\n | { k: \"steam\" }\n | { k: \"epic\" };\n\nconst obj = (props: Record<string, Node>, req?: string[]): Node => ({ k: \"obj\", props, req });\nconst bool: Node = { k: \"bool\" };\nconst str = (o: { pattern?: RegExp; min?: number; max?: number } = {}): Node => ({ k: \"str\", ...o });\nconst int = (min?: number, max?: number, zeroOr?: boolean): Node => ({ k: \"int\", min, max, zeroOr });\nconst en = (...vals: string[]): Node => ({ k: \"enum\", vals });\n\nconst ROOT: Node = obj(\n {\n version: { k: \"const\", val: 1 },\n game: obj(\n { id: str({ pattern: /^[A-Za-z0-9._-]{1,64}$/ }), netVersion: str({ min: 1, max: 32 }), title: str({ max: 64 }) },\n [\"id\"],\n ),\n engine: en(\"web\", \"node-browser\", \"godot-csharp\", \"unity\", \"dotnet\"),\n hosting: obj({\n direct: bool,\n server: bool,\n lobbyServer: str(),\n directPort: int(1024, 65535),\n openPort: bool,\n nostr: bool,\n nostrRelays: { k: \"strarr\" },\n relayOnly: bool,\n allowedOrigins: { k: \"strarr\" },\n }),\n lobby: obj({\n maxPlayers: int(2, 64),\n listed: bool,\n approveJoins: bool,\n password: bool,\n lateJoin: bool,\n rejoin: bool,\n joinLink: bool,\n meta: { k: \"meta\" },\n }),\n sync: obj({ mode: en(\"raw\", \"lockstep\", \"replication\"), tickRate: int(1, 120), hashEveryTicks: int(1, 100000) }),\n players: obj({ identity: en(\"name\", \"anonymous\", \"platform\"), maxNameLength: int(1, 24) }),\n chat: obj({ text: bool, voice: bool, voiceMode: en(\"pushToTalk\", \"voiceActivity\") }),\n session: obj({\n hostLeaves: en(\"end\", \"migrate\"),\n pause: en(\"off\", \"host\", \"anyone\"),\n readyTimeoutMs: int(0, 600000),\n peerTimeoutMs: int(6000, 600000, true),\n rateLimit: { k: \"rate\" },\n runInBackground: bool,\n }),\n platforms: obj({ steam: { k: \"steam\" }, epic: { k: \"epic\" }, discord: bool }),\n ui: obj({ kind: en(\"drop-in\", \"generated\", \"none\"), entry: str(), theme: en(\"light\", \"dark\", \"auto\") }),\n },\n [\"game\"],\n);\n\n/** Allowed (and ignored) anywhere. */\nconst IGNORED = new Set([\"$schema\", \"$comment\"]);\n\nclass Fail {\n constructor(public path: string, public rule: string, public detail: string) {}\n}\nconst join = (p: string, k: string | number) => (p ? `${p}.${k}` : String(k));\nconst isObj = (v: unknown): v is Record<string, unknown> => typeof v === \"object\" && v !== null && !Array.isArray(v);\n\nfunction check(n: Node, v: unknown, path: string): void {\n switch (n.k) {\n case \"obj\": {\n if (!isObj(v)) throw new Fail(path, \"type\", \"must be an object\");\n for (const r of n.req ?? []) if (!(r in v)) throw new Fail(join(path, r), \"required\", \"is required\");\n for (const k of Object.keys(v)) if (!IGNORED.has(k) && !(k in n.props)) throw new Fail(join(path, k), \"unknown_key\", \"unknown key\");\n for (const [k, sub] of Object.entries(n.props)) if (k in v) check(sub, v[k], join(path, k));\n return;\n }\n case \"str\": {\n if (typeof v !== \"string\") throw new Fail(path, \"type\", \"must be a string\");\n if ((n.min !== undefined && v.length < n.min) || (n.max !== undefined && v.length > n.max))\n throw new Fail(path, \"length\", `must be ${n.min ?? 0} to ${n.max ?? \"any\"} characters`);\n if (n.pattern && !n.pattern.test(v)) throw new Fail(path, \"pattern\", `must match ${n.pattern.source}`);\n return;\n }\n case \"int\": {\n if (typeof v !== \"number\" || !Number.isInteger(v)) throw new Fail(path, \"type\", \"must be an integer\");\n if (n.zeroOr && v === 0) return;\n if ((n.min !== undefined && v < n.min) || (n.max !== undefined && v > n.max))\n throw new Fail(path, \"range\", `must be ${n.zeroOr ? \"0 or \" : \"\"}${n.min ?? \"any\"} to ${n.max ?? \"any\"}`);\n return;\n }\n case \"bool\":\n if (typeof v !== \"boolean\") throw new Fail(path, \"type\", \"must be true or false\");\n return;\n case \"enum\":\n if (typeof v !== \"string\" || !n.vals.includes(v)) throw new Fail(path, \"enum\", `must be one of ${n.vals.join(\", \")}`);\n return;\n case \"const\":\n if (v !== n.val) throw new Fail(path, \"const\", `must be ${n.val}`);\n return;\n case \"strarr\": {\n if (!Array.isArray(v)) throw new Fail(path, \"type\", \"must be an array of strings\");\n v.forEach((x, i) => {\n if (typeof x !== \"string\") throw new Fail(join(path, i), \"type\", \"must be a string\");\n });\n return;\n }\n case \"meta\": {\n if (!isObj(v)) throw new Fail(path, \"type\", \"must be an object of strings\");\n const keys = Object.keys(v);\n if (keys.length > 16) throw new Fail(path, \"length\", \"at most 16 keys\");\n for (const k of keys) {\n if (k.length > 64) throw new Fail(join(path, k), \"length\", \"key is longer than 64 characters\");\n const x = v[k];\n if (typeof x !== \"string\") throw new Fail(join(path, k), \"type\", \"must be a string\");\n if (x.length > 64) throw new Fail(join(path, k), \"length\", \"value is longer than 64 characters\");\n }\n return;\n }\n case \"rate\":\n check(obj({ messages: int(1), perMs: int(1) }, [\"messages\", \"perMs\"]), v, path);\n return;\n case \"steam\":\n if (typeof v === \"boolean\") return;\n if (!isObj(v)) throw new Fail(path, \"type\", \"must be true, false or { appId }\");\n check(obj({ appId: int(1) }), v, path);\n return;\n case \"epic\": {\n if (typeof v === \"boolean\") return;\n if (!isObj(v)) throw new Fail(path, \"type\", \"must be true, false or { productId }\");\n for (const k of Object.keys(v)) if (!IGNORED.has(k) && typeof v[k] !== \"string\") throw new Fail(join(path, k), \"type\", \"must be a string\");\n return;\n }\n }\n}\n\n// ---- defaults and cross rules -----------------------------------------------------------------------------------\n\nconst sect = (x: unknown): Record<string, unknown> => (isObj(x) ? x : {});\nfunction strip(o: unknown): unknown {\n if (Array.isArray(o)) return o.map(strip);\n if (!isObj(o)) return o;\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(o)) if (k !== \"$comment\" && k !== \"$schema\") out[k] = strip(v);\n return out;\n}\nfunction deepFreeze<T>(o: T): T {\n if (typeof o === \"object\" && o !== null && !Object.isFrozen(o)) {\n Object.freeze(o);\n for (const v of Object.values(o)) deepFreeze(v);\n }\n return o;\n}\n\nfunction fill(input: Record<string, unknown>): Blueprint {\n const game = sect(input.game);\n const engine = input.engine as BlueprintEngine | undefined;\n const hosting = sect(input.hosting);\n const lobby = sect(input.lobby);\n const sync = sect(input.sync);\n const players = sect(input.players);\n const chat = sect(input.chat);\n const session = sect(input.session);\n const platforms = sect(input.platforms);\n const ui = sect(input.ui);\n const tickRate = (sync.tickRate as number | undefined) ?? 20;\n const web = engine === \"web\";\n const out = {\n version: 1,\n game: { id: game.id, netVersion: game.netVersion ?? \"1\", ...(game.title !== undefined ? { title: game.title } : {}) },\n ...(engine !== undefined ? { engine } : {}),\n hosting: {\n direct: hosting.direct ?? !web,\n server: hosting.server ?? web,\n lobbyServer: hosting.lobbyServer ?? \"\",\n directPort: hosting.directPort ?? 47810,\n openPort: hosting.openPort ?? true,\n nostr: hosting.nostr ?? true,\n nostrRelays: hosting.nostrRelays ?? [...DEFAULT_NOSTR_RELAYS],\n relayOnly: hosting.relayOnly ?? false,\n allowedOrigins: hosting.allowedOrigins ?? [],\n },\n lobby: {\n maxPlayers: lobby.maxPlayers ?? 4,\n listed: lobby.listed ?? false,\n approveJoins: lobby.approveJoins ?? false,\n password: lobby.password ?? false,\n lateJoin: lobby.lateJoin ?? true,\n rejoin: lobby.rejoin ?? true,\n joinLink: lobby.joinLink ?? (web || engine === \"node-browser\"),\n meta: lobby.meta ?? {},\n },\n sync: { mode: sync.mode ?? \"raw\", tickRate, hashEveryTicks: sync.hashEveryTicks ?? tickRate },\n players: { identity: players.identity ?? \"name\", maxNameLength: players.maxNameLength ?? 24 },\n chat: { text: chat.text ?? true, voice: chat.voice ?? false, voiceMode: chat.voiceMode ?? \"pushToTalk\" },\n session: {\n hostLeaves: session.hostLeaves ?? \"end\",\n pause: session.pause ?? \"off\",\n readyTimeoutMs: session.readyTimeoutMs ?? 15000,\n peerTimeoutMs: session.peerTimeoutMs ?? 0,\n ...(session.rateLimit !== undefined ? { rateLimit: session.rateLimit } : {}),\n runInBackground: session.runInBackground ?? true,\n },\n platforms: { steam: platforms.steam ?? false, epic: platforms.epic ?? false, discord: platforms.discord ?? false },\n ui: {\n kind: ui.kind ?? (web ? \"drop-in\" : \"generated\"),\n ...(ui.entry !== undefined ? { entry: ui.entry } : {}),\n theme: ui.theme ?? \"auto\",\n },\n };\n return out as unknown as Blueprint;\n}\n\nfunction crossRules(b: Blueprint): void {\n if (!b.hosting.direct && !b.hosting.server) throw new Fail(\"hosting\", \"hosting_none\", \"at least one of direct, server must be true\");\n if (b.lobby.listed && b.lobby.approveJoins) throw new Fail(\"lobby\", \"listed_and_approve\", \"listed and approveJoins cannot both be true\");\n if (b.engine === \"web\" && b.hosting.direct) throw new Fail(\"hosting.direct\", \"direct_needs_listener\", \"engine web cannot host direct (a browser cannot listen on a port)\");\n const csharp = b.engine === \"godot-csharp\" || b.engine === \"unity\" || b.engine === \"dotnet\";\n if (b.sync.mode === \"replication\" && !csharp) throw new Fail(\"sync.mode\", \"replication_needs_csharp\", \"replication needs engine godot-csharp, unity or dotnet\");\n if (b.sync.mode === \"replication\" && b.session.hostLeaves === \"migrate\") throw new Fail(\"session.hostLeaves\", \"migrate_with_replication\", \"replication has no host migration; use end\");\n if (b.session.hostLeaves === \"migrate\" && !b.hosting.server) throw new Fail(\"session.hostLeaves\", \"migrate_needs_server\", \"migrate needs hosting.server (direct lobbies always end)\");\n}\n\n/**\n * Validate `json` (an already-parsed onelobby.json), fill every default and return the frozen result.\n * Throws a `BlueprintError` (`OneLobbyError` code `bad_config`, with `path` and `rule`) on the first problem.\n */\nexport function parseBlueprint(json: unknown): Blueprint {\n let b: Blueprint;\n try {\n check(ROOT, json, \"\");\n b = fill(strip(json) as Record<string, unknown>);\n crossRules(b);\n } catch (e) {\n if (e instanceof Fail) throw new BlueprintError(e.path, e.rule, e.detail);\n throw e;\n }\n return deepFreeze(b);\n}\n", "/**\n * The host side of Nostr signaling (spec/nostr-signaling.md s.4): each joiner pubkey becomes one hub session, as if it\n * had opened a TCP WebSocket. The hub speaks the usual protocol; this file only moves its texts over frames.\n */\nimport type { Clock } from \"../clock.js\";\nimport type { SignalingHub, Session } from \"../direct/hub.js\";\nimport { ReliableLink, type Frame } from \"./link.js\";\nimport { NostrRoom } from \"./room.js\";\nimport type { RelayConnect } from \"./relays.js\";\nimport { NEW_SESSION_BURST, MAX_SESSION_BURST, NEW_SESSION_REFILL_MS, WAITING_MAX, WAITING_STALE_MS, RELAY_REST_WAIT_MS, RELAY_REST_POLL_MS } from \"./limits.js\";\n\nexport interface NostrHostOptions {\n /** The version 1 join code this host publishes. */\n code: string;\n relays?: readonly string[];\n connect?: RelayConnect;\n clock: Clock;\n wall?: () => number;\n /** The number of connected relays changed. */\n onChange?: () => void;\n /** The lobby's player cap: the new-session burst grows to it (see newSessionBurst). */\n maxPlayers?: number;\n}\n\n/** A lobby sized for more than NEW_SESSION_BURST players gets a burst that fits it (up to MAX_SESSION_BURST). */\nexport function newSessionBurst(maxPlayers?: number): number {\n return Math.max(NEW_SESSION_BURST, Math.min(MAX_SESSION_BURST, maxPlayers ?? 0));\n}\n\ninterface Peer {\n link: ReliableLink;\n session: Session | null;\n}\n\nconst PING = '{\"t\":\"ping\",\"n\":0}';\nconst CLOSED_MEMORY = 512;\n/**\n * NEW joiner pubkeys per lobby are a token bucket: a burst of newSessionBurst(maxPlayers) at once (a party joining\n * together), then one more per NEW_SESSION_REFILL_MS. Refused pubkeys wait in a list (up to WAITING_MAX, oldest\n * first) and are answered the moment a token refills; a waiting pubkey whose `open` (repeated every 2 s) has not\n * been heard for WAITING_STALE_MS has given up and is dropped.\n */\nexport { NEW_SESSION_BURST, MAX_SESSION_BURST, NEW_SESSION_REFILL_MS };\n\nexport class NostrHostBridge {\n private readonly peers = new Map<string, Peer>();\n private readonly gone = new Set<string>();\n private stopped = false;\n private readonly burst: number;\n private newTokens: number;\n private newTokensAt: number | null = null;\n /** Refused new pubkeys, oldest first, with when their last `open` was heard. */\n private readonly waiting = new Map<string, number>();\n private cancelWaiting: (() => void) | null = null;\n\n private constructor(private readonly hub: SignalingHub, private readonly room: NostrRoom, private readonly clock: Clock, maxPlayers?: number) {\n this.burst = this.newTokens = newSessionBurst(maxPlayers);\n room.onFrame = (f) => this.onFrame(f);\n }\n\n static async start(hub: SignalingHub, o: NostrHostOptions): Promise<NostrHostBridge> {\n return new NostrHostBridge(hub, await NostrRoom.open(o), o.clock, o.maxPlayers);\n }\n\n get relaysConnected(): number {\n return this.room.relaysConnected;\n }\n\n get relaysTotal(): number {\n return this.room.relaysTotal;\n }\n\n /** Resolves true once one relay is connected, false after `timeoutMs`. */\n whenUp(timeoutMs: number): Promise<boolean> {\n return this.room.whenUp(timeoutMs);\n }\n\n get relaysSettled(): boolean {\n return this.room.relaysSettled;\n }\n\n /** Waits for the relays: true once one is connected (then up to RELAY_REST_WAIT_MS more, less when every relay has answered), false after `firstRelayMs`. */\n async settle(firstRelayMs: number): Promise<boolean> {\n if (!(await this.whenUp(firstRelayMs))) return false;\n for (let i = 0; i < RELAY_REST_WAIT_MS / RELAY_REST_POLL_MS && !this.relaysSettled; i++) await new Promise((r) => setTimeout(r, RELAY_REST_POLL_MS));\n return true;\n }\n\n /** Joiners currently attached. */\n get peerCount(): number {\n return this.peers.size;\n }\n\n close(): void {\n if (this.stopped) return;\n this.stopped = true;\n this.cancelWaiting?.();\n this.cancelWaiting = null;\n this.waiting.clear();\n for (const [pub, p] of this.peers) {\n p.link.close(\"host left\");\n if (p.session) this.hub.disconnect(p.session);\n this.peers.delete(pub);\n }\n this.room.close(true);\n }\n\n private onFrame(f: Frame): void {\n if (this.stopped) return;\n const known = this.peers.get(f.from);\n if (f.type === \"open\") {\n if (this.gone.has(f.from)) return;\n if (known) {\n known.link.heard();\n this.answer(f.from);\n } else if (this.waiting.has(f.from)) {\n this.waiting.set(f.from, this.clock.now()); // still trying: keep its place in the queue\n this.admitWaiting();\n } else if (this.waiting.size === 0 && this.admitNew()) {\n this.accept(f.from);\n } else if (this.waiting.size < WAITING_MAX) {\n this.waiting.set(f.from, this.clock.now()); // over the cap: answered when a token refills\n this.scheduleWaiting();\n } // waiting list full too: not answered, the joiner's next open retries\n return;\n }\n if (!known) return;\n known.link.receive(f);\n // a joiner's beat is its keep-alive: the hub counts it as activity (its pong is dropped below)\n if (f.type === \"beat\" && known.session) this.hub.receive(known.session, PING);\n }\n\n /** Arms one timer for the next token while anyone waits. */\n private scheduleWaiting(): void {\n if (this.cancelWaiting || this.waiting.size === 0 || this.stopped) return;\n const missing = Math.max(0, 1 - this.newTokens);\n this.cancelWaiting = this.clock.setTimeout(() => {\n this.cancelWaiting = null;\n this.admitWaiting();\n }, Math.max(1, Math.ceil(missing * NEW_SESSION_REFILL_MS)));\n }\n\n /** Answers waiting pubkeys, oldest first, while tokens last; drops the ones that gave up. */\n private admitWaiting(): void {\n if (this.stopped) return;\n const now = this.clock.now();\n for (const [pub, heard] of this.waiting) {\n if (now - heard > WAITING_STALE_MS || this.peers.has(pub) || this.gone.has(pub)) {\n this.waiting.delete(pub);\n continue;\n }\n if (!this.admitNew()) break;\n this.waiting.delete(pub);\n this.accept(pub);\n }\n this.scheduleWaiting();\n }\n\n /** Spends one token of the new-session bucket, or false when it is empty. */\n private admitNew(): boolean {\n const now = this.clock.now();\n if (this.newTokensAt !== null) {\n this.newTokens = Math.min(this.burst, this.newTokens + (now - this.newTokensAt) / NEW_SESSION_REFILL_MS);\n }\n this.newTokensAt = now;\n if (this.newTokens < 1 - 1e-6) return false; // fractional clock times can leave a full token at 0.9999999999999999\n this.newTokens = Math.max(0, this.newTokens - 1);\n return true;\n }\n\n private answer(pub: string): void {\n void this.room.send(pub, { type: \"open\", seq: 0, ack: 0 });\n }\n\n private accept(pub: string): void {\n const peer: Peer = { session: null, link: null as unknown as ReliableLink };\n peer.link = new ReliableLink({\n clock: this.clock,\n send: (out) => void this.room.send(pub, out),\n deliver: (text) => peer.session && this.hub.receive(peer.session, text),\n onPeerClose: () => this.detach(pub, peer),\n onDead: () => this.detach(pub, peer),\n });\n this.peers.set(pub, peer);\n this.answer(pub);\n const session = this.hub.connect(\n `nostr:${pub.slice(0, 16)}`,\n (text) => {\n if (!text.startsWith('{\"t\":\"pong\"')) peer.link.send(text); // keep-alive pongs never cross Nostr\n },\n () => {\n // the hub closed this session: say so, keep the pubkey out for the rest of the session\n peer.link.close();\n this.forget(pub, peer);\n },\n );\n if (peer.link.closed) return; // full: the hub already had us close it\n if (!session) {\n peer.link.close(\"closed\");\n this.forget(pub, peer);\n return;\n }\n peer.session = session;\n }\n\n private detach(pub: string, peer: Peer): void {\n peer.link.stop();\n if (peer.session) this.hub.disconnect(peer.session);\n peer.session = null;\n this.forget(pub, peer);\n }\n\n private forget(pub: string, peer: Peer): void {\n if (this.peers.get(pub) === peer) this.peers.delete(pub);\n this.gone.add(pub);\n if (this.gone.size > CLOSED_MEMORY) this.gone.delete(this.gone.values().next().value as string);\n }\n}\n", "/** Tiny Prometheus text exposition: labelled counters plus gauges read at scrape time (C#'s `Metrics` in `server/OneLobby.Hub`). */\nexport class Metrics {\n private readonly counters = new Map<string, number>();\n private readonly gauges = new Map<string, { help: string; read: () => number }>();\n private readonly help = new Map<string, string>();\n\n describe(name: string, help: string): void {\n this.help.set(name, help);\n }\n\n gauge(name: string, help: string, read: () => number): void {\n this.gauges.set(name, { help, read });\n }\n\n /** Counter name plus optional single label, e.g. inc(\"onelobby_path_total\", \"path\", \"relayed\"). */\n inc(name: string, label?: string, value?: string, by = 1): void {\n const key = keyOf(name, label, value);\n this.counters.set(key, (this.counters.get(key) ?? 0) + by);\n }\n\n get(name: string, label?: string, value?: string): number {\n return this.counters.get(keyOf(name, label, value)) ?? 0;\n }\n\n render(): string {\n let out = \"\";\n for (const name of [...this.gauges.keys()].sort(ordinal)) {\n const g = this.gauges.get(name)!;\n out += `# HELP ${name} ${g.help}\\n# TYPE ${name} gauge\\n${name} ${g.read()}\\n`;\n }\n let lastBase: string | null = null;\n for (const key of [...this.counters.keys()].sort(ordinal)) {\n const br = key.indexOf(\"{\");\n const base = br < 0 ? key : key.slice(0, br);\n if (base !== lastBase) {\n const h = this.help.get(base);\n if (h !== undefined) out += `# HELP ${base} ${h}\\n`;\n out += `# TYPE ${base} counter\\n`;\n lastBase = base;\n }\n out += `${key} ${this.counters.get(key)}\\n`;\n }\n return out;\n }\n}\n\nconst ordinal = (a: string, b: string): number => (a < b ? -1 : a > b ? 1 : 0);\nconst escape = (s: string): string => s.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"').replace(/\\n/g, \"\\\\n\");\nconst keyOf = (name: string, label?: string, value?: string): string => (label === undefined ? name : `${name}{${label}=\"${escape(value ?? \"\")}\"}`);\n", "/**\n * Small synchronous SHA-1 / HMAC-SHA1 and CSPRNG helpers for the signaling hub, so it runs in a browser tab as well as\n * in Node (WebCrypto's digests are async and the hub is sync). SHA-1 is used only for the TURN credential\n * (`base64(HMAC-SHA1(secret, \"expiry:connId\"))`, coturn's REST scheme) and for hashing client addresses in log lines.\n */\n\nconst enc = new TextEncoder();\n\nexport function sha1(data: Uint8Array): Uint8Array {\n const h = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0];\n const len = data.length;\n const padded = new Uint8Array((((len + 8) >> 6) << 6) + 64);\n padded.set(data);\n padded[len] = 0x80;\n const dv = new DataView(padded.buffer);\n dv.setUint32(padded.length - 8, Math.floor((len * 8) / 0x100000000));\n dv.setUint32(padded.length - 4, (len * 8) >>> 0);\n const w = new Uint32Array(80);\n for (let off = 0; off < padded.length; off += 64) {\n for (let i = 0; i < 16; i++) w[i] = dv.getUint32(off + i * 4);\n for (let i = 16; i < 80; i++) {\n const x = w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16];\n w[i] = (x << 1) | (x >>> 31);\n }\n let [a, b, c, d, e] = h;\n for (let i = 0; i < 80; i++) {\n let f: number;\n let k: number;\n if (i < 20) { f = (b & c) | (~b & d); k = 0x5a827999; }\n else if (i < 40) { f = b ^ c ^ d; k = 0x6ed9eba1; }\n else if (i < 60) { f = (b & c) | (b & d) | (c & d); k = 0x8f1bbcdc; }\n else { f = b ^ c ^ d; k = 0xca62c1d6; }\n const t = (((a << 5) | (a >>> 27)) + f + e + k + w[i]) | 0;\n e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;\n }\n h[0] = (h[0] + a) | 0; h[1] = (h[1] + b) | 0; h[2] = (h[2] + c) | 0; h[3] = (h[3] + d) | 0; h[4] = (h[4] + e) | 0;\n }\n const out = new Uint8Array(20);\n const odv = new DataView(out.buffer);\n for (let i = 0; i < 5; i++) odv.setUint32(i * 4, h[i] >>> 0);\n return out;\n}\n\nexport function hmacSha1(key: Uint8Array, msg: Uint8Array): Uint8Array {\n const k = key.length > 64 ? sha1(key) : key;\n const ipad = new Uint8Array(64 + msg.length);\n const opad = new Uint8Array(84);\n for (let i = 0; i < 64; i++) {\n const kb = i < k.length ? k[i] : 0;\n ipad[i] = kb ^ 0x36;\n opad[i] = kb ^ 0x5c;\n }\n ipad.set(msg, 64);\n opad.set(sha1(ipad), 64);\n return sha1(opad);\n}\n\nexport function toHex(b: Uint8Array): string {\n let s = \"\";\n for (const x of b) s += x.toString(16).padStart(2, \"0\");\n return s;\n}\n\nfunction toBase64(b: Uint8Array): string {\n let s = \"\";\n for (const x of b) s += String.fromCharCode(x);\n return btoa(s);\n}\n\n/** `base64(HMAC-SHA1(secret, username))`, the TURN REST credential. */\nexport function turnCredential(secret: string, username: string): string {\n return toBase64(hmacSha1(enc.encode(secret), enc.encode(username)));\n}\n\n/** First 8 hex chars of SHA-1 over `text`: a short stable tag for log lines (not a security feature). */\nexport function shortHash(text: string): string {\n return toHex(sha1(enc.encode(text))).slice(0, 8);\n}\n\n/** `n` random bytes as lowercase hex, from the platform CSPRNG. */\nexport function randomHex(n: number): string {\n return toHex(globalThis.crypto.getRandomValues(new Uint8Array(n)));\n}\n\n/** Uniform integer in [0, n) from the platform CSPRNG (rejection sampling, no modulo bias). */\nexport function randomBelow(n: number): number {\n const range = 0x100000000;\n const limit = range - (range % n);\n const buf = new Uint32Array(1);\n for (;;) {\n globalThis.crypto.getRandomValues(buf);\n if (buf[0] < limit) return buf[0] % n;\n }\n}\n", "/**\n * The signaling hub (protocol lk1) in TypeScript, for a Node process or a browser tab that hosts direct lobbies\n * (docs/design/direct-ip.md s.2): a line-by-line port of `server/OneLobby.Hub/Hub.cs`, held to the same rules by\n * `conformance/server`. Transport-agnostic: feed it text frames with `receive`, call `tick` about once a second.\n * Metrics (same names, labels and help text as C#, rendered by `metrics.render()`) are counted too, so every hub shape\n * looks the same from outside; everything else (codes, list, resume, private lobbies, kicks, migration claims, TURN\n * credentials, rate limits) behaves as in C#.\n */\n\nimport { Metrics } from \"./metrics.js\";\nimport { randomBelow, randomHex, shortHash, turnCredential } from \"./sha1.js\";\n\nexport { Metrics };\nexport const PROTO = \"lk1\";\nconst NAME_MAX = 24;\nconst LOBBY_NAME_MAX = 48;\nconst META_KEYS_MAX = 16;\nconst META_LEN_MAX = 64;\nconst PENDING_MAX = 8;\nconst CROCKFORD = \"0123456789ABCDEFGHJKMNPQRSTVWXYZ\";\nconst GAME_ID = /^[A-Za-z0-9._-]{1,64}$/;\n\n/** The settings the hub reads; same names and defaults as C#'s `HubOptions`. */\nexport interface HubOptions {\n turnUrls: string[];\n turnSecret: string;\n stunUrls: string[];\n region: string;\n /** Empty = every gameId allowed. */\n allowedGames: string[];\n gameQuotas: Record<string, number>;\n maxLobbies: number;\n maxPeers: number;\n /** Default and advertised maxPlayers limit; create accepts 2..this. */\n hardMaxPlayers: number;\n rateScale: number;\n turnTtlSeconds: number;\n maxMessageBytes: number;\n disableIpLimits: boolean;\n createPerMin: number;\n joinPerMin: number;\n signalPerSec: number;\n /** Open sockets per address (loopback included); `disableIpLimits` lifts it, `rateScale` scales it. */\n socketsPerIp: number;\n openLobbiesPerIp: number;\n idleTimeoutMs: number;\n resumeWindowMs: number;\n codeReuseMs: number;\n listLimitMax: number;\n /** One lobby per hub (direct IP): later creates are refused, a join without a code joins it. */\n singleLobby: boolean;\n /** When set, join must carry the same password or gets bad_password. */\n password: string | null;\n /** False: lobby messages carry code \"\" and list is empty. */\n issueCodes: boolean;\n}\n\nexport function defaultHubOptions(): HubOptions {\n return {\n turnUrls: [], turnSecret: \"\", stunUrls: [\"stun:stun.l.google.com:19302\"], region: \"\", allowedGames: [], gameQuotas: {},\n maxLobbies: 1000, maxPeers: 10000, hardMaxPlayers: 64, rateScale: 1, turnTtlSeconds: 3600, maxMessageBytes: 16 * 1024,\n disableIpLimits: false, createPerMin: 10, joinPerMin: 60, signalPerSec: 20, socketsPerIp: 32, openLobbiesPerIp: 8, idleTimeoutMs: 30_000,\n resumeWindowMs: 60_000, codeReuseMs: 24 * 3600 * 1000, listLimitMax: 100, singleLobby: false, password: null, issueCodes: true,\n };\n}\n\n/** Settings for a hub inside the host's game (docs/design/direct-ip.md s.2): no codes, no list, no ICE servers. */\nexport function directHubOptions(maxPlayers: number, password?: string | null): HubOptions {\n const n = Math.min(250, Math.max(2, maxPlayers));\n return {\n ...defaultHubOptions(),\n singleLobby: true, issueCodes: false, password: password ? password : null, stunUrls: [], turnUrls: [],\n maxLobbies: 1, hardMaxPlayers: n, maxPeers: n + 8,\n };\n}\n\n/** A session's total `signal` budget is this many targets' worth of `signalPerSec` (spec/signaling.md rate limits). */\nexport const SIGNAL_FANOUT = 8;\n/** Hub sessions bridged from Nostr have the address `nostr:<pubkey prefix>`. */\nexport const NOSTR_ADDR = \"nostr:\";\n/** `signal` per second per (sender, Nostr target): relays reject high publish rates. */\nexport const NOSTR_SIGNAL_PER_SEC = 10;\n\n/** Sliding-window counter: at most `limit` events per `windowMs`. */\nexport class RateWindow {\n private hits: number[] = [];\n constructor(readonly limit: number, readonly windowMs: number) {}\n tryHit(now: number): boolean {\n this.trim(now);\n if (this.hits.length >= this.limit) return false;\n this.hits.push(now);\n return true;\n }\n retryAfterMs(now: number): number {\n this.trim(now);\n return this.hits.length < this.limit || this.hits.length === 0 ? 0 : Math.max(1, this.windowMs - (now - this.hits[0]));\n }\n isIdle(now: number): boolean {\n this.trim(now);\n return this.hits.length === 0;\n }\n private trim(now: number): void {\n while (this.hits.length > 0 && now - this.hits[0] >= this.windowMs) this.hits.shift();\n }\n}\n\n/** Control characters and bidi overrides out, then at most `max` UTF-16 units without splitting a surrogate pair. */\nexport function sanitize(s: string | null | undefined, max: number, fallback = \"\"): string {\n if (!s) return fallback;\n let r = \"\";\n for (const ch of s) {\n const c = ch.charCodeAt(0);\n if (ch.length === 1 && (c <= 0x1f || (c >= 0x7f && c <= 0x9f) || (c >= 0x202a && c <= 0x202e) || (c >= 0x2066 && c <= 0x2069))) continue;\n r += ch;\n }\n if (r.length > max) {\n r = r.slice(0, max);\n const last = r.charCodeAt(r.length - 1);\n if (last >= 0xd800 && last <= 0xdbff) r = r.slice(0, -1);\n }\n return r.length === 0 ? fallback : r;\n}\n\nfunction normalizeCode(input: unknown): string | null {\n if (typeof input !== \"string\") return null;\n let out = \"\";\n for (const raw of input) {\n if (raw === \" \" || raw === \"-\") continue;\n let ch = raw >= \"a\" && raw <= \"z\" ? raw.toUpperCase() : raw;\n if (ch === \"I\" || ch === \"L\") ch = \"1\";\n else if (ch === \"O\") ch = \"0\";\n if (CROCKFORD.indexOf(ch) < 0 || out.length >= 6) return null;\n out += ch;\n }\n return out.length === 6 ? out : null;\n}\n\ntype Json = Record<string, unknown>;\n\n/** One signaling connection. The hub only calls `send` and `close`. */\nexport class Session {\n closed = false;\n lastSeen = 0;\n helloDone = false;\n gameId = \"\";\n netVersion = \"\";\n name = \"\";\n lobby: HubLobby | null = null;\n member: Member | null = null;\n pendingIn: HubLobby | null = null;\n pendingMember: Member | null = null;\n constructor(\n readonly id: number,\n readonly ip: string,\n readonly connId: string,\n readonly send: (text: string) => void,\n readonly close: (code?: number, reason?: string) => void,\n ) {}\n /** Nostr joiners: the remote address of the host's WebRTC link to this session, once known (kick bans it too). */\n linkIp: string | null = null;\n /** One window per target member (a member belongs to one lobby, so a new lobby starts fresh). */\n readonly signalRates = new Map<Member, RateWindow>();\n /** All signals of this session, so the per-target budget does not grow without bound with the lobby. */\n signalTotal: RateWindow | null = null;\n}\n\nclass Member {\n peerId = 0;\n name = \"\";\n joinedAt = 0;\n session: Session | null = null;\n detachedAt = 0;\n token = \"\";\n ip = \"\";\n}\n\nclass HubLobby {\n code = \"\";\n gameId = \"\";\n netVersion = \"\";\n name = \"\";\n visibility = \"public\";\n mode = \"lockstep\";\n tickRate = 20;\n lateJoin = false;\n relayOnly = false;\n phase = \"lobby\";\n maxPlayers = 0;\n createdAt = 0;\n ownerIp = \"\";\n meta: Record<string, string> = {};\n members: Member[] = [];\n pending: Member[] = [];\n kicked = new Set<string>();\n hostId = 0;\n epoch = 0;\n /** Highest valid host_changed claim a member made while the host was still connected; applied when the host goes. */\n pendingClaimPeer = 0;\n pendingClaimEpoch = 0;\n nextPeerId = 1;\n find(peerId: number): Member | undefined {\n return this.members.find((m) => m.peerId === peerId);\n }\n}\n\ninterface IpState {\n create: RateWindow;\n join: RateWindow;\n open: number;\n sockets: number;\n}\n\nconst KNOWN_TYPES = new Set([\"hello\", \"create\", \"join\", \"list\", \"leave\", \"signal\", \"meta\", \"phase\", \"kick\", \"approve\", \"ping\", \"host_changed\", \"path\"]);\n\nconst str = (o: Json, k: string): string | null => (typeof o[k] === \"string\" ? (o[k] as string) : null);\nconst int = (o: Json, k: string): number | null => {\n const v = o[k];\n return typeof v === \"number\" && Number.isInteger(v) && Math.abs(v) < 2147483647 ? v : null;\n};\nconst bool = (o: Json, k: string): boolean | null => (typeof o[k] === \"boolean\" ? (o[k] as boolean) : null);\nconst has = (o: Json, k: string): boolean => o[k] !== undefined && o[k] !== null;\nconst field = (name: string): Json => ({ field: name });\nconst hex = (n: number): string => randomHex(n);\nconst byCode = (a: string, b: string): number => (a < b ? -1 : a > b ? 1 : 0);\n\n/** Not sanitised (spec/signaling.md): over the limits or non-string values are rejected. */\nfunction readMeta(node: unknown): { meta: Record<string, string> } | { err: string } {\n if (node === undefined || node === null) return { meta: {} };\n if (typeof node !== \"object\" || Array.isArray(node)) return { err: \"meta must be an object of strings\" };\n const entries = Object.entries(node as Json);\n if (entries.length > META_KEYS_MAX) return { err: `meta allows at most ${META_KEYS_MAX} keys` };\n const meta: Record<string, string> = {};\n for (const [k, v] of entries.sort(([a], [b]) => byCode(a, b))) {\n if (typeof v !== \"string\") return { err: \"meta values must be strings\" };\n if (k.length > META_LEN_MAX || v.length > META_LEN_MAX) return { err: `meta keys and values are limited to ${META_LEN_MAX} chars` };\n meta[k] = v;\n }\n return { meta };\n}\n\nexport interface HubDeps {\n /** Unix milliseconds; injected so tests drive time. */\n now?: () => number;\n /** Uniform integer in [0, n). */\n randomInt?: (n: number) => number;\n log?: (line: string) => void;\n}\n\n/** All signaling state and rules (DESIGN s.6, s.7, s.9, s.10, s.12; spec/signaling.md). */\nexport class SignalingHub {\n private readonly o: HubOptions;\n private readonly now: () => number;\n private readonly rand: (n: number) => number;\n private readonly log: (line: string) => void;\n private readonly lobbies = new Map<string, HubLobby>();\n private readonly retired = new Map<string, number>();\n private readonly tokens = new Map<string, { lobby: HubLobby; member: Member }>();\n private readonly sessions = new Map<number, Session>();\n private readonly gameLobbies = new Map<string, number>();\n private readonly ips = new Map<string, IpState>();\n private nextSession = 0;\n private createdOnce = false;\n private members = 0;\n private readonly logSalt = hex(8);\n readonly metrics = new Metrics();\n\n constructor(options: Partial<HubOptions> = {}, deps: HubDeps = {}) {\n this.o = { ...defaultHubOptions(), ...options };\n this.now = deps.now ?? (() => Date.now());\n this.rand = deps.randomInt ?? ((n) => randomBelow(n));\n this.log = deps.log ?? (() => {});\n const m = this.metrics;\n m.gauge(\"onelobby_lobbies\", \"Live lobbies.\", () => this.lobbyCount);\n m.gauge(\"onelobby_sockets\", \"Open signaling sockets.\", () => this.socketCount);\n m.gauge(\"onelobby_lobbies_retired\", \"Join codes held back from reuse.\", () => this.retired.size);\n m.describe(\"onelobby_messages_total\", \"Client messages received, by type.\");\n m.describe(\"onelobby_errors_total\", \"Error replies sent, by code.\");\n m.describe(\"onelobby_path_total\", \"ICE path reports from peers: direct vs relayed (DESIGN s.10).\");\n m.describe(\"onelobby_connections_total\", \"Sockets accepted since start.\");\n m.describe(\"onelobby_lobbies_created_total\", \"Lobbies created since start.\");\n m.describe(\"onelobby_rate_limit_hits_total\", \"Requests refused by a rate limit, by kind.\");\n }\n\n get lobbyCount(): number {\n return this.lobbies.size;\n }\n get socketCount(): number {\n return this.sessions.size;\n }\n private get scale(): number {\n return Math.max(1, this.o.rateScale);\n }\n\n // ---------------------------------------------------------------- connection lifecycle\n\n /** Registers a socket. Returns null (after sending server_full and closing) when over maxPeers. */\n connect(ip: string, send: (text: string) => void, close: (code?: number, reason?: string) => void): Session | null {\n const s = new Session(++this.nextSession, ip, hex(6), send, close);\n s.lastSeen = this.now();\n this.metrics.inc(\"onelobby_connections_total\");\n if (this.sessions.size >= this.o.maxPeers) {\n this.error(s, \"server_full\", \"server is at its peer limit\");\n s.close();\n return null;\n }\n const st = this.ipOf(ip);\n if (!this.o.disableIpLimits && st.sockets >= this.o.socketsPerIp * this.scale) {\n this.rateLimited(s, \"sockets\", 5000, \"max_sockets\");\n s.close(1008, \"too_many_sockets\");\n return null;\n }\n this.sessions.set(s.id, s);\n st.sockets++;\n return s;\n }\n\n disconnect(s: Session): void {\n if (!this.sessions.delete(s.id)) return;\n s.closed = true;\n const st = this.ips.get(s.ip);\n if (st) st.sockets--;\n if (s.pendingIn) {\n s.pendingIn.pending = s.pendingIn.pending.filter((p) => p !== s.pendingMember);\n s.pendingIn = null;\n s.pendingMember = null;\n }\n const m = s.member;\n const lobby = s.lobby;\n if (m && lobby) {\n // The slot and token stay reserved for the resume window (tick expires them); others hear peer_left{timeout} now.\n m.session = null;\n m.detachedAt = this.now();\n s.member = null;\n s.lobby = null;\n this.logEvent(\"detach\", lobby, s);\n this.broadcast(lobby, { t: \"peer_left\", peerId: m.peerId, reason: \"timeout\" });\n this.metrics.inc(\"onelobby_peers_left_total\", \"reason\", \"timeout\");\n if (lobby.hostId === m.peerId) this.promoteHost(lobby, m.peerId);\n }\n }\n\n /**\n * The host's WebRTC link to joiner `peerId` has the remote address `ip` (selected candidate pair; the host's own\n * lobby reports it, a relayed or mDNS address must not be passed). Only Nostr sessions take it: their socket address\n * is a pubkey the joiner chose. A joiner whose link address is banned is removed as kicked; a later kick bans it.\n */\n reportLinkAddress(peerId: number, ip: string): void {\n for (const lobby of this.lobbies.values()) {\n const mem = lobby.find(peerId);\n const sess = mem?.session;\n if (!mem || !sess || !sess.ip.startsWith(NOSTR_ADDR)) continue;\n sess.linkIp = ip;\n if (lobby.kicked.has(ip)) {\n this.removeMember(lobby, mem, \"kicked\");\n this.closeSession(sess);\n }\n return;\n }\n }\n\n private closeSession(s: Session): void {\n s.close();\n this.disconnect(s);\n }\n\n // ---------------------------------------------------------------- time\n\n /** Call about once a second: idle timeouts, resume-window expiry, code retirement. */\n tick(): void {\n const now = this.now();\n for (const s of [...this.sessions.values()])\n if (now - s.lastSeen > this.o.idleTimeoutMs) {\n this.metrics.inc(\"onelobby_idle_timeouts_total\");\n this.closeSession(s);\n }\n for (const lobby of [...this.lobbies.values()])\n for (const m of [...lobby.members]) if (m.detachedAt > 0 && now - m.detachedAt >= this.o.resumeWindowMs) this.removeMember(lobby, m, \"timeout\");\n for (const [code, at] of [...this.retired]) if (now - at >= this.o.codeReuseMs) this.retired.delete(code);\n for (const [key, st] of [...this.ips])\n if (st.sockets <= 0 && st.open <= 0 && st.create.isIdle(now) && st.join.isIdle(now)) this.ips.delete(key);\n }\n\n // ---------------------------------------------------------------- inbound\n\n receive(s: Session, text: string): void {\n if (s.closed) return;\n s.lastSeen = this.now();\n if (text.length > this.o.maxMessageBytes) {\n this.error(s, \"bad_request\", \"message too large\");\n this.closeSession(s);\n return;\n }\n let msg: Json | null = null;\n try {\n const v = JSON.parse(text) as unknown;\n msg = v && typeof v === \"object\" && !Array.isArray(v) ? (v as Json) : null;\n } catch {\n msg = null;\n }\n const t = msg ? str(msg, \"t\") : null;\n if (!msg || t === null) {\n this.error(s, \"bad_request\", \"expected a JSON object with a string t\", field(\"t\"));\n return;\n }\n this.metrics.inc(\"onelobby_messages_total\", \"type\", KNOWN_TYPES.has(t) ? t : \"unknown\");\n if (t === \"hello\") return this.hello(s, msg);\n if (!s.helloDone) {\n if (KNOWN_TYPES.has(t)) this.error(s, \"bad_request\", \"send hello first\");\n return;\n }\n switch (t) {\n case \"create\": return this.create(s, msg);\n case \"join\": return this.join(s, msg);\n case \"list\": return this.list(s, msg);\n case \"leave\": return this.leave(s);\n case \"signal\": return this.signal(s, msg);\n case \"meta\": return this.hostMeta(s, msg);\n case \"phase\": return this.hostPhase(s, msg);\n case \"kick\": return this.kick(s, msg);\n case \"approve\": return this.approve(s, msg);\n case \"ping\": {\n const n = int(msg, \"n\");\n if (n === null || n < 0) return this.error(s, \"bad_request\", \"ping needs an integer n\", field(\"n\"));\n return this.send(s, { t: \"pong\", n });\n }\n case \"host_changed\": return this.claimHost(s, msg);\n case \"path\": return this.pathReport(s, msg);\n default: return; // unknown types are ignored\n }\n }\n\n // ---------------------------------------------------------------- hello / welcome\n\n private hello(s: Session, m: Json): void {\n if (s.helloDone) return this.error(s, \"bad_request\", \"hello already sent\");\n if (str(m, \"proto\") !== PROTO) {\n this.error(s, \"bad_request\", \"unsupported proto, expected lk1\", { supported: [PROTO] });\n this.closeSession(s);\n return;\n }\n if (has(m, \"name\") && str(m, \"name\") === null) return this.error(s, \"bad_request\", \"name must be a string\", field(\"name\"));\n if (has(m, \"client\") && (typeof m.client !== \"object\" || Array.isArray(m.client)))\n return this.error(s, \"bad_request\", \"client must be an object\", field(\"client\"));\n const gameId = str(m, \"gameId\");\n const netVersion = str(m, \"netVersion\");\n if (gameId === null || !GAME_ID.test(gameId)) return this.error(s, \"bad_request\", \"gameId must match [A-Za-z0-9._-]{1,64}\", field(\"gameId\"));\n if (!netVersion || netVersion.length > 32) return this.error(s, \"bad_request\", \"netVersion must be 1-32 characters\", field(\"netVersion\"));\n if (this.o.allowedGames.length > 0 && !this.o.allowedGames.includes(gameId)) {\n this.error(s, \"game_not_allowed\", \"this server does not host that game\");\n this.closeSession(s);\n return;\n }\n s.gameId = gameId;\n s.netVersion = netVersion;\n s.name = sanitize(str(m, \"name\"), NAME_MAX, \"Player\");\n s.helloDone = true;\n\n let resumed: Member | null = null;\n let rl: HubLobby | null = null;\n const token = str(m, \"resumeToken\");\n const entry = token !== null ? this.tokens.get(token) : undefined;\n if (entry && entry.lobby.gameId === gameId && entry.lobby.netVersion === netVersion) {\n // Validate first: a live socket (take-over) or a detached seat still inside the resume window. Only a valid\n // resume closes the old socket (it detaches normally, which starts the window anew).\n const old = entry.member.session;\n const takeOver = old !== null && old !== undefined && old !== s;\n const inWindow = entry.member.detachedAt > 0 && this.now() - entry.member.detachedAt < this.o.resumeWindowMs;\n if (takeOver || inWindow) {\n if (takeOver) this.closeSession(old!);\n resumed = entry.member;\n rl = entry.lobby;\n }\n }\n\n const w: Json = {\n t: \"welcome\",\n ice: this.iceList(s),\n serverTime: this.now(),\n limits: { maxMsg: this.o.maxMessageBytes, maxPlayers: this.o.hardMaxPlayers },\n };\n if (resumed) w.peerId = resumed.peerId;\n this.send(s, w);\n\n if (resumed && rl) {\n this.tokens.delete(resumed.token);\n resumed.token = hex(16);\n this.tokens.set(resumed.token, { lobby: rl, member: resumed });\n resumed.session = s;\n resumed.detachedAt = 0;\n s.lobby = rl;\n s.member = resumed;\n this.logEvent(\"resume\", rl, s);\n this.metrics.inc(\"onelobby_resumes_total\");\n for (const other of rl.members) if (other !== resumed) this.sendTo(other, { t: \"peer_joined\", peer: this.peerObj(resumed) });\n this.send(s, this.lobbyMsg(rl, resumed));\n }\n }\n\n private iceList(s: Session): Json[] {\n const ice: Json[] = [];\n if (this.o.stunUrls.length > 0) ice.push({ urls: [...this.o.stunUrls] });\n if (this.o.turnUrls.length > 0 && this.o.turnSecret.length > 0) {\n const expiry = Math.floor(this.now() / 1000) + this.o.turnTtlSeconds;\n const username = `${expiry}:${s.connId}`;\n const credential = turnCredential(this.o.turnSecret, username);\n ice.push({ urls: [...this.o.turnUrls], username, credential, ttl: this.o.turnTtlSeconds });\n }\n return ice;\n }\n\n // ---------------------------------------------------------------- create / join / list / leave\n\n private ipOf(ip: string): IpState {\n let st = this.ips.get(ip);\n if (!st) {\n st = { create: new RateWindow(this.o.createPerMin * this.scale, 60_000), join: new RateWindow(this.o.joinPerMin * this.scale, 60_000), open: 0, sockets: 0 };\n this.ips.set(ip, st);\n }\n return st;\n }\n\n private create(s: Session, m: Json): void {\n if (s.lobby || s.pendingIn) return this.error(s, \"bad_request\", \"already in a lobby\");\n const ip = this.ipOf(s.ip);\n const now = this.now();\n if (!this.o.disableIpLimits) {\n if (!ip.create.tryHit(now)) return this.rateLimited(s, \"create\", ip.create.retryAfterMs(now), null);\n if (ip.open >= this.o.openLobbiesPerIp * this.scale) return this.rateLimited(s, \"open_lobbies\", 5000, \"max_lobbies\");\n }\n if (this.o.singleLobby && this.createdOnce) return this.error(s, \"server_full\", \"this host runs one lobby\");\n if (this.lobbies.size >= this.o.maxLobbies || this.members >= this.o.maxPeers) return this.error(s, \"server_full\", \"server is at its lobby limit\");\n const quota = this.o.gameQuotas[s.gameId];\n if (quota !== undefined && (this.gameLobbies.get(s.gameId) ?? 0) >= quota) return this.error(s, \"server_full\", \"lobby quota for this game is reached\");\n\n if (has(m, \"name\") && str(m, \"name\") === null) return this.error(s, \"bad_request\", \"name must be a string\", field(\"name\"));\n const vis = str(m, \"visibility\") ?? \"public\";\n const mode = str(m, \"mode\") ?? \"lockstep\";\n if (vis !== \"public\" && vis !== \"unlisted\" && vis !== \"private\") return this.error(s, \"bad_request\", \"visibility must be public, unlisted or private\", field(\"visibility\"));\n if (mode !== \"lockstep\" && mode !== \"raw\") return this.error(s, \"bad_request\", \"mode must be lockstep or raw\", field(\"mode\"));\n if (has(m, \"maxPlayers\") && int(m, \"maxPlayers\") === null) return this.error(s, \"bad_request\", \"maxPlayers must be an integer\", field(\"maxPlayers\"));\n const maxPlayers = int(m, \"maxPlayers\") ?? 8;\n if (maxPlayers < 2 || maxPlayers > this.o.hardMaxPlayers) return this.error(s, \"bad_request\", `maxPlayers must be 2-${this.o.hardMaxPlayers}`, field(\"maxPlayers\"));\n if (has(m, \"tickRate\") && int(m, \"tickRate\") === null) return this.error(s, \"bad_request\", \"tickRate must be an integer\", field(\"tickRate\"));\n const tick = int(m, \"tickRate\") ?? 20;\n if (tick < 1 || tick > 255) return this.error(s, \"bad_request\", \"tickRate must be 1-255\", field(\"tickRate\"));\n if (has(m, \"lateJoin\") && bool(m, \"lateJoin\") === null) return this.error(s, \"bad_request\", \"lateJoin must be a boolean\", field(\"lateJoin\"));\n if (has(m, \"relayOnly\") && bool(m, \"relayOnly\") === null) return this.error(s, \"bad_request\", \"relayOnly must be a boolean\", field(\"relayOnly\"));\n const meta = readMeta(m.meta);\n if (\"err\" in meta) return this.error(s, \"bad_request\", meta.err, field(\"meta\"));\n\n const code = this.newCode();\n if (code === null) return this.error(s, \"server_full\", \"could not allocate a join code\");\n\n const lobby = new HubLobby();\n Object.assign(lobby, {\n code, gameId: s.gameId, netVersion: s.netVersion, name: sanitize(str(m, \"name\"), LOBBY_NAME_MAX, `${s.name}'s lobby`),\n visibility: vis, mode, tickRate: tick, lateJoin: bool(m, \"lateJoin\") ?? false, relayOnly: bool(m, \"relayOnly\") ?? false,\n maxPlayers, createdAt: now, ownerIp: s.ip, meta: meta.meta,\n });\n this.lobbies.set(code, lobby);\n this.createdOnce = true;\n this.gameLobbies.set(s.gameId, (this.gameLobbies.get(s.gameId) ?? 0) + 1);\n ip.open++;\n this.metrics.inc(\"onelobby_lobbies_created_total\");\n const host = this.addMember(lobby, s, null);\n lobby.hostId = host.peerId;\n this.logEvent(\"create\", lobby, s);\n this.send(s, this.lobbyMsg(lobby, host));\n }\n\n private join(s: Session, m: Json): void {\n if (s.lobby || s.pendingIn) return this.error(s, \"bad_request\", \"already in a lobby\");\n const ip = this.ipOf(s.ip);\n const now = this.now();\n if (!this.o.disableIpLimits && !ip.join.tryHit(now)) return this.rateLimited(s, \"join\", ip.join.retryAfterMs(now), null);\n let lobby: HubLobby | undefined;\n if (this.o.singleLobby && !has(m, \"code\")) {\n // direct IP: the address is the invitation, so a join without a code means the one lobby\n lobby = this.lobbies.values().next().value;\n if (!lobby || lobby.gameId !== s.gameId) return this.error(s, \"unknown_code\", \"this host has no open lobby for that game\");\n } else {\n const code = normalizeCode(m.code);\n if (code === null) return this.error(s, \"bad_request\", \"malformed join code\", field(\"code\"));\n lobby = this.lobbies.get(code);\n if (!lobby || lobby.gameId !== s.gameId) return this.error(s, \"unknown_code\", \"no lobby with that code\");\n }\n if (this.o.password !== null && str(m, \"password\") !== this.o.password) return this.error(s, \"bad_password\", \"wrong password for this lobby\");\n if (lobby.netVersion !== s.netVersion)\n return this.error(s, \"version_mismatch\", `host is on ${lobby.netVersion}, you are on ${s.netVersion}`, { hostNetVersion: lobby.netVersion, yourNetVersion: s.netVersion });\n if (lobby.kicked.has(s.ip)) return this.error(s, \"kicked\", \"you were removed from this lobby\");\n if (lobby.phase === \"in_game\" && !lobby.lateJoin) return this.error(s, \"in_game\", \"the game has already started\");\n if (lobby.members.length >= lobby.maxPlayers) return this.error(s, \"lobby_full\", \"lobby is full\");\n if (this.members >= this.o.maxPeers) return this.error(s, \"server_full\", \"server is at its peer limit\");\n\n if (lobby.visibility === \"private\") {\n if (lobby.pending.length >= PENDING_MAX) return this.error(s, \"lobby_full\", \"too many pending requests\");\n const pm = new Member();\n pm.peerId = lobby.nextPeerId++;\n pm.name = s.name;\n pm.session = s;\n pm.ip = s.ip;\n lobby.pending.push(pm);\n s.pendingIn = lobby;\n s.pendingMember = pm;\n const hs = lobby.find(lobby.hostId)?.session;\n if (hs) this.send(hs, { t: \"join_request\", peer: this.peerObj(pm) });\n this.logEvent(\"join_request\", lobby, s);\n return;\n }\n this.admit(lobby, s, null);\n }\n\n private admit(lobby: HubLobby, s: Session, pending: Member | null): void {\n const joiner = this.addMember(lobby, s, pending);\n this.logEvent(\"join\", lobby, s);\n for (const other of lobby.members) if (other !== joiner) this.sendTo(other, { t: \"peer_joined\", peer: this.peerObj(joiner) });\n // A lobby whose host slot is only reserved (host dropped, nobody was connected) hands over to the first newcomer.\n if (!lobby.find(lobby.hostId)?.session) this.promoteHost(lobby, lobby.hostId);\n this.send(s, this.lobbyMsg(lobby, joiner));\n }\n\n private addMember(lobby: HubLobby, s: Session, reuse: Member | null): Member {\n const mem = reuse ?? new Member();\n if (!reuse) mem.peerId = lobby.nextPeerId++;\n mem.name = s.name;\n mem.session = s;\n mem.ip = s.ip;\n mem.joinedAt = this.now();\n mem.detachedAt = 0;\n mem.token = hex(16);\n lobby.members.push(mem);\n this.members++;\n this.tokens.set(mem.token, { lobby, member: mem });\n s.lobby = lobby;\n s.member = mem;\n s.pendingIn = null;\n s.pendingMember = null;\n return mem;\n }\n\n private list(s: Session, m: Json): void {\n const vis = str(m, \"visibility\");\n if (!this.o.issueCodes) return this.send(s, { t: \"lobbies\", items: [] });\n if (vis !== \"public\") return this.error(s, \"bad_request\", \"only public lobbies can be listed\", field(\"visibility\"));\n let limit = 50;\n if (has(m, \"limit\")) {\n const l = int(m, \"limit\");\n if (l === null || l < 1 || l > this.o.listLimitMax) return this.error(s, \"bad_request\", `limit must be 1-${this.o.listLimitMax}`, field(\"limit\"));\n limit = l;\n }\n let offset = 0;\n if (has(m, \"cursor\")) {\n const c = str(m, \"cursor\");\n if (c === null || !/^\\d+$/.test(c)) return this.error(s, \"bad_request\", \"bad cursor\", field(\"cursor\"));\n offset = Number(c);\n }\n let filter: Record<string, string> | null = null;\n if (has(m, \"meta\")) {\n if (typeof m.meta !== \"object\" || Array.isArray(m.meta)) return this.error(s, \"bad_request\", \"meta must be an object\", field(\"meta\"));\n filter = {};\n for (const [k, v] of Object.entries(m.meta as Json)) {\n if (typeof v !== \"string\") return this.error(s, \"bad_request\", \"meta values must be strings\", field(\"meta\"));\n filter[k] = v;\n }\n }\n const all = [...this.lobbies.values()]\n .filter((l) => l.gameId === s.gameId && l.netVersion === s.netVersion && l.visibility === \"public\" && (l.phase !== \"in_game\" || l.lateJoin)\n && (filter === null || Object.entries(filter).every(([k, v]) => l.meta[k] === v)))\n .sort((a, b) => a.createdAt - b.createdAt || byCode(a.code, b.code));\n const page = all.slice(offset, offset + limit);\n const items = page.map((l) => ({\n code: l.code, gameId: l.gameId, netVersion: l.netVersion, name: l.name, hostName: l.find(l.hostId)?.name ?? \"\",\n players: l.members.length, maxPlayers: l.maxPlayers, phase: l.phase, meta: { ...l.meta }, region: this.o.region, createdAt: l.createdAt,\n }));\n const reply: Json = { t: \"lobbies\", items };\n if (offset + page.length < all.length) reply.cursor = String(offset + page.length);\n this.send(s, reply);\n }\n\n private leave(s: Session): void {\n if (s.pendingIn) {\n s.pendingIn.pending = s.pendingIn.pending.filter((p) => p !== s.pendingMember);\n s.pendingIn = null;\n s.pendingMember = null;\n return;\n }\n if (!s.lobby || !s.member) return;\n this.removeMember(s.lobby, s.member, \"left\");\n }\n\n // ---------------------------------------------------------------- relay + host-only\n\n private signal(s: Session, m: Json): void {\n const lobby = s.lobby;\n const me = s.member;\n if (!lobby || !me) return this.error(s, \"bad_request\", \"not in a lobby\");\n const to = int(m, \"to\");\n const target = to === null ? undefined : lobby.find(to);\n if (!target || !target.session || target === me) return this.error(s, \"bad_request\", \"unknown signal target\", field(\"to\"));\n // a direct hub has no migration: the only peer links are host <-> joiner, so a joiner cannot dial another joiner\n if (this.o.singleLobby && me.peerId !== lobby.hostId && target.peerId !== lobby.hostId)\n return this.error(s, \"bad_request\", \"signals go to or from the host only\", field(\"to\"));\n const now = this.now();\n const rate = this.signalRate(s, target, now);\n if (!rate.tryHit(now)) return this.rateLimited(s, \"signal\", rate.retryAfterMs(now), null);\n const total = (s.signalTotal ??= new RateWindow(this.o.signalPerSec * SIGNAL_FANOUT * this.scale, 1000));\n if (!total.tryHit(now)) return this.rateLimited(s, \"signal\", total.retryAfterMs(now), null);\n if (!has(m, \"sdp\") && !has(m, \"ice\")) return this.error(s, \"bad_request\", \"signal needs sdp or ice\", field(\"sdp\"));\n const o: Json = { t: \"signal\", from: me.peerId };\n if (has(m, \"sdp\")) o.sdp = m.sdp;\n if (has(m, \"ice\")) o.ice = m.ice;\n this.send(target.session, o);\n this.metrics.inc(\"onelobby_signals_relayed_total\");\n }\n\n /**\n * spec/signaling.md rate limits: `signal` is counted per sender and target, so a host answering several joiners at once\n * gets the budget for each, and per sender in total (SIGNAL_FANOUT targets' worth).\n */\n private signalRate(s: Session, to: Member, now: number): RateWindow {\n let w = s.signalRates.get(to);\n if (!w) {\n for (const [k, v] of s.signalRates) if (v.isIdle(now)) s.signalRates.delete(k);\n // relays refuse high publish rates, so a Nostr-bridged target gets half the budget\n w = new RateWindow((to.session?.ip.startsWith(NOSTR_ADDR) ? NOSTR_SIGNAL_PER_SEC : this.o.signalPerSec) * this.scale, 1000);\n s.signalRates.set(to, w);\n }\n return w;\n }\n\n private requireHost(s: Session): HubLobby | null {\n if (!s.lobby || !s.member) {\n this.error(s, \"bad_request\", \"not in a lobby\");\n return null;\n }\n if (s.lobby.hostId !== s.member.peerId) {\n this.error(s, \"not_host\", \"only the host can do that\");\n return null;\n }\n return s.lobby;\n }\n\n private hostMeta(s: Session, m: Json): void {\n const lobby = this.requireHost(s);\n if (!lobby) return;\n const meta = readMeta(m.meta);\n if (\"err\" in meta || !has(m, \"meta\")) return this.error(s, \"bad_request\", \"err\" in meta ? meta.err : \"meta is required\", field(\"meta\"));\n lobby.meta = meta.meta;\n this.broadcastLobby(lobby);\n }\n\n private hostPhase(s: Session, m: Json): void {\n const lobby = this.requireHost(s);\n if (!lobby) return;\n const ph = str(m, \"phase\");\n if (ph !== \"in_game\" && ph !== \"lobby\") return this.error(s, \"bad_request\", \"phase must be in_game or lobby\", field(\"phase\"));\n lobby.phase = ph;\n this.logEvent(`phase:${ph}`, lobby, s);\n this.broadcastLobby(lobby);\n }\n\n private kick(s: Session, m: Json): void {\n const lobby = this.requireHost(s);\n if (!lobby) return;\n const id = int(m, \"peerId\");\n const target = id === null ? undefined : lobby.find(id);\n if (id === null || id === s.member!.peerId || !target) return this.error(s, \"bad_request\", \"unknown peerId\", field(\"peerId\"));\n // ban:false disconnects without the ban; closeCode / closeReason are passed to the removed peer (spec/signaling.md)\n const ban = bool(m, \"ban\");\n if (has(m, \"ban\") && ban === null) return this.error(s, \"bad_request\", \"ban must be a boolean\", field(\"ban\"));\n const code = int(m, \"closeCode\");\n if (has(m, \"closeCode\") && (code === null || !(code === 1000 || (code >= 3000 && code <= 4999))))\n return this.error(s, \"bad_request\", \"closeCode must be 1000 or 3000-4999\", field(\"closeCode\"));\n const closeReason = str(m, \"closeReason\");\n if (has(m, \"closeReason\") && (closeReason === null || closeReason.length > 120))\n return this.error(s, \"bad_request\", \"closeReason must be a string of at most 120 characters\", field(\"closeReason\"));\n const data: Json = {};\n if (ban === false) data.ban = false;\n if (code !== null) data.closeCode = code;\n if (closeReason !== null) data.closeReason = closeReason;\n if (ban !== false) {\n lobby.kicked.add(target.ip);\n // a Nostr joiner picks its own key, so also ban the address its WebRTC link came from, when the host saw it\n const link = target.session?.linkIp;\n if (link && target.ip.startsWith(NOSTR_ADDR)) lobby.kicked.add(link);\n }\n this.removeMember(lobby, target, ban === false ? \"disconnected\" : \"kicked\", Object.keys(data).length ? data : undefined);\n }\n\n private approve(s: Session, m: Json): void {\n const lobby = this.requireHost(s);\n if (!lobby) return;\n const id = int(m, \"peerId\");\n const pm = id === null ? undefined : lobby.pending.find((p) => p.peerId === id);\n const js = pm?.session;\n if (!pm || !js) return this.error(s, \"bad_request\", \"no such pending request\", field(\"peerId\"));\n if (has(m, \"ok\") && bool(m, \"ok\") === null) return this.error(s, \"bad_request\", \"ok must be a boolean\", field(\"ok\"));\n lobby.pending = lobby.pending.filter((p) => p !== pm);\n js.pendingIn = null;\n js.pendingMember = null;\n if (bool(m, \"ok\") !== true) return this.error(js, \"kicked\", \"the host declined your request\", { reason: \"denied\" });\n if (lobby.members.length >= lobby.maxPlayers) return this.error(js, \"lobby_full\", \"lobby is full\");\n if (lobby.phase === \"in_game\" && !lobby.lateJoin) return this.error(js, \"in_game\", \"the game has already started\");\n this.admit(lobby, js, pm);\n }\n\n /** Peer-side migration (s.9) tells the server: the new host announces itself with a higher epoch. */\n private claimHost(s: Session, m: Json): void {\n const lobby = s.lobby;\n const me = s.member;\n if (!lobby || !me) return;\n const host = int(m, \"hostId\");\n const epoch = int(m, \"epoch\");\n if (host !== me.peerId || epoch === null || epoch <= lobby.epoch) return;\n if (this.o.singleLobby) return; // direct lobbies have no migration: the host leaving ends the session\n if (lobby.hostId !== me.peerId && lobby.find(lobby.hostId)?.session) {\n // The host is still here, so a member cannot take over. Remember the best claim for when the host goes.\n if (epoch > lobby.pendingClaimEpoch) {\n lobby.pendingClaimPeer = me.peerId;\n lobby.pendingClaimEpoch = epoch;\n }\n return;\n }\n lobby.hostId = me.peerId;\n lobby.epoch = epoch;\n lobby.pendingClaimPeer = lobby.pendingClaimEpoch = 0;\n this.broadcast(lobby, { t: \"host_changed\", hostId: lobby.hostId, epoch: lobby.epoch });\n this.metrics.inc(\"onelobby_host_changes_total\");\n this.logEvent(\"host_claim\", lobby, s);\n }\n\n private pathReport(s: Session, m: Json): void {\n const p = str(m, \"path\");\n if (p === \"relayed\" || p === \"direct\") this.metrics.inc(\"onelobby_path_total\", \"path\", p);\n else this.error(s, \"bad_request\", \"path must be relayed or direct\", field(\"path\"));\n }\n\n // ---------------------------------------------------------------- membership changes\n\n /** Removes a member for good. A member that is already detached was announced as left, so nothing is sent for it. */\n private removeMember(lobby: HubLobby, mem: Member, reason: string, kickData?: Json): void {\n const i = lobby.members.indexOf(mem);\n if (i < 0) return;\n lobby.members.splice(i, 1);\n this.members--;\n this.tokens.delete(mem.token);\n const ms = mem.session;\n const wasConnected = mem.detachedAt === 0;\n if (ms) {\n ms.lobby = null;\n ms.member = null;\n }\n if (wasConnected) {\n this.broadcast(lobby, { t: \"peer_left\", peerId: mem.peerId, reason });\n this.metrics.inc(\"onelobby_peers_left_total\", \"reason\", reason);\n if ((reason === \"kicked\" || reason === \"disconnected\") && ms)\n this.error(ms, \"kicked\", reason === \"kicked\" ? \"the host removed you from the lobby\" : \"the host disconnected you\", kickData);\n }\n this.logEvent(reason, lobby, ms);\n if (lobby.members.length === 0) return this.closeLobby(lobby);\n if (lobby.hostId === mem.peerId) this.promoteHost(lobby, mem.peerId);\n }\n\n /** Server mirror of the peer-side rule (s.9): new host is the lowest peerId still connected; epoch +1. */\n private promoteHost(lobby: HubLobby, leavingId: number): void {\n // A claim made while the old host was alive wins if its claimant is still connected.\n const claimant =\n lobby.pendingClaimEpoch > lobby.epoch && lobby.pendingClaimPeer !== leavingId\n ? lobby.members.find((x) => x.peerId === lobby.pendingClaimPeer && x.session)\n : undefined;\n const claimEpoch = lobby.pendingClaimEpoch;\n lobby.pendingClaimPeer = lobby.pendingClaimEpoch = 0;\n let next = claimant ?? lobby.members.filter((x) => x.session && x.peerId !== leavingId).sort((a, b) => a.peerId - b.peerId)[0];\n if (!next) {\n // Nobody connected. Keep a reserved host slot (it may resume); otherwise fall back to the lowest slot.\n if (lobby.find(leavingId)) return;\n next = [...lobby.members].sort((a, b) => a.peerId - b.peerId)[0];\n if (!next) return;\n }\n lobby.hostId = next.peerId;\n if (claimant) lobby.epoch = claimEpoch;\n else lobby.epoch++;\n this.broadcast(lobby, { t: \"host_changed\", hostId: next.peerId, epoch: lobby.epoch });\n this.metrics.inc(\"onelobby_host_changes_total\");\n const hs = next.session;\n if (hs) for (const pm of lobby.pending) this.send(hs, { t: \"join_request\", peer: this.peerObj(pm) }); // the new host inherits the pending requests\n this.logEvent(\"host_changed\", lobby, next.session);\n }\n\n private closeLobby(lobby: HubLobby): void {\n this.lobbies.delete(lobby.code);\n this.retired.set(lobby.code, this.now());\n const n = this.gameLobbies.get(lobby.gameId);\n if (n !== undefined) {\n if (n <= 1) this.gameLobbies.delete(lobby.gameId);\n else this.gameLobbies.set(lobby.gameId, n - 1);\n }\n const ip = this.ips.get(lobby.ownerIp);\n if (ip && ip.open > 0) ip.open--;\n for (const pm of [...lobby.pending]) {\n const ps = pm.session;\n if (ps) {\n ps.pendingIn = null;\n ps.pendingMember = null;\n this.error(ps, \"unknown_code\", \"the lobby closed\");\n }\n }\n lobby.pending = [];\n this.logEvent(\"close\", lobby, null);\n }\n\n private newCode(): string | null {\n for (let i = 0; i < 100; i++) {\n let c = \"\";\n for (let k = 0; k < 6; k++) c += CROCKFORD[this.rand(32)];\n if (!this.lobbies.has(c) && !this.retired.has(c)) return c;\n }\n return null;\n }\n\n // ---------------------------------------------------------------- output helpers\n\n private lobbyMsg(l: HubLobby, you: Member): Json {\n const peers = l.members\n .filter((x) => x.detachedAt === 0)\n .sort((a, b) => a.peerId - b.peerId)\n .map((p) => ({ peerId: p.peerId, name: p.name, joinedAt: p.joinedAt }));\n return {\n t: \"lobby\", code: this.o.issueCodes ? l.code : \"\", gameId: l.gameId, netVersion: l.netVersion, hostId: l.hostId, you: you.peerId,\n mode: l.mode, tickRate: l.tickRate, lateJoin: l.lateJoin, phase: l.phase, meta: { ...l.meta }, peers, resumeToken: you.token,\n name: l.name, maxPlayers: l.maxPlayers, visibility: l.visibility, relayOnly: l.relayOnly,\n };\n }\n\n private broadcastLobby(l: HubLobby): void {\n for (const m of l.members) if (m.session) this.sendTo(m, this.lobbyMsg(l, m));\n }\n\n private broadcast(l: HubLobby, o: Json): void {\n for (const m of l.members) this.sendTo(m, o);\n }\n\n private peerObj(m: Member): Json {\n return { peerId: m.peerId, name: m.name };\n }\n\n private send(s: Session, o: Json): void {\n if (!s.closed) s.send(JSON.stringify(o));\n }\n\n private sendTo(m: Member, o: Json): void {\n if (m.session) this.send(m.session, o);\n }\n\n private error(s: Session, code: string, msg: string, data?: Json): void {\n this.metrics.inc(\"onelobby_errors_total\", \"code\", code);\n const o: Json = { t: \"error\", code, msg };\n if (data !== undefined) o.data = data;\n s.send(JSON.stringify(o));\n }\n\n private rateLimited(s: Session, kind: string, retryAfterMs: number, reason: string | null): void {\n this.metrics.inc(\"onelobby_rate_limit_hits_total\", \"kind\", kind);\n this.log(`rate_limit kind=${kind} ip=${this.hashIp(s.ip)}`);\n const data: Json = { retryAfterMs };\n if (reason !== null) data.reason = reason;\n this.error(s, \"rate_limited\", `too many ${kind} requests`, data);\n }\n\n private logEvent(ev: string, l: HubLobby, s: Session | null): void {\n this.log(`lobby ${ev} code=${l.code} game=${l.gameId} players=${l.members.length} ip=${s ? this.hashIp(s.ip) : \"-\"}`);\n }\n\n private hashIp(ip: string): string {\n return shortHash(this.logSalt + ip);\n }\n}\n", "import type { SocketLike } from \"../transport.js\";\nimport type { SignalingHub, Session } from \"./hub.js\";\n\n/**\n * A direct host's own signaling socket (C#'s `HubSignalingSocket`): sends go straight into the hub; the hub's\n * replies are delivered on a microtask, so callbacks never run inside the hub. No Node APIs, so a browser tab can\n * use it too (`kit.hostNostr`).\n */\nexport class LocalHubSocket implements SocketLike {\n /** Address the hub sees for the host itself (rate limits and kicks are per address). */\n static readonly ADDRESS = \"local\";\n readyState = 0;\n onopen: SocketLike[\"onopen\"] = null;\n onmessage: SocketLike[\"onmessage\"] = null;\n onclose: SocketLike[\"onclose\"] = null;\n onerror: SocketLike[\"onerror\"] = null;\n private session: Session | null;\n\n constructor(private readonly hub: SignalingHub) {\n this.session = hub.connect(LocalHubSocket.ADDRESS, (text) => queueMicrotask(() => this.readyState === 1 && this.onmessage?.({ data: text })), () =>\n this.finish(),\n );\n queueMicrotask(() => {\n if (!this.session) {\n this.finish();\n return;\n }\n this.readyState = 1;\n this.onopen?.();\n });\n }\n\n send(data: string): void {\n if (this.readyState === 1 && this.session) this.hub.receive(this.session, data);\n }\n\n close(): void {\n const s = this.session;\n const wasOpen = this.readyState === 1;\n this.finish();\n if (wasOpen && s) this.hub.disconnect(s);\n }\n\n private finish(): void {\n if (this.readyState === 3) return;\n this.readyState = 3;\n queueMicrotask(() => this.onclose?.());\n }\n}\n", "/**\n * `kit.hostNostr()`: host a direct lobby from a browser tab (or any runtime with WebSocket + WebRTC), with no Node process\n * and no listening port. The signaling hub runs inside the page, the host's own socket is wired to it in memory, and\n * joiners reach it through public Nostr relays with a normal dc1 version 1 code (spec/nostr-signaling.md). There is no IP\n * fallback: with no relay reachable it rejects with `direct_unreachable`. Nothing here imports a Node module.\n */\nimport type { HostOptions, OneLobby } from \"../onelobby.js\";\nimport type { Lobby, RateLimit } from \"../lobby.js\";\nimport { encodeRoomCode } from \"../joincode.js\";\nimport { NostrHostBridge } from \"../nostr/host.js\";\nimport { HOST_NOSTR_FIRST_RELAY_MS } from \"../nostr/limits.js\";\nimport { DEFAULT_NOSTR_RELAYS, DEFAULT_NOSTR_STUN } from \"../nostr/relays.js\";\nimport { OneLobbyError } from \"../signaling.js\";\nimport { SignalingHub, directHubOptions } from \"./hub.js\";\nimport { LocalHubSocket } from \"./hub-local.js\";\n\n/** The slice of `document` the visibility watcher uses (a test seam; default `globalThis.document`). */\nexport interface VisibilityDoc {\n visibilityState?: string;\n addEventListener(type: \"visibilitychange\", listener: () => void): void;\n removeEventListener(type: \"visibilitychange\", listener: () => void): void;\n}\n\nexport interface BrowserHostOptions {\n /** Optional; joiners must send the same one or get `bad_password`. */\n password?: string;\n maxPlayers?: number;\n /** Lobby name; default \"<displayName>'s game\". */\n name?: string;\n lateJoin?: boolean;\n meta?: Record<string, string>;\n /** Per-joiner cap on incoming raw messages, e.g. `{ messages: 60, perMs: 1000 }`; the excess is dropped. */\n rateLimit?: RateLimit;\n /** Relays (default: the blueprint's `hosting.nostrRelays`, else the library list) and STUN servers (default Google's and Cloudflare's). */\n nostr?: { relays?: string[]; stun?: string[] };\n /** Receives the startup line (`lobby.directHostLog`). Default `console.log`; `false` prints nothing. */\n log?: ((line: string) => void) | false;\n /** Receives the warning printed when the host tab goes hidden. Default `console.warn`; `false` prints nothing. */\n warn?: ((line: string) => void) | false;\n /** How long to wait for a first relay before rejecting with `direct_unreachable` (default 10000). */\n relayTimeoutMs?: number;\n /** Test seam: the document whose `visibilityState` is watched. Default `globalThis.document`. */\n document?: VisibilityDoc | null;\n}\n\n/** A browser host lobby: the normal Lobby (`hostHidden` / `hostHiddenChanged` tell when the tab is throttled). */\nexport type BrowserHostLobby = Lobby;\n\n/**\n * The startup line of a browser host, same family as `formatHostLog`:\n * `OneLobby: browser host (no port); code 7HQ-4K2-... (Nostr); nostr 4/5 relays`.\n */\nexport function formatBrowserHostLog(code: string, relays: string): string {\n return `OneLobby: browser host (no port); code ${code} (Nostr); nostr ${relays} relays`;\n}\n\nexport async function hostNostr(kit: OneLobby, opts: BrowserHostOptions = {}): Promise<BrowserHostLobby> {\n const bp = kit.blueprint;\n if (bp && !bp.hosting.nostr) {\n throw new OneLobbyError(\"nostr_disabled\", \"this game has Nostr signaling turned off (hosting.nostr is false), and a browser host has no other way to be reached\");\n }\n const defined = Object.fromEntries(Object.entries(opts).filter(([, v]) => v !== undefined)) as BrowserHostOptions;\n const o: BrowserHostOptions = bp\n ? {\n maxPlayers: bp.lobby.maxPlayers,\n lateJoin: bp.lobby.lateJoin,\n meta: { ...bp.lobby.meta },\n ...(bp.session.rateLimit ? { rateLimit: { ...bp.session.rateLimit } } : {}),\n ...defined,\n nostr: { ...defined.nostr, relays: defined.nostr?.relays ?? [...bp.hosting.nostrRelays] },\n }\n : defined;\n const maxPlayers = o.maxPlayers ?? 4;\n const relays = o.nostr?.relays && o.nostr.relays.length > 0 ? o.nostr.relays : [...DEFAULT_NOSTR_RELAYS];\n const stunUrls = o.nostr?.stun ?? [...DEFAULT_NOSTR_STUN];\n\n const hub = new SignalingHub({ ...directHubOptions(maxPlayers, o.password), stunUrls });\n const ticker = setInterval(() => hub.tick(), 1000);\n (ticker as { unref?: () => void }).unref?.();\n const code = encodeRoomCode();\n const env = kit.nostrHostEnv();\n let bridge: NostrHostBridge | undefined;\n const teardown = () => {\n clearInterval(ticker);\n bridge?.close();\n };\n try {\n try {\n bridge = await NostrHostBridge.start(hub, { code, relays, connect: env.connect, clock: env.clock, wall: env.wall, maxPlayers });\n } catch (e) {\n throw new OneLobbyError(\"direct_unreachable\", `cannot reach any Nostr relay (${e instanceof Error ? e.message : String(e)}); a browser host has no other way to be reached`);\n }\n if (!(await bridge.settle(o.relayTimeoutMs ?? HOST_NOSTR_FIRST_RELAY_MS))) {\n throw new OneLobbyError(\"direct_unreachable\", `no Nostr relay reachable (tried ${bridge.relaysTotal}); a browser host has no IP fallback, check the connection or pass other relays`);\n }\n\n const name = o.name ?? `${kit.options.displayName || \"Player\"}'s game`;\n const h: HostOptions = { name, maxPlayers, lateJoin: o.lateJoin ?? true, meta: o.meta, rateLimit: o.rateLimit };\n // joiners have no address to dial: links use the hub's STUN list, no port pin\n const lobby = await kit.hostDirectWith(h, { socketFactory: () => new LocalHubSocket(hub), wrapLinks: (inner) => inner });\n lobby.linkAddressSink = (peerId, ip) => hub.reportLinkAddress(peerId, ip);\n lobby.on(\"sessionEnded\", teardown);\n\n lobby.joinCode = code;\n lobby.joinCodeKind = \"nostr\";\n lobby.directCode = \"\";\n lobby.directCodeKind = \"none\";\n lobby.portMapping = \"off\";\n lobby.directHostLog = formatBrowserHostLog(code, `${bridge.relaysConnected}/${bridge.relaysTotal}`);\n watchVisibility(lobby, o);\n if (o.log !== false) (o.log ?? console.log)(lobby.directHostLog);\n return lobby;\n } catch (e) {\n teardown();\n throw e;\n }\n}\n\n/** Sets `lobby.hostHidden` and warns when the tab goes hidden: browsers throttle timers there (about 1/s, ~1/min after 5 min). */\nfunction watchVisibility(lobby: Lobby, o: BrowserHostOptions): void {\n const doc: VisibilityDoc | null | undefined = o.document !== undefined ? o.document : (globalThis as { document?: VisibilityDoc }).document;\n if (!doc || typeof doc.addEventListener !== \"function\") return;\n const warn = o.warn === false ? null : (o.warn ?? ((s: string) => console.warn(s)));\n const check = () => {\n const hidden = doc.visibilityState === \"hidden\";\n const changed = hidden !== lobby.hostHidden;\n lobby.setHostHidden(hidden);\n if (changed && hidden) warn?.(\"OneLobby: the host tab is hidden; the browser slows its timers, so players may lag or drop. Keep this tab visible while hosting.\");\n };\n doc.addEventListener(\"visibilitychange\", check);\n lobby.on(\"sessionEnded\", () => doc.removeEventListener(\"visibilitychange\", check));\n check();\n}\n", "import { systemClock, type Clock } from \"./clock.js\";\nimport { Emitter } from \"./emitter.js\";\nimport { normalizeCode } from \"./crockford.js\";\nimport { Lobby, type RateLimit } from \"./lobby.js\";\nimport {\n OneLobbyError,\n PROTO_ID,\n SignalingClient,\n type LobbyState,\n type LobbySummary,\n type WelcomeMsg,\n} from \"./signaling.js\";\nimport {\n defaultSocketFactory,\n type LinkFactory,\n type SocketFactory,\n} from \"./transport.js\";\nimport type { VoiceCodec, FrameSource, AudioSink } from \"./voice-types.js\";\nimport { WebAudioSink, WorkletMic, webCodecsOpus, webCodecsOpusAvailable } from \"./voice-browser.js\";\nimport { webRtcLinkFactory } from \"./webrtc.js\";\nimport { decodeJoinCode, encodeJoinCode, encodeRoomCodeFor, looksLikeJoinCode } from \"./joincode.js\";\nimport { NostrSocket } from \"./nostr/socket.js\";\nimport { DEFAULT_NOSTR_STUN, type RelayConnect } from \"./nostr/relays.js\";\nimport { DEFAULT_DIRECT_PORT, parseDirectAddress } from \"./direct-address.js\";\nimport { BlueprintError, parseBlueprint, type Blueprint } from \"./blueprint.js\";\nimport { hostNostr, type BrowserHostOptions } from \"./direct/browser-host.js\";\n\nexport const LIB_NAME = \"onelobby\";\nexport const LIB_VERSION = \"0.6.0\";\n\n/** Where `rememberSeat` keeps the resume token: the subset of the Web Storage API it uses. */\nexport interface SeatStorage {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n}\n\n/** Seams for tests, adapters and non-browser runtimes. Every default is the browser implementation. */\nexport interface AdvancedOptions {\n socketFactory?: SocketFactory;\n linkFactory?: LinkFactory;\n clock?: Clock;\n /** Wall clock in unix ms, stamps chat lines. */\n wallClock?: () => number;\n /** Opus codec; defaults to WebCodecs when available. Provide a WASM-backed one elsewhere. */\n voiceCodec?: VoiceCodec | null;\n createMic?: (() => FrameSource) | null;\n createSink?: (() => AudioSink) | null;\n /** Storage for `rememberSeat`; defaults to `sessionStorage` when the page has one. null disables it. */\n storage?: SeatStorage | null;\n /** WebSocket to a Nostr relay (default: the runtime's `WebSocket`); for Node older than 22 or tests. */\n nostrConnect?: RelayConnect;\n}\n\nexport interface OneLobbyOptions {\n gameId: string;\n netVersion: string;\n /** `ws://` / `wss://` URL of a onelobby-server. */\n lobbyServer: string;\n mode?: \"lockstep\" | \"raw\";\n tickRate?: number;\n /** Default: tickRate (once a second). */\n hashEveryTicks?: number;\n /** The player's name. Empty (the default): other players see \"Player N\", N being the seat number. */\n displayName?: string;\n relayOnly?: boolean;\n /**\n * Joiners give up on reaching the host after this many ms: `lobby.ready` rejects with code `ice_failed`, the kit\n * emits `error`, and the lobby is left. Half the time goes to a relay-only retry when the server offers TURN.\n * Default 15000; 0 waits forever.\n */\n readyTimeoutMs?: number;\n /**\n * Treat a peer as gone after this many ms of silence. Joiners ping the host every 2 s and the host answers, so the\n * host drops a silent joiner (`peerLeft`, \"timeout\") and a joiner handles a silent host as lost (a direct lobby\n * ends with \"host_left\"). Default 0 (off): a silently dead peer then surfaces when its link or signaling socket\n * times out, within about 30 s. Values under 6000 are raised to 6000.\n */\n peerTimeoutMs?: number;\n /**\n * Keep this tab's seat across a page reload (sessionStorage): `join(code)` and `resume()` take the seat back\n * instead of joining as a new player. Cleared when the session ends. Default true where sessionStorage exists.\n */\n rememberSeat?: boolean;\n /** Test and runtime seams (see AdvancedOptions). */\n advanced?: AdvancedOptions;\n\n /** @deprecated use `advanced.socketFactory` */\n socketFactory?: SocketFactory;\n /** @deprecated use `advanced.linkFactory` */\n linkFactory?: LinkFactory;\n /** @deprecated use `advanced.clock` */\n clock?: Clock;\n /** @deprecated use `advanced.wallClock` */\n wallClock?: () => number;\n /** @deprecated use `advanced.voiceCodec` */\n voiceCodec?: VoiceCodec | null;\n /** @deprecated use `advanced.createMic` */\n createMic?: (() => FrameSource) | null;\n /** @deprecated use `advanced.createSink` */\n createSink?: (() => AudioSink) | null;\n}\n\n/** Per-launch settings for `OneLobby.fromConfig`; everything else comes from the blueprint. */\nexport interface FromConfigOverrides {\n /** The player's name. Checked against `players.identity` and `players.maxNameLength`. */\n displayName?: string;\n /** Wins over `hosting.lobbyServer` (the file is the bottom layer: put `?lobby=` / storage / `<meta>` lookups here). */\n lobbyServer?: string;\n /** Default: `lobby.rejoin` from the file. */\n rememberSeat?: boolean;\n advanced?: AdvancedOptions;\n}\n\nexport interface HostOptions {\n name: string;\n maxPlayers: number;\n /**\n * \"unlisted\" (default): anyone with the code or link can join, but the lobby is not in `list()`.\n * \"public\": also listed. \"private\": the host approves each join.\n */\n visibility?: \"public\" | \"unlisted\" | \"private\";\n lateJoin?: boolean;\n meta?: Record<string, string>;\n /** Per-joiner cap on incoming raw messages, e.g. `{ messages: 60, perMs: 1000 }`; the excess is dropped before `message` fires or is relayed. */\n rateLimit?: RateLimit;\n}\n\nexport interface ListOptions {\n meta?: Record<string, string>;\n limit?: number;\n cursor?: string;\n}\n\nexport interface JoinDirectOptions {\n /** The host's password, if it set one; a wrong one fails with `bad_password`. */\n password?: string;\n /** Port for an address typed without one (default 47810, docs/design/direct-ip.md s.2). */\n defaultPort?: number;\n /** No answer on the host's TCP port within this long fails with `direct_unreachable` (default 5000). */\n connectTimeoutMs?: number;\n /** Nostr relays for a Nostr code (version 1); default the blueprint's `hosting.nostrRelays`, else the library list. */\n nostrRelays?: string[];\n /** STUN servers for a Nostr code when the host offers none; default Google's and Cloudflare's public ones. */\n stun?: string[];\n}\n\n/** Where a direct join goes: a host address (TCP) or a Nostr room. */\ninterface DirectTarget {\n /** The display form of the version 1 code, or null for an address. */\n nostr: string | null;\n url: string;\n joinedWith: string;\n links: LinkFactory;\n}\n\n/** @internal How onelobby/direct makes a host lobby: its own socket and link factory, and direct mode. */\nexport interface DirectLobbyHooks {\n socketFactory: SocketFactory;\n /** Wraps the kit's own link factory (port pin, no ICE servers). */\n wrapLinks: (inner: LinkFactory) => LinkFactory;\n}\n\nexport type OneLobbyEvents = { error: [Error] };\n\nexport const DEFAULT_READY_TIMEOUT_MS = 15_000;\n\nfunction hasMic(): boolean {\n return typeof navigator !== \"undefined\" && !!navigator.mediaDevices?.getUserMedia && typeof AudioWorkletNode !== \"undefined\";\n}\n\nfunction browserStorage(): SeatStorage | null {\n try {\n const s = (globalThis as { sessionStorage?: SeatStorage }).sessionStorage;\n return s ?? null;\n } catch {\n return null; // storage access can throw (sandboxed iframe, blocked cookies)\n }\n}\n\ninterface Seat {\n code: string;\n token: string;\n server: string;\n}\n\n/**\n * Lobbies alive in this page, by resume token. A remembered seat that is still in use here (two kits in one tab,\n * e.g. a local two-player demo) must not be taken over; after a reload this set starts empty.\n */\nconst liveSeats = new Set<Lobby>();\nfunction seatInUse(token: string): boolean {\n for (const l of liveSeats) if (l.resumeToken === token) return true;\n return false;\n}\n\n/** Entry point (DESIGN s.11.2). */\nexport class OneLobby extends Emitter<OneLobbyEvents> {\n readonly options: Required<Pick<OneLobbyOptions, \"gameId\" | \"netVersion\" | \"lobbyServer\" | \"mode\" | \"tickRate\" | \"hashEveryTicks\" | \"displayName\" | \"relayOnly\" | \"readyTimeoutMs\" | \"peerTimeoutMs\">>;\n private socketFactory: SocketFactory;\n private linkFactory: LinkFactory;\n private clock: Clock;\n private wall: () => number;\n private voiceCodec: VoiceCodec | null;\n private createMic: (() => FrameSource) | null;\n private createSink: (() => AudioSink) | null;\n private storage: SeatStorage | null;\n private nostrConnectFn: RelayConnect | undefined;\n /** The validated, frozen onelobby.json this kit was built from (`OneLobby.fromConfig`); undefined for `new OneLobby`. */\n readonly blueprint?: Blueprint;\n\n /**\n * Build a kit from an onelobby.json (already parsed; docs/design/blueprint.md s.7). Throws `bad_config` for an\n * invalid file or name, `identity_anonymous` for a name when `players.identity` is \"anonymous\". Then `host()` and\n * `hostDirect()` default their options from the file and the file's chat/listing switches are enforced.\n */\n static fromConfig(json: unknown, overrides: FromConfigOverrides = {}): OneLobby {\n const bp = parseBlueprint(json);\n const name = (overrides.displayName ?? \"\").trim();\n if (name) {\n if (bp.players.identity === \"anonymous\") throw new OneLobbyError(\"identity_anonymous\", \"this game uses anonymous players: no display names\");\n if (name.length > bp.players.maxNameLength)\n throw new BlueprintError(\"displayName\", \"length\", `must be at most ${bp.players.maxNameLength} characters`);\n }\n const kit = new OneLobby({\n gameId: bp.game.id,\n netVersion: bp.game.netVersion,\n lobbyServer: overrides.lobbyServer ?? bp.hosting.lobbyServer,\n mode: bp.sync.mode === \"lockstep\" ? \"lockstep\" : \"raw\", // replication is C# only; JS speaks raw\n tickRate: bp.sync.tickRate,\n hashEveryTicks: bp.sync.hashEveryTicks,\n displayName: name,\n relayOnly: bp.hosting.relayOnly,\n readyTimeoutMs: bp.session.readyTimeoutMs,\n peerTimeoutMs: bp.session.peerTimeoutMs,\n rememberSeat: overrides.rememberSeat ?? bp.lobby.rejoin,\n advanced: overrides.advanced,\n });\n (kit as { blueprint?: Blueprint }).blueprint = bp;\n return kit;\n }\n\n /** @internal Host options the blueprint implies (empty without one). */\n blueprintHostDefaults(): Partial<HostOptions> {\n const bp = this.blueprint;\n if (!bp) return {};\n const name = this.options.displayName;\n return {\n name: name ? `${name}'s game` : \"Game\",\n maxPlayers: bp.lobby.maxPlayers,\n visibility: bp.lobby.listed ? \"public\" : bp.lobby.approveJoins ? \"private\" : \"unlisted\",\n lateJoin: bp.lobby.lateJoin,\n meta: { ...bp.lobby.meta },\n ...(bp.session.rateLimit ? { rateLimit: { ...bp.session.rateLimit } } : {}),\n };\n }\n\n constructor(o: OneLobbyOptions) {\n super();\n const tickRate = o.tickRate ?? 20;\n this.options = {\n gameId: o.gameId,\n netVersion: o.netVersion,\n lobbyServer: o.lobbyServer,\n mode: o.mode ?? \"lockstep\",\n tickRate,\n hashEveryTicks: o.hashEveryTicks ?? tickRate,\n displayName: (o.displayName ?? \"\").trim(),\n relayOnly: o.relayOnly ?? false,\n readyTimeoutMs: o.readyTimeoutMs ?? DEFAULT_READY_TIMEOUT_MS,\n peerTimeoutMs: o.peerTimeoutMs ?? 0,\n };\n const a = o.advanced ?? {};\n const pick = <K extends keyof AdvancedOptions & keyof OneLobbyOptions>(k: K) => (a[k] !== undefined ? a[k] : o[k]);\n this.socketFactory = pick(\"socketFactory\") ?? defaultSocketFactory;\n this.linkFactory = pick(\"linkFactory\") ?? webRtcLinkFactory;\n this.clock = pick(\"clock\") ?? systemClock;\n this.wall = pick(\"wallClock\") ?? (() => Date.now());\n const codec = pick(\"voiceCodec\");\n this.voiceCodec = codec !== undefined ? codec : webCodecsOpusAvailable() ? webCodecsOpus : null;\n const mic = pick(\"createMic\");\n this.createMic = mic !== undefined ? mic : hasMic() ? () => new WorkletMic() : null;\n const sink = pick(\"createSink\");\n this.createSink = sink !== undefined ? sink : typeof AudioContext !== \"undefined\" ? () => new WebAudioSink() : null;\n this.nostrConnectFn = a.nostrConnect;\n this.storage = o.rememberSeat === false ? null : a.storage !== undefined ? a.storage : browserStorage();\n }\n\n // ---- remembered seat -------------------------------------------------------------------------------------------\n\n private get seatKey(): string {\n return `onelobby.seat.${this.options.gameId}`;\n }\n\n private loadSeat(): Seat | null {\n if (!this.storage) return null;\n try {\n const raw = this.storage.getItem(this.seatKey);\n if (!raw) return null;\n const s = JSON.parse(raw) as Partial<Seat>;\n if (typeof s.code !== \"string\" || typeof s.token !== \"string\" || s.server !== this.options.lobbyServer) return null;\n return s as Seat;\n } catch {\n return null;\n }\n }\n\n private saveSeat(code: string, token: string): void {\n if (!this.storage || !token) return;\n try {\n this.storage.setItem(this.seatKey, JSON.stringify({ code, token, server: this.options.lobbyServer } satisfies Seat));\n } catch {\n /* quota or blocked storage: the seat just is not remembered */\n }\n }\n\n private clearSeat(code?: string): void {\n if (!this.storage) return;\n try {\n if (code !== undefined) {\n const s = this.loadSeat();\n if (s && s.code !== code) return;\n }\n this.storage.removeItem(this.seatKey);\n } catch {\n /* ignore */\n }\n }\n\n /** The lobby code of the seat this tab can take back (rememberSeat), or null. */\n get rememberedCode(): string | null {\n return this.loadSeat()?.code ?? null;\n }\n\n /**\n * Take back the seat remembered from before a page reload (host or joiner). Resolves null when there is none\n * or the server no longer holds it (the seat is forgotten then).\n */\n async resume(): Promise<Lobby | null> {\n const seat = this.loadSeat();\n if (!seat || seatInUse(seat.token)) return null;\n try {\n return await this.rejoin(seat.token);\n } catch (e) {\n if (e instanceof OneLobbyError && e.code === \"invalid_token\") {\n this.clearSeat();\n return null;\n }\n throw e;\n }\n }\n\n // ---- connecting ------------------------------------------------------------------------------------------------\n\n private helloMsg(resumeToken?: string): Record<string, unknown> {\n return {\n t: \"hello\",\n proto: PROTO_ID,\n gameId: this.options.gameId,\n netVersion: this.options.netVersion,\n client: { lib: LIB_NAME, ver: LIB_VERSION, platform: typeof navigator !== \"undefined\" ? \"browser\" : \"node\" },\n name: this.options.displayName.slice(0, 24),\n ...(resumeToken ? { resumeToken } : {}),\n };\n }\n\n private async connect(url = this.options.lobbyServer, factory = this.socketFactory, timeoutMs = 0): Promise<{ sig: SignalingClient; welcome: WelcomeMsg }> {\n const sig = await this.open(url, factory, timeoutMs);\n try {\n const welcome = await sig.request<WelcomeMsg & { t: string }>(this.helloMsg(), \"welcome\");\n return { sig, welcome };\n } catch (e) {\n sig.close();\n throw e;\n }\n }\n\n /** The signaling socket only (no hello yet); with a timeout (direct IP), no answer is `direct_unreachable`. */\n private async open(url: string, factory: SocketFactory, timeoutMs: number): Promise<SignalingClient> {\n if (!url.trim() && factory === defaultSocketFactory) throw new OneLobbyError(\"no_server\", \"no lobby server address (set hosting.lobbyServer or pass lobbyServer)\");\n const sig = new SignalingClient(url, factory, this.clock);\n if (timeoutMs > 0) {\n let cancel = () => {};\n const timeout = new Promise<never>((_, reject) => {\n cancel = this.clock.setTimeout(() => reject(new OneLobbyError(\"direct_unreachable\", `no answer from ${url} within ${timeoutMs} ms`)), timeoutMs);\n });\n try {\n await Promise.race([sig.connect(), timeout]);\n } catch (e) {\n sig.close();\n if (e instanceof OneLobbyError && e.code === \"connect_failed\") throw new OneLobbyError(\"direct_unreachable\", `no answer from ${url}`);\n throw e;\n } finally {\n cancel();\n }\n } else await sig.connect();\n return sig;\n }\n\n private makeLobby(sig: SignalingClient, welcome: WelcomeMsg, state: LobbyState, direct?: { links: LinkFactory }, rateLimit?: RateLimit): Lobby {\n const lobby = new Lobby({\n signaling: sig,\n state,\n ice: welcome.ice ?? [],\n links: direct?.links ?? this.linkFactory,\n direct: !!direct,\n clock: this.clock,\n wall: this.wall,\n hashEveryTicks: this.options.hashEveryTicks,\n relayOnly: state.relayOnly ?? this.options.relayOnly,\n displayName: this.options.displayName,\n voice: { codec: this.voiceCodec, createSource: this.createMic, createSink: this.createSink },\n readyTimeoutMs: this.options.readyTimeoutMs,\n peerTimeoutMs: this.options.peerTimeoutMs,\n rateLimit,\n });\n lobby.errorSink = (e) => this.emit(\"error\", e);\n if (this.blueprint) guardLobby(lobby, this.blueprint);\n if (this.storage && !direct) {\n liveSeats.add(lobby);\n this.saveSeat(lobby.code, lobby.resumeToken);\n lobby.seatSink = (token) => this.saveSeat(lobby.code, token);\n lobby.on(\"sessionEnded\", () => {\n liveSeats.delete(lobby);\n this.clearSeat(lobby.code);\n });\n }\n return lobby;\n }\n\n /** With a blueprint (`fromConfig`) every option defaults from `lobby.*`; explicit options win. */\n async host(opts?: Partial<HostOptions>): Promise<Lobby> {\n const h = { name: \"Game\", maxPlayers: 4, ...this.blueprintHostDefaults(), ...definedOnly(opts) } as HostOptions;\n const { sig, welcome } = await this.connect();\n try {\n const state = await sig.request<LobbyState>(\n {\n t: \"create\",\n name: h.name,\n maxPlayers: h.maxPlayers,\n visibility: h.visibility ?? \"unlisted\",\n mode: this.options.mode,\n tickRate: this.options.tickRate,\n lateJoin: h.lateJoin ?? true,\n meta: h.meta ?? {},\n ...(this.options.relayOnly ? { relayOnly: true } : {}),\n },\n \"lobby\",\n );\n return this.makeLobby(sig, welcome, state, undefined, h.rateLimit);\n } catch (e) {\n sig.close();\n throw e;\n }\n }\n\n async join(code: string): Promise<Lobby> {\n const norm = normalizeCode(code);\n if (!norm) throw new OneLobbyError(\"bad_code\", `\"${code}\" is not a valid join code`);\n const seat = this.loadSeat();\n if (seat && seat.code === norm && !seatInUse(seat.token)) {\n // Same tab, same lobby (a page reload): take the old seat back instead of joining as a new player.\n try {\n return await this.rejoin(seat.token);\n } catch (e) {\n this.clearSeat();\n if (!(e instanceof OneLobbyError && e.code === \"invalid_token\")) throw e;\n }\n }\n const { sig, welcome } = await this.connect();\n try {\n const state = await sig.request<LobbyState>({ t: \"join\", code: norm }, \"lobby\");\n return this.makeLobby(sig, welcome, state);\n } catch (e) {\n sig.close();\n throw e;\n }\n }\n\n /**\n * Join a direct-IP host (docs/design/direct-ip.md): a join code from the host's screen (`6B01-RGFE-P2B`, any case, spaces\n * or hyphens) or, advanced, an address (`ip`, `ip:port`, `[v6]:port`). No lobby server is involved: the code is\n * the host's address. A mistyped code fails at once with `bad_code`; nobody listening is `direct_unreachable`.\n * The address is also given to ICE as the host's UDP candidate. When the host leaves, `sessionEnded(\"host_left\")`.\n */\n async joinDirect(codeOrAddress: string, opts: JoinDirectOptions = {}): Promise<Lobby> {\n const t = this.directTarget(codeOrAddress, opts);\n const { sig, welcome } = await this.connectDirect(t, opts);\n try {\n const state = await sig.request<LobbyState>({ t: \"join\", ...(opts.password ? { password: opts.password } : {}) }, \"lobby\");\n const lobby = this.makeLobby(sig, this.withStun(welcome, t, opts), state, { links: t.links });\n // a joiner's joinCode is what it joined with (normalized), so a HUD can show it on both sides\n lobby.joinCode = t.joinedWith;\n return lobby;\n } catch (e) {\n sig.close();\n throw e;\n }\n }\n\n /**\n * Take a direct-IP seat back after a drop or a restart: the code (or address) joined before and that lobby's\n * `resumeToken` (save it after every join; it changes on each resume). The host keeps a dropped seat for 60 s and the\n * same peer id comes back. Rejects with `invalid_token` when the seat is gone: offer `joinDirect` then.\n */\n async rejoinDirect(codeOrAddress: string, resumeToken: string, opts: JoinDirectOptions = {}): Promise<Lobby> {\n const t = this.directTarget(codeOrAddress, opts);\n const lobby = await this.resumeAt(resumeToken, t.url, t.nostr ? 0 : opts.connectTimeoutMs ?? 5000, { links: t.links }, t.nostr ? this.nostrFactory(t, opts) : undefined, (w) => this.withStun(w, t, opts));\n lobby.joinCode = t.joinedWith;\n return lobby;\n }\n\n private nostrFactory(t: DirectTarget, opts: JoinDirectOptions): SocketFactory {\n const relays = opts.nostrRelays ?? (this.blueprint ? [...this.blueprint.hosting.nostrRelays] : undefined);\n return () =>\n new NostrSocket({ code: t.nostr!, relays: relays && relays.length > 0 ? relays : undefined, connect: this.nostrConnectFn, clock: this.clock, wall: this.wall });\n }\n\n /** Opens the signaling socket of a direct target: TCP to the host's address, or Nostr relays for a version 1 code. */\n private async connectDirect(t: DirectTarget, opts: JoinDirectOptions): Promise<{ sig: SignalingClient; welcome: WelcomeMsg }> {\n if (!t.nostr) return this.connect(t.url, this.socketFactory, opts.connectTimeoutMs ?? 5000);\n // the socket enforces its own deadlines (10 s to reach a relay, 15 s for the host to answer)\n let sock: NostrSocket | null = null;\n const factory = this.nostrFactory(t, opts);\n try {\n return await this.connect(t.url, (u) => (sock = factory(u) as NostrSocket), 0);\n } catch (e) {\n const why = (sock as NostrSocket | null)?.failure;\n // same code as an IP host that does not answer, so a game handles one \"could not reach the host\" error\n if (e instanceof OneLobbyError && e.code === \"connect_failed\") throw new OneLobbyError(\"direct_unreachable\", why || \"no answer through the Nostr relays\");\n throw e;\n }\n }\n\n /** A Nostr joiner needs STUN; take the host's list (it comes in `welcome`), else the public defaults. */\n private withStun(welcome: WelcomeMsg, t: DirectTarget, opts: JoinDirectOptions): WelcomeMsg {\n if (!t.nostr || (welcome.ice && welcome.ice.length > 0)) return welcome;\n const urls = opts.stun ?? [...DEFAULT_NOSTR_STUN];\n return urls.length > 0 ? { ...welcome, ice: [{ urls }] } : welcome;\n }\n\n private directTarget(codeOrAddress: string, opts: JoinDirectOptions): DirectTarget {\n let input = codeOrAddress.trim();\n if (looksLikeJoinCode(input)) {\n const d = decodeJoinCode(input);\n if (\"error\" in d)\n throw new OneLobbyError(d.error, d.error === \"unknown_version\" ? `\"${input}\" is a join code from a newer version of the game` : `\"${input}\" is not a valid join code: check for a typo`);\n if (\"room\" in d) {\n // version 1: a Nostr room; signaling runs over public relays (spec/nostr-signaling.md), WebRTC uses STUN\n const shown = encodeRoomCodeFor(d.room);\n return { nostr: shown, url: `nostr:${shown}`, joinedWith: shown, links: this.linkFactory };\n }\n input = `${d.ip}:${d.port}`;\n }\n const addr = parseDirectAddress(input, opts.defaultPort ?? DEFAULT_DIRECT_PORT);\n if (!addr) throw new OneLobbyError(\"bad_address\", `\"${codeOrAddress}\" is not a join code or an address (try 6B01-RGFE-P2B or 192.168.1.20:47810)`);\n const inner = this.linkFactory;\n const links: LinkFactory = { create: (o) => inner.create({ ...o, iceServers: [], extraRemoteCandidates: [addr.candidate] }) };\n return { nostr: null, url: addr.url, joinedWith: joinedWith(addr), links };\n }\n\n /** @internal onelobby/direct's Nostr host: the relay seam and the kit's clocks. */\n nostrHostEnv(): { connect: RelayConnect | undefined; clock: Clock; wall: () => number } {\n return { connect: this.nostrConnectFn, clock: this.clock, wall: this.wall };\n }\n\n /** @internal onelobby/direct's host: create the one lobby on the embedded hub, in direct mode. */\n async hostDirectWith(h: HostOptions, hooks: DirectLobbyHooks): Promise<Lobby> {\n const { sig, welcome } = await this.connect(\"onelobby-direct://host/\", hooks.socketFactory);\n try {\n const state = await sig.request<LobbyState>(\n { t: \"create\", name: h.name, maxPlayers: h.maxPlayers, visibility: \"unlisted\", mode: this.options.mode, tickRate: this.options.tickRate,\n lateJoin: h.lateJoin ?? true, meta: h.meta ?? {} },\n \"lobby\",\n );\n return this.makeLobby(sig, welcome, state, { links: hooks.wrapLinks(this.linkFactory) }, h.rateLimit);\n } catch (e) {\n sig.close();\n throw e;\n }\n }\n\n /**\n * Host a direct lobby from this browser tab (or any runtime with WebSocket and WebRTC): no Node process, no port.\n * The hub runs in the page and joiners find it through Nostr relays with `lobby.joinCode` (a dc1 version 1 code,\n * `joinCodeKind` \"nostr\"); they use `kit.joinDirect(code)`. Rejects with `direct_unreachable` when no relay answers\n * within 10 s (a browser has no IP fallback) and `nostr_disabled` when the blueprint has `hosting.nostr` false.\n * Keep the tab visible: hidden tabs are throttled (`lobby.hostHidden`, event `hostHiddenChanged`).\n */\n hostNostr(opts: BrowserHostOptions = {}): Promise<Lobby> {\n return hostNostr(this, opts);\n }\n\n async rejoin(resumeToken: string): Promise<Lobby> {\n return this.resumeAt(resumeToken, this.options.lobbyServer, 0);\n }\n\n private async resumeAt(\n resumeToken: string,\n url: string,\n timeoutMs: number,\n direct?: { links: LinkFactory },\n factory?: SocketFactory,\n tweakWelcome?: (w: WelcomeMsg) => WelcomeMsg,\n ): Promise<Lobby> {\n const sig = await this.open(url, factory ?? this.socketFactory, timeoutMs);\n try {\n // The server answers hello(resumeToken) with welcome then the full lobby state; listen for both up front.\n const lobbyP = sig.expect<LobbyState>(\"lobby\");\n const welcomeP = sig.request<WelcomeMsg & { t: string }>(this.helloMsg(resumeToken), \"welcome\");\n lobbyP.catch(() => {}); // settled below or by close()\n const welcome = tweakWelcome ? tweakWelcome(await welcomeP) : await welcomeP;\n // spec: an unknown or expired token is ignored -> plain welcome (no peerId), and no lobby follows\n if (welcome.peerId === undefined) throw new OneLobbyError(\"invalid_token\", \"resume token unknown or expired\");\n const state = await lobbyP;\n return this.makeLobby(sig, welcome, state, direct);\n } catch (e) {\n sig.close();\n throw e;\n }\n }\n\n async list(opts: ListOptions = {}): Promise<LobbySummary[]> {\n if (this.blueprint && !this.blueprint.hosting.server) throw new OneLobbyError(\"listing_disabled\", \"this game has no lobby server (hosting.server is false)\");\n const { sig } = await this.connect();\n try {\n const res = await sig.request<{ t: string; items: LobbySummary[] }>(\n { t: \"list\", visibility: \"public\", meta: opts.meta ?? {}, limit: Math.min(100, opts.limit ?? 50), ...(opts.cursor ? { cursor: opts.cursor } : {}) },\n \"lobbies\",\n );\n return res.items ?? [];\n } finally {\n sig.close();\n }\n }\n}\n\n/** The join code for an IPv4 address (a host name or IPv6 has none: its `host:port` instead). */\nfunction joinedWith(addr: { host: string; port: number; isIPv6: boolean; display: string }): string {\n if (addr.isIPv6) return addr.display;\n try {\n return encodeJoinCode(addr.host, addr.port);\n } catch {\n return addr.display;\n }\n}\n\nfunction definedOnly<T extends object>(o: T | undefined): Partial<T> {\n const out: Partial<T> = {};\n if (o) for (const [k, v] of Object.entries(o)) if (v !== undefined) (out as Record<string, unknown>)[k] = v;\n return out;\n}\n\n/** Blueprint switches that gate features on the lobby: voice and text chat; chat.voiceMode is the starting voice mode. */\nfunction guardLobby(lobby: Lobby, bp: Blueprint): void {\n lobby.voice.mode = bp.chat.voiceMode;\n if (!bp.chat.voice) {\n const voice = lobby.voice;\n const d = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(voice), \"enabled\");\n if (d?.get && d.set) {\n Object.defineProperty(voice, \"enabled\", {\n configurable: true,\n get: () => d.get!.call(voice) as boolean,\n set: (v: boolean) => {\n if (v) throw new OneLobbyError(\"voice_disabled\", \"this game has voice chat turned off (chat.voice is false)\");\n d.set!.call(voice, v);\n },\n });\n }\n }\n lobby.pause.mode = bp.session.pause;\n if (!bp.chat.text) {\n lobby.chat.send = () => {\n throw new OneLobbyError(\"chat_disabled\", \"this game has text chat turned off (chat.text is false)\");\n };\n }\n}\n", "/**\n * Player-facing helpers: plain-language error text and shareable join links.\n * Nothing here talks to the network; it is safe to call from any UI code.\n */\nimport { formatCode, normalizeCode } from \"./crockford.js\";\nimport type { IpCodeKind, JoinCodeKind } from \"./join-code-kind.js\";\n\nexport interface FriendlyError {\n /** Stable machine code (the OneLobbyError code, or \"unknown\"). */\n code: string;\n /** One short sentence a player can read. */\n message: string;\n /** What the player can do about it, when there is something. */\n hint?: string;\n}\n\ntype Entry = { message: string; hint?: string };\n\n/**\n * Every `code` a `OneLobbyError` can carry. Compare against these instead of string literals; C# has the shared ones in\n * `LobbyErrorCodes`. `kicked` with `data.reason === \"denied\"` is a refused join request; `rate_limited` with\n * `data.reason === \"max_lobbies\"` is the per-address lobby limit.\n */\nexport const ErrorCodes = {\n // from the lobby server or a direct host's hub\n UnknownCode: \"unknown_code\",\n BadCode: \"bad_code\",\n LobbyFull: \"lobby_full\",\n VersionMismatch: \"version_mismatch\",\n InGame: \"in_game\",\n Kicked: \"kicked\",\n BadPassword: \"bad_password\",\n NotHost: \"not_host\",\n RateLimited: \"rate_limited\",\n ServerFull: \"server_full\",\n GameNotAllowed: \"game_not_allowed\",\n BadRequest: \"bad_request\",\n // from this client\n ServerClosed: \"server_closed\",\n IceFailed: \"ice_failed\",\n Timeout: \"timeout\",\n DirectUnreachable: \"direct_unreachable\",\n BadAddress: \"bad_address\",\n PortInUse: \"port_in_use\",\n ConnectFailed: \"connect_failed\",\n Closed: \"closed\",\n ServerLost: \"server_lost\",\n InvalidToken: \"invalid_token\",\n NoServer: \"no_server\",\n // from the shared pause (lobby.pause)\n NotInGame: \"not_in_game\",\n NotAllowed: \"not_allowed\",\n // from a blueprint (onelobby.json)\n BadConfig: \"bad_config\",\n VoiceDisabled: \"voice_disabled\",\n ChatDisabled: \"chat_disabled\",\n PauseDisabled: \"pause_disabled\",\n ListingDisabled: \"listing_disabled\",\n NostrDisabled: \"nostr_disabled\",\n IdentityAnonymous: \"identity_anonymous\",\n} as const;\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];\n\nconst TEXT: Record<string, Entry> = {\n // server errors (spec/signaling.md)\n unknown_code: { message: \"We couldn't find a lobby with that code.\", hint: \"Check the code with the host. Codes stop working once everyone leaves.\" },\n bad_code: { message: \"That doesn't look like a lobby code.\", hint: \"Codes are 6 letters and numbers, like 7HQ 4K2.\" },\n lobby_full: { message: \"That lobby is full.\", hint: \"Ask the host to make room, or start your own lobby.\" },\n version_mismatch: { message: \"You and the host are running different versions of the game.\", hint: \"Reload the page (or update the game) on both sides, then try again.\" },\n in_game: { message: \"That game has already started.\", hint: \"Ask the host to let people join mid-game, or wait for the next round.\" },\n kicked: { message: \"The host removed you from the lobby.\" },\n denied: { message: \"The host didn't let you in this time.\", hint: \"Ask the host to approve you, then try again.\" },\n not_host: { message: \"Only the host can do that.\" },\n rate_limited: { message: \"Slow down a little: too many tries in a short time.\", hint: \"Wait a moment, then try again.\" },\n max_lobbies: { message: \"You already have the most lobbies open that this server allows.\", hint: \"Close one of your other lobbies, then try again.\" },\n server_full: { message: \"The game server is full right now.\", hint: \"Try again in a few minutes.\" },\n game_not_allowed: { message: \"This game server doesn't host this game.\", hint: \"The game's server setting may be wrong; let the game's owner know.\" },\n bad_request: { message: \"The game sent something the server didn't understand.\", hint: \"Reload the page and try again. If it keeps happening, the game may need an update.\" },\n // client errors\n connect_failed: { message: \"Couldn't reach the game server.\", hint: \"Check your internet connection, then try again.\" },\n closed: { message: \"Lost the connection to the game server.\", hint: \"Check your internet connection, then try again.\" },\n server_lost: { message: \"Lost the connection to the game server.\", hint: \"Your game may keep going; new players can't join until it's back.\" },\n invalid_token: { message: \"Your old seat in that lobby is no longer available.\", hint: \"Join again with the lobby code.\" },\n ice_failed: { message: \"Couldn't connect to the other players.\", hint: \"Some networks (school, work, public Wi-Fi) block game connections. Try another network or a phone hotspot.\" },\n no_server: { message: \"This game has no lobby server address yet.\", hint: \"Enter one in the multiplayer settings, or host from your own computer with a join code.\" },\n timeout: { message: \"The connection took too long.\", hint: \"Check your internet connection, then try again.\" },\n voice_disabled: { message: \"Voice chat is turned off in this game.\" },\n chat_disabled: { message: \"Text chat is turned off in this game.\" },\n pause_disabled: { message: \"Pausing is turned off in this game.\" },\n not_in_game: { message: \"You can only pause while a game is running.\", hint: \"Start the game first.\" },\n not_allowed: { message: \"You can't do that right now.\", hint: \"The player who paused, or the host, can resume.\" },\n listing_disabled: { message: \"This game has no public lobby list.\", hint: \"Join with a code or a link from the host.\" },\n direct_unreachable: { message: \"Couldn't reach the host.\", hint: \"Check the code, and that the host is still online. A host in a browser needs its tab open and visible.\" },\n nostr_disabled: { message: \"This game can't be hosted from a browser.\", hint: \"Host from the desktop version, or let the game's owner know.\" },\n identity_anonymous: { message: \"This game doesn't use player names.\", hint: \"Everyone shows up as Player 1, Player 2, and so on.\" },\n bad_config: { message: \"The game's multiplayer settings file is not valid.\", hint: \"Let the game's owner know; the details are in the error message.\" },\n left: { message: \"You left the lobby.\" },\n unknown: { message: \"Something went wrong.\", hint: \"Try again. If it keeps happening, reload the page.\" },\n};\n\nconst CODE_RE = /^[a-z][a-z0-9_]*$/;\n\nfunction codeOf(err: unknown): { code: string | null; data?: Record<string, unknown> } {\n if (typeof err === \"string\") {\n const m = /^([a-z][a-z0-9_]*)(?::|$)/.exec(err.trim());\n return { code: m ? m[1] : null };\n }\n if (err && typeof err === \"object\") {\n const o = err as { code?: unknown; data?: unknown; message?: unknown; name?: unknown };\n const data = o.data && typeof o.data === \"object\" ? (o.data as Record<string, unknown>) : undefined;\n if (typeof o.code === \"string\" && CODE_RE.test(o.code)) return { code: o.code, data };\n if (typeof o.message === \"string\") {\n const m = /^([a-z][a-z0-9_]*):/.exec(o.message);\n if (m && TEXT[m[1]]) return { code: m[1], data };\n // browser network failures (fetch/WebSocket) surface as TypeError / NetworkError\n if (o.name === \"NetworkError\" || /network|failed to fetch|websocket/i.test(o.message)) return { code: \"connect_failed\" };\n if (o.name === \"TimeoutError\" || /timed? ?out/i.test(o.message)) return { code: \"timeout\" };\n }\n }\n return { code: null };\n}\n\n/**\n * Plain-language, player-facing text for any error the kit (or its server) can produce.\n * Never returns a stack trace or raw technical message; unknown errors get a generic friendly line.\n */\nexport function describeError(err: unknown): FriendlyError {\n const { code, data } = codeOf(err);\n if (!code) return { code: \"unknown\", ...TEXT.unknown };\n let key = code;\n if (code === \"kicked\" && data?.reason === \"denied\") key = \"denied\";\n if (code === \"rate_limited\" && data?.reason === \"max_lobbies\") key = \"max_lobbies\";\n const e = TEXT[key];\n if (!e) return { code, ...TEXT.unknown };\n const out: FriendlyError = { code, message: e.message };\n if (e.hint) out.hint = e.hint;\n if (code === \"rate_limited\" && typeof data?.retryAfterMs === \"number\" && key === \"rate_limited\") {\n const s = Math.max(1, Math.ceil(data.retryAfterMs / 1000));\n out.hint = `Wait ${s} second${s === 1 ? \"\" : \"s\"}, then try again.`;\n }\n return out;\n}\n\nfunction currentHref(): string | undefined {\n try {\n return typeof location !== \"undefined\" ? location.href : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * A link that opens the game straight into lobby `code`, e.g. `https://game.example/?join=7HQ-4K2`.\n * `base` defaults to the current page; any existing `param` is replaced. Throws on an invalid code.\n */\nexport function shareUrl(code: string, base?: string, param = \"join\"): string {\n const norm = normalizeCode(code);\n if (!norm) throw new Error(`\"${code}\" is not a valid lobby code`);\n const b = base ?? currentHref();\n if (!b) throw new Error(\"shareUrl needs a base URL outside the browser\");\n const u = new URL(b);\n u.searchParams.delete(param);\n // URLSearchParams would encode the display space as \"+\": use a hyphen, which normalizeCode also accepts\n u.searchParams.set(param, formatCode(norm).replace(\" \", \"-\"));\n return u.toString();\n}\n\n/** What a direct host answers at `GET /joininfo` (onelobby/direct). */\nexport interface JoinInfo {\n joinCode: string;\n joinCodeKind: JoinCodeKind;\n /** The IP code (address and port); equals `joinCode` when Nostr is off. */\n directCode: string;\n directCodeKind: IpCodeKind;\n /** `http://<public or LAN address>:<port>/` when the host serves pages; else null. */\n joinUrl: string | null;\n port: number;\n}\n\n/**\n * For a page served by a direct host (`hostDirect` with `http`): the host's current join code and share link, so a\n * player who opened the page at a LAN or localhost address still shares the public one. `base` defaults to the page's\n * origin. Null when the host has no code yet or cannot be reached. Ask again after a while: the code turns \"internet\"\n * when the router answers a later port-mapping retry.\n */\nexport async function directJoinInfo(base?: string): Promise<JoinInfo | null> {\n const b = base ?? currentHref();\n if (!b) return null;\n try {\n const res = await fetch(new URL(\"/joininfo\", b), { cache: \"no-store\" });\n return res.ok ? ((await res.json()) as JoinInfo) : null;\n } catch {\n return null;\n }\n}\n\n/** The lobby code carried by a share link (see shareUrl), or null when there is none or it is not a valid code. */\nexport function codeFromUrl(url?: string, param = \"join\"): string | null {\n const href = url ?? currentHref();\n if (!href) return null;\n let u: URL;\n try {\n u = new URL(href, \"http://localhost/\");\n } catch {\n return null;\n }\n const v = u.searchParams.get(param) ?? new URLSearchParams(u.hash.replace(/^#/, \"\")).get(param);\n return v ? normalizeCode(v) : null;\n}\n", "/**\n * Names from before the Keepalive -> OneLobby rename. They are the same classes and types, so old code keeps\n * working unchanged; they will be removed in the next minor release.\n */\nimport { OneLobby, type OneLobbyEvents, type OneLobbyOptions } from \"./onelobby.js\";\nimport { OneLobbyError } from \"./signaling.js\";\n\n/** @deprecated renamed to `OneLobby`; this alias goes away in the next minor release. */\nexport const Keepalive = OneLobby;\n/** @deprecated renamed to `OneLobby`. */\nexport type Keepalive = OneLobby;\n/** @deprecated renamed to `OneLobbyError`; this alias goes away in the next minor release. */\nexport const KeepaliveError = OneLobbyError;\n/** @deprecated renamed to `OneLobbyError`. */\nexport type KeepaliveError = OneLobbyError;\n/** @deprecated renamed to `OneLobbyOptions`. */\nexport type KeepaliveOptions = OneLobbyOptions;\n/** @deprecated renamed to `OneLobbyEvents`. */\nexport type KeepaliveEvents = OneLobbyEvents;\n", "/**\n * The DOM-free brain of `<one-lobby>`: a small state machine over OneLobby + Lobby (raw mode) that the element\n * renders, plus the JSON message envelope and the host-authoritative shared state. Unit-tested without a browser.\n */\nimport { OneLobbyError } from \"../signaling.js\";\nimport type { Lobby, Peer } from \"../lobby.js\";\nimport type { HostOptions } from \"../onelobby.js\";\nimport { describeError, type FriendlyError } from \"../friendly.js\";\nimport { normalizeCode } from \"../crockford.js\";\n\nexport type UiState = \"idle\" | \"connecting\" | \"lobby\" | \"playing\" | \"error\";\n\nexport interface Player {\n id: number;\n name: string;\n isHost: boolean;\n isMe: boolean;\n}\n\n/** What the controller needs from `OneLobby` (so tests can pass the in-memory rig). */\nexport interface KitLike {\n host(h: HostOptions): Promise<Lobby>;\n join(code: string): Promise<Lobby>;\n /** Take back the seat this tab held before a reload (OneLobby `rememberSeat`); null when there is none. */\n resume?(): Promise<Lobby | null>;\n readonly rememberedCode?: string | null;\n}\n\nexport interface ControllerEvents {\n message: { from: Player | null; data: unknown };\n \"player-joined\": { player: Player };\n \"player-left\": { player: Player; reason: string };\n start: Record<string, never>;\n error: FriendlyError;\n \"shared-change\": { key: string; value: unknown; from: Player | null };\n}\n\nexport interface ControllerOptions {\n makeKit: (name: string) => KitLike;\n /** A number, or a function read each time a game is hosted. */\n maxPlayers: number | (() => number);\n /** Lobby name shown to the server (not to players). */\n lobbyName?: string;\n}\n\n/** Wire envelope inside raw frames. Kept tiny and versioned by key names only. */\ntype Wire =\n | { k: \"m\"; d: unknown } // game message\n | { k: \"set\"; key: string; v: unknown } // joiner -> host: please set\n | { k: \"add\"; key: string; n: number } // joiner -> host: please add\n | { k: \"S\"; key: string; v: unknown; by: number; q: number } // host -> all: value changed (q = host change counter)\n | { k: \"SS\"; all: Record<string, unknown>; q: number }; // host -> one: full state as of change q\n\nconst enc = new TextEncoder();\nconst dec = new TextDecoder();\nconst toBytes = (w: Wire) => enc.encode(JSON.stringify(w));\n\nexport class SharedState {\n /** @internal */ values = new Map<string, unknown>();\n constructor(private c: LobbyController) {}\n get(key: string): unknown {\n return this.values.get(key);\n }\n /** Set a value for everyone. The host decides the order; everyone gets `shared-change`. */\n set(key: string, value: unknown): void {\n this.c._sharedOp({ k: \"set\", key: String(key), v: value === undefined ? null : value });\n }\n /** Add `n` (default 1) to a number, safely even when several players click at once. */\n add(key: string, n = 1): void {\n this.c._sharedOp({ k: \"add\", key: String(key), n: Number(n) || 0 });\n }\n /** A plain copy of every value. */\n all(): Record<string, unknown> {\n return Object.fromEntries(this.values);\n }\n keys(): string[] {\n return [...this.values.keys()];\n }\n}\n\nexport class LobbyController {\n state: UiState = \"idle\";\n lobby: Lobby | null = null;\n error: FriendlyError | null = null;\n /** True while a joiner waits for its direct link to the host. */\n linking = false;\n name = \"\";\n readonly shared = new SharedState(this);\n\n onChange: () => void = () => {};\n onEvent: <K extends keyof ControllerEvents>(type: K, detail: ControllerEvents[K]) => void = () => {};\n\n private lastAction: { kind: \"host\" } | { kind: \"join\"; code: string } | { kind: \"resume\" } | null = null;\n private unsub: Array<() => void> = [];\n private gen = 0;\n /** Host: changes made so far. Joiner: the newest change applied (older queued ones are skipped). */\n private seq = 0;\n\n constructor(private opts: ControllerOptions) {}\n\n get isHost(): boolean {\n return !!this.lobby && this.lobby.isHost;\n }\n get code(): string | null {\n return this.lobby?.code ?? null;\n }\n get players(): Player[] {\n const l = this.lobby;\n if (!l) return [];\n const list = l.peers.map((p) => this.toPlayer(p));\n if (!list.some((p) => p.isMe)) list.push({ id: l.localPeerId, name: this.name || `Player ${l.localPeerId}`, isHost: l.isHost, isMe: true });\n return list.sort((a, b) => a.id - b.id);\n }\n\n /** One line of plain language for the status area. */\n get statusText(): string {\n switch (this.state) {\n case \"idle\":\n return \"Host a game, or type a code to join one.\";\n case \"connecting\":\n return this.lastAction?.kind === \"join\" ? \"Joining\u2026\" : this.lastAction?.kind === \"resume\" ? \"Getting your seat back\u2026\" : \"Creating a game\u2026\";\n case \"error\":\n return this.error?.message ?? \"Something went wrong.\";\n case \"lobby\": {\n if (this.linking) return `Connecting to ${this.hostName}\u2026`;\n if (this.isHost) {\n const n = this.players.length;\n return n <= 1 ? \"Waiting for players\u2026 Share the code with your friends.\" : `${n} players here. Press Start when everyone's in.`;\n }\n return `Connected! Waiting for ${this.hostName} to start.`;\n }\n case \"playing\":\n return this.linking ? `Connecting to ${this.hostName}\u2026` : \"Game on!\";\n }\n }\n\n private get hostName(): string {\n const l = this.lobby;\n if (!l) return \"the host\";\n return l.peer(l.hostPeerId)?.name || \"the host\";\n }\n\n private toPlayer(p: Peer | undefined, id?: number): Player {\n const l = this.lobby!;\n const pid = p?.peerId ?? id ?? 0;\n return { id: pid, name: p?.name || (pid === l.localPeerId && this.name) || `Player ${pid}`, isHost: pid === l.hostPeerId, isMe: pid === l.localPeerId };\n }\n\n // ---- actions -----------------------------------------------------------------------------------------------------\n\n host(name: string): Promise<void> {\n this.lastAction = { kind: \"host\" };\n return this.run(name, (kit) => kit.host({ name: this.opts.lobbyName ?? \"game\", maxPlayers: typeof this.opts.maxPlayers === \"function\" ? this.opts.maxPlayers() : this.opts.maxPlayers, visibility: \"unlisted\", lateJoin: true }));\n }\n\n join(code: string, name: string): Promise<void> {\n const norm = normalizeCode(code);\n this.lastAction = { kind: \"join\", code };\n if (!norm) {\n this.fail(new OneLobbyError(\"bad_code\"));\n return Promise.resolve();\n }\n return this.run(name, (kit) => kit.join(norm));\n }\n\n /**\n * Take back this tab's seat after a page reload, if the kit remembers one (host or joiner). Quietly stays on the\n * start screen when there is nothing to resume or the seat is gone. Resolves true when back in a game.\n */\n async resume(name: string): Promise<boolean> {\n const kit = this.opts.makeKit(name);\n if (!kit.resume || !kit.rememberedCode) return false;\n this.lastAction = { kind: \"resume\" };\n await this.run(name, () => kit.resume!(), true);\n return !!this.lobby;\n }\n\n /** Repeat whatever failed last (host or join). */\n retry(): Promise<void> {\n const a = this.lastAction;\n if (!a || a.kind === \"resume\") {\n this.reset();\n return Promise.resolve();\n }\n return a.kind === \"host\" ? this.host(this.name) : this.join(a.code, this.name);\n }\n\n async start(): Promise<void> {\n if (!this.lobby || !this.isHost || this.state === \"playing\") return;\n await this.lobby.start();\n }\n\n async leave(): Promise<void> {\n const l = this.lobby;\n this.detach();\n await l?.leave();\n this.reset();\n }\n\n /** Back to the start screen (after an error, or to cancel). */\n reset(): void {\n this.detach();\n this.state = \"idle\";\n this.error = null;\n this.onChange();\n }\n\n /** Send any JSON-able value to everyone else. */\n send(data: unknown): void {\n if (!this.lobby) return;\n this.lobby.broadcast(toBytes({ k: \"m\", d: data === undefined ? null : data }));\n }\n\n // ---- internals ---------------------------------------------------------------------------------------------------\n\n private async run(name: string, go: (kit: KitLike) => Promise<Lobby | null>, quiet = false): Promise<void> {\n this.detach();\n const gen = ++this.gen;\n this.name = (name || \"\").trim().slice(0, 24); // empty: other players see \"Player N\"\n this.state = \"connecting\";\n this.error = null;\n this.onChange();\n let lobby: Lobby | null;\n try {\n lobby = await go(this.opts.makeKit(this.name));\n } catch (e) {\n if (gen !== this.gen) return;\n if (quiet) this.reset();\n else this.fail(e);\n return;\n }\n if (gen !== this.gen) {\n void lobby?.leave();\n return;\n }\n if (lobby) this.attach(lobby);\n else this.reset();\n }\n\n private attach(l: Lobby): void {\n this.lobby = l;\n // A joiner takes the host's values; a host keeps anything the page set up front (defaults).\n if (!l.isHost) this.shared.values.clear();\n this.seq = 0;\n this.state = l.phase === \"in_game\" ? \"playing\" : \"lobby\";\n this.linking = !l.isHost;\n const on = this.unsub;\n on.push(l.on(\"ready\", () => this.linked()));\n on.push(l.on(\"peerJoined\", (p) => {\n this.onEvent(\"player-joined\", { player: this.toPlayer(p) });\n this.onChange();\n }));\n on.push(l.on(\"peerLeft\", (p, reason) => {\n this.onEvent(\"player-left\", { player: this.toPlayer(p), reason });\n this.onChange();\n }));\n on.push(l.on(\"peerReady\", (p) => {\n if (l.isHost && p.peerId !== l.localPeerId) l.send(p.peerId, toBytes({ k: \"SS\", all: this.shared.all(), q: this.seq }));\n }));\n on.push(l.on(\"hostChanged\", () => this.onChange()));\n on.push(l.on(\"phase\", (ph) => {\n if (ph === \"in_game\" && this.state !== \"playing\") {\n this.state = \"playing\";\n this.onChange();\n this.onEvent(\"start\", {});\n }\n }));\n on.push(l.on(\"message\", (from, bytes) => this.onRaw(from, bytes)));\n on.push(l.on(\"sessionEnded\", (reason) => {\n if (this.lobby !== l) return;\n if (reason === \"left\") return;\n this.fail(new OneLobbyError(reason));\n }));\n // A joiner that cannot reach the host: the kit gives up after its readyTimeoutMs, leaves, and ends the session\n // with \"ice_failed\", which lands in the sessionEnded handler above.\n l.ready.then(() => { if (this.lobby === l) this.linked(); }, () => {});\n this.onChange();\n if (this.state === \"playing\" && !this.linking) this.onEvent(\"start\", {});\n }\n\n private linked(): void {\n if (!this.linking) return;\n this.linking = false;\n this.onChange();\n if (this.state === \"playing\") this.onEvent(\"start\", {});\n }\n\n private detach(): void {\n this.gen++;\n for (const u of this.unsub.splice(0)) u();\n this.lobby = null;\n this.linking = false;\n }\n\n private fail(e: unknown): void {\n const l = this.lobby;\n this.detach();\n if (l) void l.leave();\n this.state = \"error\";\n this.error = describeError(e);\n this.onChange();\n this.onEvent(\"error\", this.error);\n }\n\n private playerById(id: number): Player | null {\n const l = this.lobby;\n if (!l || !id) return null;\n return this.toPlayer(l.peer(id), id);\n }\n\n private onRaw(from: number, bytes: Uint8Array): void {\n let w: Wire;\n try {\n w = JSON.parse(dec.decode(bytes)) as Wire;\n } catch {\n return; // not ours\n }\n if (!w || typeof w !== \"object\") return;\n const l = this.lobby!;\n switch (w.k) {\n case \"m\":\n this.onEvent(\"message\", { from: this.playerById(from), data: w.d });\n break;\n case \"set\":\n case \"add\":\n if (l.isHost) this.applyHost(w, from);\n break;\n case \"S\":\n if (!l.isHost && from === l.hostPeerId && !(w.q <= this.seq)) {\n this.seq = w.q;\n this.applyValue(w.key, w.v, w.by);\n }\n break;\n case \"SS\":\n if (!l.isHost && from === l.hostPeerId && w.all && typeof w.all === \"object\" && !(w.q < this.seq)) {\n this.seq = w.q;\n for (const k of [...this.shared.values.keys()]) if (!(k in w.all)) this.applyValue(k, null, 0);\n for (const [k, v] of Object.entries(w.all)) this.applyValue(k, v, 0);\n }\n break;\n }\n }\n\n /** @internal from SharedState */\n _sharedOp(op: Extract<Wire, { k: \"set\" } | { k: \"add\" }>): void {\n const l = this.lobby;\n if (!l) {\n // Not in a game yet: keep it local, so a page can set defaults up front.\n const v = op.k === \"set\" ? op.v : (Number(this.shared.values.get(op.key)) || 0) + op.n;\n this.applyValue(op.key, v, 0);\n return;\n }\n if (l.isHost) this.applyHost(op, l.localPeerId);\n else l.send(l.hostPeerId, toBytes(op));\n }\n\n private applyHost(op: Extract<Wire, { k: \"set\" } | { k: \"add\" }>, by: number): void {\n const l = this.lobby!;\n if (typeof op.key !== \"string\") return;\n const v = op.k === \"set\" ? op.v : (Number(this.shared.values.get(op.key)) || 0) + (Number(op.n) || 0);\n this.applyValue(op.key, v, by);\n l.broadcast(toBytes({ k: \"S\", key: op.key, v, by, q: ++this.seq }));\n }\n\n private applyValue(key: string, v: unknown, by: number): void {\n if (v === null) this.shared.values.delete(key);\n else this.shared.values.set(key, v);\n this.onEvent(\"shared-change\", { key, value: v, from: this.playerById(by) });\n }\n}\n", "/** Shadow-DOM styles for `<one-lobby>`. Theme with CSS custom properties on the element (see docs/no-code.md). */\nexport const LOBBY_CSS = `\n:host {\n --onelobby-bg: #ffffff;\n --onelobby-fg: #1b1f24;\n --onelobby-muted: #57606a;\n --onelobby-border: #d0d7de;\n --onelobby-accent: #0b5cad;\n --onelobby-accent-fg: #ffffff;\n --onelobby-danger: #b42318;\n --onelobby-field: #f6f8fa;\n --onelobby-radius: 12px;\n --onelobby-font: system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif;\n display: block;\n max-width: 28rem;\n font-family: var(--onelobby-font);\n color: var(--onelobby-fg);\n line-height: 1.4;\n}\n:host([theme=\"dark\"]) {\n --onelobby-bg: #161b22; --onelobby-fg: #e6edf3; --onelobby-muted: #9da7b3; --onelobby-border: #30363d;\n --onelobby-accent: #4c9be8; --onelobby-accent-fg: #0d1117; --onelobby-danger: #ff7b72; --onelobby-field: #0d1117;\n}\n@media (prefers-color-scheme: dark) {\n :host([theme=\"auto\"]), :host(:not([theme])) {\n --onelobby-bg: #161b22; --onelobby-fg: #e6edf3; --onelobby-muted: #9da7b3; --onelobby-border: #30363d;\n --onelobby-accent: #4c9be8; --onelobby-accent-fg: #0d1117; --onelobby-danger: #ff7b72; --onelobby-field: #0d1117;\n }\n}\n[hidden] { display: none !important; }\n.card {\n background: var(--onelobby-bg);\n border: 1px solid var(--onelobby-border);\n border-radius: var(--onelobby-radius);\n padding: 1rem;\n display: grid;\n gap: 0.75rem;\n}\n.status { font-size: 1rem; margin: 0; }\n.status.error { color: var(--onelobby-danger); font-weight: 600; }\n.hint { margin: 0; color: var(--onelobby-muted); }\nlabel { display: grid; gap: 0.25rem; font-size: 0.9rem; color: var(--onelobby-muted); }\ninput {\n font: inherit; font-size: 1rem; color: var(--onelobby-fg); background: var(--onelobby-field);\n border: 1px solid var(--onelobby-border); border-radius: 8px; padding: 0.6rem 0.7rem; min-height: 44px; box-sizing: border-box; width: 100%;\n}\n#code { text-transform: uppercase; letter-spacing: 0.15em; font-family: ui-monospace, Consolas, monospace; }\nbutton {\n font: inherit; font-size: 1rem; min-height: 44px; padding: 0.5rem 1rem; border-radius: 8px; cursor: pointer;\n border: 1px solid var(--onelobby-border); background: var(--onelobby-field); color: var(--onelobby-fg);\n}\nbutton.primary { background: var(--onelobby-accent); color: var(--onelobby-accent-fg); border-color: var(--onelobby-accent); font-weight: 600; }\nbutton:disabled { opacity: 0.55; cursor: not-allowed; }\nbutton:focus-visible, input:focus-visible { outline: 3px solid var(--onelobby-accent); outline-offset: 2px; }\n.row { display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: end; }\n.row > label { flex: 1 1 10rem; }\n.or { text-align: center; color: var(--onelobby-muted); font-size: 0.9rem; }\n.codebox { display: grid; gap: 0.5rem; justify-items: start; }\n.code {\n font-family: ui-monospace, Consolas, monospace; font-size: clamp(2rem, 9vw, 2.75rem); font-weight: 700;\n letter-spacing: 0.12em; user-select: all;\n}\nh3 { margin: 0; font-size: 0.9rem; color: var(--onelobby-muted); font-weight: 600; }\nul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.25rem; }\nli { display: flex; gap: 0.5rem; align-items: center; }\n.tag { font-size: 0.75rem; border: 1px solid var(--onelobby-border); border-radius: 999px; padding: 0 0.5rem; color: var(--onelobby-muted); }\n.bar { display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center; justify-content: space-between; }\n.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }\n`;\n", "/**\n * The `server=\"...\"` attribute of `<one-lobby>` as a WebSocket URL, or null when it is missing, still the placeholder\n * from the guide, or not an address at all. Null makes the element say \"This page is missing its lobby server\n * address\" instead of the less helpful \"Couldn't reach the game server\".\n *\n * Accepts what people paste: `https://name.onrender.com` (the address Render shows), `wss://host/ws`, or a bare host.\n * http(s) becomes ws(s); the server answers on `/` and `/ws`, so no path is needed.\n */\nexport function lobbyServerUrl(raw: string | null | undefined): string | null {\n const s = (raw ?? \"\").trim();\n if (!s || /\\s/.test(s)) return null;\n // the guide's and example's placeholders: YOUR-LOBBY-SERVER, YOUR-SERVER, REPLACE-ME ...\n if (/your[-_ ]?(lobby[-_ ]?)?server|replace[-_ ]?me|<|>/i.test(s)) return null;\n let url = s;\n if (/^https?:\\/\\//i.test(url)) url = url.replace(/^http/i, \"ws\");\n else if (!/^wss?:\\/\\//i.test(url)) url = \"wss://\" + url;\n let u: URL;\n try {\n u = new URL(url);\n } catch {\n return null;\n }\n const host = u.hostname.toLowerCase();\n if (!host) return null;\n // documentation-only names (RFC 2606): example.com/.net/.org and *.example never reach a real server\n if (/(^|\\.)example\\.(com|net|org)$/.test(host) || /\\.example$/.test(host) || host === \"example\") return null;\n // a single word that is not localhost is a typo, not a server (e.g. server=\"lobby\")\n if (!host.includes(\".\") && !host.includes(\":\") && host !== \"localhost\") return null;\n return url;\n}\n", "/**\n * `<one-lobby>`: a drop-in lobby (host, join by code or invite link, player list, start) for people who paste\n * HTML rather than write code. Zero dependencies; shadow DOM; themed with CSS custom properties.\n *\n * <one-lobby server=\"https://your-name.onrender.com\" game=\"my-game\" max-players=\"4\">fallback text</one-lobby>\n *\n * Game API: el.send(data), el.shared.set/add/get, el.isHost, el.players, events message / player-joined /\n * player-left / start / error / shared-change (all bubble, `event.detail` carries the data).\n */\nimport { formatCode } from \"../crockford.js\";\nimport { OneLobby } from \"../onelobby.js\";\nimport { OneLobbyError } from \"../signaling.js\";\nimport { LobbyController, type Player, type SharedState, type UiState } from \"./controller.js\";\nimport { codeFromUrl, shareUrl } from \"../friendly.js\";\nimport { LOBBY_CSS } from \"./styles.js\";\nimport { lobbyServerUrl } from \"./server-url.js\";\n\nexport const LOBBY_TAG = \"one-lobby\";\nconst NAME_KEY = \"onelobby-name\";\n\nconst TEMPLATE = `\n<div class=\"card\" part=\"card\">\n <p class=\"status\" id=\"status\" role=\"status\" aria-live=\"polite\"></p>\n <p class=\"hint\" id=\"hint\" hidden></p>\n\n <div id=\"v-idle\" class=\"view\">\n <div class=\"row\">\n <label id=\"nameLabel\">Your name\n <input id=\"name\" maxlength=\"24\" autocomplete=\"nickname\" placeholder=\"e.g. Sam\">\n </label>\n </div>\n <div class=\"row\" style=\"margin-top:.75rem\">\n <button id=\"host\" class=\"primary\" type=\"button\">Host a game</button>\n </div>\n <p class=\"or\">or join a friend</p>\n <form id=\"joinForm\" class=\"row\">\n <label>Game code\n <input id=\"code\" maxlength=\"9\" autocomplete=\"off\" autocapitalize=\"characters\" spellcheck=\"false\" placeholder=\"ABC 123\">\n </label>\n <button id=\"join\" type=\"submit\">Join</button>\n </form>\n </div>\n\n <div id=\"v-busy\" class=\"view\" hidden>\n <button id=\"cancel\" type=\"button\">Cancel</button>\n </div>\n\n <div id=\"v-lobby\" class=\"view\" hidden>\n <div class=\"codebox\">\n <h3>Game code</h3>\n <span class=\"code\" id=\"codeText\"></span>\n <div class=\"row\">\n <button id=\"copyCode\" type=\"button\">Copy code</button>\n <button id=\"copyLink\" type=\"button\">Copy invite link</button>\n </div>\n <span class=\"sr\" id=\"copied\" aria-live=\"polite\"></span>\n </div>\n <h3 id=\"playersTitle\" style=\"margin-top:.75rem\">Players</h3>\n <ul id=\"players\" aria-labelledby=\"playersTitle\"></ul>\n <div class=\"bar\" style=\"margin-top:.75rem\">\n <button id=\"start\" class=\"primary\" type=\"button\">Start</button>\n <button id=\"leave\" type=\"button\">Leave</button>\n </div>\n </div>\n\n <div id=\"v-error\" class=\"view\" hidden>\n <div class=\"row\">\n <button id=\"retry\" class=\"primary\" type=\"button\">Try again</button>\n <button id=\"back\" type=\"button\">Back</button>\n </div>\n </div>\n</div>\n`;\n// No <slot>: text placed inside <one-lobby> is a fallback that shows only while the script has not loaded.\n\n/** The element's public surface (for TypeScript users). */\nexport interface OneLobbyElement extends HTMLElement {\n readonly isHost: boolean;\n readonly players: Player[];\n readonly code: string | null;\n readonly state: UiState;\n readonly shared: SharedState;\n send(data: unknown): void;\n host(): Promise<void>;\n join(code: string): Promise<void>;\n start(): Promise<void>;\n leave(): Promise<void>;\n}\n\n\nfunction readStored(): string {\n try {\n return localStorage.getItem(NAME_KEY) ?? \"\";\n } catch {\n return \"\";\n }\n}\nfunction store(name: string): void {\n try {\n localStorage.setItem(NAME_KEY, name);\n } catch {\n /* private window */\n }\n}\n\nasync function copyText(text: string): Promise<boolean> {\n try {\n await navigator.clipboard.writeText(text);\n return true;\n } catch {\n const ta = document.createElement(\"textarea\");\n ta.value = text;\n ta.style.position = \"fixed\";\n ta.style.opacity = \"0\";\n document.body.appendChild(ta);\n ta.select();\n let ok = false;\n try {\n ok = document.execCommand(\"copy\");\n } catch {\n ok = false;\n }\n ta.remove();\n return ok;\n }\n}\n\nfunction makeClass(): CustomElementConstructor {\n class OneLobbyBox extends HTMLElement implements OneLobbyElement {\n static observedAttributes = [\"name\", \"theme\"];\n /** @internal exposed for debugging and tests */\n readonly _controller: LobbyController;\n private root: ShadowRoot;\n private $ = (id: string) => this.root.getElementById(id) as HTMLElement;\n private autoJoined = false;\n private copiedTimer: ReturnType<typeof setTimeout> | undefined;\n\n constructor() {\n super();\n this.root = this.attachShadow({ mode: \"open\" });\n this.root.innerHTML = `<style>${LOBBY_CSS}</style>${TEMPLATE}`;\n this._controller = new LobbyController({ makeKit: (name) => this.makeKit(name), maxPlayers: () => this.maxPlayers });\n this._controller.onChange = () => this.render();\n this._controller.onEvent = (type, detail) => this.dispatchEvent(new CustomEvent(type, { detail, bubbles: true, composed: true }));\n this.wire();\n }\n\n // ---- public API ----------------------------------------------------------------------------------------------\n\n get isHost(): boolean { return this._controller.isHost; }\n get players(): Player[] { return this._controller.players; }\n get code(): string | null { return this._controller.code; }\n get state(): UiState { return this._controller.state; }\n get shared(): SharedState { return this._controller.shared; }\n send(data: unknown): void { this._controller.send(data); }\n host(): Promise<void> { return this._controller.host(this.playerName()); }\n join(code: string): Promise<void> { return this._controller.join(code, this.playerName()); }\n start(): Promise<void> { return this._controller.start(); }\n leave(): Promise<void> { return this._controller.leave(); }\n\n // ---- lifecycle -----------------------------------------------------------------------------------------------\n\n connectedCallback(): void {\n const name = this.getAttribute(\"name\");\n const input = this.$(\"name\") as HTMLInputElement;\n if (!input.value) input.value = name ?? readStored();\n this.$(\"nameLabel\").hidden = !!name;\n this.render();\n if (!this.autoJoined) {\n this.autoJoined = true;\n const code = codeFromUrl();\n if (code && this._controller.state === \"idle\") {\n (this.$(\"code\") as HTMLInputElement).value = formatCode(code);\n if (input.value.trim()) void this.join(code);\n else {\n this.$(\"status\").textContent = \"You're invited! Type your name, then press Join.\";\n input.focus();\n }\n } else if (!code && this._controller.state === \"idle\" && lobbyServerUrl(this.getAttribute(\"server\"))) {\n // A reload in the middle of a game: quietly take this tab's seat back (OneLobby rememberSeat).\n void this._controller.resume(input.value.trim());\n }\n }\n }\n\n disconnectedCallback(): void {\n // Leaving the page section (or the page) ends this player's session.\n if (this._controller.lobby) void this._controller.leave();\n }\n\n attributeChangedCallback(attr: string): void {\n if (attr === \"name\" && this.isConnected) {\n const v = this.getAttribute(\"name\");\n if (v) (this.$(\"name\") as HTMLInputElement).value = v;\n this.$(\"nameLabel\").hidden = !!v;\n }\n }\n\n // ---- internals -----------------------------------------------------------------------------------------------\n\n private get maxPlayers(): number {\n const n = parseInt(this.getAttribute(\"max-players\") ?? \"\", 10);\n return Number.isFinite(n) && n >= 2 ? Math.min(n, 250) : 8;\n }\n\n private playerName(): string {\n // empty is fine: other players then see \"Player 2\" and so on\n const v = (this.$(\"name\") as HTMLInputElement).value.trim() || this.getAttribute(\"name\") || \"\";\n if (!this.getAttribute(\"name\") && v) store(v);\n return v;\n }\n\n private makeKit(name: string): OneLobby {\n // missing, still the guide's placeholder, or not an address: say so instead of \"couldn't reach the server\"\n const server = lobbyServerUrl(this.getAttribute(\"server\"));\n if (!server) throw new OneLobbyError(\"no_server\", \"the <one-lobby> tag has no usable server address\");\n return new OneLobby({\n gameId: this.getAttribute(\"game\") || \"onelobby-drop-in\",\n netVersion: this.getAttribute(\"version\") || \"1\",\n lobbyServer: server,\n mode: \"raw\",\n displayName: name,\n // joiners give up (with a friendly \"couldn't connect\" and a retry button) after this long\n readyTimeoutMs: 15_000,\n advanced: { createMic: null, createSink: null, voiceCodec: null },\n });\n }\n\n private wire(): void {\n const on = (id: string, ev: string, fn: (e: Event) => void) => this.$(id).addEventListener(ev, fn);\n on(\"host\", \"click\", () => void this.host());\n on(\"joinForm\", \"submit\", (e) => {\n e.preventDefault();\n void this.join((this.$(\"code\") as HTMLInputElement).value);\n });\n on(\"cancel\", \"click\", () => this._controller.reset());\n on(\"start\", \"click\", () => void this.start());\n on(\"leave\", \"click\", () => void this.leave());\n on(\"retry\", \"click\", () => void this._controller.retry());\n on(\"back\", \"click\", () => this._controller.reset());\n on(\"copyCode\", \"click\", () => void this.copy(this.code ?? \"\", \"copyCode\", \"Copied!\"));\n on(\"copyLink\", \"click\", () => void this.copy(this.code ? shareUrl(this.code) : \"\", \"copyLink\", \"Link copied!\"));\n }\n\n private async copy(text: string, id: string, done: string): Promise<void> {\n if (!text) return;\n const ok = await copyText(text);\n const btn = this.$(id);\n const label = id === \"copyCode\" ? \"Copy code\" : \"Copy invite link\";\n btn.textContent = ok ? done : \"Couldn't copy, select it by hand\";\n this.$(\"copied\").textContent = ok ? done : \"\";\n clearTimeout(this.copiedTimer);\n this.copiedTimer = setTimeout(() => {\n btn.textContent = label;\n this.$(\"copied\").textContent = \"\";\n }, 2000);\n }\n\n private render(): void {\n const c = this._controller;\n const s = c.state;\n const status = this.$(\"status\");\n status.textContent = s === \"error\" && c.error?.code === \"no_server\"\n ? \"This page is missing its lobby server address.\"\n : c.statusText;\n status.classList.toggle(\"error\", s === \"error\");\n const hint = this.$(\"hint\");\n const hintText = s === \"error\"\n ? (c.error?.code === \"no_server\" ? 'Put your lobby server address in server=\"\u2026\" on the <one-lobby> tag (it looks like https://your-name.onrender.com). The guide shows how to get one.' : c.error?.hint ?? \"\")\n : \"\";\n hint.textContent = hintText;\n hint.hidden = !hintText;\n\n this.$(\"v-idle\").hidden = s !== \"idle\";\n this.$(\"v-busy\").hidden = s !== \"connecting\";\n this.$(\"v-lobby\").hidden = s !== \"lobby\" && s !== \"playing\";\n this.$(\"v-error\").hidden = s !== \"error\";\n this.setAttribute(\"state\", s);\n\n if (s === \"lobby\" || s === \"playing\") {\n this.$(\"codeText\").textContent = formatCode(c.code ?? \"\");\n const ul = this.$(\"players\");\n ul.replaceChildren(\n ...c.players.map((p) => {\n const li = document.createElement(\"li\");\n const nm = document.createElement(\"span\");\n nm.textContent = p.name;\n li.append(nm);\n if (p.isHost) li.append(Object.assign(document.createElement(\"span\"), { className: \"tag\", textContent: \"host\" }));\n if (p.isMe) li.append(Object.assign(document.createElement(\"span\"), { className: \"tag\", textContent: \"you\" }));\n return li;\n }),\n );\n const start = this.$(\"start\") as HTMLButtonElement;\n start.hidden = !c.isHost || s === \"playing\";\n start.disabled = c.players.length < 2;\n start.title = start.disabled ? \"Wait for at least one more player\" : \"\";\n }\n }\n }\n return OneLobbyBox;\n}\n\n/** Register `<one-lobby>` (safe to call more than once; a no-op outside a browser). */\nexport function defineLobbyElement(tag = LOBBY_TAG): void {\n if (typeof customElements === \"undefined\" || customElements.get(tag)) return;\n customElements.define(tag, makeClass());\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"one-lobby\": OneLobbyElement;\n }\n}\n", "/**\n * `onelobby/ui`: the `<one-lobby>` drop-in element. Loading this module registers the tag.\n */\nimport { defineLobbyElement } from \"./element.js\";\n\nexport { defineLobbyElement, LOBBY_TAG } from \"./element.js\";\nexport type { OneLobbyElement } from \"./element.js\";\nexport { LobbyController, SharedState } from \"./controller.js\";\nexport { lobbyServerUrl } from \"./server-url.js\";\nexport type { Player, UiState, ControllerEvents, ControllerOptions, KitLike } from \"./controller.js\";\n\ndefineLobbyElement();\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["workerInterval", "fn", "ms", "src", "url", "w", "systemClock", "h", "Emitter", "event", "fn", "list", "i", "args", "e", "CROCKFORD_ALPHABET", "asciiUpper", "s", "c", "normalizeCode", "input", "ch", "CROCKFORD_ALPHABET", "formatCode", "code", "MsgType", "MODE_LOCKSTEP", "MODE_RAW", "CodecError", "code", "detail", "enc", "dec", "decodeText", "b", "Writer", "size", "n", "nb", "v", "Reader", "buf", "encodeMessage", "msg", "route", "allowOversize", "w", "c", "f", "t", "l", "out", "TYPE_BY_ID", "k", "decodeMessage", "reassembled", "max", "r", "type", "seq", "tick", "commands", "i", "peerId", "flags", "frames", "id", "ts", "lines", "CHAT_REJECT", "chatRejectName", "code", "PAUSE_FLAG_PAUSER_LEFT", "PAUSE_REFUSE", "CHAT_MAX_BYTES", "CHAT_BURST", "CHAT_WINDOW_MS", "CHAT_PER_WINDOW", "CHAT_HISTORY_LINES", "STRIP", "sanitizeChat", "text", "utf8Length", "ChatLimiter", "peerId", "now", "b", "Chat", "Emitter", "hub", "wall", "isLocallyMuted", "id", "muted", "lines", "l", "from", "route", "msg", "chatRejectName", "rawText", "reject", "code", "CHAT_REJECT", "line", "out", "p", "shown", "CHUNK_PAYLOAD", "splitChunks", "id", "bytes", "size", "count", "out", "MAX_PARTIAL_SETS", "Reassembler", "chunk", "e", "CodecError", "pos", "p", "n", "deflateAvailable", "pipe", "bytes", "stream", "maxBytes", "w", "chunks", "r", "total", "done", "value", "CodecError", "out", "pos", "deflate", "inflate", "MAX_DESYNCS", "MAX_RESTORE_RETRIES", "HOST_SILENCE_MS", "PEER_HASH_SILENCE_MS", "MAX_PENDING_PER_PEER", "SNAPSHOT_COMPRESS_MIN", "Lockstep", "Emitter", "hub", "opts", "CHUNK_PAYLOAD", "bytes", "f", "tick", "hash", "theirs", "peer", "h", "b", "interval", "held", "now", "p", "peerId", "m", "c", "due", "commands", "a", "frame", "msg", "limit", "last", "from", "waiting", "payload", "ps", "expected", "inflate", "e", "lastTick", "hostTick", "first", "own", "hostHash", "yourHash", "n", "w", "strikeTick", "S", "raw", "flags", "deflateAvailable", "deflate", "splitChunks", "keys", "k", "pickNewHost", "connected", "failedHost", "best", "id", "epochAfter", "a", "b", "d", "claimWinsWrapped", "OneLobbyError", "code", "message", "data", "PROTO_ID", "PING_EVERY_MS", "SILENCE_MS", "SignalingClient", "url", "factory", "clock", "h", "resolve", "reject", "e", "ev", "m", "i", "w", "errorFrom", "err", "types", "p", "PAUSE_MIN_REQUEST_GAP_MS", "NONE", "refusalName", "code", "PAUSE_REFUSE", "Pause", "Emitter", "hub", "deps", "paused", "OneLobbyError", "from", "msg", "PAUSE_FLAG_PAUSER_LEFT", "peerId", "s", "peer", "wantPaused", "want", "now", "last", "to", "reason", "next", "broadcast", "prev", "p", "VoicePacketizer", "framesPerPacket", "frame", "frames", "flags", "p", "rms", "pcm", "s", "i", "VoiceGate", "level", "nowMs", "want", "edge", "seqDiff", "a", "b", "d", "JitterBuffer", "jitterMs", "n", "f", "fr", "VoiceRelayPolicy", "from", "size", "now", "w", "t", "peerId", "SPEAKING_TIMEOUT_MS", "Voice", "Emitter", "hub", "deps", "VoiceGate", "VoicePacketizer", "VoiceRelayPolicy", "v", "down", "peerId", "muted", "gain", "opus", "pcm", "e", "id", "nowMs", "level", "rms", "g", "frames", "endOfBurst", "p", "msg", "peer", "from", "route", "size", "a", "f", "now", "speaker", "s", "JitterBuffer", "r", "closeCodeFor", "reason", "nextTask", "f", "enc", "dec", "PeerSocket", "peerId", "out", "binary", "onLocalClose", "data", "code", "h", "later", "bytes", "joinerSocket", "lobby", "o", "channel", "b", "offs", "from", "close", "hostConnections", "handler", "conns", "stopped", "ensure", "conn", "p", "id", "MIN_PEER_TIMEOUT_MS", "KICK_LINGER_MS", "HELD_OFFER_MS", "seatName", "p", "n", "hasRelay", "ice", "s", "u", "HOST_WATCHDOG_MS", "PING_EVERY_MS", "Lobby", "Emitter", "hidden", "res", "rej", "cfg", "st", "self", "peerId", "msg", "route", "channel", "reason", "e", "Lockstep", "Chat", "id", "Voice", "Pause", "held", "m", "a", "b", "opts", "joinerSocket", "handler", "hostConnections", "meta", "ban", "code", "close", "ok", "bytes", "link", "ch0", "queueable", "known", "q", "ch", "encodeMessage", "CodecError", "c", "splitChunks", "total", "canRelay", "first", "budget", "err", "OneLobbyError", "relayOnly", "old", "path", "ip", "_reason", "from", "reassembled", "d", "decodeMessage", "r", "Reassembler", "whole", "lastTick", "MODE_LOCKSTEP", "MODE_RAW", "lim", "now", "w", "hostId", "failed", "gone", "pickNewHost", "epoch", "mine", "theirs", "claimWinsWrapped", "wasHost", "l", "peer", "payload", "verdict", "early", "done", "linger", "cancelWatch", "cancel", "timeout", "last", "ms", "CHANNEL_IDS", "defaultSocketFactory", "url", "webCodecsOpusAvailable", "webCodecsOpus", "opts", "onFrame", "ts", "enc", "chunk", "out", "e", "pcm", "data", "onPcm", "dec", "audio", "opus", "WORKLET_SRC", "WorkletMic", "url", "src", "node", "t", "WebAudioSink", "peerId", "gain", "buf", "g", "now", "at", "CHANNEL_INIT", "CHANNEL_IDS", "WebRtcLink", "opts", "rtc", "config", "e", "s", "name", "ch", "n", "data", "d", "offer", "c", "mid", "candidate", "answer", "attempt", "stats", "byId", "v", "pair", "l", "r", "addr", "channel", "bytes", "reason", "wasOpen", "webRtcLinkFactory", "webRtcLinkFactoryFor", "JOIN_CODE_LENGTH", "crc5", "value", "bits", "crc", "i", "bit", "top", "parseIPv4", "ip", "parts", "n", "p", "encodeJoinCode", "ipv4", "port", "encodeData", "data", "v", "s", "CROCKFORD_ALPHABET", "encodeRoomCodeFor", "roomHex", "encodeRoomCode", "b", "x", "normalizeJoinCode", "input", "c", "decodeJoinCode", "version", "a", "looksLikeJoinCode", "utf8", "byteLen", "s", "ReliableLink", "o", "text", "n", "f", "reason", "msgs", "m", "h", "now", "batch", "bytes", "ship", "frame", "seq", "p", "subtle", "s", "buf", "b", "utf8", "concatBytes", "parts", "out", "n", "p", "o", "toHex", "x", "fromHex", "i", "toBase64", "fromBase64", "bin", "sha256", "data", "hkdf", "ikm", "salt", "info", "length", "key", "bits", "deriveRoomKeys", "normalizedCode", "topic", "encryptContent", "plaintext", "iv", "k", "v", "ct", "decryptContent", "content", "raw", "pt", "P", "N", "GX", "GY", "INF", "G", "mod", "a", "m", "r", "pow", "b", "inv", "dbl", "p", "X", "Y", "Z", "A", "B", "C", "D", "E", "F", "X3", "add", "q", "X1", "Y1", "Z1", "X2", "Y2", "Z2", "Z1Z1", "Z2Z2", "U1", "U2", "S1", "S2", "H", "I", "J", "V", "Y3", "Z3", "mul", "k", "affine", "zi", "zi2", "bytesToInt", "n", "x", "intToBytes", "len", "out", "i", "taggedHash", "tag", "msgs", "sha256", "concatBytes", "schnorrPublicKey", "secret", "d", "bytesToInt", "N", "pt", "affine", "mul", "G", "intToBytes", "randomSecretKey", "b", "schnorrSign", "msg", "aux", "dPrime", "px", "th", "taggedHash", "t", "x", "i", "rand", "kPrime", "r", "k", "rx", "e", "concatBytes", "NOSTR_KIND", "eventId", "pubkey", "createdAt", "kind", "tags", "content", "toHex", "sha256", "makeEvent", "secret", "topic", "schnorrPublicKey", "id", "sig", "schnorrSign", "fromHex", "defaultRelayConnect", "url", "WS", "SEEN_MAX", "SINCE_SLACK_S", "RelayPool", "o", "r", "timeoutMs", "resolve", "cancel", "done", "v", "w", "check", "ev", "msg", "ws", "since", "e", "lost", "wasOpen", "retry", "wait", "text", "m", "t", "DEFAULT_NOSTR_RELAYS", "DEFAULT_NOSTR_STUN", "checkNostrRelays", "urls", "opts", "connect", "setT", "fn", "ms", "h", "checkOne", "t0", "b", "finished", "accepted", "finish", "ok", "error", "sub", "published", "publish", "MAX_AGE_S", "NostrRoom", "_NostrRoom", "secret", "keys", "o", "wall", "toHex", "schnorrPublicKey", "RelayPool", "DEFAULT_NOSTR_RELAYS", "defaultRelayConnect", "ev", "norm", "normalizeJoinCode", "deriveRoomKeys", "room", "randomSecretKey", "timeoutMs", "to", "f", "p", "frame", "content", "encryptContent", "makeEvent", "flush", "text", "decryptContent", "NostrSocket", "o", "data", "m", "_code", "reason", "clock", "NostrRoom", "e", "room", "f", "open", "up", "ReliableLink", "out", "text", "why", "code", "clean", "IPV4", "HOSTNAME", "isIPv4", "s", "m", "x", "isIPv6", "tail", "groups", "g", "i", "a", "parseDirectAddress", "input", "defaultPort", "host", "portText", "close", "rest", "first", "port", "v6", "display", "defineBlueprint", "x", "BlueprintError", "OneLobbyError", "path", "rule", "detail", "obj", "props", "req", "bool", "str", "o", "int", "min", "max", "zeroOr", "en", "vals", "ROOT", "IGNORED", "Fail", "join", "p", "k", "isObj", "v", "check", "n", "r", "sub", "i", "keys", "sect", "strip", "out", "deepFreeze", "fill", "input", "game", "engine", "hosting", "lobby", "sync", "players", "chat", "session", "platforms", "ui", "tickRate", "web", "DEFAULT_NOSTR_RELAYS", "crossRules", "b", "csharp", "parseBlueprint", "json", "e", "newSessionBurst", "maxPlayers", "PING", "CLOSED_MEMORY", "NostrHostBridge", "_NostrHostBridge", "hub", "room", "clock", "maxPlayers", "newSessionBurst", "f", "o", "NostrRoom", "timeoutMs", "firstRelayMs", "i", "r", "pub", "p", "known", "PING", "missing", "now", "heard", "peer", "ReliableLink", "out", "text", "session", "CLOSED_MEMORY", "Metrics", "name", "help", "read", "label", "value", "by", "key", "keyOf", "out", "ordinal", "g", "lastBase", "br", "base", "h", "a", "b", "escape", "s", "enc", "sha1", "data", "h", "len", "padded", "dv", "w", "off", "i", "x", "a", "b", "c", "d", "e", "f", "k", "t", "out", "odv", "hmacSha1", "key", "msg", "ipad", "opad", "kb", "toHex", "s", "toBase64", "turnCredential", "secret", "username", "shortHash", "text", "randomHex", "n", "randomBelow", "limit", "buf", "PROTO", "NAME_MAX", "LOBBY_NAME_MAX", "META_KEYS_MAX", "META_LEN_MAX", "PENDING_MAX", "CROCKFORD", "GAME_ID", "defaultHubOptions", "directHubOptions", "maxPlayers", "password", "n", "SIGNAL_FANOUT", "NOSTR_ADDR", "NOSTR_SIGNAL_PER_SEC", "RateWindow", "limit", "windowMs", "now", "sanitize", "s", "max", "fallback", "r", "ch", "c", "last", "normalizeCode", "input", "out", "raw", "Session", "id", "ip", "connId", "send", "close", "Member", "HubLobby", "peerId", "m", "KNOWN_TYPES", "str", "o", "k", "int", "v", "bool", "has", "field", "name", "hex", "randomHex", "byCode", "a", "b", "readMeta", "node", "entries", "meta", "SignalingHub", "Metrics", "options", "deps", "randomBelow", "st", "p", "lobby", "mem", "sess", "code", "at", "key", "text", "msg", "t", "gameId", "netVersion", "resumed", "rl", "token", "entry", "old", "takeOver", "inWindow", "w", "other", "ice", "username", "credential", "turnCredential", "quota", "vis", "mode", "tick", "host", "pm", "hs", "pending", "joiner", "reuse", "l", "offset", "filter", "all", "page", "reply", "me", "to", "target", "rate", "total", "ph", "ban", "closeReason", "data", "link", "js", "epoch", "reason", "kickData", "ms", "wasConnected", "leavingId", "claimant", "x", "claimEpoch", "next", "ps", "i", "you", "peers", "kind", "retryAfterMs", "ev", "shortHash", "LocalHubSocket", "_LocalHubSocket", "hub", "text", "data", "s", "wasOpen", "formatBrowserHostLog", "code", "relays", "hostNostr", "kit", "opts", "bp", "OneLobbyError", "defined", "v", "o", "maxPlayers", "DEFAULT_NOSTR_RELAYS", "stunUrls", "DEFAULT_NOSTR_STUN", "hub", "SignalingHub", "directHubOptions", "ticker", "encodeRoomCode", "env", "bridge", "teardown", "NostrHostBridge", "e", "h", "lobby", "LocalHubSocket", "inner", "peerId", "ip", "watchVisibility", "doc", "warn", "s", "check", "hidden", "changed", "LIB_NAME", "LIB_VERSION", "DEFAULT_READY_TIMEOUT_MS", "hasMic", "browserStorage", "liveSeats", "seatInUse", "token", "l", "OneLobby", "_OneLobby", "Emitter", "json", "overrides", "bp", "parseBlueprint", "name", "OneLobbyError", "BlueprintError", "kit", "o", "tickRate", "a", "pick", "k", "defaultSocketFactory", "webRtcLinkFactory", "systemClock", "codec", "webCodecsOpusAvailable", "webCodecsOpus", "mic", "WorkletMic", "sink", "WebAudioSink", "raw", "s", "code", "seat", "e", "resumeToken", "PROTO_ID", "url", "factory", "timeoutMs", "sig", "welcome", "SignalingClient", "cancel", "timeout", "_", "reject", "state", "direct", "rateLimit", "lobby", "Lobby", "guardLobby", "opts", "h", "definedOnly", "norm", "normalizeCode", "codeOrAddress", "t", "w", "relays", "NostrSocket", "sock", "u", "why", "urls", "DEFAULT_NOSTR_STUN", "input", "looksLikeJoinCode", "d", "decodeJoinCode", "shown", "encodeRoomCodeFor", "addr", "parseDirectAddress", "inner", "links", "joinedWith", "hooks", "hostNostr", "tweakWelcome", "lobbyP", "welcomeP", "encodeJoinCode", "out", "v", "voice", "ErrorCodes", "TEXT", "CODE_RE", "codeOf", "err", "m", "o", "data", "describeError", "code", "key", "e", "out", "s", "currentHref", "shareUrl", "base", "param", "norm", "normalizeCode", "b", "u", "formatCode", "directJoinInfo", "res", "codeFromUrl", "url", "href", "v", "Keepalive", "OneLobby", "KeepaliveError", "OneLobbyError", "enc", "dec", "toBytes", "w", "SharedState", "c", "key", "value", "n", "LobbyController", "opts", "l", "list", "p", "a", "b", "id", "pid", "name", "kit", "code", "norm", "normalizeCode", "OneLobbyError", "data", "go", "quiet", "gen", "lobby", "e", "on", "reason", "ph", "from", "bytes", "u", "describeError", "k", "v", "op", "by", "LOBBY_CSS", "lobbyServerUrl", "raw", "s", "url", "u", "host", "LOBBY_TAG", "NAME_KEY", "TEMPLATE", "readStored", "store", "name", "copyText", "text", "ta", "ok", "makeClass", "OneLobbyBox", "id", "LOBBY_CSS", "LobbyController", "type", "detail", "data", "code", "input", "codeFromUrl", "formatCode", "lobbyServerUrl", "attr", "v", "n", "server", "OneLobbyError", "OneLobby", "on", "ev", "fn", "e", "shareUrl", "done", "btn", "label", "c", "status", "hint", "hintText", "p", "li", "nm", "start", "defineLobbyElement", "tag", "defineLobbyElement"]
|
|
7
|
+
}
|