@hellacardgames/speed 0.6.0 → 0.7.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.
@@ -1,5 +1,5 @@
1
- import { g as Card, h as ChatMessage, m as ClientState, p as GameEvent } from "../index-YAYbNr-s.cjs";
2
- import { a as GetJoinableGamesResult, c as PlayCardResult, f as StartGameResult, i as GetEventsAndClearAcknowledgedResult, l as ReportNoPlayableCardsResult, n as DrawCardResult, o as JoinGameResult, r as GetClientStateAndClearEventsResult, s as LeaveGameResult, t as CreateGameResult, u as SendChatResult } from "../index-FveApXzq.cjs";
1
+ import { g as Card, h as ChatMessage, m as ClientState, p as GameEvent } from "../index-xmcja9Bl.cjs";
2
+ import { a as GetJoinableGamesResult, c as PlayCardResult, f as StartGameResult, i as GetEventsAndClearAcknowledgedResult, l as ReportNoPlayableCardsResult, n as DrawCardResult, o as JoinGameResult, r as GetClientStateAndClearEventsResult, s as LeaveGameResult, t as CreateGameResult, u as SendChatResult } from "../index-BvZyhFek.cjs";
3
3
  //#region src/client/Client.d.ts
4
4
  declare class Client {
5
5
  private readonly baseUrl;
@@ -1,5 +1,5 @@
1
- import { g as Card, h as ChatMessage, m as ClientState, p as GameEvent } from "../index-YAYbNr-s.mjs";
2
- import { a as GetJoinableGamesResult, c as PlayCardResult, f as StartGameResult, i as GetEventsAndClearAcknowledgedResult, l as ReportNoPlayableCardsResult, n as DrawCardResult, o as JoinGameResult, r as GetClientStateAndClearEventsResult, s as LeaveGameResult, t as CreateGameResult, u as SendChatResult } from "../index-J8s2QklC.mjs";
1
+ import { g as Card, h as ChatMessage, m as ClientState, p as GameEvent } from "../index-xmcja9Bl.mjs";
2
+ import { a as GetJoinableGamesResult, c as PlayCardResult, f as StartGameResult, i as GetEventsAndClearAcknowledgedResult, l as ReportNoPlayableCardsResult, n as DrawCardResult, o as JoinGameResult, r as GetClientStateAndClearEventsResult, s as LeaveGameResult, t as CreateGameResult, u as SendChatResult } from "../index-BRtLpRz1.mjs";
3
3
  //#region src/client/Client.d.ts
4
4
  declare class Client {
5
5
  private readonly baseUrl;
@@ -1,4 +1,4 @@
1
- import { m as ClientState, p as GameEvent } from "./index-YAYbNr-s.cjs";
1
+ import { c as Manager, m as ClientState, p as GameEvent } from "./index-xmcja9Bl.mjs";
2
2
  //#region src/server/Server.d.ts
3
3
  type CreateGameResult = {
4
4
  readonly success: true;
@@ -107,6 +107,7 @@ declare class Server {
107
107
  readonly path: "/startGame";
108
108
  readonly action: (input: unknown) => StartGameResult;
109
109
  }];
110
+ constructor(...args: ConstructorParameters<typeof Manager>);
110
111
  private createGame;
111
112
  private drawCard;
112
113
  private getClientStateAndClearEvents;
@@ -121,4 +122,4 @@ declare class Server {
121
122
  }
122
123
  //#endregion
123
124
  export { GetJoinableGamesResult as a, PlayCardResult as c, Server as d, StartGameResult as f, GetEventsAndClearAcknowledgedResult as i, ReportNoPlayableCardsResult as l, DrawCardResult as n, JoinGameResult as o, GetClientStateAndClearEventsResult as r, LeaveGameResult as s, CreateGameResult as t, SendChatResult as u };
124
- //# sourceMappingURL=index-FveApXzq.d.cts.map
125
+ //# sourceMappingURL=index-BRtLpRz1.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-FveApXzq.d.cts","names":[],"sources":["../src/server/Server.ts"],"mappings":";;KAIY;WAEG;WACA;WACA;;WAGA;WACA;;KAGH;WAEG;;WAGA;WACA;;KAUH;WAEG;WACA,OAAO;;WAGP;WACA;;KAGH;WAEG;WACA,iBAAiB;;WAGjB;WACA;;KAGH;WACD;aACE;aACA;;;KAID;WAEG;WACA;;WAGA;WACA;;KAQH;WAEG;;WAGA;WACA;;KAGH;WAEG;;WAGA;WACA;;KAUH;WAEG;;WAGA;WACA;;KAWH;WAEG;;WAGA;WACA;;KAGH;WAEG;;WAGA;WACA;;cAiFF;mBACM;WAER;;aAuBkB,SAAA,gBAAM,qBAAqB;;;aAI9B,SAAA,mBAAU;;;aASzB,SAAA,mBACN;;;aAUM,SAAA,mBACN;;;aASyB,cAAA;;;aAKnB,SAAA,gBAAO,gBACA,qBAEb;;;aAQsB,SAAA,mBAAU;;;aAQX,SAAA,mBAAU;;;aAQG,SAAA,mBAAU;;;aAQvB,SAAA,mBAAU;;;aAQT,SAAA,mBAAU;;UAlF3B;UAIA;UAQA;UAWA;UAWA;UAIA;UAYA;UAQA;UAQA;UAQA;UAQA"}
1
+ {"version":3,"file":"index-BRtLpRz1.d.mts","names":[],"sources":["../src/server/Server.ts"],"mappings":";;KAIY;WAEG;WACA;WACA;;WAGA;WACA;;KAGH;WAEG;;WAGA;WACA;;KAUH;WAEG;WACA,OAAO;;WAGP;WACA;;KAGH;WAEG;WACA,iBAAiB;;WAGjB;WACA;;KAGH;WACD;aACE;aACA;;;KAID;WAEG;WACA;;WAGA;WACA;;KAQH;WAEG;;WAGA;WACA;;KAGH;WAEG;;WAGA;WACA;;KAUH;WAEG;;WAGA;WACA;;KAWH;WAEG;;WAGA;WACA;;KAGH;WAEG;;WAGA;WACA;;cAiFF;mBACM;WAER;;aA2BkB,SAAA,gBAAM,qBAAqB;;;aAI9B,SAAA,mBAAU;;;aASzB,SAAA,mBACN;;;aAUM,SAAA,mBACN;;;aASyB,cAAA;;;aAKnB,SAAA,gBAAO,gBACA,qBAEb;;;aAQsB,SAAA,mBAAU;;;aAQX,SAAA,mBAAU;;;aAQG,SAAA,mBAAU;;;aAQvB,SAAA,mBAAU;;;aAQT,SAAA,mBAAU;;EAtFvB,eAAG,MAAM,6BAA6B;UAI1C;UAIA;UAQA;UAWA;UAWA;UAIA;UAYA;UAQA;UAQA;UAQA;UAQA"}
@@ -1,4 +1,4 @@
1
- import { m as ClientState, p as GameEvent } from "./index-YAYbNr-s.mjs";
1
+ import { c as Manager, m as ClientState, p as GameEvent } from "./index-xmcja9Bl.cjs";
2
2
  //#region src/server/Server.d.ts
3
3
  type CreateGameResult = {
4
4
  readonly success: true;
@@ -107,6 +107,7 @@ declare class Server {
107
107
  readonly path: "/startGame";
108
108
  readonly action: (input: unknown) => StartGameResult;
109
109
  }];
110
+ constructor(...args: ConstructorParameters<typeof Manager>);
110
111
  private createGame;
111
112
  private drawCard;
112
113
  private getClientStateAndClearEvents;
@@ -121,4 +122,4 @@ declare class Server {
121
122
  }
122
123
  //#endregion
123
124
  export { GetJoinableGamesResult as a, PlayCardResult as c, Server as d, StartGameResult as f, GetEventsAndClearAcknowledgedResult as i, ReportNoPlayableCardsResult as l, DrawCardResult as n, JoinGameResult as o, GetClientStateAndClearEventsResult as r, LeaveGameResult as s, CreateGameResult as t, SendChatResult as u };
124
- //# sourceMappingURL=index-J8s2QklC.d.mts.map
125
+ //# sourceMappingURL=index-BvZyhFek.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-J8s2QklC.d.mts","names":[],"sources":["../src/server/Server.ts"],"mappings":";;KAIY;WAEG;WACA;WACA;;WAGA;WACA;;KAGH;WAEG;;WAGA;WACA;;KAUH;WAEG;WACA,OAAO;;WAGP;WACA;;KAGH;WAEG;WACA,iBAAiB;;WAGjB;WACA;;KAGH;WACD;aACE;aACA;;;KAID;WAEG;WACA;;WAGA;WACA;;KAQH;WAEG;;WAGA;WACA;;KAGH;WAEG;;WAGA;WACA;;KAUH;WAEG;;WAGA;WACA;;KAWH;WAEG;;WAGA;WACA;;KAGH;WAEG;;WAGA;WACA;;cAiFF;mBACM;WAER;;aAuBkB,SAAA,gBAAM,qBAAqB;;;aAI9B,SAAA,mBAAU;;;aASzB,SAAA,mBACN;;;aAUM,SAAA,mBACN;;;aASyB,cAAA;;;aAKnB,SAAA,gBAAO,gBACA,qBAEb;;;aAQsB,SAAA,mBAAU;;;aAQX,SAAA,mBAAU;;;aAQG,SAAA,mBAAU;;;aAQvB,SAAA,mBAAU;;;aAQT,SAAA,mBAAU;;UAlF3B;UAIA;UAQA;UAWA;UAWA;UAIA;UAYA;UAQA;UAQA;UAQA;UAQA"}
1
+ {"version":3,"file":"index-BvZyhFek.d.cts","names":[],"sources":["../src/server/Server.ts"],"mappings":";;KAIY;WAEG;WACA;WACA;;WAGA;WACA;;KAGH;WAEG;;WAGA;WACA;;KAUH;WAEG;WACA,OAAO;;WAGP;WACA;;KAGH;WAEG;WACA,iBAAiB;;WAGjB;WACA;;KAGH;WACD;aACE;aACA;;;KAID;WAEG;WACA;;WAGA;WACA;;KAQH;WAEG;;WAGA;WACA;;KAGH;WAEG;;WAGA;WACA;;KAUH;WAEG;;WAGA;WACA;;KAWH;WAEG;;WAGA;WACA;;KAGH;WAEG;;WAGA;WACA;;cAiFF;mBACM;WAER;;aA2BkB,SAAA,gBAAM,qBAAqB;;;aAI9B,SAAA,mBAAU;;;aASzB,SAAA,mBACN;;;aAUM,SAAA,mBACN;;;aASyB,cAAA;;;aAKnB,SAAA,gBAAO,gBACA,qBAEb;;;aAQsB,SAAA,mBAAU;;;aAQX,SAAA,mBAAU;;;aAQG,SAAA,mBAAU;;;aAQvB,SAAA,mBAAU;;;aAQT,SAAA,mBAAU;;EAtFvB,eAAG,MAAM,6BAA6B;UAI1C;UAIA;UAQA;UAWA;UAWA;UAIA;UAYA;UAQA;UAQA;UAQA;UAQA"}
@@ -1,3 +1,4 @@
1
+ import { ManagerBase } from "@hellacardgames/lib";
1
2
  //#region src/manager/types/Card.d.ts
2
3
  type Card = {
3
4
  readonly id: string;
@@ -18,13 +19,13 @@ type ClientState = {
18
19
  readonly gameId: string;
19
20
  readonly playerId: string;
20
21
  readonly username: string;
21
- readonly players: readonly Player[];
22
+ readonly players: readonly Player$1[];
22
23
  readonly expiresAt: number;
23
24
  readonly chatMessages: readonly ChatMessage[];
24
25
  readonly hand: readonly Card[];
25
26
  readonly canPlayAt: number | null;
26
27
  };
27
- type Player = {
28
+ type Player$1 = {
28
29
  readonly username: string;
29
30
  readonly handSize: number;
30
31
  readonly drawPileSize: number;
@@ -107,6 +108,55 @@ type GameEvent = {
107
108
  readonly numCards: number;
108
109
  };
109
110
  //#endregion
111
+ //#region src/manager/types/Player.d.ts
112
+ type Player = {
113
+ readonly id: string;
114
+ readonly userId: string;
115
+ readonly username: string;
116
+ readonly events: GameEvent[];
117
+ readonly hand: Card[];
118
+ readonly drawPile: Card[];
119
+ readonly sidePile: Card[];
120
+ readonly centerPile: Card[];
121
+ hasNoPlayableCards: boolean;
122
+ };
123
+ //#endregion
124
+ //#region src/manager/types/Game.d.ts
125
+ type Game = OpenGame | StartedGame | CompletedGame | ForfeitedGame;
126
+ type OpenGame = {
127
+ readonly status: "open";
128
+ readonly id: string;
129
+ readonly createdAt: number;
130
+ readonly expiresAt: number;
131
+ readonly chatMessages: ChatMessage[];
132
+ readonly players: Player[];
133
+ };
134
+ type StartedGame = {
135
+ readonly status: "started";
136
+ readonly id: string;
137
+ readonly createdAt: number;
138
+ expiresAt: number;
139
+ readonly chatMessages: ChatMessage[];
140
+ readonly players: Player[];
141
+ canPlayAt: number;
142
+ };
143
+ type CompletedGame = {
144
+ readonly status: "completed";
145
+ readonly id: string;
146
+ readonly createdAt: number;
147
+ readonly expiresAt: number;
148
+ readonly chatMessages: ChatMessage[];
149
+ readonly players: Player[];
150
+ };
151
+ type ForfeitedGame = {
152
+ readonly status: "forfeited";
153
+ readonly id: string;
154
+ readonly createdAt: number;
155
+ readonly expiresAt: number;
156
+ readonly chatMessages: ChatMessage[];
157
+ readonly players: Player[];
158
+ };
159
+ //#endregion
110
160
  //#region src/manager/Manager.d.ts
111
161
  type CreateGameResult = {
112
162
  readonly success: true;
@@ -179,10 +229,7 @@ type StartGameResult = {
179
229
  readonly success: false;
180
230
  readonly error: "gameNotFound" | "invalidStatus" | "playerNotFound" | "playerNotAdmin" | "minPlayersNotReached";
181
231
  };
182
- declare class Manager {
183
- private readonly games;
184
- private readonly watchdog;
185
- constructor();
232
+ declare class Manager extends ManagerBase<Game> {
186
233
  createGame(userId: string, username: string): CreateGameResult;
187
234
  drawCard(gameId: string, playerId: string): DrawCardResult;
188
235
  getClientStateAndClearEvents(gameId: string, playerId: string): GetClientStateAndClearEventsResult;
@@ -197,4 +244,4 @@ declare class Manager {
197
244
  }
198
245
  //#endregion
199
246
  export { GetJoinableGamesResult as a, Manager as c, SendChatResult as d, StartGameResult as f, Card as g, ChatMessage as h, GetEventsAndClearAcknowledgedResult as i, PlayCardResult as l, ClientState as m, DrawCardResult as n, JoinGameResult as o, GameEvent as p, GetClientStateAndClearEventsResult as r, LeaveGameResult as s, CreateGameResult as t, ReportNoPlayableCardsResult as u };
200
- //# sourceMappingURL=index-YAYbNr-s.d.cts.map
247
+ //# sourceMappingURL=index-xmcja9Bl.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-xmcja9Bl.d.cts","names":[],"sources":["../src/manager/types/Card.ts","../src/manager/types/ChatMessage.ts","../src/manager/types/ClientState.ts","../src/manager/types/GameEvent.ts","../src/manager/types/Player.ts","../src/manager/types/Game.ts","../src/manager/Manager.ts"],"mappings":";;KAAY;WACD;WACA;WACA;;;;KCHC;WACD;WACA;WACA;;;;KCAC;WACD;WACA;WACA;WACA;WACA,kBAAkB;WAClB;WACA,uBAAuB;WACvB,eAAe;WACf;;KAGN;WACM;WACA;WACA;WACA;WACA,mBAAmB;;;;KCjBlB;WAEG;WACA;WACA;;WAGA;WACA;WACA;WACA,MAAM;;WAGN;WACA;WACA;WACA,MAAM;WACN;;WAGA;WACA;WACA,SAAS;;WAGT;WACA;WACA,MAAM;;WAGN;WACA;WACA;;WAGA;WACA;;WAGA;WACA;;WAGA;WACA;;WAGA;WACA;WACA,gBAAgB;;WAGhB;WACA;WACA;WACA,MAAM;;WAGN;WACA;WACA;WACA;;WAGA;WACA;WACA;;WAGA;WACA;WACA;WACA;;WAGA;WACA;WACA;;WAGA;WACA;WACA;;WAGA;WACA;WACA;WACA;;;;KCxFH;WACD;WACA;WACA;WACA,QAAQ;WACR,MAAM;WACN,UAAU;WACV,UAAU;WACV,YAAY;EACrB;;;;KCTU,OAAO,WAAW,cAAc,gBAAgB;KAEvD;WACM;WACA;WACA;WACA;WACA,cAAc;WACd,SAAS;;KAGf;WACM;WACA;WACA;EACT;WACS,cAAc;WACd,SAAS;EAClB;;KAGG;WACM;WACA;WACA;WACA;WACA,cAAc;WACd,SAAS;;KAGf;WACM;WACA;WACA;WACA;WACA,cAAc;WACd,SAAS;;;;KCnBR;WAEG;WACA;WACA;;WAGA;WACA;;KAGH;WAEG;;WAGA;WACA;;KASH;WAEG;WACA,OAAO;;WAGP;WACA;;KAGH;WAEG;WACA,iBAAiB;;WAGjB;WACA;;KAGH;WACD;aACE;aACA;;;KAID;WAEG;WACA;;WAGA;WACA;;KAOH;WAEG;;WAGA;WACA;;KAGH;WAEG;;WAGA;WACA;;KASH;WAEG;;WAGA;WACA;;KAUH;WAEG;;WAGA;WACA;;KAGH;WAEG;;WAGA;WACA;;cAQF,gBAAgB,YAAY;EACvC,WAAW,gBAAgB,mBAAmB;EA4B9C,SAAS,gBAAgB,mBAAmB;EA8B5C,6BACE,gBACA,mBACC;EAgCH,8BACE,gBACA,kBACA,4BACC;EAgBH,oBAAoB;EAWpB,SAAS,gBAAgB,gBAAgB,mBAAmB;EA8B5D,UAAU,gBAAgB,mBAAmB;EAkC7C,SACE,gBACA,kBACA,gBACA,gCACC;EAwDH,sBACE,gBACA,mBACC;EAuFH,SAAS,gBAAgB,kBAAkB,eAAe;EAmB1D,UAAU,gBAAgB,mBAAmB"}
@@ -1,3 +1,4 @@
1
+ import { ManagerBase } from "@hellacardgames/lib";
1
2
  //#region src/manager/types/Card.d.ts
2
3
  type Card = {
3
4
  readonly id: string;
@@ -18,13 +19,13 @@ type ClientState = {
18
19
  readonly gameId: string;
19
20
  readonly playerId: string;
20
21
  readonly username: string;
21
- readonly players: readonly Player[];
22
+ readonly players: readonly Player$1[];
22
23
  readonly expiresAt: number;
23
24
  readonly chatMessages: readonly ChatMessage[];
24
25
  readonly hand: readonly Card[];
25
26
  readonly canPlayAt: number | null;
26
27
  };
27
- type Player = {
28
+ type Player$1 = {
28
29
  readonly username: string;
29
30
  readonly handSize: number;
30
31
  readonly drawPileSize: number;
@@ -107,6 +108,55 @@ type GameEvent = {
107
108
  readonly numCards: number;
108
109
  };
109
110
  //#endregion
111
+ //#region src/manager/types/Player.d.ts
112
+ type Player = {
113
+ readonly id: string;
114
+ readonly userId: string;
115
+ readonly username: string;
116
+ readonly events: GameEvent[];
117
+ readonly hand: Card[];
118
+ readonly drawPile: Card[];
119
+ readonly sidePile: Card[];
120
+ readonly centerPile: Card[];
121
+ hasNoPlayableCards: boolean;
122
+ };
123
+ //#endregion
124
+ //#region src/manager/types/Game.d.ts
125
+ type Game = OpenGame | StartedGame | CompletedGame | ForfeitedGame;
126
+ type OpenGame = {
127
+ readonly status: "open";
128
+ readonly id: string;
129
+ readonly createdAt: number;
130
+ readonly expiresAt: number;
131
+ readonly chatMessages: ChatMessage[];
132
+ readonly players: Player[];
133
+ };
134
+ type StartedGame = {
135
+ readonly status: "started";
136
+ readonly id: string;
137
+ readonly createdAt: number;
138
+ expiresAt: number;
139
+ readonly chatMessages: ChatMessage[];
140
+ readonly players: Player[];
141
+ canPlayAt: number;
142
+ };
143
+ type CompletedGame = {
144
+ readonly status: "completed";
145
+ readonly id: string;
146
+ readonly createdAt: number;
147
+ readonly expiresAt: number;
148
+ readonly chatMessages: ChatMessage[];
149
+ readonly players: Player[];
150
+ };
151
+ type ForfeitedGame = {
152
+ readonly status: "forfeited";
153
+ readonly id: string;
154
+ readonly createdAt: number;
155
+ readonly expiresAt: number;
156
+ readonly chatMessages: ChatMessage[];
157
+ readonly players: Player[];
158
+ };
159
+ //#endregion
110
160
  //#region src/manager/Manager.d.ts
111
161
  type CreateGameResult = {
112
162
  readonly success: true;
@@ -179,10 +229,7 @@ type StartGameResult = {
179
229
  readonly success: false;
180
230
  readonly error: "gameNotFound" | "invalidStatus" | "playerNotFound" | "playerNotAdmin" | "minPlayersNotReached";
181
231
  };
182
- declare class Manager {
183
- private readonly games;
184
- private readonly watchdog;
185
- constructor();
232
+ declare class Manager extends ManagerBase<Game> {
186
233
  createGame(userId: string, username: string): CreateGameResult;
187
234
  drawCard(gameId: string, playerId: string): DrawCardResult;
188
235
  getClientStateAndClearEvents(gameId: string, playerId: string): GetClientStateAndClearEventsResult;
@@ -197,4 +244,4 @@ declare class Manager {
197
244
  }
198
245
  //#endregion
199
246
  export { GetJoinableGamesResult as a, Manager as c, SendChatResult as d, StartGameResult as f, Card as g, ChatMessage as h, GetEventsAndClearAcknowledgedResult as i, PlayCardResult as l, ClientState as m, DrawCardResult as n, JoinGameResult as o, GameEvent as p, GetClientStateAndClearEventsResult as r, LeaveGameResult as s, CreateGameResult as t, ReportNoPlayableCardsResult as u };
200
- //# sourceMappingURL=index-YAYbNr-s.d.mts.map
247
+ //# sourceMappingURL=index-xmcja9Bl.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-xmcja9Bl.d.mts","names":[],"sources":["../src/manager/types/Card.ts","../src/manager/types/ChatMessage.ts","../src/manager/types/ClientState.ts","../src/manager/types/GameEvent.ts","../src/manager/types/Player.ts","../src/manager/types/Game.ts","../src/manager/Manager.ts"],"mappings":";;KAAY;WACD;WACA;WACA;;;;KCHC;WACD;WACA;WACA;;;;KCAC;WACD;WACA;WACA;WACA;WACA,kBAAkB;WAClB;WACA,uBAAuB;WACvB,eAAe;WACf;;KAGN;WACM;WACA;WACA;WACA;WACA,mBAAmB;;;;KCjBlB;WAEG;WACA;WACA;;WAGA;WACA;WACA;WACA,MAAM;;WAGN;WACA;WACA;WACA,MAAM;WACN;;WAGA;WACA;WACA,SAAS;;WAGT;WACA;WACA,MAAM;;WAGN;WACA;WACA;;WAGA;WACA;;WAGA;WACA;;WAGA;WACA;;WAGA;WACA;WACA,gBAAgB;;WAGhB;WACA;WACA;WACA,MAAM;;WAGN;WACA;WACA;WACA;;WAGA;WACA;WACA;;WAGA;WACA;WACA;WACA;;WAGA;WACA;WACA;;WAGA;WACA;WACA;;WAGA;WACA;WACA;WACA;;;;KCxFH;WACD;WACA;WACA;WACA,QAAQ;WACR,MAAM;WACN,UAAU;WACV,UAAU;WACV,YAAY;EACrB;;;;KCTU,OAAO,WAAW,cAAc,gBAAgB;KAEvD;WACM;WACA;WACA;WACA;WACA,cAAc;WACd,SAAS;;KAGf;WACM;WACA;WACA;EACT;WACS,cAAc;WACd,SAAS;EAClB;;KAGG;WACM;WACA;WACA;WACA;WACA,cAAc;WACd,SAAS;;KAGf;WACM;WACA;WACA;WACA;WACA,cAAc;WACd,SAAS;;;;KCnBR;WAEG;WACA;WACA;;WAGA;WACA;;KAGH;WAEG;;WAGA;WACA;;KASH;WAEG;WACA,OAAO;;WAGP;WACA;;KAGH;WAEG;WACA,iBAAiB;;WAGjB;WACA;;KAGH;WACD;aACE;aACA;;;KAID;WAEG;WACA;;WAGA;WACA;;KAOH;WAEG;;WAGA;WACA;;KAGH;WAEG;;WAGA;WACA;;KASH;WAEG;;WAGA;WACA;;KAUH;WAEG;;WAGA;WACA;;KAGH;WAEG;;WAGA;WACA;;cAQF,gBAAgB,YAAY;EACvC,WAAW,gBAAgB,mBAAmB;EA4B9C,SAAS,gBAAgB,mBAAmB;EA8B5C,6BACE,gBACA,mBACC;EAgCH,8BACE,gBACA,kBACA,4BACC;EAgBH,oBAAoB;EAWpB,SAAS,gBAAgB,gBAAgB,mBAAmB;EA8B5D,UAAU,gBAAgB,mBAAmB;EAkC7C,SACE,gBACA,kBACA,gBACA,gCACC;EAwDH,sBACE,gBACA,mBACC;EAuFH,SAAS,gBAAgB,kBAAkB,eAAe;EAmB1D,UAAU,gBAAgB,mBAAmB"}
@@ -1,3 +1,3 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_manager = require("../manager-BpKvQbcp.cjs");
2
+ const require_manager = require("../manager-BxLMVu_o.cjs");
3
3
  exports.Manager = require_manager.Manager;
@@ -1,2 +1,2 @@
1
- import { a as GetJoinableGamesResult, c as Manager, d as SendChatResult, f as StartGameResult, g as Card, h as ChatMessage, i as GetEventsAndClearAcknowledgedResult, l as PlayCardResult, m as ClientState, n as DrawCardResult, o as JoinGameResult, p as GameEvent, r as GetClientStateAndClearEventsResult, s as LeaveGameResult, t as CreateGameResult, u as ReportNoPlayableCardsResult } from "../index-YAYbNr-s.cjs";
1
+ import { a as GetJoinableGamesResult, c as Manager, d as SendChatResult, f as StartGameResult, g as Card, h as ChatMessage, i as GetEventsAndClearAcknowledgedResult, l as PlayCardResult, m as ClientState, n as DrawCardResult, o as JoinGameResult, p as GameEvent, r as GetClientStateAndClearEventsResult, s as LeaveGameResult, t as CreateGameResult, u as ReportNoPlayableCardsResult } from "../index-xmcja9Bl.cjs";
2
2
  export { type Card, type ChatMessage, type ClientState, type CreateGameResult, type DrawCardResult, type GameEvent, type GetClientStateAndClearEventsResult, type GetEventsAndClearAcknowledgedResult, type GetJoinableGamesResult, type JoinGameResult, type LeaveGameResult, Manager, type PlayCardResult, type ReportNoPlayableCardsResult, type SendChatResult, type StartGameResult };
@@ -1,2 +1,2 @@
1
- import { a as GetJoinableGamesResult, c as Manager, d as SendChatResult, f as StartGameResult, g as Card, h as ChatMessage, i as GetEventsAndClearAcknowledgedResult, l as PlayCardResult, m as ClientState, n as DrawCardResult, o as JoinGameResult, p as GameEvent, r as GetClientStateAndClearEventsResult, s as LeaveGameResult, t as CreateGameResult, u as ReportNoPlayableCardsResult } from "../index-YAYbNr-s.mjs";
1
+ import { a as GetJoinableGamesResult, c as Manager, d as SendChatResult, f as StartGameResult, g as Card, h as ChatMessage, i as GetEventsAndClearAcknowledgedResult, l as PlayCardResult, m as ClientState, n as DrawCardResult, o as JoinGameResult, p as GameEvent, r as GetClientStateAndClearEventsResult, s as LeaveGameResult, t as CreateGameResult, u as ReportNoPlayableCardsResult } from "../index-xmcja9Bl.mjs";
2
2
  export { type Card, type ChatMessage, type ClientState, type CreateGameResult, type DrawCardResult, type GameEvent, type GetClientStateAndClearEventsResult, type GetEventsAndClearAcknowledgedResult, type GetJoinableGamesResult, type JoinGameResult, type LeaveGameResult, Manager, type PlayCardResult, type ReportNoPlayableCardsResult, type SendChatResult, type StartGameResult };
@@ -1,2 +1,2 @@
1
- import { t as Manager } from "../manager-BOymBQ4E.mjs";
1
+ import { t as Manager } from "../manager-8x5AFyzq.mjs";
2
2
  export { Manager };
@@ -1,6 +1,5 @@
1
+ import { ManagerBase } from "@hellacardgames/lib";
1
2
  //#region src/manager/constants.ts
2
- const GAME_KEY = "speed";
3
- const WATCHDOG_INTERVAL_MS = 1e4;
4
3
  const EXPIRY_EXTENSION_MS = 3e5;
5
4
  const CAN_PLAY_AT_DELAY_MS = 3e3;
6
5
  const CARDS = [
@@ -266,25 +265,6 @@ const CARDS = [
266
265
  }
267
266
  ];
268
267
  //#endregion
269
- //#region src/manager/Watchdog.ts
270
- var Watchdog = class {
271
- games;
272
- constructor(games) {
273
- this.games = games;
274
- }
275
- start() {
276
- console.log(`watchdog start at ${Date.now()} (${GAME_KEY})`);
277
- setInterval(() => this.wakeUp(), WATCHDOG_INTERVAL_MS);
278
- }
279
- wakeUp() {
280
- const now = Date.now();
281
- for (const game of this.games.values()) if (game.expiresAt <= now) {
282
- this.games.delete(game.id);
283
- console.log(`watchdog purged ${game.id} (${GAME_KEY})`);
284
- }
285
- }
286
- };
287
- //#endregion
288
268
  //#region src/lib/emitEvent.ts
289
269
  function emitEvent(game, data) {
290
270
  const event = {
@@ -326,16 +306,9 @@ function shuffleCards(cards) {
326
306
  }
327
307
  //#endregion
328
308
  //#region src/manager/Manager.ts
329
- var Manager = class {
330
- games;
331
- watchdog;
332
- constructor() {
333
- this.games = /* @__PURE__ */ new Map();
334
- this.watchdog = new Watchdog(this.games);
335
- this.watchdog.start();
336
- }
309
+ var Manager = class extends ManagerBase {
337
310
  createGame(userId, username) {
338
- if (this.games.size === 50) return {
311
+ if (this.games.size === this.maxGames) return {
339
312
  success: false,
340
313
  error: "maxGamesReached"
341
314
  };
@@ -814,4 +787,4 @@ var Manager = class {
814
787
  //#endregion
815
788
  export { Manager as t };
816
789
 
817
- //# sourceMappingURL=manager-BOymBQ4E.mjs.map
790
+ //# sourceMappingURL=manager-8x5AFyzq.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager-8x5AFyzq.mjs","names":[],"sources":["../src/manager/constants.ts","../src/lib/emitEvent.ts","../src/lib/emitEventToPlayer.ts","../src/manager/lib/isCardPlayable.ts","../src/manager/lib/hasPlayableCard.ts","../src/lib/shuffleCards.ts","../src/manager/Manager.ts"],"sourcesContent":["import type { Card } from \"./types/Card.js\";\n\nexport const EXPIRY_EXTENSION_MS = 300000; // 5 minutes\nexport const MIN_PLAYERS = 2;\nexport const MAX_PLAYERS = 2;\nexport const MAX_HAND_SIZE = 5;\nexport const CAN_PLAY_AT_DELAY_MS = 3000;\n\nexport const CARDS: readonly Card[] = [\n { id: crypto.randomUUID(), rank: 2, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 3, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 4, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 5, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 6, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 7, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 8, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 9, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 10, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 11, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 12, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 13, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 14, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 2, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 3, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 4, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 5, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 6, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 7, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 8, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 9, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 10, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 11, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 12, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 13, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 14, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 2, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 3, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 4, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 5, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 6, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 7, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 8, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 9, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 10, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 11, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 12, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 13, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 14, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 2, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 3, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 4, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 5, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 6, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 7, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 8, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 9, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 10, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 11, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 12, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 13, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 14, suite: \"spades\" },\n];\n","type Game = {\n readonly players: {\n readonly events: { readonly id: string }[];\n }[];\n};\n\ntype OmitId<T> = T extends unknown ? Omit<T, \"id\"> : never;\n\nexport function emitEvent<T extends Game>(\n game: T,\n data: OmitId<T[\"players\"][number][\"events\"][number]>,\n): void {\n const event = { ...data, id: crypto.randomUUID() };\n for (const p of game.players) {\n p.events.push(event);\n }\n}\n","type Player = {\n readonly events: { readonly id: string }[];\n};\n\ntype OmitId<T> = T extends unknown ? Omit<T, \"id\"> : never;\n\nexport function emitEventToPlayer<T extends Player>(\n player: T,\n data: OmitId<T[\"events\"][number]>,\n): void {\n player.events.push({ ...data, id: crypto.randomUUID() });\n}\n","import type { Card } from \"../types/Card.js\";\n\nexport function isCardPlayable(card: Card, targetPile: Card[]) {\n const targetCard = targetPile[targetPile.length - 1]!;\n const diff = Math.abs(card.rank - targetCard.rank) % 11;\n return diff === 1;\n}\n","import { isCardPlayable } from \"./isCardPlayable.js\";\nimport type { Game } from \"../types/Game.js\";\nimport type { Player } from \"../types/Player.js\";\n\nexport function hasPlayableCard(player: Player, game: Game) {\n const otherPlayer = game.players.find((p) => p !== player)!;\n const targetPile1 = player.centerPile;\n const targetPile2 = otherPlayer.centerPile;\n for (const card of player.hand) {\n if (\n isCardPlayable(card, targetPile1) ||\n isCardPlayable(card, targetPile2)\n ) {\n return true;\n }\n }\n return false;\n}\n","type Card = { readonly id: string };\n\nexport function shuffleCards<T extends Card>(cards: T[]): void {\n for (let i = cards.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [cards[i], cards[j]] = [cards[j]!, cards[i]!];\n }\n}\n","import { ManagerBase } from \"@hellacardgames/lib\";\nimport {\n CAN_PLAY_AT_DELAY_MS,\n CARDS,\n EXPIRY_EXTENSION_MS,\n MAX_HAND_SIZE,\n MAX_PLAYERS,\n MIN_PLAYERS,\n} from \"./constants.js\";\nimport { emitEvent } from \"../lib/emitEvent.js\";\nimport { emitEventToPlayer } from \"../lib/emitEventToPlayer.js\";\nimport { isCardPlayable } from \"./lib/isCardPlayable.js\";\nimport { hasPlayableCard } from \"./lib/hasPlayableCard.js\";\nimport { shuffleCards } from \"../lib/shuffleCards.js\";\nimport type { ChatMessage } from \"./types/ChatMessage.js\";\nimport type { ClientState } from \"./types/ClientState.js\";\nimport type { Game } from \"./types/Game.js\";\nimport type { GameEvent } from \"./types/GameEvent.js\";\nimport type { Player } from \"./types/Player.js\";\n\nexport type CreateGameResult =\n | {\n readonly success: true;\n readonly gameId: string;\n readonly playerId: string;\n }\n | {\n readonly success: false;\n readonly error: \"maxGamesReached\";\n };\n\nexport type DrawCardResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"gameNotFound\"\n | \"playerNotFound\"\n | \"invalidStatus\"\n | \"canPlayAtNotReached\"\n | \"handFull\"\n | \"drawPileEmpty\";\n };\n\nexport type GetClientStateAndClearEventsResult =\n | {\n readonly success: true;\n readonly state: ClientState;\n }\n | {\n readonly success: false;\n readonly error: \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type GetEventsAndClearAcknowledgedResult =\n | {\n readonly success: true;\n readonly events: readonly GameEvent[];\n }\n | {\n readonly success: false;\n readonly error: \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type GetJoinableGamesResult = {\n readonly games: readonly {\n readonly id: string;\n readonly numPlayers: number;\n }[];\n};\n\nexport type JoinGameResult =\n | {\n readonly success: true;\n readonly playerId: string;\n }\n | {\n readonly success: false;\n readonly error:\n | \"gameNotFound\"\n | \"invalidStatus\"\n | \"maxPlayersReached\"\n | \"alreadyInGame\";\n };\n\nexport type LeaveGameResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error: \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type PlayCardResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"gameNotFound\"\n | \"playerNotFound\"\n | \"invalidStatus\"\n | \"canPlayAtNotReached\"\n | \"cardNotFound\"\n | \"cardNotPlayable\";\n };\n\nexport type ReportNoPlayableCardsResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"gameNotFound\"\n | \"playerNotFound\"\n | \"invalidStatus\"\n | \"canPlayAtNotReached\"\n | \"alreadyReported\"\n | \"hasPlayableCard\"\n | \"canDraw\";\n };\n\nexport type SendChatResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error: \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type StartGameResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"gameNotFound\"\n | \"invalidStatus\"\n | \"playerNotFound\"\n | \"playerNotAdmin\"\n | \"minPlayersNotReached\";\n };\n\nexport class Manager extends ManagerBase<Game> {\n createGame(userId: string, username: string): CreateGameResult {\n if (this.games.size === this.maxGames) {\n return { success: false, error: \"maxGamesReached\" };\n }\n const player: Player = {\n id: crypto.randomUUID(),\n userId,\n username,\n events: [],\n hand: [],\n drawPile: [],\n sidePile: [],\n centerPile: [],\n hasNoPlayableCards: false,\n };\n const createdAt = Date.now();\n const game: Game = {\n status: \"open\",\n id: crypto.randomUUID(),\n createdAt,\n expiresAt: createdAt + EXPIRY_EXTENSION_MS,\n chatMessages: [],\n players: [player],\n };\n this.games.set(game.id, game);\n return { success: true, gameId: game.id, playerId: player.id };\n }\n\n drawCard(gameId: string, playerId: string): DrawCardResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n if (game.status !== \"started\") {\n return { success: false, error: \"invalidStatus\" };\n }\n if (Date.now() < game.canPlayAt) {\n return { success: false, error: \"canPlayAtNotReached\" };\n }\n if (player.hand.length >= MAX_HAND_SIZE) {\n return { success: false, error: \"handFull\" };\n }\n if (player.drawPile.length === 0) {\n return { success: false, error: \"drawPileEmpty\" };\n }\n game.expiresAt = Date.now() + EXPIRY_EXTENSION_MS;\n emitEvent(game, { type: \"expirationUpdated\", expiresAt: game.expiresAt });\n const card = player.drawPile.pop()!;\n player.hand.push(card);\n emitEventToPlayer(player, { type: \"drewCard\", card });\n emitEvent(game, { type: \"playerDrewCard\", username: player.username });\n return { success: true };\n }\n\n getClientStateAndClearEvents(\n gameId: string,\n playerId: string,\n ): GetClientStateAndClearEventsResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n const state: ClientState = {\n status: game.status,\n gameId,\n playerId,\n username: player.username,\n players: game.players.map((p) => ({\n username: p.username,\n handSize: p.hand.length,\n drawPileSize: p.drawPile.length,\n sidePileSize: p.sidePile.length,\n centerPileTopCard: p.centerPile.length\n ? p.centerPile[p.centerPile.length - 1]!\n : null,\n })),\n expiresAt: game.expiresAt,\n chatMessages: game.chatMessages,\n hand: player.hand,\n canPlayAt: game.status === \"started\" ? game.canPlayAt : null,\n };\n player.events.length = 0;\n return { success: true, state };\n }\n\n getEventsAndClearAcknowledged(\n gameId: string,\n playerId: string,\n lastReadId: string | null,\n ): GetEventsAndClearAcknowledgedResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n const lastReadEventIndex = player.events.findIndex(\n (e) => e.id === lastReadId,\n );\n player.events.splice(0, lastReadEventIndex + 1);\n return { success: true, events: player.events };\n }\n\n getJoinableGames(): GetJoinableGamesResult {\n return {\n games: Array.from(this.games.values())\n .filter((g) => g.status === \"open\" && g.players.length < MAX_PLAYERS)\n .map((g) => ({\n id: g.id,\n numPlayers: g.players.length,\n })),\n };\n }\n\n joinGame(gameId: string, userId: string, username: string): JoinGameResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n if (game.status !== \"open\") {\n return { success: false, error: \"invalidStatus\" };\n }\n if (game.players.length === MAX_PLAYERS) {\n return { success: false, error: \"maxPlayersReached\" };\n }\n if (game.players.find((p) => p.userId === userId)) {\n return { success: false, error: \"alreadyInGame\" };\n }\n const player: Player = {\n id: crypto.randomUUID(),\n userId,\n username,\n events: [],\n hand: [],\n drawPile: [],\n sidePile: [],\n centerPile: [],\n hasNoPlayableCards: false,\n };\n game.players.push(player);\n emitEvent(game, { type: \"playerJoined\", username });\n return { success: true, playerId: player.id };\n }\n\n leaveGame(gameId: string, playerId: string): LeaveGameResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const playerIndex = game.players.findIndex((p) => p.id === playerId);\n if (playerIndex === -1) {\n return { success: false, error: \"playerNotFound\" };\n }\n\n const player = game.players[playerIndex]!;\n emitEvent(game, { type: \"playerLeft\", username: player.username });\n\n game.players.splice(playerIndex, 1);\n\n if (game.status === \"started\" && game.players.length < MIN_PLAYERS) {\n const forfeitedGame: Game = {\n ...game,\n status: \"forfeited\",\n expiresAt: Date.now() + EXPIRY_EXTENSION_MS,\n };\n this.games.set(game.id, forfeitedGame);\n emitEvent(forfeitedGame, { type: \"gameForfeited\" });\n emitEvent(forfeitedGame, {\n type: \"expirationUpdated\",\n expiresAt: forfeitedGame.expiresAt,\n });\n }\n if (game.players.length === 0) {\n this.games.delete(game.id);\n }\n return { success: true };\n }\n\n playCard(\n gameId: string,\n playerId: string,\n cardId: string,\n isForOtherPlayerPile: boolean,\n ): PlayCardResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n if (game.status !== \"started\") {\n return { success: false, error: \"invalidStatus\" };\n }\n if (Date.now() < game.canPlayAt) {\n return { success: false, error: \"canPlayAtNotReached\" };\n }\n const cardIndex = player.hand.findIndex((c) => c.id === cardId);\n if (cardIndex === -1) {\n return { success: false, error: \"cardNotFound\" };\n }\n const otherPlayer = game.players.find((p) => p !== player)!;\n const targetPile = isForOtherPlayerPile\n ? otherPlayer.centerPile\n : player.centerPile;\n const card = player.hand[cardIndex]!;\n if (!isCardPlayable(card, targetPile)) {\n return { success: false, error: \"cardNotPlayable\" };\n }\n\n game.expiresAt = Date.now() + EXPIRY_EXTENSION_MS;\n emitEvent(game, { type: \"expirationUpdated\", expiresAt: game.expiresAt });\n\n player.hand.splice(cardIndex, 1);\n targetPile.push(card);\n emitEvent(game, {\n type: \"cardPlayed\",\n username: player.username,\n card,\n isForOtherPlayerPile,\n });\n\n if (otherPlayer.hasNoPlayableCards && hasPlayableCard(otherPlayer, game)) {\n otherPlayer.hasNoPlayableCards = false;\n }\n\n if (player.hand.length === 0 && player.drawPile.length === 0) {\n emitEvent(game, { type: \"gameCompleted\" });\n const completedGame: Game = {\n ...game,\n status: \"completed\",\n };\n this.games.set(game.id, completedGame);\n }\n\n return { success: true };\n }\n\n reportNoPlayableCards(\n gameId: string,\n playerId: string,\n ): ReportNoPlayableCardsResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n if (game.status !== \"started\") {\n return { success: false, error: \"invalidStatus\" };\n }\n if (Date.now() < game.canPlayAt) {\n return { success: false, error: \"canPlayAtNotReached\" };\n }\n if (player.hasNoPlayableCards) {\n return { success: false, error: \"alreadyReported\" };\n }\n if (hasPlayableCard(player, game)) {\n return { success: false, error: \"hasPlayableCard\" };\n }\n if (player.drawPile.length > 0 && player.hand.length < MAX_HAND_SIZE) {\n return { success: false, error: \"canDraw\" };\n }\n\n game.expiresAt = Date.now() + EXPIRY_EXTENSION_MS;\n emitEvent(game, { type: \"expirationUpdated\", expiresAt: game.expiresAt });\n\n player.hasNoPlayableCards = true;\n\n const otherPlayer = game.players.find((p) => p !== player)!;\n if (otherPlayer.hasNoPlayableCards) {\n if (player.sidePile.length === 0 && otherPlayer.sidePile.length === 0) {\n const cards = [...player.centerPile, ...otherPlayer.centerPile];\n shuffleCards(cards);\n player.sidePile.push(...cards.splice(0, 5));\n emitEvent(game, {\n type: \"playerSidePileInitialized\",\n username: player.username,\n numCards: player.sidePile.length,\n });\n player.centerPile.push(...cards.splice(0, 1));\n emitEvent(game, {\n type: \"playerCenterPileInitialized\",\n username: player.username,\n card: player.centerPile[player.centerPile.length - 1]!,\n });\n otherPlayer.centerPile.push(...cards.splice(0, 1));\n emitEvent(game, {\n type: \"playerCenterPileInitialized\",\n username: otherPlayer.username,\n card: otherPlayer.centerPile[otherPlayer.centerPile.length - 1]!,\n });\n otherPlayer.sidePile.push(...cards.splice(0, 5));\n emitEvent(game, {\n type: \"playerSidePileInitialized\",\n username: otherPlayer.username,\n numCards: otherPlayer.sidePile.length,\n });\n } else {\n const playerSidePileCard = player.sidePile.pop()!;\n player.centerPile.push(playerSidePileCard);\n emitEvent(game, {\n type: \"cardDrawnFromSidePileToCenterPile\",\n username: player.username,\n card: playerSidePileCard,\n });\n const otherPlayerSidePileCard = otherPlayer.sidePile.pop()!;\n otherPlayer.centerPile.push(otherPlayerSidePileCard);\n emitEvent(game, {\n type: \"cardDrawnFromSidePileToCenterPile\",\n username: otherPlayer.username,\n card: otherPlayerSidePileCard,\n });\n }\n player.hasNoPlayableCards = false;\n otherPlayer.hasNoPlayableCards = false;\n game.canPlayAt = Date.now() + CAN_PLAY_AT_DELAY_MS;\n emitEvent(game, {\n type: \"canPlayAtUpdated\",\n canPlayAt: game.canPlayAt,\n });\n }\n\n return { success: true };\n }\n\n sendChat(gameId: string, playerId: string, text: string): SendChatResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n const message: ChatMessage = {\n id: crypto.randomUUID(),\n username: player.username,\n text,\n };\n game.chatMessages.push(message);\n emitEvent(game, { type: \"chat\", message });\n return { success: true };\n }\n\n startGame(gameId: string, playerId: string): StartGameResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n if (game.status !== \"open\") {\n return { success: false, error: \"invalidStatus\" };\n }\n if (game.players.indexOf(player) !== 0) {\n return { success: false, error: \"playerNotAdmin\" };\n }\n if (game.players.length < MIN_PLAYERS) {\n return { success: false, error: \"minPlayersNotReached\" };\n }\n const otherPlayer = game.players[1]!;\n const cards = [...CARDS];\n shuffleCards(cards);\n\n player.sidePile.push(...cards.splice(0, 5));\n emitEvent(game, {\n type: \"playerSidePileInitialized\",\n username: player.username,\n numCards: player.sidePile.length,\n });\n\n player.centerPile.push(...cards.splice(0, 1));\n emitEvent(game, {\n type: \"playerCenterPileInitialized\",\n username: player.username,\n card: player.centerPile[player.centerPile.length - 1]!,\n });\n\n otherPlayer.centerPile.push(...cards.splice(0, 1));\n emitEvent(game, {\n type: \"playerCenterPileInitialized\",\n username: otherPlayer.username,\n card: otherPlayer.centerPile[otherPlayer.centerPile.length - 1]!,\n });\n\n otherPlayer.sidePile.push(...cards.splice(0, 5));\n emitEvent(game, {\n type: \"playerSidePileInitialized\",\n username: otherPlayer.username,\n numCards: otherPlayer.sidePile.length,\n });\n\n player.drawPile.push(...cards.splice(0, 15));\n emitEvent(game, {\n type: \"playerDrawPileInitialized\",\n username: player.username,\n numCards: player.drawPile.length,\n });\n\n player.hand.push(...cards.splice(0, 5));\n emitEventToPlayer(player, { type: \"handInitialized\", cards: player.hand });\n emitEvent(game, {\n type: \"playerHandInitialized\",\n username: player.username,\n numCards: player.hand.length,\n });\n\n otherPlayer.drawPile.push(...cards.splice(0, 15));\n emitEvent(game, {\n type: \"playerDrawPileInitialized\",\n username: otherPlayer.username,\n numCards: otherPlayer.drawPile.length,\n });\n\n otherPlayer.hand.push(...cards.splice(0, 5));\n emitEventToPlayer(otherPlayer, {\n type: \"handInitialized\",\n cards: otherPlayer.hand,\n });\n emitEvent(game, {\n type: \"playerHandInitialized\",\n username: otherPlayer.username,\n numCards: otherPlayer.hand.length,\n });\n\n const startedGame: Game = {\n ...game,\n status: \"started\",\n expiresAt: Date.now() + EXPIRY_EXTENSION_MS,\n canPlayAt: Date.now() + CAN_PLAY_AT_DELAY_MS,\n };\n this.games.set(game.id, startedGame);\n emitEvent(startedGame, { type: \"gameStarted\" });\n emitEvent(startedGame, {\n type: \"expirationUpdated\",\n expiresAt: startedGame.expiresAt,\n });\n emitEvent(startedGame, {\n type: \"canPlayAtUpdated\",\n canPlayAt: startedGame.canPlayAt,\n });\n return { success: true };\n }\n}\n"],"mappings":"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"}
@@ -1,6 +1,5 @@
1
+ let _hellacardgames_lib = require("@hellacardgames/lib");
1
2
  //#region src/manager/constants.ts
2
- const GAME_KEY = "speed";
3
- const WATCHDOG_INTERVAL_MS = 1e4;
4
3
  const EXPIRY_EXTENSION_MS = 3e5;
5
4
  const CAN_PLAY_AT_DELAY_MS = 3e3;
6
5
  const CARDS = [
@@ -266,25 +265,6 @@ const CARDS = [
266
265
  }
267
266
  ];
268
267
  //#endregion
269
- //#region src/manager/Watchdog.ts
270
- var Watchdog = class {
271
- games;
272
- constructor(games) {
273
- this.games = games;
274
- }
275
- start() {
276
- console.log(`watchdog start at ${Date.now()} (${GAME_KEY})`);
277
- setInterval(() => this.wakeUp(), WATCHDOG_INTERVAL_MS);
278
- }
279
- wakeUp() {
280
- const now = Date.now();
281
- for (const game of this.games.values()) if (game.expiresAt <= now) {
282
- this.games.delete(game.id);
283
- console.log(`watchdog purged ${game.id} (${GAME_KEY})`);
284
- }
285
- }
286
- };
287
- //#endregion
288
268
  //#region src/lib/emitEvent.ts
289
269
  function emitEvent(game, data) {
290
270
  const event = {
@@ -326,16 +306,9 @@ function shuffleCards(cards) {
326
306
  }
327
307
  //#endregion
328
308
  //#region src/manager/Manager.ts
329
- var Manager = class {
330
- games;
331
- watchdog;
332
- constructor() {
333
- this.games = /* @__PURE__ */ new Map();
334
- this.watchdog = new Watchdog(this.games);
335
- this.watchdog.start();
336
- }
309
+ var Manager = class extends _hellacardgames_lib.ManagerBase {
337
310
  createGame(userId, username) {
338
- if (this.games.size === 50) return {
311
+ if (this.games.size === this.maxGames) return {
339
312
  success: false,
340
313
  error: "maxGamesReached"
341
314
  };
@@ -819,4 +792,4 @@ Object.defineProperty(exports, "Manager", {
819
792
  }
820
793
  });
821
794
 
822
- //# sourceMappingURL=manager-BpKvQbcp.cjs.map
795
+ //# sourceMappingURL=manager-BxLMVu_o.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager-BxLMVu_o.cjs","names":["ManagerBase"],"sources":["../src/manager/constants.ts","../src/lib/emitEvent.ts","../src/lib/emitEventToPlayer.ts","../src/manager/lib/isCardPlayable.ts","../src/manager/lib/hasPlayableCard.ts","../src/lib/shuffleCards.ts","../src/manager/Manager.ts"],"sourcesContent":["import type { Card } from \"./types/Card.js\";\n\nexport const EXPIRY_EXTENSION_MS = 300000; // 5 minutes\nexport const MIN_PLAYERS = 2;\nexport const MAX_PLAYERS = 2;\nexport const MAX_HAND_SIZE = 5;\nexport const CAN_PLAY_AT_DELAY_MS = 3000;\n\nexport const CARDS: readonly Card[] = [\n { id: crypto.randomUUID(), rank: 2, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 3, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 4, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 5, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 6, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 7, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 8, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 9, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 10, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 11, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 12, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 13, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 14, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 2, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 3, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 4, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 5, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 6, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 7, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 8, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 9, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 10, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 11, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 12, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 13, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 14, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 2, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 3, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 4, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 5, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 6, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 7, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 8, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 9, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 10, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 11, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 12, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 13, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 14, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 2, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 3, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 4, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 5, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 6, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 7, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 8, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 9, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 10, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 11, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 12, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 13, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 14, suite: \"spades\" },\n];\n","type Game = {\n readonly players: {\n readonly events: { readonly id: string }[];\n }[];\n};\n\ntype OmitId<T> = T extends unknown ? Omit<T, \"id\"> : never;\n\nexport function emitEvent<T extends Game>(\n game: T,\n data: OmitId<T[\"players\"][number][\"events\"][number]>,\n): void {\n const event = { ...data, id: crypto.randomUUID() };\n for (const p of game.players) {\n p.events.push(event);\n }\n}\n","type Player = {\n readonly events: { readonly id: string }[];\n};\n\ntype OmitId<T> = T extends unknown ? Omit<T, \"id\"> : never;\n\nexport function emitEventToPlayer<T extends Player>(\n player: T,\n data: OmitId<T[\"events\"][number]>,\n): void {\n player.events.push({ ...data, id: crypto.randomUUID() });\n}\n","import type { Card } from \"../types/Card.js\";\n\nexport function isCardPlayable(card: Card, targetPile: Card[]) {\n const targetCard = targetPile[targetPile.length - 1]!;\n const diff = Math.abs(card.rank - targetCard.rank) % 11;\n return diff === 1;\n}\n","import { isCardPlayable } from \"./isCardPlayable.js\";\nimport type { Game } from \"../types/Game.js\";\nimport type { Player } from \"../types/Player.js\";\n\nexport function hasPlayableCard(player: Player, game: Game) {\n const otherPlayer = game.players.find((p) => p !== player)!;\n const targetPile1 = player.centerPile;\n const targetPile2 = otherPlayer.centerPile;\n for (const card of player.hand) {\n if (\n isCardPlayable(card, targetPile1) ||\n isCardPlayable(card, targetPile2)\n ) {\n return true;\n }\n }\n return false;\n}\n","type Card = { readonly id: string };\n\nexport function shuffleCards<T extends Card>(cards: T[]): void {\n for (let i = cards.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [cards[i], cards[j]] = [cards[j]!, cards[i]!];\n }\n}\n","import { ManagerBase } from \"@hellacardgames/lib\";\nimport {\n CAN_PLAY_AT_DELAY_MS,\n CARDS,\n EXPIRY_EXTENSION_MS,\n MAX_HAND_SIZE,\n MAX_PLAYERS,\n MIN_PLAYERS,\n} from \"./constants.js\";\nimport { emitEvent } from \"../lib/emitEvent.js\";\nimport { emitEventToPlayer } from \"../lib/emitEventToPlayer.js\";\nimport { isCardPlayable } from \"./lib/isCardPlayable.js\";\nimport { hasPlayableCard } from \"./lib/hasPlayableCard.js\";\nimport { shuffleCards } from \"../lib/shuffleCards.js\";\nimport type { ChatMessage } from \"./types/ChatMessage.js\";\nimport type { ClientState } from \"./types/ClientState.js\";\nimport type { Game } from \"./types/Game.js\";\nimport type { GameEvent } from \"./types/GameEvent.js\";\nimport type { Player } from \"./types/Player.js\";\n\nexport type CreateGameResult =\n | {\n readonly success: true;\n readonly gameId: string;\n readonly playerId: string;\n }\n | {\n readonly success: false;\n readonly error: \"maxGamesReached\";\n };\n\nexport type DrawCardResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"gameNotFound\"\n | \"playerNotFound\"\n | \"invalidStatus\"\n | \"canPlayAtNotReached\"\n | \"handFull\"\n | \"drawPileEmpty\";\n };\n\nexport type GetClientStateAndClearEventsResult =\n | {\n readonly success: true;\n readonly state: ClientState;\n }\n | {\n readonly success: false;\n readonly error: \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type GetEventsAndClearAcknowledgedResult =\n | {\n readonly success: true;\n readonly events: readonly GameEvent[];\n }\n | {\n readonly success: false;\n readonly error: \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type GetJoinableGamesResult = {\n readonly games: readonly {\n readonly id: string;\n readonly numPlayers: number;\n }[];\n};\n\nexport type JoinGameResult =\n | {\n readonly success: true;\n readonly playerId: string;\n }\n | {\n readonly success: false;\n readonly error:\n | \"gameNotFound\"\n | \"invalidStatus\"\n | \"maxPlayersReached\"\n | \"alreadyInGame\";\n };\n\nexport type LeaveGameResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error: \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type PlayCardResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"gameNotFound\"\n | \"playerNotFound\"\n | \"invalidStatus\"\n | \"canPlayAtNotReached\"\n | \"cardNotFound\"\n | \"cardNotPlayable\";\n };\n\nexport type ReportNoPlayableCardsResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"gameNotFound\"\n | \"playerNotFound\"\n | \"invalidStatus\"\n | \"canPlayAtNotReached\"\n | \"alreadyReported\"\n | \"hasPlayableCard\"\n | \"canDraw\";\n };\n\nexport type SendChatResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error: \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type StartGameResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"gameNotFound\"\n | \"invalidStatus\"\n | \"playerNotFound\"\n | \"playerNotAdmin\"\n | \"minPlayersNotReached\";\n };\n\nexport class Manager extends ManagerBase<Game> {\n createGame(userId: string, username: string): CreateGameResult {\n if (this.games.size === this.maxGames) {\n return { success: false, error: \"maxGamesReached\" };\n }\n const player: Player = {\n id: crypto.randomUUID(),\n userId,\n username,\n events: [],\n hand: [],\n drawPile: [],\n sidePile: [],\n centerPile: [],\n hasNoPlayableCards: false,\n };\n const createdAt = Date.now();\n const game: Game = {\n status: \"open\",\n id: crypto.randomUUID(),\n createdAt,\n expiresAt: createdAt + EXPIRY_EXTENSION_MS,\n chatMessages: [],\n players: [player],\n };\n this.games.set(game.id, game);\n return { success: true, gameId: game.id, playerId: player.id };\n }\n\n drawCard(gameId: string, playerId: string): DrawCardResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n if (game.status !== \"started\") {\n return { success: false, error: \"invalidStatus\" };\n }\n if (Date.now() < game.canPlayAt) {\n return { success: false, error: \"canPlayAtNotReached\" };\n }\n if (player.hand.length >= MAX_HAND_SIZE) {\n return { success: false, error: \"handFull\" };\n }\n if (player.drawPile.length === 0) {\n return { success: false, error: \"drawPileEmpty\" };\n }\n game.expiresAt = Date.now() + EXPIRY_EXTENSION_MS;\n emitEvent(game, { type: \"expirationUpdated\", expiresAt: game.expiresAt });\n const card = player.drawPile.pop()!;\n player.hand.push(card);\n emitEventToPlayer(player, { type: \"drewCard\", card });\n emitEvent(game, { type: \"playerDrewCard\", username: player.username });\n return { success: true };\n }\n\n getClientStateAndClearEvents(\n gameId: string,\n playerId: string,\n ): GetClientStateAndClearEventsResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n const state: ClientState = {\n status: game.status,\n gameId,\n playerId,\n username: player.username,\n players: game.players.map((p) => ({\n username: p.username,\n handSize: p.hand.length,\n drawPileSize: p.drawPile.length,\n sidePileSize: p.sidePile.length,\n centerPileTopCard: p.centerPile.length\n ? p.centerPile[p.centerPile.length - 1]!\n : null,\n })),\n expiresAt: game.expiresAt,\n chatMessages: game.chatMessages,\n hand: player.hand,\n canPlayAt: game.status === \"started\" ? game.canPlayAt : null,\n };\n player.events.length = 0;\n return { success: true, state };\n }\n\n getEventsAndClearAcknowledged(\n gameId: string,\n playerId: string,\n lastReadId: string | null,\n ): GetEventsAndClearAcknowledgedResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n const lastReadEventIndex = player.events.findIndex(\n (e) => e.id === lastReadId,\n );\n player.events.splice(0, lastReadEventIndex + 1);\n return { success: true, events: player.events };\n }\n\n getJoinableGames(): GetJoinableGamesResult {\n return {\n games: Array.from(this.games.values())\n .filter((g) => g.status === \"open\" && g.players.length < MAX_PLAYERS)\n .map((g) => ({\n id: g.id,\n numPlayers: g.players.length,\n })),\n };\n }\n\n joinGame(gameId: string, userId: string, username: string): JoinGameResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n if (game.status !== \"open\") {\n return { success: false, error: \"invalidStatus\" };\n }\n if (game.players.length === MAX_PLAYERS) {\n return { success: false, error: \"maxPlayersReached\" };\n }\n if (game.players.find((p) => p.userId === userId)) {\n return { success: false, error: \"alreadyInGame\" };\n }\n const player: Player = {\n id: crypto.randomUUID(),\n userId,\n username,\n events: [],\n hand: [],\n drawPile: [],\n sidePile: [],\n centerPile: [],\n hasNoPlayableCards: false,\n };\n game.players.push(player);\n emitEvent(game, { type: \"playerJoined\", username });\n return { success: true, playerId: player.id };\n }\n\n leaveGame(gameId: string, playerId: string): LeaveGameResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const playerIndex = game.players.findIndex((p) => p.id === playerId);\n if (playerIndex === -1) {\n return { success: false, error: \"playerNotFound\" };\n }\n\n const player = game.players[playerIndex]!;\n emitEvent(game, { type: \"playerLeft\", username: player.username });\n\n game.players.splice(playerIndex, 1);\n\n if (game.status === \"started\" && game.players.length < MIN_PLAYERS) {\n const forfeitedGame: Game = {\n ...game,\n status: \"forfeited\",\n expiresAt: Date.now() + EXPIRY_EXTENSION_MS,\n };\n this.games.set(game.id, forfeitedGame);\n emitEvent(forfeitedGame, { type: \"gameForfeited\" });\n emitEvent(forfeitedGame, {\n type: \"expirationUpdated\",\n expiresAt: forfeitedGame.expiresAt,\n });\n }\n if (game.players.length === 0) {\n this.games.delete(game.id);\n }\n return { success: true };\n }\n\n playCard(\n gameId: string,\n playerId: string,\n cardId: string,\n isForOtherPlayerPile: boolean,\n ): PlayCardResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n if (game.status !== \"started\") {\n return { success: false, error: \"invalidStatus\" };\n }\n if (Date.now() < game.canPlayAt) {\n return { success: false, error: \"canPlayAtNotReached\" };\n }\n const cardIndex = player.hand.findIndex((c) => c.id === cardId);\n if (cardIndex === -1) {\n return { success: false, error: \"cardNotFound\" };\n }\n const otherPlayer = game.players.find((p) => p !== player)!;\n const targetPile = isForOtherPlayerPile\n ? otherPlayer.centerPile\n : player.centerPile;\n const card = player.hand[cardIndex]!;\n if (!isCardPlayable(card, targetPile)) {\n return { success: false, error: \"cardNotPlayable\" };\n }\n\n game.expiresAt = Date.now() + EXPIRY_EXTENSION_MS;\n emitEvent(game, { type: \"expirationUpdated\", expiresAt: game.expiresAt });\n\n player.hand.splice(cardIndex, 1);\n targetPile.push(card);\n emitEvent(game, {\n type: \"cardPlayed\",\n username: player.username,\n card,\n isForOtherPlayerPile,\n });\n\n if (otherPlayer.hasNoPlayableCards && hasPlayableCard(otherPlayer, game)) {\n otherPlayer.hasNoPlayableCards = false;\n }\n\n if (player.hand.length === 0 && player.drawPile.length === 0) {\n emitEvent(game, { type: \"gameCompleted\" });\n const completedGame: Game = {\n ...game,\n status: \"completed\",\n };\n this.games.set(game.id, completedGame);\n }\n\n return { success: true };\n }\n\n reportNoPlayableCards(\n gameId: string,\n playerId: string,\n ): ReportNoPlayableCardsResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n if (game.status !== \"started\") {\n return { success: false, error: \"invalidStatus\" };\n }\n if (Date.now() < game.canPlayAt) {\n return { success: false, error: \"canPlayAtNotReached\" };\n }\n if (player.hasNoPlayableCards) {\n return { success: false, error: \"alreadyReported\" };\n }\n if (hasPlayableCard(player, game)) {\n return { success: false, error: \"hasPlayableCard\" };\n }\n if (player.drawPile.length > 0 && player.hand.length < MAX_HAND_SIZE) {\n return { success: false, error: \"canDraw\" };\n }\n\n game.expiresAt = Date.now() + EXPIRY_EXTENSION_MS;\n emitEvent(game, { type: \"expirationUpdated\", expiresAt: game.expiresAt });\n\n player.hasNoPlayableCards = true;\n\n const otherPlayer = game.players.find((p) => p !== player)!;\n if (otherPlayer.hasNoPlayableCards) {\n if (player.sidePile.length === 0 && otherPlayer.sidePile.length === 0) {\n const cards = [...player.centerPile, ...otherPlayer.centerPile];\n shuffleCards(cards);\n player.sidePile.push(...cards.splice(0, 5));\n emitEvent(game, {\n type: \"playerSidePileInitialized\",\n username: player.username,\n numCards: player.sidePile.length,\n });\n player.centerPile.push(...cards.splice(0, 1));\n emitEvent(game, {\n type: \"playerCenterPileInitialized\",\n username: player.username,\n card: player.centerPile[player.centerPile.length - 1]!,\n });\n otherPlayer.centerPile.push(...cards.splice(0, 1));\n emitEvent(game, {\n type: \"playerCenterPileInitialized\",\n username: otherPlayer.username,\n card: otherPlayer.centerPile[otherPlayer.centerPile.length - 1]!,\n });\n otherPlayer.sidePile.push(...cards.splice(0, 5));\n emitEvent(game, {\n type: \"playerSidePileInitialized\",\n username: otherPlayer.username,\n numCards: otherPlayer.sidePile.length,\n });\n } else {\n const playerSidePileCard = player.sidePile.pop()!;\n player.centerPile.push(playerSidePileCard);\n emitEvent(game, {\n type: \"cardDrawnFromSidePileToCenterPile\",\n username: player.username,\n card: playerSidePileCard,\n });\n const otherPlayerSidePileCard = otherPlayer.sidePile.pop()!;\n otherPlayer.centerPile.push(otherPlayerSidePileCard);\n emitEvent(game, {\n type: \"cardDrawnFromSidePileToCenterPile\",\n username: otherPlayer.username,\n card: otherPlayerSidePileCard,\n });\n }\n player.hasNoPlayableCards = false;\n otherPlayer.hasNoPlayableCards = false;\n game.canPlayAt = Date.now() + CAN_PLAY_AT_DELAY_MS;\n emitEvent(game, {\n type: \"canPlayAtUpdated\",\n canPlayAt: game.canPlayAt,\n });\n }\n\n return { success: true };\n }\n\n sendChat(gameId: string, playerId: string, text: string): SendChatResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n const message: ChatMessage = {\n id: crypto.randomUUID(),\n username: player.username,\n text,\n };\n game.chatMessages.push(message);\n emitEvent(game, { type: \"chat\", message });\n return { success: true };\n }\n\n startGame(gameId: string, playerId: string): StartGameResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n if (game.status !== \"open\") {\n return { success: false, error: \"invalidStatus\" };\n }\n if (game.players.indexOf(player) !== 0) {\n return { success: false, error: \"playerNotAdmin\" };\n }\n if (game.players.length < MIN_PLAYERS) {\n return { success: false, error: \"minPlayersNotReached\" };\n }\n const otherPlayer = game.players[1]!;\n const cards = [...CARDS];\n shuffleCards(cards);\n\n player.sidePile.push(...cards.splice(0, 5));\n emitEvent(game, {\n type: \"playerSidePileInitialized\",\n username: player.username,\n numCards: player.sidePile.length,\n });\n\n player.centerPile.push(...cards.splice(0, 1));\n emitEvent(game, {\n type: \"playerCenterPileInitialized\",\n username: player.username,\n card: player.centerPile[player.centerPile.length - 1]!,\n });\n\n otherPlayer.centerPile.push(...cards.splice(0, 1));\n emitEvent(game, {\n type: \"playerCenterPileInitialized\",\n username: otherPlayer.username,\n card: otherPlayer.centerPile[otherPlayer.centerPile.length - 1]!,\n });\n\n otherPlayer.sidePile.push(...cards.splice(0, 5));\n emitEvent(game, {\n type: \"playerSidePileInitialized\",\n username: otherPlayer.username,\n numCards: otherPlayer.sidePile.length,\n });\n\n player.drawPile.push(...cards.splice(0, 15));\n emitEvent(game, {\n type: \"playerDrawPileInitialized\",\n username: player.username,\n numCards: player.drawPile.length,\n });\n\n player.hand.push(...cards.splice(0, 5));\n emitEventToPlayer(player, { type: \"handInitialized\", cards: player.hand });\n emitEvent(game, {\n type: \"playerHandInitialized\",\n username: player.username,\n numCards: player.hand.length,\n });\n\n otherPlayer.drawPile.push(...cards.splice(0, 15));\n emitEvent(game, {\n type: \"playerDrawPileInitialized\",\n username: otherPlayer.username,\n numCards: otherPlayer.drawPile.length,\n });\n\n otherPlayer.hand.push(...cards.splice(0, 5));\n emitEventToPlayer(otherPlayer, {\n type: \"handInitialized\",\n cards: otherPlayer.hand,\n });\n emitEvent(game, {\n type: \"playerHandInitialized\",\n username: otherPlayer.username,\n numCards: otherPlayer.hand.length,\n });\n\n const startedGame: Game = {\n ...game,\n status: \"started\",\n expiresAt: Date.now() + EXPIRY_EXTENSION_MS,\n canPlayAt: Date.now() + CAN_PLAY_AT_DELAY_MS,\n };\n this.games.set(game.id, startedGame);\n emitEvent(startedGame, { type: \"gameStarted\" });\n emitEvent(startedGame, {\n type: \"expirationUpdated\",\n expiresAt: startedGame.expiresAt,\n });\n emitEvent(startedGame, {\n type: \"canPlayAtUpdated\",\n canPlayAt: startedGame.canPlayAt,\n });\n return { success: true };\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_manager = require("../manager-BpKvQbcp.cjs");
2
+ const require_manager = require("../manager-BxLMVu_o.cjs");
3
3
  let zod = require("zod");
4
4
  //#region src/server/Server.ts
5
5
  const drawCardInputSchema = zod.z.object({
@@ -53,7 +53,7 @@ const startGameInputSchema = zod.z.object({
53
53
  playerId: zod.z.string()
54
54
  }).transform(({ gameId, playerId }) => [gameId, playerId]);
55
55
  var Server = class {
56
- manager = new require_manager.Manager();
56
+ manager;
57
57
  actions = [
58
58
  {
59
59
  path: "/createGame",
@@ -100,6 +100,9 @@ var Server = class {
100
100
  action: this.startGame.bind(this)
101
101
  }
102
102
  ];
103
+ constructor(...args) {
104
+ this.manager = new require_manager.Manager(...args);
105
+ }
103
106
  createGame(userId, username) {
104
107
  return this.manager.createGame(userId, username);
105
108
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["z","Manager"],"sources":["../../src/server/Server.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { Manager } from \"../manager/index.js\";\nimport type { ClientState, GameEvent } from \"../manager/index.js\";\n\nexport type CreateGameResult =\n | {\n readonly success: true;\n readonly gameId: string;\n readonly playerId: string;\n }\n | {\n readonly success: false;\n readonly error: \"maxGamesReached\";\n };\n\nexport type DrawCardResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"invalidInput\"\n | \"gameNotFound\"\n | \"playerNotFound\"\n | \"invalidStatus\"\n | \"canPlayAtNotReached\"\n | \"handFull\"\n | \"drawPileEmpty\";\n };\n\nexport type GetClientStateAndClearEventsResult =\n | {\n readonly success: true;\n readonly state: ClientState;\n }\n | {\n readonly success: false;\n readonly error: \"invalidInput\" | \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type GetEventsAndClearAcknowledgedResult =\n | {\n readonly success: true;\n readonly events: readonly GameEvent[];\n }\n | {\n readonly success: false;\n readonly error: \"invalidInput\" | \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type GetJoinableGamesResult = {\n readonly games: readonly {\n readonly id: string;\n readonly numPlayers: number;\n }[];\n};\n\nexport type JoinGameResult =\n | {\n readonly success: true;\n readonly playerId: string;\n }\n | {\n readonly success: false;\n readonly error:\n | \"invalidInput\"\n | \"gameNotFound\"\n | \"invalidStatus\"\n | \"maxPlayersReached\"\n | \"alreadyInGame\";\n };\n\nexport type LeaveGameResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error: \"invalidInput\" | \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type PlayCardResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"invalidInput\"\n | \"gameNotFound\"\n | \"playerNotFound\"\n | \"invalidStatus\"\n | \"canPlayAtNotReached\"\n | \"cardNotFound\"\n | \"cardNotPlayable\";\n };\n\nexport type ReportNoPlayableCardsResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"invalidInput\"\n | \"gameNotFound\"\n | \"playerNotFound\"\n | \"invalidStatus\"\n | \"canPlayAtNotReached\"\n | \"alreadyReported\"\n | \"hasPlayableCard\"\n | \"canDraw\";\n };\n\nexport type SendChatResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error: \"invalidInput\" | \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type StartGameResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"invalidInput\"\n | \"gameNotFound\"\n | \"invalidStatus\"\n | \"playerNotFound\"\n | \"playerNotAdmin\"\n | \"minPlayersNotReached\";\n };\n\nconst drawCardInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n })\n .transform(({ gameId, playerId }) => [gameId, playerId] as const);\n\nconst getClientStateAndClearEventsInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n })\n .transform(({ gameId, playerId }) => [gameId, playerId] as const);\n\nconst getEventsAndClearAcknowledgedInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n lastReadId: z.string().nullable(),\n })\n .transform(\n ({ gameId, playerId, lastReadId }) =>\n [gameId, playerId, lastReadId] as const,\n );\n\nconst joinGameInputSchema = z\n .object({\n gameId: z.string(),\n })\n .transform(({ gameId }) => [gameId] as const);\n\nconst leaveGameInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n })\n .transform(({ gameId, playerId }) => [gameId, playerId] as const);\n\nconst playCardInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n cardId: z.string(),\n isForOtherPlayerPile: z.boolean(),\n })\n .transform(\n ({ gameId, playerId, cardId, isForOtherPlayerPile }) =>\n [gameId, playerId, cardId, isForOtherPlayerPile] as const,\n );\n\nconst reportNoPlayableCardsInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n })\n .transform(({ gameId, playerId }) => [gameId, playerId] as const);\n\nconst sendChatInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n text: z.string(),\n })\n .transform(({ gameId, playerId, text }) => [gameId, playerId, text] as const);\n\nconst startGameInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n })\n .transform(({ gameId, playerId }) => [gameId, playerId] as const);\n\nexport class Server {\n private readonly manager = new Manager();\n\n readonly actions = [\n { path: \"/createGame\", action: this.createGame.bind(this) },\n { path: \"/drawCard\", action: this.drawCard.bind(this) },\n {\n path: \"/getClientStateAndClearEvents\",\n action: this.getClientStateAndClearEvents.bind(this),\n },\n {\n path: \"/getEventsAndClearAcknowledged\",\n action: this.getEventsAndClearAcknowledged.bind(this),\n },\n { path: \"/getJoinableGames\", action: this.getJoinableGames.bind(this) },\n { path: \"/joinGame\", action: this.joinGame.bind(this) },\n { path: \"/leaveGame\", action: this.leaveGame.bind(this) },\n { path: \"/playCard\", action: this.playCard.bind(this) },\n {\n path: \"/reportNoPlayableCards\",\n action: this.reportNoPlayableCards.bind(this),\n },\n { path: \"/sendChat\", action: this.sendChat.bind(this) },\n { path: \"/startGame\", action: this.startGame.bind(this) },\n ] as const;\n\n private createGame(userId: string, username: string): CreateGameResult {\n return this.manager.createGame(userId, username);\n }\n\n private drawCard(input: unknown): DrawCardResult {\n const parseResult = drawCardInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.drawCard(...parseResult.data);\n }\n\n private getClientStateAndClearEvents(\n input: unknown,\n ): GetClientStateAndClearEventsResult {\n const parseResult =\n getClientStateAndClearEventsInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.getClientStateAndClearEvents(...parseResult.data);\n }\n\n private getEventsAndClearAcknowledged(\n input: unknown,\n ): GetEventsAndClearAcknowledgedResult {\n const parseResult =\n getEventsAndClearAcknowledgedInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.getEventsAndClearAcknowledged(...parseResult.data);\n }\n\n private getJoinableGames(): GetJoinableGamesResult {\n return this.manager.getJoinableGames();\n }\n\n private joinGame(\n input: unknown,\n userId: string,\n username: string,\n ): JoinGameResult {\n const parseResult = joinGameInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.joinGame(...parseResult.data, userId, username);\n }\n\n private leaveGame(input: unknown): LeaveGameResult {\n const parseResult = leaveGameInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.leaveGame(...parseResult.data);\n }\n\n private playCard(input: unknown): PlayCardResult {\n const parseResult = playCardInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.playCard(...parseResult.data);\n }\n\n private reportNoPlayableCards(input: unknown): ReportNoPlayableCardsResult {\n const parseResult = reportNoPlayableCardsInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.reportNoPlayableCards(...parseResult.data);\n }\n\n private sendChat(input: unknown): SendChatResult {\n const parseResult = sendChatInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.sendChat(...parseResult.data);\n }\n\n private startGame(input: unknown): StartGameResult {\n const parseResult = startGameInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.startGame(...parseResult.data);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["z","Manager"],"sources":["../../src/server/Server.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { Manager } from \"../manager/index.js\";\nimport type { ClientState, GameEvent } from \"../manager/index.js\";\n\nexport type CreateGameResult =\n | {\n readonly success: true;\n readonly gameId: string;\n readonly playerId: string;\n }\n | {\n readonly success: false;\n readonly error: \"maxGamesReached\";\n };\n\nexport type DrawCardResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"invalidInput\"\n | \"gameNotFound\"\n | \"playerNotFound\"\n | \"invalidStatus\"\n | \"canPlayAtNotReached\"\n | \"handFull\"\n | \"drawPileEmpty\";\n };\n\nexport type GetClientStateAndClearEventsResult =\n | {\n readonly success: true;\n readonly state: ClientState;\n }\n | {\n readonly success: false;\n readonly error: \"invalidInput\" | \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type GetEventsAndClearAcknowledgedResult =\n | {\n readonly success: true;\n readonly events: readonly GameEvent[];\n }\n | {\n readonly success: false;\n readonly error: \"invalidInput\" | \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type GetJoinableGamesResult = {\n readonly games: readonly {\n readonly id: string;\n readonly numPlayers: number;\n }[];\n};\n\nexport type JoinGameResult =\n | {\n readonly success: true;\n readonly playerId: string;\n }\n | {\n readonly success: false;\n readonly error:\n | \"invalidInput\"\n | \"gameNotFound\"\n | \"invalidStatus\"\n | \"maxPlayersReached\"\n | \"alreadyInGame\";\n };\n\nexport type LeaveGameResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error: \"invalidInput\" | \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type PlayCardResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"invalidInput\"\n | \"gameNotFound\"\n | \"playerNotFound\"\n | \"invalidStatus\"\n | \"canPlayAtNotReached\"\n | \"cardNotFound\"\n | \"cardNotPlayable\";\n };\n\nexport type ReportNoPlayableCardsResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"invalidInput\"\n | \"gameNotFound\"\n | \"playerNotFound\"\n | \"invalidStatus\"\n | \"canPlayAtNotReached\"\n | \"alreadyReported\"\n | \"hasPlayableCard\"\n | \"canDraw\";\n };\n\nexport type SendChatResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error: \"invalidInput\" | \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type StartGameResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"invalidInput\"\n | \"gameNotFound\"\n | \"invalidStatus\"\n | \"playerNotFound\"\n | \"playerNotAdmin\"\n | \"minPlayersNotReached\";\n };\n\nconst drawCardInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n })\n .transform(({ gameId, playerId }) => [gameId, playerId] as const);\n\nconst getClientStateAndClearEventsInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n })\n .transform(({ gameId, playerId }) => [gameId, playerId] as const);\n\nconst getEventsAndClearAcknowledgedInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n lastReadId: z.string().nullable(),\n })\n .transform(\n ({ gameId, playerId, lastReadId }) =>\n [gameId, playerId, lastReadId] as const,\n );\n\nconst joinGameInputSchema = z\n .object({\n gameId: z.string(),\n })\n .transform(({ gameId }) => [gameId] as const);\n\nconst leaveGameInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n })\n .transform(({ gameId, playerId }) => [gameId, playerId] as const);\n\nconst playCardInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n cardId: z.string(),\n isForOtherPlayerPile: z.boolean(),\n })\n .transform(\n ({ gameId, playerId, cardId, isForOtherPlayerPile }) =>\n [gameId, playerId, cardId, isForOtherPlayerPile] as const,\n );\n\nconst reportNoPlayableCardsInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n })\n .transform(({ gameId, playerId }) => [gameId, playerId] as const);\n\nconst sendChatInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n text: z.string(),\n })\n .transform(({ gameId, playerId, text }) => [gameId, playerId, text] as const);\n\nconst startGameInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n })\n .transform(({ gameId, playerId }) => [gameId, playerId] as const);\n\nexport class Server {\n private readonly manager: Manager;\n\n readonly actions = [\n { path: \"/createGame\", action: this.createGame.bind(this) },\n { path: \"/drawCard\", action: this.drawCard.bind(this) },\n {\n path: \"/getClientStateAndClearEvents\",\n action: this.getClientStateAndClearEvents.bind(this),\n },\n {\n path: \"/getEventsAndClearAcknowledged\",\n action: this.getEventsAndClearAcknowledged.bind(this),\n },\n { path: \"/getJoinableGames\", action: this.getJoinableGames.bind(this) },\n { path: \"/joinGame\", action: this.joinGame.bind(this) },\n { path: \"/leaveGame\", action: this.leaveGame.bind(this) },\n { path: \"/playCard\", action: this.playCard.bind(this) },\n {\n path: \"/reportNoPlayableCards\",\n action: this.reportNoPlayableCards.bind(this),\n },\n { path: \"/sendChat\", action: this.sendChat.bind(this) },\n { path: \"/startGame\", action: this.startGame.bind(this) },\n ] as const;\n\n constructor(...args: ConstructorParameters<typeof Manager>) {\n this.manager = new Manager(...args);\n }\n\n private createGame(userId: string, username: string): CreateGameResult {\n return this.manager.createGame(userId, username);\n }\n\n private drawCard(input: unknown): DrawCardResult {\n const parseResult = drawCardInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.drawCard(...parseResult.data);\n }\n\n private getClientStateAndClearEvents(\n input: unknown,\n ): GetClientStateAndClearEventsResult {\n const parseResult =\n getClientStateAndClearEventsInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.getClientStateAndClearEvents(...parseResult.data);\n }\n\n private getEventsAndClearAcknowledged(\n input: unknown,\n ): GetEventsAndClearAcknowledgedResult {\n const parseResult =\n getEventsAndClearAcknowledgedInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.getEventsAndClearAcknowledged(...parseResult.data);\n }\n\n private getJoinableGames(): GetJoinableGamesResult {\n return this.manager.getJoinableGames();\n }\n\n private joinGame(\n input: unknown,\n userId: string,\n username: string,\n ): JoinGameResult {\n const parseResult = joinGameInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.joinGame(...parseResult.data, userId, username);\n }\n\n private leaveGame(input: unknown): LeaveGameResult {\n const parseResult = leaveGameInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.leaveGame(...parseResult.data);\n }\n\n private playCard(input: unknown): PlayCardResult {\n const parseResult = playCardInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.playCard(...parseResult.data);\n }\n\n private reportNoPlayableCards(input: unknown): ReportNoPlayableCardsResult {\n const parseResult = reportNoPlayableCardsInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.reportNoPlayableCards(...parseResult.data);\n }\n\n private sendChat(input: unknown): SendChatResult {\n const parseResult = sendChatInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.sendChat(...parseResult.data);\n }\n\n private startGame(input: unknown): StartGameResult {\n const parseResult = startGameInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.startGame(...parseResult.data);\n }\n}\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- import { g as Card, h as ChatMessage, m as ClientState, p as GameEvent } from "../index-YAYbNr-s.cjs";
2
- import { a as GetJoinableGamesResult, c as PlayCardResult, d as Server, f as StartGameResult, i as GetEventsAndClearAcknowledgedResult, l as ReportNoPlayableCardsResult, n as DrawCardResult, o as JoinGameResult, r as GetClientStateAndClearEventsResult, s as LeaveGameResult, t as CreateGameResult, u as SendChatResult } from "../index-FveApXzq.cjs";
1
+ import { g as Card, h as ChatMessage, m as ClientState, p as GameEvent } from "../index-xmcja9Bl.cjs";
2
+ import { a as GetJoinableGamesResult, c as PlayCardResult, d as Server, f as StartGameResult, i as GetEventsAndClearAcknowledgedResult, l as ReportNoPlayableCardsResult, n as DrawCardResult, o as JoinGameResult, r as GetClientStateAndClearEventsResult, s as LeaveGameResult, t as CreateGameResult, u as SendChatResult } from "../index-BvZyhFek.cjs";
3
3
  export { type Card, type ChatMessage, type ClientState, type CreateGameResult, type DrawCardResult, type GameEvent, type GetClientStateAndClearEventsResult, type GetEventsAndClearAcknowledgedResult, type GetJoinableGamesResult, type JoinGameResult, type LeaveGameResult, type PlayCardResult, type ReportNoPlayableCardsResult, type SendChatResult, Server, type StartGameResult };
@@ -1,3 +1,3 @@
1
- import { g as Card, h as ChatMessage, m as ClientState, p as GameEvent } from "../index-YAYbNr-s.mjs";
2
- import { a as GetJoinableGamesResult, c as PlayCardResult, d as Server, f as StartGameResult, i as GetEventsAndClearAcknowledgedResult, l as ReportNoPlayableCardsResult, n as DrawCardResult, o as JoinGameResult, r as GetClientStateAndClearEventsResult, s as LeaveGameResult, t as CreateGameResult, u as SendChatResult } from "../index-J8s2QklC.mjs";
1
+ import { g as Card, h as ChatMessage, m as ClientState, p as GameEvent } from "../index-xmcja9Bl.mjs";
2
+ import { a as GetJoinableGamesResult, c as PlayCardResult, d as Server, f as StartGameResult, i as GetEventsAndClearAcknowledgedResult, l as ReportNoPlayableCardsResult, n as DrawCardResult, o as JoinGameResult, r as GetClientStateAndClearEventsResult, s as LeaveGameResult, t as CreateGameResult, u as SendChatResult } from "../index-BRtLpRz1.mjs";
3
3
  export { type Card, type ChatMessage, type ClientState, type CreateGameResult, type DrawCardResult, type GameEvent, type GetClientStateAndClearEventsResult, type GetEventsAndClearAcknowledgedResult, type GetJoinableGamesResult, type JoinGameResult, type LeaveGameResult, type PlayCardResult, type ReportNoPlayableCardsResult, type SendChatResult, Server, type StartGameResult };
@@ -1,4 +1,4 @@
1
- import { t as Manager } from "../manager-BOymBQ4E.mjs";
1
+ import { t as Manager } from "../manager-8x5AFyzq.mjs";
2
2
  import { z } from "zod";
3
3
  //#region src/server/Server.ts
4
4
  const drawCardInputSchema = z.object({
@@ -52,7 +52,7 @@ const startGameInputSchema = z.object({
52
52
  playerId: z.string()
53
53
  }).transform(({ gameId, playerId }) => [gameId, playerId]);
54
54
  var Server = class {
55
- manager = new Manager();
55
+ manager;
56
56
  actions = [
57
57
  {
58
58
  path: "/createGame",
@@ -99,6 +99,9 @@ var Server = class {
99
99
  action: this.startGame.bind(this)
100
100
  }
101
101
  ];
102
+ constructor(...args) {
103
+ this.manager = new Manager(...args);
104
+ }
102
105
  createGame(userId, username) {
103
106
  return this.manager.createGame(userId, username);
104
107
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/server/Server.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { Manager } from \"../manager/index.js\";\nimport type { ClientState, GameEvent } from \"../manager/index.js\";\n\nexport type CreateGameResult =\n | {\n readonly success: true;\n readonly gameId: string;\n readonly playerId: string;\n }\n | {\n readonly success: false;\n readonly error: \"maxGamesReached\";\n };\n\nexport type DrawCardResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"invalidInput\"\n | \"gameNotFound\"\n | \"playerNotFound\"\n | \"invalidStatus\"\n | \"canPlayAtNotReached\"\n | \"handFull\"\n | \"drawPileEmpty\";\n };\n\nexport type GetClientStateAndClearEventsResult =\n | {\n readonly success: true;\n readonly state: ClientState;\n }\n | {\n readonly success: false;\n readonly error: \"invalidInput\" | \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type GetEventsAndClearAcknowledgedResult =\n | {\n readonly success: true;\n readonly events: readonly GameEvent[];\n }\n | {\n readonly success: false;\n readonly error: \"invalidInput\" | \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type GetJoinableGamesResult = {\n readonly games: readonly {\n readonly id: string;\n readonly numPlayers: number;\n }[];\n};\n\nexport type JoinGameResult =\n | {\n readonly success: true;\n readonly playerId: string;\n }\n | {\n readonly success: false;\n readonly error:\n | \"invalidInput\"\n | \"gameNotFound\"\n | \"invalidStatus\"\n | \"maxPlayersReached\"\n | \"alreadyInGame\";\n };\n\nexport type LeaveGameResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error: \"invalidInput\" | \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type PlayCardResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"invalidInput\"\n | \"gameNotFound\"\n | \"playerNotFound\"\n | \"invalidStatus\"\n | \"canPlayAtNotReached\"\n | \"cardNotFound\"\n | \"cardNotPlayable\";\n };\n\nexport type ReportNoPlayableCardsResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"invalidInput\"\n | \"gameNotFound\"\n | \"playerNotFound\"\n | \"invalidStatus\"\n | \"canPlayAtNotReached\"\n | \"alreadyReported\"\n | \"hasPlayableCard\"\n | \"canDraw\";\n };\n\nexport type SendChatResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error: \"invalidInput\" | \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type StartGameResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"invalidInput\"\n | \"gameNotFound\"\n | \"invalidStatus\"\n | \"playerNotFound\"\n | \"playerNotAdmin\"\n | \"minPlayersNotReached\";\n };\n\nconst drawCardInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n })\n .transform(({ gameId, playerId }) => [gameId, playerId] as const);\n\nconst getClientStateAndClearEventsInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n })\n .transform(({ gameId, playerId }) => [gameId, playerId] as const);\n\nconst getEventsAndClearAcknowledgedInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n lastReadId: z.string().nullable(),\n })\n .transform(\n ({ gameId, playerId, lastReadId }) =>\n [gameId, playerId, lastReadId] as const,\n );\n\nconst joinGameInputSchema = z\n .object({\n gameId: z.string(),\n })\n .transform(({ gameId }) => [gameId] as const);\n\nconst leaveGameInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n })\n .transform(({ gameId, playerId }) => [gameId, playerId] as const);\n\nconst playCardInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n cardId: z.string(),\n isForOtherPlayerPile: z.boolean(),\n })\n .transform(\n ({ gameId, playerId, cardId, isForOtherPlayerPile }) =>\n [gameId, playerId, cardId, isForOtherPlayerPile] as const,\n );\n\nconst reportNoPlayableCardsInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n })\n .transform(({ gameId, playerId }) => [gameId, playerId] as const);\n\nconst sendChatInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n text: z.string(),\n })\n .transform(({ gameId, playerId, text }) => [gameId, playerId, text] as const);\n\nconst startGameInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n })\n .transform(({ gameId, playerId }) => [gameId, playerId] as const);\n\nexport class Server {\n private readonly manager = new Manager();\n\n readonly actions = [\n { path: \"/createGame\", action: this.createGame.bind(this) },\n { path: \"/drawCard\", action: this.drawCard.bind(this) },\n {\n path: \"/getClientStateAndClearEvents\",\n action: this.getClientStateAndClearEvents.bind(this),\n },\n {\n path: \"/getEventsAndClearAcknowledged\",\n action: this.getEventsAndClearAcknowledged.bind(this),\n },\n { path: \"/getJoinableGames\", action: this.getJoinableGames.bind(this) },\n { path: \"/joinGame\", action: this.joinGame.bind(this) },\n { path: \"/leaveGame\", action: this.leaveGame.bind(this) },\n { path: \"/playCard\", action: this.playCard.bind(this) },\n {\n path: \"/reportNoPlayableCards\",\n action: this.reportNoPlayableCards.bind(this),\n },\n { path: \"/sendChat\", action: this.sendChat.bind(this) },\n { path: \"/startGame\", action: this.startGame.bind(this) },\n ] as const;\n\n private createGame(userId: string, username: string): CreateGameResult {\n return this.manager.createGame(userId, username);\n }\n\n private drawCard(input: unknown): DrawCardResult {\n const parseResult = drawCardInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.drawCard(...parseResult.data);\n }\n\n private getClientStateAndClearEvents(\n input: unknown,\n ): GetClientStateAndClearEventsResult {\n const parseResult =\n getClientStateAndClearEventsInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.getClientStateAndClearEvents(...parseResult.data);\n }\n\n private getEventsAndClearAcknowledged(\n input: unknown,\n ): GetEventsAndClearAcknowledgedResult {\n const parseResult =\n getEventsAndClearAcknowledgedInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.getEventsAndClearAcknowledged(...parseResult.data);\n }\n\n private getJoinableGames(): GetJoinableGamesResult {\n return this.manager.getJoinableGames();\n }\n\n private joinGame(\n input: unknown,\n userId: string,\n username: string,\n ): JoinGameResult {\n const parseResult = joinGameInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.joinGame(...parseResult.data, userId, username);\n }\n\n private leaveGame(input: unknown): LeaveGameResult {\n const parseResult = leaveGameInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.leaveGame(...parseResult.data);\n }\n\n private playCard(input: unknown): PlayCardResult {\n const parseResult = playCardInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.playCard(...parseResult.data);\n }\n\n private reportNoPlayableCards(input: unknown): ReportNoPlayableCardsResult {\n const parseResult = reportNoPlayableCardsInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.reportNoPlayableCards(...parseResult.data);\n }\n\n private sendChat(input: unknown): SendChatResult {\n const parseResult = sendChatInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.sendChat(...parseResult.data);\n }\n\n private startGame(input: unknown): StartGameResult {\n const parseResult = startGameInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.startGame(...parseResult.data);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/server/Server.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { Manager } from \"../manager/index.js\";\nimport type { ClientState, GameEvent } from \"../manager/index.js\";\n\nexport type CreateGameResult =\n | {\n readonly success: true;\n readonly gameId: string;\n readonly playerId: string;\n }\n | {\n readonly success: false;\n readonly error: \"maxGamesReached\";\n };\n\nexport type DrawCardResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"invalidInput\"\n | \"gameNotFound\"\n | \"playerNotFound\"\n | \"invalidStatus\"\n | \"canPlayAtNotReached\"\n | \"handFull\"\n | \"drawPileEmpty\";\n };\n\nexport type GetClientStateAndClearEventsResult =\n | {\n readonly success: true;\n readonly state: ClientState;\n }\n | {\n readonly success: false;\n readonly error: \"invalidInput\" | \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type GetEventsAndClearAcknowledgedResult =\n | {\n readonly success: true;\n readonly events: readonly GameEvent[];\n }\n | {\n readonly success: false;\n readonly error: \"invalidInput\" | \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type GetJoinableGamesResult = {\n readonly games: readonly {\n readonly id: string;\n readonly numPlayers: number;\n }[];\n};\n\nexport type JoinGameResult =\n | {\n readonly success: true;\n readonly playerId: string;\n }\n | {\n readonly success: false;\n readonly error:\n | \"invalidInput\"\n | \"gameNotFound\"\n | \"invalidStatus\"\n | \"maxPlayersReached\"\n | \"alreadyInGame\";\n };\n\nexport type LeaveGameResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error: \"invalidInput\" | \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type PlayCardResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"invalidInput\"\n | \"gameNotFound\"\n | \"playerNotFound\"\n | \"invalidStatus\"\n | \"canPlayAtNotReached\"\n | \"cardNotFound\"\n | \"cardNotPlayable\";\n };\n\nexport type ReportNoPlayableCardsResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"invalidInput\"\n | \"gameNotFound\"\n | \"playerNotFound\"\n | \"invalidStatus\"\n | \"canPlayAtNotReached\"\n | \"alreadyReported\"\n | \"hasPlayableCard\"\n | \"canDraw\";\n };\n\nexport type SendChatResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error: \"invalidInput\" | \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type StartGameResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"invalidInput\"\n | \"gameNotFound\"\n | \"invalidStatus\"\n | \"playerNotFound\"\n | \"playerNotAdmin\"\n | \"minPlayersNotReached\";\n };\n\nconst drawCardInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n })\n .transform(({ gameId, playerId }) => [gameId, playerId] as const);\n\nconst getClientStateAndClearEventsInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n })\n .transform(({ gameId, playerId }) => [gameId, playerId] as const);\n\nconst getEventsAndClearAcknowledgedInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n lastReadId: z.string().nullable(),\n })\n .transform(\n ({ gameId, playerId, lastReadId }) =>\n [gameId, playerId, lastReadId] as const,\n );\n\nconst joinGameInputSchema = z\n .object({\n gameId: z.string(),\n })\n .transform(({ gameId }) => [gameId] as const);\n\nconst leaveGameInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n })\n .transform(({ gameId, playerId }) => [gameId, playerId] as const);\n\nconst playCardInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n cardId: z.string(),\n isForOtherPlayerPile: z.boolean(),\n })\n .transform(\n ({ gameId, playerId, cardId, isForOtherPlayerPile }) =>\n [gameId, playerId, cardId, isForOtherPlayerPile] as const,\n );\n\nconst reportNoPlayableCardsInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n })\n .transform(({ gameId, playerId }) => [gameId, playerId] as const);\n\nconst sendChatInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n text: z.string(),\n })\n .transform(({ gameId, playerId, text }) => [gameId, playerId, text] as const);\n\nconst startGameInputSchema = z\n .object({\n gameId: z.string(),\n playerId: z.string(),\n })\n .transform(({ gameId, playerId }) => [gameId, playerId] as const);\n\nexport class Server {\n private readonly manager: Manager;\n\n readonly actions = [\n { path: \"/createGame\", action: this.createGame.bind(this) },\n { path: \"/drawCard\", action: this.drawCard.bind(this) },\n {\n path: \"/getClientStateAndClearEvents\",\n action: this.getClientStateAndClearEvents.bind(this),\n },\n {\n path: \"/getEventsAndClearAcknowledged\",\n action: this.getEventsAndClearAcknowledged.bind(this),\n },\n { path: \"/getJoinableGames\", action: this.getJoinableGames.bind(this) },\n { path: \"/joinGame\", action: this.joinGame.bind(this) },\n { path: \"/leaveGame\", action: this.leaveGame.bind(this) },\n { path: \"/playCard\", action: this.playCard.bind(this) },\n {\n path: \"/reportNoPlayableCards\",\n action: this.reportNoPlayableCards.bind(this),\n },\n { path: \"/sendChat\", action: this.sendChat.bind(this) },\n { path: \"/startGame\", action: this.startGame.bind(this) },\n ] as const;\n\n constructor(...args: ConstructorParameters<typeof Manager>) {\n this.manager = new Manager(...args);\n }\n\n private createGame(userId: string, username: string): CreateGameResult {\n return this.manager.createGame(userId, username);\n }\n\n private drawCard(input: unknown): DrawCardResult {\n const parseResult = drawCardInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.drawCard(...parseResult.data);\n }\n\n private getClientStateAndClearEvents(\n input: unknown,\n ): GetClientStateAndClearEventsResult {\n const parseResult =\n getClientStateAndClearEventsInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.getClientStateAndClearEvents(...parseResult.data);\n }\n\n private getEventsAndClearAcknowledged(\n input: unknown,\n ): GetEventsAndClearAcknowledgedResult {\n const parseResult =\n getEventsAndClearAcknowledgedInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.getEventsAndClearAcknowledged(...parseResult.data);\n }\n\n private getJoinableGames(): GetJoinableGamesResult {\n return this.manager.getJoinableGames();\n }\n\n private joinGame(\n input: unknown,\n userId: string,\n username: string,\n ): JoinGameResult {\n const parseResult = joinGameInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.joinGame(...parseResult.data, userId, username);\n }\n\n private leaveGame(input: unknown): LeaveGameResult {\n const parseResult = leaveGameInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.leaveGame(...parseResult.data);\n }\n\n private playCard(input: unknown): PlayCardResult {\n const parseResult = playCardInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.playCard(...parseResult.data);\n }\n\n private reportNoPlayableCards(input: unknown): ReportNoPlayableCardsResult {\n const parseResult = reportNoPlayableCardsInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.reportNoPlayableCards(...parseResult.data);\n }\n\n private sendChat(input: unknown): SendChatResult {\n const parseResult = sendChatInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.sendChat(...parseResult.data);\n }\n\n private startGame(input: unknown): StartGameResult {\n const parseResult = startGameInputSchema.safeParse(input);\n if (!parseResult.success) {\n return { success: false, error: \"invalidInput\" };\n }\n return this.manager.startGame(...parseResult.data);\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hellacardgames/speed",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "A TypeScript library implementing the Speed card game.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -86,7 +86,7 @@
86
86
  "vitest": "^4.1.9"
87
87
  },
88
88
  "dependencies": {
89
- "@hellacardgames/lib": "^0.5.2",
89
+ "@hellacardgames/lib": "^0.6.1",
90
90
  "zod": "^4.4.3"
91
91
  }
92
92
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"index-YAYbNr-s.d.cts","names":[],"sources":["../src/manager/types/Card.ts","../src/manager/types/ChatMessage.ts","../src/manager/types/ClientState.ts","../src/manager/types/GameEvent.ts","../src/manager/Manager.ts"],"mappings":";KAAY;WACD;WACA;WACA;;;;KCHC;WACD;WACA;WACA;;;;KCAC;WACD;WACA;WACA;WACA;WACA,kBAAkB;WAClB;WACA,uBAAuB;WACvB,eAAe;WACf;;KAGN;WACM;WACA;WACA;WACA;WACA,mBAAmB;;;;KCjBlB;WAEG;WACA;WACA;;WAGA;WACA;WACA;WACA,MAAM;;WAGN;WACA;WACA;WACA,MAAM;WACN;;WAGA;WACA;WACA,SAAS;;WAGT;WACA;WACA,MAAM;;WAGN;WACA;WACA;;WAGA;WACA;;WAGA;WACA;;WAGA;WACA;;WAGA;WACA;WACA,gBAAgB;;WAGhB;WACA;WACA;WACA,MAAM;;WAGN;WACA;WACA;WACA;;WAGA;WACA;WACA;;WAGA;WACA;WACA;WACA;;WAGA;WACA;WACA;;WAGA;WACA;WACA;;WAGA;WACA;WACA;WACA;;;;KCtEH;WAEG;WACA;WACA;;WAGA;WACA;;KAGH;WAEG;;WAGA;WACA;;KASH;WAEG;WACA,OAAO;;WAGP;WACA;;KAGH;WAEG;WACA,iBAAiB;;WAGjB;WACA;;KAGH;WACD;aACE;aACA;;;KAID;WAEG;WACA;;WAGA;WACA;;KAOH;WAEG;;WAGA;WACA;;KAGH;WAEG;;WAGA;WACA;;KASH;WAEG;;WAGA;WACA;;KAUH;WAEG;;WAGA;WACA;;KAGH;WAEG;;WAGA;WACA;;cAQF;mBACM;mBACA;;EAQjB,WAAW,gBAAgB,mBAAmB;EA4B9C,SAAS,gBAAgB,mBAAmB;EA8B5C,6BACE,gBACA,mBACC;EAgCH,8BACE,gBACA,kBACA,4BACC;EAgBH,oBAAoB;EAWpB,SAAS,gBAAgB,gBAAgB,mBAAmB;EA8B5D,UAAU,gBAAgB,mBAAmB;EAkC7C,SACE,gBACA,kBACA,gBACA,gCACC;EAwDH,sBACE,gBACA,mBACC;EAuFH,SAAS,gBAAgB,kBAAkB,eAAe;EAmB1D,UAAU,gBAAgB,mBAAmB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index-YAYbNr-s.d.mts","names":[],"sources":["../src/manager/types/Card.ts","../src/manager/types/ChatMessage.ts","../src/manager/types/ClientState.ts","../src/manager/types/GameEvent.ts","../src/manager/Manager.ts"],"mappings":";KAAY;WACD;WACA;WACA;;;;KCHC;WACD;WACA;WACA;;;;KCAC;WACD;WACA;WACA;WACA;WACA,kBAAkB;WAClB;WACA,uBAAuB;WACvB,eAAe;WACf;;KAGN;WACM;WACA;WACA;WACA;WACA,mBAAmB;;;;KCjBlB;WAEG;WACA;WACA;;WAGA;WACA;WACA;WACA,MAAM;;WAGN;WACA;WACA;WACA,MAAM;WACN;;WAGA;WACA;WACA,SAAS;;WAGT;WACA;WACA,MAAM;;WAGN;WACA;WACA;;WAGA;WACA;;WAGA;WACA;;WAGA;WACA;;WAGA;WACA;WACA,gBAAgB;;WAGhB;WACA;WACA;WACA,MAAM;;WAGN;WACA;WACA;WACA;;WAGA;WACA;WACA;;WAGA;WACA;WACA;WACA;;WAGA;WACA;WACA;;WAGA;WACA;WACA;;WAGA;WACA;WACA;WACA;;;;KCtEH;WAEG;WACA;WACA;;WAGA;WACA;;KAGH;WAEG;;WAGA;WACA;;KASH;WAEG;WACA,OAAO;;WAGP;WACA;;KAGH;WAEG;WACA,iBAAiB;;WAGjB;WACA;;KAGH;WACD;aACE;aACA;;;KAID;WAEG;WACA;;WAGA;WACA;;KAOH;WAEG;;WAGA;WACA;;KAGH;WAEG;;WAGA;WACA;;KASH;WAEG;;WAGA;WACA;;KAUH;WAEG;;WAGA;WACA;;KAGH;WAEG;;WAGA;WACA;;cAQF;mBACM;mBACA;;EAQjB,WAAW,gBAAgB,mBAAmB;EA4B9C,SAAS,gBAAgB,mBAAmB;EA8B5C,6BACE,gBACA,mBACC;EAgCH,8BACE,gBACA,kBACA,4BACC;EAgBH,oBAAoB;EAWpB,SAAS,gBAAgB,gBAAgB,mBAAmB;EA8B5D,UAAU,gBAAgB,mBAAmB;EAkC7C,SACE,gBACA,kBACA,gBACA,gCACC;EAwDH,sBACE,gBACA,mBACC;EAuFH,SAAS,gBAAgB,kBAAkB,eAAe;EAmB1D,UAAU,gBAAgB,mBAAmB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"manager-BOymBQ4E.mjs","names":[],"sources":["../src/manager/constants.ts","../src/manager/Watchdog.ts","../src/lib/emitEvent.ts","../src/lib/emitEventToPlayer.ts","../src/manager/lib/isCardPlayable.ts","../src/manager/lib/hasPlayableCard.ts","../src/lib/shuffleCards.ts","../src/manager/Manager.ts"],"sourcesContent":["import type { Card } from \"./types/Card.js\";\n\nexport const GAME_KEY = \"speed\";\nexport const MAX_GAMES = 50;\nexport const WATCHDOG_INTERVAL_MS = 10000;\n\nexport const EXPIRY_EXTENSION_MS = 300000; // 5 minutes\nexport const MIN_PLAYERS = 2;\nexport const MAX_PLAYERS = 2;\nexport const MAX_HAND_SIZE = 5;\nexport const CAN_PLAY_AT_DELAY_MS = 3000;\n\nexport const CARDS: readonly Card[] = [\n { id: crypto.randomUUID(), rank: 2, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 3, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 4, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 5, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 6, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 7, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 8, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 9, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 10, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 11, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 12, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 13, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 14, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 2, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 3, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 4, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 5, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 6, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 7, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 8, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 9, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 10, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 11, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 12, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 13, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 14, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 2, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 3, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 4, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 5, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 6, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 7, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 8, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 9, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 10, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 11, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 12, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 13, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 14, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 2, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 3, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 4, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 5, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 6, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 7, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 8, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 9, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 10, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 11, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 12, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 13, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 14, suite: \"spades\" },\n];\n","import { GAME_KEY, WATCHDOG_INTERVAL_MS } from \"./constants.js\";\n\ntype Game = {\n readonly id: string;\n readonly expiresAt: number;\n};\n\nexport class Watchdog<TGame extends Game> {\n private readonly games: Map<string, TGame>;\n\n constructor(games: Map<string, TGame>) {\n this.games = games;\n }\n\n start() {\n console.log(`watchdog start at ${Date.now()} (${GAME_KEY})`);\n setInterval(() => this.wakeUp(), WATCHDOG_INTERVAL_MS);\n }\n\n wakeUp() {\n const now = Date.now();\n // console.log(`watchdog wakeUp at ${now} (${GAME_KEY})`);\n for (const game of this.games.values()) {\n if (game.expiresAt <= now) {\n this.games.delete(game.id);\n console.log(`watchdog purged ${game.id} (${GAME_KEY})`);\n }\n }\n }\n}\n","type Game = {\n readonly players: {\n readonly events: { readonly id: string }[];\n }[];\n};\n\ntype OmitId<T> = T extends unknown ? Omit<T, \"id\"> : never;\n\nexport function emitEvent<T extends Game>(\n game: T,\n data: OmitId<T[\"players\"][number][\"events\"][number]>,\n): void {\n const event = { ...data, id: crypto.randomUUID() };\n for (const p of game.players) {\n p.events.push(event);\n }\n}\n","type Player = {\n readonly events: { readonly id: string }[];\n};\n\ntype OmitId<T> = T extends unknown ? Omit<T, \"id\"> : never;\n\nexport function emitEventToPlayer<T extends Player>(\n player: T,\n data: OmitId<T[\"events\"][number]>,\n): void {\n player.events.push({ ...data, id: crypto.randomUUID() });\n}\n","import type { Card } from \"../types/Card.js\";\n\nexport function isCardPlayable(card: Card, targetPile: Card[]) {\n const targetCard = targetPile[targetPile.length - 1]!;\n const diff = Math.abs(card.rank - targetCard.rank) % 11;\n return diff === 1;\n}\n","import { isCardPlayable } from \"./isCardPlayable.js\";\nimport type { Game } from \"../types/Game.js\";\nimport type { Player } from \"../types/Player.js\";\n\nexport function hasPlayableCard(player: Player, game: Game) {\n const otherPlayer = game.players.find((p) => p !== player)!;\n const targetPile1 = player.centerPile;\n const targetPile2 = otherPlayer.centerPile;\n for (const card of player.hand) {\n if (\n isCardPlayable(card, targetPile1) ||\n isCardPlayable(card, targetPile2)\n ) {\n return true;\n }\n }\n return false;\n}\n","type Card = { readonly id: string };\n\nexport function shuffleCards<T extends Card>(cards: T[]): void {\n for (let i = cards.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [cards[i], cards[j]] = [cards[j]!, cards[i]!];\n }\n}\n","import {\n CAN_PLAY_AT_DELAY_MS,\n CARDS,\n EXPIRY_EXTENSION_MS,\n MAX_GAMES,\n MAX_HAND_SIZE,\n MAX_PLAYERS,\n MIN_PLAYERS,\n} from \"./constants.js\";\nimport { Watchdog } from \"./Watchdog.js\";\nimport { emitEvent } from \"../lib/emitEvent.js\";\nimport { emitEventToPlayer } from \"../lib/emitEventToPlayer.js\";\nimport { isCardPlayable } from \"./lib/isCardPlayable.js\";\nimport { hasPlayableCard } from \"./lib/hasPlayableCard.js\";\nimport { shuffleCards } from \"../lib/shuffleCards.js\";\nimport type { ChatMessage } from \"./types/ChatMessage.js\";\nimport type { ClientState } from \"./types/ClientState.js\";\nimport type { Game } from \"./types/Game.js\";\nimport type { GameEvent } from \"./types/GameEvent.js\";\nimport type { Player } from \"./types/Player.js\";\n\nexport type CreateGameResult =\n | {\n readonly success: true;\n readonly gameId: string;\n readonly playerId: string;\n }\n | {\n readonly success: false;\n readonly error: \"maxGamesReached\";\n };\n\nexport type DrawCardResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"gameNotFound\"\n | \"playerNotFound\"\n | \"invalidStatus\"\n | \"canPlayAtNotReached\"\n | \"handFull\"\n | \"drawPileEmpty\";\n };\n\nexport type GetClientStateAndClearEventsResult =\n | {\n readonly success: true;\n readonly state: ClientState;\n }\n | {\n readonly success: false;\n readonly error: \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type GetEventsAndClearAcknowledgedResult =\n | {\n readonly success: true;\n readonly events: readonly GameEvent[];\n }\n | {\n readonly success: false;\n readonly error: \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type GetJoinableGamesResult = {\n readonly games: readonly {\n readonly id: string;\n readonly numPlayers: number;\n }[];\n};\n\nexport type JoinGameResult =\n | {\n readonly success: true;\n readonly playerId: string;\n }\n | {\n readonly success: false;\n readonly error:\n | \"gameNotFound\"\n | \"invalidStatus\"\n | \"maxPlayersReached\"\n | \"alreadyInGame\";\n };\n\nexport type LeaveGameResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error: \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type PlayCardResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"gameNotFound\"\n | \"playerNotFound\"\n | \"invalidStatus\"\n | \"canPlayAtNotReached\"\n | \"cardNotFound\"\n | \"cardNotPlayable\";\n };\n\nexport type ReportNoPlayableCardsResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"gameNotFound\"\n | \"playerNotFound\"\n | \"invalidStatus\"\n | \"canPlayAtNotReached\"\n | \"alreadyReported\"\n | \"hasPlayableCard\"\n | \"canDraw\";\n };\n\nexport type SendChatResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error: \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type StartGameResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"gameNotFound\"\n | \"invalidStatus\"\n | \"playerNotFound\"\n | \"playerNotAdmin\"\n | \"minPlayersNotReached\";\n };\n\nexport class Manager {\n private readonly games: Map<string, Game>;\n private readonly watchdog: Watchdog<Game>;\n\n constructor() {\n this.games = new Map<string, Game>();\n this.watchdog = new Watchdog(this.games);\n this.watchdog.start();\n }\n\n createGame(userId: string, username: string): CreateGameResult {\n if (this.games.size === MAX_GAMES) {\n return { success: false, error: \"maxGamesReached\" };\n }\n const player: Player = {\n id: crypto.randomUUID(),\n userId,\n username,\n events: [],\n hand: [],\n drawPile: [],\n sidePile: [],\n centerPile: [],\n hasNoPlayableCards: false,\n };\n const createdAt = Date.now();\n const game: Game = {\n status: \"open\",\n id: crypto.randomUUID(),\n createdAt,\n expiresAt: createdAt + EXPIRY_EXTENSION_MS,\n chatMessages: [],\n players: [player],\n };\n this.games.set(game.id, game);\n return { success: true, gameId: game.id, playerId: player.id };\n }\n\n drawCard(gameId: string, playerId: string): DrawCardResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n if (game.status !== \"started\") {\n return { success: false, error: \"invalidStatus\" };\n }\n if (Date.now() < game.canPlayAt) {\n return { success: false, error: \"canPlayAtNotReached\" };\n }\n if (player.hand.length >= MAX_HAND_SIZE) {\n return { success: false, error: \"handFull\" };\n }\n if (player.drawPile.length === 0) {\n return { success: false, error: \"drawPileEmpty\" };\n }\n game.expiresAt = Date.now() + EXPIRY_EXTENSION_MS;\n emitEvent(game, { type: \"expirationUpdated\", expiresAt: game.expiresAt });\n const card = player.drawPile.pop()!;\n player.hand.push(card);\n emitEventToPlayer(player, { type: \"drewCard\", card });\n emitEvent(game, { type: \"playerDrewCard\", username: player.username });\n return { success: true };\n }\n\n getClientStateAndClearEvents(\n gameId: string,\n playerId: string,\n ): GetClientStateAndClearEventsResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n const state: ClientState = {\n status: game.status,\n gameId,\n playerId,\n username: player.username,\n players: game.players.map((p) => ({\n username: p.username,\n handSize: p.hand.length,\n drawPileSize: p.drawPile.length,\n sidePileSize: p.sidePile.length,\n centerPileTopCard: p.centerPile.length\n ? p.centerPile[p.centerPile.length - 1]!\n : null,\n })),\n expiresAt: game.expiresAt,\n chatMessages: game.chatMessages,\n hand: player.hand,\n canPlayAt: game.status === \"started\" ? game.canPlayAt : null,\n };\n player.events.length = 0;\n return { success: true, state };\n }\n\n getEventsAndClearAcknowledged(\n gameId: string,\n playerId: string,\n lastReadId: string | null,\n ): GetEventsAndClearAcknowledgedResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n const lastReadEventIndex = player.events.findIndex(\n (e) => e.id === lastReadId,\n );\n player.events.splice(0, lastReadEventIndex + 1);\n return { success: true, events: player.events };\n }\n\n getJoinableGames(): GetJoinableGamesResult {\n return {\n games: Array.from(this.games.values())\n .filter((g) => g.status === \"open\" && g.players.length < MAX_PLAYERS)\n .map((g) => ({\n id: g.id,\n numPlayers: g.players.length,\n })),\n };\n }\n\n joinGame(gameId: string, userId: string, username: string): JoinGameResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n if (game.status !== \"open\") {\n return { success: false, error: \"invalidStatus\" };\n }\n if (game.players.length === MAX_PLAYERS) {\n return { success: false, error: \"maxPlayersReached\" };\n }\n if (game.players.find((p) => p.userId === userId)) {\n return { success: false, error: \"alreadyInGame\" };\n }\n const player: Player = {\n id: crypto.randomUUID(),\n userId,\n username,\n events: [],\n hand: [],\n drawPile: [],\n sidePile: [],\n centerPile: [],\n hasNoPlayableCards: false,\n };\n game.players.push(player);\n emitEvent(game, { type: \"playerJoined\", username });\n return { success: true, playerId: player.id };\n }\n\n leaveGame(gameId: string, playerId: string): LeaveGameResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const playerIndex = game.players.findIndex((p) => p.id === playerId);\n if (playerIndex === -1) {\n return { success: false, error: \"playerNotFound\" };\n }\n\n const player = game.players[playerIndex]!;\n emitEvent(game, { type: \"playerLeft\", username: player.username });\n\n game.players.splice(playerIndex, 1);\n\n if (game.status === \"started\" && game.players.length < MIN_PLAYERS) {\n const forfeitedGame: Game = {\n ...game,\n status: \"forfeited\",\n expiresAt: Date.now() + EXPIRY_EXTENSION_MS,\n };\n this.games.set(game.id, forfeitedGame);\n emitEvent(forfeitedGame, { type: \"gameForfeited\" });\n emitEvent(forfeitedGame, {\n type: \"expirationUpdated\",\n expiresAt: forfeitedGame.expiresAt,\n });\n }\n if (game.players.length === 0) {\n this.games.delete(game.id);\n }\n return { success: true };\n }\n\n playCard(\n gameId: string,\n playerId: string,\n cardId: string,\n isForOtherPlayerPile: boolean,\n ): PlayCardResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n if (game.status !== \"started\") {\n return { success: false, error: \"invalidStatus\" };\n }\n if (Date.now() < game.canPlayAt) {\n return { success: false, error: \"canPlayAtNotReached\" };\n }\n const cardIndex = player.hand.findIndex((c) => c.id === cardId);\n if (cardIndex === -1) {\n return { success: false, error: \"cardNotFound\" };\n }\n const otherPlayer = game.players.find((p) => p !== player)!;\n const targetPile = isForOtherPlayerPile\n ? otherPlayer.centerPile\n : player.centerPile;\n const card = player.hand[cardIndex]!;\n if (!isCardPlayable(card, targetPile)) {\n return { success: false, error: \"cardNotPlayable\" };\n }\n\n game.expiresAt = Date.now() + EXPIRY_EXTENSION_MS;\n emitEvent(game, { type: \"expirationUpdated\", expiresAt: game.expiresAt });\n\n player.hand.splice(cardIndex, 1);\n targetPile.push(card);\n emitEvent(game, {\n type: \"cardPlayed\",\n username: player.username,\n card,\n isForOtherPlayerPile,\n });\n\n if (otherPlayer.hasNoPlayableCards && hasPlayableCard(otherPlayer, game)) {\n otherPlayer.hasNoPlayableCards = false;\n }\n\n if (player.hand.length === 0 && player.drawPile.length === 0) {\n emitEvent(game, { type: \"gameCompleted\" });\n const completedGame: Game = {\n ...game,\n status: \"completed\",\n };\n this.games.set(game.id, completedGame);\n }\n\n return { success: true };\n }\n\n reportNoPlayableCards(\n gameId: string,\n playerId: string,\n ): ReportNoPlayableCardsResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n if (game.status !== \"started\") {\n return { success: false, error: \"invalidStatus\" };\n }\n if (Date.now() < game.canPlayAt) {\n return { success: false, error: \"canPlayAtNotReached\" };\n }\n if (player.hasNoPlayableCards) {\n return { success: false, error: \"alreadyReported\" };\n }\n if (hasPlayableCard(player, game)) {\n return { success: false, error: \"hasPlayableCard\" };\n }\n if (player.drawPile.length > 0 && player.hand.length < MAX_HAND_SIZE) {\n return { success: false, error: \"canDraw\" };\n }\n\n game.expiresAt = Date.now() + EXPIRY_EXTENSION_MS;\n emitEvent(game, { type: \"expirationUpdated\", expiresAt: game.expiresAt });\n\n player.hasNoPlayableCards = true;\n\n const otherPlayer = game.players.find((p) => p !== player)!;\n if (otherPlayer.hasNoPlayableCards) {\n if (player.sidePile.length === 0 && otherPlayer.sidePile.length === 0) {\n const cards = [...player.centerPile, ...otherPlayer.centerPile];\n shuffleCards(cards);\n player.sidePile.push(...cards.splice(0, 5));\n emitEvent(game, {\n type: \"playerSidePileInitialized\",\n username: player.username,\n numCards: player.sidePile.length,\n });\n player.centerPile.push(...cards.splice(0, 1));\n emitEvent(game, {\n type: \"playerCenterPileInitialized\",\n username: player.username,\n card: player.centerPile[player.centerPile.length - 1]!,\n });\n otherPlayer.centerPile.push(...cards.splice(0, 1));\n emitEvent(game, {\n type: \"playerCenterPileInitialized\",\n username: otherPlayer.username,\n card: otherPlayer.centerPile[otherPlayer.centerPile.length - 1]!,\n });\n otherPlayer.sidePile.push(...cards.splice(0, 5));\n emitEvent(game, {\n type: \"playerSidePileInitialized\",\n username: otherPlayer.username,\n numCards: otherPlayer.sidePile.length,\n });\n } else {\n const playerSidePileCard = player.sidePile.pop()!;\n player.centerPile.push(playerSidePileCard);\n emitEvent(game, {\n type: \"cardDrawnFromSidePileToCenterPile\",\n username: player.username,\n card: playerSidePileCard,\n });\n const otherPlayerSidePileCard = otherPlayer.sidePile.pop()!;\n otherPlayer.centerPile.push(otherPlayerSidePileCard);\n emitEvent(game, {\n type: \"cardDrawnFromSidePileToCenterPile\",\n username: otherPlayer.username,\n card: otherPlayerSidePileCard,\n });\n }\n player.hasNoPlayableCards = false;\n otherPlayer.hasNoPlayableCards = false;\n game.canPlayAt = Date.now() + CAN_PLAY_AT_DELAY_MS;\n emitEvent(game, {\n type: \"canPlayAtUpdated\",\n canPlayAt: game.canPlayAt,\n });\n }\n\n return { success: true };\n }\n\n sendChat(gameId: string, playerId: string, text: string): SendChatResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n const message: ChatMessage = {\n id: crypto.randomUUID(),\n username: player.username,\n text,\n };\n game.chatMessages.push(message);\n emitEvent(game, { type: \"chat\", message });\n return { success: true };\n }\n\n startGame(gameId: string, playerId: string): StartGameResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n if (game.status !== \"open\") {\n return { success: false, error: \"invalidStatus\" };\n }\n if (game.players.indexOf(player) !== 0) {\n return { success: false, error: \"playerNotAdmin\" };\n }\n if (game.players.length < MIN_PLAYERS) {\n return { success: false, error: \"minPlayersNotReached\" };\n }\n const otherPlayer = game.players[1]!;\n const cards = [...CARDS];\n shuffleCards(cards);\n\n player.sidePile.push(...cards.splice(0, 5));\n emitEvent(game, {\n type: \"playerSidePileInitialized\",\n username: player.username,\n numCards: player.sidePile.length,\n });\n\n player.centerPile.push(...cards.splice(0, 1));\n emitEvent(game, {\n type: \"playerCenterPileInitialized\",\n username: player.username,\n card: player.centerPile[player.centerPile.length - 1]!,\n });\n\n otherPlayer.centerPile.push(...cards.splice(0, 1));\n emitEvent(game, {\n type: \"playerCenterPileInitialized\",\n username: otherPlayer.username,\n card: otherPlayer.centerPile[otherPlayer.centerPile.length - 1]!,\n });\n\n otherPlayer.sidePile.push(...cards.splice(0, 5));\n emitEvent(game, {\n type: \"playerSidePileInitialized\",\n username: otherPlayer.username,\n numCards: otherPlayer.sidePile.length,\n });\n\n player.drawPile.push(...cards.splice(0, 15));\n emitEvent(game, {\n type: \"playerDrawPileInitialized\",\n username: player.username,\n numCards: player.drawPile.length,\n });\n\n player.hand.push(...cards.splice(0, 5));\n emitEventToPlayer(player, { type: \"handInitialized\", cards: player.hand });\n emitEvent(game, {\n type: \"playerHandInitialized\",\n username: player.username,\n numCards: player.hand.length,\n });\n\n otherPlayer.drawPile.push(...cards.splice(0, 15));\n emitEvent(game, {\n type: \"playerDrawPileInitialized\",\n username: otherPlayer.username,\n numCards: otherPlayer.drawPile.length,\n });\n\n otherPlayer.hand.push(...cards.splice(0, 5));\n emitEventToPlayer(otherPlayer, {\n type: \"handInitialized\",\n cards: otherPlayer.hand,\n });\n emitEvent(game, {\n type: \"playerHandInitialized\",\n username: otherPlayer.username,\n numCards: otherPlayer.hand.length,\n });\n\n const startedGame: Game = {\n ...game,\n status: \"started\",\n expiresAt: Date.now() + EXPIRY_EXTENSION_MS,\n canPlayAt: Date.now() + CAN_PLAY_AT_DELAY_MS,\n };\n this.games.set(game.id, startedGame);\n emitEvent(startedGame, { type: \"gameStarted\" });\n emitEvent(startedGame, {\n type: \"expirationUpdated\",\n expiresAt: startedGame.expiresAt,\n });\n emitEvent(startedGame, {\n type: \"canPlayAtUpdated\",\n canPlayAt: startedGame.canPlayAt,\n });\n return { success: true };\n }\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"manager-BpKvQbcp.cjs","names":[],"sources":["../src/manager/constants.ts","../src/manager/Watchdog.ts","../src/lib/emitEvent.ts","../src/lib/emitEventToPlayer.ts","../src/manager/lib/isCardPlayable.ts","../src/manager/lib/hasPlayableCard.ts","../src/lib/shuffleCards.ts","../src/manager/Manager.ts"],"sourcesContent":["import type { Card } from \"./types/Card.js\";\n\nexport const GAME_KEY = \"speed\";\nexport const MAX_GAMES = 50;\nexport const WATCHDOG_INTERVAL_MS = 10000;\n\nexport const EXPIRY_EXTENSION_MS = 300000; // 5 minutes\nexport const MIN_PLAYERS = 2;\nexport const MAX_PLAYERS = 2;\nexport const MAX_HAND_SIZE = 5;\nexport const CAN_PLAY_AT_DELAY_MS = 3000;\n\nexport const CARDS: readonly Card[] = [\n { id: crypto.randomUUID(), rank: 2, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 3, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 4, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 5, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 6, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 7, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 8, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 9, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 10, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 11, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 12, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 13, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 14, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 2, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 3, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 4, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 5, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 6, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 7, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 8, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 9, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 10, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 11, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 12, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 13, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 14, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 2, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 3, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 4, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 5, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 6, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 7, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 8, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 9, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 10, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 11, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 12, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 13, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 14, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 2, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 3, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 4, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 5, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 6, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 7, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 8, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 9, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 10, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 11, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 12, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 13, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 14, suite: \"spades\" },\n];\n","import { GAME_KEY, WATCHDOG_INTERVAL_MS } from \"./constants.js\";\n\ntype Game = {\n readonly id: string;\n readonly expiresAt: number;\n};\n\nexport class Watchdog<TGame extends Game> {\n private readonly games: Map<string, TGame>;\n\n constructor(games: Map<string, TGame>) {\n this.games = games;\n }\n\n start() {\n console.log(`watchdog start at ${Date.now()} (${GAME_KEY})`);\n setInterval(() => this.wakeUp(), WATCHDOG_INTERVAL_MS);\n }\n\n wakeUp() {\n const now = Date.now();\n // console.log(`watchdog wakeUp at ${now} (${GAME_KEY})`);\n for (const game of this.games.values()) {\n if (game.expiresAt <= now) {\n this.games.delete(game.id);\n console.log(`watchdog purged ${game.id} (${GAME_KEY})`);\n }\n }\n }\n}\n","type Game = {\n readonly players: {\n readonly events: { readonly id: string }[];\n }[];\n};\n\ntype OmitId<T> = T extends unknown ? Omit<T, \"id\"> : never;\n\nexport function emitEvent<T extends Game>(\n game: T,\n data: OmitId<T[\"players\"][number][\"events\"][number]>,\n): void {\n const event = { ...data, id: crypto.randomUUID() };\n for (const p of game.players) {\n p.events.push(event);\n }\n}\n","type Player = {\n readonly events: { readonly id: string }[];\n};\n\ntype OmitId<T> = T extends unknown ? Omit<T, \"id\"> : never;\n\nexport function emitEventToPlayer<T extends Player>(\n player: T,\n data: OmitId<T[\"events\"][number]>,\n): void {\n player.events.push({ ...data, id: crypto.randomUUID() });\n}\n","import type { Card } from \"../types/Card.js\";\n\nexport function isCardPlayable(card: Card, targetPile: Card[]) {\n const targetCard = targetPile[targetPile.length - 1]!;\n const diff = Math.abs(card.rank - targetCard.rank) % 11;\n return diff === 1;\n}\n","import { isCardPlayable } from \"./isCardPlayable.js\";\nimport type { Game } from \"../types/Game.js\";\nimport type { Player } from \"../types/Player.js\";\n\nexport function hasPlayableCard(player: Player, game: Game) {\n const otherPlayer = game.players.find((p) => p !== player)!;\n const targetPile1 = player.centerPile;\n const targetPile2 = otherPlayer.centerPile;\n for (const card of player.hand) {\n if (\n isCardPlayable(card, targetPile1) ||\n isCardPlayable(card, targetPile2)\n ) {\n return true;\n }\n }\n return false;\n}\n","type Card = { readonly id: string };\n\nexport function shuffleCards<T extends Card>(cards: T[]): void {\n for (let i = cards.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [cards[i], cards[j]] = [cards[j]!, cards[i]!];\n }\n}\n","import {\n CAN_PLAY_AT_DELAY_MS,\n CARDS,\n EXPIRY_EXTENSION_MS,\n MAX_GAMES,\n MAX_HAND_SIZE,\n MAX_PLAYERS,\n MIN_PLAYERS,\n} from \"./constants.js\";\nimport { Watchdog } from \"./Watchdog.js\";\nimport { emitEvent } from \"../lib/emitEvent.js\";\nimport { emitEventToPlayer } from \"../lib/emitEventToPlayer.js\";\nimport { isCardPlayable } from \"./lib/isCardPlayable.js\";\nimport { hasPlayableCard } from \"./lib/hasPlayableCard.js\";\nimport { shuffleCards } from \"../lib/shuffleCards.js\";\nimport type { ChatMessage } from \"./types/ChatMessage.js\";\nimport type { ClientState } from \"./types/ClientState.js\";\nimport type { Game } from \"./types/Game.js\";\nimport type { GameEvent } from \"./types/GameEvent.js\";\nimport type { Player } from \"./types/Player.js\";\n\nexport type CreateGameResult =\n | {\n readonly success: true;\n readonly gameId: string;\n readonly playerId: string;\n }\n | {\n readonly success: false;\n readonly error: \"maxGamesReached\";\n };\n\nexport type DrawCardResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"gameNotFound\"\n | \"playerNotFound\"\n | \"invalidStatus\"\n | \"canPlayAtNotReached\"\n | \"handFull\"\n | \"drawPileEmpty\";\n };\n\nexport type GetClientStateAndClearEventsResult =\n | {\n readonly success: true;\n readonly state: ClientState;\n }\n | {\n readonly success: false;\n readonly error: \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type GetEventsAndClearAcknowledgedResult =\n | {\n readonly success: true;\n readonly events: readonly GameEvent[];\n }\n | {\n readonly success: false;\n readonly error: \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type GetJoinableGamesResult = {\n readonly games: readonly {\n readonly id: string;\n readonly numPlayers: number;\n }[];\n};\n\nexport type JoinGameResult =\n | {\n readonly success: true;\n readonly playerId: string;\n }\n | {\n readonly success: false;\n readonly error:\n | \"gameNotFound\"\n | \"invalidStatus\"\n | \"maxPlayersReached\"\n | \"alreadyInGame\";\n };\n\nexport type LeaveGameResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error: \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type PlayCardResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"gameNotFound\"\n | \"playerNotFound\"\n | \"invalidStatus\"\n | \"canPlayAtNotReached\"\n | \"cardNotFound\"\n | \"cardNotPlayable\";\n };\n\nexport type ReportNoPlayableCardsResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"gameNotFound\"\n | \"playerNotFound\"\n | \"invalidStatus\"\n | \"canPlayAtNotReached\"\n | \"alreadyReported\"\n | \"hasPlayableCard\"\n | \"canDraw\";\n };\n\nexport type SendChatResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error: \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport type StartGameResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"gameNotFound\"\n | \"invalidStatus\"\n | \"playerNotFound\"\n | \"playerNotAdmin\"\n | \"minPlayersNotReached\";\n };\n\nexport class Manager {\n private readonly games: Map<string, Game>;\n private readonly watchdog: Watchdog<Game>;\n\n constructor() {\n this.games = new Map<string, Game>();\n this.watchdog = new Watchdog(this.games);\n this.watchdog.start();\n }\n\n createGame(userId: string, username: string): CreateGameResult {\n if (this.games.size === MAX_GAMES) {\n return { success: false, error: \"maxGamesReached\" };\n }\n const player: Player = {\n id: crypto.randomUUID(),\n userId,\n username,\n events: [],\n hand: [],\n drawPile: [],\n sidePile: [],\n centerPile: [],\n hasNoPlayableCards: false,\n };\n const createdAt = Date.now();\n const game: Game = {\n status: \"open\",\n id: crypto.randomUUID(),\n createdAt,\n expiresAt: createdAt + EXPIRY_EXTENSION_MS,\n chatMessages: [],\n players: [player],\n };\n this.games.set(game.id, game);\n return { success: true, gameId: game.id, playerId: player.id };\n }\n\n drawCard(gameId: string, playerId: string): DrawCardResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n if (game.status !== \"started\") {\n return { success: false, error: \"invalidStatus\" };\n }\n if (Date.now() < game.canPlayAt) {\n return { success: false, error: \"canPlayAtNotReached\" };\n }\n if (player.hand.length >= MAX_HAND_SIZE) {\n return { success: false, error: \"handFull\" };\n }\n if (player.drawPile.length === 0) {\n return { success: false, error: \"drawPileEmpty\" };\n }\n game.expiresAt = Date.now() + EXPIRY_EXTENSION_MS;\n emitEvent(game, { type: \"expirationUpdated\", expiresAt: game.expiresAt });\n const card = player.drawPile.pop()!;\n player.hand.push(card);\n emitEventToPlayer(player, { type: \"drewCard\", card });\n emitEvent(game, { type: \"playerDrewCard\", username: player.username });\n return { success: true };\n }\n\n getClientStateAndClearEvents(\n gameId: string,\n playerId: string,\n ): GetClientStateAndClearEventsResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n const state: ClientState = {\n status: game.status,\n gameId,\n playerId,\n username: player.username,\n players: game.players.map((p) => ({\n username: p.username,\n handSize: p.hand.length,\n drawPileSize: p.drawPile.length,\n sidePileSize: p.sidePile.length,\n centerPileTopCard: p.centerPile.length\n ? p.centerPile[p.centerPile.length - 1]!\n : null,\n })),\n expiresAt: game.expiresAt,\n chatMessages: game.chatMessages,\n hand: player.hand,\n canPlayAt: game.status === \"started\" ? game.canPlayAt : null,\n };\n player.events.length = 0;\n return { success: true, state };\n }\n\n getEventsAndClearAcknowledged(\n gameId: string,\n playerId: string,\n lastReadId: string | null,\n ): GetEventsAndClearAcknowledgedResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n const lastReadEventIndex = player.events.findIndex(\n (e) => e.id === lastReadId,\n );\n player.events.splice(0, lastReadEventIndex + 1);\n return { success: true, events: player.events };\n }\n\n getJoinableGames(): GetJoinableGamesResult {\n return {\n games: Array.from(this.games.values())\n .filter((g) => g.status === \"open\" && g.players.length < MAX_PLAYERS)\n .map((g) => ({\n id: g.id,\n numPlayers: g.players.length,\n })),\n };\n }\n\n joinGame(gameId: string, userId: string, username: string): JoinGameResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n if (game.status !== \"open\") {\n return { success: false, error: \"invalidStatus\" };\n }\n if (game.players.length === MAX_PLAYERS) {\n return { success: false, error: \"maxPlayersReached\" };\n }\n if (game.players.find((p) => p.userId === userId)) {\n return { success: false, error: \"alreadyInGame\" };\n }\n const player: Player = {\n id: crypto.randomUUID(),\n userId,\n username,\n events: [],\n hand: [],\n drawPile: [],\n sidePile: [],\n centerPile: [],\n hasNoPlayableCards: false,\n };\n game.players.push(player);\n emitEvent(game, { type: \"playerJoined\", username });\n return { success: true, playerId: player.id };\n }\n\n leaveGame(gameId: string, playerId: string): LeaveGameResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const playerIndex = game.players.findIndex((p) => p.id === playerId);\n if (playerIndex === -1) {\n return { success: false, error: \"playerNotFound\" };\n }\n\n const player = game.players[playerIndex]!;\n emitEvent(game, { type: \"playerLeft\", username: player.username });\n\n game.players.splice(playerIndex, 1);\n\n if (game.status === \"started\" && game.players.length < MIN_PLAYERS) {\n const forfeitedGame: Game = {\n ...game,\n status: \"forfeited\",\n expiresAt: Date.now() + EXPIRY_EXTENSION_MS,\n };\n this.games.set(game.id, forfeitedGame);\n emitEvent(forfeitedGame, { type: \"gameForfeited\" });\n emitEvent(forfeitedGame, {\n type: \"expirationUpdated\",\n expiresAt: forfeitedGame.expiresAt,\n });\n }\n if (game.players.length === 0) {\n this.games.delete(game.id);\n }\n return { success: true };\n }\n\n playCard(\n gameId: string,\n playerId: string,\n cardId: string,\n isForOtherPlayerPile: boolean,\n ): PlayCardResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n if (game.status !== \"started\") {\n return { success: false, error: \"invalidStatus\" };\n }\n if (Date.now() < game.canPlayAt) {\n return { success: false, error: \"canPlayAtNotReached\" };\n }\n const cardIndex = player.hand.findIndex((c) => c.id === cardId);\n if (cardIndex === -1) {\n return { success: false, error: \"cardNotFound\" };\n }\n const otherPlayer = game.players.find((p) => p !== player)!;\n const targetPile = isForOtherPlayerPile\n ? otherPlayer.centerPile\n : player.centerPile;\n const card = player.hand[cardIndex]!;\n if (!isCardPlayable(card, targetPile)) {\n return { success: false, error: \"cardNotPlayable\" };\n }\n\n game.expiresAt = Date.now() + EXPIRY_EXTENSION_MS;\n emitEvent(game, { type: \"expirationUpdated\", expiresAt: game.expiresAt });\n\n player.hand.splice(cardIndex, 1);\n targetPile.push(card);\n emitEvent(game, {\n type: \"cardPlayed\",\n username: player.username,\n card,\n isForOtherPlayerPile,\n });\n\n if (otherPlayer.hasNoPlayableCards && hasPlayableCard(otherPlayer, game)) {\n otherPlayer.hasNoPlayableCards = false;\n }\n\n if (player.hand.length === 0 && player.drawPile.length === 0) {\n emitEvent(game, { type: \"gameCompleted\" });\n const completedGame: Game = {\n ...game,\n status: \"completed\",\n };\n this.games.set(game.id, completedGame);\n }\n\n return { success: true };\n }\n\n reportNoPlayableCards(\n gameId: string,\n playerId: string,\n ): ReportNoPlayableCardsResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n if (game.status !== \"started\") {\n return { success: false, error: \"invalidStatus\" };\n }\n if (Date.now() < game.canPlayAt) {\n return { success: false, error: \"canPlayAtNotReached\" };\n }\n if (player.hasNoPlayableCards) {\n return { success: false, error: \"alreadyReported\" };\n }\n if (hasPlayableCard(player, game)) {\n return { success: false, error: \"hasPlayableCard\" };\n }\n if (player.drawPile.length > 0 && player.hand.length < MAX_HAND_SIZE) {\n return { success: false, error: \"canDraw\" };\n }\n\n game.expiresAt = Date.now() + EXPIRY_EXTENSION_MS;\n emitEvent(game, { type: \"expirationUpdated\", expiresAt: game.expiresAt });\n\n player.hasNoPlayableCards = true;\n\n const otherPlayer = game.players.find((p) => p !== player)!;\n if (otherPlayer.hasNoPlayableCards) {\n if (player.sidePile.length === 0 && otherPlayer.sidePile.length === 0) {\n const cards = [...player.centerPile, ...otherPlayer.centerPile];\n shuffleCards(cards);\n player.sidePile.push(...cards.splice(0, 5));\n emitEvent(game, {\n type: \"playerSidePileInitialized\",\n username: player.username,\n numCards: player.sidePile.length,\n });\n player.centerPile.push(...cards.splice(0, 1));\n emitEvent(game, {\n type: \"playerCenterPileInitialized\",\n username: player.username,\n card: player.centerPile[player.centerPile.length - 1]!,\n });\n otherPlayer.centerPile.push(...cards.splice(0, 1));\n emitEvent(game, {\n type: \"playerCenterPileInitialized\",\n username: otherPlayer.username,\n card: otherPlayer.centerPile[otherPlayer.centerPile.length - 1]!,\n });\n otherPlayer.sidePile.push(...cards.splice(0, 5));\n emitEvent(game, {\n type: \"playerSidePileInitialized\",\n username: otherPlayer.username,\n numCards: otherPlayer.sidePile.length,\n });\n } else {\n const playerSidePileCard = player.sidePile.pop()!;\n player.centerPile.push(playerSidePileCard);\n emitEvent(game, {\n type: \"cardDrawnFromSidePileToCenterPile\",\n username: player.username,\n card: playerSidePileCard,\n });\n const otherPlayerSidePileCard = otherPlayer.sidePile.pop()!;\n otherPlayer.centerPile.push(otherPlayerSidePileCard);\n emitEvent(game, {\n type: \"cardDrawnFromSidePileToCenterPile\",\n username: otherPlayer.username,\n card: otherPlayerSidePileCard,\n });\n }\n player.hasNoPlayableCards = false;\n otherPlayer.hasNoPlayableCards = false;\n game.canPlayAt = Date.now() + CAN_PLAY_AT_DELAY_MS;\n emitEvent(game, {\n type: \"canPlayAtUpdated\",\n canPlayAt: game.canPlayAt,\n });\n }\n\n return { success: true };\n }\n\n sendChat(gameId: string, playerId: string, text: string): SendChatResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n const message: ChatMessage = {\n id: crypto.randomUUID(),\n username: player.username,\n text,\n };\n game.chatMessages.push(message);\n emitEvent(game, { type: \"chat\", message });\n return { success: true };\n }\n\n startGame(gameId: string, playerId: string): StartGameResult {\n const game = this.games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n if (game.status !== \"open\") {\n return { success: false, error: \"invalidStatus\" };\n }\n if (game.players.indexOf(player) !== 0) {\n return { success: false, error: \"playerNotAdmin\" };\n }\n if (game.players.length < MIN_PLAYERS) {\n return { success: false, error: \"minPlayersNotReached\" };\n }\n const otherPlayer = game.players[1]!;\n const cards = [...CARDS];\n shuffleCards(cards);\n\n player.sidePile.push(...cards.splice(0, 5));\n emitEvent(game, {\n type: \"playerSidePileInitialized\",\n username: player.username,\n numCards: player.sidePile.length,\n });\n\n player.centerPile.push(...cards.splice(0, 1));\n emitEvent(game, {\n type: \"playerCenterPileInitialized\",\n username: player.username,\n card: player.centerPile[player.centerPile.length - 1]!,\n });\n\n otherPlayer.centerPile.push(...cards.splice(0, 1));\n emitEvent(game, {\n type: \"playerCenterPileInitialized\",\n username: otherPlayer.username,\n card: otherPlayer.centerPile[otherPlayer.centerPile.length - 1]!,\n });\n\n otherPlayer.sidePile.push(...cards.splice(0, 5));\n emitEvent(game, {\n type: \"playerSidePileInitialized\",\n username: otherPlayer.username,\n numCards: otherPlayer.sidePile.length,\n });\n\n player.drawPile.push(...cards.splice(0, 15));\n emitEvent(game, {\n type: \"playerDrawPileInitialized\",\n username: player.username,\n numCards: player.drawPile.length,\n });\n\n player.hand.push(...cards.splice(0, 5));\n emitEventToPlayer(player, { type: \"handInitialized\", cards: player.hand });\n emitEvent(game, {\n type: \"playerHandInitialized\",\n username: player.username,\n numCards: player.hand.length,\n });\n\n otherPlayer.drawPile.push(...cards.splice(0, 15));\n emitEvent(game, {\n type: \"playerDrawPileInitialized\",\n username: otherPlayer.username,\n numCards: otherPlayer.drawPile.length,\n });\n\n otherPlayer.hand.push(...cards.splice(0, 5));\n emitEventToPlayer(otherPlayer, {\n type: \"handInitialized\",\n cards: otherPlayer.hand,\n });\n emitEvent(game, {\n type: \"playerHandInitialized\",\n username: otherPlayer.username,\n numCards: otherPlayer.hand.length,\n });\n\n const startedGame: Game = {\n ...game,\n status: \"started\",\n expiresAt: Date.now() + EXPIRY_EXTENSION_MS,\n canPlayAt: Date.now() + CAN_PLAY_AT_DELAY_MS,\n };\n this.games.set(game.id, startedGame);\n emitEvent(startedGame, { type: \"gameStarted\" });\n emitEvent(startedGame, {\n type: \"expirationUpdated\",\n expiresAt: startedGame.expiresAt,\n });\n emitEvent(startedGame, {\n type: \"canPlayAtUpdated\",\n canPlayAt: startedGame.canPlayAt,\n });\n return { success: true };\n }\n}\n"],"mappings":"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"}