# RDesk IPC Message Contract

    # Parse a valid IPC message
    raw <- '{"id":"msg_001","type":"get_data","version":"1.0","payload":{},"timestamp":1234567890}'
    msg <- RDesk::rdesk_parse_message(raw)
    msg$type
    #> [1] "get_data"

# RDesk IPC Message Contract (v1.0)

This document defines the standard JSON envelope used for all communication between the R backend and the HTML/JS frontend in RDesk applications.

## The Message Envelope

Every message sent across the native IPC bridge (WebView2 to stdin/stdout) must be a JSON object with the following structure:

    {
      "id": "msg_abc123",
      "type": "action_name",
      "version": "1.0",
      "payload": {
        "key": "value"
      },
      "timestamp": 1742123456.789
    }

### Field Definitions

- `id`: A unique string identifier for the message (e.g., for tracing or request/response correlation).
- `type`: The internal name of the message or action. This triggers handlers on the receiving end.
- `version`: The version of the message contract (currently “1.0”).
- `payload`: A JSON object containing the actual data for the message.
- `timestamp`: Seconds since the Unix epoch (including decimal fractions).

## Usage in R

When sending from R to the UI, use `app$send()`:

    app$send("update_ui", list(status = "Success"))

RDesk automatically wraps your payload in the standard envelope before transmission.

## Usage in JavaScript

When sending from the UI to R, use `rdesk.send()`:

    rdesk.send("button_click", { id: "submit_btn" });

The `rdesk.js` library automatically constructs the envelope with a unique ID and current timestamp.

## Error Handling

The parser `rdesk_parse_message()` is designed to be defensive. It will return `NULL` if: 1. The input is not valid JSON. 2. The envelope is missing required fields (`type` or `payload`).

In these cases, a `warning()` is emitted with the specific failure reason.

### Defensive Pattern for Developers

If you are building custom extensions or handlers, always check for `NULL` before processing:

    msg <- rdesk_parse_message(raw_json)
    if (is.null(msg)) {
      # Log error or silently ignore malformed traffic
      return(invisible(NULL))
    }

    # Safely access msg$type and msg$payload here

## The async result convention

When using `async()`, the result is automatically sent back to the UI as a message of type `<original_type>_result`.

For example:

R registers | JS sends | JS receives
--- | --- | ---
`on_message("filter_cars", async(...))` | `rdesk.send("filter_cars", payload)` | `rdesk.on("filter_cars_result", fn)`
`on_message("run_model", async(...))` | `rdesk.send("run_model", payload)` | `rdesk.on("run_model_result", fn)`

This convention means the JS developer always knows which event to listen for without reading the R code. It is consistent and predictable.

### Overriding the result type

If you need a different result type, use `rdesk_async()` directly (Tier 2) and call `app$send()` explicitly in your `on_done` callback.