@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.
- package/LICENSE +21 -0
- package/README.md +102 -0
- package/ai/package.json +7 -0
- package/client/package.json +7 -0
- package/core/package.json +7 -0
- package/debug/package.json +7 -0
- package/dist/boardgameio.es.js +14238 -0
- package/dist/boardgameio.js +14277 -0
- package/dist/boardgameio.min.js +16 -0
- package/dist/cjs/Debug-9d141c06.js +9586 -0
- package/dist/cjs/ai-e0e8a768.js +377 -0
- package/dist/cjs/ai.js +20 -0
- package/dist/cjs/client-76dec77b.js +258 -0
- package/dist/cjs/client-a22d7500.js +524 -0
- package/dist/cjs/client.js +26 -0
- package/dist/cjs/core.js +52 -0
- package/dist/cjs/debug.js +18 -0
- package/dist/cjs/filter-player-view-bb02e2f6.js +89 -0
- package/dist/cjs/initialize-267fcd69.js +61 -0
- package/dist/cjs/internal.js +25 -0
- package/dist/cjs/master-2904879d.js +320 -0
- package/dist/cjs/master.js +18 -0
- package/dist/cjs/multiplayer.js +23 -0
- package/dist/cjs/plugin-random-7425844d.js +229 -0
- package/dist/cjs/plugins.js +59 -0
- package/dist/cjs/react-native.js +182 -0
- package/dist/cjs/react.js +727 -0
- package/dist/cjs/reducer-16eec232.js +1203 -0
- package/dist/cjs/server.js +4087 -0
- package/dist/cjs/socketio-7a0837eb.js +478 -0
- package/dist/cjs/testing.js +30 -0
- package/dist/cjs/transport-b1874dfa.js +37 -0
- package/dist/cjs/turn-order-b2ff8740.js +1136 -0
- package/dist/cjs/util-fcfd8fb8.js +140 -0
- package/dist/esm/Debug-0141fe2d.js +9577 -0
- package/dist/esm/ai-5c06e761.js +371 -0
- package/dist/esm/ai.js +8 -0
- package/dist/esm/client-2e653027.js +522 -0
- package/dist/esm/client-5f57c3f2.js +255 -0
- package/dist/esm/client.js +16 -0
- package/dist/esm/core.js +40 -0
- package/dist/esm/debug.js +10 -0
- package/dist/esm/filter-player-view-2c6cc96f.js +87 -0
- package/dist/esm/initialize-11d626ca.js +59 -0
- package/dist/esm/internal.js +10 -0
- package/dist/esm/master-fa8f2e43.js +318 -0
- package/dist/esm/master.js +10 -0
- package/dist/esm/multiplayer.js +14 -0
- package/dist/esm/plugin-random-087f861e.js +226 -0
- package/dist/esm/plugins.js +55 -0
- package/dist/esm/react-native.js +173 -0
- package/dist/esm/react.js +716 -0
- package/dist/esm/reducer-c46da7e5.js +1198 -0
- package/dist/esm/socketio-c22ffa65.js +455 -0
- package/dist/esm/testing.js +26 -0
- package/dist/esm/transport-ce07b771.js +35 -0
- package/dist/esm/turn-order-376d315e.js +1091 -0
- package/dist/esm/util-b6147cef.js +135 -0
- package/dist/types/packages/ai.d.ts +5 -0
- package/dist/types/packages/client.d.ts +3 -0
- package/dist/types/packages/core.d.ts +5 -0
- package/dist/types/packages/debug.d.ts +2 -0
- package/dist/types/packages/internal.d.ts +8 -0
- package/dist/types/packages/master.d.ts +2 -0
- package/dist/types/packages/multiplayer.d.ts +3 -0
- package/dist/types/packages/plugins.d.ts +3 -0
- package/dist/types/packages/react-native.d.ts +2 -0
- package/dist/types/packages/react.d.ts +3 -0
- package/dist/types/packages/server.d.ts +6 -0
- package/dist/types/packages/testing.d.ts +1 -0
- package/dist/types/src/ai/ai.d.ts +53 -0
- package/dist/types/src/ai/ai.test.d.ts +1 -0
- package/dist/types/src/ai/bot.d.ts +40 -0
- package/dist/types/src/ai/mcts-bot.d.ts +60 -0
- package/dist/types/src/ai/random-bot.d.ts +27 -0
- package/dist/types/src/client/client.d.ts +104 -0
- package/dist/types/src/client/client.test.d.ts +1 -0
- package/dist/types/src/client/debug/tests/debug.test.d.ts +1 -0
- package/dist/types/src/client/manager.d.ts +61 -0
- package/dist/types/src/client/react.d.ts +75 -0
- package/dist/types/src/client/react.ssr.test.d.ts +4 -0
- package/dist/types/src/client/react.test.d.ts +1 -0
- package/dist/types/src/client/transport/dummy.d.ts +18 -0
- package/dist/types/src/client/transport/local.d.ts +59 -0
- package/dist/types/src/client/transport/local.test.d.ts +1 -0
- package/dist/types/src/client/transport/socketio.d.ts +45 -0
- package/dist/types/src/client/transport/socketio.test.d.ts +1 -0
- package/dist/types/src/client/transport/transport.d.ts +50 -0
- package/dist/types/src/client/transport/transport.test.d.ts +1 -0
- package/dist/types/src/core/action-creators.d.ts +144 -0
- package/dist/types/src/core/action-types.d.ts +10 -0
- package/dist/types/src/core/backwards-compatibility.d.ts +12 -0
- package/dist/types/src/core/constants.d.ts +6 -0
- package/dist/types/src/core/errors.d.ts +15 -0
- package/dist/types/src/core/flow.d.ts +28 -0
- package/dist/types/src/core/flow.test.d.ts +1 -0
- package/dist/types/src/core/game-methods.d.ts +9 -0
- package/dist/types/src/core/game.d.ts +26 -0
- package/dist/types/src/core/game.test.d.ts +1 -0
- package/dist/types/src/core/initialize.d.ts +9 -0
- package/dist/types/src/core/logger.d.ts +2 -0
- package/dist/types/src/core/player-view.d.ts +7 -0
- package/dist/types/src/core/player-view.test.d.ts +1 -0
- package/dist/types/src/core/reducer.d.ts +155 -0
- package/dist/types/src/core/reducer.test.d.ts +1 -0
- package/dist/types/src/core/turn-order.d.ts +179 -0
- package/dist/types/src/core/turn-order.test.d.ts +8 -0
- package/dist/types/src/lobby/client.d.ts +194 -0
- package/dist/types/src/lobby/client.test.d.ts +1 -0
- package/dist/types/src/lobby/connection.d.ts +44 -0
- package/dist/types/src/lobby/connection.test.d.ts +1 -0
- package/dist/types/src/lobby/create-match-form.d.ts +26 -0
- package/dist/types/src/lobby/login-form.d.ts +23 -0
- package/dist/types/src/lobby/match-instance.d.ts +31 -0
- package/dist/types/src/lobby/react.d.ts +113 -0
- package/dist/types/src/lobby/react.ssr.test.d.ts +4 -0
- package/dist/types/src/lobby/react.test.d.ts +1 -0
- package/dist/types/src/master/filter-player-view.d.ts +96 -0
- package/dist/types/src/master/filter-player-view.test.d.ts +1 -0
- package/dist/types/src/master/master.d.ts +94 -0
- package/dist/types/src/master/master.test.d.ts +1 -0
- package/dist/types/src/plugins/events/events.d.ts +54 -0
- package/dist/types/src/plugins/events/events.test.d.ts +1 -0
- package/dist/types/src/plugins/main.d.ts +75 -0
- package/dist/types/src/plugins/main.test.d.ts +1 -0
- package/dist/types/src/plugins/plugin-events.d.ts +5 -0
- package/dist/types/src/plugins/plugin-immer.d.ts +7 -0
- package/dist/types/src/plugins/plugin-immer.test.d.ts +1 -0
- package/dist/types/src/plugins/plugin-log.d.ts +14 -0
- package/dist/types/src/plugins/plugin-log.test.d.ts +1 -0
- package/dist/types/src/plugins/plugin-player.d.ts +29 -0
- package/dist/types/src/plugins/plugin-player.test.d.ts +1 -0
- package/dist/types/src/plugins/plugin-random.d.ts +4 -0
- package/dist/types/src/plugins/plugin-serializable.d.ts +7 -0
- package/dist/types/src/plugins/plugin-serializable.test.d.ts +1 -0
- package/dist/types/src/plugins/random/random.alea.d.ts +19 -0
- package/dist/types/src/plugins/random/random.d.ts +54 -0
- package/dist/types/src/plugins/random/random.test.d.ts +1 -0
- package/dist/types/src/server/api.d.ts +13 -0
- package/dist/types/src/server/api.test.d.ts +1 -0
- package/dist/types/src/server/auth.d.ts +38 -0
- package/dist/types/src/server/auth.test.d.ts +1 -0
- package/dist/types/src/server/cors.d.ts +4 -0
- package/dist/types/src/server/cors.test.d.ts +1 -0
- package/dist/types/src/server/db/base.d.ts +192 -0
- package/dist/types/src/server/db/flatfile.d.ts +44 -0
- package/dist/types/src/server/db/flatfile.test.d.ts +1 -0
- package/dist/types/src/server/db/index.d.ts +4 -0
- package/dist/types/src/server/db/index.test.d.ts +1 -0
- package/dist/types/src/server/db/inmemory.d.ts +43 -0
- package/dist/types/src/server/db/inmemory.test.d.ts +1 -0
- package/dist/types/src/server/db/localstorage.d.ts +7 -0
- package/dist/types/src/server/db/localstorage.test.d.ts +1 -0
- package/dist/types/src/server/index.d.ts +68 -0
- package/dist/types/src/server/index.test.d.ts +1 -0
- package/dist/types/src/server/transport/pubsub/generic-pub-sub.d.ts +6 -0
- package/dist/types/src/server/transport/pubsub/in-memory-pub-sub.d.ts +7 -0
- package/dist/types/src/server/transport/pubsub/in-memory-pub-sub.test.d.ts +1 -0
- package/dist/types/src/server/transport/socketio-simultaneous.test.d.ts +1 -0
- package/dist/types/src/server/transport/socketio.d.ts +65 -0
- package/dist/types/src/server/transport/socketio.test.d.ts +1 -0
- package/dist/types/src/server/util.d.ts +35 -0
- package/dist/types/src/testing/mock-random.d.ts +15 -0
- package/dist/types/src/testing/mock-random.test.d.ts +1 -0
- package/dist/types/src/types.d.ts +387 -0
- package/internal/package.json +7 -0
- package/master/package.json +7 -0
- package/multiplayer/package.json +7 -0
- package/package.json +211 -0
- package/plugins/package.json +7 -0
- package/react/package.json +7 -0
- package/react-native/package.json +7 -0
- package/server/package.json +6 -0
- package/src/ai/ai.test.ts +433 -0
- package/src/ai/ai.ts +84 -0
- package/src/ai/bot.ts +122 -0
- package/src/ai/mcts-bot.ts +331 -0
- package/src/ai/random-bot.ts +20 -0
- package/src/client/client.test.ts +993 -0
- package/src/client/client.ts +588 -0
- package/src/client/debug/Debug.svelte +239 -0
- package/src/client/debug/Menu.svelte +65 -0
- package/src/client/debug/ai/AI.svelte +215 -0
- package/src/client/debug/ai/Options.svelte +48 -0
- package/src/client/debug/info/Info.svelte +22 -0
- package/src/client/debug/info/Item.svelte +24 -0
- package/src/client/debug/log/Log.svelte +157 -0
- package/src/client/debug/log/LogEvent.svelte +149 -0
- package/src/client/debug/log/LogMetadata.svelte +7 -0
- package/src/client/debug/log/PhaseMarker.svelte +27 -0
- package/src/client/debug/log/TurnMarker.svelte +23 -0
- package/src/client/debug/main/ClientSwitcher.svelte +59 -0
- package/src/client/debug/main/Controls.svelte +58 -0
- package/src/client/debug/main/Hotkey.svelte +84 -0
- package/src/client/debug/main/InteractiveFunction.svelte +85 -0
- package/src/client/debug/main/Main.svelte +121 -0
- package/src/client/debug/main/Move.svelte +68 -0
- package/src/client/debug/main/PlayerInfo.svelte +70 -0
- package/src/client/debug/mcts/Action.svelte +22 -0
- package/src/client/debug/mcts/MCTS.svelte +78 -0
- package/src/client/debug/mcts/Table.svelte +98 -0
- package/src/client/debug/tests/JSONTree.mock.svelte +3 -0
- package/src/client/debug/tests/debug.test.ts +183 -0
- package/src/client/debug/utils/shortcuts.js +50 -0
- package/src/client/debug/utils/shortcuts.test.js +49 -0
- package/src/client/manager.ts +177 -0
- package/src/client/react-native.js +136 -0
- package/src/client/react-native.test.js +229 -0
- package/src/client/react.ssr.test.tsx +24 -0
- package/src/client/react.test.tsx +213 -0
- package/src/client/react.tsx +192 -0
- package/src/client/transport/dummy.ts +19 -0
- package/src/client/transport/local.test.ts +353 -0
- package/src/client/transport/local.ts +230 -0
- package/src/client/transport/socketio.test.ts +328 -0
- package/src/client/transport/socketio.ts +210 -0
- package/src/client/transport/transport.test.ts +27 -0
- package/src/client/transport/transport.ts +95 -0
- package/src/core/action-creators.ts +159 -0
- package/src/core/action-types.ts +18 -0
- package/src/core/backwards-compatibility.ts +23 -0
- package/src/core/constants.ts +6 -0
- package/src/core/errors.ts +35 -0
- package/src/core/flow.test.ts +2433 -0
- package/src/core/flow.ts +897 -0
- package/src/core/game-methods.ts +9 -0
- package/src/core/game.test.ts +286 -0
- package/src/core/game.ts +114 -0
- package/src/core/initialize.ts +77 -0
- package/src/core/logger.test.js +90 -0
- package/src/core/logger.ts +18 -0
- package/src/core/player-view.test.ts +50 -0
- package/src/core/player-view.ts +39 -0
- package/src/core/reducer.test.ts +991 -0
- package/src/core/reducer.ts +532 -0
- package/src/core/turn-order.test.ts +1123 -0
- package/src/core/turn-order.ts +473 -0
- package/src/lobby/client.test.ts +385 -0
- package/src/lobby/client.ts +358 -0
- package/src/lobby/connection.test.ts +207 -0
- package/src/lobby/connection.ts +162 -0
- package/src/lobby/create-match-form.tsx +122 -0
- package/src/lobby/login-form.tsx +75 -0
- package/src/lobby/match-instance.tsx +135 -0
- package/src/lobby/react.ssr.test.tsx +22 -0
- package/src/lobby/react.test.tsx +594 -0
- package/src/lobby/react.tsx +402 -0
- package/src/master/filter-player-view.test.ts +381 -0
- package/src/master/filter-player-view.ts +102 -0
- package/src/master/master.test.ts +1068 -0
- package/src/master/master.ts +492 -0
- package/src/plugins/events/events.test.ts +108 -0
- package/src/plugins/events/events.ts +209 -0
- package/src/plugins/main.test.ts +411 -0
- package/src/plugins/main.ts +314 -0
- package/src/plugins/plugin-events.ts +40 -0
- package/src/plugins/plugin-immer.test.ts +86 -0
- package/src/plugins/plugin-immer.ts +37 -0
- package/src/plugins/plugin-log.test.ts +37 -0
- package/src/plugins/plugin-log.ts +40 -0
- package/src/plugins/plugin-player.test.ts +172 -0
- package/src/plugins/plugin-player.ts +100 -0
- package/src/plugins/plugin-random.ts +40 -0
- package/src/plugins/plugin-serializable.test.ts +40 -0
- package/src/plugins/plugin-serializable.ts +55 -0
- package/src/plugins/random/random.alea.ts +109 -0
- package/src/plugins/random/random.test.ts +167 -0
- package/src/plugins/random/random.ts +198 -0
- package/src/server/api.test.ts +1699 -0
- package/src/server/api.ts +527 -0
- package/src/server/auth.test.ts +275 -0
- package/src/server/auth.ts +89 -0
- package/src/server/cors.test.ts +121 -0
- package/src/server/cors.ts +7 -0
- package/src/server/db/base.ts +296 -0
- package/src/server/db/flatfile.test.ts +221 -0
- package/src/server/db/flatfile.ts +228 -0
- package/src/server/db/index.test.ts +8 -0
- package/src/server/db/index.ts +12 -0
- package/src/server/db/inmemory.test.ts +143 -0
- package/src/server/db/inmemory.ts +143 -0
- package/src/server/db/localstorage.test.ts +73 -0
- package/src/server/db/localstorage.ts +44 -0
- package/src/server/index.test.ts +265 -0
- package/src/server/index.ts +175 -0
- package/src/server/transport/pubsub/generic-pub-sub.ts +11 -0
- package/src/server/transport/pubsub/in-memory-pub-sub.test.ts +47 -0
- package/src/server/transport/pubsub/in-memory-pub-sub.ts +28 -0
- package/src/server/transport/socketio-simultaneous.test.ts +603 -0
- package/src/server/transport/socketio.test.ts +303 -0
- package/src/server/transport/socketio.ts +279 -0
- package/src/server/util.ts +85 -0
- package/src/testing/mock-random.test.ts +45 -0
- package/src/testing/mock-random.ts +27 -0
- package/src/types.ts +511 -0
- 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,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
|
+
}
|