{"route":"/en-US-v0.13.1/reference/foundations/plugin/","title":"Plugin","description":"Documentation for the `plugin` function.","part":null,"outline":[{"id":"summary","name":"Summary","children":[]},{"id":"example","name":"Example","children":[]},{"id":"purity","name":"Purity","children":[]},{"id":"plugins-and-packages","name":"Plugins and Packages","children":[]},{"id":"wasi","name":"WASI","children":[]},{"id":"protocol","name":"Protocol","children":[{"id":"exports","name":"Exports","children":[]},{"id":"imports","name":"Imports","children":[]}]},{"id":"resources","name":"Resources","children":[]},{"id":"parameters","name":"Parameters","children":[{"id":"parameters-source","name":"source","children":[]}]},{"id":"definitions","name":"Definitions","children":[{"id":"definitions-transition","name":"Transition","children":[{"id":"definitions-transition-func","name":"func","children":[]},{"id":"definitions-transition-arguments","name":"arguments","children":[]}]}]}],"body":{"kind":"func","content":{"path":[],"name":"plugin","title":"Plugin","keywords":[],"oneliner":"Loads a WebAssembly module.","element":false,"contextual":false,"deprecation":null,"details":"<p>Loads a WebAssembly module.</p>\n<p>The resulting <a href=\"/en-US-v0.13.1/reference/foundations/module/\" title=\"module\">module</a> will contain one Typst <a href=\"/en-US-v0.13.1/reference/foundations/function/\" title=\"function\">function</a> for each function\nexport of the loaded WebAssembly module.</p>\n<p>Typst WebAssembly plugins need to follow a specific\n<a href=\"/en-US-v0.13.1/reference/foundations/plugin/#protocol\">protocol</a>. To run as a plugin, a program needs to be\ncompiled to a 32-bit shared WebAssembly library. Plugin functions may accept\nmultiple <a href=\"/en-US-v0.13.1/reference/foundations/bytes/\">byte buffers</a> as arguments and return a single byte\nbuffer. They should typically be wrapped in idiomatic Typst functions that\nperform the necessary conversions between native Typst types and bytes.</p>\n<p>For security reasons, plugins run in isolation from your system. This means\nthat printing, reading files, or similar things are not supported.</p>\n<h2 id=\"example\">Example</h2>\n<div class=\"previewed-code\"><pre><code><span class=\"typ-key\">#</span><span class=\"typ-key\">let</span> myplugin <span class=\"typ-op\">=</span> <span class=\"typ-func\">plugin</span><span class=\"typ-punct\">(</span><span class=\"typ-str\">&quot;hello.wasm&quot;</span><span class=\"typ-punct\">)</span>\n<span class=\"typ-key\">#</span><span class=\"typ-key\">let</span> <span class=\"typ-func\">concat</span><span class=\"typ-punct\">(</span>a<span class=\"typ-punct\">,</span> b<span class=\"typ-punct\">)</span> <span class=\"typ-op\">=</span> <span class=\"typ-func\">str</span><span class=\"typ-punct\">(</span>\n  myplugin<span class=\"typ-punct\">.</span><span class=\"typ-func\">concatenate</span><span class=\"typ-punct\">(</span>\n    <span class=\"typ-func\">bytes</span><span class=\"typ-punct\">(</span>a<span class=\"typ-punct\">)</span><span class=\"typ-punct\">,</span>\n    <span class=\"typ-func\">bytes</span><span class=\"typ-punct\">(</span>b<span class=\"typ-punct\">)</span><span class=\"typ-punct\">,</span>\n  <span class=\"typ-punct\">)</span>\n<span class=\"typ-punct\">)</span>\n\n<span class=\"typ-func\">#</span><span class=\"typ-func\">concat</span><span class=\"typ-punct\">(</span><span class=\"typ-str\">&quot;hello&quot;</span><span class=\"typ-punct\">,</span> <span class=\"typ-str\">&quot;world&quot;</span><span class=\"typ-punct\">)</span>\n</code></pre><div class=\"preview\"><img src=\"/en-US-v0.13.1/assets/563eb9b72603c710f73876546a243de1.png\" alt=\"Preview\"></div></div>\n<p>Since the plugin function returns a module, it can be used with import\nsyntax:</p>\n<pre><code><span class=\"typ-key\">#</span><span class=\"typ-key\">import</span> <span class=\"typ-func\">plugin</span><span class=\"typ-punct\">(</span><span class=\"typ-str\">&quot;hello.wasm&quot;</span><span class=\"typ-punct\">)</span><span class=\"typ-punct\">:</span> concatenate\n</code></pre>\n<h2 id=\"purity\">Purity</h2>\n<p>Plugin functions <strong>must be pure:</strong> A plugin function call most not have any\nobservable side effects on future plugin calls and given the same arguments,\nit must always return the same value.</p>\n<p>The reason for this is that Typst functions must be pure (which is quite\nfundamental to the language design) and, since Typst function can call\nplugin functions, this requirement is inherited. In particular, if a plugin\nfunction is called twice with the same arguments, Typst might cache the\nresults and call your function only once. Moreover, Typst may run multiple\ninstances of your plugin in multiple threads, with no state shared between\nthem.</p>\n<p>Typst does not enforce plugin function purity (for efficiency reasons), but\ncalling an impure function will lead to unpredictable and irreproducible\nresults and must be avoided.</p>\n<p>That said, mutable operations <em>can be</em> useful for plugins that require\ncostly runtime initialization. Due to the purity requirement, such\ninitialization cannot be performed through a normal function call. Instead,\nTypst exposes a <a href=\"/en-US-v0.13.1/reference/foundations/plugin/#definitions-transition\">plugin transition API</a>, which executes\na function call and then creates a derived module with new functions which\nwill observe the side effects produced by the transition call. The original\nplugin remains unaffected.</p>\n<h2 id=\"plugins-and-packages\">Plugins and Packages</h2>\n<p>Any Typst code can make use of a plugin simply by including a WebAssembly\nfile and loading it. However, because the byte-based plugin interface is\nquite low-level, plugins are typically exposed through a package containing\nthe plugin and idiomatic wrapper functions.</p>\n<h2 id=\"wasi\">WASI</h2>\n<p>Many compilers will use the <a href=\"https://wasi.dev/\">WASI ABI</a> by default or as\ntheir only option (e.g. emscripten), which allows printing, reading files,\netc. This ABI will not directly work with Typst. You will either need to\ncompile to a different target or <a href=\"https://github.com/astrale-sharp/wasm-minimal-protocol/tree/master/crates/wasi-stub\">stub all\nfunctions</a>.</p>\n<h2 id=\"protocol\">Protocol</h2>\n<p>To be used as a plugin, a WebAssembly module must conform to the following\nprotocol:</p>\n<h3 id=\"exports\">Exports</h3>\n<p>A plugin module can export functions to make them callable from Typst. To\nconform to the protocol, an exported function should:</p>\n<ul>\n<li>\n<p>Take <code>n</code> 32-bit integer arguments <code>a_1</code>, <code>a_2</code>, ..., <code>a_n</code> (interpreted as\nlengths, so <code>usize/size_t</code> may be preferable), and return one 32-bit\ninteger.</p>\n</li>\n<li>\n<p>The function should first allocate a buffer <code>buf</code> of length <code>a_1 + a_2 + ... + a_n</code>, and then call\n<code>wasm_minimal_protocol_write_args_to_buffer(buf.ptr)</code>.</p>\n</li>\n<li>\n<p>The <code>a_1</code> first bytes of the buffer now constitute the first argument, the\n<code>a_2</code> next bytes the second argument, and so on.</p>\n</li>\n<li>\n<p>The function can now do its job with the arguments and produce an output\nbuffer. Before returning, it should call\n<code>wasm_minimal_protocol_send_result_to_host</code> to send its result back to the\nhost.</p>\n</li>\n<li>\n<p>To signal success, the function should return <code>0</code>.</p>\n</li>\n<li>\n<p>To signal an error, the function should return <code>1</code>. The written buffer is\nthen interpreted as an UTF-8 encoded error message.</p>\n</li>\n</ul>\n<h3 id=\"imports\">Imports</h3>\n<p>Plugin modules need to import two functions that are provided by the\nruntime. (Types and functions are described using WAT syntax.)</p>\n<ul>\n<li>\n<p><code>(import &quot;typst_env&quot; &quot;wasm_minimal_protocol_write_args_to_buffer&quot; (func (param i32)))</code></p>\n<p>Writes the arguments for the current function into a plugin-allocated\nbuffer. When a plugin function is called, it <a href=\"#exports\">receives the\nlengths</a> of its input buffers as arguments. It should then\nallocate a buffer whose capacity is at least the sum of these lengths. It\nshould then call this function with a <code>ptr</code> to the buffer to fill it with\nthe arguments, one after another.</p>\n</li>\n<li>\n<p><code>(import &quot;typst_env&quot; &quot;wasm_minimal_protocol_send_result_to_host&quot; (func (param i32 i32)))</code></p>\n<p>Sends the output of the current function to the host (Typst). The first\nparameter shall be a pointer to a buffer (<code>ptr</code>), while the second is the\nlength of that buffer (<code>len</code>). The memory pointed at by <code>ptr</code> can be freed\nimmediately after this function returns. If the message should be\ninterpreted as an error message, it should be encoded as UTF-8.</p>\n</li>\n</ul>\n<h2 id=\"resources\">Resources</h2>\n<p>For more resources, check out the <a href=\"https://github.com/astrale-sharp/wasm-minimal-protocol\">wasm-minimal-protocol\nrepository</a>. It\ncontains:</p>\n<ul>\n<li>A list of example plugin implementations and a test runner for these\nexamples</li>\n<li>Wrappers to help you write your plugin in Rust (Zig wrapper in\ndevelopment)</li>\n<li>A stubber for WASI</li>\n</ul>","example":null,"self":false,"params":[{"name":"source","details":"<p>A <a href=\"/en-US-v0.13.1/reference/syntax/#paths\">path</a> to a WebAssembly file or raw WebAssembly bytes.</p>","example":null,"types":["str","bytes"],"strings":[],"default":null,"positional":true,"named":false,"required":true,"variadic":false,"settable":false}],"returns":["module"],"scope":[{"path":["plugin"],"name":"transition","title":"Transition","keywords":[],"oneliner":"Calls a plugin function that has side effects and returns a new module","element":false,"contextual":false,"deprecation":null,"details":"<p>Calls a plugin function that has side effects and returns a new module\nwith plugin functions that are guaranteed to have observed the results\nof the mutable call.</p>\n<p>Note that calling an impure function through a normal function call\n(without use of the transition API) is forbidden and leads to\nunpredictable behaviour. Read the <a href=\"/en-US-v0.13.1/reference/foundations/plugin/#purity\">section on purity</a>\nfor more details.</p>\n<p>In the example below, we load the plugin <code>hello-mut.wasm</code> which exports\ntwo functions: The <code>get()</code> function retrieves a global array as a\nstring. The <code>add(value)</code> function adds a value to the global array.</p>\n<p>We call <code>add</code> via the transition API. The call <code>mutated.get()</code> on the\nderived module will observe the addition. Meanwhile the original module\nremains untouched as demonstrated by the <code>base.get()</code> call.</p>\n<p><em>Note:</em> Due to limitations in the internal WebAssembly implementation,\nthe transition API can only guarantee to reflect changes in the plugin's\nmemory, not in WebAssembly globals. If your plugin relies on changes to\nglobals being visible after transition, you might want to avoid use of\nthe transition API for now. We hope to lift this limitation in the\nfuture.</p>","example":"<pre><code><span class=\"typ-key\">#</span><span class=\"typ-key\">let</span> base <span class=\"typ-op\">=</span> <span class=\"typ-func\">plugin</span><span class=\"typ-punct\">(</span><span class=\"typ-str\">&quot;hello-mut.wasm&quot;</span><span class=\"typ-punct\">)</span>\n<span class=\"typ-pol\">#</span><span class=\"typ-pol\">assert</span><span class=\"typ-punct\">.</span><span class=\"typ-func\">eq</span><span class=\"typ-punct\">(</span>base<span class=\"typ-punct\">.</span><span class=\"typ-func\">get</span><span class=\"typ-punct\">(</span><span class=\"typ-punct\">)</span><span class=\"typ-punct\">,</span> <span class=\"typ-str\">&quot;[]&quot;</span><span class=\"typ-punct\">)</span>\n\n<span class=\"typ-key\">#</span><span class=\"typ-key\">let</span> mutated <span class=\"typ-op\">=</span> plugin<span class=\"typ-punct\">.</span><span class=\"typ-func\">transition</span><span class=\"typ-punct\">(</span>base<span class=\"typ-punct\">.</span>add<span class=\"typ-punct\">,</span> <span class=\"typ-str\">&quot;hello&quot;</span><span class=\"typ-punct\">)</span>\n<span class=\"typ-pol\">#</span><span class=\"typ-pol\">assert</span><span class=\"typ-punct\">.</span><span class=\"typ-func\">eq</span><span class=\"typ-punct\">(</span>base<span class=\"typ-punct\">.</span><span class=\"typ-func\">get</span><span class=\"typ-punct\">(</span><span class=\"typ-punct\">)</span><span class=\"typ-punct\">,</span> <span class=\"typ-str\">&quot;[]&quot;</span><span class=\"typ-punct\">)</span>\n<span class=\"typ-pol\">#</span><span class=\"typ-pol\">assert</span><span class=\"typ-punct\">.</span><span class=\"typ-func\">eq</span><span class=\"typ-punct\">(</span>mutated<span class=\"typ-punct\">.</span><span class=\"typ-func\">get</span><span class=\"typ-punct\">(</span><span class=\"typ-punct\">)</span><span class=\"typ-punct\">,</span> <span class=\"typ-str\">&quot;[hello]&quot;</span><span class=\"typ-punct\">)</span>\n</code></pre>","self":false,"params":[{"name":"func","details":"<p>The plugin function to call.</p>","example":null,"types":["function"],"strings":[],"default":null,"positional":true,"named":false,"required":true,"variadic":false,"settable":false},{"name":"arguments","details":"<p>The byte buffers to call the function with.</p>","example":null,"types":["bytes"],"strings":[],"default":null,"positional":true,"named":false,"required":true,"variadic":true,"settable":false}],"returns":["module"],"scope":[]}]}}}