extism

    An Extism host for MoonBit: a WASM engine layer, the Extism kernel and a plugin API

    extism
    wasm
    webassembly
    plugin
    wamr
    Download zip
    Author
    Version
    0.3.0
    License
    Apache-2.0
    Last updated
    4 days ago
    Downloads
    3K

    No dependencies

    #The plugin API of moonrockz/extism

    Load an Extism plugin from a manifest and call it by export name (design PLG-1 and PLG-2). Each plugin has its own instance of the Zig kernel. The manifest gives the limits, the policy for the Extism host functions, the host functions that the embedder registers in any namespace and, when the plugin needs it, the WASI sandbox.

    FamilyPolicy choicesDefault
    config_getDeny, Empty, Values(map)Empty: handle 0
    var_get, var_setDeny, Empty (get gives 0, set is denied), Session(limit)Empty
    log_*, get_log_levelDiscard, Sink(level, sink)Discard; the level is 2147483647
    http_request, http_status_code, http_headersDeny, Handler(handler)Deny

    A call fails with a typed CallError: Plugin(code, text), Exit(code), Denied(call), HostFunction(name, message), MemoryLimit(memory), Limit(name), Engine(error), Initialization(error) or UnknownExport(name).

    ///|
    test "a manifest, a policy and a call" {
    let policy = @extism.Policy::default().config(Values({ "mode": "strict" }))
    let plugin = @extism.Plugin::load(
    @extism.Manifest::new(@fixtures.plugin())
    .policy(policy)
    .register(
    @extism.HostFunction::new(
    @extism.Namespace::parse("extism:host/user"),
    @extism.FunctionName::parse("lookup"),
    @engine.Signature::new([I64], [I64]),
    (context, args) => {
    guard args is [I64(offset)] else {
    raise @engine.HostError("bad arguments")
    }
    let input = context.read(@extism.Handle::parse(offset)) catch {
    _ => raise @engine.HostError("no input")
    }
    let output = context.allocate(input) catch {
    _ => raise @engine.HostError("no room")
    }
    [I64(output.offset())]
    },
    ),
    ),
    )
    assert_eq(plugin.call(@extism.ExportName::parse("config"), b""), b"strict")
    assert_eq(plugin.call(@extism.ExportName::parse("lookup"), b"same"), b"same")
    }

    ///|
    test "a plugin with the WASI sandbox" {
    let printed = []
    let sandbox = @wasi.Sandbox::new().stdout(bytes => {
    printed.push(bytes.to_owned())
    })
    let plugin = @extism.Plugin::load(
    @extism.Manifest::new(@fixtures.wasi_plugin()).wasi(sandbox),
    )
    assert_eq(plugin.call(@extism.ExportName::parse("print"), b""), b"")
    assert_eq(printed, [b"hello"])
    }

    CallError

    pub(all) suberror CallError {
    Plugin(Int, String)
    Exit(Int)
    Denied(DeniedCall)
    HostFunction(String, String)
    MemoryLimit(MemoryKind)
    Limit(String)
    Engine(
    CallError
    )
    Initialization(
    CallError
    )
    UnknownExport(String)
    } derive(
    Debug
    )

    An error of a plugin call.

    LoadError

    pub(all) suberror LoadError {
    UnlinkedImport(String, String)
    WrongSignature(String, String)
    Engine(
    EngineError
    )
    } derive(
    Debug
    )

    A plugin that the library refuses to load.

    ManifestError

    pub(all) suberror ManifestError {
    ReservedName(String)
    AlreadyRegistered(String)
    } derive(Eq,
    Debug
    )

    A manifest that the library refuses: the import that a registration names, and why.

    ManifestError::equal

    ManifestError::not_equal

    fn ManifestError::not_equal(x : ManifestError, y : ManifestError) -> Bool

    NameError

    pub(all) suberror NameError {
    NameError(String, String, String)
    } derive(Eq,
    Debug
    )

    A value that breaks the rule of its type: the name of the type, the value and the rule.

    NameError::equal

    fn NameError::equal(NameError, NameError) -> Bool

    NameError::not_equal

    fn NameError::not_equal(x : NameError, y : NameError) -> Bool

    ByteLimit

    pub struct ByteLimit {
    // private fields
    } derive(Eq,
    Debug
    )

    A number of bytes, from 0 to 2147483647.

    ByteLimit::bytes

    fn ByteLimit::bytes(self : ByteLimit) -> Int

    ByteLimit::equal

    fn ByteLimit::equal(ByteLimit, ByteLimit) -> Bool

    ByteLimit::not_equal

    fn ByteLimit::not_equal(x : ByteLimit, y : ByteLimit) -> Bool

    ByteLimit::parse

    fn ByteLimit::parse(bytes : Int64) -> ByteLimit raise NameError

    A byte limit: from 0 to 2147483647 bytes.

    CallContext

    pub struct CallContext {
    // private fields
    }

    The access of a registered host function to the kernel of the plugin that calls it.

    CallContext::allocate

    fn CallContext::allocate(self : CallContext, bytes : BytesView) -> Handle raise
    MemoryError

    A new block in kernel memory with bytes.

    CallContext::read

    The bytes of the block at handle in kernel memory.

    ConfigPolicy

    pub(all) enum ConfigPolicy {
    Deny
    Empty
    Values(Map[String, String])
    }

    What config_get gives.

    DeniedCall

    pub(all) struct DeniedCall {
    function : String
    method_ : String?
    url : String?
    } derive(Eq,
    Debug
    )

    A call of a denied Extism host function: the function and, for http_request, the method and the URL of the refused request.

    DeniedCall::equal

    fn DeniedCall::equal(DeniedCall, DeniedCall) -> Bool

    DeniedCall::not_equal

    fn DeniedCall::not_equal(x : DeniedCall, y : DeniedCall) -> Bool

    ExportName

    pub struct ExportName {
    // private fields
    } derive(Eq,
    Debug
    )

    The name of a function that a plugin exports, for example handle.

    ExportName::equal

    fn ExportName::equal(ExportName, ExportName) -> Bool

    ExportName::not_equal

    fn ExportName::not_equal(x : ExportName, y : ExportName) -> Bool

    ExportName::parse

    fn ExportName::parse(text : String) -> ExportName raise NameError

    An export name: not empty, at most 1024 bytes and no NUL.

    ExportName::text

    fn ExportName::text(self : ExportName) -> String

    ExtismFunction

    pub(all) enum ExtismFunction {
    ConfigGet
    VarGet
    VarSet
    LogTrace
    LogDebug
    LogInfo
    LogWarn
    LogError
    GetLogLevel
    HttpRequest
    HttpStatusCode
    HttpHeaders
    } derive(Eq,
    Debug
    )

    The Extism host functions that the library links in extism:host/env, with their upstream signatures.

    ExtismFunction::all

    ExtismFunction::equal

    ExtismFunction::from_name

    fn ExtismFunction::from_name(name : StringView) -> ExtismFunction?

    ExtismFunction::name

    fn ExtismFunction::name(self : ExtismFunction) -> String

    ExtismFunction::not_equal

    fn ExtismFunction::not_equal(x : ExtismFunction, y : ExtismFunction) -> Bool

    ExtismFunction::signature

    FunctionName

    pub struct FunctionName {
    // private fields
    } derive(Eq,
    Debug
    )

    The import field name of a host function, for example lookup.

    FunctionName::equal

    FunctionName::not_equal

    fn FunctionName::not_equal(x : FunctionName, y : FunctionName) -> Bool

    FunctionName::parse

    fn FunctionName::parse(text : String) -> FunctionName raise NameError

    A function name: not empty, at most 1024 bytes and no NUL.

    FunctionName::text

    fn FunctionName::text(self : FunctionName) -> String

    Handle

    pub struct Handle {
    // private fields
    } derive(Eq,
    Debug
    )

    A handle: the offset of a block in kernel memory. Zero is no block, so a handle is more than 0.

    Handle::equal

    fn Handle::equal(Handle, Handle) -> Bool

    Handle::not_equal

    fn Handle::not_equal(x : Handle, y : Handle) -> Bool

    Handle::offset

    fn Handle::offset(self : Handle) -> Int64

    Handle::parse

    fn Handle::parse(offset : Int64) -> Handle raise NameError

    A handle: more than 0.

    Handle::to_repr

    HostFunction

    pub struct HostFunction {
    // private fields
    }

    A host function that the embedder registers in a namespace that it chooses.

    HostFunction::label

    fn HostFunction::label(self : HostFunction) -> String

    namespace.name.

    HttpPolicy

    pub(all) enum HttpPolicy {
    Deny
    Handler((HttpRequest) -> HttpResponse raise
    HostError
    )
    }

    What http_request does. The library never opens a connection.

    HttpRequest

    pub(all) struct HttpRequest {
    method_ : String
    url : String
    headers : Map[String, String]
    body : Bytes
    }

    An HTTP request of a plugin, for the embedder's handler.

    HttpResponse

    pub(all) struct HttpResponse {
    status : Int
    headers : Map[String, String]
    body : Bytes
    }

    The embedder's answer to an HTTP request.

    Limits

    pub struct Limits {
    // private fields
    }

    The limits of a plugin: the memory ceilings of the plugin and of its kernel, the instruction budget and the deadline of each call, and the default variable store limit.

    Limits::budget

    Limits::deadline

    Limits::default

    fn Limits::default() -> Limits

    The default limits: 4096 pages for the plugin and for the kernel, no budget and no deadline.

    Limits::kernel_memory

    fn Limits::kernel_memory(self : Limits, pages :
    PageCount
    ) -> Limits

    Limits::plugin_memory

    fn Limits::plugin_memory(self : Limits, pages :
    PageCount
    ) -> Limits

    LogLevel

    pub(all) enum LogLevel {
    Trace
    Debug
    Info
    Warn
    Error
    } derive(Eq,
    Debug
    )

    An Extism log level.

    LogLevel::code

    fn LogLevel::code(self : LogLevel) -> Int

    The number that get_log_level gives for the level: 0 for trace to 4 for error.

    LogLevel::equal

    fn LogLevel::equal(LogLevel, LogLevel) -> Bool

    LogLevel::not_equal

    fn LogLevel::not_equal(x : LogLevel, y : LogLevel) -> Bool

    LogLevel::to_repr

    LogPolicy

    pub(all) enum LogPolicy {
    Discard
    Sink(LogLevel, (LogLevel, String) -> Unit)
    }

    What the log_* functions do.

    Manifest

    pub struct Manifest {
    // private fields
    }

    The description of one plugin: its module bytes, its limits, its policy, the host functions that the embedder registers and, when it is enabled, the WASI sandbox. The builder steps give a new manifest; the library reads nothing from a file, a URL or the environment.

    Manifest::limits

    fn Manifest::limits(self : Manifest, limits : Limits) -> Manifest

    Manifest::new

    fn Manifest::new(module_ : Bytes) -> Manifest

    A manifest with the module bytes, the default limits, the default policy, no registered host function and no WASI sandbox.

    Manifest::policy

    fn Manifest::policy(self : Manifest, policy : Policy) -> Manifest

    Manifest::register

    fn Manifest::register(self : Manifest, function : HostFunction) -> Manifest raise ManifestError

    The manifest with function registered. A name in extism:host/env and a name that is already registered are refused.

    Manifest::wasi

    The manifest with the WASI preview 1 sandbox enabled.

    MemoryKind

    pub(all) enum MemoryKind {
    PluginMemory
    KernelMemory
    } derive(Eq,
    Debug
    )

    The memory whose ceiling refused a grow.

    MemoryKind::equal

    fn MemoryKind::equal(MemoryKind, MemoryKind) -> Bool

    MemoryKind::not_equal

    fn MemoryKind::not_equal(x : MemoryKind, y : MemoryKind) -> Bool

    Namespace

    pub struct Namespace {
    // private fields
    } derive(Eq,
    Debug
    )

    The import module name of a host function, for example extism:host/user.

    Namespace::equal

    fn Namespace::equal(Namespace, Namespace) -> Bool

    Namespace::not_equal

    fn Namespace::not_equal(x : Namespace, y : Namespace) -> Bool

    Namespace::parse

    fn Namespace::parse(text : String) -> Namespace raise NameError

    A namespace: not empty, at most 1024 bytes and no NUL.

    Namespace::text

    fn Namespace::text(self : Namespace) -> String

    Plugin

    pub struct Plugin {
    // private fields
    }

    A loaded plugin: its own kernel instance and its instance, with the imports linked.

    Plugin::bounds

    The bounds that the engine of the plugin enforces.

    Plugin::call

    fn Plugin::call(self : Plugin, name : ExportName, input : BytesView) -> Bytes raise CallError

    Calls the export name with input: the kernel is reset, the input is put in kernel memory, the export runs with the budget and the deadline of the limits, and the output bytes are returned when it returns 0 and sets no error.

    Plugin::has_function

    fn Plugin::has_function(self : Plugin, name : ExportName) -> Bool

    Whether the plugin exports a function with the name name. No guest code runs.

    Plugin::load

    fn Plugin::load(manifest : Manifest, engine? :
    Engine
    ) -> Plugin raise LoadError

    Loads the plugin of manifest on engine: the import check, the kernel instance, then the plugin instance with each import linked.

    Policy

    pub struct Policy {
    // private fields
    }

    The embedder's decision for each family of Extism host functions (PLG-2).

    Policy::config

    fn Policy::config(self : Policy, config : ConfigPolicy) -> Policy

    Policy::default

    fn Policy::default() -> Policy

    The default policy: configuration and variables answer empty, var_set is denied, log messages are discarded and HTTP is denied.

    Policy::http

    fn Policy::http(self : Policy, http : HttpPolicy) -> Policy

    Policy::logging

    fn Policy::logging(self : Policy, logging : LogPolicy) -> Policy

    Policy::variables

    fn Policy::variables(self : Policy, variables : VariablePolicy) -> Policy

    VariablePolicy

    pub(all) enum VariablePolicy {
    Deny
    Empty
    Session(ByteLimit)
    }

    What var_get and var_set do.

    logging_off

    let logging_off : Int

    What get_log_level gives when logging is off.