@couch-kit/core 0.9.3 → 0.10.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,35 @@
1
1
  # @couch-kit/core
2
2
 
3
+ ## 0.10.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#208](https://github.com/faluciano/react-native-couch-kit/pull/208) [`99598d6`](https://github.com/faluciano/react-native-couch-kit/commit/99598d6796b0bd3042aa9f3fbd3bf44ee891b890) Thanks [@faluciano](https://github.com/faluciano)! - `replayActions` wraps the reducer it is given with `createGameReducer`. A recording that contains internal actions (`__PLAYER_JOINED__` and the others a host recorder captures) now replays the way it ran. Before, those actions were silently ignored, because a game's own reducer does not handle them. Passing a reducer that is already wrapped is harmless.
8
+
9
+ ## 0.10.0
10
+
11
+ ### Minor Changes
12
+
13
+ - [#164](https://github.com/faluciano/react-native-couch-kit/pull/164) [`a157126`](https://github.com/faluciano/react-native-couch-kit/commit/a157126424e4d73dcc7185118d5be0db6719792e) Thanks [@faluciano](https://github.com/faluciano)! - Back the time-sync ping off to 30s once the clock estimate settles
14
+
15
+ `useGameClient`'s clock sync pinged every 5 seconds for the life of the
16
+ connection. It now starts there and doubles to a 30-second ceiling
17
+ (`MAX_SYNC_INTERVAL`), resetting to the fast interval whenever a new socket is
18
+ established, including after a reconnect.
19
+
20
+ The first few pings are what converge the offset; the clock difference they
21
+ measure does not drift on a human timescale, so the fast interval stops earning
22
+ its cost within about a minute. On a relay transport it is not free: each ping
23
+ is a message in and a `PONG` back out, and pings are the only traffic an idle
24
+ table generates at all. A four-player lobby went from 5,760 relay messages an
25
+ hour to under 1,000 while sitting untouched.
26
+
27
+ `rtt` and `getServerTime()` are unchanged in accuracy — both are updated by the
28
+ same `PONG` handling as before, just less often once settled. Games needing the
29
+ old cadence can dispatch their own pings; the constants
30
+ (`DEFAULT_SYNC_INTERVAL`, `MAX_SYNC_INTERVAL`, `SYNC_BACKOFF_FACTOR`) are
31
+ exported from `@couch-kit/core`.
32
+
3
33
  ## 0.9.3
4
34
 
5
35
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -39,30 +39,32 @@ var __export = (target, all) => {
39
39
  // src/index.ts
40
40
  var exports_src = {};
41
41
  __export(exports_src, {
42
- toErrorMessage: () => toErrorMessage,
43
- replayActions: () => replayActions,
44
- isValidSecret: () => isValidSecret,
45
- generateId: () => generateId,
46
- derivePlayerIdLegacy: () => derivePlayerIdLegacy,
47
- derivePlayerId: () => derivePlayerId,
48
- createGameReducer: () => createGameReducer,
49
- applyMiddleware: () => applyMiddleware,
50
- actionValidator: () => actionValidator,
51
- actionLogger: () => actionLogger,
52
- MessageTypes: () => MessageTypes,
53
- MAX_PENDING_PINGS: () => MAX_PENDING_PINGS,
54
- MAX_FRAME_SIZE: () => MAX_FRAME_SIZE,
55
- KEEPALIVE_TIMEOUT: () => KEEPALIVE_TIMEOUT,
56
- KEEPALIVE_INTERVAL: () => KEEPALIVE_INTERVAL,
57
- InternalActionTypes: () => InternalActionTypes,
58
- DEFAULT_WS_PORT_OFFSET: () => DEFAULT_WS_PORT_OFFSET,
59
- DEFAULT_WS_PATH: () => DEFAULT_WS_PATH,
60
- DEFAULT_SYNC_INTERVAL: () => DEFAULT_SYNC_INTERVAL,
61
- DEFAULT_MAX_RETRIES: () => DEFAULT_MAX_RETRIES,
62
- DEFAULT_MAX_DELAY: () => DEFAULT_MAX_DELAY,
63
- DEFAULT_HTTP_PORT: () => DEFAULT_HTTP_PORT,
42
+ DEFAULT_BASE_DELAY: () => DEFAULT_BASE_DELAY,
64
43
  DEFAULT_DISCONNECT_TIMEOUT: () => DEFAULT_DISCONNECT_TIMEOUT,
65
- DEFAULT_BASE_DELAY: () => DEFAULT_BASE_DELAY
44
+ DEFAULT_HTTP_PORT: () => DEFAULT_HTTP_PORT,
45
+ DEFAULT_MAX_DELAY: () => DEFAULT_MAX_DELAY,
46
+ DEFAULT_MAX_RETRIES: () => DEFAULT_MAX_RETRIES,
47
+ DEFAULT_SYNC_INTERVAL: () => DEFAULT_SYNC_INTERVAL,
48
+ DEFAULT_WS_PATH: () => DEFAULT_WS_PATH,
49
+ DEFAULT_WS_PORT_OFFSET: () => DEFAULT_WS_PORT_OFFSET,
50
+ InternalActionTypes: () => InternalActionTypes,
51
+ KEEPALIVE_INTERVAL: () => KEEPALIVE_INTERVAL,
52
+ KEEPALIVE_TIMEOUT: () => KEEPALIVE_TIMEOUT,
53
+ MAX_FRAME_SIZE: () => MAX_FRAME_SIZE,
54
+ MAX_PENDING_PINGS: () => MAX_PENDING_PINGS,
55
+ MAX_SYNC_INTERVAL: () => MAX_SYNC_INTERVAL,
56
+ MessageTypes: () => MessageTypes,
57
+ SYNC_BACKOFF_FACTOR: () => SYNC_BACKOFF_FACTOR,
58
+ actionLogger: () => actionLogger,
59
+ actionValidator: () => actionValidator,
60
+ applyMiddleware: () => applyMiddleware,
61
+ createGameReducer: () => createGameReducer,
62
+ derivePlayerId: () => derivePlayerId,
63
+ derivePlayerIdLegacy: () => derivePlayerIdLegacy,
64
+ generateId: () => generateId,
65
+ isValidSecret: () => isValidSecret,
66
+ replayActions: () => replayActions,
67
+ toErrorMessage: () => toErrorMessage
66
68
  });
67
69
  module.exports = __toCommonJS(exports_src);
68
70
 
@@ -378,6 +380,8 @@ var DEFAULT_MAX_RETRIES = 5;
378
380
  var DEFAULT_BASE_DELAY = 1000;
379
381
  var DEFAULT_MAX_DELAY = 1e4;
380
382
  var DEFAULT_SYNC_INTERVAL = 5000;
383
+ var MAX_SYNC_INTERVAL = 30000;
384
+ var SYNC_BACKOFF_FACTOR = 2;
381
385
  var MAX_PENDING_PINGS = 50;
382
386
  var DEFAULT_DISCONNECT_TIMEOUT = 5 * 60 * 1000;
383
387
  var KEEPALIVE_INTERVAL = 30000;
@@ -389,7 +393,7 @@ function generateId() {
389
393
  if (typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.getRandomValues === "function") {
390
394
  const bytes = new Uint8Array(16);
391
395
  globalThis.crypto.getRandomValues(bytes);
392
- return Array.from(bytes).map((b2) => b2.toString(16).padStart(2, "0")).join("");
396
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
393
397
  }
394
398
  const a = Math.random().toString(36).substring(2, 15);
395
399
  const b = Math.random().toString(36).substring(2, 10);
@@ -418,10 +422,11 @@ function toErrorMessage(error) {
418
422
  // src/replay.ts
419
423
  function replayActions(recording, reducer) {
420
424
  const snapshots = [];
425
+ const gameReducer = createGameReducer(reducer);
421
426
  let currentState = recording.initialState;
422
427
  for (let i = 0;i < recording.actions.length; i++) {
423
428
  const { action, timestamp } = recording.actions[i];
424
- currentState = reducer(currentState, action);
429
+ currentState = gameReducer(currentState, action);
425
430
  snapshots.push({
426
431
  state: currentState,
427
432
  action,
package/dist/index.js CHANGED
@@ -310,6 +310,8 @@ var DEFAULT_MAX_RETRIES = 5;
310
310
  var DEFAULT_BASE_DELAY = 1000;
311
311
  var DEFAULT_MAX_DELAY = 1e4;
312
312
  var DEFAULT_SYNC_INTERVAL = 5000;
313
+ var MAX_SYNC_INTERVAL = 30000;
314
+ var SYNC_BACKOFF_FACTOR = 2;
313
315
  var MAX_PENDING_PINGS = 50;
314
316
  var DEFAULT_DISCONNECT_TIMEOUT = 5 * 60 * 1000;
315
317
  var KEEPALIVE_INTERVAL = 30000;
@@ -321,7 +323,7 @@ function generateId() {
321
323
  if (typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.getRandomValues === "function") {
322
324
  const bytes = new Uint8Array(16);
323
325
  globalThis.crypto.getRandomValues(bytes);
324
- return Array.from(bytes).map((b2) => b2.toString(16).padStart(2, "0")).join("");
326
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
325
327
  }
326
328
  const a = Math.random().toString(36).substring(2, 15);
327
329
  const b = Math.random().toString(36).substring(2, 10);
@@ -350,10 +352,11 @@ function toErrorMessage(error) {
350
352
  // src/replay.ts
351
353
  function replayActions(recording, reducer) {
352
354
  const snapshots = [];
355
+ const gameReducer = createGameReducer(reducer);
353
356
  let currentState = recording.initialState;
354
357
  for (let i = 0;i < recording.actions.length; i++) {
355
358
  const { action, timestamp } = recording.actions[i];
356
- currentState = reducer(currentState, action);
359
+ currentState = gameReducer(currentState, action);
357
360
  snapshots.push({
358
361
  state: currentState,
359
362
  action,
@@ -370,28 +373,30 @@ function replayActions(recording, reducer) {
370
373
  };
371
374
  }
372
375
  export {
373
- toErrorMessage,
374
- replayActions,
375
- isValidSecret,
376
- generateId,
377
- derivePlayerIdLegacy,
378
- derivePlayerId,
379
- createGameReducer,
380
- applyMiddleware,
381
- actionValidator,
382
- actionLogger,
383
- MessageTypes,
384
- MAX_PENDING_PINGS,
385
- MAX_FRAME_SIZE,
386
- KEEPALIVE_TIMEOUT,
387
- KEEPALIVE_INTERVAL,
388
- InternalActionTypes,
389
- DEFAULT_WS_PORT_OFFSET,
390
- DEFAULT_WS_PATH,
391
- DEFAULT_SYNC_INTERVAL,
392
- DEFAULT_MAX_RETRIES,
393
- DEFAULT_MAX_DELAY,
394
- DEFAULT_HTTP_PORT,
376
+ DEFAULT_BASE_DELAY,
395
377
  DEFAULT_DISCONNECT_TIMEOUT,
396
- DEFAULT_BASE_DELAY
378
+ DEFAULT_HTTP_PORT,
379
+ DEFAULT_MAX_DELAY,
380
+ DEFAULT_MAX_RETRIES,
381
+ DEFAULT_SYNC_INTERVAL,
382
+ DEFAULT_WS_PATH,
383
+ DEFAULT_WS_PORT_OFFSET,
384
+ InternalActionTypes,
385
+ KEEPALIVE_INTERVAL,
386
+ KEEPALIVE_TIMEOUT,
387
+ MAX_FRAME_SIZE,
388
+ MAX_PENDING_PINGS,
389
+ MAX_SYNC_INTERVAL,
390
+ MessageTypes,
391
+ SYNC_BACKOFF_FACTOR,
392
+ actionLogger,
393
+ actionValidator,
394
+ applyMiddleware,
395
+ createGameReducer,
396
+ derivePlayerId,
397
+ derivePlayerIdLegacy,
398
+ generateId,
399
+ isValidSecret,
400
+ replayActions,
401
+ toErrorMessage
397
402
  };
@@ -13,8 +13,20 @@ export declare const DEFAULT_MAX_RETRIES = 5;
13
13
  export declare const DEFAULT_BASE_DELAY = 1000;
14
14
  /** Default maximum delay (ms) cap for reconnection backoff. */
15
15
  export declare const DEFAULT_MAX_DELAY = 10000;
16
- /** Default time sync ping interval (ms). */
16
+ /** Default time sync ping interval (ms) — the interval a fresh socket starts at. */
17
17
  export declare const DEFAULT_SYNC_INTERVAL = 5000;
18
+ /**
19
+ * Ceiling (ms) the time-sync interval backs off to once the clock estimate has
20
+ * settled.
21
+ *
22
+ * Pings are the only traffic an idle table generates, and on a relay transport
23
+ * every one of them is a billed message for the ping *and* the host's pong. The
24
+ * offset they measure is a clock difference that does not drift on a human
25
+ * timescale, so the fast interval only earns its cost right after connecting.
26
+ */
27
+ export declare const MAX_SYNC_INTERVAL = 30000;
28
+ /** Multiplier applied to the time-sync interval after each ping, up to {@link MAX_SYNC_INTERVAL}. */
29
+ export declare const SYNC_BACKOFF_FACTOR = 2;
18
30
  /** Maximum number of outstanding pings before cleanup. */
19
31
  export declare const MAX_PENDING_PINGS = 50;
20
32
  /** Default timeout (ms) before a disconnected player is removed from state. */
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,wDAAwD;AAIxD,oDAAoD;AACpD,eAAO,MAAM,iBAAiB,OAAO,CAAC;AAEtC,4EAA4E;AAC5E,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAExC,0DAA0D;AAC1D,eAAO,MAAM,eAAe,QAAQ,CAAC;AAErC,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;;;;;;;;;;;GAWG;AACH,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAiBpE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE3D;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;AAIxD,oDAAoD;AACpD,eAAO,MAAM,iBAAiB,OAAO,CAAC;AAEtC,4EAA4E;AAC5E,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAExC,0DAA0D;AAC1D,eAAO,MAAM,eAAe,QAAQ,CAAC;AAErC,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,oFAAoF;AACpF,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAE1C;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,QAAQ,CAAC;AAEvC,qGAAqG;AACrG,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC,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;;;;;;;;;;;GAWG;AACH,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAiBpE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAGrD"}
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AACA,MAAM,MAAM,aAAa,GACrB;IACE,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5D,GACD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,GAChE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAC5D;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE,IAAI,CAAA;CAAE,CAAC;AAG7C,MAAM,MAAM,WAAW,GACnB;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;CACnE,GACD;IACE,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;CACrE,GACD;IACE,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;CACpE,GACD;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,GACtE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC;AAElE,eAAO,MAAM,YAAY;;;;;;;;;;CAaf,CAAC"}
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AACA,MAAM,MAAM,aAAa,GACrB;IACE,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5D,GACD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,GAChE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAC5D;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE,IAAI,CAAA;CAAE,CAAC;AAG7C,MAAM,MAAM,WAAW,GACnB;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;CACnE,GACD;IACE,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;CACrE,GACD;IACE,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;CACpE,GACD;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,GACtE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC;AAElE,eAAO,MAAM,YAAY;aAEvB,IAAI,EAAE,MAAM;aACZ,MAAM,EAAE,QAAQ;aAChB,IAAI,EAAE,MAAM;aACZ,aAAa,EAAE,eAAe;aAG9B,OAAO,EAAE,SAAS;aAClB,YAAY,EAAE,cAAc;aAC5B,IAAI,EAAE,MAAM;aACZ,WAAW,EAAE,aAAa;aAC1B,KAAK,EAAE,OAAO;CACN,CAAC"}
package/lib/replay.d.ts CHANGED
@@ -37,6 +37,12 @@ export interface ReplayResult<S extends IGameState = IGameState> {
37
37
  /**
38
38
  * Replays a recording against a reducer, producing the final state
39
39
  * and intermediate snapshots for each action applied.
40
+ *
41
+ * Pass the game's own reducer, as given to the host. It is wrapped with
42
+ * {@link createGameReducer} here, so recordings that include internal actions
43
+ * (`__PLAYER_JOINED__` and friends, as a host recorder captures) replay the
44
+ * same way they ran. Wrapping an already-wrapped reducer is harmless: the outer
45
+ * layer handles internal actions and the inner never sees them.
40
46
  */
41
47
  export declare function replayActions<S extends IGameState = IGameState, A extends IAction = IAction>(recording: Recording<S, A>, reducer: GameReducer<S, A>): ReplayResult<S>;
42
48
  //# sourceMappingURL=replay.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEnE;;GAEG;AACH,MAAM,WAAW,cAAc,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO;IACzD,MAAM,EAAE,CAAC,CAAC;IACV,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU;IAC9D,KAAK,EAAE,CAAC,CAAC;IACT,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,SAAS,CACxB,CAAC,SAAS,UAAU,GAAG,UAAU,EACjC,CAAC,SAAS,OAAO,GAAG,OAAO;IAE3B,YAAY,EAAE,CAAC,CAAC;IAChB,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU;IAC7D,UAAU,EAAE,CAAC,CAAC;IACd,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,CAAC,SAAS,UAAU,GAAG,UAAU,EACjC,CAAC,SAAS,OAAO,GAAG,OAAO,EAC3B,SAAS,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CA4BzE"}
1
+ {"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEnE;;GAEG;AACH,MAAM,WAAW,cAAc,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO;IACzD,MAAM,EAAE,CAAC,CAAC;IACV,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU;IAC9D,KAAK,EAAE,CAAC,CAAC;IACT,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,SAAS,CACxB,CAAC,SAAS,UAAU,GAAG,UAAU,EACjC,CAAC,SAAS,OAAO,GAAG,OAAO;IAE3B,YAAY,EAAE,CAAC,CAAC;IAChB,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU;IAC7D,UAAU,EAAE,CAAC,CAAC;IACd,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,CAAC,SAAS,UAAU,GAAG,UAAU,EACjC,CAAC,SAAS,OAAO,GAAG,OAAO,EAC3B,SAAS,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CA6BzE"}
@@ -1 +1 @@
1
- {"version":3,"file":"sha256.d.ts","sourceRoot":"","sources":["../src/sha256.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAoDH;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAmGjD"}
1
+ {"version":3,"file":"sha256.d.ts","sourceRoot":"","sources":["../src/sha256.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAoDH;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAiGjD"}
@@ -1 +1 @@
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"}
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;aAC9B,OAAO,EAAE,aAAa;aACtB,aAAa,EAAE,mBAAmB;aAClC,WAAW,EAAE,iBAAiB;aAC9B,kBAAkB,EAAE,wBAAwB;aAC5C,cAAc,EAAE,oBAAoB;CAC5B,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.3",
3
+ "version": "0.10.1",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "provenance": true
@@ -46,11 +46,10 @@
46
46
  "build": "bun build ./src/index.ts --outdir ./dist --target browser && bun build ./src/index.ts --outfile ./dist/index.cjs --target browser --format cjs && tsc -p tsconfig.build.json",
47
47
  "prepublishOnly": "bun run build",
48
48
  "test": "bun test",
49
- "lint": "eslint src/",
50
49
  "typecheck": "tsc --noEmit",
51
50
  "clean": "rm -rf dist lib"
52
51
  },
53
52
  "devDependencies": {
54
- "typescript": "^6.0.0"
53
+ "typescript": "^7.0.0"
55
54
  }
56
55
  }
package/src/constants.ts CHANGED
@@ -23,9 +23,23 @@ export const DEFAULT_BASE_DELAY = 1000;
23
23
  /** Default maximum delay (ms) cap for reconnection backoff. */
24
24
  export const DEFAULT_MAX_DELAY = 10000;
25
25
 
26
- /** Default time sync ping interval (ms). */
26
+ /** Default time sync ping interval (ms) — the interval a fresh socket starts at. */
27
27
  export const DEFAULT_SYNC_INTERVAL = 5000;
28
28
 
29
+ /**
30
+ * Ceiling (ms) the time-sync interval backs off to once the clock estimate has
31
+ * settled.
32
+ *
33
+ * Pings are the only traffic an idle table generates, and on a relay transport
34
+ * every one of them is a billed message for the ping *and* the host's pong. The
35
+ * offset they measure is a clock difference that does not drift on a human
36
+ * timescale, so the fast interval only earns its cost right after connecting.
37
+ */
38
+ export const MAX_SYNC_INTERVAL = 30000;
39
+
40
+ /** Multiplier applied to the time-sync interval after each ping, up to {@link MAX_SYNC_INTERVAL}. */
41
+ export const SYNC_BACKOFF_FACTOR = 2;
42
+
29
43
  /** Maximum number of outstanding pings before cleanup. */
30
44
  export const MAX_PENDING_PINGS = 50;
31
45
 
package/src/replay.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { createGameReducer } from "./reducer.js";
1
2
  import type { GameReducer, IAction, IGameState } from "./types.js";
2
3
 
3
4
  /**
@@ -45,17 +46,24 @@ export interface ReplayResult<S extends IGameState = IGameState> {
45
46
  /**
46
47
  * Replays a recording against a reducer, producing the final state
47
48
  * and intermediate snapshots for each action applied.
49
+ *
50
+ * Pass the game's own reducer, as given to the host. It is wrapped with
51
+ * {@link createGameReducer} here, so recordings that include internal actions
52
+ * (`__PLAYER_JOINED__` and friends, as a host recorder captures) replay the
53
+ * same way they ran. Wrapping an already-wrapped reducer is harmless: the outer
54
+ * layer handles internal actions and the inner never sees them.
48
55
  */
49
56
  export function replayActions<
50
57
  S extends IGameState = IGameState,
51
58
  A extends IAction = IAction,
52
59
  >(recording: Recording<S, A>, reducer: GameReducer<S, A>): ReplayResult<S> {
53
60
  const snapshots: StateSnapshot<S>[] = [];
61
+ const gameReducer = createGameReducer(reducer);
54
62
  let currentState = recording.initialState;
55
63
 
56
64
  for (let i = 0; i < recording.actions.length; i++) {
57
65
  const { action, timestamp } = recording.actions[i];
58
- currentState = reducer(currentState, action);
66
+ currentState = gameReducer(currentState, action);
59
67
 
60
68
  snapshots.push({
61
69
  state: currentState,
package/src/sha256.ts CHANGED
@@ -111,10 +111,8 @@ export function sha256Hex(message: string): string {
111
111
  0;
112
112
  }
113
113
  for (let i = 16; i < 64; i++) {
114
- const s0 =
115
- rotr(w[i - 15], 7) ^ rotr(w[i - 15], 18) ^ (w[i - 15] >>> 3);
116
- const s1 =
117
- rotr(w[i - 2], 17) ^ rotr(w[i - 2], 19) ^ (w[i - 2] >>> 10);
114
+ const s0 = rotr(w[i - 15], 7) ^ rotr(w[i - 15], 18) ^ (w[i - 15] >>> 3);
115
+ const s1 = rotr(w[i - 2], 17) ^ rotr(w[i - 2], 19) ^ (w[i - 2] >>> 10);
118
116
  w[i] = (w[i - 16] + s0 + w[i - 7] + s1) >>> 0;
119
117
  }
120
118