@thegeem/protocol 0.1.9 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -23,8 +23,11 @@ __export(index_exports, {
23
23
  ClientMsg: () => ClientMsg,
24
24
  GAME_IDS: () => GAME_IDS,
25
25
  LIMITS: () => LIMITS,
26
+ MODES: () => MODES,
27
+ MODE_IDS: () => MODE_IDS,
26
28
  PROTOCOL_VERSION: () => PROTOCOL_VERSION,
27
- ROOM_ALPHABET: () => ROOM_ALPHABET
29
+ ROOM_ALPHABET: () => ROOM_ALPHABET,
30
+ modesForGame: () => modesForGame
28
31
  });
29
32
  module.exports = __toCommonJS(index_exports);
30
33
  var import_zod = require("zod");
@@ -36,6 +39,14 @@ var LIMITS = {
36
39
  };
37
40
  var PROTOCOL_VERSION = 1;
38
41
  var GAME_IDS = ["trivia"];
42
+ var MODE_IDS = ["solo", "oneDevice", "snag", "multiplayer"];
43
+ var MODES = [
44
+ { id: "solo", gameId: "trivia", label: "Solo", onlineEligible: false },
45
+ { id: "oneDevice", gameId: "trivia", label: "One Device", onlineEligible: false },
46
+ { id: "snag", gameId: "trivia", label: "Snag", onlineEligible: true },
47
+ { id: "multiplayer", gameId: "trivia", label: "Multiplayer", onlineEligible: true }
48
+ ];
49
+ var modesForGame = (gameId) => MODES.filter((m) => m.gameId === gameId);
39
50
  var ROOM_ALPHABET = "ACDEFGHJKMNPQRTUVWXY2346789";
40
51
  var UNSAFE_CHARS = /[\p{Cc}<>]/gu;
41
52
  var DisplayName = import_zod.z.string().trim().min(1).max(LIMITS.MAX_NAME_LEN).transform((s) => s.replace(UNSAFE_CHARS, "").trim()).refine((s) => s.length >= 1, "name is empty after sanitizing");
@@ -85,6 +96,10 @@ var ClientMsg = import_zod.z.discriminatedUnion("t", [
85
96
  // which game to play; defaults to 'trivia'
86
97
  totalRounds: import_zod.z.number().int().min(1).max(50),
87
98
  categoryIds: import_zod.z.array(import_zod.z.string().max(60)).min(1).max(12),
99
+ // PREFERRED: pick a mode from the registry (see MODES). The server maps it to the engine
100
+ // rules. Send this and omit buzzerMode/noHost. Falls back to the legacy flags below if omitted.
101
+ modeId: import_zod.z.enum(MODE_IDS).optional(),
102
+ // LEGACY (pre-modeId clients) — the raw engine flags. Ignored when modeId is present.
88
103
  // How to play (maps to the engine). Omit → controller (each player answers from
89
104
  // their own device). 'off' = one device drives (Me-Controller); pair with noHost.
90
105
  // solo=Solo · race=Snag/buzz · controller=each-player-from-device · off=Me-Controller
@@ -120,6 +135,9 @@ var ClientMsg = import_zod.z.discriminatedUnion("t", [
120
135
  ClientMsg,
121
136
  GAME_IDS,
122
137
  LIMITS,
138
+ MODES,
139
+ MODE_IDS,
123
140
  PROTOCOL_VERSION,
124
- ROOM_ALPHABET
141
+ ROOM_ALPHABET,
142
+ modesForGame
125
143
  });
package/dist/index.d.cts CHANGED
@@ -37,6 +37,26 @@ declare const PROTOCOL_VERSION = 1;
37
37
  */
38
38
  declare const GAME_IDS: readonly ["trivia"];
39
39
  type GameId = (typeof GAME_IDS)[number];
40
+ /**
41
+ * The MODE REGISTRY — the list of ways to play each game. A client renders its mode picker from this
42
+ * and sends the chosen `modeId` on `startGame`; the SERVER owns the modeId → engine-rules mapping (the
43
+ * flags are an internal relay detail, not the client's job). Adding a mode = one entry here. Game #2
44
+ * gets its own `gameId` rows. `label` is a short English label — clients may localize by `id` (Arabic
45
+ * UI is Kuwaiti). `onlineEligible` = a finished game in this mode counts toward the global leaderboard
46
+ * (only server-arbitrated, multi-device modes; single-device/solo are local-only — the host controls
47
+ * input, so the server can't trust the score).
48
+ */
49
+ declare const MODE_IDS: readonly ["solo", "oneDevice", "snag", "multiplayer"];
50
+ type ModeId = (typeof MODE_IDS)[number];
51
+ interface ModeInfo {
52
+ id: ModeId;
53
+ gameId: GameId;
54
+ label: string;
55
+ onlineEligible: boolean;
56
+ }
57
+ declare const MODES: readonly ModeInfo[];
58
+ /** The modes available for a given game (for a client's mode picker). */
59
+ declare const modesForGame: (gameId: GameId) => ModeInfo[];
40
60
  /** Unambiguous room-code alphabet — no 0/O, 1/I/L. */
41
61
  declare const ROOM_ALPHABET = "ACDEFGHJKMNPQRTUVWXY2346789";
42
62
  declare const HelpType: z.ZodEnum<["removeTwoAnswers", "changeQuestion", "extraTime", "doublePoints", "stealPoints", "restPlayer", "tripLevel"]>;
@@ -228,6 +248,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
228
248
  game: z.ZodOptional<z.ZodEnum<["trivia"]>>;
229
249
  totalRounds: z.ZodNumber;
230
250
  categoryIds: z.ZodArray<z.ZodString, "many">;
251
+ modeId: z.ZodOptional<z.ZodEnum<["solo", "oneDevice", "snag", "multiplayer"]>>;
231
252
  buzzerMode: z.ZodOptional<z.ZodEnum<["off", "solo", "race", "controller"]>>;
232
253
  noHost: z.ZodOptional<z.ZodBoolean>;
233
254
  }, "strip", z.ZodTypeAny, {
@@ -235,14 +256,16 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
235
256
  categoryIds: string[];
236
257
  totalRounds: number;
237
258
  game?: "trivia" | undefined;
238
- buzzerMode?: "off" | "solo" | "race" | "controller" | undefined;
259
+ modeId?: "solo" | "oneDevice" | "snag" | "multiplayer" | undefined;
260
+ buzzerMode?: "solo" | "off" | "race" | "controller" | undefined;
239
261
  noHost?: boolean | undefined;
240
262
  }, {
241
263
  t: "startGame";
242
264
  categoryIds: string[];
243
265
  totalRounds: number;
244
266
  game?: "trivia" | undefined;
245
- buzzerMode?: "off" | "solo" | "race" | "controller" | undefined;
267
+ modeId?: "solo" | "oneDevice" | "snag" | "multiplayer" | undefined;
268
+ buzzerMode?: "solo" | "off" | "race" | "controller" | undefined;
246
269
  noHost?: boolean | undefined;
247
270
  }>, z.ZodObject<{
248
271
  t: z.ZodLiteral<"pickLevel">;
@@ -549,4 +572,4 @@ type ServerMsg = {
549
572
  message: string;
550
573
  };
551
574
 
552
- export { type AvatarView, type BuzzerGameMode, type CategoryView, ClientMsg, type Effect, GAME_IDS, type GameId, type GameStateView, type GameStatus, type GameplayFlow, type HelpName, LIMITS, type LobbyPlayer, PROTOCOL_VERSION, type QuestionView, ROOM_ALPHABET, type ServerMsg, type SoundName, type TeamSlotView, type TeamView, type YouContext };
575
+ export { type AvatarView, type BuzzerGameMode, type CategoryView, ClientMsg, type Effect, GAME_IDS, type GameId, type GameStateView, type GameStatus, type GameplayFlow, type HelpName, LIMITS, type LobbyPlayer, MODES, MODE_IDS, type ModeId, type ModeInfo, PROTOCOL_VERSION, type QuestionView, ROOM_ALPHABET, type ServerMsg, type SoundName, type TeamSlotView, type TeamView, type YouContext, modesForGame };
package/dist/index.d.ts CHANGED
@@ -37,6 +37,26 @@ declare const PROTOCOL_VERSION = 1;
37
37
  */
38
38
  declare const GAME_IDS: readonly ["trivia"];
39
39
  type GameId = (typeof GAME_IDS)[number];
40
+ /**
41
+ * The MODE REGISTRY — the list of ways to play each game. A client renders its mode picker from this
42
+ * and sends the chosen `modeId` on `startGame`; the SERVER owns the modeId → engine-rules mapping (the
43
+ * flags are an internal relay detail, not the client's job). Adding a mode = one entry here. Game #2
44
+ * gets its own `gameId` rows. `label` is a short English label — clients may localize by `id` (Arabic
45
+ * UI is Kuwaiti). `onlineEligible` = a finished game in this mode counts toward the global leaderboard
46
+ * (only server-arbitrated, multi-device modes; single-device/solo are local-only — the host controls
47
+ * input, so the server can't trust the score).
48
+ */
49
+ declare const MODE_IDS: readonly ["solo", "oneDevice", "snag", "multiplayer"];
50
+ type ModeId = (typeof MODE_IDS)[number];
51
+ interface ModeInfo {
52
+ id: ModeId;
53
+ gameId: GameId;
54
+ label: string;
55
+ onlineEligible: boolean;
56
+ }
57
+ declare const MODES: readonly ModeInfo[];
58
+ /** The modes available for a given game (for a client's mode picker). */
59
+ declare const modesForGame: (gameId: GameId) => ModeInfo[];
40
60
  /** Unambiguous room-code alphabet — no 0/O, 1/I/L. */
41
61
  declare const ROOM_ALPHABET = "ACDEFGHJKMNPQRTUVWXY2346789";
42
62
  declare const HelpType: z.ZodEnum<["removeTwoAnswers", "changeQuestion", "extraTime", "doublePoints", "stealPoints", "restPlayer", "tripLevel"]>;
@@ -228,6 +248,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
228
248
  game: z.ZodOptional<z.ZodEnum<["trivia"]>>;
229
249
  totalRounds: z.ZodNumber;
230
250
  categoryIds: z.ZodArray<z.ZodString, "many">;
251
+ modeId: z.ZodOptional<z.ZodEnum<["solo", "oneDevice", "snag", "multiplayer"]>>;
231
252
  buzzerMode: z.ZodOptional<z.ZodEnum<["off", "solo", "race", "controller"]>>;
232
253
  noHost: z.ZodOptional<z.ZodBoolean>;
233
254
  }, "strip", z.ZodTypeAny, {
@@ -235,14 +256,16 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
235
256
  categoryIds: string[];
236
257
  totalRounds: number;
237
258
  game?: "trivia" | undefined;
238
- buzzerMode?: "off" | "solo" | "race" | "controller" | undefined;
259
+ modeId?: "solo" | "oneDevice" | "snag" | "multiplayer" | undefined;
260
+ buzzerMode?: "solo" | "off" | "race" | "controller" | undefined;
239
261
  noHost?: boolean | undefined;
240
262
  }, {
241
263
  t: "startGame";
242
264
  categoryIds: string[];
243
265
  totalRounds: number;
244
266
  game?: "trivia" | undefined;
245
- buzzerMode?: "off" | "solo" | "race" | "controller" | undefined;
267
+ modeId?: "solo" | "oneDevice" | "snag" | "multiplayer" | undefined;
268
+ buzzerMode?: "solo" | "off" | "race" | "controller" | undefined;
246
269
  noHost?: boolean | undefined;
247
270
  }>, z.ZodObject<{
248
271
  t: z.ZodLiteral<"pickLevel">;
@@ -549,4 +572,4 @@ type ServerMsg = {
549
572
  message: string;
550
573
  };
551
574
 
552
- export { type AvatarView, type BuzzerGameMode, type CategoryView, ClientMsg, type Effect, GAME_IDS, type GameId, type GameStateView, type GameStatus, type GameplayFlow, type HelpName, LIMITS, type LobbyPlayer, PROTOCOL_VERSION, type QuestionView, ROOM_ALPHABET, type ServerMsg, type SoundName, type TeamSlotView, type TeamView, type YouContext };
575
+ export { type AvatarView, type BuzzerGameMode, type CategoryView, ClientMsg, type Effect, GAME_IDS, type GameId, type GameStateView, type GameStatus, type GameplayFlow, type HelpName, LIMITS, type LobbyPlayer, MODES, MODE_IDS, type ModeId, type ModeInfo, PROTOCOL_VERSION, type QuestionView, ROOM_ALPHABET, type ServerMsg, type SoundName, type TeamSlotView, type TeamView, type YouContext, modesForGame };
package/dist/index.js CHANGED
@@ -8,6 +8,14 @@ var LIMITS = {
8
8
  };
9
9
  var PROTOCOL_VERSION = 1;
10
10
  var GAME_IDS = ["trivia"];
11
+ var MODE_IDS = ["solo", "oneDevice", "snag", "multiplayer"];
12
+ var MODES = [
13
+ { id: "solo", gameId: "trivia", label: "Solo", onlineEligible: false },
14
+ { id: "oneDevice", gameId: "trivia", label: "One Device", onlineEligible: false },
15
+ { id: "snag", gameId: "trivia", label: "Snag", onlineEligible: true },
16
+ { id: "multiplayer", gameId: "trivia", label: "Multiplayer", onlineEligible: true }
17
+ ];
18
+ var modesForGame = (gameId) => MODES.filter((m) => m.gameId === gameId);
11
19
  var ROOM_ALPHABET = "ACDEFGHJKMNPQRTUVWXY2346789";
12
20
  var UNSAFE_CHARS = /[\p{Cc}<>]/gu;
13
21
  var DisplayName = z.string().trim().min(1).max(LIMITS.MAX_NAME_LEN).transform((s) => s.replace(UNSAFE_CHARS, "").trim()).refine((s) => s.length >= 1, "name is empty after sanitizing");
@@ -57,6 +65,10 @@ var ClientMsg = z.discriminatedUnion("t", [
57
65
  // which game to play; defaults to 'trivia'
58
66
  totalRounds: z.number().int().min(1).max(50),
59
67
  categoryIds: z.array(z.string().max(60)).min(1).max(12),
68
+ // PREFERRED: pick a mode from the registry (see MODES). The server maps it to the engine
69
+ // rules. Send this and omit buzzerMode/noHost. Falls back to the legacy flags below if omitted.
70
+ modeId: z.enum(MODE_IDS).optional(),
71
+ // LEGACY (pre-modeId clients) — the raw engine flags. Ignored when modeId is present.
60
72
  // How to play (maps to the engine). Omit → controller (each player answers from
61
73
  // their own device). 'off' = one device drives (Me-Controller); pair with noHost.
62
74
  // solo=Solo · race=Snag/buzz · controller=each-player-from-device · off=Me-Controller
@@ -91,6 +103,9 @@ export {
91
103
  ClientMsg,
92
104
  GAME_IDS,
93
105
  LIMITS,
106
+ MODES,
107
+ MODE_IDS,
94
108
  PROTOCOL_VERSION,
95
- ROOM_ALPHABET
109
+ ROOM_ALPHABET,
110
+ modesForGame
96
111
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thegeem/protocol",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Geem's wire protocol — shared zod schemas + TypeScript types for talking to the Geem game server over Socket.IO. The source of truth all clients implement against.",
5
5
  "license": "UNLICENSED",
6
6
  "homepage": "https://geem.tv",