2026-03-25 23:02:51 +00:00
|
|
|
use std::cell::RefCell;
|
2026-03-25 22:32:07 +00:00
|
|
|
use std::collections::HashMap;
|
2026-03-25 23:02:51 +00:00
|
|
|
use std::rc::Rc;
|
2026-03-25 22:32:07 +00:00
|
|
|
|
|
|
|
|
use deno_core::JsRuntime;
|
|
|
|
|
use deno_core::RuntimeOptions;
|
|
|
|
|
use wfe_core::WfeError;
|
|
|
|
|
|
|
|
|
|
use super::config::DenoConfig;
|
2026-03-25 23:02:51 +00:00
|
|
|
use super::module_loader::WfeModuleLoader;
|
2026-04-07 18:44:21 +01:00
|
|
|
use super::ops::workflow::{StepMeta, StepOutputs, WorkflowInputs, wfe_ops};
|
2026-03-25 22:32:07 +00:00
|
|
|
use super::permissions::PermissionChecker;
|
|
|
|
|
|
|
|
|
|
/// Create a configured `JsRuntime` for executing a workflow step script.
|
|
|
|
|
pub fn create_runtime(
|
|
|
|
|
config: &DenoConfig,
|
|
|
|
|
workflow_data: serde_json::Value,
|
|
|
|
|
step_name: &str,
|
|
|
|
|
) -> Result<JsRuntime, WfeError> {
|
|
|
|
|
let ext = wfe_ops::init();
|
|
|
|
|
|
2026-03-25 23:02:51 +00:00
|
|
|
// Build permissions, auto-adding esm.sh if modules are declared.
|
|
|
|
|
let mut permissions = config.permissions.clone();
|
|
|
|
|
if !config.modules.is_empty() && !permissions.net.iter().any(|h| h == "esm.sh") {
|
|
|
|
|
permissions.net.push("esm.sh".to_string());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let checker = Rc::new(RefCell::new(PermissionChecker::from_config(&permissions)));
|
|
|
|
|
let module_loader = WfeModuleLoader::new(checker.clone());
|
|
|
|
|
|
2026-03-25 22:32:07 +00:00
|
|
|
let runtime = JsRuntime::new(RuntimeOptions {
|
|
|
|
|
extensions: vec![ext],
|
2026-03-25 23:02:51 +00:00
|
|
|
module_loader: Some(Rc::new(module_loader)),
|
2026-03-25 22:32:07 +00:00
|
|
|
..Default::default()
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Populate OpState with our workflow types.
|
|
|
|
|
{
|
|
|
|
|
let state = runtime.op_state();
|
|
|
|
|
let mut state = state.borrow_mut();
|
|
|
|
|
state.put(WorkflowInputs {
|
|
|
|
|
data: workflow_data,
|
|
|
|
|
});
|
|
|
|
|
state.put(StepOutputs {
|
|
|
|
|
map: HashMap::new(),
|
|
|
|
|
});
|
|
|
|
|
state.put(StepMeta {
|
|
|
|
|
name: step_name.to_string(),
|
|
|
|
|
});
|
2026-03-25 23:02:51 +00:00
|
|
|
state.put(PermissionChecker::from_config(&permissions));
|
2026-03-25 22:32:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(runtime)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-25 23:02:51 +00:00
|
|
|
/// Returns whether esm.sh would be auto-added for the given config.
|
|
|
|
|
/// Exposed for testing.
|
|
|
|
|
pub fn would_auto_add_esm_sh(config: &DenoConfig) -> bool {
|
|
|
|
|
!config.modules.is_empty() && !config.permissions.net.iter().any(|h| h == "esm.sh")
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-25 22:32:07 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::super::config::DenoPermissions;
|
2026-04-07 18:44:21 +01:00
|
|
|
use super::*;
|
2026-03-25 22:32:07 +00:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn create_runtime_succeeds() {
|
|
|
|
|
let config = DenoConfig {
|
|
|
|
|
script: Some("1+1".to_string()),
|
|
|
|
|
file: None,
|
|
|
|
|
permissions: DenoPermissions::default(),
|
|
|
|
|
modules: vec![],
|
|
|
|
|
env: HashMap::new(),
|
|
|
|
|
timeout_ms: None,
|
|
|
|
|
};
|
|
|
|
|
let runtime = create_runtime(&config, serde_json::json!({}), "test-step");
|
|
|
|
|
assert!(runtime.is_ok());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn create_runtime_has_op_state() {
|
|
|
|
|
let config = DenoConfig {
|
|
|
|
|
script: None,
|
|
|
|
|
file: None,
|
|
|
|
|
permissions: DenoPermissions::default(),
|
|
|
|
|
modules: vec![],
|
|
|
|
|
env: HashMap::new(),
|
|
|
|
|
timeout_ms: None,
|
|
|
|
|
};
|
|
|
|
|
let runtime =
|
|
|
|
|
create_runtime(&config, serde_json::json!({"key": "val"}), "my-step").unwrap();
|
|
|
|
|
let state = runtime.op_state();
|
|
|
|
|
let state = state.borrow();
|
|
|
|
|
let inputs = state.borrow::<WorkflowInputs>();
|
|
|
|
|
assert_eq!(inputs.data, serde_json::json!({"key": "val"}));
|
|
|
|
|
let meta = state.borrow::<StepMeta>();
|
|
|
|
|
assert_eq!(meta.name, "my-step");
|
|
|
|
|
}
|
2026-03-25 23:02:51 +00:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn auto_add_esm_sh_when_modules_declared() {
|
|
|
|
|
let config = DenoConfig {
|
|
|
|
|
script: Some("1".to_string()),
|
|
|
|
|
file: None,
|
|
|
|
|
permissions: DenoPermissions::default(),
|
|
|
|
|
modules: vec!["npm:lodash@4".to_string()],
|
|
|
|
|
env: HashMap::new(),
|
|
|
|
|
timeout_ms: None,
|
|
|
|
|
};
|
|
|
|
|
assert!(would_auto_add_esm_sh(&config));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn no_auto_add_esm_sh_when_no_modules() {
|
|
|
|
|
let config = DenoConfig {
|
|
|
|
|
script: Some("1".to_string()),
|
|
|
|
|
file: None,
|
|
|
|
|
permissions: DenoPermissions::default(),
|
|
|
|
|
modules: vec![],
|
|
|
|
|
env: HashMap::new(),
|
|
|
|
|
timeout_ms: None,
|
|
|
|
|
};
|
|
|
|
|
assert!(!would_auto_add_esm_sh(&config));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn no_auto_add_esm_sh_when_already_present() {
|
|
|
|
|
let config = DenoConfig {
|
|
|
|
|
script: Some("1".to_string()),
|
|
|
|
|
file: None,
|
|
|
|
|
permissions: DenoPermissions {
|
|
|
|
|
net: vec!["esm.sh".to_string()],
|
|
|
|
|
..Default::default()
|
|
|
|
|
},
|
|
|
|
|
modules: vec!["npm:lodash@4".to_string()],
|
|
|
|
|
env: HashMap::new(),
|
|
|
|
|
timeout_ms: None,
|
|
|
|
|
};
|
|
|
|
|
assert!(!would_auto_add_esm_sh(&config));
|
|
|
|
|
}
|
2026-03-25 22:32:07 +00:00
|
|
|
}
|