@mnbroatch/boardgame.io 0.0.1

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 (296) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +102 -0
  3. package/ai/package.json +7 -0
  4. package/client/package.json +7 -0
  5. package/core/package.json +7 -0
  6. package/debug/package.json +7 -0
  7. package/dist/boardgameio.es.js +14238 -0
  8. package/dist/boardgameio.js +14277 -0
  9. package/dist/boardgameio.min.js +16 -0
  10. package/dist/cjs/Debug-9d141c06.js +9586 -0
  11. package/dist/cjs/ai-e0e8a768.js +377 -0
  12. package/dist/cjs/ai.js +20 -0
  13. package/dist/cjs/client-76dec77b.js +258 -0
  14. package/dist/cjs/client-a22d7500.js +524 -0
  15. package/dist/cjs/client.js +26 -0
  16. package/dist/cjs/core.js +52 -0
  17. package/dist/cjs/debug.js +18 -0
  18. package/dist/cjs/filter-player-view-bb02e2f6.js +89 -0
  19. package/dist/cjs/initialize-267fcd69.js +61 -0
  20. package/dist/cjs/internal.js +25 -0
  21. package/dist/cjs/master-2904879d.js +320 -0
  22. package/dist/cjs/master.js +18 -0
  23. package/dist/cjs/multiplayer.js +23 -0
  24. package/dist/cjs/plugin-random-7425844d.js +229 -0
  25. package/dist/cjs/plugins.js +59 -0
  26. package/dist/cjs/react-native.js +182 -0
  27. package/dist/cjs/react.js +727 -0
  28. package/dist/cjs/reducer-16eec232.js +1203 -0
  29. package/dist/cjs/server.js +4087 -0
  30. package/dist/cjs/socketio-7a0837eb.js +478 -0
  31. package/dist/cjs/testing.js +30 -0
  32. package/dist/cjs/transport-b1874dfa.js +37 -0
  33. package/dist/cjs/turn-order-b2ff8740.js +1136 -0
  34. package/dist/cjs/util-fcfd8fb8.js +140 -0
  35. package/dist/esm/Debug-0141fe2d.js +9577 -0
  36. package/dist/esm/ai-5c06e761.js +371 -0
  37. package/dist/esm/ai.js +8 -0
  38. package/dist/esm/client-2e653027.js +522 -0
  39. package/dist/esm/client-5f57c3f2.js +255 -0
  40. package/dist/esm/client.js +16 -0
  41. package/dist/esm/core.js +40 -0
  42. package/dist/esm/debug.js +10 -0
  43. package/dist/esm/filter-player-view-2c6cc96f.js +87 -0
  44. package/dist/esm/initialize-11d626ca.js +59 -0
  45. package/dist/esm/internal.js +10 -0
  46. package/dist/esm/master-fa8f2e43.js +318 -0
  47. package/dist/esm/master.js +10 -0
  48. package/dist/esm/multiplayer.js +14 -0
  49. package/dist/esm/plugin-random-087f861e.js +226 -0
  50. package/dist/esm/plugins.js +55 -0
  51. package/dist/esm/react-native.js +173 -0
  52. package/dist/esm/react.js +716 -0
  53. package/dist/esm/reducer-c46da7e5.js +1198 -0
  54. package/dist/esm/socketio-c22ffa65.js +455 -0
  55. package/dist/esm/testing.js +26 -0
  56. package/dist/esm/transport-ce07b771.js +35 -0
  57. package/dist/esm/turn-order-376d315e.js +1091 -0
  58. package/dist/esm/util-b6147cef.js +135 -0
  59. package/dist/types/packages/ai.d.ts +5 -0
  60. package/dist/types/packages/client.d.ts +3 -0
  61. package/dist/types/packages/core.d.ts +5 -0
  62. package/dist/types/packages/debug.d.ts +2 -0
  63. package/dist/types/packages/internal.d.ts +8 -0
  64. package/dist/types/packages/master.d.ts +2 -0
  65. package/dist/types/packages/multiplayer.d.ts +3 -0
  66. package/dist/types/packages/plugins.d.ts +3 -0
  67. package/dist/types/packages/react-native.d.ts +2 -0
  68. package/dist/types/packages/react.d.ts +3 -0
  69. package/dist/types/packages/server.d.ts +6 -0
  70. package/dist/types/packages/testing.d.ts +1 -0
  71. package/dist/types/src/ai/ai.d.ts +53 -0
  72. package/dist/types/src/ai/ai.test.d.ts +1 -0
  73. package/dist/types/src/ai/bot.d.ts +40 -0
  74. package/dist/types/src/ai/mcts-bot.d.ts +60 -0
  75. package/dist/types/src/ai/random-bot.d.ts +27 -0
  76. package/dist/types/src/client/client.d.ts +104 -0
  77. package/dist/types/src/client/client.test.d.ts +1 -0
  78. package/dist/types/src/client/debug/tests/debug.test.d.ts +1 -0
  79. package/dist/types/src/client/manager.d.ts +61 -0
  80. package/dist/types/src/client/react.d.ts +75 -0
  81. package/dist/types/src/client/react.ssr.test.d.ts +4 -0
  82. package/dist/types/src/client/react.test.d.ts +1 -0
  83. package/dist/types/src/client/transport/dummy.d.ts +18 -0
  84. package/dist/types/src/client/transport/local.d.ts +59 -0
  85. package/dist/types/src/client/transport/local.test.d.ts +1 -0
  86. package/dist/types/src/client/transport/socketio.d.ts +45 -0
  87. package/dist/types/src/client/transport/socketio.test.d.ts +1 -0
  88. package/dist/types/src/client/transport/transport.d.ts +50 -0
  89. package/dist/types/src/client/transport/transport.test.d.ts +1 -0
  90. package/dist/types/src/core/action-creators.d.ts +144 -0
  91. package/dist/types/src/core/action-types.d.ts +10 -0
  92. package/dist/types/src/core/backwards-compatibility.d.ts +12 -0
  93. package/dist/types/src/core/constants.d.ts +6 -0
  94. package/dist/types/src/core/errors.d.ts +15 -0
  95. package/dist/types/src/core/flow.d.ts +28 -0
  96. package/dist/types/src/core/flow.test.d.ts +1 -0
  97. package/dist/types/src/core/game-methods.d.ts +9 -0
  98. package/dist/types/src/core/game.d.ts +26 -0
  99. package/dist/types/src/core/game.test.d.ts +1 -0
  100. package/dist/types/src/core/initialize.d.ts +9 -0
  101. package/dist/types/src/core/logger.d.ts +2 -0
  102. package/dist/types/src/core/player-view.d.ts +7 -0
  103. package/dist/types/src/core/player-view.test.d.ts +1 -0
  104. package/dist/types/src/core/reducer.d.ts +155 -0
  105. package/dist/types/src/core/reducer.test.d.ts +1 -0
  106. package/dist/types/src/core/turn-order.d.ts +179 -0
  107. package/dist/types/src/core/turn-order.test.d.ts +8 -0
  108. package/dist/types/src/lobby/client.d.ts +194 -0
  109. package/dist/types/src/lobby/client.test.d.ts +1 -0
  110. package/dist/types/src/lobby/connection.d.ts +44 -0
  111. package/dist/types/src/lobby/connection.test.d.ts +1 -0
  112. package/dist/types/src/lobby/create-match-form.d.ts +26 -0
  113. package/dist/types/src/lobby/login-form.d.ts +23 -0
  114. package/dist/types/src/lobby/match-instance.d.ts +31 -0
  115. package/dist/types/src/lobby/react.d.ts +113 -0
  116. package/dist/types/src/lobby/react.ssr.test.d.ts +4 -0
  117. package/dist/types/src/lobby/react.test.d.ts +1 -0
  118. package/dist/types/src/master/filter-player-view.d.ts +96 -0
  119. package/dist/types/src/master/filter-player-view.test.d.ts +1 -0
  120. package/dist/types/src/master/master.d.ts +94 -0
  121. package/dist/types/src/master/master.test.d.ts +1 -0
  122. package/dist/types/src/plugins/events/events.d.ts +54 -0
  123. package/dist/types/src/plugins/events/events.test.d.ts +1 -0
  124. package/dist/types/src/plugins/main.d.ts +75 -0
  125. package/dist/types/src/plugins/main.test.d.ts +1 -0
  126. package/dist/types/src/plugins/plugin-events.d.ts +5 -0
  127. package/dist/types/src/plugins/plugin-immer.d.ts +7 -0
  128. package/dist/types/src/plugins/plugin-immer.test.d.ts +1 -0
  129. package/dist/types/src/plugins/plugin-log.d.ts +14 -0
  130. package/dist/types/src/plugins/plugin-log.test.d.ts +1 -0
  131. package/dist/types/src/plugins/plugin-player.d.ts +29 -0
  132. package/dist/types/src/plugins/plugin-player.test.d.ts +1 -0
  133. package/dist/types/src/plugins/plugin-random.d.ts +4 -0
  134. package/dist/types/src/plugins/plugin-serializable.d.ts +7 -0
  135. package/dist/types/src/plugins/plugin-serializable.test.d.ts +1 -0
  136. package/dist/types/src/plugins/random/random.alea.d.ts +19 -0
  137. package/dist/types/src/plugins/random/random.d.ts +54 -0
  138. package/dist/types/src/plugins/random/random.test.d.ts +1 -0
  139. package/dist/types/src/server/api.d.ts +13 -0
  140. package/dist/types/src/server/api.test.d.ts +1 -0
  141. package/dist/types/src/server/auth.d.ts +38 -0
  142. package/dist/types/src/server/auth.test.d.ts +1 -0
  143. package/dist/types/src/server/cors.d.ts +4 -0
  144. package/dist/types/src/server/cors.test.d.ts +1 -0
  145. package/dist/types/src/server/db/base.d.ts +192 -0
  146. package/dist/types/src/server/db/flatfile.d.ts +44 -0
  147. package/dist/types/src/server/db/flatfile.test.d.ts +1 -0
  148. package/dist/types/src/server/db/index.d.ts +4 -0
  149. package/dist/types/src/server/db/index.test.d.ts +1 -0
  150. package/dist/types/src/server/db/inmemory.d.ts +43 -0
  151. package/dist/types/src/server/db/inmemory.test.d.ts +1 -0
  152. package/dist/types/src/server/db/localstorage.d.ts +7 -0
  153. package/dist/types/src/server/db/localstorage.test.d.ts +1 -0
  154. package/dist/types/src/server/index.d.ts +68 -0
  155. package/dist/types/src/server/index.test.d.ts +1 -0
  156. package/dist/types/src/server/transport/pubsub/generic-pub-sub.d.ts +6 -0
  157. package/dist/types/src/server/transport/pubsub/in-memory-pub-sub.d.ts +7 -0
  158. package/dist/types/src/server/transport/pubsub/in-memory-pub-sub.test.d.ts +1 -0
  159. package/dist/types/src/server/transport/socketio-simultaneous.test.d.ts +1 -0
  160. package/dist/types/src/server/transport/socketio.d.ts +65 -0
  161. package/dist/types/src/server/transport/socketio.test.d.ts +1 -0
  162. package/dist/types/src/server/util.d.ts +35 -0
  163. package/dist/types/src/testing/mock-random.d.ts +15 -0
  164. package/dist/types/src/testing/mock-random.test.d.ts +1 -0
  165. package/dist/types/src/types.d.ts +387 -0
  166. package/internal/package.json +7 -0
  167. package/master/package.json +7 -0
  168. package/multiplayer/package.json +7 -0
  169. package/package.json +211 -0
  170. package/plugins/package.json +7 -0
  171. package/react/package.json +7 -0
  172. package/react-native/package.json +7 -0
  173. package/server/package.json +6 -0
  174. package/src/ai/ai.test.ts +433 -0
  175. package/src/ai/ai.ts +84 -0
  176. package/src/ai/bot.ts +122 -0
  177. package/src/ai/mcts-bot.ts +331 -0
  178. package/src/ai/random-bot.ts +20 -0
  179. package/src/client/client.test.ts +993 -0
  180. package/src/client/client.ts +588 -0
  181. package/src/client/debug/Debug.svelte +239 -0
  182. package/src/client/debug/Menu.svelte +65 -0
  183. package/src/client/debug/ai/AI.svelte +215 -0
  184. package/src/client/debug/ai/Options.svelte +48 -0
  185. package/src/client/debug/info/Info.svelte +22 -0
  186. package/src/client/debug/info/Item.svelte +24 -0
  187. package/src/client/debug/log/Log.svelte +157 -0
  188. package/src/client/debug/log/LogEvent.svelte +149 -0
  189. package/src/client/debug/log/LogMetadata.svelte +7 -0
  190. package/src/client/debug/log/PhaseMarker.svelte +27 -0
  191. package/src/client/debug/log/TurnMarker.svelte +23 -0
  192. package/src/client/debug/main/ClientSwitcher.svelte +59 -0
  193. package/src/client/debug/main/Controls.svelte +58 -0
  194. package/src/client/debug/main/Hotkey.svelte +84 -0
  195. package/src/client/debug/main/InteractiveFunction.svelte +85 -0
  196. package/src/client/debug/main/Main.svelte +121 -0
  197. package/src/client/debug/main/Move.svelte +68 -0
  198. package/src/client/debug/main/PlayerInfo.svelte +70 -0
  199. package/src/client/debug/mcts/Action.svelte +22 -0
  200. package/src/client/debug/mcts/MCTS.svelte +78 -0
  201. package/src/client/debug/mcts/Table.svelte +98 -0
  202. package/src/client/debug/tests/JSONTree.mock.svelte +3 -0
  203. package/src/client/debug/tests/debug.test.ts +183 -0
  204. package/src/client/debug/utils/shortcuts.js +50 -0
  205. package/src/client/debug/utils/shortcuts.test.js +49 -0
  206. package/src/client/manager.ts +177 -0
  207. package/src/client/react-native.js +136 -0
  208. package/src/client/react-native.test.js +229 -0
  209. package/src/client/react.ssr.test.tsx +24 -0
  210. package/src/client/react.test.tsx +213 -0
  211. package/src/client/react.tsx +192 -0
  212. package/src/client/transport/dummy.ts +19 -0
  213. package/src/client/transport/local.test.ts +353 -0
  214. package/src/client/transport/local.ts +230 -0
  215. package/src/client/transport/socketio.test.ts +328 -0
  216. package/src/client/transport/socketio.ts +210 -0
  217. package/src/client/transport/transport.test.ts +27 -0
  218. package/src/client/transport/transport.ts +95 -0
  219. package/src/core/action-creators.ts +159 -0
  220. package/src/core/action-types.ts +18 -0
  221. package/src/core/backwards-compatibility.ts +23 -0
  222. package/src/core/constants.ts +6 -0
  223. package/src/core/errors.ts +35 -0
  224. package/src/core/flow.test.ts +2433 -0
  225. package/src/core/flow.ts +897 -0
  226. package/src/core/game-methods.ts +9 -0
  227. package/src/core/game.test.ts +286 -0
  228. package/src/core/game.ts +114 -0
  229. package/src/core/initialize.ts +77 -0
  230. package/src/core/logger.test.js +90 -0
  231. package/src/core/logger.ts +18 -0
  232. package/src/core/player-view.test.ts +50 -0
  233. package/src/core/player-view.ts +39 -0
  234. package/src/core/reducer.test.ts +991 -0
  235. package/src/core/reducer.ts +532 -0
  236. package/src/core/turn-order.test.ts +1123 -0
  237. package/src/core/turn-order.ts +473 -0
  238. package/src/lobby/client.test.ts +385 -0
  239. package/src/lobby/client.ts +358 -0
  240. package/src/lobby/connection.test.ts +207 -0
  241. package/src/lobby/connection.ts +162 -0
  242. package/src/lobby/create-match-form.tsx +122 -0
  243. package/src/lobby/login-form.tsx +75 -0
  244. package/src/lobby/match-instance.tsx +135 -0
  245. package/src/lobby/react.ssr.test.tsx +22 -0
  246. package/src/lobby/react.test.tsx +594 -0
  247. package/src/lobby/react.tsx +402 -0
  248. package/src/master/filter-player-view.test.ts +381 -0
  249. package/src/master/filter-player-view.ts +102 -0
  250. package/src/master/master.test.ts +1068 -0
  251. package/src/master/master.ts +492 -0
  252. package/src/plugins/events/events.test.ts +108 -0
  253. package/src/plugins/events/events.ts +209 -0
  254. package/src/plugins/main.test.ts +411 -0
  255. package/src/plugins/main.ts +314 -0
  256. package/src/plugins/plugin-events.ts +40 -0
  257. package/src/plugins/plugin-immer.test.ts +86 -0
  258. package/src/plugins/plugin-immer.ts +37 -0
  259. package/src/plugins/plugin-log.test.ts +37 -0
  260. package/src/plugins/plugin-log.ts +40 -0
  261. package/src/plugins/plugin-player.test.ts +172 -0
  262. package/src/plugins/plugin-player.ts +100 -0
  263. package/src/plugins/plugin-random.ts +40 -0
  264. package/src/plugins/plugin-serializable.test.ts +40 -0
  265. package/src/plugins/plugin-serializable.ts +55 -0
  266. package/src/plugins/random/random.alea.ts +109 -0
  267. package/src/plugins/random/random.test.ts +167 -0
  268. package/src/plugins/random/random.ts +198 -0
  269. package/src/server/api.test.ts +1699 -0
  270. package/src/server/api.ts +527 -0
  271. package/src/server/auth.test.ts +275 -0
  272. package/src/server/auth.ts +89 -0
  273. package/src/server/cors.test.ts +121 -0
  274. package/src/server/cors.ts +7 -0
  275. package/src/server/db/base.ts +296 -0
  276. package/src/server/db/flatfile.test.ts +221 -0
  277. package/src/server/db/flatfile.ts +228 -0
  278. package/src/server/db/index.test.ts +8 -0
  279. package/src/server/db/index.ts +12 -0
  280. package/src/server/db/inmemory.test.ts +143 -0
  281. package/src/server/db/inmemory.ts +143 -0
  282. package/src/server/db/localstorage.test.ts +73 -0
  283. package/src/server/db/localstorage.ts +44 -0
  284. package/src/server/index.test.ts +265 -0
  285. package/src/server/index.ts +175 -0
  286. package/src/server/transport/pubsub/generic-pub-sub.ts +11 -0
  287. package/src/server/transport/pubsub/in-memory-pub-sub.test.ts +47 -0
  288. package/src/server/transport/pubsub/in-memory-pub-sub.ts +28 -0
  289. package/src/server/transport/socketio-simultaneous.test.ts +603 -0
  290. package/src/server/transport/socketio.test.ts +303 -0
  291. package/src/server/transport/socketio.ts +279 -0
  292. package/src/server/util.ts +85 -0
  293. package/src/testing/mock-random.test.ts +45 -0
  294. package/src/testing/mock-random.ts +27 -0
  295. package/src/types.ts +511 -0
  296. package/testing/package.json +7 -0
@@ -0,0 +1,228 @@
1
+ import * as StorageAPI from './base';
2
+ import type { State, Server, LogEntry } from '../../types';
3
+
4
+ /*
5
+ * Copyright 2017 The boardgame.io Authors
6
+ *
7
+ * Use of this source code is governed by a MIT-style
8
+ * license that can be found in the LICENSE file or at
9
+ * https://opensource.org/licenses/MIT.
10
+ */
11
+
12
+ interface InitOptions {
13
+ dir: string;
14
+ logging?: boolean;
15
+ ttl?: boolean;
16
+ }
17
+
18
+ /**
19
+ * FlatFile data storage.
20
+ */
21
+ export class FlatFile extends StorageAPI.Async {
22
+ private games: {
23
+ init: (opts: InitOptions) => Promise<void>;
24
+ setItem: (id: string, value: any) => Promise<any>;
25
+ getItem: (id: string) => Promise<State | Server.MatchData | LogEntry[]>;
26
+ removeItem: (id: string) => Promise<void>;
27
+ clear: () => void;
28
+ keys: () => Promise<string[]>;
29
+ };
30
+ private dir: string;
31
+ private logging?: boolean;
32
+ private ttl?: boolean;
33
+ private fileQueues: { [key: string]: Promise<any> };
34
+
35
+ constructor({ dir, logging, ttl }: InitOptions) {
36
+ super();
37
+ this.games = require('node-persist');
38
+ this.dir = dir;
39
+ this.logging = logging || false;
40
+ this.ttl = ttl || false;
41
+ this.fileQueues = {};
42
+ }
43
+
44
+ private async chainRequest(
45
+ key: string,
46
+ request: () => Promise<any>
47
+ ): Promise<any> {
48
+ if (!(key in this.fileQueues)) this.fileQueues[key] = Promise.resolve();
49
+
50
+ this.fileQueues[key] = this.fileQueues[key].then(request, request);
51
+ return this.fileQueues[key];
52
+ }
53
+
54
+ private async getItem<T extends any = any>(key: string): Promise<T> {
55
+ return this.chainRequest(key, () => this.games.getItem(key));
56
+ }
57
+
58
+ private async setItem<T extends any = any>(
59
+ key: string,
60
+ value: T
61
+ ): Promise<any> {
62
+ return this.chainRequest(key, () => this.games.setItem(key, value));
63
+ }
64
+
65
+ private async removeItem(key: string): Promise<void> {
66
+ return this.chainRequest(key, () => this.games.removeItem(key));
67
+ }
68
+
69
+ async connect() {
70
+ await this.games.init({
71
+ dir: this.dir,
72
+ logging: this.logging,
73
+ ttl: this.ttl,
74
+ });
75
+ return;
76
+ }
77
+
78
+ /**
79
+ * Create new match.
80
+ *
81
+ * @param matchID
82
+ * @param opts
83
+ * @override
84
+ */
85
+ async createMatch(
86
+ matchID: string,
87
+ opts: StorageAPI.CreateMatchOpts
88
+ ): Promise<void> {
89
+ // Store initial state separately for easy retrieval later.
90
+ const key = InitialStateKey(matchID);
91
+
92
+ await this.setItem(key, opts.initialState);
93
+ await this.setState(matchID, opts.initialState);
94
+ await this.setMetadata(matchID, opts.metadata);
95
+ }
96
+
97
+ async fetch<O extends StorageAPI.FetchOpts>(
98
+ matchID: string,
99
+ opts: O
100
+ ): Promise<StorageAPI.FetchResult<O>> {
101
+ const result = {} as StorageAPI.FetchFields;
102
+
103
+ if (opts.state) {
104
+ result.state = (await this.getItem(matchID)) as State;
105
+ }
106
+
107
+ if (opts.metadata) {
108
+ const key = MetadataKey(matchID);
109
+ result.metadata = (await this.getItem(key)) as Server.MatchData;
110
+ }
111
+
112
+ if (opts.log) {
113
+ const key = LogKey(matchID);
114
+ result.log = (await this.getItem(key)) as LogEntry[];
115
+ }
116
+
117
+ if (opts.initialState) {
118
+ const key = InitialStateKey(matchID);
119
+ result.initialState = (await this.getItem(key)) as State;
120
+ }
121
+
122
+ return result as StorageAPI.FetchResult<O>;
123
+ }
124
+
125
+ async clear() {
126
+ return this.games.clear();
127
+ }
128
+
129
+ async setState(id: string, state: State, deltalog?: LogEntry[]) {
130
+ if (deltalog && deltalog.length > 0) {
131
+ const key = LogKey(id);
132
+ const log: LogEntry[] = ((await this.getItem(key)) as LogEntry[]) || [];
133
+
134
+ await this.setItem(key, [...log, ...deltalog]);
135
+ }
136
+
137
+ return await this.setItem(id, state);
138
+ }
139
+
140
+ async setMetadata(id: string, metadata: Server.MatchData): Promise<void> {
141
+ const key = MetadataKey(id);
142
+
143
+ return await this.setItem(key, metadata);
144
+ }
145
+
146
+ async wipe(id: string) {
147
+ const keys = await this.games.keys();
148
+ if (!keys.includes(id)) return;
149
+
150
+ await this.removeItem(id);
151
+ await this.removeItem(InitialStateKey(id));
152
+ await this.removeItem(LogKey(id));
153
+ await this.removeItem(MetadataKey(id));
154
+ }
155
+
156
+ /**
157
+ * List matches IDs.
158
+ *
159
+ * @param opts
160
+ * @override
161
+ */
162
+ async listMatches(opts?: StorageAPI.ListMatchesOpts): Promise<string[]> {
163
+ const keys = await this.games.keys();
164
+ const suffix = ':metadata';
165
+
166
+ const arr = await Promise.all(
167
+ keys.map(async (k) => {
168
+ if (!k.endsWith(suffix)) {
169
+ return false;
170
+ }
171
+
172
+ const matchID = k.slice(0, k.length - suffix.length);
173
+
174
+ if (!opts) {
175
+ return matchID;
176
+ }
177
+
178
+ const game = await this.fetch(matchID, {
179
+ state: true,
180
+ metadata: true,
181
+ });
182
+
183
+ if (opts.gameName && opts.gameName !== game.metadata.gameName) {
184
+ return false;
185
+ }
186
+
187
+ if (opts.where !== undefined) {
188
+ if (typeof opts.where.isGameover !== 'undefined') {
189
+ const isGameover = typeof game.metadata.gameover !== 'undefined';
190
+ if (isGameover !== opts.where.isGameover) {
191
+ return false;
192
+ }
193
+ }
194
+
195
+ if (
196
+ typeof opts.where.updatedBefore !== 'undefined' &&
197
+ game.metadata.updatedAt >= opts.where.updatedBefore
198
+ ) {
199
+ return false;
200
+ }
201
+
202
+ if (
203
+ typeof opts.where.updatedAfter !== 'undefined' &&
204
+ game.metadata.updatedAt <= opts.where.updatedAfter
205
+ ) {
206
+ return false;
207
+ }
208
+ }
209
+
210
+ return matchID;
211
+ })
212
+ );
213
+
214
+ return arr.filter((r): r is string => typeof r === 'string');
215
+ }
216
+ }
217
+
218
+ function InitialStateKey(matchID: string) {
219
+ return `${matchID}:initial`;
220
+ }
221
+
222
+ function MetadataKey(matchID: string) {
223
+ return `${matchID}:metadata`;
224
+ }
225
+
226
+ function LogKey(matchID: string) {
227
+ return `${matchID}:log`;
228
+ }
@@ -0,0 +1,8 @@
1
+ import { DBFromEnv, FlatFile } from '.';
2
+
3
+ test('FLATFILE_DIR', () => {
4
+ process.env.FLATFILE_DIR = 'test';
5
+ const db = DBFromEnv();
6
+ expect(db).toBeInstanceOf(FlatFile);
7
+ delete process.env.FLATFILE_DIR;
8
+ });
@@ -0,0 +1,12 @@
1
+ import { InMemory } from './inmemory';
2
+ import { FlatFile } from './flatfile';
3
+
4
+ const DBFromEnv = () => {
5
+ return process.env.FLATFILE_DIR
6
+ ? new FlatFile({
7
+ dir: process.env.FLATFILE_DIR,
8
+ })
9
+ : new InMemory();
10
+ };
11
+
12
+ export { InMemory, FlatFile, DBFromEnv };
@@ -0,0 +1,143 @@
1
+ /*
2
+ * Copyright 2017 The boardgame.io Authors
3
+ *
4
+ * Use of this source code is governed by a MIT-style
5
+ * license that can be found in the LICENSE file or at
6
+ * https://opensource.org/licenses/MIT.
7
+ */
8
+
9
+ import { InMemory } from './inmemory';
10
+ import type { State, Server } from '../../types';
11
+
12
+ describe('InMemory', () => {
13
+ let db: InMemory;
14
+
15
+ beforeAll(() => {
16
+ db = new InMemory();
17
+ db.connect();
18
+ });
19
+
20
+ // Must return undefined when no game exists.
21
+ test('must return undefined when no game exists', () => {
22
+ const { state } = db.fetch('matchID', { state: true });
23
+ expect(state).toEqual(undefined);
24
+ });
25
+
26
+ test('createMatch', () => {
27
+ const stateEntry: unknown = { a: 1 };
28
+
29
+ // Create match.
30
+ db.createMatch('matchID', {
31
+ metadata: {
32
+ gameName: 'tic-tac-toe',
33
+ updatedAt: new Date(2020, 1).getTime(),
34
+ } as Server.MatchData,
35
+ initialState: stateEntry as State,
36
+ });
37
+
38
+ // Must return created game.
39
+ const { state } = db.fetch('matchID', { state: true });
40
+ expect(state).toEqual(stateEntry);
41
+
42
+ // Fetch initial state.
43
+ const { initialState } = db.fetch('matchID', { initialState: true });
44
+ expect(initialState).toEqual(stateEntry);
45
+ });
46
+
47
+ describe('listMatches', () => {
48
+ test('filter by gameName', () => {
49
+ let keys = db.listMatches();
50
+ expect(keys).toEqual(['matchID']);
51
+ keys = db.listMatches({ gameName: 'tic-tac-toe' });
52
+ expect(keys).toEqual(['matchID']);
53
+ keys = db.listMatches({ gameName: 'chess' });
54
+ expect(keys).toEqual([]);
55
+ });
56
+
57
+ test('filter by isGameover', () => {
58
+ const stateEntry: unknown = { a: 1 };
59
+ db.createMatch('matchID2', {
60
+ metadata: {
61
+ gameName: 'tic-tac-toe',
62
+ gameover: 'gameover',
63
+ updatedAt: new Date(2020, 3).getTime(),
64
+ } as Server.MatchData,
65
+ initialState: stateEntry as State,
66
+ });
67
+
68
+ let keys = db.listMatches({});
69
+ expect(keys).toEqual(['matchID', 'matchID2']);
70
+ keys = db.listMatches({ where: { isGameover: true } });
71
+ expect(keys).toEqual(['matchID2']);
72
+ keys = db.listMatches({ where: { isGameover: false } });
73
+ expect(keys).toEqual(['matchID']);
74
+ });
75
+
76
+ test('filter by updatedBefore', () => {
77
+ const stateEntry: unknown = { a: 1 };
78
+ db.createMatch('matchID3', {
79
+ metadata: {
80
+ gameName: 'tic-tac-toe',
81
+ updatedAt: new Date(2020, 5).getTime(),
82
+ } as Server.MatchData,
83
+ initialState: stateEntry as State,
84
+ });
85
+ const timestamp = new Date(2020, 4);
86
+
87
+ let keys = db.listMatches({});
88
+ expect(keys).toEqual(['matchID', 'matchID2', 'matchID3']);
89
+ keys = db.listMatches({ where: { updatedBefore: timestamp.getTime() } });
90
+ expect(keys).toEqual(['matchID', 'matchID2']);
91
+ });
92
+
93
+ test('filter by updatedAfter', () => {
94
+ const timestamp = new Date(2020, 4);
95
+
96
+ let keys = db.listMatches({});
97
+ expect(keys).toEqual(['matchID', 'matchID2', 'matchID3']);
98
+ keys = db.listMatches({ where: { updatedAfter: timestamp.getTime() } });
99
+ expect(keys).toEqual(['matchID3']);
100
+ });
101
+
102
+ test('filter combined', () => {
103
+ const timestamp = new Date(2020, 4);
104
+ const timestamp2 = new Date(2020, 2, 15);
105
+ let keys = db.listMatches({
106
+ gameName: 'chess',
107
+ where: { isGameover: true },
108
+ });
109
+ expect(keys).toEqual([]);
110
+ keys = db.listMatches({
111
+ where: { isGameover: true, updatedBefore: timestamp.getTime() },
112
+ });
113
+ expect(keys).toEqual(['matchID2']);
114
+ keys = db.listMatches({
115
+ where: { isGameover: false, updatedBefore: timestamp.getTime() },
116
+ });
117
+ expect(keys).toEqual(['matchID']);
118
+ keys = db.listMatches({
119
+ where: { isGameover: true, updatedAfter: timestamp.getTime() },
120
+ });
121
+ expect(keys).toEqual([]);
122
+ keys = db.listMatches({
123
+ where: { isGameover: false, updatedAfter: timestamp.getTime() },
124
+ });
125
+ expect(keys).toEqual(['matchID3']);
126
+ keys = db.listMatches({
127
+ where: {
128
+ updatedBefore: timestamp.getTime(),
129
+ updatedAfter: timestamp2.getTime(),
130
+ },
131
+ });
132
+ expect(keys).toEqual(['matchID2']);
133
+ });
134
+ });
135
+
136
+ test('remove', () => {
137
+ // Must remove game from DB
138
+ db.wipe('matchID');
139
+ expect(db.fetch('matchID', { state: true })).toEqual({});
140
+ // Shall not return error
141
+ db.wipe('matchID');
142
+ });
143
+ });
@@ -0,0 +1,143 @@
1
+ import type { State, Server, LogEntry } from '../../types';
2
+ import * as StorageAPI from './base';
3
+
4
+ /*
5
+ * Copyright 2017 The boardgame.io Authors
6
+ *
7
+ * Use of this source code is governed by a MIT-style
8
+ * license that can be found in the LICENSE file or at
9
+ * https://opensource.org/licenses/MIT.
10
+ */
11
+
12
+ /**
13
+ * InMemory data storage.
14
+ */
15
+ export class InMemory extends StorageAPI.Sync {
16
+ protected state: Map<string, State>;
17
+ protected initial: Map<string, State>;
18
+ protected metadata: Map<string, Server.MatchData>;
19
+ protected log: Map<string, LogEntry[]>;
20
+
21
+ /**
22
+ * Creates a new InMemory storage.
23
+ */
24
+ constructor() {
25
+ super();
26
+ this.state = new Map();
27
+ this.initial = new Map();
28
+ this.metadata = new Map();
29
+ this.log = new Map();
30
+ }
31
+
32
+ /**
33
+ * Create a new match.
34
+ *
35
+ * @override
36
+ */
37
+ createMatch(matchID: string, opts: StorageAPI.CreateMatchOpts) {
38
+ this.initial.set(matchID, opts.initialState);
39
+ this.setState(matchID, opts.initialState);
40
+ this.setMetadata(matchID, opts.metadata);
41
+ }
42
+
43
+ /**
44
+ * Write the match metadata to the in-memory object.
45
+ */
46
+ setMetadata(matchID: string, metadata: Server.MatchData) {
47
+ this.metadata.set(matchID, metadata);
48
+ }
49
+
50
+ /**
51
+ * Write the match state to the in-memory object.
52
+ */
53
+ setState(matchID: string, state: State, deltalog?: LogEntry[]): void {
54
+ if (deltalog && deltalog.length > 0) {
55
+ const log = this.log.get(matchID) || [];
56
+ this.log.set(matchID, [...log, ...deltalog]);
57
+ }
58
+ this.state.set(matchID, state);
59
+ }
60
+
61
+ /**
62
+ * Fetches state for a particular matchID.
63
+ */
64
+ fetch<O extends StorageAPI.FetchOpts>(
65
+ matchID: string,
66
+ opts: O
67
+ ): StorageAPI.FetchResult<O> {
68
+ const result = {} as StorageAPI.FetchFields;
69
+
70
+ if (opts.state) {
71
+ result.state = this.state.get(matchID);
72
+ }
73
+
74
+ if (opts.metadata) {
75
+ result.metadata = this.metadata.get(matchID);
76
+ }
77
+
78
+ if (opts.log) {
79
+ result.log = this.log.get(matchID) || [];
80
+ }
81
+
82
+ if (opts.initialState) {
83
+ result.initialState = this.initial.get(matchID);
84
+ }
85
+
86
+ return result as StorageAPI.FetchResult<O>;
87
+ }
88
+
89
+ /**
90
+ * Remove the match state from the in-memory object.
91
+ */
92
+ wipe(matchID: string) {
93
+ this.state.delete(matchID);
94
+ this.metadata.delete(matchID);
95
+ }
96
+
97
+ /**
98
+ * Return all keys.
99
+ *
100
+ * @override
101
+ */
102
+ listMatches(opts?: StorageAPI.ListMatchesOpts): string[] {
103
+ return [...this.metadata.entries()]
104
+ .filter(([, metadata]) => {
105
+ if (!opts) {
106
+ return true;
107
+ }
108
+
109
+ if (
110
+ opts.gameName !== undefined &&
111
+ metadata.gameName !== opts.gameName
112
+ ) {
113
+ return false;
114
+ }
115
+
116
+ if (opts.where !== undefined) {
117
+ if (opts.where.isGameover !== undefined) {
118
+ const isGameover = metadata.gameover !== undefined;
119
+ if (isGameover !== opts.where.isGameover) {
120
+ return false;
121
+ }
122
+ }
123
+
124
+ if (
125
+ opts.where.updatedBefore !== undefined &&
126
+ metadata.updatedAt >= opts.where.updatedBefore
127
+ ) {
128
+ return false;
129
+ }
130
+
131
+ if (
132
+ opts.where.updatedAfter !== undefined &&
133
+ metadata.updatedAt <= opts.where.updatedAfter
134
+ ) {
135
+ return false;
136
+ }
137
+ }
138
+
139
+ return true;
140
+ })
141
+ .map(([key]) => key);
142
+ }
143
+ }
@@ -0,0 +1,73 @@
1
+ import { nanoid } from 'nanoid';
2
+ import { LocalStorage } from './localstorage';
3
+ import type { State, Server } from '../../types';
4
+
5
+ describe('LocaLStorage', () => {
6
+ let db: LocalStorage;
7
+
8
+ beforeAll(() => {
9
+ db = new LocalStorage(nanoid());
10
+ db.connect();
11
+ });
12
+
13
+ // Must return undefined when no game exists.
14
+ test('must return undefined when no game exists', () => {
15
+ const { state } = db.fetch('gameID', { state: true });
16
+ expect(state).toEqual(undefined);
17
+ });
18
+
19
+ test('create game', () => {
20
+ const stateEntry: unknown = { a: 1 };
21
+
22
+ // Create game.
23
+ db.createMatch('gameID', {
24
+ metadata: {
25
+ gameName: 'tic-tac-toe',
26
+ } as Server.MatchData,
27
+ initialState: stateEntry as State,
28
+ });
29
+
30
+ // Must return created game.
31
+ const { state } = db.fetch('gameID', { state: true });
32
+ expect(state).toEqual(stateEntry);
33
+
34
+ // Fetch initial state.
35
+ const { initialState } = db.fetch('gameID', { initialState: true });
36
+ expect(initialState).toEqual(stateEntry);
37
+ });
38
+
39
+ test('listGames', () => {
40
+ let keys = db.listMatches({});
41
+ expect(keys).toEqual(['gameID']);
42
+ keys = db.listMatches({ gameName: 'tic-tac-toe' });
43
+ expect(keys).toEqual(['gameID']);
44
+ keys = db.listMatches({ gameName: 'chess' });
45
+ expect(keys).toEqual([]);
46
+ });
47
+
48
+ test('remove', () => {
49
+ // Must remove game from DB
50
+ db.wipe('gameID');
51
+ expect(db.fetch('gameID', { state: true })).toEqual({});
52
+ // Shall not return error
53
+ db.wipe('gameID');
54
+ });
55
+
56
+ test('must create new empty db if other localstorage key is used', () => {
57
+ // create another localstorage with anothr key
58
+ const db2 = new LocalStorage(nanoid());
59
+ const stateEntry: unknown = { a: 1 };
60
+
61
+ // create game in db
62
+ db.createMatch('gameID', {
63
+ metadata: {
64
+ gameName: 'tic-tac-toe',
65
+ } as Server.MatchData,
66
+ initialState: stateEntry as State,
67
+ });
68
+
69
+ // game shouldnt be visible in db2
70
+ const { state } = db2.fetch('gameID', { state: true });
71
+ expect(state).toEqual(undefined);
72
+ });
73
+ });
@@ -0,0 +1,44 @@
1
+ import { InMemory } from './inmemory';
2
+
3
+ class WithLocalStorageMap<Key, Value> extends Map {
4
+ key: string;
5
+
6
+ constructor(key: string) {
7
+ super();
8
+ this.key = key;
9
+ const cache = JSON.parse(localStorage.getItem(this.key)) || [];
10
+ cache.forEach((entry: [Key, Value]) => this.set(...entry));
11
+ }
12
+
13
+ sync(): void {
14
+ const entries = [...this.entries()];
15
+ localStorage.setItem(this.key, JSON.stringify(entries));
16
+ }
17
+
18
+ set(key: Key, value: Value): this {
19
+ super.set(key, value);
20
+ this.sync();
21
+ return this;
22
+ }
23
+
24
+ delete(key: Key): boolean {
25
+ const result = super.delete(key);
26
+ this.sync();
27
+ return result;
28
+ }
29
+ }
30
+
31
+ /**
32
+ * locaStorage data storage.
33
+ */
34
+ export class LocalStorage extends InMemory {
35
+ constructor(storagePrefix = 'bgio') {
36
+ super();
37
+ const StorageMap = (stateKey: string) =>
38
+ new WithLocalStorageMap(`${storagePrefix}_${stateKey}`);
39
+ this.state = StorageMap('state');
40
+ this.initial = StorageMap('initial');
41
+ this.metadata = StorageMap('metadata');
42
+ this.log = StorageMap('log');
43
+ }
44
+ }