Remote methods and streams

An extension package can expose methods that code on the other side of the boundary between the host and its clients calls directly. Mark the method as Remote, bind its owner to a service, and the runtime carries calls, streams and cancellation between the two sides. This page is the reference for that model: the authoring walkthrough is on Remote calls for extension authors, the wire messages are on The Oda Remote API, and what a build generates for such a package is on Plugin artifacts and the catalog.

Mark a method as Remote

Four markers exist, written on the method itself in TypeScript:

MarkerWhat it declares
@RemoteA direct call, under the method's own name.
@Remote('name')A direct call, exported under another name.
@Remote({ mode: 'stream' })A streaming call.
@RemoteScope('context')A contextual call, whose receiver comes from the named context, with an optional second name.

Bind the methods to a service

A marked method becomes callable once its owner is bound:

What a call carries

Streaming calls

Results and failures

How things are named

Questions, answered

What do the different key formats mean?

Each key points at a different thing. An endpoint addresses one callable as <namespace>/<method>, and a registered invocation carries the full id <package>#<namespace>/<method>. A generated schema is addressed as <package>#<name>, and one package's contribution per side as <package>#<side>; the # marks where the package part ends.

Is a marker stored on the class or on each instance?

On the class. The markers record one descriptor on the class, frozen and non-enumerable, and instances read it from there; the class declares its callable surface once, and the markers read back in declaration order.

What does the invocation context carry?

Inside a method, ctx.invocation carries the request the call made (its namespace, its method and the arguments as received), the service it reached, the peer that made the call and the cancellation signal. Outside a Remote call the property reads as undefined.

Do stream items arrive as they are produced?

Yes. Each item travels on the stream as the method produces it, so the caller sees items while the method still runs. There is no wait for the method to finish.

What happens to an open stream when the connection drops?

A handle stands for one connection generation. When the connection is lost, iteration fails with the connection error and the handle is finished; start the call again for a new stream.

What does a refused request's failure tell me?

When a call does not satisfy the endpoint, it fails with gateway/bad-request. That failure can carry an issues list describing what was wrong, so read it before you retry.

Why did my second marker fail?

A method takes one marker. A second marker that disagrees with the first fails with Remote method "<method>" has conflicting invocation markers; keep the single marker that says how the method is called and remove the other.

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.