onelobby 0.1.0 → 0.2.1

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/global.ts", "../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/voice-core.ts", "../src/voice.ts", "../src/lobby.ts", "../src/transport.ts", "../src/voice-browser.ts", "../src/webrtc.ts", "../src/joincode.ts", "../src/direct-address.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": ["/**\n * Entry for the single-file bundles: `dist/onelobby.global.js` (sets `window.OneLobby`) and\n * `dist/onelobby.bundle.mjs` (the same, as one ES module). Whatever is exported here is what a `<script src>` page\n * sees as `OneLobby.*`. It includes the drop-in UI, so loading the bundle also registers `<one-lobby>`\n * (a no-op outside the browser).\n */\nexport * from \"./index.js\";\nexport * from \"./ui/index.js\";\n", "/** 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} 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\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 }\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 }\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", "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/** Reassembles chunked payloads by id. Duplicates are ignored; out-of-order chunks are fine. */\nexport class Reassembler {\n private parts = new Map<number, Partial>();\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 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) return null;\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 clear(): void {\n this.parts.clear();\n }\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(bytes: Uint8Array, stream: { readable: ReadableStream<Uint8Array>; writable: WritableStream<Uint8Array> }) {\n const w = stream.writable.getWriter();\n void w.write(bytes as unknown as Uint8Array<ArrayBuffer>).then(() => w.close());\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 }\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}\nexport function inflate(bytes: Uint8Array): Promise<Uint8Array> {\n return pipe(bytes, new DecompressionStream(\"deflate\") as never);\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;\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 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 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 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 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) 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) this.pending.push({ peerId: from, bytes: msg.bytes });\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._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 onMessage: ((m: ServerMsg) => void) | null = null;\n onClose: (() => void) | null = null;\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 this.onMessage?.(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", "/**\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 { Chat } from \"./chat.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 { 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\";\n\nexport type Phase = \"lobby\" | \"in_game\";\nexport type Peer = PeerInfo;\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 peerLeft: [Peer, reason: string];\n joinRequested: [Peer];\n hostChanged: [hostId: number];\n phase: [Phase];\n message: [from: number, bytes: Uint8Array, channel: ChannelName];\n sessionEnded: [reason: string];\n /** The signaling server went away. The game continues on the data channels (s.9). */\n serverLost: [];\n metaChanged: [Record<string, string>];\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/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\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 readonly voice: Voice;\n\n localPeerId: number;\n hostPeerId: number;\n phase: Phase;\n meta: Record<string, 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/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 /** \"internet\": the router forwarded the port and the code carries its public address; \"same_network\": the LAN address. */\n joinCodeKind: \"internet\" | \"same_network\" | \"none\" = \"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\n private signaling: SignalingClient;\n private cfg: LobbyConfig;\n private roster = new Map<number, Peer>();\n private links = new Map<number, PeerLink>();\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 /**\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).\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.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.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\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 async kick(peerId: number): Promise<void> {\n if (!this.isHost) throw new Error(\"only the host can kick\");\n this.signaling.send({ t: \"kick\", peerId });\n this.removePeer(peerId, \"kicked\");\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 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 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 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 \"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.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 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 (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 // ---- migration -------------------------------------------------------------------------------------------------\n\n private setHost(hostId: number): void {\n if (hostId === this.hostPeerId) return;\n this.hostPeerId = hostId;\n this.emit(\"hostChanged\", hostId);\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 }\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.lockstep.becomeHost();\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 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) this.offerTo(peer.peerId);\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.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\") this.teardown(\"kicked\");\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 if (s.sdp?.type === \"offer\") {\n // We are the answerer; an offer always comes from the (new) host.\n link = this.makeLink(from);\n }\n if (!link) return;\n const payload: SignalPayload = {};\n if (s.sdp) payload.sdp = s.sdp;\n if (s.ice) payload.ice = s.ice;\n await link.handleSignal(payload);\n }\n\n private removePeer(peerId: number, reason: string): 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 const link = this.links.get(peerId);\n if (link && peerId !== this.hostPeerId) {\n link.onClose = null;\n link.onMessage = null;\n link.close();\n this.links.delete(peerId);\n this.hiSentTo.delete(link);\n }\n this.connected.delete(peerId);\n this.outbox.delete(peerId);\n this.lockstep.onPeerLeft(peerId);\n this.chat.onPeerLeft(peerId);\n this.voice.onPeerLeft(peerId);\n if (peer) this.emit(\"peerLeft\", peer, reason);\n if (peerId === this.hostPeerId && !this.isHost) {\n this.handleHostLost(reason);\n }\n }\n\n private dropPeer(peerId: number, reason: string): void {\n this.signaling.send({ t: \"kick\", peerId });\n this.removePeer(peerId, reason);\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 === \"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 || 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 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 private teardown(reason: string): void {\n if (this.ended) return;\n this.ended = true;\n this.cancelReadyTimer?.();\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()) {\n l.onClose = null;\n l.onMessage = null;\n l.close();\n }\n this.links.clear();\n this.signaling.onClose = null;\n this.signaling.onMessage = null;\n this.signaling.close();\n this.emit(\"sessionEnded\", reason);\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 /** 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/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\n private pc: RTCPeerConnection;\n private channels = {} as Record<ChannelName, RTCDataChannel>;\n private pendingIce: RTCIceCandidateInit[] = [];\n private closed = false;\n private opened = 0;\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 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 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 this.onMessage?.(name, data);\n };\n this.channels[name] = ch;\n }\n }\n\n startOffer(): void {\n void (async () => {\n try {\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 for (const c of this.pendingIce.splice(0)) await this.pc.addIceCandidate(c);\n if (this.extra.length) {\n // docs/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 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 if (this.pc.remoteDescription) 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 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/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 } | { 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 const data = (BigInt(ip) << 16n) | BigInt(port); // version 0\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/** 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 if (data >> 48n !== 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 * A direct-IP host address as a player types it (docs/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/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", "import { systemClock, type Clock } from \"./clock.js\";\nimport { Emitter } from \"./emitter.js\";\nimport { normalizeCode } from \"./crockford.js\";\nimport { Lobby } 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, looksLikeJoinCode } from \"./joincode.js\";\nimport { DEFAULT_DIRECT_PORT, parseDirectAddress } from \"./direct-address.js\";\n\nexport const LIB_NAME = \"onelobby\";\nexport const LIB_VERSION = \"0.1.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}\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 * 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\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}\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/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}\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\">>;\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\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 };\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.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 = 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 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 private makeLobby(sig: SignalingClient, welcome: WelcomeMsg, state: LobbyState, direct?: { links: LinkFactory }): 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 });\n lobby.errorSink = (e) => this.emit(\"error\", e);\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 async host(h: HostOptions): Promise<Lobby> {\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);\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/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 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 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 const { sig, welcome } = await this.connect(addr.url, this.socketFactory, opts.connectTimeoutMs ?? 5000);\n try {\n const state = await sig.request<LobbyState>({ t: \"join\", ...(opts.password ? { password: opts.password } : {}) }, \"lobby\");\n return this.makeLobby(sig, welcome, state, { links });\n } catch (e) {\n sig.close();\n throw e;\n }\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) });\n } catch (e) {\n sig.close();\n throw e;\n }\n }\n\n async rejoin(resumeToken: string): Promise<Lobby> {\n const sig = new SignalingClient(this.options.lobbyServer, this.socketFactory, this.clock);\n await sig.connect();\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 = 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);\n } catch (e) {\n sig.close();\n throw e;\n }\n }\n\n async list(opts: ListOptions = {}): Promise<LobbySummary[]> {\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 * 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\";\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\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 timeout: { message: \"The connection took too long.\", hint: \"Check your internet connection, then try again.\" },\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/** 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": ["global_exports", "__export", "CHAT_MAX_BYTES", "Chat", "DEFAULT_READY_TIMEOUT_MS", "Keepalive", "KeepaliveError", "LIB_NAME", "LIB_VERSION", "LOBBY_TAG", "Lobby", "LobbyController", "Lockstep", "OneLobby", "OneLobbyError", "SharedState", "Voice", "codeFromUrl", "decodeJoinCode", "defineLobbyElement", "describeError", "formatCode", "lobbyServerUrl", "looksLikeJoinCode", "normalizeCode", "shareUrl", "webCodecsOpusAvailable", "webRtcLinkFactoryFor", "workerInterval", "fn", "ms", "src", "url", "w", "systemClock", "h", "Emitter", "event", "fn", "list", "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", "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", "t", "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", "i", "Reassembler", "chunk", "CodecError", "pos", "p", "deflateAvailable", "pipe", "bytes", "stream", "w", "chunks", "r", "total", "done", "value", "out", "pos", "deflate", "inflate", "MAX_DESYNCS", "MAX_RESTORE_RETRIES", "HOST_SILENCE_MS", "PEER_HASH_SILENCE_MS", "SNAPSHOT_COMPRESS_MIN", "Lockstep", "Emitter", "hub", "opts", "CHUNK_PAYLOAD", "bytes", "f", "tick", "hash", "theirs", "peer", "h", "b", "interval", "peerId", "m", "c", "now", "due", "i", "commands", "a", "frame", "msg", "p", "limit", "last", "from", "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", "resolve", "reject", "e", "ev", "m", "i", "w", "errorFrom", "err", "types", "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", "id", "nowMs", "level", "rms", "g", "frames", "endOfBurst", "p", "msg", "peer", "from", "route", "size", "a", "f", "now", "speaker", "s", "JitterBuffer", "seatName", "p", "n", "hasRelay", "ice", "s", "u", "HOST_WATCHDOG_MS", "PING_EVERY_MS", "Lobby", "Emitter", "res", "rej", "cfg", "st", "self", "peerId", "msg", "route", "channel", "reason", "e", "Lockstep", "Chat", "id", "Voice", "m", "a", "b", "meta", "ok", "bytes", "opts", "link", "ch0", "queueable", "known", "q", "ch", "encodeMessage", "CodecError", "c", "splitChunks", "total", "canRelay", "first", "budget", "err", "OneLobbyError", "relayOnly", "old", "path", "_reason", "from", "reassembled", "d", "decodeMessage", "r", "Reassembler", "whole", "lastTick", "MODE_LOCKSTEP", "MODE_RAW", "hostId", "failed", "gone", "pickNewHost", "epoch", "mine", "theirs", "l", "claimWinsWrapped", "wasHost", "peer", "payload", "code", "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", "WebAudioSink", "peerId", "gain", "buf", "g", "now", "at", "CHANNEL_INIT", "CHANNEL_IDS", "WebRtcLink", "opts", "rtc", "config", "e", "s", "name", "ch", "d", "data", "offer", "c", "mid", "candidate", "answer", "attempt", "stats", "byId", "v", "pair", "l", "r", "channel", "bytes", "reason", "wasOpen", "webRtcLinkFactory", "webRtcLinkFactoryFor", "JOIN_CODE_LENGTH", "crc5", "value", "bits", "crc", "i", "bit", "top", "normalizeJoinCode", "input", "s", "c", "JOIN_CODE_LENGTH", "CROCKFORD_ALPHABET", "decodeJoinCode", "v", "data", "crc5", "port", "a", "looksLikeJoinCode", "IPV4", "HOSTNAME", "isIPv4", "s", "m", "x", "isIPv6", "tail", "groups", "g", "a", "parseDirectAddress", "input", "defaultPort", "host", "portText", "close", "rest", "first", "port", "v6", "display", "LIB_NAME", "LIB_VERSION", "DEFAULT_READY_TIMEOUT_MS", "hasMic", "browserStorage", "liveSeats", "seatInUse", "token", "l", "OneLobby", "Emitter", "o", "tickRate", "a", "pick", "k", "defaultSocketFactory", "webRtcLinkFactory", "systemClock", "codec", "webCodecsOpusAvailable", "webCodecsOpus", "mic", "WorkletMic", "sink", "WebAudioSink", "raw", "s", "code", "seat", "OneLobbyError", "resumeToken", "PROTO_ID", "url", "factory", "timeoutMs", "sig", "SignalingClient", "cancel", "timeout", "_", "reject", "e", "welcome", "state", "direct", "lobby", "Lobby", "h", "norm", "normalizeCode", "codeOrAddress", "opts", "input", "looksLikeJoinCode", "d", "decodeJoinCode", "addr", "parseDirectAddress", "inner", "links", "hooks", "lobbyP", "welcomeP", "TEXT", "CODE_RE", "codeOf", "err", "m", "o", "data", "describeError", "code", "key", "e", "out", "s", "currentHref", "shareUrl", "base", "param", "norm", "normalizeCode", "b", "u", "formatCode", "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"]
3
+ "sources": ["../src/global.ts", "../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/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/direct-address.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": ["/**\n * Entry for the single-file bundles: `dist/onelobby.global.js` (sets `window.OneLobby`) and\n * `dist/onelobby.bundle.mjs` (the same, as one ES module). Whatever is exported here is what a `<script src>` page\n * sees as `OneLobby.*`. It includes the drop-in UI, so loading the bundle also registers `<one-lobby>`\n * (a no-op outside the browser).\n */\nexport * from \"./index.js\";\nexport * from \"./ui/index.js\";\n", "/** 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} 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\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 }\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 }\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", "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/** Reassembles chunked payloads by id. Duplicates are ignored; out-of-order chunks are fine. */\nexport class Reassembler {\n private parts = new Map<number, Partial>();\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 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) return null;\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 clear(): void {\n this.parts.clear();\n }\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(bytes: Uint8Array, stream: { readable: ReadableStream<Uint8Array>; writable: WritableStream<Uint8Array> }) {\n const w = stream.writable.getWriter();\n void w.write(bytes as unknown as Uint8Array<ArrayBuffer>).then(() => w.close());\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 }\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}\nexport function inflate(bytes: Uint8Array): Promise<Uint8Array> {\n return pipe(bytes, new DecompressionStream(\"deflate\") as never);\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;\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 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 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 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 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) 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) this.pending.push({ peerId: from, bytes: msg.bytes });\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._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 onMessage: ((m: ServerMsg) => void) | null = null;\n onClose: (() => void) | null = null;\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 this.onMessage?.(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", "/**\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 { 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 { 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\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 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\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/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\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 readonly voice: Voice;\n\n localPeerId: number;\n hostPeerId: number;\n phase: Phase;\n meta: Record<string, 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/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 /** \"internet\": the router forwarded the port and the code carries its public address; \"same_network\": the LAN address. */\n joinCodeKind: \"internet\" | \"same_network\" | \"none\" = \"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\n private signaling: SignalingClient;\n private cfg: LobbyConfig;\n private roster = new Map<number, Peer>();\n private links = new Map<number, PeerLink>();\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: 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.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.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 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 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 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 \"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.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 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 }\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 }\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.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 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) this.offerTo(peer.peerId);\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.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 if (s.sdp?.type === \"offer\") {\n // We are the answerer; an offer always comes from the (new) host.\n link = this.makeLink(from);\n }\n if (!link) return;\n const payload: SignalPayload = {};\n if (s.sdp) payload.sdp = s.sdp;\n if (s.ice) payload.ice = s.ice;\n await link.handleSignal(payload);\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 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 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 === \"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 private teardown(reason: string, close?: CloseInfo): void {\n if (this.ended) return;\n this.ended = true;\n this.cancelReadyTimer?.();\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.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 /** 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/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\n private pc: RTCPeerConnection;\n private channels = {} as Record<ChannelName, RTCDataChannel>;\n private pendingIce: RTCIceCandidateInit[] = [];\n private closed = false;\n private opened = 0;\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 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 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 this.onMessage?.(name, data);\n };\n this.channels[name] = ch;\n }\n }\n\n startOffer(): void {\n void (async () => {\n try {\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 for (const c of this.pendingIce.splice(0)) await this.pc.addIceCandidate(c);\n if (this.extra.length) {\n // docs/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 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 if (this.pc.remoteDescription) 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 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/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 } | { 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 const data = (BigInt(ip) << 16n) | BigInt(port); // version 0\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/** 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 if (data >> 48n !== 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 * A direct-IP host address as a player types it (docs/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/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", "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, looksLikeJoinCode } from \"./joincode.js\";\nimport { DEFAULT_DIRECT_PORT, parseDirectAddress } from \"./direct-address.js\";\n\nexport const LIB_NAME = \"onelobby\";\nexport const LIB_VERSION = \"0.2.1\";\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}\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\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/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}\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\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.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 = 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 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 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.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 async host(h: HostOptions): Promise<Lobby> {\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/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 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 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 const { sig, welcome } = await this.connect(addr.url, this.socketFactory, opts.connectTimeoutMs ?? 5000);\n try {\n const state = await sig.request<LobbyState>({ t: \"join\", ...(opts.password ? { password: opts.password } : {}) }, \"lobby\");\n const lobby = this.makeLobby(sig, welcome, state, { links });\n // a joiner's joinCode is what it joined with (normalized), so a HUD can show it on both sides\n lobby.joinCode = joinedWith(addr);\n return lobby;\n } catch (e) {\n sig.close();\n throw e;\n }\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 async rejoin(resumeToken: string): Promise<Lobby> {\n const sig = new SignalingClient(this.options.lobbyServer, this.socketFactory, this.clock);\n await sig.connect();\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 = 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);\n } catch (e) {\n sig.close();\n throw e;\n }\n }\n\n async list(opts: ListOptions = {}): Promise<LobbySummary[]> {\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", "/**\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\";\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\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 timeout: { message: \"The connection took too long.\", hint: \"Check your internet connection, then try again.\" },\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/** 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": ["global_exports", "__export", "CHAT_MAX_BYTES", "Chat", "DEFAULT_READY_TIMEOUT_MS", "Keepalive", "KeepaliveError", "LIB_NAME", "LIB_VERSION", "LOBBY_TAG", "Lobby", "LobbyController", "Lockstep", "MIN_PEER_TIMEOUT_MS", "OneLobby", "OneLobbyError", "SharedState", "Voice", "closeCodeFor", "codeFromUrl", "decodeJoinCode", "defineLobbyElement", "describeError", "formatCode", "lobbyServerUrl", "looksLikeJoinCode", "normalizeCode", "shareUrl", "webCodecsOpusAvailable", "webRtcLinkFactoryFor", "workerInterval", "fn", "ms", "src", "url", "w", "systemClock", "h", "Emitter", "event", "fn", "list", "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", "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", "t", "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", "i", "Reassembler", "chunk", "CodecError", "pos", "p", "deflateAvailable", "pipe", "bytes", "stream", "w", "chunks", "r", "total", "done", "value", "out", "pos", "deflate", "inflate", "MAX_DESYNCS", "MAX_RESTORE_RETRIES", "HOST_SILENCE_MS", "PEER_HASH_SILENCE_MS", "SNAPSHOT_COMPRESS_MIN", "Lockstep", "Emitter", "hub", "opts", "CHUNK_PAYLOAD", "bytes", "f", "tick", "hash", "theirs", "peer", "h", "b", "interval", "peerId", "m", "c", "now", "due", "i", "commands", "a", "frame", "msg", "p", "limit", "last", "from", "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", "resolve", "reject", "e", "ev", "m", "i", "w", "errorFrom", "err", "types", "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", "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", "c", "conn", "p", "id", "MIN_PEER_TIMEOUT_MS", "KICK_LINGER_MS", "seatName", "p", "n", "hasRelay", "ice", "s", "u", "HOST_WATCHDOG_MS", "PING_EVERY_MS", "Lobby", "Emitter", "res", "rej", "cfg", "st", "self", "peerId", "msg", "route", "channel", "reason", "e", "Lockstep", "Chat", "id", "Voice", "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", "_reason", "from", "reassembled", "d", "decodeMessage", "r", "Reassembler", "whole", "lastTick", "MODE_LOCKSTEP", "MODE_RAW", "lim", "now", "w", "hostId", "failed", "gone", "pickNewHost", "epoch", "mine", "theirs", "l", "claimWinsWrapped", "wasHost", "peer", "payload", "linger", "done", "cancel", "timeout", "last", "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", "WebAudioSink", "peerId", "gain", "buf", "g", "now", "at", "CHANNEL_INIT", "CHANNEL_IDS", "WebRtcLink", "opts", "rtc", "config", "e", "s", "name", "ch", "d", "data", "offer", "c", "mid", "candidate", "answer", "attempt", "stats", "byId", "v", "pair", "l", "r", "channel", "bytes", "reason", "wasOpen", "webRtcLinkFactory", "webRtcLinkFactoryFor", "JOIN_CODE_LENGTH", "crc5", "value", "bits", "crc", "i", "bit", "top", "parseIPv4", "ip", "parts", "n", "p", "encodeJoinCode", "ipv4", "port", "data", "v", "s", "CROCKFORD_ALPHABET", "normalizeJoinCode", "input", "c", "decodeJoinCode", "a", "looksLikeJoinCode", "IPV4", "HOSTNAME", "isIPv4", "s", "m", "x", "isIPv6", "tail", "groups", "g", "a", "parseDirectAddress", "input", "defaultPort", "host", "portText", "close", "rest", "first", "port", "v6", "display", "LIB_NAME", "LIB_VERSION", "DEFAULT_READY_TIMEOUT_MS", "hasMic", "browserStorage", "liveSeats", "seatInUse", "token", "l", "OneLobby", "Emitter", "o", "tickRate", "a", "pick", "k", "defaultSocketFactory", "webRtcLinkFactory", "systemClock", "codec", "webCodecsOpusAvailable", "webCodecsOpus", "mic", "WorkletMic", "sink", "WebAudioSink", "raw", "s", "code", "seat", "OneLobbyError", "resumeToken", "PROTO_ID", "url", "factory", "timeoutMs", "sig", "SignalingClient", "cancel", "timeout", "_", "reject", "e", "welcome", "state", "direct", "rateLimit", "lobby", "Lobby", "h", "norm", "normalizeCode", "codeOrAddress", "opts", "input", "looksLikeJoinCode", "d", "decodeJoinCode", "addr", "parseDirectAddress", "inner", "links", "joinedWith", "hooks", "lobbyP", "welcomeP", "encodeJoinCode", "TEXT", "CODE_RE", "codeOf", "err", "m", "o", "data", "describeError", "code", "key", "e", "out", "s", "currentHref", "shareUrl", "base", "param", "norm", "normalizeCode", "b", "u", "formatCode", "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
7
  }