mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-08 08:29:42 +02:00
fix(review): WS state machine, per-tab supersede key, backoff, dot CSS (PR #149)
- _wsState now transitions through the full lifecycle: _connectWs() sets 'connecting', ws.onopen (inside the this._ws === ws guard) sets 'connected', _disconnectWs() resets to 'disconnected' — the connection chip's "WS" state was previously unreachable (stuck on "WS…"/"HTTP" forever). - WS registry supersede is now keyed per TAB: the upgrade URL sends cid = clientId + ':' + per-page nonce (reusing the constructor's page UUID), while input frames keep the bare browser clientId for seq dedup — two tabs/windows on one session coexist instead of 4010-evicting each other in a perpetual 5s ping-pong; a genuine same-tab reconnect still supersedes. - Exponential backoff engages: _disconnectWs() no longer zeroes _wsReconnectAttempts (it's called at the top of _connectWs, so every retry replanned at attempt 0 → ~0ms tight reconnect loop during outages); onopen resets the counter on success. - styles.css: add .connection-dot.connected (green) and .connection-dot.fallback (yellow) — both states rendered an invisible dot (no rule existed). - Remove smuggled dead code: resolveMonitorRowLabels/CodemanMonitorLabels (COD-122, no consumer, referenced test doesn't exist) and the never-written _wsLastClose/_wsInputSendCount/_httpFallbackSendCount diagnostics. - Tests: new test/ws-state-lifecycle.test.ts drives the REAL _connectWs/onopen/onclose/timer cycle (state transitions, escalating backoff delays, composite cid on the upgrade URL); registry two-tab coexistence test; static check that every emitted connection-dot class has a styles.css rule. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -100,6 +100,27 @@ describe('WsConnectionRegistry', () => {
|
||||
expect(reg.register('s1', 'alice', fresher).evictedSocket).toBe(fresh);
|
||||
});
|
||||
|
||||
it('two tabs of the same browser (shared clientId, distinct tab nonce) coexist without eviction', () => {
|
||||
// The client keys the upgrade by `clientId:tabNonce`, NOT the bare
|
||||
// browser-wide clientId — otherwise two windows on one session would
|
||||
// supersede each other in a perpetual 4010/5s reconnect ping-pong.
|
||||
const reg = new WsConnectionRegistry(5);
|
||||
const tabA = sock('tab-a');
|
||||
const tabB = sock('tab-b');
|
||||
|
||||
expect(reg.register('s1', 'c-browser:tab-A', tabA).evictedSocket).toBeUndefined();
|
||||
const resB = reg.register('s1', 'c-browser:tab-B', tabB);
|
||||
expect(resB.admitted).toBe(true);
|
||||
expect(resB.evictedSocket).toBeUndefined(); // tab A keeps its socket
|
||||
expect(reg.liveCount('s1')).toBe(2);
|
||||
|
||||
// A genuine same-tab reconnect still supersedes only its own socket.
|
||||
const tabANew = sock('tab-a-new');
|
||||
const res = reg.register('s1', 'c-browser:tab-A', tabANew);
|
||||
expect(res.evictedSocket).toBe(tabA);
|
||||
expect(reg.liveCount('s1')).toBe(2);
|
||||
});
|
||||
|
||||
it('isolates counts per session', () => {
|
||||
const reg = new WsConnectionRegistry(2);
|
||||
reg.register('s1', 'a', sock('a'));
|
||||
|
||||
Reference in New Issue
Block a user