node-app-build 6.4.1

Mini app developer CLI: scaffold, validate, package node-app-* Debian packages
//! {{name}} — native (cdylib) node-app with embedded UI.
//!
//! Scaffolded by `node-app new --profile native-fullstack`.
//!
//! ## Where to look first
//! - `AGENTS.md` (repo root) — variant matrix, manifest fields, anti-patterns.
//! - `manifest.json` — declare your capabilities. `has_ui: true`, `ui_path: "ui/dist"`.
//! - `ui/src/App.tsx` — iframe handshake skeleton.
//! - `declare_node_app!(App)` below — exports `_node_app_entry`; the host's
//!   native loader looks up exactly that symbol and calls `init()` →
//!   `handle_capability()` → `shutdown()`.

use anyhow::Result;
use async_trait::async_trait;
use node_app_sdk_rust::{
    declare_node_app, AppContext, CapabilityRequest, CapabilityResponse, NodeApp, NodeAppInfo,
    ProvidedCapability,
};
use std::sync::OnceLock;

pub struct App {
    state: OnceLock<()>,
}

impl App {
    pub const fn new() -> Self {
        Self {
            state: OnceLock::new(),
        }
    }
}

#[async_trait]
impl NodeApp for App {
    fn metadata() -> NodeAppInfo {
        NodeAppInfo {
            name: "{{name}}".into(),
            version: env!("CARGO_PKG_VERSION").into(),
            abi: "v1".into(),
        }
    }

    fn provided_capabilities() -> Vec<ProvidedCapability> {
        // Every entry here needs a matching arm in `handle_capability()` below.
        // To expose HTTP routes to your UI, add `ProvidedCapability::new("http_handler")`
        // and handle it in `handle_capability()` with payload
        // `{ method, path, headers, body }` → `{ status, headers, body }`.
        vec![ProvidedCapability::new("{{name}}.example.echo")
            .description("Echo a string back to the caller (template default).")]
    }

    async fn init(&self, _ctx: &AppContext) -> Result<()> {
        let _ = self.state.set(());
        Ok(())
    }

    async fn handle_capability(&self, request: CapabilityRequest) -> Result<CapabilityResponse> {
        match request.capability.as_str() {
            "{{name}}.example.echo" => {
                let payload = request.payload;
                Ok(CapabilityResponse::ok(serde_json::json!({
                    "echoed": payload,
                })))
            }
            other => Ok(CapabilityResponse::error(
                "UNKNOWN_CAPABILITY",
                &format!("no handler for '{}'", other),
            )),
        }
    }

    async fn shutdown(&self) -> Result<()> {
        Ok(())
    }
}

declare_node_app!(App);