# `MPP.JCS`
[🔗](https://github.com/ZenHive/mpp/blob/v0.10.0/lib/mpp/jcs.ex#L1)

RFC 8785 JSON Canonicalization Scheme (JCS) — MPP subset.

Produces deterministic JSON output by sorting object keys lexicographically
and using minimal whitespace. This ensures two implementations serializing
the same logical data produce byte-identical JSON — critical for HMAC
reproducibility across SDKs.

Used by `MPP.Plug` and `MPP.Mcp` when encoding the `request` field before
base64url encoding and HMAC computation. Matches the canonicalization in
mppx (`ox.Json.canonicalize`) and mpp-rs (`serde_json_canonicalizer`) for
all MPP protocol data.

## Scope

This implements the subset of RFC 8785 needed by MPP:

  * **Keys**: string, ASCII-only (all MPP protocol fields). Sorted by Elixir
    binary comparison, which matches UTF-16 code-unit ordering for ASCII. A
    non-string map key (atom, integer) violates the JSON/RFC 8785 contract and
    raises `ArgumentError` rather than emitting non-canonical output.
  * **Values**: strings, integers, booleans, nil, maps, lists.
  * **Not supported**: floats. MPP amounts are strings in base units
    (never floats). Passing a float raises `FunctionClauseError`.

For a general-purpose RFC 8785 implementation (non-ASCII keys, IEEE 754
float serialization), use a dedicated library.

## Examples

    iex> MPP.JCS.canonicalize(%{"currency" => "usd", "amount" => "1000"})
    ~S({"amount":"1000","currency":"usd"})

    iex> MPP.JCS.canonicalize(%{"b" => %{"d" => 1, "c" => 2}, "a" => 3})
    ~S({"a":3,"b":{"c":2,"d":1}})

## API Functions
| Function | Arity | Description | Param Kinds |
| --- | --- | --- | --- |
| `canonicalize` | 1 | Serialize a term to canonical JSON per RFC 8785 (MPP subset: ASCII keys, no floats). | `term: value` |

# `canonicalizable`

```elixir
@type canonicalizable() :: map() | list() | String.t() | integer() | boolean() | nil
```

# `canonicalize`

```elixir
@spec canonicalize(canonicalizable()) :: String.t()
```

Serialize a term to canonical JSON per RFC 8785 (MPP subset: ASCII keys, no floats).

## Parameters

  * `term` - Elixir term (map, list, string, integer, boolean, or nil) (value)

## Returns

Canonical JSON string (`string`)

```elixir
# descripex:contract
%{
  params: %{
    term: %{
      description: "Elixir term (map, list, string, integer, boolean, or nil)",
      kind: :value
    }
  },
  returns: %{type: :string, description: "Canonical JSON string"}
}
```

---

*Consult [api-reference.md](api-reference.md) for complete listing*
