@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,265 @@
|
|
|
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 request from 'supertest';
|
|
10
|
+
|
|
11
|
+
import { Server, createServerRunConfig, getPortFromServer } from '.';
|
|
12
|
+
import type { KoaServer } from '.';
|
|
13
|
+
import type { SocketIO } from './transport/socketio';
|
|
14
|
+
import type { Game, StorageAPI } from '../types';
|
|
15
|
+
|
|
16
|
+
const game: Game = { seed: 0 };
|
|
17
|
+
|
|
18
|
+
const warn = jest.spyOn(console, 'warn').mockImplementation(() => {});
|
|
19
|
+
beforeEach(warn.mockReset);
|
|
20
|
+
afterAll(warn.mockRestore);
|
|
21
|
+
|
|
22
|
+
jest.mock('../core/logger', () => ({
|
|
23
|
+
info: () => {},
|
|
24
|
+
error: () => {},
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
jest.mock('koa-socket-2', () => {
|
|
28
|
+
class MockSocket {
|
|
29
|
+
on() {}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return class {
|
|
33
|
+
constructor() {
|
|
34
|
+
(this as any).socket = new MockSocket();
|
|
35
|
+
}
|
|
36
|
+
attach(app) {
|
|
37
|
+
app.io = app._io = this;
|
|
38
|
+
}
|
|
39
|
+
of() {
|
|
40
|
+
return this;
|
|
41
|
+
}
|
|
42
|
+
on(type, callback) {
|
|
43
|
+
callback((this as any).socket);
|
|
44
|
+
}
|
|
45
|
+
adapter() {
|
|
46
|
+
return this;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('new', () => {
|
|
52
|
+
test('custom db implementation', () => {
|
|
53
|
+
const game: Game = {};
|
|
54
|
+
const db = {} as StorageAPI.Sync;
|
|
55
|
+
const server = Server({ games: [game], db });
|
|
56
|
+
expect(server.db).toBe(db);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('custom transport implementation', () => {
|
|
60
|
+
const game: Game = {};
|
|
61
|
+
const transport = { init: jest.fn() } as unknown as SocketIO;
|
|
62
|
+
Server({ games: [game], transport });
|
|
63
|
+
expect(transport.init).toBeCalled();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('custom auth implementation', () => {
|
|
67
|
+
const game: Game = {};
|
|
68
|
+
const authenticateCredentials = () => true;
|
|
69
|
+
const server = Server({ games: [game], authenticateCredentials });
|
|
70
|
+
expect(server.db).not.toBeNull();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('logs a warning if origins not set', () => {
|
|
74
|
+
Server({ games: [{}] });
|
|
75
|
+
expect(warn).toHaveBeenCalledWith(
|
|
76
|
+
expect.stringContaining('Server `origins` option is not set.')
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('does not log a warning if origins set', () => {
|
|
81
|
+
Server({ games: [{}], origins: [] });
|
|
82
|
+
expect(warn).not.toHaveBeenCalled();
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe('run', () => {
|
|
87
|
+
let server: ReturnType<typeof Server> | null;
|
|
88
|
+
let runningServer: { appServer: KoaServer; apiServer?: KoaServer } | null;
|
|
89
|
+
|
|
90
|
+
beforeEach(() => {
|
|
91
|
+
server = null;
|
|
92
|
+
runningServer = null;
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
afterEach(() => {
|
|
96
|
+
if (server && runningServer) {
|
|
97
|
+
const { apiServer, appServer } = runningServer;
|
|
98
|
+
server.kill({ apiServer, appServer });
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('single server running', async () => {
|
|
103
|
+
server = Server({ games: [game] });
|
|
104
|
+
runningServer = await server.run(undefined);
|
|
105
|
+
|
|
106
|
+
expect(server).not.toBeUndefined();
|
|
107
|
+
expect(runningServer.appServer).not.toBeUndefined();
|
|
108
|
+
expect(runningServer.apiServer).toBeUndefined();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('multiple servers running', async () => {
|
|
112
|
+
server = Server({ games: [game] });
|
|
113
|
+
runningServer = await server.run({
|
|
114
|
+
port: 57890,
|
|
115
|
+
lobbyConfig: { apiPort: 57891 },
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
expect(server).not.toBeUndefined();
|
|
119
|
+
expect(runningServer.appServer).not.toBeUndefined();
|
|
120
|
+
expect(runningServer.apiServer).not.toBeUndefined();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test('calls app callback', async () => {
|
|
124
|
+
const callback = jest.fn();
|
|
125
|
+
server = Server({ games: [game] });
|
|
126
|
+
runningServer = await server.run({ callback });
|
|
127
|
+
expect(callback).toHaveBeenCalled();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test('calls API callback', async () => {
|
|
131
|
+
const apiCallback = jest.fn();
|
|
132
|
+
server = Server({ games: [game] });
|
|
133
|
+
runningServer = await server.run({
|
|
134
|
+
lobbyConfig: { apiPort: 9999, apiCallback },
|
|
135
|
+
});
|
|
136
|
+
expect(apiCallback).toHaveBeenCalled();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test('runs route middleware', async () => {
|
|
140
|
+
const usedMiddleware = jest.fn(async (_ctx, next) => {
|
|
141
|
+
await next;
|
|
142
|
+
});
|
|
143
|
+
const unusedMiddleware = jest.fn(async (_ctx, next) => {
|
|
144
|
+
await next;
|
|
145
|
+
});
|
|
146
|
+
server = Server({ games: [game] });
|
|
147
|
+
server.router.use('/games', usedMiddleware);
|
|
148
|
+
server.router.use('/games/unused', unusedMiddleware);
|
|
149
|
+
runningServer = await server.run(8888);
|
|
150
|
+
|
|
151
|
+
await request(runningServer.appServer).get('/games');
|
|
152
|
+
expect(usedMiddleware).toHaveBeenCalled();
|
|
153
|
+
expect(unusedMiddleware).not.toHaveBeenCalled();
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
describe('kill', () => {
|
|
158
|
+
test('call close on both servers', async () => {
|
|
159
|
+
const apiServer = {
|
|
160
|
+
close: jest.fn(),
|
|
161
|
+
} as unknown as KoaServer;
|
|
162
|
+
const appServer = {
|
|
163
|
+
close: jest.fn(),
|
|
164
|
+
} as unknown as KoaServer;
|
|
165
|
+
const server = Server({ games: [game] });
|
|
166
|
+
|
|
167
|
+
server.kill({ appServer, apiServer });
|
|
168
|
+
|
|
169
|
+
expect(apiServer.close).toBeCalled();
|
|
170
|
+
expect(appServer.close).toBeCalled();
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test('do not fail if api server is not defined', async () => {
|
|
174
|
+
const appServer = {
|
|
175
|
+
close: jest.fn(),
|
|
176
|
+
} as unknown as KoaServer;
|
|
177
|
+
const server = Server({ games: [game] });
|
|
178
|
+
|
|
179
|
+
expect(() => server.kill({ appServer })).not.toThrowError();
|
|
180
|
+
expect(appServer.close).toBeCalled();
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
describe('createServerRunConfig', () => {
|
|
185
|
+
// TODO use data-driven-test here after upgrading to Jest 23+.
|
|
186
|
+
test('should return valid config with different server run arguments', () => {
|
|
187
|
+
const mockCallback = () => {};
|
|
188
|
+
const mockApiCallback = () => {};
|
|
189
|
+
|
|
190
|
+
expect(createServerRunConfig(8000)).toEqual({
|
|
191
|
+
port: 8000,
|
|
192
|
+
callback: undefined,
|
|
193
|
+
});
|
|
194
|
+
expect(createServerRunConfig(8000, mockCallback)).toEqual({
|
|
195
|
+
port: 8000,
|
|
196
|
+
callback: mockCallback,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
expect(createServerRunConfig({})).toEqual({
|
|
200
|
+
port: undefined,
|
|
201
|
+
callback: undefined,
|
|
202
|
+
});
|
|
203
|
+
expect(createServerRunConfig({ port: 1234 })).toEqual({
|
|
204
|
+
port: 1234,
|
|
205
|
+
callback: undefined,
|
|
206
|
+
});
|
|
207
|
+
expect(
|
|
208
|
+
createServerRunConfig({ port: 1234, callback: mockCallback })
|
|
209
|
+
).toEqual({
|
|
210
|
+
port: 1234,
|
|
211
|
+
callback: mockCallback,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
expect(
|
|
215
|
+
createServerRunConfig({ port: 1234, lobbyConfig: { apiPort: 5467 } })
|
|
216
|
+
).toEqual({
|
|
217
|
+
port: 1234,
|
|
218
|
+
callback: undefined,
|
|
219
|
+
lobbyConfig: { apiPort: 5467 },
|
|
220
|
+
});
|
|
221
|
+
expect(
|
|
222
|
+
createServerRunConfig({
|
|
223
|
+
port: 1234,
|
|
224
|
+
callback: mockCallback,
|
|
225
|
+
lobbyConfig: {
|
|
226
|
+
apiPort: 5467,
|
|
227
|
+
apiCallback: mockApiCallback,
|
|
228
|
+
},
|
|
229
|
+
})
|
|
230
|
+
).toEqual({
|
|
231
|
+
port: 1234,
|
|
232
|
+
callback: mockCallback,
|
|
233
|
+
lobbyConfig: {
|
|
234
|
+
apiPort: 5467,
|
|
235
|
+
apiCallback: mockApiCallback,
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
describe('getPortFromServer', () => {
|
|
242
|
+
test('returns null', () => {
|
|
243
|
+
expect(
|
|
244
|
+
getPortFromServer({
|
|
245
|
+
address: () => null,
|
|
246
|
+
} as KoaServer)
|
|
247
|
+
).toBeNull();
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
test('returns port', () => {
|
|
251
|
+
expect(
|
|
252
|
+
getPortFromServer({
|
|
253
|
+
address: () => '8000',
|
|
254
|
+
} as KoaServer)
|
|
255
|
+
).toBe('8000');
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
test('returns port from address object', () => {
|
|
259
|
+
expect(
|
|
260
|
+
getPortFromServer({
|
|
261
|
+
address: () => ({ port: '8000' }),
|
|
262
|
+
} as unknown as KoaServer)
|
|
263
|
+
).toBe('8000');
|
|
264
|
+
});
|
|
265
|
+
});
|
|
@@ -0,0 +1,175 @@
|
|
|
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 Koa from 'koa';
|
|
10
|
+
import Router from '@koa/router';
|
|
11
|
+
import type { CorsOptions } from 'cors';
|
|
12
|
+
|
|
13
|
+
import { configureRouter, configureApp } from './api';
|
|
14
|
+
import { DBFromEnv } from './db';
|
|
15
|
+
import { ProcessGameConfig } from '../core/game';
|
|
16
|
+
import * as logger from '../core/logger';
|
|
17
|
+
import { Auth } from './auth';
|
|
18
|
+
import { SocketIO } from './transport/socketio';
|
|
19
|
+
import type { Server as ServerTypes, Game, StorageAPI } from '../types';
|
|
20
|
+
|
|
21
|
+
export type KoaServer = ReturnType<Koa['listen']>;
|
|
22
|
+
export { configureRouter };
|
|
23
|
+
export { configureApp };
|
|
24
|
+
|
|
25
|
+
interface ServerConfig {
|
|
26
|
+
port?: number;
|
|
27
|
+
callback?: () => void;
|
|
28
|
+
lobbyConfig?: {
|
|
29
|
+
apiPort: number;
|
|
30
|
+
apiCallback?: () => void;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface HttpsOptions {
|
|
35
|
+
cert: string;
|
|
36
|
+
key: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Build config object from server run arguments.
|
|
41
|
+
*/
|
|
42
|
+
export const createServerRunConfig = (
|
|
43
|
+
portOrConfig: number | ServerConfig,
|
|
44
|
+
callback?: () => void
|
|
45
|
+
): ServerConfig =>
|
|
46
|
+
portOrConfig && typeof portOrConfig === 'object'
|
|
47
|
+
? {
|
|
48
|
+
...portOrConfig,
|
|
49
|
+
callback: portOrConfig.callback || callback,
|
|
50
|
+
}
|
|
51
|
+
: { port: portOrConfig as number, callback };
|
|
52
|
+
|
|
53
|
+
export const getPortFromServer = (
|
|
54
|
+
server: KoaServer
|
|
55
|
+
): string | number | null => {
|
|
56
|
+
const address = server.address();
|
|
57
|
+
if (typeof address === 'string') return address;
|
|
58
|
+
if (address === null) return null;
|
|
59
|
+
return address.port;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
interface ServerOpts {
|
|
63
|
+
games: Game[];
|
|
64
|
+
origins?: CorsOptions['origin'];
|
|
65
|
+
apiOrigins?: CorsOptions['origin'];
|
|
66
|
+
db?: StorageAPI.Async | StorageAPI.Sync;
|
|
67
|
+
transport?: SocketIO;
|
|
68
|
+
uuid?: () => string;
|
|
69
|
+
authenticateCredentials?: ServerTypes.AuthenticateCredentials;
|
|
70
|
+
generateCredentials?: ServerTypes.GenerateCredentials;
|
|
71
|
+
https?: HttpsOptions;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Instantiate a game server.
|
|
76
|
+
*
|
|
77
|
+
* @param games - The games that this server will handle.
|
|
78
|
+
* @param db - The interface with the database.
|
|
79
|
+
* @param transport - The interface with the clients.
|
|
80
|
+
* @param authenticateCredentials - Function to test player credentials.
|
|
81
|
+
* @param origins - Allowed origins to use this server, e.g. `['http://localhost:3000']`.
|
|
82
|
+
* @param apiOrigins - Allowed origins to use the Lobby API, defaults to `origins`.
|
|
83
|
+
* @param generateCredentials - Method for API to generate player credentials.
|
|
84
|
+
* @param https - HTTPS configuration options passed through to the TLS module.
|
|
85
|
+
* @param lobbyConfig - Configuration options for the Lobby API server.
|
|
86
|
+
*/
|
|
87
|
+
export function Server({
|
|
88
|
+
games,
|
|
89
|
+
db,
|
|
90
|
+
transport,
|
|
91
|
+
https,
|
|
92
|
+
uuid,
|
|
93
|
+
origins,
|
|
94
|
+
apiOrigins = origins,
|
|
95
|
+
generateCredentials = uuid,
|
|
96
|
+
authenticateCredentials,
|
|
97
|
+
}: ServerOpts) {
|
|
98
|
+
const app: ServerTypes.App = new Koa();
|
|
99
|
+
|
|
100
|
+
games = games.map((game) => ProcessGameConfig(game));
|
|
101
|
+
|
|
102
|
+
if (db === undefined) {
|
|
103
|
+
db = DBFromEnv();
|
|
104
|
+
}
|
|
105
|
+
app.context.db = db;
|
|
106
|
+
|
|
107
|
+
const auth = new Auth({ authenticateCredentials, generateCredentials });
|
|
108
|
+
app.context.auth = auth;
|
|
109
|
+
|
|
110
|
+
if (transport === undefined) {
|
|
111
|
+
transport = new SocketIO({ https });
|
|
112
|
+
}
|
|
113
|
+
if (origins === undefined) {
|
|
114
|
+
console.warn(
|
|
115
|
+
'Server `origins` option is not set.\n' +
|
|
116
|
+
'Since boardgame.io@0.45, CORS is not enabled by default and you must ' +
|
|
117
|
+
'explicitly set the origins that are allowed to connect to the server.\n' +
|
|
118
|
+
'See https://boardgame.io/documentation/#/api/Server'
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
transport.init(app, games, origins);
|
|
122
|
+
|
|
123
|
+
const router = new Router<any, ServerTypes.AppCtx>();
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
app,
|
|
127
|
+
db,
|
|
128
|
+
auth,
|
|
129
|
+
router,
|
|
130
|
+
transport,
|
|
131
|
+
|
|
132
|
+
run: async (portOrConfig: number | ServerConfig, callback?: () => void) => {
|
|
133
|
+
const serverRunConfig = createServerRunConfig(portOrConfig, callback);
|
|
134
|
+
configureRouter({ router, db, games, uuid, auth });
|
|
135
|
+
|
|
136
|
+
// DB
|
|
137
|
+
await db.connect();
|
|
138
|
+
|
|
139
|
+
// Lobby API
|
|
140
|
+
const lobbyConfig = serverRunConfig.lobbyConfig;
|
|
141
|
+
let apiServer: KoaServer | undefined;
|
|
142
|
+
if (!lobbyConfig || !lobbyConfig.apiPort) {
|
|
143
|
+
configureApp(app, router, apiOrigins);
|
|
144
|
+
} else {
|
|
145
|
+
// Run API in a separate Koa app.
|
|
146
|
+
const api: ServerTypes.App = new Koa();
|
|
147
|
+
api.context.db = db;
|
|
148
|
+
api.context.auth = auth;
|
|
149
|
+
configureApp(api, router, apiOrigins);
|
|
150
|
+
await new Promise((resolve) => {
|
|
151
|
+
apiServer = api.listen(lobbyConfig.apiPort, resolve);
|
|
152
|
+
});
|
|
153
|
+
if (lobbyConfig.apiCallback) lobbyConfig.apiCallback();
|
|
154
|
+
logger.info(`API serving on ${getPortFromServer(apiServer)}...`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Run Game Server (+ API, if necessary).
|
|
158
|
+
let appServer: KoaServer;
|
|
159
|
+
await new Promise((resolve) => {
|
|
160
|
+
appServer = app.listen(serverRunConfig.port, resolve);
|
|
161
|
+
});
|
|
162
|
+
if (serverRunConfig.callback) serverRunConfig.callback();
|
|
163
|
+
logger.info(`App serving on ${getPortFromServer(appServer)}...`);
|
|
164
|
+
|
|
165
|
+
return { apiServer, appServer };
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
kill: (servers: { apiServer?: KoaServer; appServer: KoaServer }) => {
|
|
169
|
+
if (servers.apiServer) {
|
|
170
|
+
servers.apiServer.close();
|
|
171
|
+
}
|
|
172
|
+
servers.appServer.close();
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Generic interface for pub-subs. */
|
|
2
|
+
export interface GenericPubSub<T> {
|
|
3
|
+
// Publish an event for a match.
|
|
4
|
+
publish(channelId: string, payload: T);
|
|
5
|
+
|
|
6
|
+
// Subscribes to events related to a single match.
|
|
7
|
+
subscribe(channelId: string, callback: (payload: T) => void): void;
|
|
8
|
+
|
|
9
|
+
// Cleans up subscription for a given channel.
|
|
10
|
+
unsubscribeAll(channelId: string);
|
|
11
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { InMemoryPubSub } from './in-memory-pub-sub';
|
|
2
|
+
|
|
3
|
+
const CHANNEL_FOO = 'foo';
|
|
4
|
+
|
|
5
|
+
describe('in-memory pubsub', () => {
|
|
6
|
+
it('should receive message from subscription', () => {
|
|
7
|
+
const pubSub = new InMemoryPubSub<string>();
|
|
8
|
+
const callback = jest.fn();
|
|
9
|
+
pubSub.subscribe(CHANNEL_FOO, callback);
|
|
10
|
+
const payload = 'hello world';
|
|
11
|
+
pubSub.publish(CHANNEL_FOO, payload);
|
|
12
|
+
expect(callback).toHaveBeenCalledWith(payload);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should receive message from two subscriptions', () => {
|
|
16
|
+
const pubSub = new InMemoryPubSub<string>();
|
|
17
|
+
const callback1 = jest.fn();
|
|
18
|
+
const callback2 = jest.fn();
|
|
19
|
+
pubSub.subscribe(CHANNEL_FOO, callback1);
|
|
20
|
+
pubSub.subscribe(CHANNEL_FOO, callback2);
|
|
21
|
+
const payload = 'hello world';
|
|
22
|
+
pubSub.publish(CHANNEL_FOO, payload);
|
|
23
|
+
expect(callback1).toHaveBeenCalledWith(payload);
|
|
24
|
+
expect(callback2).toHaveBeenCalledWith(payload);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should unsubscribe', () => {
|
|
28
|
+
const pubSub = new InMemoryPubSub<string>();
|
|
29
|
+
const callback = jest.fn();
|
|
30
|
+
pubSub.subscribe(CHANNEL_FOO, callback);
|
|
31
|
+
pubSub.unsubscribeAll(CHANNEL_FOO);
|
|
32
|
+
const payload = 'hello world';
|
|
33
|
+
pubSub.publish(CHANNEL_FOO, payload);
|
|
34
|
+
expect(callback).not.toHaveBeenCalled();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should ignore extra unsubscribe', () => {
|
|
38
|
+
const pubSub = new InMemoryPubSub<string>();
|
|
39
|
+
const callback = jest.fn();
|
|
40
|
+
pubSub.subscribe(CHANNEL_FOO, callback);
|
|
41
|
+
pubSub.unsubscribeAll(CHANNEL_FOO);
|
|
42
|
+
pubSub.unsubscribeAll(CHANNEL_FOO); // do nothing
|
|
43
|
+
const payload = 'hello world';
|
|
44
|
+
pubSub.publish(CHANNEL_FOO, payload);
|
|
45
|
+
expect(callback).not.toHaveBeenCalled();
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { GenericPubSub } from './generic-pub-sub';
|
|
2
|
+
|
|
3
|
+
export class InMemoryPubSub<T> implements GenericPubSub<T> {
|
|
4
|
+
callbacks: Map<string, ((payload: T) => void)[]> = new Map();
|
|
5
|
+
|
|
6
|
+
publish(channelId: string, payload: T) {
|
|
7
|
+
if (!this.callbacks.has(channelId)) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
const allCallbacks = this.callbacks.get(channelId);
|
|
11
|
+
for (const callback of allCallbacks) {
|
|
12
|
+
callback(payload);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
subscribe(channelId: string, callback: (payload: T) => void): void {
|
|
17
|
+
if (!this.callbacks.has(channelId)) {
|
|
18
|
+
this.callbacks.set(channelId, []);
|
|
19
|
+
}
|
|
20
|
+
this.callbacks.get(channelId).push(callback);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
unsubscribeAll(channelId: string) {
|
|
24
|
+
if (this.callbacks.has(channelId)) {
|
|
25
|
+
this.callbacks.delete(channelId);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|