@phalanx-engine/client 0.1.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.
Files changed (94) hide show
  1. package/README.md +1037 -0
  2. package/dist/DesyncDetector.d.ts +80 -0
  3. package/dist/DesyncDetector.d.ts.map +1 -0
  4. package/dist/DesyncDetector.js +93 -0
  5. package/dist/DesyncDetector.js.map +1 -0
  6. package/dist/DeterministicRandom.d.ts +78 -0
  7. package/dist/DeterministicRandom.d.ts.map +1 -0
  8. package/dist/DeterministicRandom.js +122 -0
  9. package/dist/DeterministicRandom.js.map +1 -0
  10. package/dist/EventEmitter.d.ts +65 -0
  11. package/dist/EventEmitter.d.ts.map +1 -0
  12. package/dist/EventEmitter.js +102 -0
  13. package/dist/EventEmitter.js.map +1 -0
  14. package/dist/FixedMath.d.ts +22 -0
  15. package/dist/FixedMath.d.ts.map +1 -0
  16. package/dist/FixedMath.js +26 -0
  17. package/dist/FixedMath.js.map +1 -0
  18. package/dist/PhalanxClient.d.ts +335 -0
  19. package/dist/PhalanxClient.d.ts.map +1 -0
  20. package/dist/PhalanxClient.js +844 -0
  21. package/dist/PhalanxClient.js.map +1 -0
  22. package/dist/RenderLoop.d.ts +95 -0
  23. package/dist/RenderLoop.d.ts.map +1 -0
  24. package/dist/RenderLoop.js +192 -0
  25. package/dist/RenderLoop.js.map +1 -0
  26. package/dist/SocketManager.d.ts +228 -0
  27. package/dist/SocketManager.d.ts.map +1 -0
  28. package/dist/SocketManager.js +584 -0
  29. package/dist/SocketManager.js.map +1 -0
  30. package/dist/StateHasher.d.ts +76 -0
  31. package/dist/StateHasher.d.ts.map +1 -0
  32. package/dist/StateHasher.js +129 -0
  33. package/dist/StateHasher.js.map +1 -0
  34. package/dist/auth/AuthManager.d.ts +188 -0
  35. package/dist/auth/AuthManager.d.ts.map +1 -0
  36. package/dist/auth/AuthManager.js +462 -0
  37. package/dist/auth/AuthManager.js.map +1 -0
  38. package/dist/auth/adapters/GoogleOAuthAdapter.d.ts +164 -0
  39. package/dist/auth/adapters/GoogleOAuthAdapter.d.ts.map +1 -0
  40. package/dist/auth/adapters/GoogleOAuthAdapter.js +521 -0
  41. package/dist/auth/adapters/GoogleOAuthAdapter.js.map +1 -0
  42. package/dist/auth/index.d.ts +45 -0
  43. package/dist/auth/index.d.ts.map +1 -0
  44. package/dist/auth/index.js +54 -0
  45. package/dist/auth/index.js.map +1 -0
  46. package/dist/auth/storage.d.ts +56 -0
  47. package/dist/auth/storage.d.ts.map +1 -0
  48. package/dist/auth/storage.js +78 -0
  49. package/dist/auth/storage.js.map +1 -0
  50. package/dist/auth/types.d.ts +212 -0
  51. package/dist/auth/types.d.ts.map +1 -0
  52. package/dist/auth/types.js +7 -0
  53. package/dist/auth/types.js.map +1 -0
  54. package/dist/index.d.ts +70 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +83 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/recovery/BrowserLifecycle.d.ts +33 -0
  59. package/dist/recovery/BrowserLifecycle.d.ts.map +1 -0
  60. package/dist/recovery/BrowserLifecycle.js +62 -0
  61. package/dist/recovery/BrowserLifecycle.js.map +1 -0
  62. package/dist/recovery/GuestPlayerIdStore.d.ts +17 -0
  63. package/dist/recovery/GuestPlayerIdStore.d.ts.map +1 -0
  64. package/dist/recovery/GuestPlayerIdStore.js +31 -0
  65. package/dist/recovery/GuestPlayerIdStore.js.map +1 -0
  66. package/dist/recovery/KeyValueStorage.d.ts +32 -0
  67. package/dist/recovery/KeyValueStorage.d.ts.map +1 -0
  68. package/dist/recovery/KeyValueStorage.js +58 -0
  69. package/dist/recovery/KeyValueStorage.js.map +1 -0
  70. package/dist/recovery/MobileTransport.d.ts +12 -0
  71. package/dist/recovery/MobileTransport.d.ts.map +1 -0
  72. package/dist/recovery/MobileTransport.js +24 -0
  73. package/dist/recovery/MobileTransport.js.map +1 -0
  74. package/dist/recovery/NetworkQuality.d.ts +22 -0
  75. package/dist/recovery/NetworkQuality.d.ts.map +1 -0
  76. package/dist/recovery/NetworkQuality.js +35 -0
  77. package/dist/recovery/NetworkQuality.js.map +1 -0
  78. package/dist/recovery/RoomPersistence.d.ts +55 -0
  79. package/dist/recovery/RoomPersistence.d.ts.map +1 -0
  80. package/dist/recovery/RoomPersistence.js +68 -0
  81. package/dist/recovery/RoomPersistence.js.map +1 -0
  82. package/dist/recovery/RoomRecoveryController.d.ts +146 -0
  83. package/dist/recovery/RoomRecoveryController.d.ts.map +1 -0
  84. package/dist/recovery/RoomRecoveryController.js +348 -0
  85. package/dist/recovery/RoomRecoveryController.js.map +1 -0
  86. package/dist/recovery/index.d.ts +13 -0
  87. package/dist/recovery/index.d.ts.map +1 -0
  88. package/dist/recovery/index.js +8 -0
  89. package/dist/recovery/index.js.map +1 -0
  90. package/dist/types.d.ts +501 -0
  91. package/dist/types.d.ts.map +1 -0
  92. package/dist/types.js +6 -0
  93. package/dist/types.js.map +1 -0
  94. package/package.json +66 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GuestPlayerIdStore.d.ts","sourceRoot":"","sources":["../../src/recovery/GuestPlayerIdStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAUpF;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,eAA0C,GAClD,MAAM,CAMR"}
@@ -0,0 +1,31 @@
1
+ import { defaultKeyValueStorage } from './KeyValueStorage.js';
2
+ /**
3
+ * Generate a fresh anonymous player id. Format mirrors the legacy
4
+ * `player-${ts}-${slug}` shape so server logs stay easy to read.
5
+ */
6
+ function generateGuestPlayerId() {
7
+ return `player-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
8
+ }
9
+ /**
10
+ * Load (or generate-and-persist) a stable guest player id.
11
+ *
12
+ * `PhalanxClient` generates a fresh id in its constructor when none is
13
+ * supplied — that id changes on every page reload, which silently
14
+ * breaks any server-side state keyed by playerId, most importantly the
15
+ * host record inside a private room. Persisting the guest id in
16
+ * localStorage lets cold-start recovery succeed for unauthenticated
17
+ * users.
18
+ *
19
+ * Falls back to an in-memory id when storage is unavailable (Safari
20
+ * private mode etc.) — recovery won't work in that environment, but
21
+ * regular play still does.
22
+ */
23
+ export function loadOrCreateGuestPlayerId(storageKey, storage = defaultKeyValueStorage()) {
24
+ const existing = storage.getItem(storageKey);
25
+ if (existing && existing.length > 0)
26
+ return existing;
27
+ const fresh = generateGuestPlayerId();
28
+ storage.setItem(storageKey, fresh);
29
+ return fresh;
30
+ }
31
+ //# sourceMappingURL=GuestPlayerIdStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GuestPlayerIdStore.js","sourceRoot":"","sources":["../../src/recovery/GuestPlayerIdStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAwB,MAAM,sBAAsB,CAAC;AAEpF;;;GAGG;AACH,SAAS,qBAAqB;IAC5B,OAAO,UAAU,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;AAC9E,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,yBAAyB,CACvC,UAAkB,EAClB,UAA2B,sBAAsB,EAAE;IAEnD,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC7C,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC;IACrD,MAAM,KAAK,GAAG,qBAAqB,EAAE,CAAC;IACtC,OAAO,CAAC,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IACnC,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Minimal sync key/value storage abstraction. `localStorage` is the
3
+ * default implementation; React Native / Capacitor / Electron host
4
+ * apps can plug in their own adapter (e.g. wrapping `Preferences`)
5
+ * without dragging in an async API surface.
6
+ */
7
+ export interface KeyValueStorage {
8
+ getItem(key: string): string | null;
9
+ setItem(key: string, value: string): void;
10
+ removeItem(key: string): void;
11
+ }
12
+ /**
13
+ * `localStorage`-backed adapter that swallows access errors (Safari
14
+ * private mode throws on `setItem`, some embedded WebViews disable
15
+ * storage entirely). Returns null when storage is unavailable so the
16
+ * caller can fall back gracefully.
17
+ */
18
+ export declare class LocalStorageAdapter implements KeyValueStorage {
19
+ getItem(key: string): string | null;
20
+ setItem(key: string, value: string): void;
21
+ removeItem(key: string): void;
22
+ }
23
+ /** In-memory fallback used in non-DOM environments and tests. */
24
+ export declare class MemoryKeyValueStorage implements KeyValueStorage {
25
+ private readonly map;
26
+ getItem(key: string): string | null;
27
+ setItem(key: string, value: string): void;
28
+ removeItem(key: string): void;
29
+ }
30
+ /** Pick the best default storage for the current environment. */
31
+ export declare function defaultKeyValueStorage(): KeyValueStorage;
32
+ //# sourceMappingURL=KeyValueStorage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KeyValueStorage.d.ts","sourceRoot":"","sources":["../../src/recovery/KeyValueStorage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACpC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED;;;;;GAKG;AACH,qBAAa,mBAAoB,YAAW,eAAe;IACzD,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IASnC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IASzC,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;CAQ9B;AAED,iEAAiE;AACjE,qBAAa,qBAAsB,YAAW,eAAe;IAC3D,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA6B;IAEjD,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAInC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAIzC,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;CAG9B;AAED,iEAAiE;AACjE,wBAAgB,sBAAsB,IAAI,eAAe,CAGxD"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * `localStorage`-backed adapter that swallows access errors (Safari
3
+ * private mode throws on `setItem`, some embedded WebViews disable
4
+ * storage entirely). Returns null when storage is unavailable so the
5
+ * caller can fall back gracefully.
6
+ */
7
+ export class LocalStorageAdapter {
8
+ getItem(key) {
9
+ try {
10
+ if (typeof localStorage === 'undefined')
11
+ return null;
12
+ return localStorage.getItem(key);
13
+ }
14
+ catch {
15
+ return null;
16
+ }
17
+ }
18
+ setItem(key, value) {
19
+ try {
20
+ if (typeof localStorage === 'undefined')
21
+ return;
22
+ localStorage.setItem(key, value);
23
+ }
24
+ catch {
25
+ // ignore — persistence is best-effort
26
+ }
27
+ }
28
+ removeItem(key) {
29
+ try {
30
+ if (typeof localStorage === 'undefined')
31
+ return;
32
+ localStorage.removeItem(key);
33
+ }
34
+ catch {
35
+ // ignore
36
+ }
37
+ }
38
+ }
39
+ /** In-memory fallback used in non-DOM environments and tests. */
40
+ export class MemoryKeyValueStorage {
41
+ map = new Map();
42
+ getItem(key) {
43
+ return this.map.has(key) ? this.map.get(key) : null;
44
+ }
45
+ setItem(key, value) {
46
+ this.map.set(key, value);
47
+ }
48
+ removeItem(key) {
49
+ this.map.delete(key);
50
+ }
51
+ }
52
+ /** Pick the best default storage for the current environment. */
53
+ export function defaultKeyValueStorage() {
54
+ if (typeof localStorage !== 'undefined')
55
+ return new LocalStorageAdapter();
56
+ return new MemoryKeyValueStorage();
57
+ }
58
+ //# sourceMappingURL=KeyValueStorage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KeyValueStorage.js","sourceRoot":"","sources":["../../src/recovery/KeyValueStorage.ts"],"names":[],"mappings":"AAYA;;;;;GAKG;AACH,MAAM,OAAO,mBAAmB;IAC9B,OAAO,CAAC,GAAW;QACjB,IAAI,CAAC;YACH,IAAI,OAAO,YAAY,KAAK,WAAW;gBAAE,OAAO,IAAI,CAAC;YACrD,OAAO,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACnC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAW,EAAE,KAAa;QAChC,IAAI,CAAC;YACH,IAAI,OAAO,YAAY,KAAK,WAAW;gBAAE,OAAO;YAChD,YAAY,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACnC,CAAC;QAAC,MAAM,CAAC;YACP,sCAAsC;QACxC,CAAC;IACH,CAAC;IAED,UAAU,CAAC,GAAW;QACpB,IAAI,CAAC;YACH,IAAI,OAAO,YAAY,KAAK,WAAW;gBAAE,OAAO;YAChD,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;IACH,CAAC;CACF;AAED,iEAAiE;AACjE,MAAM,OAAO,qBAAqB;IACf,GAAG,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEjD,OAAO,CAAC,GAAW;QACjB,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACvD,CAAC;IAED,OAAO,CAAC,GAAW,EAAE,KAAa;QAChC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC3B,CAAC;IAED,UAAU,CAAC,GAAW;QACpB,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACvB,CAAC;CACF;AAED,iEAAiE;AACjE,MAAM,UAAU,sBAAsB;IACpC,IAAI,OAAO,YAAY,KAAK,WAAW;QAAE,OAAO,IAAI,mBAAmB,EAAE,CAAC;IAC1E,OAAO,IAAI,qBAAqB,EAAE,CAAC;AACrC,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { SocketTransport } from '../types.js';
2
+ /**
3
+ * Best-effort mobile-browser sniffing. Used by `PhalanxClient` to
4
+ * default `socketTransports` to polling when `mobileFriendlyTransports`
5
+ * is opted into — Telegram WebView / iOS Safari on carrier networks
6
+ * frequently establish a websocket and then silently stop delivering
7
+ * packets, while polling reconnects transparently.
8
+ */
9
+ export declare function isMobileBrowser(): boolean;
10
+ /** Resolve the auto-defaulted transport list for the current UA. */
11
+ export declare function pickMobileFriendlyTransports(): readonly SocketTransport[];
12
+ //# sourceMappingURL=MobileTransport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MobileTransport.d.ts","sourceRoot":"","sources":["../../src/recovery/MobileTransport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD;;;;;;GAMG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAUzC;AAKD,oEAAoE;AACpE,wBAAgB,4BAA4B,IAAI,SAAS,eAAe,EAAE,CAEzE"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Best-effort mobile-browser sniffing. Used by `PhalanxClient` to
3
+ * default `socketTransports` to polling when `mobileFriendlyTransports`
4
+ * is opted into — Telegram WebView / iOS Safari on carrier networks
5
+ * frequently establish a websocket and then silently stop delivering
6
+ * packets, while polling reconnects transparently.
7
+ */
8
+ export function isMobileBrowser() {
9
+ if (typeof navigator === 'undefined')
10
+ return false;
11
+ const userAgent = navigator.userAgent;
12
+ const platform = navigator.platform;
13
+ const hasTouchScreen = navigator.maxTouchPoints > 1;
14
+ const isIpadOS = platform === 'MacIntel' && hasTouchScreen;
15
+ return (isIpadOS ||
16
+ /Android|iPhone|iPad|iPod|IEMobile|Opera Mini|Mobile/i.test(userAgent));
17
+ }
18
+ const DESKTOP_TRANSPORTS = ['websocket'];
19
+ const MOBILE_TRANSPORTS = ['polling'];
20
+ /** Resolve the auto-defaulted transport list for the current UA. */
21
+ export function pickMobileFriendlyTransports() {
22
+ return isMobileBrowser() ? MOBILE_TRANSPORTS : DESKTOP_TRANSPORTS;
23
+ }
24
+ //# sourceMappingURL=MobileTransport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MobileTransport.js","sourceRoot":"","sources":["../../src/recovery/MobileTransport.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,UAAU,eAAe;IAC7B,IAAI,OAAO,SAAS,KAAK,WAAW;QAAE,OAAO,KAAK,CAAC;IACnD,MAAM,SAAS,GAAG,SAAS,CAAC,SAAS,CAAC;IACtC,MAAM,QAAQ,GAAG,SAAS,CAAC,QAAQ,CAAC;IACpC,MAAM,cAAc,GAAG,SAAS,CAAC,cAAc,GAAG,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,QAAQ,KAAK,UAAU,IAAI,cAAc,CAAC;IAC3D,OAAO,CACL,QAAQ;QACR,sDAAsD,CAAC,IAAI,CAAC,SAAS,CAAC,CACvE,CAAC;AACJ,CAAC;AAED,MAAM,kBAAkB,GAAG,CAAC,WAAW,CAA+C,CAAC;AACvF,MAAM,iBAAiB,GAAG,CAAC,SAAS,CAA+C,CAAC;AAEpF,oEAAoE;AACpE,MAAM,UAAU,4BAA4B;IAC1C,OAAO,eAAe,EAAE,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,kBAAkB,CAAC;AACpE,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * NetworkQuality — read `navigator.connection` (when available) to pick
3
+ * a sensible recover-room request timeout. Mobile carrier networks can
4
+ * legitimately take 15+ seconds to deliver an ack on slow-2g, so a
5
+ * fixed 10s timeout would prematurely fail recovery on real mobile.
6
+ */
7
+ export interface RecoverTimeoutBudget {
8
+ /** Default timeout when the network looks healthy. */
9
+ defaultMs: number;
10
+ /** Used for 3g / 300+ms RTT. */
11
+ degradedMs: number;
12
+ /** Used for slow-2g / 2g / 600+ms RTT. */
13
+ slowMs: number;
14
+ }
15
+ export declare const DEFAULT_RECOVER_TIMEOUT_BUDGET: RecoverTimeoutBudget;
16
+ /**
17
+ * Pick a recover-room ack timeout for the current network quality.
18
+ * Returns `budget.defaultMs` when no `navigator.connection` data is
19
+ * available (older Safari, non-DOM environments, …).
20
+ */
21
+ export declare function getRecoverTimeoutMs(budget?: RecoverTimeoutBudget): number;
22
+ //# sourceMappingURL=NetworkQuality.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NetworkQuality.d.ts","sourceRoot":"","sources":["../../src/recovery/NetworkQuality.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAaH,MAAM,WAAW,oBAAoB;IACnC,sDAAsD;IACtD,SAAS,EAAE,MAAM,CAAC;IAClB,gCAAgC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,8BAA8B,EAAE,oBAI5C,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,GAAE,oBAAqD,GAC5D,MAAM,CAqBR"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * NetworkQuality — read `navigator.connection` (when available) to pick
3
+ * a sensible recover-room request timeout. Mobile carrier networks can
4
+ * legitimately take 15+ seconds to deliver an ack on slow-2g, so a
5
+ * fixed 10s timeout would prematurely fail recovery on real mobile.
6
+ */
7
+ export const DEFAULT_RECOVER_TIMEOUT_BUDGET = {
8
+ defaultMs: 10_000,
9
+ degradedMs: 15_000,
10
+ slowMs: 25_000,
11
+ };
12
+ /**
13
+ * Pick a recover-room ack timeout for the current network quality.
14
+ * Returns `budget.defaultMs` when no `navigator.connection` data is
15
+ * available (older Safari, non-DOM environments, …).
16
+ */
17
+ export function getRecoverTimeoutMs(budget = DEFAULT_RECOVER_TIMEOUT_BUDGET) {
18
+ if (typeof navigator === 'undefined')
19
+ return budget.defaultMs;
20
+ const nav = navigator;
21
+ const connection = nav.connection ?? nav.mozConnection ?? nav.webkitConnection;
22
+ if (connection?.effectiveType === 'slow-2g' ||
23
+ connection?.effectiveType === '2g') {
24
+ return budget.slowMs;
25
+ }
26
+ if (typeof connection?.rtt === 'number' && connection.rtt >= 600) {
27
+ return budget.slowMs;
28
+ }
29
+ if (connection?.effectiveType === '3g' ||
30
+ (typeof connection?.rtt === 'number' && connection.rtt >= 300)) {
31
+ return budget.degradedMs;
32
+ }
33
+ return budget.defaultMs;
34
+ }
35
+ //# sourceMappingURL=NetworkQuality.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NetworkQuality.js","sourceRoot":"","sources":["../../src/recovery/NetworkQuality.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAsBH,MAAM,CAAC,MAAM,8BAA8B,GAAyB;IAClE,SAAS,EAAE,MAAM;IACjB,UAAU,EAAE,MAAM;IAClB,MAAM,EAAE,MAAM;CACf,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CACjC,SAA+B,8BAA8B;IAE7D,IAAI,OAAO,SAAS,KAAK,WAAW;QAAE,OAAO,MAAM,CAAC,SAAS,CAAC;IAC9D,MAAM,GAAG,GAAG,SAAoC,CAAC;IACjD,MAAM,UAAU,GAAG,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,aAAa,IAAI,GAAG,CAAC,gBAAgB,CAAC;IAE/E,IACE,UAAU,EAAE,aAAa,KAAK,SAAS;QACvC,UAAU,EAAE,aAAa,KAAK,IAAI,EAClC,CAAC;QACD,OAAO,MAAM,CAAC,MAAM,CAAC;IACvB,CAAC;IACD,IAAI,OAAO,UAAU,EAAE,GAAG,KAAK,QAAQ,IAAI,UAAU,CAAC,GAAG,IAAI,GAAG,EAAE,CAAC;QACjE,OAAO,MAAM,CAAC,MAAM,CAAC;IACvB,CAAC;IACD,IACE,UAAU,EAAE,aAAa,KAAK,IAAI;QAClC,CAAC,OAAO,UAAU,EAAE,GAAG,KAAK,QAAQ,IAAI,UAAU,CAAC,GAAG,IAAI,GAAG,CAAC,EAC9D,CAAC;QACD,OAAO,MAAM,CAAC,UAAU,CAAC;IAC3B,CAAC;IACD,OAAO,MAAM,CAAC,SAAS,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,55 @@
1
+ import { type KeyValueStorage } from './KeyValueStorage.js';
2
+ /**
3
+ * RoomPersistence — survives an active private-room session across:
4
+ * - mobile browser backgrounding (where the WebSocket dies but JS
5
+ * state is usually preserved by bfcache)
6
+ * - hard reloads, as long as the same `playerId` is still available
7
+ * after reload (authenticated users always; guests when the
8
+ * `GuestPlayerIdStore` is used)
9
+ *
10
+ * Stored in `localStorage` by default (NOT `sessionStorage`) so that
11
+ * iOS Safari, which discards `sessionStorage` when the tab is killed,
12
+ * still has the entry available the next time the app is opened.
13
+ *
14
+ * Recovery is best-effort and bounded by the room TTL: if the stored
15
+ * room entry has expired, or a true cold start no longer has access to
16
+ * the previous `playerId`, recovery is not expected to succeed.
17
+ */
18
+ export type RoomRole = 'host' | 'guest';
19
+ export interface PersistedRoom {
20
+ readonly code: string;
21
+ readonly role: RoomRole;
22
+ readonly playerId: string;
23
+ readonly createdAt: number;
24
+ readonly expiresAt: number;
25
+ }
26
+ export interface RoomPersistenceConfig {
27
+ /** localStorage key. */
28
+ storageKey: string;
29
+ /** TTL mirroring the server's RoomService.ROOM_TTL_MS. */
30
+ roomTtlMs: number;
31
+ /** Storage adapter; defaults to localStorage. */
32
+ storage?: KeyValueStorage;
33
+ }
34
+ export declare class RoomPersistence {
35
+ private readonly config;
36
+ private readonly storage;
37
+ constructor(config: RoomPersistenceConfig);
38
+ /**
39
+ * Best-effort save. Errors are logged and swallowed — losing the
40
+ * persistence layer should never break room creation itself.
41
+ */
42
+ save(input: {
43
+ code: string;
44
+ role: RoomRole;
45
+ playerId: string;
46
+ }): void;
47
+ /**
48
+ * Returns the stored room if it exists and is still within its TTL.
49
+ * Auto-evicts an expired entry so subsequent calls don't keep trying
50
+ * to recover something the server has long since destroyed.
51
+ */
52
+ load(): PersistedRoom | null;
53
+ clear(): void;
54
+ }
55
+ //# sourceMappingURL=RoomPersistence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RoomPersistence.d.ts","sourceRoot":"","sources":["../../src/recovery/RoomPersistence.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEpF;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAExC,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,qBAAqB;IACpC,wBAAwB;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,0DAA0D;IAC1D,SAAS,EAAE,MAAM,CAAC;IAClB,iDAAiD;IACjD,OAAO,CAAC,EAAE,eAAe,CAAC;CAC3B;AAED,qBAAa,eAAe;IAGd,OAAO,CAAC,QAAQ,CAAC,MAAM;IAFnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkB;gBAEb,MAAM,EAAE,qBAAqB;IAI1D;;;OAGG;IACH,IAAI,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,QAAQ,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAgBrE;;;;OAIG;IACH,IAAI,IAAI,aAAa,GAAG,IAAI;IA0B5B,KAAK,IAAI,IAAI;CAOd"}
@@ -0,0 +1,68 @@
1
+ import { defaultKeyValueStorage } from './KeyValueStorage.js';
2
+ export class RoomPersistence {
3
+ config;
4
+ storage;
5
+ constructor(config) {
6
+ this.config = config;
7
+ this.storage = config.storage ?? defaultKeyValueStorage();
8
+ }
9
+ /**
10
+ * Best-effort save. Errors are logged and swallowed — losing the
11
+ * persistence layer should never break room creation itself.
12
+ */
13
+ save(input) {
14
+ try {
15
+ const now = Date.now();
16
+ const record = {
17
+ code: input.code,
18
+ role: input.role,
19
+ playerId: input.playerId,
20
+ createdAt: now,
21
+ expiresAt: now + this.config.roomTtlMs,
22
+ };
23
+ this.storage.setItem(this.config.storageKey, JSON.stringify(record));
24
+ }
25
+ catch (err) {
26
+ console.warn('[RoomPersistence] save failed:', err);
27
+ }
28
+ }
29
+ /**
30
+ * Returns the stored room if it exists and is still within its TTL.
31
+ * Auto-evicts an expired entry so subsequent calls don't keep trying
32
+ * to recover something the server has long since destroyed.
33
+ */
34
+ load() {
35
+ try {
36
+ const raw = this.storage.getItem(this.config.storageKey);
37
+ if (!raw)
38
+ return null;
39
+ const parsed = JSON.parse(raw);
40
+ if (typeof parsed.code !== 'string' ||
41
+ (parsed.role !== 'host' && parsed.role !== 'guest') ||
42
+ typeof parsed.playerId !== 'string' ||
43
+ typeof parsed.expiresAt !== 'number' ||
44
+ typeof parsed.createdAt !== 'number') {
45
+ this.clear();
46
+ return null;
47
+ }
48
+ if (Date.now() > parsed.expiresAt) {
49
+ this.clear();
50
+ return null;
51
+ }
52
+ return parsed;
53
+ }
54
+ catch (err) {
55
+ console.warn('[RoomPersistence] load failed:', err);
56
+ return null;
57
+ }
58
+ }
59
+ clear() {
60
+ try {
61
+ this.storage.removeItem(this.config.storageKey);
62
+ }
63
+ catch (err) {
64
+ console.warn('[RoomPersistence] clear failed:', err);
65
+ }
66
+ }
67
+ }
68
+ //# sourceMappingURL=RoomPersistence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RoomPersistence.js","sourceRoot":"","sources":["../../src/recovery/RoomPersistence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAwB,MAAM,sBAAsB,CAAC;AAsCpF,MAAM,OAAO,eAAe;IAGG;IAFZ,OAAO,CAAkB;IAE1C,YAA6B,MAA6B;QAA7B,WAAM,GAAN,MAAM,CAAuB;QACxD,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,sBAAsB,EAAE,CAAC;IAC5D,CAAC;IAED;;;OAGG;IACH,IAAI,CAAC,KAAyD;QAC5D,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACvB,MAAM,MAAM,GAAkB;gBAC5B,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,SAAS,EAAE,GAAG;gBACd,SAAS,EAAE,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS;aACvC,CAAC;YACF,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;QACvE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,gCAAgC,EAAE,GAAG,CAAC,CAAC;QACtD,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,IAAI;QACF,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YACzD,IAAI,CAAC,GAAG;gBAAE,OAAO,IAAI,CAAC;YACtB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA2B,CAAC;YACzD,IACE,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ;gBAC/B,CAAC,MAAM,CAAC,IAAI,KAAK,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,CAAC;gBACnD,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ;gBACnC,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ;gBACpC,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ,EACpC,CAAC;gBACD,IAAI,CAAC,KAAK,EAAE,CAAC;gBACb,OAAO,IAAI,CAAC;YACd,CAAC;YACD,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC;gBAClC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACb,OAAO,IAAI,CAAC;YACd,CAAC;YACD,OAAO,MAAuB,CAAC;QACjC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,gCAAgC,EAAE,GAAG,CAAC,CAAC;YACpD,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,KAAK;QACH,IAAI,CAAC;YACH,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAClD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,iCAAiC,EAAE,GAAG,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,146 @@
1
+ import { type RecoverTimeoutBudget } from './NetworkQuality.js';
2
+ import type { KeyValueStorage } from './KeyValueStorage.js';
3
+ import type { PhalanxClientEvents, RoomRecoveredEvent, Unsubscribe } from '../types.js';
4
+ /**
5
+ * Slim port describing the subset of `PhalanxClient` that the
6
+ * recovery controller actually drives. Defining it here decouples
7
+ * the controller from the rest of the client surface and keeps unit
8
+ * testing trivial.
9
+ */
10
+ export interface RecoveryClientPort {
11
+ connect(): Promise<void>;
12
+ disconnect(): void;
13
+ isConnected(): boolean;
14
+ recoverRoom(code: string, timeoutMs?: number): Promise<RoomRecoveredEvent>;
15
+ getPlayerId(): string;
16
+ on<K extends keyof PhalanxClientEvents>(event: K, handler: PhalanxClientEvents[K]): Unsubscribe;
17
+ }
18
+ /**
19
+ * Phase emitted via `recoveryStatus` so games can render localized
20
+ * strings without re-implementing the state machine.
21
+ */
22
+ export type RoomRecoveryPhase = 'idle' | 'recovering' | 'waiting-network' | 'retrying' | 'gave-up';
23
+ export interface RoomRecoveryStatusEvent {
24
+ phase: RoomRecoveryPhase;
25
+ /** Current attempt number (1-based) when phase is 'recovering' / 'retrying'. */
26
+ attempt?: number;
27
+ /** Delay until the next automatic retry, in ms (only when phase is 'retrying'). */
28
+ nextRetryMs?: number;
29
+ }
30
+ export interface RoomTerminatedEvent {
31
+ reason: 'expired' | 'not-found' | 'cancelled';
32
+ }
33
+ export interface RoomRecoveryControllerEvents {
34
+ recoveryStatus: (event: RoomRecoveryStatusEvent) => void;
35
+ roomTerminated: (event: RoomTerminatedEvent) => void;
36
+ }
37
+ export interface RoomRecoveryConfig {
38
+ /** Storage key for the persisted active-room record. */
39
+ storageKey: string;
40
+ /** Local TTL mirroring server RoomService.ROOM_TTL_MS. */
41
+ roomTtlMs: number;
42
+ /** Storage adapter for the persistence layer. */
43
+ storage?: KeyValueStorage;
44
+ /** Per-quality recover timeouts. */
45
+ recoverTimeoutBudget?: RecoverTimeoutBudget;
46
+ /** Max recover attempts before giving up (still leaves the user on screen). */
47
+ maxRecoverAttempts?: number;
48
+ /**
49
+ * Auto-arm a "pre-game stall" watchdog when tracking starts. If the
50
+ * matchFound→countdown→gameStart sequence stalls longer than
51
+ * `preGameStallMs` we trigger a `forceRecover` to dodge silent
52
+ * mid-flow socket failures.
53
+ * @default true
54
+ */
55
+ preGameStallWatchdog?: boolean;
56
+ /** @default 4500 */
57
+ preGameStallMs?: number;
58
+ }
59
+ /**
60
+ * Owns the entire mobile-friendly recovery state machine for a single
61
+ * private room: visibility/pageshow/online listeners, socket connect/
62
+ * disconnect hooks, exponential-backoff retry, and the localStorage
63
+ * persistence record. Emits typed status events instead of touching
64
+ * UI directly so each game can render its own strings.
65
+ *
66
+ * Lifecycle:
67
+ * client.roomRecovery.startTrackingHost(code) // host created room
68
+ * client.roomRecovery.trackGuestJoin(code) // guest is mid-join
69
+ * client.roomRecovery.resumeTrackingHost(code) // cold-start recover
70
+ * client.roomRecovery.stop() // cancel / match started
71
+ *
72
+ * The pre-game stall watchdog is auto-armed on every matchFound and
73
+ * disarmed on gameStart, so games no longer need their own
74
+ * `armPreGameStallWatchdog` helper.
75
+ */
76
+ export declare class RoomRecoveryController {
77
+ private readonly client;
78
+ private readonly persistence;
79
+ private readonly recoverBudget;
80
+ private readonly maxRecoverAttempts;
81
+ private readonly preGameStallEnabled;
82
+ private readonly preGameStallMs;
83
+ private readonly emit;
84
+ private activeRoomCode;
85
+ private isRecovering;
86
+ private pendingRecoverRequested;
87
+ private pendingForceReconnectRequested;
88
+ private recoverAttempt;
89
+ private recoverRetryTimer;
90
+ private recoverScheduleTimer;
91
+ private lifecycleHandle;
92
+ private clientEventUnsubs;
93
+ private preGameStallTimer;
94
+ private hasGameStartedSinceTracking;
95
+ constructor(client: RecoveryClientPort, config: RoomRecoveryConfig, emit: <K extends keyof RoomRecoveryControllerEvents>(event: K, ...args: Parameters<RoomRecoveryControllerEvents[K]>) => void);
96
+ hasActiveRoom(): boolean;
97
+ getActiveRoomCode(): string | null;
98
+ /**
99
+ * Cold-start recovery: read the persisted host record (mobile killed
100
+ * the tab while the user was in a messenger) and validate it against
101
+ * the current playerId. Returns the code to recover, or null when
102
+ * there's nothing to do (no record, expired, or playerId drift).
103
+ */
104
+ loadColdStartCode(): string | null;
105
+ /** Begin tracking `code` as the active host-side room. */
106
+ startTrackingHost(code: string): void;
107
+ /**
108
+ * Variant used by cold-start recover: assumes the persistence record
109
+ * was already written by a previous tab and just rearms the hooks.
110
+ */
111
+ resumeTrackingHost(code: string): void;
112
+ /** Persist a guest-side join attempt for cold-start surfacing. */
113
+ trackGuestJoin(code: string): void;
114
+ /** Stop all recovery machinery and forget the active room. Idempotent. */
115
+ stop(): void;
116
+ /**
117
+ * Recover through a fresh socket even if Socket.IO still reports the
118
+ * current one as connected. This handles mobile carrier/WebView
119
+ * stalls where server→client packets stop arriving but heartbeat has
120
+ * not fired yet, so the normal `disconnected` hook would be too late.
121
+ */
122
+ forceRecover(reason: string): void;
123
+ /**
124
+ * Attempt one recovery cycle. Idempotent against concurrent calls
125
+ * (the visibility and `connected` hooks may fire near-simultaneously
126
+ * on mobile). Schedules its own backoff retry on transient failure.
127
+ */
128
+ tryRecover(): Promise<void>;
129
+ /**
130
+ * Arm a one-shot timer: if `gameStart` doesn't arrive within
131
+ * `preGameStallMs` we proactively `forceRecover` because the most
132
+ * likely explanation is that the host's socket silently died mid-
133
+ * countdown. Auto-armed on `matchFound` / `countdown` events when
134
+ * `preGameStallWatchdog` is enabled — games rarely need to call this
135
+ * directly.
136
+ */
137
+ armPreGameStallWatchdog(reason: string): void;
138
+ clearPreGameStallWatchdog(): void;
139
+ private armHooks;
140
+ private disarmHooks;
141
+ private armBrowserLifecycle;
142
+ private armClientEventHooks;
143
+ private scheduleRecover;
144
+ private waitForOnlineAndStabilize;
145
+ }
146
+ //# sourceMappingURL=RoomRecoveryController.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RoomRecoveryController.d.ts","sourceRoot":"","sources":["../../src/recovery/RoomRecoveryController.ts"],"names":[],"mappings":"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"}