use boa_engine::{
Context, JsNativeError, JsResult, JsString, JsValue, NativeFunction, js_string,
object::{ObjectInitializer, builtins::JsPromise},
property::Attribute,
};
use crate::{resources, tdom::node::document_base_href};
use super::{js_fn, with_state};
pub(super) fn native_fetch(
_this: &JsValue,
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let url = args
.first()
.map(|v| v.to_string(context))
.transpose()?
.unwrap_or_default();
let url = url.to_std_string_escaped();
if url.is_empty() {
return Err(JsNativeError::typ()
.with_message("fetch expects a non-empty URL")
.into());
}
let Some(rc) = with_state() else {
return Ok(JsValue::undefined());
};
let (promise, resolvers) = JsPromise::new_pending(context);
let (id, tx) = {
let mut st = rc.borrow_mut();
let id = st.next_fetch_id;
st.next_fetch_id += 1;
st.active_fetches.insert(id);
(id, st.fetch_tx.clone())
};
let global = context.global_object();
let registry = global
.get(js_string!("__htuiFetchResolvers"), context)?
.to_object(context)?;
let mut entry = ObjectInitializer::new(context);
entry.property(
js_string!("resolve"),
JsValue::from(resolvers.resolve.clone()),
Attribute::all(),
);
entry.property(
js_string!("reject"),
JsValue::from(resolvers.reject.clone()),
Attribute::all(),
);
registry.set(js_string!(id.to_string()), entry.build(), false, context)?;
let resolved = {
let st = rc.borrow();
let base = document_base_href(&st.arena, st.root_id);
resources::resolve_url(base.as_deref(), &url)
};
resources::load_async(id, resolved, tx);
Ok(promise.into())
}
pub(crate) fn response_object(
context: &mut Context,
url: String,
bytes: std::sync::Arc<Vec<u8>>,
) -> JsResult<JsValue> {
let text = js_string!(String::from_utf8_lossy(bytes.as_ref()).into_owned());
let text_native =
|_this: &JsValue, _args: &[JsValue], captured: &JsString, ctx: &mut Context| {
let (promise, resolvers) = JsPromise::new_pending(ctx);
resolvers.resolve.call(
&JsValue::undefined(),
&[JsValue::from(captured.clone())],
ctx,
)?;
Ok(promise.into())
};
let text_fn = js_fn(
context,
NativeFunction::from_copy_closure_with_captures(text_native, text),
);
let mut init = ObjectInitializer::new(context);
init.property(js_string!("status"), JsValue::from(200), Attribute::all());
init.property(js_string!("ok"), JsValue::from(true), Attribute::all());
init.property(
js_string!("url"),
JsValue::from(js_string!(url)),
Attribute::all(),
);
init.property(js_string!("text"), text_fn, Attribute::all());
Ok(init.build().into())
}