added more support for supabase-js
This commit is contained in:
189
functions/src/deno_runtime.rs
Normal file
189
functions/src/deno_runtime.rs
Normal file
@@ -0,0 +1,189 @@
|
||||
use anyhow::Result;
|
||||
use deno_core::{JsRuntime, RuntimeOptions, v8};
|
||||
use serde_json::Value;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct DenoRuntime {
|
||||
// We create a new runtime for each execution to ensure isolation
|
||||
// In a production environment, we might want to pool runtimes or use isolates more efficiently
|
||||
}
|
||||
|
||||
impl DenoRuntime {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
pub async fn execute(&self, code: String, payload: Option<Value>, headers: HashMap<String, String>) -> Result<(String, String, u16, HashMap<String, String>)> {
|
||||
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
let rt = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
rt.block_on(async {
|
||||
let result = Self::execute_inner(code, payload, headers).await;
|
||||
let _ = tx.send(result);
|
||||
});
|
||||
});
|
||||
|
||||
rx.await.map_err(|_| anyhow::anyhow!("Deno execution thread panicked"))?
|
||||
}
|
||||
|
||||
async fn execute_inner(code: String, payload: Option<Value>, headers: HashMap<String, String>) -> Result<(String, String, u16, HashMap<String, String>)> {
|
||||
// Initialize JS Runtime
|
||||
let mut runtime = JsRuntime::new(RuntimeOptions::default());
|
||||
|
||||
// 1. Inject Preamble (Polyfills for Deno.serve, Request, Response, Headers)
|
||||
let preamble = r#"
|
||||
globalThis.console = {
|
||||
log: (...args) => {
|
||||
Deno.core.print(args.map(a => String(a)).join(" ") + "\n");
|
||||
},
|
||||
error: (...args) => {
|
||||
Deno.core.print("[ERROR] " + args.map(a => String(a)).join(" ") + "\n", true);
|
||||
}
|
||||
};
|
||||
|
||||
class Headers {
|
||||
constructor(init) {
|
||||
this.map = new Map();
|
||||
if (init) {
|
||||
if (init instanceof Headers) {
|
||||
init.forEach((v, k) => this.map.set(k.toLowerCase(), v));
|
||||
} else if (Array.isArray(init)) {
|
||||
init.forEach(([k, v]) => this.map.set(k.toLowerCase(), v));
|
||||
} else {
|
||||
Object.entries(init).forEach(([k, v]) => this.map.set(k.toLowerCase(), v));
|
||||
}
|
||||
}
|
||||
}
|
||||
get(key) { return this.map.get(key.toLowerCase()) || null; }
|
||||
set(key, value) { this.map.set(key.toLowerCase(), value); }
|
||||
has(key) { return this.map.has(key.toLowerCase()); }
|
||||
forEach(callback) { this.map.forEach(callback); }
|
||||
entries() { return this.map.entries(); }
|
||||
}
|
||||
globalThis.Headers = Headers;
|
||||
|
||||
globalThis.Deno = {
|
||||
serve: (handler) => {
|
||||
globalThis._handler = handler;
|
||||
},
|
||||
core: Deno.core,
|
||||
env: {
|
||||
get: (key) => {
|
||||
return globalThis._env ? globalThis._env[key] : null;
|
||||
},
|
||||
toObject: () => {
|
||||
return globalThis._env || {};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class Response {
|
||||
constructor(body, init) {
|
||||
this.body = body;
|
||||
this.status = init?.status || 200;
|
||||
this.headers = new Headers(init?.headers);
|
||||
}
|
||||
async text() { return String(this.body); }
|
||||
async json() { return JSON.parse(this.body); }
|
||||
}
|
||||
globalThis.Response = Response;
|
||||
|
||||
class Request {
|
||||
constructor(url, init) {
|
||||
this.url = url;
|
||||
this.method = init?.method || "GET";
|
||||
this._body = init?.body;
|
||||
this.headers = new Headers(init?.headers);
|
||||
}
|
||||
async json() { return typeof this._body === 'string' ? JSON.parse(this._body) : this._body; }
|
||||
async text() { return typeof this._body === 'string' ? this._body : JSON.stringify(this._body); }
|
||||
}
|
||||
globalThis.Request = Request;
|
||||
"#;
|
||||
|
||||
runtime.execute_script("<preamble>", preamble.to_string())?;
|
||||
|
||||
// 2. Execute User Code
|
||||
runtime.execute_script("<user_script>", code.to_string())?;
|
||||
|
||||
// 3. Invoke Handler
|
||||
let payload_json = serde_json::to_string(&payload.unwrap_or(serde_json::json!({})))?;
|
||||
let headers_json = serde_json::to_string(&headers)?;
|
||||
|
||||
let invoke_script = format!(r#"
|
||||
(async () => {{
|
||||
if (!globalThis._handler) {{
|
||||
return {{ error: "No handler registered via Deno.serve" }};
|
||||
}}
|
||||
try {{
|
||||
const headers = {1};
|
||||
const req = new Request("http://localhost", {{
|
||||
method: "POST",
|
||||
body: {0},
|
||||
headers: headers
|
||||
}});
|
||||
const res = await globalThis._handler(req);
|
||||
const text = await res.text();
|
||||
|
||||
// Convert Headers to plain object for return
|
||||
const resHeaders = {{}};
|
||||
if (res.headers && typeof res.headers.forEach === 'function') {{
|
||||
res.headers.forEach((v, k) => resHeaders[k] = v);
|
||||
}}
|
||||
|
||||
return {{
|
||||
result: text,
|
||||
headers: resHeaders,
|
||||
status: res.status
|
||||
}};
|
||||
}} catch (e) {{
|
||||
return {{ error: String(e) }};
|
||||
}}
|
||||
}})()
|
||||
"#, payload_json, headers_json);
|
||||
|
||||
let result_val = runtime.execute_script("<invocation>", invoke_script)?;
|
||||
let result = runtime.resolve_value(result_val).await?;
|
||||
|
||||
let scope = &mut runtime.handle_scope();
|
||||
let local = v8::Local::new(scope, result);
|
||||
let deserialized_value: Value = deno_core::serde_v8::from_v8(scope, local)?;
|
||||
|
||||
let stdout = if let Some(res) = deserialized_value.get("result") {
|
||||
res.as_str().unwrap_or("").to_string()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
let stderr = if let Some(err) = deserialized_value.get("error") {
|
||||
err.as_str().unwrap_or("Unknown error").to_string()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
let status = if let Some(s) = deserialized_value.get("status") {
|
||||
s.as_u64().unwrap_or(200) as u16
|
||||
} else {
|
||||
200
|
||||
};
|
||||
|
||||
let mut headers = HashMap::new();
|
||||
if let Some(h) = deserialized_value.get("headers") {
|
||||
if let Some(obj) = h.as_object() {
|
||||
for (k, v) in obj {
|
||||
if let Some(s) = v.as_str() {
|
||||
headers.insert(k.clone(), s.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok((stdout, stderr, status, headers))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user