Appearance
Changelog
All notable changes to ActualLab.Fusion are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
+HexNumber after version number is the commit hash of this version. It isn't included into the NuGet package version.
To track updates in real time, see "Fusion/🎉Releases" on Voxt.ai.
12.3.79+2fc189f6 | npm: 12.4.6
Release date: 2026-05-01
Breaking Changes
- RPC:
RpcStream.AckAdvanceis renamed toRpcStream.BufferSize(both .NET and TypeScript). Wire format is unchanged — only the property name and the corresponding TypeScript option (ackAdvance→bufferSize) differ. Update any code that explicitly sets the buffer-ahead limit onRpcStream<T>.
Changed
- RPC:
RpcStream.IsRealTimeskipping is now reactive instead of speculative. Previously, on hitting the buffer-ahead ceiling the sender drained the source itself looking for the nextCanSkipToitem; now the sender waits for an ACK and, when one arrives, compacts the already-buffered unsent suffix down to the latest skippable item. The sender no longer pulls ahead from the source just to hunt for a skip target. Reconnect-time skip-ahead also runs only whenIsRealTimeis set; non-realtime streams keep back-pressure semantics unchanged.RpcSharedStreamis consolidated to a singleOnRunpath covering both modes.
Documentation
- Expanded
CODING_STYLE.mdwith rules for flow-control spacing, class member ordering, primary constructors, sealed classes, preferred types likeFilePath, and TypeScript-side conventions mirroring the .NET ones.
Infrastructure
- Updated Fusion / OAuth / CliWrap package pins in
Directory.Packages.props.CommunityToolkit.HighPerformancebriefly moved to 8.4.2 and was reverted to 8.4.0 (a regression in 8.4.x is in flight upstream).
Tests
NerdbankCrossCompatTestis now guarded byNET8_0_OR_GREATER, andShardMapTestconstructs itsHashSetin a way that compiles on .NET Framework targets.
12.3.79+d2bf83a0 | npm: 12.3.85
Release date: 2026-04-30
Added
- TypeScript RPC:
RpcConnectionUrlResolvermay now returnstring | Promise<string>, and the connect pathawaits it. This unblocks resolvers that need to fetch a per-connection token (e.g. a session token) before forming the WebSocket URL. - TypeScript RPC:
sanitizeUrl(url)utility exported from@actuallab/rpc— redacts?session=...(URL-parsed when possible, regex fallback otherwise) so the connect-attempt log line no longer leaks bearer-style query parameters. Declared asexport letso library users can swap in a different sanitizer (e.g. one that redacts additional query keys).
12.3.79+a7608a16 | npm: 12.3.83
Release date: 2026-04-30
Fixed
- TypeScript RPC: proper backpressure in
RpcStream— acknowledgements are now consumer-driven instead of producer-driven. Previously the receiver sent an ACK as soon as an item landed in the buffer, which signalled false capacity to the producer and effectively disabled flow control. The stream now tracks_nextConsumedIndexand only acks up to what the iterator has actually yielded; duplicate-frame fast paths cap their ack at the consumed index as well, and the iterator loop emits a fresh_maybeSendAckafter each batch is drained. Addsrpc-stream.test.tscases covering the consumer-driven ACK behavior.
12.3.79+ef249695 | npm: 12.3.81
Release date: 2026-04-29
Added
- TypeScript RPC:
RpcErrorclass — failed remote calls now reject with anRpcError(instead of a plainError) that carries the remote exception'stypeNamewhen available. The handler parses.NET's assembly-qualifiedTypeRefstring (e.g."System.InvalidOperationException, System.Private.CoreLib") and exposes the type name only, so TypeScript callers can branch on remote exception types. The internalRpcRerouteExceptionlog path now matches against the fully-qualifiedActualLab.Rpc.RpcRerouteException. Exported from@actuallab/rpc. error-propagationE2E scenario inTypeScriptRpcE2ETest/ts-dotnet-e2e.tsvalidating .NET → TS exception propagation.
12.3.79+a4fdbdd9 | npm: 12.3.79
Release date: 2026-04-28
Fixed
- RPC: prevent connection stacking during a mid-handshake state. Connection state checks now use
IsConnectedOrHandshakinginstead ofIsConnected, so new connections no longer pile up against peers stuck in transient handshake states. Adds teardown safeguards and tightens disconnect resolution in edge cases. AffectsRpcPeer,RpcServerPeer,RpcPeerConnectionState,RpcWebSocketServer, and the TypeScriptRpcPeer.
12.3.76+d67c674e | npm: 12.3.76
Release date: 2026-04-25
Added
- RPC: compute methods can now serve a Regular call type (compute-to-regular downgrade). When an inbound message targets a
[ComputeMethod]but itsCallTypeIdisRegular, the server returns the result immediately and skips invalidation tracking — no entry is retained in the inbound call registry past completion. Useful for callers that want a one-shot value from a compute method without subscribing to invalidations. Implemented inRpcInboundContext(accepts the alternate call type) andRpcInboundComputeCall(newIsRegularCallpath that unregisters afterSendResult).
12.3.74+279ac90c | npm: 12.3.70
Release date: 2026-04-23
Added
- Nerdbank MessagePack converters for a core set of RPC and serialization types:
Result<T>,ExceptionInfo,VersionSet,RpcCacheKey,RpcCacheValue,RpcHandshake,RpcHeader,RpcHeaderKey,RpcMethodRef, andRpcObjectId. All converters emit the same array-based wire shape used by MessagePack-CSharp, somsgpackXandnmsgpackXRPC formats are now fully byte-compatible across runtimes — clients and servers can mix Nerdbank.MessagePack and MessagePack-CSharp without re-serialization. Registered in the defaultNerdbankMessagePackByteSerializerconfiguration. NerdbankCrossCompatTestcases covering the new RPC converters.
12.3.72+94144fd7 | npm: 12.3.70
Release date: 2026-04-23
Fixed
- Nerdbank
ApiMapNerdbankConverter<TKey, TValue>andImmutableOptionSetNerdbankConverternow also accept the legacy array-of-kv-pairs wire shape ([[k, v], [k, v], ...]) in addition to the standard map shape ({k: v, k: v, ...}). Keeps DB blobs written by the MessagePack-CSharp source-generated collection formatter readable after the v12.3.70 migration to Nerdbank, so no migration step is required for existing stored payloads.
12.3.70+04d6f22d | npm: 12.3.70
Release date: 2026-04-22
Added
- Nerdbank MessagePack converters for
PropertyBag,ImmutableOptionSet,ApiMap<TKey, TValue>, andTypeDecoratingUniSerialized<T>— closes the wire-compat gap where Nerdbank's default reflection shape couldn't express the legacy[Key(N)]index-based layouts used by MessagePack-CSharp. Stored blobs written by the legacy serializer remain readable. Registered in the defaultNerdbankMessagePackByteSerializerconfiguration. TextSerializedNerdbankConverter<T, TSerialized>(and closed-overNewtonsoftJsonSerializedNerdbankConverter<T>) — fixes a cross- serializer wire gap where MessagePack-CSharp wroteNewtonsoftJsonSerialized<T>as[Data]while Nerdbank emitted{Data: ...}, breaking every composite embedding such a value (notably a populatedImmutableOptionSet).NerdbankCrossCompatTestsuite — drives bytes directly between MessagePack-CSharp and Nerdbank readers/writers to catch wire-format divergence that self-round-trip tests miss.- TypeScript:
useBigInt64: truein the defaultmsgpackencoder —bigintvalues now serialize as msgpack int64/uint64, required for .NETlongfield compatibility when the value exceedsNumber.MAX_SAFE_INTEGER.
Changed
TypeDecoratingUniSerialized<T>wire format aligned with MessagePack-CSharp's[Key(0)] MessagePackDatalayout — a 1-element array with type-decorated inner bytes — so the same payload now cross-reads between Nerdbank and MessagePack-CSharp.- TypeScript: upgraded
@msgpack/msgpackto v3.1.3;Encoderinitialization refactored to the new object-based configuration API.
Fixed
- TypeScript RPC:
Dictionary<int, byte[]>arguments now serialize correctly over msgpack (unblocking the$sys.Reconnectmethod argument) via a newmsgpack-map-patch.tsalongside the v3.1.3 upgrade.
12.3.63+373bb905 | npm: 12.3.64
Release date: 2026-04-21
Added
- TypeScript:
RpcPeer.disconnect()— closes the current WebSocket connection without disposing the peer. ForRpcClientPeer, the run loop detects the disconnect and reopens the connection; the peer stays in the hub and all client proxies bound to it remain valid.
Changed
RpcStream.Disconnect()is now public (previously an explicitIRpcObject.Disconnect()implementation forwarding to a protected abstract). Callers holding anRpcStreamreference can now end it directly without casting toIRpcObject.
Fixed
RpcStream: reordered the disconnect path so the$sys.AckEndclose message is always sent before_isDisconnectedflips. This closes a race where a stream disconnecting during the "cannot reconnect" branch ofReconnectwould skip its close notification, leaving the remote side waiting.
12.3.60+21bf6f76 | npm: 12.3.60
Release date: 2026-04-20
Added
- Custom MessagePack formatters for core serialization types —
ImmutableBimapMessagePackFormatter,ResultMessagePackFormatter,BoxMessagePackFormatter, andMutableBoxMessagePackFormatter, registered inDefaultMessagePackResolverand wired up via[MessagePackFormatter]attributes onImmutableBimap,Result,Box,MutableBox, andApiArray. Works around a MessagePack source-generator bug that emits incorrect code for struct fields relying on default formatters.
Fixed
- TypeScript:
RpcPeercould enter a zombie state on reconnect when a disconnect happened mid-handshake. Connection tracking now uses a dedicated_isConnectedflag (independent of WebSocket state), which tightens state transitions, outbound call gating, andclose()behavior so silent no-ops and lingering zombie peers no longer occur.
12.3.56+9d28308c | npm: 12.3.50
Release date: 2026-04-19
Added
- Fusion:
RemoteComputeMethodFunctionracesSendRpcCallagainst peer disconnect events — remote compute calls no longer hang indefinitely when the peer drops mid-call; a stale cached value is served instead when one is available. RpcPeer.WhenDisconnectedandMarkDisconnected— disconnection is now a first-class, awaitable state.RpcPeer.WhenConnectedOrReroute— waits for a live connection and surfaces reroute exceptions so callers can re-resolve the peer instead of blocking on a dead one.RpcRouteStategained a reroute-aware hook used by the above.
Changed
RpcPeerConnectionStateExtremoved; theWhenConnectedextension methods are gone - use the identical regular method directly, or the newRpcPeer.WhenConnectedOrReroutehelper. State transitions (MarkConnected/MarkDisconnected/MarkTerminated) now live onRpcPeerConnectionStateitself.- RPC peer connection-state handling simplified: state transitions consolidated into
RpcPeerConnectionState, and the disconnection / connection-timeout flow inRpcPeertightened.RpcTestConnectionupdated to match.
Fixed
- Additional safeguards around serving stale cache on disconnect so reroute exceptions and terminal errors propagate correctly, closing race windows that could produce spurious failures right after a peer dropped.
12.3.50+c3a95b95 | npm: 12.3.50
Release date: 2026-04-18
Breaking Changes
- TypeScript:
RpcClientPeerAPI reshaped —peer.run(factory)is replaced by thewebSocketFactoryfield pluspeer.start()(ctor also gained amustStart = trueparameter);peer.connected/peer.disconnectedevents are gone — subscribe topeer.connectionStateChanged(emitsRpcConnectionState) or awaitpeer.whenConnected();peer.connectionKindrenamed topeer.connectionState; andpeer.reconnectDelayermoved up toRpcHub.reconnectDelayerso every client peer on a hub shares the delayer. Migration: dropvoid peer.run()(auto-starts by default) or setwebSocketFactory+ callpeer.start()when you needmustStart = false; replace event-based checks withconnectionStateChangedorwhenConnected(); routecancelDelays()/delays = ...throughhub.reconnectDelayer.
Added
- Fusion: remote compute methods now serve the last cached value when the peer is disconnected (instead of failing), and auto-invalidate via the new
InvalidateWhenReconnectedpath once the peer reconnects — improves UI resiliency across brief disconnects. - TypeScript:
RpcPeerRefBuilderhelper for composing peer refs.RpcPeerRefBuilder.forClient(url, format)bakes the serialization format into the URL via?f=...;RpcPeerRefBuilder.forServer(id)returnsserver://{id}. - TypeScript:
RpcClientPeer.webSocketFactoryfield for injecting a custom WebSocket constructor (Node.js / tests).
Changed
- TypeScript:
RpcClientPeerReconnectDelayeris now a single instance shared viaRpcHub.reconnectDelayerand centralized there — swap in an app-level subclass (e.g. signal-gated) on the hub before peers start.
Fixed
- TypeScript: reconnection edge cases in
RpcClientPeer— handshake timeouts now close the connection and retry after the configured delay; resolved deadlock scenarios during$sys.Reconnectcalls; addressed lost-close race conditions duringrun()iterations.
Documentation
- Rewrote
PartTS-Rpc.md,PartTS.md,PartTS-FusionRpc.md, andPartTS-React.mdexamples against the newRpcClientPeerAPI (start()/whenConnected()/connectionStateChanged/hub.reconnectDelayer).
Tests
- Added regression coverage for the reconnect edge cases above (
rpc-reconnect-edge-cases.test.ts) and migratedfusion-rpc-run-reconnection.test.tsto the new API. - Added
ComputeMethodResultStashTest(11 cases covering roundtrip, disposal cleanup, stash-twice / after-dispose errors, per-key serialization, and shared-lock-set ctor) plus aStashComputeServiceintegration test.
12.3.42+3661472f | npm: 12.3.33
Release date: 2026-04-18
Performance
- Replaced unnecessary
Interlocked.Exchangecalls withVolatile.Write/Volatile.Readacross Core, Fusion, and RPC hot paths (BatchProcessor,SafeAsyncDisposableBase,ArrayPoolBuffer,Connector,Computed,ComputedRegistry,ComputedSynchronizer,ComputedGraphPruner,RpcObjectTrackers,RpcSharedStream,RpcWebSocketTransport). AddedInterlockedExt.VolatileRead/InterlockedExt.VolatileWritehelpers for cases where a full interlocked operation isn't needed.
12.3.39+4e55ed7e | npm: 12.3.33
Release date: 2026-04-17
Breaking Changes
RpcRouteState.LocalExecutionAwaitersignature changed fromFunc<CancellationToken, ValueTask>toFunc<bool, CancellationToken, ValueTask>, andRpcRouteStateExt.PrepareLocalExecutiongained a requiredaddDependencyparameter beforecancellationToken. Custom awaiters and any directPrepareLocalExecutioncallers must accept and forward the newbool addDependencyargument.addDependencyistruefor compute method calls - you can use it to actually add a dependency for such call on, e.g., the current routing state.
Documentation
- Reworked all code snippets so every block in the docs is consumed directly from a compiled snippet source (no drifted hand-written duplicates).
- Fixed placement of
MustStore(false)in the operation-context setup example and clarified its usage. - Miscellaneous prose and formatting cleanup across the docs set.
npm: 12.3.33+59552c71
Release date: 2026-04-16
Added
- TypeScript: scoped logging API in
@actuallab/core—Log,LogLevel, andcreateLogProvider(prefix, defaults)factory for per-package, typedgetLogs(scope)helpers. EachLog.get(scope)returns a bag of optional loggers (debugLog,infoLog,warnLog,errorLog) that arenullwhen below the scope's minimum level — call sites usedebugLog?.log(...)so disabled logs cost a single nullish check. - TypeScript:
initLogging()persists user-set minimum levels tosessionStorage(3-day TTL) and installs aglobalThis.logLevelscontroller exposingoverride(scope, level),overrideAll(prefix, level),dump()(prints every known scope as aconsole.table),reset(), andclear()for runtime tweaking from the browser dev console. - TypeScript: per-package
getLogshelpers in@actuallab/rpcand@actuallab/fusionwith package-prefixed scopes (e.g.'rpc.RpcPeer','fusion.ComputedState') and explicit per-scopeLogLeveldefaults. Global baseline isWarn;rpc.RpcPeerisInfoso connection-lifecycle events surface out of the box. All other scopes areWarn— quiet by default; users opt in vialogLevels.override(...).
Changed
- TypeScript: replaced ad-hoc
console.warncalls acrossrpc-peer,rpc-stream,rpc-stream-sender,rpc-system-call-handler,rpc-system-call-sender,rpc-hub,rpc-service-host,rpc-connection,rpc-peer-state-monitor,computed-state, andui-action-trackerwith the new scoped logger, mirroring the corresponding .NET log calls.
12.3.25+0a851ea7 | npm: 12.3.29
Release date: 2026-04-16
Breaking Changes
- Removed the
RpcWebSocketServerOptions.ChangeConnectionDelayoption (both ASP.NET Core and OWIN/NetFx variants). Stale-connection teardown now happens synchronously before the WebSocket upgrade, so the dedicated delay is no longer meaningful. Drop any code that sets this option.
Added
- TypeScript:
RpcStreamsource factories of the form(abortSignal: AbortSignal) => AsyncIterable<T>now get a grace period (RpcStream.disconnectGracePeriodMs, default 100ms) ondisconnect()to honor the AbortSignal and exit cooperatively before the sender force-closes viaiterator.return(). PlainAsyncIterable<T>sources (which can't observe the signal) are force-closed immediately as before. - TypeScript:
RpcCallStageconstants (ResultReady,Invalidated,Unregistered) andcompletedStagetracking onRpcOutboundCall, both ported from .NET. - TypeScript:
IncreasingSeqCompressorin@actuallab/rpc— LEB128-based sorted-integer-sequence compression, wire-compatible with .NET. Shared fixtures between the TS test suite and a new .NETTheoryconfirm byte-for-byte wire compatibility for the$sys.Reconnectprotocol. - TypeScript:
RingBuffer<T>in@actuallab/core— fixed-capacity circular buffer matching .NETActualLab.Collections.RingBuffer<T>. - TypeScript:
RpcPeer.formatis now a mutable property (getter/setter);RpcServerPeeraccepts an explicit format override, letting the test harness align both peers on any supported wire format.
Fixed
- RPC server: stale connections are now disconnected before the new WebSocket upgrade rather than after. Previously the old-connection teardown could consume the client's
HandshakeTimeoutbudget on a dead socket; performing it before the upgrade consumesConnectTimeoutinstead, which is the correct budget for "waiting for server to be ready to talk". - RPC WebSockets: reduced the default
RpcWebSocketTransport.CloseTimeoutfrom its previous value to 1 second to limit effectiveConnectTimeoutshrinkage and lower the abrupt/graceful-close ratio impact on connection handling. - TypeScript:
RpcClientPeer._reconnectnow runs the$sys.Reconnect:3protocol on same-peer reconnects to ask the server which call IDs it no longer recognizes, and only resends those. Previously the client blindly re-sent every in-flight outbound call on every reconnect, causing the server to spawn a second handler for streaming calls (e.g. ActualChat'sPushAudio) and double-process the stream. Matches .NETRpcOutboundCallTracker.Reconnect. TS also now handles incoming$sys.Reconnectcalls: when a peer acts as server, the inbound-call tracker is consulted to produce the set of unknown call IDs, wrapped in$sys.Okexactly as .NET does. - TypeScript:
RpcClientPeer._reconnectnow disposes client-owned shared objects (e.g.RpcStreamSenderinstances) on peer change, matching .NETRpcPeer.Reset(). Previously these senders lingered indefinitely after a reconnect to a server with a differenthubId, causing an unbounded leak of stream-sender state and source iterators. - TypeScript:
RpcStreamSender.writeFromis now ACK-driven (mirroring .NETRpcSharedStream<T>): the main loop blocks waiting for a client ACK, so while the peer is disconnected no source items are pulled. PreviouslysendItemwas a no-op when disconnected but the pump kept pulling from the source, silently discarding up to thousands of items per disconnect window. A bounded replay buffer holds unacknowledged items so they can be resent on reconnect.
Documentation
- Documented
RpcRemoteExecutionModein the Call Routing reference page.
Tests
- New
.NETTypeScriptRpcE2ETest.ReconnectNoDuplicatecross-language E2E theory exercises$sys.Reconnect:3end-to-end (Node-hosted TS client ↔ ASP.NET Core .NET server) forjson5,msgpack6, andmsgpack6c. Verifies the server invokes a long-runningSlowEchohandler exactly once across a same-peer reconnect — the regression guard for the audio-double-processing bug. - New
rpc-reconnect-wire-format.test.tslocks in byte-level wire fixtures for both the JSON and MessagePack shapes of thecompletedStagesargument. - New
.NETIncreasingSeqCompressorTest.CrossPlatformWireFormatFixturestheory locks in the exact byte output of 10 representative inputs. The same fixtures are asserted by the TypeScriptincreasing-seq-compressor.test.tssuite, giving us a bidirectional wire-compatibility contract for$sys.Reconnect.
Infrastructure
- TypeScript:
Run-Tests.cmdnow setsCI=1andNO_COLOR=1and uses Vitest'sbasicreporter for consistent, silent CI output.
12.3.16+47f5b5a0 | npm: 12.3.14
Release date: 2026-04-16
Added
- New
RpcRemoteExecutionMode[Flags]enum (AwaitForConnection,AllowReconnect,AllowResend) giving per-method control over outbound RPC connection waiting, reconnection, and resending behavior RpcMethodAttribute.RemoteExecutionModeproperty for overriding the default (AwaitForConnection | AllowResend) on a per-interface or per-method basis;NoWaitmethods use0, compute methods must useDefault- TypeScript: matching
RpcRemoteExecutionModesupport inrpc-service-def,rpc-client, and decorators ShardMapBuilder.Maglev— Google's Maglev consistent hashing algorithm as a new shard map builder with perfect balance (max-min ≤ 1) and lower disruption than Rendezvous at higher node counts- TypeScript:
AbortSignal-based cancellation for localRpcStreamsources —RpcStreamSource<T>now also accepts a factory(abortSignal: AbortSignal) => AsyncIterable<T>, letting sources release resources (camera, microphone, etc.) promptly on disconnect - TypeScript:
RpcServerPeer.accept()now disconnects shared objects on connection close, andonAckEnd()delegates todisconnect()for proper iterator cleanup
Tests
- Expanded
ShardMapTest.BuilderComparisonTestwith additionalInlineDatascenarios, winner/tie tracking, and detailed comparison metrics across builder strategies - New
RpcRemoteExecutionModeTest(.NET) andrpc-remote-execution-mode.test.tscovering connection waiting, reconnection, in-flight calls, and method-definition validation - New
rpc-stream-cancellation.test.tsandNoReconnectStreamSourceCancellationTestverifying source enumerator finalization on client disconnect withAllowReconnect=false
Infrastructure
- TypeScript: ESLint warning cleanup across all packages
npm: 12.3.6+872c4869
Release date: 2026-04-15
Changed
- TypeScript: tightened ESLint rules — removed overly permissive overrides for
@typescript-eslint/no-unsafe-*rules - TypeScript: cleaned up code, fix ESLint warnings, and removed redundant ESLint disable directives across all packages
- TypeScript: removed
noUncheckedIndexedAccessfrom tsconfig and cleaned up all non-null assertion operators (!) that were only needed for it
12.3.2+f23f4ac2 | npm: 12.3.2
Release date: 2026-04-15
Added
- .NET and TypeScript: Real-time stream mode with skip-to-keyframe support in
RpcStream:- New
IsRealTime/isRealTimeproperty - New
CanSendTo/canSkipToproperty - When
IsRealTimeandAllowReconnectare both true, reconnection clears the stale buffer and skips to the nextCanSkipToitem
- New
- TypeScript:
RpcStreamis now dual-mode (local + remote), matching the .NET design — service methods can returnnew RpcStream(source, { isRealTime: true, ... })with full configuration - TypeScript:
RpcStream.toRef(peer)method creates and registers anRpcStreamSender, starts pumping items, and returns the serialized stream reference (text or binary format) - TypeScript:
RpcStream.whenSentproperty — aPromisethat resolves when the sender finishes pumping all items - TypeScript:
RpcStreamOptions<T>interface for configuring local streams - TypeScript:
RpcSerializationFormatandRpcSerializationFormatResolvertypes similar to the .NET ones - TypeScript: MessagePack format support (
msgpack6andmsgpack6c) - TypeScript: "Compact" call format support (name hash-based method resolution)
- TypeScript: bundled XXH3-64 hash implementation for method name hashing
Changed
- TypeScript:
rpc-peer.tsstream dispatch now wraps rawAsyncIterableresults inRpcStreamand delegates totoRef()instead of creatingRpcStreamSenderdirectly - TypeScript: adopted Voxt.ai TypeScript coding style (4-space indent, single quotes, prettier)
- Consolidated C# E2E tests into single class with
[Theory]
Documentation
- Documented
IsRealTime,CanSkipTo, and real-time reconnection behavior inPartR-RpcStream.md - Added TypeScript dual-mode
RpcStreamAPI documentation withtoRef()andwhenSentexamples
Tests
- .NET: Comprehensive tests for
RpcStream.IsRealTimefeature - .NET: New tests verifying that after disconnect/reconnect, the first item is a keyframe (multiple of
keyFrameInterval) - TypeScript: E2E tests for all 3 serialization formats and
isRealTimestream feature - TypeScript: Added local-mode
RpcStreamtests (construction, config, iteration,toRef,whenSent, E2E config propagation) - TypeScript: Added real-time, reconnect tests, and tests verifying
canSkipTofiltering on reset ACK - TypeScript: XXH3-64 implementation tests
Infrastructure
- TypeScript: added
Run-Lint.cmdbuild script, renamedInstall-Packages.cmdtoNpm-Install.cmd
12.2.4+09f1dc55 | npm: 12.1.115
Release date: 2026-04-04
Fixed
- Fixed NativeAOT downcast bug in additional places (
MethodDef,RpcMiddlewareContext) with conditionalUnsafe.Asworkaround
12.2.1+41e24193 | npm: 12.1.115
Release date: 2026-04-03
Breaking Changes
CodeKeeperAPI overhauled:- New and simpler
XxxCodeKeeper.IExtensionextension points. - Removed instance-based
Get<T>()/Set<T, TImpl>(),AddAction(),RunActions(),KeepUnconstructable(),CallSilently(), andFakeCallSilently()methods CodeKeeperis now a static utility with simplifiedKeep<T>(),Keep(Type),KeepSerializable<T>()methods- Removed
TypeCodeKeeper,SerializableTypeCodeKeeper,RpcMethodDefCodeKeeper RpcProxyCodeKeeper,CommanderProxyCodeKeeper,FusionProxyCodeKeeperare replaced byProxyCodeKeeper.IExtensionimplementations (RpcProxyCodeKeeperExtension,CommanderProxyCodeKeeperExtension,FusionProxyCodeKeeperExtension)
- New and simpler
RpcCallTimeouts.LogTimeoutrenamed toDelayTimeout;RpcCallTimeouts.DefaultLogTimeoutrenamed toDefaultDelayTimeout— update any code referencing these propertiesRpcMethodAttribute.LogTimeoutrenamed toDelayTimeout— update attribute usages in service interfacesRpcOutboundCallOptions.ReroutingDelayersignature changed — now takes(RpcMethodDef, int, CancellationToken)instead of(int, CancellationToken)
Added
CodeKeeper.IExtensioninterface for pluggable trimming/NativeAOT code retentionRpcDelayedCallActionflags enum for configurable delayed call handling:None,Abort,Resend,Log,LogAndAbort,LogAndResendRpcMethodAttribute.DelayActionproperty to control per-method delayed call behaviorRpcOutboundCallOptions.DelayHandlerfor custom delayed call handling logic- Delayed compute calls are now automatically re-sent by default (
LogAndResend), improving reliability
Changed
- NativeAOT sample restructured: all
CodeKeeper.Set/RunActionscalls are removed (they're unnecessary now)
Fixed
- Fixed
RpcSharedStreamusingIsCompletedinstead ofIsCompletedSuccessfullyfor task state checks, preventing incorrect behavior when tasks are faulted or cancelled
Documentation
- Updated benchmark data to v12.1.130 (.NET 10.0.5) with new latency tables
12.1.130+4189e1bf | npm: 12.1.115
Release date: 2026-04-01
Added
- Added
AllowExecuteDeleteAsyncoption toDbLogTrimmerOptionsandDbSessionInfoTrimmer.Options, allowing control over whetherExecuteDeleteAsync(bulk SQL DELETE) is used in trimmer operations. Defaults totrueon .NET 7+ andfalseon older targets - When
AllowExecuteDeleteAsyncisfalse(or on pre-.NET 7), trimmers now fall through to the row-by-row deletion path even on .NET 7+, enabling compatibility with EF providers that don't supportExecuteDeleteAsync
Changed
DbAuthServiceBuildernow registersDbSessionInfoTrimmer.Options.Defaultvia factory instead of directTryAddSingleton<Options>(), allowing easier default customization
12.1.128+31a47345 | npm: 12.1.115
Release date: 2026-03-30
Fixed
- Fixed
NullReferenceExceptioninAsyncLockSet.Releaser.Dispose()when the releaser is default-valued (uninitialized)
12.1.125+ba936b35 | npm: 12.1.115
Release date: 2026-03-26
Fixed
- Fixed potential socket errors (SocketError 125 / ECANCELED) during WebSocket connection in
RpcWebSocketClient— the connect timeoutCancellationTokenSourcecould fire after a successful connect, aborting the already-established socket. Now disposed immediately after successful connection to prevent late cancellation from affecting the live socket
12.1.123+9dc3aaeb | npm: 12.1.115
Release date: 2026-03-24
Fixed
- Prevented race condition in
RpcCallTrackerson mobile app resume that caused misleading "delayed call" reports — added keep-alive timeout check to avoid premature call timeouts during app resume scenarios
Changed
RpcCallStagenow outputs "None" for stage value 0 instead of a numeric representation
12.1.119+8e01cd91 | npm: 12.1.115
Release date: 2026-03-20
Breaking Changes
- Removed
Arithmetics,ArithmeticsProvider,Range,Tile,TileLayer,TileStackand all associated types/extensions fromActualLab.CoreMathematics namespace. These abstractions are no longer part of the library - Removed
RangeModelBinderandRangeModelBinderProviderfromActualLab.Fusion.Server
Fixed
RpcSystemCallSenderandRpcSharedStream.Batcher._isPolymorphicnow useRpcArgumentSerializer.IsPolymorphicfor polymorphic type checks
12.1.114+a74e74b2 | npm: 12.1.115
Release date: 2026-03-18
Added
RpcSerializableAttribute([RpcSerializable]) — marks abstract types as non-polymorphic for RPC serialization, allowing the underlying serializer's union support ([JsonDerivedType],[MemoryPackUnion],[Union]) to handle type discrimination instead of RPC'sTypeRefwrappingRpcSerializationFormatExceptionandRpcWebSocketCloseCode.UnsupportedFormat— better error handling when client requests a serialization format unknown to the server
Documentation
- Added "Polymorphic Serialization" section to RPC Serialization docs covering
[RpcSerializable]usage
Tests
- Added tests for
[RpcSerializable]withNonPolymorphicBasehierarchy, includingRpcStreamscenarios - Added tests for unsupported serialization format handling in
RpcWebSocketTest
12.1.107+fe771590 | npm: 12.1.100
Release date: 2026-03-17
Added
SharedFloatPoolandSharedDoublePoolinArrayPools(ActualLab.Core) — shared array pools forfloatanddoubletypes
Changed
- Removed
unmanagedconstraint fromNonPoolingArrayPool<T>, allowing it to work with any type
Tests
- Added unit tests for
NonPoolingArrayPool<T>inActualLab.Tests
12.1.102+00807c35 | npm: 12.1.100
Release date: 2026-03-17
Fixed
AsyncTaskMethodBuilderExt.FromTaskdidn't work — introducedGenericAccessors<T>to workaround unsafe accessors for generics due to runtime limitations; refactored both generic and untypedFromTaskvariant
Tests
- Added unit tests for
FromTaskandGenericFromTaskto validate functionality and correctness
12.1.100+b7edfdd5 | npm: 12.1.100
Release date: 2026-03-16
Breaking Changes (.NET)
- Removed
IsReconnectableproperty fromIRpcSharedObject— replaced byAllowReconnectonIRpcObject RpcStream.New<T>()factory method parameter renamed fromisReconnectabletoallowReconnect
Added (.NET)
AllowReconnectproperty onIRpcObjectinterface — controls whether an RPC object (stream) should reconnect or immediately disconnect when a peer connection dropsRpcStream<T>now serializesAllowReconnectas a 5th field in the wire format (backward-compatible: old 4-field format defaults toAllowReconnect = true)- Server-side
RpcSharedStreamrejects reconnect attempts forAllowReconnect = falsestreams and auto-disposes them on disconnect RpcRemoteObjectTrackerdisconnects non-reconnectable remote objects on peer disconnect
Added (TypeScript)
- TypeScript RPC:
allowReconnectsupport inRpcStream,RpcStreamSender,parseStreamRef(), andRpcRemoteObjectTracker
Documentation
- Updated RpcStream docs to reflect
AllowReconnectreplacingIsReconnectable
Tests
- Added
NoReconnectStreamTestandNoReconnectStreamDisconnectTest(.NET) - Added TypeScript unit tests for
allowReconnectinRpcStream,RpcStreamSender, andparseStreamRef - Added TypeScript end-to-end tests for
allowReconnect = falsedisconnect behavior - Cross-language E2E test (
StreamNoReconnect) verifyingAllowReconnect = falsebehavior between TypeScript client and .NET server
12.1.98+62afac4f | npm: 12.1.69
Release date: 2026-03-10
Added
CpuTimestampBasedVersionGenerator— a newVersionGenerator<long>based onCpuTimestampticks, useful for in-process only high-resolution monotonic versioningIState.GetExistingComputed()method (it was available viaComputedInput, but wasn't a part ofIState)
Changed
- Renamed
Versioning/Providers/folder toVersioning/Generators/and movedClockBasedVersionGeneratortoActualLab.Versioningnamespace
Fixed
- Stale state bug in
ComputedStateduringRecompute: concurrent invalidation could target an already-replaced computed instance, causing the state to miss updates.StateExt.InvalidateandRecomputenow useGetExistingComputed()instead ofSnapshot.Computedto address that
Documentation
- Added Standalone Authentication guide — explains how to extract Fusion's auth system into your own project for full control and simpler code
12.1.89+ee8734c3 | npm: 12.1.69
Release date: 2026-03-04
Breaking Changes
ShardMap<TNode>constructor no longer acceptsFunc<TNode, IEnumerable<int>>for custom hashing — useShardMapBuilderparameter instead- Default shard mapping algorithm changed from greedy to rendezvous hashing — existing shard assignments will differ after upgrade
DbLogReader.ProcessBatchreturn type changed fromTask<int>toTask<Moment>— subclasses must update their override signatures
Added
ShardMapBuilderabstraction with two built-in strategies:GreedyShardMapBuilder(old behavior) andRendezvousShardMapBuilder(new default) for optimal minimal reallocation when nodes changeDbEventLogReader.GetMinDelayUntilfor precise delayed event scheduling — log reader now sleeps until the next delayed entry instead of polling on a fixed interval
Changed
- Improved log processing queries in
DbEventLogReader: it uses== LogEntryState.Newfilter andOrderBy(DelayUntil)for more reliable index utilization
Fixed
- Operation event processing now schedules precisely based on the earliest pending entry's
DelayUntil, avoiding unnecessary polling DbLogReader.ProcessNewEntriesreworked to support precise sleep-until scheduling based onProcessBatchreturn value
Documentation
- Replaced Mermaid diagrams with SVG images in architecture docs
- Added animated SVG diagrams for distributed scaling, dependency graphs, caching, and recomputation
- Added TypeScript port documentation section
Tests
- Added
ShardMapBuildercomparison tests (greedy vs rendezvous) - Added delayed event processing test with precise scheduling verification
12.1.69+10006328 | npm: 12.1.69
Release date: 2026-02-22
Added (TypeScript)
RpcStreamsupport in TypeScript RPC client — full streaming with batching, reconnection, and end-of-stream handling- Stream performance test (
StreamInt32) for benchmarking TypeScript RPC stream throughput RpcType.streamreturn type in TypeScript service definitions
Changed (TypeScript)
- TypeScript RPC method definitions now use
returns: RpcType.noWaitinstead of anoWaitboolean flag, improving API consistency - Simplified wire argument count calculations in TypeScript by assuming
CancellationTokenslot as default (removedctOffsetoption)
Tests (TypeScript)
- Added comprehensive
RpcStreamunit tests in TypeScript covering batching, reconnection, multiple enumeration, and disposal - New stream performance benchmarks
12.1.61+045f13f0
Release date: 2026-02-18
Added
- "No polymorphism" JSON serialization formats (
json5np,njson5np) for strict non-polymorphic RPC serialization RpcLimits.PrematureDisconnectTimeoutfor improved connection backoff logic
Changed
- Connection retry logic now uses
ConnectionAttemptIndexinstead ofTryIndexwith handling for premature connection closures - Default serialization format for
RpcClientPeer(TypeScript) changed tojson5np
Documentation
RpcLimitsclass documentation
Tests
- Added TypeScript RPC performance harness
12.1.51+8e1051d0
Release date: 2026-02-17
Added
IsSynchronizedandWhenSynchronizedmethods onState,ComputedState, andMutableStatefor streamlined synchronization checksIState.IsSynchronized(),IState.WhenSynchronized(), andIState.Synchronize()extension methods inStateExtKeepProcessedItemsandKeepDiscardedItemsretention settings inDbLogReaderOptionsfor fine-grained control over log item lifecycle
Tests
- Added TypeScript RPC reconnection scenario tests (both unit and E2E against .NET server)
12.1.41+718a0325
Release date: 2026-02-14
Added
- Work in progress: TypeScript Fusion client. Core abstractions, compute methods,
ComputedState,MutableState, invalidation, andfusion-reactpackage with React bindings. - React-based "Todo v3" page in the TodoApp sample showing how to use the client.
Fixed
- Missing
HasName(...)calls inFusionBuilderforAddClient,AddServer, andAddDistributedServicemethods - Proper
ArgumentDataformatting inRpcInboundCallfor improved readability
Infrastructure
- Added TypeScript monorepo under
ts/with@actuallab/core,@actuallab/rpc,@actuallab/fusion, and@actuallab/fusion-rpcpackages - Integrated TypeScript build pipeline into the Todo sample Host project via MSBuild targets
12.1.14+28a7e73e
Release date: 2026-02-11
Fixed
- RPC WebSocket disconnect detection was delayed by ~50 seconds instead of being instant. When the server shut down,
RpcPeer.OnRunawaitedmaintainTasksin afinallyblock before cancellingreaderTokenSource, soSharedObjects.Maintain()kept running its keep-alive check loop for up to 55s (KeepAliveTimeout) before detecting the timeout. The fix moves thereaderTokenSourcecancellation before themaintainTasksawait.
12.1.12+0475b1ca
Release date: 2026-02-11
Breaking Changes
mempack6(c)/msgpack6(c)binary protocols no longer persist message size, fixing the compatibility issue with pre-v12 protocols. This seems to be the very first release issue attributed to Claude Code: it somehow concluded the size has to be persisted while migratingWebSocketChanneltoRpcWebSocketTransportAPI, but in reality is wasn't (the code branch persisting the size was disabled via other logic). Sorry we caught this just now: the issue is there for two weeks already (from v12.0.9).
Fixed
Option<T>now always uses explicitMessagePackFormatterinstead of conditionalMessagePackObject(true)withSuppressSourceGenerationon .NET 8+, fixing serialization consistency across target frameworks
Tests
- Added conditional flags (
UseSystemJsonSerializer,UseNewtonsoftJsonSerializer,UseMessagePackSerializer,UseMemoryPackSerializer) inSerializationTestExtfor selectively enabling/disabling specific serializers in tests - Added serialization round-trip tests for
DbUser,DbChat,DbMessage
12.1.4+7b59e831
Release date: 2026-02-07
Added
- New
ActualLab.Serialization.NerdbankMessagePackpackage – optional Nerdbank.MessagePack serialization support. You can also register newnmsgpack6/nmsgpack6cRPC formats by callingRpcNerdbankSerializationFormat.Register()at startup
Fixed
ApiNullable<T>,ApiNullable8<T>,ApiOption<T>now always use explicitMessagePackFormatterinstead of conditionally usingMessagePackObject(true)on .NET 8+
Documentation
- Added api-index.md – condensed type reference (~300 lines) alongside api-index-full.md (full API index)
12.0.85+53469221
Release date: 2026-02-06
Breaking Changes
CpuTimestamp.PositiveInfinityandCpuTimestamp.NegativeInfinityrenamed toMaxValueandMinValueCoarseCpuClockremoved as mostly useless, useCpuClockinstead;MomentClockSetno longer has aCoarseCpuClockproperty and its constructor no longer accepts acoarseCpuClockparameter
Changed
- RPC keep-alive tracking now uses
Moment(wall-clock time) instead ofCpuTimestamp, fixing reliability issues on Unix systems where CPU sleep could stall timestamps
Fixed
IRpcSharedObject.LastKeepAliveAtchanged fromCpuTimestamptoMoment–CpuTimestampcould freeze during CPU sleep on Unix, causing incorrect keep-alive trackingRpcHub.Clockrenamed toRpcHub.SystemClock(now usesSystemClockinstead ofCpuClock) by the same reason
Documentation
- XML summary descriptions added (auto-generated with Claude Code) to all public types and members
- Added
Api-Index.md– a comprehensive type catalog listing all public types acrossActualLab.FusionNuGet packages
12.0.76+7e668fb2
Release date: 2026-02-04
Fixed
RpcRerouteExceptionhandling code no longer attempts to reroute during disposal ofIServiceProvider, preventing potential rerouting cycles during shutdown
12.0.70+1775d374
Release date: 2026-02-03
Fixed
RpcStreamwithIsReconnectable == falsefails right on the first enumeration rather than after the reconnection attempt
12.0.65+68251969
Release date: 2026-02-03
Added
RpcStream.IsReconnectableproperty – controls whether a stream can be reconnected after disconnection;trueby default, set tofalseto make reconnection attempts fail withRpcStreamNotFoundExceptionRpcStreamNotFoundException– new exception thrown when attempting to reconnect a non-reconnectable or expired stream
Tests
- Added
FlakyTest.XUnitdependency for marking time-dependent tests as flaky - Marked timing-sensitive tests (
ConcurrentTimerSetTest,ConcurrentFixedTimerSetTest) with[FlakyFact]attribute for improved test reliability
12.0.60+9bb19676
Release date: 2026-02-01
Breaking Changes
IOperationEventSource.ToOperationEvent()signature changed: now acceptsIServiceProviderparameter instead of no parameters (intermediate version acceptedIOperationScope)MemoryBuffer<T>replaced withRefArrayPoolBuffer<T>– migrate by updating type references and usingArrayPool<T>constructor parameterArrayPoolBuffer<T>,ArrayOwner<T>, andBufferWriterExtmoved fromActualLab.IOtoActualLab.Collectionsnamespace
Added
RefArrayPoolBuffer<T>– a ref struct buffer backed byArrayPool<T>with configurable pool and clearing behaviorTimeSpan?.ToShortString()extension method with customizable null fallback valueArrayPoolsstatic class providing common array pool instances
Changed
ArrayPoolBuffer<T>enhanced with additional constructor overloads andToArrayOwner()method
Fixed
AsyncTaskMethodBuilderextension methods now check task completion state before accessingTask.Exception, preventing potential inner exceptions
Tests
- Improved ActualLab.Rpc test stability with positive
maxWaitTimevalidation - Better test database isolation in
FusionTestBase
Documentation
- Added
BatchSizeproperty documentation to RpcStream flow control section - Added Docker benchmark results to performance documentation
Infrastructure
- Added
Clean.cmdscript for cleaning build artifacts on Windows and Unix platforms
12.0.45+0ee956d2
Release date: 2026-01-29
Performance
- Simplified
RpcOutboundMessageby replacingWhenSerializedTask with synchronousSendHandlercallback; dependencies likeRpcOutboundCall.SendXxx,RpcStream.OnItem,OnBatch, andOnEndtransitioned fromTasktovoidreturn type reducing task allocations in the RPC send path, bringing ~20% performance improvement.
Tests
- Added comprehensive unit tests for
TaskCompletionHandlercovering various task states (completed, faulted, cancelled) and all handler variants (1, 2, 3 state objects)
Infrastructure
- Removed obsolete
docs.slnfile - Removed JetBrains.Annotations package reference
- Updated package versions: Blazorise 1.8.9 (used only in samples), Bullseye 6.1.0, and CliWrap 3.10.0 (used in Build.csproj)
- Updated analyzer packages: Moq.Analyzers, xunit.analyzers, Roslynator.Analyzers, Meziantou.Analyzer.
12.0.34+842f172c
Release date: 2026-01-28
Added
- Native little-endian serialization support in RPC serializers with optimized memory handling
TestServiceProviderTagto tag testServiceProviders and allow services to detect whether they're running in test containers- New
MemoryReaderandSpanWriterhelpers inActualLab.Core.IO.Internal
Performance
- Improved RPC performance dedicated path for little endian serialization and delegate caching
- Eliminated
RpcPeer.Sendindirection layer -RpcTransport.Sendnow handles error handling directly, reducing call stack depth and improving RPC throughput - Introduced
TaskCompletionHandler- a pooled helper for attaching completion callbacks to tasks. Each instance caches its delegate, and instances are pooled using thread-static pools to minimize allocations in hot paths like RPC transport error handling - Enabled
UseUnsafeAccessorsbuild configuration for .NET 8+ targets, improving internal reflection-based operations performance
Fixed
- Big endian system support: serialization, RPC, and all dependent code now correctly use
BinaryPrimitives.WriteInt32LittleEndianand similar methods to ensure consistent byte ordering across different CPU architectures
Tests
- Added
ResetClientServices()calls to all relevant Fusion and RPC tests - Refactored RPC test organization for better separation of concerns.
Documentation
- Added interactive BarChart component for performance benchmarks visualization
- Improved Mermaid flowchart styles and edge label rendering
- Updated Performance page with new benchmark visualizations
- Integrated CHANGELOG into the documentation website
- Updated coding style guide to clarify async method naming conventions
12.0.9+3e71b6ef
Release date: 2026-01-27
Breaking Changes
- New
v6serialization format:mempack6,msgpack6and their variants withcsuffix - All serialization formats below
v5are gone, usev11.5.1if you still need them WebSocketChannelis replaced byRpcWebSocketTransport- All authentication-related types from
ActualLab.Fusion.Serverare moved toActualLab.Fusion.Ext.*assemblies and changed namespace fromActualLab.Fusion.Server.AuthenticationtoActualLab.Fusion.Authentication, so referencingActualLab.Fusion.Serverassembly now doesn't "drag" the authentication-related types into your project (and that was the reason for this change).
Added
mempack6andmsgpack6serialization format versions; they offer a tiny improvement (2 bytes per call) over v5, and that's only because the zero-copy serialization in RPC transport layer made v5 a bit less efficient (it has to reserve 5 bytes for the message length, because it uses WriteVarUInt32 for length, so v6 changes length encoding to regular UInt32).RpcStream.BatchSizeproperty for controlling stream batching behavior (default: 64, range: 1..1024)- Real-time stock ticker demo added to
TodoAppsample.
Changed
- Consolidated buffer size properties in
RpcWebSocketTransport(renamedWriteFrameSizetoFrameSize) - Very significant changes in
ActualLab.Rpcinternals. In particular, oldRpcMessageis now represented byRpcOutboundMessageandRpcInboundMessagetypes, all serialization-related methods now accept different arguments, etc.
Performance
- Zero-copy serialization in RPC transport layer. Earlier an intermediate buffer (
ArgumentData) was used to serialize RPC call arguments and results. LaterRpcMessage(envelope) serializer was combining that data with other required pieces (method reference, call ID, etc.) in the final buffer. Now the intermediate buffer is eliminated, which significantly boosts RPC performance on large messages.Stream10Ktest shows almost 3x speed improvement on 10KB items. - Switched
WriteChannelOptionstoUnboundedChannelOptions - Buffer renewal and reuse logic was significantly improved as well.
Documentation
- New documentation website: https://fusion.actuallab.net/
11.4.7+3045fd2c
Release date: 2025-01-05
Added
- Updated EntityFrameworkCore and Npgsql versions to 10.0. There is currently no MySql EF Core provider for EF10, so if you want to use Fusion with MySql, the latest version that targets EF9 is 11.4.3; you can also add binding redirects for EF9 manually in your project.
RpcMethodAttributefor method-level RPC configurationv5serialization formats with proper polymorphicnullvalue support, includingjson5,njson5,msgpack5,msgpack5c,mempackc, andmempack5c.- New
IRpcMiddlewarestack replacingIRpcInboundCallPreprocessor RpcLocalExecutionModeenum and newRpcLocalExecutionMode.Constrained,RpcLocalExecutionMode.ConstrainedEntrymodesIOperationEventSourceinterface withOperation.AddEvent(...)overload for event sourcingIWorker.Runoverload withCancellationToken.- Much more robust RPC (re)routing logic in
RpcInterceptor,RpcRoutingCommandHandler, andRemoteComputeMethodFunction.
Changed
- Moved all
RpcXxxdelegates toRpcXxxOptionsmembers for clarity. E.g.,RpcOutboundCallOptions.RouterFactoryreplacesRpcCallRouterdelegate. - Renamed
RpcShardRoutingModetoRpcLocalExecutionMode - Renamed
RpcDefaultSessionInboundCallPreprocessortoRpcDefaultSessionReplacer - Simplified
RpcSerializationFormatResolver(the legacy resolver for the "unspecified" format is gone) - Improved
RpcServiceDefandRpcMethodDefconstructors - Improved
ComputedOptionscaching - Updated .NET SDK to version 10.0.101.
Performance
- Multiple improvements in inbound call processing performance, such as handcrafted server invokers for most frequent RPC system calls like
$sys.Ok WebSocketChannel.OptionsgotReadMode, which can beBufferedorUnbuffered. The newUnbufferedmode allows reading directly fromWebSocketbypassingChannelReader, it's used by default now.GetUnsafeinGenericInstanceCacheto eliminate some unnecessary type casts- Overall, v11.4.X is ~5-10% faster on RPC benchmarks.
Documentation
- Migrated Parts 01-13 from the old tutorial, though only parts 01-03 are truly edited at this point
- Added TOCs to videos on Fusion and ActualLab.Rpc
- Added GitHub workflow for deploying documentation to GitHub Pages: https://fusion.actuallab.net/
- Documentation is a work in progress, and you're welcome to contribute!
Fixed
- Multiple issues related to RPC rerouting
RpcCommandHandlerrepeatedly sending commands to the server- A new bug in
FrameDelayers.MustDelaymethod (introduced in late v11.3.X), which effectively disabled RPC frame delaying - Use of incorrect Handshake index on some reconnection attempts – the issue was rare, but once it happened, it was blocking RPC reconnects for ~5 min.
Task.Resultusage is replaced with.GetAwaiter().GetResult()everywhere (it's faster and safer)$csys.Invalidatecalls (remote invalidation notifications) now triggerComputed.Invalidate(immediately: true)call rather than justComputed.Invalidate(), which eliminates double delay for RPC compute methods that use invalidation delay- Various minor fixes.
Tests
WebTestHelpers.GetUnusedLocalUrihelper method inActualLab.TestingCapturingLoggerandCapturingLoggerProviderinActualLab.Testing- Improved cancellation and timeout handling in RPC tests
- Added benchmark tests for
Task.ResultvsTask.GetAwaiter().GetResult() - Added
CapturingLoggerunit test
11.0.15+ec823882
Release date: 2025-11-05
Changed
- Returned back
IMutableState.Valuesetters (they were removed in v11.0.8) - Added "Must" prefix to
RpcDefaultCallTracer.TraceInboundandTraceOutboundflags for consistency with bool field naming rules.
Fixed
- Adjusted
RpcDefaultDelegates.CallTracerFactoryto enable full tracing for server (RuntimeInfo.IsServer), and no tracing for the client.
Changed in Samples
- TodoApp: Updated Aspire SDK to version 9.5.2 and centralized its version configuration
- TodoApp: Temporarily disabled Microsoft Account authentication (the current credentials are expired).
11.0.8+1fd1d61afb
Release date: 2025-11-02
Breaking Changes
- Revamped
RpcServiceBuilderAPI. Its newInjectmethod "injects" a service described byRpcServiceBuilderintoIServiceCollection RpcBuilderandFusionBuilder'sAddXxxmethods now rely onRpcServiceBuilder.InjectIMutableStateandMutableStatelostValuesetter; useSet(...)methods to set it. Invalidation path tracking is the reason of this change: new.Set(...)overloads use[CallerFilePath],[CallerMemberName], and[CallerLineNumber]to propagate the origin of change to the invalidation logic, and there is no way to achieve the same with.Valueproperty. I may end up returning it with[Obsolete]attribute though.- Renamed
IHasIsDisposedtoIHasDisposeStatus - Removed
RpcServiceMode.DistributedPairmode and related logic (Distributedmode offers more anyway) - Removed
RpcSwitchInterceptor;
Added
- Quite useful
ComputedOptions.ConsolidationDelayand related APIs (see Releases chat on https://voxt.ai/chat/s-1KCdcYy9z2-uJVPKZsbEo for details) - Invalidation path tracking:
InvalidationSource,Invalidation.TrackingMode, and other changes Computed.ToString(InvalidationSourceFormat)isComputed.ToString(), but with invalidation path infoFusionMonitoris now capable of gathering invalidation path statistics.
Changed
- More readable output of
MethodInfo.ToShortString()is now used to dump compute service methods
Fixed
- Another issue in
RpcCallTracker.TryReconnectthat may block stateful reconnect - A couple places in Fusion RPC stack where
Computed.Invalidate(immediately: false)was used instead ofComputed.Invalidate(immediately: true) - Incorrect use of
Sampler-s inFusionMonitor: missing "not" was making it to sample where it had to skip, and vice versa 😦 That's why statistics was typically 7x exaggerated there (the probability of loggingEveryNth(8)is 7/8, i.e. close to 1, but it was reporting it as 1/8).
10.6.38+254f4ef775
Release date: 2025-10-28
Changed
- Breaking: Replaced
RpcCallRouteOverridewithRpcCallOptions, expect more changes in this area - Removed
RpcNonRoutingInterceptorand related infrastructure;RpcRoutingInterceptordoes a bit more and equally fast - Updated CODING_STYLE.md
Fixed
RpcCallTracker.TryReconnect-IncreasingSeqCompressor.Serializewas getting a potentially misordered sequence making fast (stateful) reconnect impossible, though stateless reconnect still worked in these cases- Renamed
CompleteAsyncclass toCompletionto correct a previous wrong rename RpcRoutingInterceptornow properly reroutes local calls as well
Tests
- Added new MeshRpc tests; will be extending them in the near future
- Refactored
FusionTestBasedescendants to move DI of test-specific services to specific tests - Reorganized DB model classes under
DbModelnamespace in tests
10.6.18+af2a52320f
Fixed
- ActualLab.Generators now add #if-s suppressing [UnconditionalSuppressMessage] and [ModuleInitializer] for .NET Framework 4.7.2 and .NET Standard 2.0.
10.6.16+d0431715b7
Release date: 2025-10-27
Changed
- Renamed
ChannelReadModetoWebSocketChannelReadMode - Removed
IChannelWithReadMode, updatedWebSocketChannelto implementIAsyncEnumerabledirectly.
Performance
- Adjusted
BoundedChannelOptionsforReadChannel(120→100) andWriteChannel(120→500) inWebSocketChannelto optimize buffering behavior
Infrastructure
- Added
UnbufferedPushSequence<T>for unbuffered async data streaming - Added
[UnsafeAccessor]-based helpers forAsyncTaskMethodBuilder<T>inAsyncTaskMethodBuilderExt.
Documentation
- Replaced
.instructions.mdwith updatedAGENTS.mdandCODING_STYLE.md
10.6.4
Release date: 2025-10-25
Breaking Changes
- Breaking: Removed
FusionDefaultsandFusionModetypes. UseRuntimeInfo.IsServer,ComputedRegistry.Settings, andTimeouts.Settingsinstead. - Breaking: Removed
RpcModetype. UseRuntimeInfo.IsServerinstead. - Breaking:
ComputedRegistry.Instanceis gone, the whole class is now static - Breaking:
TimeoutsandIGenericTimeoutHandlerare moved toActualLab.Timenamespace. - Breaking: reworked
OperationEventAPI enabling events with quantized delay. Such events have Uuid, which includes its DelayUntil value, and have quantized DelayUntil. So you can use them to implement reliable throttling for such activities as post-change indexing or AI processing - Breaking:
IHasDelayUntilinterface is removed, and thus its support inOperationEventhandling logic. - Breaking: .NET Framework 4.7.1 target is replaced with .NET Framework 4.7.2
Added
- .NET 10 RC2 support
allowInconsistentflag inComputed/AnyState/ComputedSource.Use()and.UseUntyped()methods. Get the value even if it's inconsistent; when called inside a compute method, instantly invalidates the newly created computed if an inconsistent dependency gets captured.bool Operation.MustStoreproperty allowing to disable the operation log entry creation; this is useful when you're sure you don't need distributed invalidation for the current operation - e.g., you know that only the local machine is responsible for exposing modified data. WhenMustStoreisfalse, aDbEvent(inProcessedstate) is created instead of anDbOperationentry to verify commit in case of commit failure.OperationScope.CompletionHandlersallowing to register operation completion handler right inside the command handlerRpcCallRouteOverridetype allowing to override outgoing RPC call routing (e.g., set destination peer)Moment.Floor,Moment.Ceiling,Moment.Round, andMoment.Convertmethods;Moment.Ceilingis used to quantize delayed events.
Changed
- Moved command routing logic to
RpcRoutingCommandHandler - Renamed
ComputeServiceCommandCompletionInvalidatortoInvalidatingCommandCompletionHandler - Improvements in
RetryPolicy/IRetryPolicy, including exception filters ArgumentListG*<...>is now IL/AOT-trimmable viaArgumentList.AllowGenericsfeature switch
Fixed
- Native AOT support in .NET 10
- A bug in
ComputeRegistrythat could causeComputedRegistryto expose a wrongComputeddue to a race betweenComputedRegistry.GetandComputedRegistry.Register. IfGetgets paused between a moment it read a handle and resolved itsTarget, the handle with the sameIntPtrvalue might get re-allocated. This is extremely rare, but nevertheless, we saw this happening in production. - The identical bug in
RpcObjectTracker(it's used to trackRpcStream-s). IDelegatingCommandnow "implements"IOutermostCommand(it was supposed to, but didn't)StatefulComponent.StateChangedhandler now suppressesExecutionContextflow- Bug in
RpcSystemCallSender.OkbreakingRpcStreamserialization in call results - Bug in
RpcStreamenumerator - Proper framework version check in
CpuTimestampfor .NET 10 WASM - Removed
Microsoft.Extensions.Httpreference fromActualLab.Rpc
Performance
WebSocketChannel.ReadModeand corresponding option enabling unbuffered reads without use ofReader(which is backed by its ownChannel). This option alone improves RPC performance by 5%.- Use
CancellationToken.NoneinWebSocketoperations inWebSocketChannelwithout sacrificing cancellation support (WebSocketis properly disconnected on cancellation now) RpcStreamnow usesMemory<byte>instead ofbyte[]for its buffer.- Use of
Unsafe.As<T>(x)instead of(T)xin a few key places. - Improvements in key Fusion components, including
ComputedRegistry,Computed, andComputedState, - Improvements in Blazor components, including
StatefulComponentBaseandComputedStateComponentBase - Improvements in infrastructure components, including
ArgumentList,FixedArray,AsyncLock, andAsyncLockSet.
Infrastructure
- Added
WeakReferenceSlim(currently unused) - Added
TaskExt.NeverEnding(CancellationToken)helper - Added
FixedTimerSetandConcurrentFixedTimerSet - Improved
CancellationTokenSource-based timeout handling code across the board - Improved tests.
Documentation
- Added
.instructions.md(like AGENTS.md) for agent rules - Finalized Part01, added examples for
Computed<T>.When()andChanges()methods.
Key Changes in 2025
- Documentation website launch – https://fusion.actuallab.net/ with VitePress, migrated Parts 01–13 from the old tutorial, video TOCs, GitHub Pages deployment workflow.
- RPC routing and distributed services overhaul – new
RpcLocalExecutionMode,IRpcMiddlewarestack (replacingIRpcInboundCallPreprocessor),RpcRoutingCommandHandler,RpcCallOptions,RpcServiceBuilder.Inject, and much more robust rerouting logic for shard-aware distributed services. - Invalidation path tracking and consolidation –
InvalidationSource,Invalidation.TrackingMode,ComputedOptions.ConsolidationDelayfor grouping rapid invalidations, andFusionMonitorinvalidation path statistics. - .NET 10 support.
Key Changes in 2024
- RPC serialization formats revamp – introduced
mempack2/msgpack2binary formats with compact method name hashes (mempack2c/msgpack2c), serialization format negotiation, and zero-copy serialization in the transport layer.RpcByteMessageSerializerandFastRpcMessageByteSerializerbrought significant throughput gains. OperationEventintroduction – event handling withDbEventTestBase,- .NET 9 support and Native AOT – full .NET 9 support (RC1 → release), proxy generators updated with AOT-compatible code generation (
ProxyCodeKeeper,KeepCode), and[UnsafeAccessor]-based helpers for internal reflection. - .NET 9 support.
Key Changes in 2023
ActualLab.Rpc– new RPC framework – built from scratch starting April 2023, replacing the oldReplica/Publisher/Replicatorbridge with a modern WebSocket-based RPC system. IncludesRpcPeer,RpcStream,RpcHub, call routing, reconnection, shared object tracking, and binary serialization.- Compute caching (client-side) –
ClientComputeMethodFunctionimprovements andRpcCacheKeyfor caching RPC compute method results on the client side. - Blazor improvements –
ComputedState.Options.TryComputeSynchronouslyfor faster initial rendering, improvedComputedStateComponent,BlazorCircuitContextenhancements, andRpcPeerStateMonitorfor connection state UI. - Rename from
Stl.*toActualLab.*– all NuGet packages, namespaces, and project files renamed in December 2023 (e.g.,Stl.Fusion→ActualLab.Fusion,Stl.CommandR→ActualLab.CommandR). - .NET 8 support – full .NET 8 support including
[UnsafeAccessor]usage, IL trimming markup,[DynamicDependency]annotations, and AOT-related preparations.
Key Changes in 2022
- Roslyn proxy generators –
Stl.Generatorspackage withProxyGeneratorfor compile-time proxy generation via Roslyn source generators, replacing runtime Castle DynamicProxy for[ComputeMethod]and command service interception. - .NET 7 support – added .NET 7 targeting (RC2 → release), updated framework dependencies, and CI workflows.
Key Changes in 2021
- Binary serialization – initial
MessagePackserialization support,IByteSerializer/ITextSerializerabstractions, andTypeDecoratingSerializerfor polymorphic serialization. Refactored text serializers fromITextWriter/ITextReaderto simpler Read/Write overloads. - OpenTelemetry support – initial
System.Diagnostics.DiagnosticSourceintegration withActivitySource-based tracing for compute methods and command handlers. - .NET 6 support.
Key Changes in 2020
Year of project inception.
- Fusion core – established the foundational
Computed<T>,ComputeMethod,State/MutableState/LiveStateabstractions,ComputedRegistry, automatic dependency tracking, and invalidation pipeline. - CQRS / CommandR –
Stl.CommandRcommand processing pipeline withICommand<T>,CommandContext,CommandHandler, and middleware pipeline for orchestrating side-effect-producing operations alongside Fusion's read model. - Replica services and WebSocket bridge –
ReplicaService,Publisher/Replicatorbridge over WebSockets,SubscriptionProcessorfor managing live subscriptions. - Entity Framework integration –
Stl.Fusion.EntityFrameworkwithDbOperationScope,DbAuthService,DbSessionInfo, early multi-tenancy support, and PostgreSQL/MySQL/SQL Server provider compatibility. - .NET 5 and multi-targeting – migrated from .NET Core 3.1 to .NET 5 with multi-targeting.
