@couch-kit/client 0.3.0

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 ADDED
@@ -0,0 +1,59 @@
1
+ # @couch-kit/client
2
+
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 95cafe9: Fix critical bugs and improve reliability across all packages.
8
+
9
+ **@couch-kit/core**: Make `IGameState.status` a flexible `string` type instead of a restrictive union, allowing games to define custom phases like `"round_end"` or `"game_over"`.
10
+
11
+ **@couch-kit/client**: Fix WELCOME message handler to hydrate state immediately on connect instead of waiting for the next STATE_UPDATE broadcast. Add `name` and `avatar` config options to `useGameClient` so games can customize player identity. Wrap `localStorage` access in try/catch for Safari private browsing and restrictive WebView compatibility.
12
+
13
+ **@couch-kit/host**: Rewrite WebSocket frame processing with proper buffer management — process all complete frames per TCP packet instead of only the first (fixes silent data loss). Add RFC 6455 opcode handling for close frames (send close response), ping frames (respond with pong), and graceful handling of binary/unknown frames. Fix corrupt JSON no longer permanently blocking a connection. Retain post-handshake data that arrives in the same TCP packet. Improve socket ID generation from ~5-6 chars to 21 chars to eliminate collision risk. Update stale `declarations.d.ts` to match actual `react-native-nitro-http-server` import.
14
+
15
+ **@couch-kit/cli**: Fix `simulate` command default WebSocket URL from port 8081 to 8082 to match actual server port.
16
+
17
+ ### Patch Changes
18
+
19
+ - Updated dependencies [95cafe9]
20
+ - @couch-kit/core@0.2.0
21
+
22
+ ## 0.2.0
23
+
24
+ ### Minor Changes
25
+
26
+ - 38bc20e: Change default WebSocket port convention from HTTP+1 to HTTP+2 to avoid conflicts with Metro bundler (which uses port 8081). Add configurable `wsPort` option to client config. Both host and client now derive WS port as `httpPort + 2` by default (e.g., HTTP 8080 -> WS 8082).
27
+
28
+ ## 0.1.0
29
+
30
+ ### Minor Changes
31
+
32
+ - - **Session Recovery:** Added support for session recovery. The client now stores a secret and sends it on join. The host tracks these secrets and restores the session (mapping the new socket to the old session).
33
+ - **Automatic Hydration:** The client now automatically handles the `HYDRATE` action. Users no longer need to implement this in their reducer.
34
+ - **Large Message Support:** The Host WebSocket implementation now supports messages larger than 64KB (64-bit frame lengths).
35
+
36
+ ### Patch Changes
37
+
38
+ - Updated dependencies
39
+ - @couch-kit/core@0.1.0
40
+
41
+ ## 0.0.4
42
+
43
+ ### Patch Changes
44
+
45
+ - Fix dependency resolution for @couch-kit/core
46
+
47
+ ## 0.0.3
48
+
49
+ ### Patch Changes
50
+
51
+ - Fix dependency mismatch: update dependencies to point to published @couch-kit/core@0.0.2
52
+
53
+ ## 0.0.2
54
+
55
+ ### Patch Changes
56
+
57
+ - Initial public release. Removed example apps, refactored logging, and prepared strictly typed packages for usage.
58
+ - Updated dependencies
59
+ - @couch-kit/core@0.0.2
package/README.md ADDED
@@ -0,0 +1,151 @@
1
+ # @couch-kit/client
2
+
3
+ The client-side library for the web controller. Designed to be lightweight and framework-agnostic (though React hooks are provided).
4
+
5
+ ## Features
6
+
7
+ - **Default connection:** By default, connects to `ws(s)://{window.location.hostname}:8082`.
8
+ - **Time synchronization:** `useServerTime()` helps estimate server time using periodic ping/pong.
9
+ - **Asset preloading:** `usePreload()` is a small helper for preloading images and fetching other URLs.
10
+ - **Optimistic UI:** State updates apply locally immediately while being sent to the server.
11
+ - **Reconnection attempts:** Automatically retries the WebSocket connection when it drops.
12
+ - **Session Recovery:** Persists a secret in local storage to recover the same player session after a page refresh.
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ bun add @couch-kit/client
18
+ ```
19
+
20
+ ## API
21
+
22
+ ### `useGameClient(config)`
23
+
24
+ Connects the controller to the TV host WebSocket, applies state updates, and provides an action sender.
25
+
26
+ Config:
27
+
28
+ - `reducer`: `(state, action) => state` (your shared reducer)
29
+ - `initialState`: initial state used until the host hydrates
30
+ - `url?`: explicit WebSocket URL. If omitted, the hook uses `ws(s)://{window.location.hostname}:8082`.
31
+ - `debug?`: enable console logs
32
+ - `onConnect?`, `onDisconnect?`: lifecycle callbacks
33
+
34
+ Returns:
35
+
36
+ - `status`: `'connecting' | 'connected' | 'disconnected' | 'error'`
37
+ - `state`: current controller state (optimistic + hydrated)
38
+ - `playerId`: server-assigned player id (after `WELCOME`)
39
+ - `sendAction(action)`: optimistic dispatch + send to host
40
+ - `getServerTime()`: NTP-ish server time based on periodic ping/pong
41
+
42
+ ## State Sync Contract
43
+
44
+ The host broadcasts full state snapshots. The client automatically applies them using a higher-order reducer wrapper.
45
+
46
+ **You do NOT need to handle `HYDRATE` manually in your reducer.** The library now handles this automatically by intercepting the hydration action before it reaches your reducer logic.
47
+
48
+ Just write your reducer as if it were local:
49
+
50
+ ```ts
51
+ function reducer(state, action) {
52
+ switch (action.type) {
53
+ case "SCORE":
54
+ return { ...state, score: state.score + 1 };
55
+ default:
56
+ return state;
57
+ }
58
+ }
59
+ ```
60
+
61
+ ## Dev Mode (Controller Served From Laptop)
62
+
63
+ If your controller is served from your laptop (Vite), `window.location.hostname` is the laptop, so the “magic connection” will try to connect WS to the laptop.
64
+
65
+ In dev, pass the TV WebSocket URL explicitly:
66
+
67
+ ```ts
68
+ useGameClient({
69
+ reducer,
70
+ initialState,
71
+ url: "ws://TV_IP:8082",
72
+ });
73
+ ```
74
+
75
+ ## Usage
76
+
77
+ ### 1. The Main Hook
78
+
79
+ The `useGameClient` hook manages the WebSocket connection and synchronizes state with the TV.
80
+
81
+ ```tsx
82
+ import { useGameClient } from "@couch-kit/client";
83
+ import { gameReducer, initialState } from "./shared/types";
84
+
85
+ export default function Controller() {
86
+ const {
87
+ status, // 'connecting' | 'connected' | 'disconnected' | 'error'
88
+ state, // The current game state (synced with Host)
89
+ playerId, // Your unique session ID
90
+ sendAction, // Function to send actions to Host
91
+ } = useGameClient({
92
+ reducer: gameReducer,
93
+ initialState: initialState,
94
+ onConnect: () => console.log("Joined the party!"),
95
+ onDisconnect: () => console.log("Left the party!"),
96
+ });
97
+
98
+ if (status === "connecting") {
99
+ return <div>Connecting to TV...</div>;
100
+ }
101
+
102
+ return (
103
+ <div className="controller">
104
+ <h1>Score: {state.score}</h1>
105
+ <button onClick={() => sendAction({ type: "JUMP" })}>JUMP!</button>
106
+ </div>
107
+ );
108
+ }
109
+ ```
110
+
111
+ ### 2. Time Synchronization
112
+
113
+ For rhythm games or precise countdowns, use `getServerTime()` instead of `Date.now()`. This accounts for network latency.
114
+
115
+ ```tsx
116
+ import { useServerTime } from "@couch-kit/client";
117
+
118
+ function Countdown({ targetTimestamp }) {
119
+ const { getServerTime } = useServerTime();
120
+
121
+ // Calculate seconds remaining based on SERVER time
122
+ const now = getServerTime();
123
+ const remaining = Math.max(0, targetTimestamp - now);
124
+
125
+ return <div>{Math.ceil(remaining / 1000)}s</div>;
126
+ }
127
+ ```
128
+
129
+ ### 3. Asset Preloading
130
+
131
+ Ensure heavy assets (images, sounds) are fully loaded before showing the game interface.
132
+
133
+ ```tsx
134
+ import { usePreload } from "@couch-kit/client";
135
+
136
+ const ASSETS = ["/images/avatar_1.png", "/sounds/buzz.mp3"];
137
+
138
+ function App() {
139
+ const { loaded, progress } = usePreload(ASSETS);
140
+
141
+ if (!loaded) {
142
+ return <div>Loading... {Math.round(progress)}%</div>;
143
+ }
144
+
145
+ return <GameController />;
146
+ }
147
+ ```
148
+
149
+ ## Notes / Limitations
150
+
151
+ - `usePreload()` preloads images via `Image()` and uses `fetch()` for other URLs; it does not currently send the protocol `ASSETS_LOADED` message.