2.56.5
(fix): Fix mock server test generation to omit null values for non-nullable
properties in inlined request bodies, matching the SDK’s
JsonIgnoreCondition.WhenWritingNull serialization behavior. When
enableExplicitNullableOptional is disabled (default), all nulls are
now unconditionally omitted from expected request JSON.
(fix): Fix pagination test build errors (CS8411) when generatePaginatedClients
is disabled. The test generator now checks the config flag before emitting
await foreach code, preventing attempts to iterate over plain response
types that do not implement IAsyncEnumerable.
2.56.4
(fix): Fix duplicate field error in wrapped request generation when an inlined
request body property has the same PascalCase name as a path parameter
(e.g., upload_request_id body property colliding with uploadRequestId
path parameter). When a collision is detected, the path parameter field
([JsonIgnore]) is skipped and the body property ([JsonPropertyName]) is
kept, so the single property serializes to both the URL path and the
JSON request body.
2.56.3
(chore): Remove legacy backslash escape in generated CI workflow’s NuGet publish
step now that Eta template engine does not interpret ${} syntax.
2.56.2
(fix): Make query-string and path parameter encoding RFC 3986 spec-compliant
with three distinct encoding contexts: path segments (pchar), query keys,
and query values. Each context now encodes exactly the characters that
RFC 3986 requires. Query keys exclude &, =, +, #; query values
exclude &, +, # (but allow =); path segments allow all
sub-delimiters plus : and @ (but encode / and ?). Path parameter
values are now percent-encoded via EncodePathSegment in
ValueConvert.ToPathParameterString. Previously, all contexts shared a
single character set and path parameters were not encoded.
2.56.1
(chore): Use a persistent CSharpier process for code formatting. Instead of
spawning a new process per format call, a single csharpier pipe-files
process is kept alive and files are piped to it on demand. This
eliminates repeated .NET startup overhead and significantly speeds up
formatting during SDK generation.
2.56.0
(feat): Add maxRetries custom config option to override the default maximum
number of retries for failed requests. The default remains 2 when not
specified.
2.57.0-rc.0
(feat): Upgrade to IR v66 which compresses the IR Name type, reducing IR size and increasing performance.
2.55.4
(fix): Fix C# compilation errors (CS0118) in generated subpackage clients
where a type name collides with a namespace segment. Type references
are now fully qualified to avoid ambiguity.
2.55.3
(chore): Upgrade WireMock.Net from 1.25.0 to 2.2.0 in generated test projects.
This is a test-only dependency and does not affect the shipped SDK.
2.55.2
(fix): Support multiple Basic Auth schemes with AuthSchemesRequirement.Any.
When an API defines more than one basic auth scheme (e.g.,
accountId/authToken and apiKey/apiKeySecret), the generated client now
produces conditional if/else if blocks that check which credential pair
was provided and sets the Authorization header accordingly. Previously,
only the first basic auth scheme was used.
2.55.1
(fix): Include extraProperties (AdditionalProperties) in WireMock mock server
response objects. Previously, object types with [JsonExtensionData]
additional properties were missing those properties in generated mock
responses, causing incomplete WireMock stubs.
2.55.0
(feat): Add Environments section to generated README showing how to select
different environments (production/sandbox) when initializing the client.
2.54.7
(fix): Wire Basic Auth credentials into the Authorization header. Previously,
the generated client accepted username/password constructor parameters
but never set the Authorization: Basic <base64> header on outgoing
requests. The constructor now encodes the credentials and adds the
header automatically.
2.54.6
(chore): Upgrade test infrastructure NuGet packages in generated C# SDK test projects.
These are test-only dependencies and do not affect the shipped SDK.
- Microsoft.NET.Test.Sdk: 17.13.0 → 18.3.0
- NUnit: 4.3.2 → 4.5.1
- NUnit3TestAdapter: 5.0.0 → 6.2.0
- NUnit.Analyzers: 4.6.0 → 4.12.0
- coverlet.collector: 6.0.4 → 8.0.1
- WireMock.Net: 1.7.4 → 1.25.0
2.54.5
(chore): Upgrade BCL/runtime NuGet package versions in generated C# SDKs.
All packages remain within the 9.x line for net8.0 compatibility.
- Portable.System.DateTimeOnly: 8.0.2 → 9.0.1
- System.Text.Json: 8.0.5 → 9.0.9 (legacy TFM conditional)
- Microsoft.Extensions.Logging.Abstractions: 8.0.2 → 9.0.13 (WebSocket)
- System.Threading.Channels: 8.0.0 → 9.0.13 (WebSocket)
- System.Net.ServerSentEvents: 9.0.9 → 9.0.14 (SSE)
2.54.4
(fix): Thread endpoint ID through dynamic snippet generator to differentiate
generated snippets by endpoint.
2.54.3
(fix): Fix C# dynamic snippets using the internal client class name (client-class-name)
instead of the exported client class name (exported-client-class-name). When both
config options are set (e.g., client-class-name: BasePayrocClient and
exported-client-class-name: PayrocClient), generated snippets now correctly use
PayrocClient in user-facing code.
2.54.2
(fix): Replace Activator.CreateInstance<TConverterType>() with new TConverterType()
in CollectionItemSerializer. The new() generic constraint allows compile-time
instantiation, removing the reflection-based call that is not AOT-safe.
2.54.1
(chore): Remove the generic EnumSerializer<T> template that used reflection
(GetField, GetCustomAttributes) at runtime. All enums already use
per-enum generated serializers with static dictionary lookups, so the
generic template was dead code. This cleanup removes the last
reflection-based enum serialization path, improving NativeAOT and
IL-trimming compatibility.
2.54.0
(feat): Support x-fern-server on AsyncAPI channels for multi-URL environments.
When a channel specifies a baseUrl (via x-fern-server) that maps to a
multi-URL environment in the IR, the C# generator now automatically derives
websocket environments from the IR’s environment definitions. This eliminates
the need for the temporary-websocket-environments workaround in
generators.yml.
2.53.0
(fix): Enforce global:: prefix on all System namespace references in generated code.
This prevents CS0118/CS0426 compile errors when customer projects define a type or
namespace named System (e.g., root namespace Foo.Net with a class System).
All using System.* directives and inline System.* type references now use
global::System.*. A CI validation script in seed.yml enforces this going forward.
2.52.0
(feat): Conditionally emit System.Text.Json as a PackageReference only when
targeting pre-net8.0 TFMs (netstandard2.0, net462). The package is
wrapped in an ItemGroup with an MSBuild IsTargetFrameworkCompatible
condition so it is skipped for net8.0+ where it ships in-box.
2.51.1
(chore): Remove dead #elif NET7_0_OR_GREATER and #else branches inside
QueryStringBuilder that could only execute on .NET 6 or .NET 7,
which are no longer targeted.
2.51.0
(feat): Add net9.0 target framework to all generated C# SDK projects. The library
now targets net462;net8.0;net9.0;netstandard2.0, enabling #if NET9_0_OR_GREATER
code paths such as ClientWebSocketOptions.KeepAliveTimeout for transport-level
dead connection detection. Test projects now target net9.0 instead of net8.0.
(feat): Add comprehensive WebSocket unit and end-to-end test templates. All generated
WebSocket SDKs now ship with 349 tests covering AsyncLock, Event<T>,
ReconnectStrategy, DisconnectionInfo, RequestMessage, Query,
WebSocketConnection, WebSocketClient, ReconnectionInfo, and
WebsocketException. End-to-end tests run against an in-process
TestWebSocketServer and cover echo, rapid-fire, reconnection, server close,
abort recovery, concurrent clients, large messages, and full lifecycle.
Templates are conditionally emitted when the Fern definition includes WebSocket
endpoints.
(feat): Expose StateCheckInterval property on WebSocketClient. The property
(default: 5 seconds) forwards to WebSocketConnection in ConnectAsync,
allowing callers to control how often the background state monitor polls
for silent disconnections.
(fix): Fix server-initiated close skipping reconnection. When the server sent a
WebSocketMessageType.Close frame, the Listen() method used return
which exited entirely — bypassing the ReconnectSynchronized call. Changed
to break with a closedByServer flag so reconnection executes with
ReconnectionType.ByServer.
(fix): Fix CancelReconnection flag being ignored for server-initiated close.
The OnDisconnectionHappened callback fires in the Close handler and the
user can set info.CancelReconnection = true, but this flag was never
checked before calling ReconnectSynchronized. Now captured into a local
cancelReconnect variable and checked before reconnecting.
2.50.0
(feat): Add exponential backoff reconnection strategy for WebSocket connections.
A new ReconnectStrategy class provides configurable exponential backoff
(doubling intervals from 1s to 60s), optional random jitter (0-25%) to
prevent thundering herd, and configurable max attempts. The strategy is
exposed as a ReconnectBackoff property on the generated Options class
and wired through WebSocketClient to WebSocketConnection.Reconnect().
When max attempts are exhausted, reconnection stops and a disconnection
event is raised. The backoff resets on successful reconnection.
2.49.0
(feat): Add per-send timeout to prevent SendAsync hangs. A new SendTimeout property
(default: 30 seconds) on WebSocketConnection wraps every SendAsync call with a
linked CancellationTokenSource that fires CancelAfter(SendTimeout). When the
timeout elapses the connection is aborted and a WebsocketException is thrown,
preventing a single dead connection from blocking the send lock for up to 30 minutes.
See: https://github.com/dotnet/runtime/issues/125257
2.48.0
(feat): Add bounded send queue with message expiration to WebSocketConnection. The
unbounded Channel<T> queues are replaced with configurable bounded channels
(SendQueueLimit, default: 10,000) that use BoundedChannelFullMode.DropWrite
to reject new messages when the queue is full. A SendCacheItemTimeout property
(default: 30 minutes) causes stale messages to be silently dropped during drain,
preventing memory growth during network outages and avoiding sending outdated
messages after reconnection. Set SendQueueLimit to 0 for unbounded or
SendCacheItemTimeout to null to disable expiration.
2.47.0
(feat): Add receive hang detection via a background state monitor task. A new
StateCheckInterval property (default: 5 seconds) controls how often the
monitor polls WebSocket.State. When the state transitions to Closed,
Aborted, or CloseReceived — indicating the TCP connection died without
notifying the receive loop — the monitor cancels the pending ReceiveAsync
via a linked CancellationTokenSource. This works around known .NET runtime
issues where ReceiveAsync hangs indefinitely on silent TCP disconnections
(dotnet/runtime#110496) and Abort() fails to cancel a pending receive
(dotnet/runtime#44272). Set StateCheckInterval to null to disable.
2.46.0
(feat): Wire up WebSocket reconnection logic. The WebSocketConnection template now
contains a complete reconnection implementation ported from the Marfusios library:
heartbeat timer (ReconnectTimeout), error-retry timer (ErrorReconnectTimeout),
lost-connection delay (LostReconnectTimeout), and Reconnect()/ReconnectOrFail()
public methods. The generated WebSocket API classes expose a Reconnecting event
and an IsReconnectionEnabled option (default: false) so callers can opt in to
automatic reconnection. All timeout properties are configurable with sensible defaults.
2.45.1
(fix): Fix sticky compression on reconnect. DeflateOptions is now explicitly reset to
null when EnableCompression is false, preventing a previous non-null value
from persisting across connection cycles.
2.45.0
(feat): Add InjectTestMessage internal method to generated WebSocket client classes.
This allows unit tests to simulate incoming WebSocket messages by injecting raw
JSON strings through the normal OnTextMessage pipeline without requiring a real
WebSocket connection. The method is marked internal so it is only accessible
via [InternalsVisibleTo] in test projects, keeping the public API surface clean.
2.44.0
(feat): Add queued sending via Channel<T> to the WebSocket connection layer. Two
unbounded channels (text and binary) are drained by dedicated background tasks
started alongside the connection. The non-blocking Send(string) and Send(byte[])
methods return bool indicating whether the message was queued, while the existing
SendInstant async-per-send path remains unchanged. Queues are completed in Dispose
before cancellation tokens are signalled. Drain loop errors are surfaced through the
ExceptionOccurred event.
2.43.0
(feat): Add per-message deflate compression support (RFC 7692) for WebSocket connections.
A new EnableCompression option on the generated WebSocket Options class enables
WebSocketDeflateOptions via ClientWebSocketOptions.DangerousDeflateOptions.
This can significantly reduce bandwidth for text-heavy (JSON) WebSocket APIs.
Warning: Do not use compression when sending data containing secrets
(CRIME/BREACH vulnerability).
See https://learn.microsoft.com/dotnet/api/system.net.websockets.clientwebsocketoptions.dangerousdeflateoptions
for details.
2.42.0
(feat): Add support for HTTP/2 WebSockets via SocketsHttpHandler. A new HttpInvoker
property on the WebSocket Options class allows passing an HttpMessageInvoker to
enable multiplexing multiple WebSocket streams over a single TCP connection. When
set, the connection factory configures HttpVersion.Version20 and uses the
ConnectAsync(Uri, HttpMessageInvoker, CancellationToken) overload on .NET 7+.
On older targets the invoker is ignored and the standard connect path is used.
2.41.0
(feat): Add ConnectTimeout property to WebSocketConnection and WebSocketClient with a
default of 5 seconds. The connection factory call in StartClient now uses a linked
CancellationTokenSource with CancelAfter(ConnectTimeout), preventing indefinite
hangs when the server is unresponsive.
2.40.0
(feat): Configure PING/PONG keep-alive on ClientWebSocket. The default connection factory
now sets KeepAliveInterval (default: 30 s) and, on .NET 9+, KeepAliveTimeout
(default: 20 s) to enable the PING/PONG keep-alive strategy that detects unresponsive
servers at the transport level. Both values are exposed as public properties on
WebSocketConnection for caller customization.
2.38.2
(fix): Add using declaration to JsonDocument in WebSocket OnTextMessage handler.
JsonDocument implements IDisposable and holds pooled memory buffers; without
disposal, high message throughput causes memory pressure. The generated code now
emits using var json = await JsonSerializer.DeserializeAsync<JsonDocument>(stream);
so the document is disposed when the method returns.