Loading the documentation…

Remote calls, streams and events

DataMind Oda's screens and its extension packages call the host that serves them through one layer: short calls that carry a request and an answer, long-lived streams that carry items in both directions, and events the host forwards to the interface. This page is the reference for that layer, from writing a Remote method in a package to the messages on the wire, so you can author a method, read a trace, debug a connection or build a client of your own. The message-level view is also on The Oda Remote API, and what a build generates for a Remote-capable package is on Plugin artifacts and the catalog. Oda also defines and runs packages in a chat itself, covered by The host half of a dynamic package and The browser half of a dynamic package.

Mark a method as Remote

An extension package exposes a method to the other side of the boundary by marking it as Remote and binding its owner to a service; the runtime then carries calls, streams and cancellation between the two sides. Markers are written in TypeScript on the method itself:

MarkerWhat it declares
@RemoteA direct call, under the method's own name.
@Remote('name')A direct call, exported under another name; the method keeps its own name on the class.
@Remote({ mode: 'stream' })A streaming call.
@RemoteScope('context')A contextual call, whose receiver comes from the named context instead of the bound service, with an optional second name, as in @RemoteScope('context', 'name').

Bind the methods to a service

A marked method becomes callable once its owner is bound:

What a call carries

Write a streaming method

Values that cross

The same lossless checks cover clients, gateways and mocks, so a value that cannot round-trip unchanged is refused wherever it would cross: call arguments, results, uplink items and forwarded-event payloads.

How calls are named

Calls

Streams

Streams are logical: they share the one WebSocket connection the client keeps open, each under its own stream id.

On a stream, each side sends a small set of frames:

FromFrameWhat it does
ClientopenOpens a logical stream, naming the method it calls and its arguments.
ClientitemSends one uplink item; its value may be omitted.
ClientendHalf-closes the uplink: no further items follow.
ClientcancelThe consumer has left; the stream is torn down.
HostitemDelivers one downlink value.
HosterrorEnds the stream with a failure: a code, a message and details.
HostendEnds the stream normally.

The session surface

Forwarded events

Account states

Limits

Close codes

A connection ends with a close code and, usually, a reason:

CodeReasonWhat it means
1000disposedThe client was disposed; the connection is closed for good.
1001peer leftThe peer's session ended, and its streams close with it.
1003text messages requiredA binary message arrived; the connection carries text only.
1008invalid Remote stream requestA malformed message, an unknown message type, or a duplicate open.
1011Remote stream failure could not be deliveredA stream failed and its failure could not be written, so the connection closed.
4000reconnect requestedThe client is making a fresh connection attempt; a new connection follows on its own.
4002an invalid frameA frame did not parse; every active stream fails and the connection closes.

When a call or stream fails

Questions, answered

What happens to open streams when the connection drops?

Nothing is replayed. When the client reconnects, each stream reopens on the new connection and re-reads the state it needs; items and notifications from the old connection are never sent again. While the replacement is being established, the interface keeps showing the state it already had. Connection states and recovery covers the recovery itself.

Why did the connection close with code 1008?

The reason invalid Remote stream request covers a malformed message, an unknown message type, or a second open for a stream id that is already open. Check the frames against the shapes on this page; a duplicate open is the one violation that closes the whole connection.

Why was my uplink item not lossless JSON?

The value used something the JSON subset does not admit, such as an undefined field, a big integer, NaN, an infinity, negative zero, a cycle, a class instance, or an accessor, hidden or symbol field. Send plain objects, dense arrays and ordinary JSON values instead. The stream the item was sent on fails with gateway/input-invalid.

Can two consumers watch the same stream?

No. A logical stream serves exactly one consumer, and opening a second one fails. When several parts of the interface need the same data, the client shares one stream between them instead of opening another.

How long may the connection stay silent?

It is pinged every two seconds by default, and that interval is also the deadline for the answer. A peer that misses two intervals in a row loses the connection. The interval is a deployment setting, so it can be raised where the default is too quick for the machines involved.

How much can a stream's uplink hold?

262,144 bytes (256 KiB) of frames by default. Over that, the stream fails with gateway/uplink-overflow and later items are dropped. Uplink items are never replayed, so anything that did not get through is lost.

Are forwarded events replayed after a reconnect?

One-way events are not: a notification missed while disconnected is gone. A request that is still waiting for an answer is different; it is sent again to the replacement connection so it can still be answered.

Can a private or static method be marked as Remote?

No. A marker needs a public, non-static instance method with a string name and a concrete implementation; private or static methods, methods without an implementation and generic methods are refused with Remote decorators require a public instance method with a string name. Mark a public instance method instead.

Why must the signal parameter be last?

Because the signal is injected, not sent: the runtime provides it at call time, and a declaration that does not end with it is refused with Remote cancellation signal must be the final parameter. Declare signal: AbortSignal as the method's last parameter and react to it when the caller goes away.

Why did my send() call throw?

send throws when the item is not a lossless JSON value, when the uplink has already been ended, or when the stream has terminated. Nothing is delivered when it throws, so fix the value or stop sending to an ended or finished stream.

Why should I check the failure code instead of using instanceof?

Because an instance check does not survive a bundle boundary: a failure is recognised structurally, not by its class. Read the stable <domain>/<reason> code and branch on that.

How is this different from the Oda SDK?

They are different surfaces. This layer connects DataMind Oda's own screens and extensions to the host that serves them. The SDK is for a program of your own: a client process talks to a runtime over standard input and output, with the messages in The Oda SDK wire protocol. Start from the Oda SDK overview, or read Automation with Oda for the serving profiles.