///|
/// HMAC-SHA256 keyed hash used to sign webhook payloads. Implemented on top
/// of the pure MoonBit SHA-256, following RFC 2104.
pub fn hmac_sha256(key : Bytes, message : Bytes) -> Array[Byte] {
  let block_size = 64
  let normalized_key = if key.length() > block_size {
    Bytes::from_array(sha256(key))
  } else {
    key
  }
  let key_arr = normalized_key.to_array()
  let key_block = Array::make(block_size, b'\x00')
  for i in 0.. String {
  bytes_to_hex(hmac_sha256(@utf8.encode(secret), @utf8.encode(payload)))
}

///|
pub fn verify_hmac_sha256(
  secret : String,
  payload : String,
  signature : String,
) -> Bool {
  constant_time_eq(sign_hmac_sha256(secret, payload), signature)
}

///|
/// Compares two signature strings in constant time to avoid leaking the
/// expected digest through early-exit comparisons.
pub fn constant_time_eq(left : String, right : String) -> Bool {
  let left_arr = @utf8.encode(left).to_array()
  let right_arr = @utf8.encode(right).to_array()
  if left_arr.length() != right_arr.length() {
    false
  } else {
    let mut diff = 0U
    for i in 0..