briefcase-node 2.4.1

Node.js bindings for Briefcase AI
Documentation
//! Node.js bindings for core data models

use napi::{Result, bindgen_prelude::*, JsObject};
use std::collections::HashMap;
use briefcase_core::{
    Input, Output, ModelParameters, ExecutionContext, DecisionSnapshot,
    Snapshot, SnapshotType, SnapshotMetadata
};

/// Node.js wrapper for Input
#[napi]
pub struct JsInput {
    inner: Input,
}

#[napi]
impl JsInput {
    #[napi(constructor)]
    pub fn new(name: String, value: serde_json::Value, data_type: String) -> Self {
        Self {
            inner: Input::new(name, value, data_type),
        }
    }

    #[napi(getter)]
    pub fn name(&self) -> String {
        self.inner.name.clone()
    }

    #[napi(getter)]
    pub fn value(&self) -> serde_json::Value {
        self.inner.value.clone()
    }

    #[napi(getter)]
    pub fn data_type(&self) -> String {
        self.inner.data_type.clone()
    }

    #[napi]
    pub fn to_object(&self) -> Result<serde_json::Value> {
        Ok(serde_json::json!({
            "name": self.inner.name,
            "value": self.inner.value,
            "data_type": self.inner.data_type,
        }))
    }
}

/// Node.js wrapper for Output
#[napi]
pub struct JsOutput {
    inner: Output,
}

#[napi]
impl JsOutput {
    #[napi(constructor)]
    pub fn new(name: String, value: serde_json::Value, data_type: String) -> Self {
        Self {
            inner: Output::new(name, value, data_type),
        }
    }

    #[napi]
    pub fn with_confidence(&mut self, confidence: f64) -> &Self {
        self.inner = self.inner.clone().with_confidence(confidence);
        self
    }

    #[napi(getter)]
    pub fn name(&self) -> String {
        self.inner.name.clone()
    }

    #[napi(getter)]
    pub fn value(&self) -> serde_json::Value {
        self.inner.value.clone()
    }

    #[napi(getter)]
    pub fn data_type(&self) -> String {
        self.inner.data_type.clone()
    }

    #[napi(getter)]
    pub fn confidence(&self) -> Option<f64> {
        self.inner.confidence
    }

    #[napi]
    pub fn to_object(&self) -> Result<serde_json::Value> {
        let mut obj = serde_json::json!({
            "name": self.inner.name,
            "value": self.inner.value,
            "data_type": self.inner.data_type,
        });

        if let Some(confidence) = self.inner.confidence {
            obj["confidence"] = serde_json::Value::Number(
                serde_json::Number::from_f64(confidence).unwrap()
            );
        }

        Ok(obj)
    }
}

/// Node.js wrapper for ModelParameters
#[napi]
pub struct JsModelParameters {
    inner: ModelParameters,
}

#[napi]
impl JsModelParameters {
    #[napi(constructor)]
    pub fn new(model_name: String) -> Self {
        Self {
            inner: ModelParameters::new(model_name),
        }
    }

    #[napi]
    pub fn with_provider(&mut self, provider: String) -> &Self {
        self.inner = self.inner.clone().with_provider(provider);
        self
    }

    #[napi]
    pub fn with_parameter(&mut self, key: String, value: serde_json::Value) -> &Self {
        self.inner = self.inner.clone().with_parameter(key, value);
        self
    }

    #[napi(getter)]
    pub fn model_name(&self) -> String {
        self.inner.model_name.clone()
    }

    #[napi(getter)]
    pub fn provider(&self) -> Option<String> {
        self.inner.provider.clone()
    }

    #[napi(getter)]
    pub fn parameters(&self) -> HashMap<String, serde_json::Value> {
        self.inner.parameters.clone()
    }
}

/// Node.js wrapper for ExecutionContext
#[napi]
pub struct JsExecutionContext {
    inner: ExecutionContext,
}

#[napi]
impl JsExecutionContext {
    #[napi(constructor)]
    pub fn new() -> Self {
        Self {
            inner: ExecutionContext::new(),
        }
    }

    #[napi]
    pub fn with_environment(&mut self, environment: String) -> &Self {
        self.inner = self.inner.clone().with_environment(environment);
        self
    }

    #[napi]
    pub fn with_service_name(&mut self, service_name: String) -> &Self {
        self.inner = self.inner.clone().with_service_name(service_name);
        self
    }

    #[napi]
    pub fn with_version(&mut self, version: String) -> &Self {
        self.inner = self.inner.clone().with_version(version);
        self
    }

    #[napi]
    pub fn with_custom_field(&mut self, key: String, value: serde_json::Value) -> &Self {
        self.inner = self.inner.clone().with_custom_field(key, value);
        self
    }

    #[napi(getter)]
    pub fn environment(&self) -> Option<String> {
        self.inner.environment.clone()
    }

    #[napi(getter)]
    pub fn service_name(&self) -> Option<String> {
        self.inner.service_name.clone()
    }

    #[napi(getter)]
    pub fn version(&self) -> Option<String> {
        self.inner.version.clone()
    }
}

/// Node.js wrapper for DecisionSnapshot
#[napi]
pub struct JsDecisionSnapshot {
    pub inner: DecisionSnapshot,
}

#[napi]
impl JsDecisionSnapshot {
    #[napi(constructor)]
    pub fn new(function_name: String) -> Self {
        Self {
            inner: DecisionSnapshot::new(function_name),
        }
    }

    #[napi]
    pub fn with_module(&mut self, module_name: String) -> &Self {
        self.inner = self.inner.clone().with_module(module_name);
        self
    }

    #[napi]
    pub fn add_input(&mut self, input: &JsInput) -> &Self {
        self.inner = self.inner.clone().add_input(input.inner.clone());
        self
    }

    #[napi]
    pub fn add_output(&mut self, output: &JsOutput) -> &Self {
        self.inner = self.inner.clone().add_output(output.inner.clone());
        self
    }

    #[napi]
    pub fn with_model_parameters(&mut self, params: &JsModelParameters) -> &Self {
        self.inner = self.inner.clone().with_model_parameters(params.inner.clone());
        self
    }

    #[napi]
    pub fn with_execution_time(&mut self, time_ms: f64) -> &Self {
        self.inner = self.inner.clone().with_execution_time(time_ms);
        self
    }

    #[napi]
    pub fn add_tag(&mut self, key: String, value: String) -> &Self {
        self.inner = self.inner.clone().add_tag(key, value);
        self
    }

    #[napi(getter)]
    pub fn function_name(&self) -> String {
        self.inner.function_name.clone()
    }

    #[napi(getter)]
    pub fn module_name(&self) -> Option<String> {
        self.inner.module_name.clone()
    }

    #[napi(getter)]
    pub fn execution_time_ms(&self) -> Option<f64> {
        self.inner.execution_time_ms
    }

    #[napi(getter)]
    pub fn tags(&self) -> HashMap<String, String> {
        self.inner.tags.clone()
    }

    #[napi]
    pub fn to_object(&self) -> Result<serde_json::Value> {
        serde_json::to_value(&self.inner)
            .map_err(|e| napi::Error::from_reason(format!("Serialization error: {}", e)))
    }
}

/// Node.js wrapper for Snapshot
#[napi]
pub struct JsSnapshot {
    pub inner: Snapshot,
}

#[napi]
impl JsSnapshot {
    #[napi(constructor)]
    pub fn new(snapshot_type: String) -> Result<Self> {
        let snap_type = match snapshot_type.as_str() {
            "session" => SnapshotType::Session,
            "decision" => SnapshotType::Decision,
            "batch" => SnapshotType::Batch,
            _ => return Err(napi::Error::from_reason(
                format!("Invalid snapshot type: {}", snapshot_type)
            )),
        };

        Ok(Self {
            inner: Snapshot::new(snap_type),
        })
    }

    #[napi]
    pub fn add_decision(&mut self, decision: &JsDecisionSnapshot) {
        self.inner.add_decision(decision.inner.clone());
    }

    #[napi(getter)]
    pub fn snapshot_type(&self) -> String {
        format!("{:?}", self.inner.snapshot_type).to_lowercase()
    }

    #[napi(getter)]
    pub fn decision_count(&self) -> u32 {
        self.inner.decisions.len() as u32
    }

    #[napi]
    pub fn to_object(&self) -> Result<serde_json::Value> {
        serde_json::to_value(&self.inner)
            .map_err(|e| napi::Error::from_reason(format!("Serialization error: {}", e)))
    }
}

/// Node.js wrapper for SnapshotQuery
#[napi]
pub struct JsSnapshotQuery {
    pub inner: briefcase_core::storage::SnapshotQuery,
}

#[napi]
impl JsSnapshotQuery {
    #[napi(constructor)]
    pub fn new() -> Self {
        Self {
            inner: briefcase_core::storage::SnapshotQuery::new(),
        }
    }

    #[napi]
    pub fn with_function_name(&mut self, function_name: String) -> &Self {
        self.inner = self.inner.clone().with_function_name(function_name);
        self
    }

    #[napi]
    pub fn with_module_name(&mut self, module_name: String) -> &Self {
        self.inner = self.inner.clone().with_module_name(module_name);
        self
    }

    #[napi]
    pub fn with_limit(&mut self, limit: u32) -> &Self {
        self.inner = self.inner.clone().with_limit(limit as usize);
        self
    }

    #[napi]
    pub fn with_offset(&mut self, offset: u32) -> &Self {
        self.inner = self.inner.clone().with_offset(offset as usize);
        self
    }

    #[napi]
    pub fn with_tag(&mut self, key: String, value: String) -> &Self {
        self.inner = self.inner.clone().with_tag(key, value);
        self
    }
}