v0.7.0

Minor Changes

  • Breaking: WASM plugins must return a packed i64 from all data-returning exports (filter, shortcode, hook, hooks, last_error). The return value packs a pointer and length into a single 64-bit integer: result = (ptr << 32) | len.

    Alloy rejects the previous multi-value (result i32 i32) form at load time with an error naming the export and its actual signature. The sret form ((param i32 i32 i32) -> () produced by Rust >= 1.82 and TinyGo tuple returns) is also rejected.

    The packed form works with current Rust, TinyGo, and AssemblyScript without unstable compiler flags:

    #[no_mangle]
    pub extern "C" fn filter(ptr: i32, len: i32) -> u64 {
        // ... transform input ...
        ((result_ptr as u64) << 32) | (result_len as u64)
    }
    //export filter
    func filter(ptr, length int32) uint64 {
        // ... transform input ...
        return (uint64(resultPtr) << 32) | uint64(resultLen)
    }
    export function filter(ptr: i32, len: i32): u64 {
      // ... transform input ...
      return (u64(resultPtr) << 32) | u64(resultLen);
    }

    The previous multi-value ABI required hand-written WAT because no mainstream toolchain could produce two separate i32 return values through stable compiler flags. This change makes WASM plugins practical for Rust, TinyGo, and AssemblyScript. See WASM Plugins for complete examples.

Patch Changes

  • Fix WASM and Node hooks losing url, path, and frontMatter when chained with other hooks on the same event. A QuickJS hook chaining after a WASM or Node hook received only the mutable field (html or content) — context fields were dropped. All three runtimes now preserve context through the full hook chain.

  • Fix plugin filter and shortcode errors being silently swallowed during template rendering. A filter that throws now fails the build with the filter name and source file path in the error message, instead of passing through the original input unchanged. Shortcode errors fail instead of producing empty output.

    Previously, {{ "hello" | myFilter }} where myFilter threw an exception would silently output hello.

Migration notes

WASM plugins — update all data-returning exports from (result i32 i32) to (result i64) with bit packing:

;; Before (v0.6.x)
(func $filter (export "filter") (param $ptr i32) (param $len i32) (result i32 i32)
  local.get $ptr
  local.get $len
)

;; After (v0.7.0)
(func $filter (export "filter") (param $ptr i32) (param $len i32) (result i64)
  local.get $ptr
  i64.extend_i32_u
  i64.const 32
  i64.shl
  local.get $len
  i64.extend_i32_u
  i64.or
)

Modules using the old ABI fail at load time with a clear error. No silent fallback.