tanxium 0.3.0

Embeddable JavaScript and TypeScript runtime with Yasumu APIs
use std::{
    collections::HashMap,
    path::PathBuf,
    sync::{Arc, Mutex, RwLock},
};

/// Thread-safe backing store for `yasumu:virtual/*` modules.
pub type VirtualModulesStore = Arc<Mutex<HashMap<String, String>>>;

#[derive(Clone, Debug, Default)]
pub struct RuntimeContext {
    /// Host application version exposed through `Yasumu.version`.
    pub app_version: String,
    pub app_data_dir: Option<PathBuf>,
    pub resource_dir: Option<PathBuf>,
    pub workspace_dir: Option<PathBuf>,
    pub dev_mode: bool,
    pub ready: bool,
    pub rpc_port: Option<u16>,
    pub echo_server_port: Option<u16>,
    pub mcp_server_port: Option<u16>,
}

#[derive(Clone, Debug)]
pub enum RuntimeEvent {
    /// JSON event emitted by the JavaScript runtime.
    Renderer(String),
    Failure(String),
}

/// A host's response to a Deno permission request.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum PermissionPromptResponse {
    /// Grant only the current permission request.
    Allow,
    /// Grant this permission category for the remainder of the runtime.
    AllowAll,
    /// Reject the request.
    Deny,
}

pub trait RuntimeHost: Send + Sync + 'static {
    /// Receives renderer and failure events generated by the runtime.
    fn emit_event(&self, event: RuntimeEvent);
    /// Requests a synchronous confirmation. The default host policy denies it.
    fn confirm(&self, _title: &str, _message: &str, _yes: &str, _no: &str, _cancel: &str) -> bool {
        false
    }
    /// Requests a Deno permission decision. GUI hosts can expose the full
    /// Allow, Allow All, and Deny choice set for unary permissions.
    fn prompt_permission(
        &self,
        message: &str,
        name: &str,
        api_name: Option<&str>,
        _is_unary: bool,
    ) -> PermissionPromptResponse {
        let api_suffix = api_name
            .map(|api| format!("\nRequested by: {api}"))
            .unwrap_or_default();
        let title = format!("Allow {name} permission?");

        if self.confirm(
            &title,
            &format!("{message}{api_suffix}"),
            "Allow",
            "Deny",
            "Deny",
        ) {
            PermissionPromptResponse::Allow
        } else {
            PermissionPromptResponse::Deny
        }
    }
}

#[derive(Default)]
pub struct NoopHost;
impl RuntimeHost for NoopHost {
    fn emit_event(&self, _event: RuntimeEvent) {}
}

pub struct RuntimeState {
    /// Mutable runtime context shared by operations and module loading.
    pub context: RwLock<RuntimeContext>,
    pub virtual_modules: VirtualModulesStore,
    pub event_sender: Mutex<Option<tokio::sync::mpsc::UnboundedSender<String>>>,
}

impl RuntimeState {
    /// Creates runtime state using the supplied context.
    pub fn new(context: RuntimeContext) -> Self {
        Self {
            context: RwLock::new(context),
            virtual_modules: Arc::new(Mutex::new(HashMap::new())),
            event_sender: Mutex::new(None),
        }
    }
}