s

sertyp

@preview

Serialization and deserialization of most Typst types into sertyp CBOR format: This includes markup, math, etc.

v0.1.5
MIT

Package Information

Last Updated
Categories
scriptingintegrationutility

1. Get the package

Download the package using the TPIX CLI:

tpix get @preview/sertyp:0.1.5

2. Import in your Typst file

Add this to your .typ file:

#import "@preview/sertyp:0.1.5": *

Version History

0.1.5
79c1d432302d...
0.1.4
8c17d99d0735...
0.1.3
159cf2291738...
0.1.2
54ef37cd227c...
0.1.1
6f69d165e50b...
0.1.0
86da7d7dc8fb...

Sertyp

Type-preserving serialization for Typst values. Converts Typst objects into an
intermediate representation. This can be used for communication with WASM
plugins.

What is this?

Unlike repr() or cbor.encode(), which produce ambiguous strings or lose type
information, sertyp:

  • Enables roundtrips: Deserialize back to the original displayable value,
    not just a string representation. Primitive values stay raw, while structured
    values keep enough type information to roundtrip correctly. This means a
    content object will again be fully displayed as content.
  • Works with WASM plugins: The intermediate representation can be further
    serialized to cbor and passed over the WASM boundary.

The
Rust backend
provides deserialization logic and typed data structures so plugins can work
with actual Typst types instead of manual parsing efforts.

Usage

#import "@preview/sertyp:0.1.5"

Examples

Basic serialization

#import "@preview/sertyp:0.1.5"

// Serialize and deserialize complex content
#let value = [
    Total displaced soil by glacial flow:
    $ 7.32 beta + sum_(i=0)^nabla (Q_i (a_i - epsilon)) / 2 $
    #metadata(title: "Glacial Flow Calculation")
]

#let serialized = sertyp.serialize(value)
#let deserialized = sertyp.deserialize(serialized)
#assert(repr(deserialized) == repr(value))

WASM Plugins using (rust sertyp crate)

use wasm_minimal_protocol::*;
use sertyp::{typst_func, Integer, String};

#[cfg(target_arch = "wasm32")]
initiate_protocol!();

// Result errors are automatically converted to typst panics.
#[typst_func]
pub fn fibonacci<'a>(n: Integer) -> Result<Integer, String<'a>> {
    let n: i32 = n.try_into().map_err(|_| "Invalid integer range")?;

    let (mut v0, mut v1) = (0, 1);
    for _ in 0..n {
        (v0, v1) = (v1, v0 + v1);
    }

    Ok(v1.into())
}

Type preservation examples

#import "@preview/sertyp:0.1.5"

#let color = rgb(255, 128, 0)
#let restored = sertyp.deserialize(sertyp.serialize(color))
// restored value is a real color object value, not a string

#let len = 2.5em + 10pt
#let restored = sertyp.deserialize(sertyp.serialize(len))
// Same with lengths and most other types

Overview

serialize(value) -> any

Converts a Typst value into an intermediate representation. Primitive values
stay raw, while structured values are represented using nested dicts/arrays and
type tags where needed.

let serialized = sertyp.serialize(rgb(255, 0, 0))
// Returns: (type: "color", value: (components: ..., space: ...))

deserialize(serialized) -> any

Reconstructs a Typst value from its intermediate representation.

let value = sertyp.deserialize(serialized)
// Returns the original displayable value

Security note: Deserialization uses eval() internally. Deserializing
untrusted values may therefore lead to arbitrary code execution. Only
deserialize trusted data
.

serialize-cbor(value) -> bytes

Serializes to CBOR binary format. Useful when passing values to WASM plugins.

let bytes = sertyp.serialize-cbor(my_value)
// Returns CBOR-encoded bytes

deserialize-cbor(bytes) -> any

Deserializes from CBOR bytes back to a Typst value.

let value = sertyp.deserialize-cbor(plugin_output)

Security note: Deserialization uses eval() internally. Deserializing
untrusted values may therefore lead to arbitrary code execution. Only
deserialize trusted data
.

call(function, ..args) -> any

Shorthand for serializing each positional argument to CBOR, calling the
function, and deserializing its CBOR result.

call-debug(function, ..args) -> any

Like call, but returns the intermediate sertyp representation of the plugin
result instead of deserializing it. This is useful when debugging a WASM
plugin's output.

Supported Types

Primitives: bool, int, float, decimal, string, bytes, none,
auto

Collections: array, dictionary

Numeric with units: length, angle, ratio, fraction, duration,
relative

Text & content: content, label, regex, symbol, version, datetime

Visual: color, stroke, gradient, alignment, direction, tiling

Advanced: function, module, selector, type, arguments, styles

Known Limitations

  • selector: Requires custom parsing for proper serialization (partially
    supported)
  • Dynamic types: context and other runtime-dependent elements cannot be
    fully serialized
  • Closures: Inline functions (..) => .. serialize but lose their captured
    state

Plugin Development

See the
Rust README
for details on building WASM plugins that work with sertyp.