refactor: frontend de-spaghetti (dup declares, dead code, log gating)

- Remove the duplicate _gameMode declaration in the client SocketHandlers
  object literal — it was declared twice, so the later `null` silently
  shadowed the earlier `'free'`. Now declared once with the effective value.
- Remove the dead SocketHandlers._appendMessage calls in smirkAuth.js; that
  method never existed, so the guard always fell through to the working
  .text() append. Keep the safe append.
- Gate broadcastManager's high-volume per-event logs (user count, chat message
  incl. message/socketId PII, game update, game start) behind debug mode;
  previously they logged on every event regardless of NODE_ENV.

Full suite: 21 suites / 155 tests.
This commit is contained in:
jwinterm
2026-05-31 14:39:12 -04:00
parent d5df5dbd12
commit 7581ba79cb
4 changed files with 25 additions and 25 deletions
+2 -2
View File
@@ -45,8 +45,8 @@ Key: `S` ≤ half day · `M` ~1–3 days · `L` ~1 week+ · 🔴 launch-blocker
## Phase 4 — De-spaghetti / structural cleanup (🟡)
- [x] **4.1 L — Break up `index.js` (substantially).** ✅ Extracted the verify HTML → `views/verifyPage.js` (now HTML-escaped), all 15 admin endpoints → `routes/admin.js`, and the 3 smirk endpoints → `routes/auth.js`, each as DI factories with smoke tests. **index.js: 2029 → 882 lines (-57%).** _Remaining user/public routes left inline — they're interleaved with bootstrap and have no route tests, so further slicing is diminishing-return/higher-risk; the structure + pattern are established for later._
- [~] **4.2 M — Kill duplicates (partial).** ✅ Deleted the dead `handlePlayerMove` handler (a footgun that double-stepped the monster + bypassed escape/collision detection) and its orphaned `playerMoveTimestamps` map/cleanup — the live path is `MovementManager`. _Remaining (lower-value/higher-effort): merge `rpcService`+`rpccalls`, unify `payout_address` column widths, address the `QueryValidator` false-positive — deferred._
- [ ] **4.3 M — Config as immutable snapshots;** stop mutating `process.env` (`gameModeManager.js:189`).
- [ ] **4.4 M — Frontend de-spaghetti.** Shared `escapeHtml`/`dom.js`; kill double-declares + dead `_appendMessage`; explicit init ordering; upgrade/drop jQuery; gate broadcast logging (`broadcastManager.js:33`).
- [⏸] **4.3 M — Config snapshots — DEFERRED (risk > value).** The `process.env` config write-back is ugly but **load-bearing**: it's how DB-persisted hot-reload config reaches the env-based readers — `difficultyConfig` selects the difficulty preset from `process.env.GAME_MODE`, and `debugManager`'s simulated-blocks **safety gate** reads it. A proper `ConfigService` means rewiring those sensitive readers; with no config-reload tests and zero correctness/safety upside, the risk isn't justified right now. Documented rather than rewritten.
- [~] **4.4 M — Frontend de-spaghetti (safe wins done).** ✅ Shared `escapeHtml` already added in 0.6. Removed the duplicate `_gameMode` declaration (the later `null` silently shadowed the earlier `'free'`). Removed the dead `SocketHandlers._appendMessage` calls in `smirkAuth.js` (the method never existed; always fell through to the working `.text()` append). Gated `broadcastManager`'s high-volume per-event logs (user-count, chat-message-with-PII, game-update, game-start) behind debug mode. _Deferred: jQuery 3.4.1 upgrade and the nested-`setTimeout` init-ordering rewrite (larger, no frontend tests, higher risk)._
- [x] **4.5 S — Delete stale `test/` scratch files.** ✅ Removed 14 non-jest leftovers (5 `.js` scratch scripts + 9 `.html` debug pages) — confirmed they contained no jest constructs and were referenced nowhere. `test/` is now exclusively the 21 real jest suites.
## Phase 5 — Test & ops overhaul (🟠 — the launch gate)
+3 -10
View File
@@ -214,11 +214,8 @@ const SmirkAuth = {
? 'Smirk wallet connected! Payout address set.'
: 'Smirk wallet authenticated! Please set your payout address manually.';
if (typeof SocketHandlers !== 'undefined' && SocketHandlers._appendMessage) {
SocketHandlers._appendMessage('status', successMsg);
} else {
$('#messages').append($('<li class="status">').text(successMsg));
}
// .text() sets textContent, so the message is safe from injection.
$('#messages').append($('<li class="status">').text(successMsg));
// Update address button status if available
if (typeof SocketHandlers !== 'undefined' && SocketHandlers._updateAddressButtonStatus) {
@@ -233,11 +230,7 @@ const SmirkAuth = {
// Show error message
const errorMsg = 'Smirk login failed: ' + err.message;
if (typeof SocketHandlers !== 'undefined' && SocketHandlers._appendMessage) {
SocketHandlers._appendMessage('error', errorMsg);
} else {
$('#messages').append($('<li class="error">').text(errorMsg));
}
$('#messages').append($('<li class="error">').text(errorMsg));
throw err;
}
+3 -3
View File
@@ -13,7 +13,8 @@ const SocketHandlers = {
_hasPayoutAddress: false,
_creditsBalance: 0,
_gameMode: 'free',
_gameMode: null, // set from the server's game-modes event; was previously declared
// twice in this object literal (the later `null` silently won)
_directPayoutsEnabled: false,
_paymentsEnabled: false,
@@ -1394,8 +1395,7 @@ const SocketHandlers = {
// =====================
// Early Entry Functions
// =====================
_gameMode: null,
_earlyEntryConfig: { enabled: false },
_isQueued: false,
+17 -10
View File
@@ -30,8 +30,10 @@ class BroadcastManager {
count = this.io.engine.clientsCount || 0;
}
console.log(`👥 Broadcasting user count: ${count} (last was ${this._lastUserCount})`);
if (this.debugManager?.CONSOLE_LOGGING) {
console.log(`👥 Broadcasting user count: ${count} (last was ${this._lastUserCount})`);
}
// Always broadcast to ensure new clients get the count
this._lastUserCount = count;
this.io.emit('user_count', { count });
@@ -60,8 +62,10 @@ class BroadcastManager {
* @param {string} socketId - Socket ID of the sender
*/
broadcastChatMessage(username, message, timestamp, socketId) {
console.log(`💬 Broadcasting chat message from ${socketId}: "${message}"`);
if (this.debugManager?.CONSOLE_LOGGING) {
console.log(`💬 Broadcasting chat message from ${socketId}: "${message}"`);
}
this.io.emit('chat_broadcast', {
username: username,
message: message,
@@ -100,11 +104,12 @@ class BroadcastManager {
* @param {object} gameState - The game state to broadcast
*/
sendGameUpdate(playerSocketId, gameState) {
// Debug logging for lighting data in updates
console.log(`📡 sendGameUpdate to ${playerSocketId}:`);
console.log(` - Lighting data: ${!!gameState.lighting} (${gameState.lighting ? Object.keys(gameState.lighting).length : 0} rows)`);
console.log(` - Torch data: ${!!gameState.torches} (${gameState.torches ? gameState.torches.length : 0} torches)`);
if (this.debugManager?.CONSOLE_LOGGING) {
console.log(`📡 sendGameUpdate to ${playerSocketId}:`);
console.log(` - Lighting data: ${!!gameState.lighting} (${gameState.lighting ? Object.keys(gameState.lighting).length : 0} rows)`);
console.log(` - Torch data: ${!!gameState.torches} (${gameState.torches ? gameState.torches.length : 0} torches)`);
}
// Send to the player
this.io.to(playerSocketId).emit('game_update', gameState);
@@ -125,7 +130,9 @@ class BroadcastManager {
* @param {object} gameState - Initial game state
*/
sendGameStart(socketId, gameState) {
console.log(`🎮 SENDING GAME_START to ${socketId}`);
if (this.debugManager?.CONSOLE_LOGGING) {
console.log(`🎮 SENDING GAME_START to ${socketId}`);
}
this.io.to(socketId).emit('game_start', gameState);
}