Complete the scope that #1952 and #1953 left incomplete. Three coupled
concerns addressed atomically to avoid further PR fragmentation:
R1. Extract host-parsing utilities from ui-server internals to
src/utils/HostUtils.ts:
The `ui-server/UIServerNet.ts` file previously mixed pure host/IP
parsing helpers (`LOOPBACK_HOSTNAME`, `isLoopback`,
`normalizeIPAddress`, `normalizeHost`, `isHostLiteralWithoutPort`)
with HTTP-header parsing helpers (`splitHeaderList`, `splitQuoted`).
The host-parsing family is a pure utility, not UI-server-specific,
and was consumed cross-layer by `src/utils/ConfigurationSchema.ts`
(a Zod validator) — an inverted dependency (`utils/` importing from
`charging-station/ui-server/`) that required a workaround comment
documenting the TDZ (Temporal Dead Zone) cycle risk at
UIServerNet.ts:3.
Move the 5 host-parsing exports plus their private helpers
(`HOSTNAME_PATTERN`, `isValidPort`, `parseIPv4MappedAddress`,
`expandIPv6`, `isIPv6Group`) to `src/utils/HostUtils.ts`. The
`ui-server/UIServerNet.ts` file now contains only the HTTP-header
parsing helpers (`splitHeaderList`, `splitQuoted`) that legitimately
belong to the ui-server access-policy layer.
Callers updated:
- ChargingStation ui-server internals (`AbstractUIServer.ts`,
`UIServerAccessPolicy.ts`, `UIServerFactory.ts`) now import from
`../../utils/index.js` (the utils barrel), each in a single
alphabetically-sorted specifier block merged with the pre-existing
utils imports from that file (no duplicate import statements).
- `src/utils/ConfigurationSchema.ts` imports from `./HostUtils.js`
directly (same directory, avoids barrel TDZ risk).
- The utils barrel (`src/utils/index.ts`) re-exports the 5 host
helpers alphabetically.
Priority-3 anchor comments preserved verbatim in the moved private
helpers (radix-16 rationale for `Number.parseInt` in
`parseIPv4MappedAddress` and `expandIPv6`).
R2. Expose AbstractUIServer as a type-only re-export from the
ui-server barrel:
Bootstrap.ts holds a `readonly uiServer: AbstractUIServer` field
without subclassing the abstract base — a legitimate external
type-reference consumer. Previously deep-imported via
`./ui-server/AbstractUIServer.js`. Now available through the
barrel via `export type { AbstractUIServer } from './AbstractUIServer.js'`.
Bootstrap merges its two ui-server imports into a single barrel
import.
The ui-server barrel `@file` header updates:
- Move `AbstractUIServer` from the "Deliberately not re-exported"
list to a positive mention: "re-exported as a type-only symbol
for external consumers (Bootstrap) that hold a reference to the
selected concrete server without subclassing it".
- Retitle the `UIServerNet` bullet: the host-parsing helpers no
longer live here; the barrel now cross-references
`src/utils/HostUtils.ts` so a reader chasing them does not
wonder where they went.
- Complete the "Deliberately not re-exported" enumeration to also
name the omitted internal symbols from `UIServerSecurity`
(`DEFAULT_MAX_PAYLOAD_SIZE_BYTES`, `PayloadTooLargeError`,
`readLimitedBody`) and `UIServerUtils`
(`isProtocolAndVersionSupported`) so the list is exhaustive.
R3. Adjacent-sub-component cycle already resolved in #1953: the
`broadcast-channel ↔ ui-server/ui-services` value-import back-edge
remains a direct import (not via the barrel) per the codebase's
established convention (`meter-values/index.ts`: "Internal helpers
are intentionally not re-exported; tests and internal callers
should import them directly from the owning sub-module"). No
code change required for R3 in this PR.
Test surface reorganized to match the code surface:
- `tests/utils/HostUtils.test.ts` (new) — 9 `isLoopback` cases and
12 `normalizeHost` cases moved from the previous
`tests/charging-station/ui-server/UIServerNet.test.ts`. Uses a
direct module import (`../../src/utils/HostUtils.js`) rather than
the utils barrel: the barrel load pulls Configuration singleton +
Logger side effects into a pure-function test surface, which under
the Node test runner produces "Promise resolution is still pending
but the event loop has already resolved" hangs. Peer utility tests
that need those side effects use the barrel; a pure host-parsing
unit test does not.
- `tests/charging-station/ui-server/UIServerNet.test.ts` retains only
the 4 `splitHeaderList` cases.