diff --git a/docs/documentation/CHANGELOG.md b/docs/documentation/CHANGELOG.md index 3fcecdd2d..0f57b5881 100644 --- a/docs/documentation/CHANGELOG.md +++ b/docs/documentation/CHANGELOG.md @@ -1,3 +1,9 @@ +### v0.45.2 + +#### Bugfixes + +* [[9753c0e](https://github.com/boardgameio/boardgame.io/commit/9753c0e)] fix: Don’t leak `STRIP_TRANSIENTS` action ([#961](https://github.com/boardgameio/boardgame.io/pull/961)) + ### v0.45.1 #### Breaking Changes diff --git a/package-lock.json b/package-lock.json index b38124c36..4e151cf6b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "boardgame.io", - "version": "0.45.1", + "version": "0.45.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -91,9 +91,9 @@ "optional": true }, "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "optional": true, "requires": { @@ -5024,12 +5024,6 @@ "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", "dev": true }, - "async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", - "dev": true - }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -7575,6 +7569,11 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==" } } }, @@ -7611,6 +7610,11 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==" } } }, @@ -7905,9 +7909,9 @@ "dev": true }, "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { "is-glob": "^4.0.1" @@ -8672,9 +8676,9 @@ } }, "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { "is-glob": "^4.0.1" @@ -10896,8 +10900,7 @@ }, "yargs-parser": { "version": "13.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", - "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "resolved": "", "dev": true, "requires": { "camelcase": "^5.0.0", @@ -11099,9 +11102,9 @@ } }, "ws": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", - "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", + "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", "dev": true } } @@ -11656,12 +11659,20 @@ "dev": true }, "ws": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", - "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.3.tgz", + "integrity": "sha512-jZArVERrMsKUatIdnLzqvcfydI85dvd/Fp1u/VOpfdDWQ4c9qWXe+VIeAbQ5FrDwciAkr+lzofXLz3Kuf26AOA==", "dev": true, "requires": { "async-limiter": "~1.0.0" + }, + "dependencies": { + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "dev": true + } } } } @@ -15631,6 +15642,13 @@ "debug": "~4.3.1", "engine.io-parser": "~4.0.0", "ws": "~7.4.2" + }, + "dependencies": { + "ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==" + } } }, "ms": { @@ -17406,9 +17424,10 @@ } }, "ws": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", - "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==" + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", + "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", + "dev": true }, "xdg-basedir": { "version": "3.0.0", @@ -17541,9 +17560,9 @@ } }, "yargs-parser": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz", - "integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==", + "version": "15.0.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.3.tgz", + "integrity": "sha512-/MVEVjTXy/cGAjdtQf8dW3V9b97bPN7rNn8ETj6BmAQL7ibC7O1Q9SPJbGjgh3SlwoBNXMzj/ZGIj8mBgl12YA==", "dev": true, "requires": { "camelcase": "^5.0.0", diff --git a/package.json b/package.json index f1b78c35e..d752e95e2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "boardgame.io", - "version": "0.45.1", + "version": "0.45.2", "description": "library for turn-based games", "repository": "https://github.com/boardgameio/boardgame.io", "scripts": { diff --git a/src/client/client.test.ts b/src/client/client.test.ts index 7c5b54e79..62c8125c8 100644 --- a/src/client/client.test.ts +++ b/src/client/client.test.ts @@ -22,6 +22,7 @@ import { gameEvent, patch, } from '../core/action-creators'; +import * as Actions from '../core/action-types'; import Debug from './debug/Debug.svelte'; import { error } from '../core/logger'; import type { LogEntry, State, SyncInfo } from '../types'; @@ -144,7 +145,7 @@ describe('multiplayer', () => { beforeAll(() => { client = Client({ - game: { moves: { A: () => {} } }, + game: { moves: { A: () => {}, Invalid: () => INVALID_MOVE } }, multiplayer: SocketIO({ server: host + ':' + port }), }); client.start(); @@ -173,6 +174,18 @@ describe('multiplayer', () => { expect(client.transport.onAction).toHaveBeenCalled(); }); + test('strip transients action not sent to transport', () => { + jest.spyOn(client.transport, 'onAction'); + const state = { G: {}, ctx: { phase: '' }, plugins: {} }; + const filteredMetadata = []; + client.store.dispatch(sync({ state, filteredMetadata } as SyncInfo)); + client.moves.Invalid(); + expect(client.transport.onAction).not.toHaveBeenCalledWith( + expect.any(Object), + { type: Actions.STRIP_TRANSIENTS } + ); + }); + test('Sends and receives chat messages', () => { jest.spyOn(client.transport, 'onAction'); client.updatePlayerID('0'); diff --git a/src/client/client.ts b/src/client/client.ts index 5f0726544..028ff665d 100644 --- a/src/client/client.ts +++ b/src/client/client.ts @@ -43,7 +43,10 @@ type ClientAction = | ActionShape.Sync | ActionShape.Update | ActionShape.Patch; -type Action = CredentialedActionShape.Any | ClientAction; +type Action = + | CredentialedActionShape.Any + | ActionShape.StripTransients + | ClientAction; export interface DebugOpt { target?: HTMLElement; @@ -287,7 +290,10 @@ export class _ClientImpl { const baseState = store.getState(); const result = next(action); - if (!('clientOnly' in action)) { + if ( + !('clientOnly' in action) && + action.type !== Actions.STRIP_TRANSIENTS + ) { this.transport.onAction(baseState, action); } diff --git a/src/master/master.test.ts b/src/master/master.test.ts index fb2021bdf..95c88e3dd 100644 --- a/src/master/master.test.ts +++ b/src/master/master.test.ts @@ -233,6 +233,18 @@ describe('update', () => { ]); }); + test('missing action', async () => { + const { error } = await master.onUpdate(null, 0, 'matchID', '0'); + expect(sendAll).not.toHaveBeenCalled(); + expect(error).toBe('missing action or action payload'); + }); + + test('missing action payload', async () => { + const { error } = await master.onUpdate({}, 0, 'matchID', '0'); + expect(sendAll).not.toHaveBeenCalled(); + expect(error).toBe('missing action or action payload'); + }); + test('invalid matchID', async () => { await master.onUpdate(action, 0, 'default:unknown', '1'); expect(sendAll).not.toHaveBeenCalled(); diff --git a/src/master/master.ts b/src/master/master.ts index ddb5960f2..c1a692c6a 100644 --- a/src/master/master.ts +++ b/src/master/master.ts @@ -161,6 +161,10 @@ export class Master { matchID: string, playerID: string ): Promise { + if (!credAction || !credAction.payload) { + return { error: 'missing action or action payload' }; + } + let metadata: Server.MatchData | undefined; if (StorageAPI.isSynchronous(this.storageAPI)) { ({ metadata } = this.storageAPI.fetch(matchID, { metadata: true })); diff --git a/src/types.ts b/src/types.ts index 7c0b306e5..85257e213 100644 --- a/src/types.ts +++ b/src/types.ts @@ -431,7 +431,9 @@ export namespace ActionShape { export type Redo = StripCredentials; // Private type used only for internal error processing. // Included here to preserve type-checking of reducer inputs. - type _StripTransients = ReturnType; + export type StripTransients = ReturnType< + typeof ActionCreators.stripTransients + >; export type Any = | MakeMove | GameEvent @@ -443,7 +445,7 @@ export namespace ActionShape { | Undo | Redo | Plugin - | _StripTransients; + | StripTransients; } export namespace ActionPayload {