2: ```rust 4: 2: use deno_core::{JsRuntime, v8}; 5: 3: use serde_json::Value; 6: 4: 7: 5: use std::collections::HashMap; 8: 6: use std::fs; 9: 7: 10: 8: pub struct DenoRuntime { 11: 9: // We create a new runtime for each execution to ensure isolation 12: 10: // In a production environment, we might want to pool runtimes or use isolates more efficiently 13: 11: } 14: 12: 15: 13: impl DenoRuntime { 16: 14: pub fn new() -> Self { 17: 15: Self {} 18: 16: } 19: 17: 20: 18: pub async fn execute(&self, code: String, payload: Option, headers: HashMap) -> Result<(String, String, u16, HashMap)> { 21: 19: let (tx, rx) = tokio::sync::oneshot::channel(); 22: 20: 23: 21: std::thread::spawn(move || { 24: 22: let rt = tokio::runtime::Builder::new_current_thread() 25: 23: .enable_all() 26: 24: .build() 27: 25: .unwrap(); 28: 26: 29: 27: let local = tokio::task::LocalSet::new(); 30: 28: let result = local.block_on(&rt, async { Self::execute_inner(code, payload, headers).await }); 31: 29: let _ = tx.send(result); 32: 30: }); 33: 31: 34: 32: tokio::time::timeout(std::time::Duration::from_secs(30), rx) 35: 33: .await 36: 34: .map_err(|_| anyhow::anyhow!("Deno execution timed out after 30s"))? 37: 35: .map_err(|_| anyhow::anyhow!("Deno execution thread panicked"))? 38: 36: } 39: 37: 40: 38: async fn execute_inner(code: String, payload: Option, headers: HashMap) -> Result<(String, String, u16, HashMap)> { 41: 39: // Initialize JS Runtime with module support 42: 40: let mut runtime = JsRuntime::new(deno_core::RuntimeOptions { 43: 41: module_loader: Some(std::rc::Rc::new(deno_core::FsModuleLoader)), 44: 42: ..Default::default() 45: 43: }); 46: 44: 47: 45: // 1. Inject Preamble (Polyfills for Deno.serve, Request, Response, Headers) 48: 46: let preamble = r#" 49: 47: globalThis.console = { 50: 48: log: (...args) => { 51: 49: Deno.core.print(args.map(a => String(a)).join(" ") + "\n"); 52: 50: }, 53: 51: error: (...args) => { 54: 52: Deno.core.print("[ERROR] " + args.map(a => String(a)).join(" ") + "\n", true); 55: 53: } 56: 54: }; 57: 55: 58: 56: class Headers { 59: 57: constructor(init) { 60: 58: this.map = new Map(); 61: 59: if (init) { 62: 60: if (init instanceof Headers) { 63: 61: init.forEach((v, k) => this.map.set(k.toLowerCase(), v)); 64: 62: } else if (Array.isArray(init)) { 65: 63: init.forEach(([k, v]) => this.map.set(k.toLowerCase(), v)); 66: 64: } else { 67: 65: Object.entries(init).forEach(([k, v]) => this.map.set(k.toLowerCase(), v)); 68: 66: } 69: 67: } 70: 68: } 71: 69: get(key) { return this.map.get(key.toLowerCase()) || null; } 72: 70: set(key, value) { this.map.set(key.toLowerCase(), value); } 73: 71: has(key) { return this.map.has(key.toLowerCase()); } 74: 72: forEach(callback) { this.map.forEach(callback); } 75: 73: entries() { return this.map.entries(); } 76: 74: } 77: 75: globalThis.Headers = Headers; 78: 76: 79: 77: globalThis.Deno = { 80: 78: serve: (handler) => { 81: 79: globalThis._handler = handler; 82: 80: }, 83: 81: core: Deno.core, 84: 82: env: { 85: 83: get: (key) => { 86: 84: return globalThis._env ? globalThis._env[key] : null; 87: 85: }, 88: 86: toObject: () => { 89: 87: return globalThis._env || {}; 90: 88: } 91: 89: } 92: 90: }; 93: 91: 94: 92: class Response { 95: 93: constructor(body, init) { 96: 94: this.body = body; 97: 95: this.status = init?.status || 200; 98: 96: this.headers = new Headers(init?.headers); 99: 97: } 100: 98: async text() { return String(this.body); } 101: 99: async json() { return JSON.parse(this.body); } 102: 100: } 103: 101: globalThis.Response = Response; 104: 102: 105: 103: class Request { 106: 104: constructor(url, init) { 107: 105: this.url = url; 108: 106: this.method = init?.method || "GET"; 109: 107: this._body = init?.body; 110: 108: this.headers = new Headers(init?.headers); 111: 109: } 112: 110: async json() { return typeof this._body === 'string' ? JSON.parse(this._body) : this._body; } 113: 111: async text() { return typeof this._body === 'string' ? this._body : JSON.stringify(this._body); } 114: 112: } 115: 113: globalThis.Request = Request; 116: 114: "#; 117: 115: 118: 116: tracing::info!("DenoRuntime: executing preamble"); 119: 117: runtime.execute_script("", preamble.to_string())?; 120: 118: 121: 119: let payload_json = serde_json::to_string(&payload.unwrap_or(serde_json::json!({})))?; 122: 120: let headers_json = serde_json::to_string(&headers)?; 123: 121: 124: 122: let module_code = format!(r#" 125: 123: // User script 126: 124: {code} 127: 125: 128: 126: // Invocation logic 129: 127: async function invoke() {{ 130: 128: if (!globalThis._handler) {{ 131: 129: return {{ error: "No handler registered via Deno.serve" }}; 132: 130: }} 133: 131: try {{ 134: 132: const req = new Request("http://localhost", {{ 135: 133: method: "POST", 136: 134: body: {payload_json}, 137: 135: headers: {headers_json} 138: 136: }}); 139: 137: const res = await globalThis._handler(req); 140: 138: const text = await res.text(); 141: 139: 142: 140: const resHeaders = {{}}; 143: 141: if (res.headers && typeof res.headers.forEach === 'function') {{ 144: 142: res.headers.forEach((v, k) => resHeaders[k] = v); 145: 143: }} 146: 144: 147: 145: return {{ 148: 146: result: text, 149: 147: headers: resHeaders, 150: 148: status: res.status 151: 149: }}; 152: 150: }} catch (e) {{ 153: 151: return {{ error: String(e) }}; 154: 152: }} 155: 153: }} 156: 154: 157: 155: globalThis._result = await invoke(); 158: 156: "#); 159: 157: 160: 158: let temp_path = format!("/tmp/deno_main_{}.js", uuid::Uuid::new_v4()); 161: 159: fs::write(&temp_path, module_code)?; 162: 160: 163: 161: let specifier = deno_core::resolve_url(&format!("file://{}", temp_path))?; 164: 162: 165: 163: tracing::info!("DenoRuntime: loading main module from {}", temp_path); 166: 164: let mod_id = runtime.load_main_es_module(&specifier).await?; 167: 165: 168: 166: tracing::info!("DenoRuntime: evaluating module"); 169: 167: let receiver = runtime.mod_evaluate(mod_id); 170: 168: 171: 169: // Wait for module execution to finish and drain event loop 172: 170: runtime.run_event_loop(deno_core::PollEventLoopOptions::default()).await?; 173: 171: receiver.await?; 174: 172: tracing::info!("DenoRuntime: module evaluated"); 175: 173: 176: 174: // Clean up temp file 177: 175: let _ = fs::remove_file(&temp_path); 178: 176: 179: 177: // Extract result 180: 178: let result_val = runtime.execute_script("", "globalThis._result".to_string())?; 181: 179: let scope = &mut runtime.handle_scope(); 182: 180: let local = v8::Local::new(scope, result_val); 183: 181: let deserialized_value: Value = deno_core::serde_v8::from_v8(scope, local)?; 184: 182: 185: 183: let stdout = if let Some(res) = deserialized_value.get("result") { 186: 184: res.as_str().unwrap_or("").to_string() 187: 185: } else { 188: 186: String::new() 189: 187: }; 190: 188: 191: 189: let stderr = if let Some(err) = deserialized_value.get("error") { 192: 190: err.as_str().unwrap_or("Unknown error").to_string() 193: 191: } else { 194: 192: String::new() 195: 193: }; 196: 194: 197: 195: let status = if let Some(s) = deserialized_value.get("status") { 198: 196: s.as_u64().unwrap_or(200) as u16 199: 197: } else { 200: 198: 200 201: 199: }; 202: 200: 203: 201: let mut headers = HashMap::new(); 204: 202: if let Some(h) = deserialized_value.get("headers") { 205: 203: if let Some(obj) = h.as_object() { 206: 204: for (k, v) in obj { 207: 205: if let Some(s) = v.as_str() { 208: 206: headers.insert(k.clone(), s.to_string()); 209: 207: } 210: 208: } 211: 209: } 212: 210: } 213: 211: 214: 212: Ok((stdout, stderr, status, headers)) 215: 213: } 216: 214: } 217: 215: 218: 216: #[cfg(test)] 219: 217: mod tests { 220: 218: use super::*; 221: 219: use std::collections::HashMap; 222: 220: 223: 221: #[tokio::test] 224: 222: async fn test_deno_runtime_simple_execution() { 225: 223: let runtime = DenoRuntime::new(); 226: 224: let code = r#" 227: 225: Deno.serve((req) => { 228: 226: return new Response("Hello from MadBase"); 229: 227: }); 230: 228: "#; 231: 229: 232: 230: let (stdout, stderr, status, _headers) = runtime.execute(code.to_string(), None, HashMap::new()) 233: 231: .await 234: 232: .expect("Execution failed"); 235: 233: 236: 234: assert_eq!(stdout, "Hello from MadBase"); 237: 235: assert_eq!(stderr, ""); 238: 236: assert_eq!(status, 200); 239: 237: } 240: 238: 241: 239: #[tokio::test] 242: 240: async fn test_deno_runtime_async_promise() { 243: 241: let runtime = DenoRuntime::new(); 244: 242: let code = r#" 245: 243: Deno.serve(async (req) => { 246: 244: await Promise.resolve(); 247: 245: return new Response("Promise OK"); 248: 246: }); 249: 247: "#; 250: 248: 251: 249: let (stdout, _stderr, status, _) = runtime.execute(code.to_string(), None, HashMap::new()) 252: 250: .await 253: 251: .expect("Execution failed"); 254: 252: 255: 253: assert_eq!(stdout, "Promise OK"); 256: 254: assert_eq!(status, 200); 257: 255: } 258: 256: 259: 257: #[tokio::test] 260: 258: async fn test_deno_runtime_error_handling() { 261: 259: let runtime = DenoRuntime::new(); 262: 260: let code = r#" 263: 261: Deno.serve((req) => { 264: 262: throw new Error("Custom Error"); 265: 263: }); 266: 264: "#; 267: 265: 268: 266: let (stdout, stderr, _status, _) = runtime.execute(code.to_string(), None, HashMap::new()) 269: 267: .await 270: 268: .expect("Execution failed"); 271: 269: 272: 270: assert_eq!(stdout, ""); 273: 271: assert!(stderr.contains("Custom Error")); 274: 272: } 275: 273: } 276: ```