Not released yet. PeltyCMS is under development and not released yet; these docs describe the upcoming release and may change.
PeltyCMS Developer docs
User docsDevelopers
v0.1.0

Developer docs/Extension reference

Hooks and mixins

#Hook declaration

A pre-event subscription: the host calls call("hook", {event, payload}) before the action and the addon may continue, cancel or modify it.

FieldTypeRequiredDescription
eventStringyes
priorityi32noLower runs first; ties ordered by addon id. Accepts an integer or a Bukkit-style name: lowest (0), low (50), normal (100), high (150), highest (200), monitor (1000, observe only).
ignore_cancelledboolnoAlso receive the event when an earlier listener cancelled it.
fail_openboolnoOn error/timeout: true lets the action continue, false blocks it.
timeout_msu64noHook budget in milliseconds (capped by the sandbox timeout).

Source: crates/pelty-wasm/src/runtime.rs (Hook).

#Priorities

NameValue
lowest0
low50
normal100
high150
highest200
monitor1000

Integers from -1000 to 1000 are also accepted. Lower values run first, and ties are ordered by addon id.

#Event names a hook may use

#Phases

Phases in order: before, during, after, finished. Source: crates/pelty-api/src/addon_events.rs.

#Mixins

A mixin: wraps an approved host operation (an API route template such as POST /api/v1/client/orders). at: before (rewrite the input), after (rewrite the output within its schema), around (both, with shared state) or return (replace the output, same schema).

FieldTypeRequiredDescription
targetStringyes
atStringyes
priorityi32no
timeout_msu64no

Source: crates/pelty-wasm/src/runtime.rs (Mixin).

at values: before, after, around, return. Mixins target <METHOD> <route template> of a core route. An after or return mixin may only change values of keys the response already has. Admins can turn an addon's mixins off.

#Example

extensions/examples/addon-guide/src/lib.rs · snippet hooksRust
/// Pre-events and lifecycle phases listed in `capabilities.hooks` call op
/// `hook`. Return `proceed`, `modify(payload)` or `cancel(reason)`.
fn hook(input: &Value) -> Value {
    let event = input["event"].as_str().unwrap_or("");
    if input["phase"].as_str().unwrap_or("before") != "before" {
        return reply::proceed(); // during/after/finished only observe
    }
    let mut p = input["payload"].clone();
    bump(&format!("hook:{event}"));
    match event {
        // Cancel: refuse sign-in for one domain.
        "login.before" if p["email"].as_str().is_some_and(|e| e.ends_with("@denied.test")) => reply::cancel("domain not allowed"),
        // Modify: tidy up the display name before the account is created.
        "client.before_register" => {
            p["display_name"] = json!(p["display_name"].as_str().unwrap_or("").trim().to_string());
            reply::modify(p)
        }
        // Own custom event (declared as `before:integration-guide:greeted`,
        // delivered with `event` = the bare name and `phase` = "before").
        "integration-guide:greeted" if p["name"] == "nobody" => reply::cancel("no greeting for nobody"),
        "order.before_place" => order_hook(input, p),
        _ => reply::proceed(),
    }
}
extensions/examples/addon-guide/src/lib.rs · snippet mixinRust
/// Mixins wrap a core route (`capabilities.mixins`). An `after` mixin may
/// change values of keys the response already has; new keys are dropped.
fn mixin(input: &Value) -> Value {
    let mut resp = input["response"].clone();
    // Opt-in per request so this example never changes other callers.
    let wanted = input["request"]["query"].as_str().unwrap_or("").contains("guide=1");
    if input["at"] == "after" && wanted {
        if let Some(c) = resp["city"].as_str() {
            resp["city"] = json!(c.to_uppercase());
        }
        return json!({ "response": resp });
    }
    json!({})
}