onelobby 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +77 -2
  3. package/dist/chat.d.ts +60 -0
  4. package/dist/chat.js +163 -0
  5. package/dist/chat.js.map +1 -0
  6. package/dist/chunking.d.ts +17 -0
  7. package/dist/chunking.js +59 -0
  8. package/dist/chunking.js.map +1 -0
  9. package/dist/clock.d.ts +8 -0
  10. package/dist/clock.js +39 -0
  11. package/dist/clock.js.map +1 -0
  12. package/dist/codec.d.ts +141 -0
  13. package/dist/codec.js +308 -0
  14. package/dist/codec.js.map +1 -0
  15. package/dist/crockford.d.ts +14 -0
  16. package/dist/crockford.js +55 -0
  17. package/dist/crockford.js.map +1 -0
  18. package/dist/deflate.d.ts +4 -0
  19. package/dist/deflate.js +32 -0
  20. package/dist/deflate.js.map +1 -0
  21. package/dist/deprecated.d.ts +18 -0
  22. package/dist/deprecated.js +11 -0
  23. package/dist/deprecated.js.map +1 -0
  24. package/dist/direct/hub-host.d.ts +82 -0
  25. package/dist/direct/hub-host.js +393 -0
  26. package/dist/direct/hub-host.js.map +1 -0
  27. package/dist/direct/hub.d.ts +178 -0
  28. package/dist/direct/hub.js +862 -0
  29. package/dist/direct/hub.js.map +1 -0
  30. package/dist/direct/index.d.ts +57 -0
  31. package/dist/direct/index.js +71 -0
  32. package/dist/direct/index.js.map +1 -0
  33. package/dist/direct/portmap.d.ts +84 -0
  34. package/dist/direct/portmap.js +465 -0
  35. package/dist/direct/portmap.js.map +1 -0
  36. package/dist/direct-address.d.ts +20 -0
  37. package/dist/direct-address.js +61 -0
  38. package/dist/direct-address.js.map +1 -0
  39. package/dist/emitter.d.ts +9 -0
  40. package/dist/emitter.js +36 -0
  41. package/dist/emitter.js.map +1 -0
  42. package/dist/friendly.d.ts +20 -0
  43. package/dist/friendly.js +120 -0
  44. package/dist/friendly.js.map +1 -0
  45. package/dist/global.d.ts +8 -0
  46. package/dist/global.js +9 -0
  47. package/dist/global.js.map +1 -0
  48. package/dist/gridpaint.d.ts +27 -0
  49. package/dist/gridpaint.js +106 -0
  50. package/dist/gridpaint.js.map +1 -0
  51. package/dist/hub.d.ts +25 -0
  52. package/dist/hub.js +2 -0
  53. package/dist/hub.js.map +1 -0
  54. package/dist/index.d.ts +35 -0
  55. package/dist/index.js +25 -0
  56. package/dist/index.js.map +1 -0
  57. package/dist/internals.d.ts +28 -0
  58. package/dist/internals.js +24 -0
  59. package/dist/internals.js.map +1 -0
  60. package/dist/joincode.d.ts +21 -0
  61. package/dist/joincode.js +84 -0
  62. package/dist/joincode.js.map +1 -0
  63. package/dist/lobby.d.ts +203 -0
  64. package/dist/lobby.js +840 -0
  65. package/dist/lobby.js.map +1 -0
  66. package/dist/lockstep.d.ts +132 -0
  67. package/dist/lockstep.js +471 -0
  68. package/dist/lockstep.js.map +1 -0
  69. package/dist/migration.d.ts +17 -0
  70. package/dist/migration.js +34 -0
  71. package/dist/migration.js.map +1 -0
  72. package/dist/onelobby.bundle.mjs +140 -0
  73. package/dist/onelobby.bundle.mjs.map +7 -0
  74. package/dist/onelobby.d.ts +151 -0
  75. package/dist/onelobby.global.js +3897 -0
  76. package/dist/onelobby.global.js.map +7 -0
  77. package/dist/onelobby.global.min.js +140 -0
  78. package/dist/onelobby.global.min.js.map +7 -0
  79. package/dist/onelobby.js +337 -0
  80. package/dist/onelobby.js.map +1 -0
  81. package/dist/signaling.d.ts +94 -0
  82. package/dist/signaling.js +134 -0
  83. package/dist/signaling.js.map +1 -0
  84. package/dist/socket.d.ts +63 -0
  85. package/dist/socket.js +142 -0
  86. package/dist/socket.js.map +1 -0
  87. package/dist/testing.d.ts +135 -0
  88. package/dist/testing.js +438 -0
  89. package/dist/testing.js.map +1 -0
  90. package/dist/transport.d.ts +80 -0
  91. package/dist/transport.js +7 -0
  92. package/dist/transport.js.map +1 -0
  93. package/dist/ui/controller.d.ts +137 -0
  94. package/dist/ui/controller.js +337 -0
  95. package/dist/ui/controller.js.map +1 -0
  96. package/dist/ui/element.d.ts +22 -0
  97. package/dist/ui/element.js +284 -0
  98. package/dist/ui/element.js.map +1 -0
  99. package/dist/ui/index.d.ts +5 -0
  100. package/dist/ui/index.js +9 -0
  101. package/dist/ui/index.js.map +1 -0
  102. package/dist/ui/server-url.d.ts +9 -0
  103. package/dist/ui/server-url.js +39 -0
  104. package/dist/ui/server-url.js.map +1 -0
  105. package/dist/ui/styles.d.ts +2 -0
  106. package/dist/ui/styles.js +70 -0
  107. package/dist/ui/styles.js.map +1 -0
  108. package/dist/voice-browser.d.ts +19 -0
  109. package/dist/voice-browser.js +151 -0
  110. package/dist/voice-browser.js.map +1 -0
  111. package/dist/voice-core.d.ts +98 -0
  112. package/dist/voice-core.js +233 -0
  113. package/dist/voice-core.js.map +1 -0
  114. package/dist/voice-types.d.ts +35 -0
  115. package/dist/voice-types.js +3 -0
  116. package/dist/voice-types.js.map +1 -0
  117. package/dist/voice.d.ts +68 -0
  118. package/dist/voice.js +223 -0
  119. package/dist/voice.js.map +1 -0
  120. package/dist/webrtc.d.ts +29 -0
  121. package/dist/webrtc.js +169 -0
  122. package/dist/webrtc.js.map +1 -0
  123. package/package.json +93 -4
@@ -0,0 +1,9 @@
1
+ /** Minimal typed event emitter (zero deps). */
2
+ export declare class Emitter<E extends {
3
+ [k: string]: unknown[];
4
+ }> {
5
+ private handlers;
6
+ on<K extends keyof E>(event: K, fn: (...args: E[K]) => void): () => void;
7
+ off<K extends keyof E>(event: K, fn: (...args: E[K]) => void): void;
8
+ protected emit<K extends keyof E>(event: K, ...args: E[K]): void;
9
+ }
@@ -0,0 +1,36 @@
1
+ /** Minimal typed event emitter (zero deps). */
2
+ export class Emitter {
3
+ handlers = new Map();
4
+ on(event, fn) {
5
+ let list = this.handlers.get(event);
6
+ if (!list)
7
+ this.handlers.set(event, (list = []));
8
+ list.push(fn);
9
+ return () => this.off(event, fn);
10
+ }
11
+ off(event, fn) {
12
+ const list = this.handlers.get(event);
13
+ if (!list)
14
+ return;
15
+ const i = list.indexOf(fn);
16
+ if (i >= 0)
17
+ list.splice(i, 1);
18
+ }
19
+ emit(event, ...args) {
20
+ const list = this.handlers.get(event);
21
+ if (!list)
22
+ return;
23
+ for (const fn of [...list]) {
24
+ try {
25
+ fn(...args);
26
+ }
27
+ catch (e) {
28
+ // A throwing listener must never break the protocol state machine.
29
+ queueMicrotask(() => {
30
+ throw e;
31
+ });
32
+ }
33
+ }
34
+ }
35
+ }
36
+ //# sourceMappingURL=emitter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emitter.js","sourceRoot":"","sources":["../src/emitter.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAC/C,MAAM,OAAO,OAAO;IACV,QAAQ,GAAG,IAAI,GAAG,EAAyC,CAAC;IAEpE,EAAE,CAAoB,KAAQ,EAAE,EAA2B;QACzD,IAAI,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,CAAC,IAAI;YAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC;QACjD,IAAI,CAAC,IAAI,CAAC,EAA2B,CAAC,CAAC;QACvC,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACnC,CAAC;IAED,GAAG,CAAoB,KAAQ,EAAE,EAA2B;QAC1D,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACtC,IAAI,CAAC,IAAI;YAAE,OAAO;QAClB,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,EAA2B,CAAC,CAAC;QACpD,IAAI,CAAC,IAAI,CAAC;YAAE,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAChC,CAAC;IAES,IAAI,CAAoB,KAAQ,EAAE,GAAG,IAAU;QACvD,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACtC,IAAI,CAAC,IAAI;YAAE,OAAO;QAClB,KAAK,MAAM,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC;YAC3B,IAAI,CAAC;gBACH,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;YACd,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,mEAAmE;gBACnE,cAAc,CAAC,GAAG,EAAE;oBAClB,MAAM,CAAC,CAAC;gBACV,CAAC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;CACF","sourcesContent":["/** 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"]}
@@ -0,0 +1,20 @@
1
+ export interface FriendlyError {
2
+ /** Stable machine code (the OneLobbyError code, or "unknown"). */
3
+ code: string;
4
+ /** One short sentence a player can read. */
5
+ message: string;
6
+ /** What the player can do about it, when there is something. */
7
+ hint?: string;
8
+ }
9
+ /**
10
+ * Plain-language, player-facing text for any error the kit (or its server) can produce.
11
+ * Never returns a stack trace or raw technical message; unknown errors get a generic friendly line.
12
+ */
13
+ export declare function describeError(err: unknown): FriendlyError;
14
+ /**
15
+ * A link that opens the game straight into lobby `code`, e.g. `https://game.example/?join=7HQ-4K2`.
16
+ * `base` defaults to the current page; any existing `param` is replaced. Throws on an invalid code.
17
+ */
18
+ export declare function shareUrl(code: string, base?: string, param?: string): string;
19
+ /** The lobby code carried by a share link (see shareUrl), or null when there is none or it is not a valid code. */
20
+ export declare function codeFromUrl(url?: string, param?: string): string | null;
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Player-facing helpers: plain-language error text and shareable join links.
3
+ * Nothing here talks to the network; it is safe to call from any UI code.
4
+ */
5
+ import { formatCode, normalizeCode } from "./crockford.js";
6
+ const TEXT = {
7
+ // server errors (spec/signaling.md)
8
+ 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." },
9
+ bad_code: { message: "That doesn't look like a lobby code.", hint: "Codes are 6 letters and numbers, like 7HQ 4K2." },
10
+ lobby_full: { message: "That lobby is full.", hint: "Ask the host to make room, or start your own lobby." },
11
+ 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." },
12
+ in_game: { message: "That game has already started.", hint: "Ask the host to let people join mid-game, or wait for the next round." },
13
+ kicked: { message: "The host removed you from the lobby." },
14
+ denied: { message: "The host didn't let you in this time.", hint: "Ask the host to approve you, then try again." },
15
+ not_host: { message: "Only the host can do that." },
16
+ rate_limited: { message: "Slow down a little: too many tries in a short time.", hint: "Wait a moment, then try again." },
17
+ max_lobbies: { message: "You already have the most lobbies open that this server allows.", hint: "Close one of your other lobbies, then try again." },
18
+ server_full: { message: "The game server is full right now.", hint: "Try again in a few minutes." },
19
+ 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." },
20
+ 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." },
21
+ // client errors
22
+ connect_failed: { message: "Couldn't reach the game server.", hint: "Check your internet connection, then try again." },
23
+ closed: { message: "Lost the connection to the game server.", hint: "Check your internet connection, then try again." },
24
+ 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." },
25
+ invalid_token: { message: "Your old seat in that lobby is no longer available.", hint: "Join again with the lobby code." },
26
+ 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." },
27
+ timeout: { message: "The connection took too long.", hint: "Check your internet connection, then try again." },
28
+ left: { message: "You left the lobby." },
29
+ unknown: { message: "Something went wrong.", hint: "Try again. If it keeps happening, reload the page." },
30
+ };
31
+ const CODE_RE = /^[a-z][a-z0-9_]*$/;
32
+ function codeOf(err) {
33
+ if (typeof err === "string") {
34
+ const m = /^([a-z][a-z0-9_]*)(?::|$)/.exec(err.trim());
35
+ return { code: m ? m[1] : null };
36
+ }
37
+ if (err && typeof err === "object") {
38
+ const o = err;
39
+ const data = o.data && typeof o.data === "object" ? o.data : undefined;
40
+ if (typeof o.code === "string" && CODE_RE.test(o.code))
41
+ return { code: o.code, data };
42
+ if (typeof o.message === "string") {
43
+ const m = /^([a-z][a-z0-9_]*):/.exec(o.message);
44
+ if (m && TEXT[m[1]])
45
+ return { code: m[1], data };
46
+ // browser network failures (fetch/WebSocket) surface as TypeError / NetworkError
47
+ if (o.name === "NetworkError" || /network|failed to fetch|websocket/i.test(o.message))
48
+ return { code: "connect_failed" };
49
+ if (o.name === "TimeoutError" || /timed? ?out/i.test(o.message))
50
+ return { code: "timeout" };
51
+ }
52
+ }
53
+ return { code: null };
54
+ }
55
+ /**
56
+ * Plain-language, player-facing text for any error the kit (or its server) can produce.
57
+ * Never returns a stack trace or raw technical message; unknown errors get a generic friendly line.
58
+ */
59
+ export function describeError(err) {
60
+ const { code, data } = codeOf(err);
61
+ if (!code)
62
+ return { code: "unknown", ...TEXT.unknown };
63
+ let key = code;
64
+ if (code === "kicked" && data?.reason === "denied")
65
+ key = "denied";
66
+ if (code === "rate_limited" && data?.reason === "max_lobbies")
67
+ key = "max_lobbies";
68
+ const e = TEXT[key];
69
+ if (!e)
70
+ return { code, ...TEXT.unknown };
71
+ const out = { code, message: e.message };
72
+ if (e.hint)
73
+ out.hint = e.hint;
74
+ if (code === "rate_limited" && typeof data?.retryAfterMs === "number" && key === "rate_limited") {
75
+ const s = Math.max(1, Math.ceil(data.retryAfterMs / 1000));
76
+ out.hint = `Wait ${s} second${s === 1 ? "" : "s"}, then try again.`;
77
+ }
78
+ return out;
79
+ }
80
+ function currentHref() {
81
+ try {
82
+ return typeof location !== "undefined" ? location.href : undefined;
83
+ }
84
+ catch {
85
+ return undefined;
86
+ }
87
+ }
88
+ /**
89
+ * A link that opens the game straight into lobby `code`, e.g. `https://game.example/?join=7HQ-4K2`.
90
+ * `base` defaults to the current page; any existing `param` is replaced. Throws on an invalid code.
91
+ */
92
+ export function shareUrl(code, base, param = "join") {
93
+ const norm = normalizeCode(code);
94
+ if (!norm)
95
+ throw new Error(`"${code}" is not a valid lobby code`);
96
+ const b = base ?? currentHref();
97
+ if (!b)
98
+ throw new Error("shareUrl needs a base URL outside the browser");
99
+ const u = new URL(b);
100
+ u.searchParams.delete(param);
101
+ // URLSearchParams would encode the display space as "+": use a hyphen, which normalizeCode also accepts
102
+ u.searchParams.set(param, formatCode(norm).replace(" ", "-"));
103
+ return u.toString();
104
+ }
105
+ /** The lobby code carried by a share link (see shareUrl), or null when there is none or it is not a valid code. */
106
+ export function codeFromUrl(url, param = "join") {
107
+ const href = url ?? currentHref();
108
+ if (!href)
109
+ return null;
110
+ let u;
111
+ try {
112
+ u = new URL(href, "http://localhost/");
113
+ }
114
+ catch {
115
+ return null;
116
+ }
117
+ const v = u.searchParams.get(param) ?? new URLSearchParams(u.hash.replace(/^#/, "")).get(param);
118
+ return v ? normalizeCode(v) : null;
119
+ }
120
+ //# sourceMappingURL=friendly.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"friendly.js","sourceRoot":"","sources":["../src/friendly.ts"],"names":[],"mappings":"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","sourcesContent":["/**\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"]}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Entry for the single-file bundles: `dist/onelobby.global.js` (sets `window.OneLobby`) and
3
+ * `dist/onelobby.bundle.mjs` (the same, as one ES module). Whatever is exported here is what a `<script src>` page
4
+ * sees as `OneLobby.*`. It includes the drop-in UI, so loading the bundle also registers `<one-lobby>`
5
+ * (a no-op outside the browser).
6
+ */
7
+ export * from "./index.js";
8
+ export * from "./ui/index.js";
package/dist/global.js ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Entry for the single-file bundles: `dist/onelobby.global.js` (sets `window.OneLobby`) and
3
+ * `dist/onelobby.bundle.mjs` (the same, as one ES module). Whatever is exported here is what a `<script src>` page
4
+ * sees as `OneLobby.*`. It includes the drop-in UI, so loading the bundle also registers `<one-lobby>`
5
+ * (a no-op outside the browser).
6
+ */
7
+ export * from "./index.js";
8
+ export * from "./ui/index.js";
9
+ //# sourceMappingURL=global.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"global.js","sourceRoot":"","sources":["../src/global.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC","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"]}
@@ -0,0 +1,27 @@
1
+ export declare const GRID_W = 16;
2
+ export declare const GRID_H = 16;
3
+ export declare const OP_PAINT = 1;
4
+ export declare const PALETTE: string[];
5
+ export declare const colourOf: (peerId: number) => number;
6
+ export declare function fnv1a32(bytes: Uint8Array): number;
7
+ export declare function fnv1a64(bytes: Uint8Array): bigint;
8
+ export declare function seedFromCode(code: string): number;
9
+ export declare function xorshift32(x: number): number;
10
+ export declare const paintCmd: (x: number, y: number) => Uint8Array;
11
+ export declare class Gridpaint {
12
+ tick: number;
13
+ rng: number;
14
+ grid: Uint8Array<ArrayBuffer>;
15
+ constructor(code: string);
16
+ /** Apply frame `tick` (must be current + 1). Returns the hash after the tick. */
17
+ step(tick: number, cmds?: ReadonlyArray<{
18
+ peerId: number;
19
+ bytes: Uint8Array;
20
+ }>): bigint;
21
+ apply(peerId: number, bytes: Uint8Array): void;
22
+ private spread;
23
+ hash(): bigint;
24
+ /** rng:u32 LE + 256 grid bytes. */
25
+ snapshot(): Uint8Array;
26
+ restore(tick: number, bytes: Uint8Array): void;
27
+ }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Gridpaint: the shared demo game (spec/demo-game.md). Pure and deterministic; ported from spec/reference/gridpaint.mjs.
3
+ * Shipped in the library so the browser demo, the tests and the conformance adapter share one implementation.
4
+ */
5
+ import { normalizeCode } from "./crockford.js";
6
+ export const GRID_W = 16;
7
+ export const GRID_H = 16;
8
+ export const OP_PAINT = 1;
9
+ export const PALETTE = [
10
+ "#000000", "#e6194b", "#3cb44b", "#ffe119", "#4363d8", "#f58231", "#911eb4", "#46f0f0",
11
+ "#f032e6", "#bcf60c", "#fabebe", "#008080", "#e6beff", "#9a6324", "#800000", "#aaffc3",
12
+ ];
13
+ const DX = [0, 1, 0, -1];
14
+ const DY = [-1, 0, 1, 0];
15
+ export const colourOf = (peerId) => ((peerId - 1) % 15) + 1;
16
+ export function fnv1a32(bytes) {
17
+ let h = 0x811c9dc5;
18
+ for (const b of bytes) {
19
+ h ^= b;
20
+ h = Math.imul(h, 0x01000193) >>> 0;
21
+ }
22
+ return h >>> 0;
23
+ }
24
+ export function fnv1a64(bytes) {
25
+ let h = 0xcbf29ce484222325n;
26
+ for (const b of bytes) {
27
+ h ^= BigInt(b);
28
+ h = (h * 0x100000001b3n) & 0xffffffffffffffffn;
29
+ }
30
+ return h;
31
+ }
32
+ export function seedFromCode(code) {
33
+ const c = normalizeCode(code);
34
+ if (!c)
35
+ throw new Error(`bad lobby code: ${code}`);
36
+ const s = fnv1a32(new TextEncoder().encode(c));
37
+ return s === 0 ? 1 : s;
38
+ }
39
+ export function xorshift32(x) {
40
+ x ^= x << 13;
41
+ x >>>= 0;
42
+ x ^= x >>> 17;
43
+ x ^= x << 5;
44
+ x >>>= 0;
45
+ return x >>> 0;
46
+ }
47
+ export const paintCmd = (x, y) => Uint8Array.of(OP_PAINT, x, y);
48
+ export class Gridpaint {
49
+ tick = 0;
50
+ rng;
51
+ grid = new Uint8Array(GRID_W * GRID_H);
52
+ constructor(code) {
53
+ this.rng = seedFromCode(code);
54
+ }
55
+ /** Apply frame `tick` (must be current + 1). Returns the hash after the tick. */
56
+ step(tick, cmds = []) {
57
+ if (tick !== this.tick + 1)
58
+ throw new Error(`expected tick ${this.tick + 1}, got ${tick}`);
59
+ for (const c of cmds)
60
+ this.apply(c.peerId, c.bytes);
61
+ this.spread();
62
+ this.tick = tick;
63
+ return this.hash();
64
+ }
65
+ apply(peerId, bytes) {
66
+ if (bytes.length !== 3 || bytes[0] !== OP_PAINT)
67
+ return;
68
+ const x = bytes[1];
69
+ const y = bytes[2];
70
+ if (x >= GRID_W || y >= GRID_H)
71
+ return;
72
+ this.grid[y * GRID_W + x] = colourOf(peerId);
73
+ }
74
+ spread() {
75
+ const old = this.grid.slice();
76
+ for (let i = 0; i < GRID_W * GRID_H; i++) {
77
+ if (old[i] === 0)
78
+ continue;
79
+ this.rng = xorshift32(this.rng);
80
+ const d = this.rng % 4;
81
+ const x = (i % GRID_W) + DX[d];
82
+ const y = Math.floor(i / GRID_W) + DY[d];
83
+ if (x < 0 || x >= GRID_W || y < 0 || y >= GRID_H)
84
+ continue;
85
+ this.grid[y * GRID_W + x] = old[i];
86
+ }
87
+ }
88
+ hash() {
89
+ return fnv1a64(this.grid);
90
+ }
91
+ /** rng:u32 LE + 256 grid bytes. */
92
+ snapshot() {
93
+ const o = new Uint8Array(4 + GRID_W * GRID_H);
94
+ new DataView(o.buffer).setUint32(0, this.rng, true);
95
+ o.set(this.grid, 4);
96
+ return o;
97
+ }
98
+ restore(tick, bytes) {
99
+ if (bytes.length !== 4 + GRID_W * GRID_H)
100
+ throw new Error("bad snapshot length");
101
+ this.tick = tick;
102
+ this.rng = new DataView(bytes.buffer, bytes.byteOffset, 4).getUint32(0, true);
103
+ this.grid = bytes.slice(4);
104
+ }
105
+ }
106
+ //# sourceMappingURL=gridpaint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gridpaint.js","sourceRoot":"","sources":["../src/gridpaint.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Gridpaint: the shared demo game (spec/demo-game.md). Pure and deterministic; ported from spec/reference/gridpaint.mjs.\n * Shipped in the library so the browser demo, the tests and the conformance adapter share one implementation.\n */\nimport { normalizeCode } from \"./crockford.js\";\n\nexport const GRID_W = 16;\nexport const GRID_H = 16;\nexport const OP_PAINT = 1;\nexport const PALETTE = [\n \"#000000\", \"#e6194b\", \"#3cb44b\", \"#ffe119\", \"#4363d8\", \"#f58231\", \"#911eb4\", \"#46f0f0\",\n \"#f032e6\", \"#bcf60c\", \"#fabebe\", \"#008080\", \"#e6beff\", \"#9a6324\", \"#800000\", \"#aaffc3\",\n];\nconst DX = [0, 1, 0, -1];\nconst DY = [-1, 0, 1, 0];\n\nexport const colourOf = (peerId: number): number => ((peerId - 1) % 15) + 1;\n\nexport function fnv1a32(bytes: Uint8Array): number {\n let h = 0x811c9dc5;\n for (const b of bytes) {\n h ^= b;\n h = Math.imul(h, 0x01000193) >>> 0;\n }\n return h >>> 0;\n}\n\nexport function fnv1a64(bytes: Uint8Array): bigint {\n let h = 0xcbf29ce484222325n;\n for (const b of bytes) {\n h ^= BigInt(b);\n h = (h * 0x100000001b3n) & 0xffffffffffffffffn;\n }\n return h;\n}\n\nexport function seedFromCode(code: string): number {\n const c = normalizeCode(code);\n if (!c) throw new Error(`bad lobby code: ${code}`);\n const s = fnv1a32(new TextEncoder().encode(c));\n return s === 0 ? 1 : s;\n}\n\nexport function xorshift32(x: number): number {\n x ^= x << 13;\n x >>>= 0;\n x ^= x >>> 17;\n x ^= x << 5;\n x >>>= 0;\n return x >>> 0;\n}\n\nexport const paintCmd = (x: number, y: number): Uint8Array => Uint8Array.of(OP_PAINT, x, y);\n\nexport class Gridpaint {\n tick = 0;\n rng: number;\n grid = new Uint8Array(GRID_W * GRID_H);\n\n constructor(code: string) {\n this.rng = seedFromCode(code);\n }\n\n /** Apply frame `tick` (must be current + 1). Returns the hash after the tick. */\n step(tick: number, cmds: ReadonlyArray<{ peerId: number; bytes: Uint8Array }> = []): bigint {\n if (tick !== this.tick + 1) throw new Error(`expected tick ${this.tick + 1}, got ${tick}`);\n for (const c of cmds) this.apply(c.peerId, c.bytes);\n this.spread();\n this.tick = tick;\n return this.hash();\n }\n\n apply(peerId: number, bytes: Uint8Array): void {\n if (bytes.length !== 3 || bytes[0] !== OP_PAINT) return;\n const x = bytes[1];\n const y = bytes[2];\n if (x >= GRID_W || y >= GRID_H) return;\n this.grid[y * GRID_W + x] = colourOf(peerId);\n }\n\n private spread(): void {\n const old = this.grid.slice();\n for (let i = 0; i < GRID_W * GRID_H; i++) {\n if (old[i] === 0) continue;\n this.rng = xorshift32(this.rng);\n const d = this.rng % 4;\n const x = (i % GRID_W) + DX[d];\n const y = Math.floor(i / GRID_W) + DY[d];\n if (x < 0 || x >= GRID_W || y < 0 || y >= GRID_H) continue;\n this.grid[y * GRID_W + x] = old[i];\n }\n }\n\n hash(): bigint {\n return fnv1a64(this.grid);\n }\n\n /** rng:u32 LE + 256 grid bytes. */\n snapshot(): Uint8Array {\n const o = new Uint8Array(4 + GRID_W * GRID_H);\n new DataView(o.buffer).setUint32(0, this.rng, true);\n o.set(this.grid, 4);\n return o;\n }\n\n restore(tick: number, bytes: Uint8Array): void {\n if (bytes.length !== 4 + GRID_W * GRID_H) throw new Error(\"bad snapshot length\");\n this.tick = tick;\n this.rng = new DataView(bytes.buffer, bytes.byteOffset, 4).getUint32(0, true);\n this.grid = bytes.slice(4);\n }\n}\n"]}
package/dist/hub.d.ts ADDED
@@ -0,0 +1,25 @@
1
+ import type { Clock } from "./clock.js";
2
+ import type { Message } from "./codec.js";
3
+ import type { ChannelName } from "./transport.js";
4
+ /**
5
+ * What lockstep/chat/voice need from the Lobby. Keeping this narrow lets them be unit-tested with a fake hub.
6
+ * Star topology: a joiner's only link is to the host; the host has one link per joiner.
7
+ */
8
+ export interface Hub {
9
+ readonly clock: Clock;
10
+ readonly localPeerId: number;
11
+ readonly hostPeerId: number;
12
+ readonly isHost: boolean;
13
+ readonly tickRate: number;
14
+ /** Remote peers with an open link (host: every joiner; joiner: just the host, if linked). */
15
+ connectedPeers(): number[];
16
+ /** Send to one directly linked peer. */
17
+ sendTo(peerId: number, msg: Message, route?: number, channel?: ChannelName): void;
18
+ /** Joiner -> host. No-op when this peer is the host. */
19
+ sendToHost(msg: Message, route?: number, channel?: ChannelName): void;
20
+ /** Host drops a misbehaving peer (kick with a reason shown to the game). */
21
+ dropPeer(peerId: number, reason: string): void;
22
+ reportError(err: Error): void;
23
+ /** Next chunk-set id for this sender (shared by snapshots and oversized frames). */
24
+ nextChunkId(): number;
25
+ }
package/dist/hub.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=hub.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hub.js","sourceRoot":"","sources":["../src/hub.ts"],"names":[],"mappings":"","sourcesContent":["import type { Clock } from \"./clock.js\";\nimport type { Message } from \"./codec.js\";\nimport type { ChannelName } from \"./transport.js\";\n\n/**\n * What lockstep/chat/voice need from the Lobby. Keeping this narrow lets them be unit-tested with a fake hub.\n * Star topology: a joiner's only link is to the host; the host has one link per joiner.\n */\nexport interface Hub {\n readonly clock: Clock;\n readonly localPeerId: number;\n readonly hostPeerId: number;\n readonly isHost: boolean;\n readonly tickRate: number;\n /** Remote peers with an open link (host: every joiner; joiner: just the host, if linked). */\n connectedPeers(): number[];\n /** Send to one directly linked peer. */\n sendTo(peerId: number, msg: Message, route?: number, channel?: ChannelName): void;\n /** Joiner -> host. No-op when this peer is the host. */\n sendToHost(msg: Message, route?: number, channel?: ChannelName): void;\n /** Host drops a misbehaving peer (kick with a reason shown to the game). */\n dropPeer(peerId: number, reason: string): void;\n reportError(err: Error): void;\n /** Next chunk-set id for this sender (shared by snapshots and oversized frames). */\n nextChunkId(): number;\n}\n"]}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Public API of `onelobby`: create a `OneLobby`, `host()` / `join(code)` / `list()` lobbies, then use the
3
+ * `Lobby`'s lockstep, raw messages, chat and voice. Lower-level pieces (codec, chunking, migration, jitter buffer,
4
+ * WebRTC/signaling classes, clocks, the gridpaint demo game) live in `onelobby/internals`.
5
+ */
6
+ export { OneLobby, LIB_NAME, LIB_VERSION } from "./onelobby.js";
7
+ export type { OneLobbyOptions, HostOptions, ListOptions, OneLobbyEvents, JoinDirectOptions } from "./onelobby.js";
8
+ export { DEFAULT_READY_TIMEOUT_MS } from "./onelobby.js";
9
+ export type { AdvancedOptions, SeatStorage } from "./onelobby.js";
10
+ export { Lobby } from "./lobby.js";
11
+ export type { LobbyEvents, LobbyConfig, Peer, Phase, RateLimit } from "./lobby.js";
12
+ export { MIN_PEER_TIMEOUT_MS } from "./lobby.js";
13
+ export { closeCodeFor } from "./socket.js";
14
+ export type { LobbySocket, LobbySocketOptions } from "./socket.js";
15
+ export { Lockstep } from "./lockstep.js";
16
+ export type { Frame, DesyncInfo, LockstepEvents } from "./lockstep.js";
17
+ export { Chat, CHAT_MAX_BYTES } from "./chat.js";
18
+ export type { ChatMessage, ChatEvents } from "./chat.js";
19
+ export { Voice } from "./voice.js";
20
+ export type { VoiceEvents, VoiceDeps } from "./voice.js";
21
+ export type { VoiceMode } from "./voice-core.js";
22
+ export type { VoiceCodec, VoiceEncoderHandle, VoiceDecoderHandle, FrameSource, AudioSink, VoiceError } from "./voice-types.js";
23
+ export { webCodecsOpusAvailable } from "./voice-browser.js";
24
+ export { normalizeCode, formatCode } from "./crockford.js";
25
+ export { decodeJoinCode, looksLikeJoinCode } from "./joincode.js";
26
+ export type { DecodedJoinCode, JoinCodeError } from "./joincode.js";
27
+ export { webRtcLinkFactoryFor } from "./webrtc.js";
28
+ export { OneLobbyError } from "./signaling.js";
29
+ export type { LobbySummary, LobbyState, PeerInfo } from "./signaling.js";
30
+ export type { PeerLink, LinkFactory, SocketFactory, SocketLike, IceServer, ChannelName } from "./transport.js";
31
+ export type { Clock } from "./clock.js";
32
+ export { describeError, shareUrl, codeFromUrl } from "./friendly.js";
33
+ export type { FriendlyError } from "./friendly.js";
34
+ export { Keepalive, KeepaliveError } from "./deprecated.js";
35
+ export type { KeepaliveOptions, KeepaliveEvents } from "./deprecated.js";
package/dist/index.js ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Public API of `onelobby`: create a `OneLobby`, `host()` / `join(code)` / `list()` lobbies, then use the
3
+ * `Lobby`'s lockstep, raw messages, chat and voice. Lower-level pieces (codec, chunking, migration, jitter buffer,
4
+ * WebRTC/signaling classes, clocks, the gridpaint demo game) live in `onelobby/internals`.
5
+ */
6
+ export { OneLobby, LIB_NAME, LIB_VERSION } from "./onelobby.js";
7
+ export { DEFAULT_READY_TIMEOUT_MS } from "./onelobby.js";
8
+ export { Lobby } from "./lobby.js";
9
+ export { MIN_PEER_TIMEOUT_MS } from "./lobby.js";
10
+ // WebSocket-shaped sockets over a lobby: lobby.asWebSocket() (joiner), lobby.onConnection(conn => ...) (host).
11
+ export { closeCodeFor } from "./socket.js";
12
+ export { Lockstep } from "./lockstep.js";
13
+ export { Chat, CHAT_MAX_BYTES } from "./chat.js";
14
+ export { Voice } from "./voice.js";
15
+ export { webCodecsOpusAvailable } from "./voice-browser.js";
16
+ export { normalizeCode, formatCode } from "./crockford.js";
17
+ // Direct IP (docs/direct-ip.md): kit.joinDirect(code); the code is the host's address in disguise. Hosting is in
18
+ // onelobby/direct (Node). webRtcLinkFactoryFor gives Node joiners a WebRTC stack (node-datachannel/polyfill).
19
+ export { decodeJoinCode, looksLikeJoinCode } from "./joincode.js";
20
+ export { webRtcLinkFactoryFor } from "./webrtc.js";
21
+ export { OneLobbyError } from "./signaling.js";
22
+ export { describeError, shareUrl, codeFromUrl } from "./friendly.js";
23
+ // Pre-rename names, kept for one version so existing games keep working.
24
+ export { Keepalive, KeepaliveError } from "./deprecated.js";
25
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEhE,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AAEzD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,+GAA+G;AAC/G,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEjD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAInC,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC3D,iHAAiH;AACjH,8GAA8G;AAC9G,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAElE,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAK/C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAErE,yEAAyE;AACzE,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC","sourcesContent":["/**\n * Public API of `onelobby`: create a `OneLobby`, `host()` / `join(code)` / `list()` lobbies, then use the\n * `Lobby`'s lockstep, raw messages, chat and voice. Lower-level pieces (codec, chunking, migration, jitter buffer,\n * WebRTC/signaling classes, clocks, the gridpaint demo game) live in `onelobby/internals`.\n */\nexport { OneLobby, LIB_NAME, LIB_VERSION } from \"./onelobby.js\";\nexport type { OneLobbyOptions, HostOptions, ListOptions, OneLobbyEvents, JoinDirectOptions } from \"./onelobby.js\";\nexport { DEFAULT_READY_TIMEOUT_MS } from \"./onelobby.js\";\nexport type { AdvancedOptions, SeatStorage } from \"./onelobby.js\";\nexport { Lobby } from \"./lobby.js\";\nexport type { LobbyEvents, LobbyConfig, Peer, Phase, RateLimit } from \"./lobby.js\";\nexport { MIN_PEER_TIMEOUT_MS } from \"./lobby.js\";\n// WebSocket-shaped sockets over a lobby: lobby.asWebSocket() (joiner), lobby.onConnection(conn => ...) (host).\nexport { closeCodeFor } from \"./socket.js\";\nexport type { LobbySocket, LobbySocketOptions } from \"./socket.js\";\nexport { Lockstep } from \"./lockstep.js\";\nexport type { Frame, DesyncInfo, LockstepEvents } from \"./lockstep.js\";\nexport { Chat, CHAT_MAX_BYTES } from \"./chat.js\";\nexport type { ChatMessage, ChatEvents } from \"./chat.js\";\nexport { Voice } from \"./voice.js\";\nexport type { VoiceEvents, VoiceDeps } from \"./voice.js\";\nexport type { VoiceMode } from \"./voice-core.js\";\nexport type { VoiceCodec, VoiceEncoderHandle, VoiceDecoderHandle, FrameSource, AudioSink, VoiceError } from \"./voice-types.js\";\nexport { webCodecsOpusAvailable } from \"./voice-browser.js\";\nexport { normalizeCode, formatCode } from \"./crockford.js\";\n// Direct IP (docs/direct-ip.md): kit.joinDirect(code); the code is the host's address in disguise. Hosting is in\n// onelobby/direct (Node). webRtcLinkFactoryFor gives Node joiners a WebRTC stack (node-datachannel/polyfill).\nexport { decodeJoinCode, looksLikeJoinCode } from \"./joincode.js\";\nexport type { DecodedJoinCode, JoinCodeError } from \"./joincode.js\";\nexport { webRtcLinkFactoryFor } from \"./webrtc.js\";\nexport { OneLobbyError } from \"./signaling.js\";\nexport type { LobbySummary, LobbyState, PeerInfo } from \"./signaling.js\";\n// Types for the seams in OneLobbyOptions (ChannelName also tags raw messages).\nexport type { PeerLink, LinkFactory, SocketFactory, SocketLike, IceServer, ChannelName } from \"./transport.js\";\nexport type { Clock } from \"./clock.js\";\nexport { describeError, shareUrl, codeFromUrl } from \"./friendly.js\";\nexport type { FriendlyError } from \"./friendly.js\";\n// Pre-rename names, kept for one version so existing games keep working.\nexport { Keepalive, KeepaliveError } from \"./deprecated.js\";\nexport type { KeepaliveOptions, KeepaliveEvents } from \"./deprecated.js\";\n"]}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Everything the package has, including the building blocks apps normally never call: wire codec, chunking, deflate,
3
+ * host-migration rule, jitter buffer and voice packetizer, signaling and WebRTC classes, clocks, and the gridpaint demo
4
+ * game. Exported as `onelobby/internals`. No stability promise: these can change in any release.
5
+ * The public API is the root export (`onelobby`); it is re-exported here too.
6
+ */
7
+ export * from "./index.js";
8
+ export { MAX_DESYNCS, HOST_SILENCE_MS } from "./lockstep.js";
9
+ export { ChatLimiter, sanitizeChat, CHAT_HISTORY_LINES } from "./chat.js";
10
+ export { JitterBuffer, VoiceGate, VoicePacketizer, VoiceRelayPolicy, FRAME_SAMPLES, SAMPLE_RATE, MAX_SPEAKERS, } from "./voice-core.js";
11
+ export type { VoicePacket } from "./voice-core.js";
12
+ export { webCodecsOpus, WorkletMic, WebAudioSink } from "./voice-browser.js";
13
+ export { pickNewHost, claimWins, claimWinsWrapped } from "./migration.js";
14
+ export type { Claim } from "./migration.js";
15
+ export { decodeCrockford, encodeCrockford, CROCKFORD_ALPHABET } from "./crockford.js";
16
+ export { encodeMessage, decodeMessage, CodecError, MAX_FRAME_BYTES, MsgType } from "./codec.js";
17
+ export type { Message, Decoded } from "./codec.js";
18
+ export { splitChunks, Reassembler, CHUNK_PAYLOAD } from "./chunking.js";
19
+ export { deflate, inflate, deflateAvailable } from "./deflate.js";
20
+ export { SignalingClient } from "./signaling.js";
21
+ export { WebRtcLink, webRtcLinkFactory } from "./webrtc.js";
22
+ export { systemClock } from "./clock.js";
23
+ export { Gridpaint, paintCmd, colourOf, PALETTE, GRID_W, GRID_H, seedFromCode, fnv1a64 } from "./gridpaint.js";
24
+ export type { Hub } from "./hub.js";
25
+ export { encodeJoinCode, decodeJoinCode, normalizeJoinCode, looksLikeJoinCode, JOIN_CODE_LENGTH } from "./joincode.js";
26
+ export { parseDirectAddress, DEFAULT_DIRECT_PORT } from "./direct-address.js";
27
+ export type { DirectAddress } from "./direct-address.js";
28
+ export { webRtcLinkFactoryFor } from "./webrtc.js";
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Everything the package has, including the building blocks apps normally never call: wire codec, chunking, deflate,
3
+ * host-migration rule, jitter buffer and voice packetizer, signaling and WebRTC classes, clocks, and the gridpaint demo
4
+ * game. Exported as `onelobby/internals`. No stability promise: these can change in any release.
5
+ * The public API is the root export (`onelobby`); it is re-exported here too.
6
+ */
7
+ export * from "./index.js";
8
+ export { MAX_DESYNCS, HOST_SILENCE_MS } from "./lockstep.js";
9
+ export { ChatLimiter, sanitizeChat, CHAT_HISTORY_LINES } from "./chat.js";
10
+ export { JitterBuffer, VoiceGate, VoicePacketizer, VoiceRelayPolicy, FRAME_SAMPLES, SAMPLE_RATE, MAX_SPEAKERS, } from "./voice-core.js";
11
+ export { webCodecsOpus, WorkletMic, WebAudioSink } from "./voice-browser.js";
12
+ export { pickNewHost, claimWins, claimWinsWrapped } from "./migration.js";
13
+ export { decodeCrockford, encodeCrockford, CROCKFORD_ALPHABET } from "./crockford.js";
14
+ export { encodeMessage, decodeMessage, CodecError, MAX_FRAME_BYTES, MsgType } from "./codec.js";
15
+ export { splitChunks, Reassembler, CHUNK_PAYLOAD } from "./chunking.js";
16
+ export { deflate, inflate, deflateAvailable } from "./deflate.js";
17
+ export { SignalingClient } from "./signaling.js";
18
+ export { WebRtcLink, webRtcLinkFactory } from "./webrtc.js";
19
+ export { systemClock } from "./clock.js";
20
+ export { Gridpaint, paintCmd, colourOf, PALETTE, GRID_W, GRID_H, seedFromCode, fnv1a64 } from "./gridpaint.js";
21
+ export { encodeJoinCode, decodeJoinCode, normalizeJoinCode, looksLikeJoinCode, JOIN_CODE_LENGTH } from "./joincode.js";
22
+ export { parseDirectAddress, DEFAULT_DIRECT_PORT } from "./direct-address.js";
23
+ export { webRtcLinkFactoryFor } from "./webrtc.js";
24
+ //# sourceMappingURL=internals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internals.js","sourceRoot":"","sources":["../src/internals.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC1E,OAAO,EACL,YAAY,EACZ,SAAS,EACT,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,WAAW,EACX,YAAY,GACb,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC7E,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAE1E,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACtF,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAEhG,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACxE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAE/G,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACvH,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC","sourcesContent":["/**\n * Everything the package has, including the building blocks apps normally never call: wire codec, chunking, deflate,\n * host-migration rule, jitter buffer and voice packetizer, signaling and WebRTC classes, clocks, and the gridpaint demo\n * game. Exported as `onelobby/internals`. No stability promise: these can change in any release.\n * The public API is the root export (`onelobby`); it is re-exported here too.\n */\nexport * from \"./index.js\";\nexport { MAX_DESYNCS, HOST_SILENCE_MS } from \"./lockstep.js\";\nexport { ChatLimiter, sanitizeChat, CHAT_HISTORY_LINES } from \"./chat.js\";\nexport {\n JitterBuffer,\n VoiceGate,\n VoicePacketizer,\n VoiceRelayPolicy,\n FRAME_SAMPLES,\n SAMPLE_RATE,\n MAX_SPEAKERS,\n} from \"./voice-core.js\";\nexport type { VoicePacket } from \"./voice-core.js\";\nexport { webCodecsOpus, WorkletMic, WebAudioSink } from \"./voice-browser.js\";\nexport { pickNewHost, claimWins, claimWinsWrapped } from \"./migration.js\";\nexport type { Claim } from \"./migration.js\";\nexport { decodeCrockford, encodeCrockford, CROCKFORD_ALPHABET } from \"./crockford.js\";\nexport { encodeMessage, decodeMessage, CodecError, MAX_FRAME_BYTES, MsgType } from \"./codec.js\";\nexport type { Message, Decoded } from \"./codec.js\";\nexport { splitChunks, Reassembler, CHUNK_PAYLOAD } from \"./chunking.js\";\nexport { deflate, inflate, deflateAvailable } from \"./deflate.js\";\nexport { SignalingClient } from \"./signaling.js\";\nexport { WebRtcLink, webRtcLinkFactory } from \"./webrtc.js\";\nexport { systemClock } from \"./clock.js\";\nexport { Gridpaint, paintCmd, colourOf, PALETTE, GRID_W, GRID_H, seedFromCode, fnv1a64 } from \"./gridpaint.js\";\nexport type { Hub } from \"./hub.js\";\nexport { encodeJoinCode, decodeJoinCode, normalizeJoinCode, looksLikeJoinCode, JOIN_CODE_LENGTH } from \"./joincode.js\";\nexport { parseDirectAddress, DEFAULT_DIRECT_PORT } from \"./direct-address.js\";\nexport type { DirectAddress } from \"./direct-address.js\";\nexport { webRtcLinkFactoryFor } from \"./webrtc.js\";\n"]}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * `dc1` join codes (docs/direct-ip.md s.2a, spec/reference/joincode.mjs): a direct-IP host's IPv4 address and port in
3
+ * 11 Crockford characters, shown `6B01-RGFE-P2B`. The code is the address in disguise, so decoding asks nobody.
4
+ * Bits, most significant first: version:2 (0 = IPv4) | ipv4:32 | port:16 | crc:5 (CRC-5, polynomial 0x05, over the 50
5
+ * bits before it). One mistyped character always fails the check.
6
+ */
7
+ export declare const JOIN_CODE_LENGTH = 11;
8
+ export type JoinCodeError = "bad_code" | "unknown_version";
9
+ export type DecodedJoinCode = {
10
+ ip: string;
11
+ port: number;
12
+ } | {
13
+ error: JoinCodeError;
14
+ };
15
+ /** Display form (`6B01-RGFE-P2B`) for an IPv4 address and port 1-65535. Throws on anything else. */
16
+ export declare function encodeJoinCode(ipv4: string, port: number): string;
17
+ /** Upper case, spaces and hyphens dropped, i/l -> 1, o -> 0; null unless 11 alphabet characters. */
18
+ export declare function normalizeJoinCode(input: string): string | null;
19
+ export declare function decodeJoinCode(input: string): DecodedJoinCode;
20
+ /** Shaped like a join code (no dots or colons, 11 alphabet characters): a failed decode is `bad_code`, not a host name. */
21
+ export declare function looksLikeJoinCode(input: string): boolean;