Skip to content

Add a compatible transport interface and observability layer over @remote-ui/rpc #14

Description

@cmath10

Summary

Introduce a first-party transport interface for @omnicajs/vue-remote that is compatible with the current @remote-ui/rpc integration, but does not require all runtimes to depend on @remote-ui/rpc forever.

The goal is not to remove @remote-ui/rpc immediately.
The goal is to stop treating it as the only transport/runtime bridge story.

This should allow:

  • existing extensions to continue using @remote-ui/rpc unchanged;
  • new integrations to adopt an alternative transport/runtime bridge if desired;
  • vue-remote itself to provide better diagnostics and controllable error handling around runtime transport failures.

Problem

@remote-ui/rpc is technically capable, but from a DX and debugging perspective it behaves too much like a black box.

When runtime failures happen, it is often hard to answer:

  • which method was called;
  • on which side it failed;
  • whether the failure happened during transport, decode, invoke, or release;
  • which function proxy was already released;
  • whether the error is a real problem or just post-unmount noise.

Typical failure modes are especially painful around:

  • retained/released function references;
  • delayed messages after unmount;
  • async teardown races;
  • transport-level errors that surface after the UI session is already effectively over.

In these cases the current error often degenerates into an opaque UUID-based failure or a generic transport exception that is not actionable.

Desired outcome

We want a transport layer that preserves current behavior where needed, but gives us better control over:

  • observability;
  • diagnostics;
  • lifecycle-aware error handling;
  • optional transport replacement.

Goals

  • Introduce a compatible transport abstraction that can sit above @remote-ui/rpc.
  • Allow multiple transport/runtime bridge implementations behind one vue-remote-level contract.
  • Keep backward compatibility for existing extensions using @remote-ui/rpc.
  • Make runtime transport activity observable.
  • Enrich transport errors with structured context.
  • Support conditional downgrading of selected transport errors from error to warning when they happen after teardown or in explicitly tolerated lifecycle windows.

Non-Goals

  • Remove @remote-ui/rpc from the project immediately.
  • Break the current integration story for existing users.
  • Rewrite the full wire protocol in the first iteration.
  • Hide real transport bugs by turning all errors into warnings.

Why this is needed

1. @remote-ui/rpc is too opaque during failures

Today, when something breaks, the transport layer often gives us too little context.
We need more than a generic RPC failure or a released-function id.
We need to know:

  • attempted method name;
  • call id / invoke id;
  • target side;
  • lifecycle phase;
  • whether the target was already released;
  • whether the failure happened after explicit teardown.

2. The current system cannot express lifecycle-aware severity well

A large class of transport errors happens after unmount / release.
Those errors are often technically true, but operationally uninteresting.
They create noise instead of signal.

We need a way to say, effectively:

  • before teardown: transport failures are errors;
  • after teardown: selected failures may be downgraded to warnings;
  • in strict/debug mode: keep them as errors again.

This downgrade must be explicit and configurable, not silent.

3. We want gradual transport evolution, not a flag day rewrite

Old extensions should be able to stay on @remote-ui/rpc.
New integrations should be able to use a different implementation later.
That means the transition path should be additive and compatibility-oriented.

Proposed direction

A. Introduce a first-party transport contract

Define a vue-remote-level transport interface that captures what the runtime bridge actually needs from an RPC layer.

This contract should be designed around:

  • request/call handling;
  • method exposure;
  • lifecycle control;
  • event hooks for observability;
  • optional memory-management integration.

The important point is that vue-remote should depend on this contract, not directly on a concrete RPC package.

B. Provide an adapter for @remote-ui/rpc

Ship a compatibility adapter that wraps @remote-ui/rpc.

This adapter becomes the default compatibility path for existing users.
That means:

  • current extensions can keep using the old runtime bridge;
  • vue-remote gains a place to add diagnostics without forcing a rewrite;
  • future alternative transports can target the same contract.

C. Make observability a first-class responsibility of the transport layer

The transport abstraction should expose hooks/events for:

  • outgoing call attempts;
  • incoming calls;
  • results and rejections;
  • release/retain lifecycle;
  • termination;
  • dropped/ignored messages.

This is the foundation for both debugger tooling and better runtime logs.

D. Add transport-aware error classification

Instead of treating every transport failure the same way, classify failures into categories such as:

  • invoke target missing;
  • released function reference;
  • decode/encode failure;
  • call after terminate;
  • late message after teardown;
  • unexpected result for unknown call id.

Each event should carry structured metadata so downstream handling can make decisions.

E. Support conditional error-to-warning downgrade

Introduce configurable policy-based severity mapping.

The intended behavior is not "swallow errors".
The intended behavior is:

  • preserve hard errors for real runtime problems;
  • downgrade known post-teardown noise where appropriate;
  • allow strict mode to keep everything loud when investigating bugs.

Example policy dimensions:

  • current lifecycle phase;
  • event kind/category;
  • whether session teardown was explicit;
  • strict/debug mode enabled or not.

Migration strategy

Phase 1. Define the compatibility contract

  • Identify the minimal bridge surface vue-remote actually needs.
  • Define a first-party transport interface around that surface.
  • Keep the interface narrow and implementation-oriented.

Phase 2. Implement @remote-ui/rpc adapter

  • Wrap createEndpoint(...) and related lifecycle behavior.
  • Preserve current behavior for existing integrations.
  • Add instrumentation hooks and structured error context.

Phase 3. Route vue-remote integration guidance through the transport contract

  • Keep @remote-ui/rpc as the default compatibility path.
  • Stop treating it as the only supported conceptual model.
  • Make room for alternative implementations in docs/examples.

Phase 4. Add alternative transport implementation(s)

  • Introduce one non-@remote-ui/rpc implementation behind the same contract.
  • Validate that old and new integrations can coexist without API breakage.

Compatibility requirements

  • Existing extensions using @remote-ui/rpc must continue to work.
  • Existing lifecycle patterns like run/release must remain valid.
  • Existing retain/release semantics must either remain compatible or be clearly adapted behind the wrapper.
  • The first iteration must not require a wire-protocol rewrite.

Suggested transport concerns to surface explicitly

The new contract or adapter layer should make the following visible:

  • method name being called;
  • call identifier;
  • side / role (host, remote, maybe bridge internally);
  • release/retain state if relevant;
  • lifecycle phase (active, releasing, released, terminated);
  • severity (error, warning, maybe debug);
  • root cause classification.

Example high-value cases

Released function reference

Current behavior is often effectively: "function already released", plus a UUID-like trail.
We need richer diagnostics, for example:

  • method attempted;
  • whether the proxy was released due to explicit teardown;
  • which side initiated release;
  • whether this happened after unmount.

Late post-unmount calls

These often should not look like catastrophic transport failures.
They are good candidates for downgrade to warnings if the session is already marked as ended.

Unknown result / orphaned response

These should stay loud and diagnosable.
They usually point to protocol or lifecycle mismatches worth investigating.

Acceptance criteria

  • vue-remote has a first-party transport contract or adapter layer.
  • @remote-ui/rpc remains usable through that layer without breaking existing integrations.
  • Transport failures carry enough structured context to be actionable.
  • Selected post-teardown transport failures can be downgraded to warnings through explicit policy.
  • Strict/debug mode can keep those failures as errors.
  • The new layer gives a clean place for debugger/instrumentation hooks.

Open questions

  1. Should retain/release compatibility stay hard-coupled to @remote-ui/rpc symbols, or be abstracted behind the new layer from the start?
  2. How narrow can the first transport contract be while still being useful for alternative implementations?
  3. Should warning-downgrade policy live in the transport adapter, in vue-remote, or in user-supplied hooks?
  4. Do we want compatibility at API shape level only, or eventual wire-protocol compatibility as well?

Recommended first step

Start with a compatibility adapter over @remote-ui/rpc, not with a rewrite.

That gives us the fastest path to:

  • better diagnostics;
  • better lifecycle-aware error handling;
  • a migration path for future transport implementations;
  • zero or minimal breakage for existing extensions.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    RefactorTasks needed to make the things easier

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions