Lossless JSON-safe encoding for checkpoint payloads.
Graph state holds arbitrary Elixir terms — message structs, atoms,
tuples, and maps with non-string keys. Plain JSON encoding destroys
those shapes: structs come back as string-keyed maps and pattern
matching on restored state breaks. This serializer tags each rich
term so decode/1 rebuilds the exact original value.
Decoding never creates atoms: module and field names are resolved
with String.to_existing_atom/1, so a malicious or corrupted payload
cannot exhaust the atom table.
Encoding scheme
| Term | Encoded form |
|---|---|
nil/bool/num | as-is |
| UTF-8 binary | as-is |
| other binary | %{"~b" => base64} |
| atom | %{"~a" => "name"} |
| tuple | %{"~t" => [encoded...]} |
| struct | %{"~s" => "Elixir.Mod", "~f" => %{...}} |
| map | %{"~m" => [[encoded_key, encoded_value], ...]} |
| list | JSON array of encoded elements |