@couch-kit/client 0.6.0 → 0.7.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 CHANGED
@@ -1,5 +1,48 @@
1
1
  # @couch-kit/client
2
2
 
3
+ ## 0.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#16](https://github.com/faluciano/react-native-couch-kit/pull/16) [`86b5ff3`](https://github.com/faluciano/react-native-couch-kit/commit/86b5ff353ce9baac0c48e6a8315bbb57eb155daf) Thanks [@faluciano](https://github.com/faluciano)! - ### Phase 1: Quick Wins — Bug Fixes, Security, and Cleanup
8
+
9
+ **Bug fixes:**
10
+ - Fix `simulate` command: add missing `secret` to JOIN payload so bots are no longer rejected with `INVALID_SECRET`
11
+ - Send `RECONNECTED` message (instead of `WELCOME`) when a player reconnects, enabling clients to distinguish reconnections
12
+ - Wire `ASSETS_LOADED` end-to-end: `usePreload` now accepts an optional `sendMessage` callback to notify the host when assets finish loading; host tracks per-player asset status
13
+ - Include dispatched action(s) in `STATE_UPDATE` broadcasts via an action queue, so clients receive the `action` field
14
+
15
+ **Security:**
16
+ - Replace `derivePlayerId` with SHA-256 (Web Crypto API) — the old implementation exposed the first 16 hex chars of the player secret
17
+ - Add dual-derivation migration: host tries SHA-256 first, falls back to legacy ID for existing players
18
+ - Add per-socket rate limiting (60 actions/second) with `RATE_LIMITED` error response
19
+ - Cache `socketId → playerId` mapping to avoid async derivation in hot paths
20
+
21
+ **Cleanup:**
22
+ - Remove dead code: `buffer-utils.ts`, `declarations.d.ts`, and their test file (TCP WebSocket remnants)
23
+ - Deprecate unused constants: `MAX_FRAME_SIZE`, `KEEPALIVE_INTERVAL`, `KEEPALIVE_TIMEOUT` (will be removed in next major)
24
+ - Extract `DEFAULT_DISCONNECT_TIMEOUT` constant (replaces hardcoded 5-minute magic number)
25
+ - Add configurable `disconnectTimeout` to `GameHostConfig`
26
+
27
+ **DX improvements:**
28
+ - Auto-detect package manager in `bundle` command (checks lock files instead of hardcoding `bun`)
29
+ - Show build output (`stdio: "inherit"` instead of `"ignore"`)
30
+ - Log bot WELCOME/ERROR responses in `simulate` command
31
+
32
+ ### Patch Changes
33
+
34
+ - Updated dependencies [[`86b5ff3`](https://github.com/faluciano/react-native-couch-kit/commit/86b5ff353ce9baac0c48e6a8315bbb57eb155daf)]:
35
+ - @couch-kit/core@0.6.0
36
+
37
+ ## 0.6.1
38
+
39
+ ### Patch Changes
40
+
41
+ - [#9](https://github.com/faluciano/react-native-couch-kit/pull/9) [`db8bbc9`](https://github.com/faluciano/react-native-couch-kit/commit/db8bbc95c3a3334c97da3e000b2c28b9c2959fb1) Thanks [@faluciano](https://github.com/faluciano)! - Add publishConfig with public access and provenance attestation to all packages. Add missing prepublishOnly build script to CLI package.
42
+
43
+ - Updated dependencies [[`db8bbc9`](https://github.com/faluciano/react-native-couch-kit/commit/db8bbc95c3a3334c97da3e000b2c28b9c2959fb1)]:
44
+ - @couch-kit/core@0.5.1
45
+
3
46
  ## 0.6.0
4
47
 
5
48
  ### Minor Changes
package/README.md CHANGED
@@ -28,6 +28,7 @@ Config:
28
28
  - `reducer`: `(state, action) => state` (your shared reducer)
29
29
  - `initialState`: initial state used until the host hydrates
30
30
  - `url?`: explicit WebSocket URL. If omitted, the hook uses `ws(s)://{window.location.hostname}:8082`.
31
+ - `wsPort?`: WebSocket port override (default: auto-detected from page URL using HTTP port + 2)
31
32
  - `name?`: player display name (default: `"Player"`)
32
33
  - `avatar?`: player avatar emoji (default: `"\u{1F600}"`)
33
34
  - `maxRetries?`: maximum reconnection attempts before giving up (default: `5`)
package/dist/index.js CHANGED
@@ -240,6 +240,7 @@ function useGameClient(config) {
240
240
  }
241
241
  // src/assets.ts
242
242
  import { useState as useState3, useEffect as useEffect3, useRef as useRef3 } from "react";
243
+ import { MessageTypes as MessageTypes3 } from "@couch-kit/core";
243
244
  function arraysEqual(a, b) {
244
245
  if (a.length !== b.length)
245
246
  return false;
@@ -249,10 +250,12 @@ function arraysEqual(a, b) {
249
250
  }
250
251
  return true;
251
252
  }
252
- function usePreload(assets) {
253
+ function usePreload(assets, sendMessage) {
253
254
  const [loaded, setLoaded] = useState3(false);
254
255
  const [progress, setProgress] = useState3(0);
255
256
  const [failedAssets, setFailedAssets] = useState3([]);
257
+ const sendMessageRef = useRef3(sendMessage);
258
+ sendMessageRef.current = sendMessage;
256
259
  const prevAssets = useRef3(assets);
257
260
  if (!arraysEqual(prevAssets.current, assets)) {
258
261
  prevAssets.current = assets;
@@ -263,6 +266,10 @@ function usePreload(assets) {
263
266
  setLoaded(true);
264
267
  setProgress(100);
265
268
  setFailedAssets([]);
269
+ sendMessageRef.current?.({
270
+ type: MessageTypes3.ASSETS_LOADED,
271
+ payload: true
272
+ });
266
273
  return;
267
274
  }
268
275
  let loadedCount = 0;
@@ -277,6 +284,10 @@ function usePreload(assets) {
277
284
  if (loadedCount === total) {
278
285
  setFailedAssets([...failed]);
279
286
  setLoaded(true);
287
+ sendMessageRef.current?.({
288
+ type: MessageTypes3.ASSETS_LOADED,
289
+ payload: true
290
+ });
280
291
  }
281
292
  };
282
293
  setLoaded(false);
package/lib/assets.d.ts CHANGED
@@ -13,5 +13,8 @@ export interface PreloadResult {
13
13
  * that failed to load). Failed assets still count toward progress so the hook
14
14
  * always reaches 100 % — check `failedAssets.length` to decide how to react.
15
15
  */
16
- export declare function usePreload(assets: string[]): PreloadResult;
16
+ export declare function usePreload(assets: string[], sendMessage?: (msg: {
17
+ type: string;
18
+ payload: unknown;
19
+ }) => void): PreloadResult;
17
20
  //# sourceMappingURL=assets.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../src/assets.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa;IAC5B,qEAAqE;IACrE,MAAM,EAAE,OAAO,CAAC;IAChB,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,sCAAsC;IACtC,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAcD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,aAAa,CAqE1D"}
1
+ {"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../src/assets.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,aAAa;IAC5B,qEAAqE;IACrE,MAAM,EAAE,OAAO,CAAC;IAChB,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,sCAAsC;IACtC,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAcD;;;;;;GAMG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,MAAM,EAAE,EAChB,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,KAAK,IAAI,GAC9D,aAAa,CAkFf"}
package/package.json CHANGED
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "name": "@couch-kit/client",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
+ "publishConfig": {
5
+ "access": "public",
6
+ "provenance": true
7
+ },
4
8
  "description": "React hooks for phone controllers in Couch Kit party games — WebSocket client and action dispatch",
5
9
  "license": "MIT",
6
10
  "repository": {
@@ -43,7 +47,7 @@
43
47
  "clean": "rm -rf dist lib"
44
48
  },
45
49
  "dependencies": {
46
- "@couch-kit/core": "0.5.0"
50
+ "@couch-kit/core": "0.6.0"
47
51
  },
48
52
  "devDependencies": {
49
53
  "react": "^18.2.0",
package/src/assets.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { useState, useEffect, useRef } from "react";
2
+ import { MessageTypes } from "@couch-kit/core";
2
3
 
3
4
  export interface PreloadResult {
4
5
  /** Whether all assets have finished loading (including failures). */
@@ -28,11 +29,19 @@ function arraysEqual(a: string[], b: string[]): boolean {
28
29
  * that failed to load). Failed assets still count toward progress so the hook
29
30
  * always reaches 100 % — check `failedAssets.length` to decide how to react.
30
31
  */
31
- export function usePreload(assets: string[]): PreloadResult {
32
+ export function usePreload(
33
+ assets: string[],
34
+ sendMessage?: (msg: { type: string; payload: unknown }) => void,
35
+ ): PreloadResult {
32
36
  const [loaded, setLoaded] = useState(false);
33
37
  const [progress, setProgress] = useState(0);
34
38
  const [failedAssets, setFailedAssets] = useState<string[]>([]);
35
39
 
40
+ // Stable ref for the callback to avoid re-triggering the effect
41
+ // when the caller passes a new function reference each render.
42
+ const sendMessageRef = useRef(sendMessage);
43
+ sendMessageRef.current = sendMessage;
44
+
36
45
  // Stable reference to the last asset list so we don't depend on
37
46
  // `JSON.stringify(assets)` — which creates a new string every render.
38
47
  const prevAssets = useRef<string[]>(assets);
@@ -49,6 +58,10 @@ export function usePreload(assets: string[]): PreloadResult {
49
58
  setLoaded(true);
50
59
  setProgress(100);
51
60
  setFailedAssets([]);
61
+ sendMessageRef.current?.({
62
+ type: MessageTypes.ASSETS_LOADED,
63
+ payload: true,
64
+ });
52
65
  return;
53
66
  }
54
67
 
@@ -64,6 +77,10 @@ export function usePreload(assets: string[]): PreloadResult {
64
77
  if (loadedCount === total) {
65
78
  setFailedAssets([...failed]);
66
79
  setLoaded(true);
80
+ sendMessageRef.current?.({
81
+ type: MessageTypes.ASSETS_LOADED,
82
+ payload: true,
83
+ });
67
84
  }
68
85
  };
69
86