Skip to main content

briefcase_node/
client.rs

1//! Node.js bindings for the BriefcaseClient.
2//!
3//! Provides a simplified, synchronous client interface for Node.js applications.
4//! Due to NAPI-RS limitations with async constructors and generics, this uses
5//! plain object types for input/output.
6
7use napi::Result;
8use napi_derive::napi;
9
10/// Configuration for creating a BriefcaseClient.
11#[napi(object)]
12pub struct NodeClientConfig {
13    pub api_key: String,
14    pub server_url: String,
15}
16
17/// Information about a validated client.
18#[napi(object)]
19pub struct NodeClientInfo {
20    pub client_id: String,
21    pub permissions: Vec<String>,
22    pub rate_limit_rps: Option<u32>,
23}
24
25/// Check whether a list of permissions includes a specific one.
26///
27/// Utility function since NAPI-RS doesn't support methods on plain objects.
28#[napi]
29pub fn has_permission(info: NodeClientInfo, permission: String) -> bool {
30    info.permissions.iter().any(|p| p == &permission)
31}
32
33/// Create a validated client info from a server validation response JSON string.
34///
35/// This parses the response from POST /api/v1/auth/validate.
36///
37/// # Example
38///
39/// ```javascript
40/// const { parseValidationResponse } = require('briefcase-node');
41///
42/// const resp = await fetch('http://localhost:8080/api/v1/auth/validate', {
43///   method: 'POST',
44///   headers: { 'Content-Type': 'application/json' },
45///   body: JSON.stringify({ api_key: 'sk-my-key' })
46/// });
47/// const data = await resp.json();
48/// const clientInfo = parseValidationResponse(JSON.stringify(data));
49/// console.log(clientInfo.client_id);
50/// ```
51#[napi]
52pub fn parse_validation_response(json: String) -> Result<NodeClientInfo> {
53    let parsed: serde_json::Value = serde_json::from_str(&json)
54        .map_err(|e| napi::Error::from_reason(format!("Invalid JSON: {}", e)))?;
55
56    let valid = parsed["valid"].as_bool().unwrap_or(false);
57    if !valid {
58        return Err(napi::Error::from_reason(
59            "Validation response indicates invalid key",
60        ));
61    }
62
63    let client = &parsed["client"];
64    let client_id = client["client_id"]
65        .as_str()
66        .ok_or_else(|| napi::Error::from_reason("Missing client.client_id"))?
67        .to_string();
68
69    let permissions = client["permissions"]
70        .as_array()
71        .ok_or_else(|| napi::Error::from_reason("Missing client.permissions"))?
72        .iter()
73        .filter_map(|v| v.as_str().map(String::from))
74        .collect();
75
76    let rate_limit_rps = client["rate_limit_rps"].as_u64().map(|v| v as u32);
77
78    Ok(NodeClientInfo {
79        client_id,
80        permissions,
81        rate_limit_rps,
82    })
83}