briefcase-node 2.4.1

Node.js bindings for Briefcase AI
Documentation
//! Simplified Node.js bindings that avoid NAPI-RS generic constraints

use briefcase_core::*;
use napi::Result;
use napi_derive::napi;

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

#[napi]
impl SimpleInput {
    #[napi(constructor)]
    pub fn new(name: String, value: String, data_type: String) -> Result<Self> {
        let json_value = serde_json::from_str(&value).unwrap_or(serde_json::Value::String(value));

        Ok(Self {
            inner: Input::new(name, json_value, data_type),
        })
    }

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

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

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

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

#[napi]
impl SimpleOutput {
    #[napi(constructor)]
    pub fn new(name: String, value: String, data_type: String) -> Result<Self> {
        let json_value = serde_json::from_str(&value).unwrap_or(serde_json::Value::String(value));

        Ok(Self {
            inner: Output::new(name, json_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) -> String {
        self.inner.value.to_string()
    }

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

/// Simple Decision Snapshot wrapper
#[napi]
pub struct SimpleDecisionSnapshot {
    inner: DecisionSnapshot,
}

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

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

    #[napi]
    pub fn add_output(&mut self, output: &SimpleOutput) -> &Self {
        self.inner = self.inner.clone().add_output(output.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 execution_time_ms(&self) -> Option<f64> {
        self.inner.execution_time_ms
    }

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

/// Simple Drift Calculator
#[napi]
pub struct SimpleDriftCalculator {
    inner: briefcase_core::DriftCalculator,
}

#[napi]
impl SimpleDriftCalculator {
    #[napi(constructor)]
    pub fn new() -> Result<Self> {
        Ok(Self {
            inner: briefcase_core::DriftCalculator::new(),
        })
    }

    #[napi(factory)]
    pub fn with_threshold(threshold: f64) -> Result<Self> {
        Ok(Self {
            inner: briefcase_core::DriftCalculator::with_threshold(threshold),
        })
    }

    #[napi]
    pub fn calculate_drift(&self, outputs: Vec<String>) -> Result<String> {
        let metrics = self.inner.calculate_drift(&outputs);
        serde_json::to_string(&serde_json::json!({
            "consistency_score": metrics.consistency_score,
            "agreement_rate": metrics.agreement_rate,
            "drift_score": metrics.drift_score,
            "consensus_output": metrics.consensus_output,
            "outliers": metrics.outliers,
        }))
        .map_err(|e| napi::Error::from_reason(format!("Serialization error: {}", e)))
    }

    #[napi]
    pub fn get_status_string(&self, outputs: Vec<String>) -> Result<String> {
        let metrics = self.inner.calculate_drift(&outputs);
        let status = self.inner.get_status(&metrics);
        Ok(format!("{:?}", status).to_lowercase())
    }
}

/// Simple Cost Calculator
#[napi]
pub struct SimpleCostCalculator {
    inner: briefcase_core::CostCalculator,
}

#[napi]
impl SimpleCostCalculator {
    #[napi(constructor)]
    pub fn new() -> Result<Self> {
        Ok(Self {
            inner: briefcase_core::CostCalculator::new(),
        })
    }

    #[napi]
    pub fn estimate_cost(
        &self,
        model: String,
        input_tokens: u32,
        output_tokens: u32,
    ) -> Result<String> {
        match self
            .inner
            .estimate_cost(&model, input_tokens as usize, output_tokens as usize)
        {
            Ok(estimate) => serde_json::to_string(&estimate)
                .map_err(|e| napi::Error::from_reason(format!("Serialization error: {}", e))),
            Err(e) => Err(napi::Error::from_reason(format!(
                "Cost estimation error: {}",
                e
            ))),
        }
    }

    #[napi]
    pub fn check_budget(&self, spent: f64, budget: f64) -> Result<String> {
        let status = self.inner.check_budget(spent, budget);
        serde_json::to_string(&status)
            .map_err(|e| napi::Error::from_reason(format!("Serialization error: {}", e)))
    }

    #[napi]
    pub fn project_monthly_cost(
        &self,
        model: String,
        daily_input: u32,
        daily_output: u32,
        days: f64,
    ) -> Result<String> {
        match self.inner.project_monthly_cost(
            &model,
            daily_input as usize,
            daily_output as usize,
            days,
        ) {
            Ok(projection) => serde_json::to_string(&projection)
                .map_err(|e| napi::Error::from_reason(format!("Serialization error: {}", e))),
            Err(e) => Err(napi::Error::from_reason(format!("Projection error: {}", e))),
        }
    }
}

/// Simple Sanitizer
#[napi]
pub struct SimpleSanitizer {
    inner: briefcase_core::Sanitizer,
}

#[napi]
impl SimpleSanitizer {
    #[napi(constructor)]
    pub fn new() -> Result<Self> {
        Ok(Self {
            inner: briefcase_core::Sanitizer::new(),
        })
    }

    #[napi]
    pub fn sanitize(&self, text: String) -> Result<String> {
        let result = self.inner.sanitize(&text);
        serde_json::to_string(&serde_json::json!({
            "sanitized": result.sanitized,
            "redactions": result.redactions.len(),
        }))
        .map_err(|e| napi::Error::from_reason(format!("Serialization error: {}", e)))
    }

    #[napi]
    pub fn add_pattern(&mut self, name: String, pattern: String) -> Result<()> {
        self.inner
            .add_pattern(&name, &pattern)
            .map_err(|e| napi::Error::from_reason(format!("Pattern error: {}", e)))
    }

    #[napi]
    pub fn analyze(&self, text: String) -> Result<String> {
        let analysis = self.inner.analyze(&text);
        serde_json::to_string(&serde_json::json!({
            "has_pii": analysis.has_pii,
            "total_matches": analysis.total_matches,
            "unique_types": analysis.unique_types,
        }))
        .map_err(|e| napi::Error::from_reason(format!("Serialization error: {}", e)))
    }
}