Repository navigation
Releases: mathisarends/canon
Release list
fastapi-streams 0.5.0
0.5.0 - 2026-10-25
Breaking changes
- The client that a generated Python SDK creates when no
http_clientis
passed now useshttpx.Timeout(None, connect=10.0)(also exported as
runtime.DEFAULT_TIMEOUT) instead oftimeout=None. Connecting to an
unreachable host now fails withTransportErrorafter 10 seconds instead of
hanging; read, write and pool waits remain unbounded, so idle streams are
unaffected. This changed default requires a minor release under the pre-1.0
version policy. Migration: regenerate the SDK. To keep unbounded connects,
passRequestOptions(timeout=None)per request or inject your own client,
for exampleClient(url, http_client=httpx.AsyncClient(timeout=None)).
Injected clients and explicitRequestOptions.timeoutvalues behave as
before.
Added
- Generated TypeScript clients accept
RequestOptions.unknownEvents. The default
"forward"keeps the current behavior;"ignore"skips JSON objects whose
discriminator is a string tag the generated event union does not declare, so
exhaustiveswitchstatements survive additive server variants. Each
generated operation now embeds its discriminator and known tags. Operations
without a top-level discriminated union reject"ignore"with aTypeError
before any request. Regenerate TypeScript SDKs to adopt the option. --diffforschema --check,generate --check,checkandsync --check
prints a unified diff of each out-of-date file (including new and stale owned
files and a changed application contract) to stdout, so CI logs and agents can
see the drift without regenerating locally.fastapi-streams syncreads[tool.fastapi-streams](app,schemaand
[[tool.fastapi-streams.clients]]withlanguage,outputand the existing
generation options) frompyproject.tomlor--config, exports the schema and
regenerates every client.sync --checkreports all stale targets read-only.
Unknown keys and invalid values are rejected with exit code 2.fastapi_streams.codegen.writer.planned_filesreturns the files including the
ownership manifest thatwrite_fileswould write.
Performance
- Generated Python and TypeScript runtimes split each decoded SSE chunk at line
endings instead of iterating characters. With the repository benchmark,
framing nested events is about 17x faster in Python and 4x faster in
TypeScript; complete small/nested stream consumption is 2-4.7x faster. Long
lines spanning many chunks are joined once. Framing semantics are unchanged:
LF/CR/CRLF (also split across chunks), the leading BOM, multi-linedata,
comments and the end-of-stream frame behave as before. A randomized test
compares both runtimes with the previous character parser. Regenerate SDKs to
adopt the faster runtime.
Documentation
- Point the README badge, example checkout and quickstart to the Canon workspace
repository and its locked workspace commands. The documentation check now
rejects links to the retired standalone repository. - Expand the package
AGENTS.mdwith the module layout, focused test/codegen
commands and the template-regeneration workflow.
fastapi-rpckit 0.4.1
0.4.1 - 2026-10-25
Performance
- Validate each socket request envelope once. The reader's validated envelope is
reused for dispatch and subscription handling instead of being validated again,
which saves about 3 µs per request. Middleware still receives the original raw
request, and error responses for invalid requests are unchanged. Wire output
is unchanged; JSON encoding stays on the standard library because
pydantic-core's serializer formats some floats differently.
Added
scripts/bench_rpc_requests.pymeasures sequential round-trip latency and
pipelined request throughput over the in-process socket runtime. See
Measure request throughput.
Documentation
- Add a core-API overview to the README and use the workspace's locked root
commands for development. Replace the stale version note with links to the
changelog and thepyrpckitmigration guide. - Refresh
AGENTS.md: current module map, locked workspace commands and one
consistent compatibility rule (preserve wire behavior and generated-client
contracts; remove superseded Python APIs only through a breaking release).
fastapi-canon 0.8.1
0.8.1 - 2026-10-25
- Update the optional Streams integration and development pin to 0.5.0 for the
TypeScriptunknownEventsoption,--diffdrift output, the config-driven
fastapi-streams synccommand and faster SSE line splitting in generated
runtimes. Canon's public API and composition behavior remain unchanged; core
imports continue to work without Streams, RPCkit or WebRTC. - Streams 0.5.0 changes the connect timeout of SDK-owned generated Python HTTP
clients. Regenerate stream SDKs and follow the
Streams 0.5.0 upgrade notes if a client
relied on an unbounded connect time.
fastapi-canon 0.8.0
0.8.0 - 2026-10-25
Breaking changes
- Extend the public constructor signatures with
CanonRouter(policy=...)and
Composition(router_policy=...). Update wrappers or signature snapshots that
mirror those constructors. Existing calls without policies keep their behavior.
To migrate a shared router helper, create aRouterPolicycontaining its
error_registry,dependencies, and sharedraises, then instantiate
CanonRouter(policy=...)explicitly. Keeproute_class=DishkaRouteon the
declaring router. UseComposition(router_policy=...)to extend contracts
across included feature routers; ordinary parentraises=remains local.
Added
- Export immutable
RouterPolicyfor reusable HTTP prerequisites and their error
contracts. Combine policy, router and operation declarations with the existing
registry collision checks and identity-based error deduplication. - Apply composition policies to nested HTTP/SSE operations without mutating
reusable feature routers or walking FastAPI's internal route tree. Preserve
FastAPI dependency behavior, combine same-status Problem schemas, and reject
manual response collisions before installing the composition. - Optional
python -m fastapi_canon.check_error_contractssource checker for
explicit application exceptions missing from direct CanonRouter error
contracts. It follows typed services, helpers and FastAPI dependencies,
accounts for supported exception handling and inheritance, and reports
unresolved paths as incomplete analysis. Warning output is the default;
strict CI modes and JSON reports are opt-in. - Reuse source-checker function summaries and inheritance results within each
invocation, preserving error paths and incomplete-analysis diagnostics. Add a
reproducible benchmark for source-processing and full-command timings.
Fixed
- Mark annotated module reassignments as incomplete source analysis, preventing
later declarations from hiding a missing route error contract or creating a
false gap for an already registered contract. - Report expanded
ErrorRegistry(**config)arguments as incomplete instead of
treating an unresolved catalog as empty and reporting missing error mappings.
fastapi-webrtc 0.3.0
0.3.0 - 2026-10-24
Breaking changes
- Replace the engine's
MediaKind,SignalingState,ConnectionState,
TransceiverDirectionandChannelStateliteral aliases with publicStrEnum
classes, also exported fromfastapi_webrtc. Use members such as
MediaKind.AUDIOandConnectionState.CONNECTEDin engine implementations,
callbacks, stats and codec-preference maps. Convert native strings with the
corresponding enum constructor; aiortc does this at its adapter boundary.
Values and string/JSON serialization stay unchanged. - Make
EngineCapabilitieskeyword-only. Replace positional flags with
EngineCapabilities(trickle_ice=..., ice_restart=..., renegotiation=...);
negotiated_data_channelsstill defaults toTrue. - Make
ChannelParametersoptions afterlabelkeyword-only. For example,
replaceChannelParameters("chat", False)with
ChannelParameters("chat", ordered=False). - Replace
MessageFormatandSessionEventKindliteral aliases with public
StrEnumclasses, also exported fromfastapi_webrtc. Use members such as
MessageFormat.JSONandSessionEventKind.TRACKwhen constructing descriptors
and events. Signaling descriptors reuseDirection,RoleandMessageFormat;
parsed SDP sections exposeTransceiverDirection. JSON values stay unchanged. - Use the public
DescriptionTypeenum forSessionDescription.typeand signaling
payloads. ReplaceSessionDescription("offer", sdp)with
SessionDescription(DescriptionType.OFFER, sdp)(likewise forANSWER).
The aiortc adapter converts native description types directly instead of
treating any non-offer value as an answer. - Declare remaining type aliases with Python 3.12's
typesyntax. These exported
names aretyping.TypeAliasTypeinstances; introspection tools must handle
their lazy__value__instead of expecting a runtime union orCallable.
SessionEvent.media_kindnow usesMediaKindtoo.
fastapi-canon 0.7.0
0.7.0 - 2026-10-24
Breaking changes
- Pin the optional
fastapi-canon[webrtc]integration to WebRTC 0.3.0. Custom
engines must use its publicStrEnumtypes and keyword-only engine/channel
options; follow the
WebRTC migration notes.
Canon core and the Streams integration keep their existing behavior.
fastapi-webrtc 0.2.0
0.2.0 - 2026-10-20
Breaking changes
- Generated runtimes export
observeVideoHealthand its types. Regenerate clients
and rename any colliding session/client declarations reported by code generation.
Existingconnect()readiness semantics are unchanged. - Add
Session.engineand allow a router without a default engine when factories
bind one per session. Custom constructor overrides should accept an optional
engine. Supply either a router engine or a session engine; missing bindings are
refused withhandler_failureand returned tracks are cleaned up. - Extend
WebRtcRouter.session()andSessionDeclarationwith the optional
admission_errorscontract-provider hook. Custom router overrides must accept
and forward this keyword. Regenerate clients to include declared admission
metadata in session descriptors. The signaling protocol remains v1.
Added
- Optional browser video health observation with separate opt-in first-frame and
ongoing-stall deadlines, recovery events, per-track decoded stats and optional
compositor frame callbacks. Abort, session close andstop()release observers;
observation never closes sessions, stops tracks or chooses a fallback UI. - A tested remote send-only adapter example with per-viewer async-context-manager
leases, explicit unsupported operations and partial statistics. Categorized
WebRtcErrorfailures during peer creation retain their stable protocol code. - Canon-independent admission contract export, including optional generated
descriptor metadata. HTTP denials remain distinct from accepted-session errors;
this metadata does not expose failed WebSocket handshake bodies in browsers. - An optional HTTP admission ticket recipe for readable
denials before signaling, with application-owned authorization and atomic ticket
consumption. It does not reserve remote viewer capacity automatically.
fastapi-webrtc 0.1.0
0.1.0 - 2026-10-20
Initial release.
WebRtcRouter.session()declares WebRTC sessions as WebSocket signaling
endpoints. FastAPI dependencies admit sessions before any peer is created and
stay open until cleanup finished, also when the server cancels the endpoint.Sessionhandlers:on_start,run,on_track, typedon_message,
on_closeand outgoing tracks.Peersends messages and tracks with
renegotiation, and exposes stats and close codes. Stats report jitter as
Nonewhen the engine cannot convert it to seconds (aiortc).on_closeruns
once per accepted session and keeps at least one second even when cancelled
handlers used upshutdown_timeout_seconds.- Track ownership: session tracks and
send_track(track)are stopped on removal
or cleanup, including rejected and partially started sessions;
send_track(track, owned=False)keeps shared sources running behind a
sender-local forwarding track. Only active
incoming tracks count againstMediaPolicy.max_incoming_tracks. - Typed data channels from Pydantic models,
strorbytes. JSON messages use
the contract's serialization aliases. Channels support negotiated IDs,
validated keyword-only reliability options, size limits, bounded inbound
queues and send backpressure. ADataChannelSpecis generic over its message
types:on_messageaccepts specs as keys, andpeer.channel(spec)and
peer.send_message(spec, ...)are type-checked against its models.
Neverasreceive_modelorsend_modeldeclares a one-way channel
(formatnone); the generated client omitssendorsubscribefor it. - Signaling protocol
fastapi-webrtc.v1. It uses perfect negotiation,
candidate buffering, ICE username checks, categorized close codes and
negotiation, media and transport deadlines. Negotiation timeouts and shutdown
cancel blocked ICE and engine startup. - ICE server providers,
TurnRestCredentialswith separate engine URLs and
session event hooks. WebRtcRouter(allowed_origins=...)refuses cross-site browser handshakes with
HTTP 403 before the session factory runs.- A pluggable
Engineprotocol, with anaiortcreference engine behind the
aiortcextra. - Tested Canon/Dishka composition with explicit async-factory injection,
session services, per-message request scopes and ordered cleanup. fastapi-webrtc contractandfastapi-webrtc generateexport a session
contract and generate a dependency-free TypeScript browser client with typed
parameters, channels and errors. Query models become flat named parameters.
Ambiguous generated names fail before files are written. Both commands support
--checkdrift detection.- The browser client merges query values from the base URL, shared credential
providers, per-call maps and named parameters, in that order;nullremoves
a value.timeoutMsbounds credential resolution and connection setup;
signalcancels the whole session. Renegotiation
and channel sends (sendTimeoutMs) have deadlines after connect, and
permanent transport failure rejects pending operations.
fastapi-rpckit 0.4.0
0.4.0 - 2026-10-20
Breaking changes
- Generated
WebSocketFactory,SocketConnectionFactoryand
BinaryWebSocketFactoryprotocols accept their URL as a positional-only
argument. Regenerate Python clients and pass the URL positionally when invoking
these factories. This matches the existing generated runtime calls and permits
custom factories with differently named first parameters. Custom factories
with named first parameters continue to work. serve_websocket()propagates caller task cancellation after endpoint cleanup.
Direct callers that cancel and await this helper must handleCancelledError.
Fixed
- Preserve the original ASGI cancellation while awaiting endpoint cleanup. This
prevents teardown stalls with minimum AnyIO and Starlette versions and avoids
leaking child-task cancellations through current Starlette TestClient sessions. - The generated default connectors adapt
websockets' nominalSubprotocoltype
inside their lazy backend boundary. Factory protocols remain independent of
websockets; consumers no longer need generator overrides for this mismatch.
fastapi-rpckit 0.3.0
0.3.0 - 2026-10-20
Breaking changes
- Remove server-to-client RPC. Every RPC method is now implemented by the
server and called by the client.channel.client.method(),RpcClientMethod,
RpcConnectedClient, theRpcClientMethod*Errorexceptions,
RpcClientClosedError,RpcProtocol.client_methods,
RpcLimits.client_method_timeoutandRpcTestClient(client_methods=...)/
RpcTestClient.handle()are gone. - Remove
channel.server. Declare operations directly on the channel with
@channel.method(),@channel.event(),@channel.subscription(),
@channel.stream()and@channel.socket(). These were already the
default spelling;channel.server.Xwas an alias. - OpenRPC documents no longer contain
x-rpc-client-methodsor
*ClientMethodrequest schemas, and the generators reject documents that
still declarex-rpc-client-methods. - Generated Python clients no longer contain
handlers.py,
internal/client_methods.py,current_client_method_metadata(), the
connect(handlers=...)option, the transport'srequest_handler=option
or thecreate()class methods on client-method errors. Like TypeScript
clients, they answer any request the server sends withmethod_not_found. - The server no longer accepts JSON-RPC response frames from the client. They
are answered withinvalid_requestlike any other malformed request. - Raw sockets are now sockets. A socket is a transport endpoint; the messages
it carries belong to the handler. Migrate as follows:@channel.raw_socket(...)becomes@channel.socket(...)with the same
options.service.raw_socket(path, handler, ...)becomes
service.socket(path, handler, ...).service.socket(path, channels=...)
still mounts JSON-RPC endpoints; passing both a handler andchannels=, or
JSON-RPC-only options with a handler, raisesProtocolDefinitionError.- Inject
RpcSocketinstead ofRpcRawSocket;RpcRawMessageis
RpcSocketMessageandRpcRawSocketEndpointisRpcSocketEndpoint.
The modulefastapi_rpckit.raw_socketsis nowfastapi_rpckit.sockets. - The low-level WebSocket adapter protocol formerly named
RpcSocketis now
RpcWebSocket. Rename custom adapters and annotations that implemented or
referenced the old protocol;FastApiSocketis unchanged.
- The contract lists sockets under
x-rpckit-socketsinstead of
x-rpckit-raw-sockets. Re-export contracts and regenerate clients together;
generators reject contracts that still containx-rpckit-raw-sockets. - Generated clients rename their socket API. Python: module
sockets(was
raw_sockets),Socket,SocketMessage,SocketMessageType,SocketName,
SocketEndpoint,SocketTransport,SocketOpener,SOCKETS,
SocketConnection,SocketConnectionFactory,SocketConnectionStream;
connect(socket_connection_factory=...)andsocket_opener=...replace
raw_socket_factoryandraw_socket_opener. TypeScript: modulesockets
(wasrawSockets),Socket,sockets,resolveSocketEndpoint,
SocketConnectionStream, and the optionssocketConnectionFactory,
socketQueueSize, andsocketOpenerreplacerawSocketFactory,
rawSocketQueueSize, andrawSocketOpener. Regenerate clients and rename
these references in consuming code. - Server log messages and close reasons say "socket" instead of "raw socket".
Socket wire behavior, close codes, and message framing are unchanged. - Typed JSON input is now validated before queueing. A malformed later message
can therefore cancel an active invocation. Clients must send valid messages
throughout a connection rather than depending on an earlier reply before an
invalid queued message is rejected.
Migration:
- Replace
@channel.server.method(...)and the otherchannel.server.*
decorators with@channel.method(...)and the matching channel decorator. - Replace each server-to-client call with a server method the client calls,
an event or a subscription. RemoveInject[RpcConnectedClient]parameters. - Drop
client_method_timeout=fromRpcLimits(...)andclient_methods=
fromRpcTestClient(...). - Regenerate clients and remove handler classes and
handlers=arguments
fromClient.connect(...)calls. - Rename
@channel.raw_socketto@channel.socket,service.raw_socket(...)to
service.socket(...),RpcRawSockettoRpcSocket, and the adapter
protocolRpcSockettoRpcWebSocket, then re-export contracts and
regenerate clients as described above.
Added
-
Infer typed JSON connections from
Inject[RpcSocket[Incoming, Outgoing]],
including aliases and unions. These handlers retain their own receive/send loop,
unsolicited messages, multiple replies, and one dependency scope per connection.
They share cached internal Pydantic validation and serialization with message
handlers. Annotations alone define message types, directional contract schemas,
and Python/TypeScript SDK types. BareRpcSocketkeeps raw text/binary behavior;
protocol metadata never enables validation. -
Declare JSON socket message handlers as
async def ping(message: Ping) -> Pong.
The library validates each input, calls the handler sequentially in a fresh
invocation scope, and validates and serializes its response. Invalid input
closes with 1002 and invalid output with 1011, without exposing message contents.
InjectingRpcSocketkeeps the existing connection-handler text/binary behavior.
Protocol metadata alone still does not enable validation. -
Export directional socket message schemas and generate Python and TypeScript
Socket[Received, Sent]clients. Add a checked, livetyped_socketexample.
Python SDK validation uses generated models; TypeScript supplies static payload
types and JSON framing, with authoritative validation on the server. -
Declare the application protocol of a socket with
@channel.socket(..., protocol=RpcSocketProtocol(...)). The declaration
holds a protocol name, version, encoding, and specification URI references.
It is metadata only: messages still pass through unchanged. Handlers read it
asRpcSocket.protocol. -
Contracts export declared protocols as
x-rpckit-protocolon socket entries.
The generator IR keeps them asSocketProtocolDecl, whose
resolve_schemas()resolves relative schema references against the contract
location. Generated Python and TypeScript clients expose them as
SocketProtocolon socket endpoints. -
Plug typed protocol clients into code generation with
fastapi_rpckit.codegen.bindings. AProtocolBindingselected by protocol
name emits a module into the generated client, and matching socket methods
return its session instead of a genericSocket. Select bindings with
bindings = [...]in the generator config,--binding, or the
fastapi_rpckit.socket_bindingsentry point group. Sockets without a
binding keep the genericSocket. -
Add the
socket_protocolexample: a small CDP-shaped protocol with Python and
TypeScript bindings, tested against a live server.
Fixed
- Close an opened TypeScript socket when a protocol binding fails to initialize,
including asynchronous failures. Preserve the initialization error if closing
the transport also fails. - Keep bindings with identical exported factory or session names separate in
generated Python and TypeScript clients by importing their modules under
distinct aliases.