@hellacardgames/speed 0.5.2 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/client/index.cjs.map +1 -1
  2. package/dist/client/index.d.cts +2 -3
  3. package/dist/client/index.d.cts.map +1 -1
  4. package/dist/client/index.d.mts +2 -3
  5. package/dist/client/index.d.mts.map +1 -1
  6. package/dist/client/index.mjs.map +1 -1
  7. package/dist/index-BilCO_4a.d.cts +216 -0
  8. package/dist/index-BilCO_4a.d.cts.map +1 -0
  9. package/dist/index-BilCO_4a.d.mts +216 -0
  10. package/dist/index-BilCO_4a.d.mts.map +1 -0
  11. package/dist/{startGame-5InkAgEp.d.cts → index-DSPZZlhq.d.cts} +39 -4
  12. package/dist/index-DSPZZlhq.d.cts.map +1 -0
  13. package/dist/{startGame-ayp8a-Cl.d.mts → index-S-oO1z9X.d.mts} +39 -4
  14. package/dist/index-S-oO1z9X.d.mts.map +1 -0
  15. package/dist/manager/index.cjs +12 -32
  16. package/dist/manager/index.d.cts +2 -109
  17. package/dist/manager/index.d.mts +2 -109
  18. package/dist/manager/index.mjs +1 -21
  19. package/dist/{startGame-BC9XfFzG.mjs → manager-CPmezdJa.mjs} +21 -2
  20. package/dist/manager-CPmezdJa.mjs.map +1 -0
  21. package/dist/{startGame-Bhk__XWk.cjs → manager-DErQ_XUq.cjs} +20 -19
  22. package/dist/manager-DErQ_XUq.cjs.map +1 -0
  23. package/dist/server/index.cjs +12 -12
  24. package/dist/server/index.cjs.map +1 -1
  25. package/dist/server/index.d.cts +3 -40
  26. package/dist/server/index.d.mts +3 -40
  27. package/dist/server/index.mjs +1 -1
  28. package/dist/server/index.mjs.map +1 -1
  29. package/package.json +1 -1
  30. package/dist/GameEvent-DDQ4jXxt.d.cts +0 -111
  31. package/dist/GameEvent-DDQ4jXxt.d.cts.map +0 -1
  32. package/dist/GameEvent-DDQ4jXxt.d.mts +0 -111
  33. package/dist/GameEvent-DDQ4jXxt.d.mts.map +0 -1
  34. package/dist/manager/index.cjs.map +0 -1
  35. package/dist/manager/index.d.cts.map +0 -1
  36. package/dist/manager/index.d.mts.map +0 -1
  37. package/dist/manager/index.mjs.map +0 -1
  38. package/dist/server/index.d.cts.map +0 -1
  39. package/dist/server/index.d.mts.map +0 -1
  40. package/dist/startGame-5InkAgEp.d.cts.map +0 -1
  41. package/dist/startGame-BC9XfFzG.mjs.map +0 -1
  42. package/dist/startGame-Bhk__XWk.cjs.map +0 -1
  43. package/dist/startGame-ayp8a-Cl.d.mts.map +0 -1
@@ -1,5 +1,4 @@
1
- import { n as ClientState, t as GameEvent } from "./GameEvent-DDQ4jXxt.mjs";
2
-
1
+ import { d as ClientState, l as GameEvent } from "./index-BilCO_4a.mjs";
3
2
  //#region src/server/actions/createGame.d.ts
4
3
  type CreateGameResult = {
5
4
  readonly success: true;
@@ -104,5 +103,41 @@ type StartGameResult = {
104
103
  };
105
104
  declare function startGame(input: unknown): StartGameResult;
106
105
  //#endregion
107
- export { createGame as S, GetClientStateAndClearEventsResult as _, ReportNoPlayableCardsResult as a, drawCard as b, playCard as c, JoinGameResult as d, joinGame as f, getEventsAndClearAcknowledged as g, GetEventsAndClearAcknowledgedResult as h, sendChat as i, LeaveGameResult as l, getJoinableGames as m, startGame as n, reportNoPlayableCards as o, GetJoinableGamesResult as p, SendChatResult as r, PlayCardResult as s, StartGameResult as t, leaveGame as u, getClientStateAndClearEvents as v, CreateGameResult as x, DrawCardResult as y };
108
- //# sourceMappingURL=startGame-ayp8a-Cl.d.mts.map
106
+ //#region src/server/index.d.ts
107
+ declare const actions: readonly [{
108
+ readonly path: "/createGame";
109
+ readonly action: typeof createGame;
110
+ }, {
111
+ readonly path: "/drawCard";
112
+ readonly action: typeof drawCard;
113
+ }, {
114
+ readonly path: "/getClientStateAndClearEvents";
115
+ readonly action: typeof getClientStateAndClearEvents;
116
+ }, {
117
+ readonly path: "/getEventsAndClearAcknowledged";
118
+ readonly action: typeof getEventsAndClearAcknowledged;
119
+ }, {
120
+ readonly path: "/getJoinableGames";
121
+ readonly action: typeof getJoinableGames;
122
+ }, {
123
+ readonly path: "/joinGame";
124
+ readonly action: typeof joinGame;
125
+ }, {
126
+ readonly path: "/leaveGame";
127
+ readonly action: typeof leaveGame;
128
+ }, {
129
+ readonly path: "/playCard";
130
+ readonly action: typeof playCard;
131
+ }, {
132
+ readonly path: "/reportNoPlayableCards";
133
+ readonly action: typeof reportNoPlayableCards;
134
+ }, {
135
+ readonly path: "/sendChat";
136
+ readonly action: typeof sendChat;
137
+ }, {
138
+ readonly path: "/startGame";
139
+ readonly action: typeof startGame;
140
+ }];
141
+ //#endregion
142
+ export { PlayCardResult as a, GetJoinableGamesResult as c, DrawCardResult as d, CreateGameResult as f, ReportNoPlayableCardsResult as i, GetEventsAndClearAcknowledgedResult as l, StartGameResult as n, LeaveGameResult as o, SendChatResult as r, JoinGameResult as s, actions as t, GetClientStateAndClearEventsResult as u };
143
+ //# sourceMappingURL=index-S-oO1z9X.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-S-oO1z9X.d.mts","names":[],"sources":["../src/server/actions/createGame.ts","../src/server/actions/drawCard.ts","../src/server/actions/getClientStateAndClearEvents.ts","../src/server/actions/getEventsAndClearAcknowledged.ts","../src/server/actions/getJoinableGames.ts","../src/server/actions/joinGame.ts","../src/server/actions/leaveGame.ts","../src/server/actions/playCard.ts","../src/server/actions/reportNoPlayableCards.ts","../src/server/actions/sendChat.ts","../src/server/actions/startGame.ts","../src/server/index.ts"],"mappings":";;KAEY;WAEG;WACA;WACA;;WAGA;WACA;;iBAGC,WAAW,gBAAgB,mBAAmB;;;KCVlD;WAEG;;WAGA;WACA;;iBAiBC,SAAS,iBAAiB;;;KCtB9B;WAEG;WACA,OAAO;;WAGP;WACA;;iBAUC,6BACd,iBACC;;;KCnBS;WAEG;WACA,iBAAiB;;WAGjB;WACA;;iBAcC,8BACd,iBACC;;;KCzBS;WACD;aACE;aACA;;;iBAIG,oBAAoB;;;KCNxB;WAEG;WACA;;WAGA;WACA;;iBAcC,SACd,gBACA,gBACA,mBACC;;;KCzBS;WAEG;;WAGA;WACA;;iBAUC,UAAU,iBAAiB;;;KChB/B;WAEG;;WAGA;WACA;;iBAsBC,SAAS,iBAAiB;;;KC5B9B;WAEG;;WAGA;WACA;;iBAkBC,sBACd,iBACC;;;KC1BS;WAEG;;WAGA;WACA;;iBAWC,SAAS,iBAAiB;;;KCjB9B;WAEG;;WAGA;WACA;;iBAgBC,UAAU,iBAAiB;;;cCM9B"}
@@ -1,33 +1,13 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_startGame = require("../startGame-Bhk__XWk.cjs");
3
- //#region src/manager/watchdog.ts
4
- var Watchdog = class {
5
- start() {
6
- console.log(`watchdog start at ${Date.now()} (${require_startGame.GAME_KEY})`);
7
- setInterval(() => this.wakeUp(), require_startGame.WATCHDOG_INTERVAL_MS);
8
- }
9
- wakeUp() {
10
- const now = Date.now();
11
- for (const game of require_startGame.games.values()) if (game.expiresAt <= now) {
12
- require_startGame.games.delete(game.id);
13
- console.log(`watchdog purged ${game.id} (${require_startGame.GAME_KEY})`);
14
- }
15
- }
16
- };
17
- //#endregion
18
- //#region src/manager/index.ts
19
- new Watchdog().start();
20
- //#endregion
21
- exports.createGame = require_startGame.createGame;
22
- exports.drawCard = require_startGame.drawCard;
23
- exports.getClientStateAndClearEvents = require_startGame.getClientStateAndClearEvents;
24
- exports.getEventsAndClearAcknowledged = require_startGame.getEventsAndClearAcknowledged;
25
- exports.getJoinableGames = require_startGame.getJoinableGames;
26
- exports.joinGame = require_startGame.joinGame;
27
- exports.leaveGame = require_startGame.leaveGame;
28
- exports.playCard = require_startGame.playCard;
29
- exports.reportNoPlayableCards = require_startGame.reportNoPlayableCards;
30
- exports.sendChat = require_startGame.sendChat;
31
- exports.startGame = require_startGame.startGame;
32
-
33
- //# sourceMappingURL=index.cjs.map
2
+ const require_manager = require("../manager-DErQ_XUq.cjs");
3
+ exports.createGame = require_manager.createGame;
4
+ exports.drawCard = require_manager.drawCard;
5
+ exports.getClientStateAndClearEvents = require_manager.getClientStateAndClearEvents;
6
+ exports.getEventsAndClearAcknowledged = require_manager.getEventsAndClearAcknowledged;
7
+ exports.getJoinableGames = require_manager.getJoinableGames;
8
+ exports.joinGame = require_manager.joinGame;
9
+ exports.leaveGame = require_manager.leaveGame;
10
+ exports.playCard = require_manager.playCard;
11
+ exports.reportNoPlayableCards = require_manager.reportNoPlayableCards;
12
+ exports.sendChat = require_manager.sendChat;
13
+ exports.startGame = require_manager.startGame;
@@ -1,109 +1,2 @@
1
- import { n as ClientState, t as GameEvent } from "../GameEvent-DDQ4jXxt.cjs";
2
-
3
- //#region src/manager/actions/createGame.d.ts
4
- type CreateGameResult = {
5
- readonly success: true;
6
- readonly gameId: string;
7
- readonly playerId: string;
8
- } | {
9
- readonly success: false;
10
- readonly error: "maxGamesReached";
11
- };
12
- declare function createGame(userId: string, username: string): CreateGameResult;
13
- //#endregion
14
- //#region src/manager/actions/drawCard.d.ts
15
- type DrawCardResult = {
16
- readonly success: true;
17
- } | {
18
- readonly success: false;
19
- readonly error: "gameNotFound" | "playerNotFound" | "invalidStatus" | "canPlayAtNotReached" | "handFull" | "drawPileEmpty";
20
- };
21
- declare function drawCard(gameId: string, playerId: string): DrawCardResult;
22
- //#endregion
23
- //#region src/manager/actions/getClientStateAndClearEvents.d.ts
24
- type GetClientStateAndClearEventsResult = {
25
- readonly success: true;
26
- readonly state: ClientState;
27
- } | {
28
- readonly success: false;
29
- readonly error: "gameNotFound" | "playerNotFound";
30
- };
31
- declare function getClientStateAndClearEvents(gameId: string, playerId: string): GetClientStateAndClearEventsResult;
32
- //#endregion
33
- //#region src/manager/actions/getEventsAndClearAcknowledged.d.ts
34
- type GetEventsAndClearAcknowledgedResult = {
35
- readonly success: true;
36
- readonly events: readonly GameEvent[];
37
- } | {
38
- readonly success: false;
39
- readonly error: "gameNotFound" | "playerNotFound";
40
- };
41
- declare function getEventsAndClearAcknowledged(gameId: string, playerId: string, lastReadId: string | null): GetEventsAndClearAcknowledgedResult;
42
- //#endregion
43
- //#region src/manager/actions/getJoinableGames.d.ts
44
- type GetJoinableGamesResult = {
45
- readonly success: true;
46
- readonly games: readonly {
47
- readonly id: string;
48
- readonly numPlayers: number;
49
- }[];
50
- };
51
- declare function getJoinableGames(): GetJoinableGamesResult;
52
- //#endregion
53
- //#region src/manager/actions/joinGame.d.ts
54
- type JoinGameResult = {
55
- readonly success: true;
56
- readonly playerId: string;
57
- } | {
58
- readonly success: false;
59
- readonly error: "gameNotFound" | "invalidStatus" | "maxPlayersReached" | "alreadyInGame";
60
- };
61
- declare function joinGame(gameId: string, userId: string, username: string): JoinGameResult;
62
- //#endregion
63
- //#region src/manager/actions/leaveGame.d.ts
64
- type LeaveGameResult = {
65
- readonly success: true;
66
- } | {
67
- readonly success: false;
68
- readonly error: "gameNotFound" | "playerNotFound";
69
- };
70
- declare function leaveGame(gameId: string, playerId: string): LeaveGameResult;
71
- //#endregion
72
- //#region src/manager/actions/playCard.d.ts
73
- type PlayCardResult = {
74
- readonly success: true;
75
- } | {
76
- readonly success: false;
77
- readonly error: "gameNotFound" | "playerNotFound" | "invalidStatus" | "canPlayAtNotReached" | "cardNotFound" | "cardNotPlayable";
78
- };
79
- declare function playCard(gameId: string, playerId: string, cardId: string, isForOtherPlayerPile: boolean): PlayCardResult;
80
- //#endregion
81
- //#region src/manager/actions/reportNoPlayableCards.d.ts
82
- type ReportNoPlayableCardsResult = {
83
- readonly success: true;
84
- } | {
85
- readonly success: false;
86
- readonly error: "gameNotFound" | "playerNotFound" | "invalidStatus" | "canPlayAtNotReached" | "alreadyReported" | "hasPlayableCard" | "canDraw";
87
- };
88
- declare function reportNoPlayableCards(gameId: string, playerId: string): ReportNoPlayableCardsResult;
89
- //#endregion
90
- //#region src/manager/actions/sendChat.d.ts
91
- type SendChatResult = {
92
- readonly success: true;
93
- } | {
94
- readonly success: false;
95
- readonly error: "gameNotFound" | "playerNotFound";
96
- };
97
- declare function sendChat(gameId: string, playerId: string, text: string): SendChatResult;
98
- //#endregion
99
- //#region src/manager/actions/startGame.d.ts
100
- type StartGameResult = {
101
- readonly success: true;
102
- } | {
103
- readonly success: false;
104
- readonly error: "gameNotFound" | "playerNotFound" | "invalidStatus" | "playerNotAdmin" | "minPlayersNotReached";
105
- };
106
- declare function startGame(gameId: string, playerId: string): StartGameResult;
107
- //#endregion
108
- export { createGame, drawCard, getClientStateAndClearEvents, getEventsAndClearAcknowledged, getJoinableGames, joinGame, leaveGame, playCard, reportNoPlayableCards, sendChat, startGame };
109
- //# sourceMappingURL=index.d.cts.map
1
+ import { a as leaveGame, c as getEventsAndClearAcknowledged, d as ClientState, f as ChatMessage, h as createGame, i as playCard, l as GameEvent, m as drawCard, n as sendChat, o as joinGame, p as Card, r as reportNoPlayableCards, s as getJoinableGames, t as startGame, u as getClientStateAndClearEvents } from "../index-BilCO_4a.cjs";
2
+ export { type Card, type ChatMessage, type ClientState, type GameEvent, createGame, drawCard, getClientStateAndClearEvents, getEventsAndClearAcknowledged, getJoinableGames, joinGame, leaveGame, playCard, reportNoPlayableCards, sendChat, startGame };
@@ -1,109 +1,2 @@
1
- import { n as ClientState, t as GameEvent } from "../GameEvent-DDQ4jXxt.mjs";
2
-
3
- //#region src/manager/actions/createGame.d.ts
4
- type CreateGameResult = {
5
- readonly success: true;
6
- readonly gameId: string;
7
- readonly playerId: string;
8
- } | {
9
- readonly success: false;
10
- readonly error: "maxGamesReached";
11
- };
12
- declare function createGame(userId: string, username: string): CreateGameResult;
13
- //#endregion
14
- //#region src/manager/actions/drawCard.d.ts
15
- type DrawCardResult = {
16
- readonly success: true;
17
- } | {
18
- readonly success: false;
19
- readonly error: "gameNotFound" | "playerNotFound" | "invalidStatus" | "canPlayAtNotReached" | "handFull" | "drawPileEmpty";
20
- };
21
- declare function drawCard(gameId: string, playerId: string): DrawCardResult;
22
- //#endregion
23
- //#region src/manager/actions/getClientStateAndClearEvents.d.ts
24
- type GetClientStateAndClearEventsResult = {
25
- readonly success: true;
26
- readonly state: ClientState;
27
- } | {
28
- readonly success: false;
29
- readonly error: "gameNotFound" | "playerNotFound";
30
- };
31
- declare function getClientStateAndClearEvents(gameId: string, playerId: string): GetClientStateAndClearEventsResult;
32
- //#endregion
33
- //#region src/manager/actions/getEventsAndClearAcknowledged.d.ts
34
- type GetEventsAndClearAcknowledgedResult = {
35
- readonly success: true;
36
- readonly events: readonly GameEvent[];
37
- } | {
38
- readonly success: false;
39
- readonly error: "gameNotFound" | "playerNotFound";
40
- };
41
- declare function getEventsAndClearAcknowledged(gameId: string, playerId: string, lastReadId: string | null): GetEventsAndClearAcknowledgedResult;
42
- //#endregion
43
- //#region src/manager/actions/getJoinableGames.d.ts
44
- type GetJoinableGamesResult = {
45
- readonly success: true;
46
- readonly games: readonly {
47
- readonly id: string;
48
- readonly numPlayers: number;
49
- }[];
50
- };
51
- declare function getJoinableGames(): GetJoinableGamesResult;
52
- //#endregion
53
- //#region src/manager/actions/joinGame.d.ts
54
- type JoinGameResult = {
55
- readonly success: true;
56
- readonly playerId: string;
57
- } | {
58
- readonly success: false;
59
- readonly error: "gameNotFound" | "invalidStatus" | "maxPlayersReached" | "alreadyInGame";
60
- };
61
- declare function joinGame(gameId: string, userId: string, username: string): JoinGameResult;
62
- //#endregion
63
- //#region src/manager/actions/leaveGame.d.ts
64
- type LeaveGameResult = {
65
- readonly success: true;
66
- } | {
67
- readonly success: false;
68
- readonly error: "gameNotFound" | "playerNotFound";
69
- };
70
- declare function leaveGame(gameId: string, playerId: string): LeaveGameResult;
71
- //#endregion
72
- //#region src/manager/actions/playCard.d.ts
73
- type PlayCardResult = {
74
- readonly success: true;
75
- } | {
76
- readonly success: false;
77
- readonly error: "gameNotFound" | "playerNotFound" | "invalidStatus" | "canPlayAtNotReached" | "cardNotFound" | "cardNotPlayable";
78
- };
79
- declare function playCard(gameId: string, playerId: string, cardId: string, isForOtherPlayerPile: boolean): PlayCardResult;
80
- //#endregion
81
- //#region src/manager/actions/reportNoPlayableCards.d.ts
82
- type ReportNoPlayableCardsResult = {
83
- readonly success: true;
84
- } | {
85
- readonly success: false;
86
- readonly error: "gameNotFound" | "playerNotFound" | "invalidStatus" | "canPlayAtNotReached" | "alreadyReported" | "hasPlayableCard" | "canDraw";
87
- };
88
- declare function reportNoPlayableCards(gameId: string, playerId: string): ReportNoPlayableCardsResult;
89
- //#endregion
90
- //#region src/manager/actions/sendChat.d.ts
91
- type SendChatResult = {
92
- readonly success: true;
93
- } | {
94
- readonly success: false;
95
- readonly error: "gameNotFound" | "playerNotFound";
96
- };
97
- declare function sendChat(gameId: string, playerId: string, text: string): SendChatResult;
98
- //#endregion
99
- //#region src/manager/actions/startGame.d.ts
100
- type StartGameResult = {
101
- readonly success: true;
102
- } | {
103
- readonly success: false;
104
- readonly error: "gameNotFound" | "playerNotFound" | "invalidStatus" | "playerNotAdmin" | "minPlayersNotReached";
105
- };
106
- declare function startGame(gameId: string, playerId: string): StartGameResult;
107
- //#endregion
108
- export { createGame, drawCard, getClientStateAndClearEvents, getEventsAndClearAcknowledged, getJoinableGames, joinGame, leaveGame, playCard, reportNoPlayableCards, sendChat, startGame };
109
- //# sourceMappingURL=index.d.mts.map
1
+ import { a as leaveGame, c as getEventsAndClearAcknowledged, d as ClientState, f as ChatMessage, h as createGame, i as playCard, l as GameEvent, m as drawCard, n as sendChat, o as joinGame, p as Card, r as reportNoPlayableCards, s as getJoinableGames, t as startGame, u as getClientStateAndClearEvents } from "../index-BilCO_4a.mjs";
2
+ export { type Card, type ChatMessage, type ClientState, type GameEvent, createGame, drawCard, getClientStateAndClearEvents, getEventsAndClearAcknowledged, getJoinableGames, joinGame, leaveGame, playCard, reportNoPlayableCards, sendChat, startGame };
@@ -1,22 +1,2 @@
1
- import { a as leaveGame, c as getEventsAndClearAcknowledged, d as createGame, f as games, i as playCard, l as getClientStateAndClearEvents, m as WATCHDOG_INTERVAL_MS, n as sendChat, o as joinGame, p as GAME_KEY, r as reportNoPlayableCards, s as getJoinableGames, t as startGame, u as drawCard } from "../startGame-BC9XfFzG.mjs";
2
- //#region src/manager/watchdog.ts
3
- var Watchdog = class {
4
- start() {
5
- console.log(`watchdog start at ${Date.now()} (${GAME_KEY})`);
6
- setInterval(() => this.wakeUp(), WATCHDOG_INTERVAL_MS);
7
- }
8
- wakeUp() {
9
- const now = Date.now();
10
- for (const game of games.values()) if (game.expiresAt <= now) {
11
- games.delete(game.id);
12
- console.log(`watchdog purged ${game.id} (${GAME_KEY})`);
13
- }
14
- }
15
- };
16
- //#endregion
17
- //#region src/manager/index.ts
18
- new Watchdog().start();
19
- //#endregion
1
+ import { a as leaveGame, c as getEventsAndClearAcknowledged, d as createGame, i as playCard, l as getClientStateAndClearEvents, n as sendChat, o as joinGame, r as reportNoPlayableCards, s as getJoinableGames, t as startGame, u as drawCard } from "../manager-CPmezdJa.mjs";
20
2
  export { createGame, drawCard, getClientStateAndClearEvents, getEventsAndClearAcknowledged, getJoinableGames, joinGame, leaveGame, playCard, reportNoPlayableCards, sendChat, startGame };
21
-
22
- //# sourceMappingURL=index.mjs.map
@@ -269,6 +269,22 @@ const CARDS = [
269
269
  //#region src/manager/games.ts
270
270
  const games = /* @__PURE__ */ new Map();
271
271
  //#endregion
272
+ //#region src/manager/watchdog.ts
273
+ var Watchdog = class {
274
+ start() {
275
+ console.log(`watchdog start at ${Date.now()} (${GAME_KEY})`);
276
+ setInterval(() => this.wakeUp(), WATCHDOG_INTERVAL_MS);
277
+ }
278
+ wakeUp() {
279
+ const now = Date.now();
280
+ for (const game of games.values()) if (game.expiresAt <= now) {
281
+ games.delete(game.id);
282
+ console.log(`watchdog purged ${game.id} (${GAME_KEY})`);
283
+ }
284
+ }
285
+ };
286
+ const watchdog = new Watchdog();
287
+ //#endregion
272
288
  //#region src/manager/actions/createGame.ts
273
289
  function createGame(userId, username) {
274
290
  if (games.size === 50) return {
@@ -810,6 +826,9 @@ function startGame(gameId, playerId) {
810
826
  return { success: true };
811
827
  }
812
828
  //#endregion
813
- export { leaveGame as a, getEventsAndClearAcknowledged as c, createGame as d, games as f, playCard as i, getClientStateAndClearEvents as l, WATCHDOG_INTERVAL_MS as m, sendChat as n, joinGame as o, GAME_KEY as p, reportNoPlayableCards as r, getJoinableGames as s, startGame as t, drawCard as u };
829
+ //#region src/manager/index.ts
830
+ watchdog.start();
831
+ //#endregion
832
+ export { leaveGame as a, getEventsAndClearAcknowledged as c, createGame as d, playCard as i, getClientStateAndClearEvents as l, sendChat as n, joinGame as o, reportNoPlayableCards as r, getJoinableGames as s, startGame as t, drawCard as u };
814
833
 
815
- //# sourceMappingURL=startGame-BC9XfFzG.mjs.map
834
+ //# sourceMappingURL=manager-CPmezdJa.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager-CPmezdJa.mjs","names":[],"sources":["../src/manager/constants.ts","../src/manager/games.ts","../src/manager/watchdog.ts","../src/manager/actions/createGame.ts","../src/lib/emitEvent.ts","../src/lib/emitEventToPlayer.ts","../src/manager/actions/drawCard.ts","../src/manager/actions/getClientStateAndClearEvents.ts","../src/manager/actions/getEventsAndClearAcknowledged.ts","../src/manager/actions/getJoinableGames.ts","../src/manager/actions/joinGame.ts","../src/manager/actions/leaveGame.ts","../src/manager/lib/isCardPlayable.ts","../src/manager/lib/hasPlayableCard.ts","../src/manager/actions/playCard.ts","../src/lib/shuffleCards.ts","../src/manager/actions/reportNoPlayableCards.ts","../src/manager/actions/sendChat.ts","../src/manager/actions/startGame.ts","../src/manager/index.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 type { Game } from \"./types/Game.js\";\n\nexport const games = new Map<string, Game>();\n","import { GAME_KEY, WATCHDOG_INTERVAL_MS } from \"./constants.js\";\nimport { games } from \"./games.js\";\n\nclass Watchdog {\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 games.values()) {\n if (game.expiresAt <= now) {\n games.delete(game.id);\n console.log(`watchdog purged ${game.id} (${GAME_KEY})`);\n }\n }\n }\n}\n\nexport const watchdog = new Watchdog();\n","import { EXPIRY_EXTENSION_MS, MAX_GAMES } from \"../constants.js\";\nimport { games } from \"../games.js\";\nimport type { Player } from \"../types/Player.js\";\nimport type { Game } from \"../types/Game.js\";\n\ntype 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 function createGame(userId: string, username: string): CreateGameResult {\n if (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 games.set(game.id, game);\n return { success: true, gameId: game.id, playerId: player.id };\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 { emitEvent } from \"../../lib/emitEvent.js\";\nimport { emitEventToPlayer } from \"../../lib/emitEventToPlayer.js\";\nimport { EXPIRY_EXTENSION_MS, MAX_HAND_SIZE } from \"../constants.js\";\nimport { games } from \"../games.js\";\n\ntype 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 function drawCard(gameId: string, playerId: string): DrawCardResult {\n const game = 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","import { games } from \"../games.js\";\nimport type { ClientState } from \"../types/ClientState.js\";\n\ntype GetClientStateAndClearEventsResult =\n | {\n readonly success: true;\n readonly state: ClientState;\n }\n | {\n readonly success: false;\n readonly error: \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport function getClientStateAndClearEvents(\n gameId: string,\n playerId: string,\n): GetClientStateAndClearEventsResult {\n const game = 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","import { games } from \"../games.js\";\nimport type { GameEvent } from \"../types/GameEvent.js\";\n\ntype 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 function getEventsAndClearAcknowledged(\n gameId: string,\n playerId: string,\n lastReadId: string | null,\n): GetEventsAndClearAcknowledgedResult {\n const game = 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","import { MAX_PLAYERS } from \"../constants.js\";\nimport { games } from \"../games.js\";\n\ntype GetJoinableGamesResult = {\n readonly success: true;\n readonly games: readonly {\n readonly id: string;\n readonly numPlayers: number;\n }[];\n};\n\nexport function getJoinableGames(): GetJoinableGamesResult {\n return {\n success: true,\n games: Array.from(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","import { emitEvent } from \"../../lib/emitEvent.js\";\nimport { MAX_PLAYERS } from \"../constants.js\";\nimport { games } from \"../games.js\";\nimport type { Player } from \"../types/Player.js\";\n\ntype 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 function joinGame(\n gameId: string,\n userId: string,\n username: string,\n): JoinGameResult {\n const game = 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","import { emitEvent } from \"../../lib/emitEvent.js\";\nimport { EXPIRY_EXTENSION_MS, MIN_PLAYERS } from \"../constants.js\";\nimport { games } from \"../games.js\";\nimport type { Game } from \"../types/Game.js\";\n\ntype LeaveGameResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error: \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport function leaveGame(gameId: string, playerId: string): LeaveGameResult {\n const game = 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 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 games.delete(game.id);\n }\n return { success: true };\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","import { emitEvent } from \"../../lib/emitEvent.js\";\nimport { EXPIRY_EXTENSION_MS } from \"../constants.js\";\nimport { games } from \"../games.js\";\nimport { hasPlayableCard } from \"../lib/hasPlayableCard.js\";\nimport { isCardPlayable } from \"../lib/isCardPlayable.js\";\nimport type { Game } from \"../types/Game.js\";\n\ntype 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 function playCard(\n gameId: string,\n playerId: string,\n cardId: string,\n isForOtherPlayerPile: boolean,\n): PlayCardResult {\n const game = 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 games.set(game.id, completedGame);\n }\n\n return { success: true };\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 { emitEvent } from \"../../lib/emitEvent.js\";\nimport { shuffleCards } from \"../../lib/shuffleCards.js\";\nimport {\n CAN_PLAY_AT_DELAY_MS,\n EXPIRY_EXTENSION_MS,\n MAX_HAND_SIZE,\n} from \"../constants.js\";\nimport { games } from \"../games.js\";\nimport { hasPlayableCard } from \"../lib/hasPlayableCard.js\";\n\ntype 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 function reportNoPlayableCards(\n gameId: string,\n playerId: string,\n): ReportNoPlayableCardsResult {\n const game = 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","import { emitEvent } from \"../../lib/emitEvent.js\";\nimport { games } from \"../games.js\";\nimport type { ChatMessage } from \"../types/ChatMessage.js\";\n\ntype SendChatResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error: \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport function sendChat(\n gameId: string,\n playerId: string,\n text: string,\n): SendChatResult {\n const game = 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","import { emitEvent } from \"../../lib/emitEvent.js\";\nimport { emitEventToPlayer } from \"../../lib/emitEventToPlayer.js\";\nimport { shuffleCards } from \"../../lib/shuffleCards.js\";\nimport {\n CAN_PLAY_AT_DELAY_MS,\n CARDS,\n EXPIRY_EXTENSION_MS,\n MIN_PLAYERS,\n} from \"../constants.js\";\nimport { games } from \"../games.js\";\nimport type { Game } from \"../types/Game.js\";\n\ntype StartGameResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"gameNotFound\"\n | \"playerNotFound\"\n | \"invalidStatus\"\n | \"playerNotAdmin\"\n | \"minPlayersNotReached\";\n };\n\nexport function startGame(gameId: string, playerId: string): StartGameResult {\n const game = 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 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","import { watchdog } from \"./watchdog.js\";\n\nexport { createGame } from \"./actions/createGame.js\";\nexport { drawCard } from \"./actions/drawCard.js\";\nexport { getClientStateAndClearEvents } from \"./actions/getClientStateAndClearEvents.js\";\nexport { getEventsAndClearAcknowledged } from \"./actions/getEventsAndClearAcknowledged.js\";\nexport { getJoinableGames } from \"./actions/getJoinableGames.js\";\nexport { joinGame } from \"./actions/joinGame.js\";\nexport { leaveGame } from \"./actions/leaveGame.js\";\nexport { playCard } from \"./actions/playCard.js\";\nexport { reportNoPlayableCards } from \"./actions/reportNoPlayableCards.js\";\nexport { sendChat } from \"./actions/sendChat.js\";\nexport { startGame } from \"./actions/startGame.js\";\n\nexport type { Card } from \"./types/Card.js\";\nexport type { ChatMessage } from \"./types/ChatMessage.js\";\nexport type { ClientState } from \"./types/ClientState.js\";\nexport type { GameEvent } from \"./types/GameEvent.js\";\n\nwatchdog.start();\n"],"mappings":"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"}
@@ -269,6 +269,22 @@ const CARDS = [
269
269
  //#region src/manager/games.ts
270
270
  const games = /* @__PURE__ */ new Map();
271
271
  //#endregion
272
+ //#region src/manager/watchdog.ts
273
+ var Watchdog = class {
274
+ start() {
275
+ console.log(`watchdog start at ${Date.now()} (${GAME_KEY})`);
276
+ setInterval(() => this.wakeUp(), WATCHDOG_INTERVAL_MS);
277
+ }
278
+ wakeUp() {
279
+ const now = Date.now();
280
+ for (const game of games.values()) if (game.expiresAt <= now) {
281
+ games.delete(game.id);
282
+ console.log(`watchdog purged ${game.id} (${GAME_KEY})`);
283
+ }
284
+ }
285
+ };
286
+ const watchdog = new Watchdog();
287
+ //#endregion
272
288
  //#region src/manager/actions/createGame.ts
273
289
  function createGame(userId, username) {
274
290
  if (games.size === 50) return {
@@ -810,18 +826,9 @@ function startGame(gameId, playerId) {
810
826
  return { success: true };
811
827
  }
812
828
  //#endregion
813
- Object.defineProperty(exports, "GAME_KEY", {
814
- enumerable: true,
815
- get: function() {
816
- return GAME_KEY;
817
- }
818
- });
819
- Object.defineProperty(exports, "WATCHDOG_INTERVAL_MS", {
820
- enumerable: true,
821
- get: function() {
822
- return WATCHDOG_INTERVAL_MS;
823
- }
824
- });
829
+ //#region src/manager/index.ts
830
+ watchdog.start();
831
+ //#endregion
825
832
  Object.defineProperty(exports, "createGame", {
826
833
  enumerable: true,
827
834
  get: function() {
@@ -834,12 +841,6 @@ Object.defineProperty(exports, "drawCard", {
834
841
  return drawCard;
835
842
  }
836
843
  });
837
- Object.defineProperty(exports, "games", {
838
- enumerable: true,
839
- get: function() {
840
- return games;
841
- }
842
- });
843
844
  Object.defineProperty(exports, "getClientStateAndClearEvents", {
844
845
  enumerable: true,
845
846
  get: function() {
@@ -895,4 +896,4 @@ Object.defineProperty(exports, "startGame", {
895
896
  }
896
897
  });
897
898
 
898
- //# sourceMappingURL=startGame-Bhk__XWk.cjs.map
899
+ //# sourceMappingURL=manager-DErQ_XUq.cjs.map