Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bindings/nodejs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ crate-type = ["cdylib"]
[dependencies]
ahash = { workspace = true }
napi = { version = "3", features = ["serde-json", "error_anyhow", "tokio_rt"] }
napi-sys = "3"
napi-derive = "3"
tokio-util = { workspace = true, features = ["rt"] }
serde_json = { workspace = true }
Expand Down
7 changes: 4 additions & 3 deletions bindings/nodejs/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
export declare class ZenDecision {
constructor()
evaluate(context: any, opts?: ZenEvaluateOptions | undefined | null): Promise<ZenEngineResponse>
safeEvaluate(context: any, opts?: ZenEvaluateOptions | undefined | null): Promise<SafeResult<ZenEngineResponse>>
safeEvaluate(context: any, opts?: ZenEvaluateOptions | undefined | null): { success: true, data: ZenEngineResponse } | { success: false; error: any; }
validate(): void
}

Expand All @@ -17,8 +17,9 @@ export declare class ZenEngine {
evaluate(key: string, context: any, opts?: ZenEvaluateOptions | undefined | null): Promise<ZenEngineResponse>
createDecision(content: ZenDecisionContent | Buffer | object): ZenDecision
getDecision(key: string): Promise<ZenDecision>
safeEvaluate(key: string, context: any, opts?: ZenEvaluateOptions | undefined | null): Promise<SafeResult<ZenEngineResponse>>
safeGetDecision(key: string): Promise<SafeResult<ZenDecision>>
safeEvaluate(key: string, context: any, opts?: ZenEvaluateOptions | undefined | null): { success: true, data: ZenEngineResponse } | { success: false; error: any; }
safeGetDecision(key: string): { success: true, data: ZenDecision } | { success: false; error: any; }
dispose(): void
}

export declare class ZenEngineHandlerRequest {
Expand Down
2 changes: 1 addition & 1 deletion bindings/nodejs/src/custom_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use zen_engine::nodes::custom::{CustomNodeAdapter, CustomNodeRequest};
use zen_engine::nodes::{NodeError, NodeResponse, NodeResult};
use zen_engine::Variable;

type CustomNodeTsfn = Arc<
pub(crate) type CustomNodeTsfn = Arc<
ThreadsafeFunction<
ZenEngineHandlerRequest,
Promise<ZenEngineHandlerResponse>,
Expand Down
4 changes: 3 additions & 1 deletion bindings/nodejs/src/decision.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ impl ZenDecision {
Ok(result)
}

#[napi(ts_return_type = "Promise<SafeResult<ZenEngineResponse>>")]
#[napi(
ts_return_type = "{ success: true, data: ZenEngineResponse } | { success: false; error: any; }"
)]
pub async fn safe_evaluate(
&self,
context: Value,
Expand Down
24 changes: 24 additions & 0 deletions bindings/nodejs/src/dispose.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use napi::check_status;
use napi::threadsafe_function::ThreadsafeFunctionHandle;

pub trait DisposeThreadsafeHandler {
fn dispose(&self) -> napi::Result<()>;
}

impl DisposeThreadsafeHandler for ThreadsafeFunctionHandle {
fn dispose(&self) -> napi::Result<()> {
self.with_write_aborted(|mut aborted_guard| {
if !*aborted_guard {
check_status!(unsafe {
napi_sys::napi_release_threadsafe_function(
self.get_raw(),
napi_sys::ThreadsafeFunctionReleaseMode::abort,
)
})?;
*aborted_guard = true;
}

Ok(())
})
}
}
65 changes: 54 additions & 11 deletions bindings/nodejs/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,27 @@ use napi::{Env, JsValue, Unknown, ValueType};
use napi_derive::napi;
use serde_json::Value;

use zen_engine::model::DecisionContent;
use zen_engine::{DecisionEngine, EvaluationSerializedOptions, EvaluationTraceKind};

use crate::content::ZenDecisionContent;
use crate::custom_node::CustomNode;
use crate::custom_node::{CustomNode, CustomNodeTsfn};
use crate::decision::ZenDecision;
use crate::http_handler::{NodeHttpHandler, ZenHttpHandlerRequest, ZenHttpHandlerResponse};
use crate::loader::DecisionLoader;
use crate::dispose::DisposeThreadsafeHandler;
use crate::http_handler::{
HttpHandlerTsfn, NodeHttpHandler, ZenHttpHandlerRequest, ZenHttpHandlerResponse,
};
use crate::loader::{DecisionLoader, LoaderTsfn};
use crate::mt::spawn_worker;
use crate::safe_result::SafeResult;
use crate::types::{ZenEngineHandlerRequest, ZenEngineHandlerResponse};
use zen_engine::model::DecisionContent;
use zen_engine::{DecisionEngine, EvaluationSerializedOptions, EvaluationTraceKind};

#[napi]
pub struct ZenEngine {
graph: Arc<DecisionEngine>,

custom_node_tsfn: Option<CustomNodeTsfn>,
loader_tsfn: Option<LoaderTsfn>,
http_handler_tsfn: Option<HttpHandlerTsfn>,
}

#[derive(Debug, Default)]
Expand Down Expand Up @@ -124,9 +130,17 @@ impl ZenEngine {
Arc::new(CustomNode::default()),
)
.into(),

loader_tsfn: None,
http_handler_tsfn: None,
custom_node_tsfn: None,
});
};

let mut loader_tsfn_opt: Option<LoaderTsfn> = None;
let mut http_handler_tsfn_opt: Option<HttpHandlerTsfn> = None;
let mut custom_node_tsfn_opt: Option<CustomNodeTsfn> = None;

let loader = match opts.loader {
None => DecisionLoader::default(),
Some(l) => {
Expand All @@ -137,7 +151,9 @@ impl ZenEngine {
.weak()
.build()?;

DecisionLoader::new(Arc::new(loader_tsfn))
let arc_loader_tsfn = Arc::new(loader_tsfn);
loader_tsfn_opt = Some(arc_loader_tsfn.clone());
DecisionLoader::new(arc_loader_tsfn)
}
};

Expand All @@ -151,7 +167,9 @@ impl ZenEngine {
.weak()
.build()?;

CustomNode::new(Arc::new(custom_tfsn))
let arc_custom_node = Arc::new(custom_tfsn);
custom_node_tsfn_opt = Some(arc_custom_node.clone());
CustomNode::new(arc_custom_node)
}
};

Expand All @@ -164,12 +182,18 @@ impl ZenEngine {
.weak()
.build()?;

let arc_http_handler_tsfn = Arc::new(http_tsfn);
http_handler_tsfn_opt = Some(arc_http_handler_tsfn.clone());
decision_engine = decision_engine
.with_http_handler(Some(Arc::new(NodeHttpHandler::new(Arc::new(http_tsfn)))));
.with_http_handler(Some(Arc::new(NodeHttpHandler::new(arc_http_handler_tsfn))));
}

Ok(Self {
graph: Arc::new(decision_engine),

loader_tsfn: loader_tsfn_opt,
http_handler_tsfn: http_handler_tsfn_opt,
custom_node_tsfn: custom_node_tsfn_opt,
})
}

Expand Down Expand Up @@ -228,7 +252,9 @@ impl ZenEngine {
Ok(ZenDecision::from(decision))
}

#[napi(ts_return_type = "Promise<SafeResult<ZenEngineResponse>>")]
#[napi(
ts_return_type = "{ success: true, data: ZenEngineResponse } | { success: false; error: any; }"
)]
pub async fn safe_evaluate(
&self,
key: String,
Expand All @@ -238,8 +264,25 @@ impl ZenEngine {
self.evaluate(key, context, opts).await.into()
}

#[napi(ts_return_type = "Promise<SafeResult<ZenDecision>>")]
#[napi(
ts_return_type = "{ success: true, data: ZenDecision } | { success: false; error: any; }"
)]
pub async fn safe_get_decision(&self, key: String) -> SafeResult<ZenDecision> {
self.get_decision(key).await.into()
}

#[napi]
pub fn dispose(&self) {
if let Some(loader_tsfn) = &self.loader_tsfn {
let _ = loader_tsfn.handle.dispose();
}

if let Some(http_handler_tsfn) = &self.http_handler_tsfn {
let _ = http_handler_tsfn.handle.dispose();
}

if let Some(custom_node) = &self.custom_node_tsfn {
let _ = custom_node.handle.dispose();
}
}
}
2 changes: 1 addition & 1 deletion bindings/nodejs/src/http_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use zen_engine::nodes::http_handler::{
HttpHandlerResponse as EngineHttpHandlerResponse,
};

type HttpHandlerTsfn = Arc<
pub(crate) type HttpHandlerTsfn = Arc<
ThreadsafeFunction<
ZenHttpHandlerRequest,
Promise<ZenHttpHandlerResponse>,
Expand Down
1 change: 1 addition & 0 deletions bindings/nodejs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod config;
mod content;
mod custom_node;
mod decision;
mod dispose;
mod engine;
mod expression;
mod http_handler;
Expand Down
2 changes: 1 addition & 1 deletion bindings/nodejs/src/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use zen_engine::model::DecisionContent;

use crate::content::ZenDecisionContent;

type LoaderTsfn = Arc<
pub(crate) type LoaderTsfn = Arc<
ThreadsafeFunction<
String,
Promise<Option<Either<Buffer, &'static ZenDecisionContent>>>,
Expand Down
Loading