@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 +59 -0
- package/README.md +151 -0
- package/dist/index.js +2052 -0
- package/package.json +25 -0
- package/src/assets.ts +43 -0
- package/src/client.ts +194 -0
- package/src/index.ts +3 -0
- package/src/time-sync.ts +82 -0
- package/tests/time-sync.test.ts +42 -0
- package/tsconfig.json +11 -0
- package/tsconfig.tsbuildinfo +1 -0
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.
|