Skip to content
kimja edited this page Sep 28, 2026 · 14 revisions

🧬 API

πŸ“œ Communciation through rzeka is carried through a set of specialised API methods.

All API methods accept a who object and return IDisposable to unregister. Observables and lambda functions you pass into them are called spells.

who is the component that owns the subscription. Eris records it on every spell occurrence so the debugger can group spells under their source. Passing this from inside a Node or component is the default.

πŸ“œπŸ§šπŸ»β€β™€οΈ rzeka code will make the characteristic waterfall 2D structures that go deep into your indentation while remaining very clear and readable. This depth might not be for everyone though. Personally I really prefer that to a 1D top-down wall of code-text, maybe you might like it too! Screenshot info: nvim, theme Aquavium, semitransparent background, CSharpier formatter.

🧬 The methods

Each method has its own page:

Method What it does Reach for it when
🧬 Strand publisher you already have an IObservable<T> source to register into the river
🧬 Pluck fire once publisher you want to drop a single matter into the river, right now
🧬 Loom transform you map, combine or transform matter into new matter
🧬 Weave subscriber you do the final effect - rendering, audio, persistence
🧬 Scry raw observable you need read-only access to a stream from inside another spell
🧬 Shuttle request/response you perform an operation and report its outcome

Collecting subscriptions

A common pattern is to collect them into rzeka's CollectibleDisposable:

CollectibleDisposable Q = new();

Q += rzeka.Loom<PlayerInputState, PlayerMovementState>(...)

// on destroy / cleanup:
Q.Dispose();

CollectibleDisposable is a wrapper around CompositeDisposable, it overloads + operator allowing you to neatly add your rzeka subscriptions to it. It does not implement .Clear() method of the CompositeDisposable because that would likely lead you to accidental memory leaks.

🧬 Circumstance rules

πŸ“œ Quick reference for when rzeka attaches circumstances automatically vs when you must attach them yourself.

Where automatic tracking works:

  • Synchronous Loom chains - the default, most common, no action needed.
  • Shuttle responses - the request is always recorded for you, because it travels on the response (Response<T> carries its T). This holds even across async boundaries and with multiple requests in flight.

Where you must attach circumstances manually:

  • Inside Pluck and Ask calls - pre-stamp the matter you're sending via .WithCircumstances<T>(trigger) so it carries its own cause.
  • Inside async boundaries within Loom - see Async Operations.
  • Shuttle responses - only the additional context you Scry in. The request is already handled; stamp just the extras (if you include the request too, rzeka dedupes it).

Where circumstances are not touched:

  • Strand - it is used for root matter emissions (e.g. caused by user input), so there can be no circumstances there.

Manual stamping inside a Loom lambda (via .WithCircumstances()) is an active decision to override the default tracking - rzeka detects pre-attached circumstances and skips its automatic step.

Clone this wiki locally