@thegeem/protocol 0.1.34 → 0.1.36

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.d.cts CHANGED
@@ -692,6 +692,34 @@ interface LobbyPlayer {
692
692
  /** Team index once the game has started; null/absent in the lobby. */
693
693
  teamIndex?: number | null;
694
694
  }
695
+ /**
696
+ * Who is in the room, carried ON the game-state push (gs#71).
697
+ *
698
+ * `players[]` in the `lobby` frame is the only other carrier of names, and the lobby does not go out
699
+ * mid-game — deliberately, since a stray `lobby` under a running game bounces clients off their board
700
+ * (the gs#49 class). So a client that refreshed mid-game had NO roster for the rest of the game, and
701
+ * every id→name lookup degraded to whatever that client happened to use as its null-fallback: «؟» on
702
+ * web, the colour name in دامة on Android (readable by luck), and an empty string in ارسمها (a sentence
703
+ * with a hole in it).
704
+ *
705
+ * It rides ON the state message rather than arriving as a separate frame because both consuming teams
706
+ * asked for that independently, for the same reason: a roster in its own message can land either side
707
+ * of the state push, giving one render with names and one without on clients that redraw per state.
708
+ * Bundled, it cannot race.
709
+ *
710
+ * Unlike `LobbyPlayer` this INCLUDES THE HOST (`isHost`), who is filtered out of `lobby.players` by
711
+ * design and therefore has no row — yet needs a face wherever the host can appear, e.g. the `tally`.
712
+ */
713
+ interface RosterEntry {
714
+ id: string;
715
+ name: string;
716
+ /** Privacy-safe avatar (emoji + color); absent if unset. */
717
+ avatar?: AvatarView;
718
+ /** True for the room's host, who has no row in `lobby.players`. */
719
+ isHost?: boolean;
720
+ /** Is this person currently connected? Mirrors `LobbyPlayer.connected`. */
721
+ connected?: boolean;
722
+ }
695
723
  /** Per-recipient context attached to lobby/state pushes. */
696
724
  interface YouContext {
697
725
  id: string;
@@ -710,6 +738,25 @@ interface CategoryView {
710
738
  /** Category's brand color (hex, e.g. '#E8473F') for board coloring; absent if the category has none. */
711
739
  color?: string;
712
740
  }
741
+ /**
742
+ * A content pack — the grouping the category strip renders as a row (geem-server#76).
743
+ *
744
+ * **The ARRAY ORDER is the pack order.** That is the point of this type: pack order previously had no
745
+ * home on the wire, so all four clients derived it by first-seen from `categories[]` (Android's
746
+ * `onlineGroupedPacks`, iOS's `order: [String]`, and web/TV their own). Four derivations of one fact,
747
+ * and when the server's own category order turned out to be non-deterministic the strip reshuffled
748
+ * between visits (gs#74) with nothing on the wire to pin it. One ordered array replaces all four.
749
+ *
750
+ * Deliberately NO `sortOrder` field: an ordered array already carries the order, and a positional index
751
+ * beside it would be the same fact stored twice — which is exactly the drift argument that made
752
+ * `has_image` a boolean rather than a filename. Position cannot disagree with itself.
753
+ */
754
+ interface PackView {
755
+ id: string;
756
+ name: string;
757
+ /** Does this pack have cover art at `/assets/cat/pack-<id>.png`? Absent until the admin field ships. */
758
+ hasImage?: boolean;
759
+ }
713
760
  type GameStatus = 'setup' | 'categorySelection' | 'inProgress' | 'completed';
714
761
  type GameplayFlow = 'classic' | 'openJudged' | 'noHost' | 'buzzer';
715
762
  type BuzzerGameMode = 'off' | 'solo' | 'race' | 'controller';
@@ -940,6 +987,7 @@ type ServerMsg = {
940
987
  hostPinned?: boolean;
941
988
  players: LobbyPlayer[];
942
989
  categories: CategoryView[];
990
+ packs?: PackView[];
943
991
  selectedCategoryIds: string[];
944
992
  teams: TeamSlotView[];
945
993
  hostActivity: 'configuring' | 'idle';
@@ -954,16 +1002,19 @@ type ServerMsg = {
954
1002
  view: GameStateView;
955
1003
  you: YouContext;
956
1004
  teamConnected: boolean[];
1005
+ roster?: RosterEntry[];
957
1006
  } | {
958
1007
  t: 'damaState';
959
1008
  gameId: 'dama';
960
1009
  view: DamaStateView;
961
1010
  you: YouContext;
1011
+ roster?: RosterEntry[];
962
1012
  } | {
963
1013
  t: 'ersimhaState';
964
1014
  gameId: 'ersimha';
965
1015
  view: ErsimhaStateView;
966
1016
  you: YouContext;
1017
+ roster?: RosterEntry[];
967
1018
  } | {
968
1019
  t: 'strokeBatch';
969
1020
  from: string;
@@ -995,4 +1046,4 @@ type ServerMsg = {
995
1046
  message: string;
996
1047
  };
997
1048
 
998
- export { type AvatarView, type BuzzerGameMode, CHARACTER_IDS, type CategoryView, type CharacterId, ClientMsg, type DamaMoveView, type DamaPieceView, type DamaStateView, EMOTE_IDS, type Effect, type EmoteId, type ErsimhaStateView, 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 StrokePointView, type TeamSlotView, type TeamView, type YouContext, gameIdForPv, gamesForPv, modesForGame };
1049
+ export { type AvatarView, type BuzzerGameMode, CHARACTER_IDS, type CategoryView, type CharacterId, ClientMsg, type DamaMoveView, type DamaPieceView, type DamaStateView, EMOTE_IDS, type Effect, type EmoteId, type ErsimhaStateView, 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 PackView, type QuestionView, ROOM_ALPHABET, type RosterEntry, type ServerMsg, type SoundName, type StrokePointView, type TeamSlotView, type TeamView, type YouContext, gameIdForPv, gamesForPv, modesForGame };
package/dist/index.d.ts CHANGED
@@ -692,6 +692,34 @@ interface LobbyPlayer {
692
692
  /** Team index once the game has started; null/absent in the lobby. */
693
693
  teamIndex?: number | null;
694
694
  }
695
+ /**
696
+ * Who is in the room, carried ON the game-state push (gs#71).
697
+ *
698
+ * `players[]` in the `lobby` frame is the only other carrier of names, and the lobby does not go out
699
+ * mid-game — deliberately, since a stray `lobby` under a running game bounces clients off their board
700
+ * (the gs#49 class). So a client that refreshed mid-game had NO roster for the rest of the game, and
701
+ * every id→name lookup degraded to whatever that client happened to use as its null-fallback: «؟» on
702
+ * web, the colour name in دامة on Android (readable by luck), and an empty string in ارسمها (a sentence
703
+ * with a hole in it).
704
+ *
705
+ * It rides ON the state message rather than arriving as a separate frame because both consuming teams
706
+ * asked for that independently, for the same reason: a roster in its own message can land either side
707
+ * of the state push, giving one render with names and one without on clients that redraw per state.
708
+ * Bundled, it cannot race.
709
+ *
710
+ * Unlike `LobbyPlayer` this INCLUDES THE HOST (`isHost`), who is filtered out of `lobby.players` by
711
+ * design and therefore has no row — yet needs a face wherever the host can appear, e.g. the `tally`.
712
+ */
713
+ interface RosterEntry {
714
+ id: string;
715
+ name: string;
716
+ /** Privacy-safe avatar (emoji + color); absent if unset. */
717
+ avatar?: AvatarView;
718
+ /** True for the room's host, who has no row in `lobby.players`. */
719
+ isHost?: boolean;
720
+ /** Is this person currently connected? Mirrors `LobbyPlayer.connected`. */
721
+ connected?: boolean;
722
+ }
695
723
  /** Per-recipient context attached to lobby/state pushes. */
696
724
  interface YouContext {
697
725
  id: string;
@@ -710,6 +738,25 @@ interface CategoryView {
710
738
  /** Category's brand color (hex, e.g. '#E8473F') for board coloring; absent if the category has none. */
711
739
  color?: string;
712
740
  }
741
+ /**
742
+ * A content pack — the grouping the category strip renders as a row (geem-server#76).
743
+ *
744
+ * **The ARRAY ORDER is the pack order.** That is the point of this type: pack order previously had no
745
+ * home on the wire, so all four clients derived it by first-seen from `categories[]` (Android's
746
+ * `onlineGroupedPacks`, iOS's `order: [String]`, and web/TV their own). Four derivations of one fact,
747
+ * and when the server's own category order turned out to be non-deterministic the strip reshuffled
748
+ * between visits (gs#74) with nothing on the wire to pin it. One ordered array replaces all four.
749
+ *
750
+ * Deliberately NO `sortOrder` field: an ordered array already carries the order, and a positional index
751
+ * beside it would be the same fact stored twice — which is exactly the drift argument that made
752
+ * `has_image` a boolean rather than a filename. Position cannot disagree with itself.
753
+ */
754
+ interface PackView {
755
+ id: string;
756
+ name: string;
757
+ /** Does this pack have cover art at `/assets/cat/pack-<id>.png`? Absent until the admin field ships. */
758
+ hasImage?: boolean;
759
+ }
713
760
  type GameStatus = 'setup' | 'categorySelection' | 'inProgress' | 'completed';
714
761
  type GameplayFlow = 'classic' | 'openJudged' | 'noHost' | 'buzzer';
715
762
  type BuzzerGameMode = 'off' | 'solo' | 'race' | 'controller';
@@ -940,6 +987,7 @@ type ServerMsg = {
940
987
  hostPinned?: boolean;
941
988
  players: LobbyPlayer[];
942
989
  categories: CategoryView[];
990
+ packs?: PackView[];
943
991
  selectedCategoryIds: string[];
944
992
  teams: TeamSlotView[];
945
993
  hostActivity: 'configuring' | 'idle';
@@ -954,16 +1002,19 @@ type ServerMsg = {
954
1002
  view: GameStateView;
955
1003
  you: YouContext;
956
1004
  teamConnected: boolean[];
1005
+ roster?: RosterEntry[];
957
1006
  } | {
958
1007
  t: 'damaState';
959
1008
  gameId: 'dama';
960
1009
  view: DamaStateView;
961
1010
  you: YouContext;
1011
+ roster?: RosterEntry[];
962
1012
  } | {
963
1013
  t: 'ersimhaState';
964
1014
  gameId: 'ersimha';
965
1015
  view: ErsimhaStateView;
966
1016
  you: YouContext;
1017
+ roster?: RosterEntry[];
967
1018
  } | {
968
1019
  t: 'strokeBatch';
969
1020
  from: string;
@@ -995,4 +1046,4 @@ type ServerMsg = {
995
1046
  message: string;
996
1047
  };
997
1048
 
998
- export { type AvatarView, type BuzzerGameMode, CHARACTER_IDS, type CategoryView, type CharacterId, ClientMsg, type DamaMoveView, type DamaPieceView, type DamaStateView, EMOTE_IDS, type Effect, type EmoteId, type ErsimhaStateView, 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 StrokePointView, type TeamSlotView, type TeamView, type YouContext, gameIdForPv, gamesForPv, modesForGame };
1049
+ export { type AvatarView, type BuzzerGameMode, CHARACTER_IDS, type CategoryView, type CharacterId, ClientMsg, type DamaMoveView, type DamaPieceView, type DamaStateView, EMOTE_IDS, type Effect, type EmoteId, type ErsimhaStateView, 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 PackView, type QuestionView, ROOM_ALPHABET, type RosterEntry, type ServerMsg, type SoundName, type StrokePointView, type TeamSlotView, type TeamView, type YouContext, gameIdForPv, gamesForPv, modesForGame };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thegeem/protocol",
3
- "version": "0.1.34",
3
+ "version": "0.1.36",
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",