feat(native): upgrade NAPI-RS to 3.0.0 beta (#12652)

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
	- Added a default export for the native binding in the frontend native module, allowing easier imports.

- **Refactor**
	- Streamlined and updated Rust-to-JavaScript type conversions and lifetime handling for improved safety and consistency.
	- Improved object and array construction in Rust modules for more idiomatic usage.
	- Simplified boolean and null value handling in JavaScript interop layers.

- **Chores**
	- Upgraded several dependencies and development tools to newer versions across backend, frontend, and common packages.
	- Updated build scripts for the frontend native package to simplify commands.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Brooooooklyn
2025-05-29 16:09:31 +00:00
parent 2a7f0162cf
commit e1ce42a6fc
12 changed files with 320 additions and 409 deletions
+1 -1
View File
@@ -32,7 +32,7 @@
"build:debug": "napi build"
},
"devDependencies": {
"@napi-rs/cli": "3.0.0-alpha.78",
"@napi-rs/cli": "3.0.0-alpha.81",
"lib0": "^0.2.99",
"tiktoken": "^1.0.17",
"tinybench": "^4.0.0",
+22 -22
View File
@@ -1,8 +1,8 @@
use affine_common::doc_loader::Doc;
use napi::{
anyhow::anyhow,
bindgen_prelude::{AsyncTask, Buffer},
Env, JsObject, Result, Task,
bindgen_prelude::{Array, AsyncTask, Buffer, Object},
Env, Result, Task,
};
pub struct Document {
@@ -14,24 +14,21 @@ impl Document {
self.inner.name.clone()
}
fn chunks(&self, env: Env) -> Result<JsObject> {
let mut array = env.create_array_with_length(self.inner.chunks.len())?;
for (i, chunk) in self.inner.chunks.iter().enumerate() {
let content = crate::utils::clean_content(&chunk.content);
let mut obj = env.create_object()?;
obj.set_named_property("index", i as i64)?;
obj.set_named_property("content", content)?;
array.set_element(i as u32, obj)?;
}
Ok(array)
}
fn resolve(self, env: Env) -> Result<JsObject> {
let mut obj = env.create_object()?;
obj.set_named_property("name", self.name())?;
obj.set_named_property("chunks", self.chunks(env)?)?;
Ok(obj)
fn chunks(&self, env: &Env) -> Result<Array> {
let vec = self
.inner
.chunks
.iter()
.enumerate()
.map(|(i, chunk)| {
let content = crate::utils::clean_content(&chunk.content);
let mut obj = Object::new(env)?;
obj.set("index", i as i64)?;
obj.set("content", content)?;
Ok(obj)
})
.collect::<Result<Vec<Object>>>()?;
Array::from_vec(env, vec)
}
}
@@ -43,7 +40,7 @@ pub struct AsyncParseDocResponse {
#[napi]
impl Task for AsyncParseDocResponse {
type Output = Document;
type JsValue = JsObject;
type JsValue = Object<'static>;
fn compute(&mut self) -> Result<Self::Output> {
let doc = Doc::new(&self.file_path, &self.doc).map_err(|e| anyhow!(e))?;
@@ -51,7 +48,10 @@ impl Task for AsyncParseDocResponse {
}
fn resolve(&mut self, env: Env, doc: Document) -> Result<Self::JsValue> {
doc.resolve(env)
let mut obj = Object::new(&env)?;
obj.set("name", doc.name())?;
obj.set("chunks", doc.chunks(&env)?)?;
Ok(obj)
}
}
+7 -7
View File
@@ -1,7 +1,7 @@
use std::convert::TryFrom;
use affine_common::hashcash::Stamp;
use napi::{bindgen_prelude::AsyncTask, Env, JsBoolean, JsString, Result as NapiResult, Task};
use napi::{bindgen_prelude::AsyncTask, Env, Result as NapiResult, Task};
use napi_derive::napi;
pub struct AsyncVerifyChallengeResponse {
@@ -13,7 +13,7 @@ pub struct AsyncVerifyChallengeResponse {
#[napi]
impl Task for AsyncVerifyChallengeResponse {
type Output = bool;
type JsValue = JsBoolean;
type JsValue = bool;
fn compute(&mut self) -> NapiResult<Self::Output> {
Ok(if let Ok(stamp) = Stamp::try_from(self.response.as_str()) {
@@ -23,8 +23,8 @@ impl Task for AsyncVerifyChallengeResponse {
})
}
fn resolve(&mut self, env: Env, output: bool) -> NapiResult<Self::JsValue> {
env.get_boolean(output)
fn resolve(&mut self, _: Env, output: bool) -> NapiResult<Self::JsValue> {
Ok(output)
}
}
@@ -49,14 +49,14 @@ pub struct AsyncMintChallengeResponse {
#[napi]
impl Task for AsyncMintChallengeResponse {
type Output = String;
type JsValue = JsString;
type JsValue = String;
fn compute(&mut self) -> NapiResult<Self::Output> {
Ok(Stamp::mint(self.resource.clone(), self.bits).format())
}
fn resolve(&mut self, env: Env, output: String) -> NapiResult<Self::JsValue> {
env.create_string(&output)
fn resolve(&mut self, _: Env, output: String) -> NapiResult<Self::JsValue> {
Ok(output)
}
}
+1 -1
View File
@@ -21,7 +21,7 @@
},
"license": "MIT",
"devDependencies": {
"@napi-rs/cli": "3.0.0-alpha.78",
"@napi-rs/cli": "3.0.0-alpha.81",
"@types/node": "^22.14.1",
"@types/prompts": "^2.4.9",
"c8": "^10.1.3",
+6 -6
View File
@@ -1,5 +1,5 @@
use napi::{
bindgen_prelude::{Array as JsArray, Env, Unknown},
bindgen_prelude::{Array as JsArray, Env, JsObjectValue, JsValue, Null, ToNapiValue, Unknown},
ValueType,
};
use y_octo::{Any, Array, Value};
@@ -34,18 +34,18 @@ impl YArray {
}
#[napi(ts_generic_types = "T = unknown", ts_return_type = "T")]
pub fn get(&self, env: Env, index: i64) -> Result<MixedYType> {
pub fn get<'a>(&'a self, env: &'a Env, index: i64) -> Result<MixedYType<'a>> {
if let Some(value) = self.array.get(index as u64) {
match value {
Value::Any(any) => get_js_unknown_from_any(env, any).map(MixedYType::D),
Value::Array(array) => Ok(MixedYType::A(YArray::inner_new(array))),
Value::Map(map) => Ok(MixedYType::B(YMap::inner_new(map))),
Value::Text(text) => Ok(MixedYType::C(YText::inner_new(text))),
_ => env.get_null().map(|v| v.into_unknown()).map(MixedYType::D),
_ => Null.into_unknown(env).map(MixedYType::D),
}
.map_err(anyhow::Error::from)
} else {
Ok(MixedYType::D(env.get_null()?.into_unknown()))
Ok(MixedYType::D(Null.into_unknown(env)?))
}
}
@@ -73,7 +73,7 @@ impl YArray {
.array
.insert(index as u64, Any::Null)
.map_err(anyhow::Error::from),
ValueType::Boolean => match unknown.coerce_to_bool().and_then(|v| v.get_value()) {
ValueType::Boolean => match unsafe { unknown.cast::<bool>() } {
Ok(boolean) => self
.array
.insert(index as u64, boolean)
@@ -140,7 +140,7 @@ impl YArray {
}
#[napi]
pub fn to_json(&self, env: Env) -> Result<JsArray> {
pub fn to_json<'env>(&'env self, env: &'env Env) -> Result<JsArray<'env>> {
let mut js_array = env.create_array(0)?;
for value in self.array.iter() {
js_array.insert(get_js_unknown_from_value(env, value)?)?;
+7 -7
View File
@@ -1,4 +1,4 @@
use napi::bindgen_prelude::{Env, Object, ValueType};
use napi::bindgen_prelude::{Env, JsValue, Null, Object, ToNapiValue, ValueType};
use y_octo::{Any, Map, Value};
use super::*;
@@ -25,18 +25,18 @@ impl YMap {
}
#[napi(ts_generic_types = "T = unknown", ts_return_type = "T")]
pub fn get(&self, env: Env, key: String) -> Result<MixedYType> {
pub fn get<'a>(&'a self, env: &'a Env, key: String) -> Result<MixedYType<'a>> {
if let Some(value) = self.map.get(&key) {
match value {
Value::Any(any) => get_js_unknown_from_any(env, any).map(MixedYType::D),
Value::Array(array) => Ok(MixedYType::A(YArray::inner_new(array))),
Value::Map(map) => Ok(MixedYType::B(YMap::inner_new(map))),
Value::Text(text) => Ok(MixedYType::C(YText::inner_new(text))),
_ => env.get_null().map(|v| v.into_unknown()).map(MixedYType::D),
_ => Null.into_unknown(env).map(MixedYType::D),
}
.map_err(anyhow::Error::from)
} else {
Ok(MixedYType::D(env.get_null()?.into_unknown()))
Ok(MixedYType::D(Null.into_unknown(env)?))
}
}
@@ -63,7 +63,7 @@ impl YMap {
ValueType::Undefined | ValueType::Null => {
self.map.insert(key, Any::Null).map_err(anyhow::Error::from)
}
ValueType::Boolean => match unknown.coerce_to_bool().and_then(|v| v.get_value()) {
ValueType::Boolean => match unsafe { unknown.cast::<bool>() } {
Ok(boolean) => self.map.insert(key, boolean).map_err(anyhow::Error::from),
Err(e) => Err(anyhow::Error::from(e).context("Failed to coerce value to boolean")),
},
@@ -106,9 +106,9 @@ impl YMap {
#[napi]
pub fn to_json(&self, env: Env) -> Result<Object> {
let mut js_object = env.create_object()?;
let mut js_object = Object::new(&env)?;
for (key, value) in self.map.iter() {
js_object.set(key, get_js_unknown_from_value(env, value))?;
js_object.set(key, get_js_unknown_from_value(&env, value))?;
}
Ok(js_object)
}
+30 -36
View File
@@ -1,38 +1,39 @@
use napi::{
bindgen_prelude::{
Either4, Env, Error, External, Object, Result, Status, ToNapiValue, Unknown, ValueType,
},
NapiValue,
use napi::bindgen_prelude::{
Array, Either4, Env, Error, External, JsObjectValue, JsValue, Null, Object, Result, Status,
ToNapiValue, Unknown, ValueType,
};
use y_octo::{AHashMap, Any, HashMapExt, Value};
use super::*;
pub type MixedYType = Either4<YArray, YMap, YText, Unknown>;
pub type MixedRefYType<'a> = Either4<&'a YArray, &'a YMap, &'a YText, Unknown>;
pub type MixedYType<'a> = Either4<YArray, YMap, YText, Unknown<'a>>;
pub type MixedRefYType<'a> = Either4<&'a YArray, &'a YMap, &'a YText, Unknown<'a>>;
pub fn get_js_unknown_from_any(env: Env, any: Any) -> Result<Unknown> {
pub fn get_js_unknown_from_any(env: &Env, any: Any) -> Result<Unknown> {
match any {
Any::Null | Any::Undefined => env.get_null().map(|v| v.into_unknown()),
Any::True => env.get_boolean(true).map(|v| v.into_unknown()),
Any::False => env.get_boolean(false).map(|v| v.into_unknown()),
Any::Integer(number) => env.create_int32(number).map(|v| v.into_unknown()),
Any::BigInt64(number) => env.create_int64(number).map(|v| v.into_unknown()),
Any::Float32(number) => env.create_double(number.0 as f64).map(|v| v.into_unknown()),
Any::Float64(number) => env.create_double(number.0).map(|v| v.into_unknown()),
Any::String(string) => env.create_string(string.as_str()).map(|v| v.into_unknown()),
Any::Null | Any::Undefined => Null.into_unknown(env),
Any::True => true.into_unknown(env),
Any::False => false.into_unknown(env),
Any::Integer(number) => number.into_unknown(env),
Any::BigInt64(number) => number.into_unknown(env),
Any::Float32(number) => number.0.into_unknown(env),
Any::Float64(number) => number.0.into_unknown(env),
Any::String(string) => string.into_unknown(env),
Any::Array(array) => {
let mut js_array = env.create_array_with_length(array.len())?;
for (i, value) in array.into_iter().enumerate() {
js_array.set_element(i as u32, get_js_unknown_from_any(env, value)?)?;
}
Ok(js_array.into_unknown())
let js_array = Array::from_vec(
env,
array
.into_iter()
.map(|value| get_js_unknown_from_any(env, value))
.collect::<Result<Vec<Unknown>>>()?,
)?;
Ok(js_array.to_unknown())
}
_ => env.get_null().map(|v| v.into_unknown()),
_ => Null.into_unknown(env),
}
}
pub fn get_js_unknown_from_value(env: Env, value: Value) -> Result<Unknown> {
pub fn get_js_unknown_from_value(env: &Env, value: Value) -> Result<Unknown> {
match value {
Value::Any(any) => get_js_unknown_from_any(env, any),
Value::Array(array) => {
@@ -49,11 +50,9 @@ pub fn get_js_unknown_from_value(env: Env, value: Value) -> Result<Unknown> {
}
Value::Text(text) => {
let external = External::new(YText::inner_new(text));
Ok(unsafe {
Unknown::from_raw_unchecked(env.raw(), ToNapiValue::to_napi_value(env.raw(), external)?)
})
external.into_unknown(env)
}
_ => env.get_null().map(|v| v.into_unknown()),
_ => Null.into_unknown(env),
}
}
@@ -71,14 +70,14 @@ pub fn get_any_from_js_object(object: Object) -> Result<Any> {
let keys = object.get_property_names()?;
if let Ok(length) = keys.get_array_length() {
for i in 0..length {
if let Ok((obj, key)) = keys.get_element::<Unknown>(i).and_then(|o| {
if let Ok(key) = keys.get_element::<Unknown>(i).and_then(|o| {
o.coerce_to_string().and_then(|obj| {
obj
.into_utf8()
.and_then(|s| s.as_str().map(|s| (obj, s.to_string())))
.and_then(|s| s.as_str().map(|s| s.to_string()))
})
}) {
if let Ok(value) = object.get_property::<_, Unknown>(obj) {
if let Ok(value) = object.get_named_property_unchecked::<Unknown>(&key) {
println!("key: {}", key);
map.insert(key, get_any_from_js_unknown(value)?);
}
@@ -92,12 +91,7 @@ pub fn get_any_from_js_object(object: Object) -> Result<Any> {
pub fn get_any_from_js_unknown(js_unknown: Unknown) -> Result<Any> {
match js_unknown.get_type()? {
ValueType::Undefined | ValueType::Null => Ok(Any::Null),
ValueType::Boolean => Ok(
js_unknown
.coerce_to_bool()
.and_then(|v| v.get_value())?
.into(),
),
ValueType::Boolean => Ok(unsafe { js_unknown.cast::<bool>()? }.into()),
ValueType::Number => Ok(
js_unknown
.coerce_to_number()
+2 -1
View File
@@ -68,7 +68,7 @@ function requireNative() {
try {
nativeBinding = require(process.env.NAPI_RS_NATIVE_LIBRARY_PATH);
} catch (err) {
loadErrors.push(err);
loadErrors.push(err)
}
} else if (process.platform === 'android') {
if (process.arch === 'arm64') {
@@ -374,6 +374,7 @@ if (!nativeBinding) {
throw new Error(`Failed to load native binding`)
}
module.exports = nativeBinding
module.exports.Application = nativeBinding.Application
module.exports.ApplicationListChangedSubscriber = nativeBinding.ApplicationListChangedSubscriber
module.exports.ApplicationStateChangedSubscriber = nativeBinding.ApplicationStateChangedSubscriber
+3 -3
View File
@@ -25,7 +25,7 @@
]
},
"devDependencies": {
"@napi-rs/cli": "3.0.0-alpha.78",
"@napi-rs/cli": "3.0.0-alpha.81",
"@napi-rs/whisper": "^0.0.4",
"@types/node": "^22.0.0",
"ava": "^6.2.0",
@@ -38,8 +38,8 @@
},
"scripts": {
"artifacts": "napi artifacts",
"build": "napi build -p affine_native --platform --release --no-dts-cache",
"build:debug": "napi build -p affine_native --platform --no-dts-cache",
"build": "napi build -p affine_native --platform --release",
"build:debug": "napi build -p affine_native --platform",
"universal": "napi universal",
"test": "ava",
"version": "napi version"