Client-supplied WebSocket UI request ids were validated for format only.
A second request reusing a still-in-flight id overwrote the prior request's
response handler (cross-delivered reply, dropped second response) and its
broadcast tracking (leaked safety-net timer firing against the wrong context).
Guard the transport ingest: when responseHandlers already holds the request id,
reject the duplicate with a typed BaseError failure on its own socket instead of
overwriting the in-flight request. A completed request releases its handler, so a
legitimate sequential reuse of the same id is still accepted. HTTP and MCP mint
server-side UUIDs and are unaffected.
Wrap the ws.send in rejectInFlightRequestId() in try/catch, matching the
sendResponse() send discipline. The helper runs in the synchronous 'message'
listener, so an uncaught send throw would escape unhandled; log and swallow it
instead. No behavior change to the in-flight guard.
Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
* test(ui-server): fail parseSentResponse with a descriptive assertion
When no message is captured at the requested index, fail with an explicit
assertion naming the index and sentMessages length instead of letting
JSON.parse throw an opaque SyntaxError. Use a length check rather than a
nullish guard, since MockWebSocket.sentMessages is typed string[] (indexed
access is not nullable under the project's tsconfig).
Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
* test(ui-server): reuse shared UI-server test helpers and constants
Hoist emitWorkerResponse into UIServerTestUtils as the single source of truth
and consume it from both AbstractUIService and UIWebSocketServer tests, removing
the duplicated worker-response injection helper (with divergent argument order).
In the WebSocket test, build the protocol request via createProtocolRequest
instead of an inline tuple, and drop the redundant explicit 'ui0.0.1' argument
(it is createMockUIWebSocket's default).