This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
WebSocket.Rx is a .NET 10 NuGet library providing reactive WebSocket client and server implementations built on R3 (modern Reactive Extensions). It is AOT-compatible, uses System.Threading.Channels for thread-safe message queuing, and ArrayPool<byte> / RecyclableMemoryStream for memory efficiency.
All commands run from the repo root. The solution file is at src/WebSocket.Rx.sln.
# Restore (locked mode, matching CI)
dotnet restore --locked-mode src/WebSocket.Rx.sln
# Build
dotnet build --configuration Release src/WebSocket.Rx.sln
# Run all tests
dotnet test --configuration Release src/WebSocket.Rx.sln
# Run a single test by name
dotnet test --configuration Release src/WebSocket.Rx.UnitTests -- --filter "FullyQualifiedName~AsyncLockTests"
# Run only unit or integration tests
dotnet test --configuration Release src/WebSocket.Rx.UnitTests
dotnet test --configuration Release src/WebSocket.Rx.IntegrationTests
# Run benchmarks
dotnet run --configuration Release --project src/WebSocket.Rx.Benchmarks
# Slopwatch (code quality gate, also runs in CI)
dotnet tool restore
dotnet tool run slopwatch analyze --fail-on errorReactiveWebSocketClient— wrapsClientWebSocket. A dedicated receive loop reads frames and pushes them into R3 subjects; a dedicated send loop drains aChannel<Payload>. ExposesObservable<Message>,Observable<Connected>,Observable<Disconnected>,Observable<ErrorOccurred>.ReactiveWebSocketServer— wrapsHttpListener. Each accepted WebSocket connection becomes aServerWebSocketAdapter(extendsReactiveWebSocketClient). Tracks clients in aConcurrentDictionary<Guid, Client>. Exposes server-level observables for client connect/disconnect, messages, and errors.Message(record) — either text (ReadOnlyMemory<char>) or binary (ReadOnlyMemory<byte>). Factory methodsMessage.Create(...).Payload(public readonly struct) — send-queue item, rents fromArrayPool<byte>and returns on disposal.Extensions— Rx operator extensions:.Send(),.SendInstant(),.TrySend(),.BroadcastAsync()etc.
Send path: caller → Channel<Payload> (unbounded, single-reader) → send loop task → ClientWebSocket.SendAsync.
Receive path: receive loop task → ClientWebSocket.ReceiveAsync → R3 subject → subscriber observables.
Critical sections use a custom AsyncLock (internal, SemaphoreSlim-based with fast-path).
- UnitTests — xUnit v3 + NSubstitute. Tests internal types like
AsyncLock. - IntegrationTests — full client↔server round-trips using
WebSocketTestServer(in-process test harness). Categories: Lifecycle, Connection, Reconnection, Sending, Receiving, Stress, Broadcast, KeepAlive, Encoding, ErrorHandling, Dispose. - Both test projects have
InternalsVisibleToaccess.
- R3 instead of System.Reactive — R3 is the reactive library; all observables are
R3.Observable<T>, notSystem.IObservable<T>. - AOT-compatible —
IsAotCompatible=true, no reflection, uses ILCompiler + ILLink. - InvariantGlobalization — no culture-specific data; text encoding is explicit via
MessageEncodingproperty. - Locked restore —
RestorePackagesWithLockFile=truewithpackages.lock.json; CI runs--locked-mode.
Versioning uses Release Please with conventional commits. Workflows are split:
ci.yml— runs on PR and push to main: slopwatch → restore → build → test with coverage reportrelease.yml— runs on push to main: Release Please creates/updates a release PR; on merge, builds, packs, publishes to NuGet, and creates a GitHub Releasecommitlint.yml— validates PR titles and commit messages against conventional commit formatcodeql.yml— CodeQL static analysis (weekly + on push/PR)
Commit messages must follow Conventional Commits: feat:, fix:, perf:, docs:, chore:, refactor:, test:, ci:, build:, deps:.