@couch-kit/core 0.9.0 → 0.9.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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @couch-kit/core
2
2
 
3
+ ## 0.9.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#54](https://github.com/faluciano/react-native-couch-kit/pull/54) [`6860811`](https://github.com/faluciano/react-native-couch-kit/commit/6860811ec2fc0648fd9fd9d676c1b6ebcb4d14d0) Thanks [@faluciano](https://github.com/faluciano)! - Move `createGameReducer` and `CreateGameReducerOptions` from `types.ts` into a dedicated `reducer.ts` module. This is an internal refactor — the public API is unchanged (both remain exported from `@couch-kit/core`) and runtime behaviour is identical. It also removes a circular import between `types.ts` and `middleware.ts`.
8
+
3
9
  ## 0.9.0
4
10
 
5
11
  ### Minor Changes
package/dist/index.cjs CHANGED
@@ -56,6 +56,14 @@ __export(exports_src, {
56
56
  });
57
57
  module.exports = __toCommonJS(exports_src);
58
58
 
59
+ // src/types.ts
60
+ var InternalActionTypes = {
61
+ HYDRATE: "__HYDRATE__",
62
+ PLAYER_JOINED: "__PLAYER_JOINED__",
63
+ PLAYER_LEFT: "__PLAYER_LEFT__",
64
+ PLAYER_RECONNECTED: "__PLAYER_RECONNECTED__",
65
+ PLAYER_REMOVED: "__PLAYER_REMOVED__"
66
+ };
59
67
  // src/middleware.ts
60
68
  function applyMiddleware(...middlewares) {
61
69
  return (reducer) => {
@@ -130,14 +138,7 @@ function actionValidator(schema) {
130
138
  };
131
139
  }
132
140
 
133
- // src/types.ts
134
- var InternalActionTypes = {
135
- HYDRATE: "__HYDRATE__",
136
- PLAYER_JOINED: "__PLAYER_JOINED__",
137
- PLAYER_LEFT: "__PLAYER_LEFT__",
138
- PLAYER_RECONNECTED: "__PLAYER_RECONNECTED__",
139
- PLAYER_REMOVED: "__PLAYER_REMOVED__"
140
- };
141
+ // src/reducer.ts
141
142
  function createGameReducer(reducer, options) {
142
143
  const baseReducer = (state, action) => {
143
144
  switch (action.type) {
package/dist/index.js CHANGED
@@ -1,3 +1,11 @@
1
+ // src/types.ts
2
+ var InternalActionTypes = {
3
+ HYDRATE: "__HYDRATE__",
4
+ PLAYER_JOINED: "__PLAYER_JOINED__",
5
+ PLAYER_LEFT: "__PLAYER_LEFT__",
6
+ PLAYER_RECONNECTED: "__PLAYER_RECONNECTED__",
7
+ PLAYER_REMOVED: "__PLAYER_REMOVED__"
8
+ };
1
9
  // src/middleware.ts
2
10
  function applyMiddleware(...middlewares) {
3
11
  return (reducer) => {
@@ -72,14 +80,7 @@ function actionValidator(schema) {
72
80
  };
73
81
  }
74
82
 
75
- // src/types.ts
76
- var InternalActionTypes = {
77
- HYDRATE: "__HYDRATE__",
78
- PLAYER_JOINED: "__PLAYER_JOINED__",
79
- PLAYER_LEFT: "__PLAYER_LEFT__",
80
- PLAYER_RECONNECTED: "__PLAYER_RECONNECTED__",
81
- PLAYER_REMOVED: "__PLAYER_REMOVED__"
82
- };
83
+ // src/reducer.ts
83
84
  function createGameReducer(reducer, options) {
84
85
  const baseReducer = (state, action) => {
85
86
  switch (action.type) {
package/lib/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./types";
2
+ export * from "./reducer";
2
3
  export * from "./protocol";
3
4
  export * from "./constants";
4
5
  export * from "./replay";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC"}
@@ -0,0 +1,32 @@
1
+ import type { Middleware } from "./middleware";
2
+ import type { IGameState, IAction, InternalAction, GameReducer } from "./types";
3
+ /** Options for `createGameReducer`. */
4
+ export interface CreateGameReducerOptions<S extends IGameState, A extends IAction> {
5
+ /**
6
+ * Optional middleware stack. Middlewares are applied in order — the first
7
+ * middleware in the array is the outermost layer and sees every action first.
8
+ *
9
+ * Middleware can observe **and** transform both user actions and internal
10
+ * actions. Custom middleware should avoid blocking internal actions (types
11
+ * prefixed with `__`) to prevent breaking framework behaviour.
12
+ */
13
+ middleware?: Middleware<S, A>[];
14
+ }
15
+ /**
16
+ * Wraps a user-provided reducer with automatic handling of internal actions:
17
+ *
18
+ * - `__HYDRATE__` -- Replaces state wholesale (used for server→client state sync).
19
+ * - `__PLAYER_JOINED__` -- Adds a player to `state.players`.
20
+ * - `__PLAYER_LEFT__` -- Marks a player as disconnected in `state.players`.
21
+ * - `__PLAYER_RECONNECTED__` -- Restores a returning player's connection status, preserving all existing data.
22
+ * - `__PLAYER_REMOVED__` -- Permanently removes a timed-out disconnected player from `state.players`.
23
+ *
24
+ * The wrapped reducer accepts both `A` (user actions) and `InternalAction<S>`.
25
+ * User reducers only need to handle their own action types.
26
+ *
27
+ * When `options.middleware` is provided, the middleware stack wraps around the
28
+ * entire reducer (including internal-action handling). Middleware can observe
29
+ * all actions flowing through the reducer.
30
+ */
31
+ export declare function createGameReducer<S extends IGameState, A extends IAction>(reducer: GameReducer<S, A>, options?: CreateGameReducerOptions<S, A>): GameReducer<S, A | InternalAction<S>>;
32
+ //# sourceMappingURL=reducer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reducer.d.ts","sourceRoot":"","sources":["../src/reducer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,OAAO,KAAK,EACV,UAAU,EACV,OAAO,EACP,cAAc,EACd,WAAW,EACZ,MAAM,SAAS,CAAC;AAEjB,uCAAuC;AACvC,MAAM,WAAW,wBAAwB,CACvC,CAAC,SAAS,UAAU,EACpB,CAAC,SAAS,OAAO;IAEjB;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;CACjC;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,UAAU,EAAE,CAAC,SAAS,OAAO,EACvE,OAAO,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,EAC1B,OAAO,CAAC,EAAE,wBAAwB,CAAC,CAAC,EAAE,CAAC,CAAC,GACvC,WAAW,CAAC,CAAC,EAAE,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC,CAgFvC"}
package/lib/types.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- import type { Middleware } from "./middleware";
2
1
  /**
3
2
  * Represents a player connected to the game session.
4
3
  *
@@ -85,33 +84,4 @@ export declare const InternalActionTypes: {
85
84
  * Used by both `GameHostProvider` and `useGameClient` to define game logic.
86
85
  */
87
86
  export type GameReducer<S extends IGameState, A extends IAction> = (state: S, action: A) => S;
88
- /** Options for `createGameReducer`. */
89
- export interface CreateGameReducerOptions<S extends IGameState, A extends IAction> {
90
- /**
91
- * Optional middleware stack. Middlewares are applied in order — the first
92
- * middleware in the array is the outermost layer and sees every action first.
93
- *
94
- * Middleware can observe **and** transform both user actions and internal
95
- * actions. Custom middleware should avoid blocking internal actions (types
96
- * prefixed with `__`) to prevent breaking framework behaviour.
97
- */
98
- middleware?: Middleware<S, A>[];
99
- }
100
- /**
101
- * Wraps a user-provided reducer with automatic handling of internal actions:
102
- *
103
- * - `__HYDRATE__` -- Replaces state wholesale (used for server→client state sync).
104
- * - `__PLAYER_JOINED__` -- Adds a player to `state.players`.
105
- * - `__PLAYER_LEFT__` -- Marks a player as disconnected in `state.players`.
106
- * - `__PLAYER_RECONNECTED__` -- Restores a returning player's connection status, preserving all existing data.
107
- * - `__PLAYER_REMOVED__` -- Permanently removes a timed-out disconnected player from `state.players`.
108
- *
109
- * The wrapped reducer accepts both `A` (user actions) and `InternalAction<S>`.
110
- * User reducers only need to handle their own action types.
111
- *
112
- * When `options.middleware` is provided, the middleware stack wraps around the
113
- * entire reducer (including internal-action handling). Middleware can observe
114
- * all actions flowing through the reducer.
115
- */
116
- export declare function createGameReducer<S extends IGameState, A extends IAction>(reducer: GameReducer<S, A>, options?: CreateGameReducerOptions<S, A>): GameReducer<S, A | InternalAction<S>>;
117
87
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED;;;;;GAKG;AACH,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU,IACxD;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,CAAC,CAAA;CAAE,GACnC;IACE,IAAI,EAAE,mBAAmB,CAAC;IAC1B,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACxD,GACD;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAC1D;IAAE,IAAI,EAAE,wBAAwB,CAAC;IAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GACjE;IAAE,IAAI,EAAE,oBAAoB,CAAC;IAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC;AAElE,+CAA+C;AAC/C,eAAO,MAAM,mBAAmB;;;;;;CAMtB,CAAC;AAEX;;;;;GAKG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,UAAU,EAAE,CAAC,SAAS,OAAO,IAAI,CACjE,KAAK,EAAE,CAAC,EACR,MAAM,EAAE,CAAC,KACN,CAAC,CAAC;AAEP,uCAAuC;AACvC,MAAM,WAAW,wBAAwB,CACvC,CAAC,SAAS,UAAU,EACpB,CAAC,SAAS,OAAO;IAEjB;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;CACjC;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,UAAU,EAAE,CAAC,SAAS,OAAO,EACvE,OAAO,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,EAC1B,OAAO,CAAC,EAAE,wBAAwB,CAAC,CAAC,EAAE,CAAC,CAAC,GACvC,WAAW,CAAC,CAAC,EAAE,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC,CAgFvC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED;;;;;GAKG;AACH,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU,IACxD;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,CAAC,CAAA;CAAE,GACnC;IACE,IAAI,EAAE,mBAAmB,CAAC;IAC1B,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACxD,GACD;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAC1D;IAAE,IAAI,EAAE,wBAAwB,CAAC;IAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GACjE;IAAE,IAAI,EAAE,oBAAoB,CAAC;IAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC;AAElE,+CAA+C;AAC/C,eAAO,MAAM,mBAAmB;;;;;;CAMtB,CAAC;AAEX;;;;;GAKG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,UAAU,EAAE,CAAC,SAAS,OAAO,IAAI,CACjE,KAAK,EAAE,CAAC,EACR,MAAM,EAAE,CAAC,KACN,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@couch-kit/core",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "provenance": true
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./types";
2
+ export * from "./reducer";
2
3
  export * from "./protocol";
3
4
  export * from "./constants";
4
5
  export * from "./replay";
package/src/reducer.ts ADDED
@@ -0,0 +1,126 @@
1
+ import { applyMiddleware } from "./middleware";
2
+ import type { Middleware } from "./middleware";
3
+ import { InternalActionTypes } from "./types";
4
+ import type {
5
+ IGameState,
6
+ IAction,
7
+ InternalAction,
8
+ GameReducer,
9
+ } from "./types";
10
+
11
+ /** Options for `createGameReducer`. */
12
+ export interface CreateGameReducerOptions<
13
+ S extends IGameState,
14
+ A extends IAction,
15
+ > {
16
+ /**
17
+ * Optional middleware stack. Middlewares are applied in order — the first
18
+ * middleware in the array is the outermost layer and sees every action first.
19
+ *
20
+ * Middleware can observe **and** transform both user actions and internal
21
+ * actions. Custom middleware should avoid blocking internal actions (types
22
+ * prefixed with `__`) to prevent breaking framework behaviour.
23
+ */
24
+ middleware?: Middleware<S, A>[];
25
+ }
26
+
27
+ /**
28
+ * Wraps a user-provided reducer with automatic handling of internal actions:
29
+ *
30
+ * - `__HYDRATE__` -- Replaces state wholesale (used for server→client state sync).
31
+ * - `__PLAYER_JOINED__` -- Adds a player to `state.players`.
32
+ * - `__PLAYER_LEFT__` -- Marks a player as disconnected in `state.players`.
33
+ * - `__PLAYER_RECONNECTED__` -- Restores a returning player's connection status, preserving all existing data.
34
+ * - `__PLAYER_REMOVED__` -- Permanently removes a timed-out disconnected player from `state.players`.
35
+ *
36
+ * The wrapped reducer accepts both `A` (user actions) and `InternalAction<S>`.
37
+ * User reducers only need to handle their own action types.
38
+ *
39
+ * When `options.middleware` is provided, the middleware stack wraps around the
40
+ * entire reducer (including internal-action handling). Middleware can observe
41
+ * all actions flowing through the reducer.
42
+ */
43
+ export function createGameReducer<S extends IGameState, A extends IAction>(
44
+ reducer: GameReducer<S, A>,
45
+ options?: CreateGameReducerOptions<S, A>,
46
+ ): GameReducer<S, A | InternalAction<S>> {
47
+ const baseReducer: GameReducer<S, A | InternalAction<S>> = (
48
+ state: S,
49
+ action: A | InternalAction<S>,
50
+ ): S => {
51
+ switch (action.type) {
52
+ case InternalActionTypes.HYDRATE:
53
+ return (action as InternalAction<S> & { type: "__HYDRATE__" }).payload;
54
+
55
+ case InternalActionTypes.PLAYER_JOINED: {
56
+ const { id, name, avatar } = (
57
+ action as InternalAction<S> & { type: "__PLAYER_JOINED__" }
58
+ ).payload;
59
+ return {
60
+ ...state,
61
+ players: {
62
+ ...state.players,
63
+ [id]: {
64
+ id,
65
+ name,
66
+ avatar,
67
+ isHost: false,
68
+ connected: true,
69
+ },
70
+ },
71
+ };
72
+ }
73
+
74
+ case InternalActionTypes.PLAYER_LEFT: {
75
+ const { playerId } = (
76
+ action as InternalAction<S> & { type: "__PLAYER_LEFT__" }
77
+ ).payload;
78
+ const player = state.players[playerId];
79
+ if (!player) return state;
80
+ return {
81
+ ...state,
82
+ players: {
83
+ ...state.players,
84
+ [playerId]: { ...player, connected: false },
85
+ },
86
+ };
87
+ }
88
+
89
+ case InternalActionTypes.PLAYER_RECONNECTED: {
90
+ const { playerId } = (
91
+ action as InternalAction<S> & { type: "__PLAYER_RECONNECTED__" }
92
+ ).payload;
93
+ const player = state.players[playerId];
94
+ if (!player) return state;
95
+ return {
96
+ ...state,
97
+ players: {
98
+ ...state.players,
99
+ [playerId]: { ...player, connected: true },
100
+ },
101
+ };
102
+ }
103
+
104
+ case InternalActionTypes.PLAYER_REMOVED: {
105
+ const { playerId } = (
106
+ action as InternalAction<S> & { type: "__PLAYER_REMOVED__" }
107
+ ).payload;
108
+ if (!state.players[playerId]) return state;
109
+ const { [playerId]: _, ...remainingPlayers } = state.players;
110
+ return {
111
+ ...state,
112
+ players: remainingPlayers,
113
+ };
114
+ }
115
+
116
+ default:
117
+ return reducer(state, action as A);
118
+ }
119
+ };
120
+
121
+ const middlewares = options?.middleware;
122
+
123
+ if (!middlewares || middlewares.length === 0) return baseReducer;
124
+
125
+ return applyMiddleware<S, A>(...middlewares)(baseReducer);
126
+ }
package/src/types.ts CHANGED
@@ -1,6 +1,3 @@
1
- import { applyMiddleware } from "./middleware";
2
- import type { Middleware } from "./middleware";
3
-
4
1
  /**
5
2
  * Represents a player connected to the game session.
6
3
  *
@@ -78,120 +75,3 @@ export type GameReducer<S extends IGameState, A extends IAction> = (
78
75
  state: S,
79
76
  action: A,
80
77
  ) => S;
81
-
82
- /** Options for `createGameReducer`. */
83
- export interface CreateGameReducerOptions<
84
- S extends IGameState,
85
- A extends IAction,
86
- > {
87
- /**
88
- * Optional middleware stack. Middlewares are applied in order — the first
89
- * middleware in the array is the outermost layer and sees every action first.
90
- *
91
- * Middleware can observe **and** transform both user actions and internal
92
- * actions. Custom middleware should avoid blocking internal actions (types
93
- * prefixed with `__`) to prevent breaking framework behaviour.
94
- */
95
- middleware?: Middleware<S, A>[];
96
- }
97
-
98
- /**
99
- * Wraps a user-provided reducer with automatic handling of internal actions:
100
- *
101
- * - `__HYDRATE__` -- Replaces state wholesale (used for server→client state sync).
102
- * - `__PLAYER_JOINED__` -- Adds a player to `state.players`.
103
- * - `__PLAYER_LEFT__` -- Marks a player as disconnected in `state.players`.
104
- * - `__PLAYER_RECONNECTED__` -- Restores a returning player's connection status, preserving all existing data.
105
- * - `__PLAYER_REMOVED__` -- Permanently removes a timed-out disconnected player from `state.players`.
106
- *
107
- * The wrapped reducer accepts both `A` (user actions) and `InternalAction<S>`.
108
- * User reducers only need to handle their own action types.
109
- *
110
- * When `options.middleware` is provided, the middleware stack wraps around the
111
- * entire reducer (including internal-action handling). Middleware can observe
112
- * all actions flowing through the reducer.
113
- */
114
- export function createGameReducer<S extends IGameState, A extends IAction>(
115
- reducer: GameReducer<S, A>,
116
- options?: CreateGameReducerOptions<S, A>,
117
- ): GameReducer<S, A | InternalAction<S>> {
118
- const baseReducer: GameReducer<S, A | InternalAction<S>> = (
119
- state: S,
120
- action: A | InternalAction<S>,
121
- ): S => {
122
- switch (action.type) {
123
- case InternalActionTypes.HYDRATE:
124
- return (action as InternalAction<S> & { type: "__HYDRATE__" }).payload;
125
-
126
- case InternalActionTypes.PLAYER_JOINED: {
127
- const { id, name, avatar } = (
128
- action as InternalAction<S> & { type: "__PLAYER_JOINED__" }
129
- ).payload;
130
- return {
131
- ...state,
132
- players: {
133
- ...state.players,
134
- [id]: {
135
- id,
136
- name,
137
- avatar,
138
- isHost: false,
139
- connected: true,
140
- },
141
- },
142
- };
143
- }
144
-
145
- case InternalActionTypes.PLAYER_LEFT: {
146
- const { playerId } = (
147
- action as InternalAction<S> & { type: "__PLAYER_LEFT__" }
148
- ).payload;
149
- const player = state.players[playerId];
150
- if (!player) return state;
151
- return {
152
- ...state,
153
- players: {
154
- ...state.players,
155
- [playerId]: { ...player, connected: false },
156
- },
157
- };
158
- }
159
-
160
- case InternalActionTypes.PLAYER_RECONNECTED: {
161
- const { playerId } = (
162
- action as InternalAction<S> & { type: "__PLAYER_RECONNECTED__" }
163
- ).payload;
164
- const player = state.players[playerId];
165
- if (!player) return state;
166
- return {
167
- ...state,
168
- players: {
169
- ...state.players,
170
- [playerId]: { ...player, connected: true },
171
- },
172
- };
173
- }
174
-
175
- case InternalActionTypes.PLAYER_REMOVED: {
176
- const { playerId } = (
177
- action as InternalAction<S> & { type: "__PLAYER_REMOVED__" }
178
- ).payload;
179
- if (!state.players[playerId]) return state;
180
- const { [playerId]: _, ...remainingPlayers } = state.players;
181
- return {
182
- ...state,
183
- players: remainingPlayers,
184
- };
185
- }
186
-
187
- default:
188
- return reducer(state, action as A);
189
- }
190
- };
191
-
192
- const middlewares = options?.middleware;
193
-
194
- if (!middlewares || middlewares.length === 0) return baseReducer;
195
-
196
- return applyMiddleware<S, A>(...middlewares)(baseReducer);
197
- }