Radical
Explanation

The bridge

Why native commands use services and schemas.

The WebView and native shell run in different runtimes. Capacitor transports calls; Radical's Bridge service gives those calls an Effect error contract.

A manifest command across the bridge
sequenceDiagram
  participant App as App Effect
  participant Client as callPlugin
  participant Bridge as BridgeLive
  participant Native as Capacitor native plugin
  App->>Client: Command arguments
  Client->>Client: Encode args schema and validate object
  Client->>Bridge: call(plugin, method, options)
  Bridge->>Native: nativePromise(plugin, method, options)
  alt Native resolves
      Native-->>Client: Unknown result
      Client->>Client: Decode result schema
      Client-->>App: Typed result or BridgeDecodeError
  else Native rejects
      Native-->>Bridge: Rejection
      Bridge-->>Client: BridgeCallFailed
      Client->>Client: Decode declared code and error data
      Client-->>App: Declared tagged error or BridgeCallFailed
  end

Invalid arguments fail before transport. A missing native bridge produces BridgeUnavailable. Unknown rejection codes and invalid error data remain BridgeCallFailed, so an unexpected failure cannot masquerade as a declared capability error.

Runtime selection stays replaceable

Capability operations select native commands or browser services when the Effect runs. Browser services use web APIs; default Google sign-in and native push have no browser equivalent.

Injectable layers let tests replace the bridge and browser services without a native project. React hooks start Effects after mount and release owned work on unmount. See Client reference for services and errors, or Plugin manifests for command schemas.

On this page