briefcase-node 2.4.1

Node.js bindings for Briefcase AI
Documentation
//! Node.js bindings for the BriefcaseClient.
//!
//! Provides a simplified, synchronous client interface for Node.js applications.
//! Due to NAPI-RS limitations with async constructors and generics, this uses
//! plain object types for input/output.

use napi::Result;
use napi_derive::napi;

/// Configuration for creating a BriefcaseClient.
#[napi(object)]
pub struct NodeClientConfig {
    pub api_key: String,
    pub server_url: String,
}

/// Information about a validated client.
#[napi(object)]
pub struct NodeClientInfo {
    pub client_id: String,
    pub permissions: Vec<String>,
    pub rate_limit_rps: Option<u32>,
}

/// Check whether a list of permissions includes a specific one.
///
/// Utility function since NAPI-RS doesn't support methods on plain objects.
#[napi]
pub fn has_permission(info: NodeClientInfo, permission: String) -> bool {
    info.permissions.iter().any(|p| p == &permission)
}

/// Create a validated client info from a server validation response JSON string.
///
/// This parses the response from POST /api/v1/auth/validate.
///
/// # Example
///
/// ```javascript
/// const { parseValidationResponse } = require('briefcase-node');
///
/// const resp = await fetch('http://localhost:8080/api/v1/auth/validate', {
///   method: 'POST',
///   headers: { 'Content-Type': 'application/json' },
///   body: JSON.stringify({ api_key: 'sk-my-key' })
/// });
/// const data = await resp.json();
/// const clientInfo = parseValidationResponse(JSON.stringify(data));
/// console.log(clientInfo.client_id);
/// ```
#[napi]
pub fn parse_validation_response(json: String) -> Result<NodeClientInfo> {
    let parsed: serde_json::Value = serde_json::from_str(&json)
        .map_err(|e| napi::Error::from_reason(format!("Invalid JSON: {}", e)))?;

    let valid = parsed["valid"].as_bool().unwrap_or(false);
    if !valid {
        return Err(napi::Error::from_reason(
            "Validation response indicates invalid key",
        ));
    }

    let client = &parsed["client"];
    let client_id = client["client_id"]
        .as_str()
        .ok_or_else(|| napi::Error::from_reason("Missing client.client_id"))?
        .to_string();

    let permissions = client["permissions"]
        .as_array()
        .ok_or_else(|| napi::Error::from_reason("Missing client.permissions"))?
        .iter()
        .filter_map(|v| v.as_str().map(String::from))
        .collect();

    let rate_limit_rps = client["rate_limit_rps"].as_u64().map(|v| v as u32);

    Ok(NodeClientInfo {
        client_id,
        permissions,
        rate_limit_rps,
    })
}