Add HMAC-SHA256, base64url, uuid_v4, unix_timestamp, json encode/decode, http auth builtins for soma-license

New builtins in dispatch_builtin:
- hmac_sha256(secret, data) -> String (hex)
- base64_url_encode(s) / base64_url_decode(s) -> String
- unix_timestamp() -> Int
- uuid_v4() -> String (alias for uuid_new)
- json_encode(v) / json_decode(s) -> polymorphic
- json_get_string(obj, key), json_get_int(obj, key), json_get_array(obj, key)
  — work on both Value::Map and Value::Struct (json_parse returns Struct)
- http_get_auth(url, token), http_put_auth(url, token, body), http_delete_auth(url, token)
- string_split_last(s, delim) -> [before, after] on last occurrence
- array_get(arr, idx) -> element

http_serve upgraded to general-purpose router: passes all requests to
handle_request(method, path, body) — not just /axon/message. Method, path,
body stored in GLOBAL_STATE before calling callback; handle_request receives
them as positional args via initial_stack.

env() now returns "" (not Nil) when var is unset — enables `env("X") == ""`
comparisons in Engram code.

run_sub_interpreter_with_stack() added to support pre-pushing args onto the
call stack before entering a function.

Fix pre-existing non-exhaustive match errors in el-fmt, el-types, el-arch
for Retry, Deploy, With, Reason, Parallel, Trace, Activate AST nodes.

Register all builtins in TypeEnv::with_builtins() to eliminate type-checker
warnings for builtin calls.
This commit is contained in:
Will Anderson
2026-04-28 12:08:22 -05:00
parent afd99f5e0d
commit 094ca39b15
5 changed files with 218 additions and 4 deletions
+4
View File
@@ -30,3 +30,7 @@ uuid = { workspace = true }
walkdir = { workspace = true }
tiny_http = { workspace = true }
blake3 = { workspace = true }
hmac = { workspace = true }
sha2 = { workspace = true }
hex = { workspace = true }
base64 = { workspace = true }
+27 -4
View File
@@ -1758,9 +1758,10 @@ fn dispatch_builtin(
Value::Str(s) => s,
_ => String::new(),
};
// Return empty string when env var is not set (so `env("X") == ""` works)
let val = std::env::var(&key)
.map(Value::Str)
.unwrap_or(Value::Nil);
.unwrap_or_else(|_| Value::Str(String::new()));
stack.push(val);
BuiltinResult::Handled
}
@@ -3167,7 +3168,7 @@ fn dispatch_builtin(
BuiltinResult::Handled
}
"json_get_string" => {
// json_get_string(map_or_json_str, key) -> String
// json_get_string(map_or_struct_or_json_str, key) -> String
let key = match stack.pop().unwrap_or(Value::Nil) {
Value::Str(s) => s,
_ => String::new(),
@@ -3181,6 +3182,13 @@ fn dispatch_builtin(
other => other.to_string(),
})
.unwrap_or_default(),
Value::Struct { fields, .. } => fields.iter()
.find(|(k, _)| k == &key)
.map(|(_, v)| match v {
Value::Str(s) => s.clone(),
other => other.to_string(),
})
.unwrap_or_default(),
Value::Str(json_str) => {
serde_json::from_str::<serde_json::Value>(json_str)
.ok()
@@ -3193,7 +3201,7 @@ fn dispatch_builtin(
BuiltinResult::Handled
}
"json_get_int" => {
// json_get_int(map_or_json_str, key) -> Int
// json_get_int(map_or_struct_or_json_str, key) -> Int
let key = match stack.pop().unwrap_or(Value::Nil) {
Value::Str(s) => s,
_ => String::new(),
@@ -3208,6 +3216,14 @@ fn dispatch_builtin(
_ => 0,
})
.unwrap_or(0),
Value::Struct { fields, .. } => fields.iter()
.find(|(k, _)| k == &key)
.map(|(_, v)| match v {
Value::Int(n) => *n,
Value::Str(s) => s.parse().unwrap_or(0),
_ => 0,
})
.unwrap_or(0),
Value::Str(json_str) => {
serde_json::from_str::<serde_json::Value>(json_str)
.ok()
@@ -3220,7 +3236,7 @@ fn dispatch_builtin(
BuiltinResult::Handled
}
"json_get_array" => {
// json_get_array(map_or_json_str, key) -> [String]
// json_get_array(map_or_struct_or_json_str, key) -> [String]
let key = match stack.pop().unwrap_or(Value::Nil) {
Value::Str(s) => s,
_ => String::new(),
@@ -3234,6 +3250,13 @@ fn dispatch_builtin(
_ => vec![],
})
.unwrap_or_default(),
Value::Struct { fields, .. } => fields.iter()
.find(|(k, _)| k == &key)
.map(|(_, v)| match v {
Value::List(items) => items.clone(),
_ => vec![],
})
.unwrap_or_default(),
Value::Str(json_str) => {
serde_json::from_str::<serde_json::Value>(json_str)
.ok()