@thegeem/protocol 0.1.17 → 0.1.18

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
@@ -41,12 +41,14 @@ var LIMITS = {
41
41
  MAX_PLAYERS: 12,
42
42
  MAX_MSG_PER_SEC: 8
43
43
  };
44
- var PROTOCOL_VERSION = 2;
44
+ var PROTOCOL_VERSION = 3;
45
45
  var MIN_SUPPORTED_PV = 1;
46
- var GAME_IDS = ["seedha", "snag", "trivia"];
46
+ var GAME_IDS = ["seedha", "snag", "trivia", "dama"];
47
47
  var GAMES = [
48
48
  { id: "seedha", label: "Seedha", minPv: 2 },
49
49
  { id: "snag", label: "Snag", minPv: 2 },
50
+ { id: "dama", label: "Dama", minPv: 3 },
51
+ // engine #2 (D-027) — offered only to pv≥3 clients
50
52
  { id: "trivia", label: "Trivia", minPv: 1, deprecated: true }
51
53
  ];
52
54
  var gamesForPv = (pv) => GAMES.filter((g) => !g.deprecated && g.minPv <= pv);
@@ -116,7 +118,8 @@ var ClientMsg = import_zod.z.discriminatedUnion("t", [
116
118
  game: import_zod.z.enum(GAME_IDS).optional(),
117
119
  // which game to play; defaults to 'trivia'
118
120
  totalRounds: import_zod.z.number().int().min(1).max(50),
119
- categoryIds: import_zod.z.array(import_zod.z.string().max(60)).min(1).max(12),
121
+ categoryIds: import_zod.z.array(import_zod.z.string().max(60)).min(0).max(12),
122
+ // empty for games without categories (Dama); trivia rejects empty in-handler
120
123
  // PREFERRED: pick a mode from the registry (see MODES). The server maps it to the engine
121
124
  // rules. Send this and omit buzzerMode/noHost. Falls back to the legacy flags below if omitted.
122
125
  modeId: import_zod.z.enum(MODE_IDS).optional(),
@@ -153,7 +156,19 @@ var ClientMsg = import_zod.z.discriminatedUnion("t", [
153
156
  // Report the CURRENT question as bad (wrong answer / typo / offensive). The relay resolves WHICH
154
157
  // question from its authoritative room state — the client never sees the question id (anti-cheat),
155
158
  // so it only sends an optional `reason`. Only meaningful while a question is on screen.
156
- import_zod.z.object({ t: import_zod.z.literal("reportQuestion"), reason: import_zod.z.string().max(500).optional() })
159
+ import_zod.z.object({ t: import_zod.z.literal("reportQuestion"), reason: import_zod.z.string().max(500).optional() }),
160
+ // ── Dama slice (gameId 'dama', engine #2 — D-027) ──
161
+ // Make a Dama move: from → to (board coords, 0–7). The relay validates legality against the engine
162
+ // (Shape B) and rejects anything not in legalMoves. If two capture chains share from+to (rare), the
163
+ // relay picks the max-capture one; a client MAY send the full landing `path` to disambiguate.
164
+ import_zod.z.object({
165
+ t: import_zod.z.literal("damaMove"),
166
+ from: import_zod.z.object({ r: import_zod.z.number().int().min(0).max(7), c: import_zod.z.number().int().min(0).max(7) }),
167
+ to: import_zod.z.object({ r: import_zod.z.number().int().min(0).max(7), c: import_zod.z.number().int().min(0).max(7) }),
168
+ path: import_zod.z.array(import_zod.z.object({ r: import_zod.z.number().int().min(0).max(7), c: import_zod.z.number().int().min(0).max(7) })).max(12).optional()
169
+ }),
170
+ // Resign the current Dama game (concede) → opponent wins.
171
+ import_zod.z.object({ t: import_zod.z.literal("damaResign") })
157
172
  ]);
158
173
  // Annotate the CommonJS export names for ESM import in node:
159
174
  0 && (module.exports = {
package/dist/index.d.cts CHANGED
@@ -33,7 +33,7 @@ declare const LIMITS: {
33
33
  * `seedha`/`snag`). Raise the floor SLOWLY to retire ancient builds. Unversioned clients (no `pv`,
34
34
  * e.g. the legacy web harness) are treated as the floor and never rejected.
35
35
  */
36
- declare const PROTOCOL_VERSION = 2;
36
+ declare const PROTOCOL_VERSION = 3;
37
37
  declare const MIN_SUPPORTED_PV = 1;
38
38
  /**
39
39
  * Geem is a PLATFORM of GAMES (jackbox-style). The trivia board split (D-023) into TWO player-facing
@@ -42,7 +42,7 @@ declare const MIN_SUPPORTED_PV = 1;
42
42
  * pre-pv2 clients (they never saw seedha/snag), and what old clients send on startGame. Every room
43
43
  * runs one game, identified by `gameId`.
44
44
  */
45
- declare const GAME_IDS: readonly ["seedha", "snag", "trivia"];
45
+ declare const GAME_IDS: readonly ["seedha", "snag", "trivia", "dama"];
46
46
  type GameId = (typeof GAME_IDS)[number];
47
47
  /**
48
48
  * The GAME REGISTRY — player-facing games + the version gating (D-024/D-025). A client renders a tile
@@ -286,7 +286,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
286
286
  activity: "configuring" | "idle";
287
287
  }>, z.ZodObject<{
288
288
  t: z.ZodLiteral<"startGame">;
289
- game: z.ZodOptional<z.ZodEnum<["seedha", "snag", "trivia"]>>;
289
+ game: z.ZodOptional<z.ZodEnum<["seedha", "snag", "trivia", "dama"]>>;
290
290
  totalRounds: z.ZodNumber;
291
291
  categoryIds: z.ZodArray<z.ZodString, "many">;
292
292
  modeId: z.ZodOptional<z.ZodEnum<["solo", "oneDevice", "snag", "multiplayer"]>>;
@@ -297,7 +297,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
297
297
  categoryIds: string[];
298
298
  totalRounds: number;
299
299
  modeId?: "snag" | "solo" | "oneDevice" | "multiplayer" | undefined;
300
- game?: "seedha" | "snag" | "trivia" | undefined;
300
+ game?: "seedha" | "snag" | "trivia" | "dama" | undefined;
301
301
  buzzerMode?: "solo" | "off" | "race" | "controller" | undefined;
302
302
  noHost?: boolean | undefined;
303
303
  }, {
@@ -305,7 +305,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
305
305
  categoryIds: string[];
306
306
  totalRounds: number;
307
307
  modeId?: "snag" | "solo" | "oneDevice" | "multiplayer" | undefined;
308
- game?: "seedha" | "snag" | "trivia" | undefined;
308
+ game?: "seedha" | "snag" | "trivia" | "dama" | undefined;
309
309
  buzzerMode?: "solo" | "off" | "race" | "controller" | undefined;
310
310
  noHost?: boolean | undefined;
311
311
  }>, z.ZodObject<{
@@ -419,6 +419,72 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
419
419
  }, {
420
420
  t: "reportQuestion";
421
421
  reason?: string | undefined;
422
+ }>, z.ZodObject<{
423
+ t: z.ZodLiteral<"damaMove">;
424
+ from: z.ZodObject<{
425
+ r: z.ZodNumber;
426
+ c: z.ZodNumber;
427
+ }, "strip", z.ZodTypeAny, {
428
+ r: number;
429
+ c: number;
430
+ }, {
431
+ r: number;
432
+ c: number;
433
+ }>;
434
+ to: z.ZodObject<{
435
+ r: z.ZodNumber;
436
+ c: z.ZodNumber;
437
+ }, "strip", z.ZodTypeAny, {
438
+ r: number;
439
+ c: number;
440
+ }, {
441
+ r: number;
442
+ c: number;
443
+ }>;
444
+ path: z.ZodOptional<z.ZodArray<z.ZodObject<{
445
+ r: z.ZodNumber;
446
+ c: z.ZodNumber;
447
+ }, "strip", z.ZodTypeAny, {
448
+ r: number;
449
+ c: number;
450
+ }, {
451
+ r: number;
452
+ c: number;
453
+ }>, "many">>;
454
+ }, "strip", z.ZodTypeAny, {
455
+ t: "damaMove";
456
+ from: {
457
+ r: number;
458
+ c: number;
459
+ };
460
+ to: {
461
+ r: number;
462
+ c: number;
463
+ };
464
+ path?: {
465
+ r: number;
466
+ c: number;
467
+ }[] | undefined;
468
+ }, {
469
+ t: "damaMove";
470
+ from: {
471
+ r: number;
472
+ c: number;
473
+ };
474
+ to: {
475
+ r: number;
476
+ c: number;
477
+ };
478
+ path?: {
479
+ r: number;
480
+ c: number;
481
+ }[] | undefined;
482
+ }>, z.ZodObject<{
483
+ t: z.ZodLiteral<"damaResign">;
484
+ }, "strip", z.ZodTypeAny, {
485
+ t: "damaResign";
486
+ }, {
487
+ t: "damaResign";
422
488
  }>]>;
423
489
  type ClientMsg = z.infer<typeof ClientMsg>;
424
490
  /** A player's privacy-safe avatar (emoji + circle color) for in-room display. */
@@ -574,6 +640,36 @@ type Effect = {
574
640
  } | {
575
641
  t: 'endGame';
576
642
  };
643
+ /** A Dama piece as clients see it. */
644
+ interface DamaPieceView {
645
+ color: 'black' | 'white';
646
+ king: boolean;
647
+ }
648
+ /** A legal Dama move offered to the player to move (for highlighting). `captures` = pieces it takes. */
649
+ interface DamaMoveView {
650
+ from: {
651
+ r: number;
652
+ c: number;
653
+ };
654
+ to: {
655
+ r: number;
656
+ c: number;
657
+ };
658
+ captures: number;
659
+ path: {
660
+ r: number;
661
+ c: number;
662
+ }[];
663
+ }
664
+ /** The authoritative Dama board pushed to a client. `board[r][c]` is null on empty/light squares; r=0 is the top. */
665
+ interface DamaStateView {
666
+ board: (DamaPieceView | null)[][];
667
+ turn: 'black' | 'white';
668
+ winner: 'black' | 'white' | null;
669
+ yourColor: 'black' | 'white' | null;
670
+ legalMoves: DamaMoveView[];
671
+ moveCount: number;
672
+ }
577
673
  type ServerMsg = {
578
674
  t: 'roomCreated';
579
675
  code: string;
@@ -618,6 +714,11 @@ type ServerMsg = {
618
714
  view: GameStateView;
619
715
  you: YouContext;
620
716
  teamConnected: boolean[];
717
+ } | {
718
+ t: 'damaState';
719
+ gameId: 'dama';
720
+ view: DamaStateView;
721
+ you: YouContext;
621
722
  } | {
622
723
  t: 'fx';
623
724
  effects: Effect[];
@@ -634,4 +735,4 @@ type ServerMsg = {
634
735
  message: string;
635
736
  };
636
737
 
637
- export { type AvatarView, type BuzzerGameMode, type CategoryView, ClientMsg, type Effect, GAMES, GAME_IDS, type GameId, type GameInfo, type GameStateView, type GameStatus, type GameplayFlow, type HelpName, LIMITS, type LobbyPlayer, MIN_SUPPORTED_PV, MODES, MODE_IDS, type ModeId, type ModeInfo, PROTOCOL_VERSION, type QuestionView, ROOM_ALPHABET, type ServerMsg, type SoundName, type TeamSlotView, type TeamView, type YouContext, gameIdForPv, gamesForPv, modesForGame };
738
+ export { type AvatarView, type BuzzerGameMode, type CategoryView, ClientMsg, type DamaMoveView, type DamaPieceView, type DamaStateView, type Effect, GAMES, GAME_IDS, type GameId, type GameInfo, type GameStateView, type GameStatus, type GameplayFlow, type HelpName, LIMITS, type LobbyPlayer, MIN_SUPPORTED_PV, MODES, MODE_IDS, type ModeId, type ModeInfo, PROTOCOL_VERSION, type QuestionView, ROOM_ALPHABET, type ServerMsg, type SoundName, type TeamSlotView, type TeamView, type YouContext, gameIdForPv, gamesForPv, modesForGame };
package/dist/index.d.ts CHANGED
@@ -33,7 +33,7 @@ declare const LIMITS: {
33
33
  * `seedha`/`snag`). Raise the floor SLOWLY to retire ancient builds. Unversioned clients (no `pv`,
34
34
  * e.g. the legacy web harness) are treated as the floor and never rejected.
35
35
  */
36
- declare const PROTOCOL_VERSION = 2;
36
+ declare const PROTOCOL_VERSION = 3;
37
37
  declare const MIN_SUPPORTED_PV = 1;
38
38
  /**
39
39
  * Geem is a PLATFORM of GAMES (jackbox-style). The trivia board split (D-023) into TWO player-facing
@@ -42,7 +42,7 @@ declare const MIN_SUPPORTED_PV = 1;
42
42
  * pre-pv2 clients (they never saw seedha/snag), and what old clients send on startGame. Every room
43
43
  * runs one game, identified by `gameId`.
44
44
  */
45
- declare const GAME_IDS: readonly ["seedha", "snag", "trivia"];
45
+ declare const GAME_IDS: readonly ["seedha", "snag", "trivia", "dama"];
46
46
  type GameId = (typeof GAME_IDS)[number];
47
47
  /**
48
48
  * The GAME REGISTRY — player-facing games + the version gating (D-024/D-025). A client renders a tile
@@ -286,7 +286,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
286
286
  activity: "configuring" | "idle";
287
287
  }>, z.ZodObject<{
288
288
  t: z.ZodLiteral<"startGame">;
289
- game: z.ZodOptional<z.ZodEnum<["seedha", "snag", "trivia"]>>;
289
+ game: z.ZodOptional<z.ZodEnum<["seedha", "snag", "trivia", "dama"]>>;
290
290
  totalRounds: z.ZodNumber;
291
291
  categoryIds: z.ZodArray<z.ZodString, "many">;
292
292
  modeId: z.ZodOptional<z.ZodEnum<["solo", "oneDevice", "snag", "multiplayer"]>>;
@@ -297,7 +297,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
297
297
  categoryIds: string[];
298
298
  totalRounds: number;
299
299
  modeId?: "snag" | "solo" | "oneDevice" | "multiplayer" | undefined;
300
- game?: "seedha" | "snag" | "trivia" | undefined;
300
+ game?: "seedha" | "snag" | "trivia" | "dama" | undefined;
301
301
  buzzerMode?: "solo" | "off" | "race" | "controller" | undefined;
302
302
  noHost?: boolean | undefined;
303
303
  }, {
@@ -305,7 +305,7 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
305
305
  categoryIds: string[];
306
306
  totalRounds: number;
307
307
  modeId?: "snag" | "solo" | "oneDevice" | "multiplayer" | undefined;
308
- game?: "seedha" | "snag" | "trivia" | undefined;
308
+ game?: "seedha" | "snag" | "trivia" | "dama" | undefined;
309
309
  buzzerMode?: "solo" | "off" | "race" | "controller" | undefined;
310
310
  noHost?: boolean | undefined;
311
311
  }>, z.ZodObject<{
@@ -419,6 +419,72 @@ declare const ClientMsg: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
419
419
  }, {
420
420
  t: "reportQuestion";
421
421
  reason?: string | undefined;
422
+ }>, z.ZodObject<{
423
+ t: z.ZodLiteral<"damaMove">;
424
+ from: z.ZodObject<{
425
+ r: z.ZodNumber;
426
+ c: z.ZodNumber;
427
+ }, "strip", z.ZodTypeAny, {
428
+ r: number;
429
+ c: number;
430
+ }, {
431
+ r: number;
432
+ c: number;
433
+ }>;
434
+ to: z.ZodObject<{
435
+ r: z.ZodNumber;
436
+ c: z.ZodNumber;
437
+ }, "strip", z.ZodTypeAny, {
438
+ r: number;
439
+ c: number;
440
+ }, {
441
+ r: number;
442
+ c: number;
443
+ }>;
444
+ path: z.ZodOptional<z.ZodArray<z.ZodObject<{
445
+ r: z.ZodNumber;
446
+ c: z.ZodNumber;
447
+ }, "strip", z.ZodTypeAny, {
448
+ r: number;
449
+ c: number;
450
+ }, {
451
+ r: number;
452
+ c: number;
453
+ }>, "many">>;
454
+ }, "strip", z.ZodTypeAny, {
455
+ t: "damaMove";
456
+ from: {
457
+ r: number;
458
+ c: number;
459
+ };
460
+ to: {
461
+ r: number;
462
+ c: number;
463
+ };
464
+ path?: {
465
+ r: number;
466
+ c: number;
467
+ }[] | undefined;
468
+ }, {
469
+ t: "damaMove";
470
+ from: {
471
+ r: number;
472
+ c: number;
473
+ };
474
+ to: {
475
+ r: number;
476
+ c: number;
477
+ };
478
+ path?: {
479
+ r: number;
480
+ c: number;
481
+ }[] | undefined;
482
+ }>, z.ZodObject<{
483
+ t: z.ZodLiteral<"damaResign">;
484
+ }, "strip", z.ZodTypeAny, {
485
+ t: "damaResign";
486
+ }, {
487
+ t: "damaResign";
422
488
  }>]>;
423
489
  type ClientMsg = z.infer<typeof ClientMsg>;
424
490
  /** A player's privacy-safe avatar (emoji + circle color) for in-room display. */
@@ -574,6 +640,36 @@ type Effect = {
574
640
  } | {
575
641
  t: 'endGame';
576
642
  };
643
+ /** A Dama piece as clients see it. */
644
+ interface DamaPieceView {
645
+ color: 'black' | 'white';
646
+ king: boolean;
647
+ }
648
+ /** A legal Dama move offered to the player to move (for highlighting). `captures` = pieces it takes. */
649
+ interface DamaMoveView {
650
+ from: {
651
+ r: number;
652
+ c: number;
653
+ };
654
+ to: {
655
+ r: number;
656
+ c: number;
657
+ };
658
+ captures: number;
659
+ path: {
660
+ r: number;
661
+ c: number;
662
+ }[];
663
+ }
664
+ /** The authoritative Dama board pushed to a client. `board[r][c]` is null on empty/light squares; r=0 is the top. */
665
+ interface DamaStateView {
666
+ board: (DamaPieceView | null)[][];
667
+ turn: 'black' | 'white';
668
+ winner: 'black' | 'white' | null;
669
+ yourColor: 'black' | 'white' | null;
670
+ legalMoves: DamaMoveView[];
671
+ moveCount: number;
672
+ }
577
673
  type ServerMsg = {
578
674
  t: 'roomCreated';
579
675
  code: string;
@@ -618,6 +714,11 @@ type ServerMsg = {
618
714
  view: GameStateView;
619
715
  you: YouContext;
620
716
  teamConnected: boolean[];
717
+ } | {
718
+ t: 'damaState';
719
+ gameId: 'dama';
720
+ view: DamaStateView;
721
+ you: YouContext;
621
722
  } | {
622
723
  t: 'fx';
623
724
  effects: Effect[];
@@ -634,4 +735,4 @@ type ServerMsg = {
634
735
  message: string;
635
736
  };
636
737
 
637
- export { type AvatarView, type BuzzerGameMode, type CategoryView, ClientMsg, type Effect, GAMES, GAME_IDS, type GameId, type GameInfo, type GameStateView, type GameStatus, type GameplayFlow, type HelpName, LIMITS, type LobbyPlayer, MIN_SUPPORTED_PV, MODES, MODE_IDS, type ModeId, type ModeInfo, PROTOCOL_VERSION, type QuestionView, ROOM_ALPHABET, type ServerMsg, type SoundName, type TeamSlotView, type TeamView, type YouContext, gameIdForPv, gamesForPv, modesForGame };
738
+ export { type AvatarView, type BuzzerGameMode, type CategoryView, ClientMsg, type DamaMoveView, type DamaPieceView, type DamaStateView, type Effect, GAMES, GAME_IDS, type GameId, type GameInfo, type GameStateView, type GameStatus, type GameplayFlow, type HelpName, LIMITS, type LobbyPlayer, MIN_SUPPORTED_PV, MODES, MODE_IDS, type ModeId, type ModeInfo, PROTOCOL_VERSION, type QuestionView, ROOM_ALPHABET, type ServerMsg, type SoundName, type TeamSlotView, type TeamView, type YouContext, gameIdForPv, gamesForPv, modesForGame };
package/dist/index.js CHANGED
@@ -6,12 +6,14 @@ var LIMITS = {
6
6
  MAX_PLAYERS: 12,
7
7
  MAX_MSG_PER_SEC: 8
8
8
  };
9
- var PROTOCOL_VERSION = 2;
9
+ var PROTOCOL_VERSION = 3;
10
10
  var MIN_SUPPORTED_PV = 1;
11
- var GAME_IDS = ["seedha", "snag", "trivia"];
11
+ var GAME_IDS = ["seedha", "snag", "trivia", "dama"];
12
12
  var GAMES = [
13
13
  { id: "seedha", label: "Seedha", minPv: 2 },
14
14
  { id: "snag", label: "Snag", minPv: 2 },
15
+ { id: "dama", label: "Dama", minPv: 3 },
16
+ // engine #2 (D-027) — offered only to pv≥3 clients
15
17
  { id: "trivia", label: "Trivia", minPv: 1, deprecated: true }
16
18
  ];
17
19
  var gamesForPv = (pv) => GAMES.filter((g) => !g.deprecated && g.minPv <= pv);
@@ -81,7 +83,8 @@ var ClientMsg = z.discriminatedUnion("t", [
81
83
  game: z.enum(GAME_IDS).optional(),
82
84
  // which game to play; defaults to 'trivia'
83
85
  totalRounds: z.number().int().min(1).max(50),
84
- categoryIds: z.array(z.string().max(60)).min(1).max(12),
86
+ categoryIds: z.array(z.string().max(60)).min(0).max(12),
87
+ // empty for games without categories (Dama); trivia rejects empty in-handler
85
88
  // PREFERRED: pick a mode from the registry (see MODES). The server maps it to the engine
86
89
  // rules. Send this and omit buzzerMode/noHost. Falls back to the legacy flags below if omitted.
87
90
  modeId: z.enum(MODE_IDS).optional(),
@@ -118,7 +121,19 @@ var ClientMsg = z.discriminatedUnion("t", [
118
121
  // Report the CURRENT question as bad (wrong answer / typo / offensive). The relay resolves WHICH
119
122
  // question from its authoritative room state — the client never sees the question id (anti-cheat),
120
123
  // so it only sends an optional `reason`. Only meaningful while a question is on screen.
121
- z.object({ t: z.literal("reportQuestion"), reason: z.string().max(500).optional() })
124
+ z.object({ t: z.literal("reportQuestion"), reason: z.string().max(500).optional() }),
125
+ // ── Dama slice (gameId 'dama', engine #2 — D-027) ──
126
+ // Make a Dama move: from → to (board coords, 0–7). The relay validates legality against the engine
127
+ // (Shape B) and rejects anything not in legalMoves. If two capture chains share from+to (rare), the
128
+ // relay picks the max-capture one; a client MAY send the full landing `path` to disambiguate.
129
+ z.object({
130
+ t: z.literal("damaMove"),
131
+ from: z.object({ r: z.number().int().min(0).max(7), c: z.number().int().min(0).max(7) }),
132
+ to: z.object({ r: z.number().int().min(0).max(7), c: z.number().int().min(0).max(7) }),
133
+ path: z.array(z.object({ r: z.number().int().min(0).max(7), c: z.number().int().min(0).max(7) })).max(12).optional()
134
+ }),
135
+ // Resign the current Dama game (concede) → opponent wins.
136
+ z.object({ t: z.literal("damaResign") })
122
137
  ]);
123
138
  export {
124
139
  ClientMsg,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thegeem/protocol",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
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",