@couch-kit/core 0.5.1 → 0.6.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,34 @@
1
1
  # @couch-kit/core
2
2
 
3
+ ## 0.6.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
+
3
32
  ## 0.5.1
4
33
 
5
34
  ### Patch Changes
package/dist/index.js CHANGED
@@ -89,6 +89,7 @@ var DEFAULT_BASE_DELAY = 1000;
89
89
  var DEFAULT_MAX_DELAY = 1e4;
90
90
  var DEFAULT_SYNC_INTERVAL = 5000;
91
91
  var MAX_PENDING_PINGS = 50;
92
+ var DEFAULT_DISCONNECT_TIMEOUT = 5 * 60 * 1000;
92
93
  var KEEPALIVE_INTERVAL = 30000;
93
94
  var KEEPALIVE_TIMEOUT = 1e4;
94
95
  function generateId() {
@@ -108,7 +109,12 @@ function isValidSecret(secret) {
108
109
  const hex = secret.replace(/-/g, "");
109
110
  return hex.length >= 32 && /^[0-9a-f]+$/i.test(hex);
110
111
  }
111
- function derivePlayerId(secret) {
112
+ async function derivePlayerId(secret) {
113
+ const data = new TextEncoder().encode(secret);
114
+ const hash = await crypto.subtle.digest("SHA-256", data);
115
+ return Array.from(new Uint8Array(hash)).map((b) => b.toString(16).padStart(2, "0")).join("").slice(0, 16);
116
+ }
117
+ function derivePlayerIdLegacy(secret) {
112
118
  return secret.replace(/-/g, "").slice(0, 16);
113
119
  }
114
120
  function toErrorMessage(error) {
@@ -120,6 +126,7 @@ export {
120
126
  toErrorMessage,
121
127
  isValidSecret,
122
128
  generateId,
129
+ derivePlayerIdLegacy,
123
130
  derivePlayerId,
124
131
  createGameReducer,
125
132
  MessageTypes,
@@ -133,5 +140,6 @@ export {
133
140
  DEFAULT_MAX_RETRIES,
134
141
  DEFAULT_MAX_DELAY,
135
142
  DEFAULT_HTTP_PORT,
143
+ DEFAULT_DISCONNECT_TIMEOUT,
136
144
  DEFAULT_BASE_DELAY
137
145
  };
@@ -3,7 +3,7 @@
3
3
  export declare const DEFAULT_HTTP_PORT = 8080;
4
4
  /** Default WebSocket port offset from the HTTP port (skips Metro on +1). */
5
5
  export declare const DEFAULT_WS_PORT_OFFSET = 2;
6
- /** Maximum allowed WebSocket frame payload size (1 MB). */
6
+ /** @deprecated Not used by Couch Kit internals. Will be removed in the next major version. */
7
7
  export declare const MAX_FRAME_SIZE: number;
8
8
  /** Default maximum reconnection attempts for the client. */
9
9
  export declare const DEFAULT_MAX_RETRIES = 5;
@@ -15,9 +15,11 @@ export declare const DEFAULT_MAX_DELAY = 10000;
15
15
  export declare const DEFAULT_SYNC_INTERVAL = 5000;
16
16
  /** Maximum number of outstanding pings before cleanup. */
17
17
  export declare const MAX_PENDING_PINGS = 50;
18
- /** Server-side keepalive ping interval (ms). */
18
+ /** Default timeout (ms) before a disconnected player is removed from state. */
19
+ export declare const DEFAULT_DISCONNECT_TIMEOUT: number;
20
+ /** @deprecated Not used by Couch Kit internals. Will be removed in the next major version. */
19
21
  export declare const KEEPALIVE_INTERVAL = 30000;
20
- /** Keepalive timeout -- disconnect if no pong received (ms). */
22
+ /** @deprecated Not used by Couch Kit internals. Will be removed in the next major version. */
21
23
  export declare const KEEPALIVE_TIMEOUT = 10000;
22
24
  /**
23
25
  * Generate a cryptographically random ID string.
@@ -31,13 +33,17 @@ export declare function generateId(): string;
31
33
  */
32
34
  export declare function isValidSecret(secret: string): boolean;
33
35
  /**
34
- * Derives a stable, public player ID from a secret UUID.
36
+ * Derives a stable, public player ID from a secret UUID using SHA-256.
35
37
  *
36
- * Strips dashes and takes the first 16 hex characters. This is NOT a
37
- * cryptographic hash — it simply avoids exposing the raw secret in
38
- * state that gets broadcast to all clients.
38
+ * Takes the first 16 hex characters of the SHA-256 hash, producing
39
+ * a deterministic ID that cannot be reversed to recover the secret.
39
40
  */
40
- export declare function derivePlayerId(secret: string): string;
41
+ export declare function derivePlayerId(secret: string): Promise<string>;
42
+ /**
43
+ * @deprecated Legacy player ID derivation. Insecure — exposes first 16 hex chars of secret.
44
+ * Kept temporarily for dual-derivation migration in the host. Will be removed in next major.
45
+ */
46
+ export declare function derivePlayerIdLegacy(secret: string): string;
41
47
  /**
42
48
  * Safely extract an error message from an unknown caught value.
43
49
  * In JavaScript, anything can be thrown -- this normalizes it.
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,wDAAwD;AAExD,oDAAoD;AACpD,eAAO,MAAM,iBAAiB,OAAO,CAAC;AAEtC,4EAA4E;AAC5E,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAExC,2DAA2D;AAC3D,eAAO,MAAM,cAAc,QAAc,CAAC;AAE1C,4DAA4D;AAC5D,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC,oEAAoE;AACpE,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAEvC,+DAA+D;AAC/D,eAAO,MAAM,iBAAiB,QAAQ,CAAC;AAEvC,4CAA4C;AAC5C,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAE1C,0DAA0D;AAC1D,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAEpC,gDAAgD;AAChD,eAAO,MAAM,kBAAkB,QAAQ,CAAC;AAExC,gEAAgE;AAChE,eAAO,MAAM,iBAAiB,QAAQ,CAAC;AAEvC;;;;;GAKG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAwBnC;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAGrD;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAGrD"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,wDAAwD;AAExD,oDAAoD;AACpD,eAAO,MAAM,iBAAiB,OAAO,CAAC;AAEtC,4EAA4E;AAC5E,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAExC,8FAA8F;AAC9F,eAAO,MAAM,cAAc,QAAc,CAAC;AAE1C,4DAA4D;AAC5D,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC,oEAAoE;AACpE,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAEvC,+DAA+D;AAC/D,eAAO,MAAM,iBAAiB,QAAQ,CAAC;AAEvC,4CAA4C;AAC5C,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAE1C,0DAA0D;AAC1D,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAEpC,+EAA+E;AAC/E,eAAO,MAAM,0BAA0B,QAAgB,CAAC;AAExD,8FAA8F;AAC9F,eAAO,MAAM,kBAAkB,QAAQ,CAAC;AAExC,8FAA8F;AAC9F,eAAO,MAAM,iBAAiB,QAAQ,CAAC;AAEvC;;;;;GAKG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAwBnC;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAGrD;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAOpE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAGrD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@couch-kit/core",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "provenance": true
package/src/constants.ts CHANGED
@@ -6,7 +6,7 @@ export const DEFAULT_HTTP_PORT = 8080;
6
6
  /** Default WebSocket port offset from the HTTP port (skips Metro on +1). */
7
7
  export const DEFAULT_WS_PORT_OFFSET = 2;
8
8
 
9
- /** Maximum allowed WebSocket frame payload size (1 MB). */
9
+ /** @deprecated Not used by Couch Kit internals. Will be removed in the next major version. */
10
10
  export const MAX_FRAME_SIZE = 1024 * 1024;
11
11
 
12
12
  /** Default maximum reconnection attempts for the client. */
@@ -24,10 +24,13 @@ export const DEFAULT_SYNC_INTERVAL = 5000;
24
24
  /** Maximum number of outstanding pings before cleanup. */
25
25
  export const MAX_PENDING_PINGS = 50;
26
26
 
27
- /** Server-side keepalive ping interval (ms). */
27
+ /** Default timeout (ms) before a disconnected player is removed from state. */
28
+ export const DEFAULT_DISCONNECT_TIMEOUT = 5 * 60 * 1000;
29
+
30
+ /** @deprecated Not used by Couch Kit internals. Will be removed in the next major version. */
28
31
  export const KEEPALIVE_INTERVAL = 30000;
29
32
 
30
- /** Keepalive timeout -- disconnect if no pong received (ms). */
33
+ /** @deprecated Not used by Couch Kit internals. Will be removed in the next major version. */
31
34
  export const KEEPALIVE_TIMEOUT = 10000;
32
35
 
33
36
  /**
@@ -71,13 +74,25 @@ export function isValidSecret(secret: string): boolean {
71
74
  }
72
75
 
73
76
  /**
74
- * Derives a stable, public player ID from a secret UUID.
77
+ * Derives a stable, public player ID from a secret UUID using SHA-256.
75
78
  *
76
- * Strips dashes and takes the first 16 hex characters. This is NOT a
77
- * cryptographic hash — it simply avoids exposing the raw secret in
78
- * state that gets broadcast to all clients.
79
+ * Takes the first 16 hex characters of the SHA-256 hash, producing
80
+ * a deterministic ID that cannot be reversed to recover the secret.
81
+ */
82
+ export async function derivePlayerId(secret: string): Promise<string> {
83
+ const data = new TextEncoder().encode(secret);
84
+ const hash = await crypto.subtle.digest("SHA-256", data);
85
+ return Array.from(new Uint8Array(hash))
86
+ .map((b) => b.toString(16).padStart(2, "0"))
87
+ .join("")
88
+ .slice(0, 16);
89
+ }
90
+
91
+ /**
92
+ * @deprecated Legacy player ID derivation. Insecure — exposes first 16 hex chars of secret.
93
+ * Kept temporarily for dual-derivation migration in the host. Will be removed in next major.
79
94
  */
80
- export function derivePlayerId(secret: string): string {
95
+ export function derivePlayerIdLegacy(secret: string): string {
81
96
  return secret.replace(/-/g, "").slice(0, 16);
82
97
  }
83
98