fix(napi): in "Object::get", return null values wrapped in a "Some" (#1934)

Hi 👋🏻, Alberto from @prisma here.
We are users of the `serde-json` feature in [23fdc5965b/Cargo.toml (L55-L60)).

## The Problem

While investigating a [user-reported Prisma issue](https://github.com/prisma/prisma/issues/22294), we noticed that napi.rs treats `null` values in `Object` like `undefined` ones.

However, `null` and `undefined` are semantically different in JavaScript:
- `undefined` indicates that a value is not set
- `null` indicates that a value has been explicitly set

This PR, which we tested internally, fixes the user's issue on Prisma, and hopefully provides value to the `napi-rs` project as a whole.

## Scenario

Consider [this scenario](dcb8cb9817/query-engine/driver-adapters/src/napi/result.rs (L32-L33)), effectively equivalent to:

```rust
let napi_env: napi::sys::napi_env = /* ... */;

let value: JsUnknown = object.get_named_property("value")?;

// napi.rs implements the `FromNapiValue` trait for `serde_json` if
// the `serde_json` feature is enabled (which we use already).
let json = serde_json::Value::from_napi_value(napi_env, value.raw())?;

Ok(Self::Ok(json.into()));
```

By looking at `napi.rs`' [`serde.rs` source file](https://github.com/napi-rs/napi-rs/blob/napi%402.12.4/crates/napi/src/bindgen_runtime/js_values/serde.rs), we [can see](https://github.com/napi-rs/napi-rs/blob/napi%402.12.4/crates/napi/src/bindgen_runtime/js_values/serde.rs#L104-L108) that "JSON subobjects" (`Map<String, serde_json::Value>`) are populated only if the value returned by `JsObject::get` is not `None`.

This implies that, given `napi-rs`'  [`Object::get` definition](https://github.com/napi-rs/napi-rs/blob/napi%402.12.4/crates/napi/src/bindgen_runtime/js_values/object.rs#L24-L44), `Object::get("key")` returns `None` both when the original object is

```js
{
  // "key" is `undefined`
}
```

and when the object is

```js
{
  "key": null
}
```

It just so happens that `prisma-engines` explicitly set a few `null` values explicitly for the features it offers, but these values are stripped away when we use the [napi-flavoured Prisma Query Engine](https://github.com/prisma/prisma-engines/tree/main/query-engine/query-engine-node-api).

---

I am available for further comments, clarifications, and refactorings. Have a good day!
This commit is contained in:
LongYinan 2024-02-01 22:13:54 +08:00 committed by GitHub
commit f40747aa4c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -35,7 +35,7 @@ impl Object {
let ty = type_of!(self.0.env, ret)?;
Ok(if ty == ValueType::Undefined || ty == ValueType::Null {
Ok(if ty == ValueType::Undefined {
None
} else {
Some(V::from_napi_value(self.0.env, ret)?)