use std::ptr;
use std::ffi::CString;
use std::collections::VecDeque;
use libc::size_t;
use log::error;
use log::debug;
use log::trace;
use crate::sys::napi_env;
use crate::sys::napi_value;
use crate::sys::napi_callback_info;
use crate::sys::napi_callback_raw;
use crate::sys::napi_finalize_raw;
use crate::sys::napi_valuetype;
use crate::sys::napi_get_property;
use crate::sys::napi_has_property;
use crate::sys::napi_ref;
use crate::sys::napi_deferred;
use crate::sys::napi_threadsafe_function_call_js;
use crate::napi_call_result;
use crate::napi_call_assert;
use crate::PropertiesBuilder;
use crate::NjError;
use crate::JSObjectWrapper;
use crate::JSValue;
use crate::TryIntoJs;
fn napi_value_type_to_string(js_type: napi_valuetype) -> &'static str {
match js_type {
crate::sys::napi_valuetype_napi_bigint => "big_int",
crate::sys::napi_valuetype_napi_boolean => "bool",
crate::sys::napi_valuetype_napi_number => "number",
crate::sys::napi_valuetype_napi_string => "string",
crate::sys::napi_valuetype_napi_symbol => "symbol",
crate::sys::napi_valuetype_napi_function => "function",
crate::sys::napi_valuetype_napi_null => "null",
crate::sys::napi_valuetype_napi_external => "external",
crate::sys::napi_valuetype_napi_undefined => "undefined",
_ => "other",
}
}
#[derive(Clone)]
pub struct JsNapiValue(napi_value);
unsafe impl Send for JsNapiValue {}
impl From<napi_value> for JsNapiValue {
fn from(value: napi_value) -> Self {
Self(value)
}
}
#[derive(Clone, Copy, Debug)]
pub struct JsEnv(napi_env);
impl From<napi_env> for JsEnv {
fn from(env: napi_env) -> Self {
Self(env)
}
}
unsafe impl Send for JsEnv {}
unsafe impl Sync for JsEnv {}
impl JsEnv {
pub fn new(env: napi_env) -> Self {
Self(env)
}
pub fn inner(&self) -> napi_env {
self.0
}
pub fn create_string_utf8(&self, r_string: &str) -> Result<napi_value, NjError> {
trace!("create utf8 string: {}", r_string);
use nj_sys::napi_create_string_utf8;
let mut js_value = ptr::null_mut();
napi_call_result!(napi_create_string_utf8(
self.0,
r_string.as_ptr() as *const i8,
r_string.len(),
&mut js_value
))?;
Ok(js_value)
}
pub fn create_string_utf8_from_bytes(&self, r_string: &[u8]) -> Result<napi_value, NjError> {
use nj_sys::napi_create_string_utf8;
let mut js_value = ptr::null_mut();
napi_call_result!(napi_create_string_utf8(
self.0,
r_string.as_ptr() as *const i8,
r_string.len(),
&mut js_value
))?;
Ok(js_value)
}
pub fn create_double(&self, value: f64) -> Result<napi_value, NjError> {
let mut result = ptr::null_mut();
napi_call_result!(crate::sys::napi_create_double(self.0, value, &mut result))?;
Ok(result)
}
pub fn create_int64(&self, value: i64) -> Result<napi_value, NjError> {
let mut result = ptr::null_mut();
napi_call_result!(crate::sys::napi_create_int64(self.0, value, &mut result))?;
Ok(result)
}
pub fn create_int32(&self, value: i32) -> Result<napi_value, NjError> {
let mut result = ptr::null_mut();
napi_call_result!(crate::sys::napi_create_int32(self.0, value, &mut result))?;
Ok(result)
}
pub fn create_bigint_uint64(&self, value: u64) -> Result<napi_value, NjError> {
let mut result = ptr::null_mut();
napi_call_result!(crate::sys::napi_create_bigint_uint64(
self.0,
value,
&mut result
))?;
Ok(result)
}
pub fn create_object(&self) -> Result<napi_value, NjError> {
let mut result = ptr::null_mut();
napi_call_result!(crate::sys::napi_create_object(self.0, &mut result))?;
Ok(result)
}
pub fn create_boolean(&self, value: bool) -> Result<napi_value, NjError> {
let mut result = ptr::null_mut();
napi_call_result!(crate::sys::napi_get_boolean(self.0, value, &mut result,))?;
Ok(result)
}
pub fn create_array_with_len(&self, len: usize) -> Result<napi_value, NjError> {
let mut array = ptr::null_mut();
napi_call_result!(crate::sys::napi_create_array_with_length(
self.0, len, &mut array
))?;
Ok(array)
}
pub fn set_element(
&self,
object: napi_value,
element: napi_value,
index: usize,
) -> Result<(), NjError> {
napi_call_result!(crate::sys::napi_set_element(
self.0,
object,
index as u32,
element
))?;
Ok(())
}
pub fn get_element(&self, array: napi_value, index: u32) -> Result<napi_value, NjError> {
let mut element = ptr::null_mut();
napi_call_result!(crate::sys::napi_get_element(
self.0,
array,
index,
&mut element
))?;
Ok(element)
}
pub fn is_array(&self, array: napi_value) -> Result<bool, NjError> {
let mut result: bool = false;
napi_call_result!(crate::sys::napi_is_array(self.0, array, &mut result))?;
Ok(result)
}
pub fn is_array_buffer(&self, array: napi_value) -> Result<bool, NjError> {
let mut result: bool = false;
napi_call_result!(crate::sys::napi_is_arraybuffer(self.0, array, &mut result))?;
Ok(result)
}
pub fn is_buffer(&self, n_value: napi_value) -> Result<bool, NjError> {
let mut result: bool = false;
napi_call_result!(crate::sys::napi_is_buffer(self.0, n_value, &mut result))?;
Ok(result)
}
pub fn is_date(&self, n_value: napi_value) -> Result<bool, NjError> {
let mut result: bool = false;
napi_call_result!(crate::sys::napi_is_date(self.0, n_value, &mut result))?;
Ok(result)
}
pub fn is_error(&self, n_value: napi_value) -> Result<bool, NjError> {
let mut result: bool = false;
napi_call_result!(crate::sys::napi_is_error(self.0, n_value, &mut result))?;
Ok(result)
}
pub fn get_global(&self) -> Result<napi_value, NjError> {
use nj_sys::napi_get_global;
let mut js_global = ptr::null_mut();
napi_call_result!(napi_get_global(self.0, &mut js_global))?;
Ok(js_global)
}
pub fn call_function(
&self,
recv: napi_value,
func: napi_value,
mut argv: Vec<napi_value>,
) -> Result<napi_value, NjError> {
use nj_sys::napi_call_function;
let mut result = ptr::null_mut();
napi_call_result!(napi_call_function(
self.0,
recv,
func,
argv.len(),
argv.as_mut_ptr(),
&mut result
))?;
Ok(result)
}
pub fn get_cb_info(
&self,
info: napi_callback_info,
max_count: usize,
) -> Result<JsCallback, NjError> {
use nj_sys::napi_get_cb_info;
let mut this = ptr::null_mut();
let mut argc: size_t = max_count as size_t;
let mut args = vec![ptr::null_mut(); max_count];
napi_call_result!(napi_get_cb_info(
self.0,
info,
&mut argc,
args.as_mut_ptr(),
&mut this,
ptr::null_mut()
))?;
args.resize(argc, ptr::null_mut());
Ok(JsCallback::new(JsEnv::new(self.0), this, args))
}
pub fn define_class(
&self,
name: &str,
constructor: napi_callback_raw,
properties: PropertiesBuilder,
) -> Result<napi_value, NjError> {
let mut js_constructor = ptr::null_mut();
let mut raw_properties = properties.as_raw_properties();
debug!(
"defining class: {} with {} properties",
name,
raw_properties.len()
);
napi_call_result!(crate::sys::napi_define_class(
self.0,
name.as_ptr() as *const i8,
name.len(),
Some(constructor),
ptr::null_mut(),
raw_properties.len(),
raw_properties.as_mut_ptr(),
&mut js_constructor
))?;
Ok(js_constructor)
}
pub fn create_reference(&self, cons: napi_value, count: u32) -> Result<napi_ref, NjError> {
let mut result = ptr::null_mut();
napi_call_result!(crate::sys::napi_create_reference(
self.0,
cons,
count,
&mut result
))?;
Ok(result)
}
pub fn delete_reference(&self, ref_: napi_ref) -> Result<(), NjError> {
Ok(napi_call_result!(crate::sys::napi_delete_reference(
self.0, ref_
))?)
}
pub fn get_new_target(&self, info: napi_callback_info) -> Result<napi_value, NjError> {
let mut result = ptr::null_mut();
napi_call_result!(crate::sys::napi_get_new_target(self.0, info, &mut result))?;
Ok(result)
}
pub fn wrap(
&self,
js_object: napi_value,
rust_obj: *mut u8,
finalize: napi_finalize_raw,
) -> Result<napi_ref, NjError> {
let mut result = ptr::null_mut();
napi_call_result!(crate::sys::napi_wrap(
self.0,
js_object,
rust_obj as *mut core::ffi::c_void,
Some(finalize),
ptr::null_mut(),
&mut result
))?;
Ok(result)
}
pub fn unwrap<T>(&self, js_this: napi_value) -> Result<&'static T, NjError> {
let mut result: *mut ::std::os::raw::c_void = ptr::null_mut();
napi_call_result!(crate::sys::napi_unwrap(self.0, js_this, &mut result))?;
Ok(unsafe {
let rust_ref: &T = &mut *(result as *mut T);
rust_ref
})
}
pub fn unwrap_mut<T>(&self, js_this: napi_value) -> Result<&'static mut T, NjError> {
let mut result: *mut ::std::os::raw::c_void = ptr::null_mut();
napi_call_result!(crate::sys::napi_unwrap(self.0, js_this, &mut result))?;
Ok(unsafe {
let rust_ref: &mut T = &mut *(result as *mut T);
rust_ref
})
}
pub fn new_instance(
&self,
constructor: napi_value,
mut args: Vec<napi_value>,
) -> Result<napi_value, NjError> {
trace!("napi new instance: {}", args.len());
let mut result = ptr::null_mut();
napi_call_result!(crate::sys::napi_new_instance(
self.0,
constructor,
args.len(),
args.as_mut_ptr(),
&mut result
))?;
Ok(result)
}
pub fn get_reference_value(&self, obj_ref: napi_ref) -> Result<napi_value, NjError> {
let mut result = ptr::null_mut();
napi_call_result!(crate::sys::napi_get_reference_value(
self.0,
obj_ref,
&mut result
))?;
Ok(result)
}
pub fn create_promise(&self) -> Result<(napi_value, napi_deferred), NjError> {
let mut deferred = ptr::null_mut();
let mut promise = ptr::null_mut();
napi_call_result!(crate::sys::napi_create_promise(
self.0,
&mut deferred,
&mut promise
))?;
Ok((promise, deferred))
}
pub fn resolve_deferred(
&self,
deferred: napi_deferred,
resolution: napi_value,
) -> Result<(), NjError> {
napi_call_result!(crate::sys::napi_resolve_deferred(
self.0, deferred, resolution
))
}
pub fn reject_deferred(
&self,
deferred: napi_deferred,
rejection: napi_value,
) -> Result<(), NjError> {
napi_call_result!(crate::sys::napi_reject_deferred(
self.0, deferred, rejection
))
}
pub fn create_thread_safe_function(
&self,
name: &str,
js_func: Option<napi_value>,
call_js_cb: napi_threadsafe_function_call_js,
) -> Result<crate::ThreadSafeFunction, NjError> {
use crate::sys::napi_create_threadsafe_function;
let work_name = self.create_string_utf8(name)?;
let mut tsfn = ptr::null_mut();
trace!("trying to create threadsafe fn: {}", name);
napi_call_result!(napi_create_threadsafe_function(
self.inner(),
js_func.unwrap_or(ptr::null_mut()),
ptr::null_mut(),
work_name,
0,
1,
ptr::null_mut(),
None,
ptr::null_mut(),
call_js_cb,
&mut tsfn
))?;
trace!("created threadsafe fn: {}", name);
Ok(crate::ThreadSafeFunction::new(self.0, tsfn))
}
pub fn is_exception_pending(&self) -> bool {
let mut pending = false;
napi_call_assert!(crate::sys::napi_is_exception_pending(
self.inner(),
&mut pending
));
pending
}
pub fn throw_type_error(&self, message: &str) {
debug!("throwing type error: {}", message);
if self.is_exception_pending() {
error!(
"there is exception pending when trying to throw {}, ignoring for now",
message
);
return;
}
let c_error_msg = CString::new(message).expect("message should not contain null");
unsafe {
crate::sys::napi_throw_type_error(self.inner(), ptr::null_mut(), c_error_msg.as_ptr())
};
}
pub fn create_error(&self, message: &str) -> Result<napi_value, NjError> {
let mut result = ptr::null_mut();
let err_message = self.create_string_utf8(message)?;
napi_call_result!(crate::sys::napi_create_error(
self.0,
ptr::null_mut(),
err_message,
&mut result
))?;
Ok(result)
}
pub fn value_type(&self, napi_value: napi_value) -> Result<napi_valuetype, NjError> {
use crate::sys::napi_typeof;
let mut valuetype: napi_valuetype = 0;
napi_call_result!(napi_typeof(self.inner(), napi_value, &mut valuetype))?;
Ok(valuetype)
}
pub fn value_type_string(&self, napi_value: napi_value) -> Result<&'static str, NjError> {
Ok(napi_value_type_to_string(self.value_type(napi_value)?))
}
pub fn assert_type(
&self,
napi_value: napi_value,
should_be_type: napi_valuetype,
) -> Result<(), NjError> {
let valuetype = self.value_type(napi_value)?;
if valuetype != should_be_type {
debug!(
"value type is: {}-{} but should be: {}-{}",
napi_value_type_to_string(valuetype),
valuetype,
napi_value_type_to_string(should_be_type),
should_be_type
);
Err(NjError::InvalidType(
napi_value_type_to_string(should_be_type).to_owned(),
napi_value_type_to_string(valuetype).to_owned(),
))
} else {
Ok(())
}
}
pub fn convert_to_rust<'a, T>(&'a self, napi_value: napi_value) -> Result<T, NjError>
where
T: JSValue<'a>,
{
T::convert_to_rust(&self, napi_value)
}
pub fn get_undefined(&self) -> Result<napi_value, NjError> {
let mut result = ptr::null_mut();
napi_call_result!(crate::sys::napi_get_undefined(self.0, &mut result))?;
Ok(result)
}
pub fn get_buffer_info(&self, napi_value: napi_value) -> Result<&[u8], NjError> {
use std::slice;
use crate::sys::napi_get_buffer_info;
let mut len: size_t = 0;
let mut data = ptr::null_mut();
napi_call_result!(napi_get_buffer_info(
self.inner(),
napi_value,
&mut data,
&mut len
))?;
let array: &[u8] = unsafe { slice::from_raw_parts(data as *const u8, len as usize) };
Ok(array)
}
pub fn detach_arraybuffer(&self, napi_value: napi_value) -> Result<(), NjError> {
napi_call_result!(crate::sys::napi_detach_arraybuffer(
self.inner(),
napi_value
))?;
Ok(())
}
pub fn is_detached_arraybuffer(&self, napi_value: napi_value) -> Result<bool, NjError> {
let mut is_detached = false;
napi_call_result!(crate::sys::napi_is_detached_arraybuffer(
self.inner(),
napi_value,
&mut is_detached,
))?;
Ok(is_detached)
}
#[allow(unused_unsafe)]
#[allow(clippy::missing_safety_doc)]
pub unsafe fn add_env_clean_up_hook(
&self,
init_func: Option<unsafe extern "C" fn(arg: *mut ::std::os::raw::c_void)>,
arg: *mut ::std::os::raw::c_void,
) -> Result<(), NjError> {
use crate::sys::napi_add_env_cleanup_hook;
napi_call_result!(napi_add_env_cleanup_hook(self.inner(), init_func, arg))?;
Ok(())
}
}
#[derive(Clone)]
pub struct JsCallback {
env: JsEnv,
this: napi_value,
args: VecDeque<napi_value>,
}
unsafe impl Send for JsCallback {}
unsafe impl Sync for JsCallback {}
impl JsCallback {
pub fn new(env: JsEnv, this: napi_value, args: Vec<napi_value>) -> Self {
Self {
env,
this,
args: args.into(),
}
}
pub fn env(&self) -> &JsEnv {
&self.env
}
pub fn args(&self, index: usize) -> napi_value {
self.args[index]
}
pub fn this(&self) -> napi_value {
self.this
}
pub fn this_owned(self) -> napi_value {
self.this
}
pub fn remove_napi(&mut self) -> Option<napi_value> {
self.args.remove(0)
}
pub fn get_value<'a, T>(&'a mut self) -> Result<T, NjError>
where
T: ExtractFromJs<'a>,
{
trace!("trying extract value out of {} args", self.args.len());
T::extract(self)
}
pub fn get_value_at<'a, T>(&'a self, index: usize) -> Result<T, NjError>
where
T: ExtractArgFromJs<'a>,
{
trace!("trying extract value at: {}", index);
T::convert_arg_at(self, index)
}
pub fn create_thread_safe_function(
&mut self,
name: &str,
call_js_cb: napi_threadsafe_function_call_js,
) -> Result<crate::ThreadSafeFunction, NjError> {
if let Some(n_value) = self.remove_napi() {
self.env
.create_thread_safe_function(name, Some(n_value), call_js_cb)
} else {
Err(NjError::Other("expected js callback".to_owned()))
}
}
pub fn create_thread_safe_function_at(
&self,
name: &str,
index: usize,
call_js_cb: napi_threadsafe_function_call_js,
) -> Result<crate::ThreadSafeFunction, NjError> {
if index < self.args.len() {
self.env
.create_thread_safe_function(name, Some(self.args[index]), call_js_cb)
} else {
Err(NjError::Other(format!(
"expected js callback at: {}",
index
)))
}
}
pub fn unwrap_mut<T>(&self) -> Result<&'static mut T, NjError> {
Ok(self
.env
.unwrap_mut::<JSObjectWrapper<T>>(self.this())?
.mut_inner())
}
pub fn unwrap<T>(&self) -> Result<&'static T, NjError> {
Ok(self.env.unwrap::<JSObjectWrapper<T>>(self.this())?.inner())
}
}
pub trait ExtractFromJs<'a>: Sized {
fn label() -> &'static str {
std::any::type_name::<Self>()
}
fn extract(js_cb: &'a mut JsCallback) -> Result<Self, NjError>;
}
impl<'a, T: ?Sized> ExtractFromJs<'a> for T
where
T: JSValue<'a>,
{
fn label() -> &'static str {
T::label()
}
fn extract(js_cb: &'a mut JsCallback) -> Result<Self, NjError> {
if let Some(n_value) = js_cb.remove_napi() {
T::convert_to_rust(js_cb.env(), n_value)
} else {
Err(NjError::Other(format!(
"expected argument of type: {}",
Self::label()
)))
}
}
}
impl<'a, T: Sized> ExtractFromJs<'a> for Option<T>
where
T: JSValue<'a>,
{
fn label() -> &'static str {
T::label()
}
fn extract(js_cb: &'a mut JsCallback) -> Result<Self, NjError> {
if let Some(n_value) = js_cb.remove_napi() {
Ok(Some(T::convert_to_rust(js_cb.env(), n_value)?))
} else {
Ok(None)
}
}
}
impl ExtractFromJs<'_> for JsEnv {
fn extract(js_cb: &mut JsCallback) -> Result<Self, NjError> {
Ok(*js_cb.env())
}
}
pub trait ExtractArgFromJs<'a>: Sized {
fn label() -> &'static str {
std::any::type_name::<Self>()
}
fn convert_arg_at(js_cb: &'a JsCallback, index: usize) -> Result<Self, NjError>;
}
impl<'a, T: ?Sized> ExtractArgFromJs<'a> for T
where
T: JSValue<'a>,
{
fn label() -> &'static str {
T::label()
}
fn convert_arg_at(js_cb: &'a JsCallback, index: usize) -> Result<Self, NjError> {
if index < js_cb.args.len() {
T::convert_to_rust(js_cb.env(), js_cb.args[index])
} else {
Err(NjError::Other(format!(
"expected argument of type: {} at: {}",
Self::label(),
index
)))
}
}
}
impl<'a, T: Sized> ExtractArgFromJs<'a> for Option<T>
where
T: JSValue<'a>,
{
fn label() -> &'static str {
T::label()
}
fn convert_arg_at(js_cb: &'a JsCallback, index: usize) -> Result<Self, NjError> {
if index < js_cb.args.len() {
Ok(Some(T::convert_to_rust(js_cb.env(), js_cb.args[index])?))
} else {
Ok(None)
}
}
}
impl ExtractArgFromJs<'_> for JsEnv {
fn convert_arg_at(js_cb: &JsCallback, _index: usize) -> Result<Self, NjError> {
Ok(*js_cb.env())
}
}
pub struct JsExports {
inner: napi_value,
env: JsEnv,
}
impl JsExports {
pub fn new(env: napi_env, exports: napi_value) -> Self {
Self {
inner: exports,
env: JsEnv::new(env),
}
}
pub fn env(&self) -> &JsEnv {
&self.env
}
pub fn prop_builder(&self) -> PropertiesBuilder {
PropertiesBuilder::new()
}
pub fn define_property(&self, properties: PropertiesBuilder) -> Result<(), NjError> {
let mut raw_properties = properties.as_raw_properties();
napi_call_result!(crate::sys::napi_define_properties(
self.env.inner(),
self.inner,
raw_properties.len(),
raw_properties.as_mut_ptr()
))
}
pub fn set_name_property(&self, name: &str, js_class: napi_value) -> Result<(), NjError> {
let c_name = CString::new(name).expect("should work");
napi_call_result!(crate::sys::napi_set_named_property(
self.env.inner(),
self.inner,
c_name.as_ptr(),
js_class
))
}
}
pub struct JsCallbackFunction {
ctx: napi_value,
js_func: napi_value,
env: JsEnv,
}
unsafe impl Send for JsCallbackFunction {}
unsafe impl Sync for JsCallbackFunction {}
impl JSValue<'_> for JsCallbackFunction {
fn label() -> &'static str {
"callback"
}
fn convert_to_rust(env: &JsEnv, js_value: napi_value) -> Result<Self, NjError> {
env.assert_type(js_value, crate::sys::napi_valuetype_napi_function)?;
let ctx = env.get_global()?;
Ok(Self {
ctx,
js_func: js_value,
env: *env,
})
}
}
impl JsCallbackFunction {
pub fn call<T>(&self, rust_argv: Vec<T>) -> Result<napi_value, NjError>
where
T: TryIntoJs,
{
trace!("invoking normal js callback");
let env = &self.env;
let mut argv: Vec<napi_value> = vec![];
for rust_arg in rust_argv {
match rust_arg.try_to_js(env) {
Ok(js_value) => argv.push(js_value),
Err(err) => return Err(err),
}
}
self.env.call_function(self.ctx, self.js_func, argv)
}
}
pub struct JsObject {
env: JsEnv,
napi_value: napi_value,
}
unsafe impl Send for JsObject {}
impl JsObject {
pub fn new(env: JsEnv, napi_value: napi_value) -> Self {
Self { env, napi_value }
}
pub fn create(env: &JsEnv) -> Result<Self, NjError> {
Ok(Self::new(*env, env.create_object()?))
}
pub fn env(&self) -> &JsEnv {
&self.env
}
pub fn napi_value(&self) -> napi_value {
self.napi_value
}
pub fn get_property(&self, key: &str) -> Result<Option<Self>, NjError> {
let property_key = self.env.create_string_utf8(key)?;
let mut exist: bool = false;
napi_call_result!(napi_has_property(
self.env.inner(),
self.napi_value,
property_key,
&mut exist,
))?;
if !exist {
return Ok(None);
}
let mut property_value = ptr::null_mut();
napi_call_result!(napi_get_property(
self.env.inner(),
self.napi_value,
property_key,
&mut property_value,
))?;
Ok(Some(Self {
env: self.env,
napi_value: property_value,
}))
}
pub fn set_property(&mut self, key: &str, property_value: napi_value) -> Result<(), NjError> {
use crate::sys::napi_set_property;
let property_key = self.env.create_string_utf8(key)?;
napi_call_result!(napi_set_property(
self.env.inner(),
self.napi_value,
property_key,
property_value,
))?;
Ok(())
}
pub fn as_value<'a, T>(&'a self) -> Result<T, NjError>
where
T: JSValue<'a>,
{
self.env.convert_to_rust(self.napi_value)
}
}
impl JSValue<'_> for JsObject {
fn convert_to_rust(env: &JsEnv, js_value: napi_value) -> Result<Self, NjError> {
env.assert_type(js_value, crate::sys::napi_valuetype_napi_object)?;
Ok(Self::new(*env, js_value))
}
}
impl TryIntoJs for JsObject {
fn try_to_js(self, _js_env: &JsEnv) -> Result<napi_value, NjError> {
Ok(self.napi_value)
}
}