Repository navigation
[Refactoring] Decompose 238-line trackTokenUsage in containers/api-proxy/token-tracker-http.js into testable sub-functions #4934
Description
Refactoring Opportunity
Summary
- File:
containers/api-proxy/token-tracker-http.js - Current size: 305 lines
- Responsibilities identified: 3 distinct concerns collapsed into a single 238-line function
- Score: 5 (3 mixed responsibilities + 2 functions >80 lines)
Evidence
trackTokenUsage is 238 lines (lines 67–303)
The entire file is one exported function containing two large inner functions and three distinct phases:
trackTokenUsage(proxyRes, opts)
├─ Phase 1: Stream setup (~40 lines, lines 85–181)
│ ├─ State variable initialization (chunks, streaming flags, partial-line buffer)
│ ├─ Decompressor creation (conditional)
│ └─ Event listener wiring (data/end on proxyRes or decompressor)
│
├─ Inner fn handleDecodedChunk (~38 lines, lines 111–148) [embedded]
│ ├─ Streaming path: SSE line assembly, extractUsageFromSseLine, usage accumulation
│ └─ Non-streaming path: buffer with overflow guard
│
└─ Inner fn finalizeTracking (~120 lines, lines 183–302) [embedded]
├─ Skip non-2xx responses
├─ Finalize SSE partial line or parse buffered JSON
├─ normalizeUsage + cache-read rollup warning
├─ onUsage callback
├─ incrementTokenMetrics
├─ buildTokenUsageRecord + mergeBudgetFields
├─ writeTokenUsage (JSONL log)
├─ logRequest summary
└─ onSpanEnd callback
finalizeTracking alone is ~120 lines, contains 8 distinct actions, and is currently untestable in isolation because it closes over all the phase-1 state variables.
The companion file token-tracker-ws.js uses the same inner-function pattern, confirmed by issue #4711 (now closed/completed) which fixed the duplicate budgetResult + cache-read rollup logic. The closed-over inner function approach was the root cause of that duplication.
Proposed Split
containers/api-proxy/token-tracker-http.js (refactored, ~150 lines):
// Extracted inner → top-level, accepting explicit state
function createChunkHandler({ requestId, streaming, chunks, ... }) {
// returns handleDecodedChunk — no closure over outer scope
}
function finalizeHttpTracking(state, opts) {
// explicit args instead of closed-over vars
// testable without standing up a full HTTP response stream
}
function trackTokenUsage(proxyRes, opts) {
const state = initState(opts); // ~10 lines
const onChunk = createChunkHandler(state);
wireListeners(proxyRes, state, onChunk, () => finalizeHttpTracking(state, opts));
}This pattern:
- Makes
finalizeHttpTrackingunit-testable by passing explicit state instead of relying on closure - Mirrors the shape of
token-tracker-ws.jsto reduce future duplication risk (see closed issue [Duplicate Code]budgetResultfields merge and cache-read rollup warning duplicated in HTTP and WebSocket token trackers #4711) - Keeps the public API (
trackTokenUsage) unchanged for the single callertoken-tracker.js
Affected Callers
containers/api-proxy/token-tracker.js — the only caller; imports { trackTokenUsage }
Public API is unchanged — no caller updates needed.
Effort Estimate
Low — mechanical extraction of the two inner functions; the main risk is preserving the variable capture semantics while switching from closure to explicit state passing.
Benefits
finalizeHttpTrackingcan be unit-tested with a synthetic state object — no need to construct a mockhttp.IncomingMessagestream- Reduces the risk of re-introducing the budget/cache-read duplication fixed in [Duplicate Code]
budgetResultfields merge and cache-read rollup warning duplicated in HTTP and WebSocket token trackers #4711 by making both trackers structurally symmetric - Reduces cognitive load: each top-level function has one clear job (setup, chunk-handling, finalization)
Detected by Refactoring Scanner workflow. Run date: 2026-06-14
Generated by Refactoring Opportunity Scanner · ◷
- expires on Aug 13, 2026, 3:41 PM UTC
Activity
refactor: decompose trackTokenUsage into testable sub-functions
refactor(token-tracker-http): decompose 238-line trackTokenUsage into…