diff --git a/runtime/src/napi_engine/ns_proxy.rs b/runtime/src/napi_engine/ns_proxy.rs index 85e18e9..0389c6b 100644 --- a/runtime/src/napi_engine/ns_proxy.rs +++ b/runtime/src/napi_engine/ns_proxy.rs @@ -648,8 +648,32 @@ fn create_enum_object(env: &Env, declaration: &Decl) -> napi::Result { Ok(obj) } -/// A lazy namespace proxy: `Windows.Data.Json` resolves children through metadata on access. +/// Defines `name` on `obj` as a read-only, non-enumerable, non-configurable data property. +fn define_readonly(env: &Env, obj: &JsObject, name: &str, value: &JsUnknown) -> bool { + let Ok(name) = std::ffi::CString::new(name) else { + return false; + }; + let desc = napi::sys::napi_property_descriptor { + utf8name: name.as_ptr(), + name: std::ptr::null_mut(), + method: None, + getter: None, + setter: None, + value: unsafe { value.raw() }, + attributes: napi::sys::PropertyAttributes::default, + data: std::ptr::null_mut(), + }; + let status = unsafe { napi::sys::napi_define_properties(env.raw(), obj.raw(), 1, &desc) }; + status == napi::sys::Status::napi_ok +} + +/// A lazy namespace: `Windows.Data.Json` resolves children through metadata on first access. +/// The object's prototype is the resolving Proxy; each resolved name becomes a read-only own +/// property, so later reads never reach the Proxy. pub fn create_namespace_proxy(env: &Env, full_name: &str) -> napi::Result { + let ns = env.create_object()?; + define_readonly(env, &ns, "__typeName__", &as_unknown(env, env.create_string(full_name)?)); + let mut handler = env.create_object()?; let ns_name = full_name.to_string(); @@ -659,10 +683,13 @@ pub fn create_namespace_proxy(env: &Env, full_name: &str) -> napi::Result(2) { + define_readonly(env, &receiver, &prop, &value); + } } - resolve_member(env, &format!("{}.{}", get_ns, prop)) + Ok(value) })?; handler.set_named_property("get", get_fn)?; @@ -678,7 +705,9 @@ pub fn create_namespace_proxy(env: &Env, full_name: &str) -> napi::Result